Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 43 additions & 12 deletions src/Loader/Utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = '';
Expand All @@ -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')) {
Expand All @@ -247,24 +250,30 @@ 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 === '\\') {
out += code[i + 1];
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;
Expand All @@ -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++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const complexHelper = function (content, options) {
const SEP = /(\s|&nbsp;|<br\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 = `<p class="title">${escapeHTML(lastWord)}</p>`;
Expand Down
23 changes: 17 additions & 6 deletions test/stripComments.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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/; ');
});
});