@component-hook/create-app 1.1.5 → 1.2.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/index.js +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +254 -0
- package/package.json +5 -5
- package/template-react/package.json +26 -26
- package/template-shared/.github/actions/setup-environment/action.yml +2 -2
- package/template-vue/package.json +26 -26
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -255
package/bin/index.js
CHANGED
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { readFile, readdir, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import artTemplate from "art-template";
|
|
6
|
+
import { copy, ensureDirSync, remove } from "fs-extra/esm";
|
|
7
|
+
import minimist from "minimist";
|
|
8
|
+
import ora from "ora";
|
|
9
|
+
import colors from "picocolors";
|
|
10
|
+
import prompts from "prompts";
|
|
11
|
+
|
|
12
|
+
//#region src/constants.ts
|
|
13
|
+
const DEFAULT_PROJECT_NAME = "basic-project";
|
|
14
|
+
const TEMPLATES = ["vue", "react"];
|
|
15
|
+
const GITHUB_ACTIONS = "GitHub Actions";
|
|
16
|
+
const GITLAB_CI = "GitLab CI";
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/utils.ts
|
|
20
|
+
function formatTargetDir(targetDir) {
|
|
21
|
+
if (!targetDir) return targetDir;
|
|
22
|
+
targetDir = targetDir.trim();
|
|
23
|
+
if (targetDir[0] === "/") targetDir = targetDir.slice(1);
|
|
24
|
+
if (targetDir.at(-1) === "/") targetDir = targetDir.slice(0, -1);
|
|
25
|
+
return targetDir;
|
|
26
|
+
}
|
|
27
|
+
function getProjectName(targetDir) {
|
|
28
|
+
if (!targetDir) return DEFAULT_PROJECT_NAME;
|
|
29
|
+
return path.basename(path.resolve(targetDir));
|
|
30
|
+
}
|
|
31
|
+
function isValidPackageName(projectName) {
|
|
32
|
+
return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(projectName);
|
|
33
|
+
}
|
|
34
|
+
function toValidPackageName(projectName) {
|
|
35
|
+
return projectName.trim().toLowerCase().replaceAll(/\s+/g, "-").replace(/^[._]/, "").replaceAll(/[^a-z\d\-~]+/g, "-");
|
|
36
|
+
}
|
|
37
|
+
function toUpperCasePackageName(projectName) {
|
|
38
|
+
return projectName.split("-").map((str) => `${str[0].toUpperCase()}${str.slice(1)}`).join(" ");
|
|
39
|
+
}
|
|
40
|
+
function getPkgManagerInfo() {
|
|
41
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
42
|
+
if (!userAgent) return;
|
|
43
|
+
const [pkgManager] = userAgent.split(" ");
|
|
44
|
+
const [name, version] = pkgManager.split("/");
|
|
45
|
+
return {
|
|
46
|
+
name,
|
|
47
|
+
version
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function isEmptyFolder(dir) {
|
|
51
|
+
const files = readdirSync(dir);
|
|
52
|
+
return files.length === 0 || files.length === 1 && files[0] === ".git";
|
|
53
|
+
}
|
|
54
|
+
function clearFolder(dir) {
|
|
55
|
+
if (!existsSync(dir)) return;
|
|
56
|
+
const files = readdirSync(dir).filter((file) => file !== ".git");
|
|
57
|
+
return Promise.all(files.map((file) => remove(path.resolve(dir, file))));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/prompts.ts
|
|
62
|
+
function builtinTemplate(template) {
|
|
63
|
+
return template && TEMPLATES.includes(template);
|
|
64
|
+
}
|
|
65
|
+
function onCancel() {
|
|
66
|
+
throw new Error(`${colors.red("✖")} Operation cancelled`);
|
|
67
|
+
}
|
|
68
|
+
function styleTitle(title) {
|
|
69
|
+
return colors.bold(colors.magenta(title));
|
|
70
|
+
}
|
|
71
|
+
function operationPrompts({ targetDir, template }) {
|
|
72
|
+
return prompts([
|
|
73
|
+
{
|
|
74
|
+
type: targetDir ? null : "text",
|
|
75
|
+
name: "projectName",
|
|
76
|
+
message: styleTitle("Project name:"),
|
|
77
|
+
initial: DEFAULT_PROJECT_NAME
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
type: (nameAnswer) => {
|
|
81
|
+
return existsSync(nameAnswer) && !isEmptyFolder(nameAnswer) ? "select" : null;
|
|
82
|
+
},
|
|
83
|
+
name: "overwrite",
|
|
84
|
+
message: (nameAnswer) => {
|
|
85
|
+
return styleTitle(`${nameAnswer === "." ? "current directory" : `Target directory "${targetDir}"`} is not empty. Please choose how to proceed:`);
|
|
86
|
+
},
|
|
87
|
+
initial: 0,
|
|
88
|
+
choices: [
|
|
89
|
+
{
|
|
90
|
+
title: "Cancel operation",
|
|
91
|
+
value: "cancel"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
title: "Remove existing files and continue",
|
|
95
|
+
value: "overwrite"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
title: "Ignore files and continue",
|
|
99
|
+
value: "ignore"
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
type: (overwriteAnswer) => {
|
|
105
|
+
if (overwriteAnswer === "cancel") onCancel();
|
|
106
|
+
return null;
|
|
107
|
+
},
|
|
108
|
+
name: "overwriteChecker"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: (_, { projectName: nameAnswer }) => {
|
|
112
|
+
return isValidPackageName(getProjectName(nameAnswer)) ? null : "text";
|
|
113
|
+
},
|
|
114
|
+
name: "packageName",
|
|
115
|
+
message: styleTitle("Package name:"),
|
|
116
|
+
initial: (_, { projectName: nameAnswer }) => toValidPackageName(getProjectName(nameAnswer)),
|
|
117
|
+
validate: (name) => {
|
|
118
|
+
return isValidPackageName(name) || "Invalid package.json name";
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
type: builtinTemplate(template) ? null : "select",
|
|
123
|
+
name: "framework",
|
|
124
|
+
message: template ? styleTitle(`${colors.red(template)} isn't a valid template. Please choose from below:`) : styleTitle("Select a framework:"),
|
|
125
|
+
initial: 0,
|
|
126
|
+
choices: [{
|
|
127
|
+
title: colors.green("Vue"),
|
|
128
|
+
value: "vue"
|
|
129
|
+
}, {
|
|
130
|
+
title: colors.blue("React"),
|
|
131
|
+
value: "react"
|
|
132
|
+
}]
|
|
133
|
+
}
|
|
134
|
+
], { onCancel });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
//#endregion
|
|
138
|
+
//#region src/index.ts
|
|
139
|
+
const cwd = process.cwd();
|
|
140
|
+
const argv = minimist(process.argv.slice(2), {
|
|
141
|
+
default: {
|
|
142
|
+
gitlab: false,
|
|
143
|
+
help: false
|
|
144
|
+
},
|
|
145
|
+
alias: {
|
|
146
|
+
t: "template",
|
|
147
|
+
l: "gitlab",
|
|
148
|
+
h: "help"
|
|
149
|
+
},
|
|
150
|
+
string: ["_"]
|
|
151
|
+
});
|
|
152
|
+
const renameFiles = {
|
|
153
|
+
_gitignore: ".gitignore",
|
|
154
|
+
"_gitlab-ci.yml": ".gitlab-ci.yml"
|
|
155
|
+
};
|
|
156
|
+
const helpMessage = `
|
|
157
|
+
Usage: create-component-hook [project name] [options]
|
|
158
|
+
|
|
159
|
+
Create a new project in TypeScript.
|
|
160
|
+
With no arguments, start the CLI in interactive mode.
|
|
161
|
+
|
|
162
|
+
Options:
|
|
163
|
+
-t, --template [name] Choose a framework template
|
|
164
|
+
-l, --gitlab Use Gitlab CI/CD, default is Github CI/CD
|
|
165
|
+
-h, --help Display this help message
|
|
166
|
+
|
|
167
|
+
Available templates:
|
|
168
|
+
${colors.green("vue")}
|
|
169
|
+
${colors.blue("react")}
|
|
170
|
+
`;
|
|
171
|
+
async function writePackageJson(root, dir, packageName) {
|
|
172
|
+
const pkgJson = await readFile(path.join(dir, "package.json"), "utf8");
|
|
173
|
+
const pkg = JSON.parse(pkgJson);
|
|
174
|
+
const targetPath = path.join(root, "package.json");
|
|
175
|
+
pkg.name = packageName;
|
|
176
|
+
return writeFile(targetPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
177
|
+
}
|
|
178
|
+
async function copyFolder(root, dir, packageName) {
|
|
179
|
+
const isGitlab = argv.l || argv.gitlab;
|
|
180
|
+
const filterFiles = (await readdir(dir)).filter((file) => {
|
|
181
|
+
return file !== "package.json" && file !== (isGitlab ? ".github" : ".gitlab") && file !== (isGitlab ? "" : "_gitlab-ci.yml");
|
|
182
|
+
});
|
|
183
|
+
const rewriteOrCopyFile = (file) => {
|
|
184
|
+
const targetPath = path.join(root, renameFiles?.[file] ?? file);
|
|
185
|
+
const templatePath = path.join(dir, file);
|
|
186
|
+
if (file.endsWith(".art")) {
|
|
187
|
+
const name = file.endsWith(".js.art") || file.endsWith(".ts.art") ? packageName : toUpperCasePackageName(packageName);
|
|
188
|
+
const replacedPath = targetPath.replace(".art", "");
|
|
189
|
+
const renderedResult = artTemplate(templatePath, {
|
|
190
|
+
projectName: name,
|
|
191
|
+
gitRepository: isGitlab ? GITLAB_CI : GITHUB_ACTIONS
|
|
192
|
+
});
|
|
193
|
+
ensureDirSync(path.dirname(replacedPath));
|
|
194
|
+
return writeFile(replacedPath, renderedResult);
|
|
195
|
+
}
|
|
196
|
+
if (statSync(templatePath).isDirectory()) {
|
|
197
|
+
const rewriteOrCopySubFilePromises = readdirSync(templatePath).map((subFile) => rewriteOrCopyFile(path.join(file, subFile)));
|
|
198
|
+
return Promise.all(rewriteOrCopySubFilePromises).then(() => {});
|
|
199
|
+
} else return copy(templatePath, targetPath);
|
|
200
|
+
};
|
|
201
|
+
return Promise.all(filterFiles.map((file) => rewriteOrCopyFile(file)));
|
|
202
|
+
}
|
|
203
|
+
async function createApp() {
|
|
204
|
+
if (argv.h || argv.help) {
|
|
205
|
+
console.log(helpMessage);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
const targetDir = formatTargetDir(argv._[0]);
|
|
209
|
+
const template = argv.t || argv.template;
|
|
210
|
+
const result = await operationPrompts({
|
|
211
|
+
targetDir,
|
|
212
|
+
template
|
|
213
|
+
});
|
|
214
|
+
const packageName = result.packageName || getProjectName(result.projectName);
|
|
215
|
+
const framework = result.framework || template;
|
|
216
|
+
const root = path.join(cwd, result.projectName);
|
|
217
|
+
const pkgManager = getPkgManagerInfo()?.name ?? "npm";
|
|
218
|
+
const spinner = ora();
|
|
219
|
+
console.log();
|
|
220
|
+
spinner.start(colors.yellow(`Scaffolding project...`));
|
|
221
|
+
if (result.overwrite === "overwrite") await clearFolder(root);
|
|
222
|
+
else ensureDirSync(root);
|
|
223
|
+
const filePath = fileURLToPath(import.meta.url);
|
|
224
|
+
const sharedDir = path.resolve(filePath, "../..", "template-shared");
|
|
225
|
+
const templateDir = path.resolve(filePath, "../..", `template-${framework}`);
|
|
226
|
+
Promise.all([
|
|
227
|
+
copyFolder(root, sharedDir, packageName),
|
|
228
|
+
copyFolder(root, templateDir, packageName),
|
|
229
|
+
writePackageJson(root, templateDir, packageName),
|
|
230
|
+
new Promise((resolve) => setTimeout(resolve, 300))
|
|
231
|
+
]).then(() => {
|
|
232
|
+
const cdProjectName = path.relative(cwd, root);
|
|
233
|
+
const formattedCdProjectName = cdProjectName.includes(" ") ? `"${cdProjectName}"` : cdProjectName;
|
|
234
|
+
spinner.succeed(`${colors.green("Scaffolded project in")} ${root}`);
|
|
235
|
+
console.log(`\n${colors.green("Done. Now run:")}\n`);
|
|
236
|
+
console.log(colors.cyan(` cd ${formattedCdProjectName}`));
|
|
237
|
+
if (pkgManager === "yarn") {
|
|
238
|
+
console.log(colors.cyan(` yarn`));
|
|
239
|
+
console.log(colors.cyan(` yarn dev`));
|
|
240
|
+
} else {
|
|
241
|
+
console.log(colors.cyan(` ${pkgManager} install`));
|
|
242
|
+
console.log(colors.cyan(` ${pkgManager} run dev`));
|
|
243
|
+
}
|
|
244
|
+
console.log();
|
|
245
|
+
}).catch(() => {
|
|
246
|
+
spinner.fail(colors.red("Failed to scaffold project"));
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
createApp().catch((error) => {
|
|
250
|
+
console.error(colors.red(error.message));
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
//#endregion
|
|
254
|
+
export { };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@component-hook/create-app",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Scaffolding a basic typescript project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"dist"
|
|
15
15
|
],
|
|
16
16
|
"engines": {
|
|
17
|
-
"node": ">=20.19.
|
|
17
|
+
"node": ">=20.19.5"
|
|
18
18
|
},
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"homepage": "https://github.com/tzuyi0817/component-hook/tree/master/packages/create-app#readme",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"art-template": "^4.13.4",
|
|
30
|
-
"fs-extra": "^11.3.
|
|
30
|
+
"fs-extra": "^11.3.2",
|
|
31
31
|
"minimist": "^1.2.8",
|
|
32
32
|
"ora": "^8.2.0",
|
|
33
33
|
"picocolors": "^1.1.1",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"@types/prompts": "^2.4.9"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
|
-
"dev": "
|
|
43
|
-
"build": "
|
|
42
|
+
"dev": "tsdown --watch",
|
|
43
|
+
"build": "tsdown",
|
|
44
44
|
"typecheck": "tsc --noEmit"
|
|
45
45
|
}
|
|
46
46
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"private": true,
|
|
4
4
|
"version": "0.0.0",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"packageManager": "pnpm@10.
|
|
6
|
+
"packageManager": "pnpm@10.20.0",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"dev": "vite",
|
|
9
9
|
"dev:mock": "MOCK=true vite",
|
|
@@ -18,47 +18,47 @@
|
|
|
18
18
|
"preview": "vite preview"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"axios": "^1.
|
|
22
|
-
"i18next": "^25.
|
|
23
|
-
"react": "^19.
|
|
24
|
-
"react-dom": "^19.
|
|
25
|
-
"react-i18next": "^15.7.
|
|
26
|
-
"react-router-dom": "^7.
|
|
21
|
+
"axios": "^1.13.2",
|
|
22
|
+
"i18next": "^25.6.0",
|
|
23
|
+
"react": "^19.2.0",
|
|
24
|
+
"react-dom": "^19.2.0",
|
|
25
|
+
"react-i18next": "^15.7.4",
|
|
26
|
+
"react-router-dom": "^7.9.5",
|
|
27
27
|
"zustand": "^5.0.8"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@component-hook/eslint-plugin": "^1.1.
|
|
31
|
-
"@playwright/test": "^1.
|
|
32
|
-
"@tailwindcss/vite": "^4.1.
|
|
33
|
-
"@testing-library/jest-dom": "^6.
|
|
30
|
+
"@component-hook/eslint-plugin": "^1.1.5",
|
|
31
|
+
"@playwright/test": "^1.56.1",
|
|
32
|
+
"@tailwindcss/vite": "^4.1.16",
|
|
33
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
34
34
|
"@testing-library/react": "^16.3.0",
|
|
35
35
|
"@testing-library/user-event": "^14.6.1",
|
|
36
36
|
"@tsconfig/node22": "^22.0.2",
|
|
37
|
-
"@types/node": "^22.
|
|
38
|
-
"@types/react": "^19.
|
|
39
|
-
"@types/react-dom": "^19.
|
|
40
|
-
"@vitejs/plugin-react-swc": "^4.
|
|
37
|
+
"@types/node": "^22.19.0",
|
|
38
|
+
"@types/react": "^19.2.2",
|
|
39
|
+
"@types/react-dom": "^19.2.2",
|
|
40
|
+
"@vitejs/plugin-react-swc": "^4.2.1",
|
|
41
41
|
"@vitest/coverage-v8": "^3.2.4",
|
|
42
42
|
"autoprefixer": "^10.4.21",
|
|
43
|
-
"cssnano": "^7.1.
|
|
44
|
-
"eslint": "^9.
|
|
45
|
-
"jsdom": "^
|
|
46
|
-
"msw": "^2.
|
|
43
|
+
"cssnano": "^7.1.2",
|
|
44
|
+
"eslint": "^9.39.1",
|
|
45
|
+
"jsdom": "^27.1.0",
|
|
46
|
+
"msw": "^2.12.0",
|
|
47
47
|
"postcss": "^8.5.6",
|
|
48
|
-
"postcss-preset-env": "^10.
|
|
48
|
+
"postcss-preset-env": "^10.4.0",
|
|
49
49
|
"postcss-pxtorem": "^6.1.0",
|
|
50
50
|
"prettier": "^3.6.2",
|
|
51
|
-
"tailwindcss": "^4.1.
|
|
52
|
-
"typescript": "^5.9.
|
|
53
|
-
"vite": "^7.1
|
|
51
|
+
"tailwindcss": "^4.1.16",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"vite": "^7.2.1",
|
|
54
54
|
"vite-plugin-svg-icons": "^2.0.1",
|
|
55
55
|
"vitest": "^3.2.4"
|
|
56
56
|
},
|
|
57
57
|
"optionalDependencies": {
|
|
58
|
-
"@commitlint/cli": "^
|
|
59
|
-
"@commitlint/config-conventional": "^
|
|
58
|
+
"@commitlint/cli": "^20.1.0",
|
|
59
|
+
"@commitlint/config-conventional": "^20.0.0",
|
|
60
60
|
"husky": "^9.1.7",
|
|
61
|
-
"lint-staged": "^16.
|
|
61
|
+
"lint-staged": "^16.2.6"
|
|
62
62
|
},
|
|
63
63
|
"msw": {
|
|
64
64
|
"workerDirectory": [
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"version": "0.0.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
|
-
"packageManager": "pnpm@10.
|
|
6
|
+
"packageManager": "pnpm@10.20.0",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"dev": "vite",
|
|
9
9
|
"dev:mock": "MOCK=true vite",
|
|
@@ -18,50 +18,50 @@
|
|
|
18
18
|
"preview": "vite preview"
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"axios": "^1.
|
|
22
|
-
"pinia": "^3.0.
|
|
23
|
-
"pinia-plugin-persistedstate": "^4.
|
|
24
|
-
"vue": "^3.5.
|
|
25
|
-
"vue-i18n": "^11.1.
|
|
26
|
-
"vue-router": "^4.
|
|
21
|
+
"axios": "^1.13.2",
|
|
22
|
+
"pinia": "^3.0.4",
|
|
23
|
+
"pinia-plugin-persistedstate": "^4.7.1",
|
|
24
|
+
"vue": "^3.5.23",
|
|
25
|
+
"vue-i18n": "^11.1.12",
|
|
26
|
+
"vue-router": "^4.6.3"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@component-hook/eslint-plugin": "1.1.
|
|
30
|
-
"@intlify/unplugin-vue-i18n": "^
|
|
31
|
-
"@playwright/test": "^1.
|
|
32
|
-
"@tailwindcss/vite": "^4.1.
|
|
33
|
-
"@testing-library/jest-dom": "^6.
|
|
29
|
+
"@component-hook/eslint-plugin": "^1.1.5",
|
|
30
|
+
"@intlify/unplugin-vue-i18n": "^11.0.1",
|
|
31
|
+
"@playwright/test": "^1.56.1",
|
|
32
|
+
"@tailwindcss/vite": "^4.1.16",
|
|
33
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
34
34
|
"@testing-library/user-event": "^14.6.1",
|
|
35
35
|
"@testing-library/vue": "^8.1.0",
|
|
36
36
|
"@tsconfig/node22": "^22.0.2",
|
|
37
|
-
"@types/node": "^22.
|
|
37
|
+
"@types/node": "^22.19.0",
|
|
38
38
|
"@vitejs/plugin-vue": "^6.0.1",
|
|
39
39
|
"@vitest/coverage-v8": "^3.2.4",
|
|
40
40
|
"@vue/tsconfig": "^0.8.1",
|
|
41
41
|
"autoprefixer": "^10.4.21",
|
|
42
|
-
"cssnano": "^7.1.
|
|
43
|
-
"eslint": "^9.
|
|
44
|
-
"jsdom": "^
|
|
45
|
-
"msw": "^2.
|
|
42
|
+
"cssnano": "^7.1.2",
|
|
43
|
+
"eslint": "^9.39.1",
|
|
44
|
+
"jsdom": "^27.1.0",
|
|
45
|
+
"msw": "^2.12.0",
|
|
46
46
|
"postcss": "^8.5.6",
|
|
47
|
-
"postcss-preset-env": "^10.
|
|
47
|
+
"postcss-preset-env": "^10.4.0",
|
|
48
48
|
"postcss-pxtorem": "^6.1.0",
|
|
49
49
|
"prettier": "^3.6.2",
|
|
50
|
-
"rollup-plugin-visualizer": "^6.0.
|
|
51
|
-
"tailwindcss": "^4.1.
|
|
52
|
-
"typescript": "^5.9.
|
|
53
|
-
"vite": "^7.1
|
|
50
|
+
"rollup-plugin-visualizer": "^6.0.5",
|
|
51
|
+
"tailwindcss": "^4.1.16",
|
|
52
|
+
"typescript": "^5.9.3",
|
|
53
|
+
"vite": "^7.2.1",
|
|
54
54
|
"vite-plugin-svg-icons": "^2.0.1",
|
|
55
55
|
"vitest": "^3.2.4",
|
|
56
|
-
"vue-tsc": "^3.
|
|
56
|
+
"vue-tsc": "^3.1.3"
|
|
57
57
|
},
|
|
58
58
|
"msw": {
|
|
59
59
|
"workerDirectory": "public"
|
|
60
60
|
},
|
|
61
61
|
"optionalDependencies": {
|
|
62
|
-
"@commitlint/cli": "^
|
|
63
|
-
"@commitlint/config-conventional": "^
|
|
62
|
+
"@commitlint/cli": "^20.1.0",
|
|
63
|
+
"@commitlint/config-conventional": "^20.0.0",
|
|
64
64
|
"husky": "^9.1.7",
|
|
65
|
-
"lint-staged": "^16.
|
|
65
|
+
"lint-staged": "^16.2.6"
|
|
66
66
|
}
|
|
67
67
|
}
|
package/dist/index.d.ts
DELETED
package/dist/index.js
DELETED
|
@@ -1,255 +0,0 @@
|
|
|
1
|
-
// src/index.ts
|
|
2
|
-
import { readdirSync as readdirSync2, statSync } from "fs";
|
|
3
|
-
import { readdir, readFile, writeFile } from "fs/promises";
|
|
4
|
-
import path2 from "path";
|
|
5
|
-
import { fileURLToPath } from "url";
|
|
6
|
-
import artTemplate from "art-template";
|
|
7
|
-
import { copy, ensureDirSync } from "fs-extra/esm";
|
|
8
|
-
import minimist from "minimist";
|
|
9
|
-
import ora from "ora";
|
|
10
|
-
import colors2 from "picocolors";
|
|
11
|
-
|
|
12
|
-
// src/constants.ts
|
|
13
|
-
var DEFAULT_PROJECT_NAME = "basic-project";
|
|
14
|
-
var TEMPLATES = ["vue", "react"];
|
|
15
|
-
var GITHUB_ACTIONS = "GitHub Actions";
|
|
16
|
-
var GITLAB_CI = "GitLab CI";
|
|
17
|
-
|
|
18
|
-
// src/prompts.ts
|
|
19
|
-
import { existsSync as existsSync2 } from "fs";
|
|
20
|
-
import colors from "picocolors";
|
|
21
|
-
import prompts from "prompts";
|
|
22
|
-
|
|
23
|
-
// src/utils.ts
|
|
24
|
-
import { existsSync, readdirSync } from "fs";
|
|
25
|
-
import path from "path";
|
|
26
|
-
import { remove } from "fs-extra/esm";
|
|
27
|
-
function formatTargetDir(targetDir) {
|
|
28
|
-
if (!targetDir) return targetDir;
|
|
29
|
-
targetDir = targetDir.trim();
|
|
30
|
-
if (targetDir[0] === "/") {
|
|
31
|
-
targetDir = targetDir.slice(1);
|
|
32
|
-
}
|
|
33
|
-
if (targetDir.at(-1) === "/") {
|
|
34
|
-
targetDir = targetDir.slice(0, -1);
|
|
35
|
-
}
|
|
36
|
-
return targetDir;
|
|
37
|
-
}
|
|
38
|
-
function getProjectName(targetDir) {
|
|
39
|
-
if (!targetDir) {
|
|
40
|
-
return DEFAULT_PROJECT_NAME;
|
|
41
|
-
}
|
|
42
|
-
return path.basename(path.resolve(targetDir));
|
|
43
|
-
}
|
|
44
|
-
function isValidPackageName(projectName) {
|
|
45
|
-
return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(projectName);
|
|
46
|
-
}
|
|
47
|
-
function toValidPackageName(projectName) {
|
|
48
|
-
return projectName.trim().toLowerCase().replaceAll(/\s+/g, "-").replace(/^[._]/, "").replaceAll(/[^a-z\d\-~]+/g, "-");
|
|
49
|
-
}
|
|
50
|
-
function toUpperCasePackageName(projectName) {
|
|
51
|
-
return projectName.split("-").map((str) => `${str[0].toUpperCase()}${str.slice(1)}`).join(" ");
|
|
52
|
-
}
|
|
53
|
-
function getPkgManagerInfo() {
|
|
54
|
-
const userAgent = process.env.npm_config_user_agent;
|
|
55
|
-
if (!userAgent) return;
|
|
56
|
-
const [pkgManager] = userAgent.split(" ");
|
|
57
|
-
const [name, version] = pkgManager.split("/");
|
|
58
|
-
return { name, version };
|
|
59
|
-
}
|
|
60
|
-
function isEmptyFolder(dir) {
|
|
61
|
-
const files = readdirSync(dir);
|
|
62
|
-
return files.length === 0 || files.length === 1 && files[0] === ".git";
|
|
63
|
-
}
|
|
64
|
-
function clearFolder(dir) {
|
|
65
|
-
if (!existsSync(dir)) return;
|
|
66
|
-
const files = readdirSync(dir).filter((file) => file !== ".git");
|
|
67
|
-
return Promise.all(files.map((file) => remove(path.resolve(dir, file))));
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// src/prompts.ts
|
|
71
|
-
function builtinTemplate(template) {
|
|
72
|
-
return template && TEMPLATES.includes(template);
|
|
73
|
-
}
|
|
74
|
-
function onCancel() {
|
|
75
|
-
throw new Error(`${colors.red("\u2716")} Operation cancelled`);
|
|
76
|
-
}
|
|
77
|
-
function styleTitle(title) {
|
|
78
|
-
return colors.bold(colors.magenta(title));
|
|
79
|
-
}
|
|
80
|
-
function operationPrompts({ targetDir, template }) {
|
|
81
|
-
const projectName = {
|
|
82
|
-
type: targetDir ? null : "text",
|
|
83
|
-
name: "projectName",
|
|
84
|
-
message: styleTitle("Project name:"),
|
|
85
|
-
initial: DEFAULT_PROJECT_NAME
|
|
86
|
-
};
|
|
87
|
-
const overwrite = {
|
|
88
|
-
type: (nameAnswer) => {
|
|
89
|
-
return existsSync2(nameAnswer) && !isEmptyFolder(nameAnswer) ? "select" : null;
|
|
90
|
-
},
|
|
91
|
-
name: "overwrite",
|
|
92
|
-
message: (nameAnswer) => {
|
|
93
|
-
const isCurrentDir = nameAnswer === ".";
|
|
94
|
-
const dir = isCurrentDir ? "current directory" : `Target directory "${targetDir}"`;
|
|
95
|
-
return styleTitle(`${dir} is not empty. Please choose how to proceed:`);
|
|
96
|
-
},
|
|
97
|
-
initial: 0,
|
|
98
|
-
choices: [
|
|
99
|
-
{ title: "Cancel operation", value: "cancel" },
|
|
100
|
-
{ title: "Remove existing files and continue", value: "overwrite" },
|
|
101
|
-
{ title: "Ignore files and continue", value: "ignore" }
|
|
102
|
-
]
|
|
103
|
-
};
|
|
104
|
-
const overwriteChecker = {
|
|
105
|
-
type: (overwriteAnswer) => {
|
|
106
|
-
if (overwriteAnswer === "cancel") onCancel();
|
|
107
|
-
return null;
|
|
108
|
-
},
|
|
109
|
-
name: "overwriteChecker"
|
|
110
|
-
};
|
|
111
|
-
const packageName = {
|
|
112
|
-
type: (_, { projectName: nameAnswer }) => {
|
|
113
|
-
return isValidPackageName(getProjectName(nameAnswer)) ? null : "text";
|
|
114
|
-
},
|
|
115
|
-
name: "packageName",
|
|
116
|
-
message: styleTitle("Package name:"),
|
|
117
|
-
initial: (_, { projectName: nameAnswer }) => toValidPackageName(getProjectName(nameAnswer)),
|
|
118
|
-
validate: (name) => {
|
|
119
|
-
return isValidPackageName(name) || "Invalid package.json name";
|
|
120
|
-
}
|
|
121
|
-
};
|
|
122
|
-
const framework = {
|
|
123
|
-
type: builtinTemplate(template) ? null : "select",
|
|
124
|
-
name: "framework",
|
|
125
|
-
message: template ? styleTitle(`${colors.red(template)} isn't a valid template. Please choose from below:`) : styleTitle("Select a framework:"),
|
|
126
|
-
initial: 0,
|
|
127
|
-
choices: [
|
|
128
|
-
{ title: colors.green("Vue"), value: "vue" },
|
|
129
|
-
{ title: colors.blue("React"), value: "react" }
|
|
130
|
-
]
|
|
131
|
-
};
|
|
132
|
-
return prompts([projectName, overwrite, overwriteChecker, packageName, framework], { onCancel });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// src/index.ts
|
|
136
|
-
var cwd = process.cwd();
|
|
137
|
-
var argv = minimist(process.argv.slice(2), {
|
|
138
|
-
default: { gitlab: false, help: false },
|
|
139
|
-
alias: { t: "template", l: "gitlab", h: "help" },
|
|
140
|
-
string: ["_"]
|
|
141
|
-
});
|
|
142
|
-
var renameFiles = {
|
|
143
|
-
_gitignore: ".gitignore",
|
|
144
|
-
"_gitlab-ci.yml": ".gitlab-ci.yml"
|
|
145
|
-
};
|
|
146
|
-
var helpMessage = `
|
|
147
|
-
Usage: create-component-hook [project name] [options]
|
|
148
|
-
|
|
149
|
-
Create a new project in TypeScript.
|
|
150
|
-
With no arguments, start the CLI in interactive mode.
|
|
151
|
-
|
|
152
|
-
Options:
|
|
153
|
-
-t, --template [name] Choose a framework template
|
|
154
|
-
-l, --gitlab Use Gitlab CI/CD, default is Github CI/CD
|
|
155
|
-
-h, --help Display this help message
|
|
156
|
-
|
|
157
|
-
Available templates:
|
|
158
|
-
${colors2.green("vue")}
|
|
159
|
-
${colors2.blue("react")}
|
|
160
|
-
`;
|
|
161
|
-
async function writePackageJson(root, dir, packageName) {
|
|
162
|
-
const pkgJson = await readFile(path2.join(dir, "package.json"), "utf-8");
|
|
163
|
-
const pkg = JSON.parse(pkgJson);
|
|
164
|
-
const targetPath = path2.join(root, "package.json");
|
|
165
|
-
pkg.name = packageName;
|
|
166
|
-
return writeFile(targetPath, `${JSON.stringify(pkg, null, 2)}
|
|
167
|
-
`);
|
|
168
|
-
}
|
|
169
|
-
async function copyFolder(root, dir, packageName) {
|
|
170
|
-
const isGitlab = argv.l || argv.gitlab;
|
|
171
|
-
const files = await readdir(dir);
|
|
172
|
-
const filterFiles = files.filter((file) => {
|
|
173
|
-
const filterCi = isGitlab ? ".github" : ".gitlab";
|
|
174
|
-
const filterCiYml = isGitlab ? "" : "_gitlab-ci.yml";
|
|
175
|
-
return file !== "package.json" && file !== filterCi && file !== filterCiYml;
|
|
176
|
-
});
|
|
177
|
-
const rewriteOrCopyFile = (file) => {
|
|
178
|
-
const targetPath = path2.join(root, renameFiles?.[file] ?? file);
|
|
179
|
-
const templatePath = path2.join(dir, file);
|
|
180
|
-
if (file.endsWith(".art")) {
|
|
181
|
-
const isScript = file.endsWith(".js.art") || file.endsWith(".ts.art");
|
|
182
|
-
const name = isScript ? packageName : toUpperCasePackageName(packageName);
|
|
183
|
-
const replacedPath = targetPath.replace(".art", "");
|
|
184
|
-
const renderedResult = artTemplate(templatePath, {
|
|
185
|
-
projectName: name,
|
|
186
|
-
gitRepository: isGitlab ? GITLAB_CI : GITHUB_ACTIONS
|
|
187
|
-
});
|
|
188
|
-
ensureDirSync(path2.dirname(replacedPath));
|
|
189
|
-
return writeFile(replacedPath, renderedResult);
|
|
190
|
-
}
|
|
191
|
-
if (statSync(templatePath).isDirectory()) {
|
|
192
|
-
const subFiles = readdirSync2(templatePath);
|
|
193
|
-
const rewriteOrCopySubFilePromises = subFiles.map((subFile) => rewriteOrCopyFile(path2.join(file, subFile)));
|
|
194
|
-
return Promise.all(rewriteOrCopySubFilePromises).then(() => {
|
|
195
|
-
});
|
|
196
|
-
} else {
|
|
197
|
-
return copy(templatePath, targetPath);
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
return Promise.all(filterFiles.map((file) => rewriteOrCopyFile(file)));
|
|
201
|
-
}
|
|
202
|
-
async function createApp() {
|
|
203
|
-
const help = argv.h || argv.help;
|
|
204
|
-
if (help) {
|
|
205
|
-
console.log(helpMessage);
|
|
206
|
-
return;
|
|
207
|
-
}
|
|
208
|
-
const targetDir = formatTargetDir(argv._[0]);
|
|
209
|
-
const template = argv.t || argv.template;
|
|
210
|
-
const result = await operationPrompts({ targetDir, template });
|
|
211
|
-
const packageName = result.packageName || getProjectName(result.projectName);
|
|
212
|
-
const framework = result.framework || template;
|
|
213
|
-
const root = path2.join(cwd, result.projectName);
|
|
214
|
-
const pkgManagerInfo = getPkgManagerInfo();
|
|
215
|
-
const pkgManager = pkgManagerInfo?.name ?? "npm";
|
|
216
|
-
const spinner = ora();
|
|
217
|
-
console.log();
|
|
218
|
-
spinner.start(colors2.yellow(`Scaffolding project...`));
|
|
219
|
-
if (result.overwrite === "overwrite") {
|
|
220
|
-
await clearFolder(root);
|
|
221
|
-
} else {
|
|
222
|
-
ensureDirSync(root);
|
|
223
|
-
}
|
|
224
|
-
const filePath = fileURLToPath(import.meta.url);
|
|
225
|
-
const sharedDir = path2.resolve(filePath, "../..", "template-shared");
|
|
226
|
-
const templateDir = path2.resolve(filePath, "../..", `template-${framework}`);
|
|
227
|
-
const copyPromises = Promise.all([
|
|
228
|
-
copyFolder(root, sharedDir, packageName),
|
|
229
|
-
copyFolder(root, templateDir, packageName),
|
|
230
|
-
writePackageJson(root, templateDir, packageName),
|
|
231
|
-
new Promise((resolve) => setTimeout(resolve, 300))
|
|
232
|
-
]);
|
|
233
|
-
copyPromises.then(() => {
|
|
234
|
-
const cdProjectName = path2.relative(cwd, root);
|
|
235
|
-
const formattedCdProjectName = cdProjectName.includes(" ") ? `"${cdProjectName}"` : cdProjectName;
|
|
236
|
-
spinner.succeed(`${colors2.green("Scaffolded project in")} ${root}`);
|
|
237
|
-
console.log(`
|
|
238
|
-
${colors2.green("Done. Now run:")}
|
|
239
|
-
`);
|
|
240
|
-
console.log(colors2.cyan(` cd ${formattedCdProjectName}`));
|
|
241
|
-
if (pkgManager === "yarn") {
|
|
242
|
-
console.log(colors2.cyan(` yarn`));
|
|
243
|
-
console.log(colors2.cyan(` yarn dev`));
|
|
244
|
-
} else {
|
|
245
|
-
console.log(colors2.cyan(` ${pkgManager} install`));
|
|
246
|
-
console.log(colors2.cyan(` ${pkgManager} run dev`));
|
|
247
|
-
}
|
|
248
|
-
console.log();
|
|
249
|
-
}).catch(() => {
|
|
250
|
-
spinner.fail(colors2.red("Failed to scaffold project"));
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
createApp().catch((error) => {
|
|
254
|
-
console.error(colors2.red(error.message));
|
|
255
|
-
});
|