create-template-project 1.1.3 → 1.2.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 CHANGED
@@ -132,6 +132,16 @@ A full-stack monorepo featuring:
132
132
  - **Server**: Express.js backend with tRPC for end-to-end type safety.
133
133
  - **E2E**: Playwright for end-to-end testing.
134
134
 
135
+ ## Release Process
136
+
137
+ To cut a new release and automatically create a GitHub Release securely:
138
+
139
+ ```bash
140
+ GITHUB_TOKEN=$(gh auth token) pnpm release -- patch # minor, or major
141
+ ```
142
+
143
+ For more details on the release flow, see [CONTRIBUTING.md](CONTRIBUTING.md).
144
+
135
145
  ## Contributing
136
146
 
137
147
  Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to this project.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
3
  "vite": {
4
- "version": "8.0.5",
4
+ "version": "8.0.8",
5
5
  "description": "Native-ESM powered web dev build tool"
6
6
  },
7
7
  "@vitejs/plugin-react": {
@@ -9,11 +9,11 @@
9
9
  "description": "The default Vite plugin for React projects."
10
10
  },
11
11
  "@vitest/browser": {
12
- "version": "4.1.2",
12
+ "version": "4.1.4",
13
13
  "description": "Run Vitest in the browser."
14
14
  },
15
15
  "@vitest/browser-playwright": {
16
- "version": "4.1.2",
16
+ "version": "4.1.4",
17
17
  "description": "Playwright provider for Vitest browser mode."
18
18
  },
19
19
  "playwright": {
@@ -37,11 +37,11 @@
37
37
  "description": "TypeScript definitions for debug."
38
38
  },
39
39
  "@types/node": {
40
- "version": "25.5.2",
40
+ "version": "25.6.0",
41
41
  "description": "TypeScript definitions for Node.js."
42
42
  },
43
43
  "@vitest/coverage-v8": {
44
- "version": "4.1.2",
44
+ "version": "4.1.4",
45
45
  "description": "V8 coverage provider for Vitest."
46
46
  },
47
47
  "conventional-changelog": {
@@ -53,7 +53,7 @@
53
53
  "description": "Modern native git hooks made easy."
54
54
  },
55
55
  "oxlint": {
56
- "version": "1.58.0",
56
+ "version": "1.60.0",
57
57
  "description": "A JavaScript linter written in Rust."
58
58
  },
59
59
  "oxlint-tsgolint": {
@@ -61,7 +61,7 @@
61
61
  "description": "TypeScript-specific rules for oxlint."
62
62
  },
63
63
  "oxfmt": {
64
- "version": "0.43.0",
64
+ "version": "0.45.0",
65
65
  "description": "High performance JavaScript / TypeScript formatter."
66
66
  },
67
67
  "typescript": {
@@ -69,7 +69,7 @@
69
69
  "description": "A superset of JavaScript that compiles to clean JavaScript output."
70
70
  },
71
71
  "vitest": {
72
- "version": "4.1.2",
72
+ "version": "4.1.4",
73
73
  "description": "A Vite-native unit test framework."
74
74
  },
75
75
  "commander": {
@@ -93,19 +93,19 @@
93
93
  "description": "TypeScript definitions for Express."
94
94
  },
95
95
  "react": {
96
- "version": "19.2.4",
96
+ "version": "19.2.5",
97
97
  "description": "A JavaScript library for building user interfaces."
98
98
  },
99
99
  "react-dom": {
100
- "version": "19.2.4",
100
+ "version": "19.2.5",
101
101
  "description": "React package for working with the DOM."
102
102
  },
103
103
  "@mui/material": {
104
- "version": "7.3.9",
104
+ "version": "9.0.0",
105
105
  "description": "Material UI components."
106
106
  },
107
107
  "@mui/icons-material": {
108
- "version": "7.3.9",
108
+ "version": "9.0.0",
109
109
  "description": "Material Design icons distributed as SVG React components."
110
110
  },
111
111
  "@emotion/react": {
@@ -141,7 +141,7 @@
141
141
  "description": "tRPC integration for React Query."
142
142
  },
