ember.nvp 0.5.0 → 0.7.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 (91) hide show
  1. package/.github/workflows/ci.yml +17 -2
  2. package/.release-plan.json +84 -4
  3. package/CHANGELOG.md +68 -0
  4. package/README.md +29 -0
  5. package/package.json +4 -2
  6. package/packages/build-tooling-utils/README.md +37 -0
  7. package/packages/build-tooling-utils/index.ts +2 -0
  8. package/packages/build-tooling-utils/package.json +42 -0
  9. package/packages/build-tooling-utils/rolldown.config.js +18 -0
  10. package/packages/build-tooling-utils/src/extensions.ts +18 -0
  11. package/packages/build-tooling-utils/src/maybe-babel.ts +146 -0
  12. package/packages/rolldown/README.md +115 -0
  13. package/packages/rolldown/index.ts +56 -0
  14. package/packages/rolldown/package.json +59 -0
  15. package/packages/rolldown/rolldown.config.js +21 -0
  16. package/packages/rolldown/src/babel.ts +136 -0
  17. package/packages/rolldown/src/define-config.ts +32 -0
  18. package/packages/rolldown/src/externals.ts +83 -0
  19. package/packages/rolldown/src/isolated-declarations.ts +65 -0
  20. package/packages/rolldown/src/transform.ts +162 -0
  21. package/packages/vite/README.md +7 -21
  22. package/packages/vite/index.ts +1 -1
  23. package/packages/vite/package.json +4 -6
  24. package/packages/vite/src/ember.ts +129 -15
  25. package/packages/vite/src/test-html.ts +86 -0
  26. package/src/bases/minimal-app/files/app/router.ts +1 -0
  27. package/src/bases/minimal-app/files/tsconfig.json +1 -0
  28. package/src/bases/minimal-app/index.js +2 -2
  29. package/src/bases/minimal-library/files/README.md +41 -0
  30. package/src/bases/minimal-library/files/package.json +49 -0
  31. package/src/bases/minimal-library/files/tsconfig.json +10 -0
  32. package/src/bases/minimal-library/files/tsdown.config.js +6 -0
  33. package/src/bases/minimal-library/index.js +158 -2
  34. package/src/cli/args.js +7 -1
  35. package/src/cli/index.js +90 -16
  36. package/src/cli/questions/layers.js +5 -2
  37. package/src/cli/questions/project-type.js +0 -5
  38. package/src/cli/questions/write.js +169 -0
  39. package/src/index.js +1 -0
  40. package/src/layers/are-the-types-wrong/index.js +74 -0
  41. package/src/layers/git/files/gitignore +14 -1
  42. package/src/layers/git/index.js +6 -5
  43. package/src/layers/github-actions/index.js +12 -8
  44. package/src/layers/index.js +1 -1
  45. package/src/layers/inspector-support/index.js +101 -0
  46. package/src/layers/prettier/files/.prettierignore +4 -0
  47. package/src/layers/publint/index.js +68 -0
  48. package/src/layers/qunit/files/tests/unit/macros-test.ts +2 -2
  49. package/src/layers/qunit/index.js +107 -7
  50. package/src/layers/qunit/library-files/config/test/babel.config.js +41 -0
  51. package/src/layers/qunit/library-files/config/test/testem.cjs +26 -0
  52. package/src/layers/qunit/library-files/tests/test-helper.ts +36 -0
  53. package/src/layers/qunit/library-files/tests/unit/.gitkeep +0 -0
  54. package/src/layers/qunit/library-files/vite.config.mjs +6 -0
  55. package/src/layers/typescript/index.js +43 -7
  56. package/src/layers/vitest/files/vitest.config.mjs +11 -11
  57. package/src/layers/vitest/index.js +70 -32
  58. package/src/utils/babel.js +9 -6
  59. package/src/utils/fs.js +44 -3
  60. package/src/utils/inspector-support.js +196 -0
  61. package/src/utils/npm.js +21 -7
  62. package/src/utils/remove-types.js +5 -0
  63. package/src/utils/rewrite-imports.js +157 -0
  64. package/src/utils/stage.js +317 -0
  65. package/src/utils/tsdown-config.js +85 -0
  66. package/src/utils/types.ts +5 -1
  67. package/test/ember.nvp/base-library.test.ts +232 -0
  68. package/test/ember.nvp/generator-replace-or-update.test.ts +2 -3
  69. package/test/ember.nvp/inspector-support.test.ts +179 -0
  70. package/test/ember.nvp/layer-inspector-support.test.ts +69 -0
  71. package/test/ember.nvp/layer-publish-checks.test.ts +103 -0
  72. package/test/ember.nvp/layer-qunit.test.ts +376 -15
  73. package/test/ember.nvp/layer-vitest.test.ts +250 -0
  74. package/test/ember.nvp/layers.test.ts +16 -0
  75. package/test/ember.nvp/library-src-fixtures.ts +81 -0
  76. package/test/ember.nvp/permutations/app-1.test.ts +3 -0
  77. package/test/ember.nvp/permutations/app-2.test.ts +3 -0
  78. package/test/ember.nvp/permutations/library-1.test.ts +3 -0
  79. package/test/ember.nvp/permutations/library-2.test.ts +3 -0
  80. package/test/ember.nvp/permutations/shared.ts +239 -0
  81. package/test/ember.nvp/rewrite-imports.test.ts +235 -0
  82. package/test/ember.nvp/stage.test.ts +188 -0
  83. package/test/test-helpers/permutate.test.ts +17 -0
  84. package/test/test-helpers.ts +20 -7
  85. package/tsconfig.json +1 -0
  86. package/packages/vite/src/maybe-babel.ts +0 -219
  87. package/src/layers/qunit/files/run-tests.mjs +0 -30
  88. package/src/layers/vitest/files/tests/test-helper.ts +0 -5
  89. package/test/ember.nvp/permutations.test.ts +0 -200
  90. /package/src/{layers/vitest/files/tests/integration/.gitkeep → bases/minimal-library/files/src/index.ts} +0 -0
  91. /package/src/layers/{vitest/files/tests/unit → qunit/library-files/tests/rendering}/.gitkeep +0 -0
