ember.nvp 0.1.0 → 0.2.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.
Files changed (92) hide show
  1. package/.github/workflows/ci.yml +41 -0
  2. package/.github/workflows/plan-release.yml +2 -2
  3. package/.github/workflows/publish.yml +2 -2
  4. package/.prettierignore +2 -1
  5. package/.prettierrc.js +10 -0
  6. package/.release-plan.json +11 -3
  7. package/CHANGELOG.md +37 -0
  8. package/README.md +79 -142
  9. package/package.json +28 -5
  10. package/pnpm-workspace.yaml +3 -0
  11. package/src/bases/README.md +5 -0
  12. package/src/bases/minimal-app/app.test.ts +203 -0
  13. package/src/bases/minimal-app/files/.env.development +8 -0
  14. package/src/bases/minimal-app/files/app/app.ts +21 -0
  15. package/src/bases/minimal-app/files/app/config.ts +30 -0
  16. package/src/bases/minimal-app/files/app/router.ts +51 -0
  17. package/src/bases/minimal-app/files/app/templates/application.gts +5 -0
  18. package/src/{layers/minimal/files-ts → bases/minimal-app/files}/babel.config.js +1 -4
  19. package/src/{layers/minimal → bases/minimal-app}/files/index.html +3 -5
  20. package/src/bases/minimal-app/files/package.json +57 -0
  21. package/src/{layers/minimal → bases/minimal-app}/files/tsconfig.json +0 -6
  22. package/src/bases/minimal-app/index.js +111 -0
  23. package/src/bases/minimal-library/index.js +10 -0
  24. package/src/cli/args.js +57 -0
  25. package/src/cli/generator.js +66 -11
  26. package/src/cli/index.js +44 -13
  27. package/src/cli/questions/layers.js +39 -50
  28. package/src/cli/questions/name.js +22 -6
  29. package/src/cli/questions/ok.js +38 -0
  30. package/src/cli/questions/package-manager.js +23 -2
  31. package/src/cli/questions/path.js +59 -0
  32. package/src/cli/questions/project-type.js +55 -0
  33. package/src/consolidators/linting.js +51 -0
  34. package/src/index.js +2 -0
  35. package/src/layers/eslint-bundled-ember/files/eslint.config.js +14 -0
  36. package/src/layers/eslint-bundled-ember/index.js +91 -0
  37. package/src/layers/eslint-bundled-nvp/app.test.ts +91 -0
  38. package/src/layers/eslint-bundled-nvp/files/eslint.config.js +16 -0
  39. package/src/layers/eslint-bundled-nvp/index.js +91 -0
  40. package/src/layers/eslint-ejected/index.js +95 -0
  41. package/src/layers/git/index.js +33 -0
  42. package/src/layers/github-actions/app.test.ts +73 -0
  43. package/src/layers/github-actions/index.js +200 -0
  44. package/src/layers/index.js +47 -0
  45. package/src/layers/prettier/files/.prettierrc.js +4 -0
  46. package/src/layers/prettier/index.js +45 -18
  47. package/src/layers/qunit/app.test.ts +90 -0
  48. package/src/layers/qunit/files/testem.cjs +26 -0
  49. package/src/layers/qunit/files/tests/index.html +16 -7
  50. package/src/layers/qunit/files/tests/test-helper.ts +61 -7
  51. package/src/layers/qunit/index.js +83 -34
  52. package/src/layers/release-plan/index.js +5 -0
  53. package/src/layers/renovate/files/app.json5 +14 -0
  54. package/src/layers/renovate/files/library.json5 +16 -0
  55. package/src/layers/renovate/index.js +68 -0
  56. package/src/layers/typescript/app.test.ts +93 -0
  57. package/src/layers/typescript/index.js +145 -0
  58. package/src/layers/vitest/files/vitest.config.mjs +1 -5
  59. package/src/layers/vitest/index.js +43 -26
  60. package/src/utils/babel.js +76 -0
  61. package/src/utils/buffer.js +15 -0
  62. package/src/utils/cli.js +45 -0
  63. package/src/utils/cwd.js +1 -0
  64. package/src/utils/fs.js +85 -0
  65. package/src/utils/git.js +41 -0
  66. package/src/utils/git.test.ts +36 -0
  67. package/src/utils/manifest.js +9 -0
  68. package/src/utils/npm.js +29 -0
  69. package/src/utils/object.js +10 -0
  70. package/src/utils/project.js +249 -0
  71. package/src/utils/remove-types.js +90 -0
  72. package/src/utils/types.ts +48 -0
  73. package/test/ember.nvp/cli.test.ts +12 -0
  74. package/test/ember.nvp/layers.test.ts +35 -0
  75. package/test/ember.nvp/permutations.test.ts +200 -0
  76. package/test/manual.sh +11 -0
  77. package/test/test-helpers/permutate.test.ts +70 -0
  78. package/test/test-helpers.ts +175 -0
  79. package/tsconfig.json +10 -2
  80. package/vitest.config.js +9 -0
  81. package/src/layers/eslint/index.js +0 -42
  82. package/src/layers/minimal/files/app/app.ts +0 -10
  83. package/src/layers/minimal/files/app/config.ts +0 -18
  84. package/src/layers/minimal/files/app/router.ts +0 -9
  85. package/src/layers/minimal/files/app/templates/application.gts +0 -7
  86. package/src/layers/minimal/files-js/babel.config.js +0 -38
  87. package/src/layers/minimal/index.js +0 -89
  88. package/src/layers/prettier/files/.prettierrc.cjs +0 -15
  89. /package/src/{layers/minimal → bases/minimal-app}/files/vite.config.mjs +0 -0
  90. /package/src/layers/{eslint/files/eslint.config.mjs → eslint-ejected/files/eslint.config.js} +0 -0
  91. /package/src/layers/{minimal/files/app/routes → qunit/files/tests/application}/.gitkeep +0 -0
  92. /package/src/layers/qunit/files/tests/{integration → rendering}/.gitkeep +0 -0
