create-template-project 0.1.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 (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +97 -0
  3. package/dist/cli.mjs +272 -0
  4. package/dist/config/dependencies.json +156 -0
  5. package/dist/generators/project.mjs +354 -0
  6. package/dist/index.d.mts +4 -0
  7. package/dist/index.mjs +32 -0
  8. package/dist/templates/base/files/.github/workflows/node.js.yml +20 -0
  9. package/dist/templates/base/files/.prettierrc.json +6 -0
  10. package/dist/templates/base/files/AGENTS.md +5 -0
  11. package/dist/templates/base/files/CONTRIBUTING.md +31 -0
  12. package/dist/templates/base/files/README.md +23 -0
  13. package/dist/templates/base/files/_oxlint.config.ts +74 -0
  14. package/dist/templates/base/files/commitlint.config.js +1 -0
  15. package/dist/templates/base/files/package.json +32 -0
  16. package/dist/templates/base/files/tsconfig.json +42 -0
  17. package/dist/templates/base/files/vitest.config.ts +3 -0
  18. package/dist/templates/base/index.mjs +16 -0
  19. package/dist/templates/cli/files/package.json +14 -0
  20. package/dist/templates/cli/files/src/index.test.ts +5 -0
  21. package/dist/templates/cli/files/src/index.ts +13 -0
  22. package/dist/templates/cli/files/tsdown.config.ts +3 -0
  23. package/dist/templates/cli/index.mjs +16 -0
  24. package/dist/templates/fullstack/files/client/index.html +8 -0
  25. package/dist/templates/fullstack/files/client/package.json +10 -0
  26. package/dist/templates/fullstack/files/client/src/App.test.tsx +8 -0
  27. package/dist/templates/fullstack/files/client/src/App.tsx +50 -0
  28. package/dist/templates/fullstack/files/client/src/components/ProtectedRoute.tsx +21 -0
  29. package/dist/templates/fullstack/files/client/src/contexts/AuthContext.tsx +63 -0
  30. package/dist/templates/fullstack/files/client/src/main.tsx +9 -0
  31. package/dist/templates/fullstack/files/client/src/pages/Dashboard.tsx +39 -0
  32. package/dist/templates/fullstack/files/client/src/pages/Login.tsx +81 -0
  33. package/dist/templates/fullstack/files/client/src/trpc.ts +4 -0
  34. package/dist/templates/fullstack/files/client/tsdown.config.ts +3 -0
  35. package/dist/templates/fullstack/files/package.json +35 -0
  36. package/dist/templates/fullstack/files/server/package.json +10 -0
  37. package/dist/templates/fullstack/files/server/src/context.ts +24 -0
  38. package/dist/templates/fullstack/files/server/src/index.test.ts +7 -0
  39. package/dist/templates/fullstack/files/server/src/index.ts +32 -0
  40. package/dist/templates/fullstack/files/server/src/routers/_app.ts +8 -0
  41. package/dist/templates/fullstack/files/server/src/routers/auth.ts +29 -0
  42. package/dist/templates/fullstack/files/server/src/trpc.ts +18 -0
  43. package/dist/templates/fullstack/files/server/tsdown.config.ts +3 -0
  44. package/dist/templates/fullstack/index.mjs +42 -0
  45. package/dist/templates/webapp/files/backend/src/index.ts +17 -0
  46. package/dist/templates/webapp/files/frontend/index.html +9 -0
  47. package/dist/templates/webapp/files/frontend/src/index.ts +4 -0
  48. package/dist/templates/webapp/files/package.json +13 -0
  49. package/dist/templates/webapp/files/src/index.test.ts +5 -0
  50. package/dist/templates/webapp/files/tsdown.config.ts +10 -0
  51. package/dist/templates/webapp/index.mjs +16 -0
  52. package/dist/templates/webpage/files/index.html +8 -0
  53. package/dist/templates/webpage/files/package.json +8 -0
  54. package/dist/templates/webpage/files/src/index.test.ts +5 -0
  55. package/dist/templates/webpage/files/src/index.ts +1 -0
  56. package/dist/templates/webpage/index.mjs +16 -0
  57. package/dist/types.mjs +30 -0
  58. package/dist/utils/file.mjs +101 -0
  59. package/package.json +79 -0
@@ -0,0 +1,354 @@
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 };
@@ -0,0 +1,4 @@
1
+ //#region src/index.d.ts
2
+ declare const main: () => Promise<void>;
3
+ //#endregion
4
+ export { main };
package/dist/index.mjs ADDED
@@ -0,0 +1,32 @@
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 };
@@ -0,0 +1,20 @@
1
+ name: Node.js CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, master]
6
+ pull_request:
7
+ branches: [main, master]
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - name: Use Node.js
15
+ uses: actions/setup-node@v4
16
+ with:
17
+ node-version: '22.x'
18
+ cache: 'npm'
19
+ - run: npm ci
20
+ - run: npm run ci
@@ -0,0 +1,6 @@
1
+ {
2
+ "useTabs": true,
3
+ "singleQuote": true,
4
+ "trailingComma": "all",
5
+ "printWidth": 100
6
+ }
@@ -0,0 +1,5 @@
1
+ # Agent Guidelines: {{projectName}}
2
+
3
+ Build/Lint/Test:
4
+ - `npm run ci`
5
+ - `npx vitest <file>`
@@ -0,0 +1,31 @@
1
+ ## Commit Message Guidelines
2
+
3
+ We follow the **Conventional Commits** specification. This is **enforced** by `commitlint` and is required for automated changelog generation.
4
+
5
+ **Format:** `type(scope): subject`
6
+
7
+ **Common Types:**
8
+ - `feat`: A new feature
9
+ - `fix`: A bug fix
10
+ - `docs`: Documentation only changes
11
+ - `style`: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
12
+ - `refactor`: A code change that neither fixes a bug nor adds a feature
13
+ - `perf`: A code change that improves performance
14
+ - `test`: Adding missing tests or correcting existing tests
15
+ - `chore`: Changes to the build process or auxiliary tools and libraries such as documentation generation
16
+
17
+ **Examples:**
18
+ - `feat(cli): add support for jsonc files`
19
+ - `fix(parser): handle empty input gracefully`
20
+ - `docs: update contributing guidelines`
21
+
22
+ ## Release Process
23
+
24
+ 1. **Verify**: `npm run ci`
25
+ 2. **Bump Version**: `npm version <patch|minor|major> --no-git-tag-version`
26
+ 3. **Update Changelog**: `npm run create-changelog`
27
+ 4. **Commit**: `git add . && git commit -m "chore(release): $(node -p 'require("./package.json").version')"`
28
+ 5. **Tag & Push**: `git tag v$(node -p 'require("./package.json").version') && git push && git push --tags`
29
+ 6. **Create GitHub Release**: `gh release create v$(node -p 'require("./package.json").version') --generate-notes`
30
+ 7. **Publish**: `npm publish`
31
+
@@ -0,0 +1,23 @@
1
+ # {{projectName}}
2
+
3
+ {{description}}
4
+
5
+ [![NPM Version](https://img.shields.io/npm/v/{{projectName}}.svg)](https://www.npmjs.com/package/{{projectName}})
6
+ [![NPM Downloads](https://img.shields.io/npm/dm/{{projectName}}.svg)](https://www.npmjs.com/package/{{projectName}})
7
+ [![Node.js CI](https://github.com/doberkofler/{{projectName}}/actions/workflows/node.js.yml/badge.svg)](https://github.com/doberkofler/{{projectName}}/actions/workflows/node.js.yml)
8
+
9
+ ## Generated with create-template-project.
10
+
11
+ ## Templates
12
+ - **cli**: Node.js CLI application.
13
+ - **webpage**: Standalone web page.
14
+ - **webapp**: Web application with Express.
15
+ - **fullstack**: Monorepo with Express and React.
16
+
17
+ ## Getting Started
18
+ ```bash
19
+ npm install
20
+ npm run dev # if applicable
21
+ npm run build
22
+ npm run test
23
+ ```
@@ -0,0 +1,74 @@
1
+ import { defineConfig } from 'oxlint';
2
+ import pluginRegexp from 'eslint-plugin-regexp';
3
+
4
+ export default defineConfig({
5
+ options: {
6
+ typeAware: true,
7
+ typeCheck: true
8
+ },
9
+ plugins: [
10
+ 'unicorn',
11
+ 'typescript',
12
+ 'oxc',
13
+ 'import',
14
+ 'react',
15
+ 'jsdoc',
16
+ 'promise',
17
+ 'vitest'
18
+ ],
19
+ jsPlugins: [
20
+ 'eslint-plugin-regexp'
21
+ ],
22
+ categories: {
23
+ correctness: 'error'
24
+ },
25
+ rules: {
26
+ ...pluginRegexp.configs.recommended.rules,
27
+ 'typescript/no-unused-vars': [
28
+ 'error',
29
+ {
30
+ caughtErrors: 'none',
31
+ argsIgnorePattern: '^_'
32
+ }
33
+ ]
34
+ },
35
+ settings: {
36
+ 'jsx-a11y': {
37
+ polymorphicPropName: undefined,
38
+ components: {},
39
+ attributes: {}
40
+ },
41
+ next: {
42
+ rootDir: []
43
+ },
44
+ react: {
45
+ formComponents: [],
46
+ linkComponents: [],
47
+ version: undefined
48
+ },
49
+ jsdoc: {
50
+ ignorePrivate: false,
51
+ ignoreInternal: false,
52
+ ignoreReplacesDocs: true,
53
+ overrideReplacesDocs: true,
54
+ augmentsExtendsReplacesDocs: false,
55
+ implementsReplacesDocs: false,
56
+ exemptDestructuredRootsFromChecks: false,
57
+ tagNamePreference: {}
58
+ },
59
+ vitest: {
60
+ typecheck: false
61
+ }
62
+ },
63
+ env: {
64
+ builtin: true
65
+ },
66
+ globals: {},
67
+ ignorePatterns: [
68
+ '**/.*',
69
+ 'node_modules/**',
70
+ 'dist/**',
71
+ 'coverage/**',
72
+ 'public/**'
73
+ ]
74
+ });
@@ -0,0 +1 @@
1
+ export default {extends: ['@commitlint/config-conventional']};
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "lint": "tsc && oxlint -c oxlint.config.ts && npm run prettier",
7
+ "prettier": "prettier --check .",
8
+ "prettier-write": "prettier --write .",
9
+ "test": "vitest run --coverage",
10
+ "ci": "npm run lint && npm run build && npm run test",
11
+ "prepare": "husky"
12
+ },
13
+ "dependencies": {
14
+ "debug": ""
15
+ },
16
+ "devDependencies": {
17
+ "@commitlint/cli": "",
18
+ "@commitlint/config-conventional": "",
19
+ "@types/debug": "",
20
+ "@types/node": "",
21
+ "@vitest/coverage-v8": "",
22
+ "conventional-changelog": "",
23
+ "husky": "",
24
+ "oxlint": "",
25
+ "oxlint-tsgolint": "",
26
+ "eslint-plugin-regexp": "",
27
+ "tinyexec": "",
28
+ "prettier": "",
29
+ "typescript": "",
30
+ "vitest": ""
31
+ }
32
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "compilerOptions": {
3
+ /* Language and Environment */
4
+ "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
5
+ "lib": ["ESNext"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
6
+ "module": "NodeNext", /* Specify what module code is generated. */
7
+ "moduleResolution": "NodeNext", /* Specify how TypeScript looks up a file from a given module specifier. */
8
+ "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. */
9
+ "resolveJsonModule": true, /* Enable importing .json files. */
10
+
11
+ /* Strict Type-Checking Options */
12
+ "strict": true, /* Enable all strict type-checking options. */
13
+ "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
14
+ "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
15
+ "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
16
+ "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
17
+ "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
18
+ "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
19
+ "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
20
+ "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
21
+ "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
22
+ "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
23
+ "exactOptionalPropertyTypes": true, /* Interpret optional property types as strictly typed, preventing assignment of 'undefined'. */
24
+ "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
25
+ "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
26
+ "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
27
+ "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
28
+ "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
29
+ "allowUnusedLabels": false, /* Disable error reporting for unused labels. */
30
+ "allowUnreachableCode": false, /* Disable error reporting for unreachable code. */
31
+
32
+ /* Emit */
33
+ "outDir": "./dist", /* Specify an output folder for all emitted files. */
34
+
35
+ /* Completeness */
36
+ "skipLibCheck": true, /* Skip type checking all .d.ts files. */
37
+ "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */
38
+ },
39
+ "include": [
40
+ "src/**/*"
41
+ ]
42
+ }
@@ -0,0 +1,3 @@
1
+ import { defineConfig } from 'vitest/config';
2
+
3
+ export default defineConfig({ test: { coverage: { provider: 'v8' } } });
@@ -0,0 +1,16 @@
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 };
@@ -0,0 +1,14 @@
1
+ {
2
+ "dependencies": {
3
+ "commander": "",
4
+ "cli-progress": ""
5
+ },
6
+ "devDependencies": {
7
+ "@types/cli-progress": "",
8
+ "tsdown": ""
9
+ },
10
+ "scripts": {
11
+ "dev": "tsdown --watch",
12
+ "build": "tsdown"
13
+ }
14
+ }