create-template-project 0.1.0 → 0.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.
Files changed (70) hide show
  1. package/README.md +8 -8
  2. package/dist/cli.mjs +54 -18
  3. package/dist/config/dependencies.json +59 -39
  4. package/dist/generators/info.mjs +58 -0
  5. package/dist/generators/project.mjs +60 -27
  6. package/dist/index.mjs +4 -1
  7. package/dist/templates/base/files/.github/workflows/node.js.yml +5 -3
  8. package/dist/templates/base/files/.prettierignore +50 -0
  9. package/dist/templates/base/files/.prettierrc.json +4 -2
  10. package/dist/templates/base/files/AGENTS.md +5 -2
  11. package/dist/templates/base/files/README.md +36 -13
  12. package/dist/templates/base/files/_oxlint.config.ts +17 -33
  13. package/dist/templates/base/files/tsconfig.json +32 -32
  14. package/dist/templates/base/files/vitest.config.ts +2 -2
  15. package/dist/templates/base/index.mjs +40 -1
  16. package/dist/templates/cli/index.mjs +14 -1
  17. package/dist/templates/web-app/files/index.html +11 -0
  18. package/dist/templates/web-app/files/playwright.config.ts +26 -0
  19. package/dist/templates/web-app/files/src/index.tsx +21 -0
  20. package/dist/templates/web-app/files/tests/e2e/basic.e2e-test.ts +13 -0
  21. package/dist/templates/web-app/files/vite.config.ts +30 -0
  22. package/dist/templates/web-app/index.mjs +69 -0
  23. package/dist/templates/web-fullstack/files/client/index.html +10 -0
  24. package/dist/templates/{fullstack → web-fullstack}/files/client/package.json +2 -2
  25. package/dist/templates/{fullstack → web-fullstack}/files/client/src/contexts/AuthContext.tsx +1 -5
  26. package/dist/templates/{fullstack → web-fullstack}/files/client/src/pages/Login.tsx +4 -33
  27. package/dist/templates/web-fullstack/files/client/vite.config.ts +26 -0
  28. package/dist/templates/web-fullstack/files/playwright.config.ts +33 -0
  29. package/dist/templates/{fullstack → web-fullstack}/files/server/package.json +2 -2
  30. package/dist/templates/web-fullstack/files/server/vite.config.ts +21 -0
  31. package/dist/templates/web-fullstack/files/tests/e2e/basic.e2e-test.ts +14 -0
  32. package/dist/templates/web-fullstack/index.mjs +78 -0
  33. package/dist/templates/web-vanilla/files/index.html +11 -0
  34. package/dist/templates/web-vanilla/files/package.json +17 -0
  35. package/dist/templates/web-vanilla/files/playwright.config.ts +26 -0
  36. package/dist/templates/web-vanilla/files/tests/e2e/basic.e2e-test.ts +11 -0
  37. package/dist/templates/web-vanilla/files/vite.config.ts +24 -0
  38. package/dist/templates/web-vanilla/index.mjs +45 -0
  39. package/dist/types.mjs +4 -4
  40. package/dist/utils/file.mjs +31 -13
  41. package/package.json +24 -21
  42. package/dist/templates/fullstack/files/client/index.html +0 -8
  43. package/dist/templates/fullstack/files/client/tsdown.config.ts +0 -3
  44. package/dist/templates/fullstack/files/server/tsdown.config.ts +0 -3
  45. package/dist/templates/fullstack/index.mjs +0 -42
  46. package/dist/templates/webapp/files/backend/src/index.ts +0 -17
  47. package/dist/templates/webapp/files/frontend/index.html +0 -9
  48. package/dist/templates/webapp/files/frontend/src/index.ts +0 -4
  49. package/dist/templates/webapp/files/package.json +0 -13
  50. package/dist/templates/webapp/files/tsdown.config.ts +0 -10
  51. package/dist/templates/webapp/index.mjs +0 -16
  52. package/dist/templates/webpage/files/index.html +0 -8
  53. package/dist/templates/webpage/files/package.json +0 -8
  54. package/dist/templates/webpage/index.mjs +0 -16
  55. /package/dist/templates/{webapp → web-app}/files/src/index.test.ts +0 -0
  56. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/App.test.tsx +0 -0
  57. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/App.tsx +0 -0
  58. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/components/ProtectedRoute.tsx +0 -0
  59. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/main.tsx +0 -0
  60. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/pages/Dashboard.tsx +0 -0
  61. /package/dist/templates/{fullstack → web-fullstack}/files/client/src/trpc.ts +0 -0
  62. /package/dist/templates/{fullstack → web-fullstack}/files/package.json +0 -0
  63. /package/dist/templates/{fullstack → web-fullstack}/files/server/src/context.ts +0 -0
  64. /package/dist/templates/{fullstack → web-fullstack}/files/server/src/index.test.ts +0 -0
  65. /package/dist/templates/{fullstack → web-fullstack}/files/server/src/index.ts +0 -0
  66. /package/dist/templates/{fullstack → web-fullstack}/files/server/src/routers/_app.ts +0 -0
  67. /package/dist/templates/{fullstack → web-fullstack}/files/server/src/routers/auth.ts +0 -0
  68. /package/dist/templates/{fullstack → web-fullstack}/files/server/src/trpc.ts +0 -0
  69. /package/dist/templates/{webpage → web-vanilla}/files/src/index.test.ts +0 -0
  70. /package/dist/templates/{webpage → web-vanilla}/files/src/index.ts +0 -0
