ember.nvp 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-plan.json +22 -4
- package/CHANGELOG.md +20 -0
- package/README.md +65 -4
- package/package.json +2 -1
- package/packages/rolldown/package.json +6 -2
- package/packages/rolldown/src/dts.test.ts +105 -1
- package/packages/rolldown/src/isolated-declarations.ts +67 -61
- package/packages/rolldown/src/transform.test.ts +212 -15
- package/packages/rolldown/src/transform.ts +106 -26
- package/src/cli/args.js +103 -14
- package/src/cli/generator.js +2 -1
- package/src/cli/help.js +66 -16
- package/src/cli/index.js +15 -0
- package/src/cli/questions/layer-options.js +130 -0
- package/src/cli/questions/validate-option.js +106 -0
- package/src/layers/eslint-bundled/app.test.ts +182 -0
- package/src/layers/eslint-bundled/index.js +172 -0
- package/src/layers/expect-type/index.js +11 -1
- package/src/layers/github-actions/app.test.ts +1 -1
- package/src/layers/typescript/app.test.ts +6 -0
- package/src/layers/typescript/index.js +48 -148
- package/src/layers/typescript/typescript-6.js +102 -0
- package/src/layers/typescript/typescript-7.js +177 -0
- package/src/utils/manifest.js +13 -0
- package/src/utils/npm.js +27 -0
- package/src/utils/project.js +23 -0
- package/src/utils/types.ts +52 -1
- package/src/utils/typescript.js +147 -0
- package/test/ember.nvp/base-library.test.ts +11 -0
- package/test/ember.nvp/cli.test.ts +86 -1
- package/test/ember.nvp/layer-options.test.ts +248 -0
- package/test/ember.nvp/layer-readme.test.ts +4 -2
- package/test/ember.nvp/layer-typescript.test.ts +302 -0
- package/test/ember.nvp/layers.test.ts +1 -2
- package/test/ember.nvp/permutations/shared.ts +3 -4
- package/test/ember.nvp/validate-option.test.ts +152 -0
- package/test/test-helpers/permutate.test.ts +2 -7
- package/test/test-helpers.ts +8 -1
- package/src/layers/eslint-bundled-ember/index.js +0 -91
- package/src/layers/eslint-bundled-nvp/app.test.ts +0 -91
- package/src/layers/eslint-bundled-nvp/index.js +0 -91
- /package/src/layers/{eslint-bundled-ember/files → eslint-bundled/files/ember}/eslint.config.js +0 -0
- /package/src/layers/{eslint-bundled-nvp/files → eslint-bundled/files/nvp}/eslint.config.js +0 -0
package/.release-plan.json
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
"solution": {
|
|
3
3
|
"ember.nvp": {
|
|
4
4
|
"impact": "minor",
|
|
5
|
-
"oldVersion": "1.
|
|
6
|
-
"newVersion": "1.
|
|
5
|
+
"oldVersion": "1.9.0",
|
|
6
|
+
"newVersion": "1.10.0",
|
|
7
7
|
"tagName": "latest",
|
|
8
8
|
"constraints": [
|
|
9
9
|
{
|
|
10
10
|
"impact": "minor",
|
|
11
11
|
"reason": "Appears in changelog section :rocket: Enhancement"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"impact": "patch",
|
|
15
|
+
"reason": "Appears in changelog section :bug: Bug Fix"
|
|
12
16
|
}
|
|
13
17
|
],
|
|
14
18
|
"pkgJSONPath": "./package.json"
|
|
@@ -17,11 +21,25 @@
|
|
|
17
21
|
"oldVersion": "1.2.0"
|
|
18
22
|
},
|
|
19
23
|
"@nullvoxpopuli/ember-rolldown": {
|
|
20
|
-
"
|
|
24
|
+
"impact": "minor",
|
|
25
|
+
"oldVersion": "2.9.0",
|
|
26
|
+
"newVersion": "2.10.0",
|
|
27
|
+
"tagName": "latest",
|
|
28
|
+
"constraints": [
|
|
29
|
+
{
|
|
30
|
+
"impact": "minor",
|
|
31
|
+
"reason": "Appears in changelog section :rocket: Enhancement"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"impact": "patch",
|
|
35
|
+
"reason": "Appears in changelog section :bug: Bug Fix"
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
"pkgJSONPath": "./packages/rolldown/package.json"
|
|
21
39
|
},
|
|
22
40
|
"@nullvoxpopuli/ember-vite": {
|
|
23
41
|
"oldVersion": "1.2.0"
|
|
24
42
|
}
|
|
25
43
|
},
|
|
26
|
-
"description": "## Release (2026-09-
|
|
44
|
+
"description": "## Release (2026-09-30)\n\n* ember.nvp 1.10.0 (minor)\n* @nullvoxpopuli/ember-rolldown 2.10.0 (minor)\n\n#### :rocket: Enhancement\n* `ember.nvp`, `@nullvoxpopuli/ember-rolldown`\n * [#144](https://github.com/NullVoxPopuli/ember.nvp/pull/144) Layer options, with an ESLint preset and a TypeScript version ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#142](https://github.com/NullVoxPopuli/ember.nvp/pull/142) Add a TypeScript 7.1+ layer ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n* `@nullvoxpopuli/ember-rolldown`, `ember.nvp`\n * [#140](https://github.com/NullVoxPopuli/ember.nvp/pull/140) Point .gts declaration maps at the .gts source ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n\n#### :bug: Bug Fix\n* `@nullvoxpopuli/ember-rolldown`, `ember.nvp`\n * [#145](https://github.com/NullVoxPopuli/ember.nvp/pull/145) Make .gts source maps point at the real source ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n * [#140](https://github.com/NullVoxPopuli/ember.nvp/pull/140) Point .gts declaration maps at the .gts source ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n\n#### Committers: 1\n- @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n"
|
|
27
45
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Release (2026-09-30)
|
|
4
|
+
|
|
5
|
+
* ember.nvp 1.10.0 (minor)
|
|
6
|
+
* @nullvoxpopuli/ember-rolldown 2.10.0 (minor)
|
|
7
|
+
|
|
8
|
+
#### :rocket: Enhancement
|
|
9
|
+
* `ember.nvp`, `@nullvoxpopuli/ember-rolldown`
|
|
10
|
+
* [#144](https://github.com/NullVoxPopuli/ember.nvp/pull/144) Layer options, with an ESLint preset and a TypeScript version ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
11
|
+
* [#142](https://github.com/NullVoxPopuli/ember.nvp/pull/142) Add a TypeScript 7.1+ layer ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
12
|
+
* `@nullvoxpopuli/ember-rolldown`, `ember.nvp`
|
|
13
|
+
* [#140](https://github.com/NullVoxPopuli/ember.nvp/pull/140) Point .gts declaration maps at the .gts source ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
14
|
+
|
|
15
|
+
#### :bug: Bug Fix
|
|
16
|
+
* `@nullvoxpopuli/ember-rolldown`, `ember.nvp`
|
|
17
|
+
* [#145](https://github.com/NullVoxPopuli/ember.nvp/pull/145) Make .gts source maps point at the real source ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
18
|
+
* [#140](https://github.com/NullVoxPopuli/ember.nvp/pull/140) Point .gts declaration maps at the .gts source ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
19
|
+
|
|
20
|
+
#### Committers: 1
|
|
21
|
+
- @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
22
|
+
|
|
3
23
|
## Release (2026-09-28)
|
|
4
24
|
|
|
5
25
|
* ember.nvp 1.9.0 (minor)
|
package/README.md
CHANGED
|
@@ -154,6 +154,11 @@ Adds modern ESLint configuration with:
|
|
|
154
154
|
- Flat config (ESLint 9+)
|
|
155
155
|
- Prettier compatibility
|
|
156
156
|
|
|
157
|
+
Choose a preset:
|
|
158
|
+
|
|
159
|
+
- `ember` (default): [ember-eslint](https://github.com/NullVoxPopuli/ember-eslint), which follows the official configuration
|
|
160
|
+
- `nvp`: [@nullvoxpopuli/eslint-configs](https://github.com/NullVoxPopuli/eslint-configs)
|
|
161
|
+
|
|
157
162
|
### 🎨 Prettier (optional)
|
|
158
163
|
|
|
159
164
|
Code formatting with:
|
|
@@ -172,6 +177,18 @@ Code formatting with:
|
|
|
172
177
|
|
|
173
178
|
Super experimental vitest setup using [ember-vitest](https://github.com/NullVoxPopuli/ember-vitest)
|
|
174
179
|
|
|
180
|
+
### TypeScript (optional, on by default for libraries)
|
|
181
|
+
|
|
182
|
+
Choose a version:
|
|
183
|
+
|
|
184
|
+
- `7` (default): native TypeScript 7 and [ember-content-mapper](https://github.com/NullVoxPopuli/ember-content-mapper) for `.gts` and `.gjs`.
|
|
185
|
+
`lint:types` runs `tsc --noEmit --runExternalCode`.
|
|
186
|
+
ESLint keeps TypeScript 6, because typescript-eslint needs its API.
|
|
187
|
+
- `6`: TypeScript 6 and Glint's `ember-tsc`.
|
|
188
|
+
|
|
189
|
+
A TypeScript 6 project moves to 7 when you choose 7.
|
|
190
|
+
A TypeScript 7 project stays on 7.
|
|
191
|
+
|
|
175
192
|
### expect-type (optional)
|
|
176
193
|
|
|
177
194
|
Type tests for TypeScript libraries with [expect-type](https://github.com/mmkal/expect-type).
|
|
@@ -188,7 +205,7 @@ Type tests for TypeScript libraries with [expect-type](https://github.com/mmkal/
|
|
|
188
205
|
2. **Selection**: the user selects which optional layers to include
|
|
189
206
|
3. **Execution**: each layer's `run()` function is called in sequence:
|
|
190
207
|
```js
|
|
191
|
-
await layer.run(project);
|
|
208
|
+
await layer.run(project, project.getLayerOptions(layer.name));
|
|
192
209
|
```
|
|
193
210
|
4. **Layer Functions**: inside `run()`, layers use [`ember-apply`](https://ember-apply.pages.dev/) to apply codemods:
|
|
194
211
|
- copy files from the `files/` directory
|
|
@@ -204,20 +221,34 @@ To add a new layer, create a new directory in `src/layers/` with:
|
|
|
204
221
|
1. **`index.js`** - Layer definition:
|
|
205
222
|
|
|
206
223
|
```js
|
|
224
|
+
// src/layers/itemizer/index.js
|
|
207
225
|
import { packageJson, files } from "ember-apply";
|
|
208
226
|
import { join } from "node:path";
|
|
209
227
|
|
|
210
228
|
export default {
|
|
211
229
|
label: "My Feature",
|
|
212
|
-
|
|
230
|
+
hint: "What this feature does",
|
|
231
|
+
|
|
232
|
+
// Optional: questions to ask when the layer is selected (see "Layer options")
|
|
233
|
+
options: {
|
|
234
|
+
maxItems: {
|
|
235
|
+
type: "number",
|
|
236
|
+
prompt: "Enter maximum item count:",
|
|
237
|
+
default: 10,
|
|
238
|
+
validate: (val) => (val > 0 ? undefined : "Must be greater than 0"),
|
|
239
|
+
},
|
|
240
|
+
},
|
|
213
241
|
|
|
214
|
-
async run(project) {
|
|
242
|
+
async run(project, { maxItems }) {
|
|
215
243
|
// Copy files from files/ directory
|
|
216
244
|
await files.applyFolder(join(import.meta.dirname, "files"), project.directory);
|
|
217
245
|
|
|
218
246
|
await packageJson.addDependencies({ "some-package": "^1.0.0" }, project.directory);
|
|
219
247
|
|
|
220
|
-
await packageJson.addScripts(
|
|
248
|
+
await packageJson.addScripts(
|
|
249
|
+
{ "my-script": `echo "Max items: ${maxItems}"` },
|
|
250
|
+
project.directory,
|
|
251
|
+
);
|
|
221
252
|
},
|
|
222
253
|
|
|
223
254
|
// Optionally add something to the README.md file
|
|
@@ -231,3 +262,33 @@ export default {
|
|
|
231
262
|
- files are copied to the target directory, keeping their structure
|
|
232
263
|
|
|
233
264
|
The CLI discovers the layer and offers it as an option.
|
|
265
|
+
|
|
266
|
+
### Layer options
|
|
267
|
+
|
|
268
|
+
A layer's `options` are questions that the CLI asks after the user selects the layer.
|
|
269
|
+
`run(project, options)` receives the answers, with defaults filled in.
|
|
270
|
+
`project.getLayerOptions(name)` returns the options of any layer.
|
|
271
|
+
|
|
272
|
+
Each option needs:
|
|
273
|
+
|
|
274
|
+
- `type`: `"text"`, `"number"`, `"select"`, `"confirm"`, or `"multiselect"`
|
|
275
|
+
- `prompt`: the question, also shown in `--help`
|
|
276
|
+
- `options`, for `select` and `multiselect`: the choices, as `{ value, label, hint }`
|
|
277
|
+
|
|
278
|
+
Each option can also have:
|
|
279
|
+
|
|
280
|
+
- `default`
|
|
281
|
+
- `validate`: the same as [clack's `validate`](https://github.com/bombshell-dev/clack/tree/main/packages/prompts#text).
|
|
282
|
+
A function that returns a message to reject the value, or a [Standard Schema](https://standardschema.dev).
|
|
283
|
+
- `detect(project)`: the value that an existing project uses now.
|
|
284
|
+
When updating a project, the question starts at this value instead of `default`.
|
|
285
|
+
|
|
286
|
+
Each option is also a CLI flag, `--<layer>.<option>`:
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
npx ember.nvp --layers eslint-bundled --eslint-bundled.preset nvp
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
- `confirm` options are on with `--<layer>.<option>`, and off with `--no-<layer>.<option>`.
|
|
293
|
+
- `multiselect` options take the flag more than once, or a comma-separated list.
|
|
294
|
+
- `npx ember.nvp --help` lists every option.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ember.nvp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.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",
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"exports": "./src/index.js",
|
|
20
20
|
"bin": "./src/cli/index.js",
|
|
21
21
|
"dependencies": {
|
|
22
|
+
"@clack/core": "^1.4.2",
|
|
22
23
|
"@clack/prompts": "^1.6.0",
|
|
23
24
|
"babel-remove-types": "^2.0.0",
|
|
24
25
|
"content-tag": "^4.2.0",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nullvoxpopuli/ember-rolldown",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.0",
|
|
4
4
|
"description": "Meta plugin for building ember v2 libraries/addons with rolldown & tsdown.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.ts",
|
|
@@ -46,10 +46,13 @@
|
|
|
46
46
|
"@babel/core": "^7.28.10 || ^8.0.0",
|
|
47
47
|
"@babel/plugin-transform-typescript": "^7.28.10 || ^8.0.0",
|
|
48
48
|
"@embroider/core": "^4.4.2",
|
|
49
|
+
"@jridgewell/remapping": "^2.3.5",
|
|
49
50
|
"@nullvoxpopuli/ember-build-tooling-utils": "workspace:*",
|
|
50
51
|
"babel-plugin-ember-template-compilation": "^4.0.0",
|
|
51
52
|
"content-tag": "^4.2.0",
|
|
52
|
-
"decorator-transforms": "^2.4.0"
|
|
53
|
+
"decorator-transforms": "^2.4.0",
|
|
54
|
+
"get-tsconfig": "^4.14.3",
|
|
55
|
+
"magic-string": "^1.4.2"
|
|
53
56
|
},
|
|
54
57
|
"engines": {
|
|
55
58
|
"node": ">= 24"
|
|
@@ -67,6 +70,7 @@
|
|
|
67
70
|
}
|
|
68
71
|
},
|
|
69
72
|
"devDependencies": {
|
|
73
|
+
"@jridgewell/trace-mapping": "^0.3.31",
|
|
70
74
|
"@tsdown/css": "^0.22.13",
|
|
71
75
|
"rolldown": "^1.2.0",
|
|
72
76
|
"tsdown": "^0.22.9",
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { originalPositionFor, TraceMap } from "@jridgewell/trace-mapping";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
1
3
|
import { mkdtemp, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
4
|
import { tmpdir } from "node:os";
|
|
3
5
|
import path from "node:path";
|
|
@@ -7,6 +9,10 @@ import { afterEach, describe, expect, it } from "vitest";
|
|
|
7
9
|
import { ember } from "../index.ts";
|
|
8
10
|
|
|
9
11
|
interface Dist {
|
|
12
|
+
/**
|
|
13
|
+
* The emitted directory.
|
|
14
|
+
*/
|
|
15
|
+
dir: string;
|
|
10
16
|
/**
|
|
11
17
|
* Sorted dist-relative paths of every emitted file.
|
|
12
18
|
*/
|
|
@@ -71,7 +77,7 @@ async function buildFixture(files: Record<string, string>, entry: string[]): Pro
|
|
|
71
77
|
});
|
|
72
78
|
|
|
73
79
|
const distDir = path.join(dir, "dist");
|
|
74
|
-
const dist: Dist = { files: [], contents: {} };
|
|
80
|
+
const dist: Dist = { dir: distDir, files: [], contents: {} };
|
|
75
81
|
|
|
76
82
|
for (const entry of await readdir(distDir, { withFileTypes: true, recursive: true })) {
|
|
77
83
|
if (!entry.isFile()) continue;
|
|
@@ -99,6 +105,41 @@ function printed(dist: Dist): string {
|
|
|
99
105
|
.join("\n");
|
|
100
106
|
}
|
|
101
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Follows each declaration in a `.d.ts` through its declaration map,
|
|
110
|
+
* the way an editor does on "go to definition".
|
|
111
|
+
*
|
|
112
|
+
* One line per declaration: the source the map names, and that source's line.
|
|
113
|
+
*/
|
|
114
|
+
async function traced(dist: Dist, file: string): Promise<string> {
|
|
115
|
+
const map = new TraceMap(await readFile(path.join(dist.dir, `${file}.map`), "utf8"));
|
|
116
|
+
const result: string[] = [];
|
|
117
|
+
|
|
118
|
+
for (const [index, line] of (dist.contents[file] ?? "").split("\n").entries()) {
|
|
119
|
+
const name = line.match(/\b(?:interface|class|const|function)\s+(\w+)/)?.[1];
|
|
120
|
+
if (!name) continue;
|
|
121
|
+
|
|
122
|
+
const { source, line: sourceLine } = originalPositionFor(map, {
|
|
123
|
+
line: index + 1,
|
|
124
|
+
column: line.indexOf(name),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
if (!source || sourceLine === null) {
|
|
128
|
+
result.push(`${name} -> (unmapped)`);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const sourcePath = path.resolve(dist.dir, source);
|
|
133
|
+
const text = existsSync(sourcePath)
|
|
134
|
+
? (await readFile(sourcePath, "utf8")).split("\n")[sourceLine - 1]?.trim()
|
|
135
|
+
: "(no such file)";
|
|
136
|
+
|
|
137
|
+
result.push(`${name} -> ${source}:${sourceLine} ${text}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return result.join("\n");
|
|
141
|
+
}
|
|
142
|
+
|
|
102
143
|
describe("declarations (full ember() via tsdown)", () => {
|
|
103
144
|
it("emits .d.ts for an entry that type-imports from a non-entry .gts", async () => {
|
|
104
145
|
// Mirrors a design-system shape:
|
|
@@ -300,6 +341,69 @@ describe("declarations (full ember() via tsdown)", () => {
|
|
|
300
341
|
`);
|
|
301
342
|
});
|
|
302
343
|
|
|
344
|
+
it("maps declarations back to their .gts source lines", async () => {
|
|
345
|
+
// Declaration maps let a consumer's "go to definition" open the library's source.
|
|
346
|
+
//
|
|
347
|
+
// A .gts module reaches the declaration pipeline as a virtual .ts id,
|
|
348
|
+
// holding content-tag's output (which moves lines around).
|
|
349
|
+
// The maps must still name the .gts file, at its own line numbers,
|
|
350
|
+
// including for a .gts with no <template> to compile.
|
|
351
|
+
const dist = await buildFixture(
|
|
352
|
+
{
|
|
353
|
+
"src/index.ts": [
|
|
354
|
+
`export { default as Popup, type AfterTemplate, type PopupBlock } from './popup.gts';`,
|
|
355
|
+
`export { double } from './helpers.gts';`,
|
|
356
|
+
`export { default as Plain, type PlainArgs } from './plain.ts';`,
|
|
357
|
+
].join("\n"),
|
|
358
|
+
"src/popup.gts": [
|
|
359
|
+
`import Component from '@glimmer/component';`,
|
|
360
|
+
``,
|
|
361
|
+
`export interface PopupBlock {`,
|
|
362
|
+
` open: () => void;`,
|
|
363
|
+
`}`,
|
|
364
|
+
``,
|
|
365
|
+
`export default class Popup extends Component<{ Blocks: { default: [PopupBlock] } }> {`,
|
|
366
|
+
` <template>`,
|
|
367
|
+
` <div class="popup">`,
|
|
368
|
+
` {{yield (hash open=this.open)}}`,
|
|
369
|
+
` </div>`,
|
|
370
|
+
` </template>`,
|
|
371
|
+
``,
|
|
372
|
+
` open = (): void => {};`,
|
|
373
|
+
`}`,
|
|
374
|
+
``,
|
|
375
|
+
`export interface AfterTemplate {`,
|
|
376
|
+
` after: boolean;`,
|
|
377
|
+
`}`,
|
|
378
|
+
].join("\n"),
|
|
379
|
+
"src/helpers.gts": [
|
|
380
|
+
`export function double(value: number): number {`,
|
|
381
|
+
` return value * 2;`,
|
|
382
|
+
`}`,
|
|
383
|
+
].join("\n"),
|
|
384
|
+
"src/plain.ts": [
|
|
385
|
+
`export interface PlainArgs {`,
|
|
386
|
+
` name: string;`,
|
|
387
|
+
`}`,
|
|
388
|
+
``,
|
|
389
|
+
`export default class Plain {`,
|
|
390
|
+
` args?: PlainArgs;`,
|
|
391
|
+
`}`,
|
|
392
|
+
].join("\n"),
|
|
393
|
+
},
|
|
394
|
+
["./src/index.ts"],
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
expect(await traced(dist, "index.d.ts")).toMatchInlineSnapshot(`
|
|
398
|
+
"PopupBlock -> ../src/popup.gts:3 export interface PopupBlock {
|
|
399
|
+
Popup -> ../src/popup.gts:7 export default class Popup extends Component<{ Blocks: { default: [PopupBlock] } }> {
|
|
400
|
+
AfterTemplate -> ../src/popup.gts:17 export interface AfterTemplate {
|
|
401
|
+
double -> ../src/helpers.gts:1 export function double(value: number): number {
|
|
402
|
+
PlainArgs -> ../src/plain.ts:1 export interface PlainArgs {
|
|
403
|
+
Plain -> ../src/plain.ts:5 export default class Plain {"
|
|
404
|
+
`);
|
|
405
|
+
});
|
|
406
|
+
|
|
303
407
|
it("bundles co-located CSS (via @tsdown/css) without breaking declarations", async () => {
|
|
304
408
|
// Ember components routinely import a co-located .css file.
|
|
305
409
|
//
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync, statSync } from "node:fs";
|
|
2
|
-
import {
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
3
|
|
|
4
|
+
import { parseTsconfig, type TsConfigJsonResolved } from "get-tsconfig";
|
|
4
5
|
import type { Plugin } from "rolldown";
|
|
5
6
|
import type { UserConfig } from "tsdown";
|
|
6
7
|
|
|
@@ -61,8 +62,11 @@ function resolveTsconfigPath(option: string | boolean | undefined): string | und
|
|
|
61
62
|
* Isolated declarations then constrain exactly the code that gets declarations emitted,
|
|
62
63
|
* and nothing else.
|
|
63
64
|
*
|
|
64
|
-
* Projects with no tsconfig (JavaScript libraries)
|
|
65
|
-
*
|
|
65
|
+
* Projects with no tsconfig (JavaScript libraries) have no declarations to emit,
|
|
66
|
+
* so there is nothing to check.
|
|
67
|
+
*
|
|
68
|
+
* The tsconfig is read without the `typescript` package.
|
|
69
|
+
* TypeScript 7 has no JavaScript API, and a TypeScript 7 library can have no TypeScript 6.
|
|
66
70
|
*
|
|
67
71
|
* `tsconfig: false` is different.
|
|
68
72
|
* It is an explicit opt-out.
|
|
@@ -88,64 +92,66 @@ export function emberIsolatedDeclarations(): Plugin {
|
|
|
88
92
|
emitsDeclarations = config.dts !== false;
|
|
89
93
|
},
|
|
90
94
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
95
|
+
buildStart: {
|
|
96
|
+
/**
|
|
97
|
+
* Before rolldown-plugin-dts, whose buildStart loads the `typescript` package
|
|
98
|
+
* when isolatedDeclarations is off.
|
|
99
|
+
*
|
|
100
|
+
* In a TypeScript 7 library that package is TypeScript 7, which has no JavaScript API,
|
|
101
|
+
* so it crashes before this message can show.
|
|
102
|
+
*/
|
|
103
|
+
order: "pre",
|
|
104
|
+
async handler() {
|
|
105
|
+
if (tsconfigOption === false) {
|
|
106
|
+
if (!emitsDeclarations) return;
|
|
107
|
+
|
|
108
|
+
// Without a tsconfig there is no `isolatedDeclarations`,
|
|
109
|
+
// so tsdown falls back to the tsc-based declaration pipeline.
|
|
110
|
+
//
|
|
111
|
+
// That pipeline reads source files from disk,
|
|
112
|
+
// and dies with "Source file not found" on every `.gts` / `.gjs`,
|
|
113
|
+
// since those only exist compiled in the module graph.
|
|
114
|
+
//
|
|
115
|
+
// Say so here instead of leaving that to be decoded.
|
|
116
|
+
this.error(
|
|
117
|
+
`\`tsconfig: false\` cannot be combined with declaration emit.\n\n` +
|
|
118
|
+
`Declarations are emitted by the isolated-declarations pipeline, which needs a ` +
|
|
119
|
+
`tsconfig setting "compilerOptions.isolatedDeclarations": true -- it is the only ` +
|
|
120
|
+
`pipeline that can see compiled .gts/.gjs modules.\n\n` +
|
|
121
|
+
`Either point tsdown's \`tsconfig\` option at such a config, or set \`dts: false\` ` +
|
|
122
|
+
`if this library ships no types.`,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const tsconfigPath = resolveTsconfigPath(tsconfigOption);
|
|
127
|
+
|
|
128
|
+
if (!tsconfigPath || !existsSync(tsconfigPath)) return;
|
|
129
|
+
|
|
130
|
+
let tsconfig: TsConfigJsonResolved;
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
// Resolves `extends` chains, so the flag may come from a base config.
|
|
134
|
+
tsconfig = parseTsconfig(tsconfigPath);
|
|
135
|
+
} catch {
|
|
136
|
+
// Unreadable / unparsable tsconfig.
|
|
137
|
+
// tsc itself will report this with a better message than we can.
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (tsconfig.compilerOptions?.isolatedDeclarations !== true) {
|
|
142
|
+
this.error(
|
|
143
|
+
`${tsconfigPath} must set "compilerOptions.isolatedDeclarations": true.\n\n` +
|
|
144
|
+
`Declarations for .gts/.gjs (template tag) modules can only be emitted by the ` +
|
|
145
|
+
`isolated-declarations pipeline, which reads compiled modules from the bundler's ` +
|
|
146
|
+
`module graph -- the tsc-based pipeline reads from disk and cannot see them.\n\n` +
|
|
147
|
+
`Isolated declarations require exported values to have explicit type annotations, ` +
|
|
148
|
+
`e.g. \`export const X: TOC<Sig> = <template>...\`.\n\n` +
|
|
149
|
+
`If this tsconfig also covers dev-only code (a demo app, in-package tests) that ` +
|
|
150
|
+
`should not be constrained this way, point tsdown's \`tsconfig\` option at a ` +
|
|
151
|
+
`publish-only tsconfig that sets the flag and covers just the published sources.`,
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
149
155
|
},
|
|
150
156
|
};
|
|
151
157
|
}
|