diff --git a/src/bridge/vault-core-crlf.test.ts b/src/bridge/vault-core-crlf.test.ts new file mode 100644 index 0000000..bc749ee --- /dev/null +++ b/src/bridge/vault-core-crlf.test.ts @@ -0,0 +1,29 @@ +import assert from 'node:assert/strict' +import { describe, it } from 'node:test' +import { loadMobileModule } from '../../tooling/load-mobile-module.ts' + +// A note saved with Windows line endings (CRLF) splits at "\n" into lines +// that still end in "\r", and JavaScript's `.` never matches "\r", so the +// fence pattern's `(.*)$` never matched: a CRLF fence never opened, and the +// code inside it leaked into the note's tags and links. The desktop fixed +// its copies of stripCodeContent in ZenNotes/zennotes 7afe7a3b (2.77.0); +// this is the phones' copy of the same logic. +const { stripCodeContent, extractTags, extractWikilinks } = await loadMobileModule(['./src/bridge/vault-core']) + +describe('vault-core reads code fences in notes saved with CRLF line endings', () => { + it('ignores #tags inside a fence', () => { + assert.deepEqual(extractTags('```c\r\n#include \r\n```\r\nreal #ok\r\n'), ['ok']) + assert.deepEqual(extractTags('- item\r\n ~~~\r\n #nope\r\n ~~~\r\n #after\r\n'), ['after']) + }) + + it('ignores [[links]] inside a fence', () => { + assert.deepEqual(extractWikilinks('```\r\n[[Not a link]]\r\n```\r\nSee [[Real note]]\r\n'), ['Real note']) + }) + + it('keeps every line, and reads an LF note exactly as before', () => { + const crlf = '# Note\r\n```\r\n#code\r\n```\r\ntext\r\n' + assert.equal(stripCodeContent(crlf).split('\n').length, crlf.split('\n').length) + const lf = '# Note\n```js\nconst tag = "#x"\n```\nplain #kept\n' + assert.equal(stripCodeContent(lf), '# Note\n \n \n \nplain #kept\n') + }) +}) diff --git a/src/bridge/vault-core.ts b/src/bridge/vault-core.ts index 2e0b40e..6f57773 100644 --- a/src/bridge/vault-core.ts +++ b/src/bridge/vault-core.ts @@ -207,7 +207,8 @@ export function stripCodeContent(body: string): string { let fenceLen = 0 for (let i = 0; i < lines.length; i++) { const line = lines[i] as string - const m = /^[ \t]*(`{3,}|~{3,})(.*)$/.exec(line) + // `.` never matches `\r`, so the `\r?` is what lets a CRLF-saved fence line match. + const m = /^[ \t]*(`{3,}|~{3,})(.*)\r?$/.exec(line) if (m) { const marker = m[1] as string const char = marker[0] as string