@@ -1,15 +1,5 @@
1
1
  import {useState, FormEvent} from 'react';
2
- import {
3
- Button,
4
- TextField,
5
- Dialog,
6
- DialogActions,
7
- DialogContent,
8
- DialogTitle,
9
- Typography,
10
- Alert,
11
- Box,
12
- } from '@mui/material';
2
+ import {Button, TextField, Dialog, DialogActions, DialogContent, DialogTitle, Typography, Alert, Box} from '@mui/material';
13
3
  import {useAuth} from '../contexts/AuthContext.js';
14
4
  import {trpc} from '../trpc.js';
15
5
  import {useNavigate} from 'react-router-dom';
@@ -47,31 +37,12 @@ export const Login = () => {
47
37
  <Typography variant="body2" color="textSecondary">
48
38
  Use <b>demo@example.com</b> / <b>password</b> to login.
49
39
  </Typography>
50
- <TextField
51
- label="Email"
52
- type="email"
53
- fullWidth
54
- required
55
- value={email}
56
- onChange={(e) => setEmail(e.currentTarget.value)}
57
- />
58
- <TextField
59
- label="Password"
60
- type="password"
61
- fullWidth
62
- required
63
- value={password}
64
- onChange={(e) => setPassword(e.currentTarget.value)}
65
- />
40
+ <TextField label="Email" type="email" fullWidth required value={email} onChange={(e) => setEmail(e.currentTarget.value)} />
41
+ <TextField label="Password" type="password" fullWidth required value={password} onChange={(e) => setPassword(e.currentTarget.value)} />
66
42
  </Box>
67
43
  </DialogContent>
68
44
  <DialogActions>
69
- <Button
70
- type="submit"
71
- variant="contained"
72
- fullWidth
73
- disabled={loginMutation.isPending}
74
- >
45
+ <Button type="submit" variant="contained" fullWidth disabled={loginMutation.isPending}>
75
46
  {loginMutation.isPending ? 'Logging in...' : 'Login'}
76
47
  </Button>
77
48
  </DialogActions>
@@ -0,0 +1,26 @@
1
+ import path from 'node:path';
2
+ import {defineConfig} from 'vitest/config';
3
+ import react from '@vitejs/plugin-react';
4
+ import {playwright} from '@vitest/browser-playwright';
5
+
6
+ export default defineConfig({
7
+ plugins: [react()],
8
+ test: {
9
+ include: ['src/**/*.test.{ts,tsx}'],
10
+ browser: {
11
+ enabled: true,
12
+ headless: true,
13
+ screenshotDirectory: path.resolve('./temp/vitest/__screenshots__'),
14
+ instances: [
15
+ {
16
+ browser: 'chromium',
17
+ provider: playwright({
18
+ launchOptions: {
19
+ args: ['--disable-web-security'],
20
+ },
21
+ }),
22
+ },
23
+ ],
24
+ },
25
+ },
26
+ });
@@ -0,0 +1,33 @@
1
+ import {defineConfig, devices} from '@playwright/test';
2
+
3
+ export default defineConfig({
4
+ testDir: './tests/e2e',
5
+ testMatch: '**/*.e2e-test.ts',
6
+ fullyParallel: true,
7
+ forbidOnly: !!process.env.CI,
8
+ retries: process.env.CI ? 2 : 0,
9
+ workers: process.env.CI ? 1 : undefined,
10
+ reporter: 'html',
11
+ use: {
12
+ baseURL: 'http://localhost:5173',
13
+ trace: 'on-first-retry',
14
+ },
15
+ projects: [
16
+ {
17
+ name: 'chromium',
18
+ use: {...devices['Desktop Chrome']},
19
+ },
20
+ ],
21
+ webServer: [
22
+ {
23
+ command: 'npm run dev --workspace=server',
24
+ port: 3000,
25
+ reuseExistingServer: !process.env.CI,
26
+ },
27
+ {
28
+ command: 'npm run dev --workspace=client',
29
+ port: 5173,
30
+ reuseExistingServer: !process.env.CI,
31
+ },
32
+ ],
33
+ });
@@ -3,8 +3,8 @@
3
3
  "version": "0.1.0",
4
4
  "type": "module",
5
5
  "scripts": {
6
- "build": "tsdown",
7
- "dev": "tsdown --watch",
6
+ "build": "vite build",
7
+ "dev": "vite build --watch",
8
8
  "test": "vitest run"
9
9
  }
10
10
  }
