@component-hook/create-app 1.4.4 → 1.5.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/{template-shared/LICENSE → LICENSE} +1 -1
- package/README.md +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +110 -101
- package/package.json +3 -3
- package/template-react/.vscode/extensions.json +0 -9
- package/template-react/.vscode/settings.json +0 -18
- package/template-react/README.md.art +0 -84
- package/template-react/eslint.config.js +0 -18
- package/template-react/index.html.art +0 -13
- package/template-react/lint-staged.config.js +0 -6
- package/template-react/package.json +0 -69
- package/template-react/public/vite.svg +0 -1
- package/template-react/src/App.tsx +0 -6
- package/template-react/src/__tests__/e2e/home.spec.ts +0 -40
- package/template-react/src/__tests__/unit/pages/home/components/HelloWorld.test.tsx +0 -45
- package/template-react/src/__tests__/unit/pages/home/index.test.tsx +0 -19
- package/template-react/src/__tests__/unit/render/index.tsx +0 -15
- package/template-react/src/assets/svg-icons/react.svg +0 -1
- package/template-react/src/components/common/index.ts +0 -1
- package/template-react/src/components/common/svg-icon/index.ts +0 -1
- package/template-react/src/components/common/svg-icon/src/index.tsx +0 -23
- package/template-react/src/main.tsx +0 -26
- package/template-react/src/mocks/browser.ts +0 -4
- package/template-react/src/mocks/handlers/index.ts +0 -1
- package/template-react/src/mocks/server.ts +0 -4
- package/template-react/src/pages/home/components/hello-world/index.tsx +0 -49
- package/template-react/src/pages/home/index.module.css +0 -8
- package/template-react/src/pages/home/index.tsx +0 -37
- package/template-react/src/plugins/i18n/index.ts +0 -15
- package/template-react/src/plugins/i18n/locales/en-US.json +0 -3
- package/template-react/src/plugins/i18n/locales/zh-TW.json +0 -3
- package/template-react/src/router/index.tsx +0 -22
- package/template-react/src/stores/constants.ts +0 -1
- package/template-react/src/stores/index.ts +0 -1
- package/template-react/src/stores/middleware.ts +0 -17
- package/template-react/src/stores/modules/config.ts +0 -27
- package/template-react/src/styles/base.css +0 -68
- package/template-react/src/styles/common/all.css +0 -1
- package/template-react/src/styles/common/button.css +0 -26
- package/template-react/src/styles/index.css +0 -3
- package/template-react/src/styles/tailwind.css +0 -14
- package/template-react/src/vite-env.d.ts +0 -9
- package/template-react/tsconfig.app.json +0 -32
- package/template-react/tsconfig.vitest.json +0 -10
- package/template-react/vite.config.ts +0 -40
- package/template-react/vitest.config.ts +0 -18
- package/template-react/vitest.setup.ts +0 -13
- package/template-shared/.browserslistrc +0 -10
- package/template-shared/.github/actions/setup-environment/action.yml +0 -21
- package/template-shared/.github/renovate.json +0 -14
- package/template-shared/.github/workflows/ci.yml +0 -22
- package/template-shared/.github/workflows/deploy.yml +0 -27
- package/template-shared/.github/workflows/preview.yml +0 -17
- package/template-shared/.gitlab/setup.yml +0 -18
- package/template-shared/.husky/commit-msg +0 -1
- package/template-shared/.husky/pre-commit +0 -1
- package/template-shared/_gitignore +0 -32
- package/template-shared/_gitlab-ci.yml +0 -22
- package/template-shared/commitlint.config.js +0 -3
- package/template-shared/playwright.config.ts +0 -49
- package/template-shared/postcss.config.js +0 -15
- package/template-shared/prettier.config.js +0 -13
- package/template-shared/scripts/build-info/index.ts +0 -21
- package/template-shared/tsconfig.json +0 -14
- package/template-shared/tsconfig.node.json +0 -12
- package/template-vue/.vscode/extensions.json +0 -10
- package/template-vue/.vscode/settings.json +0 -18
- package/template-vue/README.md.art +0 -80
- package/template-vue/eslint.config.js +0 -18
- package/template-vue/index.html.art +0 -28
- package/template-vue/lint-staged.config.js +0 -6
- package/template-vue/package.json +0 -68
- package/template-vue/public/vite.svg +0 -1
- package/template-vue/src/App.vue +0 -7
- package/template-vue/src/__tests__/e2e/home.spec.ts +0 -61
- package/template-vue/src/__tests__/unit/pages/home/components/HelloWorld.test.ts +0 -57
- package/template-vue/src/__tests__/unit/pages/home/index.test.ts +0 -17
- package/template-vue/src/__tests__/unit/render/index.ts +0 -28
- package/template-vue/src/assets/svg-icons/vue.svg +0 -1
- package/template-vue/src/components/common/index.ts +0 -1
- package/template-vue/src/components/common/svg-icon/index.ts +0 -1
- package/template-vue/src/components/common/svg-icon/src/index.vue +0 -29
- package/template-vue/src/locales/en-US.json +0 -3
- package/template-vue/src/locales/zh-TW.json +0 -3
- package/template-vue/src/main.ts +0 -21
- package/template-vue/src/mocks/browser.ts +0 -4
- package/template-vue/src/mocks/handlers/index.ts +0 -1
- package/template-vue/src/mocks/server.ts +0 -4
- package/template-vue/src/pages/home/components/HelloWorld.vue +0 -54
- package/template-vue/src/pages/home/index.vue +0 -46
- package/template-vue/src/plugins/i18n.ts +0 -11
- package/template-vue/src/router/index.ts +0 -23
- package/template-vue/src/stores/constants.ts.art +0 -1
- package/template-vue/src/stores/index.ts +0 -15
- package/template-vue/src/stores/modules/config.ts +0 -20
- package/template-vue/src/styles/base.css +0 -59
- package/template-vue/src/styles/common/all.css +0 -1
- package/template-vue/src/styles/common/button.css +0 -15
- package/template-vue/src/styles/index.css +0 -3
- package/template-vue/src/styles/tailwind.css +0 -1
- package/template-vue/src/vite-env.d.ts +0 -9
- package/template-vue/tsconfig.app.json +0 -12
- package/template-vue/tsconfig.vitest.json +0 -10
- package/template-vue/vite.config.ts +0 -46
- package/template-vue/vitest.config.ts +0 -18
- package/template-vue/vitest.setup.ts +0 -12
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Scaffolding a basic `typescript` project.
|
|
4
4
|
|
|
5
5
|
> **Compatibility Note:**
|
|
6
|
-
> Requires [Node.js](https://nodejs.org/en/) version 20.19+. However, some templates require a higher Node.js version to work, please upgrade if your package manager warns about it.
|
|
6
|
+
> Requires [Node.js](https://nodejs.org/en/) version 20.19.6+. However, some templates require a higher Node.js version to work, please upgrade if your package manager warns about it.
|
|
7
7
|
|
|
8
8
|
<p>
|
|
9
9
|
<a href="https://npm-stat.com/charts.html?package=@component-hook/create-app">
|
package/dist/index.d.mts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/dist/index.mjs
CHANGED
|
@@ -8,7 +8,6 @@ import minimist from "minimist";
|
|
|
8
8
|
import ora from "ora";
|
|
9
9
|
import colors from "picocolors";
|
|
10
10
|
import prompts from "prompts";
|
|
11
|
-
|
|
12
11
|
//#region src/constants.ts
|
|
13
12
|
const DEFAULT_PROJECT_NAME = "basic-project";
|
|
14
13
|
const TEMPLATES = ["vue", "react"];
|
|
@@ -19,7 +18,6 @@ const CI = [
|
|
|
19
18
|
"gitlab-ci",
|
|
20
19
|
"none"
|
|
21
20
|
];
|
|
22
|
-
|
|
23
21
|
//#endregion
|
|
24
22
|
//#region src/utils.ts
|
|
25
23
|
function formatTargetDir(targetDir) {
|
|
@@ -45,8 +43,8 @@ function toUpperCasePackageName(projectName) {
|
|
|
45
43
|
function getPkgManagerInfo() {
|
|
46
44
|
const userAgent = process.env.npm_config_user_agent;
|
|
47
45
|
if (!userAgent) return;
|
|
48
|
-
const [pkgManager] = userAgent.split(" ");
|
|
49
|
-
const [name, version] = pkgManager.split("/");
|
|
46
|
+
const [pkgManager] = userAgent.split(" ", 1);
|
|
47
|
+
const [name, version] = pkgManager.split("/", 2);
|
|
50
48
|
return {
|
|
51
49
|
name,
|
|
52
50
|
version
|
|
@@ -69,7 +67,6 @@ function getCiArtContent(ci = "") {
|
|
|
69
67
|
if (!ciName) return "";
|
|
70
68
|
return `- **CI/CD**: Integrates \`${ciName}\` for automated testing and deployment.`;
|
|
71
69
|
}
|
|
72
|
-
|
|
73
70
|
//#endregion
|
|
74
71
|
//#region src/prompts.ts
|
|
75
72
|
function builtinTemplate(template) {
|
|
@@ -85,95 +82,100 @@ function styleTitle(title) {
|
|
|
85
82
|
return colors.bold(colors.magenta(title));
|
|
86
83
|
}
|
|
87
84
|
function operationPrompts({ targetDir, template, ci }) {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
{
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
return existsSync(dir) && !isEmptyFolder(dir) ? "select" : null;
|
|
99
|
-
},
|
|
100
|
-
name: "overwrite",
|
|
101
|
-
message: (nameAnswer) => {
|
|
102
|
-
const dirName = nameAnswer ?? targetDir;
|
|
103
|
-
return styleTitle(`${dirName === "." ? "current directory" : `Target directory "${dirName}"`} is not empty. Please choose how to proceed:`);
|
|
104
|
-
},
|
|
105
|
-
initial: 0,
|
|
106
|
-
choices: [
|
|
107
|
-
{
|
|
108
|
-
title: "Cancel operation",
|
|
109
|
-
value: "cancel"
|
|
110
|
-
},
|
|
111
|
-
{
|
|
112
|
-
title: "Remove existing files and continue",
|
|
113
|
-
value: "overwrite"
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
title: "Ignore files and continue",
|
|
117
|
-
value: "ignore"
|
|
118
|
-
}
|
|
119
|
-
]
|
|
85
|
+
const projectName = {
|
|
86
|
+
type: targetDir ? null : "text",
|
|
87
|
+
name: "projectName",
|
|
88
|
+
message: styleTitle("Project name:"),
|
|
89
|
+
initial: DEFAULT_PROJECT_NAME
|
|
90
|
+
};
|
|
91
|
+
const overwrite = {
|
|
92
|
+
type: (nameAnswer) => {
|
|
93
|
+
const dir = nameAnswer ?? targetDir;
|
|
94
|
+
return existsSync(dir) && !isEmptyFolder(dir) ? "select" : null;
|
|
120
95
|
},
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
},
|
|
126
|
-
name: "overwriteChecker"
|
|
96
|
+
name: "overwrite",
|
|
97
|
+
message: (nameAnswer) => {
|
|
98
|
+
const dirName = nameAnswer ?? targetDir;
|
|
99
|
+
return styleTitle(`${dirName === "." ? "current directory" : `Target directory "${dirName}"`} is not empty. Please choose how to proceed:`);
|
|
127
100
|
},
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
101
|
+
initial: 0,
|
|
102
|
+
choices: [
|
|
103
|
+
{
|
|
104
|
+
title: "Cancel operation",
|
|
105
|
+
value: "cancel"
|
|
131
106
|
},
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
return toValidPackageName(getProjectName(nameAnswer ?? targetDir));
|
|
107
|
+
{
|
|
108
|
+
title: "Remove existing files and continue",
|
|
109
|
+
value: "overwrite"
|
|
136
110
|
},
|
|
137
|
-
|
|
138
|
-
|
|
111
|
+
{
|
|
112
|
+
title: "Ignore files and continue",
|
|
113
|
+
value: "ignore"
|
|
139
114
|
}
|
|
115
|
+
]
|
|
116
|
+
};
|
|
117
|
+
const overwriteChecker = {
|
|
118
|
+
type: (overwriteAnswer) => {
|
|
119
|
+
if (overwriteAnswer === "cancel") onCancel();
|
|
120
|
+
return null;
|
|
140
121
|
},
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
choices: [{
|
|
147
|
-
title: colors.green("Vue"),
|
|
148
|
-
value: "vue"
|
|
149
|
-
}, {
|
|
150
|
-
title: colors.blue("React"),
|
|
151
|
-
value: "react"
|
|
152
|
-
}]
|
|
122
|
+
name: "overwriteChecker"
|
|
123
|
+
};
|
|
124
|
+
const packageName = {
|
|
125
|
+
type: (_, { projectName: nameAnswer }) => {
|
|
126
|
+
return isValidPackageName(getProjectName(nameAnswer ?? targetDir)) ? null : "text";
|
|
153
127
|
},
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
value: "github-actions"
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
title: "GitLab CI",
|
|
165
|
-
value: "gitlab-ci"
|
|
166
|
-
},
|
|
167
|
-
{
|
|
168
|
-
title: "None",
|
|
169
|
-
value: "none"
|
|
170
|
-
}
|
|
171
|
-
],
|
|
172
|
-
initial: 0
|
|
128
|
+
name: "packageName",
|
|
129
|
+
message: styleTitle("Package name:"),
|
|
130
|
+
initial: (_, { projectName: nameAnswer }) => {
|
|
131
|
+
return toValidPackageName(getProjectName(nameAnswer ?? targetDir));
|
|
132
|
+
},
|
|
133
|
+
validate: (name) => {
|
|
134
|
+
return isValidPackageName(name) || "Invalid package.json name";
|
|
173
135
|
}
|
|
136
|
+
};
|
|
137
|
+
const framework = {
|
|
138
|
+
type: builtinTemplate(template) ? null : "select",
|
|
139
|
+
name: "framework",
|
|
140
|
+
message: styleTitle(template ? `${colors.red(template)} isn't a valid template. Please choose from below:` : "Select a framework:"),
|
|
141
|
+
initial: 0,
|
|
142
|
+
choices: [{
|
|
143
|
+
title: colors.green("Vue"),
|
|
144
|
+
value: "vue"
|
|
145
|
+
}, {
|
|
146
|
+
title: colors.blue("React"),
|
|
147
|
+
value: "react"
|
|
148
|
+
}]
|
|
149
|
+
};
|
|
150
|
+
const ciPrompt = {
|
|
151
|
+
type: checkCi(ci) ? null : "select",
|
|
152
|
+
name: "ci",
|
|
153
|
+
message: styleTitle(ci ? `${colors.red(ci)} isn't a valid CI/CD. Please choose from below:` : "Choose CI/CD:"),
|
|
154
|
+
choices: [
|
|
155
|
+
{
|
|
156
|
+
title: "GitHub Actions",
|
|
157
|
+
value: "github-actions"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
title: "GitLab CI",
|
|
161
|
+
value: "gitlab-ci"
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
title: "None",
|
|
165
|
+
value: "none"
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
initial: 0
|
|
169
|
+
};
|
|
170
|
+
return prompts([
|
|
171
|
+
projectName,
|
|
172
|
+
overwrite,
|
|
173
|
+
overwriteChecker,
|
|
174
|
+
packageName,
|
|
175
|
+
framework,
|
|
176
|
+
ciPrompt
|
|
174
177
|
], { onCancel });
|
|
175
178
|
}
|
|
176
|
-
|
|
177
179
|
//#endregion
|
|
178
180
|
//#region src/index.ts
|
|
179
181
|
const cwd = process.cwd();
|
|
@@ -223,9 +225,9 @@ async function writePackageJson(root, dir, packageName) {
|
|
|
223
225
|
}
|
|
224
226
|
async function copyFolder(root, dir, packageName, ci) {
|
|
225
227
|
const files = await readdir(dir);
|
|
226
|
-
const ignoreFiles = new Set(["package.json", ...ciFilters[ci] || ciFilters.none]);
|
|
228
|
+
const ignoreFiles = /* @__PURE__ */ new Set(["package.json", ...ciFilters[ci] || ciFilters.none]);
|
|
227
229
|
const filterFiles = files.filter((file) => !ignoreFiles.has(file));
|
|
228
|
-
const rewriteOrCopyFile = (file) => {
|
|
230
|
+
const rewriteOrCopyFile = async (file) => {
|
|
229
231
|
const targetPath = path.join(root, renameFiles?.[file] ?? file);
|
|
230
232
|
const templatePath = path.join(dir, file);
|
|
231
233
|
if (file.endsWith(".art")) {
|
|
@@ -240,8 +242,10 @@ async function copyFolder(root, dir, packageName, ci) {
|
|
|
240
242
|
}
|
|
241
243
|
if (statSync(templatePath).isDirectory()) {
|
|
242
244
|
const rewriteOrCopySubFilePromises = readdirSync(templatePath).map((subFile) => rewriteOrCopyFile(path.join(file, subFile)));
|
|
243
|
-
|
|
244
|
-
|
|
245
|
+
await Promise.all(rewriteOrCopySubFilePromises);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
return copy(templatePath, targetPath);
|
|
245
249
|
};
|
|
246
250
|
return Promise.all(filterFiles.map((file) => rewriteOrCopyFile(file)));
|
|
247
251
|
}
|
|
@@ -272,12 +276,13 @@ async function createApp() {
|
|
|
272
276
|
const filePath = fileURLToPath(import.meta.url);
|
|
273
277
|
const sharedDir = path.resolve(filePath, "../..", "template-shared");
|
|
274
278
|
const templateDir = path.resolve(filePath, "../..", `template-${framework}`);
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
279
|
+
try {
|
|
280
|
+
await Promise.all([
|
|
281
|
+
copyFolder(root, sharedDir, packageName, ciName),
|
|
282
|
+
copyFolder(root, templateDir, packageName, ciName),
|
|
283
|
+
writePackageJson(root, templateDir, packageName),
|
|
284
|
+
new Promise((resolve) => setTimeout(resolve, 300))
|
|
285
|
+
]);
|
|
281
286
|
const cdProjectName = path.relative(cwd, root);
|
|
282
287
|
const formattedCdProjectName = cdProjectName.includes(" ") ? `"${cdProjectName}"` : cdProjectName;
|
|
283
288
|
spinner.succeed(`${colors.green("Scaffolded project in")} ${colors.yellow(root)}`);
|
|
@@ -291,13 +296,17 @@ async function createApp() {
|
|
|
291
296
|
console.log(colors.cyan(` ${pkgManager} run dev`));
|
|
292
297
|
}
|
|
293
298
|
console.log();
|
|
294
|
-
}
|
|
299
|
+
} catch {
|
|
295
300
|
spinner.fail(colors.red("Failed to scaffold project"));
|
|
296
|
-
}
|
|
301
|
+
}
|
|
297
302
|
}
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
303
|
+
async function bootstrap() {
|
|
304
|
+
try {
|
|
305
|
+
await createApp();
|
|
306
|
+
} catch (error) {
|
|
307
|
+
console.error(colors.red(error instanceof Error ? error.message : String(error)));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
bootstrap();
|
|
302
311
|
//#endregion
|
|
303
|
-
export {
|
|
312
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@component-hook/create-app",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.5.1",
|
|
5
5
|
"description": "Scaffolding a basic typescript project",
|
|
6
6
|
"author": "Tzuyi",
|
|
7
7
|
"license": "MIT",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"art-template": "^4.13.4",
|
|
30
|
-
"fs-extra": "^11.
|
|
30
|
+
"fs-extra": "^11.4.0",
|
|
31
31
|
"minimist": "^1.2.8",
|
|
32
|
-
"ora": "^9.
|
|
32
|
+
"ora": "^9.4.1",
|
|
33
33
|
"picocolors": "^1.1.1",
|
|
34
34
|
"prompts": "^2.4.2"
|
|
35
35
|
},
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"editor.tabSize": 2,
|
|
3
|
-
"editor.formatOnSave": true,
|
|
4
|
-
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
5
|
-
|
|
6
|
-
"[json]": {
|
|
7
|
-
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
8
|
-
},
|
|
9
|
-
|
|
10
|
-
"debug.javascript.autoAttachFilter": "smart",
|
|
11
|
-
|
|
12
|
-
"typescript.tsdk": "node_modules/typescript/lib",
|
|
13
|
-
|
|
14
|
-
"workbench.editor.splitInGroupLayout": "vertical",
|
|
15
|
-
"terminal.integrated.shellIntegration.enabled": true,
|
|
16
|
-
|
|
17
|
-
"cSpell.words": ["dotenv", "unplugin", "Sonarjs", "typecheck", "pxtorem", "norem", "partialize"]
|
|
18
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# {{projectName}}
|
|
2
|
-
|
|
3
|
-
This project is built with [react](https://react.dev/) + [vite](https://vite.dev/), using [pnpm](https://pnpm.io/) for package management and [zustand](https://zustand-demo.pmnd.rs/) for state management.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- **Development Experience**: Written in [typeScript](https://www.typescriptlang.org/), styled with [tailwindcss](https://tailwindcss.com/), and structured with [react-router-dom](https://reactrouter.com/), and [react-i18next](https://react.i18next.com/). Supports SVGs ([vite-plugin-svg-icons](https://github.com/vbenjs/vite-plugin-svg-icons)).
|
|
8
|
-
- **API & Mocking**: Handles requests with [axios](https://axios-http.com/docs/intro) and mocks APIs using [msw](https://mswjs.io/).
|
|
9
|
-
- **Code Quality**: Enforces standards with [eslint](https://eslint.org/) and [prettier](https://prettier.io/), with [husky](https://www.npmjs.com/package/husky), [commitlint](https://commitlint.js.org/#/), and [lint-staged](https://github.com/okonet/lint-staged) for commit validation.
|
|
10
|
-
- **Testing**: Uses [vitest](https://vitest.dev/) + [testing-library](https://testing-library.com/) for unit testing and [playwright](https://playwright.dev/) for E2E testing.
|
|
11
|
-
- **Optimization**: Compresses CSS with [cssnano](https://cssnano.github.io/cssnano/), converts units with [postcss-pxtorem](https://www.npmjs.com/package/postcss-pxtorem), convert modern CSS into something most browsers can understand with [postcss-preset-env](https://www.npmjs.com/package/postcss-preset-env), and analyzes assets with [rollup-plugin-visualizer](https://github.com/btd/rollup-plugin-visualizer).
|
|
12
|
-
{{ciContent}}
|
|
13
|
-
|
|
14
|
-
## Code Features / Opinions
|
|
15
|
-
|
|
16
|
-
- Project root aliased as `@` to `<project_root>/src`
|
|
17
|
-
- ESlint plugin use [@component-hook/eslint-plugin](https://www.npmjs.com/package/@component-hook/eslint-plugin?activeTab=readme)
|
|
18
|
-
- if encounter npx: command not found, can execute `ln -s $(which npx) /usr/local/bin/npx` in zsh
|
|
19
|
-
- Predefined and fully typed global variables:
|
|
20
|
-
- `VITE_APP_VERSION` is read from `package.json` version at build time
|
|
21
|
-
- `VITE_APP_LAST_COMMIT_HASH` is last commit hash at build time
|
|
22
|
-
- `VITE_APP_BUILD_EPOCH` is populated as `Date.now()` at build time
|
|
23
|
-
- `VITE_APP_MOCK` is use mock environment
|
|
24
|
-
|
|
25
|
-
## Project Files
|
|
26
|
-
|
|
27
|
-
```text
|
|
28
|
-
src/
|
|
29
|
-
├── __tests__/
|
|
30
|
-
│ ├── __mocks__/
|
|
31
|
-
│ ├── e2e/*
|
|
32
|
-
│ └── unit/*
|
|
33
|
-
├── assets/*
|
|
34
|
-
│ └── svg-icons
|
|
35
|
-
│ └── react.svg
|
|
36
|
-
├── components/
|
|
37
|
-
│ └── common
|
|
38
|
-
│ ├── svg-icon/
|
|
39
|
-
│ │ ├── src
|
|
40
|
-
│ │ │ └── index.tsx
|
|
41
|
-
│ │ └── index.ts
|
|
42
|
-
│ └── index.ts
|
|
43
|
-
├── constants/*
|
|
44
|
-
├── hooks/*
|
|
45
|
-
├── mocks/
|
|
46
|
-
│ ├── handlers/*
|
|
47
|
-
│ │ ├── modules/*
|
|
48
|
-
│ │ └── index.ts
|
|
49
|
-
│ │── browser.ts
|
|
50
|
-
│ └── server.ts
|
|
51
|
-
├── pages/
|
|
52
|
-
│ └── home/
|
|
53
|
-
│ ├── components
|
|
54
|
-
│ │ └── hello-world
|
|
55
|
-
│ │ └── index.tsx
|
|
56
|
-
│ ├── index.module.css
|
|
57
|
-
│ └── index.tsx
|
|
58
|
-
├── plugins/
|
|
59
|
-
│ └── i18n
|
|
60
|
-
│ ├── locales
|
|
61
|
-
│ │ ├── es-US.json
|
|
62
|
-
│ │ └── zh-TW.json
|
|
63
|
-
│ └── index.ts
|
|
64
|
-
├── router/
|
|
65
|
-
│ └── index.tsx
|
|
66
|
-
├── stores/
|
|
67
|
-
│ ├── modules
|
|
68
|
-
│ │ └── config.ts
|
|
69
|
-
│ ├── constants.ts
|
|
70
|
-
│ ├── index.ts
|
|
71
|
-
│ └── middleware.ts
|
|
72
|
-
├── styles/
|
|
73
|
-
│ ├── common/
|
|
74
|
-
| │ ├── all.css
|
|
75
|
-
| │ └── button.css
|
|
76
|
-
│ ├── base.css
|
|
77
|
-
│ ├── index.css
|
|
78
|
-
│ └── tailwind.css
|
|
79
|
-
├── types/*
|
|
80
|
-
├── utils/*
|
|
81
|
-
├── App.tsx
|
|
82
|
-
├── vite-env.d.ts
|
|
83
|
-
└── main.tsx
|
|
84
|
-
```
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import componentHookPlugin from '@component-hook/eslint-plugin';
|
|
2
|
-
|
|
3
|
-
export default [
|
|
4
|
-
...componentHookPlugin.configs.basic,
|
|
5
|
-
...componentHookPlugin.configs.react,
|
|
6
|
-
componentHookPlugin.configs.prettier,
|
|
7
|
-
...componentHookPlugin.configs.sonarjs,
|
|
8
|
-
componentHookPlugin.configs.security,
|
|
9
|
-
...componentHookPlugin.configs.markdown,
|
|
10
|
-
{
|
|
11
|
-
files: ['**/__tests__/unit/**/*.test.[jt]s?(x)'],
|
|
12
|
-
...componentHookPlugin.configs['testing-library/react'],
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
files: ['**/__tests__/e2e/**/*.spec.[jt]s?(x)'],
|
|
16
|
-
...componentHookPlugin.configs.playwright,
|
|
17
|
-
},
|
|
18
|
-
];
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
-
<title>{{projectName}}</title>
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div id="root"></div>
|
|
11
|
-
<script type="module" src="/src/main.tsx"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "react-starter",
|
|
3
|
-
"type": "module",
|
|
4
|
-
"version": "0.0.0",
|
|
5
|
-
"private": true,
|
|
6
|
-
"packageManager": "pnpm@10.29.2",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"dev": "vite",
|
|
9
|
-
"dev:mock": "MOCK=true vite",
|
|
10
|
-
"build": "pnpm typecheck && vite build",
|
|
11
|
-
"lint": "eslint --fix .",
|
|
12
|
-
"test:unit": "vitest --coverage",
|
|
13
|
-
"test:e2e": "playwright test --headed",
|
|
14
|
-
"test:ui-e2e": "playwright test --ui",
|
|
15
|
-
"test:ci-e2e": "playwright test",
|
|
16
|
-
"typecheck": "tsc --noEmit",
|
|
17
|
-
"prepare": "husky",
|
|
18
|
-
"preview": "vite preview"
|
|
19
|
-
},
|
|
20
|
-
"dependencies": {
|
|
21
|
-
"axios": "^1.13.5",
|
|
22
|
-
"i18next": "^25.8.4",
|
|
23
|
-
"react": "^19.2.4",
|
|
24
|
-
"react-dom": "^19.2.4",
|
|
25
|
-
"react-i18next": "^16.5.4",
|
|
26
|
-
"react-router-dom": "^7.13.0",
|
|
27
|
-
"zustand": "^5.0.11"
|
|
28
|
-
},
|
|
29
|
-
"optionalDependencies": {
|
|
30
|
-
"@commitlint/cli": "^20.4.1",
|
|
31
|
-
"@commitlint/config-conventional": "^20.4.1",
|
|
32
|
-
"husky": "^9.1.7",
|
|
33
|
-
"lint-staged": "^16.2.7"
|
|
34
|
-
},
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"@component-hook/eslint-plugin": "^1.4.2",
|
|
37
|
-
"@playwright/test": "^1.58.2",
|
|
38
|
-
"@tailwindcss/vite": "^4.1.18",
|
|
39
|
-
"@testing-library/jest-dom": "^6.9.1",
|
|
40
|
-
"@testing-library/react": "^16.3.2",
|
|
41
|
-
"@testing-library/user-event": "^14.6.1",
|
|
42
|
-
"@tsconfig/node24": "^24.0.4",
|
|
43
|
-
"@types/node": "^22.19.11",
|
|
44
|
-
"@types/react": "^19.2.13",
|
|
45
|
-
"@types/react-dom": "^19.2.3",
|
|
46
|
-
"@vitejs/plugin-react-swc": "^4.2.3",
|
|
47
|
-
"@vitest/coverage-v8": "^4.0.18",
|
|
48
|
-
"cssnano": "^7.1.2",
|
|
49
|
-
"eslint": "^9.39.2",
|
|
50
|
-
"fast-glob": "^3.3.3",
|
|
51
|
-
"jsdom": "^28.1.0",
|
|
52
|
-
"msw": "^2.12.10",
|
|
53
|
-
"postcss": "^8.5.6",
|
|
54
|
-
"postcss-preset-env": "^11.1.3",
|
|
55
|
-
"postcss-pxtorem": "^6.1.0",
|
|
56
|
-
"prettier": "^3.8.1",
|
|
57
|
-
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
58
|
-
"tailwindcss": "^4.1.18",
|
|
59
|
-
"typescript": "^5.9.3",
|
|
60
|
-
"vite": "^7.3.1",
|
|
61
|
-
"vite-plugin-svg-icons": "^2.0.1",
|
|
62
|
-
"vitest": "^4.0.18"
|
|
63
|
-
},
|
|
64
|
-
"msw": {
|
|
65
|
-
"workerDirectory": [
|
|
66
|
-
"public"
|
|
67
|
-
]
|
|
68
|
-
}
|
|
69
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { expect, test } from '@playwright/test';
|
|
2
|
-
|
|
3
|
-
test.beforeEach(async ({ page }) => {
|
|
4
|
-
await page.goto('/');
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
test('click Vite logo open Vite official website blank tab', async ({ page }) => {
|
|
8
|
-
const link = page.getByRole('link', { name: /vite logo/i });
|
|
9
|
-
|
|
10
|
-
await expect(link).toBeVisible();
|
|
11
|
-
const [popup] = await Promise.all([page.waitForEvent('popup'), link.evaluate((node: HTMLElement) => node.click())]);
|
|
12
|
-
|
|
13
|
-
await popup.waitForLoadState();
|
|
14
|
-
await expect(popup).toHaveTitle('Vite | Next Generation Frontend Tooling');
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
test('click React logo open React official website blank tab', async ({ page }) => {
|
|
18
|
-
await page.addStyleTag({ content: `* { animation: none !important; }` });
|
|
19
|
-
const link = page.getByLabel(/react link/i);
|
|
20
|
-
|
|
21
|
-
await expect(link).toBeVisible();
|
|
22
|
-
const [popup] = await Promise.all([page.waitForEvent('popup'), link.evaluate((node: HTMLElement) => node.click())]);
|
|
23
|
-
|
|
24
|
-
await popup.waitForLoadState();
|
|
25
|
-
await expect(popup).toHaveTitle('React');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('homepage count button interact', async ({ page }) => {
|
|
29
|
-
await page.getByRole('button', { name: 'count is 0' }).click();
|
|
30
|
-
await expect(page.getByRole('button', { name: 'count is 1' })).toBeVisible();
|
|
31
|
-
await page.getByRole('button', { name: 'count is 1' }).click();
|
|
32
|
-
await expect(page.getByRole('button', { name: 'count is 2' })).toBeVisible();
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
test('locale change interact', async ({ page }) => {
|
|
36
|
-
await page.getByRole('button', { name: 'English' }).click();
|
|
37
|
-
await expect(page.getByRole('button', { name: '中文' })).toBeVisible();
|
|
38
|
-
await page.getByRole('button', { name: '中文' }).click();
|
|
39
|
-
await expect(page.getByRole('button', { name: 'English' })).toBeVisible();
|
|
40
|
-
});
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { screen } from '@testing-library/react';
|
|
2
|
-
import userEvent from '@testing-library/user-event';
|
|
3
|
-
import { renderComponent } from '@/__tests__/unit/render';
|
|
4
|
-
import { HelloWorld } from '@/pages/home/components/hello-world';
|
|
5
|
-
import i18n from '@/plugins/i18n';
|
|
6
|
-
import { useConfigStore } from '@/stores';
|
|
7
|
-
|
|
8
|
-
describe('home page HelloWorld component', () => {
|
|
9
|
-
const { t } = i18n;
|
|
10
|
-
|
|
11
|
-
it('renders the correct content', () => {
|
|
12
|
-
const title = 'Hello World!';
|
|
13
|
-
const { appMeta } = useConfigStore.getState();
|
|
14
|
-
|
|
15
|
-
renderComponent(<HelloWorld title={title} />);
|
|
16
|
-
expect(screen.getByRole('heading', { name: title })).toBeInTheDocument();
|
|
17
|
-
expect(screen.getByRole('button', { name: /count is 0/i })).toBeInTheDocument();
|
|
18
|
-
expect(screen.getByText(/edit/i)).toBeInTheDocument();
|
|
19
|
-
expect(screen.getByText(/src\/app\.tsx/i)).toBeInTheDocument();
|
|
20
|
-
expect(screen.getByText(/save to test hmr/i)).toBeInTheDocument();
|
|
21
|
-
expect(screen.getByText(/click on the vite and react logos to learn more/i)).toBeInTheDocument();
|
|
22
|
-
expect(screen.getByText(`${appMeta.version} - Built at: ${appMeta.builtAt.toLocaleString()}`)).toBeInTheDocument();
|
|
23
|
-
expect(screen.getByRole('button', { name: t('language') })).toBeInTheDocument();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('count button interact', async () => {
|
|
27
|
-
renderComponent(<HelloWorld />);
|
|
28
|
-
const countButton = screen.getByRole('button', { name: /count is 0/i });
|
|
29
|
-
|
|
30
|
-
await userEvent.click(countButton);
|
|
31
|
-
expect(countButton).toHaveTextContent(/count is 1/i);
|
|
32
|
-
await userEvent.click(countButton);
|
|
33
|
-
await userEvent.click(countButton);
|
|
34
|
-
expect(countButton).toHaveTextContent(/count is 3/i);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('locale change interact', async () => {
|
|
38
|
-
renderComponent(<HelloWorld />);
|
|
39
|
-
const localeButton = screen.getByRole('button', { name: t('language') });
|
|
40
|
-
|
|
41
|
-
expect(localeButton).toHaveTextContent('English');
|
|
42
|
-
await userEvent.click(localeButton);
|
|
43
|
-
expect(localeButton).toHaveTextContent('中文');
|
|
44
|
-
});
|
|
45
|
-
});
|