ember.nvp 1.8.0 → 1.9.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 +6 -44
- package/CHANGELOG.md +13 -0
- package/README.md +8 -0
- package/package.json +1 -1
- package/src/cli/args.js +65 -32
- package/src/cli/help.js +32 -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 +104 -0
- package/test/ember.nvp/cli.test.ts +56 -0
- package/test/ember.nvp/layer-expect-type.test.ts +113 -0
- package/test/ember.nvp/layers.test.ts +1 -0
- package/test/ember.nvp/permutations/shared.ts +3 -2
- package/test/test-helpers.ts +4 -1
- package/vitest.config.js +7 -1
package/.release-plan.json
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
"solution": {
|
|
3
3
|
"ember.nvp": {
|
|
4
4
|
"impact": "minor",
|
|
5
|
-
"oldVersion": "1.
|
|
6
|
-
"newVersion": "1.
|
|
5
|
+
"oldVersion": "1.8.0",
|
|
6
|
+
"newVersion": "1.9.0",
|
|
7
7
|
"tagName": "latest",
|
|
8
8
|
"constraints": [
|
|
9
9
|
{
|
|
@@ -14,52 +14,14 @@
|
|
|
14
14
|
"pkgJSONPath": "./package.json"
|
|
15
15
|
},
|
|
16
16
|
"@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"
|
|
17
|
+
"oldVersion": "1.2.0"
|
|
28
18
|
},
|
|
29
19
|
"@nullvoxpopuli/ember-rolldown": {
|
|
30
|
-
"
|
|
31
|
-
"oldVersion": "2.8.1",
|
|
32
|
-
"newVersion": "2.9.0",
|
|
33
|
-
"tagName": "latest",
|
|
34
|
-
"constraints": [
|
|
35
|
-
{
|
|
36
|
-
"impact": "patch",
|
|
37
|
-
"reason": "Has dependency `workspace:*` on @nullvoxpopuli/ember-build-tooling-utils"
|
|
38
|
-
},
|
|
39
|
-
{
|
|
40
|
-
"impact": "minor",
|
|
41
|
-
"reason": "Appears in changelog section :rocket: Enhancement"
|
|
42
|
-
}
|
|
43
|
-
],
|
|
44
|
-
"pkgJSONPath": "./packages/rolldown/package.json"
|
|
20
|
+
"oldVersion": "2.9.0"
|
|
45
21
|
},
|
|
46
22
|
"@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"
|
|
23
|
+
"oldVersion": "1.2.0"
|
|
62
24
|
}
|
|
63
25
|
},
|
|
64
|
-
"description": "## Release (2026-09-
|
|
26
|
+
"description": "## Release (2026-09-28)\n\n* ember.nvp 1.9.0 (minor)\n\n#### :rocket: Enhancement\n* `ember.nvp`\n * [#139](https://github.com/NullVoxPopuli/ember.nvp/pull/139) CLI help ([@tcjr](https://github.com/tcjr))\n * [#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))\n\n#### Committers: 2\n- @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n- Tom Carter ([@tcjr](https://github.com/tcjr))\n"
|
|
65
27
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Release (2026-09-28)
|
|
4
|
+
|
|
5
|
+
* ember.nvp 1.9.0 (minor)
|
|
6
|
+
|
|
7
|
+
#### :rocket: Enhancement
|
|
8
|
+
* `ember.nvp`
|
|
9
|
+
* [#139](https://github.com/NullVoxPopuli/ember.nvp/pull/139) CLI help ([@tcjr](https://github.com/tcjr))
|
|
10
|
+
* [#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))
|
|
11
|
+
|
|
12
|
+
#### Committers: 2
|
|
13
|
+
- @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
14
|
+
- Tom Carter ([@tcjr](https://github.com/tcjr))
|
|
15
|
+
|
|
3
16
|
## Release (2026-09-21)
|
|
4
17
|
|
|
5
18
|
* ember.nvp 1.8.0 (minor)
|
package/README.md
CHANGED
|
@@ -172,6 +172,14 @@ Code formatting with:
|
|
|
172
172
|
|
|
173
173
|
Super experimental vitest setup using [ember-vitest](https://github.com/NullVoxPopuli/ember-vitest)
|
|
174
174
|
|
|
175
|
+
### expect-type (optional)
|
|
176
|
+
|
|
177
|
+
Type tests for TypeScript libraries with [expect-type](https://github.com/mmkal/expect-type).
|
|
178
|
+
|
|
179
|
+
- Tests live in `type-tests/`, with their own tsconfig.
|
|
180
|
+
- `lint:type-tests` typechecks them, and `lint` runs it too.
|
|
181
|
+
- JavaScript projects and apps get nothing.
|
|
182
|
+
|
|
175
183
|
## Architecture
|
|
176
184
|
|
|
177
185
|
### How Layers Work
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ember.nvp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.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",
|
package/src/cli/args.js
CHANGED
|
@@ -1,48 +1,81 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
2
|
import { styleText, parseArgs } from "node:util";
|
|
3
|
+
import { printHelp } from "./help.js";
|
|
3
4
|
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
export const coreOptions = /** @type {const} */ ({
|
|
6
|
+
help: {
|
|
7
|
+
type: "boolean",
|
|
8
|
+
short: "h",
|
|
9
|
+
description: "Show CLI help and option details",
|
|
10
|
+
},
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
name: {
|
|
13
|
+
type: "string",
|
|
14
|
+
description: "Name of the project",
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
path: {
|
|
18
|
+
type: "string",
|
|
19
|
+
description: "Target directory path for the project",
|
|
20
|
+
},
|
|
13
21
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
22
|
+
type: {
|
|
23
|
+
type: "string",
|
|
24
|
+
choices: ["app", "addon", "library", "extension", "custom-element"],
|
|
25
|
+
description: "Type of project to generate",
|
|
26
|
+
},
|
|
18
27
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
confirm: {
|
|
29
|
+
type: "string",
|
|
30
|
+
choices: ["yes", "no"],
|
|
31
|
+
description: "Bypass target confirmation step",
|
|
32
|
+
},
|
|
23
33
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
34
|
+
layers: {
|
|
35
|
+
type: "string",
|
|
36
|
+
multiple: true,
|
|
37
|
+
description: "Layers to apply to the project (repeat for multiple layers)",
|
|
38
|
+
},
|
|
28
39
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
packageManager: {
|
|
41
|
+
type: "string",
|
|
42
|
+
choices: ["npm", "pnpm"],
|
|
43
|
+
description: "Package manager to configure for the project",
|
|
44
|
+
},
|
|
33
45
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
46
|
+
replaceOrUpdate: {
|
|
47
|
+
type: "string",
|
|
48
|
+
choices: ["replace", "update"],
|
|
49
|
+
description: "Strategy to use if target directory exists",
|
|
50
|
+
},
|
|
38
51
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
write: {
|
|
53
|
+
type: "string",
|
|
54
|
+
choices: ["yes", "no"],
|
|
55
|
+
description: "Confirm writing changes to disk",
|
|
43
56
|
},
|
|
44
57
|
});
|
|
45
58
|
|
|
59
|
+
/**
|
|
60
|
+
* This lightweight check is here because Node's `parseArgs` is in strict mode.
|
|
61
|
+
* If a user runs `npx ember.nvp --help` alongside invalid or unknown flags,
|
|
62
|
+
* standard strict parsing would throw a `TypeError (ERR_PARSE_ARGS_UNKNOWN_OPTION)`
|
|
63
|
+
* before reaching any help handler. Pre-checking raw arguments ensures `--help`
|
|
64
|
+
* always prints successfully regardless of invalid flags.
|
|
65
|
+
*/
|
|
66
|
+
const isHelpRequested = process.argv.slice(2).some((arg) => arg === "--help" || arg === "-h");
|
|
67
|
+
if (isHelpRequested) {
|
|
68
|
+
printHelp(coreOptions);
|
|
69
|
+
process.exit(0);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The CLI options are parsed using Node's `parseArgs` in default strict mode.
|
|
74
|
+
*/
|
|
75
|
+
const { values } = parseArgs({
|
|
76
|
+
options: coreOptions,
|
|
77
|
+
});
|
|
78
|
+
|
|
46
79
|
const { replaceOrUpdate, name, type, layers = [], packageManager, path, confirm, write } = values;
|
|
47
80
|
|
|
48
81
|
export const answers = {
|
package/src/cli/help.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { styleText } from "node:util";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Print help text to stdout.
|
|
5
|
+
*
|
|
6
|
+
* @param {Record<string, any>} coreOptions
|
|
7
|
+
*/
|
|
8
|
+
export function printHelp(coreOptions) {
|
|
9
|
+
const title = styleText(["bgCyan", "black"], " ember.nvp ");
|
|
10
|
+
console.log(`${title}\n`);
|
|
11
|
+
console.log(`${styleText("bold", "Usage:")} npx ember.nvp [options]\n`);
|
|
12
|
+
|
|
13
|
+
console.log(styleText("bold", "Core Options:"));
|
|
14
|
+
for (const [name, config] of Object.entries(coreOptions)) {
|
|
15
|
+
const flag = `--${name}`;
|
|
16
|
+
const alias = config.short ? `-${config.short}, ` : " ";
|
|
17
|
+
const typeStr = config.type ? styleText("dim", `<${config.type}>`) : "";
|
|
18
|
+
const desc = config.description || "";
|
|
19
|
+
const choices = config.choices
|
|
20
|
+
? styleText(
|
|
21
|
+
"yellow",
|
|
22
|
+
` [choices: ${config.choices.map((/** @type {string} */ c) => `"${c}"`).join(", ")}]`,
|
|
23
|
+
)
|
|
24
|
+
: "";
|
|
25
|
+
|
|
26
|
+
console.log(` ${styleText("cyan", alias)}${styleText("cyan", flag)} ${typeStr}`);
|
|
27
|
+
if (desc || choices) {
|
|
28
|
+
console.log(` ${desc}${choices}`);
|
|
29
|
+
}
|
|
30
|
+
console.log("");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { packageJson } from "ember-apply";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { applyFolderTo } from "#utils/fs.js";
|
|
4
|
+
import { getLatest } from "#utils/npm.js";
|
|
5
|
+
|
|
6
|
+
const deps = {
|
|
7
|
+
"expect-type": "^1.3.0",
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The library tsconfig only includes `src` (it drives declaration output),
|
|
12
|
+
* so type tests get their own project.
|
|
13
|
+
*/
|
|
14
|
+
const scripts = {
|
|
15
|
+
"lint:type-tests": "ember-tsc --noEmit --project type-tests",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @type {import('#types').Layer}
|
|
20
|
+
*/
|
|
21
|
+
export default {
|
|
22
|
+
label: "expect-type",
|
|
23
|
+
hint: "type tests for the published API",
|
|
24
|
+
|
|
25
|
+
async run(project) {
|
|
26
|
+
// Apps aren't published, so they have no public API to pin
|
|
27
|
+
if (!project.isLibrary) return;
|
|
28
|
+
|
|
29
|
+
if (!(await project.hasOrWantsLayer("typescript"))) return;
|
|
30
|
+
|
|
31
|
+
await applyFolderTo(join(import.meta.dirname, "files"), project);
|
|
32
|
+
await packageJson.addDevDependencies(await getLatest(deps), project.directory);
|
|
33
|
+
await packageJson.addScripts(scripts, project.directory);
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @overload
|
|
38
|
+
* @param {import('#utils/project.js').Project} project
|
|
39
|
+
* @param {true} explain
|
|
40
|
+
* @returns {Promise<{ isSetup: boolean; reasons: string[] }>}
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* @overload
|
|
44
|
+
* @param {import('#utils/project.js').Project} project
|
|
45
|
+
* @param {boolean | undefined} [explain]
|
|
46
|
+
* @returns {Promise<boolean>}
|
|
47
|
+
*/
|
|
48
|
+
async isSetup(project, explain) {
|
|
49
|
+
/** @type {string[]} */
|
|
50
|
+
const reasons = [];
|
|
51
|
+
|
|
52
|
+
// Nothing to set up outside TypeScript libraries,
|
|
53
|
+
// so nothing can be missing
|
|
54
|
+
if (!project.isLibrary || !(await project.hasOrWantsLayer("typescript"))) {
|
|
55
|
+
return explain ? { isSetup: true, reasons } : true;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!project.hasFile("type-tests/tsconfig.json")) {
|
|
59
|
+
if (!explain) return false;
|
|
60
|
+
|
|
61
|
+
reasons.push("type-tests/tsconfig.json is missing");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let manifest = await packageJson.read(project.directory);
|
|
65
|
+
|
|
66
|
+
for (let script of Object.keys(scripts)) {
|
|
67
|
+
if (!manifest.scripts?.[script]) {
|
|
68
|
+
if (!explain) return false;
|
|
69
|
+
|
|
70
|
+
reasons.push(`package.json is missing the "${script}" script`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (let dep of Object.keys(deps)) {
|
|
75
|
+
if (!manifest.devDependencies?.[dep]) {
|
|
76
|
+
if (!explain) return false;
|
|
77
|
+
|
|
78
|
+
reasons.push(`package.json is missing ${dep} in devDependencies`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (explain) {
|
|
83
|
+
return {
|
|
84
|
+
isSetup: reasons.length === 0,
|
|
85
|
+
reasons,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return reasons.length === 0;
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @param {import('#utils/project.js').Project} project
|
|
94
|
+
*/
|
|
95
|
+
async readme(project) {
|
|
96
|
+
if (!project.isLibrary || !(await project.hasOrWantsLayer("typescript"))) return;
|
|
97
|
+
|
|
98
|
+
return `### Type tests
|
|
99
|
+
|
|
100
|
+
Type tests in \`type-tests/\` use [expect-type](https://github.com/mmkal/expect-type) to pin the types of the public API.
|
|
101
|
+
|
|
102
|
+
- \`${project.runPrefix} lint:type-tests\` - Typecheck the type tests`;
|
|
103
|
+
},
|
|
104
|
+
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { test, expect as hardExpect } from "vitest";
|
|
2
2
|
import { cli } from "#test-helpers";
|
|
3
|
+
import { stripVTControlCharacters } from "node:util";
|
|
3
4
|
|
|
4
5
|
const expect = hardExpect.soft;
|
|
5
6
|
|
|
@@ -10,3 +11,58 @@ test.skip("cli works", async () => {
|
|
|
10
11
|
|
|
11
12
|
expect(execaPromise.exitCode).toBe(0);
|
|
12
13
|
});
|
|
14
|
+
|
|
15
|
+
const BASIC_HELP_OUTPUT = `
|
|
16
|
+
" ember.nvp
|
|
17
|
+
|
|
18
|
+
Usage: npx ember.nvp [options]
|
|
19
|
+
|
|
20
|
+
Core Options:
|
|
21
|
+
-h, --help <boolean>
|
|
22
|
+
Show CLI help and option details
|
|
23
|
+
|
|
24
|
+
--name <string>
|
|
25
|
+
Name of the project
|
|
26
|
+
|
|
27
|
+
--path <string>
|
|
28
|
+
Target directory path for the project
|
|
29
|
+
|
|
30
|
+
--type <string>
|
|
31
|
+
Type of project to generate [choices: "app", "addon", "library", "extension", "custom-element"]
|
|
32
|
+
|
|
33
|
+
--confirm <string>
|
|
34
|
+
Bypass target confirmation step [choices: "yes", "no"]
|
|
35
|
+
|
|
36
|
+
--layers <string>
|
|
37
|
+
Layers to apply to the project (repeat for multiple layers)
|
|
38
|
+
|
|
39
|
+
--packageManager <string>
|
|
40
|
+
Package manager to configure for the project [choices: "npm", "pnpm"]
|
|
41
|
+
|
|
42
|
+
--replaceOrUpdate <string>
|
|
43
|
+
Strategy to use if target directory exists [choices: "replace", "update"]
|
|
44
|
+
|
|
45
|
+
--write <string>
|
|
46
|
+
Confirm writing changes to disk [choices: "yes", "no"]
|
|
47
|
+
"
|
|
48
|
+
`;
|
|
49
|
+
|
|
50
|
+
test("cli --help outputs usage and core options", async () => {
|
|
51
|
+
let { execaPromise } = cli(["--help"]);
|
|
52
|
+
|
|
53
|
+
const res = await execaPromise;
|
|
54
|
+
|
|
55
|
+
expect(res.exitCode).toBe(0);
|
|
56
|
+
const outStr = stripVTControlCharacters(res.stdout);
|
|
57
|
+
expect(outStr).toMatchInlineSnapshot(BASIC_HELP_OUTPUT);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("cli -h alias works", async () => {
|
|
61
|
+
let { execaPromise } = cli(["-h"]);
|
|
62
|
+
|
|
63
|
+
const res = await execaPromise;
|
|
64
|
+
|
|
65
|
+
expect(res.exitCode).toBe(0);
|
|
66
|
+
const outStr = stripVTControlCharacters(res.stdout);
|
|
67
|
+
expect(outStr).toMatchInlineSnapshot(BASIC_HELP_OUTPUT);
|
|
68
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { describe, it, beforeAll, afterAll, expect } from "vitest";
|
|
2
|
+
import { generate, expectIsSetup, layers } from "#test-helpers";
|
|
3
|
+
import { writeLibrarySource } from "./library-src-fixtures.ts";
|
|
4
|
+
import { execa } from "execa";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { rm, writeFile } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
|
|
9
|
+
import type { Project } from "ember.nvp";
|
|
10
|
+
|
|
11
|
+
const expectType = layers.find((layer) => layer.name === "expect-type")!;
|
|
12
|
+
|
|
13
|
+
async function typecheck(project: Project) {
|
|
14
|
+
return execa("pnpm lint:type-tests", {
|
|
15
|
+
cwd: project.directory,
|
|
16
|
+
shell: true,
|
|
17
|
+
all: true,
|
|
18
|
+
reject: false,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("layer: expect-type", () => {
|
|
23
|
+
const dirs: string[] = [];
|
|
24
|
+
|
|
25
|
+
afterAll(async () => {
|
|
26
|
+
if (process.env.CI) return;
|
|
27
|
+
|
|
28
|
+
for (const dir of dirs) {
|
|
29
|
+
await rm(dir, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("TypeScript library", () => {
|
|
34
|
+
let project: Project;
|
|
35
|
+
|
|
36
|
+
beforeAll(async () => {
|
|
37
|
+
project = await generate({
|
|
38
|
+
type: "library",
|
|
39
|
+
name: "my-lib",
|
|
40
|
+
layers: ["typescript", "expect-type"],
|
|
41
|
+
});
|
|
42
|
+
dirs.push(project.directory);
|
|
43
|
+
|
|
44
|
+
await writeLibrarySource(project, "typescript");
|
|
45
|
+
|
|
46
|
+
let install = await execa("pnpm install", { cwd: project.directory, shell: true });
|
|
47
|
+
expect(install.exitCode).toBe(0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("is setup", async () => {
|
|
51
|
+
await expectIsSetup(project, expectType);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("the generated type tests pass", async () => {
|
|
55
|
+
let result = await typecheck(project);
|
|
56
|
+
|
|
57
|
+
expect(result.exitCode, result.all).toBe(0);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("checks the library's real exports", async () => {
|
|
61
|
+
await writeFile(
|
|
62
|
+
join(project.directory, "type-tests/math.test.ts"),
|
|
63
|
+
`import { expectTypeOf } from "expect-type";
|
|
64
|
+
import { add } from "../src/index.ts";
|
|
65
|
+
|
|
66
|
+
expectTypeOf(add).parameters.toEqualTypeOf<[number, number]>();
|
|
67
|
+
expectTypeOf(add).returns.toEqualTypeOf<number>();
|
|
68
|
+
`,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
let result = await typecheck(project);
|
|
72
|
+
|
|
73
|
+
expect(result.exitCode, result.all).toBe(0);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("fails on a wrong type assertion", async () => {
|
|
77
|
+
await writeFile(
|
|
78
|
+
join(project.directory, "type-tests/wrong.test.ts"),
|
|
79
|
+
`import { expectTypeOf } from "expect-type";
|
|
80
|
+
import { add } from "../src/index.ts";
|
|
81
|
+
|
|
82
|
+
expectTypeOf(add).returns.toEqualTypeOf<string>();
|
|
83
|
+
`,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
let result = await typecheck(project);
|
|
87
|
+
|
|
88
|
+
expect(result.exitCode).not.toBe(0);
|
|
89
|
+
expect(result.all).toContain("wrong.test.ts");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("keeps type tests out of the build", async () => {
|
|
93
|
+
await rm(join(project.directory, "type-tests/wrong.test.ts"));
|
|
94
|
+
|
|
95
|
+
let build = await execa("pnpm build", { cwd: project.directory, shell: true });
|
|
96
|
+
expect(build.exitCode).toBe(0);
|
|
97
|
+
|
|
98
|
+
expect(existsSync(join(project.directory, "dist/type-tests"))).toBe(false);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe("JavaScript library", () => {
|
|
103
|
+
it("adds nothing (no declarations to test)", async () => {
|
|
104
|
+
let project = await generate({ type: "library", name: "my-lib", layers: ["expect-type"] });
|
|
105
|
+
dirs.push(project.directory);
|
|
106
|
+
|
|
107
|
+
await expectIsSetup(project, expectType);
|
|
108
|
+
|
|
109
|
+
expect(existsSync(join(project.directory, "type-tests"))).toBe(false);
|
|
110
|
+
expect(await project.read("package.json")).not.toContain("expect-type");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -28,11 +28,11 @@ const INITIAL_ONLY = new Set<string>([
|
|
|
28
28
|
*/
|
|
29
29
|
const NOT_YET_SUPPORTED: Record<ProjectType, Set<string>> = {
|
|
30
30
|
// publish checks: apps aren't published
|
|
31
|
-
app: new Set(["publint", "are-the-types-wrong"]),
|
|
31
|
+
app: new Set(["publint", "are-the-types-wrong", "expect-type"]),
|
|
32
32
|
// libraries have no Application to wire inspector support into
|
|
33
33
|
library: new Set(["inspector-support"]),
|
|
34
34
|
// publish checks: extensions aren't published (to npm)
|
|
35
|
-
extension: new Set(["publint", "are-the-types-wrong"]),
|
|
35
|
+
extension: new Set(["publint", "are-the-types-wrong", "expect-type"]),
|
|
36
36
|
// same toolchain as libraries: no Application either
|
|
37
37
|
"custom-element": new Set(["inspector-support"]),
|
|
38
38
|
};
|
|
@@ -61,6 +61,7 @@ const CHECK_LAYERS = new Set<string>([
|
|
|
61
61
|
"prettier",
|
|
62
62
|
"publint",
|
|
63
63
|
"are-the-types-wrong",
|
|
64
|
+
"expect-type",
|
|
64
65
|
]);
|
|
65
66
|
|
|
66
67
|
type PermutationGroup = "main" | "checks";
|
package/test/test-helpers.ts
CHANGED
|
@@ -120,7 +120,10 @@ export function cli(args: string[] = []) {
|
|
|
120
120
|
let output = new MockWritable();
|
|
121
121
|
let input = new MockReadable();
|
|
122
122
|
|
|
123
|
-
let execaPromise = execa("node",
|
|
123
|
+
let execaPromise = execa("node", [cliPath, ...args]);
|
|
124
|
+
if (execaPromise.stdout) {
|
|
125
|
+
execaPromise.stdout.pipe(output);
|
|
126
|
+
}
|
|
124
127
|
|
|
125
128
|
return {
|
|
126
129
|
execaPromise,
|
package/vitest.config.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import { defineConfig } from "vitest/config";
|
|
1
|
+
import { configDefaults, defineConfig } from "vitest/config";
|
|
2
2
|
|
|
3
3
|
export default defineConfig({
|
|
4
4
|
test: {
|
|
5
5
|
testTimeout: 60_000,
|
|
6
6
|
hookTimeout: 60_000,
|
|
7
7
|
include: ["test/**/*.test.ts", "src/**/*.test.ts", "packages/*/src/**/*.test.ts"],
|
|
8
|
+
// Templates copied into generated projects, which run their own tests
|
|
9
|
+
exclude: configDefaults.exclude.concat([
|
|
10
|
+
"src/**/layers/*/files/**",
|
|
11
|
+
"src/**/layers/*/library-files/**",
|
|
12
|
+
"src/**/bases/*/files/**",
|
|
13
|
+
]),
|
|
8
14
|
},
|
|
9
15
|
});
|