@@ -0,0 +1,21 @@
1
+ import {defineConfig} from 'vitest/config';
2
+ import path from 'node:path';
3
+
4
+ export default defineConfig({
5
+ build: {
6
+ lib: {
7
+ entry: path.resolve(__dirname, 'src/index.ts'),
8
+ formats: ['es'],
9
+ fileName: 'index',
10
+ },
11
+ outDir: 'dist',
12
+ emptyOutDir: true,
13
+ rollupOptions: {
14
+ external: ['express', 'cors', 'zod', '@trpc/server', 'node:path', 'node:url'],
15
+ },
16
+ },
17
+ test: {
18
+ globals: true,
19
+ environment: 'node',
20
+ },
21
+ });
@@ -0,0 +1,14 @@
1
+ import {test, expect} from '@playwright/test';
2
+
3
+ test('has title', async ({page}) => {
4
+ await page.goto('/');
5
+ await expect(page).toHaveTitle(/{{projectName}}/);
6
+ });
7
+
8
+ test('can login', async ({page}) => {
9
+ await page.goto('/login');
10
+ await page.fill('input[name="username"]', 'admin');
11
+ await page.fill('input[name="password"]', 'password');
12
+ await page.click('button[type="submit"]');
13
+ await expect(page).toHaveURL('/');
14
+ });
@@ -0,0 +1,78 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ //#region src/templates/web-fullstack/index.ts
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ const getWebFullstackTemplate = (_opts) => {
6
+ return {
7
+ name: "web-fullstack",
8
+ description: "A comprehensive full-stack monorepo featuring an Express backend with tRPC and a modern React client with MUI.",
9
+ components: [
10
+ {
11
+ name: "React",
12
+ description: "Powerful library for building component-based user interfaces."
13
+ },
14
+ {
15
+ name: "MUI",
16
+ description: "Rich set of Material Design UI components for React."
17
+ },
18
+ {
19
+ name: "tRPC",
20
+ description: "End-to-end typesafe APIs made easy."
21
+ },
22
+ {
23
+ name: "React Query",
24
+ description: "Powerful asynchronous state management for React."
25
+ },
26
+ {
27
+ name: "Express",
28
+ description: "Fast, minimalist backend web framework."
29
+ },
30
+ {
31
+ name: "React Router",
32
+ description: "Declarative routing for the frontend."
33
+ },
34
+ {
35
+ name: "Vite",
36
+ description: "Fast, modern frontend build tool."
37
+ }
38
+ ],
39
+ dependencies: {
40
+ react: "react",
41
+ "react-dom": "react-dom",
42
+ "@mui/material": "@mui/material",
43
+ "@mui/icons-material": "@mui/icons-material",
44
+ "@emotion/react": "@emotion/react",
45
+ "@emotion/styled": "@emotion/styled",
46
+ express: "express",
47
+ "@trpc/server": "@trpc/server",
48
+ "@trpc/client": "@trpc/client",
49
+ "@trpc/react-query": "@trpc/react-query",
50
+ "@tanstack/react-query": "@tanstack/react-query",
51
+ "react-router-dom": "react-router-dom",
52
+ cors: "cors"
53
+ },
54
+ devDependencies: {
55
+ "@types/react": "@types/react",
56
+ "@types/react-dom": "@types/react-dom",
57
+ "@types/express": "@types/express",
58
+ "@types/cors": "@types/cors",
59
+ "@playwright/test": "@playwright/test",
60
+ vite: "vite",
61
+ "@vitejs/plugin-react": "@vitejs/plugin-react",
62
+ vitest: "vitest",
63
+ "@vitest/browser": "@vitest/browser",
64
+ "@vitest/browser-playwright": "@vitest/browser-playwright",
65
+ playwright: "playwright"
66
+ },
67
+ scripts: {
68
+ build: "npm run build --workspaces",
69
+ dev: "npm run dev --workspaces",
70
+ test: "npm run test --workspaces",
71
+ "test:e2e": "playwright test"
72
+ },
73
+ files: [],
74
+ templateDir: path.resolve(__dirname, "files")
75
+ };
76
+ };
77
+ //#endregion
78
+ export { getWebFullstackTemplate };
@@ -0,0 +1,11 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>{{projectName}}</title>
5
+ </head>
6
+ <body>
7
+ <h1>{{projectName}}</h1>
8
+ <div id="app"></div>
9
+ <script type="module" src="/src/index.ts"></script>
10
+ </body>
11
+ </html>
@@ -0,0 +1,17 @@
1
+ {
2
+ "devDependencies": {
3
+ "vite": "",
4
+ "vitest": "",
5
+ "@vitest/browser": "",
6
+ "playwright": "",
7
+ "@playwright/test": ""
8
+ },
9
+ "scripts": {
10
+ "dev": "vite",
11
+ "build": "vite build",
12
+ "preview": "vite preview",
13
+ "test": "vitest run",
14
+ "test:ui": "vitest",
15
+ "test:e2e": "playwright test"
16
+ }
17
+ }
@@ -0,0 +1,26 @@
1
+ import {defineConfig, devices} from '@playwright/test';
2
+
3
+ export default defineConfig({
4
+ testDir: './tests/e2e',
5
+ testMatch: '**/*.e2e-test.ts',
6
+ fullyParallel: true,
7
+ forbidOnly: !!process.env.CI,
8
+ retries: process.env.CI ? 2 : 0,
9
+ workers: process.env.CI ? 1 : undefined,
10
+ reporter: 'html',
11
+ use: {
12
+ baseURL: 'http://localhost:5173',
13
+ trace: 'on-first-retry',
14
+ },
15
+ projects: [
16
+ {
17
+ name: 'chromium',
18
+ use: {...devices['Desktop Chrome']},
19
+ },
20
+ ],
21
+ webServer: {
22
+ command: 'npm run dev',
23
+ url: 'http://localhost:5173',
24
+ reuseExistingServer: !process.env.CI,
25
+ },
26
+ });
@@ -0,0 +1,11 @@
1
+ import {test, expect} from '@playwright/test';
2
+
3
+ test('has title', async ({page}) => {
4
+ await page.goto('/');
5
+ await expect(page).toHaveTitle(/{{projectName}}/);
6
+ });
7
+
8
+ test('header is visible', async ({page}) => {
9
+ await page.goto('/');
10
+ await expect(page.locator('h1')).toBeVisible();
11
+ });
@@ -0,0 +1,24 @@
1
+ import path from 'node:path';
2
+ import {defineConfig} from 'vitest/config';
3
+ import {playwright} from '@vitest/browser-playwright';
4
+
5
+ export default defineConfig({
6
+ test: {
7
+ include: ['src/**/*.test.ts'],
8
+ browser: {
9
+ enabled: true,
10
+ headless: true,
11
+ screenshotDirectory: path.resolve('./temp/vitest/__screenshots__'),
12
+ instances: [
13
+ {
14
+ browser: 'chromium',
15
+ provider: playwright({
16
+ launchOptions: {
17
+ args: ['--disable-web-security'],
18
+ },
19
+ }),
20
+ },
21
+ ],
22
+ },
23
+ },
24
+ });
@@ -0,0 +1,45 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ //#region src/templates/web-vanilla/index.ts
4
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ const getWebVanillaTemplate = (_opts) => {
6
+ return {
7
+ name: "web-vanilla",
8
+ description: "A modern, standalone web page template with built-in development and testing tooling.",
9
+ components: [
10
+ {
11
+ name: "Vite",
12
+ description: "Fast frontend build tool and development server."
13
+ },
14
+ {
15
+ name: "Vitest",
16
+ description: "Modern testing framework with browser support."
17
+ },
18
+ {
19
+ name: "Playwright",
20
+ description: "Comprehensive end-to-end testing for modern web apps."
21
+ }
22
+ ],
23
+ dependencies: {},
24
+ devDependencies: {
25
+ vite: "vite",
26
+ vitest: "vitest",
27
+ "@vitest/browser": "@vitest/browser",
28
+ "@vitest/browser-playwright": "@vitest/browser-playwright",
29
+ playwright: "playwright",
30
+ "@playwright/test": "@playwright/test"
31
+ },
32
+ scripts: {
33
+ dev: "vite",
34
+ build: "vite build",
35
+ preview: "vite preview",
36
+ test: "vitest run",
37
+ "test:ui": "vitest",
38
+ "test:e2e": "playwright test"
39
+ },
40
+ files: [],
41
+ templateDir: path.resolve(__dirname, "files")
42
+ };
43
+ };
44
+ //#endregion
45
+ export { getWebVanillaTemplate };
package/dist/types.mjs CHANGED
@@ -2,9 +2,9 @@ import { z } from "zod";
2
2
  //#region src/types.ts
