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
@@ -29,13 +29,28 @@ jobs:
29
29
  - run: pnpm lint
30
30
 
31
31
  node_tests:
32
- name: "Node Tests"
32
+ name: "Node Tests (${{ matrix.slice.name }})"
33
33
  runs-on: ubuntu-latest
34
34
  timeout-minutes: 15
35
35
 
36
+ strategy:
37
+ fail-fast: false
38
+ matrix:
39
+ slice:
40
+ - name: app permutations 1/2
41
+ args: test/ember.nvp/permutations/app-1.test.ts
42
+ - name: app permutations 2/2
43
+ args: test/ember.nvp/permutations/app-2.test.ts
44
+ - name: library permutations 1/2
45
+ args: test/ember.nvp/permutations/library-1.test.ts
46
+ - name: library permutations 2/2
47
+ args: test/ember.nvp/permutations/library-2.test.ts
48
+ - name: everything else
49
+ args: --exclude 'test/ember.nvp/permutations/**'
50
+
36
51
  steps:
37
52
  - uses: wyvox/action@v1
38
53
  - run: |
39
54
  git config --global user.name "ci"
40
55
  git config --global user.email "non@none.none"
41
- pnpm test:node
56
+ pnpm test:node ${{ matrix.slice.args }}
@@ -2,14 +2,22 @@
2
2
  "solution": {
3
3
  "ember.nvp": {
4
4
  "impact": "minor",
5
- "oldVersion": "0.4.0",
6
- "newVersion": "0.5.0",
5
+ "oldVersion": "0.6.0",
6
+ "newVersion": "0.7.0",
7
7
  "tagName": "latest",
8
8
  "constraints": [
9
9
  {
10
10
  "impact": "minor",
11
11
  "reason": "Appears in changelog section :rocket: Enhancement"
12
12
  },
13
+ {
14
+ "impact": "patch",
15
+ "reason": "Appears in changelog section :bug: Bug Fix"
16
+ },
17
+ {
18
+ "impact": "patch",
19
+ "reason": "Appears in changelog section :memo: Documentation"
20
+ },
13
21
  {
14
22
  "impact": "patch",
15
23
  "reason": "Appears in changelog section :house: Internal"
@@ -17,9 +25,81 @@
17
25
  ],
18
26
  "pkgJSONPath": "./package.json"
19
27
  },
28
+ "@nullvoxpopuli/ember-build-tooling-utils": {
29
+ "impact": "minor",
30
+ "oldVersion": "1.0.0",
31
+ "newVersion": "1.1.0",
32
+ "tagName": "latest",
33
+ "constraints": [
34
+ {
35
+ "impact": "minor",
36
+ "reason": "Appears in changelog section :rocket: Enhancement"
37
+ },
38
+ {
39
+ "impact": "patch",
40
+ "reason": "Appears in changelog section :bug: Bug Fix"
41
+ },
42
+ {
43
+ "impact": "patch",
44
+ "reason": "Appears in changelog section :memo: Documentation"
45
+ }
46
+ ],
47
+ "pkgJSONPath": "./packages/build-tooling-utils/package.json"
48
+ },
49
+ "@nullvoxpopuli/ember-rolldown": {
50
+ "impact": "minor",
51
+ "oldVersion": "1.0.0",
52
+ "newVersion": "1.1.0",
53
+ "tagName": "latest",
54
+ "constraints": [
55
+ {
56
+ "impact": "patch",
57
+ "reason": "Has dependency `workspace:*` on @nullvoxpopuli/ember-build-tooling-utils"
58
+ },
59
+ {
60
+ "impact": "minor",
61
+ "reason": "Appears in changelog section :rocket: Enhancement"
62
+ },
63
+ {
64
+ "impact": "patch",
65
+ "reason": "Appears in changelog section :bug: Bug Fix"
66
+ },
67
+ {
68
+ "impact": "patch",
69
+ "reason": "Appears in changelog section :memo: Documentation"
70
+ },
71
+ {
72
+ "impact": "patch",
73
+ "reason": "Appears in changelog section :house: Internal"
74
+ }
75
+ ],
76
+ "pkgJSONPath": "./packages/rolldown/package.json"
77
+ },
20
78
  "@nullvoxpopuli/ember-vite": {
21
- "oldVersion": "1.0.3"
79
+ "impact": "minor",
80
+ "oldVersion": "1.0.3",
81
+ "newVersion": "1.1.0",
82
+ "tagName": "latest",
83
+ "constraints": [
84
+ {
85
+ "impact": "minor",
86
+ "reason": "Appears in changelog section :rocket: Enhancement"
87
+ },
88
+ {
89
+ "impact": "patch",
90
+ "reason": "Has dependency `workspace:*` on @nullvoxpopuli/ember-build-tooling-utils"
91
+ },
92
+ {
93
+ "impact": "patch",
94
+ "reason": "Appears in changelog section :bug: Bug Fix"
95
+ },
96
+ {
97
+ "impact": "patch",
98
+ "reason": "Appears in changelog section :house: Internal"
99
+ }
100
+ ],
101
+ "pkgJSONPath": "./packages/vite/package.json"
22
102
  }
23
103
  },
24
- "description": "## Release (2026-06-29)\n\n* ember.nvp 0.5.0 (minor)\n\n#### :rocket: Enhancement\n* `ember.nvp`\n * [#55](https://github.com/NullVoxPopuli/ember.nvp/pull/55) Upgrade @clack/prompts ([@tcjr](https://github.com/tcjr))\n\n#### :house: Internal\n* `ember.nvp`\n * [#53](https://github.com/NullVoxPopuli/ember.nvp/pull/53) Add typescript to root ([@tcjr](https://github.com/tcjr))\n\n#### Committers: 1\n- Tom Carter ([@tcjr](https://github.com/tcjr))\n"
104
+ "description": "## Release (2026-07-17)\n\n* ember.nvp 0.7.0 (minor)\n* @nullvoxpopuli/ember-build-tooling-utils 1.1.0 (minor)\n* @nullvoxpopuli/ember-rolldown 1.1.0 (minor)\n* @nullvoxpopuli/ember-vite 1.1.0 (minor)\n\n#### :rocket: Enhancement\n* `ember.nvp`\n * [#78](https://github.com/NullVoxPopuli/ember.nvp/pull/78) publint and are-the-types-wrong layers ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#77](https://github.com/NullVoxPopuli/ember.nvp/pull/77) TypeScript is the default for libraries ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#73](https://github.com/NullVoxPopuli/ember.nvp/pull/73) Implement the vitest layer for libraries ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#71](https://github.com/NullVoxPopuli/ember.nvp/pull/71) Add inspector-support layer (Ember Inspector wiring via ember-estree codemod) ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n* `@nullvoxpopuli/ember-vite`, `ember.nvp`\n * [#72](https://github.com/NullVoxPopuli/ember.nvp/pull/72) qunit layer: support libraries (tests without any index.html) ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#61](https://github.com/NullVoxPopuli/ember.nvp/pull/61) Update maybeBabel ([@NullVoxPopuli](https://github.com/NullVoxPopuli))\n* `ember.nvp`, `@nullvoxpopuli/ember-build-tooling-utils`, `@nullvoxpopuli/ember-rolldown`, `@nullvoxpopuli/ember-vite`\n * [#63](https://github.com/NullVoxPopuli/ember.nvp/pull/63) Library support via rolldown ([@NullVoxPopuli](https://github.com/NullVoxPopuli))\n * [#66](https://github.com/NullVoxPopuli/ember.nvp/pull/66) Lean on node 24: RegExp.escape, import.meta.dirname, fs.rm ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n* `@nullvoxpopuli/ember-rolldown`, `ember.nvp`\n * [#67](https://github.com/NullVoxPopuli/ember.nvp/pull/67) Export our own defineConfig, preloaded with ember library defaults ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#64](https://github.com/NullVoxPopuli/ember.nvp/pull/64) Make both library flavors build: isolated declarations, hbs targetFormat, real tests ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n* `@nullvoxpopuli/ember-build-tooling-utils`, `@nullvoxpopuli/ember-rolldown`, `ember.nvp`\n * [#65](https://github.com/NullVoxPopuli/ember.nvp/pull/65) No generated babel.config.js; drop the plugin packages' dist build ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n* `@nullvoxpopuli/ember-vite`\n * [#62](https://github.com/NullVoxPopuli/ember.nvp/pull/62) Cleanup maybeBabel ([@NullVoxPopuli](https://github.com/NullVoxPopuli))\n\n#### :bug: Bug Fix\n* `@nullvoxpopuli/ember-build-tooling-utils`, `@nullvoxpopuli/ember-rolldown`, `ember.nvp`\n * [#83](https://github.com/NullVoxPopuli/ember.nvp/pull/83) Can't publish TS, build with rolldown ([@NullVoxPopuli](https://github.com/NullVoxPopuli))\n* `@nullvoxpopuli/ember-rolldown`\n * [#82](https://github.com/NullVoxPopuli/ember.nvp/pull/82) Fix watching in the rolldown plugin ([@NullVoxPopuli](https://github.com/NullVoxPopuli))\n* `ember.nvp`\n * [#79](https://github.com/NullVoxPopuli/ember.nvp/pull/79) The library base generates an empty src ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#74](https://github.com/NullVoxPopuli/ember.nvp/pull/74) Generated TS libraries type check: drop allowJs, load ember/glint types ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#70](https://github.com/NullVoxPopuli/ember.nvp/pull/70) Library template is private; generated libraries are publishable ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#58](https://github.com/NullVoxPopuli/ember.nvp/pull/58) Fix lint:types in generated qunit apps: import #app/app.ts with extension ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#59](https://github.com/NullVoxPopuli/ember.nvp/pull/59) Make freshly generated apps pass their own pnpm lint ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n* `@nullvoxpopuli/ember-vite`, `ember.nvp`\n * [#75](https://github.com/NullVoxPopuli/ember.nvp/pull/75) packages/vite: built-in babel fallback when no config file exists ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n\n#### :memo: Documentation\n* `@nullvoxpopuli/ember-build-tooling-utils`, `@nullvoxpopuli/ember-rolldown`, `ember.nvp`\n * [#68](https://github.com/NullVoxPopuli/ember.nvp/pull/68) Docs pass: outcome-focused, timeless READMEs and comments ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n\n#### :house: Internal\n* `ember.nvp`\n * [#81](https://github.com/NullVoxPopuli/ember.nvp/pull/81) Split the app permutations CI slice in two ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#80](https://github.com/NullVoxPopuli/ember.nvp/pull/80) Split the app and library permutations CI slices in two ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#76](https://github.com/NullVoxPopuli/ember.nvp/pull/76) Shard CI: per-base permutation files, parallel Node Tests jobs ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n* `@nullvoxpopuli/ember-rolldown`, `@nullvoxpopuli/ember-vite`, `ember.nvp`\n * [#69](https://github.com/NullVoxPopuli/ember.nvp/pull/69) Remove comments from config files (generated and in READMEs) ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n\n#### Committers: 2\n- @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n- [@NullVoxPopuli](https://github.com/NullVoxPopuli)\n"
25
105
  }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,73 @@
