ember.nvp 1.8.0 → 1.10.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.
Files changed (47) hide show
  1. package/.release-plan.json +15 -35
  2. package/CHANGELOG.md +33 -0
  3. package/README.md +73 -4
  4. package/package.json +2 -1
  5. package/packages/rolldown/package.json +6 -2
  6. package/packages/rolldown/src/dts.test.ts +105 -1
  7. package/packages/rolldown/src/isolated-declarations.ts +67 -61
  8. package/packages/rolldown/src/transform.test.ts +212 -15
  9. package/packages/rolldown/src/transform.ts +106 -26
  10. package/src/cli/args.js +170 -48
  11. package/src/cli/generator.js +2 -1
  12. package/src/cli/help.js +82 -0
  13. package/src/cli/index.js +15 -0
  14. package/src/cli/questions/layer-options.js +130 -0
  15. package/src/cli/questions/validate-option.js +106 -0
  16. package/src/layers/eslint-bundled/app.test.ts +182 -0
  17. package/src/layers/eslint-bundled/index.js +172 -0
  18. package/src/layers/expect-type/files/type-tests/index.test.ts +4 -0
  19. package/src/layers/expect-type/files/type-tests/tsconfig.json +8 -0
  20. package/src/layers/expect-type/index.js +114 -0
  21. package/src/layers/github-actions/app.test.ts +1 -1
  22. package/src/layers/typescript/app.test.ts +6 -0
  23. package/src/layers/typescript/index.js +48 -148
  24. package/src/layers/typescript/typescript-6.js +102 -0
  25. package/src/layers/typescript/typescript-7.js +177 -0
  26. package/src/utils/manifest.js +13 -0
  27. package/src/utils/npm.js +27 -0
  28. package/src/utils/project.js +23 -0
  29. package/src/utils/types.ts +52 -1
  30. package/src/utils/typescript.js +147 -0
  31. package/test/ember.nvp/base-library.test.ts +11 -0
  32. package/test/ember.nvp/cli.test.ts +142 -1
  33. package/test/ember.nvp/layer-expect-type.test.ts +113 -0
  34. package/test/ember.nvp/layer-options.test.ts +248 -0
  35. package/test/ember.nvp/layer-readme.test.ts +4 -2
  36. package/test/ember.nvp/layer-typescript.test.ts +302 -0
  37. package/test/ember.nvp/layers.test.ts +2 -2
  38. package/test/ember.nvp/permutations/shared.ts +6 -6
  39. package/test/ember.nvp/validate-option.test.ts +152 -0
  40. package/test/test-helpers/permutate.test.ts +2 -7
  41. package/test/test-helpers.ts +12 -2
  42. package/vitest.config.js +7 -1
  43. package/src/layers/eslint-bundled-ember/index.js +0 -91
  44. package/src/layers/eslint-bundled-nvp/app.test.ts +0 -91
  45. package/src/layers/eslint-bundled-nvp/index.js +0 -91
  46. /package/src/layers/{eslint-bundled-ember/files → eslint-bundled/files/ember}/eslint.config.js +0 -0
  47. /package/src/layers/{eslint-bundled-nvp/files → eslint-bundled/files/nvp}/eslint.config.js +0 -0
