ember.nvp 1.8.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 +15 -35
- package/CHANGELOG.md +33 -0
- package/README.md +73 -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 +170 -48
- package/src/cli/generator.js +2 -1
- package/src/cli/help.js +82 -0
- 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/files/type-tests/index.test.ts +4 -0
- package/src/layers/expect-type/files/type-tests/tsconfig.json +8 -0
- package/src/layers/expect-type/index.js +114 -0
- 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 +142 -1
- package/test/ember.nvp/layer-expect-type.test.ts +113 -0
- 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 +2 -2
- package/test/ember.nvp/permutations/shared.ts +6 -6
- package/test/ember.nvp/validate-option.test.ts +152 -0
- package/test/test-helpers/permutate.test.ts +2 -7
- package/test/test-helpers.ts +12 -2
- package/vitest.config.js +7 -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,64 +2,44 @@
|
|
|
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"
|
|
15
19
|
},
|
|
16
20
|
"@nullvoxpopuli/ember-build-tooling-utils": {
|
|
17
|
-
"
|
|
18
|
-
"oldVersion": "1.1.1",
|
|
19
|
-
"newVersion": "1.2.0",
|
|
20
|
-
"tagName": "latest",
|
|
21
|
-
"constraints": [
|
|
22
|
-
{
|
|
23
|
-
"impact": "minor",
|
|
24
|
-
"reason": "Appears in changelog section :rocket: Enhancement"
|
|
25
|
-
}
|
|
26
|
-
],
|
|
27
|
-
"pkgJSONPath": "./packages/build-tooling-utils/package.json"
|
|
21
|
+
"oldVersion": "1.2.0"
|
|
28
22
|
},
|
|
29
23
|
"@nullvoxpopuli/ember-rolldown": {
|
|
30
24
|
"impact": "minor",
|
|
31
|
-
"oldVersion": "2.
|
|
32
|
-
"newVersion": "2.
|
|
25
|
+
"oldVersion": "2.9.0",
|
|
26
|
+
"newVersion": "2.10.0",
|
|
33
27
|
"tagName": "latest",
|
|
34
28
|
"constraints": [
|
|
35
|
-
{
|
|
36
|
-
"impact": "patch",
|
|
37
|
-
"reason": "Has dependency `workspace:*` on @nullvoxpopuli/ember-build-tooling-utils"
|
|
38
|
-
},
|
|
39
29
|
{
|
|
40
30
|
"impact": "minor",
|
|
41
31
|
"reason": "Appears in changelog section :rocket: Enhancement"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"impact": "patch",
|
|
35
|
+
"reason": "Appears in changelog section :bug: Bug Fix"
|
|
42
36
|
}
|
|
43
37
|
],
|
|
44
38
|
"pkgJSONPath": "./packages/rolldown/package.json"
|
|
45
39
|
},
|
|
46
40
|
"@nullvoxpopuli/ember-vite": {
|
|
47
|
-
"
|
|
48
|
-
"oldVersion": "1.1.1",
|
|
49
|
-
"newVersion": "1.2.0",
|
|
50
|
-
"tagName": "latest",
|
|
51
|
-
"constraints": [
|
|
52
|
-
{
|
|
53
|
-
"impact": "patch",
|
|
54
|
-
"reason": "Has dependency `workspace:*` on @nullvoxpopuli/ember-build-tooling-utils"
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"impact": "minor",
|
|
58
|
-
"reason": "Appears in changelog section :rocket: Enhancement"
|
|
59
|
-
}
|
|
60
|
-
],
|
|
61
|
-
"pkgJSONPath": "./packages/vite/package.json"
|
|
41
|
+
"oldVersion": "1.2.0"
|
|
62
42
|
}
|
|
63
43
|
},
|
|
64
|
-
"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"
|
|
65
45
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
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
|
+
|
|
23
|
+
## Release (2026-09-28)
|
|
24
|
+
|
|
25
|
+
* ember.nvp 1.9.0 (minor)
|
|
26
|
+
|
|
27
|
+
#### :rocket: Enhancement
|
|
28
|
+
* `ember.nvp`
|
|
29
|
+
* [#139](https://github.com/NullVoxPopuli/ember.nvp/pull/139) CLI help ([@tcjr](https://github.com/tcjr))
|
|
30
|
+
* [#137](https://github.com/NullVoxPopuli/ember.nvp/pull/137) Add an expect-type layer for type tests ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
31
|
+
|
|
32
|
+
#### Committers: 2
|
|
33
|
+
- @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
34
|
+
- Tom Carter ([@tcjr](https://github.com/tcjr))
|
|
35
|
+
|
|
3
36
|
## Release (2026-09-21)
|
|
4
37
|
|
|
5
38
|
* ember.nvp 1.8.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,26 @@ 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
|
+
|
|
192
|
+
### expect-type (optional)
|
|
193
|
+
|
|
194
|
+
Type tests for TypeScript libraries with [expect-type](https://github.com/mmkal/expect-type).
|
|
195
|
+
|
|
196
|
+
- Tests live in `type-tests/`, with their own tsconfig.
|
|
197
|
+
- `lint:type-tests` typechecks them, and `lint` runs it too.
|
|
198
|
+
- JavaScript projects and apps get nothing.
|
|
199
|
+
|
|
175
200
|
## Architecture
|
|
176
201
|
|
|
177
202
|
### How Layers Work
|
|
@@ -180,7 +205,7 @@ Super experimental vitest setup using [ember-vitest](https://github.com/NullVoxP
|
|
|
180
205
|
2. **Selection**: the user selects which optional layers to include
|
|
181
206
|
3. **Execution**: each layer's `run()` function is called in sequence:
|
|
182
207
|
```js
|
|
183
|
-
await layer.run(project);
|
|
208
|
+
await layer.run(project, project.getLayerOptions(layer.name));
|
|
184
209
|
```
|
|
185
210
|
4. **Layer Functions**: inside `run()`, layers use [`ember-apply`](https://ember-apply.pages.dev/) to apply codemods:
|
|
186
211
|
- copy files from the `files/` directory
|
|
@@ -196,20 +221,34 @@ To add a new layer, create a new directory in `src/layers/` with:
|
|
|
196
221
|
1. **`index.js`** - Layer definition:
|
|
197
222
|
|
|
198
223
|
```js
|
|
224
|
+
// src/layers/itemizer/index.js
|
|
199
225
|
import { packageJson, files } from "ember-apply";
|
|
200
226
|
import { join } from "node:path";
|
|
201
227
|
|
|
202
228
|
export default {
|
|
203
229
|
label: "My Feature",
|
|
204
|
-
|
|
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
|
+
},
|
|
205
241
|
|
|
206
|
-
async run(project) {
|
|
242
|
+
async run(project, { maxItems }) {
|
|
207
243
|
// Copy files from files/ directory
|
|
208
244
|
await files.applyFolder(join(import.meta.dirname, "files"), project.directory);
|
|
209
245
|
|
|
210
246
|
await packageJson.addDependencies({ "some-package": "^1.0.0" }, project.directory);
|
|
211
247
|
|
|
212
|
-
await packageJson.addScripts(
|
|
248
|
+
await packageJson.addScripts(
|
|
249
|
+
{ "my-script": `echo "Max items: ${maxItems}"` },
|
|
250
|
+
project.directory,
|
|
251
|
+
);
|
|
213
252
|
},
|
|
214
253
|
|
|
215
254
|
// Optionally add something to the README.md file
|
|
@@ -223,3 +262,33 @@ export default {
|
|
|
223
262
|
- files are copied to the target directory, keeping their structure
|
|
224
263
|
|
|
225
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
|
}
|