ember.nvp 1.6.0 → 1.7.1
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 +5 -1
- package/.release-plan.json +51 -9
- package/CHANGELOG.md +30 -0
- package/README.md +41 -28
- package/RELEASE.md +16 -14
- package/package.json +1 -1
- package/packages/build-tooling-utils/README.md +7 -12
- package/packages/build-tooling-utils/package.json +1 -1
- package/packages/build-tooling-utils/src/extensions.ts +4 -3
- package/packages/build-tooling-utils/src/maybe-babel.ts +18 -15
- package/packages/rolldown/README.md +138 -98
- package/packages/rolldown/index.ts +66 -28
- package/packages/rolldown/package.json +1 -1
- package/packages/rolldown/src/app-reexports.test.ts +4 -3
- package/packages/rolldown/src/app-reexports.ts +37 -27
- package/packages/rolldown/src/babel.test.ts +10 -5
- package/packages/rolldown/src/babel.ts +117 -72
- package/packages/rolldown/src/bundle.test.ts +149 -0
- package/packages/rolldown/src/bundle.ts +71 -0
- package/packages/rolldown/src/config.ts +42 -18
- package/packages/rolldown/src/dts.test.ts +42 -26
- package/packages/rolldown/src/externals.test.ts +16 -11
- package/packages/rolldown/src/externals.ts +45 -28
- package/packages/rolldown/src/isolated-declarations.ts +56 -43
- package/packages/rolldown/src/plugin-like.ts +18 -15
- package/packages/rolldown/src/published-types.test.ts +18 -13
- package/packages/rolldown/src/transform.test.ts +26 -17
- package/packages/rolldown/src/transform.ts +68 -48
- package/packages/vite/README.md +10 -11
- package/packages/vite/package.json +1 -1
- package/packages/vite/src/ember.ts +44 -36
- package/packages/vite/src/test-html.ts +13 -11
- package/src/bases/README.md +6 -3
- package/src/bases/minimal-app/app.test.ts +7 -4
- package/src/bases/minimal-app/files/app/app.ts +2 -3
- package/src/bases/minimal-app/files/app/router.ts +3 -3
- package/src/bases/minimal-app/index.js +10 -6
- package/src/bases/minimal-custom-element/files/README.md +95 -0
- package/src/bases/minimal-custom-element/files/package.json +47 -0
- package/src/bases/minimal-custom-element/files/src/components/counter.gts +27 -0
- package/src/bases/minimal-custom-element/files/src/element.ts +45 -0
- package/src/bases/minimal-custom-element/files/src/index.ts +2 -0
- package/src/bases/minimal-custom-element/files/src/register.ts +5 -0
- package/src/bases/minimal-custom-element/files/tsconfig.json +10 -0
- package/src/bases/minimal-custom-element/files/tsdown.config.js +7 -0
- package/src/bases/minimal-custom-element/index.js +212 -0
- package/src/bases/minimal-extension/files/README.md +13 -5
- package/src/bases/minimal-extension/files/app/app.ts +2 -3
- package/src/bases/minimal-extension/files/app/config.ts +5 -3
- package/src/bases/minimal-extension/index.js +16 -11
- package/src/bases/minimal-library/files/README.md +8 -7
- package/src/bases/minimal-library/index.js +21 -15
- package/src/cli/args.js +1 -1
- package/src/cli/generator.js +4 -0
- package/src/cli/index.js +23 -18
- package/src/cli/questions/project-type.js +17 -2
- package/src/cli/questions/write.js +12 -10
- package/src/layers/are-the-types-wrong/index.js +6 -6
- package/src/layers/github-actions/index.js +7 -4
- package/src/layers/inspector-support/index.js +2 -2
- package/src/layers/publint/index.js +2 -2
- package/src/layers/qunit/index.js +21 -17
- package/src/layers/readme/index.js +55 -4
- package/src/layers/renovate/index.js +5 -5
- package/src/layers/typescript/index.js +20 -13
- package/src/layers/vitest/index.js +8 -4
- package/src/utils/babel.js +3 -2
- package/src/utils/buffer.js +2 -2
- package/src/utils/cli.js +4 -4
- package/src/utils/fs.js +23 -14
- package/src/utils/inspector-support.js +22 -19
- package/src/utils/npm.js +15 -12
- package/src/utils/project-type.js +19 -0
- package/src/utils/project.js +14 -2
- package/src/utils/remove-types.js +2 -4
- package/src/utils/rewrite-imports.js +43 -31
- package/src/utils/stage.js +44 -33
- package/src/utils/tsdown-config.js +6 -5
- package/src/utils/types.ts +10 -9
- package/test/ember.nvp/base-custom-element.test.ts +440 -0
- package/test/ember.nvp/base-library.test.ts +6 -4
- package/test/ember.nvp/generator-replace-or-update.test.ts +6 -4
- package/test/ember.nvp/inspector-support.test.ts +4 -3
- package/test/ember.nvp/layer-inspector-support.test.ts +2 -3
- package/test/ember.nvp/layer-publish-checks.test.ts +2 -3
- package/test/ember.nvp/layer-qunit.test.ts +2 -2
- package/test/ember.nvp/layer-readme.test.ts +63 -3
- package/test/ember.nvp/layer-vitest.test.ts +16 -9
- package/test/ember.nvp/layers-load.test.ts +26 -0
- package/test/ember.nvp/library-src-fixtures.ts +7 -3
- package/test/ember.nvp/permutations/custom-element-1.test.ts +3 -0
- package/test/ember.nvp/permutations/custom-element-2.test.ts +3 -0
- package/test/ember.nvp/permutations/custom-element-checks.test.ts +3 -0
- package/test/ember.nvp/permutations/shared.ts +22 -12
- package/test/ember.nvp/publish-configs.test.ts +44 -31
- package/test/ember.nvp/rewrite-imports.test.ts +4 -3
- package/test/ember.nvp/scoped-css-library.test.ts +6 -4
- package/test/test-helpers/permutate.test.ts +3 -3
- package/test/test-helpers.ts +12 -7
package/src/utils/fs.js
CHANGED
|
@@ -7,17 +7,22 @@ import { rewriteImportsToMatchFiles } from "./rewrite-imports.js";
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* `**\/*` alone skips dotfiles, which bases legitimately provide
|
|
10
|
-
* (`.gitignore`, `.env.development`, ...)
|
|
11
|
-
*
|
|
10
|
+
* (`.gitignore`, `.env.development`, ...).
|
|
11
|
+
*
|
|
12
|
+
* The extra patterns match:
|
|
13
|
+
* - dotfiles at any depth
|
|
14
|
+
* - files inside dot-directories (`.github/...`)
|
|
12
15
|
*/
|
|
13
16
|
const EVERY_FILE = ["**/*", "**/.*", "**/.*/**/*"];
|
|
14
17
|
|
|
15
18
|
/**
|
|
19
|
+
* Modified version of applyFolder from ember-apply.
|
|
16
20
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
21
|
+
* Here, the caller decides the final output path and contents.
|
|
22
|
+
* The version from ember-apply only allows the same file paths
|
|
23
|
+
* in source and destination.
|
|
19
24
|
*
|
|
20
|
-
* We need
|
|
25
|
+
* We need that freedom because we intake ts and output js.
|
|
21
26
|
*
|
|
22
27
|
* @param {string} from
|
|
23
28
|
* @param {{to: import('#utils/project.js').Project, process: (data: { entry: string, contents: string }) => string | Promise<void>}} options sub folder within the target project to copy the contents to
|
|
@@ -56,11 +61,12 @@ export async function applyFolderTo(from, project) {
|
|
|
56
61
|
/**
|
|
57
62
|
* Files we wrote, to be import-rewritten in a second pass.
|
|
58
63
|
*
|
|
59
|
-
* Rewriting has to wait until every file is on disk
|
|
60
|
-
* file a specifier actually resolves to,
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
+
* Rewriting has to wait until every file is on disk.
|
|
65
|
+
* It checks which file a specifier actually resolves to,
|
|
66
|
+
* so rewriting a half-applied folder depends on iteration order.
|
|
67
|
+
*
|
|
68
|
+
* For example: `index.js` pointing at `./utils/math.ts` before `math.js` exists
|
|
69
|
+
* would be left alone, emitting a broken import.
|
|
64
70
|
*
|
|
65
71
|
* @type {string[]}
|
|
66
72
|
*/
|
|
@@ -110,10 +116,13 @@ export async function applyFolderTo(from, project) {
|
|
|
110
116
|
});
|
|
111
117
|
|
|
112
118
|
/**
|
|
113
|
-
* Imports must always match the emitted files
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
119
|
+
* Imports must always match the emitted files, and carry their extension.
|
|
120
|
+
*
|
|
121
|
+
* The util decides which files it applies to.
|
|
122
|
+
* It is a no-op for everything else.
|
|
123
|
+
*
|
|
124
|
+
* Files that already existed are the user's.
|
|
125
|
+
* We leave them alone.
|
|
117
126
|
*/
|
|
118
127
|
for (let filePath of written) {
|
|
119
128
|
let contents = await readFile(filePath, "utf-8");
|
|
@@ -9,9 +9,9 @@ export const INSPECTOR_PACKAGE = "@embroider/legacy-inspector-support";
|
|
|
9
9
|
export const INSPECTOR_ENTRY = `${INSPECTOR_PACKAGE}/ember-source-4.12`;
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
|
-
* Modules whose default export is the Application (sub)class an app
|
|
13
|
-
*
|
|
14
|
-
* gets attached.
|
|
12
|
+
* Modules whose default export is the Application (sub)class an app extends.
|
|
13
|
+
*
|
|
14
|
+
* The class extending one of these is where inspector support gets attached.
|
|
15
15
|
*/
|
|
16
16
|
const APPLICATION_MODULES = new Set(["ember-strict-application-resolver", "@ember/application"]);
|
|
17
17
|
|
|
@@ -56,10 +56,11 @@ function classMembers(classNode) {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
|
-
* Everything wiring and detection need to know about the module:
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
59
|
+
* Everything wiring and detection need to know about the module:
|
|
60
|
+
* - its import declarations
|
|
61
|
+
* - the import from {@link INSPECTOR_PACKAGE} (any subpath)
|
|
62
|
+
* - the class extending an Application import
|
|
63
|
+
* - that class's `inspector` member
|
|
63
64
|
*
|
|
64
65
|
* @param {string} code
|
|
65
66
|
* @param {string} filePath
|
|
@@ -117,9 +118,9 @@ function analyze(code, filePath) {
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
/**
|
|
120
|
-
* Whether the module already wires inspector support:
|
|
121
|
-
* {@link INSPECTOR_PACKAGE}
|
|
122
|
-
* `inspector` member
|
|
121
|
+
* Whether the module already wires inspector support:
|
|
122
|
+
* - it imports {@link INSPECTOR_PACKAGE}
|
|
123
|
+
* - its Application class carries the `inspector` member
|
|
123
124
|
*
|
|
124
125
|
* @param {string} code
|
|
125
126
|
* @param {string} filePath
|
|
@@ -143,16 +144,18 @@ export function hasInspectorSupport(code, filePath) {
|
|
|
143
144
|
* }
|
|
144
145
|
* ```
|
|
145
146
|
*
|
|
146
|
-
* The Application definition is found structurally
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
147
|
+
* The Application definition is found structurally:
|
|
148
|
+
* any class extending a binding imported from an Application-providing module.
|
|
149
|
+
* - declaration or expression
|
|
150
|
+
* - named, anonymous, or assigned to a variable
|
|
151
|
+
*
|
|
152
|
+
* So user-restructured app files are wired the same as freshly generated ones.
|
|
153
|
+
*
|
|
154
|
+
* Idempotent.
|
|
155
|
+
* Already-wired (or partially wired) modules keep their existing import name and member.
|
|
151
156
|
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
* there is nothing to do -- including when no Application definition
|
|
155
|
-
* can be found.
|
|
157
|
+
* Returns the code unchanged when there is nothing to do,
|
|
158
|
+
* including when no Application definition can be found.
|
|
156
159
|
*
|
|
157
160
|
* @param {string} code
|
|
158
161
|
* @param {string} filePath
|
package/src/utils/npm.js
CHANGED
|
@@ -8,9 +8,10 @@ import { join, resolve } from "node:path";
|
|
|
8
8
|
const CACHE = {};
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Local, not-yet-published workspace packages
|
|
12
|
-
* than resolved from the registry.
|
|
13
|
-
*
|
|
11
|
+
* Local, not-yet-published workspace packages.
|
|
12
|
+
* These must be `link:`ed rather than resolved from the registry.
|
|
13
|
+
*
|
|
14
|
+
* Maps the published package name to its folder under `packages/`.
|
|
14
15
|
*
|
|
15
16
|
* @type {{ [name: string]: string }}
|
|
16
17
|
*/
|
|
@@ -37,16 +38,18 @@ export async function getLatest(deps) {
|
|
|
37
38
|
|
|
38
39
|
/**
|
|
39
40
|
* HACK FOR CI.
|
|
40
|
-
* In practice, these packages will be published separately, and those
|
|
41
|
-
* versions will be used. Only the local, not-yet-published
|
|
42
|
-
* @nullvoxpopuli/ember-* packages need the link; every other dependency
|
|
43
|
-
* must still resolve its real version.
|
|
44
41
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
42
|
+
* In practice, these packages will be published separately,
|
|
43
|
+
* and those versions will be used.
|
|
44
|
+
*
|
|
45
|
+
* Only the local, not-yet-published @nullvoxpopuli/ember-* packages need the link.
|
|
46
|
+
* Every other dependency must still resolve its real version.
|
|
47
|
+
*
|
|
48
|
+
* We use `link:` (a symlink) rather than `file:` (a copy into the store) on purpose:
|
|
49
|
+
* - the packages ship TypeScript source
|
|
50
|
+
* - Node 24 refuses to strip types for files physically located under node_modules
|
|
51
|
+
* - a symlink makes Node resolve the realpath to packages/* (outside node_modules),
|
|
52
|
+
* so its `.ts` runs directly
|
|
50
53
|
*/
|
|
51
54
|
if (needsLocalLink && LOCAL_PACKAGES[dep]) {
|
|
52
55
|
version =
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Libraries and custom elements share one toolchain:
|
|
3
|
+
* - built with tsdown
|
|
4
|
+
* - no babel config
|
|
5
|
+
* - published to npm
|
|
6
|
+
*
|
|
7
|
+
* Layers that branch on "is this a library?" must treat both the same.
|
|
8
|
+
*
|
|
9
|
+
* This module has no imports on purpose.
|
|
10
|
+
* Layers are loaded by `#layers`, and `#utils/project.js` imports `#layers`.
|
|
11
|
+
* A layer that imports `#utils/project.js` at the top level
|
|
12
|
+
* creates a cycle that never settles, and Node exits with code 13.
|
|
13
|
+
*
|
|
14
|
+
* @param {import('#types').ProjectType} type
|
|
15
|
+
* @returns {boolean}
|
|
16
|
+
*/
|
|
17
|
+
export function isLibraryType(type) {
|
|
18
|
+
return type === "library" || type === "custom-element";
|
|
19
|
+
}
|
package/src/utils/project.js
CHANGED
|
@@ -5,12 +5,15 @@ import { join } from "node:path";
|
|
|
5
5
|
import { hasGit } from "#utils/git.js";
|
|
6
6
|
import { assert } from "node:console";
|
|
7
7
|
import { layers, layerNames } from "#layers";
|
|
8
|
+
import { isLibraryType } from "#utils/project-type.js";
|
|
9
|
+
|
|
10
|
+
export { isLibraryType };
|
|
8
11
|
|
|
9
12
|
/**
|
|
10
13
|
* State container for the project.
|
|
11
14
|
*
|
|
12
|
-
* May eventually include information for discovering
|
|
13
|
-
* about a project.
|
|
15
|
+
* May eventually include information for discovering
|
|
16
|
+
* existing state about a project.
|
|
14
17
|
*/
|
|
15
18
|
export class Project {
|
|
16
19
|
#directory;
|
|
@@ -40,6 +43,15 @@ export class Project {
|
|
|
40
43
|
return this.desires.type;
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
/**
|
|
47
|
+
* See {@link isLibraryType}
|
|
48
|
+
*
|
|
49
|
+
* @type {boolean}
|
|
50
|
+
*/
|
|
51
|
+
get isLibrary() {
|
|
52
|
+
return isLibraryType(this.type);
|
|
53
|
+
}
|
|
54
|
+
|
|
43
55
|
/**
|
|
44
56
|
* @type {import('#types').PackageManager}
|
|
45
57
|
*/
|
|
@@ -26,9 +26,7 @@ export async function removeTypes(extension, code) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
29
|
* Support for removeTypes
|
|
31
|
-
*
|
|
32
30
|
*/
|
|
33
31
|
|
|
34
32
|
const preprocessor = new Preprocessor();
|
|
@@ -78,8 +76,8 @@ async function wrappedRemoveTypes(code = "", callback = babelRemoveTypes) {
|
|
|
78
76
|
templateTag,
|
|
79
77
|
);
|
|
80
78
|
} else {
|
|
81
|
-
// babel-remove-types uses prettier under the hood, and adds trailing `;` where allowed
|
|
82
|
-
//
|
|
79
|
+
// babel-remove-types uses prettier under the hood, and adds trailing `;` where allowed.
|
|
80
|
+
// Restoring the template tags has to account for that:
|
|
83
81
|
transformedWithTemplateTag = transformedWithTemplateTag.replace(
|
|
84
82
|
`${replacementExpression(i)};`,
|
|
85
83
|
templateTag,
|
|
@@ -6,10 +6,12 @@ import enhancedResolve from "enhanced-resolve";
|
|
|
6
6
|
const { ResolverFactory, CachedInputFileSystem } = enhancedResolve;
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* The module files this util operates on.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* The module files this util operates on.
|
|
10
|
+
*
|
|
11
|
+
* Doubles as the candidate extensions the resolver may append
|
|
12
|
+
* when a request has none, or when we retry a request without its original extension.
|
|
13
|
+
*
|
|
14
|
+
* The single source of truth for both.
|
|
13
15
|
*/
|
|
14
16
|
const MODULE_EXTENSIONS = [".js", ".gjs", ".ts", ".gts"];
|
|
15
17
|
|
|
@@ -19,38 +21,48 @@ function createResolver() {
|
|
|
19
21
|
fileSystem: new CachedInputFileSystem(fs, 100),
|
|
20
22
|
useSyncFileSystemCalls: true,
|
|
21
23
|
extensions: MODULE_EXTENSIONS,
|
|
22
|
-
// no conditionNames: we only resolve local paths and sub-path
|
|
23
|
-
//
|
|
24
|
-
//
|
|
24
|
+
// no conditionNames: we only resolve local paths and sub-path self-imports.
|
|
25
|
+
// Their targets in the emitted `imports` field are plain strings,
|
|
26
|
+
// which match regardless of conditions.
|
|
25
27
|
});
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
/**
|
|
29
|
-
* Imports must match files.
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* tree:
|
|
31
|
+
* Imports must match files.
|
|
32
|
+
*
|
|
33
|
+
* Generation renames files (e.g. type removal turns .ts into .js).
|
|
34
|
+
* So for every local specifier, we check what actually exists in the emitted project tree.
|
|
34
35
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* -
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
36
|
+
* Local specifiers are:
|
|
37
|
+
* - ./ and ../
|
|
38
|
+
* - # subpath-imports, expanded against the project's package.json `imports` field
|
|
39
|
+
*
|
|
40
|
+
* For each one:
|
|
41
|
+
* - it resolves to a file and carries an extension?
|
|
42
|
+
* leave it alone.
|
|
43
|
+
* - it resolves but has no extension?
|
|
44
|
+
* append the resolved file's extension (fully-specified local imports resolve faster).
|
|
45
|
+
* Unless the fully-specified form no longer maps to the same file
|
|
46
|
+
* (e.g. `#config`, whose `imports` target carries the extension itself).
|
|
47
|
+
* - it doesn't resolve, but the same specifier with whatever extension IS on disk does?
|
|
48
|
+
* rewrite it to that.
|
|
49
|
+
* - nothing matches?
|
|
50
|
+
* leave it alone. We can't know better, and the project's own build will report it.
|
|
45
51
|
*
|
|
46
52
|
* Non-module files are returned untouched.
|
|
47
53
|
*
|
|
48
|
-
* ember-estree parses gjs natively
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
+
* ember-estree parses gjs natively.
|
|
55
|
+
* Its visitors fire on actual module specifiers only:
|
|
56
|
+
* - static imports
|
|
57
|
+
* - re-exports
|
|
58
|
+
* - dynamic import()
|
|
59
|
+
*
|
|
60
|
+
* Matched specifiers are rewritten on the AST,
|
|
61
|
+
* and the File is printed back out (comments included).
|
|
62
|
+
*
|
|
63
|
+
* Formatting is the project's own concern.
|
|
64
|
+
* New projects run their configured lint:fix / format right away
|
|
65
|
+
* (it's in the CLI's "Next steps").
|
|
54
66
|
*
|
|
55
67
|
* Files with no specifiers to rewrite are returned untouched.
|
|
56
68
|
*
|
|
@@ -101,9 +113,9 @@ export function rewriteImportsToMatchFiles(code, emittedPath) {
|
|
|
101
113
|
// fully specified and correct
|
|
102
114
|
if (currentExtension) return;
|
|
103
115
|
|
|
104
|
-
// extensionless: append the resolved file's extension,
|
|
105
|
-
// the fully-specified form still maps to the same file
|
|
106
|
-
// maps with the extension in the target, so `#config.js` wouldn't)
|
|
116
|
+
// extensionless: append the resolved file's extension,
|
|
117
|
+
// as long as the fully-specified form still maps to the same file.
|
|
118
|
+
// (`#config` maps with the extension in the target, so `#config.js` wouldn't)
|
|
107
119
|
let candidate = specifier + extname(resolved);
|
|
108
120
|
|
|
109
121
|
if (resolveFrom(candidate) !== resolved) return;
|
package/src/utils/stage.js
CHANGED
|
@@ -6,9 +6,13 @@ import { styleText } from "node:util";
|
|
|
6
6
|
import { structuredPatch } from "diff";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* Directories that belong to the target project's environment
|
|
10
|
-
* its generated contents.
|
|
11
|
-
*
|
|
9
|
+
* Directories that belong to the target project's environment,
|
|
10
|
+
* not to its generated contents.
|
|
11
|
+
*
|
|
12
|
+
* They are:
|
|
13
|
+
* - never seeded into the stage
|
|
14
|
+
* - never diffed
|
|
15
|
+
* - never deleted on commit
|
|
12
16
|
*
|
|
13
17
|
* (`.git` has one exception: see {@link Stage#commit})
|
|
14
18
|
*/
|
|
@@ -20,30 +24,31 @@ const ENVIRONMENT_DIRECTORIES = new Set(["node_modules", ".git"]);
|
|
|
20
24
|
*/
|
|
21
25
|
|
|
22
26
|
/**
|
|
23
|
-
* A copy-on-write overlay for a project directory,
|
|
24
|
-
* Docker's layered file systems:
|
|
27
|
+
* A copy-on-write overlay for a project directory,
|
|
28
|
+
* in the spirit of Docker's layered file systems:
|
|
25
29
|
*
|
|
26
30
|
* - the target directory is the read-only lower layer
|
|
27
31
|
* - a real directory in the OS temp dir is the writable upper layer,
|
|
28
32
|
* seeded ("copied up") from the target
|
|
29
33
|
*
|
|
30
|
-
* All generation (bases, layers, consolidators) runs against the upper
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
34
|
+
* All generation (bases, layers, consolidators) runs against the upper layer,
|
|
35
|
+
* via `stage.directory`.
|
|
36
|
+
*
|
|
37
|
+
* The upper layer is a real directory on purpose.
|
|
38
|
+
* Layers keep using `node:fs`, `ember-apply`, and subprocesses
|
|
39
|
+
* (`git`, package managers) exactly as they would against the real project.
|
|
40
|
+
* An in-process virtual fs can't offer that: child processes can't see it.
|
|
36
41
|
*
|
|
37
|
-
* Nothing touches the target until {@link Stage#commit}
|
|
38
|
-
* throws the upper layer away.
|
|
42
|
+
* Nothing touches the target until {@link Stage#commit}.
|
|
43
|
+
* {@link Stage#discard} throws the upper layer away.
|
|
39
44
|
*/
|
|
40
45
|
export class Stage {
|
|
41
46
|
/**
|
|
42
|
-
* @param {string} targetDirectory the directory the project
|
|
47
|
+
* @param {string} targetDirectory the directory the project must end up in
|
|
43
48
|
* @param {{ seed?: boolean }} [options]
|
|
44
|
-
* seed: copy the target's current contents into the stage first
|
|
45
|
-
*
|
|
46
|
-
* generation (e.g. the "replace" flow).
|
|
49
|
+
* seed: copy the target's current contents into the stage first (a "copy up").
|
|
50
|
+
* Defaults to true.
|
|
51
|
+
* Pass false to stage a from-scratch generation (e.g. the "replace" flow).
|
|
47
52
|
* @returns {Promise<Stage>}
|
|
48
53
|
*/
|
|
49
54
|
static async create(targetDirectory, options = {}) {
|
|
@@ -80,8 +85,10 @@ export class Stage {
|
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
/**
|
|
83
|
-
* The writable upper layer.
|
|
84
|
-
*
|
|
88
|
+
* The writable upper layer.
|
|
89
|
+
*
|
|
90
|
+
* Generate in here:
|
|
91
|
+
* `new Project(stage.directory, desires)`
|
|
85
92
|
*
|
|
86
93
|
* @type {string}
|
|
87
94
|
*/
|
|
@@ -169,19 +176,23 @@ export class Stage {
|
|
|
169
176
|
}
|
|
170
177
|
|
|
171
178
|
/**
|
|
172
|
-
* Applies the staged changes to the target directory, then removes the
|
|
173
|
-
* stage. Only paths reported by {@link Stage#changes} are touched --
|
|
174
|
-
* everything else in the target (untracked files, node_modules, .git)
|
|
175
|
-
* is left alone.
|
|
179
|
+
* Applies the staged changes to the target directory, then removes the stage.
|
|
176
180
|
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
+
* Only paths reported by {@link Stage#changes} are touched.
|
|
182
|
+
* Everything else in the target is left alone:
|
|
183
|
+
* - untracked files
|
|
184
|
+
* - node_modules
|
|
185
|
+
* - .git
|
|
181
186
|
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
187
|
+
* One exception: when the stage has a `.git` directory and the target does not
|
|
188
|
+
* (the git layer ran `git init` inside the stage),
|
|
189
|
+
* the `.git` directory is carried over.
|
|
190
|
+
* A freshly generated project keeps its history that way.
|
|
191
|
+
*
|
|
192
|
+
* @param {Change[]} [changes] the changes to apply.
|
|
193
|
+
* Pass a subset of {@link Stage#changes} to apply only some of them
|
|
194
|
+
* (e.g. the ones a user accepted during review).
|
|
195
|
+
* Defaults to all staged changes.
|
|
185
196
|
* @returns {Promise<Change[]>} the changes that were written
|
|
186
197
|
*/
|
|
187
198
|
async commit(changes) {
|
|
@@ -222,9 +233,9 @@ export class Stage {
|
|
|
222
233
|
}
|
|
223
234
|
|
|
224
235
|
/**
|
|
225
|
-
* All files
|
|
226
|
-
*
|
|
227
|
-
* `fs.glob`
|
|
236
|
+
* All files, as paths relative to `directory`, excluding environment directories.
|
|
237
|
+
*
|
|
238
|
+
* Includes dotfiles (`.gitignore`, `.github/`, ...), which `fs.glob` skips.
|
|
228
239
|
*
|
|
229
240
|
* @param {string} directory
|
|
230
241
|
* @returns {Promise<Set<string>>}
|
|
@@ -2,12 +2,13 @@ import { readFile, writeFile } from "node:fs/promises";
|
|
|
2
2
|
import { toTree, print, statements } from "ember-estree";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Adds a property to the object passed to `defineConfig(...)`
|
|
6
|
-
* project's tsdown.config.js.
|
|
5
|
+
* Adds a property to the object passed to `defineConfig(...)`
|
|
6
|
+
* in the project's tsdown.config.js.
|
|
7
7
|
*
|
|
8
|
-
* No-ops when
|
|
9
|
-
* the
|
|
10
|
-
*
|
|
8
|
+
* No-ops when:
|
|
9
|
+
* - the property is already present (layers re-run)
|
|
10
|
+
* - the config doesn't have the `defineConfig({ ... })` shape
|
|
11
|
+
* (an existing project's hand-rolled config is the user's to manage)
|
|
11
12
|
*
|
|
12
13
|
* @param {import('#utils/project.js').Project} project
|
|
13
14
|
* @param {string} propertyName
|
package/src/utils/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Project } from "./project.js";
|
|
2
2
|
export type PackageManager = "pnpm" | "npm";
|
|
3
|
-
export type ProjectType = "app" | "library" | "extension";
|
|
3
|
+
export type ProjectType = "app" | "library" | "extension" | "custom-element";
|
|
4
4
|
|
|
5
5
|
export interface Layer {
|
|
6
6
|
/**
|
|
@@ -9,13 +9,14 @@ export interface Layer {
|
|
|
9
9
|
label: string;
|
|
10
10
|
hint?: string;
|
|
11
11
|
/**
|
|
12
|
-
* Whether the layer is pre-selected in the CLI
|
|
13
|
-
* deselect it
|
|
12
|
+
* Whether the layer is pre-selected in the CLI.
|
|
13
|
+
* The user can still deselect it.
|
|
14
14
|
*/
|
|
15
15
|
defaultValue?: (projectType: ProjectType) => unknown;
|
|
16
16
|
/**
|
|
17
|
-
* Optional README documentation snippet or function.
|
|
18
|
-
*
|
|
17
|
+
* Optional README documentation snippet or function.
|
|
18
|
+
*
|
|
19
|
+
* Whatever is returned shows up in the generated project's README file.
|
|
19
20
|
*/
|
|
20
21
|
readme?: string | ((project: Project) => string | undefined | Promise<string | undefined>);
|
|
21
22
|
/**
|
|
@@ -40,10 +41,10 @@ export interface Layer {
|
|
|
40
41
|
|
|
41
42
|
export interface DiscoveredLayer extends Layer {
|
|
42
43
|
/**
|
|
43
|
-
* The unique name of the layer
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
44
|
+
* The unique name of the layer.
|
|
45
|
+
*
|
|
46
|
+
* An exact match of the folder name.
|
|
47
|
+
* Not provided by the layer itself.
|
|
47
48
|
*/
|
|
48
49
|
name: string;
|
|
49
50
|
}
|