vue-art-e4n 0.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/bin/cli.js +30 -0
- package/config/architectures.js +54 -0
- package/config/featuresConfig.js +13 -0
- package/config/packages.js +55 -0
- package/config/rules.js +16 -0
- package/core/createPackageJson.js +131 -0
- package/core/createProjectStructure.js +1185 -0
- package/core/featureRules.js +35 -0
- package/core/installPackages.js +92 -0
- package/core/runCLI.js +67 -0
- package/core/selectArchitecture.js +89 -0
- package/core/steps/collectProjectConfig.js +103 -0
- package/core/steps/finalizeProject.js +24 -0
- package/core/steps/reviewAndConfirm.js +242 -0
- package/core/utils/getPackagesList.js +29 -0
- package/helpers/prompts.js +47 -0
- package/helpers/selectArchitecture.js +58 -0
- package/package.json +31 -0
- package/templates/Vue/Hello/331/214World.vue +54 -0
- package/templates/Vue/assets/base.css +86 -0
- package/templates/Vue/assets/logo.svg +1 -0
- package/templates/Vue/assets/main.css +35 -0
- package/templates/Vue/components/E4N.vue +69 -0
- package/templates/Vue/components/TheWelcome.vue +95 -0
- package/templates/Vue/components/WelcomeItem.vue +87 -0
- package/templates/Vue/components/icons/IconCommunity.vue +7 -0
- package/templates/Vue/components/icons/IconDocumentation.vue +7 -0
- package/templates/Vue/components/icons/IconEcosystem.vue +7 -0
- package/templates/Vue/components/icons/IconSupport.vue +7 -0
- package/templates/Vue/components/icons/IconTooling.vue +19 -0
- package/templates/Vuetify/HelloWorld.vue +88 -0
- package/templates/assets/E4N.vue +69 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { EXCLUSIVE_RULES, STORE_ARCHITECTURE_RULES } from "../config/rules.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Disables feature choices based on exclusive selection rules.
|
|
5
|
+
*
|
|
6
|
+
* @param {string[]} selectedFeatures - Features currently selected by the user.
|
|
7
|
+
* @param {Array<{ value: string, label: string, disabled?: string }>} featureChoices - All available feature choices.
|
|
8
|
+
* @returns {Array<{ value: string, label: string, disabled?: string }>} Updated feature choices with disabled reasons applied.
|
|
9
|
+
*/
|
|
10
|
+
export function applyExclusiveRules(selectedFeatures, featureChoices) {
|
|
11
|
+
return featureChoices.map((choice) => {
|
|
12
|
+
const copy = { ...choice };
|
|
13
|
+
EXCLUSIVE_RULES.forEach((rule) => {
|
|
14
|
+
if (
|
|
15
|
+
selectedFeatures.includes(rule.ifSelected) &&
|
|
16
|
+
rule.disable === choice.value
|
|
17
|
+
) {
|
|
18
|
+
copy.disabled = rule.reason;
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
return copy;
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Filters architectures that are forbidden for a specific store.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} store - The selected store type.
|
|
28
|
+
* @param {string[]} architectures - List of available architectures.
|
|
29
|
+
* @returns {string[]} Filtered architectures allowed for the store.
|
|
30
|
+
*/
|
|
31
|
+
export function filterArchitecturesByStore(store, architectures) {
|
|
32
|
+
const rule = STORE_ARCHITECTURE_RULES.find((r) => r.store === store);
|
|
33
|
+
if (!rule) return architectures;
|
|
34
|
+
return architectures.filter((a) => !rule.forbiddenArchitectures.includes(a));
|
|
35
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { execa } from "execa";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { createPackageJson } from "./createPackageJson.js";
|
|
5
|
+
/**
|
|
6
|
+
* Installs npm dependencies using selected package manager.
|
|
7
|
+
* Handles Ctrl+C interruption gracefully.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} options
|
|
10
|
+
* @param {string} options.projectDir
|
|
11
|
+
* @param {"npm"|"yarn"|"pnpm"} options.packageManager
|
|
12
|
+
* @param {string[]} options.features
|
|
13
|
+
* @param {string} options.framework
|
|
14
|
+
*
|
|
15
|
+
* @returns {Promise<void>}
|
|
16
|
+
*/
|
|
17
|
+
export async function installPackages({
|
|
18
|
+
projectDir,
|
|
19
|
+
framework,
|
|
20
|
+
features,
|
|
21
|
+
packageManager,
|
|
22
|
+
projectName,
|
|
23
|
+
}) {
|
|
24
|
+
const pkgPath = path.join(projectDir, "package.json");
|
|
25
|
+
if (!fs.existsSync(pkgPath)) {
|
|
26
|
+
const { CORE_PACKAGES, PACKAGE_MAP } = await import(
|
|
27
|
+
"../config/packages.js"
|
|
28
|
+
);
|
|
29
|
+
const packages = new Set();
|
|
30
|
+
CORE_PACKAGES[framework]?.forEach((p) => packages.add(p));
|
|
31
|
+
features.forEach((f) => {
|
|
32
|
+
const pkgList = PACKAGE_MAP[framework]?.[f];
|
|
33
|
+
if (pkgList) pkgList.forEach((p) => packages.add(p));
|
|
34
|
+
});
|
|
35
|
+
createPackageJson({
|
|
36
|
+
projectDir,
|
|
37
|
+
projectName,
|
|
38
|
+
framework,
|
|
39
|
+
features,
|
|
40
|
+
packages: [...packages],
|
|
41
|
+
});
|
|
42
|
+
console.log("📝 Created package.json");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const pkgContent = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
46
|
+
const hasDependencies =
|
|
47
|
+
(pkgContent.dependencies &&
|
|
48
|
+
Object.keys(pkgContent.dependencies).length > 0) ||
|
|
49
|
+
(pkgContent.devDependencies &&
|
|
50
|
+
Object.keys(pkgContent.devDependencies).length > 0);
|
|
51
|
+
|
|
52
|
+
if (!hasDependencies) {
|
|
53
|
+
console.log("No packages to install.");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log(`\n📦 Installing packages with ${packageManager}...\n`);
|
|
58
|
+
|
|
59
|
+
let args = [];
|
|
60
|
+
if (packageManager === "npm") args = ["install"];
|
|
61
|
+
if (packageManager === "yarn") args = ["install"];
|
|
62
|
+
if (packageManager === "pnpm") args = ["install"];
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const subprocess = execa(packageManager, args, {
|
|
66
|
+
cwd: projectDir,
|
|
67
|
+
stdio: "inherit",
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
await subprocess;
|
|
71
|
+
console.log("\n✅ Packages installed successfully!\n");
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (
|
|
74
|
+
error.signal === "SIGINT" ||
|
|
75
|
+
error.code === "SIGINT" ||
|
|
76
|
+
error.exitCode === 130 ||
|
|
77
|
+
error.code === 130 ||
|
|
78
|
+
error.message?.includes("SIGINT") ||
|
|
79
|
+
error.message?.includes("canceled") ||
|
|
80
|
+
error.message?.includes("User force closed")
|
|
81
|
+
) {
|
|
82
|
+
const sigintError = new Error(
|
|
83
|
+
"Installation interrupted by user (SIGINT)"
|
|
84
|
+
);
|
|
85
|
+
sigintError.signal = "SIGINT";
|
|
86
|
+
sigintError.code = "SIGINT";
|
|
87
|
+
sigintError.exitCode = 130;
|
|
88
|
+
throw sigintError;
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
}
|
package/core/runCLI.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { collectProjectConfig } from "./steps/collectProjectConfig.js";
|
|
4
|
+
import { reviewAndConfirm } from "./steps/reviewAndConfirm.js";
|
|
5
|
+
import { createProjectStructure } from "./createProjectStructure.js";
|
|
6
|
+
import { createPackageJson } from "./createPackageJson.js";
|
|
7
|
+
import { installPackages } from "./installPackages.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Main CLI workflow controller.
|
|
11
|
+
*
|
|
12
|
+
* Responsibilities:
|
|
13
|
+
* - Collect user configuration
|
|
14
|
+
* - Allow review & editing
|
|
15
|
+
* - Generate project structure
|
|
16
|
+
* - Generate package.json
|
|
17
|
+
* - Optionally install dependencies
|
|
18
|
+
*
|
|
19
|
+
* @returns {Promise<void>}
|
|
20
|
+
*/
|
|
21
|
+
export async function runCLI() {
|
|
22
|
+
try {
|
|
23
|
+
const config = await collectProjectConfig();
|
|
24
|
+
|
|
25
|
+
const confirmed = await reviewAndConfirm(config);
|
|
26
|
+
if (!confirmed) {
|
|
27
|
+
console.log(chalk.yellow("Project creation canceled."));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const projectDir = path.resolve(process.cwd(), config.projectName);
|
|
32
|
+
|
|
33
|
+
createProjectStructure(config);
|
|
34
|
+
|
|
35
|
+
createPackageJson({
|
|
36
|
+
projectDir,
|
|
37
|
+
...config,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
console.log("📝 Created package.json");
|
|
41
|
+
|
|
42
|
+
if (confirmed.installNodeModules) {
|
|
43
|
+
await installPackages({
|
|
44
|
+
projectDir,
|
|
45
|
+
...config,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(chalk.green("\n✨ Project created successfully!\n"));
|
|
50
|
+
const pm = confirmed.packageManager || "npm";
|
|
51
|
+
const pmCommand = pm === "yarn" ? "yarn" : pm === "pnpm" ? "pnpm" : "npm";
|
|
52
|
+
|
|
53
|
+
console.log(chalk.cyan("📋 Next steps:\n"));
|
|
54
|
+
console.log(chalk.cyan(` cd ${confirmed.projectName}`));
|
|
55
|
+
if (!confirmed.installNodeModules) {
|
|
56
|
+
console.log(chalk.yellow(` ${pmCommand} install`));
|
|
57
|
+
}
|
|
58
|
+
console.log(chalk.cyan(` ${pmCommand} run dev\n`));
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (error?.name === "ExitPromptError") {
|
|
61
|
+
console.log(chalk.yellow("\n⛔ Operation canceled.\n"));
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompts the user to select a project architecture.
|
|
3
|
+
* Filters available architectures based on store and recommends an optimal choice.
|
|
4
|
+
*
|
|
5
|
+
* @param {Object} options
|
|
6
|
+
* @param {"vue"|"vuetify"|"nuxt"} options.framework - Selected framework.
|
|
7
|
+
* @param {string} options.scale - Project scale (e.g., small, medium, large).
|
|
8
|
+
* @param {string} options.lifetime - Expected project lifetime.
|
|
9
|
+
* @param {string[]} options.features - Selected project features.
|
|
10
|
+
* @param {Record<string, string[]>} options.ARCHITECTURES - Available architectures per framework.
|
|
11
|
+
* @param {function} options.filterArchitecturesByStore - Function to filter architectures by store.
|
|
12
|
+
* @param {function} options.getRecommendedArchitecture - Function returning recommended architecture.
|
|
13
|
+
* @param {function} options.confirmPrompt - Async function to prompt user for yes/no confirmation.
|
|
14
|
+
* @param {function} options.singleChoiceCheckbox - Async function to prompt user to select a single option.
|
|
15
|
+
* @param {boolean} [options.skipRecommendation=false] - If true, skip recommendation and show list directly.
|
|
16
|
+
* @param {string} [options.currentArchitecture] - Current architecture value (for edit mode).
|
|
17
|
+
* @returns {Promise<string>} Selected or recommended architecture.
|
|
18
|
+
*/
|
|
19
|
+
export async function selectArchitecture({
|
|
20
|
+
framework,
|
|
21
|
+
scale,
|
|
22
|
+
lifetime,
|
|
23
|
+
features,
|
|
24
|
+
ARCHITECTURES,
|
|
25
|
+
filterArchitecturesByStore,
|
|
26
|
+
getRecommendedArchitecture,
|
|
27
|
+
confirmPrompt,
|
|
28
|
+
singleChoiceCheckbox,
|
|
29
|
+
skipRecommendation = false,
|
|
30
|
+
currentArchitecture,
|
|
31
|
+
}) {
|
|
32
|
+
let architectures = ARCHITECTURES[framework];
|
|
33
|
+
|
|
34
|
+
if (!architectures || architectures.length === 0) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
`No architectures defined for framework: ${framework}. Available frameworks: ${Object.keys(ARCHITECTURES).join(", ")}`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
architectures = filterArchitecturesByStore(
|
|
41
|
+
features.includes("pinia")
|
|
42
|
+
? "pinia"
|
|
43
|
+
: features.includes("vuex")
|
|
44
|
+
? "vuex"
|
|
45
|
+
: null,
|
|
46
|
+
architectures
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (!architectures || architectures.length === 0) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`No architectures available after filtering for framework: ${framework}`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (skipRecommendation) {
|
|
56
|
+
return singleChoiceCheckbox({
|
|
57
|
+
name: "architecture",
|
|
58
|
+
message: "Select architecture:",
|
|
59
|
+
choices: architectures.map((a) => ({ name: a, value: a })),
|
|
60
|
+
defaultValue: currentArchitecture && architectures.includes(currentArchitecture)
|
|
61
|
+
? currentArchitecture
|
|
62
|
+
: undefined,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const recommended = getRecommendedArchitecture({
|
|
67
|
+
framework,
|
|
68
|
+
scale,
|
|
69
|
+
lifetime,
|
|
70
|
+
features,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const isRecommendedAvailable = architectures.includes(recommended);
|
|
74
|
+
|
|
75
|
+
if (isRecommendedAvailable) {
|
|
76
|
+
const useRecommended = await confirmPrompt(
|
|
77
|
+
`Recommended architecture: "${recommended}". Use it?`,
|
|
78
|
+
true
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
if (useRecommended) return recommended;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return singleChoiceCheckbox({
|
|
85
|
+
name: "architecture",
|
|
86
|
+
message: "Select architecture:",
|
|
87
|
+
choices: architectures.map((a) => ({ name: a, value: a })),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { singleChoiceCheckbox, confirmPrompt } from "../../helpers/prompts.js";
|
|
2
|
+
import { FEATURE_CHOICES } from "../../config/featuresConfig.js";
|
|
3
|
+
import {
|
|
4
|
+
ARCHITECTURES,
|
|
5
|
+
getRecommendedArchitecture,
|
|
6
|
+
} from "../../config/architectures.js";
|
|
7
|
+
import {
|
|
8
|
+
applyExclusiveRules,
|
|
9
|
+
filterArchitecturesByStore,
|
|
10
|
+
} from "../featureRules.js";
|
|
11
|
+
import { selectArchitecture } from "../selectArchitecture.js";
|
|
12
|
+
import inquirer from "inquirer";
|
|
13
|
+
|
|
14
|
+
export async function collectProjectConfig() {
|
|
15
|
+
const { projectName } = await inquirer.prompt([
|
|
16
|
+
{
|
|
17
|
+
type: "input",
|
|
18
|
+
name: "projectName",
|
|
19
|
+
message: "Project name:",
|
|
20
|
+
validate: (v) => (v && !v.includes(" ") ? true : "Invalid project name"),
|
|
21
|
+
},
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const framework = await singleChoiceCheckbox({
|
|
25
|
+
name: "framework",
|
|
26
|
+
message: "Select project type:",
|
|
27
|
+
choices: [
|
|
28
|
+
{ name: "Vue", value: "vue" },
|
|
29
|
+
{ name: "Nuxt 3", value: "nuxt" },
|
|
30
|
+
{ name: "Vue 3 + Vuetify", value: "vuetify" },
|
|
31
|
+
],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
let features = [];
|
|
35
|
+
const featureChoices = applyExclusiveRules(features, FEATURE_CHOICES);
|
|
36
|
+
const featureResult = await inquirer.prompt([
|
|
37
|
+
{
|
|
38
|
+
type: "checkbox",
|
|
39
|
+
name: "features",
|
|
40
|
+
message: "Select features:",
|
|
41
|
+
choices: featureChoices,
|
|
42
|
+
},
|
|
43
|
+
]);
|
|
44
|
+
features = featureResult.features;
|
|
45
|
+
|
|
46
|
+
const store = await singleChoiceCheckbox({
|
|
47
|
+
name: "store",
|
|
48
|
+
message: "Select store:",
|
|
49
|
+
choices: [
|
|
50
|
+
{ name: "None", value: null },
|
|
51
|
+
{ name: "Pinia", value: "pinia" },
|
|
52
|
+
{ name: "Vuex", value: "vuex" },
|
|
53
|
+
],
|
|
54
|
+
});
|
|
55
|
+
if (store) features.push(store);
|
|
56
|
+
|
|
57
|
+
const scale = await singleChoiceCheckbox({
|
|
58
|
+
name: "scale",
|
|
59
|
+
message: "Project scale:",
|
|
60
|
+
choices: [
|
|
61
|
+
{ name: "Small", value: "small" },
|
|
62
|
+
{ name: "Medium", value: "medium" },
|
|
63
|
+
{ name: "Large", value: "large" },
|
|
64
|
+
],
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
const lifetime = await singleChoiceCheckbox({
|
|
68
|
+
name: "lifetime",
|
|
69
|
+
message: "Project lifetime:",
|
|
70
|
+
choices: [
|
|
71
|
+
{ name: "Short-term", value: "short" },
|
|
72
|
+
{ name: "Long-term", value: "long" },
|
|
73
|
+
],
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const architecture = await selectArchitecture({
|
|
77
|
+
framework,
|
|
78
|
+
scale,
|
|
79
|
+
lifetime,
|
|
80
|
+
features,
|
|
81
|
+
ARCHITECTURES,
|
|
82
|
+
filterArchitecturesByStore,
|
|
83
|
+
getRecommendedArchitecture,
|
|
84
|
+
confirmPrompt,
|
|
85
|
+
singleChoiceCheckbox,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const packageManager = await singleChoiceCheckbox({
|
|
89
|
+
name: "packageManager",
|
|
90
|
+
message: "Select package manager:",
|
|
91
|
+
choices: ["npm", "yarn", "pnpm"].map((v) => ({ name: v, value: v })),
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
projectName,
|
|
96
|
+
framework,
|
|
97
|
+
features,
|
|
98
|
+
architecture,
|
|
99
|
+
scale,
|
|
100
|
+
lifetime,
|
|
101
|
+
packageManager,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { createProjectStructure } from "../createProjectStructure.js";
|
|
4
|
+
import { createPackageJson } from "../createPackageJson.js";
|
|
5
|
+
import { installPackages } from "../installPackages.js";
|
|
6
|
+
import { getPackagesList } from "../utils/getPackagesList.js";
|
|
7
|
+
|
|
8
|
+
export async function finalizeProject(config) {
|
|
9
|
+
const projectDir = path.resolve(process.cwd(), config.projectName);
|
|
10
|
+
const packages = getPackagesList(config.framework, config.features);
|
|
11
|
+
|
|
12
|
+
createProjectStructure(config);
|
|
13
|
+
createPackageJson({ ...config, projectDir, packages });
|
|
14
|
+
|
|
15
|
+
const install = await import("../../helpers/prompts.js").then((m) =>
|
|
16
|
+
m.confirmPrompt("Install node_modules now?", true)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
if (install) {
|
|
20
|
+
await installPackages({ ...config, projectDir });
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
console.log(chalk.green("\n✨ Project created successfully!\n"));
|
|
24
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { confirmPrompt, singleChoiceCheckbox } from "../../helpers/prompts.js";
|
|
2
|
+
import { getPackagesList } from "../utils/getPackagesList.js";
|
|
3
|
+
import inquirer from "inquirer";
|
|
4
|
+
import { FEATURE_CHOICES } from "../../config/featuresConfig.js";
|
|
5
|
+
import {
|
|
6
|
+
ARCHITECTURES,
|
|
7
|
+
getRecommendedArchitecture,
|
|
8
|
+
} from "../../config/architectures.js";
|
|
9
|
+
import {
|
|
10
|
+
applyExclusiveRules,
|
|
11
|
+
filterArchitecturesByStore,
|
|
12
|
+
} from "../featureRules.js";
|
|
13
|
+
import { selectArchitecture } from "../selectArchitecture.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Allows user to edit individual configuration options
|
|
17
|
+
*/
|
|
18
|
+
async function editConfig(config) {
|
|
19
|
+
let editedConfig = { ...config };
|
|
20
|
+
|
|
21
|
+
// Edit project name
|
|
22
|
+
if (
|
|
23
|
+
await confirmPrompt(
|
|
24
|
+
`Edit project name? (Current: ${config.projectName})`,
|
|
25
|
+
false
|
|
26
|
+
)
|
|
27
|
+
) {
|
|
28
|
+
const { projectName } = await inquirer.prompt([
|
|
29
|
+
{
|
|
30
|
+
type: "input",
|
|
31
|
+
name: "projectName",
|
|
32
|
+
message: "Project name:",
|
|
33
|
+
default: config.projectName,
|
|
34
|
+
validate: (v) =>
|
|
35
|
+
v && !v.includes(" ") ? true : "Invalid project name",
|
|
36
|
+
},
|
|
37
|
+
]);
|
|
38
|
+
editedConfig.projectName = projectName;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Edit framework
|
|
42
|
+
if (
|
|
43
|
+
await confirmPrompt(`Edit framework? (Current: ${config.framework})`, false)
|
|
44
|
+
) {
|
|
45
|
+
editedConfig.framework = await singleChoiceCheckbox({
|
|
46
|
+
name: "framework",
|
|
47
|
+
message: "Select project type:",
|
|
48
|
+
choices: [
|
|
49
|
+
{ name: "Vue", value: "vue" },
|
|
50
|
+
{ name: "Nuxt 3", value: "nuxt" },
|
|
51
|
+
{ name: "Vue 3 + Vuetify", value: "vuetify" },
|
|
52
|
+
],
|
|
53
|
+
defaultValue: config.framework,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Edit features
|
|
58
|
+
const currentStore = config.features.find(
|
|
59
|
+
(f) => f === "pinia" || f === "vuex"
|
|
60
|
+
);
|
|
61
|
+
const featuresWithoutStore = config.features.filter(
|
|
62
|
+
(f) => f !== "pinia" && f !== "vuex"
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
await confirmPrompt(
|
|
67
|
+
`Edit features? (Current: ${config.features.join(", ") || "none"})`,
|
|
68
|
+
false
|
|
69
|
+
)
|
|
70
|
+
) {
|
|
71
|
+
const featureChoices = applyExclusiveRules(
|
|
72
|
+
featuresWithoutStore,
|
|
73
|
+
FEATURE_CHOICES
|
|
74
|
+
);
|
|
75
|
+
const featureResult = await inquirer.prompt([
|
|
76
|
+
{
|
|
77
|
+
type: "checkbox",
|
|
78
|
+
name: "features",
|
|
79
|
+
message: "Select features:",
|
|
80
|
+
choices: featureChoices,
|
|
81
|
+
default: featuresWithoutStore,
|
|
82
|
+
},
|
|
83
|
+
]);
|
|
84
|
+
editedConfig.features = featureResult.features;
|
|
85
|
+
} else {
|
|
86
|
+
// Keep features as is (without store, we'll add it separately)
|
|
87
|
+
editedConfig.features = [...featuresWithoutStore];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Edit store (separate question)
|
|
91
|
+
if (
|
|
92
|
+
await confirmPrompt(
|
|
93
|
+
`Edit store? (Current: ${currentStore || "None"})`,
|
|
94
|
+
false
|
|
95
|
+
)
|
|
96
|
+
) {
|
|
97
|
+
const store = await singleChoiceCheckbox({
|
|
98
|
+
name: "store",
|
|
99
|
+
message: "Select store:",
|
|
100
|
+
choices: [
|
|
101
|
+
{ name: "None", value: null },
|
|
102
|
+
{ name: "Pinia", value: "pinia" },
|
|
103
|
+
{ name: "Vuex", value: "vuex" },
|
|
104
|
+
],
|
|
105
|
+
defaultValue: currentStore || null,
|
|
106
|
+
});
|
|
107
|
+
if (store) editedConfig.features.push(store);
|
|
108
|
+
} else if (currentStore) {
|
|
109
|
+
// Keep old store if not editing
|
|
110
|
+
editedConfig.features.push(currentStore);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Edit scale
|
|
114
|
+
if (
|
|
115
|
+
await confirmPrompt(`Edit project scale? (Current: ${config.scale})`, false)
|
|
116
|
+
) {
|
|
117
|
+
editedConfig.scale = await singleChoiceCheckbox({
|
|
118
|
+
name: "scale",
|
|
119
|
+
message: "Project scale:",
|
|
120
|
+
choices: [
|
|
121
|
+
{ name: "Small", value: "small" },
|
|
122
|
+
{ name: "Medium", value: "medium" },
|
|
123
|
+
{ name: "Large", value: "large" },
|
|
124
|
+
],
|
|
125
|
+
defaultValue: config.scale,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Edit lifetime
|
|
130
|
+
if (
|
|
131
|
+
await confirmPrompt(
|
|
132
|
+
`Edit project lifetime? (Current: ${config.lifetime})`,
|
|
133
|
+
false
|
|
134
|
+
)
|
|
135
|
+
) {
|
|
136
|
+
editedConfig.lifetime = await singleChoiceCheckbox({
|
|
137
|
+
name: "lifetime",
|
|
138
|
+
message: "Project lifetime:",
|
|
139
|
+
choices: [
|
|
140
|
+
{ name: "Short-term", value: "short" },
|
|
141
|
+
{ name: "Long-term", value: "long" },
|
|
142
|
+
],
|
|
143
|
+
defaultValue: config.lifetime,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Edit architecture
|
|
148
|
+
if (
|
|
149
|
+
await confirmPrompt(
|
|
150
|
+
`Edit architecture? (Current: ${editedConfig.architecture})`,
|
|
151
|
+
false
|
|
152
|
+
)
|
|
153
|
+
) {
|
|
154
|
+
editedConfig.architecture = await selectArchitecture({
|
|
155
|
+
framework: editedConfig.framework,
|
|
156
|
+
scale: editedConfig.scale,
|
|
157
|
+
lifetime: editedConfig.lifetime,
|
|
158
|
+
features: editedConfig.features,
|
|
159
|
+
ARCHITECTURES,
|
|
160
|
+
filterArchitecturesByStore,
|
|
161
|
+
getRecommendedArchitecture,
|
|
162
|
+
confirmPrompt,
|
|
163
|
+
singleChoiceCheckbox,
|
|
164
|
+
skipRecommendation: true,
|
|
165
|
+
currentArchitecture: editedConfig.architecture,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Edit package manager
|
|
170
|
+
if (
|
|
171
|
+
await confirmPrompt(
|
|
172
|
+
`Edit package manager? (Current: ${config.packageManager})`,
|
|
173
|
+
false
|
|
174
|
+
)
|
|
175
|
+
) {
|
|
176
|
+
editedConfig.packageManager = await singleChoiceCheckbox({
|
|
177
|
+
name: "packageManager",
|
|
178
|
+
message: "Select package manager:",
|
|
179
|
+
choices: ["npm", "yarn", "pnpm"].map((v) => ({ name: v, value: v })),
|
|
180
|
+
defaultValue: config.packageManager,
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return editedConfig;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export async function reviewAndConfirm(config) {
|
|
188
|
+
const packages = getPackagesList(config.framework, config.features);
|
|
189
|
+
|
|
190
|
+
// Ask if user wants to edit
|
|
191
|
+
const wantsToEdit = await confirmPrompt(
|
|
192
|
+
`
|
|
193
|
+
Project: ${config.projectName}
|
|
194
|
+
Framework: ${config.framework}
|
|
195
|
+
Architecture: ${config.architecture}
|
|
196
|
+
Features: ${config.features.join(", ") || "none"}
|
|
197
|
+
Packages: ${packages.join(", ") || "none"}
|
|
198
|
+
|
|
199
|
+
Do you want to edit any of these options?`,
|
|
200
|
+
false
|
|
201
|
+
);
|
|
202
|
+
|
|
203
|
+
let finalConfig = config;
|
|
204
|
+
if (wantsToEdit) {
|
|
205
|
+
finalConfig = await editConfig(config);
|
|
206
|
+
// Show updated config and ask again
|
|
207
|
+
const updatedPackages = getPackagesList(
|
|
208
|
+
finalConfig.framework,
|
|
209
|
+
finalConfig.features
|
|
210
|
+
);
|
|
211
|
+
const proceed = await confirmPrompt(
|
|
212
|
+
`
|
|
213
|
+
Updated configuration:
|
|
214
|
+
Project: ${finalConfig.projectName}
|
|
215
|
+
Framework: ${finalConfig.framework}
|
|
216
|
+
Architecture: ${finalConfig.architecture}
|
|
217
|
+
Features: ${finalConfig.features.join(", ") || "none"}
|
|
218
|
+
Packages: ${updatedPackages.join(", ") || "none"}
|
|
219
|
+
Proceed?`,
|
|
220
|
+
true
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
if (!proceed) {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
const proceed = await confirmPrompt("Proceed?", true);
|
|
228
|
+
if (!proceed) {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const installNodeModules = await confirmPrompt(
|
|
234
|
+
"Do you want to install dependencies?",
|
|
235
|
+
true
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
...finalConfig,
|
|
240
|
+
installNodeModules,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { CORE_PACKAGES, PACKAGE_MAP } from "../../config/packages.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generates a unique list of npm packages based on framework and selected features.
|
|
5
|
+
*
|
|
6
|
+
* - Includes core framework packages
|
|
7
|
+
* - Merges feature-based packages
|
|
8
|
+
* - Automatically removes duplicates
|
|
9
|
+
*
|
|
10
|
+
* @param {"vue"|"nuxt"|"vuetify"} framework
|
|
11
|
+
* The selected framework.
|
|
12
|
+
*
|
|
13
|
+
* @param {string[]} features
|
|
14
|
+
* List of enabled features (e.g. ["pinia", "eslint", "tailwind"]).
|
|
15
|
+
*
|
|
16
|
+
* @returns {string[]}
|
|
17
|
+
* A deduplicated array of npm package names.
|
|
18
|
+
*/
|
|
19
|
+
export function getPackagesList(framework, features) {
|
|
20
|
+
const set = new Set(CORE_PACKAGES[framework] || []);
|
|
21
|
+
|
|
22
|
+
features.forEach((feature) => {
|
|
23
|
+
PACKAGE_MAP[framework]?.[feature]?.forEach((pkg) => {
|
|
24
|
+
set.add(pkg);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return [...set];
|
|
29
|
+
}
|