Skip to content

Commit fa459b1

Browse files
authored
Merge pull request #181 from Poliklot/fix/stripcomments-regexp-180
fix: stripComments handles RegExp literals (#180)
2 parents 5feef14 + 278de8f commit fa459b1

3 files changed

Lines changed: 61 additions & 19 deletions

File tree

src/Loader/Utils.js

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,9 @@ const stringifyJSON = (data) => {
217217
*/
218218
function stripComments(code) {
219219
const len = code.length;
220-
let inStr = null; // "'", '"', or '`'
220+
let inStr = null; // "'", '"', or '`'
221+
let inRegex = false;
222+
let regexClass = false; // inside [...]
221223
let inBlockComment = false;
222224
let inLineComment = false;
223225
let out = '';
@@ -226,6 +228,7 @@ function stripComments(code) {
226228
while (i < len) {
227229
const char = code[i];
228230
const next = code[i + 1];
231+
const prev = code[i - 1];
229232

230233
// end of line comment
231234
if (inLineComment && (char === '\n' || char === '\r')) {
@@ -247,24 +250,30 @@ function stripComments(code) {
247250
continue;
248251
}
249252

250-
// string start
251-
if (!inStr && (char === '"' || char === "'" || char === '`')) {
252-
inStr = char;
253-
out += char;
254-
i++;
255-
continue;
256-
}
257-
258-
// string end (skip escaped)
253+
// inside string
259254
if (inStr) {
260255
out += char;
261256
if (char === '\\') {
262257
out += code[i + 1];
263258
i += 2;
264259
continue;
265260
}
266-
if (char === inStr) {
267-
inStr = null;
261+
if (char === inStr) inStr = null;
262+
i++;
263+
continue;
264+
}
265+
266+
// inside RegExp
267+
if (inRegex) {
268+
out += char;
269+
if (regexClass) {
270+
if (char === ']' && prev !== '\\') regexClass = false;
271+
} else {
272+
if (char === '[' && prev !== '\\') {
273+
regexClass = true;
274+
} else if (char === '/' && prev !== '\\') {
275+
inRegex = false; // flags follow
276+
}
268277
}
269278
i++;
270279
continue;
@@ -284,6 +293,28 @@ function stripComments(code) {
284293
continue;
285294
}
286295

296+
// string start
297+
if (char === '"' || char === "'" || char === '`') {
298+
inStr = char;
299+
out += char;
300+
i++;
301+
continue;
302+
}
303+
304+
// RegExp start (простая, но практичная эвристика)
305+
if (char === '/' && next !== '/' && next !== '*') {
306+
let j = i - 1;
307+
while (j >= 0 && /\s/.test(code[j])) j--;
308+
const prevSym = j >= 0 ? code[j] : '';
309+
if (j < 0 || /[({\[=:+\-*,!&|?;<>]/.test(prevSym)) {
310+
inRegex = true;
311+
regexClass = false;
312+
out += char;
313+
i++;
314+
continue;
315+
}
316+
}
317+
287318
out += char;
288319
i++;
289320
}

test/cases/_preprocessor/js-tmpl-hbs-compile-helpers-strict/src/helpers/my-helpers.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const complexHelper = function (content, options) {
4040
const SEP = /(\s|&nbsp;|<br\s*\/?>)+/gi;
4141
const parts = content.split(SEP).filter(Boolean);
4242
const lastWord = parts.pop() || '';
43-
const firstPart = parts.join('');
43+
const firstPart = parts.join(''); // magic comment
4444

4545
if (!firstPart.trim()) {
4646
out = `<p class="title">${escapeHTML(lastWord)}</p>`;

test/stripComments.test.js

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,23 @@ describe('stripComments', () => {
9595
expect(stripComments(code)).toBe(expected);
9696
});
9797

98-
test('does not remove slashes inside regex literals (known limitation)', () => {
99-
// NOTE: This will fail! The function does not support regex detection.
100-
// Documented as a limitation.
98+
test('correctly preserves regex with double slashes', () => {
10199
const code = `const re = /\\/\\/.*$/; // regex`;
102-
// The actual output will be incorrect.
103-
// This test documents the limitation (not a failure if you comment it out).
104-
// expect(stripComments(code)).toBe(`const re = /\\/\\/.*$/; `);
100+
expect(stripComments(code)).toBe(`const re = /\\/\\/.*$/; `);
101+
});
102+
103+
test('preserves RegExp with double slashes inside', () => {
104+
expect(stripComments('const re = /https?:\\/\\/example\\.com/; // trailing comment'))
105+
.toBe('const re = /https?:\\/\\/example\\.com/; ');
106+
});
107+
108+
test('does not break on block comment inside RegExp character class', () => {
109+
expect(stripComments('const re = /[/*]/; // comment'))
110+
.toBe('const re = /[/*]/; ');
111+
});
112+
113+
test('does not treat RegExp with /* */ as comment', () => {
114+
expect(stripComments('const r = /a*/*test/; // trailing'))
115+
.toBe('const r = /a*/*test/; ');
105116
});
106117
});

0 commit comments

Comments
 (0)