ember.nvp 1.11.1 → 1.12.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 -6
- package/CHANGELOG.md +22 -0
- package/README.md +9 -0
- package/package.json +4 -2
- package/src/cli/index.js +10 -6
- package/src/cli/questions/name.js +26 -4
- package/src/cli/questions/replace-or-update.js +51 -1
- package/src/utils/npm.js +25 -2
- package/test/ember.nvp/cli.test.ts +52 -1
- package/test/ember.nvp/layer-typescript.test.ts +3 -3
- package/test/ember.nvp/npm.test.ts +24 -2
- package/test/test-helpers.ts +2 -2
package/.release-plan.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"solution": {
|
|
3
3
|
"ember.nvp": {
|
|
4
|
-
"impact": "
|
|
5
|
-
"oldVersion": "1.11.
|
|
6
|
-
"newVersion": "1.
|
|
4
|
+
"impact": "minor",
|
|
5
|
+
"oldVersion": "1.11.2",
|
|
6
|
+
"newVersion": "1.12.0",
|
|
7
7
|
"tagName": "latest",
|
|
8
8
|
"constraints": [
|
|
9
9
|
{
|
|
10
|
-
"impact": "
|
|
11
|
-
"reason": "Appears in changelog section :
|
|
10
|
+
"impact": "minor",
|
|
11
|
+
"reason": "Appears in changelog section :rocket: Enhancement"
|
|
12
12
|
}
|
|
13
13
|
],
|
|
14
14
|
"pkgJSONPath": "./package.json"
|
|
@@ -23,5 +23,5 @@
|
|
|
23
23
|
"oldVersion": "1.2.0"
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
|
-
"description": "## Release (2026-09-30)\n\n* ember.nvp 1.
|
|
26
|
+
"description": "## Release (2026-09-30)\n\n* ember.nvp 1.12.0 (minor)\n\n#### :rocket: Enhancement\n* `ember.nvp`\n * [#152](https://github.com/NullVoxPopuli/ember.nvp/pull/152) Ask about the project in the current directory first ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n\n#### Committers: 1\n- @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))\n"
|
|
27
27
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
## Release (2026-09-30)
|
|
4
4
|
|
|
5
|
+
* ember.nvp 1.12.0 (minor)
|
|
6
|
+
|
|
7
|
+
#### :rocket: Enhancement
|
|
8
|
+
* `ember.nvp`
|
|
9
|
+
* [#152](https://github.com/NullVoxPopuli/ember.nvp/pull/152) Ask about the project in the current directory first ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
10
|
+
|
|
11
|
+
#### Committers: 1
|
|
12
|
+
- @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
13
|
+
|
|
14
|
+
## Release (2026-09-30)
|
|
15
|
+
|
|
16
|
+
* ember.nvp 1.11.2 (patch)
|
|
17
|
+
|
|
18
|
+
#### :bug: Bug Fix
|
|
19
|
+
* `ember.nvp`
|
|
20
|
+
* [#150](https://github.com/NullVoxPopuli/ember.nvp/pull/150) Keep the range when bumping a dependency ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
21
|
+
|
|
22
|
+
#### Committers: 1
|
|
23
|
+
- @NullVoxPopuli's reduced-access machine account for AI usage ([@NullVoxPopuli-ai-agent](https://github.com/NullVoxPopuli-ai-agent))
|
|
24
|
+
|
|
25
|
+
## Release (2026-09-30)
|
|
26
|
+
|
|
5
27
|
* ember.nvp 1.11.1 (patch)
|
|
6
28
|
|
|
7
29
|
#### :bug: Bug Fix
|
package/README.md
CHANGED
|
@@ -37,6 +37,15 @@ npx NullVoxPopuli/ember.nvp
|
|
|
37
37
|
### Updating an existing project
|
|
38
38
|
|
|
39
39
|
You can run `ember.nvp` on top of an existing project to add layers to it.
|
|
40
|
+
|
|
41
|
+
Run it inside the project. When the current directory has a `package.json`, the first question is what to do with it:
|
|
42
|
+
|
|
43
|
+
- **update**: update the project in this directory. The project name starts as the `package.json` name.
|
|
44
|
+
- **replace**: delete this directory and generate a new project in it.
|
|
45
|
+
- **new**: generate a new project in another directory.
|
|
46
|
+
|
|
47
|
+
`--path` skips this question, and `--replaceOrUpdate` answers it.
|
|
48
|
+
|
|
40
49
|
Nothing is written to your project until you confirm. Before finishing, you choose to:
|
|
41
50
|
|
|
42
51
|
- **write the files**: apply all of the staged changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ember.nvp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.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",
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"enhanced-resolve": "^5.24.2",
|
|
30
30
|
"execa": "^9.6.1",
|
|
31
31
|
"latest-version": "^9.0.0",
|
|
32
|
-
"package-name-regex": "^5.0.0"
|
|
32
|
+
"package-name-regex": "^5.0.0",
|
|
33
|
+
"semver": "^7.8.5"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
36
|
"@ember/app-tsconfig": "^2.0.0",
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
"@glint/ember-tsc": "^1.7.5",
|
|
38
39
|
"@glint/template": "^1.7.7",
|
|
39
40
|
"@glint/tsserver-plugin": "^2.5.5",
|
|
41
|
+
"@types/semver": "^7.8.0",
|
|
40
42
|
"concurrently": "^9.2.1",
|
|
41
43
|
"ember-source": "^7.0.0",
|
|
42
44
|
"prettier": "^3.8.3",
|
package/src/cli/index.js
CHANGED
|
@@ -12,10 +12,11 @@ import { askIfOK } from "./questions/ok.js";
|
|
|
12
12
|
import { askToWrite } from "./questions/write.js";
|
|
13
13
|
import { styleText } from "node:util";
|
|
14
14
|
import { readFile, rm } from "node:fs/promises";
|
|
15
|
-
import { join } from "node:path";
|
|
15
|
+
import { join, relative } from "node:path";
|
|
16
|
+
import { cwd } from "#utils/cwd.js";
|
|
16
17
|
import { Project } from "#utils/project.js";
|
|
17
18
|
import { Stage } from "#utils/stage.js";
|
|
18
|
-
import { askReplaceOrUpdate } from "./questions/replace-or-update.js";
|
|
19
|
+
import { askAboutProjectHere, askReplaceOrUpdate } from "./questions/replace-or-update.js";
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* This whole file's primary purpose is to be an interactive CLI
|
|
@@ -29,9 +30,10 @@ async function main() {
|
|
|
29
30
|
|
|
30
31
|
p.intro(styleText(["bgCyan", "black"], " ember.nvp "));
|
|
31
32
|
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
33
|
+
const here = await askAboutProjectHere();
|
|
34
|
+
const projectName = await askName(here === "update" ? cwd : undefined);
|
|
35
|
+
const projectPath = here ? cwd : await askPath(projectName);
|
|
36
|
+
const replaceOrUpdate = here ?? (await askReplaceOrUpdate(projectPath));
|
|
35
37
|
const projectType = await askProjectType();
|
|
36
38
|
const selectedLayers = await askLayers(projectType);
|
|
37
39
|
const packageManager = await askPackageManager();
|
|
@@ -158,8 +160,10 @@ async function main() {
|
|
|
158
160
|
// no readable package.json -- skip the suggestion
|
|
159
161
|
}
|
|
160
162
|
|
|
163
|
+
let projectDir = relative(cwd, projectPath);
|
|
164
|
+
|
|
161
165
|
p.note(
|
|
162
|
-
`cd ${
|
|
166
|
+
(projectDir ? `cd ${projectDir}\n` : "") +
|
|
163
167
|
`${packageManager} install\n` +
|
|
164
168
|
lintFixStep +
|
|
165
169
|
`${packageManager} ${packageManager === "npm" ? "run " : ""}start`,
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { cancel, isCancel, text } from "@clack/prompts";
|
|
2
2
|
import packageNameRegex from "package-name-regex";
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
3
5
|
import { answers, printArgInUse } from "#args";
|
|
4
6
|
|
|
5
7
|
const DEFAULT = "my-app";
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} [existingProject] a directory whose package.json name fills in the answer
|
|
11
|
+
*/
|
|
12
|
+
export async function askName(existingProject) {
|
|
8
13
|
if (answers.name) {
|
|
9
14
|
let isValid = packageNameRegex.test(answers.name);
|
|
10
15
|
if (isValid) {
|
|
@@ -13,10 +18,15 @@ export async function askName() {
|
|
|
13
18
|
return answers.name;
|
|
14
19
|
}
|
|
15
20
|
}
|
|
21
|
+
|
|
22
|
+
let existingName = existingProject ? readName(existingProject) : undefined;
|
|
23
|
+
let fallback = existingName ?? DEFAULT;
|
|
24
|
+
|
|
16
25
|
const projectName = await text({
|
|
17
26
|
message: "What is your project name?",
|
|
18
|
-
placeholder:
|
|
19
|
-
defaultValue:
|
|
27
|
+
placeholder: fallback,
|
|
28
|
+
defaultValue: fallback,
|
|
29
|
+
initialValue: existingName,
|
|
20
30
|
validate(value) {
|
|
21
31
|
if (!value || value.length === 0) return;
|
|
22
32
|
|
|
@@ -33,5 +43,17 @@ export async function askName() {
|
|
|
33
43
|
return process.exit(0);
|
|
34
44
|
}
|
|
35
45
|
|
|
36
|
-
return projectName ??
|
|
46
|
+
return projectName ?? fallback;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {string} directory
|
|
51
|
+
* @returns {string | undefined}
|
|
52
|
+
*/
|
|
53
|
+
function readName(directory) {
|
|
54
|
+
let { name } = JSON.parse(readFileSync(join(directory, "package.json"), "utf-8"));
|
|
55
|
+
|
|
56
|
+
if (typeof name === "string" && packageNameRegex.test(name)) {
|
|
57
|
+
return name;
|
|
58
|
+
}
|
|
37
59
|
}
|
|
@@ -2,11 +2,13 @@ import * as p from "@clack/prompts";
|
|
|
2
2
|
|
|
3
3
|
import { answers, printArgInUse } from "#args";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
import { cwd } from "#utils/cwd.js";
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
*
|
|
8
10
|
* @param {string | undefined} value
|
|
9
|
-
* @returns
|
|
11
|
+
* @returns {value is "replace" | "update"}
|
|
10
12
|
*/
|
|
11
13
|
function isValid(value) {
|
|
12
14
|
if (!value) return false;
|
|
@@ -54,3 +56,51 @@ export async function askReplaceOrUpdate(projectPath) {
|
|
|
54
56
|
|
|
55
57
|
return answer;
|
|
56
58
|
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* A package.json in the current directory is most likely the project to work on.
|
|
62
|
+
* This question comes before the name, so that an update can default to that project's name.
|
|
63
|
+
*
|
|
64
|
+
* Skipped when `--path` picks the target.
|
|
65
|
+
*
|
|
66
|
+
* @returns {Promise<"replace" | "update" | undefined>} undefined for a new project in another directory
|
|
67
|
+
*/
|
|
68
|
+
export async function askAboutProjectHere() {
|
|
69
|
+
if (answers.path) return;
|
|
70
|
+
if (!existsSync(join(cwd, "package.json"))) return;
|
|
71
|
+
|
|
72
|
+
if (isValid(answers.replaceOrUpdate)) {
|
|
73
|
+
printArgInUse("replaceOrUpdate", answers.replaceOrUpdate);
|
|
74
|
+
|
|
75
|
+
return answers.replaceOrUpdate;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const answer = await p.select({
|
|
79
|
+
message: "This directory has a package.json. What would you like to do?",
|
|
80
|
+
initialValue: "update",
|
|
81
|
+
options: [
|
|
82
|
+
{
|
|
83
|
+
value: "update",
|
|
84
|
+
label: "update",
|
|
85
|
+
hint: "Updates the project in this directory",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
value: "replace",
|
|
89
|
+
label: "replace",
|
|
90
|
+
hint: "Deletes this directory and generates a new project in it",
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
value: "new",
|
|
94
|
+
label: "new",
|
|
95
|
+
hint: "Generates a new project in another directory",
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
if (p.isCancel(answer)) {
|
|
101
|
+
p.cancel("Operation cancelled");
|
|
102
|
+
return process.exit(0);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return isValid(answer) ? answer : undefined;
|
|
106
|
+
}
|
package/src/utils/npm.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import latestVersion, { VersionNotFoundError } from "latest-version";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { join, resolve } from "node:path";
|
|
4
|
+
import semver from "semver";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @type {{ [name: string]: { [version: string]: string } }}
|
|
@@ -56,12 +57,12 @@ export async function getLatest(deps) {
|
|
|
56
57
|
version =
|
|
57
58
|
"link:" + resolve(join(import.meta.dirname, "../../packages", LOCAL_PACKAGES[dep]));
|
|
58
59
|
} else if (alias) {
|
|
59
|
-
version = `npm:${alias.name}@${await
|
|
60
|
+
version = `npm:${alias.name}@${await bump(alias.name, alias.range)}`;
|
|
60
61
|
} else {
|
|
61
62
|
if (range == "workspace:*") {
|
|
62
63
|
range = "latest";
|
|
63
64
|
}
|
|
64
|
-
version = isFromRegistry(range) ? await
|
|
65
|
+
version = isFromRegistry(range) ? await bump(dep, range) : range;
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
CACHE[dep] ||= {};
|
|
@@ -85,6 +86,28 @@ function isFromRegistry(range) {
|
|
|
85
86
|
return !/[:/]/.test(range);
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Moves a range to the newest version it allows, and keeps its kind:
|
|
91
|
+
* - `^5.4.1` becomes `^5.5.2`, and `~` works the same way
|
|
92
|
+
* - an exact version stays exact
|
|
93
|
+
* - a dist-tag becomes the version it points at
|
|
94
|
+
*
|
|
95
|
+
* Other ranges (`>= 4.1.0`, `1.x`, `^1 || ^2`) stay as written.
|
|
96
|
+
* A new floor there would drop versions that the project supports.
|
|
97
|
+
*
|
|
98
|
+
* @param {string} name
|
|
99
|
+
* @param {string} range
|
|
100
|
+
*/
|
|
101
|
+
async function bump(name, range) {
|
|
102
|
+
let isTag = !semver.validRange(range);
|
|
103
|
+
let isExact = Boolean(semver.valid(range));
|
|
104
|
+
let [, operator = ""] = /^([\^~])\s*v?\d[\w.+-]*$/.exec(range) ?? [];
|
|
105
|
+
|
|
106
|
+
if (!isTag && !isExact && !operator) return range;
|
|
107
|
+
|
|
108
|
+
return operator + (await resolveVersion(name, range));
|
|
109
|
+
}
|
|
110
|
+
|
|
88
111
|
/**
|
|
89
112
|
* Deprecated versions are skipped when the range allows another version.
|
|
90
113
|
*
|
|
@@ -2,7 +2,7 @@ import { test, expect as hardExpect } from "vitest";
|
|
|
2
2
|
import { cli, mktemp } from "#test-helpers";
|
|
3
3
|
import { packageJson } from "ember-apply";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
|
-
import { rm } from "node:fs/promises";
|
|
5
|
+
import { readFile, rm, writeFile } from "node:fs/promises";
|
|
6
6
|
import { join } from "node:path";
|
|
7
7
|
import { stripVTControlCharacters } from "node:util";
|
|
8
8
|
|
|
@@ -153,3 +153,54 @@ test("cli exits on an invalid layer option flag, before writing anything", async
|
|
|
153
153
|
await rm(parent, { recursive: true, force: true });
|
|
154
154
|
}
|
|
155
155
|
});
|
|
156
|
+
|
|
157
|
+
test("in a project directory, asks to update first and fills in the project's name", async () => {
|
|
158
|
+
let dir = await mktemp("cli-update-here");
|
|
159
|
+
await writeFile(join(dir, "package.json"), JSON.stringify({ name: "@scope/my-lib" }));
|
|
160
|
+
|
|
161
|
+
// No --name or --path, so the CLI asks about the project in this directory
|
|
162
|
+
let args = [
|
|
163
|
+
"--type",
|
|
164
|
+
"library",
|
|
165
|
+
"--layers",
|
|
166
|
+
"prettier",
|
|
167
|
+
"--packageManager",
|
|
168
|
+
"pnpm",
|
|
169
|
+
"--confirm",
|
|
170
|
+
"yes",
|
|
171
|
+
"--write",
|
|
172
|
+
"yes",
|
|
173
|
+
];
|
|
174
|
+
let prompts = ["This directory has a package.json", "What is your project name?"];
|
|
175
|
+
|
|
176
|
+
try {
|
|
177
|
+
let { execaPromise } = cli(args, { cwd: dir });
|
|
178
|
+
let output = "";
|
|
179
|
+
|
|
180
|
+
execaPromise.stdout?.on("data", (chunk) => {
|
|
181
|
+
output += chunk;
|
|
182
|
+
|
|
183
|
+
// Enter keeps the answer each prompt starts with: "update", then the name
|
|
184
|
+
if (prompts[0] && stripVTControlCharacters(output).includes(prompts[0])) {
|
|
185
|
+
prompts.shift();
|
|
186
|
+
execaPromise.stdin?.write("\r");
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
let res = await execaPromise;
|
|
191
|
+
let text = stripVTControlCharacters(output);
|
|
192
|
+
|
|
193
|
+
expect(res.exitCode).toBe(0);
|
|
194
|
+
expect(text.indexOf("This directory has a package.json")).toBeLessThan(
|
|
195
|
+
text.indexOf("What is your project name?"),
|
|
196
|
+
);
|
|
197
|
+
expect(text).not.toContain("Where would you like to place your project?");
|
|
198
|
+
expect(text).not.toContain("cd ");
|
|
199
|
+
|
|
200
|
+
let manifest = JSON.parse(await readFile(join(dir, "package.json"), "utf-8"));
|
|
201
|
+
|
|
202
|
+
expect(manifest.name).toBe("@scope/my-lib");
|
|
203
|
+
} finally {
|
|
204
|
+
await rm(dir, { recursive: true, force: true });
|
|
205
|
+
}
|
|
206
|
+
});
|
|
@@ -55,8 +55,8 @@ describe("layer: typescript, version 7", () => {
|
|
|
55
55
|
it("installs TypeScript 7 as @typescript/native, and TypeScript 6 for typescript-eslint", async () => {
|
|
56
56
|
let deps = await devDependencies(project);
|
|
57
57
|
|
|
58
|
-
expect(deps["@typescript/native"]).toMatch(/^npm:typescript
|
|
59
|
-
expect(deps["typescript"]).toMatch(/^npm:@typescript\/typescript6
|
|
58
|
+
expect(deps["@typescript/native"]).toMatch(/^npm:typescript@\^7\./);
|
|
59
|
+
expect(deps["typescript"]).toMatch(/^npm:@typescript\/typescript6@\^6\./);
|
|
60
60
|
expect(deps).toHaveProperty("ember-content-mapper");
|
|
61
61
|
expect(deps).not.toHaveProperty("@glint/tsserver-plugin");
|
|
62
62
|
});
|
|
@@ -172,7 +172,7 @@ describe("layer: typescript, version 7", () => {
|
|
|
172
172
|
let manifest = await packageJson.read(project.directory);
|
|
173
173
|
|
|
174
174
|
expect(manifest.scripts?.["lint:types"]).toBe("tsc --noEmit --runExternalCode");
|
|
175
|
-
expect(manifest.devDependencies?.["@typescript/native"]).toMatch(/^npm:typescript
|
|
175
|
+
expect(manifest.devDependencies?.["@typescript/native"]).toMatch(/^npm:typescript@\^7\./);
|
|
176
176
|
expect(manifest.devDependencies).not.toHaveProperty("typescript");
|
|
177
177
|
expect(manifest.devDependencies).not.toHaveProperty("@glint/tsserver-plugin");
|
|
178
178
|
});
|
|
@@ -6,13 +6,35 @@ describe("getLatest", () => {
|
|
|
6
6
|
// ESLint deprecated all of 9.x once 10 was out
|
|
7
7
|
const { eslint } = await getLatest({ eslint: "^9.39.2" });
|
|
8
8
|
|
|
9
|
-
expect(eslint).toMatch(
|
|
9
|
+
expect(eslint).toMatch(/^\^9\.39\.\d+$/);
|
|
10
10
|
});
|
|
11
11
|
|
|
12
12
|
it("still prefers versions that are not deprecated", async () => {
|
|
13
13
|
const { eslint } = await getLatest({ eslint: "^10.0.0" });
|
|
14
14
|
|
|
15
|
-
expect(eslint).toMatch(
|
|
15
|
+
expect(eslint).toMatch(/^\^10\.\d+\.\d+$/);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("bumps a range without changing its kind", async () => {
|
|
19
|
+
const result = await getLatest({
|
|
20
|
+
"@ember/test-helpers": "^5.4.1",
|
|
21
|
+
"ember-source": "~7.2.0",
|
|
22
|
+
"@embroider/macros": "1.20.2",
|
|
23
|
+
"@ember/test-waiters": ">= 4.1.0",
|
|
24
|
+
"ember-modifier": "^4.2.0 || ^5.0.0",
|
|
25
|
+
"decorator-transforms": "latest",
|
|
26
|
+
typescript: "npm:@typescript/typescript6@^6.0.2",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(result).toEqual({
|
|
30
|
+
"@ember/test-helpers": expect.stringMatching(/^\^5\.\d+\.\d+$/),
|
|
31
|
+
"ember-source": expect.stringMatching(/^~7\.2\.\d+$/),
|
|
32
|
+
"@embroider/macros": "1.20.2",
|
|
33
|
+
"@ember/test-waiters": ">= 4.1.0",
|
|
34
|
+
"ember-modifier": "^4.2.0 || ^5.0.0",
|
|
35
|
+
"decorator-transforms": expect.stringMatching(/^\d+\.\d+\.\d+$/),
|
|
36
|
+
typescript: expect.stringMatching(/^npm:@typescript\/typescript6@\^6\.\d+\.\d+$/),
|
|
37
|
+
});
|
|
16
38
|
});
|
|
17
39
|
|
|
18
40
|
it("keeps specs that do not come from the registry", async () => {
|
package/test/test-helpers.ts
CHANGED
|
@@ -119,7 +119,7 @@ export async function generate({
|
|
|
119
119
|
|
|
120
120
|
const cliPath = join(import.meta.dirname, "../src/cli/index.js");
|
|
121
121
|
|
|
122
|
-
export function cli(args: string[] = []) {
|
|
122
|
+
export function cli(args: string[] = [], options: { cwd?: string } = {}) {
|
|
123
123
|
let cmd = `node ${cliPath} ${args.join(" ")}`;
|
|
124
124
|
|
|
125
125
|
console.log(`Running '${styleText("cyan", cmd)}'`);
|
|
@@ -127,7 +127,7 @@ export function cli(args: string[] = []) {
|
|
|
127
127
|
let output = new MockWritable();
|
|
128
128
|
let input = new MockReadable();
|
|
129
129
|
|
|
130
|
-
let execaPromise = execa("node", [cliPath, ...args]);
|
|
130
|
+
let execaPromise = execa("node", [cliPath, ...args], options);
|
|
131
131
|
if (execaPromise.stdout) {
|
|
132
132
|
execaPromise.stdout.pipe(output);
|
|
133
133
|
}
|