@@ -0,0 +1,179 @@
1
+ import { describe, it, expect } from "vitest";
2
+
3
+ import { hasInspectorSupport, wireInspectorSupport } from "#utils/inspector-support.js";
4
+
5
+ /**
6
+ * The Application definition is found structurally, so every shape an app
7
+ * author may have refactored their app file into gets wired the same way
8
+ * the freshly generated shape does.
9
+ */
10
+ describe("wireInspectorSupport", () => {
11
+ it("wires the generated shape (export default class extends Application)", () => {
12
+ const code = [
13
+ `import Application from "ember-strict-application-resolver";`,
14
+ ``,
15
+ `export default class App extends Application {`,
16
+ ` modules = {`,
17
+ ` ...import.meta.glob("./router.*", { eager: true }),`,
18
+ ` };`,
19
+ `}`,
20
+ ].join("\n");
21
+
22
+ expect(wireInspectorSupport(code, "app/app.ts")).toMatchInlineSnapshot(`
23
+ "import Application from "ember-strict-application-resolver";
24
+ import setupInspector from "@embroider/legacy-inspector-support/ember-source-4.12";
25
+ export default class App extends Application {
26
+ modules = { ...import.meta.glob("./router.*", { eager: true }) };
27
+ inspector = setupInspector(this);
28
+ }"
29
+ `);
30
+ });
31
+
32
+ it("wires a named class exported in a separate statement", () => {
33
+ const code = [
34
+ `import Application from "ember-strict-application-resolver";`,
35
+ ``,
36
+ `class App extends Application {`,
37
+ ` modules = {};`,
38
+ `}`,
39
+ ``,
40
+ `export default App;`,
41
+ ].join("\n");
42
+
43
+ expect(wireInspectorSupport(code, "app/app.js")).toMatchInlineSnapshot(`
44
+ "import Application from "ember-strict-application-resolver";
45
+ import setupInspector from "@embroider/legacy-inspector-support/ember-source-4.12";
46
+ class App extends Application {
47
+ modules = { };
48
+ inspector = setupInspector(this);
49
+ }
50
+ export default App"
51
+ `);
52
+ });
53
+
54
+ it("wires an anonymous default-exported class", () => {
55
+ const code = [
56
+ `import Application from "ember-strict-application-resolver";`,
57
+ ``,
58
+ `export default class extends Application {}`,
59
+ ].join("\n");
60
+
61
+ expect(wireInspectorSupport(code, "app/app.js")).toMatchInlineSnapshot(`
62
+ "import Application from "ember-strict-application-resolver";
63
+ import setupInspector from "@embroider/legacy-inspector-support/ember-source-4.12";
64
+ export default class extends Application {
65
+ inspector = setupInspector(this);
66
+ }"
67
+ `);
68
+ });
69
+
70
+ it("wires a class expression assigned to a variable", () => {
71
+ const code = [
72
+ `import Application from "ember-strict-application-resolver";`,
73
+ ``,
74
+ `const App = class extends Application {`,
75
+ ` modules = {};`,
76
+ `};`,
77
+ ``,
78
+ `export default App;`,
79
+ ].join("\n");
80
+
81
+ expect(wireInspectorSupport(code, "app/app.js")).toMatchInlineSnapshot(`
82
+ "import Application from "ember-strict-application-resolver";
83
+ import setupInspector from "@embroider/legacy-inspector-support/ember-source-4.12";
84
+ const App = class extends Application {
85
+ modules = { };
86
+ inspector = setupInspector(this);
87
+ };
88
+ export default App"
89
+ `);
90
+ });
91
+
92
+ it("wires a class extending @ember/application under a renamed import", () => {
93
+ const code = [
94
+ `import Base from "@ember/application";`,
95
+ ``,
96
+ `export default class App extends Base {}`,
97
+ ].join("\n");
98
+
99
+ expect(wireInspectorSupport(code, "app/app.ts")).toMatchInlineSnapshot(`
100
+ "import Base from "@ember/application";
101
+ import setupInspector from "@embroider/legacy-inspector-support/ember-source-4.12";
102
+ export default class App extends Base {
103
+ inspector = setupInspector(this);
104
+ }"
105
+ `);
106
+ });
107
+
108
+ it("adds only the missing member when the import is already present", () => {
109
+ const code = [
110
+ `import wireUp from "@embroider/legacy-inspector-support/ember-source-4.12";`,
111
+ `import Application from "ember-strict-application-resolver";`,
112
+ ``,
113
+ `export default class App extends Application {}`,
114
+ ].join("\n");
115
+
116
+ expect(wireInspectorSupport(code, "app/app.ts")).toMatchInlineSnapshot(`
117
+ "import wireUp from "@embroider/legacy-inspector-support/ember-source-4.12";
118
+ import Application from "ember-strict-application-resolver";
119
+ export default class App extends Application {
120
+ inspector = wireUp(this);
121
+ }"
122
+ `);
123
+ });
124
+
125
+ it("is idempotent", () => {
126
+ const code = [
127
+ `import Application from "ember-strict-application-resolver";`,
128
+ ``,
129
+ `export default class App extends Application {`,
130
+ ` modules = {};`,
131
+ `}`,
132
+ ].join("\n");
133
+
134
+ const once = wireInspectorSupport(code, "app/app.ts");
135
+ const twice = wireInspectorSupport(once, "app/app.ts");
136
+
137
+ expect(twice).toBe(once);
138
+ });
139
+
140
+ it("leaves a module without an Application definition alone", () => {
141
+ const code = [`export function noApplicationHere() {}`, ``].join("\n");
142
+
143
+ expect(wireInspectorSupport(code, "app/app.js")).toBe(code);
144
+ });
145
+
146
+ it("leaves a class that does not extend an Application import alone", () => {
147
+ const code = [
148
+ `import Component from "@glimmer/component";`,
149
+ ``,
150
+ `export default class App extends Component {}`,
151
+ ].join("\n");
152
+
153
+ expect(wireInspectorSupport(code, "app/app.ts")).toBe(code);
154
+ });
155
+ });
156
+
157
+ describe("hasInspectorSupport", () => {
158
+ it("is false before wiring and true after", () => {
159
+ const code = [
160
+ `import Application from "ember-strict-application-resolver";`,
161
+ ``,
162
+ `export default class App extends Application {}`,
163
+ ].join("\n");
164
+
165
+ expect(hasInspectorSupport(code, "app/app.ts")).toBe(false);
166
+ expect(hasInspectorSupport(wireInspectorSupport(code, "app/app.ts"), "app/app.ts")).toBe(true);
167
+ });
168
+
169
+ it("is false when only the import is present", () => {
170
+ const code = [
171
+ `import setupInspector from "@embroider/legacy-inspector-support/ember-source-4.12";`,
172
+ `import Application from "ember-strict-application-resolver";`,
173
+ ``,
174
+ `export default class App extends Application {}`,
175
+ ].join("\n");
176
+
177
+ expect(hasInspectorSupport(code, "app/app.ts")).toBe(false);
178
+ });
179
+ });
@@ -0,0 +1,69 @@
1
+ import { describe, it, afterAll, expect } from "vitest";
2
+ import { generate, build, expectIsSetup, layers } from "#test-helpers";
3
+ import { execa } from "execa";
4
+ import { readFile, rm } from "node:fs/promises";
5
+ import { join } from "node:path";
6
+
7
+ const inspectorSupport = layers.find((layer) => layer.name === "inspector-support");
8
+
9
+ describe("layer: inspector-support", () => {
10
+ const dirs: string[] = [];
11
+
12
+ afterAll(async () => {
13
+ if (process.env.CI) return;
14
+
15
+ for (const dir of dirs) {
16
+ await rm(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
17
+ }
18
+ });
19
+
20
+ it("wires a TypeScript app, which still builds", async () => {
21
+ const project = await generate({ type: "app", layers: ["typescript", "inspector-support"] });
22
+ dirs.push(project.directory);
23
+
24
+ expect(inspectorSupport).toBeTruthy();
25
+ await expectIsSetup(project, inspectorSupport!);
26
+
27
+ const install = await execa("pnpm install", { cwd: project.directory, shell: true });
28
+ expect(install.exitCode).toBe(0);
29
+
30
+ const result = await build(project);
31
+ expect(result.exitCode).toBe(0);
32
+ });
33
+
34
+ it("wires a JavaScript app (no typescript layer), which still builds", async () => {
35
+ const project = await generate({ type: "app", layers: ["inspector-support"] });
36
+ dirs.push(project.directory);
37
+
38
+ expect(inspectorSupport).toBeTruthy();
39
+ await expectIsSetup(project, inspectorSupport!);
40
+
41
+ const appFile = await readFile(join(project.directory, "app/app.js"), "utf8");
42
+ expect(appFile).toMatchInlineSnapshot(`
43
+ "/**
44
+ * Looking for services that come from addons?
45
+ *
46
+ * See: https://github.com/embroider-build/embroider/issues/2659
47
+ *
48
+ * We currently don't support app-tree merging from libraries.
49
+ *
50
+ * For services, I highly recommend looking in to either of
51
+ * - https://github.com/chancancode/ember-polaris-service-
52
+ * - https://ember-primitives.pages.dev/6-utils/createService.md
53
+ * - https://ember-primitives.pages.dev/6-utils/createAsyncService.md
54
+ */
55
+ import Application from 'ember-strict-application-resolver';
56
+ import setupInspector from "@embroider/legacy-inspector-support/ember-source-4.12";
57
+ export default class App extends Application {
58
+ modules = { ...import.meta.glob('./router.*', { eager: true }), ...import.meta.glob('./templates/**/*', { eager: true }), ...import.meta.glob('./services/**/*', { eager: true }) };
59
+ inspector = setupInspector(this);
60
+ }"
61
+ `);
62
+
63
+ const install = await execa("pnpm install", { cwd: project.directory, shell: true });
64
+ expect(install.exitCode).toBe(0);
65
+
66
+ const result = await build(project);
67
+ expect(result.exitCode).toBe(0);
68
+ });
69
+ });
@@ -0,0 +1,103 @@
1
+ import { describe, it, beforeAll, afterAll, expect } from "vitest";
2
+ import { generate } from "#test-helpers";
3
+ import { execa } from "execa";
4
+ import { readFile, rm } from "node:fs/promises";
5
+ import { join } from "node:path";
6
+
7
+ import type { Project } from "ember.nvp";
8
+
9
+ /**
10
+ * publint and are-the-types-wrong run inside the library's own build
11
+ * (tsdown), so the real assertion is their report lines in `pnpm build`
12
+ * output.
13
+ */
14
+
15
+ async function read(project: Project, filePath: string): Promise<string> {
16
+ return readFile(join(project.directory, filePath), "utf-8");
17
+ }
18
+
19
+ describe("layers: publint + are-the-types-wrong", () => {
20
+ const dirs: string[] = [];
21
+
22
+ afterAll(async () => {
23
+ if (process.env.CI) return;
24
+
25
+ for (const dir of dirs) {
26
+ await rm(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
27
+ }
28
+ });
29
+
30
+ describe("TypeScript library", () => {
31
+ let project: Project;
32
+
33
+ beforeAll(async () => {
34
+ project = await generate({
35
+ type: "library",
36
+ name: "my-lib",
37
+ layers: ["typescript", "publint", "are-the-types-wrong"],
38
+ });
39
+ dirs.push(project.directory);
40
+ });
41
+
42
+ it("wires both checks into the build", async () => {
43
+ expect(await read(project, "tsdown.config.js")).toMatchInlineSnapshot(`
44
+ "import { defineConfig, ember } from "@nullvoxpopuli/ember-rolldown";
45
+ export default defineConfig({ entry: ["./src/index.ts"], plugins: [ember()], attw: { profile: "esm-only" }, publint: true })"
46
+ `);
47
+ });
48
+
49
+ it("both checks run and pass in the build", async () => {
50
+ let install = await execa("pnpm install", { cwd: project.directory, shell: true });
51
+ expect(install.exitCode).toBe(0);
52
+
53
+ let build = await execa("pnpm build", {
54
+ cwd: project.directory,
55
+ shell: true,
56
+ all: true,
57
+ // assertions match the report lines as plain text
58
+ env: { FORCE_COLOR: "0" },
59
+ });
60
+ expect(build.exitCode).toBe(0);
61
+
62
+ expect(build.all).toContain("[publint] No issues found");
63
+ expect(build.all).toContain("[attw] No problems found");
64
+ });
65
+ });
66
+
67
+ describe("JavaScript library", () => {
68
+ let project: Project;
69
+
70
+ beforeAll(async () => {
71
+ project = await generate({
72
+ type: "library",
73
+ name: "my-lib",
74
+ layers: ["publint", "are-the-types-wrong"],
75
+ });
76
+ dirs.push(project.directory);
77
+ });
78
+
79
+ it("wires publint only (no declarations to check)", async () => {
80
+ expect(await read(project, "tsdown.config.js")).toMatchInlineSnapshot(`
81
+ "import { defineConfig, ember } from "@nullvoxpopuli/ember-rolldown";
82
+ export default defineConfig({ entry: ["./src/index.js"], dts: false, plugins: [ember()], publint: true })"
83
+ `);
84
+ });
85
+
86
+ it("publint runs and passes in the build", async () => {
87
+ let install = await execa("pnpm install", { cwd: project.directory, shell: true });
88
+ expect(install.exitCode).toBe(0);
89
+
90
+ let build = await execa("pnpm build", {
91
+ cwd: project.directory,
92
+ shell: true,
93
+ all: true,
94
+ // assertions match the report lines as plain text
95
+ env: { FORCE_COLOR: "0" },
96
+ });
97
+ expect(build.exitCode).toBe(0);
98
+
99
+ expect(build.all).toContain("[publint] No issues found");
100
+ expect(build.all).not.toContain("[attw]");
101
+ });
102
+ });
103
+ });