@@ -0,0 +1,248 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { stripVTControlCharacters } from "node:util";
3
+ import { Project } from "#utils/project.js";
4
+ import { coreOptions, parseCliArgs, parseLayerOptionsFromParsedArgs } from "#args";
5
+ import { printHelp } from "../../src/cli/help.js";
6
+ import type { DiscoveredLayer } from "#types";
7
+
8
+ describe("Layer Options Feature", () => {
9
+ const fakeKitchenSinkLayer: DiscoveredLayer = {
10
+ name: "fake-kitchen-sink",
11
+ label: "Fake Kitchen Sink Layer",
12
+ hint: "demonstrates options",
13
+ options: {
14
+ unitCount: {
15
+ type: "number",
16
+ prompt: "How many units do you want?",
17
+ default: 7,
18
+ validate: (val: number) => (val > 0 ? undefined : "Must be greater than 0"),
19
+ },
20
+ customTitle: {
21
+ type: "text",
22
+ prompt: "Enter a custom title",
23
+ default: "My Kitchen Sink",
24
+ validate: (input: string) =>
25
+ input.trim().length > 0 ? undefined : "Title cannot be empty",
26
+ },
27
+ flavor: {
28
+ type: "select",
29
+ prompt: "Which kitchen sink flavor do you prefer?",
30
+ default: "standard",
31
+ options: [
32
+ { label: "Standard", value: "standard", hint: "Regular kitchen sink setup" },
33
+ { label: "Deluxe", value: "deluxe", hint: "Includes extra features" },
34
+ ],
35
+ },
36
+ enableLogging: {
37
+ type: "confirm",
38
+ prompt: "Enable detailed sink logging?",
39
+ default: true,
40
+ },
41
+ extras: {
42
+ type: "multiselect",
43
+ prompt: "Select optional kitchen sink extras",
44
+ default: ["soap-dispenser"],
45
+ options: [
46
+ { label: "Soap Dispenser", value: "soap-dispenser", hint: "Built-in pump" },
47
+ { label: "Garbage Disposal", value: "garbage-disposal", hint: "Continuous feed" },
48
+ ],
49
+ validate: (val: string[]) => (val.length > 0 ? undefined : "Select at least one extra"),
50
+ },
51
+ },
52
+ async run(_project, _options = {}) {
53
+ // noop
54
+ },
55
+ async isSetup(_project?: Project, explain?: boolean): Promise<any> {
56
+ return explain ? { isSetup: true, reasons: [] } : true;
57
+ },
58
+ };
59
+
60
+ describe("Project.prototype.getLayerOptions", () => {
61
+ it("returns default values when no user options are provided", () => {
62
+ const project = new Project("/tmp/test", {
63
+ name: "my-app",
64
+ type: "app",
65
+ path: "/tmp/test",
66
+ packageManager: "pnpm",
67
+ layers: [fakeKitchenSinkLayer],
68
+ });
69
+
70
+ expect(project.getLayerOptions("fake-kitchen-sink")).toEqual({
71
+ unitCount: 7,
72
+ customTitle: "My Kitchen Sink",
73
+ flavor: "standard",
74
+ enableLogging: true,
75
+ extras: ["soap-dispenser"],
76
+ });
77
+ });
78
+
79
+ it("overrides default values with user-supplied options", () => {
80
+ const project = new Project("/tmp/test", {
81
+ name: "my-app",
82
+ type: "app",
83
+ path: "/tmp/test",
84
+ packageManager: "pnpm",
85
+ layers: [fakeKitchenSinkLayer],
86
+ options: {
87
+ "fake-kitchen-sink": {
88
+ unitCount: 12,
89
+ customTitle: "Custom Sink",
90
+ flavor: "deluxe",
91
+ enableLogging: false,
92
+ extras: ["garbage-disposal"],
93
+ },
94
+ },
95
+ });
96
+
97
+ expect(project.getLayerOptions("fake-kitchen-sink")).toEqual({
98
+ unitCount: 12,
99
+ customTitle: "Custom Sink",
100
+ flavor: "deluxe",
101
+ enableLogging: false,
102
+ extras: ["garbage-disposal"],
103
+ });
104
+ });
105
+
106
+ it("returns empty object for layers without options or schemas", () => {
107
+ const plainLayer: DiscoveredLayer = {
108
+ name: "plain",
109
+ label: "Plain Layer",
110
+ async run() {},
111
+ async isSetup(_project?: Project, explain?: boolean): Promise<any> {
112
+ return explain ? { isSetup: true, reasons: [] } : true;
113
+ },
114
+ };
115
+
116
+ const project = new Project("/tmp/test", {
117
+ name: "my-app",
118
+ type: "app",
119
+ path: "/tmp/test",
120
+ packageManager: "pnpm",
121
+ layers: [plainLayer],
122
+ });
123
+
124
+ expect(project.getLayerOptions("plain")).toEqual({});
125
+ });
126
+ });
127
+
128
+ describe("CLI flags", () => {
129
+ const layers = [fakeKitchenSinkLayer];
130
+
131
+ function parse(args: string[]) {
132
+ return parseLayerOptionsFromParsedArgs(layers, parseCliArgs(args, layers));
133
+ }
134
+
135
+ it("parses layer options for all option types", () => {
136
+ expect(
137
+ parse([
138
+ "--fake-kitchen-sink.unitCount=15",
139
+ "--fake-kitchen-sink.customTitle",
140
+ "CLI Title",
141
+ "--fake-kitchen-sink.flavor",
142
+ "deluxe",
143
+ "--no-fake-kitchen-sink.enableLogging",
144
+ "--fake-kitchen-sink.extras",
145
+ "soap-dispenser",
146
+ "--fake-kitchen-sink.extras",
147
+ "garbage-disposal",
148
+ ]),
149
+ ).toEqual({
150
+ "fake-kitchen-sink": {
151
+ unitCount: 15,
152
+ customTitle: "CLI Title",
153
+ flavor: "deluxe",
154
+ enableLogging: false,
155
+ extras: ["soap-dispenser", "garbage-disposal"],
156
+ },
157
+ });
158
+ });
159
+
160
+ it("turns a confirm option on without a value", () => {
161
+ expect(parse(["--fake-kitchen-sink.enableLogging"])).toEqual({
162
+ "fake-kitchen-sink": { enableLogging: true },
163
+ });
164
+ });
165
+
166
+ it("parses multiselect options supplied as comma-separated string", () => {
167
+ expect(parse(["--fake-kitchen-sink.extras", "soap-dispenser, garbage-disposal"])).toEqual({
168
+ "fake-kitchen-sink": {
169
+ extras: ["soap-dispenser", "garbage-disposal"],
170
+ },
171
+ });
172
+ });
173
+
174
+ it("leaves out options that were not passed", () => {
175
+ expect(parse(["--name", "my-app"])).toEqual({});
176
+ });
177
+
178
+ it("ignores flags for layers that were not selected", () => {
179
+ const values = parseCliArgs(["--fake-kitchen-sink.flavor", "deluxe"], layers);
180
+
181
+ expect(parseLayerOptionsFromParsedArgs([], values)).toEqual({});
182
+ });
183
+
184
+ it("throws when a confirm option is given a value", () => {
185
+ expect(() => parseCliArgs(["--fake-kitchen-sink.enableLogging=false"], layers)).toThrow(
186
+ "Option '--fake-kitchen-sink.enableLogging' does not take an argument",
187
+ );
188
+ });
189
+
190
+ it("throws on an unknown option", () => {
191
+ expect(() => parseCliArgs(["--fake-kitchen-sink.unitcount=3"], layers)).toThrow(
192
+ "Unknown option '--fake-kitchen-sink.unitcount'",
193
+ );
194
+ });
195
+
196
+ it.each([
197
+ ["a value that fails validate", "--fake-kitchen-sink.unitCount=-5"],
198
+ ["a number in hex", "--fake-kitchen-sink.unitCount=0x10"],
199
+ ["an unlisted select value", "--fake-kitchen-sink.flavor=ultra"],
200
+ ["an unlisted multiselect value", "--fake-kitchen-sink.extras=invalid-extra"],
201
+ ])("exits on %s", (_, arg) => {
202
+ const exitSpy = vi.spyOn(process, "exit").mockImplementation((() => {
203
+ throw new Error("process.exit called");
204
+ }) as any);
205
+
206
+ try {
207
+ expect(() => parse([arg])).toThrow("process.exit called");
208
+ expect(exitSpy).toHaveBeenCalledWith(1);
209
+ } finally {
210
+ exitSpy.mockRestore();
211
+ }
212
+ });
213
+ });
214
+
215
+ it("lists layer options in --help", () => {
216
+ const lines: string[] = [];
217
+ const logSpy = vi.spyOn(console, "log").mockImplementation((line: string) => {
218
+ lines.push(stripVTControlCharacters(line));
219
+ });
220
+
221
+ try {
222
+ printHelp(coreOptions, [fakeKitchenSinkLayer]);
223
+ } finally {
224
+ logSpy.mockRestore();
225
+ }
226
+
227
+ const output = lines.join("\n");
228
+
229
+ expect(output.slice(output.indexOf("Layer Options:"))).toMatchInlineSnapshot(`
230
+ "Layer Options:
231
+ --fake-kitchen-sink.unitCount <number>
232
+ How many units do you want? [default: 7]
233
+
234
+ --fake-kitchen-sink.customTitle <string>
235
+ Enter a custom title [default: "My Kitchen Sink"]
236
+
237
+ --fake-kitchen-sink.flavor <string>
238
+ Which kitchen sink flavor do you prefer? [choices: "standard", "deluxe"] [default: "standard"]
239
+
240
+ --[no-]fake-kitchen-sink.enableLogging <boolean>
241
+ Enable detailed sink logging? [default: true]
242
+
243
+ --fake-kitchen-sink.extras <string>
244
+ Select optional kitchen sink extras [choices: "soap-dispenser", "garbage-disposal"] [default: ["soap-dispenser"]]
245
+ "
246
+ `);
247
+ });
248
+ });
@@ -71,9 +71,11 @@ describe("layer: readme", () => {
71
71
 
72
72
  ### TypeScript
73
73
 
74
- This project uses TypeScript and Glint for static type checking.
74
+ This project uses TypeScript 7 and [ember-content-mapper](https://github.com/NullVoxPopuli/ember-content-mapper) for static type checking.
75
75
 
76
- - \`pnpm lint:types\` - Typecheck code with Glint/TypeScript
76
+ - \`pnpm lint:types\` - Typecheck code with TypeScript
77
+ - Imports of \`.gts\` and \`.gjs\` modules must include the extension
78
+ - Editor setup: see [ember-content-mapper's editors section](https://github.com/NullVoxPopuli/ember-content-mapper#editors)
77
79
  "
78
80
  `);
79
81
  });