3
3
  const TemplateTypeSchema = z.enum([
4
4
  "cli",
5
- "webpage",
6
- "webapp",
7
- "fullstack"
5
+ "web-vanilla",
6
+ "web-app",
7
+ "web-fullstack"
8
8
  ]);
9
9
  const PackageManagerSchema = z.enum([
10
10
  "npm",
@@ -27,4 +27,4 @@ const ProjectOptionsSchema = z.object({
27
27
  silent: z.boolean().optional().default(false)
28
28
  });
29
29
  //#endregion
30
- export { ProjectOptionsSchema };
30
+ export { ProjectOptionsSchema, TemplateTypeSchema };
@@ -17,28 +17,39 @@ function processContent(filePath, content, opts, addedDeps) {
17
17
  case "cli":
18
18
  description = "A modern Node.js CLI application with TypeScript and automated tooling.";
19
19
  break;
20
- case "webpage":
20
+ case "web-vanilla":
21
21
  description = "A standalone web page/application for modern browsers.";
22
22
  break;
23
- case "fullstack":
23
+ case "web-fullstack":
24
24
  description = "A full-stack monorepo with an Express server and a React/MUI client.";
25
25
  break;
26
- case "webapp":
27
- description = "A classic web application with an Express backend.";
26
+ case "web-app":
27
+ description = "A React application with MUI and TanStack Query.";
28
28
  break;
29
29
  }
30
30
  let processed = content.replaceAll("{{projectName}}", projectName).replaceAll("{{description}}", description);
31
- if (template === "webpage" && filePath === "index.html") processed = processed.replace("{{scriptSrc}}", opts.skipBuild ? "./src/index.js" : "./dist/index.js");
31
+ if (filePath.includes(".github/workflows/node.js.yml")) {
32
+ const pm = opts.packageManager || "npm";
33
+ let installCommand = "npm ci";
34
+ let pmSetup = "";
35
+ if (pm === "pnpm") {
36
+ installCommand = "pnpm install --frozen-lockfile";
37
+ pmSetup = "- uses: pnpm/action-setup@v4\n with:\n version: 9";
38
+ } else if (pm === "yarn") installCommand = "yarn install --frozen-lockfile";
39
+ let playwrightSetup = "";
40
+ if (template === "web-fullstack" || template === "web-app" || template === "web-vanilla") playwrightSetup = "- name: Install Playwright Browsers & Deps\n run: npx playwright install --with-deps chromium";
41
+ processed = processed.replaceAll("{{packageManager}}", pm).replaceAll("{{installCommand}}", installCommand).replaceAll("# [PM_SETUP]", pmSetup).replaceAll("# [PLAYWRIGHT_SETUP]", playwrightSetup);
42
+ processed = processed.replace(/^\s*# \[PM_SETUP\]\s*\n/m, "");
43
+ processed = processed.replace(/^\s*# \[PLAYWRIGHT_SETUP\]\s*\n/m, "");
44
+ }
45
+ if (template === "web-vanilla" && filePath === "index.html") processed = processed.replace("{{scriptSrc}}", opts.skipBuild ? "./src/index.js" : "./dist/index.mjs");
32
46
  if (filePath === "CONTRIBUTING.md" && addedDeps.length > 0) {
33
47
  processed += "\n## Dependencies\n\n";
34
48
  const uniqueDeps = Array.from(new Set(addedDeps.map((d) => JSON.stringify(d)))).map((s) => JSON.parse(s));
35
49
  for (const dep of uniqueDeps) processed += `- **${dep.name}**: ${dep.description}\n`;
36
50
  }
37
- if (template === "fullstack" && filePath === "tsconfig.json") {
38
- processed = processed.replace(/"lib":\s*\["ESNext"\]/, "\"lib\": [\"ESNext\", \"DOM\"]");
39
- processed = processed.replace(/"module":\s*"NodeNext"/, "\"module\": \"NodeNext\",\n \"jsx\": \"react-jsx\"");
40
- processed = processed.replace(/"include":\s*\[\s*"src\/\*\*\/\*"\s*\]/, "\"include\": [\"client/src/**/*\", \"server/src/**/*\"]");
41
- }
51
+ if ((template === "web-fullstack" || template === "web-vanilla" || template === "web-app") && filePath === "tsconfig.json") processed = processed.replace(/\/\* Language and Environment \*\/[\s\S]*?\/\* Strict Type-Checking Options \*\//, "/* Language and Environment */\n \"target\": \"ES2023\" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,\n \"lib\": [\"ES2023\", \"DOM\", \"DOM.Iterable\"] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,\n \"module\": \"ESNext\" /* Specify what module code is generated. */,\n \"moduleResolution\": \"bundler\" /* Specify how TypeScript looks up a file from a given module specifier. */,\n \"esModuleInterop\": true /* Emit additional JavaScript to ease support for importing CommonJS modules. */,\n \"resolveJsonModule\": true /* Enable importing .json files. */,\n \"allowImportingTsExtensions\": true /* Allow imports to include TypeScript file extensions. */,\n \"noEmit\": true /* Disable emitting files from a compilation. */,\n \"jsx\": \"react-jsx\" /* Specify what JSX code is generated. */,\n\n /* Strict Type-Checking Options */");
52
+ if (template === "web-fullstack" && filePath === "tsconfig.json") processed = processed.replace(/"include":\s*\[\s*"src\/\*\*\/\*"\s*\]/, "\"include\": [\"client/src/**/*\", \"server/src/**/*\"]");
42
53
  return processed;
43
54
  }
44
55
  function mergePackageJson(target, source) {
@@ -66,7 +77,8 @@ function isSeedFile(filePath) {
66
77
  ].some((dir) => filePath.startsWith(dir)) || [
67
78
  "index.html",
68
79
  "App.tsx",
69
- "main.tsx"
80
+ "main.tsx",
81
+ "index.tsx"
70
82
  ].some((file) => filePath === file);
71
83
  }
72
84
  async function mergeFile(filePath, existing, template, log) {
@@ -77,18 +89,24 @@ async function mergeFile(filePath, existing, template, log) {
77
89
  await fs.writeFile(tempNew, template);
78
90
  await fs.writeFile(tempBase, "");
79
91
  try {
92
+ const stdio = debug.enabled ? "inherit" : "pipe";
93
+ debug("Executing: git merge-file %s %s %s", filePath, tempBase, tempNew);
80
94
  await execa("git", [
81
95
  "merge-file",
82
96
  filePath,
83
97
  tempBase,
84
98
  tempNew
85
- ], { preferLocal: true });
99
+ ], {
100
+ stdio,
101
+ preferLocal: true
102
+ });
86
103
  return (await fs.readFile(filePath, "utf8")).trim() !== template.trim() ? "merged" : "updated";
87
104
  } catch (e) {
88
105
  if (e.exitCode === 1) return "conflict";
89
106
  else {
90
107
  debug("Git merge-file failed: %O", e);
91
- log.error(`Failed to merge ${filePath}: ${e.message}`);
108
+ const detail = e.stdout || e.stderr ? `\n\nOutput:\n${e.stdout}\n${e.stderr}` : "";
109
+ log.error(`Failed to merge ${filePath}: ${e.message}${detail}`);
92
110
  return "error";
93
111
  }
94
112
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-template-project",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Dieter Oberkofler",
@@ -18,20 +18,24 @@
18
18
  },
19
19
  "scripts": {
20
20
  "run-interactive": "node dist/index.mjs interactive",
21
- "run-create-cli": "node dist/index.mjs create --name=my-cli --directory=./temp --template=cli --overwrite --package-manager=pnpm --build",
22
- "run-create-webpage": "node dist/index.mjs create --name=my-webpage --directory=./temp --template=webpage --package-manager=pnpm --overwrite --build",
23
- "run-create-webapp": "node dist/index.mjs create --name=my-webapp --directory=./temp --template=webapp --package-manager=pnpm --overwrite --build",
24
- "run-create-fullstack": "node dist/index.mjs create --name=my-fullstack --directory=./temp --template=fullstack --package-manager=pnpm --overwrite --build",
25
- "run-create-all": "npm run run-create-cli && npm run run-create-webpage && npm run run-create-webapp && npm run run-create-fullstack",
21
+ "run-create:cli": "node dist/index.mjs create --name=my-cli --directory=./temp --template=cli --overwrite --package-manager=pnpm --build",
22
+ "run-create:web-vanilla": "node dist/index.mjs create --name=my-web-vanilla --directory=./temp --template=web-vanilla --package-manager=pnpm --overwrite --build",
23
+ "run-create:web-app": "node dist/index.mjs create --name=my-web-app --directory=./temp --template=web-app --package-manager=pnpm --overwrite --build",
24
+ "run-create:web-fullstack": "node dist/index.mjs create --name=my-web-fullstack --directory=./temp --template=web-fullstack --package-manager=pnpm --overwrite --build",
25
+ "run-create": "pnpm run build && pnpm run run-create:cli && pnpm run run-create:web-vanilla && pnpm run run-create:web-app && pnpm run run-create:web-fullstack",
26
26
  "build": "tsdown && node scripts/copy-templates.ts",
27
27
  "dependencies-check": "node scripts/dependencies.ts",
28
28
  "dependencies-update": "node scripts/dependencies.ts --update",
29
- "lint": "tsc && oxlint --ignore-pattern \"src/templates/**/files\" && npm run prettier",
29
+ "lint": "tsc && oxlint --ignore-pattern \"src/templates/**/files\" && pnpm run prettier",
30
30
  "prettier": "prettier --check .",
31
31
  "prettier-write": "prettier --write .",
32
- "test": "vitest run --coverage --exclude '**/*.integration.test.ts'",
33
- "test:integration": "vitest run src/generators/project.integration.test.ts --fileParallelism=false --testTimeout=600000",
34
- "ci": "npm run lint && npm run build && npm run test",
32
+ "test": "vitest run --coverage --exclude '**/*.integration.*.test.ts'",
33
+ "test:integration:cli": "vitest run src/generators/project.integration.cli.test.ts --testTimeout=600000",
34
+ "test:integration:web-vanilla": "vitest run src/generators/project.integration.web-vanilla.test.ts --testTimeout=600000",
35
+ "test:integration:web-app": "vitest run src/generators/project.integration.web-app.test.ts --testTimeout=600000",
36
+ "test:integration:web-fullstack": "vitest run src/generators/project.integration.web-fullstack.test.ts --testTimeout=600000",
37
+ "test:integration": "pnpm run test:integration:cli && pnpm run test:integration:web-vanilla && pnpm run test:integration:web-app && pnpm run test:integration:web-fullstack",
38
+ "ci": "pnpm run lint && pnpm run build && pnpm run test",
35
39
  "create-changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 0",
36
40
  "prepare": "husky"
37
41
  },
@@ -51,29 +55,28 @@
51
55
  },
52
56
  "homepage": "https://github.com/doberkofler/create-template-project#readme",
53
57
  "devDependencies": {
54
- "@commitlint/cli": "20.4.4",
55
- "@commitlint/config-conventional": "20.4.4",
58
+ "@commitlint/cli": "20.5.0",
59
+ "@commitlint/config-conventional": "20.5.0",
56
60
  "@types/cli-progress": "3.11.6",
57
- "@types/debug": "4.1.12",
58
- "@types/node": "25.5.0",
61
+ "@types/debug": "4.1.13",
59
62
  "@vitest/coverage-v8": "4.1.0",
60
- "conventional-changelog": "^7.2.0",
63
+ "conventional-changelog": "7.2.0",
61
64
  "conventional-changelog-angular": "8.3.0",
62
65
  "eslint-plugin-regexp": "3.1.0",
63
66
  "husky": "9.1.7",
64
- "oxlint": "1.55.0",
65
- "oxlint-tsgolint": "0.16.0",
66
- "pnpm": "^10.32.1",
67
+ "oxlint": "1.56.0",
68
+ "oxlint-tsgolint": "0.17.0",
69
+ "pnpm": "10.32.1",
67
70
  "prettier": "3.8.1",
68
- "tsdown": "0.21.2",
71
+ "tsdown": "0.21.4",
69
72
  "typescript": "5.9.3",
70
73
  "vitest": "4.1.0"
71
74
  },
72
75
  "dependencies": {
73
- "@clack/prompts": "^1.1.0",
76
+ "@clack/prompts": "1.1.0",
74
77
  "commander": "14.0.3",
75
78
  "debug": "4.4.3",
76
- "execa": "^9.6.1",
79
+ "execa": "9.6.1",
77
80
  "zod": "4.3.6"
78
81
  }
79
82
  }
@@ -1,8 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head><title>Server Template - Client</title></head>
4
- <body>
5
- <div id="root"></div>
6
- <script src="./dist/index.js"></script>
7
- </body>
8
- </html>
@@ -1,3 +0,0 @@
1
- import {defineConfig} from 'tsdown';
2
-
3
- export default defineConfig({entry: ['./src/main.tsx'], format: ['esm'], clean: true});
@@ -1,3 +0,0 @@
1
- import {defineConfig} from 'tsdown';
2
-
3
- export default defineConfig({entry: ['./src/index.ts'], format: ['esm'], clean: true});
@@ -1,42 +0,0 @@
1
- import path from "node:path";
2
- import { fileURLToPath } from "node:url";
3
- //#region src/templates/fullstack/index.ts
4
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
5
- const getFullstackTemplate = (_opts) => {
6
- return {
7
- name: "fullstack",
8
- dependencies: {
9
- react: "react",
10
- "react-dom": "react-dom",
11
- "@mui/material": "@mui/material",
12
- "@mui/icons-material": "@mui/icons-material",
13
- "@emotion/react": "@emotion/react",
14
- "@emotion/styled": "@emotion/styled",
15
- express: "express",
16
- "@trpc/server": "@trpc/server",
17
- "@trpc/client": "@trpc/client",
18
- "@trpc/react-query": "@trpc/react-query",
19
- "@tanstack/react-query": "@tanstack/react-query",
20
- zod: "zod",
21
- "react-router-dom": "react-router-dom",
22
- cors: "cors"
23
- },
24
- devDependencies: {
25
- "@types/react": "@types/react",
26
- "@types/react-dom": "@types/react-dom",
27
- "@types/express": "@types/express",
28
- "@types/cors": "@types/cors",
29
- "@playwright/test": "@playwright/test",
30
- tsdown: "tsdown"
31
- },
32
- scripts: {
33
- build: "npm run build --workspaces",
34
- dev: "npm run dev --workspaces",
35
- "test:e2e": "playwright test"
36
- },
37
- files: [],
38
- templateDir: path.resolve(__dirname, "files")
39
- };
40
- };
41
- //#endregion
42
- export { getFullstackTemplate };
@@ -1,17 +0,0 @@
1
- import express from 'express';
2
- import path from 'node:path';
3
- import {fileURLToPath} from 'node:url';
4
-
5
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
- const app = express();
7
- const port = process.env.PORT || 3000;
8
-
9
- app.use(express.static(path.join(__dirname, '../../frontend/dist')));
10
-
11
- app.get('/api/hello', (req, res) => {
12
- res.json({message: 'Hello from Express!'});
13
- });
14
-
15
- app.listen(port, () => {
16
- console.log(`Server running at http://localhost:${port}`);
17
- });