Skip to content

Commit 682c9e4

Browse files
GiveMe-A-Name谢奇璇
and
谢奇璇
authored
test: Add WASM plugin tests and snapshots for emotion, relay, and remove-console (#423)
## Summary Add WASM plugin test to ensure current @swc/core can load and run wasm plugin corrently. Co-authored-by: 谢奇璇 <[email protected]>
1 parent 099c682 commit 682c9e4

File tree

26 files changed

+5394
-26
lines changed

26 files changed

+5394
-26
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55

66
node_modules
77
*.wasm
8-
.yarn/
8+
.yarn/
9+
.swc

package.json

+7-2
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,19 @@
33
"packageManager": "[email protected]",
44
"private": true,
55
"scripts": {
6-
"prepare": "husky install"
6+
"prepare": "husky install",
7+
"prepack": "pnpm --filter @swc/* run prepack",
8+
"test": "vitest run"
79
},
810
"devDependencies": {
911
"@changesets/cli": "^2.27.1",
12+
"@swc/core": "^1.11.7",
1013
"@taplo/cli": "^0.7.0",
14+
"@types/node": "^22.13.9",
1115
"husky": "^9.0.11",
1216
"lint-staged": "^15.2.2",
13-
"prettier": "^3.2.5"
17+
"prettier": "^3.2.5",
18+
"vitest": "^3.0.7"
1419
},
1520
"lint-staged": {
1621
"*.toml": [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`Should transform emotion css correctly 1`] = `
4+
"function _tagged_template_literal(strings, raw) {
5+
if (!raw) {
6+
raw = strings.slice(0);
7+
}
8+
return Object.freeze(Object.defineProperties(strings, {
9+
raw: {
10+
value: Object.freeze(raw)
11+
}
12+
}));
13+
}
14+
function _templateObject() {
15+
var data = _tagged_template_literal([
16+
"\\n width: 96px;\\n height: 96px;\\n "
17+
]);
18+
_templateObject = function _templateObject() {
19+
return data;
20+
};
21+
return data;
22+
}
23+
function _templateObject1() {
24+
var data = _tagged_template_literal([
25+
"\\n font-size: 20px;\\n @media (min-width: 420px) {\\n color: blue;\\n ",
26+
";\\n line-height: 26px;\\n }\\n background: green;\\n ",
27+
";\\n"
28+
]);
29+
_templateObject1 = function _templateObject() {
30+
return data;
31+
};
32+
return data;
33+
}
34+
var myStyles = css(_templateObject1(), css(_templateObject()), {
35+
backgroundColor: "hotpink"
36+
});
37+
"
38+
`;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
const myStyles = css`
2+
font-size: 20px;
3+
@media (min-width: 420px) {
4+
color: blue;
5+
${css`
6+
width: 96px;
7+
height: 96px;
8+
`};
9+
line-height: 26px;
10+
}
11+
background: green;
12+
${{ backgroundColor: "hotpink" }};
13+
`;
+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { describe, expect, test } from "vitest";
2+
import path from "node:path";
3+
import fs from "node:fs/promises";
4+
import url from "node:url";
5+
import { type Options, transform } from "@swc/core";
6+
7+
const pluginName = "swc_plugin_emotion.wasm";
8+
9+
const options: Options = {
10+
jsc: {
11+
parser: {
12+
syntax: "ecmascript",
13+
},
14+
experimental: {
15+
plugins: [
16+
[
17+
path.join(
18+
path.dirname(url.fileURLToPath(import.meta.url)),
19+
"..",
20+
pluginName,
21+
),
22+
{},
23+
],
24+
],
25+
},
26+
},
27+
};
28+
29+
test("Should transform emotion css correctly", async () => {
30+
const code = await fs.readFile(
31+
path.resolve(
32+
url.fileURLToPath(import.meta.url),
33+
"..",
34+
"fixtures",
35+
"input.js",
36+
),
37+
"utf-8",
38+
);
39+
const output = await transform(code, options);
40+
expect(output.code).toMatchSnapshot();
41+
});

packages/emotion/package.json

+23-23
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
{
2-
"name": "@swc/plugin-emotion",
3-
"version": "9.0.0",
4-
"description": "SWC plugin for emotion css-in-js library",
5-
"main": "swc_plugin_emotion.wasm",
6-
"scripts": {
7-
"prepack": "cargo build --release -p swc_plugin_emotion --target wasm32-wasi && cp ../../target/wasm32-wasi/release/swc_plugin_emotion.wasm ."
8-
},
9-
"homepage": "https://swc.rs",
10-
"repository": {
11-
"type": "git",
12-
"url": "git+https://github.com/swc-project/plugins.git",
13-
"directory": "packages/emotion"
14-
},
15-
"bugs": {
16-
"url": "https://github.com/swc-project/plugins/issues"
17-
},
18-
"author": "강동윤 <[email protected]>",
19-
"keywords": [],
20-
"license": "Apache-2.0",
21-
"preferUnplugged": true,
22-
"dependencies": {
23-
"@swc/counter": "^0.1.3"
24-
}
2+
"name": "@swc/plugin-emotion",
3+
"version": "9.0.0",
4+
"description": "SWC plugin for emotion css-in-js library",
5+
"main": "swc_plugin_emotion.wasm",
6+
"scripts": {
7+
"prepack": "cargo build --release -p swc_plugin_emotion --target wasm32-wasi && cp ../../target/wasm32-wasi/release/swc_plugin_emotion.wasm ."
8+
},
9+
"homepage": "https://swc.rs",
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/swc-project/plugins.git",
13+
"directory": "packages/emotion"
14+
},
15+
"bugs": {
16+
"url": "https://github.com/swc-project/plugins/issues"
17+
},
18+
"author": "강동윤 <[email protected]>",
19+
"keywords": [],
20+
"license": "Apache-2.0",
21+
"preferUnplugged": true,
22+
"dependencies": {
23+
"@swc/counter": "^0.1.3"
24+
}
2525
}
+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import { describe, expect, it } from "vitest";
2+
import { transform } from "@swc/core";
3+
import path from "node:path";
4+
import url from "node:url";
5+
6+
const transformCode = async (code: string, options = {}) => {
7+
const result = await transform(code, {
8+
jsc: {
9+
parser: {
10+
syntax: "typescript",
11+
tsx: true,
12+
},
13+
experimental: {
14+
plugins: [
15+
[
16+
path.join(
17+
path.dirname(url.fileURLToPath(import.meta.url)),
18+
"..",
19+
"swc_plugin_formatjs.wasm",
20+
),
21+
options,
22+
],
23+
],
24+
},
25+
},
26+
});
27+
return result.code;
28+
};
29+
30+
describe("formatjs swc plugin", () => {
31+
it("should transform FormattedMessage component", async () => {
32+
const input = `
33+
import React from 'react';
34+
import { FormattedMessage } from 'react-intl';
35+
36+
export function Greeting() {
37+
return (
38+
<FormattedMessage
39+
defaultMessage="Hello, {name}!"
40+
description="Greeting message"
41+
/>
42+
);
43+
}
44+
`;
45+
46+
const output = await transformCode(input);
47+
48+
expect(output).toMatch(/id: \".+\"/);
49+
expect(output).toMatch(/defaultMessage: "Hello, \{name\}!"/);
50+
expect(output).not.toMatch(/description/);
51+
});
52+
53+
it("should transform defineMessage function", async () => {
54+
const input = `
55+
import { defineMessage } from 'react-intl';
56+
57+
const message = defineMessage({
58+
defaultMessage: "Welcome to {site}",
59+
description: "Welcome message"
60+
});
61+
`;
62+
63+
const output = await transformCode(input);
64+
65+
expect(output).toMatch(/id: "[^"]+"/);
66+
expect(output).toMatch(/defaultMessage: "Welcome to \{site\}"/);
67+
expect(output).not.toMatch(/description/);
68+
});
69+
70+
it("should transform multiple messages in defineMessages", async () => {
71+
const input = `
72+
import { defineMessages } from 'react-intl';
73+
74+
const messages = defineMessages({
75+
greeting: {
76+
defaultMessage: "Hello",
77+
description: "Greeting"
78+
},
79+
farewell: {
80+
defaultMessage: "Goodbye",
81+
description: "Farewell"
82+
}
83+
});
84+
`;
85+
86+
const output = await transformCode(input);
87+
88+
const idMatches = output.match(/id:/g);
89+
expect(idMatches).toHaveLength(2);
90+
91+
expect(output).toMatch(/defaultMessage: "Hello"/);
92+
expect(output).toMatch(/defaultMessage: "Goodbye"/);
93+
94+
expect(output).not.toMatch(/description/);
95+
});
96+
97+
it("should handle formatMessage calls", async () => {
98+
const input = `
99+
import { useIntl } from 'react-intl';
100+
101+
function MyComponent() {
102+
const intl = useIntl();
103+
return intl.formatMessage({
104+
defaultMessage: "Click here",
105+
description: "Button text"
106+
});
107+
}
108+
`;
109+
110+
const output = await transformCode(input);
111+
112+
expect(output).toMatch(/id: "[^"]+"/);
113+
expect(output).toMatch(/defaultMessage: "Click here"/);
114+
expect(output).not.toMatch(/description/);
115+
});
116+
117+
it("should preserve whitespace when option is enabled", async () => {
118+
const input = `
119+
import { FormattedMessage } from 'react-intl';
120+
121+
export function Greeting() {
122+
return (
123+
<FormattedMessage
124+
defaultMessage="Hello, {name}!"
125+
description="Greeting message"
126+
/>
127+
);
128+
}
129+
`;
130+
131+
const output = await transformCode(input, {
132+
preserve_whitespace: true,
133+
});
134+
135+
expect(output).toMatch(/defaultMessage: "Hello, {4}\{name\}!"/);
136+
});
137+
138+
it("should use custom id interpolation pattern", async () => {
139+
const input = `
140+
import { FormattedMessage } from 'react-intl';
141+
142+
export function Greeting() {
143+
return (
144+
<FormattedMessage
145+
defaultMessage="Hello, {name}!"
146+
description="Greeting message"
147+
/>
148+
);
149+
}
150+
`;
151+
152+
const output = await transformCode(input, {
153+
idInterpolationPattern: "[name]_[hash:base64:5]",
154+
});
155+
156+
expect(output).toMatch(/id: "file_[a-zA-Z0-9]{5}"/);
157+
});
158+
159+
it("should handle additional component names", async () => {
160+
const input = `
161+
import { CustomMessage } from './custom-intl';
162+
163+
export function Greeting() {
164+
return (
165+
<CustomMessage
166+
defaultMessage="Hello, {name}!"
167+
description="Greeting message"
168+
/>
169+
);
170+
}
171+
`;
172+
173+
const output = await transformCode(input, {
174+
additionalComponentNames: ["CustomMessage"],
175+
});
176+
177+
expect(output).toMatch(/id: "[^"]+"/);
178+
expect(output).toMatch(/defaultMessage: "Hello, \{name\}!"/);
179+
expect(output).not.toMatch(/description/);
180+
});
181+
});

0 commit comments

Comments
 (0)