@@ -0,0 +1,302 @@
1
+ import { describe, it, beforeAll, afterAll, expect } from "vitest";
2
+ import { generate, expectIsSetup, layers, reapply } from "#test-helpers";
3
+ import { writeLibrarySource } from "./library-src-fixtures.ts";
4
+ import { execa } from "execa";
5
+ import { rm, writeFile } from "node:fs/promises";
6
+ import { join } from "node:path";
7
+ import { packageJson } from "ember-apply";
8
+
9
+ import type { Project } from "ember.nvp";
10
+
11
+ const typescript = layers.find((layer) => layer.name === "typescript")!;
12
+ const eslint = layers.find((layer) => layer.name === "eslint-bundled")!;
13
+
14
+ const typescript6 = { typescript: { version: "6" }, "eslint-bundled": { preset: "nvp" } };
15
+ const typescript7 = { typescript: { version: "7" }, "eslint-bundled": { preset: "nvp" } };
16
+
17
+ function run(project: Project, command: string) {
18
+ return execa(command, { cwd: project.directory, shell: true, all: true, reject: false });
19
+ }
20
+
21
+ async function devDependencies(project: Project): Promise<Record<string, string>> {
22
+ let manifest = await packageJson.read(project.directory);
23
+
24
+ return manifest.devDependencies ?? {};
25
+ }
26
+
27
+ describe("layer: typescript, version 7", () => {
28
+ const dirs: string[] = [];
29
+
30
+ afterAll(async () => {
31
+ if (process.env.CI) return;
32
+
33
+ for (const dir of dirs) {
34
+ await rm(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
35
+ }
36
+ });
37
+
38
+ describe("app with eslint", () => {
39
+ let project: Project;
40
+
41
+ beforeAll(async () => {
42
+ project = await generate({
43
+ type: "app",
44
+ layers: ["typescript", "eslint-bundled", "git"],
45
+ options: typescript7,
46
+ });
47
+ dirs.push(project.directory);
48
+ });
49
+
50
+ it("is setup", async () => {
51
+ await expectIsSetup(project, typescript);
52
+ await expectIsSetup(project, eslint);
53
+ });
54
+
55
+ it("installs TypeScript 7 as @typescript/native, and TypeScript 6 for typescript-eslint", async () => {
56
+ let deps = await devDependencies(project);
57
+
58
+ expect(deps["@typescript/native"]).toMatch(/^npm:typescript@7\./);
59
+ expect(deps["typescript"]).toMatch(/^npm:@typescript\/typescript6@6\./);
60
+ expect(deps).toHaveProperty("ember-content-mapper");
61
+ expect(deps).not.toHaveProperty("@glint/tsserver-plugin");
62
+ });
63
+
64
+ it("reapplying causes no changes", async () => {
65
+ await reapply(project, ["typescript", "eslint-bundled", "git"]);
66
+
67
+ expect(await project.gitHasDiff()).toBe(false);
68
+ });
69
+
70
+ describe("after install", () => {
71
+ beforeAll(async () => {
72
+ let install = await run(project, "pnpm install");
73
+ expect(install.exitCode, install.all).toBe(0);
74
+ });
75
+
76
+ it("type checking works", async () => {
77
+ let result = await run(project, "pnpm lint:types");
78
+
79
+ expect(result.exitCode, result.all).toBe(0);
80
+ });
81
+
82
+ it("reports template errors in .gts files", async () => {
83
+ let file = "app/templates/application.gts";
84
+ let original = await project.read(file);
85
+
86
+ await writeFile(
87
+ join(project.directory, file),
88
+ `const count: number = 1;\n\n<template>{{count.nope}}</template>\n`,
89
+ );
90
+
91
+ let result = await run(project, "pnpm lint:types");
92
+
93
+ await writeFile(join(project.directory, file), original!);
94
+
95
+ expect(result.exitCode).not.toBe(0);
96
+ expect(result.all).toContain("application.gts");
97
+ expect(result.all).toContain("'nope'");
98
+ });
99
+
100
+ it("linting works", async () => {
101
+ await run(project, "pnpm lint:eslint --fix");
102
+
103
+ let result = await run(project, "pnpm lint:eslint");
104
+
105
+ expect(result.exitCode, result.all).toBe(0);
106
+ });
107
+
108
+ it("build works", async () => {
109
+ let result = await run(project, "pnpm build");
110
+
111
+ expect(result.exitCode, result.all).toBe(0);
112
+ });
113
+ });
114
+ });
115
+
116
+ describe("app without eslint", () => {
117
+ let project: Project;
118
+
119
+ beforeAll(async () => {
120
+ project = await generate({ type: "app", layers: ["typescript"], options: typescript7 });
121
+ dirs.push(project.directory);
122
+ });
123
+
124
+ it("is setup", async () => {
125
+ await expectIsSetup(project, typescript);
126
+ });
127
+
128
+ it("has no typescript, because nothing needs TypeScript 6", async () => {
129
+ expect(await devDependencies(project)).not.toHaveProperty("typescript");
130
+ });
131
+
132
+ it("type checking works", async () => {
133
+ let install = await run(project, "pnpm install");
134
+ expect(install.exitCode, install.all).toBe(0);
135
+
136
+ let result = await run(project, "pnpm lint:types");
137
+
138
+ expect(result.exitCode, result.all).toBe(0);
139
+ });
140
+ });
141
+
142
+ describe.each(["app", "library"] as const)(
143
+ "migrating a TypeScript 6 %s without eslint",
144
+ (type) => {
145
+ let project: Project;
146
+
147
+ beforeAll(async () => {
148
+ let layers = ["typescript", "git"];
149
+ let before = await generate({
150
+ type,
151
+ layers,
152
+ options: { typescript: { version: "6" } },
153
+ });
154
+ dirs.push(before.directory);
155
+
156
+ expect(await devDependencies(before)).toHaveProperty("typescript");
157
+
158
+ project = await generate({
159
+ directory: before.directory,
160
+ type,
161
+ layers,
162
+ options: { typescript: { version: "7" } },
163
+ });
164
+ });
165
+
166
+ it("is setup, and detects version 7", async () => {
167
+ await expectIsSetup(project, typescript);
168
+ expect(await typescript.options?.version?.detect?.(project)).toBe("7");
169
+ });
170
+
171
+ it("removes typescript and the tsserver plugin, because nothing needs TypeScript 6", async () => {
172
+ let manifest = await packageJson.read(project.directory);
173
+
174
+ expect(manifest.scripts?.["lint:types"]).toBe("tsc --noEmit --runExternalCode");
175
+ expect(manifest.devDependencies?.["@typescript/native"]).toMatch(/^npm:typescript@7\./);
176
+ expect(manifest.devDependencies).not.toHaveProperty("typescript");
177
+ expect(manifest.devDependencies).not.toHaveProperty("@glint/tsserver-plugin");
178
+ });
179
+
180
+ it("reapplying causes no changes", async () => {
181
+ await reapply(project, ["typescript", "git"]);
182
+
183
+ expect(await project.gitHasDiff()).toBe(false);
184
+ });
185
+
186
+ it("type checking and the build work", async () => {
187
+ if (type === "library") await writeLibrarySource(project, "typescript");
188
+
189
+ let install = await run(project, "pnpm install");
190
+ expect(install.exitCode, install.all).toBe(0);
191
+
192
+ let types = await run(project, "pnpm lint:types");
193
+ expect(types.exitCode, types.all).toBe(0);
194
+
195
+ let build = await run(project, "pnpm build");
196
+ expect(build.exitCode, build.all).toBe(0);
197
+ });
198
+ },
199
+ );
200
+
201
+ describe("migrating a TypeScript 6 app with eslint", () => {
202
+ let project: Project;
203
+
204
+ beforeAll(async () => {
205
+ let layers = ["typescript", "eslint-bundled", "git"];
206
+ let before = await generate({ type: "app", layers, options: typescript6 });
207
+ dirs.push(before.directory);
208
+
209
+ project = await generate({
210
+ directory: before.directory,
211
+ type: "app",
212
+ layers,
213
+ options: typescript7,
214
+ });
215
+ });
216
+
217
+ it("is setup", async () => {
218
+ await expectIsSetup(project, typescript);
219
+ await expectIsSetup(project, eslint);
220
+ });
221
+
222
+ it("detects the version the project uses", async () => {
223
+ expect(await typescript.options?.version?.detect?.(project)).toBe("7");
224
+ });
225
+
226
+ it("switches to TypeScript 7 and keeps TypeScript 6 for typescript-eslint", async () => {
227
+ let manifest = await packageJson.read(project.directory);
228
+
229
+ expect(manifest.scripts?.["lint:types"]).toBe("tsc --noEmit --runExternalCode");
230
+ expect(manifest.devDependencies?.["typescript"]).toMatch(/^npm:@typescript\/typescript6@/);
231
+ expect(manifest.devDependencies).not.toHaveProperty("@glint/tsserver-plugin");
232
+ });
233
+
234
+ it("type checking and linting work", async () => {
235
+ let install = await run(project, "pnpm install");
236
+ expect(install.exitCode, install.all).toBe(0);
237
+
238
+ let types = await run(project, "pnpm lint:types");
239
+ expect(types.exitCode, types.all).toBe(0);
240
+
241
+ await run(project, "pnpm lint:eslint --fix");
242
+
243
+ let lint = await run(project, "pnpm lint:eslint");
244
+ expect(lint.exitCode, lint.all).toBe(0);
245
+ });
246
+ });
247
+
248
+ describe("library", () => {
249
+ let project: Project;
250
+
251
+ beforeAll(async () => {
252
+ project = await generate({
253
+ type: "library",
254
+ name: "my-lib",
255
+ layers: ["typescript", "expect-type"],
256
+ options: typescript7,
257
+ });
258
+ dirs.push(project.directory);
259
+
260
+ await writeLibrarySource(project, "typescript");
261
+
262
+ let install = await run(project, "pnpm install");
263
+ expect(install.exitCode, install.all).toBe(0);
264
+ });
265
+
266
+ it("is setup", async () => {
267
+ await expectIsSetup(project, typescript);
268
+ });
269
+
270
+ it("has no typescript, because nothing needs TypeScript 6", async () => {
271
+ expect(await devDependencies(project)).not.toHaveProperty("typescript");
272
+ });
273
+
274
+ it("type checking works", async () => {
275
+ let result = await run(project, "pnpm lint:types");
276
+
277
+ expect(result.exitCode, result.all).toBe(0);
278
+ });
279
+
280
+ it("type tests resolve .gts modules", async () => {
281
+ await writeFile(
282
+ join(project.directory, "type-tests/greeting.test.ts"),
283
+ `import { expectTypeOf } from "expect-type";
284
+ import { Greeting, type GreetingSignature } from "../src/index.ts";
285
+
286
+ expectTypeOf(Greeting).not.toBeAny();
287
+ expectTypeOf<GreetingSignature["Args"]>().toEqualTypeOf<{ name: string }>();
288
+ `,
289
+ );
290
+
291
+ let result = await run(project, "pnpm lint:type-tests");
292
+
293
+ expect(result.exitCode, result.all).toBe(0);
294
+ });
295
+
296
+ it("build works", async () => {
297
+ let result = await run(project, "pnpm build");
298
+
299
+ expect(result.exitCode, result.all).toBe(0);
300
+ });
301
+ });
302
+ });
@@ -11,8 +11,8 @@ describe("discoverLayers", () => {
11
11
  expect(layerNames).toMatchInlineSnapshot(`
12
12
  [
13
13
  "are-the-types-wrong",
14
- "eslint-bundled-ember",
15
- "eslint-bundled-nvp",
14
+ "eslint-bundled",
15
+ "expect-type",
16
16
  "git",
17
17
  "github-actions",
18
18
  "inspector-support",
@@ -16,7 +16,7 @@ const RE_APPLY_ONLY = new Set<string>([
16
16
  ]);
17
17
  const INITIAL_ONLY = new Set<string>([
18
18
  // baseline,
19
- // "eslint-bundled-nvp",
19
+ // "eslint-bundled",
20
20
  // "typescript"
21
21
  ]);
22
22
 
@@ -28,11 +28,11 @@ const INITIAL_ONLY = new Set<string>([
28
28
  */
29
29
  const NOT_YET_SUPPORTED: Record<ProjectType, Set<string>> = {
30
30
  // publish checks: apps aren't published
31
- app: new Set(["publint", "are-the-types-wrong"]),
31
+ app: new Set(["publint", "are-the-types-wrong", "expect-type"]),
32
32
  // libraries have no Application to wire inspector support into
33
33
  library: new Set(["inspector-support"]),
34
34
  // publish checks: extensions aren't published (to npm)
35
- extension: new Set(["publint", "are-the-types-wrong"]),
35
+ extension: new Set(["publint", "are-the-types-wrong", "expect-type"]),
36
36
  // same toolchain as libraries: no Application either
37
37
  "custom-element": new Set(["inspector-support"]),
38
38
  };
@@ -45,7 +45,7 @@ const TYPE_FOR_BASE: Record<(typeof bases)[number], ProjectType> = {
45
45
  };
46
46
 
47
47
  /**
48
- * Layers that only add lint / format / publish checks.
48
+ * Layers that only add lint / format / type / publish checks.
49
49
  *
50
50
  * They don't affect the runtime shape of a project,
51
51
  * so they permutate among themselves in a small "checks" matrix
@@ -55,12 +55,12 @@ const TYPE_FOR_BASE: Record<(typeof bases)[number], ProjectType> = {
55
55
  * so linear cross-group coverage remains.
56
56
  */
57
57
  const CHECK_LAYERS = new Set<string>([
58
- "eslint-bundled-ember",
59
- "eslint-bundled-nvp",
58
+ "eslint-bundled",
60
59
  "eslint-ejected",
61
60
  "prettier",
62
61
  "publint",
63
62
  "are-the-types-wrong",
63
+ "expect-type",
64
64
  ]);
65
65
 
66
66
  type PermutationGroup = "main" | "checks";