1
1
  # Changelog
2
2
 
3
+ ## Release (2026-07-17)
4
+
5
+ * ember.nvp 0.7.0 (minor)
6
+ * @nullvoxpopuli/ember-build-tooling-utils 1.1.0 (minor)
7
+ * @nullvoxpopuli/ember-rolldown 1.1.0 (minor)
8
+ * @nullvoxpopuli/ember-vite 1.1.0 (minor)
9
+
10
+ #### :rocket: Enhancement
11
+ * `ember.nvp`
12
+ * [#78](https://github.com/NullVoxPopuli/ember.nvp/pull/78) publint and are-the-types-wrong layers ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
13
+ * [#77](https://github.com/NullVoxPopuli/ember.nvp/pull/77) TypeScript is the default for libraries ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
14
+ * [#73](https://github.com/NullVoxPopuli/ember.nvp/pull/73) Implement the vitest layer for libraries ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
15
+ * [#71](https://github.com/NullVoxPopuli/ember.nvp/pull/71) Add inspector-support layer (Ember Inspector wiring via ember-estree codemod) ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
16
+ * `@nullvoxpopuli/ember-vite`, `ember.nvp`
17
+ * [#72](https://github.com/NullVoxPopuli/ember.nvp/pull/72) qunit layer: support libraries (tests without any index.html) ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
18
+ * [#61](https://github.com/NullVoxPopuli/ember.nvp/pull/61) Update maybeBabel ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
19
+ * `ember.nvp`, `@nullvoxpopuli/ember-build-tooling-utils`, `@nullvoxpopuli/ember-rolldown`, `@nullvoxpopuli/ember-vite`
20
+ * [#63](https://github.com/NullVoxPopuli/ember.nvp/pull/63) Library support via rolldown ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
21
+ * [#66](https://github.com/NullVoxPopuli/ember.nvp/pull/66) Lean on node 24: RegExp.escape, import.meta.dirname, fs.rm ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
22
+ * `@nullvoxpopuli/ember-rolldown`, `ember.nvp`
23
+ * [#67](https://github.com/NullVoxPopuli/ember.nvp/pull/67) Export our own defineConfig, preloaded with ember library defaults ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
24
+ * [#64](https://github.com/NullVoxPopuli/ember.nvp/pull/64) Make both library flavors build: isolated declarations, hbs targetFormat, real tests ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
25
+ * `@nullvoxpopuli/ember-build-tooling-utils`, `@nullvoxpopuli/ember-rolldown`, `ember.nvp`
26
+ * [#65](https://github.com/NullVoxPopuli/ember.nvp/pull/65) No generated babel.config.js; drop the plugin packages' dist build ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
27
+ * `@nullvoxpopuli/ember-vite`
28
+ * [#62](https://github.com/NullVoxPopuli/ember.nvp/pull/62) Cleanup maybeBabel ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
29
+
30
+ #### :bug: Bug Fix
31
+ * `@nullvoxpopuli/ember-build-tooling-utils`, `@nullvoxpopuli/ember-rolldown`, `ember.nvp`
32
+ * [#83](https://github.com/NullVoxPopuli/ember.nvp/pull/83) Can't publish TS, build with rolldown ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
33
+ * `@nullvoxpopuli/ember-rolldown`
34
+ * [#82](https://github.com/NullVoxPopuli/ember.nvp/pull/82) Fix watching in the rolldown plugin ([@NullVoxPopuli](https://github.com/NullVoxPopuli))
35
+ * `ember.nvp`
36
+ * [#79](https://github.com/NullVoxPopuli/ember.nvp/pull/79) The library base generates an empty src ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
37
+ * [#74](https://github.com/NullVoxPopuli/ember.nvp/pull/74) Generated TS libraries type check: drop allowJs, load ember/glint types ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
38
+ * [#70](https://github.com/NullVoxPopuli/ember.nvp/pull/70) Library template is private; generated libraries are publishable ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
39
+ * [#58](https://github.com/NullVoxPopuli/ember.nvp/pull/58) Fix lint:types in generated qunit apps: import #app/app.ts with extension ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
40
+ * [#59](https://github.com/NullVoxPopuli/ember.nvp/pull/59) Make freshly generated apps pass their own pnpm lint ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
41
+ * `@nullvoxpopuli/ember-vite`, `ember.nvp`
42
+ * [#75](https://github.com/NullVoxPopuli/ember.nvp/pull/75) packages/vite: built-in babel fallback when no config file exists ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
43
+
44
+ #### :memo: Documentation
45
+ * `@nullvoxpopuli/ember-build-tooling-utils`, `@nullvoxpopuli/ember-rolldown`, `ember.nvp`
46
+ * [#68](https://github.com/NullVoxPopuli/ember.nvp/pull/68) Docs pass: outcome-focused, timeless READMEs and comments ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
47
+
48
+ #### :house: Internal
49
+ * `ember.nvp`
50
+ * [#81](https://github.com/NullVoxPopuli/ember.nvp/pull/81) Split the app permutations CI slice in two ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
51
+ * [#80](https://github.com/NullVoxPopuli/ember.nvp/pull/80) Split the app and library permutations CI slices in two ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
52
+ * [#76](https://github.com/NullVoxPopuli/ember.nvp/pull/76) Shard CI: per-base permutation files, parallel Node Tests jobs ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
53
+ * `@nullvoxpopuli/ember-rolldown`, `@nullvoxpopuli/ember-vite`, `ember.nvp`
54
+ * [#69](https://github.com/NullVoxPopuli/ember.nvp/pull/69) Remove comments from config files (generated and in READMEs) ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
55
+
56
+ #### Committers: 2
57
+ - @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
58
+ - [@NullVoxPopuli](https://github.com/NullVoxPopuli)
59
+
60
+ ## Release (2026-07-04)
61
+
62
+ * ember.nvp 0.6.0 (minor)
63
+
64
+ #### :rocket: Enhancement
65
+ * `ember.nvp`
66
+ * [#56](https://github.com/NullVoxPopuli/ember.nvp/pull/56) Stage all generation in a copy-on-write overlay; confirm before writing ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
67
+
68
+ #### Committers: 1
69
+ - @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
70
+
3
71
  ## Release (2026-06-29)
4
72
 
5
73
  * ember.nvp 0.5.0 (minor)
package/README.md CHANGED
@@ -30,6 +30,18 @@ pnpm dlx NullVoxPopuli/ember.nvp
30
30
  npx NullVoxPopuli/ember.nvp
31
31
  ```
32
32
 
33
+ ### Updating an existing project
34
+
35
+ You can run `ember.nvp` on top of an existing project to add layers to it. Generation never writes directly to your project -- everything runs in a staging directory first, and before finishing you choose to:
36
+
37
+ - **write the files** -- apply all of the staged changes
38
+ - **review the diff** -- step through each changed file's diff, accepting or rejecting it individually (or accept/reject everything remaining)
39
+ - **cancel** -- discard everything; your project is untouched
40
+
41
+ Only files that actually changed are written. `node_modules`, `.git`, and everything else in your project are left alone, and the accepted changes land as uncommitted edits for you to review with your own git tooling.
42
+
43
+ New projects skip the confirmation and are written as soon as generation succeeds. For scripting, `--write yes` / `--write no` answers the confirmation up front.
44
+
33
45
  ### Wrapping
34
46
 
35
47
  The provided CLI is only a wrapper around our exported `generateProject` function.
@@ -55,6 +67,23 @@ await generateProject(new Project(
55
67
 
56
68
  All parts of the generator are idempotent, so running generators on existing projects _can_ no-op.
57
69
 
70
+ To get the same don't-write-until-confirmed behavior as the CLI, wrap generation in a `Stage`. A stage is a real directory in the OS temp dir, seeded with a copy of the target directory's current contents (sans `node_modules`/`.git`) -- so layers run against the project's existing state with plain `node:fs`, `ember-apply`, and subprocesses, and authoring a layer is exactly the same with or without one.
71
+
72
+ ```js
73
+ import { generateProject, Project, Stage } from "ember.nvp";
74
+
75
+ const stage = await Stage.create(directoryToGenerateIn);
76
+
77
+ await generateProject(new Project(stage.directory, { name, path, type, layers, packageManager }));
78
+
79
+ const changes = await stage.changes(); // [{ path, status: 'added' | 'modified' | 'deleted' }]
80
+ console.log(await stage.diff(changes)); // unified diff against the target directory
81
+
82
+ await stage.commit(); // write the changes to the target directory
83
+ // or: await stage.commit(someOfTheChanges) -- write only an accepted subset
84
+ // or: await stage.discard() -- throw everything away, touching nothing
85
+ ```
86
+
58
87
  ## Reqs
59
88
 
60
89
  - node 24+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember.nvp",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "ember project generator, alternative to the official blueprints, but more modern -- hopefully one day to upstream back in to ember-cli / official blueprints",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,7 +22,10 @@
22
22
  "@clack/prompts": "^1.6.0",
23
23
  "babel-remove-types": "^2.0.0",
24
24
  "content-tag": "^4.2.0",
25
+ "diff": "^9.0.0",
25
26
  "ember-apply": "^2.17.0",
27
+ "ember-estree": "^0.7.0",
28
+ "enhanced-resolve": "^5.24.2",
26
29
  "execa": "^9.6.1",
27
30
  "latest-version": "^9.0.0",
28
31
  "package-name-regex": "^5.0.0"
@@ -38,7 +41,6 @@
38
41
  "prettier": "^3.8.3",
39
42
  "prettier-plugin-ember-template-tag": "^2.1.6",
40
43
  "release-plan": "^0.18.0",
41
- "rimraf": "^6.1.3",
42
44
  "typescript": "^5.9.3",
43
45
  "vite": "^8.0.14",
44
46
  "vitest": "^4.1.7"
@@ -0,0 +1,37 @@
1
+ # `@nullvoxpopuli/ember-build-tooling-utils`
2
+
3
+ Shared build tooling for the `@nullvoxpopuli` ember meta-plugins
4
+ ([`@nullvoxpopuli/ember-vite`](../vite) and
5
+ [`@nullvoxpopuli/ember-rolldown`](../rolldown)).
6
+
7
+ It's intentionally bundler-agnostic (no `vite`/`rolldown` dependency) so both
8
+ meta-plugins can consume the same logic.
9
+
10
+ Requires node 24+, and — since this package ships TypeScript source — a
11
+ modern TypeScript when type-checking: 6+ with `lib` covering `es2025` (e.g.
12
+ `esnext`).
13
+
14
+ ## Exports
15
+
16
+ ### `maybeBabel(options?)`
17
+
18
+ A babel plugin (`@rollup/plugin-babel`) wrapped in a
19
+ [`@rolldown/pluginutils`](https://github.com/rolldown/rolldown) filter so babel
20
+ runs **only** on files that actually need it:
21
+
22
+ - template-tag files (`.gts`/`.gjs`)
23
+ - files importing template/macro modules (`@ember/template-compiler`,
24
+ `@embroider/macros`, …)
25
+ - local (non-`node_modules`) code using decorators
26
+
27
+ Everything else skips babel and is handled by the bundler's native (oxc)
28
+ transform. The point: don't buy rolldown/oxc speed and then give it all back by
29
+ routing every module through babel.
30
+
31
+ `options` extends `@rollup/plugin-babel`'s options, plus a `filter.include`
32
+ (`imports` / `code`) to opt additional files in.
33
+
34
+ ### `extensions`
35
+
36
+ The list of file extensions ember source can be authored in. Mirrors
37
+ embroider's canonical list (see `extensions.ts` for details).
@@ -0,0 +1,2 @@
1
+ export { maybeBabel } from "./src/maybe-babel.ts";
2
+ export { extensions } from "./src/extensions.ts";
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@nullvoxpopuli/ember-build-tooling-utils",
3
+ "version": "1.1.0",
4
+ "description": "Shared build tooling for @nullvoxpopuli ember meta-plugins: maybeBabel (run babel only where needed) and the ember file extensions list.",
5
+ "type": "module",
6
+ "main": "index.ts",
7
+ "publishConfig": {
8
+ "main": "dist/index.js"
9
+ },
10
+ "keywords": [
11
+ "ember",
12
+ "babel",
13
+ "rolldown",
14
+ "vite",
15
+ "meta plugin",
16
+ "build"
17
+ ],
18
+ "author": "NullVoxPopuli",
19
+ "license": "MIT",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/NullVoxPopuli/ember.nvp",
23
+ "directory": "packages/build-tooling-utils"
24
+ },
25
+ "scripts": {
26
+ "build": "rolldown --config",
27
+ "prepack": "pnpm build"
28
+ },
29
+ "packageManager": "pnpm@10.28.0",
30
+ "dependencies": {
31
+ "@babel/core": "^7.28.10",
32
+ "@rolldown/pluginutils": "^1.0.1",
33
+ "@rollup/plugin-babel": "^7.1.0"
34
+ },
35
+ "engines": {
36
+ "node": ">= 24"
37
+ },
38
+ "devDependencies": {
39
+ "rolldown": "^1.1.2",
40
+ "typescript": "^6.0.3"
41
+ }
42
+ }
@@ -0,0 +1,18 @@
1
+ import { defineConfig } from "rolldown";
2
+
3
+ import manifest from "./package.json" with { type: "json" };
4
+
5
+ const deps = Object.keys(manifest.dependencies || {});
6
+
7
+ export default defineConfig({
8
+ input: "index.ts",
9
+ output: {
10
+ format: "esm",
11
+ file: "dist/index.js",
12
+ codeSplitting: false,
13
+ },
14
+ external(id) {
15
+ if (id.startsWith("node:")) return true;
16
+ if (deps.some((dep) => id.startsWith(dep))) return true;
17
+ },
18
+ });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * The file extensions ember source can be authored in.
3
+ *
4
+ * Mirrors embroider's canonical list, which is only exported from
5
+ * `@embroider/vite` (its `src/ember.ts`) — copied rather than depended on so
6
+ * this shared tooling stays bundler-agnostic. Keep in sync.
7
+ */
8
+ export const extensions = [
9
+ ".mjs",
10
+ ".gjs",
11
+ ".js",
12
+ ".mts",
13
+ ".gts",
14
+ ".ts",
15
+ ".hbs",
16
+ ".hbs.js",
17
+ ".json",
18
+ ];
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Reasons we have to have babel
3
+ * - decorators
4
+ * - ember-concurrency
5
+ * - scoped-css
6
+ * - template compilation optimization
7
+ *
8
+ * `maybeBabel` runs babel ONLY on the files that actually need it (template-tag
9
+ * files, files importing template/macro modules, and local code using
10
+ * decorators). Everything else skips babel so the bundler's native (oxc)
11
+ * transform handles it -- the whole point being to keep the fast path fast and
12
+ * not re-slow the build by sending every file through babel.
13
+ *
14
+ * This is shared between the vite (app) and rolldown/tsdown (library)
15
+ * meta-plugins.
16
+ */
17
+ import { and, code, id, include, not, or } from "@rolldown/pluginutils";
18
+ import { babel } from "@rollup/plugin-babel";
19
+ import type { RollupBabelInputPluginOptions } from "@rollup/plugin-babel";
20
+
21
+ import { extensions } from "./extensions.ts";
22
+
23
+ /**
24
+ * If a file imports any of these, it needs babel (templates, macros, and a
25
+ * couple of addons that ship decorator-adjacent runtime code).
26
+ */
27
+ const babelRequiredImports = [
28
+ // Templates
29
+ // (old non template() form)
30
+ "@ember/template-compiler",
31
+ "@ember/template-compilation",
32
+
33
+ // Legacy templates (hbs / loose mode)
34
+ "ember-cli-htmlbars",
35
+ "ember-cli-htmlbars-inline-precompile",
36
+ "htmlbars-inline-precompile",
37
+
38
+ // Build Macros
39
+ // (since import.meta.env is not available in all environments)
40
+ "@embroider/macros",
41
+ "@glimmer/env",
42
+ "@ember/debug",
43
+ "@ember/application/deprecations",
44
+ ];
45
+
46
+ const decoratorRegex = /(?<![\w'"`])(?<!\*\s+)(?<!\/\/[^\n]*)(?<!\/\*[^\n]*)@\w+/;
47
+ // └────┬─────┘└───┬───┘└──────┬──────┘└──────┬──────┘└┬─┘
48
+ // │ │ │ │ │
49
+ // │ │ │ │ └── the `@decorator`
50
+ // │ │ │ └──────── not inside a single-line block comment (`/* @dec */`)
51
+ // │ │ └─────────────────────── not on a `//` line comment
52
+ // │ └─────────────────────────────────── not a JSDoc tag, even with multiple spaces (`* @param`)
53
+ // └────────────────────────────────────────────── not mid-identifier or inside a string
54
+
55
+ const nodeModulesPattern = /\/node_modules\//;
56
+
57
+ const extensionRegExp = new RegExp(
58
+ `(${extensions
59
+ .filter((ext) => ext !== ".json")
60
+ .map(RegExp.escape)
61
+ .join("|")})(\\?.*)?(#.*)?$`,
62
+ );
63
+
64
+ type Options = Omit<RollupBabelInputPluginOptions, "filter"> & {
65
+ filter?: {
66
+ include: {
67
+ /**
68
+ * If any additional (custom) plugins are provided, a pattern
69
+ * should be provided that detects their usage
70
+ *
71
+ * for example, to also run babel on files that import from ember-concurrency
72
+ * ```js
73
+ * {
74
+ * code: ['ember-concurrency'],
75
+ * }
76
+ * ```
77
+ */
78
+ imports: string[];
79
+ /**
80
+ * If any additional (custom) plugins are provided, a pattern
81
+ * should be provided that detects their usage
82
+ *
83
+ * for example, to also run babel on files that use polyfilled APIs,
84
+ * or use the "formatMessage" technique for translations
85
+ * ```js
86
+ * {
87
+ * code: ['myPolyfilledAPICall(', /\bintl\.formatMessage\b/],
88
+ * }
89
+ * ```
90
+ */
91
+ code: (string | RegExp)[];
92
+ };
93
+ };
94
+ };
95
+
96
+ export function maybeBabel(userOptions: Options = {}) {
97
+ const { filter, ...options } = userOptions;
98
+
99
+ const plugin = babel({
100
+ babelHelpers: "runtime",
101
+ extensions,
102
+ skipPreflightCheck: true,
103
+ ...options,
104
+ });
105
+
106
+ const importsRegex = new RegExp(
107
+ babelRequiredImports
108
+ .concat(filter?.include?.imports ?? [])
109
+ .map(RegExp.escape)
110
+ .join("|"),
111
+ );
112
+
113
+ const maybeBabelFilter = [
114
+ include(
115
+ and(
116
+ // is one of the babel-supported extensions
117
+ id(extensionRegExp),
118
+ or(
119
+ // always run gts and gjs through babel
120
+ id(/\.gts$/),
121
+ id(/\.gjs$/),
122
+ // imports one of the modules above
123
+ code(importsRegex),
124
+ // (a common way to do translations)
125
+ // local app code using a decorator
126
+ // NOTE: maybeBabel requires that all libraries compile away their decorators
127
+ //
128
+ // TODO: what do we do when native decorators start shipping?
129
+ // (ignore decorator transforming entirely?)
130
+ and(not(id(nodeModulesPattern)), code(decoratorRegex)),
131
+ // user provided additional opt-ins to the regex here
132
+ ...(filter?.include?.code?.map((x) => code(x)) ?? []),
133
+ ),
134
+ ),
135
+ ),
136
+ ];
137
+
138
+ // types are incorrect
139
+ (plugin.transform as { filter: unknown }).filter = maybeBabelFilter;
140
+
141
+ return {
142
+ ...plugin,
143
+ enforce: "pre",
144
+ name: "nullvoxpopuli:babel",
145
+ } as ReturnType<typeof babel>;
146
+ }