create-template-project 0.5.0 → 1.0.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 +4 -3
- package/dist/index.js +199 -79
- package/dist/templates/base/files/.github/workflows/node.js.yml +35 -9
- package/dist/templates/base/files/AGENTS.md +1 -1
- package/dist/templates/base/files/README.md +2 -2
- 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 +2 -2
package/README.md
CHANGED
|
@@ -22,9 +22,10 @@ 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
30
|
- **GitHub Integration:** Automatically initializes a Git repository and can create a GitHub repository using the `gh` CLI.
|
|
30
31
|
- **CI Ready:** Generates GitHub Actions workflows for automated testing and linting.
|
package/dist/index.js
CHANGED
|
@@ -37,23 +37,8 @@ var ProjectOptionsSchema = z.object({
|
|
|
37
37
|
progress: z.boolean().optional().default(true)
|
|
38
38
|
});
|
|
39
39
|
//#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) {
|
|
40
|
+
//#region src/utils/templating/generic.ts
|
|
41
|
+
var genericProcessor = (content, { opts }) => {
|
|
57
42
|
const { projectName, template, author, githubUsername } = opts;
|
|
58
43
|
let description = opts.description || "";
|
|
59
44
|
if (!description) switch (template) {
|
|
@@ -72,29 +57,105 @@ function processContent(filePath, content, opts, addedDeps) {
|
|
|
72
57
|
}
|
|
73
58
|
const pm = opts.packageManager || "npm";
|
|
74
59
|
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"
|
|
60
|
+
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);
|
|
61
|
+
};
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/utils/templating/github-workflow.ts
|
|
64
|
+
var WORKFLOW_PNPM_SETUP = ` - name: Setup pnpm
|
|
65
|
+
uses: pnpm/action-setup@v4
|
|
66
|
+
with:
|
|
67
|
+
version: 9
|
|
68
|
+
run_install: false`;
|
|
69
|
+
var WORKFLOW_PLAYWRIGHT_SETUP = ` - name: Install Playwright Browsers & Deps
|
|
70
|
+
run: npx playwright install --with-deps chromium`;
|
|
71
|
+
var githubWorkflowProcessor = (content, { filePath, opts }) => {
|
|
72
|
+
if (!filePath.includes(".github/workflows/node.js.yml")) return content;
|
|
73
|
+
const { template, packageManager: pm = "npm" } = opts;
|
|
74
|
+
let installCommand = "npm ci";
|
|
75
|
+
let pmSetup = "";
|
|
76
|
+
if (pm === "pnpm") {
|
|
77
|
+
installCommand = "pnpm install --frozen-lockfile";
|
|
78
|
+
pmSetup = WORKFLOW_PNPM_SETUP;
|
|
79
|
+
} else if (pm === "yarn") installCommand = "yarn install --frozen-lockfile";
|
|
80
|
+
let playwrightSetup = "";
|
|
81
|
+
if (template === "web-fullstack" || template === "web-app" || template === "web-vanilla") playwrightSetup = WORKFLOW_PLAYWRIGHT_SETUP;
|
|
82
|
+
let processed = content.replaceAll("{{installCommand}}", installCommand).replaceAll("# [PM_SETUP]", pmSetup).replaceAll("# [PLAYWRIGHT_SETUP]", playwrightSetup);
|
|
83
|
+
processed = processed.replace(/^\s*# \[PM_SETUP\]\s*\n/m, "");
|
|
84
|
+
processed = processed.replace(/^\s*# \[PLAYWRIGHT_SETUP\]\s*\n/m, "");
|
|
85
|
+
return processed;
|
|
86
|
+
};
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region src/utils/templating/tsconfig.ts
|
|
89
|
+
var WEB_ENV = `/* Language and Environment */
|
|
90
|
+
"target": "ES2023" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
|
91
|
+
"lib": ["ES2023", "DOM", "DOM.Iterable"] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
|
92
|
+
"module": "ESNext" /* Specify what module code is generated. */,
|
|
93
|
+
"moduleResolution": "bundler" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
|
94
|
+
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. */,
|
|
95
|
+
"resolveJsonModule": true /* Enable importing .json files. */,
|
|
96
|
+
"allowImportingTsExtensions": true /* Allow imports to include TypeScript file extensions. */,
|
|
97
|
+
"noEmit": true /* Disable emitting files from a compilation. */,
|
|
98
|
+
"jsx": "react-jsx" /* Specify what JSX code is generated. */,`;
|
|
99
|
+
var tsconfigProcessor = (content, { filePath, opts }) => {
|
|
100
|
+
if (filePath !== "tsconfig.json") return content;
|
|
101
|
+
const { template } = opts;
|
|
102
|
+
let processed = content;
|
|
103
|
+
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 */");
|
|
104
|
+
if (template === "web-fullstack") processed = processed.replace(/"include":\s*\[\s*"src\/\*\*\/\*"\s*\]/, "\"include\": [\"client/src/**/*\", \"server/src/**/*\"]");
|
|
105
|
+
return processed;
|
|
106
|
+
};
|
|
107
|
+
//#endregion
|
|
108
|
+
//#region src/utils/templating/contributing.ts
|
|
109
|
+
var contributingProcessor = (content, { filePath, addedDeps }) => {
|
|
110
|
+
if (filePath !== "CONTRIBUTING.md" || addedDeps.length === 0) return content;
|
|
111
|
+
let processed = content;
|
|
112
|
+
processed += "\n## Dependencies\n\n";
|
|
113
|
+
const uniqueDeps = Array.from(new Set(addedDeps.map((d) => JSON.stringify(d)))).map((s) => JSON.parse(s));
|
|
114
|
+
for (const dep of uniqueDeps) processed += `- **${dep.name}**: ${dep.description}\n`;
|
|
97
115
|
return processed;
|
|
116
|
+
};
|
|
117
|
+
//#endregion
|
|
118
|
+
//#region src/utils/templating/web-vanilla-html.ts
|
|
119
|
+
var webVanillaHtmlProcessor = (content, { filePath, opts }) => {
|
|
120
|
+
if (opts.template === "web-vanilla" && filePath === "index.html") return content.replace("{{scriptSrc}}", "/src/index.ts");
|
|
121
|
+
return content;
|
|
122
|
+
};
|
|
123
|
+
//#endregion
|
|
124
|
+
//#region src/utils/templating/index.ts
|
|
125
|
+
var processors = [
|
|
126
|
+
genericProcessor,
|
|
127
|
+
githubWorkflowProcessor,
|
|
128
|
+
tsconfigProcessor,
|
|
129
|
+
contributingProcessor,
|
|
130
|
+
webVanillaHtmlProcessor
|
|
131
|
+
];
|
|
132
|
+
function processContent$1(filePath, content, opts, addedDeps) {
|
|
133
|
+
const context = {
|
|
134
|
+
filePath,
|
|
135
|
+
opts,
|
|
136
|
+
addedDeps
|
|
137
|
+
};
|
|
138
|
+
return processors.reduce((acc, processor) => processor(acc, context), content);
|
|
139
|
+
}
|
|
140
|
+
//#endregion
|
|
141
|
+
//#region src/utils/file.ts
|
|
142
|
+
var debug$3 = debugLib("create-template-project:utils:file");
|
|
143
|
+
function getTemplateDir(dirname, templateName) {
|
|
144
|
+
const sourcePath = path.resolve(dirname, "files");
|
|
145
|
+
const distPath = path.resolve(dirname, "templates", templateName, "files");
|
|
146
|
+
return existsSync(distPath) ? distPath : sourcePath;
|
|
147
|
+
}
|
|
148
|
+
async function getAllFiles(dirPath, arrayOfFiles = []) {
|
|
149
|
+
const files = await fs.readdir(dirPath);
|
|
150
|
+
for (const file of files) {
|
|
151
|
+
if (file === ".DS_Store") continue;
|
|
152
|
+
if ((await fs.stat(path.join(dirPath, file))).isDirectory()) arrayOfFiles = await getAllFiles(path.join(dirPath, file), arrayOfFiles);
|
|
153
|
+
else arrayOfFiles.push(path.join(dirPath, file));
|
|
154
|
+
}
|
|
155
|
+
return arrayOfFiles;
|
|
156
|
+
}
|
|
157
|
+
function processContent(filePath, content, opts, addedDeps) {
|
|
158
|
+
return processContent$1(filePath, content, opts, addedDeps);
|
|
98
159
|
}
|
|
99
160
|
function mergePackageJson(target, source) {
|
|
100
161
|
if (source.scripts) target.scripts = {
|
|
@@ -277,7 +338,7 @@ var getWebVanillaTemplate = (_opts) => {
|
|
|
277
338
|
preview: "vite preview",
|
|
278
339
|
test: "vitest run",
|
|
279
340
|
"test:ui": "vitest",
|
|
280
|
-
"test
|
|
341
|
+
"integration-test": "playwright test"
|
|
281
342
|
},
|
|
282
343
|
files: [],
|
|
283
344
|
templateDir: getTemplateDir(__dirname$3, "web-vanilla")
|
|
@@ -343,7 +404,7 @@ var getWebAppTemplate = (_opts) => {
|
|
|
343
404
|
preview: "vite preview",
|
|
344
405
|
test: "vitest run",
|
|
345
406
|
"test:ui": "vitest",
|
|
346
|
-
"test
|
|
407
|
+
"integration-test": "playwright test",
|
|
347
408
|
start: "vite preview"
|
|
348
409
|
},
|
|
349
410
|
files: [],
|
|
@@ -420,7 +481,7 @@ var getWebFullstackTemplate = (_opts) => {
|
|
|
420
481
|
build: "npm run build --workspaces",
|
|
421
482
|
dev: "npm run dev --workspaces",
|
|
422
483
|
test: "npm run test --workspaces",
|
|
423
|
-
"test
|
|
484
|
+
"integration-test": "playwright test"
|
|
424
485
|
},
|
|
425
486
|
files: [],
|
|
426
487
|
templateDir: getTemplateDir(__dirname$1, "web-fullstack")
|
|
@@ -1007,7 +1068,8 @@ var generateProject = async (opts) => {
|
|
|
1007
1068
|
if (isUpdate && isSeedFile(relativePath)) {
|
|
1008
1069
|
actions.push({
|
|
1009
1070
|
type: "SKIP",
|
|
1010
|
-
path: relativePath
|
|
1071
|
+
path: relativePath,
|
|
1072
|
+
reason: "Seed file - skipped during update to preserve manual changes"
|
|
1011
1073
|
});
|
|
1012
1074
|
continue;
|
|
1013
1075
|
}
|
|
@@ -1015,7 +1077,8 @@ var generateProject = async (opts) => {
|
|
|
1015
1077
|
if (isUpdate && await pathExists(targetPath)) {
|
|
1016
1078
|
actions.push({
|
|
1017
1079
|
type: "DELETE",
|
|
1018
|
-
path: relativePath
|
|
1080
|
+
path: relativePath,
|
|
1081
|
+
reason: "File no longer required for this template type"
|
|
1019
1082
|
});
|
|
1020
1083
|
pendingOperations.push(async () => {
|
|
1021
1084
|
await fs.rm(targetPath, { force: true });
|
|
@@ -1035,21 +1098,36 @@ var generateProject = async (opts) => {
|
|
|
1035
1098
|
if (isUpdate && exists) {
|
|
1036
1099
|
const existingContent = await fs.readFile(finalTargetPath, "utf8");
|
|
1037
1100
|
if (existingContent.trim() !== content.trim()) {
|
|
1038
|
-
|
|
1101
|
+
const action = {
|
|
1039
1102
|
type: "MODIFY",
|
|
1040
|
-
path: finalRelativePath
|
|
1041
|
-
|
|
1103
|
+
path: finalRelativePath,
|
|
1104
|
+
reason: "Template tooling or configuration update"
|
|
1105
|
+
};
|
|
1106
|
+
actions.push(action);
|
|
1042
1107
|
pendingOperations.push(async () => {
|
|
1043
1108
|
const result = await mergeFile(finalTargetPath, existingContent, content, log);
|
|
1044
|
-
if (result === "merged")
|
|
1045
|
-
|
|
1046
|
-
|
|
1109
|
+
if (result === "merged") {
|
|
1110
|
+
action.type = "MERGE";
|
|
1111
|
+
action.reason = "Merged template updates with your manual changes";
|
|
1112
|
+
action.recommendedAction = "Review changes for correct integration";
|
|
1113
|
+
log.info(`ℹ Merged: ${finalRelativePath}`);
|
|
1114
|
+
} else if (result === "conflict") {
|
|
1115
|
+
action.type = "CONFLICT";
|
|
1116
|
+
action.reason = "Conflicting changes between template and your code";
|
|
1117
|
+
action.recommendedAction = "Resolve git conflict markers in this file";
|
|
1118
|
+
log.warn(`⚠ Conflict: ${finalRelativePath}`);
|
|
1119
|
+
} else if (result === "updated") {
|
|
1120
|
+
action.type = "UPDATED";
|
|
1121
|
+
action.reason = "File was updated to the latest template version";
|
|
1122
|
+
log.info(`✔ Updated: ${finalRelativePath}`);
|
|
1123
|
+
}
|
|
1047
1124
|
});
|
|
1048
1125
|
}
|
|
1049
1126
|
} else if (!exists) {
|
|
1050
1127
|
actions.push({
|
|
1051
1128
|
type: "ADD",
|
|
1052
|
-
path: finalRelativePath
|
|
1129
|
+
path: finalRelativePath,
|
|
1130
|
+
reason: "New template file added"
|
|
1053
1131
|
});
|
|
1054
1132
|
pendingOperations.push(async () => {
|
|
1055
1133
|
await fs.mkdir(path.dirname(finalTargetPath), { recursive: true });
|
|
@@ -1065,7 +1143,8 @@ var generateProject = async (opts) => {
|
|
|
1065
1143
|
if (isUpdate && isSeedFile(file.path)) {
|
|
1066
1144
|
actions.push({
|
|
1067
1145
|
type: "SKIP",
|
|
1068
|
-
path: file.path
|
|
1146
|
+
path: file.path,
|
|
1147
|
+
reason: "Seed file - skipped during update to preserve manual changes"
|
|
1069
1148
|
});
|
|
1070
1149
|
continue;
|
|
1071
1150
|
}
|
|
@@ -1073,7 +1152,8 @@ var generateProject = async (opts) => {
|
|
|
1073
1152
|
if (isUpdate && await pathExists(targetPath)) {
|
|
1074
1153
|
actions.push({
|
|
1075
1154
|
type: "DELETE",
|
|
1076
|
-
path: file.path
|
|
1155
|
+
path: file.path,
|
|
1156
|
+
reason: "File no longer required for this template type"
|
|
1077
1157
|
});
|
|
1078
1158
|
pendingOperations.push(async () => {
|
|
1079
1159
|
await fs.rm(targetPath, { force: true });
|
|
@@ -1087,21 +1167,36 @@ var generateProject = async (opts) => {
|
|
|
1087
1167
|
if (isUpdate && exists) {
|
|
1088
1168
|
const existingContent = await fs.readFile(targetPath, "utf8");
|
|
1089
1169
|
if (existingContent.trim() !== content.trim()) {
|
|
1090
|
-
|
|
1170
|
+
const action = {
|
|
1091
1171
|
type: "MODIFY",
|
|
1092
|
-
path: file.path
|
|
1093
|
-
|
|
1172
|
+
path: file.path,
|
|
1173
|
+
reason: "Template configuration update"
|
|
1174
|
+
};
|
|
1175
|
+
actions.push(action);
|
|
1094
1176
|
pendingOperations.push(async () => {
|
|
1095
1177
|
const result = await mergeFile(targetPath, existingContent, content, log);
|
|
1096
|
-
if (result === "merged")
|
|
1097
|
-
|
|
1098
|
-
|
|
1178
|
+
if (result === "merged") {
|
|
1179
|
+
action.type = "MERGE";
|
|
1180
|
+
action.reason = "Merged template updates with your manual changes";
|
|
1181
|
+
action.recommendedAction = "Review changes for correct integration";
|
|
1182
|
+
log.info(`ℹ Merged: ${file.path}`);
|
|
1183
|
+
} else if (result === "conflict") {
|
|
1184
|
+
action.type = "CONFLICT";
|
|
1185
|
+
action.reason = "Conflicting changes between template and your code";
|
|
1186
|
+
action.recommendedAction = "Resolve git conflict markers in this file";
|
|
1187
|
+
log.warn(`⚠ Conflict: ${file.path}`);
|
|
1188
|
+
} else if (result === "updated") {
|
|
1189
|
+
action.type = "UPDATED";
|
|
1190
|
+
action.reason = "File was updated to the latest template version";
|
|
1191
|
+
log.info(`✔ Updated: ${file.path}`);
|
|
1192
|
+
}
|
|
1099
1193
|
});
|
|
1100
1194
|
}
|
|
1101
1195
|
} else if (!exists) {
|
|
1102
1196
|
actions.push({
|
|
1103
1197
|
type: "ADD",
|
|
1104
|
-
path: file.path
|
|
1198
|
+
path: file.path,
|
|
1199
|
+
reason: "New template file added"
|
|
1105
1200
|
});
|
|
1106
1201
|
pendingOperations.push(async () => {
|
|
1107
1202
|
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
@@ -1124,7 +1219,8 @@ var generateProject = async (opts) => {
|
|
|
1124
1219
|
if (workspaceChanged) {
|
|
1125
1220
|
actions.push({
|
|
1126
1221
|
type: workspaceExists ? "MODIFY" : "ADD",
|
|
1127
|
-
path: "pnpm-workspace.yaml"
|
|
1222
|
+
path: "pnpm-workspace.yaml",
|
|
1223
|
+
reason: "Updated workspace configuration for pnpm"
|
|
1128
1224
|
});
|
|
1129
1225
|
pendingOperations.push(async () => {
|
|
1130
1226
|
await fs.writeFile(workspacePath, workspaceYaml);
|
|
@@ -1142,11 +1238,13 @@ var generateProject = async (opts) => {
|
|
|
1142
1238
|
if (pkgChanged) {
|
|
1143
1239
|
if (isUpdate) actions.push({
|
|
1144
1240
|
type: "MODIFY",
|
|
1145
|
-
path: "package.json"
|
|
1241
|
+
path: "package.json",
|
|
1242
|
+
reason: "Updated dependencies and scripts to match latest template"
|
|
1146
1243
|
});
|
|
1147
1244
|
else actions.push({
|
|
1148
1245
|
type: "ADD",
|
|
1149
|
-
path: "package.json"
|
|
1246
|
+
path: "package.json",
|
|
1247
|
+
reason: "Initial project configuration"
|
|
1150
1248
|
});
|
|
1151
1249
|
pendingOperations.push(async () => {
|
|
1152
1250
|
debug$1("Writing final consolidated package.json to: %s", pkgPath);
|
|
@@ -1292,12 +1390,12 @@ var generateProject = async (opts) => {
|
|
|
1292
1390
|
hasErrors,
|
|
1293
1391
|
hasWarnings,
|
|
1294
1392
|
errorMessages
|
|
1295
|
-
});
|
|
1393
|
+
}, actions);
|
|
1296
1394
|
const successMsg = `Project "${projectName}" ${isUpdate ? "updated" : "scaffolded"} successfully in ${projectDir}. A detailed setup guide has been generated at GENERATED.md`;
|
|
1297
1395
|
if (hasWarnings) log.warn(`${successMsg} (completed with warnings)`);
|
|
1298
1396
|
else log.success(successMsg);
|
|
1299
1397
|
};
|
|
1300
|
-
async function generateGeneratedMd(projectDir, opts, pm, states, isUpdate, status) {
|
|
1398
|
+
async function generateGeneratedMd(projectDir, opts, pm, states, isUpdate, status, actions) {
|
|
1301
1399
|
const statusBadge = status.hasErrors ? "🔴 **Completed with Errors**" : status.hasWarnings ? "🟡 **Completed with Warnings**" : "🟢 **Successfully Completed**";
|
|
1302
1400
|
const md = [
|
|
1303
1401
|
`# 🚀 Project Setup Guide: ${opts.projectName}`,
|
|
@@ -1319,7 +1417,7 @@ async function generateGeneratedMd(projectDir, opts, pm, states, isUpdate, statu
|
|
|
1319
1417
|
"---",
|
|
1320
1418
|
"",
|
|
1321
1419
|
"## 📋 Initialization Checklist",
|
|
1322
|
-
"The following tasks were executed during the generation process:",
|
|
1420
|
+
isUpdate ? "The project was updated with the latest template changes:" : "The following tasks were executed during the generation process:",
|
|
1323
1421
|
`- [x] Scaffold project files and directories`,
|
|
1324
1422
|
`- [x] Configure \`package.json\` with appropriate dependencies`,
|
|
1325
1423
|
`- [${states.depsInstalled ? "x" : " "}] Install dependencies using \`${pm}\`${states.depsSkipped ? " *(Skipped)*" : ""}`,
|
|
@@ -1327,6 +1425,41 @@ async function generateGeneratedMd(projectDir, opts, pm, states, isUpdate, statu
|
|
|
1327
1425
|
`- [${states.githubCreated ? "x" : " "}] Create GitHub repository${states.githubSkipped ? " *(Skipped)*" : states.githubError ? " *(Failed)*" : ""}`,
|
|
1328
1426
|
`- [${states.ciRun ? "x" : " "}] Run initial CI pipeline (lint, build, test)${states.ciSkipped ? " *(Skipped)*" : ""}`,
|
|
1329
1427
|
"",
|
|
1428
|
+
...isUpdate ? [
|
|
1429
|
+
"### 🛠️ Upgrade Details",
|
|
1430
|
+
"The following files were affected by this update:",
|
|
1431
|
+
"",
|
|
1432
|
+
"| File Path | Action | Reason | Next Steps |",
|
|
1433
|
+
"| :--- | :--- | :--- | :--- |",
|
|
1434
|
+
...actions.filter((a) => a.type !== "SKIP").map((a) => {
|
|
1435
|
+
const actionIcon = {
|
|
1436
|
+
ADD: "➕ ADD",
|
|
1437
|
+
MODIFY: "📝 MODIFY",
|
|
1438
|
+
MERGE: "🔀 MERGE",
|
|
1439
|
+
CONFLICT: "🔥 CONFLICT",
|
|
1440
|
+
DELETE: "🗑️ DELETE",
|
|
1441
|
+
UPDATED: "✨ UPDATED"
|
|
1442
|
+
}[a.type] || a.type;
|
|
1443
|
+
return `| \`${a.path}\` | ${actionIcon} | ${a.reason || "-"} | ${a.recommendedAction || (a.type === "CONFLICT" ? "**Resolve conflicts**" : "Review changes")} |`;
|
|
1444
|
+
}),
|
|
1445
|
+
""
|
|
1446
|
+
] : [],
|
|
1447
|
+
"---",
|
|
1448
|
+
"",
|
|
1449
|
+
"## 🛠️ Manual Adjustments Needed",
|
|
1450
|
+
"To complete your project setup, please review and manually update the following:",
|
|
1451
|
+
"- [ ] **`LICENSE`**: Verify the copyright year and author name.",
|
|
1452
|
+
"- [ ] **`package.json`**: Review the description, keywords, author, and repository links.",
|
|
1453
|
+
"- [ ] **`README.md`**: Update with project-specific instructions, architecture details, and contribution guidelines.",
|
|
1454
|
+
"- [ ] **`.gitignore`**: Note that there is a **`# Custom`** section at the end of the file for your own ignores.",
|
|
1455
|
+
"",
|
|
1456
|
+
"---",
|
|
1457
|
+
"",
|
|
1458
|
+
"## 💡 Next Steps",
|
|
1459
|
+
"1. Review the generated codebase to familiarize yourself with the structure.",
|
|
1460
|
+
`2. Start the development server using \`${pm} run dev\`.`,
|
|
1461
|
+
"3. Make your first commit and push to your remote repository.",
|
|
1462
|
+
"",
|
|
1330
1463
|
"---",
|
|
1331
1464
|
"",
|
|
1332
1465
|
"## 💻 Available Commands",
|
|
@@ -1402,19 +1535,6 @@ async function generateGeneratedMd(projectDir, opts, pm, states, isUpdate, statu
|
|
|
1402
1535
|
"",
|
|
1403
1536
|
"---",
|
|
1404
1537
|
"",
|
|
1405
|
-
"## 🛠️ Manual Adjustments Needed",
|
|
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.",
|
|
1410
|
-
"",
|
|
1411
|
-
"---",
|
|
1412
|
-
"",
|
|
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
1538
|
"<br>",
|
|
1419
1539
|
"<p align=\"center\"><i>This file was auto-generated by <b>create-template-project</b>.</i></p>"
|
|
1420
1540
|
].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
|
|
@@ -4,6 +4,6 @@ Build/Lint/Test:
|
|
|
4
4
|
- `pnpm run ci`: Runs lint, build, and all tests.
|
|
5
5
|
- `pnpm run dev`: Starts the development server.
|
|
6
6
|
- `pnpm run test`: Runs unit tests (browser-based for web projects).
|
|
7
|
-
- `pnpm run test
|
|
7
|
+
- `pnpm run integration-test`: Runs integration tests.
|
|
8
8
|
- `pnpm exec vitest <file>`: Runs a specific test file.
|
|
9
9
|
- `pnpm run lint`: Lints and formats the codebase (oxlint + oxfmt).
|
|
@@ -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
|
|
@@ -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,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-template-project",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"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
5
|
"keywords": [
|
|
6
6
|
"boilerplate",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"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
59
|
"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
60
|
"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
|
|
61
|
+
"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
62
|
"ci": "pnpm run lint && pnpm run build && pnpm run test",
|
|
63
63
|
"create-changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
|
|
64
64
|
"prepare": "husky"
|