├── .node-version ├── .prettierignore ├── template ├── stylelint │ ├── _stylelintignore │ └── stylelint.config.js ├── vitest │ ├── _stylelintignore │ ├── _gitignore │ ├── __tests__ │ │ └── utils │ │ │ └── index.spec.js │ ├── src │ │ └── utils │ │ │ └── index.js │ └── vitest.config.js ├── base │ ├── _npmrc │ ├── src │ │ ├── pages │ │ │ ├── home │ │ │ │ ├── index.json │ │ │ │ ├── index.html │ │ │ │ └── index.css │ │ │ └── mine │ │ │ │ ├── index.json │ │ │ │ ├── index.html │ │ │ │ └── index.css │ │ ├── images │ │ │ ├── home-dark.png │ │ │ ├── home-light.png │ │ │ ├── mine-dark.png │ │ │ ├── mine-light.png │ │ │ ├── vue-mini.png │ │ │ ├── home-dark-selected.png │ │ │ ├── home-light-selected.png │ │ │ ├── mine-dark-selected.png │ │ │ └── mine-light-selected.png │ │ ├── sitemap.json │ │ ├── app.css │ │ ├── theme.json │ │ └── app.json │ ├── _gitignore │ ├── _editorconfig │ ├── postcss.config.js │ ├── project.config.json │ └── package.json.ejs ├── vitest-typescript │ ├── _stylelintignore │ ├── _gitignore │ ├── __tests__ │ │ └── utils │ │ │ └── index.spec.ts │ ├── src │ │ └── utils │ │ │ └── index.ts │ ├── vitest.config.ts │ └── tsconfig.json ├── vitest-pnpm │ └── pnpm-workspace.yaml ├── pinia │ └── src │ │ ├── pinia.js │ │ ├── app.js │ │ └── stores │ │ └── count.js ├── prettier │ └── prettier.config.js ├── pinia-typescript │ └── src │ │ ├── pinia.ts │ │ ├── app.ts │ │ └── stores │ │ └── count.ts ├── javascript │ ├── src │ │ ├── app.js │ │ └── pages │ │ │ ├── mine │ │ │ └── index.js │ │ │ └── home │ │ │ └── index.js │ ├── babel.config.js │ └── build.js ├── typescript │ ├── src │ │ ├── app.ts │ │ └── pages │ │ │ ├── mine │ │ │ └── index.ts │ │ │ └── home │ │ │ └── index.ts │ ├── tsconfig.json │ ├── babel.config.js │ └── build.js └── eslint │ └── eslint.config.js.ejs ├── .gitignore ├── media └── screenshot-cli-v2.png ├── .vscode └── settings.json ├── prettier.config.js ├── .editorconfig ├── .github └── workflows │ └── ci.yml ├── tsconfig.json ├── eslint.config.js ├── LICENSE ├── package.json ├── README.md ├── index.ts └── pnpm-lock.yaml /.node-version: -------------------------------------------------------------------------------- 1 | v25.2.1 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | template 2 | -------------------------------------------------------------------------------- /template/stylelint/_stylelintignore: -------------------------------------------------------------------------------- 1 | dist 2 | -------------------------------------------------------------------------------- /template/vitest/_stylelintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | dist 3 | -------------------------------------------------------------------------------- /template/base/_npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmmirror.com/ 2 | -------------------------------------------------------------------------------- /template/vitest-typescript/_stylelintignore: -------------------------------------------------------------------------------- 1 | coverage 2 | dist 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | *.log* 4 | 5 | outfile.js 6 | -------------------------------------------------------------------------------- /template/base/src/pages/home/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } 4 | -------------------------------------------------------------------------------- /template/base/src/pages/mine/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": {} 3 | } 4 | -------------------------------------------------------------------------------- /template/vitest-pnpm/pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | onlyBuiltDependencies: 2 | - esbuild 3 | -------------------------------------------------------------------------------- /template/base/_gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | dist 4 | *.log* 5 | .vscode 6 | .idea 7 | -------------------------------------------------------------------------------- /template/vitest/_gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | coverage 4 | dist 5 | *.log* 6 | .vscode 7 | .idea 8 | -------------------------------------------------------------------------------- /media/screenshot-cli-v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-mini/create-vue-mini/HEAD/media/screenshot-cli-v2.png -------------------------------------------------------------------------------- /template/pinia/src/pinia.js: -------------------------------------------------------------------------------- 1 | import { createPinia } from '@vue-mini/pinia'; 2 | 3 | export const pinia = createPinia(); 4 | -------------------------------------------------------------------------------- /template/prettier/prettier.config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | singleQuote: true, 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /template/vitest-typescript/_gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | coverage 4 | dist 5 | *.log* 6 | .vscode 7 | .idea 8 | -------------------------------------------------------------------------------- /template/base/src/images/home-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-mini/create-vue-mini/HEAD/template/base/src/images/home-dark.png -------------------------------------------------------------------------------- /template/base/src/images/home-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-mini/create-vue-mini/HEAD/template/base/src/images/home-light.png -------------------------------------------------------------------------------- /template/base/src/images/mine-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-mini/create-vue-mini/HEAD/template/base/src/images/mine-dark.png -------------------------------------------------------------------------------- /template/base/src/images/mine-light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-mini/create-vue-mini/HEAD/template/base/src/images/mine-light.png -------------------------------------------------------------------------------- /template/base/src/images/vue-mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-mini/create-vue-mini/HEAD/template/base/src/images/vue-mini.png -------------------------------------------------------------------------------- /template/pinia-typescript/src/pinia.ts: -------------------------------------------------------------------------------- 1 | import { createPinia } from '@vue-mini/pinia'; 2 | 3 | export const pinia = createPinia(); 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true 4 | } 5 | -------------------------------------------------------------------------------- /template/javascript/src/app.js: -------------------------------------------------------------------------------- 1 | import { createApp } from '@vue-mini/core'; 2 | 3 | createApp(() => { 4 | console.log('App Launched!'); 5 | }); 6 | -------------------------------------------------------------------------------- /template/typescript/src/app.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from '@vue-mini/core'; 2 | 3 | createApp(() => { 4 | console.log('App Launched!'); 5 | }); 6 | -------------------------------------------------------------------------------- /template/base/src/images/home-dark-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-mini/create-vue-mini/HEAD/template/base/src/images/home-dark-selected.png -------------------------------------------------------------------------------- /template/base/src/images/home-light-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-mini/create-vue-mini/HEAD/template/base/src/images/home-light-selected.png -------------------------------------------------------------------------------- /template/base/src/images/mine-dark-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-mini/create-vue-mini/HEAD/template/base/src/images/mine-dark-selected.png -------------------------------------------------------------------------------- /template/base/src/images/mine-light-selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vue-mini/create-vue-mini/HEAD/template/base/src/images/mine-light-selected.png -------------------------------------------------------------------------------- /template/pinia/src/app.js: -------------------------------------------------------------------------------- 1 | import { createApp } from '@vue-mini/core'; 2 | import './pinia'; 3 | 4 | createApp(() => { 5 | console.log('App Launched!'); 6 | }); 7 | -------------------------------------------------------------------------------- /template/pinia-typescript/src/app.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from '@vue-mini/core'; 2 | import './pinia'; 3 | 4 | createApp(() => { 5 | console.log('App Launched!'); 6 | }); 7 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | semi: false, 3 | singleQuote: true, 4 | objectWrap: 'collapse', 5 | experimentalTernaries: true, 6 | } 7 | 8 | export default config 9 | -------------------------------------------------------------------------------- /template/base/src/pages/home/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ greeting }} 4 | 5 | -------------------------------------------------------------------------------- /template/base/src/pages/mine/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{ greeting }} 4 | 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /template/base/_editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /template/javascript/src/pages/mine/index.js: -------------------------------------------------------------------------------- 1 | import { defineComponent, ref } from '@vue-mini/core'; 2 | 3 | defineComponent(() => { 4 | const greeting = ref('希望你会喜欢'); 5 | 6 | return { 7 | greeting, 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /template/typescript/src/pages/mine/index.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, ref } from '@vue-mini/core'; 2 | 3 | defineComponent(() => { 4 | const greeting = ref('希望你会喜欢'); 5 | 6 | return { 7 | greeting, 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /template/javascript/src/pages/home/index.js: -------------------------------------------------------------------------------- 1 | import { defineComponent, ref } from '@vue-mini/core'; 2 | 3 | defineComponent(() => { 4 | const greeting = ref('欢迎使用 Vue Mini'); 5 | 6 | return { 7 | greeting, 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /template/typescript/src/pages/home/index.ts: -------------------------------------------------------------------------------- 1 | import { defineComponent, ref } from '@vue-mini/core'; 2 | 3 | defineComponent(() => { 4 | const greeting = ref('欢迎使用 Vue Mini'); 5 | 6 | return { 7 | greeting, 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /template/base/src/sitemap.json: -------------------------------------------------------------------------------- 1 | { 2 | "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", 3 | "rules": [ 4 | { 5 | "action": "allow", 6 | "page": "*" 7 | } 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /template/vitest/__tests__/utils/index.spec.js: -------------------------------------------------------------------------------- 1 | import { describe, test, expect } from 'vitest'; 2 | import { stringifyQuery } from '@/utils'; 3 | 4 | describe('utils', () => { 5 | test('stringifyQuery', () => { 6 | expect(stringifyQuery({ page: '1', size: '10', status: 'a,b' })).toBe( 7 | '?page=1&size=10&status=a%2Cb', 8 | ); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /template/base/src/app.css: -------------------------------------------------------------------------------- 1 | page { 2 | color: #181818; 3 | font-size: 28px; 4 | font-family: 5 | ui-sans-serif, 6 | system-ui, 7 | -apple-system, 8 | BlinkMacSystemFont, 9 | sans-serif; 10 | background: #fff; 11 | } 12 | 13 | @media (prefers-color-scheme: dark) { 14 | page { 15 | color: #fff; 16 | background: #181818; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /template/vitest-typescript/__tests__/utils/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { describe, test, expect } from 'vitest'; 2 | import { stringifyQuery } from '@/utils'; 3 | 4 | describe('utils', () => { 5 | test('stringifyQuery', () => { 6 | expect(stringifyQuery({ page: '1', size: '10', status: 'a,b' })).toBe( 7 | '?page=1&size=10&status=a%2Cb', 8 | ); 9 | }); 10 | }); 11 | -------------------------------------------------------------------------------- /template/base/src/pages/home/index.css: -------------------------------------------------------------------------------- 1 | .home { 2 | text-align: center; 3 | } 4 | 5 | .logo { 6 | width: 400px; 7 | height: 400px; 8 | margin-top: 120px; 9 | } 10 | 11 | .greeting { 12 | color: #2c3e50; 13 | font-size: 64px; 14 | font-weight: bold; 15 | } 16 | 17 | @media (prefers-color-scheme: dark) { 18 | .greeting { 19 | color: #fff; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /template/base/src/pages/mine/index.css: -------------------------------------------------------------------------------- 1 | .mine { 2 | text-align: center; 3 | } 4 | 5 | .logo { 6 | width: 400px; 7 | height: 400px; 8 | margin-top: 120px; 9 | } 10 | 11 | .greeting { 12 | color: #2c3e50; 13 | font-size: 64px; 14 | font-weight: bold; 15 | } 16 | 17 | @media (prefers-color-scheme: dark) { 18 | .greeting { 19 | color: #fff; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /template/stylelint/stylelint.config.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | extends: 'stylelint-config-standard', 3 | rules: { 4 | 'alpha-value-notation': 'number', 5 | 'color-function-notation': 'legacy', 6 | 'selector-type-no-unknown': [ 7 | true, 8 | { 9 | ignoreTypes: ['page'], 10 | }, 11 | ], 12 | }, 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /template/vitest/src/utils/index.js: -------------------------------------------------------------------------------- 1 | export function stringifyQuery(query) { 2 | let queryString = ''; 3 | for (const [key, value] of Object.entries(query)) { 4 | queryString = `${queryString}&${encodeURIComponent( 5 | key, 6 | )}=${encodeURIComponent(value)}`; 7 | } 8 | 9 | queryString &&= queryString.replace(/^&/, '?'); 10 | 11 | return queryString; 12 | } 13 | -------------------------------------------------------------------------------- /template/pinia/src/stores/count.js: -------------------------------------------------------------------------------- 1 | import { ref, computed } from '@vue-mini/core'; 2 | import { defineStore } from '@vue-mini/pinia'; 3 | 4 | export const useCountStore = defineStore('count', () => { 5 | const count = ref(0); 6 | const double = computed(() => count.value * 2); 7 | 8 | const increment = () => { 9 | count.value++; 10 | }; 11 | 12 | return { count, double, increment }; 13 | }); 14 | -------------------------------------------------------------------------------- /template/pinia-typescript/src/stores/count.ts: -------------------------------------------------------------------------------- 1 | import { ref, computed } from '@vue-mini/core'; 2 | import { defineStore } from '@vue-mini/pinia'; 3 | 4 | export const useCountStore = defineStore('count', () => { 5 | const count = ref(0); 6 | const double = computed(() => count.value * 2); 7 | 8 | const increment = () => { 9 | count.value++; 10 | }; 11 | 12 | return { count, double, increment }; 13 | }); 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: push 3 | jobs: 4 | run: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v5 8 | - uses: pnpm/action-setup@v4 9 | - uses: actions/setup-node@v6 10 | with: 11 | node-version: 24.x 12 | cache: pnpm 13 | - run: pnpm install 14 | - run: pnpm format 15 | - run: pnpm lint 16 | - run: pnpm type 17 | -------------------------------------------------------------------------------- /template/vitest-typescript/src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export function stringifyQuery(query: Record): string { 2 | let queryString = ''; 3 | for (const [key, value] of Object.entries(query)) { 4 | queryString = `${queryString}&${encodeURIComponent( 5 | key, 6 | )}=${encodeURIComponent(value)}`; 7 | } 8 | 9 | queryString &&= queryString.replace(/^&/, '?'); 10 | 11 | return queryString; 12 | } 13 | -------------------------------------------------------------------------------- /template/base/postcss.config.js: -------------------------------------------------------------------------------- 1 | import pxtorpx from 'postcss-pxtorpx-pro'; 2 | 3 | const config = { 4 | plugins: [ 5 | { 6 | postcssPlugin: 'postcss-import-css-to-wxss', 7 | AtRule: { 8 | import: (atRule) => { 9 | atRule.params = atRule.params.replace('.css', '.wxss'); 10 | }, 11 | }, 12 | }, 13 | pxtorpx({ transform: (x) => x }), 14 | ], 15 | }; 16 | 17 | export default config; 18 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "lib": ["es2023"], 5 | "skipLibCheck": true, 6 | "esModuleInterop": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "module": "nodenext", 10 | "moduleResolution": "nodenext", 11 | "isolatedModules": true, 12 | "resolveJsonModule": true, 13 | "noFallthroughCasesInSwitch": true, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true 16 | }, 17 | "include": ["index.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /template/vitest/vitest.config.js: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url'; 2 | import { defineConfig } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | resolve: { 6 | alias: { 7 | '@': fileURLToPath(new URL('src', import.meta.url)), 8 | }, 9 | }, 10 | test: { 11 | include: ['__tests__/**/*.spec.js'], 12 | sequence: { 13 | hooks: 'list', 14 | }, 15 | coverage: { 16 | provider: 'v8', 17 | reporter: ['text', 'html', 'json'], 18 | include: ['src/**/*.js'], 19 | }, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /template/vitest-typescript/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url'; 2 | import { defineConfig } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | resolve: { 6 | alias: { 7 | '@': fileURLToPath(new URL('src', import.meta.url)), 8 | }, 9 | }, 10 | test: { 11 | include: ['__tests__/**/*.spec.ts'], 12 | sequence: { 13 | hooks: 'list', 14 | }, 15 | coverage: { 16 | provider: 'v8', 17 | reporter: ['text', 'html', 'json'], 18 | include: ['src/**/*.ts'], 19 | }, 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /template/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "lib": ["esnext", "dom"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "strict": true, 9 | "strictFunctionTypes": false, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "paths": { 19 | "@/*": ["./src/*"] 20 | } 21 | }, 22 | "include": ["src"] 23 | } 24 | -------------------------------------------------------------------------------- /template/base/src/theme.json: -------------------------------------------------------------------------------- 1 | { 2 | "light": { 3 | "style": "black", 4 | "color": "#181818", 5 | "bgColor": "#ffffff", 6 | "homeIcon": "/images/home-light.png", 7 | "homeSelectedIcon": "/images/home-light-selected.png", 8 | "mineIcon": "/images/mine-light.png", 9 | "mineSelectedIcon": "/images/mine-light-selected.png" 10 | }, 11 | "dark": { 12 | "style": "white", 13 | "color": "#ffffff", 14 | "bgColor": "#181818", 15 | "homeIcon": "/images/home-dark.png", 16 | "homeSelectedIcon": "/images/home-dark-selected.png", 17 | "mineIcon": "/images/mine-dark.png", 18 | "mineSelectedIcon": "/images/mine-dark-selected.png" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /template/vitest-typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "lib": ["esnext", "dom"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "strict": true, 9 | "strictFunctionTypes": false, 10 | "forceConsistentCasingInFileNames": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "noUnusedLocals": true, 17 | "noUnusedParameters": true, 18 | "paths": { 19 | "@/*": ["./src/*"] 20 | } 21 | }, 22 | "include": ["src", "__tests__", "vitest.config.ts"] 23 | } 24 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import eslint from '@eslint/js' 2 | import tseslint from 'typescript-eslint' 3 | 4 | const config = [ 5 | { ignores: ['template/', 'outfile.js'] }, 6 | { 7 | files: ['**/*.js', '**/*.ts'], 8 | linterOptions: { reportUnusedDisableDirectives: true }, 9 | rules: { 10 | ...eslint.configs.recommended.rules, 11 | // Avoid conflicts with Prettier 12 | // https://github.com/prettier/eslint-config-prettier#no-unexpected-multiline 13 | 'no-unexpected-multiline': 'off', 14 | }, 15 | }, 16 | ...tseslint.configs.recommendedTypeChecked.map((c) => ({ 17 | ...c, 18 | files: ['**/*.ts'], 19 | })), 20 | { 21 | files: ['**/*.ts'], 22 | languageOptions: { 23 | parserOptions: { 24 | projectService: true, 25 | tsconfigRootDir: import.meta.dirname, 26 | }, 27 | }, 28 | }, 29 | ] 30 | 31 | export default config 32 | -------------------------------------------------------------------------------- /template/base/src/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "darkmode": true, 3 | "themeLocation": "theme.json", 4 | "pages": ["pages/home/index", "pages/mine/index"], 5 | "window": { 6 | "navigationBarBackgroundColor": "@bgColor", 7 | "navigationBarTextStyle": "@style", 8 | "navigationBarTitleText": "vue-mini-project", 9 | "backgroundColor": "@bgColor", 10 | "backgroundColorTop": "@bgColor", 11 | "backgroundColorBottom": "@bgColor" 12 | }, 13 | "tabBar": { 14 | "color": "@color", 15 | "selectedColor": "@color", 16 | "backgroundColor": "@bgColor", 17 | "borderStyle": "@style", 18 | "list": [ 19 | { 20 | "text": "主页", 21 | "pagePath": "pages/home/index", 22 | "iconPath": "@homeIcon", 23 | "selectedIconPath": "@homeSelectedIcon" 24 | }, 25 | { 26 | "text": "我", 27 | "pagePath": "pages/mine/index", 28 | "iconPath": "@mineIcon", 29 | "selectedIconPath": "@mineSelectedIcon" 30 | } 31 | ] 32 | }, 33 | "sitemapLocation": "sitemap.json", 34 | "lazyCodeLoading": "requiredComponents" 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Vue Mini 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-vue-mini", 3 | "version": "1.4.3", 4 | "type": "module", 5 | "packageManager": "pnpm@10.24.0", 6 | "description": "Vue Mini 脚手架", 7 | "bin": { 8 | "create-vue-mini": "outfile.js" 9 | }, 10 | "files": [ 11 | "outfile.js", 12 | "template" 13 | ], 14 | "engines": { 15 | "node": ">=18.19.1 <19 || >=20.6.1" 16 | }, 17 | "scripts": { 18 | "format": "prettier --check \"**/*.{js,ts,json,md}\"", 19 | "lint": "eslint .", 20 | "type": "tsc --noEmit", 21 | "build": "tsc && mv index.js outfile.js" 22 | }, 23 | "repository": "https://github.com/vue-mini/create-vue-mini.git", 24 | "author": "Yang Mingshan ", 25 | "license": "MIT", 26 | "dependencies": { 27 | "ejs": "^3.1.10", 28 | "kolorist": "^1.8.0", 29 | "prompts": "^2.4.2" 30 | }, 31 | "devDependencies": { 32 | "@eslint/js": "^9.39.1", 33 | "@types/ejs": "^3.1.5", 34 | "@types/node": "^24.10.1", 35 | "@types/prompts": "^2.4.9", 36 | "eslint": "^9.39.1", 37 | "prettier": "^3.7.2", 38 | "typescript": "^5.9.3", 39 | "typescript-eslint": "^8.48.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # create-vue-mini node compatibility 2 | 3 | 创建 Vue Mini 小程序项目的最佳方案。 4 | 5 |

6 | 7 |

8 | 9 | ## 使用 10 | 11 | 要使用 `create-vue-mini` 创建一个全新的 Vue Mini 小程序项目,请直接在终端内运行以下命令: 12 | 13 | ```bash 14 | npm create vue-mini@latest 15 | ``` 16 | 17 | 18 | > [!NOTE] 19 | > `@latest` **不能**省略,否则 `npm` 可能会使用已缓存的旧版本。 20 | 21 | ## 常见问题 22 | 23 | ### 如何使用小程序组件库? 24 | 25 | `create-vue-mini` 对小程序组件库提供了开箱支持,**不需要**使用微信开发者工具的 `工具 -> 构建 npm` 功能,**不需要**勾选 `将 JS 编译成 ES5`,只需要将小程序组件库作为**生产依赖**(即 dependencies)安装,就可以直接使用。`create-vue-mini` 会在幕后帮你将一切处理妥当。视频介绍:[《Vue Mini 如何使用小程序组件库》](https://www.bilibili.com/video/BV1w1421t7US/) 26 | 27 | ### 支持小程序独立分包吗? 28 | 29 | 支持。但是不支持动态添加独立分包,也就是说在 `app.json` 添加独立分包后需要重启开发服务器。 30 | 31 | ## 致谢 32 | 33 | 此项目由 [create-vue](https://github.com/vuejs/create-vue) 修改而来。 34 | 35 | ## 许可证 36 | 37 | [MIT](https://opensource.org/licenses/MIT) 38 | 39 | Copyright (c) 2024-present Yang Mingshan 40 | -------------------------------------------------------------------------------- /template/javascript/babel.config.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | 3 | const runtimeVersion = JSON.parse( 4 | fs.readFileSync( 5 | new URL(import.meta.resolve('@babel/runtime/package.json')), 6 | 'utf8', 7 | ), 8 | ).version; 9 | 10 | const config = { 11 | targets: {}, 12 | assumptions: { 13 | arrayLikeIsIterable: true, 14 | constantReexports: true, 15 | constantSuper: true, 16 | enumerableModuleMeta: true, 17 | ignoreFunctionLength: true, 18 | ignoreToPrimitiveHint: true, 19 | iterableIsArray: true, 20 | mutableTemplateObject: true, 21 | noClassCalls: true, 22 | noDocumentAll: true, 23 | noNewArrows: true, 24 | objectRestNoSymbols: true, 25 | privateFieldsAsProperties: true, 26 | pureGetters: true, 27 | setClassMethods: true, 28 | setComputedProperties: true, 29 | setPublicClassFields: true, 30 | setSpreadProperties: true, 31 | skipForOfIteratorClosing: true, 32 | superIsCallableConstructor: true, 33 | }, 34 | presets: [ 35 | [ 36 | '@babel/preset-env', 37 | { 38 | bugfixes: true, 39 | modules: 'commonjs', 40 | }, 41 | ], 42 | ], 43 | plugins: [ 44 | [ 45 | '@babel/plugin-transform-runtime', 46 | { 47 | version: runtimeVersion, 48 | }, 49 | ], 50 | 'transform-inline-environment-variables', 51 | [ 52 | 'module-resolver', 53 | { 54 | alias: { 55 | '@': './src', 56 | }, 57 | }, 58 | ], 59 | 'autocomplete-index', 60 | ], 61 | }; 62 | 63 | export default config; 64 | -------------------------------------------------------------------------------- /template/typescript/babel.config.js: -------------------------------------------------------------------------------- 1 | import fs from 'node:fs'; 2 | 3 | const runtimeVersion = JSON.parse( 4 | fs.readFileSync( 5 | new URL(import.meta.resolve('@babel/runtime/package.json')), 6 | 'utf8', 7 | ), 8 | ).version; 9 | 10 | const config = { 11 | targets: {}, 12 | assumptions: { 13 | arrayLikeIsIterable: true, 14 | constantReexports: true, 15 | constantSuper: true, 16 | enumerableModuleMeta: true, 17 | ignoreFunctionLength: true, 18 | ignoreToPrimitiveHint: true, 19 | iterableIsArray: true, 20 | mutableTemplateObject: true, 21 | noClassCalls: true, 22 | noDocumentAll: true, 23 | noNewArrows: true, 24 | objectRestNoSymbols: true, 25 | privateFieldsAsProperties: true, 26 | pureGetters: true, 27 | setClassMethods: true, 28 | setComputedProperties: true, 29 | setPublicClassFields: true, 30 | setSpreadProperties: true, 31 | skipForOfIteratorClosing: true, 32 | superIsCallableConstructor: true, 33 | }, 34 | presets: [ 35 | [ 36 | '@babel/preset-env', 37 | { 38 | bugfixes: true, 39 | modules: 'commonjs', 40 | }, 41 | ], 42 | '@babel/preset-typescript', 43 | ], 44 | plugins: [ 45 | [ 46 | '@babel/plugin-transform-runtime', 47 | { 48 | version: runtimeVersion, 49 | }, 50 | ], 51 | 'transform-inline-environment-variables', 52 | [ 53 | 'module-resolver', 54 | { 55 | alias: { 56 | '@': './src', 57 | }, 58 | }, 59 | ], 60 | 'autocomplete-index', 61 | ], 62 | }; 63 | 64 | export default config; 65 | -------------------------------------------------------------------------------- /template/base/project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "项目配置文件", 3 | "miniprogramRoot": "dist/", 4 | "packOptions": { 5 | "ignore": [], 6 | "include": [] 7 | }, 8 | "setting": { 9 | "urlCheck": false, 10 | "es6": false, 11 | "enhance": false, 12 | "postcss": false, 13 | "preloadBackgroundData": false, 14 | "minified": false, 15 | "newFeature": true, 16 | "coverView": true, 17 | "nodeModules": false, 18 | "autoAudits": false, 19 | "showShadowRootInWxmlPanel": false, 20 | "scopeDataCheck": false, 21 | "uglifyFileName": false, 22 | "checkInvalidKey": true, 23 | "checkSiteMap": true, 24 | "uploadWithSourceMap": false, 25 | "compileHotReLoad": true, 26 | "lazyloadPlaceholderEnable": false, 27 | "useMultiFrameRuntime": true, 28 | "useApiHook": true, 29 | "useApiHostProcess": true, 30 | "babelSetting": { 31 | "ignore": [], 32 | "disablePlugins": [], 33 | "outputPath": "" 34 | }, 35 | "useIsolateContext": false, 36 | "userConfirmedUseIsolateContext": false, 37 | "userConfirmedBundleSwitch": false, 38 | "packNpmManually": false, 39 | "packNpmRelationList": [], 40 | "minifyWXSS": true, 41 | "disableUseStrict": false, 42 | "minifyWXML": true, 43 | "showES6CompileOption": false, 44 | "useCompilerPlugins": false, 45 | "ignoreUploadUnusedFiles": true, 46 | "compileWorklet": false, 47 | "localPlugins": false, 48 | "condition": false, 49 | "swc": false, 50 | "disableSWC": true, 51 | "skylineRenderEnable": false, 52 | "useStaticServer": false, 53 | "useLanDebug": false, 54 | "ignoreDevUnusedFiles": true, 55 | "bigPackageSizeSupport": false 56 | }, 57 | "compileType": "miniprogram", 58 | "libVersion": "development", 59 | "appid": "", 60 | "projectname": "vue-mini-project", 61 | "simulatorType": "wechat", 62 | "simulatorPluginLibVersion": {}, 63 | "condition": {}, 64 | "srcMiniprogramRoot": "dist/", 65 | "editorSetting": { 66 | "tabIndent": "insertSpaces", 67 | "tabSize": 2 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /template/eslint/eslint.config.js.ejs: -------------------------------------------------------------------------------- 1 | import globals from 'globals'; 2 | import eslint from '@eslint/js'; 3 | <%_ if (needsTypeScript) { _%> 4 | import tseslint from 'typescript-eslint'; 5 | <%_ } _%> 6 | <%_ if (needsPrettier) { _%> 7 | import prettier from 'eslint-config-prettier'; 8 | <%_ } _%> 9 | 10 | <%_ if (needsTypeScript) { _%> 11 | const config = [ 12 | { 13 | <%_ if (needsVitest) { _%> 14 | ignores: ['dist/', 'coverage/'], 15 | <%_ } else { _%> 16 | ignores: ['dist/'], 17 | <%_ } _%> 18 | }, 19 | { 20 | files: ['**/*.js', '**/*.ts'], 21 | linterOptions: { 22 | reportUnusedDisableDirectives: true, 23 | }, 24 | ...eslint.configs.recommended, 25 | }, 26 | { 27 | files: ['**/*.js'], 28 | languageOptions: { 29 | globals: { 30 | ...globals.node, 31 | wx: 'readonly', 32 | getApp: 'readonly', 33 | getCurrentPages: 'readonly', 34 | }, 35 | }, 36 | <%_ if (needsPrettier) { _%> 37 | ...prettier, 38 | <%_ } _%> 39 | }, 40 | ...tseslint.configs.recommendedTypeChecked.map((c) => ({ 41 | ...c, 42 | files: ['**/*.ts'], 43 | })), 44 | { 45 | files: ['**/*.ts'], 46 | languageOptions: { 47 | parserOptions: { 48 | projectService: true, 49 | tsconfigRootDir: import.meta.dirname, 50 | }, 51 | }, 52 | <%_ if (needsPrettier) { _%> 53 | ...prettier, 54 | <%_ } _%> 55 | }, 56 | ]; 57 | <%_ } else { _%> 58 | const config = [ 59 | { 60 | files: ['**/*.js'], 61 | <%_ if (needsVitest) { _%> 62 | ignores: ['dist/**/*', 'coverage/**/*'], 63 | <%_ } else { _%> 64 | ignores: ['dist/**/*'], 65 | <%_ } _%> 66 | linterOptions: { 67 | reportUnusedDisableDirectives: true, 68 | }, 69 | languageOptions: { 70 | globals: { 71 | ...globals.node, 72 | wx: 'readonly', 73 | getApp: 'readonly', 74 | getCurrentPages: 'readonly', 75 | }, 76 | }, 77 | <%_ if (needsPrettier) { _%> 78 | rules: { 79 | ...eslint.configs.recommended.rules, 80 | ...prettier.rules, 81 | }, 82 | <%_ } else { _%> 83 | ...eslint.configs.recommended, 84 | <%_ } _%> 85 | }, 86 | ]; 87 | <%_ } _%> 88 | 89 | export default config; 90 | -------------------------------------------------------------------------------- /template/base/package.json.ejs: -------------------------------------------------------------------------------- 1 | { 2 | "name": "<%- packageName %>", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | <%_ if (packageManager) { _%> 7 | "packageManager": "<%- packageManager %>", 8 | <%_ } _%> 9 | "engines": { 10 | "node": ">=18.19.1 <19 || >=20.6.1" 11 | }, 12 | "scripts": { 13 | <%_ if (needsPrettier) { _%> 14 | "format": "prettier --write \"**/*.{js<% if (needsEslint) { %>,cjs<% } %><% if (needsTypeScript) { %>,ts<% } %>,json,css,html,md}\"", 15 | <%_ } _%> 16 | <%_ if (needsEslint) { _%> 17 | "lint<% if (needsStylelint) { %>:script<% } %>": "eslint .", 18 | <%_ } _%> 19 | <%_ if (needsStylelint) { _%> 20 | "lint<% if (needsEslint) { %>:style<% } %>": "stylelint \"src/**/*.css\"", 21 | <%_ } _%> 22 | <%_ if (needsTypeScript) { _%> 23 | "type-check": "tsc", 24 | <%_ } _%> 25 | <%_ if (needsVitest) { _%> 26 | "test": "vitest", 27 | <%_ } _%> 28 | "dev": "cross-env NODE_ENV=development node build.js", 29 | "build": "cross-env NODE_ENV=production node build.js" 30 | }, 31 | "dependencies": { 32 | "@babel/runtime": "^7.28.4", 33 | "@vue-mini/core": "^1.2.8", 34 | <%_ if (needsPinia) { _%> 35 | "@vue-mini/pinia": "^1.0.0", 36 | <%_ } _%> 37 | trailing-comma 38 | }, 39 | "devDependencies": { 40 | "@babel/core": "^7.28.5", 41 | "@babel/plugin-transform-runtime": "^7.28.5", 42 | "@babel/preset-env": "^7.28.5", 43 | <%_ if (needsTypeScript) { _%> 44 | "@babel/preset-typescript": "^7.28.5", 45 | <%_ } _%> 46 | "@babel/traverse": "^7.28.5", 47 | "@babel/types": "^7.28.5", 48 | <%_ if (needsEslint) { _%> 49 | "@eslint/js": "^9.39.1", 50 | <%_ } _%> 51 | "@rollup/plugin-commonjs": "^29.0.0", 52 | "@rollup/plugin-node-resolve": "^16.0.3", 53 | "@rollup/plugin-replace": "^6.0.3", 54 | "@rollup/plugin-terser": "^0.4.4", 55 | <%_ if (needsTypeScript && needsVitest) { _%> 56 | "@types/node": "^24.10.1", 57 | <%_ } _%> 58 | <%_ if (needsVitest) { _%> 59 | "@vitest/coverage-v8": "^4.0.14", 60 | <%_ } _%> 61 | "babel-plugin-autocomplete-index": "^0.2.0", 62 | "babel-plugin-module-resolver": "^5.0.2", 63 | "babel-plugin-transform-inline-environment-variables": "^0.4.4", 64 | "chokidar": "^5.0.0", 65 | "cross-env": "^10.1.0", 66 | <%_ if (needsEslint) { _%> 67 | "eslint": "^9.39.1", 68 | <%_ if (needsPrettier) { _%> 69 | "eslint-config-prettier": "^10.1.8", 70 | <%_ } _%> 71 | <%_ } _%> 72 | "fs-extra": "^11.3.2", 73 | <%_ if (needsEslint) { _%> 74 | "globals": "^16.5.0", 75 | <%_ } _%> 76 | "kolorist": "^1.8.0", 77 | "local-pkg": "^1.1.2", 78 | "postcss": "^8.5.6", 79 | "postcss-load-config": "^6.0.1", 80 | "postcss-pxtorpx-pro": "^2.0.0", 81 | <%_ if (needsPrettier) { _%> 82 | "prettier": "^3.7.2", 83 | <%_ } _%> 84 | "rollup": "^4.53.3", 85 | <%_ if (needsStylelint) { _%> 86 | "stylelint": "^16.26.1", 87 | "stylelint-config-standard": "^39.0.1", 88 | <%_ } _%> 89 | "terser": "^5.44.1", 90 | <%_ if (needsTypeScript) { _%> 91 | "typescript": "^5.9.3", 92 | <%_ } _%> 93 | <%_ if (needsTypeScript && needsEslint) { _%> 94 | "typescript-eslint": "^8.48.0", 95 | <%_ } _%> 96 | <%_ if (needsVitest) { _%> 97 | "vitest": "^4.0.14", 98 | <%_ } _%> 99 | trailing-comma 100 | }, 101 | "browserslist": [ 102 | "iOS >= 10", 103 | "Chrome >= 63" 104 | ] 105 | } 106 | -------------------------------------------------------------------------------- /template/javascript/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @version v1.4.3 3 | * https://github.com/vue-mini/create-vue-mini 4 | * 请谨慎修改此文件,改动过多可能会导致你后续升级困难。 5 | */ 6 | import path from 'node:path'; 7 | import process from 'node:process'; 8 | import fs from 'fs-extra'; 9 | import chokidar from 'chokidar'; 10 | import babel from '@babel/core'; 11 | import traverse from '@babel/traverse'; 12 | import t from '@babel/types'; 13 | import { minify } from 'terser'; 14 | import postcss from 'postcss'; 15 | import postcssrc from 'postcss-load-config'; 16 | import { rollup } from 'rollup'; 17 | import replace from '@rollup/plugin-replace'; 18 | import terser from '@rollup/plugin-terser'; 19 | import resolve from '@rollup/plugin-node-resolve'; 20 | import commonjs from '@rollup/plugin-commonjs'; 21 | import { green, bold } from 'kolorist'; 22 | import { getPackageInfo } from 'local-pkg'; 23 | 24 | let topLevelJobs = []; 25 | let bundleJobs = []; 26 | const startTime = Date.now(); 27 | const NODE_ENV = process.env.NODE_ENV || 'production'; 28 | const __PROD__ = NODE_ENV === 'production'; 29 | const terserOptions = { 30 | ecma: 2016, 31 | toplevel: true, 32 | safari10: true, 33 | format: { comments: false }, 34 | }; 35 | 36 | let independentPackages = []; 37 | async function findIndependentPackages() { 38 | const { subpackages } = await fs.readJson( 39 | path.resolve('src', 'app.json'), 40 | 'utf8', 41 | ); 42 | if (subpackages) { 43 | independentPackages = subpackages 44 | .filter(({ independent }) => independent) 45 | .map(({ root }) => root); 46 | } 47 | } 48 | 49 | const builtLibraries = []; 50 | const bundledModules = new Map(); 51 | async function bundleModule(module, pkg) { 52 | const bundled = bundledModules.get(pkg); 53 | if ( 54 | bundled?.has(module) || 55 | builtLibraries.some((library) => module.startsWith(library)) 56 | ) { 57 | return false; 58 | } 59 | if (bundled) { 60 | bundled.add(module); 61 | } else { 62 | bundledModules.set(pkg, new Set([module])); 63 | } 64 | 65 | const { 66 | packageJson: { peerDependencies }, 67 | } = await getPackageInfo(module); 68 | const bundle = await rollup({ 69 | input: module, 70 | external: peerDependencies ? Object.keys(peerDependencies) : undefined, 71 | plugins: [ 72 | commonjs(), 73 | replace({ 74 | preventAssignment: true, 75 | values: { 76 | 'process.env.NODE_ENV': JSON.stringify(NODE_ENV), 77 | }, 78 | }), 79 | resolve(), 80 | __PROD__ && terser(terserOptions), 81 | ].filter(Boolean), 82 | }); 83 | await bundle.write({ 84 | exports: 'named', 85 | file: `${pkg.replace('src', 'dist')}/miniprogram_npm/${module}/index.js`, 86 | format: 'cjs', 87 | }); 88 | return true; 89 | } 90 | 91 | function traverseAST(ast, pkg, babelOnly = false) { 92 | traverse.default(ast, { 93 | CallExpression({ node }) { 94 | if ( 95 | node.callee.name !== 'require' || 96 | !t.isStringLiteral(node.arguments[0]) || 97 | node.arguments[0].value.startsWith('.') || 98 | (babelOnly && !node.arguments[0].value.startsWith('@babel/runtime')) 99 | ) { 100 | return; 101 | } 102 | 103 | const module = node.arguments[0].value; 104 | let promise = bundleModule(module, pkg); 105 | if (babelOnly) { 106 | promise = promise.then((valid) => { 107 | if (!valid) return; 108 | return Promise.all( 109 | independentPackages.map((item) => { 110 | const bundled = bundledModules.get(item); 111 | if (bundled) { 112 | bundled.add(module); 113 | } else { 114 | bundledModules.set(pkg, new Set([module])); 115 | } 116 | return fs.copy( 117 | path.resolve('dist', 'miniprogram_npm', module), 118 | path.resolve('dist', item, 'miniprogram_npm', module), 119 | ); 120 | }), 121 | ); 122 | }); 123 | } 124 | bundleJobs?.push(promise); 125 | }, 126 | }); 127 | } 128 | 129 | async function buildComponentLibrary(name) { 130 | const { 131 | rootPath, 132 | packageJson: { miniprogram }, 133 | } = await getPackageInfo(name); 134 | 135 | let source = ''; 136 | if (miniprogram) { 137 | source = path.join(rootPath, miniprogram); 138 | } else { 139 | try { 140 | const dist = path.join(rootPath, 'miniprogram_dist'); 141 | const stats = await fs.stat(dist); 142 | if (stats.isDirectory()) { 143 | source = dist; 144 | } 145 | } catch { 146 | // Empty 147 | } 148 | } 149 | 150 | if (!source) return; 151 | 152 | builtLibraries.push(name); 153 | const destination = path.resolve('dist', 'miniprogram_npm', name); 154 | await fs.copy(source, destination); 155 | 156 | return new Promise((resolve) => { 157 | const jobs = []; 158 | const tnm = async (filePath) => { 159 | const result = await babel.transformFileAsync(filePath, { ast: true }); 160 | traverseAST(result.ast, 'src', true); 161 | const code = __PROD__ 162 | ? (await minify(result.code, terserOptions)).code 163 | : result.code; 164 | await fs.writeFile(filePath, code); 165 | }; 166 | 167 | const watcher = chokidar.watch([destination], { 168 | ignored: (file, stats) => stats?.isFile() && !file.endsWith('.js'), 169 | }); 170 | watcher.on('add', (filePath) => { 171 | const promise = tnm(filePath); 172 | jobs.push(promise); 173 | }); 174 | watcher.on('ready', async () => { 175 | const promise = watcher.close(); 176 | jobs.push(promise); 177 | await Promise.all(jobs); 178 | if (independentPackages.length > 0) { 179 | await Promise.all( 180 | independentPackages.map((item) => 181 | fs.copy( 182 | destination, 183 | path.resolve('dist', item, 'miniprogram_npm', name), 184 | ), 185 | ), 186 | ); 187 | } 188 | resolve(); 189 | }); 190 | }); 191 | } 192 | 193 | async function scanDependencies() { 194 | const { dependencies } = await fs.readJson('package.json', 'utf8'); 195 | for (const name of Object.keys(dependencies)) { 196 | const promise = buildComponentLibrary(name); 197 | topLevelJobs.push(promise); 198 | } 199 | } 200 | 201 | async function processScript(filePath) { 202 | let ast, code; 203 | try { 204 | const result = await babel.transformFileAsync(path.resolve(filePath), { 205 | ast: true, 206 | }); 207 | ast = result.ast; 208 | code = result.code; 209 | } catch (error) { 210 | console.error(`Failed to compile ${filePath}`); 211 | 212 | if (__PROD__) throw error; 213 | 214 | console.error(error); 215 | return; 216 | } 217 | 218 | const pkg = independentPackages.find((item) => 219 | filePath.startsWith(path.normalize(`src/${item}`)), 220 | ); 221 | // The `src/` prefix is added to to distinguish `src` and `src/src`. 222 | traverseAST(ast, pkg ? `src/${pkg}` : 'src'); 223 | 224 | if (__PROD__) { 225 | code = (await minify(code, terserOptions)).code; 226 | } 227 | 228 | const destination = filePath.replace('src', 'dist'); 229 | // Make sure the directory already exists when write file 230 | await fs.copy(filePath, destination); 231 | await fs.writeFile(destination, code); 232 | } 233 | 234 | async function processTemplate(filePath) { 235 | const destination = filePath 236 | .replace('src', 'dist') 237 | .replace(/\.html$/, '.wxml'); 238 | await fs.copy(filePath, destination); 239 | } 240 | 241 | async function processStyle(filePath) { 242 | const source = await fs.readFile(filePath, 'utf8'); 243 | const { plugins, options } = await postcssrc({ from: filePath }); 244 | 245 | let css; 246 | try { 247 | const result = await postcss(plugins).process(source, options); 248 | css = result.css; 249 | } catch (error) { 250 | console.error(`Failed to compile ${filePath}`); 251 | 252 | if (__PROD__) throw error; 253 | 254 | console.error(error); 255 | return; 256 | } 257 | 258 | const destination = filePath 259 | .replace('src', 'dist') 260 | .replace(/\.css$/, '.wxss'); 261 | // Make sure the directory already exists when write file 262 | await fs.copy(filePath, destination); 263 | await fs.writeFile(destination, css); 264 | } 265 | 266 | const cb = async (filePath) => { 267 | if (filePath.endsWith('.js')) { 268 | await processScript(filePath); 269 | return; 270 | } 271 | 272 | if (filePath.endsWith('.html')) { 273 | await processTemplate(filePath); 274 | return; 275 | } 276 | 277 | if (filePath.endsWith('.css')) { 278 | await processStyle(filePath); 279 | return; 280 | } 281 | 282 | await fs.copy(filePath, filePath.replace('src', 'dist')); 283 | }; 284 | 285 | async function dev() { 286 | await fs.remove('dist'); 287 | await findIndependentPackages(); 288 | await scanDependencies(); 289 | chokidar 290 | .watch(['src'], { 291 | ignored: (file, stats) => 292 | stats?.isFile() && 293 | (file.endsWith('.gitkeep') || file.endsWith('.DS_Store')), 294 | }) 295 | .on('add', (filePath) => { 296 | const promise = cb(filePath); 297 | topLevelJobs?.push(promise); 298 | }) 299 | .on('change', (filePath) => { 300 | cb(filePath); 301 | }) 302 | .on('ready', async () => { 303 | await Promise.all(topLevelJobs); 304 | await Promise.all(bundleJobs); 305 | console.log(bold(green(`启动完成,耗时:${Date.now() - startTime}ms`))); 306 | console.log(bold(green('监听文件变化中...'))); 307 | // Release memory. 308 | topLevelJobs = null; 309 | bundleJobs = null; 310 | }); 311 | } 312 | 313 | async function prod() { 314 | await fs.remove('dist'); 315 | await findIndependentPackages(); 316 | await scanDependencies(); 317 | const watcher = chokidar.watch(['src'], { 318 | ignored: (file, stats) => 319 | stats?.isFile() && 320 | (file.endsWith('.gitkeep') || file.endsWith('.DS_Store')), 321 | }); 322 | watcher.on('add', (filePath) => { 323 | const promise = cb(filePath); 324 | topLevelJobs.push(promise); 325 | }); 326 | watcher.on('ready', async () => { 327 | const promise = watcher.close(); 328 | topLevelJobs.push(promise); 329 | await Promise.all(topLevelJobs); 330 | await Promise.all(bundleJobs); 331 | console.log(bold(green(`构建完成,耗时:${Date.now() - startTime}ms`))); 332 | }); 333 | } 334 | 335 | if (__PROD__) { 336 | await prod(); 337 | } else { 338 | await dev(); 339 | } 340 | -------------------------------------------------------------------------------- /template/typescript/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @version v1.4.3 3 | * https://github.com/vue-mini/create-vue-mini 4 | * 请谨慎修改此文件,改动过多可能会导致你后续升级困难。 5 | */ 6 | import path from 'node:path'; 7 | import process from 'node:process'; 8 | import fs from 'fs-extra'; 9 | import chokidar from 'chokidar'; 10 | import babel from '@babel/core'; 11 | import traverse from '@babel/traverse'; 12 | import t from '@babel/types'; 13 | import { minify } from 'terser'; 14 | import postcss from 'postcss'; 15 | import postcssrc from 'postcss-load-config'; 16 | import { rollup } from 'rollup'; 17 | import replace from '@rollup/plugin-replace'; 18 | import terser from '@rollup/plugin-terser'; 19 | import resolve from '@rollup/plugin-node-resolve'; 20 | import commonjs from '@rollup/plugin-commonjs'; 21 | import { green, bold } from 'kolorist'; 22 | import { getPackageInfo } from 'local-pkg'; 23 | 24 | let topLevelJobs = []; 25 | let bundleJobs = []; 26 | const startTime = Date.now(); 27 | const NODE_ENV = process.env.NODE_ENV || 'production'; 28 | const __PROD__ = NODE_ENV === 'production'; 29 | const terserOptions = { 30 | ecma: 2016, 31 | toplevel: true, 32 | safari10: true, 33 | format: { comments: false }, 34 | }; 35 | 36 | let independentPackages = []; 37 | async function findIndependentPackages() { 38 | const { subpackages } = await fs.readJson( 39 | path.resolve('src', 'app.json'), 40 | 'utf8', 41 | ); 42 | if (subpackages) { 43 | independentPackages = subpackages 44 | .filter(({ independent }) => independent) 45 | .map(({ root }) => root); 46 | } 47 | } 48 | 49 | const builtLibraries = []; 50 | const bundledModules = new Map(); 51 | async function bundleModule(module, pkg) { 52 | const bundled = bundledModules.get(pkg); 53 | if ( 54 | bundled?.has(module) || 55 | builtLibraries.some((library) => module.startsWith(library)) 56 | ) { 57 | return false; 58 | } 59 | if (bundled) { 60 | bundled.add(module); 61 | } else { 62 | bundledModules.set(pkg, new Set([module])); 63 | } 64 | 65 | const { 66 | packageJson: { peerDependencies }, 67 | } = await getPackageInfo(module); 68 | const bundle = await rollup({ 69 | input: module, 70 | external: peerDependencies ? Object.keys(peerDependencies) : undefined, 71 | plugins: [ 72 | commonjs(), 73 | replace({ 74 | preventAssignment: true, 75 | values: { 76 | 'process.env.NODE_ENV': JSON.stringify(NODE_ENV), 77 | }, 78 | }), 79 | resolve(), 80 | __PROD__ && terser(terserOptions), 81 | ].filter(Boolean), 82 | }); 83 | await bundle.write({ 84 | exports: 'named', 85 | file: `${pkg.replace('src', 'dist')}/miniprogram_npm/${module}/index.js`, 86 | format: 'cjs', 87 | }); 88 | return true; 89 | } 90 | 91 | function traverseAST(ast, pkg, babelOnly = false) { 92 | traverse.default(ast, { 93 | CallExpression({ node }) { 94 | if ( 95 | node.callee.name !== 'require' || 96 | !t.isStringLiteral(node.arguments[0]) || 97 | node.arguments[0].value.startsWith('.') || 98 | (babelOnly && !node.arguments[0].value.startsWith('@babel/runtime')) 99 | ) { 100 | return; 101 | } 102 | 103 | const module = node.arguments[0].value; 104 | let promise = bundleModule(module, pkg); 105 | if (babelOnly) { 106 | promise = promise.then((valid) => { 107 | if (!valid) return; 108 | return Promise.all( 109 | independentPackages.map((item) => { 110 | const bundled = bundledModules.get(item); 111 | if (bundled) { 112 | bundled.add(module); 113 | } else { 114 | bundledModules.set(pkg, new Set([module])); 115 | } 116 | return fs.copy( 117 | path.resolve('dist', 'miniprogram_npm', module), 118 | path.resolve('dist', item, 'miniprogram_npm', module), 119 | ); 120 | }), 121 | ); 122 | }); 123 | } 124 | bundleJobs?.push(promise); 125 | }, 126 | }); 127 | } 128 | 129 | async function buildComponentLibrary(name) { 130 | const { 131 | rootPath, 132 | packageJson: { miniprogram }, 133 | } = await getPackageInfo(name); 134 | 135 | let source = ''; 136 | if (miniprogram) { 137 | source = path.join(rootPath, miniprogram); 138 | } else { 139 | try { 140 | const dist = path.join(rootPath, 'miniprogram_dist'); 141 | const stats = await fs.stat(dist); 142 | if (stats.isDirectory()) { 143 | source = dist; 144 | } 145 | } catch { 146 | // Empty 147 | } 148 | } 149 | 150 | if (!source) return; 151 | 152 | builtLibraries.push(name); 153 | const destination = path.resolve('dist', 'miniprogram_npm', name); 154 | await fs.copy(source, destination); 155 | 156 | return new Promise((resolve) => { 157 | const jobs = []; 158 | const tnm = async (filePath) => { 159 | const result = await babel.transformFileAsync(filePath, { ast: true }); 160 | traverseAST(result.ast, 'src', true); 161 | const code = __PROD__ 162 | ? (await minify(result.code, terserOptions)).code 163 | : result.code; 164 | await fs.writeFile(filePath, code); 165 | }; 166 | 167 | const watcher = chokidar.watch([destination], { 168 | ignored: (file, stats) => stats?.isFile() && !file.endsWith('.js'), 169 | }); 170 | watcher.on('add', (filePath) => { 171 | const promise = tnm(filePath); 172 | jobs.push(promise); 173 | }); 174 | watcher.on('ready', async () => { 175 | const promise = watcher.close(); 176 | jobs.push(promise); 177 | await Promise.all(jobs); 178 | if (independentPackages.length > 0) { 179 | await Promise.all( 180 | independentPackages.map((item) => 181 | fs.copy( 182 | destination, 183 | path.resolve('dist', item, 'miniprogram_npm', name), 184 | ), 185 | ), 186 | ); 187 | } 188 | resolve(); 189 | }); 190 | }); 191 | } 192 | 193 | async function scanDependencies() { 194 | const { dependencies } = await fs.readJson('package.json', 'utf8'); 195 | for (const name of Object.keys(dependencies)) { 196 | const promise = buildComponentLibrary(name); 197 | topLevelJobs.push(promise); 198 | } 199 | } 200 | 201 | async function processScript(filePath) { 202 | let ast, code; 203 | try { 204 | const result = await babel.transformFileAsync(path.resolve(filePath), { 205 | ast: true, 206 | }); 207 | ast = result.ast; 208 | code = result.code; 209 | } catch (error) { 210 | console.error(`Failed to compile ${filePath}`); 211 | 212 | if (__PROD__) throw error; 213 | 214 | console.error(error); 215 | return; 216 | } 217 | 218 | const pkg = independentPackages.find((item) => 219 | filePath.startsWith(path.normalize(`src/${item}`)), 220 | ); 221 | // The `src/` prefix is added to to distinguish `src` and `src/src`. 222 | traverseAST(ast, pkg ? `src/${pkg}` : 'src'); 223 | 224 | if (__PROD__) { 225 | code = (await minify(code, terserOptions)).code; 226 | } 227 | 228 | const destination = filePath.replace('src', 'dist').replace(/\.ts$/, '.js'); 229 | // Make sure the directory already exists when write file 230 | await fs.copy(filePath, destination); 231 | await fs.writeFile(destination, code); 232 | } 233 | 234 | async function processTemplate(filePath) { 235 | const destination = filePath 236 | .replace('src', 'dist') 237 | .replace(/\.html$/, '.wxml'); 238 | await fs.copy(filePath, destination); 239 | } 240 | 241 | async function processStyle(filePath) { 242 | const source = await fs.readFile(filePath, 'utf8'); 243 | const { plugins, options } = await postcssrc({ from: filePath }); 244 | 245 | let css; 246 | try { 247 | const result = await postcss(plugins).process(source, options); 248 | css = result.css; 249 | } catch (error) { 250 | console.error(`Failed to compile ${filePath}`); 251 | 252 | if (__PROD__) throw error; 253 | 254 | console.error(error); 255 | return; 256 | } 257 | 258 | const destination = filePath 259 | .replace('src', 'dist') 260 | .replace(/\.css$/, '.wxss'); 261 | // Make sure the directory already exists when write file 262 | await fs.copy(filePath, destination); 263 | await fs.writeFile(destination, css); 264 | } 265 | 266 | const cb = async (filePath) => { 267 | if (filePath.endsWith('.ts') || filePath.endsWith('.js')) { 268 | await processScript(filePath); 269 | return; 270 | } 271 | 272 | if (filePath.endsWith('.html')) { 273 | await processTemplate(filePath); 274 | return; 275 | } 276 | 277 | if (filePath.endsWith('.css')) { 278 | await processStyle(filePath); 279 | return; 280 | } 281 | 282 | await fs.copy(filePath, filePath.replace('src', 'dist')); 283 | }; 284 | 285 | async function dev() { 286 | await fs.remove('dist'); 287 | await findIndependentPackages(); 288 | await scanDependencies(); 289 | chokidar 290 | .watch(['src'], { 291 | ignored: (file, stats) => 292 | stats?.isFile() && 293 | (file.endsWith('.gitkeep') || file.endsWith('.DS_Store')), 294 | }) 295 | .on('add', (filePath) => { 296 | const promise = cb(filePath); 297 | topLevelJobs?.push(promise); 298 | }) 299 | .on('change', (filePath) => { 300 | cb(filePath); 301 | }) 302 | .on('ready', async () => { 303 | await Promise.all(topLevelJobs); 304 | await Promise.all(bundleJobs); 305 | console.log(bold(green(`启动完成,耗时:${Date.now() - startTime}ms`))); 306 | console.log(bold(green('监听文件变化中...'))); 307 | // Release memory. 308 | topLevelJobs = null; 309 | bundleJobs = null; 310 | }); 311 | } 312 | 313 | async function prod() { 314 | await fs.remove('dist'); 315 | await findIndependentPackages(); 316 | await scanDependencies(); 317 | const watcher = chokidar.watch(['src'], { 318 | ignored: (file, stats) => 319 | stats?.isFile() && 320 | (file.endsWith('.gitkeep') || file.endsWith('.DS_Store')), 321 | }); 322 | watcher.on('add', (filePath) => { 323 | const promise = cb(filePath); 324 | topLevelJobs.push(promise); 325 | }); 326 | watcher.on('ready', async () => { 327 | const promise = watcher.close(); 328 | topLevelJobs.push(promise); 329 | await Promise.all(topLevelJobs); 330 | await Promise.all(bundleJobs); 331 | console.log(bold(green(`构建完成,耗时:${Date.now() - startTime}ms`))); 332 | }); 333 | } 334 | 335 | if (__PROD__) { 336 | await prod(); 337 | } else { 338 | await dev(); 339 | } 340 | -------------------------------------------------------------------------------- /index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */ 4 | /* eslint-disable @typescript-eslint/no-unsafe-assignment */ 5 | import fs from 'node:fs' 6 | import path from 'node:path' 7 | import process from 'node:process' 8 | import { fileURLToPath } from 'node:url' 9 | import ejs from 'ejs' 10 | import prompts from 'prompts' 11 | import { red, green, bold } from 'kolorist' 12 | 13 | const defaultBanner = 'Vue Mini - 简单,强大,高性能的小程序框架' 14 | 15 | // Generated by the following code: 16 | // 17 | // require('gradient-string')([ 18 | // { color: '#42d392', pos: 0 }, 19 | // { color: '#42d392', pos: 0.1 }, 20 | // { color: '#647eff', pos: 1 } 21 | // ])('Vue Mini - 简单,强大,高性能的小程序框架') 22 | // 23 | // Use the output directly here to keep the bundle small. 24 | const gradientBanner = 25 | '\u001B[38;2;66;211;146mV\u001B[39m\u001B[38;2;66;211;146mu\u001B[39m\u001B[38;2;66;211;146me\u001B[39m \u001B[38;2;68;207;151mM\u001B[39m\u001B[38;2;69;203;157mi\u001B[39m\u001B[38;2;71;198;162mn\u001B[39m\u001B[38;2;73;194;168mi\u001B[39m \u001B[38;2;75;190;173m-\u001B[39m \u001B[38;2;76;186;179m简\u001B[39m\u001B[38;2;78;181;184m单\u001B[39m\u001B[38;2;80;177;190m,\u001B[39m\u001B[38;2;81;173;195m强\u001B[39m\u001B[38;2;83;169;201m大\u001B[39m\u001B[38;2;85;164;206m,\u001B[39m\u001B[38;2;86;160;211m高\u001B[39m\u001B[38;2;88;156;217m性\u001B[39m\u001B[38;2;90;152;222m能\u001B[39m\u001B[38;2;92;147;228m的\u001B[39m\u001B[38;2;93;143;233m小\u001B[39m\u001B[38;2;95;139;239m程\u001B[39m\u001B[38;2;97;135;244m序\u001B[39m\u001B[38;2;98;130;250m框\u001B[39m\u001B[38;2;100;126;255m架\u001B[39m' 26 | 27 | function postOrderDirectoryTraverse( 28 | dir: string, 29 | dirCallback: (dir: string) => void, 30 | fileCallback: (file: string) => void, 31 | ) { 32 | for (const filename of fs.readdirSync(dir)) { 33 | if (filename === '.git') { 34 | continue 35 | } 36 | 37 | const fullpath = path.resolve(dir, filename) 38 | if (fs.lstatSync(fullpath).isDirectory()) { 39 | postOrderDirectoryTraverse(fullpath, dirCallback, fileCallback) 40 | dirCallback(fullpath) 41 | continue 42 | } 43 | 44 | fileCallback(fullpath) 45 | } 46 | } 47 | 48 | function isValidPackageName(projectName: string) { 49 | return /^(?:@[a-z\d-*~][a-z\d-*._~]*\/)?[a-z\d-~][a-z\d-._~]*$/.test( 50 | projectName, 51 | ) 52 | } 53 | 54 | function toValidPackageName(projectName: string) { 55 | return projectName 56 | .trim() 57 | .toLowerCase() 58 | .replaceAll(/\s+/g, '-') 59 | .replace(/^[._]/, '') 60 | .replaceAll(/[^a-z\d-~]+/g, '-') 61 | } 62 | 63 | function canSkipEmptying(dir: string) { 64 | if (!fs.existsSync(dir)) { 65 | return true 66 | } 67 | 68 | const files = fs.readdirSync(dir) 69 | if (files.length === 0) { 70 | return true 71 | } 72 | 73 | if (files.length === 1 && files[0] === '.git') { 74 | return true 75 | } 76 | 77 | return false 78 | } 79 | 80 | function emptyDir(dir: string) { 81 | if (!fs.existsSync(dir)) { 82 | return 83 | } 84 | 85 | postOrderDirectoryTraverse( 86 | dir, 87 | (dir) => { 88 | fs.rmdirSync(dir) 89 | }, 90 | (file) => { 91 | fs.unlinkSync(file) 92 | }, 93 | ) 94 | } 95 | 96 | type Result = { 97 | projectName?: string 98 | shouldOverwrite?: boolean 99 | packageName?: string 100 | needsTypeScript?: boolean 101 | needsPinia?: boolean 102 | needsVitest?: boolean 103 | needsEslint?: boolean 104 | needsStylelint?: boolean 105 | needsPrettier?: boolean 106 | } 107 | 108 | function renderTemplate( 109 | src: string, 110 | dest: string, 111 | result: Required> & { 112 | packageManager: string 113 | }, 114 | ) { 115 | const stats = fs.statSync(src) 116 | 117 | if (stats.isDirectory()) { 118 | // If it's a directory, render its subdirectories and files recursively 119 | fs.mkdirSync(dest, { recursive: true }) 120 | for (const file of fs.readdirSync(src)) { 121 | renderTemplate( 122 | path.resolve(src, file), 123 | path.resolve( 124 | dest, 125 | /^_[a-z]/.test(file) ? file.replace('_', '.') : file, 126 | ), 127 | result, 128 | ) 129 | } 130 | 131 | return 132 | } 133 | 134 | const filename = path.basename(src) 135 | 136 | if (filename.endsWith('.ejs')) { 137 | const template = fs.readFileSync(src, 'utf8') 138 | let content = ejs.render(template, result) 139 | 140 | if (filename === 'package.json.ejs') { 141 | content = content.replaceAll(',\n trailing-comma', '') 142 | } 143 | 144 | fs.writeFileSync(dest.replace(/\.ejs$/, ''), content) 145 | return 146 | } 147 | 148 | if (filename === 'project.config.json') { 149 | const project = JSON.parse(fs.readFileSync(src, 'utf8')) 150 | project.projectname = result.packageName 151 | fs.writeFileSync(dest, JSON.stringify(project, null, 2) + '\n') 152 | return 153 | } 154 | 155 | if (filename === 'app.json') { 156 | const app = JSON.parse(fs.readFileSync(src, 'utf8')) 157 | app.window.navigationBarTitleText = result.packageName 158 | fs.writeFileSync(dest, JSON.stringify(app, null, 2) + '\n') 159 | return 160 | } 161 | 162 | fs.copyFileSync(src, dest) 163 | } 164 | 165 | function getCommand(packageManager: string, scriptName: string) { 166 | if (scriptName === 'install') { 167 | return packageManager === 'yarn' ? 'yarn' : `${packageManager} install` 168 | } 169 | 170 | return ['npm', 'bun'].includes(packageManager) ? 171 | `${packageManager} run ${scriptName}` 172 | : `${packageManager} ${scriptName}` 173 | } 174 | 175 | function generateReadme({ 176 | packageManager, 177 | projectName, 178 | needsTypeScript, 179 | needsVitest, 180 | needsEslint, 181 | needsStylelint, 182 | needsPrettier, 183 | }: { packageManager: string } & Required< 184 | Omit 185 | >) { 186 | let readme = `# ${projectName} 187 | 188 | ⚠️ 注意:将此项目导入微信开发者工具时请选择项目根目录而非 \`dist\` 目录。 189 | 190 | 更多信息请访问官方文档:[vuemini.org](https://vuemini.org) 191 | 192 | ## 依赖安装 193 | 194 | \`\`\`sh 195 | ${getCommand(packageManager, 'install')} 196 | \`\`\` 197 | 198 | ## 本地开发 199 | 200 | \`\`\`sh 201 | ${getCommand(packageManager, 'dev')} 202 | \`\`\` 203 | 204 | ## 生产构建 205 | 206 | \`\`\`sh 207 | ${getCommand(packageManager, 'build')} 208 | \`\`\` 209 | ` 210 | 211 | if (needsPrettier) { 212 | readme += ` 213 | ## 代码格式化 214 | 215 | \`\`\`sh 216 | ${getCommand(packageManager, 'format')} 217 | \`\`\` 218 | ` 219 | } 220 | 221 | if (needsEslint) { 222 | readme += ` 223 | ## ${needsTypeScript ? 'TS' : 'JS'} 代码质量检测 224 | 225 | \`\`\`sh 226 | ${getCommand(packageManager, needsStylelint ? 'lint:script' : 'lint')} 227 | \`\`\` 228 | ` 229 | } 230 | 231 | if (needsStylelint) { 232 | readme += ` 233 | ## CSS 代码质量检测 234 | 235 | \`\`\`sh 236 | ${getCommand(packageManager, needsEslint ? 'lint:style' : 'lint')} 237 | \`\`\` 238 | ` 239 | } 240 | 241 | if (needsTypeScript) { 242 | readme += ` 243 | ## 类型检测 244 | 245 | \`\`\`sh 246 | ${getCommand(packageManager, 'type-check')} 247 | \`\`\` 248 | ` 249 | } 250 | 251 | if (needsVitest) { 252 | readme += ` 253 | ## 单元测试 254 | 255 | \`\`\`sh 256 | ${getCommand(packageManager, 'test')} 257 | \`\`\` 258 | ` 259 | } 260 | 261 | return readme 262 | } 263 | 264 | async function init() { 265 | console.log() 266 | console.log( 267 | process.stdout.isTTY && process.stdout.getColorDepth() > 8 ? 268 | gradientBanner 269 | : defaultBanner, 270 | ) 271 | console.log() 272 | 273 | const cwd = process.cwd() 274 | let targetDir = process.argv[2] 275 | const defaultProjectName = targetDir ?? 'vue-mini-project' 276 | 277 | let result: Result = {} 278 | 279 | try { 280 | // Prompts: 281 | // - Project name: 282 | // - whether to overwrite the existing directory or not? 283 | // - enter a valid package name for package.json 284 | result = await prompts( 285 | [ 286 | { 287 | name: 'projectName', 288 | type: targetDir ? null : 'text', 289 | message: '请输入项目名称:', 290 | initial: defaultProjectName, 291 | onState(state) { 292 | targetDir = String(state.value).trim() || defaultProjectName 293 | }, 294 | }, 295 | { 296 | name: 'shouldOverwrite', 297 | type: () => (canSkipEmptying(targetDir) ? null : 'toggle'), 298 | message() { 299 | const dirForPrompt = 300 | targetDir === '.' ? '当前目录' : `目标文件夹 "${targetDir}"` 301 | 302 | return `${dirForPrompt} 非空,是否覆盖?` 303 | }, 304 | initial: true, 305 | active: '是', 306 | inactive: '否', 307 | }, 308 | { 309 | name: 'overwriteChecker', 310 | type(_, values) { 311 | if (values.shouldOverwrite === false) { 312 | throw new Error(red('✖') + ` 操作取消`) 313 | } 314 | 315 | return null 316 | }, 317 | }, 318 | { 319 | name: 'packageName', 320 | type: () => (isValidPackageName(targetDir) ? null : 'text'), 321 | message: '请输入包名称:', 322 | initial: () => toValidPackageName(targetDir), 323 | validate: (dir: string) => 324 | isValidPackageName(dir) || '无效的 package.json 名称', 325 | }, 326 | { 327 | name: 'needsTypeScript', 328 | type: 'toggle', 329 | message: '是否使用 TypeScript 语法?', 330 | initial: false, 331 | active: '是', 332 | inactive: '否', 333 | }, 334 | { 335 | name: 'needsPinia', 336 | type: 'toggle', 337 | message: '是否引入 Pinia 用于状态管理?', 338 | initial: false, 339 | active: '是', 340 | inactive: '否', 341 | }, 342 | { 343 | name: 'needsVitest', 344 | type: 'toggle', 345 | message: '是否引入 Vitest 用于单元测试?', 346 | initial: false, 347 | active: '是', 348 | inactive: '否', 349 | }, 350 | { 351 | name: 'needsEslint', 352 | type: 'toggle', 353 | message: (_, values) => 354 | `是否引入 ESLint 用于 ${values.needsTypeScript ? 'TS' : 'JS'} 代码质量检测?`, 355 | initial: false, 356 | active: '是', 357 | inactive: '否', 358 | }, 359 | { 360 | name: 'needsStylelint', 361 | type: 'toggle', 362 | message: '是否引入 Stylelint 用于 CSS 代码质量检测?', 363 | initial: false, 364 | active: '是', 365 | inactive: '否', 366 | }, 367 | { 368 | name: 'needsPrettier', 369 | type: 'toggle', 370 | message: '是否引入 Prettier 用于代码格式化?', 371 | initial: false, 372 | active: '是', 373 | inactive: '否', 374 | }, 375 | ], 376 | { 377 | onCancel() { 378 | throw new Error(red('✖') + ` 操作取消`) 379 | }, 380 | }, 381 | ) 382 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 383 | } catch (error: any) { 384 | console.error(error.message) 385 | process.exit(1) 386 | } 387 | 388 | // `initial` won't take effect if the prompt type is null 389 | // so we still have to assign the default values here 390 | const { 391 | projectName, 392 | shouldOverwrite = false, 393 | packageName = projectName ?? defaultProjectName, 394 | needsTypeScript = false, 395 | needsPinia = false, 396 | needsVitest = false, 397 | needsEslint = false, 398 | needsStylelint = false, 399 | needsPrettier = false, 400 | } = result 401 | 402 | const root = path.join(cwd, targetDir) 403 | 404 | if (fs.existsSync(root) && shouldOverwrite) { 405 | emptyDir(root) 406 | } else if (!fs.existsSync(root)) { 407 | fs.mkdirSync(root) 408 | } 409 | 410 | console.log(`\n正在初始化项目 ${root}...`) 411 | 412 | const templateRoot = fileURLToPath(new URL('template', import.meta.url)) 413 | 414 | // Instructions: 415 | // Supported package managers: pnpm > yarn > npm 416 | const userAgent = process.env.npm_config_user_agent ?? '' 417 | const packageManager = 418 | userAgent.includes('pnpm') ? 'pnpm' 419 | : userAgent.includes('yarn') ? 'yarn' 420 | : userAgent.includes('bun') ? 'bun' 421 | : 'npm' 422 | 423 | const render = (templateName: string) => { 424 | renderTemplate(path.resolve(templateRoot, templateName), root, { 425 | packageName, 426 | needsTypeScript, 427 | needsPinia, 428 | needsVitest, 429 | needsEslint, 430 | needsStylelint, 431 | needsPrettier, 432 | packageManager: 433 | ['npm', 'bun'].includes(packageManager) ? '' : ( 434 | userAgent.split(' ')[0].replace('/', '@') 435 | ), 436 | }) 437 | } 438 | 439 | render('base') 440 | 441 | if (needsTypeScript) { 442 | render('typescript') 443 | } else { 444 | render('javascript') 445 | } 446 | 447 | if (needsPinia) { 448 | if (needsTypeScript) { 449 | render('pinia-typescript') 450 | } else { 451 | render('pinia') 452 | } 453 | } 454 | 455 | if (needsEslint) { 456 | render('eslint') 457 | } 458 | 459 | if (needsStylelint) { 460 | render('stylelint') 461 | } 462 | 463 | if (needsPrettier) { 464 | render('prettier') 465 | } 466 | 467 | // Vitest should be the last one to render. 468 | if (needsVitest) { 469 | if (packageManager === 'pnpm') { 470 | render('vitest-pnpm') 471 | } 472 | 473 | if (needsTypeScript) { 474 | render('vitest-typescript') 475 | } else { 476 | render('vitest') 477 | } 478 | } 479 | 480 | fs.writeFileSync( 481 | path.resolve(root, 'README.md'), 482 | generateReadme({ 483 | packageManager, 484 | projectName: projectName ?? packageName ?? defaultProjectName, 485 | needsTypeScript, 486 | needsPinia, 487 | needsVitest, 488 | needsEslint, 489 | needsStylelint, 490 | needsPrettier, 491 | }), 492 | ) 493 | 494 | console.log(`\n项目初始化完成,可执行以下命令:\n`) 495 | if (root !== cwd) { 496 | const cdProjectName = path.relative(cwd, root) 497 | console.log( 498 | ` ${bold(green(`cd ${cdProjectName.includes(' ') ? `"${cdProjectName}"` : cdProjectName}`))}`, 499 | ) 500 | } 501 | 502 | console.log(` ${bold(green(getCommand(packageManager, 'install')))}`) 503 | 504 | if (needsPrettier) { 505 | console.log(` ${bold(green(getCommand(packageManager, 'format')))}`) 506 | } 507 | 508 | console.log(` ${bold(green(getCommand(packageManager, 'dev')))}`) 509 | console.log() 510 | } 511 | 512 | try { 513 | await init() 514 | } catch (error) { 515 | console.error(error) 516 | } 517 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | ejs: 12 | specifier: ^3.1.10 13 | version: 3.1.10 14 | kolorist: 15 | specifier: ^1.8.0 16 | version: 1.8.0 17 | prompts: 18 | specifier: ^2.4.2 19 | version: 2.4.2 20 | devDependencies: 21 | '@eslint/js': 22 | specifier: ^9.39.1 23 | version: 9.39.1 24 | '@types/ejs': 25 | specifier: ^3.1.5 26 | version: 3.1.5 27 | '@types/node': 28 | specifier: ^24.10.1 29 | version: 24.10.1 30 | '@types/prompts': 31 | specifier: ^2.4.9 32 | version: 2.4.9 33 | eslint: 34 | specifier: ^9.39.1 35 | version: 9.39.1 36 | prettier: 37 | specifier: ^3.7.2 38 | version: 3.7.2 39 | typescript: 40 | specifier: ^5.9.3 41 | version: 5.9.3 42 | typescript-eslint: 43 | specifier: ^8.48.0 44 | version: 8.48.0(eslint@9.39.1)(typescript@5.9.3) 45 | 46 | packages: 47 | 48 | '@eslint-community/eslint-utils@4.9.0': 49 | resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} 50 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 51 | peerDependencies: 52 | eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 53 | 54 | '@eslint-community/regexpp@4.12.2': 55 | resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} 56 | engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} 57 | 58 | '@eslint/config-array@0.21.1': 59 | resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} 60 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 61 | 62 | '@eslint/config-helpers@0.4.2': 63 | resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} 64 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 65 | 66 | '@eslint/core@0.17.0': 67 | resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} 68 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 69 | 70 | '@eslint/eslintrc@3.3.3': 71 | resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} 72 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 73 | 74 | '@eslint/js@9.39.1': 75 | resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==} 76 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 77 | 78 | '@eslint/object-schema@2.1.7': 79 | resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} 80 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 81 | 82 | '@eslint/plugin-kit@0.4.1': 83 | resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} 84 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 85 | 86 | '@humanfs/core@0.19.1': 87 | resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} 88 | engines: {node: '>=18.18.0'} 89 | 90 | '@humanfs/node@0.16.7': 91 | resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} 92 | engines: {node: '>=18.18.0'} 93 | 94 | '@humanwhocodes/module-importer@1.0.1': 95 | resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} 96 | engines: {node: '>=12.22'} 97 | 98 | '@humanwhocodes/retry@0.4.3': 99 | resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} 100 | engines: {node: '>=18.18'} 101 | 102 | '@types/ejs@3.1.5': 103 | resolution: {integrity: sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==} 104 | 105 | '@types/estree@1.0.8': 106 | resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} 107 | 108 | '@types/json-schema@7.0.15': 109 | resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} 110 | 111 | '@types/node@24.10.1': 112 | resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} 113 | 114 | '@types/prompts@2.4.9': 115 | resolution: {integrity: sha512-qTxFi6Buiu8+50/+3DGIWLHM6QuWsEKugJnnP6iv2Mc4ncxE4A/OJkjuVOA+5X0X1S/nq5VJRa8Lu+nwcvbrKA==} 116 | 117 | '@typescript-eslint/eslint-plugin@8.48.0': 118 | resolution: {integrity: sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==} 119 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 120 | peerDependencies: 121 | '@typescript-eslint/parser': ^8.48.0 122 | eslint: ^8.57.0 || ^9.0.0 123 | typescript: '>=4.8.4 <6.0.0' 124 | 125 | '@typescript-eslint/parser@8.48.0': 126 | resolution: {integrity: sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==} 127 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 128 | peerDependencies: 129 | eslint: ^8.57.0 || ^9.0.0 130 | typescript: '>=4.8.4 <6.0.0' 131 | 132 | '@typescript-eslint/project-service@8.48.0': 133 | resolution: {integrity: sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==} 134 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 135 | peerDependencies: 136 | typescript: '>=4.8.4 <6.0.0' 137 | 138 | '@typescript-eslint/scope-manager@8.48.0': 139 | resolution: {integrity: sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==} 140 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 141 | 142 | '@typescript-eslint/tsconfig-utils@8.48.0': 143 | resolution: {integrity: sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==} 144 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 145 | peerDependencies: 146 | typescript: '>=4.8.4 <6.0.0' 147 | 148 | '@typescript-eslint/type-utils@8.48.0': 149 | resolution: {integrity: sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==} 150 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 151 | peerDependencies: 152 | eslint: ^8.57.0 || ^9.0.0 153 | typescript: '>=4.8.4 <6.0.0' 154 | 155 | '@typescript-eslint/types@8.48.0': 156 | resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==} 157 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 158 | 159 | '@typescript-eslint/typescript-estree@8.48.0': 160 | resolution: {integrity: sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==} 161 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 162 | peerDependencies: 163 | typescript: '>=4.8.4 <6.0.0' 164 | 165 | '@typescript-eslint/utils@8.48.0': 166 | resolution: {integrity: sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==} 167 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 168 | peerDependencies: 169 | eslint: ^8.57.0 || ^9.0.0 170 | typescript: '>=4.8.4 <6.0.0' 171 | 172 | '@typescript-eslint/visitor-keys@8.48.0': 173 | resolution: {integrity: sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==} 174 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 175 | 176 | acorn-jsx@5.3.2: 177 | resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} 178 | peerDependencies: 179 | acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 180 | 181 | acorn@8.15.0: 182 | resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} 183 | engines: {node: '>=0.4.0'} 184 | hasBin: true 185 | 186 | ajv@6.12.6: 187 | resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} 188 | 189 | ansi-styles@4.3.0: 190 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 191 | engines: {node: '>=8'} 192 | 193 | argparse@2.0.1: 194 | resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} 195 | 196 | async@3.2.6: 197 | resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} 198 | 199 | balanced-match@1.0.2: 200 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 201 | 202 | brace-expansion@1.1.12: 203 | resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} 204 | 205 | brace-expansion@2.0.2: 206 | resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} 207 | 208 | callsites@3.1.0: 209 | resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} 210 | engines: {node: '>=6'} 211 | 212 | chalk@4.1.2: 213 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 214 | engines: {node: '>=10'} 215 | 216 | color-convert@2.0.1: 217 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 218 | engines: {node: '>=7.0.0'} 219 | 220 | color-name@1.1.4: 221 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 222 | 223 | concat-map@0.0.1: 224 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 225 | 226 | cross-spawn@7.0.6: 227 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 228 | engines: {node: '>= 8'} 229 | 230 | debug@4.4.3: 231 | resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} 232 | engines: {node: '>=6.0'} 233 | peerDependencies: 234 | supports-color: '*' 235 | peerDependenciesMeta: 236 | supports-color: 237 | optional: true 238 | 239 | deep-is@0.1.4: 240 | resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} 241 | 242 | ejs@3.1.10: 243 | resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} 244 | engines: {node: '>=0.10.0'} 245 | hasBin: true 246 | 247 | escape-string-regexp@4.0.0: 248 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 249 | engines: {node: '>=10'} 250 | 251 | eslint-scope@8.4.0: 252 | resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} 253 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 254 | 255 | eslint-visitor-keys@3.4.3: 256 | resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} 257 | engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} 258 | 259 | eslint-visitor-keys@4.2.1: 260 | resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} 261 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 262 | 263 | eslint@9.39.1: 264 | resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==} 265 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 266 | hasBin: true 267 | peerDependencies: 268 | jiti: '*' 269 | peerDependenciesMeta: 270 | jiti: 271 | optional: true 272 | 273 | espree@10.4.0: 274 | resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} 275 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 276 | 277 | esquery@1.6.0: 278 | resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} 279 | engines: {node: '>=0.10'} 280 | 281 | esrecurse@4.3.0: 282 | resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} 283 | engines: {node: '>=4.0'} 284 | 285 | estraverse@5.3.0: 286 | resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} 287 | engines: {node: '>=4.0'} 288 | 289 | esutils@2.0.3: 290 | resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} 291 | engines: {node: '>=0.10.0'} 292 | 293 | fast-deep-equal@3.1.3: 294 | resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} 295 | 296 | fast-json-stable-stringify@2.1.0: 297 | resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} 298 | 299 | fast-levenshtein@2.0.6: 300 | resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} 301 | 302 | fdir@6.5.0: 303 | resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} 304 | engines: {node: '>=12.0.0'} 305 | peerDependencies: 306 | picomatch: ^3 || ^4 307 | peerDependenciesMeta: 308 | picomatch: 309 | optional: true 310 | 311 | file-entry-cache@8.0.0: 312 | resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} 313 | engines: {node: '>=16.0.0'} 314 | 315 | filelist@1.0.4: 316 | resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} 317 | 318 | find-up@5.0.0: 319 | resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} 320 | engines: {node: '>=10'} 321 | 322 | flat-cache@4.0.1: 323 | resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} 324 | engines: {node: '>=16'} 325 | 326 | flatted@3.3.3: 327 | resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} 328 | 329 | glob-parent@6.0.2: 330 | resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} 331 | engines: {node: '>=10.13.0'} 332 | 333 | globals@14.0.0: 334 | resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} 335 | engines: {node: '>=18'} 336 | 337 | graphemer@1.4.0: 338 | resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} 339 | 340 | has-flag@4.0.0: 341 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 342 | engines: {node: '>=8'} 343 | 344 | ignore@5.3.2: 345 | resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} 346 | engines: {node: '>= 4'} 347 | 348 | ignore@7.0.5: 349 | resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} 350 | engines: {node: '>= 4'} 351 | 352 | import-fresh@3.3.1: 353 | resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} 354 | engines: {node: '>=6'} 355 | 356 | imurmurhash@0.1.4: 357 | resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} 358 | engines: {node: '>=0.8.19'} 359 | 360 | is-extglob@2.1.1: 361 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 362 | engines: {node: '>=0.10.0'} 363 | 364 | is-glob@4.0.3: 365 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 366 | engines: {node: '>=0.10.0'} 367 | 368 | isexe@2.0.0: 369 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 370 | 371 | jake@10.9.4: 372 | resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} 373 | engines: {node: '>=10'} 374 | hasBin: true 375 | 376 | js-yaml@4.1.1: 377 | resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} 378 | hasBin: true 379 | 380 | json-buffer@3.0.1: 381 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 382 | 383 | json-schema-traverse@0.4.1: 384 | resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} 385 | 386 | json-stable-stringify-without-jsonify@1.0.1: 387 | resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} 388 | 389 | keyv@4.5.4: 390 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 391 | 392 | kleur@3.0.3: 393 | resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} 394 | engines: {node: '>=6'} 395 | 396 | kolorist@1.8.0: 397 | resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} 398 | 399 | levn@0.4.1: 400 | resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} 401 | engines: {node: '>= 0.8.0'} 402 | 403 | locate-path@6.0.0: 404 | resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} 405 | engines: {node: '>=10'} 406 | 407 | lodash.merge@4.6.2: 408 | resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} 409 | 410 | minimatch@3.1.2: 411 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 412 | 413 | minimatch@5.1.6: 414 | resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} 415 | engines: {node: '>=10'} 416 | 417 | minimatch@9.0.5: 418 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 419 | engines: {node: '>=16 || 14 >=14.17'} 420 | 421 | ms@2.1.3: 422 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 423 | 424 | natural-compare@1.4.0: 425 | resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} 426 | 427 | optionator@0.9.4: 428 | resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} 429 | engines: {node: '>= 0.8.0'} 430 | 431 | p-limit@3.1.0: 432 | resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} 433 | engines: {node: '>=10'} 434 | 435 | p-locate@5.0.0: 436 | resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} 437 | engines: {node: '>=10'} 438 | 439 | parent-module@1.0.1: 440 | resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} 441 | engines: {node: '>=6'} 442 | 443 | path-exists@4.0.0: 444 | resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} 445 | engines: {node: '>=8'} 446 | 447 | path-key@3.1.1: 448 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 449 | engines: {node: '>=8'} 450 | 451 | picocolors@1.1.1: 452 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 453 | 454 | picomatch@4.0.3: 455 | resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} 456 | engines: {node: '>=12'} 457 | 458 | prelude-ls@1.2.1: 459 | resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 460 | engines: {node: '>= 0.8.0'} 461 | 462 | prettier@3.7.2: 463 | resolution: {integrity: sha512-n3HV2J6QhItCXndGa3oMWvWFAgN1ibnS7R9mt6iokScBOC0Ul9/iZORmU2IWUMcyAQaMPjTlY3uT34TqocUxMA==} 464 | engines: {node: '>=14'} 465 | hasBin: true 466 | 467 | prompts@2.4.2: 468 | resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} 469 | engines: {node: '>= 6'} 470 | 471 | punycode@2.3.1: 472 | resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} 473 | engines: {node: '>=6'} 474 | 475 | resolve-from@4.0.0: 476 | resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} 477 | engines: {node: '>=4'} 478 | 479 | semver@7.7.3: 480 | resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} 481 | engines: {node: '>=10'} 482 | hasBin: true 483 | 484 | shebang-command@2.0.0: 485 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 486 | engines: {node: '>=8'} 487 | 488 | shebang-regex@3.0.0: 489 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 490 | engines: {node: '>=8'} 491 | 492 | sisteransi@1.0.5: 493 | resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} 494 | 495 | strip-json-comments@3.1.1: 496 | resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} 497 | engines: {node: '>=8'} 498 | 499 | supports-color@7.2.0: 500 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 501 | engines: {node: '>=8'} 502 | 503 | tinyglobby@0.2.15: 504 | resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} 505 | engines: {node: '>=12.0.0'} 506 | 507 | ts-api-utils@2.1.0: 508 | resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} 509 | engines: {node: '>=18.12'} 510 | peerDependencies: 511 | typescript: '>=4.8.4' 512 | 513 | type-check@0.4.0: 514 | resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} 515 | engines: {node: '>= 0.8.0'} 516 | 517 | typescript-eslint@8.48.0: 518 | resolution: {integrity: sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==} 519 | engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} 520 | peerDependencies: 521 | eslint: ^8.57.0 || ^9.0.0 522 | typescript: '>=4.8.4 <6.0.0' 523 | 524 | typescript@5.9.3: 525 | resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} 526 | engines: {node: '>=14.17'} 527 | hasBin: true 528 | 529 | undici-types@7.16.0: 530 | resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} 531 | 532 | uri-js@4.4.1: 533 | resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} 534 | 535 | which@2.0.2: 536 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 537 | engines: {node: '>= 8'} 538 | hasBin: true 539 | 540 | word-wrap@1.2.5: 541 | resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} 542 | engines: {node: '>=0.10.0'} 543 | 544 | yocto-queue@0.1.0: 545 | resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} 546 | engines: {node: '>=10'} 547 | 548 | snapshots: 549 | 550 | '@eslint-community/eslint-utils@4.9.0(eslint@9.39.1)': 551 | dependencies: 552 | eslint: 9.39.1 553 | eslint-visitor-keys: 3.4.3 554 | 555 | '@eslint-community/regexpp@4.12.2': {} 556 | 557 | '@eslint/config-array@0.21.1': 558 | dependencies: 559 | '@eslint/object-schema': 2.1.7 560 | debug: 4.4.3 561 | minimatch: 3.1.2 562 | transitivePeerDependencies: 563 | - supports-color 564 | 565 | '@eslint/config-helpers@0.4.2': 566 | dependencies: 567 | '@eslint/core': 0.17.0 568 | 569 | '@eslint/core@0.17.0': 570 | dependencies: 571 | '@types/json-schema': 7.0.15 572 | 573 | '@eslint/eslintrc@3.3.3': 574 | dependencies: 575 | ajv: 6.12.6 576 | debug: 4.4.3 577 | espree: 10.4.0 578 | globals: 14.0.0 579 | ignore: 5.3.2 580 | import-fresh: 3.3.1 581 | js-yaml: 4.1.1 582 | minimatch: 3.1.2 583 | strip-json-comments: 3.1.1 584 | transitivePeerDependencies: 585 | - supports-color 586 | 587 | '@eslint/js@9.39.1': {} 588 | 589 | '@eslint/object-schema@2.1.7': {} 590 | 591 | '@eslint/plugin-kit@0.4.1': 592 | dependencies: 593 | '@eslint/core': 0.17.0 594 | levn: 0.4.1 595 | 596 | '@humanfs/core@0.19.1': {} 597 | 598 | '@humanfs/node@0.16.7': 599 | dependencies: 600 | '@humanfs/core': 0.19.1 601 | '@humanwhocodes/retry': 0.4.3 602 | 603 | '@humanwhocodes/module-importer@1.0.1': {} 604 | 605 | '@humanwhocodes/retry@0.4.3': {} 606 | 607 | '@types/ejs@3.1.5': {} 608 | 609 | '@types/estree@1.0.8': {} 610 | 611 | '@types/json-schema@7.0.15': {} 612 | 613 | '@types/node@24.10.1': 614 | dependencies: 615 | undici-types: 7.16.0 616 | 617 | '@types/prompts@2.4.9': 618 | dependencies: 619 | '@types/node': 24.10.1 620 | kleur: 3.0.3 621 | 622 | '@typescript-eslint/eslint-plugin@8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3)': 623 | dependencies: 624 | '@eslint-community/regexpp': 4.12.2 625 | '@typescript-eslint/parser': 8.48.0(eslint@9.39.1)(typescript@5.9.3) 626 | '@typescript-eslint/scope-manager': 8.48.0 627 | '@typescript-eslint/type-utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) 628 | '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) 629 | '@typescript-eslint/visitor-keys': 8.48.0 630 | eslint: 9.39.1 631 | graphemer: 1.4.0 632 | ignore: 7.0.5 633 | natural-compare: 1.4.0 634 | ts-api-utils: 2.1.0(typescript@5.9.3) 635 | typescript: 5.9.3 636 | transitivePeerDependencies: 637 | - supports-color 638 | 639 | '@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3)': 640 | dependencies: 641 | '@typescript-eslint/scope-manager': 8.48.0 642 | '@typescript-eslint/types': 8.48.0 643 | '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) 644 | '@typescript-eslint/visitor-keys': 8.48.0 645 | debug: 4.4.3 646 | eslint: 9.39.1 647 | typescript: 5.9.3 648 | transitivePeerDependencies: 649 | - supports-color 650 | 651 | '@typescript-eslint/project-service@8.48.0(typescript@5.9.3)': 652 | dependencies: 653 | '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) 654 | '@typescript-eslint/types': 8.48.0 655 | debug: 4.4.3 656 | typescript: 5.9.3 657 | transitivePeerDependencies: 658 | - supports-color 659 | 660 | '@typescript-eslint/scope-manager@8.48.0': 661 | dependencies: 662 | '@typescript-eslint/types': 8.48.0 663 | '@typescript-eslint/visitor-keys': 8.48.0 664 | 665 | '@typescript-eslint/tsconfig-utils@8.48.0(typescript@5.9.3)': 666 | dependencies: 667 | typescript: 5.9.3 668 | 669 | '@typescript-eslint/type-utils@8.48.0(eslint@9.39.1)(typescript@5.9.3)': 670 | dependencies: 671 | '@typescript-eslint/types': 8.48.0 672 | '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) 673 | '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) 674 | debug: 4.4.3 675 | eslint: 9.39.1 676 | ts-api-utils: 2.1.0(typescript@5.9.3) 677 | typescript: 5.9.3 678 | transitivePeerDependencies: 679 | - supports-color 680 | 681 | '@typescript-eslint/types@8.48.0': {} 682 | 683 | '@typescript-eslint/typescript-estree@8.48.0(typescript@5.9.3)': 684 | dependencies: 685 | '@typescript-eslint/project-service': 8.48.0(typescript@5.9.3) 686 | '@typescript-eslint/tsconfig-utils': 8.48.0(typescript@5.9.3) 687 | '@typescript-eslint/types': 8.48.0 688 | '@typescript-eslint/visitor-keys': 8.48.0 689 | debug: 4.4.3 690 | minimatch: 9.0.5 691 | semver: 7.7.3 692 | tinyglobby: 0.2.15 693 | ts-api-utils: 2.1.0(typescript@5.9.3) 694 | typescript: 5.9.3 695 | transitivePeerDependencies: 696 | - supports-color 697 | 698 | '@typescript-eslint/utils@8.48.0(eslint@9.39.1)(typescript@5.9.3)': 699 | dependencies: 700 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 701 | '@typescript-eslint/scope-manager': 8.48.0 702 | '@typescript-eslint/types': 8.48.0 703 | '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) 704 | eslint: 9.39.1 705 | typescript: 5.9.3 706 | transitivePeerDependencies: 707 | - supports-color 708 | 709 | '@typescript-eslint/visitor-keys@8.48.0': 710 | dependencies: 711 | '@typescript-eslint/types': 8.48.0 712 | eslint-visitor-keys: 4.2.1 713 | 714 | acorn-jsx@5.3.2(acorn@8.15.0): 715 | dependencies: 716 | acorn: 8.15.0 717 | 718 | acorn@8.15.0: {} 719 | 720 | ajv@6.12.6: 721 | dependencies: 722 | fast-deep-equal: 3.1.3 723 | fast-json-stable-stringify: 2.1.0 724 | json-schema-traverse: 0.4.1 725 | uri-js: 4.4.1 726 | 727 | ansi-styles@4.3.0: 728 | dependencies: 729 | color-convert: 2.0.1 730 | 731 | argparse@2.0.1: {} 732 | 733 | async@3.2.6: {} 734 | 735 | balanced-match@1.0.2: {} 736 | 737 | brace-expansion@1.1.12: 738 | dependencies: 739 | balanced-match: 1.0.2 740 | concat-map: 0.0.1 741 | 742 | brace-expansion@2.0.2: 743 | dependencies: 744 | balanced-match: 1.0.2 745 | 746 | callsites@3.1.0: {} 747 | 748 | chalk@4.1.2: 749 | dependencies: 750 | ansi-styles: 4.3.0 751 | supports-color: 7.2.0 752 | 753 | color-convert@2.0.1: 754 | dependencies: 755 | color-name: 1.1.4 756 | 757 | color-name@1.1.4: {} 758 | 759 | concat-map@0.0.1: {} 760 | 761 | cross-spawn@7.0.6: 762 | dependencies: 763 | path-key: 3.1.1 764 | shebang-command: 2.0.0 765 | which: 2.0.2 766 | 767 | debug@4.4.3: 768 | dependencies: 769 | ms: 2.1.3 770 | 771 | deep-is@0.1.4: {} 772 | 773 | ejs@3.1.10: 774 | dependencies: 775 | jake: 10.9.4 776 | 777 | escape-string-regexp@4.0.0: {} 778 | 779 | eslint-scope@8.4.0: 780 | dependencies: 781 | esrecurse: 4.3.0 782 | estraverse: 5.3.0 783 | 784 | eslint-visitor-keys@3.4.3: {} 785 | 786 | eslint-visitor-keys@4.2.1: {} 787 | 788 | eslint@9.39.1: 789 | dependencies: 790 | '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.1) 791 | '@eslint-community/regexpp': 4.12.2 792 | '@eslint/config-array': 0.21.1 793 | '@eslint/config-helpers': 0.4.2 794 | '@eslint/core': 0.17.0 795 | '@eslint/eslintrc': 3.3.3 796 | '@eslint/js': 9.39.1 797 | '@eslint/plugin-kit': 0.4.1 798 | '@humanfs/node': 0.16.7 799 | '@humanwhocodes/module-importer': 1.0.1 800 | '@humanwhocodes/retry': 0.4.3 801 | '@types/estree': 1.0.8 802 | ajv: 6.12.6 803 | chalk: 4.1.2 804 | cross-spawn: 7.0.6 805 | debug: 4.4.3 806 | escape-string-regexp: 4.0.0 807 | eslint-scope: 8.4.0 808 | eslint-visitor-keys: 4.2.1 809 | espree: 10.4.0 810 | esquery: 1.6.0 811 | esutils: 2.0.3 812 | fast-deep-equal: 3.1.3 813 | file-entry-cache: 8.0.0 814 | find-up: 5.0.0 815 | glob-parent: 6.0.2 816 | ignore: 5.3.2 817 | imurmurhash: 0.1.4 818 | is-glob: 4.0.3 819 | json-stable-stringify-without-jsonify: 1.0.1 820 | lodash.merge: 4.6.2 821 | minimatch: 3.1.2 822 | natural-compare: 1.4.0 823 | optionator: 0.9.4 824 | transitivePeerDependencies: 825 | - supports-color 826 | 827 | espree@10.4.0: 828 | dependencies: 829 | acorn: 8.15.0 830 | acorn-jsx: 5.3.2(acorn@8.15.0) 831 | eslint-visitor-keys: 4.2.1 832 | 833 | esquery@1.6.0: 834 | dependencies: 835 | estraverse: 5.3.0 836 | 837 | esrecurse@4.3.0: 838 | dependencies: 839 | estraverse: 5.3.0 840 | 841 | estraverse@5.3.0: {} 842 | 843 | esutils@2.0.3: {} 844 | 845 | fast-deep-equal@3.1.3: {} 846 | 847 | fast-json-stable-stringify@2.1.0: {} 848 | 849 | fast-levenshtein@2.0.6: {} 850 | 851 | fdir@6.5.0(picomatch@4.0.3): 852 | optionalDependencies: 853 | picomatch: 4.0.3 854 | 855 | file-entry-cache@8.0.0: 856 | dependencies: 857 | flat-cache: 4.0.1 858 | 859 | filelist@1.0.4: 860 | dependencies: 861 | minimatch: 5.1.6 862 | 863 | find-up@5.0.0: 864 | dependencies: 865 | locate-path: 6.0.0 866 | path-exists: 4.0.0 867 | 868 | flat-cache@4.0.1: 869 | dependencies: 870 | flatted: 3.3.3 871 | keyv: 4.5.4 872 | 873 | flatted@3.3.3: {} 874 | 875 | glob-parent@6.0.2: 876 | dependencies: 877 | is-glob: 4.0.3 878 | 879 | globals@14.0.0: {} 880 | 881 | graphemer@1.4.0: {} 882 | 883 | has-flag@4.0.0: {} 884 | 885 | ignore@5.3.2: {} 886 | 887 | ignore@7.0.5: {} 888 | 889 | import-fresh@3.3.1: 890 | dependencies: 891 | parent-module: 1.0.1 892 | resolve-from: 4.0.0 893 | 894 | imurmurhash@0.1.4: {} 895 | 896 | is-extglob@2.1.1: {} 897 | 898 | is-glob@4.0.3: 899 | dependencies: 900 | is-extglob: 2.1.1 901 | 902 | isexe@2.0.0: {} 903 | 904 | jake@10.9.4: 905 | dependencies: 906 | async: 3.2.6 907 | filelist: 1.0.4 908 | picocolors: 1.1.1 909 | 910 | js-yaml@4.1.1: 911 | dependencies: 912 | argparse: 2.0.1 913 | 914 | json-buffer@3.0.1: {} 915 | 916 | json-schema-traverse@0.4.1: {} 917 | 918 | json-stable-stringify-without-jsonify@1.0.1: {} 919 | 920 | keyv@4.5.4: 921 | dependencies: 922 | json-buffer: 3.0.1 923 | 924 | kleur@3.0.3: {} 925 | 926 | kolorist@1.8.0: {} 927 | 928 | levn@0.4.1: 929 | dependencies: 930 | prelude-ls: 1.2.1 931 | type-check: 0.4.0 932 | 933 | locate-path@6.0.0: 934 | dependencies: 935 | p-locate: 5.0.0 936 | 937 | lodash.merge@4.6.2: {} 938 | 939 | minimatch@3.1.2: 940 | dependencies: 941 | brace-expansion: 1.1.12 942 | 943 | minimatch@5.1.6: 944 | dependencies: 945 | brace-expansion: 2.0.2 946 | 947 | minimatch@9.0.5: 948 | dependencies: 949 | brace-expansion: 2.0.2 950 | 951 | ms@2.1.3: {} 952 | 953 | natural-compare@1.4.0: {} 954 | 955 | optionator@0.9.4: 956 | dependencies: 957 | deep-is: 0.1.4 958 | fast-levenshtein: 2.0.6 959 | levn: 0.4.1 960 | prelude-ls: 1.2.1 961 | type-check: 0.4.0 962 | word-wrap: 1.2.5 963 | 964 | p-limit@3.1.0: 965 | dependencies: 966 | yocto-queue: 0.1.0 967 | 968 | p-locate@5.0.0: 969 | dependencies: 970 | p-limit: 3.1.0 971 | 972 | parent-module@1.0.1: 973 | dependencies: 974 | callsites: 3.1.0 975 | 976 | path-exists@4.0.0: {} 977 | 978 | path-key@3.1.1: {} 979 | 980 | picocolors@1.1.1: {} 981 | 982 | picomatch@4.0.3: {} 983 | 984 | prelude-ls@1.2.1: {} 985 | 986 | prettier@3.7.2: {} 987 | 988 | prompts@2.4.2: 989 | dependencies: 990 | kleur: 3.0.3 991 | sisteransi: 1.0.5 992 | 993 | punycode@2.3.1: {} 994 | 995 | resolve-from@4.0.0: {} 996 | 997 | semver@7.7.3: {} 998 | 999 | shebang-command@2.0.0: 1000 | dependencies: 1001 | shebang-regex: 3.0.0 1002 | 1003 | shebang-regex@3.0.0: {} 1004 | 1005 | sisteransi@1.0.5: {} 1006 | 1007 | strip-json-comments@3.1.1: {} 1008 | 1009 | supports-color@7.2.0: 1010 | dependencies: 1011 | has-flag: 4.0.0 1012 | 1013 | tinyglobby@0.2.15: 1014 | dependencies: 1015 | fdir: 6.5.0(picomatch@4.0.3) 1016 | picomatch: 4.0.3 1017 | 1018 | ts-api-utils@2.1.0(typescript@5.9.3): 1019 | dependencies: 1020 | typescript: 5.9.3 1021 | 1022 | type-check@0.4.0: 1023 | dependencies: 1024 | prelude-ls: 1.2.1 1025 | 1026 | typescript-eslint@8.48.0(eslint@9.39.1)(typescript@5.9.3): 1027 | dependencies: 1028 | '@typescript-eslint/eslint-plugin': 8.48.0(@typescript-eslint/parser@8.48.0(eslint@9.39.1)(typescript@5.9.3))(eslint@9.39.1)(typescript@5.9.3) 1029 | '@typescript-eslint/parser': 8.48.0(eslint@9.39.1)(typescript@5.9.3) 1030 | '@typescript-eslint/typescript-estree': 8.48.0(typescript@5.9.3) 1031 | '@typescript-eslint/utils': 8.48.0(eslint@9.39.1)(typescript@5.9.3) 1032 | eslint: 9.39.1 1033 | typescript: 5.9.3 1034 | transitivePeerDependencies: 1035 | - supports-color 1036 | 1037 | typescript@5.9.3: {} 1038 | 1039 | undici-types@7.16.0: {} 1040 | 1041 | uri-js@4.4.1: 1042 | dependencies: 1043 | punycode: 2.3.1 1044 | 1045 | which@2.0.2: 1046 | dependencies: 1047 | isexe: 2.0.0 1048 | 1049 | word-wrap@1.2.5: {} 1050 | 1051 | yocto-queue@0.1.0: {} 1052 | --------------------------------------------------------------------------------