143
143
  "@tanstack/react-query": {
144
- "version": "5.96.2",
144
+ "version": "5.99.0",
145
145
  "description": "Powerful asynchronous state management for TS/JS."
146
146
  },
147
147
  "zod": {
@@ -149,7 +149,7 @@
149
149
  "description": "TypeScript-first schema validation with static type inference."
150
150
  },
151
151
  "react-router-dom": {
152
- "version": "7.14.0",
152
+ "version": "7.14.1",
153
153
  "description": "Declarative routing for React web applications."
154
154
  },
155
155
  "release-it": {
package/dist/index.js CHANGED
@@ -7,7 +7,6 @@ import { execa } from "execa";
7
7
  import path from "node:path";
8
8
  import fs from "node:fs/promises";
9
9
  import debugLib from "debug";
10
- import { fileURLToPath } from "node:url";
11
10
  import { existsSync } from "node:fs";
12
11
  //#region src/shared/types.ts
13
12
  var TemplateTypeSchema = z.enum([
@@ -56,10 +55,7 @@ var genericProcessor = (content, { opts }) => {
56
55
  default: break;
57
56
  }
58
57
  const pm = opts.packageManager;
59
- let lockfileRules = "";
60
- if (pm === "pnpm") lockfileRules = "package-lock.json\nyarn.lock";
61
- else if (pm === "yarn") lockfileRules = "package-lock.json\npnpm-lock.yaml";
62
- else lockfileRules = "yarn.lock\npnpm-lock.yaml";
58
+ const lockfileRules = pm === "pnpm" ? "package-lock.json\nyarn.lock" : pm === "yarn" ? "package-lock.json\npnpm-lock.yaml" : "yarn.lock\npnpm-lock.yaml";
63
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);
64
60
  };
65
61
  //#endregion
@@ -260,7 +256,6 @@ var mergeFile = async (filePath, existing, template, log) => {
260
256
  };
261
257
  //#endregion
262
258
  //#region src/templates/base/index.ts
263
- var __dirname$5 = path.dirname(fileURLToPath(import.meta.url));
264
259
  var getBaseTemplate = (_opts) => ({
265
260
  name: "base",
266
261
  description: "The foundation for all project templates, including common tooling and configuration.",
@@ -313,11 +308,10 @@ var getBaseTemplate = (_opts) => ({
313
308
  },
314
309
  scripts: {},
315
310
  files: [],
316
- templateDir: getTemplateDir(__dirname$5, "base")
311
+ templateDir: getTemplateDir(import.meta.dirname, "base")
317
312
  });
318
313
  //#endregion
319
314
  //#region src/templates/cli/index.ts
320
- var __dirname$4 = path.dirname(fileURLToPath(import.meta.url));
321
315
  var getCliTemplate = (_opts) => {
322
316
  return {
323
317
  name: "cli",
@@ -340,12 +334,11 @@ var getCliTemplate = (_opts) => {
340
334
  devDependencies: { vite: "vite" },
341
335
  scripts: {},
342
336
  files: [],
343
- templateDir: getTemplateDir(__dirname$4, "cli")
337
+ templateDir: getTemplateDir(import.meta.dirname, "cli")
344
338
  };
345
339
  };
346
340
  //#endregion
347
341
  //#region src/templates/web-vanilla/index.ts
348
- var __dirname$3 = path.dirname(fileURLToPath(import.meta.url));
349
342
  var getWebVanillaTemplate = (_opts) => ({
350
343
  name: "web-vanilla",
351
344
  description: "A modern, standalone web page template with built-in development and testing tooling.",
@@ -381,11 +374,10 @@ var getWebVanillaTemplate = (_opts) => ({
381
374
  "integration-test": "playwright test"
382
375
  },
383
376
  files: [],
384
- templateDir: getTemplateDir(__dirname$3, "web-vanilla")
377
+ templateDir: getTemplateDir(import.meta.dirname, "web-vanilla")
385
378
  });
386
379
  //#endregion
387
380
  //#region src/templates/web-app/index.ts
388
- var __dirname$2 = path.dirname(fileURLToPath(import.meta.url));
389
381
  var getWebAppTemplate = (_opts) => ({
390
382
  name: "web-app",
391
383
  description: "A React application with MUI and TanStack Query, powered by Vite.",
@@ -446,11 +438,10 @@ var getWebAppTemplate = (_opts) => ({
446
438
  start: "vite preview"
447
439
  },
448
440
  files: [],
449
- templateDir: getTemplateDir(__dirname$2, "web-app")
441
+ templateDir: getTemplateDir(import.meta.dirname, "web-app")
450
442
  });
451
443
  //#endregion
452
444
  //#region src/templates/web-fullstack/index.ts
453
- var __dirname$1 = path.dirname(fileURLToPath(import.meta.url));
454
445
  var getWebFullstackTemplate = (_opts) => ({
455
446
  name: "web-fullstack",
456
447
  description: "A comprehensive full-stack monorepo featuring an Express backend with tRPC and a modern React client with MUI.",
@@ -520,7 +511,7 @@ var getWebFullstackTemplate = (_opts) => ({
520
511
  "integration-test": "playwright test"
521
512
  },
522
513
  files: [],
523
- templateDir: getTemplateDir(__dirname$1, "web-fullstack")
514
+ templateDir: getTemplateDir(import.meta.dirname, "web-fullstack")
524
515
  });
525
516
  //#endregion
526
517
  //#region src/templates/registry.ts
@@ -1034,7 +1025,7 @@ Restrictions & Behavior:
1034
1025
  //#endregion
1035
1026
  //#region src/generators/project.ts
1036
1027
  var debug$1 = debugLib("create-template-project:generator");
1037
- var __dirname = path.dirname(fileURLToPath(import.meta.url));
1028
+ var moduleDir = import.meta.dirname;
1038
1029
  var pathExists = async (filePath) => {
1039
1030
  try {
1040
1031
  await fs.access(filePath);
@@ -1044,8 +1035,8 @@ var pathExists = async (filePath) => {
1044
1035
  }
1045
1036
  };
1046
1037
  var getDependencyConfigPath = async () => {
1047
- const sourcePath = path.resolve(__dirname, "../config/dependencies.json");
1048
- const distPath = path.resolve(__dirname, "config/dependencies.json");
1038
+ const sourcePath = path.resolve(moduleDir, "../config/dependencies.json");
1039
+ const distPath = path.resolve(moduleDir, "config/dependencies.json");
1049
1040
  return await pathExists(distPath) ? distPath : sourcePath;
1050
1041
  };
1051
1042
  var getLog = (progress) => ({
@@ -1604,6 +1595,11 @@ var generateProject = async (opts) => {
1604
1595
  };
1605
1596
  generateGeneratedMd = async (projectDir, opts, pm, states, isUpdate, status, actions) => {
1606
1597
  const statusBadge = status.hasErrors ? "🔴 **Completed with Errors**" : status.hasWarnings ? "🟡 **Completed with Warnings**" : "🟢 **Successfully Completed**";
1598
+ const skippedStepInstructions = [
1599
+ ...states.depsSkipped ? [`- [ ] **Install dependencies manually:** Run \`${pm} install\` from the project root.`] : [],
1600
+ ...states.githubSkipped ? [`- [ ] **Create and push GitHub repository manually:** Verify GitHub CLI auth with \`gh auth status\`, then run \`gh repo create ${opts.projectName} --public --source=. --remote=origin --push\`.`] : [],
1601
+ ...states.ciSkipped ? [`- [ ] **Run CI checks manually:** After dependencies are installed, run \`${pm} run ci\`.`] : []
1602
+ ];
1607
1603
  const md = [
1608
1604
  `# 🚀 Project Setup Guide: ${opts.projectName}`,
1609
1605
  "",
@@ -1632,6 +1628,12 @@ generateGeneratedMd = async (projectDir, opts, pm, states, isUpdate, status, act
1632
1628
  `- [${states.githubCreated ? "x" : " "}] Create and push GitHub repository${states.githubSkipped ? " *(Skipped)*" : states.githubError ? " *(Failed)*" : ""}`,
1633
1629
  `- [${states.ciRun ? "x" : " "}] Run initial CI pipeline (lint, build, test)${states.ciSkipped ? " *(Skipped)*" : ""}`,
1634
1630
  "",
1631
+ ...skippedStepInstructions.length > 0 ? [
1632
+ "## ⏭️ Complete Skipped Steps Manually",
1633
+ "Some initialization steps were marked as *(Skipped)*. Use the guidance below to complete them yourself:",
1634
+ ...skippedStepInstructions,
1635
+ ""
1636
+ ] : [],
1635
1637
  ...isUpdate ? [
1636
1638
  "### 🛠️ Upgrade Details",
1637
1639
  "The following files were affected by this update:",
@@ -1722,6 +1724,7 @@ generateGeneratedMd = async (projectDir, opts, pm, states, isUpdate, status, act
1722
1724
  "| Command | Description |",
1723
1725
  "| :--- | :--- |",
1724
1726
  "| `gh repo view --web` | Open the repository in your default web browser |",
1727
+ "| `gh repo create <name> --public --source=. --remote=origin --push` | Create and push a new GitHub repository |",
1725
1728
  "| `gh pr create` | Create a new Pull Request |",
1726
1729
  "| `gh pr checkout <pr-number>` | Checkout a Pull Request branch locally |",
1727
1730
  "| `gh issue create` | Create a new Issue |",
@@ -5,7 +5,7 @@
5
5
  "git": {
6
6
  "requireCleanWorkingDir": true,
7
7
  "commit": true,
8
- "commitMessage": "chore(release): ${version}",
8
+ "commitMessage": "chore: release ${version}",
9
9
  "tag": true,
10
10
  "tagName": "v${version}",
11
11
  "push": true,
@@ -149,6 +149,7 @@ export const linter = defineConfig({
149
149
  'unicorn/no-array-reduce': 'off', // TODO: consider enabling
150
150
  'unicorn/no-array-sort': 'off', // TODO: consider enabling
151
151
  'unicorn/no-hex-escape': 'off',
152
+ 'unicorn/no-immediate-mutation': 'off',
152
153
  'unicorn/no-nested-ternary': 'off',
153
154
  'unicorn/no-null': 'off', // TODO: consider enabling
154
155
  'unicorn/no-process-exit': 'off', // TODO: consider enabling
@@ -161,6 +162,14 @@ export const linter = defineConfig({
161
162
  'vitest/prefer-to-be-truthy': 'off', // FIXME: Conflict Detected: prefer-strict-boolean-matchers enforces toBe(true), but prefer-to-be-truthy enforces toBeTruthy().
162
163
  'vitest/require-test-timeout': 'off',
163
164
  },
165
+ overrides: [
166
+ {
167
+ files: ['tests/e2e/**/*.e2e-test.ts', '**/*.e2e-test.ts'],
168
+ rules: {
169
+ 'vitest/prefer-importing-vitest-globals': 'off',
170
+ },
171
+ },
172
+ ],
164
173
  settings: {
165
174
  'jsx-a11y': {
166
175
  polymorphicPropName: undefined,
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable react/jsx-max-depth */
2
- import {useState, type ReactNode} from 'react';
2
+ import {useMemo, type ReactNode} from 'react';
3
3
  import {BrowserRouter, Routes, Route, Navigate} from 'react-router-dom';
4
4
  import {QueryClient, QueryClientProvider} from '@tanstack/react-query';
5
5
  import {httpBatchLink} from '@trpc/client';
@@ -13,8 +13,9 @@ import {CssBaseline, ThemeProvider, createTheme} from '@mui/material';
13
13
  const theme = createTheme();
14
14
 
15
15
  export const App = (): ReactNode => {
16
- const [queryClient] = useState(() => new QueryClient());
17
- const [trpcClient] = useState(() =>
16
+ const queryClient = useMemo(() => new QueryClient(), []);
17
+ const trpcClient = useMemo(
18
+ () =>
18
19
  trpc.createClient({
19
20
  links: [
20
21
  httpBatchLink({
@@ -26,6 +27,7 @@ export const App = (): ReactNode => {
26
27
  }),
27
28
  ],
28
29
  }),
30
+ [],
29
31
  );
30
32
 
31
33
  return (
@@ -9,7 +9,7 @@ export const ProtectedRoute = (): ReactNode => {
9
9
 
10
10
  if (isLoading) {
11
11
  return (
12
- <Box display="flex" justifyContent="center" alignItems="center" minHeight="100vh">
12
+ <Box sx={{display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '100vh'}}>
13
13
  <CircularProgress />
14
14
  </Box>
15
15
  );
@@ -18,9 +18,9 @@ export const Dashboard = (): ReactNode => {
18
18
 
19
19
  return (
20
20
  <Container maxWidth="md">
21
- <Box mt={4}>
21
+ <Box sx={{mt: 4}}>
22
22
  <Paper elevation={3}>
23
- <Box p={4}>
23
+ <Box sx={{p: 4}}>
24
24
  <Typography variant="h4" gutterBottom>
25
25
  Dashboard
26
26
  </Typography>
@@ -28,7 +28,7 @@ export const Dashboard = (): ReactNode => {
28
28
  <Typography variant="body1" color="textSecondary" gutterBottom>
29
29
  Email: {user?.email ?? ''}
30
30
  </Typography>
31
- <Box mt={4}>
31
+ <Box sx={{mt: 4}}>
32
32
  <Typography variant="body2" gutterBottom>
33
33
  This is a protected page. You can only see this because you are logged in.
34
34
  </Typography>
@@ -36,7 +36,7 @@ export const Login = (): ReactNode => {
36
36
  <form onSubmit={handleSubmit}>
37
37
  <DialogTitle>Login</DialogTitle>
38
38
  <DialogContent>
39
- <Box display="flex" flexDirection="column" gap={2} pt={1}>
39
+ <Box sx={{display: 'flex', flexDirection: 'column', gap: 2, pt: 1}}>
40
40
  {error !== null && <Alert severity="error">{error}</Alert>}
41
41
  <Typography variant="body2" color="textSecondary">
42
42
  Use <b>demo@example.com</b> / <b>password</b> to login.
@@ -4,9 +4,8 @@ import * as trpcExpress from '@trpc/server/adapters/express';
4
4
  import {appRouter} from './routers/_app.js';
5
5
  import {createContext} from './context.js';
6
6
  import path from 'node:path';
7
- import {fileURLToPath} from 'node:url';
8
7
 
9
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+ const __dirname = import.meta.dirname;
10
9
  const app = express();
11
10
  const port = Number(process.env['PORT'] ?? 3001);
12
11
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-template-project",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
4
4
  "private": false,
5
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.",
6
6
  "keywords": [
@@ -82,21 +82,21 @@
82
82
  "@commitlint/config-conventional": "20.5.0",
83
83
  "@types/cli-progress": "3.11.6",
84
84
  "@types/debug": "4.1.13",
85
- "@types/node": "25.5.2",
86
- "@vitest/coverage-v8": "4.1.2",
85
+ "@types/node": "25.6.0",
86
+ "@vitest/coverage-v8": "4.1.4",
87
87
  "conventional-changelog": "7.2.0",
88
88
  "conventional-changelog-angular": "8.3.1",
89
89
  "eslint-plugin-regexp": "3.1.0",
90
90
  "husky": "9.1.7",
91
- "oxfmt": "0.43.0",
92
- "oxlint": "1.58.0",
91
+ "oxfmt": "0.45.0",
92
+ "oxlint": "1.60.0",
93
93
  "oxlint-tsgolint": "0.20.0",
94
94
  "pnpm": "10.33.0",
95
95
  "release-it": "19.2.4",
96
96
  "rimraf": "6.1.3",
97
97
  "typescript": "6.0.2",
98
- "vite": "8.0.5",
99
- "vitest": "4.1.2"
98
+ "vite": "8.0.8",
99
+ "vitest": "4.1.4"
100
100
  },
101
101
  "engines": {
102
102
  "node": ">=22"