create-template-project 0.1.0 → 0.3.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 (84) hide show
  1. package/README.md +11 -11
  2. package/dist/config/dependencies.json +60 -44
  3. package/dist/index.js +1221 -0
  4. package/dist/templates/base/files/.github/workflows/node.js.yml +5 -3
  5. package/dist/templates/base/files/.husky/commit-msg +1 -0
  6. package/dist/templates/base/files/.husky/pre-commit +1 -0
  7. package/dist/templates/base/files/.prettierignore +53 -0
  8. package/dist/templates/base/files/.prettierrc.json +4 -2
  9. package/dist/templates/base/files/AGENTS.md +5 -2
  10. package/dist/templates/base/files/README.md +36 -13
  11. package/dist/templates/base/files/_oxlint.config.ts +17 -33
  12. package/dist/templates/base/files/package.json +1 -1
  13. package/dist/templates/base/files/tsconfig.json +32 -32
  14. package/dist/templates/base/files/vitest.config.ts +11 -2
  15. package/dist/templates/cli/files/package.json +4 -3
  16. package/dist/templates/cli/files/src/index.test.ts +10 -2
  17. package/dist/templates/cli/files/src/index.ts +4 -1
  18. package/dist/templates/cli/files/src/lib.ts +10 -0
  19. package/dist/templates/cli/files/vite.config.ts +22 -0
  20. package/dist/templates/web-app/files/index.html +11 -0
  21. package/dist/templates/web-app/files/playwright.config.ts +26 -0
  22. package/dist/templates/web-app/files/src/App.test.tsx +9 -0
  23. package/dist/templates/web-app/files/src/App.tsx +14 -0
  24. package/dist/templates/web-app/files/src/index.tsx +8 -0
  25. package/dist/templates/web-app/files/tests/e2e/basic.e2e-test.ts +13 -0
  26. package/dist/templates/web-app/files/vite.config.ts +34 -0
  27. package/dist/templates/web-fullstack/files/client/index.html +10 -0
  28. package/dist/templates/{fullstack → web-fullstack}/files/client/package.json +2 -2
  29. package/dist/templates/web-fullstack/files/client/src/App.test.tsx +11 -0
  30. package/dist/templates/{fullstack → web-fullstack}/files/client/src/contexts/AuthContext.tsx +1 -5
  31. package/dist/templates/{fullstack → web-fullstack}/files/client/src/pages/Login.tsx +4 -33
  32. package/dist/templates/web-fullstack/files/client/vite.config.ts +30 -0
  33. package/dist/templates/{fullstack → web-fullstack}/files/package.json +1 -2
  34. package/dist/templates/web-fullstack/files/playwright.config.ts +33 -0
  35. package/dist/templates/{fullstack → web-fullstack}/files/server/package.json +2 -2
  36. package/dist/templates/web-fullstack/files/server/src/index.test.ts +28 -0
  37. package/dist/templates/{fullstack → web-fullstack}/files/server/src/trpc.ts +1 -1
  38. package/dist/templates/web-fullstack/files/server/vite.config.ts +24 -0
  39. package/dist/templates/web-fullstack/files/tests/e2e/basic.e2e-test.ts +14 -0
  40. package/dist/templates/web-vanilla/files/index.html +11 -0
  41. package/dist/templates/web-vanilla/files/package.json +17 -0
  42. package/dist/templates/web-vanilla/files/playwright.config.ts +26 -0
  43. package/dist/templates/web-vanilla/files/src/index.test.ts +12 -0
  44. package/dist/templates/web-vanilla/files/src/index.ts +3 -0
  45. package/dist/templates/web-vanilla/files/src/lib.ts +9 -0
  46. package/dist/templates/web-vanilla/files/tests/e2e/basic.e2e-test.ts +11 -0
  47. package/dist/templates/web-vanilla/files/vite.config.ts +28 -0
  48. package/package.json +25 -27
  49. package/dist/cli.mjs +0 -272
  50. package/dist/generators/project.mjs +0 -354
  51. package/dist/index.d.mts +0 -4
  52. package/dist/index.mjs +0 -32
  53. package/dist/templates/base/index.mjs +0 -16
  54. package/dist/templates/cli/files/tsdown.config.ts +0 -3
  55. package/dist/templates/cli/index.mjs +0 -16
  56. package/dist/templates/fullstack/files/client/index.html +0 -8
  57. package/dist/templates/fullstack/files/client/src/App.test.tsx +0 -8
  58. package/dist/templates/fullstack/files/client/tsdown.config.ts +0 -3
  59. package/dist/templates/fullstack/files/server/src/index.test.ts +0 -7
  60. package/dist/templates/fullstack/files/server/tsdown.config.ts +0 -3
  61. package/dist/templates/fullstack/index.mjs +0 -42
  62. package/dist/templates/webapp/files/backend/src/index.ts +0 -17
  63. package/dist/templates/webapp/files/frontend/index.html +0 -9
  64. package/dist/templates/webapp/files/frontend/src/index.ts +0 -4
  65. package/dist/templates/webapp/files/package.json +0 -13
  66. package/dist/templates/webapp/files/src/index.test.ts +0 -5
  67. package/dist/templates/webapp/files/tsdown.config.ts +0 -10
  68. package/dist/templates/webapp/index.mjs +0 -16
  69. package/dist/templates/webpage/files/index.html +0 -8
  70. package/dist/templates/webpage/files/package.json +0 -8
  71. package/dist/templates/webpage/files/src/index.test.ts +0 -5
  72. package/dist/templates/webpage/files/src/index.ts +0 -1
  73. package/dist/templates/webpage/index.mjs +0 -16
  74. package/dist/types.mjs +0 -30
  75. package/dist/utils/file.mjs +0 -101
  76. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/App.tsx +0 -0
  77. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/components/ProtectedRoute.tsx +0 -0
  78. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/main.tsx +0 -0
  79. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/pages/Dashboard.tsx +0 -0
  80. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/trpc.ts +0 -0
  81. /package/dist/templates/{fullstack → web-fullstack}/files/server/src/context.ts +0 -0
  82. /package/dist/templates/{fullstack → web-fullstack}/files/server/src/index.ts +0 -0
  83. /package/dist/templates/{fullstack → web-fullstack}/files/server/src/routers/_app.ts +0 -0
  84. /package/dist/templates/{fullstack → web-fullstack}/files/server/src/routers/auth.ts +0 -0
