ember.nvp 0.6.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +17 -2
- package/.release-plan.json +29 -5
- package/CHANGELOG.md +73 -0
- package/package.json +3 -2
- package/packages/build-tooling-utils/README.md +37 -0
- package/packages/build-tooling-utils/index.ts +2 -0
- package/packages/build-tooling-utils/package.json +42 -0
- package/packages/build-tooling-utils/rolldown.config.js +18 -0
- package/packages/build-tooling-utils/src/extensions.ts +18 -0
- package/packages/build-tooling-utils/src/maybe-babel.ts +146 -0
- package/packages/rolldown/README.md +120 -0
- package/packages/rolldown/index.ts +60 -0
- package/packages/rolldown/package.json +59 -0
- package/packages/rolldown/rolldown.config.js +21 -0
- package/packages/rolldown/src/babel.ts +136 -0
- package/packages/rolldown/src/config.ts +50 -0
- package/packages/rolldown/src/externals.ts +83 -0
- package/packages/rolldown/src/isolated-declarations.ts +65 -0
- package/packages/rolldown/src/transform.ts +162 -0
- package/packages/vite/README.md +7 -21
- package/packages/vite/index.ts +1 -1
- package/packages/vite/package.json +4 -6
- package/packages/vite/src/ember.ts +129 -15
- package/packages/vite/src/test-html.ts +86 -0
- package/src/bases/minimal-app/files/app/router.ts +1 -0
- package/src/bases/minimal-app/files/tsconfig.json +1 -0
- package/src/bases/minimal-app/index.js +2 -2
- package/src/bases/minimal-library/files/README.md +41 -0
- package/src/bases/minimal-library/files/package.json +49 -0
- package/src/bases/minimal-library/files/tsconfig.json +10 -0
- package/src/bases/minimal-library/files/tsdown.config.js +7 -0
- package/src/bases/minimal-library/index.js +158 -2
- package/src/cli/index.js +21 -2
- package/src/cli/questions/layers.js +5 -2
- package/src/cli/questions/project-type.js +0 -5
- package/src/layers/are-the-types-wrong/index.js +74 -0
- package/src/layers/git/files/gitignore +14 -1
- package/src/layers/git/index.js +6 -5
- package/src/layers/github-actions/index.js +12 -8
- package/src/layers/index.js +1 -1
- package/src/layers/inspector-support/index.js +101 -0
- package/src/layers/prettier/files/.prettierignore +4 -0
- package/src/layers/publint/index.js +68 -0
- package/src/layers/qunit/files/tests/unit/macros-test.ts +2 -2
- package/src/layers/qunit/index.js +107 -7
- package/src/layers/qunit/library-files/config/test/babel.config.js +41 -0
- package/src/layers/qunit/library-files/config/test/testem.cjs +26 -0
- package/src/layers/qunit/library-files/tests/test-helper.ts +36 -0
- package/src/layers/qunit/library-files/tests/unit/.gitkeep +0 -0
- package/src/layers/qunit/library-files/vite.config.mjs +6 -0
- package/src/layers/typescript/index.js +43 -7
- package/src/layers/vitest/files/vitest.config.mjs +11 -11
- package/src/layers/vitest/index.js +70 -32
- package/src/utils/babel.js +9 -6
- package/src/utils/fs.js +44 -3
- package/src/utils/inspector-support.js +196 -0
- package/src/utils/npm.js +21 -7
- package/src/utils/remove-types.js +5 -0
- package/src/utils/rewrite-imports.js +157 -0
- package/src/utils/tsdown-config.js +85 -0
- package/src/utils/types.ts +5 -1
- package/test/ember.nvp/base-library.test.ts +236 -0
- package/test/ember.nvp/generator-replace-or-update.test.ts +2 -3
- package/test/ember.nvp/inspector-support.test.ts +179 -0
- package/test/ember.nvp/layer-inspector-support.test.ts +69 -0
- package/test/ember.nvp/layer-publish-checks.test.ts +109 -0
- package/test/ember.nvp/layer-qunit.test.ts +381 -16
- package/test/ember.nvp/layer-vitest.test.ts +250 -0
- package/test/ember.nvp/layers.test.ts +16 -0
- package/test/ember.nvp/library-src-fixtures.ts +81 -0
- package/test/ember.nvp/permutations/app-1.test.ts +3 -0
- package/test/ember.nvp/permutations/app-2.test.ts +3 -0
- package/test/ember.nvp/permutations/library-1.test.ts +3 -0
- package/test/ember.nvp/permutations/library-2.test.ts +3 -0
- package/test/ember.nvp/permutations/shared.ts +239 -0
- package/test/ember.nvp/rewrite-imports.test.ts +235 -0
- package/test/test-helpers/permutate.test.ts +17 -0
- package/test/test-helpers.ts +46 -8
- package/tsconfig.json +1 -0
- package/packages/vite/src/maybe-babel.ts +0 -219
- package/src/layers/qunit/files/run-tests.mjs +0 -30
- package/src/layers/vitest/files/tests/test-helper.ts +0 -5
- package/test/ember.nvp/permutations.test.ts +0 -200
- /package/src/{layers/vitest/files/tests/integration/.gitkeep → bases/minimal-library/files/src/index.ts} +0 -0
- /package/src/layers/{vitest/files/tests/unit → qunit/library-files/tests/rendering}/.gitkeep +0 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
+
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
|
|
6
|
+
import { rewriteImportsToMatchFiles } from "#utils/rewrite-imports.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Generation renames files (e.g. type removal turns .ts into .js), so
|
|
10
|
+
* import specifiers are checked against the emitted project tree:
|
|
11
|
+
* whatever file actually exists on disk wins.
|
|
12
|
+
*/
|
|
13
|
+
describe("rewriteImportsToMatchFiles", () => {
|
|
14
|
+
const dirs: string[] = [];
|
|
15
|
+
|
|
16
|
+
afterEach(async () => {
|
|
17
|
+
for (const dir of dirs.splice(0)) {
|
|
18
|
+
await rm(dir, { recursive: true, force: true });
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Lays out an emitted-project fixture on disk (the way generation
|
|
24
|
+
* stages real files) and returns its root.
|
|
25
|
+
*/
|
|
26
|
+
async function makeProject(files: Record<string, string>) {
|
|
27
|
+
const dir = await mkdtemp(join(tmpdir(), "rewrite-imports-test-"));
|
|
28
|
+
dirs.push(dir);
|
|
29
|
+
|
|
30
|
+
for (const [path, contents] of Object.entries(files)) {
|
|
31
|
+
await mkdir(join(dir, dirname(path)), { recursive: true });
|
|
32
|
+
await writeFile(join(dir, path), contents);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return dir;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const manifest = JSON.stringify({
|
|
39
|
+
name: "fixture",
|
|
40
|
+
imports: {
|
|
41
|
+
"#app/*": "./app/*",
|
|
42
|
+
"#config": "./app/config.js",
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("rewrites a specifier whose file was renamed", async () => {
|
|
47
|
+
const project = await makeProject({
|
|
48
|
+
"package.json": manifest,
|
|
49
|
+
"app/app.js": "",
|
|
50
|
+
"app/config.js": "",
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const result = rewriteImportsToMatchFiles(
|
|
54
|
+
[`import Application from "#app/app.ts";`, `import config from "#config";`].join("\n"),
|
|
55
|
+
join(project, "tests/test-helper.js"),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
expect(result).toMatchInlineSnapshot(`
|
|
59
|
+
"import Application from "#app/app.js";
|
|
60
|
+
import config from "#config";"
|
|
61
|
+
`);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("leaves a specifier alone when its file exists as written", async () => {
|
|
65
|
+
const project = await makeProject({
|
|
66
|
+
"package.json": manifest,
|
|
67
|
+
"app/helpers/x.ts": "",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const result = rewriteImportsToMatchFiles(
|
|
71
|
+
`import x from "./helpers/x.ts";`,
|
|
72
|
+
join(project, "app/consumer.ts"),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
expect(result).toMatchInlineSnapshot(`"import x from "./helpers/x.ts";"`);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("adds the on-disk extension to extensionless local imports", async () => {
|
|
79
|
+
const project = await makeProject({
|
|
80
|
+
"package.json": manifest,
|
|
81
|
+
"app/helpers/x.js": "",
|
|
82
|
+
"app/app.ts": "",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const result = rewriteImportsToMatchFiles(
|
|
86
|
+
[`import x from "./helpers/x";`, `import Application from "#app/app";`].join("\n"),
|
|
87
|
+
join(project, "app/consumer.js"),
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
expect(result).toMatchInlineSnapshot(`
|
|
91
|
+
"import x from "./helpers/x.js";
|
|
92
|
+
import Application from "#app/app.ts";"
|
|
93
|
+
`);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("keeps an extensionless specifier whose fully-specified form would not map", async () => {
|
|
97
|
+
const project = await makeProject({
|
|
98
|
+
"package.json": manifest,
|
|
99
|
+
"app/config.js": "",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// `#config` maps to "./app/config.js" -- the extension lives in the
|
|
103
|
+
// `imports` target, so "#config.js" would not resolve
|
|
104
|
+
const result = rewriteImportsToMatchFiles(
|
|
105
|
+
`import config from "#config";`,
|
|
106
|
+
join(project, "app/consumer.js"),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(result).toMatchInlineSnapshot(`"import config from "#config";"`);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("rewrites relative, re-export, and dynamic import() specifiers", async () => {
|
|
113
|
+
const project = await makeProject({
|
|
114
|
+
"package.json": manifest,
|
|
115
|
+
"app/thing.js": "",
|
|
116
|
+
"shared/other.js": "",
|
|
117
|
+
"app/lazy.js": "",
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const result = rewriteImportsToMatchFiles(
|
|
121
|
+
[
|
|
122
|
+
`export { thing } from "./thing.ts";`,
|
|
123
|
+
`export * from "../shared/other.ts";`,
|
|
124
|
+
`export async function load() {`,
|
|
125
|
+
` return await import("./lazy.ts");`,
|
|
126
|
+
`}`,
|
|
127
|
+
].join("\n"),
|
|
128
|
+
join(project, "app/consumer.js"),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
expect(result).toMatchInlineSnapshot(`
|
|
132
|
+
"export { thing } from "./thing.js";
|
|
133
|
+
export * from "../shared/other.js";
|
|
134
|
+
export async function load() {
|
|
135
|
+
return await import("./lazy.js");
|
|
136
|
+
}"
|
|
137
|
+
`);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("leaves a specifier alone when no file matches at all", async () => {
|
|
141
|
+
const project = await makeProject({
|
|
142
|
+
"package.json": manifest,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
const result = rewriteImportsToMatchFiles(
|
|
146
|
+
`import missing from "./does-not-exist.ts";`,
|
|
147
|
+
join(project, "app/consumer.js"),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
expect(result).toMatchInlineSnapshot(`"import missing from "./does-not-exist.ts";"`);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("leaves package specifiers and non-import strings alone", async () => {
|
|
154
|
+
const project = await makeProject({
|
|
155
|
+
"package.json": manifest,
|
|
156
|
+
"app/looks-like-a-file.js": "",
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
const result = rewriteImportsToMatchFiles(
|
|
160
|
+
[`import lib from "some-lib/file.ts";`, `const notAnImport = "./looks-like-a-file.ts";`].join(
|
|
161
|
+
"\n",
|
|
162
|
+
),
|
|
163
|
+
join(project, "app/consumer.js"),
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
expect(result).toMatchInlineSnapshot(`
|
|
167
|
+
"import lib from "some-lib/file.ts";
|
|
168
|
+
const notAnImport = "./looks-like-a-file.ts";"
|
|
169
|
+
`);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("rewrites imports in gjs files while preserving the template", async () => {
|
|
173
|
+
const project = await makeProject({
|
|
174
|
+
"package.json": manifest,
|
|
175
|
+
"app/components/button.gjs": "",
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const result = rewriteImportsToMatchFiles(
|
|
179
|
+
[
|
|
180
|
+
`import Component from "@glimmer/component";`,
|
|
181
|
+
`import Button from "./button.gts";`,
|
|
182
|
+
``,
|
|
183
|
+
`export default class Foo extends Component {`,
|
|
184
|
+
` <template>`,
|
|
185
|
+
` <Button />`,
|
|
186
|
+
` </template>`,
|
|
187
|
+
`}`,
|
|
188
|
+
].join("\n"),
|
|
189
|
+
join(project, "app/components/foo.gjs"),
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
expect(result).toMatchInlineSnapshot(`
|
|
193
|
+
"import Component from "@glimmer/component";
|
|
194
|
+
import Button from "./button.gjs";
|
|
195
|
+
export default class Foo extends Component {
|
|
196
|
+
<template><Button /></template>
|
|
197
|
+
}"
|
|
198
|
+
`);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it("keeps comments in files whose imports are rewritten", async () => {
|
|
202
|
+
const project = await makeProject({
|
|
203
|
+
"package.json": manifest,
|
|
204
|
+
"app/app.js": "",
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const result = rewriteImportsToMatchFiles(
|
|
208
|
+
[
|
|
209
|
+
`// top comment`,
|
|
210
|
+
`import Application from "#app/app.ts";`,
|
|
211
|
+
``,
|
|
212
|
+
`/**`,
|
|
213
|
+
` * doc comment for start`,
|
|
214
|
+
` */`,
|
|
215
|
+
`export function start() {`,
|
|
216
|
+
` // inner comment`,
|
|
217
|
+
` Application.create();`,
|
|
218
|
+
`}`,
|
|
219
|
+
].join("\n"),
|
|
220
|
+
join(project, "tests/test-helper.js"),
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect(result).toMatchInlineSnapshot(`
|
|
224
|
+
"// top comment
|
|
225
|
+
import Application from "#app/app.js";
|
|
226
|
+
/**
|
|
227
|
+
* doc comment for start
|
|
228
|
+
*/
|
|
229
|
+
export function start() {
|
|
230
|
+
// inner comment
|
|
231
|
+
Application.create();
|
|
232
|
+
}"
|
|
233
|
+
`);
|
|
234
|
+
});
|
|
235
|
+
});
|
|
@@ -51,6 +51,23 @@ describe("permutate", () => {
|
|
|
51
51
|
expect(hasOneEslint).toBe(true);
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
+
it("enforces mutual exclusivity for testing layers", () => {
|
|
55
|
+
const result = permutate(["qunit", "vitest", "other"]);
|
|
56
|
+
|
|
57
|
+
for (const permutation of result) {
|
|
58
|
+
const testingCount = permutation.filter(
|
|
59
|
+
(item) => item === "qunit" || item === "vitest",
|
|
60
|
+
).length;
|
|
61
|
+
expect(testingCount).toBeLessThanOrEqual(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const hasQunit = result.some((perm) => perm.includes("qunit"));
|
|
65
|
+
const hasVitest = result.some((perm) => perm.includes("vitest"));
|
|
66
|
+
|
|
67
|
+
expect(hasQunit).toBe(true);
|
|
68
|
+
expect(hasVitest).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
54
71
|
it("generates valid permutations with mixed items", () => {
|
|
55
72
|
const result = permutate(["a", "eslint-foo", "eslint-bar", "b"]);
|
|
56
73
|
|
package/test/test-helpers.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { execa } from "execa";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { styleText } from "node:util";
|
|
4
4
|
import { Readable, Writable } from "node:stream";
|
|
5
|
-
import { mkdtemp } from "node:fs/promises";
|
|
5
|
+
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
|
6
6
|
import { tmpdir } from "node:os";
|
|
7
7
|
import { generateProject, Project } from "ember.nvp";
|
|
8
8
|
import { discoverLayers } from "#layers";
|
|
@@ -16,10 +16,22 @@ export const bases = [minimalApp, minimalAddon];
|
|
|
16
16
|
|
|
17
17
|
export const layers = await discoverLayers();
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Layers in the same group are alternatives to each other (one eslint
|
|
21
|
+
* config, one test framework): selecting two of them would produce a
|
|
22
|
+
* broken project, so permutations never combine them.
|
|
23
|
+
*/
|
|
24
|
+
function exclusivityGroup(name: string): string | undefined {
|
|
25
|
+
if (name.startsWith("eslint-")) return "eslint";
|
|
26
|
+
if (name === "qunit" || name === "vitest") return "testing";
|
|
27
|
+
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
19
31
|
export function permutate(toPermutate: string[]): string[][] {
|
|
20
32
|
const out: string[][] = [];
|
|
21
33
|
|
|
22
|
-
function backtrack(startIndex: number, prefix: string[],
|
|
34
|
+
function backtrack(startIndex: number, prefix: string[], usedGroups: Set<string>) {
|
|
23
35
|
if (prefix.length > 0) out.push(prefix.slice());
|
|
24
36
|
|
|
25
37
|
for (let i = startIndex; i < toPermutate.length; i++) {
|
|
@@ -34,19 +46,20 @@ export function permutate(toPermutate: string[]): string[][] {
|
|
|
34
46
|
// (silly TS not understanding loop bounds)
|
|
35
47
|
if (!option) continue;
|
|
36
48
|
|
|
37
|
-
const
|
|
49
|
+
const group = exclusivityGroup(option);
|
|
38
50
|
|
|
39
|
-
// Skip this option if
|
|
40
|
-
|
|
41
|
-
if (isEslint && hasEslint) continue;
|
|
51
|
+
// Skip this option if we already have one from its exclusivity group
|
|
52
|
+
if (group && usedGroups.has(group)) continue;
|
|
42
53
|
|
|
54
|
+
if (group) usedGroups.add(group);
|
|
43
55
|
prefix.push(option);
|
|
44
|
-
backtrack(i + 1, prefix,
|
|
56
|
+
backtrack(i + 1, prefix, usedGroups);
|
|
45
57
|
prefix.pop();
|
|
58
|
+
if (group) usedGroups.delete(group);
|
|
46
59
|
}
|
|
47
60
|
}
|
|
48
61
|
|
|
49
|
-
backtrack(0, [],
|
|
62
|
+
backtrack(0, [], new Set());
|
|
50
63
|
return out;
|
|
51
64
|
}
|
|
52
65
|
|
|
@@ -54,6 +67,31 @@ export async function mktemp(name = "ember.nvp_test-") {
|
|
|
54
67
|
return await mkdtemp(join(tmpdir(), `${name}-`));
|
|
55
68
|
}
|
|
56
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Temporary: `rolldown-plugin-dts` (used by tsdown for `.d.ts` generation)
|
|
72
|
+
* imports `walk` from `yuku-parser`, but yuku-parser >= 0.6.6 moved `walk` to
|
|
73
|
+
* `yuku-ast`. Its dependency range still floats to the broken versions, so a
|
|
74
|
+
* fresh install of a generated library crashes when tsdown builds.
|
|
75
|
+
*
|
|
76
|
+
* The yuku toolchain publishes in lockstep on a shared `@yuku-toolchain/types`,
|
|
77
|
+
* so `yuku-parser` and `yuku-codegen` must be pinned together (pinning only one
|
|
78
|
+
* splits the toolchain and the dts/publint pipeline silently bails). Pin the
|
|
79
|
+
* last working set before install -- here in the test harness only, so the
|
|
80
|
+
* generated blueprint stays override-free. Remove once the plugin is fixed:
|
|
81
|
+
* https://github.com/sxzz/rolldown-plugin-dts/issues/277
|
|
82
|
+
*/
|
|
83
|
+
export async function pinYukuParser(project: Project): Promise<void> {
|
|
84
|
+
const manifestPath = join(project.directory, "package.json");
|
|
85
|
+
const manifest = JSON.parse(await readFile(manifestPath, "utf-8"));
|
|
86
|
+
|
|
87
|
+
manifest.pnpm ??= {};
|
|
88
|
+
manifest.pnpm.overrides ??= {};
|
|
89
|
+
manifest.pnpm.overrides["yuku-parser"] = "0.6.5";
|
|
90
|
+
manifest.pnpm.overrides["yuku-codegen"] = "0.6.5";
|
|
91
|
+
|
|
92
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2));
|
|
93
|
+
}
|
|
94
|
+
|
|
57
95
|
export async function reapply(project: Project, layers: string[]) {
|
|
58
96
|
return await generate({
|
|
59
97
|
directory: project.directory,
|
package/tsconfig.json
CHANGED
|
@@ -1,219 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reasons we have to have babel
|
|
3
|
-
* - decorators
|
|
4
|
-
* - ember-concurrency
|
|
5
|
-
* - scoped-css
|
|
6
|
-
* - template compilation optimization
|
|
7
|
-
*/
|
|
8
|
-
import { extensions } from "@embroider/vite";
|
|
9
|
-
import { babel } from "@rollup/plugin-babel";
|
|
10
|
-
import { join, resolve } from "node:path";
|
|
11
|
-
import { parse as oxcParse } from "oxc-parser";
|
|
12
|
-
import { transform } from "oxc-transform";
|
|
13
|
-
import { walk } from "zimmerframe";
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* if a file imports any of these, it must be optimize
|
|
17
|
-
*/
|
|
18
|
-
const babelTemplateImports = new Set([
|
|
19
|
-
// Templates
|
|
20
|
-
"@ember/template-compiler",
|
|
21
|
-
"@ember/template-compilation",
|
|
22
|
-
"ember-cli-htmlbars",
|
|
23
|
-
"ember-cli-htmlbars-inline-precompile",
|
|
24
|
-
"htmlbars-inline-precompile",
|
|
25
|
-
]);
|
|
26
|
-
|
|
27
|
-
const babelMacroImports = new Set([
|
|
28
|
-
// Macros
|
|
29
|
-
"@embroider/macros",
|
|
30
|
-
"@glimmer/env",
|
|
31
|
-
"@ember/application/deprecations",
|
|
32
|
-
// Macros only needed in prod
|
|
33
|
-
"@ember/debug",
|
|
34
|
-
]); // Babel plugins required from libraries
|
|
35
|
-
const babelOtherImports = new Set([
|
|
36
|
-
"ember-concurrency",
|
|
37
|
-
"ember-scoped-css",
|
|
38
|
-
"ember-intl/helpers/format-message",
|
|
39
|
-
]);
|
|
40
|
-
|
|
41
|
-
const babelRequiredImports = [...babelTemplateImports, ...babelMacroImports, ...babelOtherImports];
|
|
42
|
-
|
|
43
|
-
const counts = {
|
|
44
|
-
total: 0,
|
|
45
|
-
babel: 0,
|
|
46
|
-
why: {
|
|
47
|
-
decorators: 0,
|
|
48
|
-
formatMessage: 0,
|
|
49
|
-
imports: {
|
|
50
|
-
templates: 0,
|
|
51
|
-
macros: 0,
|
|
52
|
-
other: 0,
|
|
53
|
-
},
|
|
54
|
-
initializeRuntimeMacrosConfig: 0,
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
export function maybeBabel(options: { parallel?: boolean; configFile: string; env: any }) {
|
|
58
|
-
const { env, parallel, ...restOptions } = options;
|
|
59
|
-
const original = (() => {
|
|
60
|
-
const babelPath = resolve(join(process.cwd(), "./babel.config.js"));
|
|
61
|
-
|
|
62
|
-
return babel({
|
|
63
|
-
babelHelpers: "runtime",
|
|
64
|
-
extensions,
|
|
65
|
-
skipPreflightCheck: true,
|
|
66
|
-
...restOptions,
|
|
67
|
-
configFile: babelPath,
|
|
68
|
-
parallel: parallel ?? true,
|
|
69
|
-
});
|
|
70
|
-
})();
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* In @rollup/plugin-babel v7+, the `transform` hook is the object form
|
|
74
|
-
* `{ filter, handler }` rather than a plain function. Normalize to the
|
|
75
|
-
* underlying handler so we can invoke it directly.
|
|
76
|
-
*/
|
|
77
|
-
const originalTransform = (
|
|
78
|
-
typeof original.transform === "function" ? original.transform : original.transform?.handler
|
|
79
|
-
)!;
|
|
80
|
-
|
|
81
|
-
const babelMacros = new Set(babelRequiredImports);
|
|
82
|
-
if (env.mode === "development") {
|
|
83
|
-
babelMacros.delete("@ember/debug");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function doTransform(this: any, code: string, id: string) {
|
|
87
|
-
counts.total++;
|
|
88
|
-
|
|
89
|
-
const ext = id.split(".").at(-1);
|
|
90
|
-
const lang = (ext === "gjs" ? "js" : ext === "gts" ? "ts" : ext) as
|
|
91
|
-
| "js"
|
|
92
|
-
| "ts"
|
|
93
|
-
| "jsx"
|
|
94
|
-
| "tsx"
|
|
95
|
-
| "dts"
|
|
96
|
-
| undefined;
|
|
97
|
-
|
|
98
|
-
let needsBabel = false;
|
|
99
|
-
|
|
100
|
-
const estree = await oxcParse(id, code, { lang });
|
|
101
|
-
|
|
102
|
-
walk(
|
|
103
|
-
estree.program as any,
|
|
104
|
-
/* state */ {},
|
|
105
|
-
{
|
|
106
|
-
Decorator(_node: any, { stop }: any) {
|
|
107
|
-
needsBabel = true;
|
|
108
|
-
counts.why.decorators++;
|
|
109
|
-
stop();
|
|
110
|
-
},
|
|
111
|
-
MemberExpression(node: any, { stop }: any) {
|
|
112
|
-
if (node.property?.name === "formatMessage" && node.object?.name === "intl") {
|
|
113
|
-
needsBabel = true;
|
|
114
|
-
counts.why.formatMessage++;
|
|
115
|
-
stop();
|
|
116
|
-
}
|
|
117
|
-
},
|
|
118
|
-
ImportDeclaration(node: any, { stop }: any) {
|
|
119
|
-
const value = node.source.value;
|
|
120
|
-
if (babelMacros.has(value)) {
|
|
121
|
-
needsBabel = true;
|
|
122
|
-
|
|
123
|
-
if (babelTemplateImports.has(value)) {
|
|
124
|
-
counts.why.imports.templates++;
|
|
125
|
-
} else if (babelMacroImports.has(value)) {
|
|
126
|
-
counts.why.imports.macros++;
|
|
127
|
-
} else {
|
|
128
|
-
counts.why.imports.other++;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
stop();
|
|
132
|
-
}
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
if (!needsBabel && code.includes("initializeRuntimeMacrosConfig")) {
|
|
138
|
-
counts.why.initializeRuntimeMacrosConfig++;
|
|
139
|
-
needsBabel = true;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (needsBabel) {
|
|
143
|
-
counts.babel++;
|
|
144
|
-
|
|
145
|
-
const result = await originalTransform.call(this, code, id);
|
|
146
|
-
|
|
147
|
-
return result;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (ext === "json") {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (ext === "js" || ext === "gjs") {
|
|
155
|
-
// We don't need to process JS
|
|
156
|
-
return null;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const result = await transform(id, code, {
|
|
160
|
-
lang,
|
|
161
|
-
typescript: {
|
|
162
|
-
onlyRemoveTypeImports: true,
|
|
163
|
-
/**
|
|
164
|
-
* We should work towards disabling this.
|
|
165
|
-
*/
|
|
166
|
-
allowNamespaces: true,
|
|
167
|
-
removeClassFieldsWithoutInitializer: false,
|
|
168
|
-
rewriteImportExtensions: false,
|
|
169
|
-
},
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
if (result.errors?.length) {
|
|
173
|
-
console.error(`Errors during oxc-transform of ${id}:`);
|
|
174
|
-
for (const err of result.errors) {
|
|
175
|
-
if (err.labels) {
|
|
176
|
-
console.error(err.labels);
|
|
177
|
-
}
|
|
178
|
-
console.error(err.codeframe || err.message);
|
|
179
|
-
}
|
|
180
|
-
throw result.errors;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return result;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return {
|
|
187
|
-
enforce: "pre",
|
|
188
|
-
buildEnd() {
|
|
189
|
-
if (process.env.INSPECT) {
|
|
190
|
-
console.debug(`Babel usage: ${counts.babel} / ${counts.total}`, counts.why);
|
|
191
|
-
}
|
|
192
|
-
},
|
|
193
|
-
...original,
|
|
194
|
-
name: "nullvoxpopuli:babel",
|
|
195
|
-
transform: {
|
|
196
|
-
filter: {
|
|
197
|
-
id: {
|
|
198
|
-
include: [/\.js/, /\.gjs/, /\.ts/, /\.gts/],
|
|
199
|
-
exclude: [/\.json/],
|
|
200
|
-
},
|
|
201
|
-
// Enabling this opts us out of template compilation
|
|
202
|
-
// it's an AND instead of OR
|
|
203
|
-
// code: {
|
|
204
|
-
// include: [/initializeRuntimeMacrosConfig/, /precompileTemplate/],
|
|
205
|
-
// },
|
|
206
|
-
},
|
|
207
|
-
handler(this: any, code: string, id: string) {
|
|
208
|
-
try {
|
|
209
|
-
return doTransform.call(this, code, id);
|
|
210
|
-
} catch (e) {
|
|
211
|
-
// Use VSCode JavaScript Debug Terminal
|
|
212
|
-
// eslint-disable-next-line no-debugger
|
|
213
|
-
debugger;
|
|
214
|
-
throw e;
|
|
215
|
-
}
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
};
|
|
219
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { chromium } from "playwright";
|
|
2
|
-
|
|
3
|
-
const browser = await chromium.launch();
|
|
4
|
-
const page = await browser.newPage();
|
|
5
|
-
|
|
6
|
-
page.on("console", (msg) => console.log(msg.text()));
|
|
7
|
-
|
|
8
|
-
await page.goto("http://localhost:4173/tests");
|
|
9
|
-
|
|
10
|
-
await page.waitForFunction(
|
|
11
|
-
() => {
|
|
12
|
-
const elem = document.getElementById("qunit-testresult");
|
|
13
|
-
return elem?.classList.contains("complete");
|
|
14
|
-
},
|
|
15
|
-
{ timeout: 30000 },
|
|
16
|
-
);
|
|
17
|
-
|
|
18
|
-
const failed = await page.evaluate(() => {
|
|
19
|
-
const elem = document.getElementById("qunit-testresult");
|
|
20
|
-
return elem?.querySelector(".failed")?.textContent;
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
await browser.close();
|
|
24
|
-
|
|
25
|
-
if (failed && parseInt(failed) > 0) {
|
|
26
|
-
console.error(`❌ ${failed} test(s) failed`);
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
console.log("✅ All tests passed!");
|