ember.nvp 1.0.0 → 1.2.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 +6 -0
- package/.release-plan.json +11 -15
- package/CHANGELOG.md +47 -0
- package/package.json +1 -1
- package/packages/rolldown/README.md +101 -1
- package/packages/rolldown/package.json +13 -4
- package/packages/rolldown/rolldown.config.js +5 -3
- package/packages/rolldown/src/app-reexports.test.ts +220 -0
- package/packages/rolldown/src/app-reexports.ts +152 -0
- package/packages/rolldown/src/babel.test.ts +17 -0
- package/packages/rolldown/src/babel.ts +21 -3
- package/packages/rolldown/src/config.ts +1 -1
- package/packages/rolldown/src/dts.test.ts +366 -0
- package/packages/rolldown/src/transform.test.ts +208 -0
- package/packages/rolldown/src/transform.ts +64 -31
- package/src/bases/minimal-extension/extension.test.ts +251 -0
- package/src/bases/minimal-extension/files/.env.development +8 -0
- package/src/bases/minimal-extension/files/README.md +36 -0
- package/src/bases/minimal-extension/files/app/app.ts +21 -0
- package/src/bases/minimal-extension/files/app/boot.ts +8 -0
- package/src/bases/minimal-extension/files/app/config.ts +22 -0
- package/src/bases/minimal-extension/files/app/router.ts +10 -0
- package/src/bases/minimal-extension/files/app/templates/application.gts +5 -0
- package/src/bases/minimal-extension/files/babel.config.js +49 -0
- package/src/bases/minimal-extension/files/index.html +17 -0
- package/src/bases/minimal-extension/files/package.json +58 -0
- package/src/bases/minimal-extension/files/public/manifest.json +9 -0
- package/src/bases/minimal-extension/files/tsconfig.json +13 -0
- package/src/bases/minimal-extension/files/vite.config.mjs +6 -0
- package/src/bases/minimal-extension/index.js +142 -0
- package/src/cli/generator.js +4 -0
- package/src/cli/questions/project-type.js +9 -2
- package/src/layers/renovate/index.js +4 -3
- package/src/layers/typescript/index.js +6 -0
- package/src/utils/types.ts +1 -1
- package/test/ember.nvp/base-library.test.ts +39 -32
- package/test/ember.nvp/layer-publish-checks.test.ts +4 -13
- package/test/ember.nvp/layer-qunit.test.ts +13 -30
- package/test/ember.nvp/layer-vitest.test.ts +4 -18
- package/test/ember.nvp/permutations/app-checks.test.ts +3 -0
- package/test/ember.nvp/permutations/extension-1.test.ts +3 -0
- package/test/ember.nvp/permutations/extension-2.test.ts +3 -0
- package/test/ember.nvp/permutations/extension-checks.test.ts +3 -0
- package/test/ember.nvp/permutations/library-checks.test.ts +3 -0
- package/test/ember.nvp/permutations/shared.ts +68 -19
- package/test/ember.nvp/scoped-css-library.test.ts +211 -0
- package/test/test-helpers.ts +25 -30
- package/vitest.config.js +1 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { packageJson } from "ember-apply";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { getLatest } from "#utils/npm.js";
|
|
5
|
+
import { applyFolderTo } from "#utils/fs.js";
|
|
6
|
+
import { removeConfiguredPlugin } from "#utils/babel.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Minimal Extension Base
|
|
10
|
+
*
|
|
11
|
+
* Provides the absolute bare minimum for a browser extension (Manifest V3)
|
|
12
|
+
* whose popup is an Ember app:
|
|
13
|
+
* - type: "module" in package.json
|
|
14
|
+
* - public/manifest.json copied verbatim into dist
|
|
15
|
+
* - index.html popup booting from an external script (extension pages
|
|
16
|
+
* forbid inline scripts via CSP)
|
|
17
|
+
* - No testing framework
|
|
18
|
+
* - No linting or formatting
|
|
19
|
+
* - No ember-welcome-page
|
|
20
|
+
*/
|
|
21
|
+
export default {
|
|
22
|
+
label: "Minimal Extension Base",
|
|
23
|
+
description: "Bare minimum browser extension structure with an Ember popup",
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 1. Apply files
|
|
27
|
+
* a. Remove TS if needed
|
|
28
|
+
* 2. Remove TS deps/files if needed
|
|
29
|
+
* 3. Update name(s)
|
|
30
|
+
* 4. Upgrade in-range dependencies
|
|
31
|
+
* 5. Update an existing babel config, if any
|
|
32
|
+
*
|
|
33
|
+
* @param {import('#utils/project.js').Project} project
|
|
34
|
+
*/
|
|
35
|
+
async run(project) {
|
|
36
|
+
await applyFiles(project);
|
|
37
|
+
await updateName(project);
|
|
38
|
+
await makeJavaScript(project);
|
|
39
|
+
await upgradeDependencies(project);
|
|
40
|
+
await updateBabelConfig(project);
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {import('#utils/project.js').Project} project
|
|
46
|
+
*/
|
|
47
|
+
async function updateBabelConfig(project) {
|
|
48
|
+
if (await project.hasOrWantsLayer("typescript")) return;
|
|
49
|
+
|
|
50
|
+
await removeConfiguredPlugin(project, "@babel/plugin-transform-typescript");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {import('#utils/project.js').Project} project
|
|
55
|
+
*/
|
|
56
|
+
async function applyFiles(project) {
|
|
57
|
+
let filePath = join(import.meta.dirname, "files");
|
|
58
|
+
|
|
59
|
+
await applyFolderTo(filePath, project);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Operates on known files where the name matters
|
|
64
|
+
*
|
|
65
|
+
* @param {import('#utils/project.js').Project} project
|
|
66
|
+
*/
|
|
67
|
+
async function updateName(project) {
|
|
68
|
+
await packageJson.modify((json) => {
|
|
69
|
+
json.name = project.desires.name;
|
|
70
|
+
}, project.directory);
|
|
71
|
+
|
|
72
|
+
if (!project.hasFile("public/manifest.json")) return;
|
|
73
|
+
|
|
74
|
+
let manifestPath = project.path("public/manifest.json");
|
|
75
|
+
let manifest = JSON.parse(await readFile(manifestPath, "utf-8"));
|
|
76
|
+
|
|
77
|
+
manifest.name = project.desires.name;
|
|
78
|
+
|
|
79
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* @param {import('#utils/project.js').Project} project
|
|
84
|
+
*/
|
|
85
|
+
async function makeJavaScript(project) {
|
|
86
|
+
if (await project.hasOrWantsLayer("typescript")) return;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* We don't actually remove anything, because we want intellisense for JS
|
|
90
|
+
*
|
|
91
|
+
* But perhaps for the sake of minimal, we do remoev it.
|
|
92
|
+
* And we add a layer later for JSDoc ased TS or something
|
|
93
|
+
*/
|
|
94
|
+
await packageJson.removeDevDependencies(
|
|
95
|
+
[
|
|
96
|
+
"@ember/app-tsconfig",
|
|
97
|
+
"@glint/ember-tsc",
|
|
98
|
+
"@glint/template",
|
|
99
|
+
"@glint/tsserver-plugin",
|
|
100
|
+
"typescript",
|
|
101
|
+
],
|
|
102
|
+
project.directory,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
await project.removeFile("tsconfig.json");
|
|
106
|
+
|
|
107
|
+
await packageJson.modify((json) => {
|
|
108
|
+
json.imports ||= {};
|
|
109
|
+
json.imports["#config"] = "./app/config.js";
|
|
110
|
+
}, project.directory);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* The import-rewriting pass only handles module files; the popup's
|
|
114
|
+
* <script src> points at the boot module by path, so it has to follow
|
|
115
|
+
* the .ts -> .js rename by hand.
|
|
116
|
+
*/
|
|
117
|
+
if (!project.hasFile("index.html")) return;
|
|
118
|
+
|
|
119
|
+
let indexPath = project.path("index.html");
|
|
120
|
+
let indexHtml = await readFile(indexPath, "utf-8");
|
|
121
|
+
|
|
122
|
+
await writeFile(indexPath, indexHtml.replace("./app/boot.ts", "./app/boot.js"));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Bumps in-range only.
|
|
127
|
+
* Majors will need to go through PR to this repo.
|
|
128
|
+
*
|
|
129
|
+
* @param {import('#utils/project.js').Project} project
|
|
130
|
+
*/
|
|
131
|
+
async function upgradeDependencies(project) {
|
|
132
|
+
let existing = await packageJson.read(project.directory);
|
|
133
|
+
|
|
134
|
+
await packageJson.modify(async (json) => {
|
|
135
|
+
if (json.dependencies) {
|
|
136
|
+
Object.assign(json.dependencies, await getLatest(existing.dependencies));
|
|
137
|
+
}
|
|
138
|
+
if (json.devDependencies) {
|
|
139
|
+
Object.assign(json.devDependencies, await getLatest(existing.devDependencies));
|
|
140
|
+
}
|
|
141
|
+
}, project.directory);
|
|
142
|
+
}
|
package/src/cli/generator.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mkdir, rm } from "node:fs/promises";
|
|
2
2
|
|
|
3
3
|
import baseApp from "#bases/minimal-app";
|
|
4
|
+
import baseExtension from "#bases/minimal-extension";
|
|
4
5
|
import baseLibrary from "#bases/minimal-library";
|
|
5
6
|
import { consolidateLintingScripts } from "../consolidators/linting.js";
|
|
6
7
|
import { hasGit } from "#utils/git.js";
|
|
@@ -24,6 +25,9 @@ export async function generateProject(project, replaceOrUpdate) {
|
|
|
24
25
|
case "library":
|
|
25
26
|
await baseLibrary.run(project);
|
|
26
27
|
break;
|
|
28
|
+
case "extension":
|
|
29
|
+
await baseExtension.run(project);
|
|
30
|
+
break;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
/**
|
|
@@ -3,12 +3,13 @@ import { answers, printArgInUse } from "#args";
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* addon is alias for library
|
|
6
|
+
* web-extension is alias for extension
|
|
6
7
|
*/
|
|
7
|
-
const SUPPORTED = new Set(["app", "library", "addon"]);
|
|
8
|
+
const SUPPORTED = new Set(["app", "library", "addon", "extension", "web-extension"]);
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* @param {string | undefined} selected
|
|
11
|
-
* @returns {selected is "app" | "library" | "addon"}
|
|
12
|
+
* @returns {selected is "app" | "library" | "addon" | "extension" | "web-extension"}
|
|
12
13
|
*/
|
|
13
14
|
function isValid(selected) {
|
|
14
15
|
if (!selected) return false;
|
|
@@ -25,6 +26,7 @@ export async function askProjectType() {
|
|
|
25
26
|
printArgInUse("type", answers.type);
|
|
26
27
|
|
|
27
28
|
if (answers.type === "addon") return "library";
|
|
29
|
+
if (answers.type === "web-extension") return "extension";
|
|
28
30
|
|
|
29
31
|
return answers.type;
|
|
30
32
|
}
|
|
@@ -38,6 +40,11 @@ export async function askProjectType() {
|
|
|
38
40
|
label: "library",
|
|
39
41
|
hint: "sharable code, usable by other libraries and apps alike",
|
|
40
42
|
},
|
|
43
|
+
{
|
|
44
|
+
value: "extension",
|
|
45
|
+
label: "browser extension",
|
|
46
|
+
hint: "Manifest V3 extension whose popup is an Ember app",
|
|
47
|
+
},
|
|
41
48
|
],
|
|
42
49
|
});
|
|
43
50
|
|
|
@@ -14,10 +14,11 @@ export default {
|
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// extensions aren't published to npm either, so they follow the app config
|
|
17
18
|
let source =
|
|
18
|
-
project.type === "
|
|
19
|
-
? join(import.meta.dirname, "files/
|
|
20
|
-
: join(import.meta.dirname, "files/
|
|
19
|
+
project.type === "library"
|
|
20
|
+
? join(import.meta.dirname, "files/library.json5")
|
|
21
|
+
: join(import.meta.dirname, "files/app.json5");
|
|
21
22
|
|
|
22
23
|
let destination = join(project.directory, ".github/renovate.json5");
|
|
23
24
|
|
|
@@ -7,6 +7,7 @@ import { getLatest } from "#utils/npm.js";
|
|
|
7
7
|
|
|
8
8
|
const bases = join(import.meta.dirname, "../../bases");
|
|
9
9
|
const appBase = join(bases, "minimal-app/files");
|
|
10
|
+
const extensionBase = join(bases, "minimal-extension/files");
|
|
10
11
|
const libraryBase = join(bases, "minimal-library/files");
|
|
11
12
|
|
|
12
13
|
const sharedDeps = {
|
|
@@ -149,6 +150,11 @@ async function addTSConfig(project) {
|
|
|
149
150
|
return;
|
|
150
151
|
}
|
|
151
152
|
|
|
153
|
+
if (project.type === "extension") {
|
|
154
|
+
await cp(join(extensionBase, "tsconfig.json"), project.path("tsconfig.json"));
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
152
158
|
if (project.type === "library") {
|
|
153
159
|
await cp(join(libraryBase, "tsconfig.json"), project.path("tsconfig.json"));
|
|
154
160
|
}
|
package/src/utils/types.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { describe, it, beforeAll, afterAll, expect } from "vitest";
|
|
2
|
-
import { generate,
|
|
2
|
+
import { generate, listFiles, mktemp } from "#test-helpers";
|
|
3
3
|
import { writeLibrarySource } from "./library-src-fixtures.ts";
|
|
4
4
|
import { execa } from "execa";
|
|
5
|
-
import { mkdir,
|
|
6
|
-
import { join
|
|
5
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { join } from "node:path";
|
|
7
7
|
|
|
8
8
|
import type { Project } from "ember.nvp";
|
|
9
9
|
|
|
@@ -14,23 +14,7 @@ import type { Project } from "ember.nvp";
|
|
|
14
14
|
* them.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
async function listFiles(directory: string): Promise<string[]> {
|
|
18
|
-
const entries = await readdir(directory, { withFileTypes: true, recursive: true });
|
|
19
|
-
|
|
20
|
-
return entries
|
|
21
|
-
.filter((entry) => entry.isFile())
|
|
22
|
-
.map((entry) => relative(directory, join(entry.parentPath, entry.name)))
|
|
23
|
-
.filter((path) => !path.split(sep).includes("node_modules"))
|
|
24
|
-
.sort();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function read(project: Project, filePath: string): Promise<string> {
|
|
28
|
-
return readFile(join(project.directory, filePath), "utf-8");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
17
|
async function installAndBuild(project: Project) {
|
|
32
|
-
await pinYukuParser(project);
|
|
33
|
-
|
|
34
18
|
let install = await execa("pnpm install", { cwd: project.directory, shell: true });
|
|
35
19
|
expect(install.exitCode).toBe(0);
|
|
36
20
|
|
|
@@ -68,11 +52,11 @@ describe("base: minimal-library", () => {
|
|
|
68
52
|
]
|
|
69
53
|
`);
|
|
70
54
|
|
|
71
|
-
expect(await read(
|
|
55
|
+
expect(await project.read("src/index.js")).toBe("");
|
|
72
56
|
});
|
|
73
57
|
|
|
74
58
|
it("is publishable", async () => {
|
|
75
|
-
let manifest = JSON.parse(await read(
|
|
59
|
+
let manifest = JSON.parse((await project.read("package.json"))!);
|
|
76
60
|
|
|
77
61
|
// The template is private (it lives in a workspace); the generated
|
|
78
62
|
// library must not be
|
|
@@ -80,14 +64,14 @@ describe("base: minimal-library", () => {
|
|
|
80
64
|
});
|
|
81
65
|
|
|
82
66
|
it("has no TypeScript leftovers", async () => {
|
|
83
|
-
let manifest = JSON.parse(await read(
|
|
67
|
+
let manifest = JSON.parse((await project.read("package.json"))!);
|
|
84
68
|
|
|
85
69
|
expect(manifest.devDependencies).not.toHaveProperty("typescript");
|
|
86
70
|
expect(manifest.devDependencies).not.toHaveProperty("@ember/library-tsconfig");
|
|
87
71
|
// No declarations are emitted without types
|
|
88
72
|
expect(JSON.stringify(manifest.exports)).not.toContain("types");
|
|
89
73
|
|
|
90
|
-
expect(await read(
|
|
74
|
+
expect(await project.read("tsdown.config.js")).toMatchInlineSnapshot(`
|
|
91
75
|
"import { defineConfig } from "tsdown";
|
|
92
76
|
import { ember } from "@nullvoxpopuli/ember-rolldown";
|
|
93
77
|
|
|
@@ -131,7 +115,7 @@ describe("base: minimal-library", () => {
|
|
|
131
115
|
await writeLibrarySource(project, "javascript");
|
|
132
116
|
await installAndBuild(project);
|
|
133
117
|
|
|
134
|
-
let output = await read(
|
|
118
|
+
let output = await project.read("dist/index.js");
|
|
135
119
|
|
|
136
120
|
// Published libraries ship precompileTemplate (the consuming app does
|
|
137
121
|
// the final compile), never wire format
|
|
@@ -167,12 +151,11 @@ describe("base: minimal-library", () => {
|
|
|
167
151
|
]
|
|
168
152
|
`);
|
|
169
153
|
|
|
170
|
-
expect(await read(
|
|
154
|
+
expect(await project.read("src/index.ts")).toBe("");
|
|
171
155
|
});
|
|
172
156
|
|
|
173
157
|
it("type checks", async () => {
|
|
174
158
|
await writeLibrarySource(project, "typescript");
|
|
175
|
-
await pinYukuParser(project);
|
|
176
159
|
|
|
177
160
|
let install = await execa("pnpm install", { cwd: project.directory, shell: true });
|
|
178
161
|
expect(install.exitCode).toBe(0);
|
|
@@ -184,16 +167,40 @@ describe("base: minimal-library", () => {
|
|
|
184
167
|
it("builds, including declarations", async () => {
|
|
185
168
|
await installAndBuild(project);
|
|
186
169
|
|
|
187
|
-
let output = await read(
|
|
170
|
+
let output = await project.read("dist/index.js");
|
|
188
171
|
|
|
189
172
|
expect(output).toContain("precompileTemplate");
|
|
190
173
|
expect(output).not.toContain("createTemplateFactory");
|
|
191
174
|
|
|
192
|
-
let declarations = await read(
|
|
193
|
-
|
|
194
|
-
expect(declarations).
|
|
195
|
-
|
|
196
|
-
|
|
175
|
+
let declarations = await project.read("dist/index.d.ts");
|
|
176
|
+
|
|
177
|
+
expect(declarations).toMatchInlineSnapshot(`
|
|
178
|
+
"import { TOC } from "@ember/component/template-only";
|
|
179
|
+
import Component from "@glimmer/component";
|
|
180
|
+
//#region src/components/badge.d.ts
|
|
181
|
+
interface BadgeSignature {
|
|
182
|
+
Element: HTMLSpanElement;
|
|
183
|
+
Blocks: {
|
|
184
|
+
default: [];
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
declare const Badge: TOC<BadgeSignature>;
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/components/greeting.d.ts
|
|
190
|
+
interface GreetingSignature {
|
|
191
|
+
Element: HTMLParagraphElement;
|
|
192
|
+
Args: {
|
|
193
|
+
name: string;
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
declare class Greeting extends Component<GreetingSignature> {}
|
|
197
|
+
//#endregion
|
|
198
|
+
//#region src/utils/math.d.ts
|
|
199
|
+
declare function add(a: number, b: number): number;
|
|
200
|
+
//#endregion
|
|
201
|
+
export { Badge, type BadgeSignature, Greeting, type GreetingSignature, add };
|
|
202
|
+
//# sourceMappingURL=index.d.ts.map"
|
|
203
|
+
`);
|
|
197
204
|
|
|
198
205
|
expect(await listFiles(join(project.directory, "dist"))).toMatchInlineSnapshot(`
|
|
199
206
|
[
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { describe, it, beforeAll, afterAll, expect } from "vitest";
|
|
2
|
-
import { generate
|
|
2
|
+
import { generate } from "#test-helpers";
|
|
3
3
|
import { execa } from "execa";
|
|
4
|
-
import {
|
|
5
|
-
import { join } from "node:path";
|
|
4
|
+
import { rm } from "node:fs/promises";
|
|
6
5
|
|
|
7
6
|
import type { Project } from "ember.nvp";
|
|
8
7
|
|
|
@@ -12,10 +11,6 @@ import type { Project } from "ember.nvp";
|
|
|
12
11
|
* output.
|
|
13
12
|
*/
|
|
14
13
|
|
|
15
|
-
async function read(project: Project, filePath: string): Promise<string> {
|
|
16
|
-
return readFile(join(project.directory, filePath), "utf-8");
|
|
17
|
-
}
|
|
18
|
-
|
|
19
14
|
describe("layers: publint + are-the-types-wrong", () => {
|
|
20
15
|
const dirs: string[] = [];
|
|
21
16
|
|
|
@@ -40,7 +35,7 @@ describe("layers: publint + are-the-types-wrong", () => {
|
|
|
40
35
|
});
|
|
41
36
|
|
|
42
37
|
it("wires both checks into the build", async () => {
|
|
43
|
-
expect(await read(
|
|
38
|
+
expect(await project.read("tsdown.config.js")).toMatchInlineSnapshot(`
|
|
44
39
|
"import { defineConfig } from "tsdown";
|
|
45
40
|
import { ember } from "@nullvoxpopuli/ember-rolldown";
|
|
46
41
|
export default defineConfig({ entry: ["./src/index.ts"], plugins: [ember()], attw: { profile: "esm-only" }, publint: true })"
|
|
@@ -48,8 +43,6 @@ describe("layers: publint + are-the-types-wrong", () => {
|
|
|
48
43
|
});
|
|
49
44
|
|
|
50
45
|
it("both checks run and pass in the build", async () => {
|
|
51
|
-
await pinYukuParser(project);
|
|
52
|
-
|
|
53
46
|
let install = await execa("pnpm install", { cwd: project.directory, shell: true });
|
|
54
47
|
expect(install.exitCode).toBe(0);
|
|
55
48
|
|
|
@@ -80,7 +73,7 @@ describe("layers: publint + are-the-types-wrong", () => {
|
|
|
80
73
|
});
|
|
81
74
|
|
|
82
75
|
it("wires publint only (no declarations to check)", async () => {
|
|
83
|
-
expect(await read(
|
|
76
|
+
expect(await project.read("tsdown.config.js")).toMatchInlineSnapshot(`
|
|
84
77
|
"import { defineConfig } from "tsdown";
|
|
85
78
|
import { ember } from "@nullvoxpopuli/ember-rolldown";
|
|
86
79
|
export default defineConfig({ entry: ["./src/index.js"], dts: false, plugins: [ember()], publint: true })"
|
|
@@ -88,8 +81,6 @@ describe("layers: publint + are-the-types-wrong", () => {
|
|
|
88
81
|
});
|
|
89
82
|
|
|
90
83
|
it("publint runs and passes in the build", async () => {
|
|
91
|
-
await pinYukuParser(project);
|
|
92
|
-
|
|
93
84
|
let install = await execa("pnpm install", { cwd: project.directory, shell: true });
|
|
94
85
|
expect(install.exitCode).toBe(0);
|
|
95
86
|
|
|
@@ -1,30 +1,13 @@
|
|
|
1
1
|
import { describe, it, beforeAll, afterAll, expect } from "vitest";
|
|
2
|
-
import { generate,
|
|
2
|
+
import { generate, listFiles } from "#test-helpers";
|
|
3
3
|
import { writeLibrarySource } from "./library-src-fixtures.ts";
|
|
4
4
|
import { execa } from "execa";
|
|
5
|
-
import { mkdir,
|
|
6
|
-
import { dirname, join
|
|
5
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
7
|
|
|
8
8
|
import type { Project } from "ember.nvp";
|
|
9
9
|
|
|
10
|
-
async function listFiles(directory: string): Promise<string[]> {
|
|
11
|
-
const entries = await readdir(directory, { withFileTypes: true, recursive: true });
|
|
12
|
-
|
|
13
|
-
return entries
|
|
14
|
-
.filter((entry) => entry.isFile())
|
|
15
|
-
.map((entry) => relative(directory, join(entry.parentPath, entry.name)))
|
|
16
|
-
.filter((path) => !path.split(sep).includes("node_modules"))
|
|
17
|
-
.filter((path) => !path.startsWith(`dist${sep}`))
|
|
18
|
-
.sort();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
async function read(project: Project, filePath: string): Promise<string> {
|
|
22
|
-
return readFile(join(project.directory, filePath), "utf-8");
|
|
23
|
-
}
|
|
24
|
-
|
|
25
10
|
async function installAndTest(project: Project) {
|
|
26
|
-
await pinYukuParser(project);
|
|
27
|
-
|
|
28
11
|
let install = await execa("pnpm install", { cwd: project.directory, shell: true });
|
|
29
12
|
expect(install.exitCode).toBe(0);
|
|
30
13
|
|
|
@@ -169,7 +152,7 @@ describe("layer: qunit", () => {
|
|
|
169
152
|
});
|
|
170
153
|
|
|
171
154
|
it("generates the expected files, without any index.html", async () => {
|
|
172
|
-
let files = await listFiles(project.directory);
|
|
155
|
+
let files = await listFiles(project.directory, { includeDist: false });
|
|
173
156
|
|
|
174
157
|
expect(files.some((file) => file.endsWith("index.html"))).toBe(false);
|
|
175
158
|
expect(files).toMatchInlineSnapshot(`
|
|
@@ -191,7 +174,7 @@ describe("layer: qunit", () => {
|
|
|
191
174
|
});
|
|
192
175
|
|
|
193
176
|
it("generates the test build config", async () => {
|
|
194
|
-
expect(await read(
|
|
177
|
+
expect(await project.read("vite.config.mjs")).toMatchInlineSnapshot(`
|
|
195
178
|
"import { defineConfig } from "vite";
|
|
196
179
|
import { ember } from "@nullvoxpopuli/ember-vite";
|
|
197
180
|
|
|
@@ -200,7 +183,7 @@ describe("layer: qunit", () => {
|
|
|
200
183
|
});
|
|
201
184
|
"
|
|
202
185
|
`);
|
|
203
|
-
expect(await read(
|
|
186
|
+
expect(await project.read("config/test/babel.config.js")).toMatchInlineSnapshot(`
|
|
204
187
|
"import { buildMacros } from "@embroider/macros/babel";
|
|
205
188
|
|
|
206
189
|
const macros = buildMacros({
|
|
@@ -244,7 +227,7 @@ describe("layer: qunit", () => {
|
|
|
244
227
|
};
|
|
245
228
|
"
|
|
246
229
|
`);
|
|
247
|
-
expect(await read(
|
|
230
|
+
expect(await project.read("config/test/testem.cjs")).toMatchInlineSnapshot(`
|
|
248
231
|
""use strict";
|
|
249
232
|
|
|
250
233
|
if (typeof module !== "undefined") {
|
|
@@ -276,7 +259,7 @@ describe("layer: qunit", () => {
|
|
|
276
259
|
});
|
|
277
260
|
|
|
278
261
|
it("leaves the publish build config alone", async () => {
|
|
279
|
-
expect(await read(
|
|
262
|
+
expect(await project.read("tsdown.config.js")).toMatchInlineSnapshot(`
|
|
280
263
|
"import { defineConfig } from "tsdown";
|
|
281
264
|
import { ember } from "@nullvoxpopuli/ember-rolldown";
|
|
282
265
|
|
|
@@ -298,7 +281,7 @@ describe("layer: qunit", () => {
|
|
|
298
281
|
let build = await execa("pnpm build", { cwd: project.directory, shell: true });
|
|
299
282
|
expect(build.exitCode).toBe(0);
|
|
300
283
|
|
|
301
|
-
let output = await read(
|
|
284
|
+
let output = await project.read("dist/index.js");
|
|
302
285
|
|
|
303
286
|
expect(output).toContain("precompileTemplate");
|
|
304
287
|
expect(output).not.toContain("createTemplateFactory");
|
|
@@ -314,7 +297,7 @@ describe("layer: qunit", () => {
|
|
|
314
297
|
});
|
|
315
298
|
|
|
316
299
|
it("generates the expected files, without any index.html", async () => {
|
|
317
|
-
let files = await listFiles(project.directory);
|
|
300
|
+
let files = await listFiles(project.directory, { includeDist: false });
|
|
318
301
|
|
|
319
302
|
expect(files.some((file) => file.endsWith("index.html"))).toBe(false);
|
|
320
303
|
expect(files).toMatchInlineSnapshot(`
|
|
@@ -335,7 +318,7 @@ describe("layer: qunit", () => {
|
|
|
335
318
|
});
|
|
336
319
|
|
|
337
320
|
it("has no TypeScript leftovers in the test build config", async () => {
|
|
338
|
-
expect(await read(
|
|
321
|
+
expect(await project.read("config/test/babel.config.js")).toMatchInlineSnapshot(`
|
|
339
322
|
"import { buildMacros } from "@embroider/macros/babel";
|
|
340
323
|
|
|
341
324
|
const macros = buildMacros({
|
|
@@ -367,7 +350,7 @@ describe("layer: qunit", () => {
|
|
|
367
350
|
};
|
|
368
351
|
"
|
|
369
352
|
`);
|
|
370
|
-
expect(await read(
|
|
353
|
+
expect(await project.read("tsdown.config.js")).toMatchInlineSnapshot(`
|
|
371
354
|
"import { defineConfig } from "tsdown";
|
|
372
355
|
import { ember } from "@nullvoxpopuli/ember-rolldown";
|
|
373
356
|
|
|
@@ -390,7 +373,7 @@ describe("layer: qunit", () => {
|
|
|
390
373
|
let build = await execa("pnpm build", { cwd: project.directory, shell: true });
|
|
391
374
|
expect(build.exitCode).toBe(0);
|
|
392
375
|
|
|
393
|
-
let output = await read(
|
|
376
|
+
let output = await project.read("dist/index.js");
|
|
394
377
|
|
|
395
378
|
expect(output).toContain("precompileTemplate");
|
|
396
379
|
expect(output).not.toContain("createTemplateFactory");
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { describe, it, beforeAll, afterAll, expect } from "vitest";
|
|
2
|
-
import { generate } from "#test-helpers";
|
|
2
|
+
import { generate, listFiles } from "#test-helpers";
|
|
3
3
|
import { writeLibrarySource } from "./library-src-fixtures.ts";
|
|
4
4
|
import { execa } from "execa";
|
|
5
|
-
import { mkdir,
|
|
6
|
-
import { dirname, join
|
|
5
|
+
import { mkdir, rm, writeFile } from "node:fs/promises";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
7
|
|
|
8
8
|
import type { Project } from "ember.nvp";
|
|
9
9
|
|
|
@@ -14,20 +14,6 @@ import type { Project } from "ember.nvp";
|
|
|
14
14
|
* headless-browser mode), asserting the specs genuinely run and pass.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
async function listFiles(directory: string): Promise<string[]> {
|
|
18
|
-
const entries = await readdir(directory, { withFileTypes: true, recursive: true });
|
|
19
|
-
|
|
20
|
-
return entries
|
|
21
|
-
.filter((entry) => entry.isFile())
|
|
22
|
-
.map((entry) => relative(directory, join(entry.parentPath, entry.name)))
|
|
23
|
-
.filter((path) => !path.split(sep).includes("node_modules"))
|
|
24
|
-
.sort();
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function read(project: Project, filePath: string): Promise<string> {
|
|
28
|
-
return readFile(join(project.directory, filePath), "utf-8");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
17
|
async function emit(project: Project, files: Record<string, string>) {
|
|
32
18
|
for (let [path, contents] of Object.entries(files)) {
|
|
33
19
|
let filePath = join(project.directory, path);
|
|
@@ -200,7 +186,7 @@ describe("layer: vitest", () => {
|
|
|
200
186
|
});
|
|
201
187
|
|
|
202
188
|
it("generates the vitest config", async () => {
|
|
203
|
-
expect(await read(
|
|
189
|
+
expect(await project.read("vitest.config.mjs")).toMatchInlineSnapshot(`
|
|
204
190
|
"import { ember } from "@nullvoxpopuli/ember-vite";
|
|
205
191
|
import { webdriverio } from "@vitest/browser-webdriverio";
|
|
206
192
|
import { defineConfig } from "vitest/config";
|