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.
- package/README.md +79 -0
- package/dist/helpers/constants.js +28 -0
- package/dist/helpers/git.js +32 -0
- package/dist/helpers/package-manager.js +77 -0
- package/dist/helpers/template.js +27 -0
- package/dist/helpers/utils.js +42 -0
- package/dist/index.js +96 -0
- package/eslint.config.ts +26 -0
- package/package.json +58 -0
- package/playwright.config.ts +6 -0
- package/src/helpers/git.ts +28 -0
- package/src/helpers/package-manager.ts +86 -0
- package/src/helpers/template.ts +35 -0
- package/src/helpers/utils.ts +83 -0
- package/src/index.ts +106 -0
- package/templates/base/.env.example +11 -0
- package/templates/base/README.md +58 -0
- package/templates/base/app/app.vue +20 -0
- package/templates/base/app/assets/styles.css +257 -0
- package/templates/base/app/components/dialog.vue +54 -0
- package/templates/base/app/components/navbar.vue +51 -0
- package/templates/base/app/components/toast.vue +116 -0
- package/templates/base/app/composables/use-session-monitor.ts +40 -0
- package/templates/base/app/composables/use-theme.ts +41 -0
- package/templates/base/app/composables/use-toast.ts +37 -0
- package/templates/base/app/error.vue +23 -0
- package/templates/base/app/layouts/default.vue +7 -0
- package/templates/base/app/pages/index.vue +53 -0
- package/templates/base/app/pages/sign-in.vue +39 -0
- package/templates/base/app/stores/user-store.ts +50 -0
- package/templates/base/app/utils/helpers.ts +42 -0
- package/templates/base/eslint.config.ts +74 -0
- package/templates/base/nuxt.config.ts +39 -0
- package/templates/base/package.json +39 -0
- package/templates/base/prisma/schema.prisma +30 -0
- package/templates/base/prisma.config.ts +12 -0
- package/templates/base/server/api/auth/[provider].ts +67 -0
- package/templates/base/server/api/user/index.delete.ts +8 -0
- package/templates/base/server/api/user/index.get.ts +10 -0
- package/templates/base/server/utils/auth.ts +48 -0
- package/templates/base/server/utils/db.ts +15 -0
- package/templates/base/server/utils/helpers.ts +14 -0
- package/templates/base/shared/types/auth.d.ts +31 -0
- package/templates/base/shared/types/globals.d.ts +15 -0
- package/templates/base/tsconfig.json +18 -0
- package/templates/with-i18n/app/app.vue +29 -0
- package/templates/with-i18n/app/components/navbar.vue +74 -0
- package/templates/with-i18n/app/error.vue +25 -0
- package/templates/with-i18n/app/pages/index.vue +53 -0
- package/templates/with-i18n/app/pages/sign-in.vue +39 -0
- package/templates/with-i18n/app/utils/i18n.config.ts +14 -0
- package/templates/with-i18n/app/utils/locales/en-US.json +50 -0
- package/templates/with-i18n/app/utils/locales/fr-FR.json +50 -0
- package/templates/with-i18n/nuxt.config.ts +51 -0
- package/templates/with-tests/app/pages/index.vue +51 -0
- package/templates/with-tests/nuxt.config.ts +31 -0
- package/templates/with-tests/playwright.config.ts +13 -0
- package/templates/with-tests/tests/e2e/e2e-hello.test.ts +8 -0
- package/templates/with-tests/tests/hello.test.ts +7 -0
- package/templates/with-tests/vitest.config.ts +16 -0
- package/tests/git.test.ts +54 -0
- package/tests/package-manager.test.ts +100 -0
- package/tests/template.test.ts +73 -0
- package/tests/utils.test.ts +155 -0
- package/tsconfig.json +13 -0
- 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();
|
package/eslint.config.ts
ADDED
|
@@ -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,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
|
+
}
|