diff --git a/src/Loader/Utils.js b/src/Loader/Utils.js
index 4bb02fed..e5b0fc9f 100644
--- a/src/Loader/Utils.js
+++ b/src/Loader/Utils.js
@@ -217,7 +217,9 @@ const stringifyJSON = (data) => {
*/
function stripComments(code) {
const len = code.length;
- let inStr = null; // "'", '"', or '`'
+ let inStr = null; // "'", '"', or '`'
+ let inRegex = false;
+ let regexClass = false; // inside [...]
let inBlockComment = false;
let inLineComment = false;
let out = '';
@@ -226,6 +228,7 @@ function stripComments(code) {
while (i < len) {
const char = code[i];
const next = code[i + 1];
+ const prev = code[i - 1];
// end of line comment
if (inLineComment && (char === '\n' || char === '\r')) {
@@ -247,15 +250,7 @@ function stripComments(code) {
continue;
}
- // string start
- if (!inStr && (char === '"' || char === "'" || char === '`')) {
- inStr = char;
- out += char;
- i++;
- continue;
- }
-
- // string end (skip escaped)
+ // inside string
if (inStr) {
out += char;
if (char === '\\') {
@@ -263,8 +258,22 @@ function stripComments(code) {
i += 2;
continue;
}
- if (char === inStr) {
- inStr = null;
+ if (char === inStr) inStr = null;
+ i++;
+ continue;
+ }
+
+ // inside RegExp
+ if (inRegex) {
+ out += char;
+ if (regexClass) {
+ if (char === ']' && prev !== '\\') regexClass = false;
+ } else {
+ if (char === '[' && prev !== '\\') {
+ regexClass = true;
+ } else if (char === '/' && prev !== '\\') {
+ inRegex = false; // flags follow
+ }
}
i++;
continue;
@@ -284,6 +293,28 @@ function stripComments(code) {
continue;
}
+ // string start
+ if (char === '"' || char === "'" || char === '`') {
+ inStr = char;
+ out += char;
+ i++;
+ continue;
+ }
+
+ // RegExp start (простая, но практичная эвристика)
+ if (char === '/' && next !== '/' && next !== '*') {
+ let j = i - 1;
+ while (j >= 0 && /\s/.test(code[j])) j--;
+ const prevSym = j >= 0 ? code[j] : '';
+ if (j < 0 || /[({\[=:+\-*,!&|?;<>]/.test(prevSym)) {
+ inRegex = true;
+ regexClass = false;
+ out += char;
+ i++;
+ continue;
+ }
+ }
+
out += char;
i++;
}
diff --git a/test/cases/_preprocessor/js-tmpl-hbs-compile-helpers-strict/src/helpers/my-helpers.js b/test/cases/_preprocessor/js-tmpl-hbs-compile-helpers-strict/src/helpers/my-helpers.js
index 096483d5..cbf39665 100644
--- a/test/cases/_preprocessor/js-tmpl-hbs-compile-helpers-strict/src/helpers/my-helpers.js
+++ b/test/cases/_preprocessor/js-tmpl-hbs-compile-helpers-strict/src/helpers/my-helpers.js
@@ -40,7 +40,7 @@ const complexHelper = function (content, options) {
const SEP = /(\s| |
)+/gi;
const parts = content.split(SEP).filter(Boolean);
const lastWord = parts.pop() || '';
- const firstPart = parts.join('');
+ const firstPart = parts.join(''); // magic comment
if (!firstPart.trim()) {
out = `
${escapeHTML(lastWord)}
`; diff --git a/test/stripComments.test.js b/test/stripComments.test.js index 45c3036c..da5393d2 100644 --- a/test/stripComments.test.js +++ b/test/stripComments.test.js @@ -95,12 +95,23 @@ describe('stripComments', () => { expect(stripComments(code)).toBe(expected); }); - test('does not remove slashes inside regex literals (known limitation)', () => { - // NOTE: This will fail! The function does not support regex detection. - // Documented as a limitation. + test('correctly preserves regex with double slashes', () => { const code = `const re = /\\/\\/.*$/; // regex`; - // The actual output will be incorrect. - // This test documents the limitation (not a failure if you comment it out). - // expect(stripComments(code)).toBe(`const re = /\\/\\/.*$/; `); + expect(stripComments(code)).toBe(`const re = /\\/\\/.*$/; `); + }); + + test('preserves RegExp with double slashes inside', () => { + expect(stripComments('const re = /https?:\\/\\/example\\.com/; // trailing comment')) + .toBe('const re = /https?:\\/\\/example\\.com/; '); + }); + + test('does not break on block comment inside RegExp character class', () => { + expect(stripComments('const re = /[/*]/; // comment')) + .toBe('const re = /[/*]/; '); + }); + + test('does not treat RegExp with /* */ as comment', () => { + expect(stripComments('const r = /a*/*test/; // trailing')) + .toBe('const r = /a*/*test/; '); }); });