ember.nvp 1.10.0 → 1.11.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.
@@ -1,15 +1,11 @@
1
1
  {
2
2
  "solution": {
3
3
  "ember.nvp": {
4
- "impact": "minor",
5
- "oldVersion": "1.9.0",
6
- "newVersion": "1.10.0",
4
+ "impact": "patch",
5
+ "oldVersion": "1.11.0",
6
+ "newVersion": "1.11.1",
7
7
  "tagName": "latest",
8
8
  "constraints": [
9
- {
10
- "impact": "minor",
11
- "reason": "Appears in changelog section :rocket: Enhancement"
12
- },
13
9
  {
14
10
  "impact": "patch",
15
11
  "reason": "Appears in changelog section :bug: Bug Fix"
@@ -21,25 +17,11 @@
21
17
  "oldVersion": "1.2.0"
22
18
  },
23
19
  "@nullvoxpopuli/ember-rolldown": {
24
- "impact": "minor",
25
- "oldVersion": "2.9.0",
26
- "newVersion": "2.10.0",
27
- "tagName": "latest",
28
- "constraints": [
29
- {
30
- "impact": "minor",
31
- "reason": "Appears in changelog section :rocket: Enhancement"
32
- },
33
- {
34
- "impact": "patch",
35
- "reason": "Appears in changelog section :bug: Bug Fix"
36
- }
37
- ],
38
- "pkgJSONPath": "./packages/rolldown/package.json"
20
+ "oldVersion": "2.10.0"
39
21
  },
40
22
  "@nullvoxpopuli/ember-vite": {
41
23
  "oldVersion": "1.2.0"
42
24
  }
43
25
  },
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"
26
+ "description": "## Release (2026-09-30)\n\n* ember.nvp 1.11.1 (patch)\n\n#### :bug: Bug Fix\n* `ember.nvp`\n * [#148](https://github.com/NullVoxPopuli/ember.nvp/pull/148) Keep update runs working for deprecated and non-registry deps ([@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"
45
27
  }
package/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## Release (2026-09-30)
4
4
 
5
+ * ember.nvp 1.11.1 (patch)
6
+
7
+ #### :bug: Bug Fix
8
+ * `ember.nvp`
9
+ * [#148](https://github.com/NullVoxPopuli/ember.nvp/pull/148) Keep update runs working for deprecated and non-registry deps ([@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.0 (minor)
17
+
18
+ #### :rocket: Enhancement
19
+ * `ember.nvp`
20
+ * [#146](https://github.com/NullVoxPopuli/ember.nvp/pull/146) Better help ([@tcjr](https://github.com/tcjr))
21
+
22
+ #### Committers: 1
23
+ - Tom Carter ([@tcjr](https://github.com/tcjr))
24
+
25
+ ## Release (2026-09-30)
26
+
5
27
  * ember.nvp 1.10.0 (minor)
6
28
  * @nullvoxpopuli/ember-rolldown 2.10.0 (minor)
7
29
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ember.nvp",
3
- "version": "1.10.0",
3
+ "version": "1.11.1",
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,6 +1,6 @@
1
1
  import * as p from "@clack/prompts";
2
2
  import { styleText, parseArgs } from "node:util";
3
- import { layers as discoveredLayers } from "#layers";
3
+ import { layers as discoveredLayers, layerNames } from "#layers";
4
4
  import { validateOption } from "./questions/validate-option.js";
5
5
  import { printHelp } from "./help.js";
6
6
 
@@ -36,7 +36,8 @@ export const coreOptions = /** @type {const} */ ({
36
36
  layers: {
37
37
  type: "string",
38
38
  multiple: true,
39
- description: "Layers to apply to the project (repeat for multiple layers)",
39
+ choices: layerNames,
40
+ description: "Layers to apply to the project",
40
41
  },
41
42
 
42
43
  packageManager: {
package/src/cli/help.js CHANGED
@@ -30,6 +30,7 @@ export function printHelp(coreOptions, layers = []) {
30
30
  flag: `--${name}`,
31
31
  alias: config.short ? `-${config.short}, ` : " ",
32
32
  type: config.type,
33
+ multiple: config.multiple,
33
34
  description: config.description,
34
35
  choices: config.choices,
35
36
  });
@@ -41,6 +42,7 @@ export function printHelp(coreOptions, layers = []) {
41
42
 
42
43
  console.log(styleText("bold", "Layer Options:"));
43
44
  for (const layer of withOptions) {
45
+ console.log(` Layer: ${styleText(["cyan", "bold"], layer.name)}`);
44
46
  for (const [key, schema] of Object.entries(layer.options ?? {})) {
45
47
  const name = `${layer.name}.${key}`;
46
48
 
@@ -48,6 +50,7 @@ export function printHelp(coreOptions, layers = []) {
48
50
  flag: schema.type === "confirm" ? `--[no-]${name}` : `--${name}`,
49
51
  alias: " ",
50
52
  type: FLAG_TYPES[schema.type],
53
+ multiple: schema.type === "multiselect",
51
54
  description: schema.prompt,
52
55
  choices: schema.options?.map((choice) => choice.value),
53
56
  fallback: schema.default,
@@ -61,20 +64,27 @@ export function printHelp(coreOptions, layers = []) {
61
64
  * flag: string,
62
65
  * alias: string,
63
66
  * type?: string,
67
+ * multiple?: boolean,
64
68
  * description?: string,
65
69
  * choices?: string[],
66
70
  * fallback?: unknown,
67
71
  * }} option
68
72
  */
69
- function printOption({ flag, alias, type, description = "", choices, fallback }) {
73
+ function printOption({ flag, alias, type, multiple, description = "", choices, fallback }) {
70
74
  const typeStr = type ? styleText("dim", `<${type}>`) : "";
75
+ const repeatStr = multiple
76
+ ? type
77
+ ? ` ${styleText("dim", "[")}${styleText("cyan", flag)} ${typeStr}${styleText("dim", " ...]")}`
78
+ : ` ${styleText("dim", "[")}${styleText("cyan", flag)}${styleText("dim", " ...]")}`
79
+ : "";
80
+ const flagAndType = [styleText("cyan", flag), typeStr].filter(Boolean).join(" ");
71
81
  const choicesStr = choices
72
82
  ? styleText("yellow", ` [choices: ${choices.map((c) => `"${c}"`).join(", ")}]`)
73
83
  : "";
74
84
  const fallbackStr =
75
85
  fallback === undefined ? "" : styleText("dim", ` [default: ${JSON.stringify(fallback)}]`);
76
86
 
77
- console.log(` ${styleText("cyan", alias)}${styleText("cyan", flag)} ${typeStr}`);
87
+ console.log(` ${styleText("cyan", alias)}${flagAndType}${repeatStr}`);
78
88
  if (description || choicesStr || fallbackStr) {
79
89
  console.log(` ${description}${choicesStr}${fallbackStr}`);
80
90
  }
package/src/utils/npm.js CHANGED
@@ -1,4 +1,4 @@
1
- import latestVersion from "latest-version";
1
+ import latestVersion, { VersionNotFoundError } from "latest-version";
2
2
  import { existsSync } from "node:fs";
3
3
  import { join, resolve } from "node:path";
4
4
 
@@ -56,12 +56,12 @@ export async function getLatest(deps) {
56
56
  version =
57
57
  "link:" + resolve(join(import.meta.dirname, "../../packages", LOCAL_PACKAGES[dep]));
58
58
  } else if (alias) {
59
- version = `npm:${alias.name}@${await latestVersion(alias.name, { version: alias.range })}`;
59
+ version = `npm:${alias.name}@${await resolveVersion(alias.name, alias.range)}`;
60
60
  } else {
61
61
  if (range == "workspace:*") {
62
62
  range = "latest";
63
63
  }
64
- version = await latestVersion(dep, { version: range });
64
+ version = isFromRegistry(range) ? await resolveVersion(dep, range) : range;
65
65
  }
66
66
 
67
67
  CACHE[dep] ||= {};
@@ -74,6 +74,36 @@ export async function getLatest(deps) {
74
74
  return Object.fromEntries(results);
75
75
  }
76
76
 
77
+ /**
78
+ * git, file, link, url, and catalog specs have no registry version to bump to.
79
+ *
80
+ * Semver ranges and dist-tags never contain `:` or `/`.
81
+ *
82
+ * @param {string} range
83
+ */
84
+ function isFromRegistry(range) {
85
+ return !/[:/]/.test(range);
86
+ }
87
+
88
+ /**
89
+ * Deprecated versions are skipped when the range allows another version.
90
+ *
91
+ * A package can deprecate a whole major (ESLint 9, once 10 was out).
92
+ * An existing project can still ask for that range.
93
+ *
94
+ * @param {string} name
95
+ * @param {string} range
96
+ */
97
+ async function resolveVersion(name, range) {
98
+ try {
99
+ return await latestVersion(name, { version: range });
100
+ } catch (error) {
101
+ if (!(error instanceof VersionNotFoundError)) throw error;
102
+
103
+ return await latestVersion(name, { version: range, omitDeprecated: false });
104
+ }
105
+ }
106
+
77
107
  /**
78
108
  * An npm alias installs a package under another name:
79
109
  *
@@ -37,8 +37,8 @@ const BASIC_HELP_OUTPUT = `
37
37
  --confirm <string>
38
38
  Bypass target confirmation step [choices: "yes", "no"]
39
39
 
40
- --layers <string>
41
- Layers to apply to the project (repeat for multiple layers)
40
+ --layers <string> [--layers <string> ...]
41
+ Layers to apply to the project [choices: "are-the-types-wrong", "eslint-bundled", "expect-type", "git", "github-actions", "inspector-support", "prettier", "publint", "qunit", "readme", "renovate", "typescript", "vitest"]
42
42
 
43
43
  --packageManager <string>
44
44
  Package manager to configure for the project [choices: "npm", "pnpm"]
@@ -50,9 +50,11 @@ const BASIC_HELP_OUTPUT = `
50
50
  Confirm writing changes to disk [choices: "yes", "no"]
51
51
 
52
52
  Layer Options:
53
+ Layer: eslint-bundled
53
54
  --eslint-bundled.preset <string>
54
55
  Which ESLint config? [choices: "ember", "nvp"] [default: "ember"]
55
56
 
57
+ Layer: typescript
56
58
  --typescript.version <string>
57
59
  Which TypeScript version? [choices: "7", "6"] [default: "7"]
58
60
  "
@@ -228,6 +228,7 @@ describe("Layer Options Feature", () => {
228
228
 
229
229
  expect(output.slice(output.indexOf("Layer Options:"))).toMatchInlineSnapshot(`
230
230
  "Layer Options:
231
+ Layer: fake-kitchen-sink
231
232
  --fake-kitchen-sink.unitCount <number>
232
233
  How many units do you want? [default: 7]
233
234
 
@@ -240,7 +241,7 @@ describe("Layer Options Feature", () => {
240
241
  --[no-]fake-kitchen-sink.enableLogging <boolean>
241
242
  Enable detailed sink logging? [default: true]
242
243
 
243
- --fake-kitchen-sink.extras <string>
244
+ --fake-kitchen-sink.extras <string> [--fake-kitchen-sink.extras <string> ...]
244
245
  Select optional kitchen sink extras [choices: "soap-dispenser", "garbage-disposal"] [default: ["soap-dispenser"]]
245
246
  "
246
247
  `);
@@ -0,0 +1,29 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { getLatest } from "#utils/npm.js";
3
+
4
+ describe("getLatest", () => {
5
+ it("resolves a range where every version is deprecated", async () => {
6
+ // ESLint deprecated all of 9.x once 10 was out
7
+ const { eslint } = await getLatest({ eslint: "^9.39.2" });
8
+
9
+ expect(eslint).toMatch(/^9\.39\.\d+$/);
10
+ });
11
+
12
+ it("still prefers versions that are not deprecated", async () => {
13
+ const { eslint } = await getLatest({ eslint: "^10.0.0" });
14
+
15
+ expect(eslint).toMatch(/^10\.\d+\.\d+$/);
16
+ });
17
+
18
+ it("keeps specs that do not come from the registry", async () => {
19
+ const specs = {
20
+ "@embroider/try": "github:NullVoxPopuli/try#768631a",
21
+ "my-local": "file:../my-local",
22
+ "my-linked": "link:../my-linked",
23
+ "my-tarball": "https://example.com/my-tarball.tgz",
24
+ "my-catalog": "catalog:",
25
+ };
26
+
27
+ expect(await getLatest(specs)).toEqual(specs);
28
+ });
29
+ });