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/cli/index.js
CHANGED
|
@@ -20,7 +20,7 @@ import { askReplaceOrUpdate } from "./questions/replace-or-update.js";
|
|
|
20
20
|
* This whole file's primary purpose is to be an interactive CLI
|
|
21
21
|
* for `generateProject`.
|
|
22
22
|
*
|
|
23
|
-
* Other tools can call `generateProject` themselves
|
|
23
|
+
* Other tools can call `generateProject` themselves
|
|
24
24
|
* if they want to provide different TUI/GUIs.
|
|
25
25
|
*/
|
|
26
26
|
async function main() {
|
|
@@ -38,19 +38,19 @@ async function main() {
|
|
|
38
38
|
await askIfOK();
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Everything is generated in a stage:
|
|
42
|
-
* dir) acting as a writable overlay of the target directory.
|
|
41
|
+
* Everything is generated in a stage:
|
|
42
|
+
* a real directory (in the OS temp dir) acting as a writable overlay of the target directory.
|
|
43
43
|
*
|
|
44
|
-
* When updating an existing project, the stage is seeded with a full
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* the codemods modify them in the stage exactly as if they were
|
|
49
|
-
* operating in place. "replace" starts from an empty stage instead.
|
|
44
|
+
* When updating an existing project, the stage is seeded with a full copy of it
|
|
45
|
+
* (sans node_modules/.git), so the bases and layers run against the project's current state:
|
|
46
|
+
* - isSetup checks, package.json reads, file existence checks, etc. all see the existing files
|
|
47
|
+
* - the codemods modify them in the stage exactly as if they were operating in place
|
|
50
48
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
49
|
+
* "replace" starts from an empty stage instead.
|
|
50
|
+
*
|
|
51
|
+
* The target itself is not touched until stage.commit() below.
|
|
52
|
+
* That applies the difference between the stage and the target
|
|
53
|
+
* (or the subset of it the user accepts during review).
|
|
54
54
|
*/
|
|
55
55
|
const stage = await Stage.create(projectPath, { seed: replaceOrUpdate !== "replace" });
|
|
56
56
|
|
|
@@ -98,9 +98,12 @@ async function main() {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
/**
|
|
101
|
-
* New projects (and "replace", which was already confirmed) are written
|
|
102
|
-
*
|
|
103
|
-
*
|
|
101
|
+
* New projects (and "replace", which was already confirmed) are written without asking.
|
|
102
|
+
*
|
|
103
|
+
* Updates to an existing project must be confirmed:
|
|
104
|
+
* - write all
|
|
105
|
+
* - review each change (accept/reject)
|
|
106
|
+
* - cancel
|
|
104
107
|
*/
|
|
105
108
|
const isUpdate = !stage.isNew && replaceOrUpdate !== "replace";
|
|
106
109
|
|
|
@@ -123,9 +126,11 @@ async function main() {
|
|
|
123
126
|
|
|
124
127
|
/**
|
|
125
128
|
* Converted / generated files are written expecting the project's own
|
|
126
|
-
* formatting and lint pass to run right away
|
|
127
|
-
* import groups are `eslint --fix`'s job)
|
|
128
|
-
*
|
|
129
|
+
* formatting and lint pass to run right away.
|
|
130
|
+
* (e.g. blank lines between import groups are `eslint --fix`'s job)
|
|
131
|
+
*
|
|
132
|
+
* So when the project has a lint:fix script,
|
|
133
|
+
* surface it as the step right after install.
|
|
129
134
|
*/
|
|
130
135
|
let lintFixStep = "";
|
|
131
136
|
try {
|
|
@@ -4,12 +4,21 @@ import { answers, printArgInUse } from "#args";
|
|
|
4
4
|
/**
|
|
5
5
|
* addon is alias for library
|
|
6
6
|
* web-extension is alias for extension
|
|
7
|
+
* web-component is alias for custom-element
|
|
7
8
|
*/
|
|
8
|
-
const SUPPORTED = new Set([
|
|
9
|
+
const SUPPORTED = new Set([
|
|
10
|
+
"app",
|
|
11
|
+
"library",
|
|
12
|
+
"addon",
|
|
13
|
+
"extension",
|
|
14
|
+
"web-extension",
|
|
15
|
+
"custom-element",
|
|
16
|
+
"web-component",
|
|
17
|
+
]);
|
|
9
18
|
|
|
10
19
|
/**
|
|
11
20
|
* @param {string | undefined} selected
|
|
12
|
-
* @returns {selected is "app" | "library" | "addon" | "extension" | "web-extension"}
|
|
21
|
+
* @returns {selected is "app" | "library" | "addon" | "extension" | "web-extension" | "custom-element" | "web-component"}
|
|
13
22
|
*/
|
|
14
23
|
function isValid(selected) {
|
|
15
24
|
if (!selected) return false;
|
|
@@ -27,6 +36,7 @@ export async function askProjectType() {
|
|
|
27
36
|
|
|
28
37
|
if (answers.type === "addon") return "library";
|
|
29
38
|
if (answers.type === "web-extension") return "extension";
|
|
39
|
+
if (answers.type === "web-component") return "custom-element";
|
|
30
40
|
|
|
31
41
|
return answers.type;
|
|
32
42
|
}
|
|
@@ -45,6 +55,11 @@ export async function askProjectType() {
|
|
|
45
55
|
label: "browser extension",
|
|
46
56
|
hint: "Manifest V3 extension whose popup is an Ember app",
|
|
47
57
|
},
|
|
58
|
+
{
|
|
59
|
+
value: "custom-element",
|
|
60
|
+
label: "custom element",
|
|
61
|
+
hint: "an Ember component packaged as a web component, usable from any page",
|
|
62
|
+
},
|
|
48
63
|
],
|
|
49
64
|
});
|
|
50
65
|
|
|
@@ -17,10 +17,11 @@ function isValid(selected) {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Nothing has been written to the target directory yet
|
|
21
|
-
* far only exists in the stage.
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* Nothing has been written to the target directory yet.
|
|
21
|
+
* Everything so far only exists in the stage.
|
|
22
|
+
*
|
|
23
|
+
* Ask whether to write the staged changes,
|
|
24
|
+
* with the option to review the diff change-by-change, accepting or rejecting each one.
|
|
24
25
|
*
|
|
25
26
|
* @param {import('#utils/stage.js').Stage} stage
|
|
26
27
|
* @param {import('#utils/stage.js').Change[]} changes
|
|
@@ -61,10 +62,11 @@ export async function askToWrite(stage, changes) {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
/**
|
|
64
|
-
* Show each change's diff and ask what to do with it.
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
65
|
+
* Show each change's diff and ask what to do with it.
|
|
66
|
+
*
|
|
67
|
+
* "accept remaining" and "reject remaining" finish the review early,
|
|
68
|
+
* so they require an extra confirmation.
|
|
69
|
+
* Declining the confirmation returns to the change being reviewed.
|
|
68
70
|
*
|
|
69
71
|
* @param {import('#utils/stage.js').Stage} stage
|
|
70
72
|
* @param {import('#utils/stage.js').Change[]} changes
|
|
@@ -83,8 +85,8 @@ async function reviewEachChange(stage, changes) {
|
|
|
83
85
|
|
|
84
86
|
const remaining = changes.length - index;
|
|
85
87
|
|
|
86
|
-
// Bypass clack for the diff itself
|
|
87
|
-
// log decorations wrap every line.
|
|
88
|
+
// Bypass clack for the diff itself.
|
|
89
|
+
// Diffs are long, and clack's log decorations wrap every line.
|
|
88
90
|
console.log("\n" + (await stage.diff([change])));
|
|
89
91
|
|
|
90
92
|
const answer = await p.select({
|
|
@@ -15,13 +15,13 @@ export default {
|
|
|
15
15
|
|
|
16
16
|
async run(project) {
|
|
17
17
|
// Publish checks: apps aren't published
|
|
18
|
-
if (project.
|
|
18
|
+
if (!project.isLibrary) return;
|
|
19
19
|
|
|
20
20
|
// Declarations are the thing being checked
|
|
21
21
|
if (!(await project.hasOrWantsLayer("typescript"))) return;
|
|
22
22
|
|
|
23
|
-
// ESM-only packages don't offer the CJS resolution modes
|
|
24
|
-
// profile requires
|
|
23
|
+
// ESM-only packages don't offer the CJS resolution modes
|
|
24
|
+
// that the strict profile requires
|
|
25
25
|
await addTsdownConfigProperty(project, "attw", `attw: { profile: "esm-only" }`);
|
|
26
26
|
await packageJson.addDevDependencies(await getLatest(deps), project.directory);
|
|
27
27
|
},
|
|
@@ -42,9 +42,9 @@ export default {
|
|
|
42
42
|
/** @type {string[]} */
|
|
43
43
|
const reasons = [];
|
|
44
44
|
|
|
45
|
-
// Nothing to set up outside TypeScript libraries,
|
|
46
|
-
// missing
|
|
47
|
-
if (project.
|
|
45
|
+
// Nothing to set up outside TypeScript libraries,
|
|
46
|
+
// so nothing can be missing
|
|
47
|
+
if (!project.isLibrary || !(await project.hasOrWantsLayer("typescript"))) {
|
|
48
48
|
return explain ? { isSetup: true, reasons } : true;
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -55,8 +55,9 @@ export default {
|
|
|
55
55
|
};
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
59
|
-
*
|
|
58
|
+
* String concatenation is a very silly way to handle YAML, but 🤷
|
|
59
|
+
*
|
|
60
|
+
* Will update if this starts getting to be troublesome.
|
|
60
61
|
*/
|
|
61
62
|
const preamble = `name: CI
|
|
62
63
|
|
|
@@ -135,8 +136,10 @@ async function transform(project) {
|
|
|
135
136
|
content = await addOrUpdateTests(project, content);
|
|
136
137
|
|
|
137
138
|
/**
|
|
138
|
-
*
|
|
139
|
-
*
|
|
139
|
+
* A workflow needs at least one job.
|
|
140
|
+
*
|
|
141
|
+
* If nothing was added, there is no job to run,
|
|
142
|
+
* so the preamble is not added either.
|
|
140
143
|
*/
|
|
141
144
|
if (content === "") {
|
|
142
145
|
return { content, original, didChange: original !== content, outputPath };
|
|
@@ -42,8 +42,8 @@ export default {
|
|
|
42
42
|
await writeFile(path, wired);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
//
|
|
46
|
-
//
|
|
45
|
+
// The codemod leaves unrecognized Application shapes alone.
|
|
46
|
+
// The dependency is only useful once the Application is wired.
|
|
47
47
|
if (!hasInspectorSupport(wired, path)) return;
|
|
48
48
|
|
|
49
49
|
await packageJson.addDevDependencies(await getLatest(deps), project.directory);
|
|
@@ -15,7 +15,7 @@ export default {
|
|
|
15
15
|
|
|
16
16
|
async run(project) {
|
|
17
17
|
// Publish checks: apps aren't published
|
|
18
|
-
if (project.
|
|
18
|
+
if (!project.isLibrary) return;
|
|
19
19
|
|
|
20
20
|
await addTsdownConfigProperty(project, "publint", "publint: true");
|
|
21
21
|
await packageJson.addDevDependencies(await getLatest(deps), project.directory);
|
|
@@ -38,7 +38,7 @@ export default {
|
|
|
38
38
|
const reasons = [];
|
|
39
39
|
|
|
40
40
|
// Nothing to set up outside libraries, so nothing can be missing
|
|
41
|
-
if (project.
|
|
41
|
+
if (!project.isLibrary) {
|
|
42
42
|
return explain ? { isSetup: true, reasons } : true;
|
|
43
43
|
}
|
|
44
44
|
|
|
@@ -18,9 +18,12 @@ const tsDeps = {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
* Libraries don't have the app base's vite stack
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* Libraries don't have the app base's vite stack.
|
|
22
|
+
*
|
|
23
|
+
* So the test build brings the pieces that the generated configs
|
|
24
|
+
* and test-helper import directly.
|
|
25
|
+
*
|
|
26
|
+
* Everything else (babel itself, the embroider machinery)
|
|
24
27
|
* comes with @nullvoxpopuli/ember-vite.
|
|
25
28
|
*/
|
|
26
29
|
const libraryDeps = {
|
|
@@ -41,14 +44,14 @@ const TEST_BABEL_CONFIG = "config/test/babel.config.js";
|
|
|
41
44
|
* @param {import('#utils/project.js').Project} project
|
|
42
45
|
*/
|
|
43
46
|
function depsFor(project) {
|
|
44
|
-
return project.
|
|
47
|
+
return project.isLibrary ? libraryDeps : deps;
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
/**
|
|
48
51
|
* @param {import('#utils/project.js').Project} project
|
|
49
52
|
*/
|
|
50
53
|
function tsDepsFor(project) {
|
|
51
|
-
return project.
|
|
54
|
+
return project.isLibrary ? libraryTsDeps : tsDeps;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
57
|
/**
|
|
@@ -58,7 +61,7 @@ export default {
|
|
|
58
61
|
label: "QUnit",
|
|
59
62
|
|
|
60
63
|
async run(project) {
|
|
61
|
-
let isLibrary = project.
|
|
64
|
+
let isLibrary = project.isLibrary;
|
|
62
65
|
let ts = await project.hasOrWantsLayer("typescript");
|
|
63
66
|
|
|
64
67
|
if (isLibrary) {
|
|
@@ -79,7 +82,6 @@ export default {
|
|
|
79
82
|
project.directory,
|
|
80
83
|
);
|
|
81
84
|
|
|
82
|
-
// Add scripts
|
|
83
85
|
await packageJson.addScripts(
|
|
84
86
|
{
|
|
85
87
|
"build:test": "EMBER_ENV=test NODE_ENV=development vite build --mode development",
|
|
@@ -151,10 +153,11 @@ export default {
|
|
|
151
153
|
};
|
|
152
154
|
|
|
153
155
|
/**
|
|
154
|
-
* Tests import the library's source
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
156
|
+
* Tests import the library's source, not its published dist.
|
|
157
|
+
*
|
|
158
|
+
* The `#src/*` subpath must exist before the test files are applied:
|
|
159
|
+
* applying rewrites their import extensions
|
|
160
|
+
* by resolving each specifier through this mapping.
|
|
158
161
|
*
|
|
159
162
|
* @param {import('#utils/project.js').Project} project
|
|
160
163
|
*/
|
|
@@ -166,12 +169,13 @@ async function addSourceImports(project) {
|
|
|
166
169
|
}
|
|
167
170
|
|
|
168
171
|
/**
|
|
169
|
-
* The test babel config is the qunit layer's own
|
|
170
|
-
* when a library has one, belongs to the publish build)
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
172
|
+
* The test babel config is the qunit layer's own.
|
|
173
|
+
* (a root babel config, when a library has one, belongs to the publish build)
|
|
174
|
+
*
|
|
175
|
+
* So this layer keeps the config's TypeScript handling in step with the project:
|
|
176
|
+
* - the shipped config carries the TS plugin
|
|
177
|
+
* - it must not survive in a JavaScript project
|
|
178
|
+
* - it must come back when a project adopts the typescript layer later
|
|
175
179
|
*
|
|
176
180
|
* @param {import('#utils/project.js').Project} project
|
|
177
181
|
* @param {boolean} ts
|
|
@@ -2,6 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { writeFile } from "node:fs/promises";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { formatLabel } from "#utils/cli.js";
|
|
5
|
+
import { tagNameFor } from "#bases/minimal-custom-element";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @param {import('#utils/project.js').Project} project
|
|
@@ -73,13 +74,13 @@ ${project.runPrefix} start
|
|
|
73
74
|
|
|
74
75
|
### Building
|
|
75
76
|
|
|
76
|
-
To build the
|
|
77
|
+
To build the extension:
|
|
77
78
|
|
|
78
79
|
\`\`\`sh
|
|
79
80
|
${project.runPrefix} build
|
|
80
81
|
\`\`\`
|
|
81
82
|
|
|
82
|
-
or
|
|
83
|
+
or, to rebuild on change:
|
|
83
84
|
|
|
84
85
|
\`\`\`sh
|
|
85
86
|
${project.runPrefix} build:watch
|
|
@@ -113,6 +114,55 @@ ${project.runPrefix} build
|
|
|
113
114
|
${layerDocsMarkdown}`;
|
|
114
115
|
}
|
|
115
116
|
|
|
117
|
+
/**
|
|
118
|
+
* @param {import('#utils/project.js').Project} project
|
|
119
|
+
* @param {string} layerDocsMarkdown
|
|
120
|
+
*/
|
|
121
|
+
function customElementReadme(project, layerDocsMarkdown) {
|
|
122
|
+
let tagName = tagNameFor(project.name);
|
|
123
|
+
|
|
124
|
+
return `# ${project.name}
|
|
125
|
+
|
|
126
|
+
An Ember component, packaged as a custom element, created with \`ember.nvp\`.
|
|
127
|
+
The built package contains ember, so the page that uses the element needs nothing else.
|
|
128
|
+
|
|
129
|
+
## Usage
|
|
130
|
+
|
|
131
|
+
Import the \`register\` entry once. It defines the \`<${tagName}>\` tag.
|
|
132
|
+
|
|
133
|
+
\`\`\`js
|
|
134
|
+
import "${project.name}/register";
|
|
135
|
+
\`\`\`
|
|
136
|
+
|
|
137
|
+
\`\`\`html
|
|
138
|
+
<${tagName} label="Clicks" step="2"></${tagName}>
|
|
139
|
+
\`\`\`
|
|
140
|
+
|
|
141
|
+
To choose your own tag name, import the class and define it yourself:
|
|
142
|
+
|
|
143
|
+
\`\`\`js
|
|
144
|
+
import { CounterElement } from "${project.name}";
|
|
145
|
+
|
|
146
|
+
customElements.define("my-counter", CounterElement);
|
|
147
|
+
\`\`\`
|
|
148
|
+
|
|
149
|
+
## Getting Started
|
|
150
|
+
|
|
151
|
+
### Prerequisites
|
|
152
|
+
|
|
153
|
+
- Node.js >= 24
|
|
154
|
+
- ${project.packageManager}
|
|
155
|
+
|
|
156
|
+
### Development & Building
|
|
157
|
+
|
|
158
|
+
To build the package:
|
|
159
|
+
|
|
160
|
+
\`\`\`sh
|
|
161
|
+
${project.runPrefix} build
|
|
162
|
+
\`\`\`
|
|
163
|
+
${layerDocsMarkdown}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
116
166
|
export default {
|
|
117
167
|
label: formatLabel("README.md", "generate project documentation"),
|
|
118
168
|
hint: "project README",
|
|
@@ -125,7 +175,6 @@ export default {
|
|
|
125
175
|
* @param {import('#utils/project.js').Project} project
|
|
126
176
|
*/
|
|
127
177
|
async run(project) {
|
|
128
|
-
// Collect layer docs
|
|
129
178
|
const layerDocs = [];
|
|
130
179
|
for (const layer of project.desires.layers) {
|
|
131
180
|
if (layer.name === "readme" || !layer.readme) {
|
|
@@ -149,7 +198,6 @@ export default {
|
|
|
149
198
|
layerDocsMarkdown = `\n## Features & Tooling\n\n${layerDocs.join("\n\n")}\n`;
|
|
150
199
|
}
|
|
151
200
|
|
|
152
|
-
// Merge the layers' snippets into the main readme template
|
|
153
201
|
let content;
|
|
154
202
|
switch (project.type) {
|
|
155
203
|
case "extension":
|
|
@@ -158,6 +206,9 @@ export default {
|
|
|
158
206
|
case "library":
|
|
159
207
|
content = libraryReadme(project, layerDocsMarkdown);
|
|
160
208
|
break;
|
|
209
|
+
case "custom-element":
|
|
210
|
+
content = customElementReadme(project, layerDocsMarkdown);
|
|
211
|
+
break;
|
|
161
212
|
default:
|
|
162
213
|
content = appReadme(project, layerDocsMarkdown);
|
|
163
214
|
break;
|
|
@@ -14,11 +14,11 @@ export default {
|
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
// extensions aren't published to npm either,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
// extensions aren't published to npm either,
|
|
18
|
+
// so they follow the app config
|
|
19
|
+
let source = project.isLibrary
|
|
20
|
+
? join(import.meta.dirname, "files/library.json5")
|
|
21
|
+
: join(import.meta.dirname, "files/app.json5");
|
|
22
22
|
|
|
23
23
|
let destination = join(project.directory, ".github/renovate.json5");
|
|
24
24
|
|
|
@@ -4,11 +4,13 @@ import { cp } from "node:fs/promises";
|
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import { hasConfiguredTSBabel, prependPlugin } from "#utils/babel.js";
|
|
6
6
|
import { getLatest } from "#utils/npm.js";
|
|
7
|
+
import { isLibraryType } from "#utils/project-type.js";
|
|
7
8
|
|
|
8
9
|
const bases = join(import.meta.dirname, "../../bases");
|
|
9
10
|
const appBase = join(bases, "minimal-app/files");
|
|
10
11
|
const extensionBase = join(bases, "minimal-extension/files");
|
|
11
12
|
const libraryBase = join(bases, "minimal-library/files");
|
|
13
|
+
const customElementBase = join(bases, "minimal-custom-element/files");
|
|
12
14
|
|
|
13
15
|
const sharedDeps = {
|
|
14
16
|
"@glint/ember-tsc": "^1.0.8",
|
|
@@ -18,8 +20,8 @@ const sharedDeps = {
|
|
|
18
20
|
};
|
|
19
21
|
|
|
20
22
|
const appDeps = {
|
|
21
|
-
// Apps strip types via their own babel.config.js
|
|
22
|
-
// config
|
|
23
|
+
// Apps strip types via their own babel.config.js.
|
|
24
|
+
// Libraries have no babel config: ember() handles type stripping.
|
|
23
25
|
"@babel/plugin-transform-typescript": "^7.28.5",
|
|
24
26
|
"@ember/app-tsconfig": "^2.0.0",
|
|
25
27
|
};
|
|
@@ -34,7 +36,7 @@ const libraryDeps = {
|
|
|
34
36
|
function depsFor(project) {
|
|
35
37
|
return {
|
|
36
38
|
...sharedDeps,
|
|
37
|
-
...(project.
|
|
39
|
+
...(project.isLibrary ? libraryDeps : appDeps),
|
|
38
40
|
};
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -45,21 +47,19 @@ export default {
|
|
|
45
47
|
label: "TypeScript",
|
|
46
48
|
|
|
47
49
|
/**
|
|
48
|
-
* Libraries publish declarations,
|
|
49
|
-
* user opts out.
|
|
50
|
+
* Libraries publish declarations,
|
|
51
|
+
* so they are TypeScript unless the user opts out.
|
|
50
52
|
*
|
|
51
53
|
* @param {import('#types').ProjectType} projectType
|
|
52
54
|
*/
|
|
53
55
|
defaultValue(projectType) {
|
|
54
|
-
return projectType
|
|
56
|
+
return isLibraryType(projectType);
|
|
55
57
|
},
|
|
56
58
|
|
|
57
59
|
async run(project) {
|
|
58
60
|
/**
|
|
59
|
-
*
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* if tsconfig exists,
|
|
61
|
+
* TODO:
|
|
62
|
+
* - if jsconfig exists, switch to tsconfig
|
|
63
63
|
*/
|
|
64
64
|
await addTSConfig(project);
|
|
65
65
|
await updatePackageJson(project);
|
|
@@ -87,8 +87,8 @@ export default {
|
|
|
87
87
|
reasons.push("tsconfig.json is missing");
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
// Only projects with their own babel config need the TS plugin in it
|
|
91
|
-
//
|
|
90
|
+
// Only projects with their own babel config need the TS plugin in it.
|
|
91
|
+
// Without one (libraries), ember() strips types.
|
|
92
92
|
if (project.hasFile("babel.config.js") && !(await hasConfiguredTSBabel(project))) {
|
|
93
93
|
if (!explain) return false;
|
|
94
94
|
|
|
@@ -149,6 +149,8 @@ async function updatePackageJson(project) {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
/**
|
|
152
|
+
* Copies the base's tsconfig, unless the project already has one.
|
|
153
|
+
*
|
|
152
154
|
* @param {import('#utils/project.js').Project} project
|
|
153
155
|
*/
|
|
154
156
|
async function addTSConfig(project) {
|
|
@@ -168,6 +170,11 @@ async function addTSConfig(project) {
|
|
|
168
170
|
|
|
169
171
|
if (project.type === "library") {
|
|
170
172
|
await cp(join(libraryBase, "tsconfig.json"), project.path("tsconfig.json"));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (project.type === "custom-element") {
|
|
177
|
+
await cp(join(customElementBase, "tsconfig.json"), project.path("tsconfig.json"));
|
|
171
178
|
}
|
|
172
179
|
}
|
|
173
180
|
|
|
@@ -176,7 +183,7 @@ async function addTSConfig(project) {
|
|
|
176
183
|
*/
|
|
177
184
|
async function updateBabelConfig(project) {
|
|
178
185
|
if (!project.hasFile("babel.config.js")) {
|
|
179
|
-
//
|
|
186
|
+
// Nothing to patch (libraries): ember() strips types.
|
|
180
187
|
return;
|
|
181
188
|
}
|
|
182
189
|
|
|
@@ -13,9 +13,13 @@ const deps = {
|
|
|
13
13
|
webdriverio: "^9.29.1",
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Apps already get these from their base.
|
|
18
|
+
*
|
|
19
|
+
* The babel plugins are resolved by name from the project,
|
|
20
|
+
* by @nullvoxpopuli/ember-vite's no-config babel fallback.
|
|
21
|
+
* (decorator-transforms is already a base dependency)
|
|
22
|
+
*/
|
|
19
23
|
const libraryDeps = {
|
|
20
24
|
"@babel/plugin-transform-typescript": "^7.28.10",
|
|
21
25
|
"@nullvoxpopuli/ember-vite": "workspace:*",
|
|
@@ -35,7 +39,7 @@ const scripts = {
|
|
|
35
39
|
function depsFor(project) {
|
|
36
40
|
return {
|
|
37
41
|
...deps,
|
|
38
|
-
...(project.
|
|
42
|
+
...(project.isLibrary ? libraryDeps : {}),
|
|
39
43
|
};
|
|
40
44
|
}
|
|
41
45
|
|
package/src/utils/babel.js
CHANGED
|
@@ -80,8 +80,9 @@ export async function prependPlugin(project, plugin, configPath = "babel.config.
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
|
-
* The plugins value may be
|
|
84
|
-
*
|
|
83
|
+
* The plugins value may be:
|
|
84
|
+
* - the array itself
|
|
85
|
+
* - the array with methods chained off of it: `[ ... ].filter(Boolean)`
|
|
85
86
|
*
|
|
86
87
|
* @param {any} node
|
|
87
88
|
* @returns {any | undefined} the underlying ArrayExpression
|
package/src/utils/buffer.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Takes a substring of a string based on byte offsets.
|
|
3
|
+
*
|
|
2
4
|
* from https://github.com/ember-cli/ember-cli/blob/8ea5b2e22d37805f5749a4917e0ef25fac3c9cda/lib/models/blueprint.js#L1789
|
|
3
5
|
*
|
|
4
|
-
* Takes a substring of a string based on byte offsets.
|
|
5
|
-
|
|
6
6
|
* @param {string} value : The input string.
|
|
7
7
|
* @param {number} start : The byte index of the substring start.
|
|
8
8
|
* @param {number} end : The byte index of the substring end.
|
package/src/utils/cli.js
CHANGED
|
@@ -7,12 +7,12 @@ export function isInteractive() {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* I would like to have
|
|
10
|
+
* I would like to have selects look more like a table,
|
|
11
11
|
* but @clack/prompts makes this a bit hard.
|
|
12
12
|
*
|
|
13
|
-
* I tried using padding
|
|
14
|
-
* I don't think there is a way to format just that
|
|
15
|
-
* there are a bunch of goofy spacings in the labels.
|
|
13
|
+
* I tried using padding.
|
|
14
|
+
* But when it prints your selection, I don't think there is a way to format just that.
|
|
15
|
+
* So there are a bunch of goofy spacings in the labels.
|
|
16
16
|
*/
|
|
17
17
|
const PADDING = 10;
|
|
18
18
|
|