ember.nvp 1.7.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/.release-plan.json +45 -13
- package/CHANGELOG.md +18 -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 +105 -100
- package/packages/rolldown/index.ts +37 -24
- 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 +88 -69
- package/packages/rolldown/src/config.ts +21 -17
- package/packages/rolldown/src/dts.test.ts +42 -26
- package/packages/rolldown/src/externals.test.ts +16 -11
- package/packages/rolldown/src/externals.ts +18 -12
- 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 +16 -12
- 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/index.js +23 -18
- package/src/cli/questions/write.js +12 -10
- package/src/layers/are-the-types-wrong/index.js +4 -4
- package/src/layers/github-actions/index.js +7 -4
- package/src/layers/inspector-support/index.js +2 -2
- package/src/layers/qunit/index.js +18 -14
- package/src/layers/readme/index.js +2 -4
- package/src/layers/renovate/index.js +2 -1
- package/src/layers/typescript/index.js +12 -12
- package/src/layers/vitest/index.js +7 -3
- 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 +4 -13
- 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 +9 -8
- 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 +2 -2
- 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/shared.ts +19 -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 +10 -6
|
@@ -5,16 +5,16 @@ import type { Plugin } from "rolldown";
|
|
|
5
5
|
import type { UserConfig } from "tsdown";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* Resolve the tsconfig the build will
|
|
9
|
-
* `tsconfig` option (`string | boolean`):
|
|
8
|
+
* Resolve the tsconfig the build will use.
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
* -
|
|
13
|
-
* - a path to a
|
|
14
|
-
* -
|
|
10
|
+
* Mirrors tsdown's own `tsconfig` option (`string | boolean`):
|
|
11
|
+
* - `undefined` / `true`: `tsconfig.json` in the cwd
|
|
12
|
+
* - a path to a file: that file
|
|
13
|
+
* - a path to a directory: `tsconfig.json` inside it
|
|
14
|
+
* - `false`: no tsconfig at all
|
|
15
15
|
*
|
|
16
|
-
* Under plain rolldown there is no such option,
|
|
17
|
-
* is all there is.
|
|
16
|
+
* Under plain rolldown there is no such option,
|
|
17
|
+
* so the cwd's `tsconfig.json` is all there is.
|
|
18
18
|
*/
|
|
19
19
|
function resolveTsconfigPath(option: string | boolean | undefined): string | undefined {
|
|
20
20
|
if (option === false) return undefined;
|
|
@@ -33,33 +33,41 @@ function resolveTsconfigPath(option: string | boolean | undefined): string | und
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* Errors the build when the tsconfig it builds with
|
|
37
|
-
* `isolatedDeclarations: true`.
|
|
36
|
+
* Errors the build when the tsconfig it builds with
|
|
37
|
+
* does not set `isolatedDeclarations: true`.
|
|
38
38
|
*
|
|
39
|
-
* This is required
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* with "Source file not found".
|
|
39
|
+
* This is required.
|
|
40
|
+
* Declarations for `.gts` / `.gjs` (template tag) modules can only be emitted
|
|
41
|
+
* by the isolated-declarations pipeline,
|
|
42
|
+
* which reads compiled modules from the bundler's module graph.
|
|
44
43
|
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* same rules the build enforces (exported values need explicit type
|
|
48
|
-
* annotations, e.g. `export const X: TOC<Sig> = <template>...`).
|
|
44
|
+
* The `tsc`-based pipeline reads source files from disk.
|
|
45
|
+
* The compiled modules don't exist there, so it fails with "Source file not found".
|
|
49
46
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* sources -- isolated declarations then constrain exactly the code that gets
|
|
56
|
-
* declarations emitted for it, and nothing else.
|
|
47
|
+
* Failing fast here, rather than only when declaration emit happens to run,
|
|
48
|
+
* keeps the project honest.
|
|
49
|
+
* Editors and `tsc` / `ember-tsc` then check the same rules the build enforces:
|
|
50
|
+
* exported values need explicit type annotations,
|
|
51
|
+
* e.g. `export const X: TOC<Sig> = <template>...`
|
|
57
52
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
53
|
+
* The tsconfig checked is the one tsdown's `tsconfig` option points at,
|
|
54
|
+
* not necessarily `tsconfig.json`.
|
|
55
|
+
*
|
|
56
|
+
* So a library whose package also holds dev-only code (a demo app, in-package tests) can:
|
|
57
|
+
* - keep a permissive `tsconfig.json` covering everything,
|
|
58
|
+
* for editors and `tsc --noEmit`
|
|
59
|
+
* - point the build at a `tsconfig.publish.json` that covers only the publishable sources
|
|
60
|
+
*
|
|
61
|
+
* Isolated declarations then constrain exactly the code that gets declarations emitted,
|
|
62
|
+
* and nothing else.
|
|
63
|
+
*
|
|
64
|
+
* Projects with no tsconfig (JavaScript libraries) or without the `typescript` package
|
|
65
|
+
* have no declarations to emit, so there is nothing to check.
|
|
66
|
+
*
|
|
67
|
+
* `tsconfig: false` is different.
|
|
68
|
+
* It is an explicit opt-out.
|
|
69
|
+
* Combined with declaration emit, it produces a confusing "Source file not found"
|
|
70
|
+
* from the tsc-based pipeline, so it errors here instead.
|
|
63
71
|
*/
|
|
64
72
|
export function emberIsolatedDeclarations(): Plugin {
|
|
65
73
|
let tsconfigOption: string | boolean | undefined;
|
|
@@ -68,13 +76,15 @@ export function emberIsolatedDeclarations(): Plugin {
|
|
|
68
76
|
return {
|
|
69
77
|
name: "ember:isolated-declarations",
|
|
70
78
|
|
|
71
|
-
// Not called under plain rolldown, which leaves `tsconfigOption` undefined
|
|
72
|
-
//
|
|
79
|
+
// Not called under plain rolldown, which leaves `tsconfigOption` undefined.
|
|
80
|
+
// That means the cwd's tsconfig.json, the only thing it could mean there.
|
|
73
81
|
tsdownConfig(config: UserConfig) {
|
|
74
82
|
tsconfigOption = config.tsconfig;
|
|
75
|
-
// `emberConfig()` turns dts on unless the library turned it off,
|
|
76
|
-
// explicit `false` is the only "no declarations" signal.
|
|
77
|
-
//
|
|
83
|
+
// `emberConfig()` turns dts on unless the library turned it off,
|
|
84
|
+
// so an explicit `false` is the only "no declarations" signal.
|
|
85
|
+
//
|
|
86
|
+
// Hook order between plugins isn't guaranteed,
|
|
87
|
+
// so don't rely on it having run.
|
|
78
88
|
emitsDeclarations = config.dts !== false;
|
|
79
89
|
},
|
|
80
90
|
|
|
@@ -82,11 +92,14 @@ export function emberIsolatedDeclarations(): Plugin {
|
|
|
82
92
|
if (tsconfigOption === false) {
|
|
83
93
|
if (!emitsDeclarations) return;
|
|
84
94
|
|
|
85
|
-
// Without a tsconfig there is no `isolatedDeclarations`,
|
|
86
|
-
// falls back to the tsc-based declaration pipeline
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
95
|
+
// Without a tsconfig there is no `isolatedDeclarations`,
|
|
96
|
+
// so tsdown falls back to the tsc-based declaration pipeline.
|
|
97
|
+
//
|
|
98
|
+
// That pipeline reads source files from disk,
|
|
99
|
+
// and dies with "Source file not found" on every `.gts` / `.gjs`,
|
|
100
|
+
// since those only exist compiled in the module graph.
|
|
101
|
+
//
|
|
102
|
+
// Say so here instead of leaving that to be decoded.
|
|
90
103
|
this.error(
|
|
91
104
|
`\`tsconfig: false\` cannot be combined with declaration emit.\n\n` +
|
|
92
105
|
`Declarations are emitted by the isolated-declarations pipeline, which needs a ` +
|
|
@@ -112,8 +125,8 @@ export function emberIsolatedDeclarations(): Plugin {
|
|
|
112
125
|
const { config, error } = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
113
126
|
|
|
114
127
|
if (error) {
|
|
115
|
-
// Unreadable/unparsable tsconfig
|
|
116
|
-
// better message than we can.
|
|
128
|
+
// Unreadable / unparsable tsconfig.
|
|
129
|
+
// tsc itself will report this with a better message than we can.
|
|
117
130
|
return;
|
|
118
131
|
}
|
|
119
132
|
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* What this package hands back:
|
|
3
|
-
* `plugins`.
|
|
2
|
+
* What this package hands back:
|
|
3
|
+
* an object that rolldown and tsdown accept in `plugins`.
|
|
4
4
|
*
|
|
5
|
-
* Structurally this *is* rolldown's `Plugin
|
|
6
|
-
* member, every hook is optional
|
|
7
|
-
* directions: a consumer who imports rolldown's own type can still write
|
|
8
|
-
* `const plugins: Plugin[] = ember()`, and tsdown's `plugins` option lists
|
|
9
|
-
* `{ name: string }` among the shapes it takes.
|
|
5
|
+
* Structurally this *is* rolldown's `Plugin`.
|
|
6
|
+
* `name` is its only required member, every hook is optional.
|
|
10
7
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* rolldown's types instead, nominally distinct from the consumer's own.
|
|
8
|
+
* So the two are assignable in both directions:
|
|
9
|
+
* - a consumer who imports rolldown's own type can still write
|
|
10
|
+
* `const plugins: Plugin[] = ember()`
|
|
11
|
+
* - tsdown's `plugins` option lists `{ name: string }` among the shapes it takes
|
|
16
12
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
13
|
+
* Declaring it here, rather than re-exporting rolldown's type,
|
|
14
|
+
* keeps `rolldown` out of the published declarations.
|
|
15
|
+
*
|
|
16
|
+
* Otherwise it has to become a runtime `dependency`, purely so consumers can resolve a type.
|
|
17
|
+
* Leave it a devDependency, and the declaration bundler inlines a ~200kB private copy
|
|
18
|
+
* of rolldown's types instead, nominally distinct from the consumer's own.
|
|
19
|
+
*
|
|
20
|
+
* The precise hook signatures still apply inside this package.
|
|
21
|
+
* Each plugin function is typed with rolldown's real `Plugin` (a devDependency).
|
|
22
|
+
* This type only widens what crosses the package boundary.
|
|
20
23
|
*/
|
|
21
24
|
export interface RolldownPluginLike {
|
|
22
25
|
name: string;
|
|
@@ -5,10 +5,13 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
import { beforeAll, describe, expect, it } from "vitest";
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
* This package's own published shape
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* This package's own published shape.
|
|
9
|
+
*
|
|
10
|
+
* Consumers write their build config in TypeScript,
|
|
11
|
+
* so `import { ember } from "@nullvoxpopuli/ember-rolldown"` has to carry types.
|
|
12
|
+
*
|
|
13
|
+
* Without them it is an implicit `any` (TS7016),
|
|
14
|
+
* and a config file silently stops being type-checked.
|
|
12
15
|
*/
|
|
13
16
|
const packageDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
14
17
|
|
|
@@ -43,8 +46,8 @@ async function declarations() {
|
|
|
43
46
|
|
|
44
47
|
describe("published types", () => {
|
|
45
48
|
beforeAll(async () => {
|
|
46
|
-
// `clean` is on, but don't let these assertions depend on that
|
|
47
|
-
// artifacts from an earlier build satisfy every one of them on their own
|
|
49
|
+
// `clean` is on, but don't let these assertions depend on that.
|
|
50
|
+
// Stale artifacts from an earlier build satisfy every one of them on their own.
|
|
48
51
|
await rm(path.join(packageDir, "dist"), { recursive: true, force: true });
|
|
49
52
|
await execa("pnpm", ["build"], { cwd: packageDir });
|
|
50
53
|
}, 120_000);
|
|
@@ -66,14 +69,16 @@ describe("published types", () => {
|
|
|
66
69
|
|
|
67
70
|
/**
|
|
68
71
|
* A declaration may only reach for packages a consumer is guaranteed to have.
|
|
69
|
-
* Reference a devDependency and there are two ways to lose, both quiet:
|
|
70
|
-
* the specifier survives and consumers can't resolve it (`skipLibCheck` hides
|
|
71
|
-
* that, degrading the type to an error type), or the declaration bundler
|
|
72
|
-
* inlines the whole type surface — a ~200kB copy of rolldown's types, in the
|
|
73
|
-
* case that prompted this test, nominally distinct from the consumer's own.
|
|
74
72
|
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
73
|
+
* Reference a devDependency, and there are two ways to lose, both quiet:
|
|
74
|
+
* - the specifier survives and consumers can't resolve it
|
|
75
|
+
* (`skipLibCheck` hides that, degrading the type to an error type)
|
|
76
|
+
* - the declaration bundler inlines the whole type surface
|
|
77
|
+
* (a ~200kB copy of rolldown's types, in the case that prompted this test,
|
|
78
|
+
* nominally distinct from the consumer's own)
|
|
79
|
+
*
|
|
80
|
+
* `ember()` returns plugins,
|
|
81
|
+
* so `RolldownPluginLike` exists to keep rolldown on the devDependency side of that line.
|
|
77
82
|
*/
|
|
78
83
|
it("declarations only reference packages consumers have", async () => {
|
|
79
84
|
const manifest = await readManifest();
|
|
@@ -8,10 +8,15 @@ import { describe, expect, it } from "vitest";
|
|
|
8
8
|
import { emberTransform } from "./transform.ts";
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Drives the plugin through a real rolldown build.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* Drives the plugin through a real rolldown build.
|
|
12
|
+
*
|
|
13
|
+
* `files` is a map of relative path -> source.
|
|
14
|
+
* The build entry is `index.ts`.
|
|
15
|
+
*
|
|
16
|
+
* Every bare (package) specifier is marked external,
|
|
17
|
+
* so the build only exercises the local `.gts` / `.gjs` / `.ts` handling
|
|
18
|
+
* and doesn't need real dependencies installed.
|
|
19
|
+
*
|
|
15
20
|
* Returns the concatenated code of every emitted chunk.
|
|
16
21
|
*/
|
|
17
22
|
async function bundle(
|
|
@@ -45,8 +50,8 @@ async function bundle(
|
|
|
45
50
|
.map((chunk) => chunk.code)
|
|
46
51
|
.join("\n");
|
|
47
52
|
|
|
48
|
-
// rolldown's `//#region <path>` comments reference the random temp dir
|
|
49
|
-
//
|
|
53
|
+
// rolldown's `//#region <path>` comments reference the random temp dir.
|
|
54
|
+
// Collapse them to the bare filename so snapshots stay deterministic.
|
|
50
55
|
return code.replace(/(\/\/#region ).*\/([^/\n]+)$/gm, "$1$2");
|
|
51
56
|
}
|
|
52
57
|
|
|
@@ -180,11 +185,13 @@ describe("emberTransform (full plugin via rolldown)", () => {
|
|
|
180
185
|
|
|
181
186
|
it("resolves an absolute .gts specifier imported from a plugin's virtual module", async () => {
|
|
182
187
|
/**
|
|
183
|
-
* The shape a code-generating plugin emits:
|
|
184
|
-
* with `\0`, so it exists nowhere on disk)
|
|
185
|
-
* real files by absolute path,
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
+
* The shape a code-generating plugin emits:
|
|
189
|
+
* a virtual module (id prefixed with `\0`, so it exists nowhere on disk)
|
|
190
|
+
* whose generated source imports real files by absolute path,
|
|
191
|
+
* because it has no directory to be relative to.
|
|
192
|
+
*
|
|
193
|
+
* `path.dirname` of such an id is `"\0."`,
|
|
194
|
+
* so the specifier must not be resolved against the importer's directory.
|
|
188
195
|
*/
|
|
189
196
|
const registry = (dir: string): Plugin[] => {
|
|
190
197
|
const source = "./registry?virtual=views";
|
|
@@ -225,10 +232,12 @@ describe("emberTransform (full plugin via rolldown)", () => {
|
|
|
225
232
|
it("compiles a .gts module used directly as an entry (no importer)", async () => {
|
|
226
233
|
const code = await bundle(
|
|
227
234
|
{
|
|
228
|
-
// Both an entry .gts and a .ts entry importing it
|
|
229
|
-
//
|
|
230
|
-
//
|
|
231
|
-
//
|
|
235
|
+
// Both an entry .gts and a .ts entry importing it.
|
|
236
|
+
//
|
|
237
|
+
// The shared module must resolve to ONE id
|
|
238
|
+
// (the entry resolution realpaths, matching rolldown's own resolver),
|
|
239
|
+
// so it lands in the entry chunk and the index chunk imports it,
|
|
240
|
+
// rather than duplicating the code.
|
|
232
241
|
"index.ts": `export { default as Foo } from './foo.gts';`,
|
|
233
242
|
"foo.gts": [
|
|
234
243
|
`import Component from '@glimmer/component';`,
|
|
@@ -299,8 +308,8 @@ describe("emberTransform (full plugin via rolldown)", () => {
|
|
|
299
308
|
|
|
300
309
|
const chunk = output.find((entry) => entry.type === "chunk" && "map" in entry);
|
|
301
310
|
expect(chunk).toBeDefined();
|
|
302
|
-
// The composed map traces through the rewrite back to the original .gts
|
|
303
|
-
//
|
|
311
|
+
// The composed map traces through the rewrite back to the original .gts source.
|
|
312
|
+
// (content-tag's map survives the specifier-rewrite pass)
|
|
304
313
|
const sources = (chunk as { map?: { sources: string[] } }).map?.sources ?? [];
|
|
305
314
|
expect(sources.some((source) => source.endsWith("foo.gts"))).toBe(true);
|
|
306
315
|
});
|
|
@@ -7,9 +7,10 @@ import type { Plugin } from "rolldown";
|
|
|
7
7
|
const processor = new Preprocessor();
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* `.d.ts` files emitted for `.gts` source reference the original `.gts`
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* `.d.ts` files emitted for `.gts` source reference the original `.gts` specifiers.
|
|
11
|
+
*
|
|
12
|
+
* Consumers can't resolve those,
|
|
13
|
+
* so we rewrite them back to extension-less specifiers.
|
|
13
14
|
*/
|
|
14
15
|
function fixDeclarationImports(content: string): string {
|
|
15
16
|
return content
|
|
@@ -41,10 +42,12 @@ async function fixDtsExtensionsInDir(dir: string): Promise<void> {
|
|
|
41
42
|
/**
|
|
42
43
|
* A sourcemap mapping every generated line to the same line of the input.
|
|
43
44
|
*
|
|
44
|
-
* `AAAA` is the [0, 0, 0, 0] VLQ segment
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
* - `AAAA` is the [0, 0, 0, 0] VLQ segment
|
|
46
|
+
* (first line maps to source 0, line 0, column 0)
|
|
47
|
+
* - `AACA` is [0, 0, +1, 0]
|
|
48
|
+
* (each following line advances the source line by one)
|
|
49
|
+
*
|
|
50
|
+
* Exact at line granularity, which is all the specifier rewrite can disturb.
|
|
48
51
|
*/
|
|
49
52
|
function lineIdentityMap(id: string, source: string) {
|
|
50
53
|
return {
|
|
@@ -60,8 +63,10 @@ function lineIdentityMap(id: string, source: string) {
|
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
/**
|
|
63
|
-
*
|
|
64
|
-
* ts
|
|
66
|
+
* Preprocesses `<template>` via content-tag, and maps
|
|
67
|
+
* `.gts` → `.ts`
|
|
68
|
+
* `.gjs` → `.js`
|
|
69
|
+
* so rolldown can identify them as ts / js.
|
|
65
70
|
*/
|
|
66
71
|
export function emberTransform(): Plugin {
|
|
67
72
|
return {
|
|
@@ -70,21 +75,26 @@ export function emberTransform(): Plugin {
|
|
|
70
75
|
resolveId: {
|
|
71
76
|
order: "pre",
|
|
72
77
|
handler(id, importer) {
|
|
73
|
-
// Entries have no importer
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
78
|
+
// Entries have no importer.
|
|
79
|
+
// Their ids come straight from the build config
|
|
80
|
+
// (tsdown expands entry globs to on-disk paths).
|
|
81
|
+
//
|
|
82
|
+
// Resolve a `.gts` / `.gjs` entry to the same virtual `.ts` / `.js` id
|
|
83
|
+
// an imported module gets, so the load hook compiles it via content-tag
|
|
84
|
+
// instead of the raw `<template>` source hitting the parser.
|
|
85
|
+
//
|
|
86
|
+
// Entries may be any extension.
|
|
87
|
+
// The emitted `.js` / `.d.ts` paths mirror the entry paths either way
|
|
88
|
+
// (that's how tsdown's dts support works).
|
|
80
89
|
if (!importer) {
|
|
81
90
|
if (!id.endsWith(".gts") && !id.endsWith(".gjs")) return null;
|
|
82
91
|
if (!existsSync(id)) return null;
|
|
83
92
|
|
|
84
|
-
// rolldown's default resolver realpaths entry ids
|
|
85
|
-
// /var -> /private/var).
|
|
86
|
-
//
|
|
87
|
-
//
|
|
93
|
+
// rolldown's default resolver realpaths entry ids
|
|
94
|
+
// (e.g. macOS /var -> /private/var).
|
|
95
|
+
//
|
|
96
|
+
// Match it, or this same file imported from another entry
|
|
97
|
+
// resolves to a second module id and gets duplicated into both chunks.
|
|
88
98
|
const fileName = realpathSync(path.resolve(id));
|
|
89
99
|
|
|
90
100
|
return {
|
|
@@ -93,13 +103,16 @@ export function emberTransform(): Plugin {
|
|
|
93
103
|
};
|
|
94
104
|
}
|
|
95
105
|
|
|
96
|
-
// An absolute specifier already names the file,
|
|
97
|
-
//
|
|
98
|
-
// directory would corrupt it.
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
106
|
+
// An absolute specifier already names the file,
|
|
107
|
+
// so it needs no importer-relative resolution.
|
|
108
|
+
// Joining it onto the importer's directory would corrupt it.
|
|
109
|
+
//
|
|
110
|
+
// That is not hypothetical.
|
|
111
|
+
// A plugin's virtual module (id prefixed with `\0`)
|
|
112
|
+
// can generate imports of on-disk files by absolute path,
|
|
113
|
+
// and `path.dirname("\0./registry")` is `"\0."`.
|
|
114
|
+
// Joining then yields `"\0./Users/.../thing.gts"`:
|
|
115
|
+
// a path no `existsSync` / `readFile` can accept (it has a null byte in it).
|
|
103
116
|
const fileName = path.isAbsolute(id) ? id : path.join(path.dirname(importer), id);
|
|
104
117
|
|
|
105
118
|
if (id.endsWith(".gts")) {
|
|
@@ -116,15 +129,19 @@ export function emberTransform(): Plugin {
|
|
|
116
129
|
};
|
|
117
130
|
}
|
|
118
131
|
|
|
119
|
-
// A `.ts
|
|
120
|
-
// resolves to the virtual module.
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
125
|
-
//
|
|
126
|
-
//
|
|
127
|
-
//
|
|
132
|
+
// A `.ts` / `.js` specifier whose only backing file is a `.gts` / `.gjs`
|
|
133
|
+
// resolves to the virtual module.
|
|
134
|
+
//
|
|
135
|
+
// This must also serve `.d.ts` importers.
|
|
136
|
+
// rolldown-plugin-dts resolves a declaration module's imports
|
|
137
|
+
// through the plugin pipeline (`this.resolve`).
|
|
138
|
+
// When the resolution is a source file, it loads it (registering its declaration)
|
|
139
|
+
// before mapping the import to the declaration id.
|
|
140
|
+
//
|
|
141
|
+
// Virtual modules exist nowhere else.
|
|
142
|
+
// If we don't answer here, the import either:
|
|
143
|
+
// - fails to resolve (no file on disk)
|
|
144
|
+
// - or, worse, resolves to a declaration id that was never registered
|
|
128
145
|
if (id.endsWith(".ts")) {
|
|
129
146
|
const gtsFileName = fileName.replace(/\.ts$/, ".gts");
|
|
130
147
|
if (existsSync(gtsFileName) && !existsSync(fileName)) {
|
|
@@ -152,9 +169,11 @@ export function emberTransform(): Plugin {
|
|
|
152
169
|
const meta = this.getModuleInfo(id)?.meta ?? {};
|
|
153
170
|
let fileName = meta?.fileName;
|
|
154
171
|
|
|
155
|
-
// A virtual id can be loaded without having passed through our
|
|
156
|
-
//
|
|
157
|
-
//
|
|
172
|
+
// A virtual id can be loaded without having passed through our resolveId
|
|
173
|
+
// (rolldown-plugin-dts calls `this.load({ id })` with just the id),
|
|
174
|
+
// so no meta is attached.
|
|
175
|
+
//
|
|
176
|
+
// Recover the backing file from disk.
|
|
158
177
|
if (!fileName && !existsSync(id)) {
|
|
159
178
|
if (id.endsWith(".ts") && existsSync(id.replace(/\.ts$/, ".gts"))) {
|
|
160
179
|
fileName = id.replace(/\.ts$/, ".gts");
|
|
@@ -188,12 +207,13 @@ export function emberTransform(): Plugin {
|
|
|
188
207
|
id: /\.(js|ts)$/,
|
|
189
208
|
},
|
|
190
209
|
handler(input, id) {
|
|
191
|
-
// Rewrite `.gts` specifiers to `.ts
|
|
192
|
-
// runtime modules AND declaration modules alike.
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
//
|
|
210
|
+
// Rewrite `.gts` specifiers to `.ts`, the (virtual) source id,
|
|
211
|
+
// in runtime modules AND declaration modules alike.
|
|
212
|
+
//
|
|
213
|
+
// Declaration modules must NOT be rewritten to `.d.ts`.
|
|
214
|
+
// rolldown-plugin-dts's resolver treats a source-file resolution as
|
|
215
|
+
// "load it (registering its declaration), then map to the declaration id".
|
|
216
|
+
// A `.d.ts` id is returned as-is: unloadable when the module isn't registered yet
|
|
197
217
|
// (e.g. the `.gts` is only ever type-imported).
|
|
198
218
|
const output = input.replace(
|
|
199
219
|
/(['"`])((?:\.\.?\/|\/|@|[A-Za-z0-9_\-])[^'"]*?\.gts)\1/g,
|
|
@@ -204,9 +224,9 @@ export function emberTransform(): Plugin {
|
|
|
204
224
|
return null;
|
|
205
225
|
}
|
|
206
226
|
|
|
207
|
-
// The rewrite only shortens import specifiers in place,
|
|
208
|
-
// line-identity map is accurate to the line
|
|
209
|
-
// everything before the first rewritten specifier on a line).
|
|
227
|
+
// The rewrite only shortens import specifiers in place,
|
|
228
|
+
// so a line-identity map is accurate to the line
|
|
229
|
+
// (and to the column, for everything before the first rewritten specifier on a line).
|
|
210
230
|
return { code: output, map: lineIdentityMap(id, input) };
|
|
211
231
|
},
|
|
212
232
|
},
|
package/packages/vite/README.md
CHANGED
|
@@ -8,30 +8,29 @@ A speed-optimized default meta-config for ember projects.
|
|
|
8
8
|
npm add @nullvoxpopuli/ember-vite
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Requires node 24
|
|
12
|
-
|
|
13
|
-
`esnext`).
|
|
11
|
+
Requires node 24+. These packages ship TypeScript source, so type-checking
|
|
12
|
+
needs TypeScript 6+ with `lib` covering `es2025` (for example `esnext`).
|
|
14
13
|
|
|
15
14
|
## Usage
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
In your vite config:
|
|
18
17
|
|
|
19
18
|
```js
|
|
20
19
|
import { defineConfig } from "vite";
|
|
21
20
|
import { ember } from "@nullvoxpopuli/ember-vite";
|
|
22
21
|
|
|
23
22
|
export default defineConfig({
|
|
24
|
-
plugins: [
|
|
25
|
-
|
|
26
|
-
],
|
|
27
|
-
}
|
|
23
|
+
plugins: [ember()],
|
|
24
|
+
});
|
|
28
25
|
```
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
Then:
|
|
28
|
+
|
|
29
|
+
1. Remove any plugins from embroider or babel.
|
|
30
|
+
2. Delete from your package.json: `@rollup/plugin-babel`, `@embroider/core`, `@embroider/vite`.
|
|
32
31
|
|
|
33
32
|
> [!NOTE]
|
|
34
|
-
>
|
|
33
|
+
> Linting still needs the babel related deps, so keep those.
|
|
35
34
|
|
|
36
35
|
## Requirements
|
|
37
36
|
|