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,74 @@
|
|
|
1
|
+
import { packageJson } from "ember-apply";
|
|
2
|
+
import { getLatest } from "#utils/npm.js";
|
|
3
|
+
import { addTsdownConfigProperty, hasTsdownConfigProperty } from "#utils/tsdown-config.js";
|
|
4
|
+
|
|
5
|
+
const deps = {
|
|
6
|
+
"@arethetypeswrong/core": "^0.18.5",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @type {import('#types').Layer}
|
|
11
|
+
*/
|
|
12
|
+
export default {
|
|
13
|
+
label: "Are The Types Wrong?",
|
|
14
|
+
hint: "validate the published declarations on every build",
|
|
15
|
+
|
|
16
|
+
async run(project) {
|
|
17
|
+
// Publish checks: apps aren't published
|
|
18
|
+
if (project.type !== "library") return;
|
|
19
|
+
|
|
20
|
+
// Declarations are the thing being checked
|
|
21
|
+
if (!(await project.hasOrWantsLayer("typescript"))) return;
|
|
22
|
+
|
|
23
|
+
// ESM-only packages don't offer the CJS resolution modes the strict
|
|
24
|
+
// profile requires
|
|
25
|
+
await addTsdownConfigProperty(project, "attw", `attw: { profile: "esm-only" }`);
|
|
26
|
+
await packageJson.addDevDependencies(await getLatest(deps), project.directory);
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @overload
|
|
31
|
+
* @param {import('#utils/project.js').Project} project
|
|
32
|
+
* @param {true} explain
|
|
33
|
+
* @returns {Promise<{ isSetup: boolean; reasons: string[] }>}
|
|
34
|
+
*/
|
|
35
|
+
/**
|
|
36
|
+
* @overload
|
|
37
|
+
* @param {import('#utils/project.js').Project} project
|
|
38
|
+
* @param {boolean | undefined} [explain]
|
|
39
|
+
* @returns {Promise<boolean>}
|
|
40
|
+
*/
|
|
41
|
+
async isSetup(project, explain) {
|
|
42
|
+
/** @type {string[]} */
|
|
43
|
+
const reasons = [];
|
|
44
|
+
|
|
45
|
+
// Nothing to set up outside TypeScript libraries, so nothing can be
|
|
46
|
+
// missing
|
|
47
|
+
if (project.type !== "library" || !(await project.hasOrWantsLayer("typescript"))) {
|
|
48
|
+
return explain ? { isSetup: true, reasons } : true;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!(await hasTsdownConfigProperty(project, "attw"))) {
|
|
52
|
+
if (!explain) return false;
|
|
53
|
+
|
|
54
|
+
reasons.push("tsdown.config.js does not configure attw");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let manifest = await packageJson.read(project.directory);
|
|
58
|
+
|
|
59
|
+
if (!manifest.devDependencies?.["@arethetypeswrong/core"]) {
|
|
60
|
+
if (!explain) return false;
|
|
61
|
+
|
|
62
|
+
reasons.push("package.json is missing @arethetypeswrong/core in devDependencies");
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (explain) {
|
|
66
|
+
return {
|
|
67
|
+
isSetup: reasons.length === 0,
|
|
68
|
+
reasons,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return reasons.length === 0;
|
|
73
|
+
},
|
|
74
|
+
};
|
package/src/layers/git/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { hasGit, initGit, isInGit } from "#utils/git.js";
|
|
2
2
|
import { formatLabel } from "#utils/cli.js";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
import { cp } from "node:fs/promises";
|
|
5
6
|
|
|
@@ -7,10 +8,7 @@ export default {
|
|
|
7
8
|
label: formatLabel("git init"),
|
|
8
9
|
hint: "set up this project as a fresh git repository",
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
* @param {import('#utils/project.js').Project} project
|
|
12
|
-
*/
|
|
13
|
-
async defaultValue(project) {
|
|
11
|
+
async defaultValue() {
|
|
14
12
|
return !isInGit();
|
|
15
13
|
},
|
|
16
14
|
|
|
@@ -27,7 +25,10 @@ export default {
|
|
|
27
25
|
let gitIgnorePath = join(import.meta.dirname, "files/gitignore");
|
|
28
26
|
let targetPath = join(project.directory, ".gitignore");
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
// Don't clobber a .gitignore provided by a base or by the user
|
|
29
|
+
if (!existsSync(targetPath)) {
|
|
30
|
+
await cp(gitIgnorePath, targetPath);
|
|
31
|
+
}
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
return initOk;
|
|
@@ -69,7 +69,7 @@ concurrency:
|
|
|
69
69
|
cancel-in-progress: true`;
|
|
70
70
|
|
|
71
71
|
const lint = {
|
|
72
|
-
npm: `lint:
|
|
72
|
+
npm: ` lint:
|
|
73
73
|
name: "Lints"
|
|
74
74
|
runs-on: ubuntu-latest
|
|
75
75
|
steps:
|
|
@@ -78,18 +78,20 @@ const lint = {
|
|
|
78
78
|
with:
|
|
79
79
|
node-version: 24
|
|
80
80
|
- run: npm install
|
|
81
|
-
- run: npm run lint
|
|
81
|
+
- run: npm run lint
|
|
82
|
+
`,
|
|
82
83
|
pnpm: ` lint:
|
|
83
84
|
name: "Lints"
|
|
84
85
|
runs-on: ubuntu-latest
|
|
85
86
|
steps:
|
|
86
87
|
- uses: actions/checkout@v6
|
|
87
88
|
- uses: wyvox/action-setup-pnpm@v3
|
|
88
|
-
- run: pnpm lint
|
|
89
|
+
- run: pnpm lint
|
|
90
|
+
`,
|
|
89
91
|
};
|
|
90
92
|
|
|
91
93
|
const test = {
|
|
92
|
-
npm: `test:
|
|
94
|
+
npm: ` test:
|
|
93
95
|
name: "Tests"
|
|
94
96
|
runs-on: ubuntu-latest
|
|
95
97
|
steps:
|
|
@@ -98,14 +100,16 @@ const test = {
|
|
|
98
100
|
with:
|
|
99
101
|
node-version: 24
|
|
100
102
|
- run: npm install
|
|
101
|
-
- run: npm test
|
|
103
|
+
- run: npm test
|
|
104
|
+
`,
|
|
102
105
|
pnpm: ` test:
|
|
103
106
|
name: "Tests"
|
|
104
107
|
runs-on: ubuntu-latest
|
|
105
108
|
steps:
|
|
106
109
|
- uses: actions/checkout@v6
|
|
107
110
|
- uses: wyvox/action-setup-pnpm@v3
|
|
108
|
-
- run: pnpm test
|
|
111
|
+
- run: pnpm test
|
|
112
|
+
`,
|
|
109
113
|
};
|
|
110
114
|
|
|
111
115
|
/**
|
|
@@ -175,7 +179,7 @@ async function addOrUpdateLints(project, file) {
|
|
|
175
179
|
file += "\n\njobs:\n";
|
|
176
180
|
}
|
|
177
181
|
|
|
178
|
-
return file +
|
|
182
|
+
return file + lint[project.desires.packageManager];
|
|
179
183
|
}
|
|
180
184
|
|
|
181
185
|
/**
|
|
@@ -196,5 +200,5 @@ async function addOrUpdateTests(project, file) {
|
|
|
196
200
|
file += "\n\njobs:\n";
|
|
197
201
|
}
|
|
198
202
|
|
|
199
|
-
return file +
|
|
203
|
+
return file + test[project.desires.packageManager];
|
|
200
204
|
}
|
package/src/layers/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
/**
|
|
5
5
|
* @type {Set<string>}
|
|
6
6
|
*/
|
|
7
|
-
export const TODO = new Set(["
|
|
7
|
+
export const TODO = new Set(["release-plan", "eslint-ejected"]);
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @returns {Promise<Array<import('#types').DiscoveredLayer>>}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { packageJson } from "ember-apply";
|
|
2
|
+
import { writeFile } from "node:fs/promises";
|
|
3
|
+
import { getLatest } from "#utils/npm.js";
|
|
4
|
+
import {
|
|
5
|
+
hasInspectorSupport,
|
|
6
|
+
INSPECTOR_PACKAGE,
|
|
7
|
+
wireInspectorSupport,
|
|
8
|
+
} from "#utils/inspector-support.js";
|
|
9
|
+
|
|
10
|
+
const deps = {
|
|
11
|
+
[INSPECTOR_PACKAGE]: "^0.1.3",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const APPLICATION_FILES = ["app/app.ts", "app/app.js"];
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {import('#utils/project.js').Project} project
|
|
18
|
+
* @returns {string | undefined}
|
|
19
|
+
*/
|
|
20
|
+
function findApplicationFile(project) {
|
|
21
|
+
return APPLICATION_FILES.find((file) => project.hasFile(file));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @type {import('#types').Layer}
|
|
26
|
+
*/
|
|
27
|
+
export default {
|
|
28
|
+
label: "Inspector Support",
|
|
29
|
+
hint: "Make the app inspectable by the Ember Inspector browser extension",
|
|
30
|
+
|
|
31
|
+
async run(project) {
|
|
32
|
+
let file = findApplicationFile(project);
|
|
33
|
+
|
|
34
|
+
// libraries have no Application to wire
|
|
35
|
+
if (!file) return;
|
|
36
|
+
|
|
37
|
+
let path = project.path(file);
|
|
38
|
+
let code = (await project.read(file)) ?? "";
|
|
39
|
+
let wired = wireInspectorSupport(code, path);
|
|
40
|
+
|
|
41
|
+
if (wired !== code) {
|
|
42
|
+
await writeFile(path, wired);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// the dependency is only useful once the Application actually got
|
|
46
|
+
// wired (the codemod leaves unrecognized Application shapes alone)
|
|
47
|
+
if (!hasInspectorSupport(wired, path)) return;
|
|
48
|
+
|
|
49
|
+
await packageJson.addDevDependencies(await getLatest(deps), project.directory);
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @overload
|
|
54
|
+
* @param {import('#utils/project.js').Project} project
|
|
55
|
+
* @param {true} explain
|
|
56
|
+
* @returns {Promise<{ isSetup: boolean; reasons: string[] }>}
|
|
57
|
+
*/
|
|
58
|
+
/**
|
|
59
|
+
* @overload
|
|
60
|
+
* @param {import('#utils/project.js').Project} project
|
|
61
|
+
* @param {boolean | undefined} [explain]
|
|
62
|
+
* @returns {Promise<boolean>}
|
|
63
|
+
*/
|
|
64
|
+
async isSetup(project, explain) {
|
|
65
|
+
const reasons = [];
|
|
66
|
+
let file = findApplicationFile(project);
|
|
67
|
+
|
|
68
|
+
if (!file) {
|
|
69
|
+
if (!explain) return false;
|
|
70
|
+
|
|
71
|
+
reasons.push("there is no Application to wire (no app/app.ts or app/app.js)");
|
|
72
|
+
} else {
|
|
73
|
+
let code = (await project.read(file)) ?? "";
|
|
74
|
+
|
|
75
|
+
if (!hasInspectorSupport(code, project.path(file))) {
|
|
76
|
+
if (!explain) return false;
|
|
77
|
+
|
|
78
|
+
reasons.push(`${file} does not wire ${INSPECTOR_PACKAGE} into the Application`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let manifest = await packageJson.read(project.directory);
|
|
83
|
+
|
|
84
|
+
for (let dep of Object.keys(deps)) {
|
|
85
|
+
if (!manifest.devDependencies?.[dep]) {
|
|
86
|
+
if (!explain) return false;
|
|
87
|
+
|
|
88
|
+
reasons.push(`package.json is missing ${dep} in devDependencies`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (explain) {
|
|
93
|
+
return {
|
|
94
|
+
isSetup: reasons.length === 0,
|
|
95
|
+
reasons,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return reasons.length === 0;
|
|
100
|
+
},
|
|
101
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { packageJson } from "ember-apply";
|
|
2
|
+
import { getLatest } from "#utils/npm.js";
|
|
3
|
+
import { addTsdownConfigProperty, hasTsdownConfigProperty } from "#utils/tsdown-config.js";
|
|
4
|
+
|
|
5
|
+
const deps = {
|
|
6
|
+
publint: "^0.3.21",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @type {import('#types').Layer}
|
|
11
|
+
*/
|
|
12
|
+
export default {
|
|
13
|
+
label: "publint",
|
|
14
|
+
hint: "validate the publishable package shape on every build",
|
|
15
|
+
|
|
16
|
+
async run(project) {
|
|
17
|
+
// Publish checks: apps aren't published
|
|
18
|
+
if (project.type !== "library") return;
|
|
19
|
+
|
|
20
|
+
await addTsdownConfigProperty(project, "publint", "publint: true");
|
|
21
|
+
await packageJson.addDevDependencies(await getLatest(deps), project.directory);
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @overload
|
|
26
|
+
* @param {import('#utils/project.js').Project} project
|
|
27
|
+
* @param {true} explain
|
|
28
|
+
* @returns {Promise<{ isSetup: boolean; reasons: string[] }>}
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* @overload
|
|
32
|
+
* @param {import('#utils/project.js').Project} project
|
|
33
|
+
* @param {boolean | undefined} [explain]
|
|
34
|
+
* @returns {Promise<boolean>}
|
|
35
|
+
*/
|
|
36
|
+
async isSetup(project, explain) {
|
|
37
|
+
/** @type {string[]} */
|
|
38
|
+
const reasons = [];
|
|
39
|
+
|
|
40
|
+
// Nothing to set up outside libraries, so nothing can be missing
|
|
41
|
+
if (project.type !== "library") {
|
|
42
|
+
return explain ? { isSetup: true, reasons } : true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!(await hasTsdownConfigProperty(project, "publint"))) {
|
|
46
|
+
if (!explain) return false;
|
|
47
|
+
|
|
48
|
+
reasons.push("tsdown.config.js does not configure publint");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let manifest = await packageJson.read(project.directory);
|
|
52
|
+
|
|
53
|
+
if (!manifest.devDependencies?.["publint"]) {
|
|
54
|
+
if (!explain) return false;
|
|
55
|
+
|
|
56
|
+
reasons.push("package.json is missing publint in devDependencies");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (explain) {
|
|
60
|
+
return {
|
|
61
|
+
isSetup: reasons.length === 0,
|
|
62
|
+
reasons,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return reasons.length === 0;
|
|
67
|
+
},
|
|
68
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { assert as emberAssert } from "@ember/debug";
|
|
1
2
|
import { module, test } from "qunit";
|
|
2
3
|
import { isTesting } from "@embroider/macros";
|
|
3
|
-
import { assert as emberAssert } from "@ember/debug";
|
|
4
4
|
|
|
5
5
|
module("@embroider/macros | runtime mode in tests", function () {
|
|
6
6
|
/**
|
|
@@ -11,7 +11,7 @@ module("@embroider/macros | runtime mode in tests", function () {
|
|
|
11
11
|
* `setTesting(true)` in tests/test-helper.ts.
|
|
12
12
|
*/
|
|
13
13
|
test("isTesting() is true", function (assert) {
|
|
14
|
-
assert.
|
|
14
|
+
assert.true(isTesting());
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { applyFolderTo } from "#utils/fs.js";
|
|
2
2
|
import { getLatest } from "#utils/npm.js";
|
|
3
|
-
import {
|
|
3
|
+
import { hasConfiguredPlugin, prependPlugin, removeConfiguredPlugin } from "#utils/babel.js";
|
|
4
|
+
import { packageJson } from "ember-apply";
|
|
4
5
|
import { join } from "node:path";
|
|
5
6
|
|
|
6
7
|
const deps = {
|
|
@@ -16,6 +17,41 @@ const tsDeps = {
|
|
|
16
17
|
"@types/qunit": "^2.19.12",
|
|
17
18
|
};
|
|
18
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Libraries don't have the app base's vite stack, so the test build
|
|
22
|
+
* brings the pieces the generated configs and test-helper import
|
|
23
|
+
* directly. Everything else (babel itself, the embroider machinery)
|
|
24
|
+
* comes with @nullvoxpopuli/ember-vite.
|
|
25
|
+
*/
|
|
26
|
+
const libraryDeps = {
|
|
27
|
+
...deps,
|
|
28
|
+
"@embroider/macros": "^1.20.3",
|
|
29
|
+
"@nullvoxpopuli/ember-vite": "workspace:*",
|
|
30
|
+
"babel-plugin-ember-template-compilation": "^4.0.0",
|
|
31
|
+
"ember-strict-application-resolver": "^0.1.0",
|
|
32
|
+
vite: "^8.0.14",
|
|
33
|
+
};
|
|
34
|
+
const libraryTsDeps = {
|
|
35
|
+
...tsDeps,
|
|
36
|
+
"@babel/plugin-transform-typescript": "^7.28.5",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const TEST_BABEL_CONFIG = "config/test/babel.config.js";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param {import('#utils/project.js').Project} project
|
|
43
|
+
*/
|
|
44
|
+
function depsFor(project) {
|
|
45
|
+
return project.type === "library" ? libraryDeps : deps;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {import('#utils/project.js').Project} project
|
|
50
|
+
*/
|
|
51
|
+
function tsDepsFor(project) {
|
|
52
|
+
return project.type === "library" ? libraryTsDeps : tsDeps;
|
|
53
|
+
}
|
|
54
|
+
|
|
19
55
|
/**
|
|
20
56
|
* @type {import('#types').Layer}
|
|
21
57
|
*/
|
|
@@ -23,14 +59,23 @@ export default {
|
|
|
23
59
|
label: "QUnit",
|
|
24
60
|
|
|
25
61
|
async run(project) {
|
|
26
|
-
let
|
|
27
|
-
await applyFolderTo(filePath, project);
|
|
62
|
+
let isLibrary = project.type === "library";
|
|
28
63
|
let ts = await project.hasOrWantsLayer("typescript");
|
|
29
64
|
|
|
65
|
+
if (isLibrary) {
|
|
66
|
+
await addSourceImports(project);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
await applyFolderTo(join(import.meta.dirname, isLibrary ? "library-files" : "files"), project);
|
|
70
|
+
|
|
71
|
+
if (isLibrary) {
|
|
72
|
+
await syncTestBabelConfig(project, ts);
|
|
73
|
+
}
|
|
74
|
+
|
|
30
75
|
await packageJson.addDevDependencies(
|
|
31
76
|
await getLatest({
|
|
32
|
-
...
|
|
33
|
-
...(ts ?
|
|
77
|
+
...depsFor(project),
|
|
78
|
+
...(ts ? tsDepsFor(project) : {}),
|
|
34
79
|
}),
|
|
35
80
|
project.directory,
|
|
36
81
|
);
|
|
@@ -39,7 +84,9 @@ export default {
|
|
|
39
84
|
await packageJson.addScripts(
|
|
40
85
|
{
|
|
41
86
|
"build:test": "EMBER_ENV=test NODE_ENV=development vite build --mode development",
|
|
42
|
-
"test:ci":
|
|
87
|
+
"test:ci": isLibrary
|
|
88
|
+
? "testem ci --file config/test/testem.cjs --port 0"
|
|
89
|
+
: "testem ci --port 0",
|
|
43
90
|
test: "pnpm build:test && pnpm test:ci",
|
|
44
91
|
},
|
|
45
92
|
project.directory,
|
|
@@ -59,7 +106,10 @@ export default {
|
|
|
59
106
|
*/
|
|
60
107
|
async isSetup(project, explain) {
|
|
61
108
|
const reasons = [];
|
|
62
|
-
let depsToCheck = [
|
|
109
|
+
let depsToCheck = [
|
|
110
|
+
Object.keys(depsFor(project)),
|
|
111
|
+
project.wantsTypeScript ? Object.keys(tsDepsFor(project)) : "",
|
|
112
|
+
]
|
|
63
113
|
.flat()
|
|
64
114
|
.filter(Boolean);
|
|
65
115
|
|
|
@@ -100,3 +150,53 @@ export default {
|
|
|
100
150
|
return reasons.length === 0;
|
|
101
151
|
},
|
|
102
152
|
};
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Tests import the library's source (not its published dist), so the
|
|
156
|
+
* `#src/*` subpath must exist before the test files are applied --
|
|
157
|
+
* applying rewrites their import extensions by resolving each specifier
|
|
158
|
+
* through this mapping.
|
|
159
|
+
*
|
|
160
|
+
* @param {import('#utils/project.js').Project} project
|
|
161
|
+
*/
|
|
162
|
+
async function addSourceImports(project) {
|
|
163
|
+
await packageJson.modify((json) => {
|
|
164
|
+
json.imports ||= {};
|
|
165
|
+
json.imports["#src/*"] ||= "./src/*";
|
|
166
|
+
}, project.directory);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The test babel config is the qunit layer's own (a root babel config,
|
|
171
|
+
* when a library has one, belongs to the publish build), so this layer
|
|
172
|
+
* also keeps the config's TypeScript handling in step with the project:
|
|
173
|
+
* the shipped config carries the TS plugin, which must not survive in a
|
|
174
|
+
* JavaScript project, and must come back when a project adopts the
|
|
175
|
+
* typescript layer later.
|
|
176
|
+
*
|
|
177
|
+
* @param {import('#utils/project.js').Project} project
|
|
178
|
+
* @param {boolean} ts
|
|
179
|
+
*/
|
|
180
|
+
async function syncTestBabelConfig(project, ts) {
|
|
181
|
+
if (!ts) {
|
|
182
|
+
await removeConfiguredPlugin(project, "@babel/plugin-transform-typescript", TEST_BABEL_CONFIG);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (await hasConfiguredPlugin(project, "@babel/plugin-transform-typescript", TEST_BABEL_CONFIG)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
await prependPlugin(
|
|
191
|
+
project,
|
|
192
|
+
`[
|
|
193
|
+
"@babel/plugin-transform-typescript",
|
|
194
|
+
{
|
|
195
|
+
allExtensions: true,
|
|
196
|
+
onlyRemoveTypeImports: true,
|
|
197
|
+
allowDeclareFields: true,
|
|
198
|
+
},
|
|
199
|
+
]`,
|
|
200
|
+
TEST_BABEL_CONFIG,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { buildMacros } from "@embroider/macros/babel";
|
|
2
|
+
|
|
3
|
+
const macros = buildMacros({
|
|
4
|
+
configure(config) {
|
|
5
|
+
if (process.env.EMBER_ENV === "test") {
|
|
6
|
+
config.enableRuntimeMode();
|
|
7
|
+
}
|
|
8
|
+
},
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export default {
|
|
12
|
+
plugins: [
|
|
13
|
+
[
|
|
14
|
+
"@babel/plugin-transform-typescript",
|
|
15
|
+
{
|
|
16
|
+
allExtensions: true,
|
|
17
|
+
onlyRemoveTypeImports: true,
|
|
18
|
+
allowDeclareFields: true,
|
|
19
|
+
},
|
|
20
|
+
],
|
|
21
|
+
[
|
|
22
|
+
"babel-plugin-ember-template-compilation",
|
|
23
|
+
{
|
|
24
|
+
transforms: [...macros.templateMacros],
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
"module:decorator-transforms",
|
|
29
|
+
{
|
|
30
|
+
runtime: {
|
|
31
|
+
import: import.meta.resolve("decorator-transforms/runtime-esm"),
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
...macros.babelMacros,
|
|
36
|
+
],
|
|
37
|
+
|
|
38
|
+
generatorOpts: {
|
|
39
|
+
compact: false,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
if (typeof module !== "undefined") {
|
|
4
|
+
module.exports = {
|
|
5
|
+
test_page: "tests/index.html?hidepassed",
|
|
6
|
+
cwd: "dist",
|
|
7
|
+
disable_watching: true,
|
|
8
|
+
launch_in_ci: ["Chrome"],
|
|
9
|
+
launch_in_dev: ["Chrome"],
|
|
10
|
+
browser_start_timeout: 120,
|
|
11
|
+
browser_args: {
|
|
12
|
+
Chrome: {
|
|
13
|
+
ci: [
|
|
14
|
+
// --no-sandbox is needed when running Chrome inside a container
|
|
15
|
+
process.env.CI ? "--no-sandbox" : null,
|
|
16
|
+
"--headless",
|
|
17
|
+
"--disable-dev-shm-usage",
|
|
18
|
+
"--disable-software-rasterizer",
|
|
19
|
+
"--mute-audio",
|
|
20
|
+
"--remote-debugging-port=0",
|
|
21
|
+
"--window-size=1440,900",
|
|
22
|
+
].filter(Boolean),
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import EmberRouter from "@ember/routing/router";
|
|
2
|
+
import { resetOnerror, setApplication } from "@ember/test-helpers";
|
|
3
|
+
import * as QUnit from "qunit";
|
|
4
|
+
import { setup } from "qunit-dom";
|
|
5
|
+
import { setupEmberOnerrorValidation, start as qunitStart } from "ember-qunit";
|
|
6
|
+
import { setTesting } from "@embroider/macros";
|
|
7
|
+
import EmberApp from "ember-strict-application-resolver";
|
|
8
|
+
|
|
9
|
+
class Router extends EmberRouter {
|
|
10
|
+
location = "none";
|
|
11
|
+
rootURL = "/";
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
Router.map(function () {});
|
|
15
|
+
|
|
16
|
+
class TestApp extends EmberApp {
|
|
17
|
+
modules = {
|
|
18
|
+
"./router": Router,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function start() {
|
|
23
|
+
setTesting(true);
|
|
24
|
+
setApplication(
|
|
25
|
+
TestApp.create({
|
|
26
|
+
autoboot: false,
|
|
27
|
+
rootElement: "#ember-testing",
|
|
28
|
+
}),
|
|
29
|
+
);
|
|
30
|
+
setup(QUnit.assert);
|
|
31
|
+
setupEmberOnerrorValidation();
|
|
32
|
+
|
|
33
|
+
QUnit.testDone(resetOnerror);
|
|
34
|
+
|
|
35
|
+
qunitStart();
|
|
36
|
+
}
|
|
File without changes
|