diff --git a/.gitignore b/.gitignore index 375853db..850f5572 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,9 @@ dist # Published package *.tgz +.build_complete + +# jest +/coverage + + diff --git a/__tests__/ExpensiMark-test.js b/__tests__/ExpensiMark-test.js index befe1958..fb16552d 100644 --- a/__tests__/ExpensiMark-test.js +++ b/__tests__/ExpensiMark-test.js @@ -16,6 +16,30 @@ test('Test text is unescaped', () => { expect(parser.htmlToText(htmlString)).toBe(resultString); }); +describe('codeFence', () => { + + test('Test standard code fence', () => { + const markdownString = '```\ncodeblock\nsecond line\n```'; + const htmlString = '
codeblock'; + const rawInputHtmlString = '
second line
\ncodeblock\nsecond line\n'; + expect(parser.replace(markdownString)).toBe(htmlString); + expect(parser.htmlToMarkdown(htmlString)).toBe(markdownString); + expect(parser.replace(markdownString, {shouldKeepRawInput: true})).toBe(rawInputHtmlString); + expect(parser.htmlToMarkdown(htmlString)).toBe(markdownString); + }); + + test('Test code fence with code variant', () => { + const markdownString = '\n```bash\ncodeblock\nsecond line\n```'; + const htmlString = '
codeblock'; + const rawInputHtmlString = '\n
second line
\ncodeblock\nsecond line\n'; + + expect(parser.replace(markdownString)).toBe(htmlString); + expect(parser.htmlToMarkdown(htmlString)).toBe(markdownString); + expect(parser.replace(markdownString, {shouldKeepRawInput: true})).toBe(rawInputHtmlString); + expect(parser.htmlToMarkdown(htmlString)).toBe(markdownString); + }); +}); + test('Test with regex Maximum regex stack depth reached error', () => { const testString = '
${group}`;
+ return `${group}`;
},
- rawInputReplacement: (_extras, _match, _g1, newLineCharacter, textWithinFences) => {
+ rawInputReplacement: (_extras, _match, _g1, code, newLineCharacter, textWithinFences) => {
const group = textWithinFences.replace(/(?:(?![\n\r])\s)/g, ' ').replace(/${newLineCharacter}${group}`;
+ return `${newLineCharacter}${group}`;
},
},
@@ -691,8 +691,10 @@ export default class ExpensiMark {
},
{
name: 'codeFence',
- regex: /<(pre)(?:"[^"]*"|'[^']*'|[^'">])*>([\s\S]*?)(\n?)<\/\1>(?![^<]*(<\/pre>|<\/code>))/gi,
- replacement: (_extras, _match, _g1, g2) => `\`\`\`\n${g2}\n\`\`\``,
+ regex: /<(pre)(?:\s+[^>]*?\b(?:data-info-string)\s*=\s*["']?([^\s"'<>]+)["']?[^>]*)?\s*>([\s\S]*?)(\n?)<\/\1>(?![^<]*(<\/pre>|<\/code>))/gi,
+ replacement: (_extras, _match, _g1, code, g2) => {
+ return `\`\`\`${code ?? ''}\n${g2}\n\`\`\``;
+ },
},
{
name: 'anchor',
diff --git a/package-lock.json b/package-lock.json
index e866473b..1d06313a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -48,6 +48,7 @@
"jest": "^29.0.0",
"jest-environment-jsdom": "^29.7.0",
"jit-grunt": "^0.10.0",
+ "nodemon": "^3.1.3",
"prettier": "^3.3.3",
"typescript": "^5.7.2"
}
@@ -3943,10 +3944,11 @@
}
},
"node_modules/anymatch": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
- "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@@ -4648,39 +4650,28 @@
}
},
"node_modules/chokidar": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz",
- "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "anymatch": "~3.1.1",
+ "anymatch": "~3.1.2",
"braces": "~3.0.2",
- "glob-parent": "~5.1.0",
+ "glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
"normalize-path": "~3.0.0",
- "readdirp": "~3.5.0"
+ "readdirp": "~3.6.0"
},
"engines": {
"node": ">= 8.10.0"
},
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
"optionalDependencies": {
- "fsevents": "~2.1.2"
- }
- },
- "node_modules/chokidar/node_modules/fsevents": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
- "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
- "deprecated": "\"Please update to latest v2.3 or v2.2\"",
- "dev": true,
- "hasInstallScript": true,
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ "fsevents": "~2.3.2"
}
},
"node_modules/ci-info": {
@@ -7600,6 +7591,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/has-property-descriptors": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
@@ -7781,6 +7782,13 @@
"node": ">= 4"
}
},
+ "node_modules/ignore-by-default": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/immediate": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
@@ -10386,6 +10394,19 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
+ "node_modules/link": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/link/-/link-2.1.1.tgz",
+ "integrity": "sha512-NV3AUVYBovJ6eVQcTeRoPnZSxzt2LOijNd+ugEZKRy/XeQlpTRhVRkuDv5kOlXwMAUx30vfUc7asRFb9RT65yg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "link": "dist/cli.js"
+ },
+ "funding": {
+ "url": "https://github.com/privatenumber/link?sponsor=1"
+ }
+ },
"node_modules/livereload-js": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz",
@@ -10589,6 +10610,48 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/nodemon": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz",
+ "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^3.5.2",
+ "debug": "^4",
+ "ignore-by-default": "^1.0.1",
+ "minimatch": "^3.1.2",
+ "pstree.remy": "^1.1.8",
+ "semver": "^7.5.3",
+ "simple-update-notifier": "^2.0.0",
+ "supports-color": "^5.5.0",
+ "touch": "^3.1.0",
+ "undefsafe": "^2.0.5"
+ },
+ "bin": {
+ "nodemon": "bin/nodemon.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/nodemon"
+ }
+ },
+ "node_modules/nodemon/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -11127,6 +11190,13 @@
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
"dev": true
},
+ "node_modules/pstree.remy": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@@ -11236,10 +11306,11 @@
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/readdirp": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
- "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -11673,6 +11744,19 @@
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
"dev": true
},
+ "node_modules/simple-update-notifier": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/simply-deferred": {
"version": "3.0.0",
"resolved": "git+https://git@github.com/Expensify/simply-deferred.git#77a08a95754660c7bd6e0b6979fdf84e8e831bf5",
@@ -11901,6 +11985,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/supports-preserve-symlinks-flag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
@@ -12002,6 +12099,16 @@
"node": ">=8.0"
}
},
+ "node_modules/touch": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz",
+ "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "nodetouch": "bin/nodetouch.js"
+ }
+ },
"node_modules/tough-cookie": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz",
@@ -12239,6 +12346,13 @@
"node": ">=0.10.0"
}
},
+ "node_modules/undefsafe": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/underscore": {
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
diff --git a/package.json b/package.json
index 4396d911..e0fdd9f9 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"grunt": "grunt",
"typecheck": "tsc --noEmit",
"build": "tsc -p tsconfig.build.json && cp ./lib/*.d.ts ./dist",
+ "build:watch": "nodemon --watch lib --ext .js,.jsx,.ts,.tsx,.css --exec \"rm -f .build_complete && npm run build && npm pack && touch .build_complete\"",
"test": "jest",
"lint": "eslint lib/",
"prettier": "prettier --write lib/",
@@ -43,6 +44,7 @@
"ua-parser-js": "^1.0.38"
},
"devDependencies": {
+ "link": "2.1.1",
"@babel/preset-env": "^7.26.0",
"@babel/preset-typescript": "^7.24.7",
"@lwc/eslint-plugin-lwc": "^1.8.2",
@@ -68,7 +70,9 @@
"jest-environment-jsdom": "^29.7.0",
"jit-grunt": "^0.10.0",
"prettier": "^3.3.3",
- "typescript": "^5.7.2"
+ "typescript": "^5.7.2",
+ "nodemon": "^3.1.10"
+
},
"browserify": {
"transform": [