@lukoweb/create-apitogo 0.1.1 → 0.1.2

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,317 +1,317 @@
1
- // biome-ignore-all lint/suspicious/noConsole: Logging allowed here
2
- import { execFileSync } from "node:child_process";
3
- import fs from "node:fs/promises";
4
- import os from "node:os";
5
- import path from "node:path";
6
- import { bold, cyan } from "picocolors";
7
- import { copy } from "../helpers/copy";
8
- import { install } from "../helpers/install";
9
- import type { GetTemplateFileArgs, InstallTemplateArgs } from "./types";
10
-
11
- // When bundled by ncc, __dirname points to dist/, but templates are at dist/templates/
12
- const PACKAGE_ROOT = __dirname.endsWith("dist")
13
- ? path.dirname(__dirname)
14
- : path.dirname(__dirname);
15
- const TEMPLATES_DIR = __dirname.endsWith("dist")
16
- ? path.join(__dirname, "templates")
17
- : __dirname;
18
- const WORKSPACE_ROOT = path.resolve(PACKAGE_ROOT, "../..");
19
- const WINDOWS_SHELL = process.env.ComSpec ?? "cmd.exe";
20
-
21
- const parseWorkspaceCatalog = async (workspaceFile: string) => {
22
- const content = await fs.readFile(workspaceFile, "utf8");
23
- const catalog: Record<string, string> = {};
24
- let inCatalog = false;
25
-
26
- for (const line of content.split(/\r?\n/)) {
27
- if (!inCatalog) {
28
- inCatalog = line.trim() === "catalog:";
29
- continue;
30
- }
31
-
32
- if (!line.startsWith(" ")) break;
33
-
34
- const match = line.match(/^ {2}(?:"([^"]+)"|([^:]+)):\s+(.+)$/);
35
- if (!match) continue;
36
-
37
- const dependency = match[1] ?? match[2]?.trim();
38
- const version = match[3]?.trim();
39
- if (dependency && version) catalog[dependency] = version;
40
- }
41
-
42
- return catalog;
43
- };
44
-
45
- const replaceCatalogVersions = (
46
- value: unknown,
47
- catalog: Record<string, string>,
48
- ): unknown => {
49
- if (Array.isArray(value)) {
50
- return value.map((entry) => replaceCatalogVersions(entry, catalog));
51
- }
52
-
53
- if (!value || typeof value !== "object") {
54
- return value;
55
- }
56
-
57
- return Object.fromEntries(
58
- Object.entries(value).map(([key, entry]) => [
59
- key,
60
- entry === "catalog:"
61
- ? (catalog[key] ?? entry)
62
- : replaceCatalogVersions(entry, catalog),
63
- ]),
64
- );
65
- };
66
-
67
- const stageLocalZudokuPackage = async (
68
- sourceDir: string,
69
- stagingDir: string,
70
- ) => {
71
- const workspaceCatalog = await parseWorkspaceCatalog(
72
- path.join(WORKSPACE_ROOT, "pnpm-workspace.yaml"),
73
- );
74
- const packageJsonPath = path.join(sourceDir, "package.json");
75
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
76
- const stagedPackageJson = replaceCatalogVersions(
77
- packageJson,
78
- workspaceCatalog,
79
- ) as typeof packageJson;
80
- const packageFiles = new Set<string>([
81
- "package.json",
82
- ...(Array.isArray(packageJson.files) ? packageJson.files : []),
83
- ]);
84
-
85
- await fs.rm(stagingDir, { recursive: true, force: true });
86
- await fs.mkdir(stagingDir, { recursive: true });
87
-
88
- for (const entry of packageFiles) {
89
- if (entry === "package.json") continue;
90
-
91
- const sourcePath = path.join(sourceDir, entry);
92
- const destinationPath = path.join(stagingDir, entry);
93
- const stat = await fs.stat(sourcePath).catch(() => null);
94
-
95
- if (!stat) continue;
96
-
97
- if (stat.isDirectory()) {
98
- await fs.cp(sourcePath, destinationPath, { recursive: true });
99
- } else {
100
- await fs.mkdir(path.dirname(destinationPath), { recursive: true });
101
- await fs.copyFile(sourcePath, destinationPath);
102
- }
103
- }
104
-
105
- delete stagedPackageJson.scripts?.prepublishOnly;
106
- delete stagedPackageJson.scripts?.postpublish;
107
- if (
108
- stagedPackageJson.scripts &&
109
- Object.keys(stagedPackageJson.scripts).length === 0
110
- ) {
111
- delete stagedPackageJson.scripts;
112
- }
113
-
114
- await fs.writeFile(
115
- path.join(stagingDir, "package.json"),
116
- JSON.stringify(stagedPackageJson, null, 2) + os.EOL,
117
- );
118
- };
119
-
120
- /**
121
- * Get the file path for a given file in a template, e.g. "next.config.js".
122
- */
123
- export const getTemplateFile = ({
124
- template,
125
- mode,
126
- file,
127
- }: GetTemplateFileArgs): string => {
128
- return path.join(TEMPLATES_DIR, template, mode, file);
129
- };
130
-
131
- /**
132
- * Install a Zudoku internal template to a given `root` directory.
133
- */
134
- export const installTemplate = async ({
135
- appName,
136
- root,
137
- packageManager,
138
- isOnline,
139
- template,
140
- mode,
141
- eslint,
142
- skipInstall,
143
- zudokuVersion,
144
- }: InstallTemplateArgs) => {
145
- console.log(bold(`Using ${packageManager}.`));
146
-
147
- /**
148
- * Copy the template files to the target directory.
149
- */
150
- console.log("\nInitializing project with template:", template, "\n");
151
- const templatePath = path.join(TEMPLATES_DIR, template, mode);
152
- const copySource = ["**"];
153
- if (!eslint) copySource.push("!eslintrc.json");
154
-
155
- await copy(copySource, root, {
156
- parents: true,
157
- cwd: templatePath,
158
- rename(name) {
159
- switch (name) {
160
- case "gitignore":
161
- case "eslintrc.json": {
162
- return `.${name}`;
163
- }
164
- // README.md is ignored by webpack-asset-relocator-loader used by ncc:
165
- // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227
166
- case "README-template.md": {
167
- return "README.md";
168
- }
169
- case "zudoku.config.tsx": {
170
- return "apitogo.config.tsx";
171
- }
172
- case "zudoku.config.jsx": {
173
- return "apitogo.config.jsx";
174
- }
175
- default: {
176
- return name;
177
- }
178
- }
179
- },
180
- });
181
-
182
- // update import alias in any files if not using the default
183
-
184
- /** Copy the version from package.json or override for tests. */
185
- const version = process.env.ZUDOKU_PRIVATE_TEST_VERSION ?? zudokuVersion;
186
- const localZudokuPackageDir = path.join(WORKSPACE_ROOT, "packages", "zudoku");
187
- const localZudokuPackageJson = path.join(
188
- localZudokuPackageDir,
189
- "package.json",
190
- );
191
- const resolvedZudokuDependency = await fs
192
- .access(localZudokuPackageJson)
193
- .then(async () => {
194
- const localPackageDir = path.join(root, ".apitogo-local");
195
- const stagedPackageDir = path.join(localPackageDir, "zudoku-package");
196
- await fs.mkdir(localPackageDir, { recursive: true });
197
- await stageLocalZudokuPackage(localZudokuPackageDir, stagedPackageDir);
198
-
199
- const packDestinationArg = path.relative(
200
- stagedPackageDir,
201
- localPackageDir,
202
- );
203
- const packOutput = (
204
- process.platform === "win32"
205
- ? execFileSync(
206
- WINDOWS_SHELL,
207
- [
208
- "/d",
209
- "/c",
210
- "npm",
211
- "pack",
212
- "--pack-destination",
213
- packDestinationArg,
214
- ],
215
- { cwd: stagedPackageDir, encoding: "utf8" },
216
- )
217
- : execFileSync(
218
- "npm",
219
- ["pack", "--pack-destination", packDestinationArg],
220
- {
221
- cwd: stagedPackageDir,
222
- encoding: "utf8",
223
- },
224
- )
225
- )
226
- .trim()
227
- .split(/\r?\n/)
228
- .at(-1);
229
-
230
- if (!packOutput) {
231
- throw new Error("Failed to pack local zudoku package.");
232
- }
233
-
234
- return `file:.apitogo-local/${packOutput}`;
235
- })
236
- .catch(() => version);
237
-
238
- /** Create a package.json for the new project and write it to disk. */
239
- // biome-ignore lint/suspicious/noExplicitAny: Allow any type
240
- const packageJson: any = {
241
- name: appName,
242
- version: "0.1.0",
243
- type: "module",
244
- private: true,
245
- scripts: {
246
- dev: "apitogo dev",
247
- build: "apitogo build",
248
- preview: "apitogo preview",
249
- lint: "eslint",
250
- },
251
- /**
252
- * Default dependencies.
253
- */
254
- dependencies: {
255
- react: ">=19.0.0",
256
- "react-dom": ">=19.0.0",
257
- "@lukoweb/apitogo": resolvedZudokuDependency,
258
- },
259
- devDependencies: {},
260
- };
261
-
262
- /**
263
- * TypeScript projects will have type definitions and other devDependencies.
264
- */
265
- if (mode === "ts") {
266
- packageJson.devDependencies = {
267
- ...packageJson.devDependencies,
268
- "@types/node": "^22",
269
- "@types/react": "^19",
270
- "@types/react-dom": "^19",
271
- typescript: "^5",
272
- };
273
- }
274
-
275
- /* Default ESLint dependencies. */
276
- if (eslint) {
277
- packageJson.devDependencies = {
278
- ...packageJson.devDependencies,
279
- eslint: "^8",
280
- };
281
- }
282
-
283
- // Typescript eslint dependencies
284
- if (eslint && mode === "ts") {
285
- packageJson.devDependencies = {
286
- ...packageJson.devDependencies,
287
- "@typescript-eslint/eslint-plugin": "^8",
288
- "@typescript-eslint/parser": "^8",
289
- };
290
- }
291
-
292
- const devDeps = Object.keys(packageJson.devDependencies).length;
293
- if (!devDeps) delete packageJson.devDependencies;
294
-
295
- await fs.writeFile(
296
- path.join(root, "package.json"),
297
- JSON.stringify(packageJson, null, 2) + os.EOL,
298
- );
299
-
300
- if (skipInstall) return;
301
-
302
- console.log("\nInstalling dependencies:");
303
- for (const dependency in packageJson.dependencies)
304
- console.log(`- ${cyan(dependency)}`);
305
-
306
- if (devDeps) {
307
- console.log("\nInstalling devDependencies:");
308
- for (const dependency in packageJson.devDependencies)
309
- console.log(`- ${cyan(dependency)}`);
310
- }
311
-
312
- console.log();
313
-
314
- await install(packageManager, isOnline);
315
- };
316
-
317
- export * from "./types";
1
+ // biome-ignore-all lint/suspicious/noConsole: Logging allowed here
2
+ import { execFileSync } from "node:child_process";
3
+ import fs from "node:fs/promises";
4
+ import os from "node:os";
5
+ import path from "node:path";
6
+ import { bold, cyan } from "picocolors";
7
+ import { copy } from "../helpers/copy";
8
+ import { install } from "../helpers/install";
9
+ import type { GetTemplateFileArgs, InstallTemplateArgs } from "./types";
10
+
11
+ // When bundled by ncc, __dirname points to dist/, but templates are at dist/templates/
12
+ const PACKAGE_ROOT = __dirname.endsWith("dist")
13
+ ? path.dirname(__dirname)
14
+ : path.dirname(__dirname);
15
+ const TEMPLATES_DIR = __dirname.endsWith("dist")
16
+ ? path.join(__dirname, "templates")
17
+ : __dirname;
18
+ const WORKSPACE_ROOT = path.resolve(PACKAGE_ROOT, "../..");
19
+ const WINDOWS_SHELL = process.env.ComSpec ?? "cmd.exe";
20
+
21
+ const parseWorkspaceCatalog = async (workspaceFile: string) => {
22
+ const content = await fs.readFile(workspaceFile, "utf8");
23
+ const catalog: Record<string, string> = {};
24
+ let inCatalog = false;
25
+
26
+ for (const line of content.split(/\r?\n/)) {
27
+ if (!inCatalog) {
28
+ inCatalog = line.trim() === "catalog:";
29
+ continue;
30
+ }
31
+
32
+ if (!line.startsWith(" ")) break;
33
+
34
+ const match = line.match(/^ {2}(?:"([^"]+)"|([^:]+)):\s+(.+)$/);
35
+ if (!match) continue;
36
+
37
+ const dependency = match[1] ?? match[2]?.trim();
38
+ const version = match[3]?.trim();
39
+ if (dependency && version) catalog[dependency] = version;
40
+ }
41
+
42
+ return catalog;
43
+ };
44
+
45
+ const replaceCatalogVersions = (
46
+ value: unknown,
47
+ catalog: Record<string, string>,
48
+ ): unknown => {
49
+ if (Array.isArray(value)) {
50
+ return value.map((entry) => replaceCatalogVersions(entry, catalog));
51
+ }
52
+
53
+ if (!value || typeof value !== "object") {
54
+ return value;
55
+ }
56
+
57
+ return Object.fromEntries(
58
+ Object.entries(value).map(([key, entry]) => [
59
+ key,
60
+ entry === "catalog:"
61
+ ? (catalog[key] ?? entry)
62
+ : replaceCatalogVersions(entry, catalog),
63
+ ]),
64
+ );
65
+ };
66
+
67
+ const stageLocalZudokuPackage = async (
68
+ sourceDir: string,
69
+ stagingDir: string,
70
+ ) => {
71
+ const workspaceCatalog = await parseWorkspaceCatalog(
72
+ path.join(WORKSPACE_ROOT, "pnpm-workspace.yaml"),
73
+ );
74
+ const packageJsonPath = path.join(sourceDir, "package.json");
75
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
76
+ const stagedPackageJson = replaceCatalogVersions(
77
+ packageJson,
78
+ workspaceCatalog,
79
+ ) as typeof packageJson;
80
+ const packageFiles = new Set<string>([
81
+ "package.json",
82
+ ...(Array.isArray(packageJson.files) ? packageJson.files : []),
83
+ ]);
84
+
85
+ await fs.rm(stagingDir, { recursive: true, force: true });
86
+ await fs.mkdir(stagingDir, { recursive: true });
87
+
88
+ for (const entry of packageFiles) {
89
+ if (entry === "package.json") continue;
90
+
91
+ const sourcePath = path.join(sourceDir, entry);
92
+ const destinationPath = path.join(stagingDir, entry);
93
+ const stat = await fs.stat(sourcePath).catch(() => null);
94
+
95
+ if (!stat) continue;
96
+
97
+ if (stat.isDirectory()) {
98
+ await fs.cp(sourcePath, destinationPath, { recursive: true });
99
+ } else {
100
+ await fs.mkdir(path.dirname(destinationPath), { recursive: true });
101
+ await fs.copyFile(sourcePath, destinationPath);
102
+ }
103
+ }
104
+
105
+ delete stagedPackageJson.scripts?.prepublishOnly;
106
+ delete stagedPackageJson.scripts?.postpublish;
107
+ if (
108
+ stagedPackageJson.scripts &&
109
+ Object.keys(stagedPackageJson.scripts).length === 0
110
+ ) {
111
+ delete stagedPackageJson.scripts;
112
+ }
113
+
114
+ await fs.writeFile(
115
+ path.join(stagingDir, "package.json"),
116
+ JSON.stringify(stagedPackageJson, null, 2) + os.EOL,
117
+ );
118
+ };
119
+
120
+ /**
121
+ * Get the file path for a given file in a template, e.g. "next.config.js".
122
+ */
123
+ export const getTemplateFile = ({
124
+ template,
125
+ mode,
126
+ file,
127
+ }: GetTemplateFileArgs): string => {
128
+ return path.join(TEMPLATES_DIR, template, mode, file);
129
+ };
130
+
131
+ /**
132
+ * Install a Zudoku internal template to a given `root` directory.
133
+ */
134
+ export const installTemplate = async ({
135
+ appName,
136
+ root,
137
+ packageManager,
138
+ isOnline,
139
+ template,
140
+ mode,
141
+ eslint,
142
+ skipInstall,
143
+ zudokuVersion,
144
+ }: InstallTemplateArgs) => {
145
+ console.log(bold(`Using ${packageManager}.`));
146
+
147
+ /**
148
+ * Copy the template files to the target directory.
149
+ */
150
+ console.log("\nInitializing project with template:", template, "\n");
151
+ const templatePath = path.join(TEMPLATES_DIR, template, mode);
152
+ const copySource = ["**"];
153
+ if (!eslint) copySource.push("!eslintrc.json");
154
+
155
+ await copy(copySource, root, {
156
+ parents: true,
157
+ cwd: templatePath,
158
+ rename(name) {
159
+ switch (name) {
160
+ case "gitignore":
161
+ case "eslintrc.json": {
162
+ return `.${name}`;
163
+ }
164
+ // README.md is ignored by webpack-asset-relocator-loader used by ncc:
165
+ // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227
166
+ case "README-template.md": {
167
+ return "README.md";
168
+ }
169
+ case "zudoku.config.tsx": {
170
+ return "apitogo.config.tsx";
171
+ }
172
+ case "zudoku.config.jsx": {
173
+ return "apitogo.config.jsx";
174
+ }
175
+ default: {
176
+ return name;
177
+ }
178
+ }
179
+ },
180
+ });
181
+
182
+ // update import alias in any files if not using the default
183
+
184
+ /** Copy the version from package.json or override for tests. */
185
+ const version = process.env.ZUDOKU_PRIVATE_TEST_VERSION ?? zudokuVersion;
186
+ const localZudokuPackageDir = path.join(WORKSPACE_ROOT, "packages", "zudoku");
187
+ const localZudokuPackageJson = path.join(
188
+ localZudokuPackageDir,
189
+ "package.json",
190
+ );
191
+ const resolvedZudokuDependency = await fs
192
+ .access(localZudokuPackageJson)
193
+ .then(async () => {
194
+ const localPackageDir = path.join(root, ".apitogo-local");
195
+ const stagedPackageDir = path.join(localPackageDir, "zudoku-package");
196
+ await fs.mkdir(localPackageDir, { recursive: true });
197
+ await stageLocalZudokuPackage(localZudokuPackageDir, stagedPackageDir);
198
+
199
+ const packDestinationArg = path.relative(
200
+ stagedPackageDir,
201
+ localPackageDir,
202
+ );
203
+ const packOutput = (
204
+ process.platform === "win32"
205
+ ? execFileSync(
206
+ WINDOWS_SHELL,
207
+ [
208
+ "/d",
209
+ "/c",
210
+ "npm",
211
+ "pack",
212
+ "--pack-destination",
213
+ packDestinationArg,
214
+ ],
215
+ { cwd: stagedPackageDir, encoding: "utf8" },
216
+ )
217
+ : execFileSync(
218
+ "npm",
219
+ ["pack", "--pack-destination", packDestinationArg],
220
+ {
221
+ cwd: stagedPackageDir,
222
+ encoding: "utf8",
223
+ },
224
+ )
225
+ )
226
+ .trim()
227
+ .split(/\r?\n/)
228
+ .at(-1);
229
+
230
+ if (!packOutput) {
231
+ throw new Error("Failed to pack local zudoku package.");
232
+ }
233
+
234
+ return `file:.apitogo-local/${packOutput}`;
235
+ })
236
+ .catch(() => version);
237
+
238
+ /** Create a package.json for the new project and write it to disk. */
239
+ // biome-ignore lint/suspicious/noExplicitAny: Allow any type
240
+ const packageJson: any = {
241
+ name: appName,
242
+ version: "0.1.0",
243
+ type: "module",
244
+ private: true,
245
+ scripts: {
246
+ dev: "apitogo dev",
247
+ build: "apitogo build",
248
+ preview: "apitogo preview",
249
+ lint: "eslint",
250
+ },
251
+ /**
252
+ * Default dependencies.
253
+ */
254
+ dependencies: {
255
+ react: ">=19.0.0",
256
+ "react-dom": ">=19.0.0",
257
+ "@lukoweb/apitogo": resolvedZudokuDependency,
258
+ },
259
+ devDependencies: {},
260
+ };
261
+
262
+ /**
263
+ * TypeScript projects will have type definitions and other devDependencies.
264
+ */
265
+ if (mode === "ts") {
266
+ packageJson.devDependencies = {
267
+ ...packageJson.devDependencies,
268
+ "@types/node": "^22",
269
+ "@types/react": "^19",
270
+ "@types/react-dom": "^19",
271
+ typescript: "^5",
272
+ };
273
+ }
274
+
275
+ /* Default ESLint dependencies. */
276
+ if (eslint) {
277
+ packageJson.devDependencies = {
278
+ ...packageJson.devDependencies,
279
+ eslint: "^8",
280
+ };
281
+ }
282
+
283
+ // Typescript eslint dependencies
284
+ if (eslint && mode === "ts") {
285
+ packageJson.devDependencies = {
286
+ ...packageJson.devDependencies,
287
+ "@typescript-eslint/eslint-plugin": "^8",
288
+ "@typescript-eslint/parser": "^8",
289
+ };
290
+ }
291
+
292
+ const devDeps = Object.keys(packageJson.devDependencies).length;
293
+ if (!devDeps) delete packageJson.devDependencies;
294
+
295
+ await fs.writeFile(
296
+ path.join(root, "package.json"),
297
+ JSON.stringify(packageJson, null, 2) + os.EOL,
298
+ );
299
+
300
+ if (skipInstall) return;
301
+
302
+ console.log("\nInstalling dependencies:");
303
+ for (const dependency in packageJson.dependencies)
304
+ console.log(`- ${cyan(dependency)}`);
305
+
306
+ if (devDeps) {
307
+ console.log("\nInstalling devDependencies:");
308
+ for (const dependency in packageJson.devDependencies)
309
+ console.log(`- ${cyan(dependency)}`);
310
+ }
311
+
312
+ console.log();
313
+
314
+ await install(packageManager, isOnline);
315
+ };
316
+
317
+ export * from "./types";