@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.
Files changed (107) hide show
  1. package/{template-shared/LICENSE → LICENSE} +1 -1
  2. package/README.md +1 -1
  3. package/dist/index.d.mts +1 -1
  4. package/dist/index.mjs +110 -101
  5. package/package.json +3 -3
  6. package/template-react/.vscode/extensions.json +0 -9
  7. package/template-react/.vscode/settings.json +0 -18
  8. package/template-react/README.md.art +0 -84
  9. package/template-react/eslint.config.js +0 -18
  10. package/template-react/index.html.art +0 -13
  11. package/template-react/lint-staged.config.js +0 -6
  12. package/template-react/package.json +0 -69
  13. package/template-react/public/vite.svg +0 -1
  14. package/template-react/src/App.tsx +0 -6
  15. package/template-react/src/__tests__/e2e/home.spec.ts +0 -40
  16. package/template-react/src/__tests__/unit/pages/home/components/HelloWorld.test.tsx +0 -45
  17. package/template-react/src/__tests__/unit/pages/home/index.test.tsx +0 -19
  18. package/template-react/src/__tests__/unit/render/index.tsx +0 -15
  19. package/template-react/src/assets/svg-icons/react.svg +0 -1
  20. package/template-react/src/components/common/index.ts +0 -1
  21. package/template-react/src/components/common/svg-icon/index.ts +0 -1
  22. package/template-react/src/components/common/svg-icon/src/index.tsx +0 -23
  23. package/template-react/src/main.tsx +0 -26
  24. package/template-react/src/mocks/browser.ts +0 -4
  25. package/template-react/src/mocks/handlers/index.ts +0 -1
  26. package/template-react/src/mocks/server.ts +0 -4
  27. package/template-react/src/pages/home/components/hello-world/index.tsx +0 -49
  28. package/template-react/src/pages/home/index.module.css +0 -8
  29. package/template-react/src/pages/home/index.tsx +0 -37
  30. package/template-react/src/plugins/i18n/index.ts +0 -15
  31. package/template-react/src/plugins/i18n/locales/en-US.json +0 -3
  32. package/template-react/src/plugins/i18n/locales/zh-TW.json +0 -3
  33. package/template-react/src/router/index.tsx +0 -22
  34. package/template-react/src/stores/constants.ts +0 -1
  35. package/template-react/src/stores/index.ts +0 -1
  36. package/template-react/src/stores/middleware.ts +0 -17
  37. package/template-react/src/stores/modules/config.ts +0 -27
  38. package/template-react/src/styles/base.css +0 -68
  39. package/template-react/src/styles/common/all.css +0 -1
  40. package/template-react/src/styles/common/button.css +0 -26
  41. package/template-react/src/styles/index.css +0 -3
  42. package/template-react/src/styles/tailwind.css +0 -14
  43. package/template-react/src/vite-env.d.ts +0 -9
  44. package/template-react/tsconfig.app.json +0 -32
  45. package/template-react/tsconfig.vitest.json +0 -10
  46. package/template-react/vite.config.ts +0 -40
  47. package/template-react/vitest.config.ts +0 -18
  48. package/template-react/vitest.setup.ts +0 -13
  49. package/template-shared/.browserslistrc +0 -10
  50. package/template-shared/.github/actions/setup-environment/action.yml +0 -21
  51. package/template-shared/.github/renovate.json +0 -14
  52. package/template-shared/.github/workflows/ci.yml +0 -22
  53. package/template-shared/.github/workflows/deploy.yml +0 -27
  54. package/template-shared/.github/workflows/preview.yml +0 -17
  55. package/template-shared/.gitlab/setup.yml +0 -18
  56. package/template-shared/.husky/commit-msg +0 -1
  57. package/template-shared/.husky/pre-commit +0 -1
  58. package/template-shared/_gitignore +0 -32
  59. package/template-shared/_gitlab-ci.yml +0 -22
  60. package/template-shared/commitlint.config.js +0 -3
  61. package/template-shared/playwright.config.ts +0 -49
  62. package/template-shared/postcss.config.js +0 -15
  63. package/template-shared/prettier.config.js +0 -13
  64. package/template-shared/scripts/build-info/index.ts +0 -21
  65. package/template-shared/tsconfig.json +0 -14
  66. package/template-shared/tsconfig.node.json +0 -12
  67. package/template-vue/.vscode/extensions.json +0 -10
  68. package/template-vue/.vscode/settings.json +0 -18
  69. package/template-vue/README.md.art +0 -80
  70. package/template-vue/eslint.config.js +0 -18
  71. package/template-vue/index.html.art +0 -28
  72. package/template-vue/lint-staged.config.js +0 -6
  73. package/template-vue/package.json +0 -68
  74. package/template-vue/public/vite.svg +0 -1
  75. package/template-vue/src/App.vue +0 -7
  76. package/template-vue/src/__tests__/e2e/home.spec.ts +0 -61
  77. package/template-vue/src/__tests__/unit/pages/home/components/HelloWorld.test.ts +0 -57
  78. package/template-vue/src/__tests__/unit/pages/home/index.test.ts +0 -17
  79. package/template-vue/src/__tests__/unit/render/index.ts +0 -28
  80. package/template-vue/src/assets/svg-icons/vue.svg +0 -1
  81. package/template-vue/src/components/common/index.ts +0 -1
  82. package/template-vue/src/components/common/svg-icon/index.ts +0 -1
  83. package/template-vue/src/components/common/svg-icon/src/index.vue +0 -29
  84. package/template-vue/src/locales/en-US.json +0 -3
  85. package/template-vue/src/locales/zh-TW.json +0 -3
  86. package/template-vue/src/main.ts +0 -21
  87. package/template-vue/src/mocks/browser.ts +0 -4
  88. package/template-vue/src/mocks/handlers/index.ts +0 -1
  89. package/template-vue/src/mocks/server.ts +0 -4
  90. package/template-vue/src/pages/home/components/HelloWorld.vue +0 -54
  91. package/template-vue/src/pages/home/index.vue +0 -46
  92. package/template-vue/src/plugins/i18n.ts +0 -11
  93. package/template-vue/src/router/index.ts +0 -23
  94. package/template-vue/src/stores/constants.ts.art +0 -1
  95. package/template-vue/src/stores/index.ts +0 -15
  96. package/template-vue/src/stores/modules/config.ts +0 -20
  97. package/template-vue/src/styles/base.css +0 -59
  98. package/template-vue/src/styles/common/all.css +0 -1
  99. package/template-vue/src/styles/common/button.css +0 -15
  100. package/template-vue/src/styles/index.css +0 -3
  101. package/template-vue/src/styles/tailwind.css +0 -1
  102. package/template-vue/src/vite-env.d.ts +0 -9
  103. package/template-vue/tsconfig.app.json +0 -12
  104. package/template-vue/tsconfig.vitest.json +0 -10
  105. package/template-vue/vite.config.ts +0 -46
  106. package/template-vue/vitest.config.ts +0 -18
  107. package/template-vue/vitest.setup.ts +0 -12
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c)
3
+ Copyright (c) 2023 Tzuyi
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
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
- return prompts([
89
- {
90
- type: targetDir ? null : "text",
91
- name: "projectName",
92
- message: styleTitle("Project name:"),
93
- initial: DEFAULT_PROJECT_NAME
94
- },
95
- {
96
- type: (nameAnswer) => {
97
- const dir = nameAnswer ?? targetDir;
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
- type: (overwriteAnswer) => {
123
- if (overwriteAnswer === "cancel") onCancel();
124
- return null;
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
- type: (_, { projectName: nameAnswer }) => {
130
- return isValidPackageName(getProjectName(nameAnswer ?? targetDir)) ? null : "text";
101
+ initial: 0,
102
+ choices: [
103
+ {
104
+ title: "Cancel operation",
105
+ value: "cancel"
131
106
  },
132
- name: "packageName",
133
- message: styleTitle("Package name:"),
134
- initial: (_, { projectName: nameAnswer }) => {
135
- return toValidPackageName(getProjectName(nameAnswer ?? targetDir));
107
+ {
108
+ title: "Remove existing files and continue",
109
+ value: "overwrite"
136
110
  },
137
- validate: (name) => {
138
- return isValidPackageName(name) || "Invalid package.json name";
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
- type: builtinTemplate(template) ? null : "select",
143
- name: "framework",
144
- message: template ? styleTitle(`${colors.red(template)} isn't a valid template. Please choose from below:`) : styleTitle("Select a framework:"),
145
- initial: 0,
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
- type: checkCi(ci) ? null : "select",
156
- name: "ci",
157
- message: ci ? styleTitle(`${colors.red(ci)} isn't a valid CI/CD. Please choose from below:`) : styleTitle("Choose CI/CD:"),
158
- choices: [
159
- {
160
- title: "GitHub Actions",
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
- return Promise.all(rewriteOrCopySubFilePromises).then(() => {});
244
- } else return copy(templatePath, targetPath);
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
- Promise.all([
276
- copyFolder(root, sharedDir, packageName, ciName),
277
- copyFolder(root, templateDir, packageName, ciName),
278
- writePackageJson(root, templateDir, packageName),
279
- new Promise((resolve) => setTimeout(resolve, 300))
280
- ]).then(() => {
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
- }).catch(() => {
299
+ } catch {
295
300
  spinner.fail(colors.red("Failed to scaffold project"));
296
- });
301
+ }
297
302
  }
298
- createApp().catch((error) => {
299
- console.error(colors.red(error.message));
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.4",
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.3.3",
30
+ "fs-extra": "^11.4.0",
31
31
  "minimist": "^1.2.8",
32
- "ora": "^9.3.0",
32
+ "ora": "^9.4.1",
33
33
  "picocolors": "^1.1.1",
34
34
  "prompts": "^2.4.2"
35
35
  },
@@ -1,9 +0,0 @@
1
- {
2
- "recommendations": [
3
- "bradlc.vscode-tailwindcss",
4
- "esbenp.prettier-vscode",
5
- "csstools.postcss",
6
- "cpylua.language-postcss",
7
- "streetsidesoftware.code-spell-checker"
8
- ]
9
- }
@@ -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,6 +0,0 @@
1
- /** @type {import('lint-staged').Configuration} */
2
-
3
- export default {
4
- '*.{ts,tsx}': () => 'pnpm typecheck',
5
- '*.{ts,js,tsx,jsx}': () => 'pnpm lint',
6
- };
@@ -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,6 +0,0 @@
1
- import { RouterProvider } from 'react-router-dom';
2
- import router from './router';
3
-
4
- export function App() {
5
- return <RouterProvider router={router} />;
6
- }
@@ -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
- });