nuxt-bake 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/README.md +79 -0
  2. package/dist/helpers/constants.js +28 -0
  3. package/dist/helpers/git.js +32 -0
  4. package/dist/helpers/package-manager.js +77 -0
  5. package/dist/helpers/template.js +27 -0
  6. package/dist/helpers/utils.js +42 -0
  7. package/dist/index.js +96 -0
  8. package/eslint.config.ts +26 -0
  9. package/package.json +58 -0
  10. package/playwright.config.ts +6 -0
  11. package/src/helpers/git.ts +28 -0
  12. package/src/helpers/package-manager.ts +86 -0
  13. package/src/helpers/template.ts +35 -0
  14. package/src/helpers/utils.ts +83 -0
  15. package/src/index.ts +106 -0
  16. package/templates/base/.env.example +11 -0
  17. package/templates/base/README.md +58 -0
  18. package/templates/base/app/app.vue +20 -0
  19. package/templates/base/app/assets/styles.css +257 -0
  20. package/templates/base/app/components/dialog.vue +54 -0
  21. package/templates/base/app/components/navbar.vue +51 -0
  22. package/templates/base/app/components/toast.vue +116 -0
  23. package/templates/base/app/composables/use-session-monitor.ts +40 -0
  24. package/templates/base/app/composables/use-theme.ts +41 -0
  25. package/templates/base/app/composables/use-toast.ts +37 -0
  26. package/templates/base/app/error.vue +23 -0
  27. package/templates/base/app/layouts/default.vue +7 -0
  28. package/templates/base/app/pages/index.vue +53 -0
  29. package/templates/base/app/pages/sign-in.vue +39 -0
  30. package/templates/base/app/stores/user-store.ts +50 -0
  31. package/templates/base/app/utils/helpers.ts +42 -0
  32. package/templates/base/eslint.config.ts +74 -0
  33. package/templates/base/nuxt.config.ts +39 -0
  34. package/templates/base/package.json +39 -0
  35. package/templates/base/prisma/schema.prisma +30 -0
  36. package/templates/base/prisma.config.ts +12 -0
  37. package/templates/base/server/api/auth/[provider].ts +67 -0
  38. package/templates/base/server/api/user/index.delete.ts +8 -0
  39. package/templates/base/server/api/user/index.get.ts +10 -0
  40. package/templates/base/server/utils/auth.ts +48 -0
  41. package/templates/base/server/utils/db.ts +15 -0
  42. package/templates/base/server/utils/helpers.ts +14 -0
  43. package/templates/base/shared/types/auth.d.ts +31 -0
  44. package/templates/base/shared/types/globals.d.ts +15 -0
  45. package/templates/base/tsconfig.json +18 -0
  46. package/templates/with-i18n/app/app.vue +29 -0
  47. package/templates/with-i18n/app/components/navbar.vue +74 -0
  48. package/templates/with-i18n/app/error.vue +25 -0
  49. package/templates/with-i18n/app/pages/index.vue +53 -0
  50. package/templates/with-i18n/app/pages/sign-in.vue +39 -0
  51. package/templates/with-i18n/app/utils/i18n.config.ts +14 -0
  52. package/templates/with-i18n/app/utils/locales/en-US.json +50 -0
  53. package/templates/with-i18n/app/utils/locales/fr-FR.json +50 -0
  54. package/templates/with-i18n/nuxt.config.ts +51 -0
  55. package/templates/with-tests/app/pages/index.vue +51 -0
  56. package/templates/with-tests/nuxt.config.ts +31 -0
  57. package/templates/with-tests/playwright.config.ts +13 -0
  58. package/templates/with-tests/tests/e2e/e2e-hello.test.ts +8 -0
  59. package/templates/with-tests/tests/hello.test.ts +7 -0
  60. package/templates/with-tests/vitest.config.ts +16 -0
  61. package/tests/git.test.ts +54 -0
  62. package/tests/package-manager.test.ts +100 -0
  63. package/tests/template.test.ts +73 -0
  64. package/tests/utils.test.ts +155 -0
  65. package/tsconfig.json +13 -0
  66. package/vitest.config.ts +15 -0
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # nuxt-bake
2
+
3
+ A CLI tool to quickly scaffold a Nuxt.js starter project with modern tools and best practices.
4
+
5
+ ## Testing
6
+
7
+ The project includes unit and end-to-end tests set up with Vitest and Playwright. For development, you can run the tests using the following commands:
8
+
9
+ - To run unit tests with Vitest:
10
+
11
+ ```bash
12
+ npm run test
13
+ ```
14
+
15
+ - To check test coverage:
16
+
17
+ ```bash
18
+ npm run coverage
19
+ ```
20
+
21
+ - To run end-to-end tests with Playwright:
22
+
23
+ ```bash
24
+ npm run test:e2e
25
+ ```
26
+
27
+ ## Publishing the Package
28
+
29
+ The package includes release scripts that handle versioning and publishing:
30
+
31
+ ```bash
32
+ # For bug fixes (1.0.10 → 1.0.11)
33
+ npm run release:patch
34
+
35
+ # For new features (1.0.10 → 1.1.0)
36
+ npm run release:minor
37
+
38
+ # For breaking changes (1.0.10 → 2.0.0)
39
+ npm run release:major
40
+ ```
41
+
42
+ Each script will:
43
+
44
+ 1. Bump the version accordingly
45
+ 2. Publish to npm with public access
46
+
47
+ ## Usage
48
+
49
+ Create a new project using npx:
50
+
51
+ ```bash
52
+ npx nuxt-bake
53
+
54
+ # or install globally:
55
+ npm install -g nuxt-bake
56
+ nuxt-bake
57
+ ```
58
+
59
+ You’ll be prompted to choose:
60
+
61
+ - Project name
62
+ - Features (i18n, testing, etc.)
63
+ - Install dependencies?
64
+ - Package manager (npm, pnpm, yarn)
65
+ - Git repository initialization?
66
+
67
+ After setup, you’ll have a fresh Nuxt.js project ready to go!
68
+
69
+ ## Contact
70
+
71
+ Feel free to reach out to discuss collaboration opportunities or to say hello!
72
+
73
+ - [**My Email**](mailto:matheus.felipe.19rt@gmail.com)
74
+ - [**My LinkedIn Profile**](https://www.linkedin.com/in/matheus-mortari-19rt)
75
+ - [**My GitHub Profile**](https://github.com/matimortari)
76
+
77
+ ## License
78
+
79
+ This project is licensed under the [**MIT License**](https://github.com/matimortari/nuxt-bake/blob/main/LICENSE).
@@ -0,0 +1,28 @@
1
+ export const REPO_URL = "https://github.com/matimortari/nuxtjs-starter.git";
2
+ export const PRESET_EXTRA_SCRIPTS = {
3
+ "standard": {},
4
+ "with-i18n": {},
5
+ "with-tests": {
6
+ "test": "vitest",
7
+ "test:e2e": "playwright test",
8
+ "coverage": "vitest --coverage",
9
+ },
10
+ };
11
+ export const PRESET_EXTRA_PACKAGES = {
12
+ "standard": {},
13
+ "with-i18n": {
14
+ dependencies: {
15
+ "@nuxtjs/i18n": "10.2.1",
16
+ },
17
+ },
18
+ "with-tests": {
19
+ devDependencies: {
20
+ "@nuxt/test-utils": "3.19.2",
21
+ "@vitest/coverage-v8": "4.0.15",
22
+ "@vue/test-utils": "3.20.0",
23
+ "happy-dom": "13.7.6",
24
+ "@playwright/test": "1.57.0",
25
+ "vitest": "4.0.15",
26
+ },
27
+ },
28
+ };
@@ -0,0 +1,32 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ export function cloneRepoToTemp(repoUrl) {
5
+ const tmpDir = path.join(os.tmpdir(), `nuxtjs-starter-${Date.now()}`);
6
+ const result = spawnSync("git", ["clone", "--depth=1", "--quiet", repoUrl, tmpDir], {
7
+ stdio: "pipe",
8
+ shell: true,
9
+ });
10
+ if (result.error) {
11
+ console.error("Failed to get project from remote:", result.error.message);
12
+ return null;
13
+ }
14
+ if (result.status !== 0) {
15
+ console.error("Failed to clone repository");
16
+ return null;
17
+ }
18
+ return tmpDir;
19
+ }
20
+ export function promptAndInitGit(targetDir) {
21
+ const result = spawnSync("git", ["init", "--quiet"], {
22
+ cwd: targetDir,
23
+ stdio: "pipe",
24
+ shell: true,
25
+ });
26
+ if (result.error) {
27
+ console.error("Failed to initialize Git:", result.error.message);
28
+ }
29
+ else if (result.status !== 0) {
30
+ console.error("Git init failed");
31
+ }
32
+ }
@@ -0,0 +1,77 @@
1
+ import { execSync } from "node:child_process";
2
+ import path from "node:path";
3
+ import fs from "fs-extra";
4
+ import inquirer from "inquirer";
5
+ import ora from "ora";
6
+ function mergeObjects(base = {}, extra = {}) {
7
+ return { ...base, ...extra };
8
+ }
9
+ export function createPackageManagerCommands(pkgManager) {
10
+ switch (pkgManager) {
11
+ case "yarn":
12
+ return {
13
+ name: "yarn",
14
+ installCmd: "yarn install",
15
+ runScript: (script) => `yarn ${script}`,
16
+ };
17
+ case "pnpm":
18
+ return {
19
+ name: "pnpm",
20
+ installCmd: "pnpm install",
21
+ runScript: (script) => `pnpm ${script}`,
22
+ };
23
+ case "bun":
24
+ return {
25
+ name: "bun",
26
+ installCmd: "bun install",
27
+ runScript: (script) => `bun run ${script}`,
28
+ };
29
+ case "npm":
30
+ default:
31
+ return {
32
+ name: "npm",
33
+ installCmd: "npm install",
34
+ runScript: (script) => `npm run ${script}`,
35
+ };
36
+ }
37
+ }
38
+ export async function promptForPackageManager() {
39
+ const { pkgManager } = await inquirer.prompt({
40
+ type: "list",
41
+ name: "pkgManager",
42
+ message: "Which package manager do you want to use?",
43
+ choices: [
44
+ { name: "npm", value: "npm" },
45
+ { name: "yarn", value: "yarn" },
46
+ { name: "pnpm", value: "pnpm" },
47
+ { name: "bun", value: "bun" },
48
+ ],
49
+ default: "npm",
50
+ });
51
+ return createPackageManagerCommands(pkgManager);
52
+ }
53
+ export async function installDependencies(targetDir, pkgManager) {
54
+ const spinner = ora("Installing dependencies...").start();
55
+ try {
56
+ spinner.stop();
57
+ execSync(pkgManager.installCmd, { cwd: targetDir, stdio: "inherit" });
58
+ execSync(pkgManager.runScript("lint:fix"), { cwd: targetDir, stdio: "ignore" });
59
+ spinner.succeed("Dependencies installed!");
60
+ }
61
+ catch (err) {
62
+ spinner.fail("Failed to install dependencies");
63
+ throw err;
64
+ }
65
+ }
66
+ export async function updatePackageJson(rootTemplateDir, targetDir, preset, extras) {
67
+ const rootPkgPath = path.join(rootTemplateDir, "package.json");
68
+ const targetPkgPath = path.join(targetDir, "package.json");
69
+ const rootPkg = JSON.parse(await fs.readFile(rootPkgPath, "utf8"));
70
+ const mergedPkg = {
71
+ ...rootPkg,
72
+ dependencies: mergeObjects(rootPkg.dependencies, extras.dependencies),
73
+ devDependencies: mergeObjects(rootPkg.devDependencies, extras.devDependencies),
74
+ scripts: mergeObjects(rootPkg.scripts, extras.scripts),
75
+ };
76
+ await fs.writeFile(targetPkgPath, JSON.stringify(mergedPkg, null, 2), "utf8");
77
+ }
@@ -0,0 +1,27 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ export async function copyRootTemplate(tmpDir, targetDir) {
4
+ const rootTemplateDir = path.join(tmpDir, "package", "templates", "base");
5
+ if (!(await fs.pathExists(rootTemplateDir))) {
6
+ throw new Error(`Root template directory "${rootTemplateDir}" not found in the repo.`);
7
+ }
8
+ await fs.copy(rootTemplateDir, targetDir);
9
+ return rootTemplateDir;
10
+ }
11
+ export async function copyPresetFiles(tmpDir, preset, targetDir) {
12
+ const presetDir = path.join(tmpDir, "package", "templates", preset);
13
+ if (!(await fs.pathExists(presetDir))) {
14
+ throw new Error(`Preset directory "${presetDir}" not found in the repo.`);
15
+ }
16
+ const presetRootFiles = await fs.readdir(presetDir);
17
+ for (const file of presetRootFiles) {
18
+ if (file === "app") {
19
+ continue;
20
+ }
21
+ await fs.copy(path.join(presetDir, file), path.join(targetDir, file), { overwrite: true });
22
+ }
23
+ const presetAppDir = path.join(presetDir, "app");
24
+ if (await fs.pathExists(presetAppDir)) {
25
+ await fs.copy(presetAppDir, path.join(targetDir, "app"), { overwrite: true });
26
+ }
27
+ }
@@ -0,0 +1,42 @@
1
+ import path from "node:path";
2
+ import fs from "fs-extra";
3
+ import inquirer from "inquirer";
4
+ export function getProjectNameFromArgs() {
5
+ const args = process.argv.slice(2);
6
+ const nIndex = args.findIndex(a => a === "-n" || a === "--name");
7
+ if (nIndex !== -1 && args.length > nIndex + 1) {
8
+ return args[nIndex + 1];
9
+ }
10
+ return null;
11
+ }
12
+ export async function promptForProjectName() {
13
+ let projectName = getProjectNameFromArgs();
14
+ if (!projectName) {
15
+ const { projectName: answerName } = await inquirer.prompt({
16
+ type: "input",
17
+ name: "projectName",
18
+ message: "Enter your new project folder name:",
19
+ default: "my-nuxt-app",
20
+ validate: input => (input ? true : "Project folder name cannot be empty"),
21
+ });
22
+ projectName = answerName;
23
+ }
24
+ return projectName;
25
+ }
26
+ export async function validateTargetDirectory(projectName) {
27
+ const targetDir = path.resolve(process.cwd(), projectName);
28
+ const exists = await fs.pathExists(targetDir);
29
+ if (exists) {
30
+ const { overwrite } = await inquirer.prompt({
31
+ type: "confirm",
32
+ name: "overwrite",
33
+ message: `Directory "${projectName}" already exists. Overwrite?`,
34
+ default: false,
35
+ });
36
+ if (!overwrite) {
37
+ return null;
38
+ }
39
+ await fs.remove(targetDir);
40
+ }
41
+ return targetDir;
42
+ }
package/dist/index.js ADDED
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/env node
2
+ import fs from "fs-extra";
3
+ import inquirer from "inquirer";
4
+ import ora from "ora";
5
+ import { PRESET_EXTRA_PACKAGES, PRESET_EXTRA_SCRIPTS, REPO_URL } from "./helpers/constants.js";
6
+ import { cloneRepoToTemp, promptAndInitGit } from "./helpers/git.js";
7
+ import { installDependencies, promptForPackageManager, updatePackageJson } from "./helpers/package-manager.js";
8
+ import { copyPresetFiles, copyRootTemplate } from "./helpers/template.js";
9
+ import { promptForProjectName, validateTargetDirectory } from "./helpers/utils.js";
10
+ async function run() {
11
+ let tmpDir;
12
+ try {
13
+ const projectName = await promptForProjectName();
14
+ if (!projectName) {
15
+ console.log("\nProject name is required. Please provide a valid name.\n");
16
+ process.exit(1);
17
+ }
18
+ const targetDir = await validateTargetDirectory(projectName);
19
+ if (!targetDir) {
20
+ console.log(`\nFolder "${projectName}" already exists. Please choose another name or remove it.\n`);
21
+ process.exit(1);
22
+ }
23
+ const spinnerClone = ora("Creating project root...").start();
24
+ tmpDir = cloneRepoToTemp(REPO_URL);
25
+ if (!tmpDir) {
26
+ spinnerClone.fail("Failed to clone repository.");
27
+ process.exit(1);
28
+ }
29
+ const rootTemplateDir = await copyRootTemplate(tmpDir, targetDir);
30
+ spinnerClone.succeed();
31
+ const { preset } = await inquirer.prompt({
32
+ type: "list",
33
+ name: "preset",
34
+ message: "Select a preset:",
35
+ choices: [
36
+ { name: "Standard", value: "standard" },
37
+ { name: "With i18n", value: "with-i18n" },
38
+ { name: "With Tests", value: "with-tests" },
39
+ ],
40
+ });
41
+ await copyPresetFiles(tmpDir, preset, targetDir);
42
+ await updatePackageJson(rootTemplateDir, targetDir, preset, {
43
+ dependencies: PRESET_EXTRA_PACKAGES[preset]?.dependencies || {},
44
+ devDependencies: PRESET_EXTRA_PACKAGES[preset]?.devDependencies || {},
45
+ scripts: PRESET_EXTRA_SCRIPTS[preset] || {},
46
+ });
47
+ const { installDeps } = await inquirer.prompt({
48
+ type: "confirm",
49
+ name: "installDeps",
50
+ message: "Install dependencies now?",
51
+ default: true,
52
+ });
53
+ if (installDeps) {
54
+ const pkgManager = await promptForPackageManager();
55
+ const spinnerInstall = ora("Installing dependencies...").start();
56
+ await installDependencies(targetDir, pkgManager);
57
+ spinnerInstall.succeed("Dependencies installed!");
58
+ }
59
+ const { initGit } = await inquirer.prompt({
60
+ type: "confirm",
61
+ name: "initGit",
62
+ message: "Initialize a Git repository?",
63
+ default: true,
64
+ });
65
+ if (initGit) {
66
+ const spinnerGit = ora("Initializing Git repo...").start();
67
+ promptAndInitGit(targetDir);
68
+ spinnerGit.succeed(`Git repository initialized for ${projectName}`);
69
+ }
70
+ console.log(`
71
+ ✅ Project setup complete!
72
+
73
+ Next steps:
74
+ 1. Navigate to your project:
75
+ cd ${projectName}
76
+
77
+ 2. Migrate or push database schemas:
78
+ npm run db:migrate
79
+ npm run db:push
80
+ npm run db:generate
81
+
82
+ 3. Start the development server:
83
+ npm run dev
84
+ `);
85
+ }
86
+ catch (error) {
87
+ console.error("Error:", error);
88
+ process.exit(1);
89
+ }
90
+ finally {
91
+ if (tmpDir && await fs.pathExists(tmpDir)) {
92
+ await fs.remove(tmpDir);
93
+ }
94
+ }
95
+ }
96
+ run();
@@ -0,0 +1,26 @@
1
+ import antfu from "@antfu/eslint-config"
2
+
3
+ export default antfu({
4
+ typescript: true,
5
+ jsonc: true,
6
+ formatters: { markdown: true },
7
+ stylistic: {
8
+ indent: 2,
9
+ quotes: "double",
10
+ semi: false,
11
+ },
12
+ rules: {
13
+ "no-new": "off",
14
+ "no-undef": "off",
15
+ "no-alert": "off",
16
+ "no-console": "off",
17
+ "node/prefer-global/process": "off",
18
+ "curly": ["error", "all"],
19
+ "object-curly-newline": ["error", {
20
+ ObjectExpression: { multiline: false, consistent: true },
21
+ ObjectPattern: { multiline: false, consistent: true },
22
+ ImportDeclaration: { multiline: false, consistent: true },
23
+ ExportDeclaration: { multiline: false, consistent: true },
24
+ }],
25
+ },
26
+ })
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "nuxt-bake",
3
+ "type": "module",
4
+ "version": "1.0.1",
5
+ "description": "A CLI tool to quickly scaffold a Nuxt.js starter project",
6
+ "author": "Matheus Mortari <matheus.felipe.19rt@gmail.com>",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/matimortari/nuxt-bake.git",
11
+ "directory": "package"
12
+ },
13
+ "keywords": [
14
+ "starter",
15
+ "template",
16
+ "nuxt",
17
+ "nuxt4",
18
+ "nuxtjs",
19
+ "vue",
20
+ "typescript",
21
+ "tailwindcss"
22
+ ],
23
+ "bin": {
24
+ "nuxt-bake": "dist/index.js"
25
+ },
26
+ "engines": {
27
+ "node": ">=18"
28
+ },
29
+ "scripts": {
30
+ "dev": "tsc --watch",
31
+ "build": "tsc",
32
+ "start": "node dist/index.js",
33
+ "typecheck": "tsc --noEmit",
34
+ "lint": "eslint .",
35
+ "lint:fix": "eslint . --fix",
36
+ "clean": "rimraf dist my-nuxt-app",
37
+ "test": "vitest run",
38
+ "coverage": "vitest --coverage",
39
+ "test:e2e": "playwright test",
40
+ "release:patch": "npm version patch && npm publish --access public",
41
+ "release:minor": "npm version minor && npm publish --access public",
42
+ "release:major": "npm version major && npm publish --access public"
43
+ },
44
+ "dependencies": {
45
+ "chalk": "5.6.2",
46
+ "figures": "6.1.0",
47
+ "fs-extra": "11.3.3",
48
+ "inquirer": "13.3.0",
49
+ "ora": "9.3.0",
50
+ "rimraf": "6.1.3"
51
+ },
52
+ "devDependencies": {
53
+ "@playwright/test": "1.58.2",
54
+ "@types/fs-extra": "11.0.4",
55
+ "@vitest/coverage-v8": "4.0.18",
56
+ "vitest": "4.0.18"
57
+ }
58
+ }
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from "@playwright/test"
2
+
3
+ export default defineConfig({
4
+ outputDir: "./tests/e2e/test-results",
5
+ testDir: "./tests/e2e",
6
+ })
@@ -0,0 +1,28 @@
1
+ import { spawnSync } from "node:child_process"
2
+ import os from "node:os"
3
+ import path from "node:path"
4
+
5
+ export function cloneRepoToTemp(repoUrl: string) {
6
+ const tmpDir = path.join(os.tmpdir(), `nuxt-bake-${Date.now()}`)
7
+ const result = spawnSync("git", ["clone", "--depth=1", "--quiet", repoUrl, tmpDir], { stdio: "pipe", shell: true })
8
+ if (result.error) {
9
+ console.error("Failed to get project from remote:", result.error.message)
10
+ return null
11
+ }
12
+ if (result.status !== 0) {
13
+ console.error("Failed to clone repository")
14
+ return null
15
+ }
16
+
17
+ return tmpDir
18
+ }
19
+
20
+ export function promptAndInitGit(targetDir: string) {
21
+ const result = spawnSync("git", ["init", "--quiet"], { cwd: targetDir, stdio: "pipe", shell: true })
22
+ if (result.error) {
23
+ console.error("Failed to initialize Git:", result.error.message)
24
+ }
25
+ else if (result.status !== 0) {
26
+ console.error("Git init failed")
27
+ }
28
+ }
@@ -0,0 +1,86 @@
1
+ import { execSync } from "node:child_process"
2
+ import path from "node:path"
3
+ import fs from "fs-extra"
4
+ import inquirer from "inquirer"
5
+ import ora from "ora"
6
+
7
+ function mergeObjects(base = {}, extra = {}) {
8
+ return { ...base, ...extra }
9
+ }
10
+
11
+ export function createPackageManagerCommands(pkgManager: string) {
12
+ switch (pkgManager) {
13
+ case "yarn":
14
+ return {
15
+ name: "yarn",
16
+ installCmd: "yarn install",
17
+ runScript: (script: string) => `yarn ${script}`,
18
+ }
19
+ case "pnpm":
20
+ return {
21
+ name: "pnpm",
22
+ installCmd: "pnpm install",
23
+ runScript: (script: string) => `pnpm ${script}`,
24
+ }
25
+ case "bun":
26
+ return {
27
+ name: "bun",
28
+ installCmd: "bun install",
29
+ runScript: (script: string) => `bun run ${script}`,
30
+ }
31
+ case "npm":
32
+ default:
33
+ return {
34
+ name: "npm",
35
+ installCmd: "npm install",
36
+ runScript: (script: string) => `npm run ${script}`,
37
+ }
38
+ }
39
+ }
40
+
41
+ export async function promptForPackageManager() {
42
+ const { pkgManager } = await inquirer.prompt({
43
+ type: "list",
44
+ name: "pkgManager",
45
+ message: "Which package manager do you want to use?",
46
+ choices: [
47
+ { name: "npm", value: "npm" },
48
+ { name: "yarn", value: "yarn" },
49
+ { name: "pnpm", value: "pnpm" },
50
+ { name: "bun", value: "bun" },
51
+ ],
52
+ default: "npm",
53
+ })
54
+
55
+ return createPackageManagerCommands(pkgManager)
56
+ }
57
+
58
+ export async function installDependencies(targetDir: string, pkgManager: any) {
59
+ const spinner = ora("Installing dependencies...").start()
60
+
61
+ try {
62
+ spinner.stop()
63
+ execSync(pkgManager.installCmd, { cwd: targetDir, stdio: "inherit" })
64
+ execSync(pkgManager.runScript("lint:fix"), { cwd: targetDir, stdio: "ignore" })
65
+ spinner.succeed("Dependencies installed!")
66
+ }
67
+ catch (err: any) {
68
+ spinner.fail("Failed to install dependencies")
69
+ throw err
70
+ }
71
+ }
72
+
73
+ export async function updatePackageJson(rootTemplateDir: string, targetDir: string, preset: string, extras: any) {
74
+ const rootPkgPath = path.join(rootTemplateDir, "package.json")
75
+ const targetPkgPath = path.join(targetDir, "package.json")
76
+ const rootPkg = JSON.parse(await fs.readFile(rootPkgPath, "utf8"))
77
+
78
+ const mergedPkg = {
79
+ ...rootPkg,
80
+ dependencies: mergeObjects(rootPkg.dependencies, extras.dependencies),
81
+ devDependencies: mergeObjects(rootPkg.devDependencies, extras.devDependencies),
82
+ scripts: mergeObjects(rootPkg.scripts, extras.scripts),
83
+ }
84
+
85
+ await fs.writeFile(targetPkgPath, JSON.stringify(mergedPkg, null, 2), "utf8")
86
+ }
@@ -0,0 +1,35 @@
1
+ import type { Preset } from "./utils"
2
+ import path from "node:path"
3
+ import fs from "fs-extra"
4
+
5
+ export async function copyRootTemplate(tmpDir: string, targetDir: string) {
6
+ const rootTemplateDir = path.join(tmpDir, "package", "templates", "base")
7
+ if (!(await fs.pathExists(rootTemplateDir))) {
8
+ throw new Error(`Root template directory "${rootTemplateDir}" not found in the repo.`)
9
+ }
10
+
11
+ await fs.copy(rootTemplateDir, targetDir)
12
+
13
+ return rootTemplateDir
14
+ }
15
+
16
+ export async function copyPresetFiles(tmpDir: string, preset: Preset, targetDir: string) {
17
+ const presetDir = path.join(tmpDir, "package", "templates", preset)
18
+ if (!(await fs.pathExists(presetDir))) {
19
+ throw new Error(`Preset directory "${presetDir}" not found in the repo.`)
20
+ }
21
+
22
+ const presetRootFiles = await fs.readdir(presetDir)
23
+ for (const file of presetRootFiles) {
24
+ if (file === "app") {
25
+ continue
26
+ }
27
+
28
+ await fs.copy(path.join(presetDir, file), path.join(targetDir, file), { overwrite: true })
29
+ }
30
+
31
+ const presetAppDir = path.join(presetDir, "app")
32
+ if (await fs.pathExists(presetAppDir)) {
33
+ await fs.copy(presetAppDir, path.join(targetDir, "app"), { overwrite: true })
34
+ }
35
+ }