create-template-project 0.2.0 → 0.4.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/README.md +3 -3
- package/dist/config/dependencies.json +10 -14
- package/dist/index.js +1259 -0
- package/dist/templates/base/files/.husky/commit-msg +1 -0
- package/dist/templates/base/files/.husky/pre-commit +1 -0
- package/dist/templates/base/files/.prettierignore +3 -0
- package/dist/templates/base/files/package.json +1 -1
- package/dist/templates/base/files/vitest.config.ts +10 -1
- package/dist/templates/cli/files/package.json +4 -3
- package/dist/templates/cli/files/src/index.test.ts +10 -2
- package/dist/templates/cli/files/src/index.ts +4 -1
- package/dist/templates/cli/files/src/lib.ts +10 -0
- package/dist/templates/cli/files/vite.config.ts +22 -0
- package/dist/templates/web-app/files/src/App.test.tsx +9 -0
- package/dist/templates/web-app/files/src/App.tsx +14 -0
- package/dist/templates/web-app/files/src/index.tsx +1 -14
- package/dist/templates/web-app/files/vite.config.ts +4 -0
- package/dist/templates/web-fullstack/files/client/src/App.test.tsx +5 -2
- package/dist/templates/web-fullstack/files/client/vite.config.ts +4 -0
- package/dist/templates/web-fullstack/files/package.json +1 -2
- package/dist/templates/web-fullstack/files/server/src/index.test.ts +24 -3
- package/dist/templates/web-fullstack/files/server/src/trpc.ts +1 -1
- package/dist/templates/web-fullstack/files/server/vite.config.ts +6 -3
- package/dist/templates/web-vanilla/files/index.html +1 -1
- package/dist/templates/web-vanilla/files/src/index.test.ts +9 -2
- package/dist/templates/web-vanilla/files/src/index.ts +3 -1
- package/dist/templates/web-vanilla/files/src/lib.ts +9 -0
- package/dist/templates/web-vanilla/files/vite.config.ts +4 -0
- package/package.json +13 -18
- package/dist/cli.mjs +0 -308
- package/dist/generators/info.mjs +0 -58
- package/dist/generators/project.mjs +0 -387
- package/dist/index.d.mts +0 -4
- package/dist/index.mjs +0 -35
- package/dist/templates/base/index.mjs +0 -55
- package/dist/templates/cli/files/tsdown.config.ts +0 -3
- package/dist/templates/cli/index.mjs +0 -29
- package/dist/templates/web-app/files/src/index.test.ts +0 -5
- package/dist/templates/web-app/index.mjs +0 -69
- package/dist/templates/web-fullstack/index.mjs +0 -78
- package/dist/templates/web-vanilla/index.mjs +0 -45
- package/dist/types.mjs +0 -30
- package/dist/utils/file.mjs +0 -119
|
@@ -1,387 +0,0 @@
|
|
|
1
|
-
import { getBaseTemplate } from "../templates/base/index.mjs";
|
|
2
|
-
import { getCliTemplate } from "../templates/cli/index.mjs";
|
|
3
|
-
import { getWebVanillaTemplate } from "../templates/web-vanilla/index.mjs";
|
|
4
|
-
import { getWebAppTemplate } from "../templates/web-app/index.mjs";
|
|
5
|
-
import { getWebFullstackTemplate } from "../templates/web-fullstack/index.mjs";
|
|
6
|
-
import { getAllFiles, isSeedFile, mergeFile, mergePackageJson, processContent } from "../utils/file.mjs";
|
|
7
|
-
import * as p from "@clack/prompts";
|
|
8
|
-
import path from "node:path";
|
|
9
|
-
import fs from "node:fs/promises";
|
|
10
|
-
import debugLib from "debug";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
|
-
import { execa } from "execa";
|
|
13
|
-
//#region src/generators/project.ts
|
|
14
|
-
const debug = debugLib("create-template-project:generator");
|
|
15
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
-
const DEPENDENCY_CONFIG_PATH = path.resolve(__dirname, "../config/dependencies.json");
|
|
17
|
-
const pathExists = (p) => fs.access(p).then(() => true).catch(() => false);
|
|
18
|
-
const getLog = (silent) => ({
|
|
19
|
-
info: (msg) => !silent ? p.log.info(msg) : void 0,
|
|
20
|
-
success: (msg) => !silent ? p.log.success(msg) : void 0,
|
|
21
|
-
warn: (msg) => !silent ? p.log.warn(msg) : void 0,
|
|
22
|
-
error: (msg) => !silent ? p.log.error(msg) : void 0
|
|
23
|
-
});
|
|
24
|
-
const getSpinner = (silent) => {
|
|
25
|
-
const s = p.spinner();
|
|
26
|
-
return {
|
|
27
|
-
start: (msg) => !silent ? s.start(msg) : void 0,
|
|
28
|
-
stop: (msg) => !silent ? s.stop(msg) : void 0,
|
|
29
|
-
message: (msg) => !silent ? s.message(msg) : void 0
|
|
30
|
-
};
|
|
31
|
-
};
|
|
32
|
-
const showNote = (msg, title, silent) => {
|
|
33
|
-
if (silent) return;
|
|
34
|
-
if (title) p.log.success(title);
|
|
35
|
-
p.note(msg);
|
|
36
|
-
};
|
|
37
|
-
const generateProject = async (opts) => {
|
|
38
|
-
const { template: type, projectName, directory, update, overwrite, skipBuild, silent } = opts;
|
|
39
|
-
const isSilent = !!silent;
|
|
40
|
-
const log = getLog(isSilent);
|
|
41
|
-
const spinner = () => getSpinner(isSilent);
|
|
42
|
-
const projectDir = path.join(directory, projectName);
|
|
43
|
-
debug("Project generation started for: %s", projectName);
|
|
44
|
-
debug("Options: %O", opts);
|
|
45
|
-
debug("Project directory: %s", projectDir);
|
|
46
|
-
let isUpdate = !!update;
|
|
47
|
-
if (await pathExists(projectDir)) {
|
|
48
|
-
if (overwrite) {
|
|
49
|
-
await fs.rm(projectDir, {
|
|
50
|
-
recursive: true,
|
|
51
|
-
force: true
|
|
52
|
-
});
|
|
53
|
-
isUpdate = false;
|
|
54
|
-
} else if (!isUpdate) throw new Error(`Directory "${projectDir}" already exists. Use --overwrite to replace it or --update to update.`);
|
|
55
|
-
}
|
|
56
|
-
const templates = [getBaseTemplate(opts)];
|
|
57
|
-
debug("Applying template: base");
|
|
58
|
-
switch (type) {
|
|
59
|
-
case "cli":
|
|
60
|
-
debug("Applying template: cli");
|
|
61
|
-
templates.push(getCliTemplate(opts));
|
|
62
|
-
break;
|
|
63
|
-
case "web-vanilla":
|
|
64
|
-
debug("Applying template: web-vanilla");
|
|
65
|
-
templates.push(getWebVanillaTemplate(opts));
|
|
66
|
-
break;
|
|
67
|
-
case "web-app":
|
|
68
|
-
debug("Applying template: web-app");
|
|
69
|
-
templates.push(getWebAppTemplate(opts));
|
|
70
|
-
break;
|
|
71
|
-
case "web-fullstack":
|
|
72
|
-
debug("Applying template: web-fullstack");
|
|
73
|
-
templates.push(getWebFullstackTemplate(opts));
|
|
74
|
-
break;
|
|
75
|
-
}
|
|
76
|
-
debug("Ensuring directory exists: %s", projectDir);
|
|
77
|
-
await fs.mkdir(projectDir, { recursive: true });
|
|
78
|
-
debug("Loading dependency configuration");
|
|
79
|
-
const depConfig = JSON.parse(await fs.readFile(DEPENDENCY_CONFIG_PATH, "utf8"));
|
|
80
|
-
const addedDeps = [];
|
|
81
|
-
const resolveDeps = (deps = {}) => {
|
|
82
|
-
for (const dep of Object.keys(deps)) {
|
|
83
|
-
const config = depConfig.dependencies[dep];
|
|
84
|
-
if (config) {
|
|
85
|
-
deps[dep] = config.version;
|
|
86
|
-
addedDeps.push({
|
|
87
|
-
name: dep,
|
|
88
|
-
description: config.description
|
|
89
|
-
});
|
|
90
|
-
} else {
|
|
91
|
-
log.warn(`Dependency "${dep}" not found in master configuration. Using empty version.`);
|
|
92
|
-
debug(`Dependency "${dep}" missing in config`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
let finalPkg = {
|
|
97
|
-
name: projectName,
|
|
98
|
-
version: "0.1.0",
|
|
99
|
-
type: "module",
|
|
100
|
-
"create-template-project": { template: type },
|
|
101
|
-
scripts: {},
|
|
102
|
-
dependencies: {},
|
|
103
|
-
devDependencies: {}
|
|
104
|
-
};
|
|
105
|
-
const pkgPath = path.join(projectDir, "package.json");
|
|
106
|
-
if (isUpdate && await pathExists(pkgPath)) {
|
|
107
|
-
debug("Loading existing package.json for update");
|
|
108
|
-
const existingPkg = JSON.parse(await fs.readFile(pkgPath, "utf8"));
|
|
109
|
-
finalPkg = {
|
|
110
|
-
...finalPkg,
|
|
111
|
-
...existingPkg
|
|
112
|
-
};
|
|
113
|
-
finalPkg["create-template-project"] = {
|
|
114
|
-
...existingPkg["create-template-project"],
|
|
115
|
-
template: type
|
|
116
|
-
};
|
|
117
|
-
finalPkg.scripts = { ...existingPkg.scripts };
|
|
118
|
-
finalPkg.dependencies = { ...existingPkg.dependencies };
|
|
119
|
-
finalPkg.devDependencies = { ...existingPkg.devDependencies };
|
|
120
|
-
debug("Loaded existing package.json: %O", finalPkg);
|
|
121
|
-
}
|
|
122
|
-
for (const t of templates) {
|
|
123
|
-
debug("Collecting dependencies and scripts from template: %s", t.name);
|
|
124
|
-
const templateDeps = { ...t.dependencies };
|
|
125
|
-
const templateDevDeps = { ...t.devDependencies };
|
|
126
|
-
resolveDeps(templateDeps);
|
|
127
|
-
resolveDeps(templateDevDeps);
|
|
128
|
-
Object.assign(finalPkg.scripts, t.scripts);
|
|
129
|
-
Object.assign(finalPkg.dependencies, templateDeps);
|
|
130
|
-
Object.assign(finalPkg.devDependencies, templateDevDeps);
|
|
131
|
-
if (t.workspaces) finalPkg.workspaces = t.workspaces;
|
|
132
|
-
if (t.templateDir) {
|
|
133
|
-
const templatePkgPath = path.join(t.templateDir, "package.json");
|
|
134
|
-
if (await pathExists(templatePkgPath)) {
|
|
135
|
-
const pkgPart = JSON.parse(await fs.readFile(templatePkgPath, "utf8"));
|
|
136
|
-
resolveDeps(pkgPart.dependencies);
|
|
137
|
-
resolveDeps(pkgPart.devDependencies);
|
|
138
|
-
mergePackageJson(finalPkg, pkgPart);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
for (const t of templates) {
|
|
143
|
-
debug("Processing template files for: %s", t.name);
|
|
144
|
-
if (t.templateDir) {
|
|
145
|
-
debug("Reading physical files from: %s", t.templateDir);
|
|
146
|
-
const files = await getAllFiles(t.templateDir);
|
|
147
|
-
for (const file of files) {
|
|
148
|
-
let relativePath = path.relative(t.templateDir, file);
|
|
149
|
-
let targetPath = path.join(projectDir, relativePath);
|
|
150
|
-
if (isUpdate && isSeedFile(relativePath)) {
|
|
151
|
-
debug("Skipping seed file during update: %s", relativePath);
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (relativePath === "_oxlint.config.ts") {
|
|
155
|
-
relativePath = "oxlint.config.ts";
|
|
156
|
-
targetPath = path.join(projectDir, relativePath);
|
|
157
|
-
}
|
|
158
|
-
if (relativePath === "package.json") continue;
|
|
159
|
-
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
160
|
-
debug("Reading and processing content for: %s", relativePath);
|
|
161
|
-
let content = await fs.readFile(file, "utf8");
|
|
162
|
-
content = processContent(relativePath, content, opts, addedDeps);
|
|
163
|
-
let finalTargetPath = targetPath;
|
|
164
|
-
if (type === "web-vanilla" && skipBuild && relativePath === "src/index.ts") {
|
|
165
|
-
debug("Changing target path for web-vanilla index.ts to .js due to skipBuild");
|
|
166
|
-
finalTargetPath = path.join(projectDir, "src/index.js");
|
|
167
|
-
}
|
|
168
|
-
if (isUpdate && await pathExists(finalTargetPath)) {
|
|
169
|
-
debug("File exists, attempting to update/merge: %s", finalTargetPath);
|
|
170
|
-
const existingContent = await fs.readFile(finalTargetPath, "utf8");
|
|
171
|
-
if (existingContent.trim() !== content.trim()) {
|
|
172
|
-
const result = await mergeFile(finalTargetPath, existingContent, content, log);
|
|
173
|
-
if (result === "merged") log.info(`ℹ Merged: ${relativePath}`);
|
|
174
|
-
else if (result === "conflict") log.warn(`⚠ Conflict: ${relativePath}`);
|
|
175
|
-
else if (result === "updated") log.info(`✔ Updated: ${relativePath}`);
|
|
176
|
-
} else debug("Content identical, skipping: %s", finalTargetPath);
|
|
177
|
-
} else {
|
|
178
|
-
debug("Writing file: %s", finalTargetPath);
|
|
179
|
-
await fs.writeFile(finalTargetPath, content);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
for (const file of t.files) {
|
|
184
|
-
const targetPath = path.join(projectDir, file.path);
|
|
185
|
-
if (isUpdate && isSeedFile(file.path)) {
|
|
186
|
-
debug("Skipping programmatic seed file: %s", file.path);
|
|
187
|
-
continue;
|
|
188
|
-
}
|
|
189
|
-
debug("Processing programmatic file: %s", file.path);
|
|
190
|
-
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
191
|
-
let content = typeof file.content === "function" ? file.content() : file.content;
|
|
192
|
-
content = processContent(file.path, content, opts, addedDeps);
|
|
193
|
-
if (isUpdate && await pathExists(targetPath)) {
|
|
194
|
-
debug("File exists, attempting to update/merge programmatic file: %s", targetPath);
|
|
195
|
-
const existingContent = await fs.readFile(targetPath, "utf8");
|
|
196
|
-
if (existingContent.trim() !== content.trim()) {
|
|
197
|
-
const result = await mergeFile(targetPath, existingContent, content, log);
|
|
198
|
-
if (result === "merged") log.info(`ℹ Merged: ${file.path}`);
|
|
199
|
-
else if (result === "conflict") log.warn(`⚠ Conflict: ${file.path}`);
|
|
200
|
-
else if (result === "updated") log.info(`✔ Updated: ${file.path}`);
|
|
201
|
-
} else debug("Content identical, skipping programmatic: %s", targetPath);
|
|
202
|
-
} else {
|
|
203
|
-
debug("Writing programmatic file: %s", targetPath);
|
|
204
|
-
await fs.writeFile(targetPath, content);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
const pm = opts.packageManager || "npm";
|
|
209
|
-
if (pm !== "npm") {
|
|
210
|
-
for (const [key, value] of Object.entries(finalPkg.scripts)) if (typeof value === "string") finalPkg.scripts[key] = value.replaceAll("npm run ", `${pm} run `);
|
|
211
|
-
}
|
|
212
|
-
if (pm === "pnpm" && finalPkg.workspaces) {
|
|
213
|
-
debug("Creating pnpm-workspace.yaml");
|
|
214
|
-
const workspaceYaml = `packages:\n${finalPkg.workspaces.map((w) => ` - '${w}'`).join("\n")}\n`;
|
|
215
|
-
await fs.writeFile(path.join(projectDir, "pnpm-workspace.yaml"), workspaceYaml);
|
|
216
|
-
delete finalPkg.workspaces;
|
|
217
|
-
for (const key of Object.keys(finalPkg.scripts)) {
|
|
218
|
-
const value = finalPkg.scripts[key];
|
|
219
|
-
if (typeof value === "string" && value.includes("--workspaces")) finalPkg.scripts[key] = value.replace(" run ", " -r run ").replace(" --workspaces", "");
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
if (skipBuild) {
|
|
223
|
-
debug("Applying skipBuild overrides");
|
|
224
|
-
delete finalPkg.scripts.build;
|
|
225
|
-
delete finalPkg.scripts.dev;
|
|
226
|
-
if (finalPkg.devDependencies) {
|
|
227
|
-
delete finalPkg.devDependencies.tsdown;
|
|
228
|
-
delete finalPkg.devDependencies.vite;
|
|
229
|
-
delete finalPkg.devDependencies["@vitejs/plugin-react"];
|
|
230
|
-
}
|
|
231
|
-
if (finalPkg.scripts.ci) finalPkg.scripts.ci = finalPkg.scripts.ci.replace(" && npm run build", "").replace(` && ${pm} run build`, "");
|
|
232
|
-
debug("Removing build tool configs due to skipBuild");
|
|
233
|
-
await fs.rm(path.join(projectDir, "tsdown.config.ts"), { force: true });
|
|
234
|
-
await fs.rm(path.join(projectDir, "vite.config.ts"), { force: true });
|
|
235
|
-
await fs.rm(path.join(projectDir, "vite.config.server.ts"), { force: true });
|
|
236
|
-
await fs.rm(path.join(projectDir, "client/vite.config.ts"), { force: true });
|
|
237
|
-
await fs.rm(path.join(projectDir, "server/vite.config.ts"), { force: true });
|
|
238
|
-
}
|
|
239
|
-
debug("Writing final consolidated package.json to: %s", pkgPath);
|
|
240
|
-
await fs.writeFile(pkgPath, JSON.stringify(finalPkg, null, " "));
|
|
241
|
-
const stdio = debug.enabled ? "inherit" : "pipe";
|
|
242
|
-
if (!await pathExists(path.join(projectDir, ".git"))) {
|
|
243
|
-
debug("Initializing Git repository");
|
|
244
|
-
try {
|
|
245
|
-
debug("Executing: git init");
|
|
246
|
-
await execa("git", ["init"], {
|
|
247
|
-
cwd: projectDir,
|
|
248
|
-
stdio,
|
|
249
|
-
preferLocal: true
|
|
250
|
-
});
|
|
251
|
-
log.success("Initialized Git repository (git init).");
|
|
252
|
-
} catch (e) {
|
|
253
|
-
debug("Failed to initialize Git: %O", e);
|
|
254
|
-
const detail = e.stdout || e.stderr ? `\n\nOutput:\n${e.stdout}\n${e.stderr}` : "";
|
|
255
|
-
log.error(`Failed to initialize Git: ${e.message}${detail}`);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
if (opts.createGithubRepository && !isUpdate) {
|
|
259
|
-
debug("Creating GitHub repository");
|
|
260
|
-
try {
|
|
261
|
-
debug("Executing: gh repo create %s --public --source=. --remote=origin", projectName);
|
|
262
|
-
await execa("gh", [
|
|
263
|
-
"repo",
|
|
264
|
-
"create",
|
|
265
|
-
projectName,
|
|
266
|
-
"--public",
|
|
267
|
-
"--source=.",
|
|
268
|
-
"--remote=origin"
|
|
269
|
-
], {
|
|
270
|
-
cwd: projectDir,
|
|
271
|
-
stdio,
|
|
272
|
-
preferLocal: true
|
|
273
|
-
});
|
|
274
|
-
log.success("Created GitHub repository (gh repo create).");
|
|
275
|
-
} catch (e) {
|
|
276
|
-
debug("Failed to create GitHub repository: %O", e);
|
|
277
|
-
const detail = e.stdout || e.stderr ? `\n\nOutput:\n${e.stdout}\n${e.stderr}` : "";
|
|
278
|
-
log.warn(`Failed to create GitHub repository: ${e.message}${detail}\nEnsure "gh" CLI is installed and authenticated.`);
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
if (opts.installDependencies) {
|
|
282
|
-
debug("Installing dependencies using %s", pm);
|
|
283
|
-
const s = spinner();
|
|
284
|
-
s.start(`Installing dependencies using ${pm}...`);
|
|
285
|
-
try {
|
|
286
|
-
debug("Executing: %s install", pm);
|
|
287
|
-
await execa(pm, ["install"], {
|
|
288
|
-
cwd: projectDir,
|
|
289
|
-
stdio,
|
|
290
|
-
preferLocal: true
|
|
291
|
-
});
|
|
292
|
-
s.stop(`\x1b[1G\x1b[2K\x1b[32m◆\x1b[39m Dependencies installed (${pm} install).`);
|
|
293
|
-
} catch (e) {
|
|
294
|
-
debug("Failed to install dependencies: %O", e);
|
|
295
|
-
s.stop("Failed to install dependencies.");
|
|
296
|
-
const detail = e.stdout || e.stderr ? `\n\nOutput:\n${e.stdout}\n${e.stderr}` : "";
|
|
297
|
-
log.error(`${e.message}${detail}`);
|
|
298
|
-
throw new Error(`Failed to install dependencies: ${e.message}${detail}`);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
if (opts.build && finalPkg.scripts.ci) {
|
|
302
|
-
debug("Running CI script");
|
|
303
|
-
const s = spinner();
|
|
304
|
-
if (finalPkg.scripts["prettier-write"]) {
|
|
305
|
-
s.start(`Formatting files with Prettier (${pm} run prettier-write)...`);
|
|
306
|
-
try {
|
|
307
|
-
debug("Executing: %s run prettier-write", pm);
|
|
308
|
-
await execa(pm, ["run", "prettier-write"], {
|
|
309
|
-
cwd: projectDir,
|
|
310
|
-
stdio,
|
|
311
|
-
preferLocal: true
|
|
312
|
-
});
|
|
313
|
-
s.stop(`\x1b[1G\x1b[2K\x1b[32m◆\x1b[39m Files formatted (${pm} run prettier-write).`);
|
|
314
|
-
} catch (e) {
|
|
315
|
-
debug("Failed to format files: %O", e);
|
|
316
|
-
s.stop("Failed to format files.");
|
|
317
|
-
const detail = e.stdout || e.stderr ? `\n\nOutput:\n${e.stdout}\n${e.stderr}` : "";
|
|
318
|
-
log.error(`${e.message}${detail}`);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
s.start(`Running CI script (lint, build, test) (${pm} run ci)...`);
|
|
322
|
-
try {
|
|
323
|
-
debug("Executing: %s run ci", pm);
|
|
324
|
-
await execa(pm, ["run", "ci"], {
|
|
325
|
-
cwd: projectDir,
|
|
326
|
-
stdio,
|
|
327
|
-
preferLocal: true
|
|
328
|
-
});
|
|
329
|
-
s.stop(`\x1b[1G\x1b[2K\x1b[32m◆\x1b[39m CI script completed (${pm} run ci).`);
|
|
330
|
-
} catch (e) {
|
|
331
|
-
debug("Failed to run CI script: %O", e);
|
|
332
|
-
s.stop("Failed to run CI script.");
|
|
333
|
-
const detail = e.stdout || e.stderr ? `\n\nOutput:\n${e.stdout}\n${e.stderr}` : "";
|
|
334
|
-
log.error(`${e.message}${detail}`);
|
|
335
|
-
throw new Error(`Failed to run CI script: ${e.message}${detail}`);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
log.success(`Project "${projectName}" ${isUpdate ? "updated" : "scaffolded"} successfully in ${projectDir}`);
|
|
339
|
-
showSummary(opts, pm, isSilent);
|
|
340
|
-
if (opts.dev && finalPkg.scripts.dev) {
|
|
341
|
-
log.info("Starting dev server...");
|
|
342
|
-
if (opts.open) try {
|
|
343
|
-
debug("Executing: %s run dev -- --open", pm);
|
|
344
|
-
await execa(pm, [
|
|
345
|
-
"run",
|
|
346
|
-
"dev",
|
|
347
|
-
"--",
|
|
348
|
-
"--open"
|
|
349
|
-
], {
|
|
350
|
-
cwd: projectDir,
|
|
351
|
-
stdio: "inherit",
|
|
352
|
-
preferLocal: true
|
|
353
|
-
});
|
|
354
|
-
} catch (e) {
|
|
355
|
-
log.error("Dev server failed: " + e);
|
|
356
|
-
}
|
|
357
|
-
else try {
|
|
358
|
-
debug("Executing: %s run dev", pm);
|
|
359
|
-
await execa(pm, ["run", "dev"], {
|
|
360
|
-
cwd: projectDir,
|
|
361
|
-
stdio: "inherit",
|
|
362
|
-
preferLocal: true
|
|
363
|
-
});
|
|
364
|
-
} catch (e) {
|
|
365
|
-
log.error("Dev server failed: " + e);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
};
|
|
369
|
-
function showSummary(opts, pm, isSilent) {
|
|
370
|
-
debug("Showing summary for options: %O", opts);
|
|
371
|
-
const { projectName, template } = opts;
|
|
372
|
-
const summary = [
|
|
373
|
-
`Successfully created a new ${template} project named '${projectName}'.`,
|
|
374
|
-
"",
|
|
375
|
-
"Available Commands:"
|
|
376
|
-
];
|
|
377
|
-
const commands = [
|
|
378
|
-
`${pm} run dev - Starts the development server`,
|
|
379
|
-
`${pm} run build - Builds the project for production`,
|
|
380
|
-
`${pm} run test - Runs the unit test suite (Vitest)`,
|
|
381
|
-
`${pm} run lint - Lints and formats the codebase`,
|
|
382
|
-
`${pm} run ci - Runs lint, build, and test (used by CI/CD)`
|
|
383
|
-
];
|
|
384
|
-
showNote([...summary, ...commands.map((c) => ` ${c}`)].join("\n"), "Project ready", isSilent);
|
|
385
|
-
}
|
|
386
|
-
//#endregion
|
|
387
|
-
export { generateProject };
|
package/dist/index.d.mts
DELETED
package/dist/index.mjs
DELETED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { parseArgs } from "./cli.mjs";
|
|
3
|
-
import { generateProject } from "./generators/project.mjs";
|
|
4
|
-
import { cancel, intro, outro } from "@clack/prompts";
|
|
5
|
-
import debugLib from "debug";
|
|
6
|
-
//#region src/index.ts
|
|
7
|
-
if (process.argv.includes("--debug")) {
|
|
8
|
-
process.env["DEBUG"] = "create-template-project:*";
|
|
9
|
-
debugLib.enable("create-template-project:*");
|
|
10
|
-
}
|
|
11
|
-
const debug = debugLib("create-template-project:main");
|
|
12
|
-
const main = async () => {
|
|
13
|
-
try {
|
|
14
|
-
debug("Starting CLI execution");
|
|
15
|
-
debug("Parsing arguments");
|
|
16
|
-
const options = await parseArgs();
|
|
17
|
-
if (!options) return;
|
|
18
|
-
const isSilent = !!options.silent;
|
|
19
|
-
if (!isSilent) intro("create-template-project");
|
|
20
|
-
debug("Arguments parsed: %O", options);
|
|
21
|
-
debug("Generating project");
|
|
22
|
-
await generateProject(options);
|
|
23
|
-
debug("Project generation complete");
|
|
24
|
-
if (!isSilent) outro("Done!");
|
|
25
|
-
} catch (error) {
|
|
26
|
-
debug("Execution failed: %O", error);
|
|
27
|
-
if (error.code === "PROCESS_EXIT_0" || error.code === "commander.helpDisplayed" || error.code === "commander.version") process.exit(0);
|
|
28
|
-
if (error.code === "PROCESS_EXIT_1") process.exit(1);
|
|
29
|
-
cancel(error?.message || String(error));
|
|
30
|
-
process.exit(1);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
if (import.meta.url.endsWith("src/index.ts") || import.meta.url.endsWith("dist/index.mjs")) await main();
|
|
34
|
-
//#endregion
|
|
35
|
-
export { main };
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
//#region src/templates/base/index.ts
|
|
4
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
-
const getBaseTemplate = (_opts) => {
|
|
6
|
-
return {
|
|
7
|
-
name: "base",
|
|
8
|
-
description: "The foundation for all project templates, including common tooling and configuration.",
|
|
9
|
-
components: [
|
|
10
|
-
{
|
|
11
|
-
name: "TypeScript",
|
|
12
|
-
description: "Mandatory strict mode for type safety."
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
name: "oxlint",
|
|
16
|
-
description: "Ultra-fast Rust-based linter."
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
name: "Prettier",
|
|
20
|
-
description: "Consistent code formatting."
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: "Vitest",
|
|
24
|
-
description: "Modern, fast unit testing with coverage."
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: "Husky",
|
|
28
|
-
description: "Git hooks for pre-commit linting."
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
name: "commitlint",
|
|
32
|
-
description: "Standardized commit messages."
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: "conventional-changelog",
|
|
36
|
-
description: "Automated release notes."
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
name: "debug",
|
|
40
|
-
description: "Structured logging for debugging."
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
name: "Zod",
|
|
44
|
-
description: "TypeScript-first schema validation for runtime type safety."
|
|
45
|
-
}
|
|
46
|
-
],
|
|
47
|
-
dependencies: { zod: "zod" },
|
|
48
|
-
devDependencies: { "eslint-plugin-regexp": "" },
|
|
49
|
-
scripts: {},
|
|
50
|
-
files: [],
|
|
51
|
-
templateDir: path.resolve(__dirname, "files")
|
|
52
|
-
};
|
|
53
|
-
};
|
|
54
|
-
//#endregion
|
|
55
|
-
export { getBaseTemplate };
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
//#region src/templates/cli/index.ts
|
|
4
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
-
const getCliTemplate = (opts) => {
|
|
6
|
-
const components = [{
|
|
7
|
-
name: "commander",
|
|
8
|
-
description: "The complete solution for Node.js command-line interfaces."
|
|
9
|
-
}, {
|
|
10
|
-
name: "cli-progress",
|
|
11
|
-
description: "Easy to use progress-bar for terminal applications."
|
|
12
|
-
}];
|
|
13
|
-
if (!opts.skipBuild) components.push({
|
|
14
|
-
name: "tsdown",
|
|
15
|
-
description: "A zero-config bundler for TypeScript."
|
|
16
|
-
});
|
|
17
|
-
return {
|
|
18
|
-
name: "cli",
|
|
19
|
-
description: "A robust Node.js command-line application template.",
|
|
20
|
-
components,
|
|
21
|
-
dependencies: {},
|
|
22
|
-
devDependencies: {},
|
|
23
|
-
scripts: {},
|
|
24
|
-
files: [],
|
|
25
|
-
templateDir: path.resolve(__dirname, "files")
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
//#endregion
|
|
29
|
-
export { getCliTemplate };
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
//#region src/templates/web-app/index.ts
|
|
4
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
-
const getWebAppTemplate = (_opts) => {
|
|
6
|
-
return {
|
|
7
|
-
name: "web-app",
|
|
8
|
-
description: "A React application with MUI and TanStack Query, powered by Vite.",
|
|
9
|
-
components: [
|
|
10
|
-
{
|
|
11
|
-
name: "React",
|
|
12
|
-
description: "Powerful library for building component-based user interfaces."
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
name: "MUI",
|
|
16
|
-
description: "Rich set of Material Design UI components for React."
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
name: "TanStack React Query",
|
|
20
|
-
description: "Powerful asynchronous state management for React."
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: "Vite",
|
|
24
|
-
description: "Next-generation frontend tooling."
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: "Vitest",
|
|
28
|
-
description: "Testing framework with cross-browser support."
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
name: "Playwright",
|
|
32
|
-
description: "End-to-end testing for modern web apps."
|
|
33
|
-
}
|
|
34
|
-
],
|
|
35
|
-
dependencies: {
|
|
36
|
-
react: "react",
|
|
37
|
-
"react-dom": "react-dom",
|
|
38
|
-
"@mui/material": "@mui/material",
|
|
39
|
-
"@mui/icons-material": "@mui/icons-material",
|
|
40
|
-
"@emotion/react": "@emotion/react",
|
|
41
|
-
"@emotion/styled": "@emotion/styled",
|
|
42
|
-
"@tanstack/react-query": "@tanstack/react-query"
|
|
43
|
-
},
|
|
44
|
-
devDependencies: {
|
|
45
|
-
"@types/react": "@types/react",
|
|
46
|
-
"@types/react-dom": "@types/react-dom",
|
|
47
|
-
vite: "vite",
|
|
48
|
-
"@vitejs/plugin-react": "@vitejs/plugin-react",
|
|
49
|
-
vitest: "vitest",
|
|
50
|
-
"@vitest/browser": "@vitest/browser",
|
|
51
|
-
"@vitest/browser-playwright": "@vitest/browser-playwright",
|
|
52
|
-
playwright: "playwright",
|
|
53
|
-
"@playwright/test": "@playwright/test"
|
|
54
|
-
},
|
|
55
|
-
scripts: {
|
|
56
|
-
dev: "vite",
|
|
57
|
-
build: "vite build",
|
|
58
|
-
preview: "vite preview",
|
|
59
|
-
test: "vitest run",
|
|
60
|
-
"test:ui": "vitest",
|
|
61
|
-
"test:e2e": "playwright test",
|
|
62
|
-
start: "vite preview"
|
|
63
|
-
},
|
|
64
|
-
files: [],
|
|
65
|
-
templateDir: path.resolve(__dirname, "files")
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
//#endregion
|
|
69
|
-
export { getWebAppTemplate };
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
//#region src/templates/web-fullstack/index.ts
|
|
4
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
5
|
-
const getWebFullstackTemplate = (_opts) => {
|
|
6
|
-
return {
|
|
7
|
-
name: "web-fullstack",
|
|
8
|
-
description: "A comprehensive full-stack monorepo featuring an Express backend with tRPC and a modern React client with MUI.",
|
|
9
|
-
components: [
|
|
10
|
-
{
|
|
11
|
-
name: "React",
|
|
12
|
-
description: "Powerful library for building component-based user interfaces."
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
name: "MUI",
|
|
16
|
-
description: "Rich set of Material Design UI components for React."
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
name: "tRPC",
|
|
20
|
-
description: "End-to-end typesafe APIs made easy."
|
|
21
|
-
},
|
|
22
|
-
{
|
|
23
|
-
name: "React Query",
|
|
24
|
-
description: "Powerful asynchronous state management for React."
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
name: "Express",
|
|
28
|
-
description: "Fast, minimalist backend web framework."
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
name: "React Router",
|
|
32
|
-
description: "Declarative routing for the frontend."
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
name: "Vite",
|
|
36
|
-
description: "Fast, modern frontend build tool."
|
|
37
|
-
}
|
|
38
|
-
],
|
|
39
|
-
dependencies: {
|
|
40
|
-
react: "react",
|
|
41
|
-
"react-dom": "react-dom",
|
|
42
|
-
"@mui/material": "@mui/material",
|
|
43
|
-
"@mui/icons-material": "@mui/icons-material",
|
|
44
|
-
"@emotion/react": "@emotion/react",
|
|
45
|
-
"@emotion/styled": "@emotion/styled",
|
|
46
|
-
express: "express",
|
|
47
|
-
"@trpc/server": "@trpc/server",
|
|
48
|
-
"@trpc/client": "@trpc/client",
|
|
49
|
-
"@trpc/react-query": "@trpc/react-query",
|
|
50
|
-
"@tanstack/react-query": "@tanstack/react-query",
|
|
51
|
-
"react-router-dom": "react-router-dom",
|
|
52
|
-
cors: "cors"
|
|
53
|
-
},
|
|
54
|
-
devDependencies: {
|
|
55
|
-
"@types/react": "@types/react",
|
|
56
|
-
"@types/react-dom": "@types/react-dom",
|
|
57
|
-
"@types/express": "@types/express",
|
|
58
|
-
"@types/cors": "@types/cors",
|
|
59
|
-
"@playwright/test": "@playwright/test",
|
|
60
|
-
vite: "vite",
|
|
61
|
-
"@vitejs/plugin-react": "@vitejs/plugin-react",
|
|
62
|
-
vitest: "vitest",
|
|
63
|
-
"@vitest/browser": "@vitest/browser",
|
|
64
|
-
"@vitest/browser-playwright": "@vitest/browser-playwright",
|
|
65
|
-
playwright: "playwright"
|
|
66
|
-
},
|
|
67
|
-
scripts: {
|
|
68
|
-
build: "npm run build --workspaces",
|
|
69
|
-
dev: "npm run dev --workspaces",
|
|
70
|
-
test: "npm run test --workspaces",
|
|
71
|
-
"test:e2e": "playwright test"
|
|
72
|
-
},
|
|
73
|
-
files: [],
|
|
74
|
-
templateDir: path.resolve(__dirname, "files")
|
|
75
|
-
};
|
|
76
|
-
};
|
|
77
|
-
//#endregion
|
|
78
|
-
export { getWebFullstackTemplate };
|