@@ -0,0 +1,203 @@
1
+ import { beforeAll, describe, it, expect as hardExpect, afterAll } from "vitest";
2
+ import { generate, build, reapply } from "#test-helpers";
3
+ import { packageJson } from "ember-apply";
4
+
5
+ import type { Project } from "ember.nvp";
6
+ import { rm } from "node:fs/promises";
7
+ import { globSync } from "node:fs";
8
+
9
+ const expect = hardExpect.soft;
10
+
11
+ describe("typescript", () => {
12
+ let project: Project;
13
+ beforeAll(async () => {
14
+ project = await generate({
15
+ type: "app",
16
+ layers: ["typescript"],
17
+ });
18
+
19
+ let { exitCode } = await project.install();
20
+
21
+ hardExpect(exitCode, "Install succeeds").toBe(0);
22
+ });
23
+
24
+ afterAll(async () => {
25
+ await rm(project.directory, { recursive: true, force: true });
26
+ });
27
+
28
+ it("project check", () => {
29
+ expect(project.desires.layers.map((layer) => layer.name)).toMatchInlineSnapshot(`
30
+ [
31
+ "typescript",
32
+ ]
33
+ `);
34
+ expect(project.wantsTypeScript).toBe(true);
35
+ expect(project.type).toBe("app");
36
+ });
37
+
38
+ it("has the typescript babel plugin", async () => {
39
+ let content = await project.read("babel.config.js");
40
+
41
+ expect(content).toContain("@babel/plugin-transform-typescript");
42
+ });
43
+
44
+ it("build for development (testing, etc)", async () => {
45
+ let { exitCode } = await build(project);
46
+
47
+ expect(exitCode).toBe(0);
48
+ });
49
+
50
+ it("build for production", async () => {
51
+ let { exitCode } = await build(project, "production");
52
+
53
+ expect(exitCode).toBe(0);
54
+ });
55
+
56
+ it("has no JS files", async () => {
57
+ let results = globSync("app/**/*", {
58
+ exclude: ["node_modules", "dist"],
59
+ cwd: project.directory,
60
+ });
61
+ expect(results).toMatchInlineSnapshot(`
62
+ [
63
+ "app/app.ts",
64
+ "app/config.ts",
65
+ "app/router.ts",
66
+ "app/templates",
67
+ "app/templates/application.gts",
68
+ ]
69
+ `);
70
+ });
71
+
72
+ it("when re-applying, it no-ops correctly", async () => {
73
+ let files = new Set(
74
+ globSync("**/*", { cwd: project.directory, exclude: ["node_modules", "dist"] }),
75
+ );
76
+
77
+ await reapply(project, []);
78
+
79
+ let filesAfter = new Set(
80
+ globSync("**/*", { cwd: project.directory, exclude: ["node_modules", "dist"] }),
81
+ );
82
+
83
+ expect(filesAfter).toEqual(files);
84
+ expect(files).toMatchInlineSnapshot(`
85
+ Set {
86
+ "app",
87
+ "babel.config.js",
88
+ "index.html",
89
+ "package.json",
90
+ "pnpm-lock.yaml",
91
+ "tsconfig.json",
92
+ "vite.config.mjs",
93
+ "app/app.ts",
94
+ "app/config.ts",
95
+ "app/router.ts",
96
+ "app/templates",
97
+ "app/templates/application.gts",
98
+ }
99
+ `);
100
+ });
101
+ });
102
+
103
+ describe("javascript", () => {
104
+ let project: Project;
105
+ beforeAll(async () => {
106
+ project = await generate({
107
+ type: "app",
108
+ layers: [],
109
+ });
110
+
111
+ let { exitCode } = await project.install();
112
+
113
+ hardExpect(exitCode, "Install succeeds").toBe(0);
114
+ });
115
+
116
+ afterAll(async () => {
117
+ await rm(project.directory, { recursive: true, force: true });
118
+ });
119
+
120
+ it("project check", () => {
121
+ expect(project.desires.layers.map((layer) => layer.name)).toMatchInlineSnapshot(`[]`);
122
+ expect(project.wantsTypeScript).toBe(false);
123
+ expect(project.type).toBe("app");
124
+ });
125
+
126
+ it("does not have the typescript babel plugin", async () => {
127
+ let content = await project.read("babel.config.js");
128
+
129
+ expect(content).not.toContain("@babel/plugin-transform-typescript");
130
+ });
131
+
132
+ it("updated the sub-path imports to not have ts extensions", async () => {
133
+ let manifest = await packageJson.read(project.directory);
134
+
135
+ expect(manifest.imports).toMatchInlineSnapshot(`
136
+ {
137
+ "#app/*": "./app/*",
138
+ "#components/*": "./app/components/*",
139
+ "#config": "./app/config.js",
140
+ "#services/*": "./app/services/*",
141
+ "#test-helpers/*": "./tests/helpers/*",
142
+ "#utils/*": "./app/utils/*",
143
+ }
144
+ `);
145
+ });
146
+
147
+ it("build for development (testing, etc)", async () => {
148
+ let { exitCode } = await build(project);
149
+
150
+ expect(exitCode).toBe(0);
151
+ });
152
+
153
+ it("build for production", async () => {
154
+ let { exitCode } = await build(project, "production");
155
+
156
+ expect(exitCode).toBe(0);
157
+ });
158
+
159
+ it("has no TS files", async () => {
160
+ let results = globSync("app/**/*", {
161
+ exclude: ["node_modules", "dist"],
162
+ cwd: project.directory,
163
+ });
164
+ expect(results).toMatchInlineSnapshot(`
165
+ [
166
+ "app/app.js",
167
+ "app/config.js",
168
+ "app/router.js",
169
+ "app/templates",
170
+ "app/templates/application.gjs",
171
+ ]
172
+ `);
173
+ });
174
+
175
+ it("when re-applying, it no-ops correctly", async () => {
176
+ let files = new Set(
177
+ globSync("**/*", { cwd: project.directory, exclude: ["node_modules", "dist"] }),
178
+ );
179
+
180
+ await reapply(project, []);
181
+
182
+ let filesAfter = new Set(
183
+ globSync("**/*", { cwd: project.directory, exclude: ["node_modules", "dist"] }),
184
+ );
185
+
186
+ expect(filesAfter).toEqual(files);
187
+ expect(files).toMatchInlineSnapshot(`
188
+ Set {
189
+ "app",
190
+ "babel.config.js",
191
+ "index.html",
192
+ "package.json",
193
+ "pnpm-lock.yaml",
194
+ "vite.config.mjs",
195
+ "app/app.js",
196
+ "app/config.js",
197
+ "app/router.js",
198
+ "app/templates",
199
+ "app/templates/application.gjs",
200
+ }
201
+ `);
202
+ });
203
+ });
@@ -0,0 +1,8 @@
1
+ # This file is committed to git and should not contain any secrets.
2
+ #
3
+ # Vite recommends using .env.local or .env.[mode].local if you need to manage secrets
4
+ # SEE: https://vite.dev/guide/env-and-mode.html#env-files for more information.
5
+
6
+
7
+ # Default NODE_ENV with vite build --mode=test is production
8
+ NODE_ENV=development
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Looking for services that come from addons?
3
+ *
4
+ * See: https://github.com/embroider-build/embroider/issues/2659
5
+ *
6
+ * We currently don't support app-tree merging from libraries.
7
+ *
8
+ * For services, I highly recommend looking in to either of
9
+ * - https://github.com/chancancode/ember-polaris-service-
10
+ * - https://ember-primitives.pages.dev/6-utils/createService.md
11
+ * - https://ember-primitives.pages.dev/6-utils/createAsyncService.md
12
+ */
13
+ import Application from "ember-strict-application-resolver";
14
+
15
+ export default class App extends Application {
16
+ modules = {
17
+ ...import.meta.glob("./router.*", { eager: true }),
18
+ ...import.meta.glob("./templates/**/*", { eager: true }),
19
+ ...import.meta.glob("./services/**/*", { eager: true }),
20
+ };
21
+ }
@@ -0,0 +1,30 @@
1
+ interface Config {
2
+ environment: "development" | "production";
3
+ locationType: "history" | "hash" | "none" | "auto";
4
+ rootURL: string;
5
+ EmberENV?: Record<string, unknown>;
6
+ APP: Record<string, unknown> & { rootElement?: string; autoboot?: boolean };
7
+ }
8
+
9
+ const ENV: Config = {
10
+ environment: import.meta.env.DEV ? "development" : "production",
11
+ rootURL: "/",
12
+ locationType: "history",
13
+ EmberENV: {},
14
+ APP: {},
15
+ };
16
+
17
+ export default ENV;
18
+
19
+ // @ts-expect-error: Ignoreing private API
20
+ import { getGlobalConfig } from "@embroider/macros/src/addon/runtime";
21
+
22
+ export function enterTestMode() {
23
+ ENV.locationType = "none";
24
+ ENV.APP.rootElement = "#ember-testing";
25
+ ENV.APP.autoboot = false;
26
+
27
+ const theMacrosGlobal = getGlobalConfig();
28
+ theMacrosGlobal["@embroider/macros"] ||= {};
29
+ theMacrosGlobal["@embroider/macros"].isTesting ||= true;
30
+ }
@@ -0,0 +1,51 @@
1
+ import EmbroiderRouter from "@embroider/router";
2
+ import config from "#config";
3
+
4
+ export default class Router extends EmbroiderRouter {
5
+ location = config.locationType;
6
+ rootURL = config.rootURL;
7
+ }
8
+
9
+ Router.map(function () {});
10
+
11
+ /**
12
+ * Caveat:
13
+ * - https://github.com/embroider-build/embroider/issues/2521
14
+ * We don't yet have a way to do this in a nice way
15
+ *
16
+ */
17
+ // function bundle(name: string, loader: () => Promise<{ default: unknown }>[]) {
18
+ // return {
19
+ // names: [name],
20
+ // load: async () => {
21
+ // const [template, route, controller] = await Promise.all(loader());
22
+ // let slashName = name.replaceAll(".", "/");
23
+ // let results: Record<string, unknown> = {};
24
+
25
+ // if (template) results[`./templates/${slashName}`] = template.default;
26
+ // if (route) results[`./routes/${slashName}`] = route.default;
27
+ // if (controller) results[`./controllers/${slashName}`] = controller.default;
28
+
29
+ // return {
30
+ // default: results,
31
+ // };
32
+ // },
33
+ // };
34
+ // }
35
+
36
+ /**
37
+ * Examples from:
38
+ * - https://github.com/NullVoxPopuli/limber/blob/67e2f54bbe224052e38f9a9e566d704411e65e86/apps/repl/app/router.ts#L35
39
+ */
40
+ // (window as any)._embroiderRouteBundles_ = [
41
+ // bundle("docs", () => [import("./templates/docs.gts")]),
42
+ // bundle("docs.repl-sdk", () => [import("./templates/docs/repl-sdk.gts")]),
43
+ // bundle("docs.ember-repl", () => [import("./templates/docs/ember-repl.gts")]),
44
+ // bundle("docs.embedding", () => [import("./templates/docs/embedding.gts")]),
45
+ // bundle("docs.editor", () => [import("./templates/docs/editor.gts")]),
46
+ // bundle("docs.whatever", () => [
47
+ // import("./the/template.gts"),
48
+ // import("./the/route.ts"),
49
+ // import("./the/controller.ts"),
50
+ // ]),
51
+ // ];
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <h1>Welcome to Ember</h1>
3
+
4
+ {{outlet}}
5
+ </template>
@@ -1,5 +1,3 @@
1
- import { dirname } from "node:path";
2
- import { fileURLToPath } from "node:url";
3
1
  import { buildMacros } from "@embroider/macros/babel";
