create-template-project 0.5.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -8
- package/dist/config/dependencies.json +4 -0
- package/dist/index.js +284 -137
- package/dist/templates/base/files/.github/workflows/node.js.yml +35 -9
- package/dist/templates/base/files/.release-it.json +23 -0
- package/dist/templates/base/files/AGENTS.md +80 -3
- package/dist/templates/base/files/CONTRIBUTING.md +18 -7
- package/dist/templates/base/files/README.md +2 -2
- package/dist/templates/base/files/package.json +3 -0
- package/dist/templates/base/files/vitest.config.ts +1 -1
- package/dist/templates/cli/files/package.json +2 -1
- package/dist/templates/cli/files/vite.config.ts +1 -1
- package/dist/templates/web-app/files/vite.config.ts +1 -1
- package/dist/templates/web-fullstack/files/client/vite.config.ts +1 -1
- package/dist/templates/web-fullstack/files/package.json +1 -1
- package/dist/templates/web-fullstack/files/server/vite.config.ts +1 -1
- package/dist/templates/web-vanilla/files/package.json +1 -1
- package/dist/templates/web-vanilla/files/vite.config.ts +1 -1
- package/package.json +9 -6
package/README.md
CHANGED
|
@@ -22,11 +22,12 @@ pnpm dlx create-template-project interactive
|
|
|
22
22
|
|
|
23
23
|
- **Modern Tech Stack:** All templates come with `commitlint`, `husky`, `vitest`, `oxlint`, `oxfmt`, and `typescript` (strict mode).
|
|
24
24
|
- **Interactive CLI:** Prompts you for project details if CLI arguments are missing, using `@clack/prompts`.
|
|
25
|
-
- **🔄 Update Mode:** Detects existing projects and offers a safe update path
|
|
26
|
-
-
|
|
27
|
-
-
|
|
25
|
+
- **🔄 Update Mode:** Detects existing projects and offers a safe update path.
|
|
26
|
+
- **Intelligent Tracking:** Automatically generates a detailed `GENERATED.md` with an "Upgrade Details" table showing exactly what changed, why, and what actions (like conflict resolution) are needed.
|
|
27
|
+
- **Seed File Protection:** Files in `src/`, all `*.md` files, and other core files are skipped to protect your application logic and custom documentation.
|
|
28
|
+
- **Tooling Sync:** Keeps your project's boilerplate (linting, CI, configs, scripts) up-to-date with the latest template versions.
|
|
28
29
|
- **No-Build Option:** Supports creating simple projects without a build step (strips Vite).
|
|
29
|
-
- **GitHub Integration:** Automatically initializes a Git repository and can create a GitHub repository using the `gh` CLI.
|
|
30
|
+
- **GitHub Integration:** Automatically initializes a Git repository and can create a GitHub repository (including initial commit and push) using the `gh` CLI.
|
|
30
31
|
- **CI Ready:** Generates GitHub Actions workflows for automated testing and linting.
|
|
31
32
|
|
|
32
33
|
## Installation
|
|
@@ -88,9 +89,8 @@ create-template-project update --template cli
|
|
|
88
89
|
- `-a, --author <author>`: Author name (defaults to 'git config user.name')
|
|
89
90
|
- `--github-username <username>`: GitHub username (defaults to 'git config github.user')
|
|
90
91
|
- `-p, --package-manager <pm>`: Package manager (`npm`, `pnpm`, `yarn`) (defaults to `pnpm`)
|
|
91
|
-
- `--create-github-repository`: Create GitHub
|
|
92
|
+
- `--create-github-repository`: Create GitHub repository and push initial commit (requires `gh` CLI authenticated)
|
|
92
93
|
- `--path <path>`: Output directory (mandatory)
|
|
93
|
-
- `--install-dependencies`: Install dependencies after scaffolding
|
|
94
94
|
- `--build`: Run the CI script (lint, build, test) after scaffolding
|
|
95
95
|
- `--no-progress`: Do not show progress indicators
|
|
96
96
|
|
|
@@ -102,9 +102,8 @@ create-template-project update --template cli
|
|
|
102
102
|
- `-a, --author <author>`: Author name (defaults to 'git config user.name')
|
|
103
103
|
- `--github-username <username>`: GitHub username (defaults to 'git config github.user')
|
|
104
104
|
- `-p, --package-manager <pm>`: Package manager (`npm`, `pnpm`, `yarn`) (defaults to `pnpm`)
|
|
105
|
-
- `--create-github-repository`: Create GitHub
|
|
105
|
+
- `--create-github-repository`: Create GitHub repository and push initial commit (requires `gh` CLI authenticated)
|
|
106
106
|
- `-d, --directory <path>`: Output directory (defaults to `.`)
|
|
107
|
-
- `--install-dependencies`: Install dependencies after updating
|
|
108
107
|
- `--build`: Run the CI script (lint, build, test) after updating
|
|
109
108
|
- `--dev`: Run the dev server after updating
|
|
110
109
|
- `--open`: Open the browser after updating
|
|
@@ -152,6 +152,10 @@
|
|
|
152
152
|
"version": "7.13.2",
|
|
153
153
|
"description": "Declarative routing for React web applications."
|
|
154
154
|
},
|
|
155
|
+
"release-it": {
|
|
156
|
+
"version": "19.2.4",
|
|
157
|
+
"description": "Interactive release-tool for Git repositories."
|
|
158
|
+
},
|
|
155
159
|
"cors": {
|
|
156
160
|
"version": "2.8.6",
|
|
157
161
|
"description": "Node.js CORS middleware."
|
package/dist/index.js
CHANGED
|
@@ -32,28 +32,12 @@ var ProjectOptionsSchema = z.object({
|
|
|
32
32
|
createGithubRepository: z.boolean().optional().default(false),
|
|
33
33
|
directory: z.string(),
|
|
34
34
|
update: z.boolean().optional().default(false),
|
|
35
|
-
installDependencies: z.boolean().optional().default(false),
|
|
36
35
|
build: z.boolean().optional().default(false),
|
|
37
36
|
progress: z.boolean().optional().default(true)
|
|
38
37
|
});
|
|
39
38
|
//#endregion
|
|
40
|
-
//#region src/utils/
|
|
41
|
-
var
|
|
42
|
-
function getTemplateDir(dirname, templateName) {
|
|
43
|
-
const sourcePath = path.resolve(dirname, "files");
|
|
44
|
-
const distPath = path.resolve(dirname, "templates", templateName, "files");
|
|
45
|
-
return existsSync(distPath) ? distPath : sourcePath;
|
|
46
|
-
}
|
|
47
|
-
async function getAllFiles(dirPath, arrayOfFiles = []) {
|
|
48
|
-
const files = await fs.readdir(dirPath);
|
|
49
|
-
for (const file of files) {
|
|
50
|
-
if (file === ".DS_Store") continue;
|
|
51
|
-
if ((await fs.stat(path.join(dirPath, file))).isDirectory()) arrayOfFiles = await getAllFiles(path.join(dirPath, file), arrayOfFiles);
|
|
52
|
-
else arrayOfFiles.push(path.join(dirPath, file));
|
|
53
|
-
}
|
|
54
|
-
return arrayOfFiles;
|
|
55
|
-
}
|
|
56
|
-
function processContent(filePath, content, opts, addedDeps) {
|
|
39
|
+
//#region src/utils/templating/generic.ts
|
|
40
|
+
var genericProcessor = (content, { opts }) => {
|
|
57
41
|
const { projectName, template, author, githubUsername } = opts;
|
|
58
42
|
let description = opts.description || "";
|
|
59
43
|
if (!description) switch (template) {
|
|
@@ -72,29 +56,105 @@ function processContent(filePath, content, opts, addedDeps) {
|
|
|
72
56
|
}
|
|
73
57
|
const pm = opts.packageManager || "npm";
|
|
74
58
|
const lockfileRules = pm === "pnpm" ? "package-lock.json\nyarn.lock" : pm === "yarn" ? "package-lock.json\npnpm-lock.yaml" : "yarn.lock\npnpm-lock.yaml";
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (template === "web-fullstack"
|
|
59
|
+
return content.replaceAll("{{projectName}}", projectName).replaceAll("{{description}}", description).replaceAll("{{packageManager}}", pm).replaceAll("{{author}}", author || "").replaceAll("{{githubUsername}}", githubUsername || "").replaceAll("{{year}}", (/* @__PURE__ */ new Date()).getFullYear().toString()).replaceAll("{{lockfileRules}}", lockfileRules);
|
|
60
|
+
};
|
|
61
|
+
//#endregion
|
|
62
|
+
//#region src/utils/templating/github-workflow.ts
|
|
63
|
+
var WORKFLOW_PNPM_SETUP = ` - name: Setup pnpm
|
|
64
|
+
uses: pnpm/action-setup@v4
|
|
65
|
+
with:
|
|
66
|
+
version: 9
|
|
67
|
+
run_install: false`;
|
|
68
|
+
var WORKFLOW_PLAYWRIGHT_SETUP = ` - name: Install Playwright Browsers & Deps
|
|
69
|
+
run: npx playwright install --with-deps chromium`;
|
|
70
|
+
var githubWorkflowProcessor = (content, { filePath, opts }) => {
|
|
71
|
+
if (!filePath.includes(".github/workflows/node.js.yml")) return content;
|
|
72
|
+
const { template, packageManager: pm = "npm" } = opts;
|
|
73
|
+
let installCommand = "npm ci";
|
|
74
|
+
let pmSetup = "";
|
|
75
|
+
if (pm === "pnpm") {
|
|
76
|
+
installCommand = "pnpm install --frozen-lockfile";
|
|
77
|
+
pmSetup = WORKFLOW_PNPM_SETUP;
|
|
78
|
+
} else if (pm === "yarn") installCommand = "yarn install --frozen-lockfile";
|
|
79
|
+
let playwrightSetup = "";
|
|
80
|
+
if (template === "web-fullstack" || template === "web-app" || template === "web-vanilla") playwrightSetup = WORKFLOW_PLAYWRIGHT_SETUP;
|
|
81
|
+
let processed = content.replaceAll("{{installCommand}}", installCommand).replaceAll("# [PM_SETUP]", pmSetup).replaceAll("# [PLAYWRIGHT_SETUP]", playwrightSetup);
|
|
82
|
+
processed = processed.replace(/^\s*# \[PM_SETUP\]\s*\n/m, "");
|
|
83
|
+
processed = processed.replace(/^\s*# \[PLAYWRIGHT_SETUP\]\s*\n/m, "");
|
|
84
|
+
return processed;
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/utils/templating/tsconfig.ts
|
|
88
|
+
var WEB_ENV = `/* Language and Environment */
|
|
89
|
+
"target": "ES2023" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
|
90
|
+
"lib": ["ES2023", "DOM", "DOM.Iterable"] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
|
91
|
+
"module": "ESNext" /* Specify what module code is generated. */,
|
|
92
|
+
"moduleResolution": "bundler" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
|
93
|
+
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. */,
|
|
94
|
+
"resolveJsonModule": true /* Enable importing .json files. */,
|
|
95
|
+
"allowImportingTsExtensions": true /* Allow imports to include TypeScript file extensions. */,
|
|
96
|
+
"noEmit": true /* Disable emitting files from a compilation. */,
|
|
97
|
+
"jsx": "react-jsx" /* Specify what JSX code is generated. */,`;
|
|
98
|
+
var tsconfigProcessor = (content, { filePath, opts }) => {
|
|
99
|
+
if (filePath !== "tsconfig.json") return content;
|
|
100
|
+
const { template } = opts;
|
|
101
|
+
let processed = content;
|
|
102
|
+
if (template === "web-fullstack" || template === "web-vanilla" || template === "web-app") processed = processed.replace(/\/\* Language and Environment \*\/[\s\S]*?\/\* Strict Type-Checking Options \*\//, WEB_ENV + "\n\n /* Strict Type-Checking Options */");
|
|
103
|
+
if (template === "web-fullstack") processed = processed.replace(/"include":\s*\[\s*"src\/\*\*\/\*"\s*\]/, "\"include\": [\"client/src/**/*\", \"server/src/**/*\"]");
|
|
97
104
|
return processed;
|
|
105
|
+
};
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/utils/templating/contributing.ts
|
|
108
|
+
var contributingProcessor = (content, { filePath, addedDeps }) => {
|
|
109
|
+
if (filePath !== "CONTRIBUTING.md" || addedDeps.length === 0) return content;
|
|
110
|
+
let processed = content;
|
|
111
|
+
processed += "\n## Dependencies\n\n";
|
|
112
|
+
const uniqueDeps = Array.from(new Set(addedDeps.map((d) => JSON.stringify(d)))).map((s) => JSON.parse(s));
|
|
113
|
+
for (const dep of uniqueDeps) processed += `- **${dep.name}**: ${dep.description}\n`;
|
|
114
|
+
return processed;
|
|
115
|
+
};
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/utils/templating/web-vanilla-html.ts
|
|
118
|
+
var webVanillaHtmlProcessor = (content, { filePath, opts }) => {
|
|
119
|
+
if (opts.template === "web-vanilla" && filePath === "index.html") return content.replace("{{scriptSrc}}", "/src/index.ts");
|
|
120
|
+
return content;
|
|
121
|
+
};
|
|
122
|
+
//#endregion
|
|
123
|
+
//#region src/utils/templating/index.ts
|
|
124
|
+
var processors = [
|
|
125
|
+
genericProcessor,
|
|
126
|
+
githubWorkflowProcessor,
|
|
127
|
+
tsconfigProcessor,
|
|
128
|
+
contributingProcessor,
|
|
129
|
+
webVanillaHtmlProcessor
|
|
130
|
+
];
|
|
131
|
+
function processContent$1(filePath, content, opts, addedDeps) {
|
|
132
|
+
const context = {
|
|
133
|
+
filePath,
|
|
134
|
+
opts,
|
|
135
|
+
addedDeps
|
|
136
|
+
};
|
|
137
|
+
return processors.reduce((acc, processor) => processor(acc, context), content);
|
|
138
|
+
}
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/utils/file.ts
|
|
141
|
+
var debug$3 = debugLib("create-template-project:utils:file");
|
|
142
|
+
function getTemplateDir(dirname, templateName) {
|
|
143
|
+
const sourcePath = path.resolve(dirname, "files");
|
|
144
|
+
const distPath = path.resolve(dirname, "templates", templateName, "files");
|
|
145
|
+
return existsSync(distPath) ? distPath : sourcePath;
|
|
146
|
+
}
|
|
147
|
+
async function getAllFiles(dirPath, arrayOfFiles = []) {
|
|
148
|
+
const files = await fs.readdir(dirPath);
|
|
149
|
+
for (const file of files) {
|
|
150
|
+
if (file === ".DS_Store") continue;
|
|
151
|
+
if ((await fs.stat(path.join(dirPath, file))).isDirectory()) arrayOfFiles = await getAllFiles(path.join(dirPath, file), arrayOfFiles);
|
|
152
|
+
else arrayOfFiles.push(path.join(dirPath, file));
|
|
153
|
+
}
|
|
154
|
+
return arrayOfFiles;
|
|
155
|
+
}
|
|
156
|
+
function processContent(filePath, content, opts, addedDeps) {
|
|
157
|
+
return processContent$1(filePath, content, opts, addedDeps);
|
|
98
158
|
}
|
|
99
159
|
function mergePackageJson(target, source) {
|
|
100
160
|
if (source.scripts) target.scripts = {
|
|
@@ -197,6 +257,10 @@ var getBaseTemplate = (_opts) => {
|
|
|
197
257
|
name: "conventional-changelog",
|
|
198
258
|
description: "Automated release notes."
|
|
199
259
|
},
|
|
260
|
+
{
|
|
261
|
+
name: "release-it",
|
|
262
|
+
description: "Automated release workflow."
|
|
263
|
+
},
|
|
200
264
|
{
|
|
201
265
|
name: "debug",
|
|
202
266
|
description: "Structured logging for debugging."
|
|
@@ -207,7 +271,10 @@ var getBaseTemplate = (_opts) => {
|
|
|
207
271
|
}
|
|
208
272
|
],
|
|
209
273
|
dependencies: { zod: "zod" },
|
|
210
|
-
devDependencies: {
|
|
274
|
+
devDependencies: {
|
|
275
|
+
"eslint-plugin-regexp": "",
|
|
276
|
+
"release-it": ""
|
|
277
|
+
},
|
|
211
278
|
scripts: {},
|
|
212
279
|
files: [],
|
|
213
280
|
templateDir: getTemplateDir(__dirname$5, "base")
|
|
@@ -277,7 +344,7 @@ var getWebVanillaTemplate = (_opts) => {
|
|
|
277
344
|
preview: "vite preview",
|
|
278
345
|
test: "vitest run",
|
|
279
346
|
"test:ui": "vitest",
|
|
280
|
-
"test
|
|
347
|
+
"integration-test": "playwright test"
|
|
281
348
|
},
|
|
282
349
|
files: [],
|
|
283
350
|
templateDir: getTemplateDir(__dirname$3, "web-vanilla")
|
|
@@ -343,7 +410,7 @@ var getWebAppTemplate = (_opts) => {
|
|
|
343
410
|
preview: "vite preview",
|
|
344
411
|
test: "vitest run",
|
|
345
412
|
"test:ui": "vitest",
|
|
346
|
-
"test
|
|
413
|
+
"integration-test": "playwright test",
|
|
347
414
|
start: "vite preview"
|
|
348
415
|
},
|
|
349
416
|
files: [],
|
|
@@ -420,7 +487,7 @@ var getWebFullstackTemplate = (_opts) => {
|
|
|
420
487
|
build: "npm run build --workspaces",
|
|
421
488
|
dev: "npm run dev --workspaces",
|
|
422
489
|
test: "npm run test --workspaces",
|
|
423
|
-
"test
|
|
490
|
+
"integration-test": "playwright test"
|
|
424
491
|
},
|
|
425
492
|
files: [],
|
|
426
493
|
templateDir: getTemplateDir(__dirname$1, "web-fullstack")
|
|
@@ -436,7 +503,6 @@ var MOCK_OPTS = {
|
|
|
436
503
|
directory: ".",
|
|
437
504
|
packageManager: "npm",
|
|
438
505
|
update: false,
|
|
439
|
-
installDependencies: false,
|
|
440
506
|
build: false,
|
|
441
507
|
progress: true,
|
|
442
508
|
createGithubRepository: false
|
|
@@ -479,6 +545,17 @@ var getAllTemplatesInfo = () => {
|
|
|
479
545
|
//#endregion
|
|
480
546
|
//#region src/cli.ts
|
|
481
547
|
var pathExists$1 = (p) => fs.access(p).then(() => true).catch(() => false);
|
|
548
|
+
var stripQuotes = (str) => {
|
|
549
|
+
if (typeof str !== "string") return str;
|
|
550
|
+
let result = str.trim();
|
|
551
|
+
while (result.length >= 2) {
|
|
552
|
+
const first = result[0];
|
|
553
|
+
const last = result[result.length - 1];
|
|
554
|
+
if (first === "\"" && last === "\"" || first === "'" && last === "'") result = result.substring(1, result.length - 1).trim();
|
|
555
|
+
else break;
|
|
556
|
+
}
|
|
557
|
+
return result;
|
|
558
|
+
};
|
|
482
559
|
var getDefaultAuthor = async () => {
|
|
483
560
|
try {
|
|
484
561
|
const { stdout } = await execa("git", ["config", "user.name"]);
|
|
@@ -548,7 +625,7 @@ Templates:
|
|
|
548
625
|
p.outro("Use \"create\" to scaffold a new project.");
|
|
549
626
|
process.exit(0);
|
|
550
627
|
});
|
|
551
|
-
program.command("create").description("Create a new project from a template").option("-t, --template <type>", "Template type (cli, web-vanilla, web-app, web-fullstack)").option("-n, --name <name>", "Project name").option("--description <description>", "Project description").option("-k, --keywords <keywords>", "Project keywords (comma separated)").option("-a, --author <author>", "Author name (defaults to 'git config user.name')").option("--github-username <username>", "GitHub username (defaults to 'git config github.user')").option("-p, --package-manager <pm>", "Package manager (npm, pnpm, yarn)", "pnpm").option("--create-github-repository", "Create GitHub
|
|
628
|
+
program.command("create").description("Create a new project from a template").option("-t, --template <type>", "Template type (cli, web-vanilla, web-app, web-fullstack)").option("-n, --name <name>", "Project name").option("--description <description>", "Project description").option("-k, --keywords <keywords>", "Project keywords (comma separated)").option("-a, --author <author>", "Author name (defaults to 'git config user.name')").option("--github-username <username>", "GitHub username (defaults to 'git config github.user')").option("-p, --package-manager <pm>", "Package manager (npm, pnpm, yarn)", "pnpm").option("--create-github-repository", "Create GitHub repository and push initial commit").requiredOption("--path <path>", "Output directory").option("--build", "Run the CI script (lint, build, test) after scaffolding", false).option("--no-progress", "Do not show progress indicators").action(async (opts) => {
|
|
552
629
|
debug$2("Executing \"create\" command with options: %O", opts);
|
|
553
630
|
commandResult = {
|
|
554
631
|
...opts,
|
|
@@ -576,7 +653,7 @@ Restrictions & Behavior:
|
|
|
576
653
|
- package.json: Dependencies and scripts are merged. Existing versions are preserved unless they are missing.
|
|
577
654
|
- Merging: For non-seed files, the tool attempts to merge template changes. If a conflict occurs, it will be marked with standard git conflict markers.
|
|
578
655
|
- Confirmation: The command will always show a summary of proposed changes (ADD, MODIFY) and ask for your confirmation before applying them.
|
|
579
|
-
`).option("-t, --template <type>", "Template type (cli, web-vanilla, web-app, web-fullstack)").option("--description <description>", "Project description").option("-k, --keywords <keywords>", "Project keywords (comma separated)").option("-a, --author <author>", "Author name (defaults to 'git config user.name')").option("--github-username <username>", "GitHub username (defaults to 'git config github.user')").option("-p, --package-manager <pm>", "Package manager (npm, pnpm, yarn)", "pnpm").option("--create-github-repository", "Create GitHub
|
|
656
|
+
`).option("-t, --template <type>", "Template type (cli, web-vanilla, web-app, web-fullstack)").option("--description <description>", "Project description").option("-k, --keywords <keywords>", "Project keywords (comma separated)").option("-a, --author <author>", "Author name (defaults to 'git config user.name')").option("--github-username <username>", "GitHub username (defaults to 'git config github.user')").option("-p, --package-manager <pm>", "Package manager (npm, pnpm, yarn)", "pnpm").option("--create-github-repository", "Create GitHub repository and push initial commit").option("-d, --directory <path>", "Output directory", ".").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("--no-progress", "Do not show progress indicators").action(async (opts) => {
|
|
580
657
|
debug$2("Executing \"update\" command with options: %O", opts);
|
|
581
658
|
const directory = path.resolve(opts.directory);
|
|
582
659
|
const pkgPath = path.join(directory, "package.json");
|
|
@@ -773,29 +850,16 @@ Restrictions & Behavior:
|
|
|
773
850
|
process.exit(0);
|
|
774
851
|
}
|
|
775
852
|
}
|
|
776
|
-
const
|
|
777
|
-
message: "Should we
|
|
853
|
+
const build = await p.confirm({
|
|
854
|
+
message: "Should we run the CI script (lint, build, test)?",
|
|
778
855
|
initialValue: true
|
|
779
856
|
});
|
|
780
|
-
if (p.isCancel(
|
|
857
|
+
if (p.isCancel(build)) {
|
|
781
858
|
p.cancel("Operation cancelled.");
|
|
782
859
|
process.exit(0);
|
|
783
860
|
}
|
|
784
|
-
const installDependencies = installDependenciesRes;
|
|
785
|
-
let build = false;
|
|
786
|
-
if (installDependencies) {
|
|
787
|
-
const res = await p.confirm({
|
|
788
|
-
message: "Should we run the CI script (lint, build, test)?",
|
|
789
|
-
initialValue: true
|
|
790
|
-
});
|
|
791
|
-
if (p.isCancel(res)) {
|
|
792
|
-
p.cancel("Operation cancelled.");
|
|
793
|
-
process.exit(0);
|
|
794
|
-
}
|
|
795
|
-
build = res;
|
|
796
|
-
}
|
|
797
861
|
const createGithubRepositoryRes = await p.confirm({
|
|
798
|
-
message: "
|
|
862
|
+
message: "Create GitHub repository and push initial commit?",
|
|
799
863
|
initialValue: false
|
|
800
864
|
});
|
|
801
865
|
if (p.isCancel(createGithubRepositoryRes)) {
|
|
@@ -813,7 +877,6 @@ Restrictions & Behavior:
|
|
|
813
877
|
createGithubRepository: createGithubRepositoryRes,
|
|
814
878
|
directory: projectDir,
|
|
815
879
|
update,
|
|
816
|
-
installDependencies,
|
|
817
880
|
build,
|
|
818
881
|
progress: true
|
|
819
882
|
};
|
|
@@ -835,6 +898,13 @@ Restrictions & Behavior:
|
|
|
835
898
|
p.cancel("Unknown command or missing options.");
|
|
836
899
|
process.exit(1);
|
|
837
900
|
}
|
|
901
|
+
if (commandResult.template) commandResult.template = stripQuotes(commandResult.template);
|
|
902
|
+
if (commandResult.projectName) commandResult.projectName = stripQuotes(commandResult.projectName);
|
|
903
|
+
if (commandResult.description) commandResult.description = stripQuotes(commandResult.description);
|
|
904
|
+
if (commandResult.keywords) commandResult.keywords = stripQuotes(commandResult.keywords);
|
|
905
|
+
if (commandResult.author) commandResult.author = stripQuotes(commandResult.author);
|
|
906
|
+
if (commandResult.githubUsername) commandResult.githubUsername = stripQuotes(commandResult.githubUsername);
|
|
907
|
+
if (commandResult.packageManager) commandResult.packageManager = stripQuotes(commandResult.packageManager);
|
|
838
908
|
debug$2("Validating command result with Zod");
|
|
839
909
|
const validationResult = ProjectOptionsSchema.safeParse(commandResult);
|
|
840
910
|
if (!validationResult.success) {
|
|
@@ -848,7 +918,6 @@ Restrictions & Behavior:
|
|
|
848
918
|
p.cancel(`Directory "${projectDir}" already exists. Use the "update" command to update.`);
|
|
849
919
|
process.exit(1);
|
|
850
920
|
}
|
|
851
|
-
if (commandResult.build) commandResult.installDependencies = true;
|
|
852
921
|
return commandResult;
|
|
853
922
|
};
|
|
854
923
|
//#endregion
|
|
@@ -940,6 +1009,7 @@ var generateProject = async (opts) => {
|
|
|
940
1009
|
let finalPkg = {
|
|
941
1010
|
name: projectName,
|
|
942
1011
|
version: "0.1.0",
|
|
1012
|
+
private: true,
|
|
943
1013
|
description: opts.description || "TODO: Add project description",
|
|
944
1014
|
keywords: opts.keywords ? opts.keywords.split(",").map((k) => k.trim()) : ["TODO: Add keywords"],
|
|
945
1015
|
homepage: `https://github.com/${githubUsername}/${projectName}#readme`,
|
|
@@ -1007,7 +1077,8 @@ var generateProject = async (opts) => {
|
|
|
1007
1077
|
if (isUpdate && isSeedFile(relativePath)) {
|
|
1008
1078
|
actions.push({
|
|
1009
1079
|
type: "SKIP",
|
|
1010
|
-
path: relativePath
|
|
1080
|
+
path: relativePath,
|
|
1081
|
+
reason: "Seed file - skipped during update to preserve manual changes"
|
|
1011
1082
|
});
|
|
1012
1083
|
continue;
|
|
1013
1084
|
}
|
|
@@ -1015,7 +1086,8 @@ var generateProject = async (opts) => {
|
|
|
1015
1086
|
if (isUpdate && await pathExists(targetPath)) {
|
|
1016
1087
|
actions.push({
|
|
1017
1088
|
type: "DELETE",
|
|
1018
|
-
path: relativePath
|
|
1089
|
+
path: relativePath,
|
|
1090
|
+
reason: "File no longer required for this template type"
|
|
1019
1091
|
});
|
|
1020
1092
|
pendingOperations.push(async () => {
|
|
1021
1093
|
await fs.rm(targetPath, { force: true });
|
|
@@ -1035,21 +1107,36 @@ var generateProject = async (opts) => {
|
|
|
1035
1107
|
if (isUpdate && exists) {
|
|
1036
1108
|
const existingContent = await fs.readFile(finalTargetPath, "utf8");
|
|
1037
1109
|
if (existingContent.trim() !== content.trim()) {
|
|
1038
|
-
|
|
1110
|
+
const action = {
|
|
1039
1111
|
type: "MODIFY",
|
|
1040
|
-
path: finalRelativePath
|
|
1041
|
-
|
|
1112
|
+
path: finalRelativePath,
|
|
1113
|
+
reason: "Template tooling or configuration update"
|
|
1114
|
+
};
|
|
1115
|
+
actions.push(action);
|
|
1042
1116
|
pendingOperations.push(async () => {
|
|
1043
1117
|
const result = await mergeFile(finalTargetPath, existingContent, content, log);
|
|
1044
|
-
if (result === "merged")
|
|
1045
|
-
|
|
1046
|
-
|
|
1118
|
+
if (result === "merged") {
|
|
1119
|
+
action.type = "MERGE";
|
|
1120
|
+
action.reason = "Merged template updates with your manual changes";
|
|
1121
|
+
action.recommendedAction = "Review changes for correct integration";
|
|
1122
|
+
log.info(`ℹ Merged: ${finalRelativePath}`);
|
|
1123
|
+
} else if (result === "conflict") {
|
|
1124
|
+
action.type = "CONFLICT";
|
|
1125
|
+
action.reason = "Conflicting changes between template and your code";
|
|
1126
|
+
action.recommendedAction = "Resolve git conflict markers in this file";
|
|
1127
|
+
log.warn(`⚠ Conflict: ${finalRelativePath}`);
|
|
1128
|
+
} else if (result === "updated") {
|
|
1129
|
+
action.type = "UPDATED";
|
|
1130
|
+
action.reason = "File was updated to the latest template version";
|
|
1131
|
+
log.info(`✔ Updated: ${finalRelativePath}`);
|
|
1132
|
+
}
|
|
1047
1133
|
});
|
|
1048
1134
|
}
|
|
1049
1135
|
} else if (!exists) {
|
|
1050
1136
|
actions.push({
|
|
1051
1137
|
type: "ADD",
|
|
1052
|
-
path: finalRelativePath
|
|
1138
|
+
path: finalRelativePath,
|
|
1139
|
+
reason: "New template file added"
|
|
1053
1140
|
});
|
|
1054
1141
|
pendingOperations.push(async () => {
|
|
1055
1142
|
await fs.mkdir(path.dirname(finalTargetPath), { recursive: true });
|
|
@@ -1065,7 +1152,8 @@ var generateProject = async (opts) => {
|
|
|
1065
1152
|
if (isUpdate && isSeedFile(file.path)) {
|
|
1066
1153
|
actions.push({
|
|
1067
1154
|
type: "SKIP",
|
|
1068
|
-
path: file.path
|
|
1155
|
+
path: file.path,
|
|
1156
|
+
reason: "Seed file - skipped during update to preserve manual changes"
|
|
1069
1157
|
});
|
|
1070
1158
|
continue;
|
|
1071
1159
|
}
|
|
@@ -1073,7 +1161,8 @@ var generateProject = async (opts) => {
|
|
|
1073
1161
|
if (isUpdate && await pathExists(targetPath)) {
|
|
1074
1162
|
actions.push({
|
|
1075
1163
|
type: "DELETE",
|
|
1076
|
-
path: file.path
|
|
1164
|
+
path: file.path,
|
|
1165
|
+
reason: "File no longer required for this template type"
|
|
1077
1166
|
});
|
|
1078
1167
|
pendingOperations.push(async () => {
|
|
1079
1168
|
await fs.rm(targetPath, { force: true });
|
|
@@ -1087,21 +1176,36 @@ var generateProject = async (opts) => {
|
|
|
1087
1176
|
if (isUpdate && exists) {
|
|
1088
1177
|
const existingContent = await fs.readFile(targetPath, "utf8");
|
|
1089
1178
|
if (existingContent.trim() !== content.trim()) {
|
|
1090
|
-
|
|
1179
|
+
const action = {
|
|
1091
1180
|
type: "MODIFY",
|
|
1092
|
-
path: file.path
|
|
1093
|
-
|
|
1181
|
+
path: file.path,
|
|
1182
|
+
reason: "Template configuration update"
|
|
1183
|
+
};
|
|
1184
|
+
actions.push(action);
|
|
1094
1185
|
pendingOperations.push(async () => {
|
|
1095
1186
|
const result = await mergeFile(targetPath, existingContent, content, log);
|
|
1096
|
-
if (result === "merged")
|
|
1097
|
-
|
|
1098
|
-
|
|
1187
|
+
if (result === "merged") {
|
|
1188
|
+
action.type = "MERGE";
|
|
1189
|
+
action.reason = "Merged template updates with your manual changes";
|
|
1190
|
+
action.recommendedAction = "Review changes for correct integration";
|
|
1191
|
+
log.info(`ℹ Merged: ${file.path}`);
|
|
1192
|
+
} else if (result === "conflict") {
|
|
1193
|
+
action.type = "CONFLICT";
|
|
1194
|
+
action.reason = "Conflicting changes between template and your code";
|
|
1195
|
+
action.recommendedAction = "Resolve git conflict markers in this file";
|
|
1196
|
+
log.warn(`⚠ Conflict: ${file.path}`);
|
|
1197
|
+
} else if (result === "updated") {
|
|
1198
|
+
action.type = "UPDATED";
|
|
1199
|
+
action.reason = "File was updated to the latest template version";
|
|
1200
|
+
log.info(`✔ Updated: ${file.path}`);
|
|
1201
|
+
}
|
|
1099
1202
|
});
|
|
1100
1203
|
}
|
|
1101
1204
|
} else if (!exists) {
|
|
1102
1205
|
actions.push({
|
|
1103
1206
|
type: "ADD",
|
|
1104
|
-
path: file.path
|
|
1207
|
+
path: file.path,
|
|
1208
|
+
reason: "New template file added"
|
|
1105
1209
|
});
|
|
1106
1210
|
pendingOperations.push(async () => {
|
|
1107
1211
|
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
@@ -1124,7 +1228,8 @@ var generateProject = async (opts) => {
|
|
|
1124
1228
|
if (workspaceChanged) {
|
|
1125
1229
|
actions.push({
|
|
1126
1230
|
type: workspaceExists ? "MODIFY" : "ADD",
|
|
1127
|
-
path: "pnpm-workspace.yaml"
|
|
1231
|
+
path: "pnpm-workspace.yaml",
|
|
1232
|
+
reason: "Updated workspace configuration for pnpm"
|
|
1128
1233
|
});
|
|
1129
1234
|
pendingOperations.push(async () => {
|
|
1130
1235
|
await fs.writeFile(workspacePath, workspaceYaml);
|
|
@@ -1142,11 +1247,13 @@ var generateProject = async (opts) => {
|
|
|
1142
1247
|
if (pkgChanged) {
|
|
1143
1248
|
if (isUpdate) actions.push({
|
|
1144
1249
|
type: "MODIFY",
|
|
1145
|
-
path: "package.json"
|
|
1250
|
+
path: "package.json",
|
|
1251
|
+
reason: "Updated dependencies and scripts to match latest template"
|
|
1146
1252
|
});
|
|
1147
1253
|
else actions.push({
|
|
1148
1254
|
type: "ADD",
|
|
1149
|
-
path: "package.json"
|
|
1255
|
+
path: "package.json",
|
|
1256
|
+
reason: "Initial project configuration"
|
|
1150
1257
|
});
|
|
1151
1258
|
pendingOperations.push(async () => {
|
|
1152
1259
|
debug$1("Writing final consolidated package.json to: %s", pkgPath);
|
|
@@ -1175,7 +1282,7 @@ var generateProject = async (opts) => {
|
|
|
1175
1282
|
githubSkipped: !opts.createGithubRepository || isUpdate,
|
|
1176
1283
|
githubError: "",
|
|
1177
1284
|
depsInstalled: false,
|
|
1178
|
-
depsSkipped: !opts.
|
|
1285
|
+
depsSkipped: !opts.build,
|
|
1179
1286
|
ciRun: false,
|
|
1180
1287
|
ciSkipped: !opts.build || !finalPkg.scripts.ci
|
|
1181
1288
|
};
|
|
@@ -1197,32 +1304,7 @@ var generateProject = async (opts) => {
|
|
|
1197
1304
|
log.error(`Failed to initialize Git: ${e.message}${detail}`);
|
|
1198
1305
|
}
|
|
1199
1306
|
} else states.gitInitialized = true;
|
|
1200
|
-
if (opts.
|
|
1201
|
-
debug$1("Creating GitHub repository");
|
|
1202
|
-
try {
|
|
1203
|
-
debug$1("Executing: gh repo create %s --public --source=. --remote=origin", projectName);
|
|
1204
|
-
await execa("gh", [
|
|
1205
|
-
"repo",
|
|
1206
|
-
"create",
|
|
1207
|
-
projectName,
|
|
1208
|
-
"--public",
|
|
1209
|
-
"--source=.",
|
|
1210
|
-
"--remote=origin"
|
|
1211
|
-
], {
|
|
1212
|
-
cwd: projectDir,
|
|
1213
|
-
stdio,
|
|
1214
|
-
preferLocal: true
|
|
1215
|
-
});
|
|
1216
|
-
log.success("Created GitHub repository (gh repo create).");
|
|
1217
|
-
states.githubCreated = true;
|
|
1218
|
-
} catch (e) {
|
|
1219
|
-
debug$1("Failed to create GitHub repository: %O", e);
|
|
1220
|
-
const detail = e.stdout || e.stderr ? `\n\nOutput:\n${e.stdout}\n${e.stderr}` : "";
|
|
1221
|
-
log.warn(`Failed to create GitHub repository: ${e.message}${detail}\nEnsure "gh" CLI is installed and authenticated.`);
|
|
1222
|
-
states.githubError = e.message;
|
|
1223
|
-
}
|
|
1224
|
-
}
|
|
1225
|
-
if (opts.installDependencies) {
|
|
1307
|
+
if (opts.build) {
|
|
1226
1308
|
debug$1("Installing dependencies using %s", pm);
|
|
1227
1309
|
const s = spinner();
|
|
1228
1310
|
s.start(`Installing dependencies using ${pm}...`);
|
|
@@ -1281,6 +1363,51 @@ var generateProject = async (opts) => {
|
|
|
1281
1363
|
throw new Error(`Failed to run CI script: ${e.message}${detail}`);
|
|
1282
1364
|
}
|
|
1283
1365
|
}
|
|
1366
|
+
if (opts.createGithubRepository && !isUpdate) {
|
|
1367
|
+
debug$1("Creating and pushing GitHub repository");
|
|
1368
|
+
const s = spinner();
|
|
1369
|
+
s.start("Creating and pushing GitHub repository...");
|
|
1370
|
+
try {
|
|
1371
|
+
debug$1("Executing: git add .");
|
|
1372
|
+
await execa("git", ["add", "."], {
|
|
1373
|
+
cwd: projectDir,
|
|
1374
|
+
stdio,
|
|
1375
|
+
preferLocal: true
|
|
1376
|
+
});
|
|
1377
|
+
debug$1("Executing: git commit -m \"chore: initial scaffold\"");
|
|
1378
|
+
await execa("git", [
|
|
1379
|
+
"commit",
|
|
1380
|
+
"-m",
|
|
1381
|
+
"chore: initial scaffold"
|
|
1382
|
+
], {
|
|
1383
|
+
cwd: projectDir,
|
|
1384
|
+
stdio,
|
|
1385
|
+
preferLocal: true
|
|
1386
|
+
});
|
|
1387
|
+
debug$1("Executing: gh repo create %s --public --source=. --remote=origin --push", projectName);
|
|
1388
|
+
await execa("gh", [
|
|
1389
|
+
"repo",
|
|
1390
|
+
"create",
|
|
1391
|
+
projectName,
|
|
1392
|
+
"--public",
|
|
1393
|
+
"--source=.",
|
|
1394
|
+
"--remote=origin",
|
|
1395
|
+
"--push"
|
|
1396
|
+
], {
|
|
1397
|
+
cwd: projectDir,
|
|
1398
|
+
stdio,
|
|
1399
|
+
preferLocal: true
|
|
1400
|
+
});
|
|
1401
|
+
s.stop(`\x1b[1G\x1b[2K\x1b[32m◆\x1b[39m Created GitHub repository and pushed initial commit.`);
|
|
1402
|
+
states.githubCreated = true;
|
|
1403
|
+
} catch (e) {
|
|
1404
|
+
debug$1("Failed to create/push GitHub repository: %O", e);
|
|
1405
|
+
s.stop("Failed to create/push GitHub repository.");
|
|
1406
|
+
const detail = e.stdout || e.stderr ? `\n\nOutput:\n${e.stdout}\n${e.stderr}` : "";
|
|
1407
|
+
log.warn(`Failed to create/push GitHub repository: ${e.message}${detail}\nEnsure "gh" CLI is installed and authenticated.`);
|
|
1408
|
+
states.githubError = e.message;
|
|
1409
|
+
}
|
|
1410
|
+
}
|
|
1284
1411
|
let hasErrors = false;
|
|
1285
1412
|
let hasWarnings = false;
|
|
1286
1413
|
const errorMessages = [];
|
|
@@ -1292,12 +1419,12 @@ var generateProject = async (opts) => {
|
|
|
1292
1419
|
hasErrors,
|
|
1293
1420
|
hasWarnings,
|
|
1294
1421
|
errorMessages
|
|
1295
|
-
});
|
|
1422
|
+
}, actions);
|
|
1296
1423
|
const successMsg = `Project "${projectName}" ${isUpdate ? "updated" : "scaffolded"} successfully in ${projectDir}. A detailed setup guide has been generated at GENERATED.md`;
|
|
1297
1424
|
if (hasWarnings) log.warn(`${successMsg} (completed with warnings)`);
|
|
1298
1425
|
else log.success(successMsg);
|
|
1299
1426
|
};
|
|
1300
|
-
async function generateGeneratedMd(projectDir, opts, pm, states, isUpdate, status) {
|
|
1427
|
+
async function generateGeneratedMd(projectDir, opts, pm, states, isUpdate, status, actions) {
|
|
1301
1428
|
const statusBadge = status.hasErrors ? "🔴 **Completed with Errors**" : status.hasWarnings ? "🟡 **Completed with Warnings**" : "🟢 **Successfully Completed**";
|
|
1302
1429
|
const md = [
|
|
1303
1430
|
`# 🚀 Project Setup Guide: ${opts.projectName}`,
|
|
@@ -1319,14 +1446,49 @@ async function generateGeneratedMd(projectDir, opts, pm, states, isUpdate, statu
|
|
|
1319
1446
|
"---",
|
|
1320
1447
|
"",
|
|
1321
1448
|
"## 📋 Initialization Checklist",
|
|
1322
|
-
"The following tasks were executed during the generation process:",
|
|
1449
|
+
isUpdate ? "The project was updated with the latest template changes:" : "The following tasks were executed during the generation process:",
|
|
1323
1450
|
`- [x] Scaffold project files and directories`,
|
|
1324
1451
|
`- [x] Configure \`package.json\` with appropriate dependencies`,
|
|
1325
1452
|
`- [${states.depsInstalled ? "x" : " "}] Install dependencies using \`${pm}\`${states.depsSkipped ? " *(Skipped)*" : ""}`,
|
|
1326
1453
|
`- [${states.gitInitialized ? "x" : " "}] Initialize Git repository`,
|
|
1327
|
-
`- [${states.githubCreated ? "x" : " "}] Create GitHub repository${states.githubSkipped ? " *(Skipped)*" : states.githubError ? " *(Failed)*" : ""}`,
|
|
1454
|
+
`- [${states.githubCreated ? "x" : " "}] Create and push GitHub repository${states.githubSkipped ? " *(Skipped)*" : states.githubError ? " *(Failed)*" : ""}`,
|
|
1328
1455
|
`- [${states.ciRun ? "x" : " "}] Run initial CI pipeline (lint, build, test)${states.ciSkipped ? " *(Skipped)*" : ""}`,
|
|
1329
1456
|
"",
|
|
1457
|
+
...isUpdate ? [
|
|
1458
|
+
"### 🛠️ Upgrade Details",
|
|
1459
|
+
"The following files were affected by this update:",
|
|
1460
|
+
"",
|
|
1461
|
+
"| File Path | Action | Reason | Next Steps |",
|
|
1462
|
+
"| :--- | :--- | :--- | :--- |",
|
|
1463
|
+
...actions.filter((a) => a.type !== "SKIP").map((a) => {
|
|
1464
|
+
const actionIcon = {
|
|
1465
|
+
ADD: "➕ ADD",
|
|
1466
|
+
MODIFY: "📝 MODIFY",
|
|
1467
|
+
MERGE: "🔀 MERGE",
|
|
1468
|
+
CONFLICT: "🔥 CONFLICT",
|
|
1469
|
+
DELETE: "🗑️ DELETE",
|
|
1470
|
+
UPDATED: "✨ UPDATED"
|
|
1471
|
+
}[a.type] || a.type;
|
|
1472
|
+
return `| \`${a.path}\` | ${actionIcon} | ${a.reason || "-"} | ${a.recommendedAction || (a.type === "CONFLICT" ? "**Resolve conflicts**" : "Review changes")} |`;
|
|
1473
|
+
}),
|
|
1474
|
+
""
|
|
1475
|
+
] : [],
|
|
1476
|
+
"---",
|
|
1477
|
+
"",
|
|
1478
|
+
"## 🛠️ Manual Adjustments Needed",
|
|
1479
|
+
"To complete your project setup, please review and manually update the following:",
|
|
1480
|
+
"- [ ] **`LICENSE`**: Verify the copyright year and author name.",
|
|
1481
|
+
"- [ ] **`package.json`**: Review the description, keywords, author, and repository links.",
|
|
1482
|
+
"- [ ] **`README.md`**: Update with project-specific instructions, architecture details, and contribution guidelines.",
|
|
1483
|
+
"- [ ] **`.gitignore`**: Note that there is a **`# Custom`** section at the end of the file for your own ignores.",
|
|
1484
|
+
"",
|
|
1485
|
+
"---",
|
|
1486
|
+
"",
|
|
1487
|
+
"## 💡 Next Steps",
|
|
1488
|
+
"1. Review the generated codebase to familiarize yourself with the structure.",
|
|
1489
|
+
`2. Start the development server using \`${pm} run dev\`.`,
|
|
1490
|
+
"3. Make your first commit and push to your remote repository.",
|
|
1491
|
+
"",
|
|
1330
1492
|
"---",
|
|
1331
1493
|
"",
|
|
1332
1494
|
"## 💻 Available Commands",
|
|
@@ -1386,35 +1548,20 @@ async function generateGeneratedMd(projectDir, opts, pm, states, isUpdate, statu
|
|
|
1386
1548
|
"| `gh pr checkout <pr-number>` | Checkout a Pull Request branch locally |",
|
|
1387
1549
|
"| `gh issue create` | Create a new Issue |",
|
|
1388
1550
|
"| `gh issue list` | List all open Issues |",
|
|
1389
|
-
"| `gh repo delete <owner>/<repo> --
|
|
1551
|
+
"| `gh repo delete <owner>/<repo> --yes` | Dangerously delete a repository completely (use with caution!) |",
|
|
1390
1552
|
"",
|
|
1391
1553
|
"---",
|
|
1392
1554
|
"",
|
|
1393
1555
|
"## 🚀 Creating a Release",
|
|
1394
1556
|
"This project uses Conventional Commits and automated changelogs. To create a new release:",
|
|
1395
|
-
"1. **
|
|
1396
|
-
"2. **Bump version:** `pnpm version <patch|minor|major> --no-git-tag-version`",
|
|
1397
|
-
"3. **Update changelog:** `pnpm run create-changelog`",
|
|
1398
|
-
"4. **Commit changes:** `git add . && git commit -m \"chore(release): $(node -p 'require(\"./package.json\").version')\"`",
|
|
1399
|
-
"5. **Tag & Push:** `git tag v$(node -p 'require(\"./package.json\").version') && git push && git push --tags`",
|
|
1400
|
-
"6. **Create GitHub Release:** `gh release create v$(node -p 'require(\"./package.json\").version') --generate-notes`",
|
|
1401
|
-
"7. **Publish (if applicable):** `pnpm publish`",
|
|
1557
|
+
"1. **Run the release command:** `pnpm release`",
|
|
1402
1558
|
"",
|
|
1403
|
-
"
|
|
1559
|
+
"This single command will automatically run your CI suite, bump the version, generate a changelog, create a Git tag, push to GitHub, and create a GitHub release.",
|
|
1404
1560
|
"",
|
|
1405
|
-
"
|
|
1406
|
-
"To complete your project setup, please review and manually update the following:",
|
|
1407
|
-
"- [ ] **`LICENSE`**: Verify the copyright year and author name.",
|
|
1408
|
-
"- [ ] **`package.json`**: Review the description, keywords, author, and repository links.",
|
|
1409
|
-
"- [ ] **`README.md`**: Update with project-specific instructions, architecture details, and contribution guidelines.",
|
|
1561
|
+
"**Note:** NPM publishing is disabled by default. See `CONTRIBUTING.md` for details on how to enable it.",
|
|
1410
1562
|
"",
|
|
1411
1563
|
"---",
|
|
1412
1564
|
"",
|
|
1413
|
-
"## 💡 Next Steps",
|
|
1414
|
-
"1. Review the generated codebase to familiarize yourself with the structure.",
|
|
1415
|
-
`2. Start the development server using \`${pm} run dev\`.`,
|
|
1416
|
-
"3. Make your first commit and push to your remote repository.",
|
|
1417
|
-
"",
|
|
1418
1565
|
"<br>",
|
|
1419
1566
|
"<p align=\"center\"><i>This file was auto-generated by <b>create-template-project</b>.</i></p>"
|
|
1420
1567
|
].join("\n");
|
|
@@ -2,23 +2,49 @@ name: Node.js CI
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
-
branches: [main
|
|
5
|
+
branches: [main]
|
|
6
6
|
pull_request:
|
|
7
|
-
branches: [main
|
|
7
|
+
branches: [main]
|
|
8
8
|
|
|
9
9
|
jobs:
|
|
10
10
|
build:
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
node-version: [22.x, 24.x]
|
|
16
|
+
|
|
12
17
|
steps:
|
|
13
|
-
-
|
|
14
|
-
|
|
15
|
-
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
# [PM_SETUP]
|
|
22
|
+
|
|
23
|
+
- name: Setup Node.js
|
|
16
24
|
uses: actions/setup-node@v4
|
|
17
25
|
with:
|
|
18
|
-
node-version:
|
|
26
|
+
node-version: ${{ matrix.node-version }}
|
|
19
27
|
cache: "{{packageManager}}"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
|
|
29
|
+
- name: Verify {{packageManager}}
|
|
30
|
+
run: {{packageManager}} --version
|
|
31
|
+
|
|
32
|
+
- name: Install dependencies
|
|
33
|
+
run: {{installCommand}}
|
|
34
|
+
|
|
35
|
+
# [PLAYWRIGHT_SETUP]
|
|
36
|
+
|
|
37
|
+
- name: Lint
|
|
38
|
+
run: {{packageManager}} run lint
|
|
39
|
+
|
|
40
|
+
- name: Build
|
|
41
|
+
run: {{packageManager}} run build
|
|
42
|
+
|
|
43
|
+
- name: Unit test
|
|
44
|
+
run: {{packageManager}} run test
|
|
45
|
+
|
|
23
46
|
- name: Coveralls GitHub Action
|
|
24
47
|
uses: coverallsapp/github-action@v2
|
|
48
|
+
|
|
49
|
+
- name: Integration test
|
|
50
|
+
run: {{packageManager}} run integration-test
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"npm": {
|
|
3
|
+
"publish": false
|
|
4
|
+
},
|
|
5
|
+
"git": {
|
|
6
|
+
"requireCleanWorkingDir": true,
|
|
7
|
+
"commit": true,
|
|
8
|
+
"commitMessage": "chore(release): ${version}",
|
|
9
|
+
"tag": true,
|
|
10
|
+
"tagName": "v${version}",
|
|
11
|
+
"push": true,
|
|
12
|
+
"pushArgs": ["--follow-tags"]
|
|
13
|
+
},
|
|
14
|
+
"github": {
|
|
15
|
+
"release": true,
|
|
16
|
+
"autoGenerate": true
|
|
17
|
+
},
|
|
18
|
+
"hooks": {
|
|
19
|
+
"before:init": "pnpm run ci",
|
|
20
|
+
"after:bump": "pnpm run create-changelog",
|
|
21
|
+
"before:git:commit": "git add CHANGELOG.md"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -1,9 +1,86 @@
|
|
|
1
1
|
# Agent Guidelines: {{projectName}}
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
This document provides essential instructions for autonomous agents (like Cursor, GitHub Copilot, or CLI agents) working on the `{{projectName}}` repository.
|
|
4
|
+
|
|
5
|
+
## Build/Lint/Test
|
|
6
|
+
|
|
4
7
|
- `pnpm run ci`: Runs lint, build, and all tests.
|
|
5
8
|
- `pnpm run dev`: Starts the development server.
|
|
6
|
-
- `pnpm run test`: Runs unit tests
|
|
7
|
-
- `pnpm run test
|
|
9
|
+
- `pnpm run test`: Runs unit tests.
|
|
10
|
+
- `pnpm run integration-test`: Runs integration tests.
|
|
8
11
|
- `pnpm exec vitest <file>`: Runs a specific test file.
|
|
9
12
|
- `pnpm run lint`: Lints and formats the codebase (oxlint + oxfmt).
|
|
13
|
+
|
|
14
|
+
## Project Overview
|
|
15
|
+
|
|
16
|
+
A modern project built with {{projectName}}, emphasizing type safety, performance, and best practices.
|
|
17
|
+
|
|
18
|
+
## Build, Lint, and Test Commands
|
|
19
|
+
|
|
20
|
+
### Core Commands
|
|
21
|
+
|
|
22
|
+
- **Install dependencies:** `pnpm install`
|
|
23
|
+
- **Build project:** `pnpm run build`
|
|
24
|
+
- **Lint code:** `pnpm run lint` (runs `tsc`, `oxlint`, and `oxfmt`)
|
|
25
|
+
- **Run all tests:** `pnpm run test` (uses `vitest` with coverage)
|
|
26
|
+
- **Run CI suite:** `pnpm run ci` (lint + build + test)
|
|
27
|
+
- **Run integration tests:** `pnpm run integration-test`
|
|
28
|
+
|
|
29
|
+
### Targeted Testing
|
|
30
|
+
|
|
31
|
+
- **Run a single test file:** `pnpm exec vitest src/index.test.ts`
|
|
32
|
+
- **Run tests matching a pattern:** `pnpm exec vitest -t "feature"`
|
|
33
|
+
- **Watch mode:** `pnpm exec vitest`
|
|
34
|
+
|
|
35
|
+
## Code Style & Conventions
|
|
36
|
+
|
|
37
|
+
### Language & Runtime
|
|
38
|
+
|
|
39
|
+
- **TypeScript:** Strict mode is mandatory. Use explicit types for function boundaries.
|
|
40
|
+
- **Node.js:** Targets >= 22.0.0.
|
|
41
|
+
- **ESM:** The project uses ES Modules (`"type": "module"` in `package.json`).
|
|
42
|
+
|
|
43
|
+
### Imports
|
|
44
|
+
|
|
45
|
+
- **Extensions:** Always use `.js` extensions in relative imports (e.g., `import { main } from './lib.js';`) to comply with ESM requirements in Node.js, even though source files are `.ts`.
|
|
46
|
+
- **Built-ins:** Use the `node:` prefix for built-in modules (e.g., `import path from 'node:path';`).
|
|
47
|
+
|
|
48
|
+
### Formatting
|
|
49
|
+
|
|
50
|
+
- **Tooling:** oxfmt is enforced via `pnpm run lint`.
|
|
51
|
+
- **Indentation:** Tabs are used for indentation.
|
|
52
|
+
- **Quotes:** Single quotes for strings, except when double quotes prevent escaping.
|
|
53
|
+
|
|
54
|
+
### Architecture & Types
|
|
55
|
+
|
|
56
|
+
- **Modularity:** Keep logic modularized.
|
|
57
|
+
- **Type Safety:** Use `zod` for runtime schema validation and `z.infer` for type definitions.
|
|
58
|
+
- **Immutability:** Prefer `readonly` and `const` where possible to ensure data integrity.
|
|
59
|
+
|
|
60
|
+
### Naming Conventions
|
|
61
|
+
|
|
62
|
+
- **Variables/Functions:** `camelCase`.
|
|
63
|
+
- **Constants:** `UPPER_SNAKE_CASE`.
|
|
64
|
+
- **Files:** `kebab-case.ts`.
|
|
65
|
+
|
|
66
|
+
### Error Handling
|
|
67
|
+
|
|
68
|
+
- **CLI Errors:** Throw descriptive errors.
|
|
69
|
+
- **Safety:** Always use `try...finally` or `using` (if applicable) to ensure resources like file handles or connections are closed.
|
|
70
|
+
- **Validation:** Validate all user inputs and CLI arguments using Zod schemas before processing.
|
|
71
|
+
|
|
72
|
+
## Mandatory Completion Protocol (Definition of Done)
|
|
73
|
+
|
|
74
|
+
No task involving code changes is considered complete until the agent provides the following "Evidence of Done" in its final response:
|
|
75
|
+
|
|
76
|
+
1. **CI Verification:** The agent MUST run `pnpm run ci` and include the full terminal output (showing `Found 0 warnings and 0 errors` and `Tests passed`).
|
|
77
|
+
2. **Test Coverage:** The agent MUST ensure that all new code is covered by tests and all existing tests pass.
|
|
78
|
+
|
|
79
|
+
**Failure to provide these logs means the task is INCOMPLETE.** The user is encouraged to reject any response that lacks this section.
|
|
80
|
+
|
|
81
|
+
## Git Workflow
|
|
82
|
+
|
|
83
|
+
- **Conventional Commits:** Adhere to the specification (e.g., `feat:`, `fix:`, `chore:`, `test:`).
|
|
84
|
+
- **Hooks:** Husky runs `pnpm run ci` on pre-commit. Do not bypass hooks.
|
|
85
|
+
- **Branches:** Use descriptive branch names like `feat/feature-name` or `fix/issue-description`.
|
|
86
|
+
|
|
@@ -21,11 +21,22 @@ We follow the **Conventional Commits** specification. This is **enforced** by `c
|
|
|
21
21
|
|
|
22
22
|
## Release Process
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
To release a new version:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
pnpm release -- patch # or minor / major
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
This will automatically:
|
|
31
|
+
1. Run the CI suite (`pnpm run ci`).
|
|
32
|
+
2. Bump the version in `package.json`.
|
|
33
|
+
3. Update the `CHANGELOG.md`.
|
|
34
|
+
4. Commit, tag, and push the changes.
|
|
35
|
+
5. Create a GitHub release with auto-generated notes.
|
|
36
|
+
6. Publish to npm (if configured).
|
|
37
|
+
|
|
38
|
+
**Note:** NPM publishing is **disabled** by default for new projects. To enable it:
|
|
39
|
+
1. Set `"private": false` in `package.json`.
|
|
40
|
+
2. Set `"publish": true` in `.release-it.json`.
|
|
41
|
+
3. Ensure you have the necessary `NPM_TOKEN` configured.
|
|
31
42
|
|
|
@@ -17,7 +17,7 @@ This project is built using **Vite 8** for high-performance development and bund
|
|
|
17
17
|
- `pnpm run build`: Builds the project for production.
|
|
18
18
|
- `pnpm run preview`: Previews the production build.
|
|
19
19
|
- `pnpm run test`: Runs the unit test suite (browser-based for web targets using **Vitest** and **Playwright**).
|
|
20
|
-
- `pnpm run test
|
|
20
|
+
- `pnpm run integration-test`: Runs integration tests using **Playwright**.
|
|
21
21
|
- `pnpm run lint`: Lints and formats the codebase using **oxlint** and **oxfmt**.
|
|
22
22
|
- `pnpm run ci`: Full CI pipeline (lint, build, test).
|
|
23
23
|
|
|
@@ -34,7 +34,7 @@ This project is built using **Vite 8** for high-performance development and bund
|
|
|
34
34
|
3. **Run tests**:
|
|
35
35
|
```bash
|
|
36
36
|
pnpm run test
|
|
37
|
-
pnpm run test
|
|
37
|
+
pnpm run integration-test
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Tooling
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "{{projectName}}",
|
|
3
3
|
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
4
5
|
"type": "module",
|
|
5
6
|
"scripts": {
|
|
6
7
|
"lint": "tsc && oxlint --disable-nested-config src client server tests *.config.ts && npm run format:check",
|
|
7
8
|
"format": "oxfmt --write .",
|
|
8
9
|
"format:check": "oxfmt --check .",
|
|
9
10
|
"test": "vitest run --coverage",
|
|
11
|
+
"release": "release-it",
|
|
10
12
|
"ci": "npm run lint && npm run build && npm run test",
|
|
11
13
|
"create-changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
|
|
12
14
|
"prepare": "husky"
|
|
@@ -25,6 +27,7 @@
|
|
|
25
27
|
"oxlint": "",
|
|
26
28
|
"oxlint-tsgolint": "",
|
|
27
29
|
"oxfmt": "",
|
|
30
|
+
"release-it": "",
|
|
28
31
|
"eslint-plugin-regexp": "",
|
|
29
32
|
"typescript": "",
|
|
30
33
|
"vitest": ""
|
|
@@ -4,7 +4,7 @@ export default defineConfig({
|
|
|
4
4
|
test: {
|
|
5
5
|
coverage: {
|
|
6
6
|
provider: 'v8',
|
|
7
|
-
reporter: ['text', 'json', 'html'],
|
|
7
|
+
reporter: ['text', 'json', 'html', 'lcov'],
|
|
8
8
|
include: ['src/**/*.ts', 'src/**/*.tsx'],
|
|
9
9
|
exclude: ['src/**/*.test.ts', 'src/**/*.test.tsx', 'src/**/*.d.ts'],
|
|
10
10
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-template-project",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"private": false,
|
|
4
5
|
"description": "An ultra-modular, type-safe Node.js CLI tool used to scaffold new project templates (CLI, Webpage, Webapp, Fullstack) with best-practice configurations pre-installed.",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"boilerplate",
|
|
@@ -58,10 +59,11 @@
|
|
|
58
59
|
"integration-test:web-vanilla": "rimraf ./temp/web-vanilla && node ./dist/index.js create --package-manager=pnpm --build --debug --no-progress --template=web-vanilla --name=web-vanilla --author=\"Jon Doe\" --github-username=\"jon-doe\" --path=./temp/web-vanilla",
|
|
59
60
|
"integration-test:web-app": "rimraf ./temp/web-app && node ./dist/index.js create --package-manager=pnpm --build --debug --no-progress --template=web-app --name=web-app --author=\"Jon Doe\" --github-username=\"jon-doe\" --path=./temp/web-app",
|
|
60
61
|
"integration-test:web-fullstack": "rimraf ./temp/web-fullstack && node ./dist/index.js create --package-manager=pnpm --build --debug --no-progress --template=web-fullstack --name=web-fullstack --author=\"Jon Doe\" --github-username=\"jon-doe\" --path=./temp/web-fullstack",
|
|
61
|
-
"integration-test
|
|
62
|
+
"integration-test": "pnpm run integration-test:cli && pnpm run integration-test:web-vanilla && pnpm run integration-test:web-app && pnpm run integration-test:web-fullstack",
|
|
62
63
|
"ci": "pnpm run lint && pnpm run build && pnpm run test",
|
|
63
64
|
"create-changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
|
|
64
|
-
"prepare": "husky"
|
|
65
|
+
"prepare": "husky",
|
|
66
|
+
"release": "release-it"
|
|
65
67
|
},
|
|
66
68
|
"dependencies": {
|
|
67
69
|
"@clack/prompts": "1.1.0",
|
|
@@ -80,10 +82,11 @@
|
|
|
80
82
|
"conventional-changelog-angular": "8.3.0",
|
|
81
83
|
"eslint-plugin-regexp": "3.1.0",
|
|
82
84
|
"husky": "9.1.7",
|
|
83
|
-
"oxfmt": "0.
|
|
84
|
-
"oxlint": "1.
|
|
85
|
-
"oxlint-tsgolint": "0.17.
|
|
85
|
+
"oxfmt": "0.42.0",
|
|
86
|
+
"oxlint": "1.57.0",
|
|
87
|
+
"oxlint-tsgolint": "0.17.3",
|
|
86
88
|
"pnpm": "10.32.1",
|
|
89
|
+
"release-it": "19.2.4",
|
|
87
90
|
"rimraf": "6.1.3",
|
|
88
91
|
"typescript": "5.9.3",
|
|
89
92
|
"vite": "8.0.2",
|