package/dist/cli.mjs DELETED
@@ -1,272 +0,0 @@
1
- import { ProjectOptionsSchema } from "./types.mjs";
2
- import { Command } from "commander";
3
- import * as p from "@clack/prompts";
4
- import path from "node:path";
5
- import fs from "node:fs/promises";
6
- import debugLib from "debug";
7
- //#region src/cli.ts
8
- const pathExists = (p) => fs.access(p).then(() => true).catch(() => false);
9
- const debug = debugLib("create-template-project:cli");
10
- const parseArgs = async () => {
11
- debug("Parsing CLI arguments: %O", process.argv);
12
- const program = new Command();
13
- if (process.env.NODE_ENV === "test") program.configureOutput({
14
- writeOut: () => {},
15
- writeErr: () => {}
16
- });
17
- program.name("create-template-project").exitOverride().description("Scaffold a new project template").version("0.1.0").option("--debug", "Enable debug output").on("option:debug", () => {
18
- debugLib.enable("create-template-project:*");
19
- }).addHelpText("after", `
20
- Templates:
21
- cli - Node.js CLI application with commander and cli-progress.
22
- webpage - Standalone web page (modern HTML/JS).
23
- webapp - Web application with TypeScript and an Express backend.
24
- fullstack - Full-stack monorepo with Express server and React/MUI client.
25
- `);
26
- let commandResult;
27
- program.command("create").description("Create a new project from a template").option("-t, --template <type>", "Template type (cli, webpage, webapp, fullstack)").option("-n, --name <name>", "Project name").option("-p, --package-manager <pm>", "Package manager (npm, pnpm, yarn)", "npm").option("--create-github-repository", "Create GitHub project").option("-d, --directory <path>", "Output directory", ".").option("--overwrite", "Overwrite existing directory by removing it first", false).option("--skip-build", "Skip build tooling (disables bundling and uses raw source files)", false).option("--install-dependencies", "Install dependencies after scaffolding", false).option("--build", "Run the CI script (lint, build, test) after scaffolding", false).option("--dev", "Run the dev server after scaffolding", false).option("--open", "Open the browser after scaffolding", false).option("--silent", "Reduce console output", false).action((opts) => {
28
- debug("Executing \"create\" command with options: %O", opts);
29
- if (opts.template === "webapp" && opts.skipBuild) {
30
- p.log.error("The --skip-build option is not allowed for the \"webapp\" template.");
31
- process.exit(1);
32
- }
33
- commandResult = {
34
- ...opts,
35
- update: false,
36
- template: opts.template,
37
- projectName: opts.name,
38
- packageManager: opts.packageManager,
39
- directory: path.resolve(opts.directory),
40
- createGithubRepository: !!opts.createGithubRepository,
41
- overwrite: !!opts.overwrite,
42
- silent: !!opts.silent
43
- };
44
- debug("Processed \"create\" options: %O", commandResult);
45
- });
46
- program.command("update").description("Update an existing project from its template").option("-t, --template <type>", "Template type (cli, webpage, webapp, fullstack)").option("-n, --name <name>", "Project name").option("-p, --package-manager <pm>", "Package manager (npm, pnpm, yarn)", "npm").option("--create-github-repository", "Create GitHub project").option("-d, --directory <path>", "Output directory", ".").option("--overwrite", "Overwrite existing directory by removing it first", false).option("--skip-build", "Skip build tooling (disables bundling and uses raw source files)", false).option("--install-dependencies", "Install dependencies after scaffolding", false).option("--build", "Run the CI script (lint, build, test) after updating", false).option("--dev", "Run the dev server after scaffolding", false).option("--open", "Open the browser after scaffolding", false).option("--silent", "Reduce console output", false).action((opts) => {
47
- debug("Executing \"update\" command with options: %O", opts);
48
- if (opts.template === "webapp" && opts.skipBuild) {
49
- p.log.error("The --skip-build option is not allowed for the \"webapp\" template.");
50
- process.exit(1);
51
- }
52
- commandResult = {
53
- ...opts,
54
- update: true,
55
- template: opts.template,
56
- projectName: opts.name,
57
- packageManager: opts.packageManager,
58
- directory: path.resolve(opts.directory),
59
- createGithubRepository: !!opts.createGithubRepository,
60
- overwrite: !!opts.overwrite,
61
- silent: !!opts.silent
62
- };
63
- debug("Processed \"update\" options: %O", commandResult);
64
- });
65
- program.command("interactive").description("Start interactive project configuration").action(async () => {
66
- debug("Starting interactive configuration");
67
- const projectName = await p.text({
68
- message: "Project name:",
69
- placeholder: "my-app",
70
- defaultValue: "my-app",
71
- validate: (value) => value && value.length > 0 ? void 0 : "Project name is required"
72
- });
73
- if (p.isCancel(projectName)) {
74
- p.cancel("Operation cancelled.");
75
- process.exit(0);
76
- }
77
- const directory = await p.text({
78
- message: "Target directory:",
79
- initialValue: "."
80
- });
81
- if (p.isCancel(directory)) {
82
- p.cancel("Operation cancelled.");
83
- process.exit(0);
84
- }
85
- const projectDir = path.resolve(directory, projectName);
86
- const exists = await pathExists(projectDir);
87
- const pkgPath = path.join(projectDir, "package.json");
88
- const pkgExists = await pathExists(pkgPath);
89
- let existingConfig = {};
90
- if (pkgExists) try {
91
- existingConfig = JSON.parse(await fs.readFile(pkgPath, "utf8"))["create-template-project"] || {};
92
- debug("Found existing project config: %O", existingConfig);
93
- } catch (e) {
94
- debug("Failed to read existing package.json: %O", e);
95
- }
96
- let update = false;
97
- let overwrite = false;
98
- if (exists) {
99
- const action = await p.select({
100
- message: `Directory "${projectDir}" already exists. What would you like to do?`,
101
- options: [
102
- {
103
- label: "Run an update",
104
- value: "update"
105
- },
106
- {
107
- label: "Overwrite existing directory by removing it first",
108
- value: "overwrite"
109
- },
110
- {
111
- label: "Cancel",
112
- value: "cancel"
113
- }
114
- ]
115
- });
116
- if (p.isCancel(action) || action === "cancel") {
117
- p.cancel("Operation cancelled.");
118
- process.exit(0);
119
- }
120
- if (action === "update") update = true;
121
- else if (action === "overwrite") overwrite = true;
122
- }
123
- let template = existingConfig.template;
124
- if (!update || !template) {
125
- template = await p.select({
126
- message: "Select project template:",
127
- initialValue: template || "cli",
128
- options: [
129
- {
130
- label: "CLI Application (Node.js)",
131
- value: "cli"
132
- },
133
- {
134
- label: "Webpage (Standalone)",
135
- value: "webpage"
136
- },
137
- {
138
- label: "Webapp (Express Backend)",
139
- value: "webapp"
140
- },
141
- {
142
- label: "Full-stack (Express + React/MUI Monorepo)",
143
- value: "fullstack"
144
- }
145
- ]
146
- });
147
- if (p.isCancel(template)) {
148
- p.cancel("Operation cancelled.");
149
- process.exit(0);
150
- }
151
- } else p.log.info(`Using existing template type: ${template}`);
152
- let packageManager = "npm";
153
- if (!update) {
154
- packageManager = await p.select({
155
- message: "Select package manager:",
156
- initialValue: "npm",
157
- options: [
158
- {
159
- label: "npm",
160
- value: "npm"
161
- },
162
- {
163
- label: "pnpm",
164
- value: "pnpm"
165
- },
166
- {
167
- label: "yarn",
168
- value: "yarn"
169
- }
170
- ]
171
- });
172
- if (p.isCancel(packageManager)) {
173
- p.cancel("Operation cancelled.");
174
- process.exit(0);
175
- }
176
- }
177
- let skipBuild = false;
178
- if (template !== "webapp") {
179
- const res = await p.confirm({
180
- message: "Should we use build tooling? (Enables bundling using tsdown, and uses raw dist/ instead of src/)",
181
- initialValue: true
182
- });
183
- if (p.isCancel(res)) {
184
- p.cancel("Operation cancelled.");
185
- process.exit(0);
186
- }
187
- skipBuild = !res;
188
- }
189
- const installDependenciesRes = await p.confirm({
190
- message: "Should we install dependencies?",
191
- initialValue: true
192
- });
193
- if (p.isCancel(installDependenciesRes)) {
194
- p.cancel("Operation cancelled.");
195
- process.exit(0);
196
- }
197
- const installDependencies = installDependenciesRes;
198
- let build = false;
199
- if (installDependencies) {
200
- const res = await p.confirm({
201
- message: "Should we run the CI script (lint, build, test)?",
202
- initialValue: true
203
- });
204
- if (p.isCancel(res)) {
205
- p.cancel("Operation cancelled.");
206
- process.exit(0);
207
- }
208
- build = res;
209
- }
210
- const createGithubRepositoryRes = await p.confirm({
211
- message: "Should we create a GitHub repository?",
212
- initialValue: false
213
- });
214
- if (p.isCancel(createGithubRepositoryRes)) {
215
- p.cancel("Operation cancelled.");
216
- process.exit(0);
217
- }
218
- commandResult = {
219
- template,
220
- projectName,
221
- packageManager,
222
- createGithubRepository: createGithubRepositoryRes,
223
- directory: path.resolve(directory),
224
- update,
225
- overwrite,
226
- skipBuild,
227
- installDependencies,
228
- build,
229
- dev: false,
230
- open: false,
231
- silent: false
232
- };
233
- });
234
- if (process.argv.length <= 2) {
235
- program.outputHelp();
236
- process.exit(0);
237
- }
238
- try {
239
- await program.parseAsync(process.argv);
240
- } catch (e) {
241
- if (e.code === "commander.helpDisplayed" || e.code === "commander.version" || e.code === "PROCESS_EXIT_0") process.exit(0);
242
- if (e.code === "PROCESS_EXIT_1") process.exit(1);
243
- p.cancel(e.message);
244
- process.exit(1);
245
- }
246
- if (!commandResult) {
247
- debug("No command result found");
248
- p.cancel("Unknown command or missing options.");
249
- process.exit(1);
250
- }
251
- debug("Validating command result with Zod");
252
- const validationResult = ProjectOptionsSchema.safeParse(commandResult);
253
- if (!validationResult.success) {
254
- const errors = validationResult.error.issues.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
255
- p.cancel(`Invalid options: ${errors}`);
256
- process.exit(1);
257
- }
258
- commandResult = validationResult.data;
259
- const projectDir = path.resolve(commandResult.directory, commandResult.projectName);
260
- if (await pathExists(projectDir) && !commandResult.update && !commandResult.overwrite) {
261
- p.cancel(`Directory "${projectDir}" already exists. Use --overwrite to overwrite or "update" command.`);
262
- process.exit(1);
263
- }
264
- if (commandResult.open) {
265
- commandResult.dev = true;
266
- commandResult.installDependencies = true;
267
- }
268
- if (commandResult.dev || commandResult.build) commandResult.installDependencies = true;
269
- return commandResult;
270
- };
271
- //#endregion
272
- export { parseArgs };
@@ -1,354 +0,0 @@
1
- import { getBaseTemplate } from "../templates/base/index.mjs";
2
- import { getCliTemplate } from "../templates/cli/index.mjs";
3
- import { getWebpageTemplate } from "../templates/webpage/index.mjs";
4
- import { getWebappTemplate } from "../templates/webapp/index.mjs";
5
- import { getFullstackTemplate } from "../templates/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) => !silent ? p.note(msg, title) : void 0;
33
- const generateProject = async (opts) => {
34
- const { template: type, projectName, directory, update, overwrite, skipBuild, silent } = opts;
35
- const isSilent = !!silent;
36
- const log = getLog(isSilent);
37
- const spinner = () => getSpinner(isSilent);
38
- const projectDir = path.join(directory, projectName);
39
- debug("Project generation started for: %s", projectName);
40
- debug("Options: %O", opts);
41
- debug("Project directory: %s", projectDir);
42
- let isUpdate = !!update;
43
- if (await pathExists(projectDir)) {
44
- if (overwrite) {
45
- await fs.rm(projectDir, {
46
- recursive: true,
47
- force: true
48
- });
49
- isUpdate = false;
50
- } else if (!isUpdate) throw new Error(`Directory "${projectDir}" already exists. Use --overwrite to replace it or --update to update.`);
51
- }
52
- const templates = [getBaseTemplate(opts)];
53
- debug("Applying template: base");
54
- switch (type) {
55
- case "cli":
56
- debug("Applying template: cli");
57
- templates.push(getCliTemplate(opts));
58
- break;
59
- case "webpage":
60
- debug("Applying template: webpage");
61
- templates.push(getWebpageTemplate(opts));
62
- break;
63
- case "webapp":
64
- debug("Applying template: webapp");
65
- templates.push(getWebappTemplate(opts));
66
- break;
67
- case "fullstack":
68
- debug("Applying template: fullstack");
69
- templates.push(getFullstackTemplate(opts));
70
- break;
71
- }
72
- debug("Ensuring directory exists: %s", projectDir);
73
- await fs.mkdir(projectDir, { recursive: true });
74
- debug("Loading dependency configuration");
75
- const depConfig = JSON.parse(await fs.readFile(DEPENDENCY_CONFIG_PATH, "utf8"));
76
- const addedDeps = [];
77
- const resolveDeps = (deps = {}) => {
78
- for (const dep of Object.keys(deps)) {
79
- const config = depConfig.dependencies[dep];
80
- if (config) {
81
- deps[dep] = config.version;
82
- addedDeps.push({
83
- name: dep,
84
- description: config.description
85
- });
86
- } else {
87
- log.warn(`Dependency "${dep}" not found in master configuration. Using empty version.`);
88
- debug(`Dependency "${dep}" missing in config`);
89
- }
90
- }
91
- };
92
- let finalPkg = {
93
- name: projectName,
94
- version: "0.1.0",
95
- type: "module",
96
- "create-template-project": { template: type },
97
- scripts: {},
98
- dependencies: {},
99
- devDependencies: {}
100
- };
101
- const pkgPath = path.join(projectDir, "package.json");
102
- if (isUpdate && await pathExists(pkgPath)) {
103
- debug("Loading existing package.json for update");
104
- const existingPkg = JSON.parse(await fs.readFile(pkgPath, "utf8"));
105
- finalPkg = {
106
- ...finalPkg,
107
- ...existingPkg
108
- };
109
- finalPkg["create-template-project"] = {
110
- ...existingPkg["create-template-project"],
111
- template: type
112
- };
113
- finalPkg.scripts = { ...existingPkg.scripts };
114
- finalPkg.dependencies = { ...existingPkg.dependencies };
115
- finalPkg.devDependencies = { ...existingPkg.devDependencies };
116
- debug("Loaded existing package.json: %O", finalPkg);
117
- }
118
- for (const t of templates) {
119
- debug("Collecting dependencies and scripts from template: %s", t.name);
120
- const templateDeps = { ...t.dependencies };
121
- const templateDevDeps = { ...t.devDependencies };
122
- resolveDeps(templateDeps);
123
- resolveDeps(templateDevDeps);
124
- Object.assign(finalPkg.scripts, t.scripts);
125
- Object.assign(finalPkg.dependencies, templateDeps);
126
- Object.assign(finalPkg.devDependencies, templateDevDeps);
127
- if (t.templateDir) {
128
- const templatePkgPath = path.join(t.templateDir, "package.json");
129
- if (await pathExists(templatePkgPath)) {
130
- const pkgPart = JSON.parse(await fs.readFile(templatePkgPath, "utf8"));
131
- resolveDeps(pkgPart.dependencies);
132
- resolveDeps(pkgPart.devDependencies);
133
- mergePackageJson(finalPkg, pkgPart);
134
- }
135
- }
136
- }
137
- for (const t of templates) {
138
- debug("Processing template files for: %s", t.name);
139
- if (t.templateDir) {
140
- debug("Reading physical files from: %s", t.templateDir);
141
- const files = await getAllFiles(t.templateDir);
142
- for (const file of files) {
143
- let relativePath = path.relative(t.templateDir, file);
144
- let targetPath = path.join(projectDir, relativePath);
145
- if (isUpdate && isSeedFile(relativePath)) {
146
- debug("Skipping seed file during update: %s", relativePath);
147
- continue;
148
- }
149
- if (relativePath === "_oxlint.config.ts") {
150
- relativePath = "oxlint.config.ts";
151
- targetPath = path.join(projectDir, relativePath);
152
- }
153
- if (relativePath === "package.json") continue;
154
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
155
- debug("Reading and processing content for: %s", relativePath);
156
- let content = await fs.readFile(file, "utf8");
157
- content = processContent(relativePath, content, opts, addedDeps);
158
- let finalTargetPath = targetPath;
159
- if (type === "webpage" && skipBuild && relativePath === "src/index.ts") {
160
- debug("Changing target path for webpage index.ts to .js due to skipBuild");
161
- finalTargetPath = path.join(projectDir, "src/index.js");
162
- }
163
- if (isUpdate && await pathExists(finalTargetPath)) {
164
- debug("File exists, attempting to update/merge: %s", finalTargetPath);
165
- const existingContent = await fs.readFile(finalTargetPath, "utf8");
166
- if (existingContent.trim() !== content.trim()) {
167
- const result = await mergeFile(finalTargetPath, existingContent, content, log);
168
- if (result === "merged") log.info(`ℹ Merged: ${relativePath}`);
169
- else if (result === "conflict") log.warn(`⚠ Conflict: ${relativePath}`);
170
- else if (result === "updated") log.info(`✔ Updated: ${relativePath}`);
171
- } else debug("Content identical, skipping: %s", finalTargetPath);
172
- } else {
173
- debug("Writing file: %s", finalTargetPath);
174
- await fs.writeFile(finalTargetPath, content);
175
- }
176
- }
177
- }
178
- for (const file of t.files) {
179
- const targetPath = path.join(projectDir, file.path);
180
- if (isUpdate && isSeedFile(file.path)) {
181
- debug("Skipping programmatic seed file: %s", file.path);
182
- continue;
183
- }
184
- debug("Processing programmatic file: %s", file.path);
185
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
186
- let content = typeof file.content === "function" ? file.content() : file.content;
187
- content = processContent(file.path, content, opts, addedDeps);
188
- if (isUpdate && await pathExists(targetPath)) {
189
- debug("File exists, attempting to update/merge programmatic file: %s", targetPath);
190
- const existingContent = await fs.readFile(targetPath, "utf8");
191
- if (existingContent.trim() !== content.trim()) {
192
- const result = await mergeFile(targetPath, existingContent, content, log);
193
- if (result === "merged") log.info(`ℹ Merged: ${file.path}`);
194
- else if (result === "conflict") log.warn(`⚠ Conflict: ${file.path}`);
195
- else if (result === "updated") log.info(`✔ Updated: ${file.path}`);
196
- } else debug("Content identical, skipping programmatic: %s", targetPath);
197
- } else {
198
- debug("Writing programmatic file: %s", targetPath);
199
- await fs.writeFile(targetPath, content);
200
- }
201
- }
202
- }
203
- const pm = opts.packageManager || "npm";
204
- if (pm !== "npm") {
205
- for (const [key, value] of Object.entries(finalPkg.scripts)) if (typeof value === "string") finalPkg.scripts[key] = value.replaceAll("npm run ", `${pm} run `);
206
- }
207
- if (pm === "pnpm" && finalPkg.workspaces) {
208
- debug("Creating pnpm-workspace.yaml");
209
- const workspaceYaml = `packages:\n${finalPkg.workspaces.map((w) => ` - '${w}'`).join("\n")}\n`;
210
- await fs.writeFile(path.join(projectDir, "pnpm-workspace.yaml"), workspaceYaml);
211
- delete finalPkg.workspaces;
212
- for (const [key, value] of Object.entries(finalPkg.scripts)) if (typeof value === "string" && value.includes("--workspaces")) finalPkg.scripts[key] = value.replace("run ", "-r run ").replace(" --workspaces", "");
213
- }
214
- if (skipBuild) {
215
- debug("Applying skipBuild overrides");
216
- delete finalPkg.scripts.build;
217
- delete finalPkg.scripts.dev;
218
- if (finalPkg.devDependencies) delete finalPkg.devDependencies.tsdown;
219
- if (finalPkg.scripts.ci) finalPkg.scripts.ci = finalPkg.scripts.ci.replace(" && npm run build", "").replace(` && ${pm} run build`, "");
220
- debug("Removing tsdown configs due to skipBuild");
221
- await fs.rm(path.join(projectDir, "tsdown.config.ts"), { force: true });
222
- await fs.rm(path.join(projectDir, "client/tsdown.config.ts"), { force: true });
223
- await fs.rm(path.join(projectDir, "server/tsdown.config.ts"), { force: true });
224
- }
225
- debug("Writing final consolidated package.json to: %s", pkgPath);
226
- await fs.writeFile(pkgPath, JSON.stringify(finalPkg, null, " "));
227
- if (!await pathExists(path.join(projectDir, ".git"))) {
228
- debug("Initializing Git repository");
229
- try {
230
- await execa("git", ["init"], {
231
- cwd: projectDir,
232
- preferLocal: true
233
- });
234
- log.success("Initialized Git repository (git init).");
235
- } catch (e) {
236
- debug("Failed to initialize Git: %O", e);
237
- log.error("Failed to initialize Git: " + e);
238
- }
239
- }
240
- if (opts.createGithubRepository && !isUpdate) {
241
- debug("Creating GitHub repository");
242
- try {
243
- await execa("gh", [
244
- "repo",
245
- "create",
246
- projectName,
247
- "--public",
248
- "--source=.",
249
- "--remote=origin"
250
- ], {
251
- cwd: projectDir,
252
- preferLocal: true
253
- });
254
- log.success("Created GitHub repository (gh repo create).");
255
- } catch (e) {
256
- debug("Failed to create GitHub repository: %O", e);
257
- log.warn("Failed to create GitHub repository. Ensure \"gh\" CLI is installed and authenticated.");
258
- }
259
- }
260
- if (opts.installDependencies) {
261
- debug("Installing dependencies using %s", pm);
262
- const s = spinner();
263
- s.start(`Installing dependencies using ${pm}...`);
264
- try {
265
- await execa(pm, ["install"], {
266
- cwd: projectDir,
267
- preferLocal: true
268
- });
269
- s.stop(`\x1b[1G\x1b[2K\x1b[32m◆\x1b[39m Dependencies installed (${pm} install).`);
270
- } catch (e) {
271
- debug("Failed to install dependencies: %O", e);
272
- s.stop("Failed to install dependencies.");
273
- log.error(String(e));
274
- throw new Error("Failed to install dependencies.");
275
- }
276
- }
277
- if (opts.build && finalPkg.scripts.ci) {
278
- debug("Running CI script");
279
- const s = spinner();
280
- if (finalPkg.scripts["prettier-write"]) {
281
- s.start(`Formatting files with Prettier (${pm} run prettier-write)...`);
282
- try {
283
- await execa(pm, ["run", "prettier-write"], {
284
- cwd: projectDir,
285
- preferLocal: true
286
- });
287
- s.stop(`\x1b[1G\x1b[2K\x1b[32m◆\x1b[39m Files formatted (${pm} run prettier-write).`);
288
- } catch (e) {
289
- debug("Failed to format files: %O", e);
290
- s.stop("Failed to format files.");
291
- }
292
- }
293
- s.start(`Running CI script (lint, build, test) (${pm} run ci)...`);
294
- try {
295
- await execa(pm, ["run", "ci"], {
296
- cwd: projectDir,
297
- preferLocal: true
298
- });
299
- s.stop(`\x1b[1G\x1b[2K\x1b[32m◆\x1b[39m CI script completed (${pm} run ci).`);
300
- } catch (e) {
301
- debug("Failed to run CI script: %O", e);
302
- s.stop("Failed to run CI script.");
303
- log.error(String(e));
304
- throw new Error("Failed to run CI script.");
305
- }
306
- }
307
- log.info(`Project "${projectName}" ${isUpdate ? "updated" : "scaffolded"} successfully in ${projectDir}`);
308
- showSummary(opts, pm, isSilent);
309
- if (opts.dev && finalPkg.scripts.dev) {
310
- log.info("Starting dev server...");
311
- if (opts.open) try {
312
- await execa(pm, [
313
- "run",
314
- "dev",
315
- "--",
316
- "--open"
317
- ], {
318
- cwd: projectDir,
319
- stdio: "inherit",
320
- preferLocal: true
321
- });
322
- } catch (e) {
323
- log.error("Dev server failed: " + e);
324
- }
325
- else try {
326
- await execa(pm, ["run", "dev"], {
327
- cwd: projectDir,
328
- stdio: "inherit",
329
- preferLocal: true
330
- });
331
- } catch (e) {
332
- log.error("Dev server failed: " + e);
333
- }
334
- }
335
- };
336
- function showSummary(opts, pm, isSilent) {
337
- debug("Showing summary for options: %O", opts);
338
- const { projectName, template } = opts;
339
- const summary = [
340
- `Successfully created a new ${template} project named '${projectName}'.`,
341
- "",
342
- "Available Commands:"
343
- ];
344
- const commands = [
345
- `${pm} run dev - Starts the development server`,
346
- `${pm} run build - Builds the project for production`,
347
- `${pm} run test - Runs the unit test suite (Vitest)`,
348
- `${pm} run lint - Lints and formats the codebase`,
349
- `${pm} run ci - Runs lint, build, and test (used by CI/CD)`
350
- ];
351
- showNote([...summary, ...commands.map((c) => ` ${c}`)].join("\n"), "Project ready", isSilent);
352
- }
353
- //#endregion
354
- export { generateProject };
package/dist/index.d.mts DELETED
@@ -1,4 +0,0 @@
1
- //#region src/index.d.ts
2
- declare const main: () => Promise<void>;
3
- //#endregion
4
- export { main };
package/dist/index.mjs DELETED
@@ -1,32 +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")) debugLib.enable("*");
8
- const debug = debugLib("create-template-project:main");
9
- const main = async () => {
10
- try {
11
- debug("Starting CLI execution");
12
- debug("Parsing arguments");
13
- const options = await parseArgs();
14
- if (!options) return;
15
- const isSilent = !!options.silent;
16
- if (!isSilent) intro("create-template-project");
17
- debug("Arguments parsed: %O", options);
18
- debug("Generating project");
19
- await generateProject(options);
20
- debug("Project generation complete");
21
- if (!isSilent) outro("Done!");
22
- } catch (error) {
23
- debug("Execution failed: %O", error);
24
- if (error.code === "PROCESS_EXIT_0" || error.code === "commander.helpDisplayed" || error.code === "commander.version") process.exit(0);
25
- if (error.code === "PROCESS_EXIT_1") process.exit(1);
26
- cancel(error?.message || String(error));
27
- process.exit(1);
28
- }
29
- };
30
- if (import.meta.url.endsWith("src/index.ts") || import.meta.url.endsWith("dist/index.mjs")) await main();
31
- //#endregion
32
- export { main };
@@ -1,16 +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
- dependencies: {},
9
- devDependencies: { "eslint-plugin-regexp": "" },
10
- scripts: {},
11
- files: [],
12
- templateDir: path.resolve(__dirname, "files")
13
- };
14
- };
15
- //#endregion
16
- export { getBaseTemplate };
@@ -1,3 +0,0 @@
1
- import {defineConfig} from 'tsdown';
2
-
3
- export default defineConfig({entry: ['./src/index.ts'], format: ['esm'], clean: true, dts: true});