4
2
 
5
3
  const macros = buildMacros();
@@ -17,7 +15,6 @@ export default {
17
15
  [
18
16
  "babel-plugin-ember-template-compilation",
19
17
  {
20
- compilerPath: "ember-source/dist/ember-template-compiler.js",
21
18
  transforms: [...macros.templateMacros],
22
19
  },
23
20
  ],
@@ -32,7 +29,7 @@ export default {
32
29
  [
33
30
  "@babel/plugin-transform-runtime",
34
31
  {
35
- absoluteRuntime: dirname(fileURLToPath(import.meta.url)),
32
+ absoluteRuntime: import.meta.dirname,
36
33
  useESModules: true,
37
34
  regenerator: false,
38
35
  },
@@ -2,16 +2,14 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
- <title><%= namespace %></title>
5
+ <title>Minimal App</title>
6
6
  <meta name="description" content="" />
7
7
  <meta name="viewport" content="width=device-width, initial-scale=1" />
8
-
9
- <link rel="stylesheet" href="/app/styles/app.css" />
10
8
  </head>
11
9
  <body>
12
10
  <script type="module">
13
- import Application from "./app/app";
14
- import environment from "./app/config/environment";
11
+ import Application from "#app/app";
12
+ import environment from "#config";
15
13
 
16
14
  Application.create(environment.APP);
17
15
  </script>
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "minimal-app",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "description": "Your Ember App's description here",
6
+ "repository": "",
7
+ "license": "MIT",
8
+ "author": "",
9
+ "type": "module",
10
+ "imports": {
11
+ "#app/*": "./app/*",
12
+ "#config": "./app/config.ts",
13
+ "#components/*": "./app/components/*",
14
+ "#services/*": "./app/services/*",
15
+ "#test-helpers/*": "./tests/helpers/*",
16
+ "#utils/*": "./app/utils/*"
17
+ },
18
+ "exports": {
19
+ "./tests/*": "./tests/*",
20
+ "./*": "./app/*"
21
+ },
22
+ "scripts": {
23
+ "build": "vite build",
24
+ "dev": "vite",
25
+ "start": "vite"
26
+ },
27
+ "dependencies": {
28
+ "@embroider/macros": "^1.19.6",
29
+ "@embroider/router": "^3.0.6",
30
+ "@glimmer/component": "^2.0.0",
31
+ "decorator-transforms": "^2.3.1",
32
+ "ember-source": "~6.10.0",
33
+ "ember-strict-application-resolver": "^0.1.0"
34
+ },
35
+ "devDependencies": {
36
+ "@babel/core": "^7.28.5",
37
+ "@babel/plugin-transform-runtime": "^7.28.5",
38
+ "@babel/plugin-transform-typescript": "^7.28.5",
39
+ "@babel/runtime": "^7.28.4",
40
+ "@ember/app-tsconfig": "^2.0.0",
41
+ "@embroider/core": "^4.4.2",
42
+ "@embroider/vite": "^1.5.0",
43
+ "@glint/ember-tsc": "^1.0.8",
44
+ "@glint/template": "^1.7.3",
45
+ "@glint/tsserver-plugin": "^2.0.8",
46
+ "@rollup/plugin-babel": "^6.1.0",
47
+ "babel-plugin-ember-template-compilation": "^4.0.0",
48
+ "typescript": "^5.9.3",
49
+ "vite": "^7.3.0"
50
+ },
51
+ "engines": {
52
+ "node": ">= 24"
53
+ },
54
+ "ember": {
55
+ "edition": "octane"
56
+ }
57
+ }
@@ -1,13 +1,7 @@
1
1
  {
2
2
  "extends": "@ember/app-tsconfig",
3
- "include": ["app", "tests", "types"],
4
3
  "compilerOptions": {
5
4
  "allowJs": true,
6
- "paths": {
7
- "<%= name %>/tests/*": ["./tests/*"],
8
- "<%= name %>/*": ["./app/*"],
9
- "*": ["./types/*"]
10
- },
11
5
  "types": [
12
6
  "ember-source/types",
13
7
  "@embroider/core/virtual",
@@ -0,0 +1,111 @@
1
+ import { packageJson, js } from "ember-apply";
2
+ import { getLatest } from "#utils/npm.js";
3
+ import { fileURLToPath } from "node:url";
4
+ import { applyFolderTo } from "#utils/fs.js";
5
+ import { removeConfiguredPlugin } from "#utils/babel.js";
6
+
7
+ /**
8
+ * Minimal Layer
9
+ *
10
+ * Provides the absolute bare minimum for an Ember app:
11
+ * - type: "module" in package.json
12
+ * - No @embroider/compat (no-compat mode)
13
+ * - No testing framework
14
+ * - No linting or formatting
15
+ * - No ember-welcome-page
16
+ * - No warp-drive (opt-in if needed)
17
+ */
18
+ export default {
19
+ label: "Minimal App Base",
20
+ description: "Bare minimum Ember app structure",
21
+
22
+ /**
23
+ * 1. Apply files
24
+ * a. Remove TS if needed
25
+ * 2. Remove TS deps/files if needed
26
+ * 3. Update name(s)
27
+ *
28
+ * @param {import('#utils/project.js').Project} project
29
+ */
30
+ async run(project) {
31
+ await applyFiles(project);
32
+ await updateName(project);
33
+ await makeJavaScript(project);
34
+ await upgradeDependencies(project);
35
+ await updateBabelConfig(project);
36
+ },
37
+ };
38
+
39
+ /**
40
+ * @param {import('#utils/project.js').Project} project
41
+ */
42
+ async function updateBabelConfig(project) {
43
+ if (await project.hasOrWantsLayer("typescript")) return;
44
+
45
+ await removeConfiguredPlugin(project, "@babel/plugin-transform-typescript");
46
+ }
47
+
48
+ /**
49
+ * @param {import('#utils/project.js').Project} project
50
+ */
51
+ async function applyFiles(project) {
52
+ let filePath = fileURLToPath(new URL("files", import.meta.url));
53
+
54
+ await applyFolderTo(filePath, project);
55
+ }
56
+
57
+ /**
58
+ * Operates on known files where the name matters
59
+ *
60
+ * @param {import('#utils/project.js').Project} project
61
+ */
62
+ async function updateName(project) {
63
+ await packageJson.modify((json) => {
64
+ json.name = project.desires.name;
65
+ }, project.directory);
66
+ }
67
+
68
+ /**
69
+ * @param {import('#utils/project.js').Project} project
70
+ */
71
+ async function makeJavaScript(project) {
72
+ if (await project.hasOrWantsLayer("typescript")) return;
73
+
74
+ /**
75
+ * We don't actually remove anything, because we want intellisense for JS
76
+ *
77
+ * But perhaps for the sake of minimal, we do remoev it.
78
+ * And we add a layer later for JSDoc ased TS or something
79
+ */
80
+ await packageJson.removeDevDependencies(
81
+ [
82
+ "@ember/app-tsconfig",
83
+ "@glint/ember-tsc",
84
+ "@glint/template",
85
+ "@glint/tsserver-plugin",
86
+ "typescript",
87
+ ],
88
+ project.directory,
89
+ );
90
+
91
+ await project.removeFile("tsconfig.json");
92
+
93
+ await packageJson.modify((json) => {
94
+ json.imports["#config"] = "./app/config.js";
95
+ }, project.directory);
96
+ }
97
+
98
+ /**
99
+ * Bumps in-range only.
100
+ * Majors will need to go through PR to this repo.
101
+ *
102
+ * @param {import('#utils/project.js').Project} project
103
+ */
104
+ async function upgradeDependencies(project) {
105
+ let existing = await packageJson.read(project.directory);
106
+
107
+ await packageJson.modify(async (json) => {
108
+ Object.assign(json.dependencies, await getLatest(existing.dependencies));
109
+ Object.assign(json.devDependencies, await getLatest(existing.devDependencies));
110
+ }, project.directory);
111
+ }
@@ -0,0 +1,10 @@
1
+ export default {
2
+ label: "Minimal Library Base",
3
+ description: "Bare minimum library structure",
4
+
5
+ /**
6
+ *
7
+ * @param {import('#utils/project.js').Project} project
8
+ */
9
+ async run(project) {},
10
+ };
@@ -0,0 +1,57 @@
1
+ import * as p from "@clack/prompts";
2
+ import { styleText, parseArgs } from "node:util";
3
+
4
+ const { values } = parseArgs({
5
+ options: {
6
+ name: {
7
+ type: "string",
8
+ },
9
+
10
+ path: {
11
+ type: "string",
12
+ },
13
+
14
+ type: {
15
+ type: "string",
16
+ choices: ["app", "addon", "library"],
17
+ },
18
+
19
+ confirm: {
20
+ type: "string",
21
+ choices: ["yes", "no"],
22
+ },
23
+
24
+ layers: {
25
+ type: "string",
26
+ multiple: true,
27
+ },
28
+
29
+ packageManager: {
30
+ type: "string",
31
+ choices: ["npm", "pnpm"],
32
+ },
33
+ },
34
+ });
35
+
36
+ const { name, type, layers = [], packageManager, path, confirm } = values;
37
+
38
+ export const answers = {
39
+ name,
40
+ type,
41
+ layers,
42
+ packageManager,
43
+ path,
44
+ confirm,
45
+ };
46
+
47
+ /**
48
+ *
49
+ * @param {string} label
50
+ * @param {string} value
51
+ */
52
+ export function printArgInUse(label, value) {
53
+ let l = styleText(["gray", "bold"], label);
54
+ let v = styleText(["yellow", "italic"], value);
55
+ let u = styleText("dim", "using");
56
+ p.log.info(`${u} ${l}: ${v}`);
57
+ }