ember.nvp 1.1.0 → 1.3.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 +5 -21
- package/CHANGELOG.md +59 -0
- package/README.md +5 -0
- package/package.json +1 -1
- package/packages/rolldown/README.md +96 -9
- package/packages/rolldown/package.json +1 -1
- package/packages/rolldown/src/app-reexports.test.ts +35 -2
- package/packages/rolldown/src/app-reexports.ts +14 -3
- package/packages/rolldown/src/babel.test.ts +17 -0
- package/packages/rolldown/src/babel.ts +21 -3
- package/packages/rolldown/src/dts.test.ts +238 -38
- package/packages/rolldown/src/externals.test.ts +118 -0
- package/packages/rolldown/src/externals.ts +45 -0
- package/packages/rolldown/src/isolated-declarations.test.ts +122 -0
- package/packages/rolldown/src/isolated-declarations.ts +56 -8
- package/packages/rolldown/src/transform.test.ts +88 -2
- package/packages/rolldown/src/transform.ts +87 -33
- package/src/layers/github-actions/index.js +3 -0
- package/src/layers/prettier/index.js +12 -0
- package/src/layers/readme/index.js +176 -0
- package/src/layers/typescript/index.js +11 -0
- package/src/layers/vitest/index.js +11 -0
- package/src/utils/project.js +7 -0
- package/src/utils/types.ts +5 -0
- package/test/ember.nvp/layer-readme.test.ts +195 -0
- package/test/ember.nvp/layers.test.ts +1 -0
- package/test/ember.nvp/scoped-css-library.test.ts +211 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
+
import { generate } from "#test-helpers";
|
|
3
|
+
import { rm } from "node:fs/promises";
|
|
4
|
+
import type { Project } from "ember.nvp";
|
|
5
|
+
|
|
6
|
+
describe("layer: readme", () => {
|
|
7
|
+
const dirs: string[] = [];
|
|
8
|
+
|
|
9
|
+
afterAll(async () => {
|
|
10
|
+
if (process.env.CI) return;
|
|
11
|
+
|
|
12
|
+
for (const dir of dirs) {
|
|
13
|
+
await rm(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe("App project README generation", () => {
|
|
18
|
+
let project: Project;
|
|
19
|
+
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
project = await generate({
|
|
22
|
+
type: "app",
|
|
23
|
+
name: "my-app",
|
|
24
|
+
packageManager: "pnpm",
|
|
25
|
+
layers: ["readme", "git", "prettier", "typescript"],
|
|
26
|
+
});
|
|
27
|
+
dirs.push(project.directory);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it("generates README.md with project info and selected layer docs", async () => {
|
|
31
|
+
expect(await project.read("README.md")).toMatchInlineSnapshot(`
|
|
32
|
+
"# my-app
|
|
33
|
+
|
|
34
|
+
An Ember application created with \`ember.nvp\`.
|
|
35
|
+
|
|
36
|
+
## Getting Started
|
|
37
|
+
|
|
38
|
+
### Prerequisites
|
|
39
|
+
|
|
40
|
+
- Node.js >= 24
|
|
41
|
+
- pnpm
|
|
42
|
+
|
|
43
|
+
### Installation
|
|
44
|
+
|
|
45
|
+
~~~sh
|
|
46
|
+
pnpm install
|
|
47
|
+
~~~
|
|
48
|
+
|
|
49
|
+
### Development
|
|
50
|
+
|
|
51
|
+
To start the local development server:
|
|
52
|
+
|
|
53
|
+
~~~sh
|
|
54
|
+
pnpm dev
|
|
55
|
+
~~~
|
|
56
|
+
|
|
57
|
+
or
|
|
58
|
+
|
|
59
|
+
~~~sh
|
|
60
|
+
pnpm start
|
|
61
|
+
~~~
|
|
62
|
+
|
|
63
|
+
## Features & Tooling
|
|
64
|
+
|
|
65
|
+
### Prettier
|
|
66
|
+
|
|
67
|
+
Code formatting is managed with [Prettier](https://prettier.io/).
|
|
68
|
+
|
|
69
|
+
- \`pnpm format\` - Format code
|
|
70
|
+
- \`pnpm lint:prettier\` - Check code formatting
|
|
71
|
+
|
|
72
|
+
### TypeScript
|
|
73
|
+
|
|
74
|
+
This project uses TypeScript and Glint for static type checking.
|
|
75
|
+
|
|
76
|
+
- \`pnpm lint:types\` - Typecheck code with Glint/TypeScript
|
|
77
|
+
"
|
|
78
|
+
`);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("Library project README generation with npm", () => {
|
|
83
|
+
let project: Project;
|
|
84
|
+
|
|
85
|
+
beforeAll(async () => {
|
|
86
|
+
project = await generate({
|
|
87
|
+
type: "library",
|
|
88
|
+
name: "my-lib",
|
|
89
|
+
packageManager: "npm",
|
|
90
|
+
layers: ["readme", "prettier"],
|
|
91
|
+
});
|
|
92
|
+
dirs.push(project.directory);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("generates README.md with npm-specific commands for library", async () => {
|
|
96
|
+
expect(await project.read("README.md")).toMatchInlineSnapshot(`
|
|
97
|
+
"# my-lib
|
|
98
|
+
|
|
99
|
+
An Ember library/addon created with \`ember.nvp\`.
|
|
100
|
+
|
|
101
|
+
## Getting Started
|
|
102
|
+
|
|
103
|
+
### Prerequisites
|
|
104
|
+
|
|
105
|
+
- Node.js >= 24
|
|
106
|
+
- npm
|
|
107
|
+
|
|
108
|
+
### Development & Building
|
|
109
|
+
|
|
110
|
+
To build the library:
|
|
111
|
+
|
|
112
|
+
~~~sh
|
|
113
|
+
npm run build
|
|
114
|
+
~~~
|
|
115
|
+
|
|
116
|
+
## Features & Tooling
|
|
117
|
+
|
|
118
|
+
### Prettier
|
|
119
|
+
|
|
120
|
+
Code formatting is managed with [Prettier](https://prettier.io/).
|
|
121
|
+
|
|
122
|
+
- \`npm run format\` - Format code
|
|
123
|
+
- \`npm run lint:prettier\` - Check code formatting
|
|
124
|
+
"
|
|
125
|
+
`);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe("Extension project README generation", () => {
|
|
130
|
+
let project: Project;
|
|
131
|
+
|
|
132
|
+
beforeAll(async () => {
|
|
133
|
+
project = await generate({
|
|
134
|
+
type: "extension",
|
|
135
|
+
name: "my-extension",
|
|
136
|
+
packageManager: "pnpm",
|
|
137
|
+
layers: ["readme", "prettier"],
|
|
138
|
+
});
|
|
139
|
+
dirs.push(project.directory);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("generates README.md for browser extension project", async () => {
|
|
143
|
+
expect(await project.read("README.md")).toMatchInlineSnapshot(`
|
|
144
|
+
"# my-extension
|
|
145
|
+
|
|
146
|
+
A browser extension using Ember created with \`ember.nvp\`.
|
|
147
|
+
|
|
148
|
+
## Getting Started
|
|
149
|
+
|
|
150
|
+
### Prerequisites
|
|
151
|
+
|
|
152
|
+
- Node.js >= 24
|
|
153
|
+
- pnpm
|
|
154
|
+
|
|
155
|
+
### Development
|
|
156
|
+
|
|
157
|
+
To start development:
|
|
158
|
+
|
|
159
|
+
~~~sh
|
|
160
|
+
pnpm dev
|
|
161
|
+
~~~
|
|
162
|
+
|
|
163
|
+
or
|
|
164
|
+
|
|
165
|
+
~~~sh
|
|
166
|
+
pnpm start
|
|
167
|
+
~~~
|
|
168
|
+
|
|
169
|
+
### Building
|
|
170
|
+
|
|
171
|
+
To build the library:
|
|
172
|
+
|
|
173
|
+
~~~sh
|
|
174
|
+
pnpm build
|
|
175
|
+
~~~
|
|
176
|
+
|
|
177
|
+
or
|
|
178
|
+
|
|
179
|
+
~~~sh
|
|
180
|
+
pnpm build:watch
|
|
181
|
+
~~~
|
|
182
|
+
|
|
183
|
+
## Features & Tooling
|
|
184
|
+
|
|
185
|
+
### Prettier
|
|
186
|
+
|
|
187
|
+
Code formatting is managed with [Prettier](https://prettier.io/).
|
|
188
|
+
|
|
189
|
+
- \`pnpm format\` - Format code
|
|
190
|
+
- \`pnpm lint:prettier\` - Check code formatting
|
|
191
|
+
"
|
|
192
|
+
`);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { describe, it, beforeAll, afterAll, expect } from "vitest";
|
|
2
|
+
import { generate, listFiles } from "#test-helpers";
|
|
3
|
+
import { execa } from "execa";
|
|
4
|
+
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
|
|
7
|
+
import type { Project } from "ember.nvp";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Integration test for building a library that uses ember-scoped-css through
|
|
11
|
+
* `@nullvoxpopuli/ember-rolldown` (tsdown):
|
|
12
|
+
*
|
|
13
|
+
* - the `scopedCSS()` unplugin (ember-scoped-css/rollup) resolves and rewrites
|
|
14
|
+
* the `?scoped=` CSS requests the template transform injects
|
|
15
|
+
* - the template transform + babel plugin ride along via `ember()`'s
|
|
16
|
+
* `babel.templateTransforms` / `babel.plugins` options
|
|
17
|
+
* - `@tsdown/css` with `css.inject` bundles the scoped CSS into
|
|
18
|
+
* `dist/style.css` and keeps the import in `dist/index.js`, so consuming
|
|
19
|
+
* apps load the styles through the module graph
|
|
20
|
+
*
|
|
21
|
+
* Covers all three authoring modes: co-located `.css`, inline
|
|
22
|
+
* `<style scoped>`, and the `scopedClass` pseudo-helper in module code.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
const source = {
|
|
26
|
+
"tsdown.config.js": `import { defineConfig } from "tsdown";
|
|
27
|
+
import { ember } from "@nullvoxpopuli/ember-rolldown";
|
|
28
|
+
import { scopedCSS } from "ember-scoped-css/rollup";
|
|
29
|
+
import { scopedCSS as scopedCssBabel } from "ember-scoped-css/babel";
|
|
30
|
+
|
|
31
|
+
export default defineConfig({
|
|
32
|
+
entry: ["./src/index.ts"],
|
|
33
|
+
css: { inject: true },
|
|
34
|
+
plugins: [
|
|
35
|
+
ember({
|
|
36
|
+
babel: {
|
|
37
|
+
plugins: [scopedCssBabel()],
|
|
38
|
+
templateTransforms: [scopedCssBabel.template({})],
|
|
39
|
+
},
|
|
40
|
+
}),
|
|
41
|
+
scopedCSS(),
|
|
42
|
+
],
|
|
43
|
+
});
|
|
44
|
+
`,
|
|
45
|
+
"src/index.ts": `export { Badge, type BadgeSignature } from "./components/badge.gts";
|
|
46
|
+
export { Banner, type BannerSignature } from "./components/banner.gts";
|
|
47
|
+
export { Chip, type ChipSignature, chipClass } from "./components/chip.gts";
|
|
48
|
+
`,
|
|
49
|
+
// co-located CSS
|
|
50
|
+
"src/components/badge.gts": `import type { TOC } from "@ember/component/template-only";
|
|
51
|
+
|
|
52
|
+
export interface BadgeSignature {
|
|
53
|
+
Element: HTMLSpanElement;
|
|
54
|
+
Blocks: {
|
|
55
|
+
default: [];
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const Badge: TOC<BadgeSignature> = <template>
|
|
60
|
+
<span class="badge" ...attributes>{{yield}}</span>
|
|
61
|
+
</template>;
|
|
62
|
+
`,
|
|
63
|
+
"src/components/badge.css": `.badge {
|
|
64
|
+
border-radius: 0.25rem;
|
|
65
|
+
padding: 0.25rem 0.5rem;
|
|
66
|
+
}
|
|
67
|
+
`,
|
|
68
|
+
// inline <style scoped>
|
|
69
|
+
"src/components/banner.gts": `import type { TOC } from "@ember/component/template-only";
|
|
70
|
+
|
|
71
|
+
export interface BannerSignature {
|
|
72
|
+
Element: HTMLDivElement;
|
|
73
|
+
Blocks: {
|
|
74
|
+
default: [];
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const Banner: TOC<BannerSignature> = <template>
|
|
79
|
+
<style scoped>
|
|
80
|
+
.banner {
|
|
81
|
+
border: 1px solid;
|
|
82
|
+
}
|
|
83
|
+
</style>
|
|
84
|
+
<div class="banner" ...attributes>{{yield}}</div>
|
|
85
|
+
</template>;
|
|
86
|
+
`,
|
|
87
|
+
// scopedClass pseudo-helper in module code
|
|
88
|
+
"src/components/chip.gts": `import type { TOC } from "@ember/component/template-only";
|
|
89
|
+
import { scopedClass } from "ember-scoped-css";
|
|
90
|
+
|
|
91
|
+
export const chipClass: string = scopedClass("chip");
|
|
92
|
+
|
|
93
|
+
export interface ChipSignature {
|
|
94
|
+
Element: HTMLSpanElement;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export const Chip: TOC<ChipSignature> = <template>
|
|
98
|
+
<span class="chip" ...attributes></span>
|
|
99
|
+
</template>;
|
|
100
|
+
`,
|
|
101
|
+
"src/components/chip.css": `.chip {
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
}
|
|
104
|
+
`,
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
async function writeSource(project: Project) {
|
|
108
|
+
for (let [filePath, contents] of Object.entries(source)) {
|
|
109
|
+
let target = join(project.directory, filePath);
|
|
110
|
+
|
|
111
|
+
await mkdir(dirname(target), { recursive: true });
|
|
112
|
+
await writeFile(target, contents);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function addDependencies(project: Project) {
|
|
117
|
+
let manifestPath = join(project.directory, "package.json");
|
|
118
|
+
let manifest = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
119
|
+
|
|
120
|
+
// @tsdown/css releases in lockstep with tsdown; both ranges track 0.22.x
|
|
121
|
+
manifest.devDependencies["@tsdown/css"] = "^0.22.13";
|
|
122
|
+
manifest.devDependencies["ember-scoped-css"] = "^3.1.0";
|
|
123
|
+
|
|
124
|
+
await writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
describe("library with ember-scoped-css", () => {
|
|
128
|
+
let project: Project;
|
|
129
|
+
|
|
130
|
+
beforeAll(async () => {
|
|
131
|
+
project = await generate({ type: "library", name: "scoped-lib", layers: ["typescript"] });
|
|
132
|
+
|
|
133
|
+
await writeSource(project);
|
|
134
|
+
await addDependencies(project);
|
|
135
|
+
|
|
136
|
+
let install = await execa("pnpm install", { cwd: project.directory, shell: true });
|
|
137
|
+
expect(install.exitCode).toBe(0);
|
|
138
|
+
|
|
139
|
+
let build = await execa("pnpm build", { cwd: project.directory, shell: true });
|
|
140
|
+
expect(build.exitCode).toBe(0);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
afterAll(async () => {
|
|
144
|
+
if (process.env.CI) return;
|
|
145
|
+
|
|
146
|
+
await rm(project.directory, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("emits the expected files", async () => {
|
|
150
|
+
expect(await listFiles(join(project.directory, "dist"))).toMatchInlineSnapshot(`
|
|
151
|
+
[
|
|
152
|
+
"index.d.ts",
|
|
153
|
+
"index.d.ts.map",
|
|
154
|
+
"index.js",
|
|
155
|
+
"index.js.map",
|
|
156
|
+
"style.css",
|
|
157
|
+
]
|
|
158
|
+
`);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("scopes classes in the precompiled templates and keeps the CSS import", async () => {
|
|
162
|
+
let output = await project.read("dist/index.js");
|
|
163
|
+
|
|
164
|
+
expect(output).toMatchInlineSnapshot(`
|
|
165
|
+
"import './style.css';
|
|
166
|
+
import { precompileTemplate } from "@ember/template-compilation";
|
|
167
|
+
import { setComponentTemplate } from "@ember/component";
|
|
168
|
+
import templateOnly from "@ember/component/template-only";
|
|
169
|
+
//#region src/components/badge.ts
|
|
170
|
+
const Badge = setComponentTemplate(precompileTemplate("<span class=\\"badge_e1025e732\\" ...attributes>{{yield}}</span>", { strictMode: true }), templateOnly());
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/components/banner.ts
|
|
173
|
+
const Banner = setComponentTemplate(precompileTemplate("\\n<div class=\\"banner_e88952290\\" ...attributes>{{yield}}</div>", { strictMode: true }), templateOnly());
|
|
174
|
+
//#endregion
|
|
175
|
+
//#region src/components/chip.ts
|
|
176
|
+
const chipClass = "chip_ec34dd962";
|
|
177
|
+
const Chip = setComponentTemplate(precompileTemplate("<span class=\\"chip_ec34dd962\\" ...attributes></span>", { strictMode: true }), templateOnly());
|
|
178
|
+
//#endregion
|
|
179
|
+
export { Badge, Banner, Chip, chipClass };
|
|
180
|
+
|
|
181
|
+
//# sourceMappingURL=index.js.map"
|
|
182
|
+
`);
|
|
183
|
+
|
|
184
|
+
// the scopedClass import is compiled away; nothing of ember-scoped-css
|
|
185
|
+
// remains at runtime
|
|
186
|
+
expect(output).not.toContain("ember-scoped-css");
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("rewrites and bundles all three CSS sources", async () => {
|
|
190
|
+
expect(await project.read("dist/style.css")).toMatchInlineSnapshot(`
|
|
191
|
+
".badge_e1025e732 {
|
|
192
|
+
border-radius: .25rem;
|
|
193
|
+
padding: .25rem .5rem;
|
|
194
|
+
}
|
|
195
|
+
.banner_e88952290 {
|
|
196
|
+
border: 1px solid;
|
|
197
|
+
}
|
|
198
|
+
.chip_ec34dd962 {
|
|
199
|
+
display: inline-flex;
|
|
200
|
+
}
|
|
201
|
+
"
|
|
202
|
+
`);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it("emits declarations untouched by scoping", async () => {
|
|
206
|
+
let declarations = await project.read("dist/index.d.ts");
|
|
207
|
+
|
|
208
|
+
expect(declarations).toContain("declare const Badge: TOC<BadgeSignature>");
|
|
209
|
+
expect(declarations).not.toContain("badge_");
|
|
210
|
+
});
|
|
211
|
+
});
|