Skip to content
Open
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
62 changes: 31 additions & 31 deletions e2e/features.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import path from 'node:path';
import { expect } from 'vitest';
import { test } from 'vitest';
import { createSnapshotSerializer } from 'path-serializer';
import { describe, expect } from 'vitest';
import { test } from 'vitest';

expect.addSnapshotSerializer(
createSnapshotSerializer({
Expand All @@ -13,44 +13,44 @@ expect.addSnapshotSerializer(
}),
);

test('should serialize file content without escaping \\"', () => {
const fileContent = `"use strict";
const a = "${require.resolve('@rslib/core')}";
`;
test('transformWin32Path', () => {
const fileContent = `{
loader: 'D:\\Users\\user\\Documents\\code\\user\\fe-dev-scripts\\src\\utils.ts',
}`;

expect(fileContent).toMatchInlineSnapshot(`
"use strict";
const a = "<ROOT>/node_modules/<PNPM_INNER>/@rslib/core/dist/index.js";
{
loader: '/d/Users/user/Documents/code/user/fe-dev-scripts/src/utils.ts',
}
`);
});

test('should serialize webpack output content', () => {
const fileContent = `;// CONCATENATED MODULE: ../../../../node_modules/.pnpm/@[email protected]/node_modules/@swc/helpers/esm/_class_private_method_get.js
function _class_private_method_get(receiver, privateSet, fn) {
if (!privateSet.has(receiver)) throw new TypeError("attempted to get private field on non-instance");
test('transformWin32RelativePath', () => {
const case1 = `Cannot find file "..\\..\\getting-started\\next"`;

return fn;
}
`;
expect(case1).toMatchInlineSnapshot(
`Cannot find file "../../getting-started/next"`,
);

expect(fileContent).toMatchInlineSnapshot(`
;// CONCATENATED MODULE: ../../../../node_modules/<PNPM_INNER>/@swc/helpers/esm/_class_private_method_get.js
function _class_private_method_get(receiver, privateSet, fn) {
if (!privateSet.has(receiver)) throw new TypeError("attempted to get private field on non-instance");
const case2 = `Cannot find file "..\\getting-started\\next"`;
expect(case2).toMatchInlineSnapshot(
`Cannot find file "../getting-started/next"`,
);

return fn;
}
`);
const case3 = `Cannot find file ".\\getting-started\\next"`;
expect(case3).toMatchInlineSnapshot(
`Cannot find file "./getting-started/next"`,
);
});

test('should serialize the Win32 path', () => {
const fileContent = `{
loader: 'D:\\Users\\user\\Documents\\code\\user\\fe-dev-scripts\\src\\utils.ts',
}`;
describe('transformCLR', () => {
test('should transform the color', () => {
const input = '\u001b[1mBold Text\u001b[0m';
expect(input).toMatchInlineSnapshot('<CLR=BOLD>Bold Text<CLR=0>');
});

expect(fileContent).toMatchInlineSnapshot(`
{
loader: '/d/Users/user/Documents/code/user/fe-dev-scripts/src/utils.ts',
}
`);
test('bad case 1', () => {
const input = 'static/react.svg'; // bad case: 'static/reactXsvg'
expect(input).toMatchInlineSnapshot('static/react.svg');
});
});
44 changes: 44 additions & 0 deletions e2e/realworld.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import path from 'node:path';
import { createSnapshotSerializer } from 'path-serializer';
import { expect } from 'vitest';
import { test } from 'vitest';

expect.addSnapshotSerializer(
createSnapshotSerializer({
root: path.resolve(__dirname, '..'),
features: {
escapeDoubleQuotes: false,
addDoubleQuotes: false,
},
}),
);

test('should serialize file content without escaping \\"', () => {
const fileContent = `"use strict";
const a = "${require.resolve('@rslib/core')}";
`;

expect(fileContent).toMatchInlineSnapshot(`
"use strict";
const a = "<ROOT>/node_modules/<PNPM_INNER>/@rslib/core/dist/index.js";
`);
});

test('should serialize webpack output content', () => {
const fileContent = `;// CONCATENATED MODULE: ../../../../node_modules/.pnpm/@[email protected]/node_modules/@swc/helpers/esm/_class_private_method_get.js
function _class_private_method_get(receiver, privateSet, fn) {
if (!privateSet.has(receiver)) throw new TypeError("attempted to get private field on non-instance");

return fn;
}
`;

expect(fileContent).toMatchInlineSnapshot(`
;// CONCATENATED MODULE: ../../../../node_modules/<PNPM_INNER>/@swc/helpers/esm/_class_private_method_get.js
function _class_private_method_get(receiver, privateSet, fn) {
if (!privateSet.has(receiver)) throw new TypeError("attempted to get private field on non-instance");

return fn;
}
`);
});
19 changes: 0 additions & 19 deletions e2e/transformCLR.test.ts

This file was deleted.

6 changes: 6 additions & 0 deletions src/createSnapshotSerializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
normalizeCLR,
normalizeCodeToPosix,
normalizePathToPosix,
normalizeWin32RelativePath,
} from './normalize';
import type { PathMatcher, SnapshotSerializerOptions } from './types';

Expand Down Expand Up @@ -37,6 +38,7 @@ export function createSnapshotSerializer(
replaceHomeDir = true,
addDoubleQuotes = true,
transformWin32Path = true,
transformWin32RelativePath = true,
escapeDoubleQuotes = true,
escapeEOL = true,
transformCLR = true,
Expand Down Expand Up @@ -87,6 +89,10 @@ export function createSnapshotSerializer(
replaced = normalizeCodeToPosix(replaced);
}

if (transformWin32RelativePath) {
replaced = normalizeWin32RelativePath(replaced);
}

replaced = applyMatcherReplacement(pathMatchers, replaced);

if (transformCLR) {
Expand Down
13 changes: 13 additions & 0 deletions src/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,25 @@
);
};

export function normalizeWin32RelativePath(p: string): string {
return p.replace(
/(['"`])(\.\.?([\\/]))+((?:[^\W_]|-)+\3?)+[^\\/]*\1/g,

Check failure

Code scanning / CodeQL

Inefficient regular expression High

This part of the regular expression may cause exponential backtracking on strings starting with '"./' and containing many repetitions of '-'.

Copilot Autofix

AI 8 months ago

To fix the issue, we need to remove the ambiguity in the regular expression. The problematic part (?:[^\W_]|-)+ can be rewritten to explicitly match either alphanumeric characters or hyphens without ambiguity. This can be achieved by replacing [^\W_] with a more specific character class, such as [a-zA-Z0-9], and ensuring that the hyphen - is handled separately. The updated regular expression will avoid exponential backtracking while maintaining the intended functionality.

The specific change will be made on line 24 of src/normalize.ts.


Suggested changeset 1
src/normalize.ts

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/normalize.ts b/src/normalize.ts
--- a/src/normalize.ts
+++ b/src/normalize.ts
@@ -23,3 +23,3 @@
   return p.replace(
-    /(['"`])(\.\.?([\\/]))+((?:[^\W_]|-)+\3?)+[^\\/]*\1/g,
+    /(['"`])(\.\.?([\\/]))+([a-zA-Z0-9-]+\3?)+[^\\/]*\1/g,
     (match: string) => {
EOF
@@ -23,3 +23,3 @@
return p.replace(
/(['"`])(\.\.?([\\/]))+((?:[^\W_]|-)+\3?)+[^\\/]*\1/g,
/(['"`])(\.\.?([\\/]))+([a-zA-Z0-9-]+\3?)+[^\\/]*\1/g,
(match: string) => {
Copilot is powered by AI and may make mistakes. Always verify output.
(match: string) => {
return match.replace(/\\/g, '/');
},
);
Comment on lines +23 to +28

Check failure

Code scanning / CodeQL

Polynomial regular expression used on uncontrolled data High

This
regular expression
that depends on
library input
may run slow on strings with many repetitions of '--'.
}

export const normalizeCLR = (str: string): string => {
return (
str
// biome-ignore lint/suspicious/noControlCharactersInRegex: copied code
.replace(/\u001b\[1m\u001b\[([0-9;]*)m/g, '<CLR=$1,BOLD>')
// biome-ignore lint/suspicious/noControlCharactersInRegex: copied code
.replace(/\u001b\[1m/g, '<CLR=BOLD>')
// biome-ignore lint/suspicious/noControlCharactersInRegex: copied code
.replace(/\u001b\[39m\u001b\[22m/g, '</CLR>')
// biome-ignore lint/suspicious/noControlCharactersInRegex: copied code
.replace(/\u001b\[([0-9;]*)m/g, '<CLR=$1>')
// CHANGE: The time unit display in Rspack is second
// CHANGE2: avoid a bad case "./react/assets.svg" -> "./react/assetsXsvg"
Expand Down
6 changes: 6 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ export interface Features {
* @default true
*/
transformWin32Path?: boolean;
/**
* `..\..\getting-started\next`
* -> `../../getting-started/next`
* @default true
*/
transformWin32RelativePath?: boolean;
/**
* "" -> \"\"
* @default true
Expand Down