From 89b1587dfd6bc77e0a36409c461d04fc0b6faa33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=8E=E4=BC=9F=E6=9D=B0?= <674416404@qq.com> Date: Thu, 23 Oct 2025 00:20:41 +0800 Subject: [PATCH 01/16] chore: migrate to tsdown --- .npmrc | 1 + package.json | 22 +++++----- rollup.config.js | 86 --------------------------------------- src/{main.ts => index.ts} | 0 tsdown.config.ts | 41 +++++++++++++++++++ 5 files changed, 53 insertions(+), 97 deletions(-) create mode 100644 .npmrc delete mode 100644 rollup.config.js rename src/{main.ts => index.ts} (100%) create mode 100644 tsdown.config.ts diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..918fa4a --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +registry=https://registry.npmmirror.com/ \ No newline at end of file diff --git a/package.json b/package.json index 9717da1..860dc69 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,17 @@ "version": "0.5.3", "description": "CLI tool for TDesign Starter project", "type": "module", - "main": "./src/main.ts", + "main": "./bin/index.js", "lib": "./bin/index.js", "bin": { "td-starter": "./bin/index.js" }, "typings": "./src/types/index.d.ts", "scripts": { - "watch": "rollup -w -c", + "watch": "tsdown -w", "test": "npm run dev", "dev": "node ./bin/index.js", - "prebuild": "rimraf bin/", - "build": "rollup -c ", + "build": "tsdown", "lint": "eslint 'src/**/*.{js,ts}'", "site": "echo 'no need to build site'", "site:preview": "echo 'no need to run site preview'" @@ -32,11 +31,6 @@ "@babel/plugin-transform-runtime": "7.24.3", "@babel/preset-env": "7.24.4", "@babel/preset-typescript": "^7.24.1", - "@rollup/plugin-babel": "^6.0.4", - "@rollup/plugin-json": "6.1.0", - "@rollup/plugin-node-resolve": "15.2.3", - "@rollup/plugin-terser": "^0.4.4", - "@rollup/plugin-typescript": "^11.1.6", "@types/babel__core": "^7.20.5", "@types/node": "^22.1.0", "@types/shelljs": "^0.8.15", @@ -45,8 +39,8 @@ "babel-loader": "9.1.3", "eslint": "^8.56.0", "prettier": "^3.2.5", - "rollup": "^4.29.1", - "rollup-plugin-copy": "3.5.0" + "rollup-plugin-copy": "3.5.0", + "tsdown": "^0.15.9" }, "dependencies": { "@babel/plugin-transform-typescript": "^7.24.4", @@ -99,5 +93,11 @@ ], "engines": { "node": ">=16.0.0" + }, + "module": "./bin/index.js", + "types": "./bin/index.d.ts", + "exports": { + ".": "./bin/index.js", + "./package.json": "./package.json" } } diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index 43f9d01..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,86 +0,0 @@ -import path from 'path'; - -import babel from '@rollup/plugin-babel'; -import nodeResolve from '@rollup/plugin-node-resolve'; -import typescript from 'typescript'; -import json from '@rollup/plugin-json'; -import terser from '@rollup/plugin-terser'; -import rollupTypescript from '@rollup/plugin-typescript'; -import pkg from './package.json' with { type: 'json' }; -import copy from 'rollup-plugin-copy'; -import { dirname } from 'path'; -import { fileURLToPath } from 'url'; - -const __dirname = dirname(fileURLToPath(import.meta.url)); - -const extensions = ['.js', '.ts']; - -const external = Object.keys(pkg.dependencies || ''); -const globals = external.reduce((prev, current) => { - const newPrev = prev; - newPrev[current] = current; - return newPrev; -}, {}); - -const defaultConfig = { - input: pkg.main, - output: { - file: path.join(__dirname, pkg.lib), - format: 'es', - banner: '#!/usr/bin/env node', - globals - }, - external, - plugins: [ - rollupTypescript({ - exclude: 'node_modules/**', - typescript, - tsconfig: './tsconfig.json' - }), - json(), - terser(), - nodeResolve({ - extensions, - modulesOnly: true, - // 查找和打包node_modules中的第三方模块 - customResolveOptions: { - moduleDirectories: ['src'] - }, - preferBuiltins: true - }), - babel({ - exclude: 'node_modules/**', - extensions - }), - // 复制templates文件; 复制 .gitignore 文件;添加.npmignore文件:"!.gitignore\n.npmignore" - copy({ - targets: [ - 'templates/farm/vue-lite', - 'templates/farm/vue-next-lite', - 'templates/farm/react-lite', - 'templates/vite/vue-lite', - 'templates/vite/vue-next-lite', - 'templates/vite/react-lite', - 'templates/webpack/vue-lite', - 'templates/webpack/vue-next-lite', - 'templates/webpack/react-lite', - ] - .map((filePath) => [ - { - src: [`${filePath}/*`, `${filePath}/.gitignore`, `!${filePath}/node_modules`], - dest: `bin/${filePath}` - }, - { - src: `${filePath}/.gitignore`, - dest: `bin/${filePath}`, - rename: '.npmignore', - transform: () => Buffer.from('!.gitignore\n.npmignore', 'utf-8') - } - ]) - .flat(), - verbose: true - }) - ] -}; - -export default defaultConfig; diff --git a/src/main.ts b/src/index.ts similarity index 100% rename from src/main.ts rename to src/index.ts diff --git a/tsdown.config.ts b/tsdown.config.ts new file mode 100644 index 0000000..ba12fcb --- /dev/null +++ b/tsdown.config.ts @@ -0,0 +1,41 @@ +import { defineConfig } from 'tsdown' +import copy from 'rollup-plugin-copy'; + +export default defineConfig({ + entry: 'src/index.ts', + platform: 'node', + outDir: 'bin', + exports: true, + shims: true, + dts: false, + plugins: [ + // 复制templates文件; 复制 .gitignore 文件;添加.npmignore文件:"!.gitignore\n.npmignore" + copy({ + targets: [ + 'templates/farm/vue-lite', + 'templates/farm/vue-next-lite', + 'templates/farm/react-lite', + 'templates/vite/vue-lite', + 'templates/vite/vue-next-lite', + 'templates/vite/react-lite', + 'templates/webpack/vue-lite', + 'templates/webpack/vue-next-lite', + 'templates/webpack/react-lite', + ] + .map((filePath) => [ + { + src: [`${filePath}/*`, `${filePath}/.gitignore`, `!${filePath}/node_modules`], + dest: `bin/${filePath}` + }, + { + src: `${filePath}/.gitignore`, + dest: `bin/${filePath}`, + rename: '.npmignore', + transform: () => Buffer.from('!.gitignore\n.npmignore', 'utf-8') + } + ]) + .flat(), + verbose: true + }) + ], +}) \ No newline at end of file From a5e231aba413fe4bc88780cae2a43f5caf806df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=8E=E4=BC=9F=E6=9D=B0?= <674416404@qq.com> Date: Thu, 23 Oct 2025 08:50:57 +0800 Subject: [PATCH 02/16] chore: remove .npmrc --- .npmrc | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .npmrc diff --git a/.npmrc b/.npmrc deleted file mode 100644 index 918fa4a..0000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -registry=https://registry.npmmirror.com/ \ No newline at end of file From cb244b05ef8dd116719bc0203fd937f58feb6bf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=8E=E4=BC=9F=E6=9D=B0?= <674416404@qq.com> Date: Thu, 23 Oct 2025 08:56:18 +0800 Subject: [PATCH 03/16] chore: update export --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 860dc69..3c215b2 100644 --- a/package.json +++ b/package.json @@ -95,7 +95,6 @@ "node": ">=16.0.0" }, "module": "./bin/index.js", - "types": "./bin/index.d.ts", "exports": { ".": "./bin/index.js", "./package.json": "./package.json" From 9e65d35911274011c6f601e89b7b4410e7564901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Sun, 5 Apr 2026 05:01:44 +0800 Subject: [PATCH 04/16] =?UTF-8?q?chore:=20=E5=8D=87=E7=BA=A7=20tsdown=20?= =?UTF-8?q?=E5=B9=B6=E6=9B=B4=E6=96=B0=E6=9E=84=E5=BB=BA=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 3c215b2..354555f 100644 --- a/package.json +++ b/package.json @@ -3,16 +3,14 @@ "version": "0.5.3", "description": "CLI tool for TDesign Starter project", "type": "module", - "main": "./bin/index.js", - "lib": "./bin/index.js", "bin": { - "td-starter": "./bin/index.js" + "td-starter": "./bin/index.mjs" }, "typings": "./src/types/index.d.ts", "scripts": { "watch": "tsdown -w", "test": "npm run dev", - "dev": "node ./bin/index.js", + "dev": "node ./bin/index.mjs", "build": "tsdown", "lint": "eslint 'src/**/*.{js,ts}'", "site": "echo 'no need to build site'", @@ -24,33 +22,26 @@ }, "license": "MIT", "devDependencies": { - "@babel/cli": "7.24.1", "@babel/core": "7.24.4", "@babel/eslint-plugin": "^7.23.5", - "@babel/plugin-proposal-export-default-from": "7.24.1", - "@babel/plugin-transform-runtime": "7.24.3", - "@babel/preset-env": "7.24.4", + "@babel/plugin-transform-typescript": "^7.24.4", "@babel/preset-typescript": "^7.24.1", "@types/babel__core": "^7.20.5", "@types/node": "^22.1.0", "@types/shelljs": "^0.8.15", "@typescript-eslint/eslint-plugin": "7.7.0", "@typescript-eslint/parser": "7.7.0", - "babel-loader": "9.1.3", "eslint": "^8.56.0", "prettier": "^3.2.5", "rollup-plugin-copy": "3.5.0", - "tsdown": "^0.15.9" + "tsdown": "^0.21.7" }, "dependencies": { + "@babel/core": "7.24.4", "@babel/plugin-transform-typescript": "^7.24.4", - "@babel/runtime": "7.24.4", - "@types/chalk": "2.2.0", - "@types/commander": "2.12.2", "@types/fs-extra": "11.0.4", "@types/inquirer": "9.0.7", - "@types/node": "20.12.7", - "@types/rimraf": "4.0.5", + "@types/node": "^22.1.0", "axios": "~1.7.4", "chalk": "5.3.0", "clear": "0.1.0", @@ -89,14 +80,17 @@ "types/*", "package.json", "docs/*", - "template/*" + "templates/*" ], "engines": { - "node": ">=16.0.0" + "node": ">=22.18.0" }, - "module": "./bin/index.js", "exports": { - ".": "./bin/index.js", + ".": "./bin/index.mjs", "./package.json": "./package.json" - } -} + }, + "publishConfig": { + "access": "public" + }, + "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319" +} \ No newline at end of file From 099371041c02cb098c5e15ad1cb82904a1b6f61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Mon, 6 Apr 2026 00:21:03 +0800 Subject: [PATCH 05/16] =?UTF-8?q?chore:=20=E4=BC=98=E5=8C=96=E6=9E=84?= =?UTF-8?q?=E5=BB=BA=E9=85=8D=E7=BD=AE=E4=B8=8E=E4=BE=9D=E8=B5=96=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js => .eslintrc.cjs | 3 +- .gitignore | 5 +- .husky/pre-commit | 1 + .prettierrc.js | 4 +- package.json | 35 ++++-------- src/core/CoreGitDownloader.ts | 1 - src/core/CoreIndex.ts | 10 ++-- src/core/CoreSelector.ts | 2 +- .../core-options/CoreOptionsFilterForReact.ts | 13 +++-- .../core-options/CoreOptionsFilterForVue2.ts | 4 +- tsconfig.json | 8 ++- tsdown.config.ts | 54 ++++++------------- 12 files changed, 49 insertions(+), 91 deletions(-) rename .eslintrc.js => .eslintrc.cjs (76%) create mode 100755 .husky/pre-commit diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 76% rename from .eslintrc.js rename to .eslintrc.cjs index 4ec444e..700ffaf 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -10,6 +10,7 @@ module.exports = { // 'no-console': 'error' '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/ban-ts-comment': 'off' + '@typescript-eslint/ban-ts-comment': 'off', + '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }] } }; diff --git a/.gitignore b/.gitignore index be17caf..e2db568 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,7 @@ src/config/ConfigDev.ts # 测试文件 template-vite-* template-webpack-* -template-farm-* \ No newline at end of file +template-farm-* + +# npm/pnpm pack 产物 +*.tgz \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..2312dc5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.prettierrc.js b/.prettierrc.js index 7774535..e0e7a32 100644 --- a/.prettierrc.js +++ b/.prettierrc.js @@ -1,4 +1,4 @@ -module.exports = { +export default { // 一行最多 120 字符 printWidth: 150, // 使用 2 个空格缩进 @@ -17,8 +17,6 @@ module.exports = { trailingComma: 'none', // 大括号内的首尾需要空格 bracketSpacing: true, - // jsx 标签的反尖括号需要换行 - jsxBracketSameLine: false, // 箭头函数,只有一个参数的时候,也需要括号 arrowParens: 'always', // 每个文件格式化的范围是文件的全部内容 diff --git a/package.json b/package.json index 354555f..716e26c 100644 --- a/package.json +++ b/package.json @@ -6,15 +6,12 @@ "bin": { "td-starter": "./bin/index.mjs" }, - "typings": "./src/types/index.d.ts", "scripts": { "watch": "tsdown -w", - "test": "npm run dev", "dev": "node ./bin/index.mjs", "build": "tsdown", "lint": "eslint 'src/**/*.{js,ts}'", - "site": "echo 'no need to build site'", - "site:preview": "echo 'no need to run site preview'" + "prepare": "husky" }, "repository": { "type": "git", @@ -27,21 +24,21 @@ "@babel/plugin-transform-typescript": "^7.24.4", "@babel/preset-typescript": "^7.24.1", "@types/babel__core": "^7.20.5", + "@types/fs-extra": "11.0.4", + "@types/inquirer": "9.0.7", "@types/node": "^22.1.0", "@types/shelljs": "^0.8.15", "@typescript-eslint/eslint-plugin": "7.7.0", "@typescript-eslint/parser": "7.7.0", "eslint": "^8.56.0", + "husky": "9.0.11", + "lint-staged": "15.2.2", "prettier": "^3.2.5", - "rollup-plugin-copy": "3.5.0", - "tsdown": "^0.21.7" + "tsdown": "^0.21.7", + "typescript": "~5.7.0" }, "dependencies": { "@babel/core": "7.24.4", - "@babel/plugin-transform-typescript": "^7.24.4", - "@types/fs-extra": "11.0.4", - "@types/inquirer": "9.0.7", - "@types/node": "^22.1.0", "axios": "~1.7.4", "chalk": "5.3.0", "clear": "0.1.0", @@ -50,23 +47,11 @@ "download-git-repo": "3.0.2", "figlet": "1.7.0", "fs-extra": "11.2.0", - "glob": "^10.3.12", - "husky": "9.0.11", + "glob": "^13.0.6", "inquirer": "9.2.19", - "is-ci": "3.0.1", - "lint-staged": "15.2.2", - "lodash": "4.17.21", - "minimist": "1.2.8", "ora": "8.0.1", "rimraf": "5.0.5", - "shelljs": "^0.8.5", - "tslib": "2.6.2", - "typescript": "5.4.5" - }, - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } + "shelljs": "^0.8.5" }, "lint-staged": { "*.{js,ts,json}": [ @@ -93,4 +78,4 @@ "access": "public" }, "packageManager": "pnpm@10.33.0+sha512.10568bb4a6afb58c9eb3630da90cc9516417abebd3fabbe6739f0ae795728da1491e9db5a544c76ad8eb7570f5c4bb3d6c637b2cb41bfdcdb47fa823c8649319" -} \ No newline at end of file +} diff --git a/src/core/CoreGitDownloader.ts b/src/core/CoreGitDownloader.ts index 36d2d4f..08f920a 100644 --- a/src/core/CoreGitDownloader.ts +++ b/src/core/CoreGitDownloader.ts @@ -4,7 +4,6 @@ import download from 'download-git-repo'; import ora from 'ora'; import chalk from 'chalk'; import path from 'path'; -import { SupportedTemplate } from '../types/type'; import { CoreOptionsFilterForVue2, IOptionsFilter } from './core-options/CoreOptionsFilterForVue2'; import { CoreOptionsFilterForVue3 } from './core-options/CoreOptionsFilterForVue3'; import { CoreOptionsFilterForReact } from './core-options/CoreOptionsFilterForReact'; diff --git a/src/core/CoreIndex.ts b/src/core/CoreIndex.ts index 027a91f..1b5e207 100644 --- a/src/core/CoreIndex.ts +++ b/src/core/CoreIndex.ts @@ -15,7 +15,7 @@ import { CreatorOptions, SupportedTemplateSize } from '../types/type'; import { CoreLiteDownloader } from './core-lite/CoreLiteDownloader'; class Creator { - constructor(name: string, options: Omit, command: any) { + constructor(name: string, options: Omit, _command: any) { clear(); console.log('*****************************'); console.log(chalk.green(figlet.textSync('TDesign Starter', { horizontalLayout: 'full' }))); @@ -24,12 +24,11 @@ class Creator { const spinner = ora('👉 检查构建环境...').start(); - // 如果有name参数,直接下载模板 if (name) { const answer: CreatorOptions = { ...options, - name, + name }; let isValid = true; @@ -65,8 +64,8 @@ class Creator { } } if (!isValid) { - return - }; + return; + } spinner.succeed(chalk.green('构建环境正常!')); console.log(); @@ -91,7 +90,6 @@ class Creator { spinner.succeed(chalk.green('构建环境正常!')); console.log(); this.init(); - } /** diff --git a/src/core/CoreSelector.ts b/src/core/CoreSelector.ts index 3a27bf3..611da85 100644 --- a/src/core/CoreSelector.ts +++ b/src/core/CoreSelector.ts @@ -8,7 +8,7 @@ import { IParsedSourceData } from './CoreParsedConfig'; import coreTemplateVue2Config from './core-template/CoreTemplateVue2Config'; import coreTemplateVue3Config from './core-template/CoreTemplateVue3Config'; import coreTemplateReactConfig from './core-template/CoreTemplateReactConfig'; -import { CreatorOptions, SupportedTemplate } from '../types/type'; +import { CreatorOptions } from '../types/type'; /** * 分段内容选择 diff --git a/src/core/core-options/CoreOptionsFilterForReact.ts b/src/core/core-options/CoreOptionsFilterForReact.ts index 0dbfef0..3410a78 100644 --- a/src/core/core-options/CoreOptionsFilterForReact.ts +++ b/src/core/core-options/CoreOptionsFilterForReact.ts @@ -1,8 +1,8 @@ -import { CoreOptionsFilterForVue2 } from "./CoreOptionsFilterForVue2"; -import { ICoreTemplate } from "../core-template/CoreTemplateVue2Config"; -import coreTemplateReactConfig from "../core-template/CoreTemplateReactConfig"; -import { IParsedSourceData } from "../CoreParsedConfig"; -import path from "path"; +import { CoreOptionsFilterForVue2 } from './CoreOptionsFilterForVue2'; +import { ICoreTemplate } from '../core-template/CoreTemplateVue2Config'; +import coreTemplateReactConfig from '../core-template/CoreTemplateReactConfig'; +import { IParsedSourceData } from '../CoreParsedConfig'; +import path from 'path'; import { deleteAsync } from 'del'; import fs from 'fs'; @@ -13,9 +13,8 @@ import fs from 'fs'; * @class CoreOptionsFilter */ export class CoreOptionsFilterForReact extends CoreOptionsFilterForVue2 { - /** override 生成原始配置 */ - public generateSourceModulesData(options: any, finalOptions: any, downloadConfigSource: any = '') { + public generateSourceModulesData(options: any, finalOptions: any, _downloadConfigSource: any = '') { // REACT比较特殊,使用配置驱动 const configDataContent: any = [ { diff --git a/src/core/core-options/CoreOptionsFilterForVue2.ts b/src/core/core-options/CoreOptionsFilterForVue2.ts index bce3ab1..adfaf39 100644 --- a/src/core/core-options/CoreOptionsFilterForVue2.ts +++ b/src/core/core-options/CoreOptionsFilterForVue2.ts @@ -306,7 +306,7 @@ export class CoreOptionsFilterForVue2 implements IOptionsFilter { * * @memberOf CoreOptionsFilter */ - protected async excludeSouceDeleteFolder(keepedTypeList: Array, options: any, finalOptions: any) { + protected async excludeSouceDeleteFolder(keepedTypeList: Array, options: any, _finalOptions: any) { for (const iterator of keepedTypeList) { const element: IParsedSourceData = iterator; const elementPath = `${process.env.PWD}/${options.name}/src/pages`; @@ -446,7 +446,7 @@ export class CoreOptionsFilterForVue2 implements IOptionsFilter { * * @memberOf CoreOptionsFilter */ - protected saveRouterFilter(saveedList: any[], configData: string, options: any, finalOptions: any) { + protected saveRouterFilter(saveedList: any[], configData: string, options: any, _finalOptions: any) { let configDataContent = JSON.stringify(saveedList); configDataContent = this.formatJson(configDataContent); diff --git a/tsconfig.json b/tsconfig.json index 3ff0bb3..01c5e0a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "ES2022", "module": "ESNext", "sourceMap": false, "declaration": false, @@ -9,14 +9,12 @@ "esModuleInterop": true, "resolveJsonModule": true, "removeComments": false, - "importHelpers": true, + "importHelpers": false, "strict": true, "skipLibCheck": true, "lib": ["ES6", "DOM"] }, - "types": [ - "node" - ], + "types": ["node"], "include": ["src"], "exclude": ["node_modules"] } diff --git a/tsdown.config.ts b/tsdown.config.ts index ba12fcb..76be7c3 100644 --- a/tsdown.config.ts +++ b/tsdown.config.ts @@ -1,41 +1,17 @@ -import { defineConfig } from 'tsdown' -import copy from 'rollup-plugin-copy'; +import { defineConfig } from 'tsdown'; export default defineConfig({ - entry: 'src/index.ts', - platform: 'node', - outDir: 'bin', - exports: true, - shims: true, - dts: false, - plugins: [ - // 复制templates文件; 复制 .gitignore 文件;添加.npmignore文件:"!.gitignore\n.npmignore" - copy({ - targets: [ - 'templates/farm/vue-lite', - 'templates/farm/vue-next-lite', - 'templates/farm/react-lite', - 'templates/vite/vue-lite', - 'templates/vite/vue-next-lite', - 'templates/vite/react-lite', - 'templates/webpack/vue-lite', - 'templates/webpack/vue-next-lite', - 'templates/webpack/react-lite', - ] - .map((filePath) => [ - { - src: [`${filePath}/*`, `${filePath}/.gitignore`, `!${filePath}/node_modules`], - dest: `bin/${filePath}` - }, - { - src: `${filePath}/.gitignore`, - dest: `bin/${filePath}`, - rename: '.npmignore', - transform: () => Buffer.from('!.gitignore\n.npmignore', 'utf-8') - } - ]) - .flat(), - verbose: true - }) - ], -}) \ No newline at end of file + entry: 'src/index.ts', + platform: 'node', + outDir: 'bin', + exports: true, + shims: true, + dts: false, + copy: [ + { + from: 'templates', + to: 'bin', + verbose: true + } + ] +}); From 0963a9680ca16fb1d03a74d8ed350a4c8a7ad75e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Tue, 7 Apr 2026 16:17:42 +0800 Subject: [PATCH 06/16] =?UTF-8?q?fix(deploy):=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E6=89=A7=E8=A1=8C=E8=B7=AF=E5=BE=84=E4=B8=BA?= =?UTF-8?q?.mjs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/deploy.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index d8100e1..7db0f0f 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -95,8 +95,7 @@ const configFilesReg = async (configReg: RegExp, reg: RegExp, getNewConfigFile: await execAsync(`pnpm install && pnpm run build`, { cwd: templateDir }); console.log(`构建完成`); } catch (buildError) { - console.error(`构建失败: ${buildError}`); - continue; + throw new Error(`模板 ${template} 构建失败: ${buildError}`); } // 拷贝dist文件夹到根目录并且重命名 @@ -107,16 +106,14 @@ const configFilesReg = async (configReg: RegExp, reg: RegExp, getNewConfigFile: console.log(`准备拷贝 ${outputDir}: ${distFilePath} -> ${newDistFilePath}`); if (!fs.existsSync(distFilePath)) { - console.error(`${outputDir} 目录不存在: ${distFilePath}`); - continue; + throw new Error(`模板 ${template} 的 ${outputDir} 目录不存在: ${distFilePath}`); } try { await fse.copy(distFilePath, newDistFilePath); console.log(`dist 目录已拷贝到 ${newDistFilePath}`); } catch (copyError) { - console.error(`拷贝 dist 目录失败: ${copyError}`); - continue; + throw new Error(`模板 ${template} 拷贝 ${outputDir} 目录失败: ${copyError}`); } console.log(`========== 模板 ${template} 处理完成 ==========\n`); } @@ -126,7 +123,7 @@ const configFilesReg = async (configReg: RegExp, reg: RegExp, getNewConfigFile: const initTemplates = async (templates: TemplateConfig[]) => { for (const template of templates) { await execAsync( - `node ./bin/index.js init ${template.name} --description "${template.description}" --type ${template.type} --template lite --buildToolType ${template.buildToolType}` + `node ./bin/index.mjs init ${template.name} --description "${template.description}" --type ${template.type} --template lite --buildToolType ${template.buildToolType}` ); } }; From 55ef724ac6a0880356be72f00981327dac51f404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Tue, 7 Apr 2026 16:28:33 +0800 Subject: [PATCH 07/16] =?UTF-8?q?refactor(deploy):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=E8=84=9A=E6=9C=AC=E7=BB=93=E6=9E=84=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/deploy.ts | 317 +++++++++++++++++++++++++++------------------- 1 file changed, 188 insertions(+), 129 deletions(-) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 7db0f0f..231f030 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -1,135 +1,151 @@ -import { promisify } from 'util'; +/** + * 部署脚本:初始化所有模板项目 → 重写配置(添加 base/publicPath)→ 安装依赖 → 构建 → 拷贝产物到 dist/ + * + * 用法: pnpm run build && node --import tsx scripts/deploy.ts + */ import { exec } from 'child_process'; -import fs, { mkdirSync } from 'fs'; +import { promisify } from 'util'; +import fs from 'fs'; import path from 'path'; -import fse from 'fs-extra'; -/** 异步 exec 命令 */ const execAsync = promisify(exec); -/** 模板配置类型 */ -interface TemplateConfig { +// ==================== 类型定义 ==================== + +/** init 命令所需的模板参数 */ +interface TemplateInitConfig { + /** CLI 传入的模板目录名,如 template-vite-vue3 */ name: string; description: string; type: string; buildToolType: string; } -/** 获取目录下的文件夹列表 - * @param root 根目录 - * @param reg 匹配规则 - * @param fullPath 是否返回完整路径 - * @return 返回文件夹名或路径数组 - */ -const getMatchedDirs = (root: string, reg: RegExp, fullPath = false): string[] => { - const dirs = fs.readdirSync(root, { withFileTypes: true }) - .filter(dirent => dirent.isDirectory()) - .map(dirent => dirent.name) - .filter(dirName => reg.test(dirName)); - return fullPath ? dirs.map(dir => path.join(root, dir)) : dirs; +/** 按构建工具分组的处理配置 */ +interface BuildToolConfig { + /** 匹配模板目录名的正则,如 /^template-vite/ */ + templateDirPattern: RegExp; + /** 匹配构建配置文件名的正则,如 /^vite\.config\. */ + configFilePattern: RegExp; + /** 生成添加 base/publicPath 后的配置内容 */ + generateConfig: (content: string, template: string) => string; + /** 额外的配置文件重写(如 webpack-react 的 webpack.config.js) */ + rewriteExtraConfig?: (templateDir: string, template: string) => void; + /** 构建输出目录名,默认 dist */ + outputDir?: string; } -type GetNewConfigFileFn = (readConfigFile: string, template: string) => string; +// ==================== 工具函数 ==================== -/** 模板类型配置 */ -const TEMPLATE_CONFIGS: { vite: RegExp; farm: RegExp; webpack: RegExp } = { - vite: /^template-vite/, - farm: /^template-farm/, - webpack: /^template-webpack/, +/** 获取目录下匹配正则的子文件夹名列表 */ +const getMatchedDirs = (root: string, pattern: RegExp): string[] => { + return fs.readdirSync(root, { withFileTypes: true }) + .filter(d => d.isDirectory() && pattern.test(d.name)) + .map(d => d.name); }; -/** 工具函数:统一替换配置 */ -const applyReplacementRules = (content: string, rules: { mate: string; sub: string }[]): string => { +/** 在目录中查找第一个匹配正则的文件,返回完整路径 */ +const findConfigFile = (dir: string, pattern: RegExp): string | undefined => { + for (const entry of fs.readdirSync(dir, { withFileTypes: true })) { + if (entry.isFile() && pattern.test(entry.name)) { + return path.join(dir, entry.name); + } + } + return undefined; +}; + +/** 对内容按规则做文本替换(每条规则仅替换首次匹配) */ +const replaceContent = (content: string, rules: { match: string; replacement: string }[]): string => { let result = content; - for (const rule of rules) { - if (result.includes(rule.mate)) { - result = result.replace(rule.mate, rule.sub); + for (const { match, replacement } of rules) { + if (result.includes(match)) { + result = result.replace(match, replacement); } } return result; }; -/** 处理重写文件(添加path) vite.config.js/ts farm.config.js/ts - * @param configReg 匹配规则 - * @param reg 匹配规则 - * @param getNewConfigFile 获取新的config.* 文件 - */ -const configFilesReg = async (configReg: RegExp, reg: RegExp, getNewConfigFile: GetNewConfigFileFn) => { - const cwd = process.cwd(); - const templates = getMatchedDirs(cwd, configReg); - console.log(`找到 ${templates.length} 个模板: ${templates.join(', ')}`); - - for (const template of templates) { - console.log(`\n========== 开始处理模板: ${template} ==========`); - - // 特殊处理 template-webpack-react 使用 webpack 配置 - if (template === 'template-webpack-react') { - const webpackConfigPath = path.join(cwd, template, 'webpack.config.js'); - if (fs.existsSync(webpackConfigPath)) { - let webpackConfig = fs.readFileSync(webpackConfigPath, 'utf-8'); - // 添加 publicPath 配置 - webpackConfig = webpackConfig.replace( - /publicPath:\s*['"]\/['"]/, - `publicPath: '/${template}/'` - ); - fs.writeFileSync(webpackConfigPath, webpackConfig); - console.log(`webpack.config.js 已更新`); - } - } +// ==================== 核心流程 ==================== - // 匹配config.* 文件 - const templateDir = path.join(cwd, template); - const configFilePath = getMatchedDirs(templateDir, reg, true)?.[0]; - console.log(`配置文件路径: ${configFilePath || '未找到'}`); - - if (configFilePath) { - // 重写config.* 文件 - const readConfigFile = fs.readFileSync(configFilePath, 'utf-8'); - const newConfigFile = getNewConfigFile(readConfigFile, template); - fs.writeFileSync(configFilePath, newConfigFile); - console.log(`配置文件已更新`); - } +/** 步骤 1:使用 CLI 初始化单个模板项目 */ +const initTemplate = async (template: TemplateInitConfig, cliBinPath: string): Promise => { + console.log(`[init] ${template.name} ...`); + try { + await execAsync( + `node ${cliBinPath} init ${template.name} --description "${template.description}" --type ${template.type} --template lite --buildToolType ${template.buildToolType}`, + ); + } catch (err) { + throw new Error(`模板 ${template.name} 初始化失败: ${err}`); + } +}; - console.log(`开始安装依赖并构建...`); - try { - await execAsync(`pnpm install && pnpm run build`, { cwd: templateDir }); - console.log(`构建完成`); - } catch (buildError) { - throw new Error(`模板 ${template} 构建失败: ${buildError}`); - } +/** 步骤 2:重写配置文件(注入 base / publicPath) */ +const injectBasePath = (configFilePath: string, template: string, generateConfig: (content: string, template: string) => string) => { + const content = fs.readFileSync(configFilePath, 'utf-8'); + fs.writeFileSync(configFilePath, generateConfig(content, template)); + console.log(` 配置已注入 base path: ${path.basename(configFilePath)}`); +}; - // 拷贝dist文件夹到根目录并且重命名 - // webpack-react 使用 build 目录 - const outputDir = template === 'template-webpack-react' ? 'build' : 'dist'; - const distFilePath = path.join(cwd, template, outputDir); - const newDistFilePath = path.join(cwd, 'dist', template); - console.log(`准备拷贝 ${outputDir}: ${distFilePath} -> ${newDistFilePath}`); +/** 步骤 3:安装依赖 */ +const installDeps = async (templateDir: string, template: string): Promise => { + console.log(` [install] ...`); + try { + await execAsync('pnpm install', { cwd: templateDir }); + } catch (err) { + throw new Error(`模板 ${template} 依赖安装失败: ${err}`); + } +}; - if (!fs.existsSync(distFilePath)) { - throw new Error(`模板 ${template} 的 ${outputDir} 目录不存在: ${distFilePath}`); - } +/** 步骤 4:构建 */ +const buildTemplate = async (templateDir: string, template: string): Promise => { + console.log(` [build] ...`); + try { + await execAsync('pnpm run build', { cwd: templateDir }); + console.log(` 构建完成`); + } catch (err) { + throw new Error(`模板 ${template} 构建失败: ${err}`); + } +}; - try { - await fse.copy(distFilePath, newDistFilePath); - console.log(`dist 目录已拷贝到 ${newDistFilePath}`); - } catch (copyError) { - throw new Error(`模板 ${template} 拷贝 ${outputDir} 目录失败: ${copyError}`); - } - console.log(`========== 模板 ${template} 处理完成 ==========\n`); +/** 步骤 5:拷贝构建产物到 dist/ */ +const copyOutput = (cwd: string, template: string, outputDir: string): void => { + const srcPath = path.join(cwd, template, outputDir); + const destPath = path.join(cwd, 'dist', template); + + if (!fs.existsSync(srcPath)) { + throw new Error(`模板 ${template} 的构建产物不存在: ${srcPath}`); } - console.log(`configFilesReg 完成`); -} -const initTemplates = async (templates: TemplateConfig[]) => { - for (const template of templates) { - await execAsync( - `node ./bin/index.mjs init ${template.name} --description "${template.description}" --type ${template.type} --template lite --buildToolType ${template.buildToolType}` - ); + fs.cpSync(srcPath, destPath, { recursive: true }); + console.log(` 产物已拷贝: ${outputDir}/ -> dist/${template}/`); +}; + +/** 处理单个模板的完整流程 */ +const processTemplate = async (template: string, cwd: string, config: BuildToolConfig): Promise => { + const templateDir = path.join(cwd, template); + const outputDir = config.outputDir ?? 'dist'; + console.log(`\n========== ${template} ==========`); + + // 额外配置重写(如 webpack-react 的 webpack.config.js) + config.rewriteExtraConfig?.(templateDir, template); + + // 注入 base path 到构建配置 + const configFilePath = findConfigFile(templateDir, config.configFilePattern); + if (configFilePath) { + injectBasePath(configFilePath, template, config.generateConfig); + } else { + console.log(` 未找到构建配置文件,跳过 base path 注入`); } + + await installDeps(templateDir, template); + await buildTemplate(templateDir, template); + copyOutput(cwd, template, outputDir); }; -/** 预定义模板列表 */ -const TEMPLATES: TemplateConfig[] = [ +// ==================== 配置数据 ==================== + +/** 需要初始化的模板列表 */ +const TEMPLATES: TemplateInitConfig[] = [ { name: 'template-vite-vue3', description: '这是一个vite构建的vue3项目', type: 'vue3', buildToolType: 'vite' }, { name: 'template-vite-vue2', description: '这是一个vite构建的vue2项目', type: 'vue2', buildToolType: 'vite' }, { name: 'template-vite-react', description: '这是一个vite构建的react项目', type: 'react', buildToolType: 'vite' }, @@ -141,35 +157,78 @@ const TEMPLATES: TemplateConfig[] = [ { name: 'template-webpack-react', description: '这是一个webpack构建的react项目', type: 'react', buildToolType: 'webpack' }, ]; -const preview = async () => { - try { - // 创建 dist 目录 - mkdirSync('dist', { recursive: true }); - - await initTemplates(TEMPLATES); - - // vite 模版重写 - 使用统一替换函数 - const generateViteConfig = (readConfigFile: string, template: string) => applyReplacementRules(readConfigFile, [ - { mate: 'defineConfig({', sub: `defineConfig({\n base: '/${template}',` }, - { mate: 'export default {', sub: `export default {\n base: '/${template}',` }, - ]); - - // farm 模版重写 - const generateFarmConfig = (readConfigFile: string, template: string) => applyReplacementRules(readConfigFile, [ - { mate: 'defineConfig({', sub: `defineConfig({ \n compilation: {\n output: {\n publicPath: '/${template}/',\n },\n },\n` }, - ]); - - // webpack 模版重写 - const generateWebpackConfig = (readConfigFile: string, template: string) => applyReplacementRules(readConfigFile, [ - { mate: 'module.exports = {', sub: `module.exports = {\n publicPath: '/${template}',\n` }, - ]); - - await configFilesReg(TEMPLATE_CONFIGS.vite, /^vite.config.*/, generateViteConfig); - await configFilesReg(TEMPLATE_CONFIGS.farm, /^farm.config.*/, generateFarmConfig); - await configFilesReg(TEMPLATE_CONFIGS.webpack, /^vue.config.*/, generateWebpackConfig); - } catch (e) { - console.error(e); +/** 各构建工具对应的配置重写规则 */ +const BUILD_TOOL_CONFIGS: BuildToolConfig[] = [ + { + templateDirPattern: /^template-vite/, + configFilePattern: /^vite\.config/, + generateConfig: (content, template) => replaceContent(content, [ + { match: 'defineConfig({', replacement: `defineConfig({\n base: '/${template}',` }, + { match: 'export default {', replacement: `export default {\n base: '/${template}',` }, + ]), + }, + { + templateDirPattern: /^template-farm/, + configFilePattern: /^farm\.config/, + generateConfig: (content, template) => replaceContent(content, [ + { match: 'defineConfig({', replacement: `defineConfig({\n compilation: {\n output: {\n publicPath: '/${template}/',\n },\n },\n` }, + ]), + }, + { + templateDirPattern: /^template-webpack/, + configFilePattern: /^vue\.config/, + generateConfig: (content, template) => replaceContent(content, [ + { match: 'module.exports = {', replacement: `module.exports = {\n publicPath: '/${template}',\n` }, + ]), + // webpack-react 额外需要处理 webpack.config.js + rewriteExtraConfig: (templateDir, template) => { + const webpackConfigPath = path.join(templateDir, 'webpack.config.js'); + if (!fs.existsSync(webpackConfigPath)) return; + const content = fs.readFileSync(webpackConfigPath, 'utf-8'); + fs.writeFileSync(webpackConfigPath, content.replace( + /publicPath:\s*['"]\/['"]/, + `publicPath: '/${template}/'`, + )); + console.log(` 额外配置已更新: webpack.config.js`); + }, + outputDir: 'build', + }, +]; + +// ==================== 入口 ==================== + +const main = async () => { + const cliBinPath = path.resolve('bin/index.mjs'); + const cwd = process.cwd(); + + // 0. 确保构建产物已存在 + if (!fs.existsSync(cliBinPath)) { + throw new Error(`CLI 未构建,请先运行: pnpm run build`); + } + + // 1. 创建产物根目录 + fs.mkdirSync(path.join(cwd, 'dist'), { recursive: true }); + + // 2. 并行初始化所有模板项目 + console.log(`开始初始化 ${TEMPLATES.length} 个模板...`); + await Promise.all(TEMPLATES.map(t => initTemplate(t, cliBinPath))); + console.log(`所有模板初始化完成`); + + // 3. 按构建工具分组,串行处理(每组内串行构建) + for (const config of BUILD_TOOL_CONFIGS) { + const templates = getMatchedDirs(cwd, config.templateDirPattern); + if (templates.length === 0) continue; + + console.log(`\n----- 处理 ${config.templateDirPattern} 模板 (${templates.length} 个) -----`); + for (const template of templates) { + await processTemplate(template, cwd, config); + } } + + console.log('\n✅ 全部部署完成'); }; -preview().catch(e => console.error(e)); \ No newline at end of file +main().catch((err) => { + console.error(err); + process.exitCode = 1; +}); From b7f877b5e1049ee5b38dee44980b81891bc4fb7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Tue, 7 Apr 2026 16:32:56 +0800 Subject: [PATCH 08/16] =?UTF-8?q?feat(deploy):=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E5=8A=A8=E6=80=81=E8=BE=93=E5=87=BA=E7=9B=AE?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/deploy.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 231f030..f8b3c60 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -31,8 +31,8 @@ interface BuildToolConfig { generateConfig: (content: string, template: string) => string; /** 额外的配置文件重写(如 webpack-react 的 webpack.config.js) */ rewriteExtraConfig?: (templateDir: string, template: string) => void; - /** 构建输出目录名,默认 dist */ - outputDir?: string; + /** 构建输出目录名,默认 dist;可传函数按模板名动态判断 */ + outputDir?: string | ((template: string) => string); } // ==================== 工具函数 ==================== @@ -123,7 +123,7 @@ const copyOutput = (cwd: string, template: string, outputDir: string): void => { /** 处理单个模板的完整流程 */ const processTemplate = async (template: string, cwd: string, config: BuildToolConfig): Promise => { const templateDir = path.join(cwd, template); - const outputDir = config.outputDir ?? 'dist'; + const outputDir = typeof config.outputDir === 'function' ? config.outputDir(template) : config.outputDir ?? 'dist'; console.log(`\n========== ${template} ==========`); // 额外配置重写(如 webpack-react 的 webpack.config.js) @@ -146,15 +146,15 @@ const processTemplate = async (template: string, cwd: string, config: BuildToolC /** 需要初始化的模板列表 */ const TEMPLATES: TemplateInitConfig[] = [ - { name: 'template-vite-vue3', description: '这是一个vite构建的vue3项目', type: 'vue3', buildToolType: 'vite' }, - { name: 'template-vite-vue2', description: '这是一个vite构建的vue2项目', type: 'vue2', buildToolType: 'vite' }, - { name: 'template-vite-react', description: '这是一个vite构建的react项目', type: 'react', buildToolType: 'vite' }, - { name: 'template-farm-vue3', description: '这是一个farm构建的vue3项目', type: 'vue3', buildToolType: 'farm' }, - { name: 'template-farm-vue2', description: '这是一个farm构建的vue2项目', type: 'vue2', buildToolType: 'farm' }, - { name: 'template-farm-react', description: '这是一个farm构建的react项目', type: 'react', buildToolType: 'farm' }, - { name: 'template-webpack-vue3', description: '这是一个webpack构建的vue3项目', type: 'vue3', buildToolType: 'webpack' }, - { name: 'template-webpack-vue2', description: '这是一个webpack构建的vue2项目', type: 'vue2', buildToolType: 'webpack' }, - { name: 'template-webpack-react', description: '这是一个webpack构建的react项目', type: 'react', buildToolType: 'webpack' }, + { name: 'template-vite-vue3', description: '这是一个 Vite 构建的 Vue3 项目', type: 'vue3', buildToolType: 'vite' }, + { name: 'template-vite-vue2', description: '这是一个 Vite 构建的 Vue2 项目', type: 'vue2', buildToolType: 'vite' }, + { name: 'template-vite-react', description: '这是一个 Vite 构建的 React 项目', type: 'react', buildToolType: 'vite' }, + { name: 'template-farm-vue3', description: '这是一个 Farm 构建的 Vue3 项目', type: 'vue3', buildToolType: 'farm' }, + { name: 'template-farm-vue2', description: '这是一个 Farm 构建的 Vue2 项目', type: 'vue2', buildToolType: 'farm' }, + { name: 'template-farm-react', description: '这是一个 Farm 构建的 React 项目', type: 'react', buildToolType: 'farm' }, + { name: 'template-webpack-vue3', description: '这是一个 Webpack 构建的 Vue3 项目', type: 'vue3', buildToolType: 'webpack' }, + { name: 'template-webpack-vue2', description: '这是一个 Webpack 构建的 Vue2 项目', type: 'vue2', buildToolType: 'webpack' }, + { name: 'template-webpack-react', description: '这是一个 Webpack 构建的 React 项目', type: 'react', buildToolType: 'webpack' }, ]; /** 各构建工具对应的配置重写规则 */ @@ -191,7 +191,7 @@ const BUILD_TOOL_CONFIGS: BuildToolConfig[] = [ )); console.log(` 额外配置已更新: webpack.config.js`); }, - outputDir: 'build', + outputDir: (template) => template.includes('react') ? 'build' : 'dist', }, ]; From 6c8e856c7d0da359efd7476e476347b0c326ed83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Tue, 7 Apr 2026 16:39:56 +0800 Subject: [PATCH 09/16] =?UTF-8?q?ci:=20=E4=BF=AE=E5=A4=8D=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E9=83=A8=E7=BD=B2=E5=9F=9F=E5=90=8D=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/preview.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml index a6dad24..f36fdfa 100644 --- a/.github/workflows/preview.yml +++ b/.github/workflows/preview.yml @@ -20,5 +20,5 @@ jobs: - run: bun scripts/deploy.ts - run: sleep 3s - run: | - npx surge --project ./dist --domain tdesign-starter-cli.surge.sh --token ${{ secrets.TDESIGN_SURGE_TOKEN }} + npx surge --project ./dist --domain https://tdesign-starter-cli.surge.sh --token ${{ secrets.TDESIGN_SURGE_TOKEN }} ls dist From 839f81bc5579a18b98603deb7cb5ee50ff958788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Wed, 8 Apr 2026 16:43:43 +0800 Subject: [PATCH 10/16] =?UTF-8?q?ci(deploy):=20=E5=B0=86=20dist=20?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E9=87=8D=E5=91=BD=E5=90=8D=E4=B8=BA=20=5Fsit?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 +-- scripts/deploy.ts | 54 +++++++++++++++++++++++++++-------------------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/package.json b/package.json index 95acd44..f6ea12f 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,11 @@ "watch": "tsdown -w", "dev": "node ./bin/index.mjs", "build": "tsdown", - "lint": "eslint 'src/**/*.{js,ts}'", + "lint": "eslint src --ext .js,.ts", "site": "echo 'no need to build site'", "site:preview": "npm run build && tsx scripts/deploy.ts", "test": "echo \"No tests yet\" && exit 0", "prepare": "husky" - }, "repository": { "type": "git", diff --git a/scripts/deploy.ts b/scripts/deploy.ts index f8b3c60..307d52c 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -39,9 +39,10 @@ interface BuildToolConfig { /** 获取目录下匹配正则的子文件夹名列表 */ const getMatchedDirs = (root: string, pattern: RegExp): string[] => { - return fs.readdirSync(root, { withFileTypes: true }) - .filter(d => d.isDirectory() && pattern.test(d.name)) - .map(d => d.name); + return fs + .readdirSync(root, { withFileTypes: true }) + .filter((d) => d.isDirectory() && pattern.test(d.name)) + .map((d) => d.name); }; /** 在目录中查找第一个匹配正则的文件,返回完整路径 */ @@ -72,7 +73,7 @@ const initTemplate = async (template: TemplateInitConfig, cliBinPath: string): P console.log(`[init] ${template.name} ...`); try { await execAsync( - `node ${cliBinPath} init ${template.name} --description "${template.description}" --type ${template.type} --template lite --buildToolType ${template.buildToolType}`, + `node ${cliBinPath} init ${template.name} --description "${template.description}" --type ${template.type} --template lite --buildToolType ${template.buildToolType}` ); } catch (err) { throw new Error(`模板 ${template.name} 初始化失败: ${err}`); @@ -123,7 +124,7 @@ const copyOutput = (cwd: string, template: string, outputDir: string): void => { /** 处理单个模板的完整流程 */ const processTemplate = async (template: string, cwd: string, config: BuildToolConfig): Promise => { const templateDir = path.join(cwd, template); - const outputDir = typeof config.outputDir === 'function' ? config.outputDir(template) : config.outputDir ?? 'dist'; + const outputDir = typeof config.outputDir === 'function' ? config.outputDir(template) : (config.outputDir ?? 'dist'); console.log(`\n========== ${template} ==========`); // 额外配置重写(如 webpack-react 的 webpack.config.js) @@ -154,7 +155,7 @@ const TEMPLATES: TemplateInitConfig[] = [ { name: 'template-farm-react', description: '这是一个 Farm 构建的 React 项目', type: 'react', buildToolType: 'farm' }, { name: 'template-webpack-vue3', description: '这是一个 Webpack 构建的 Vue3 项目', type: 'vue3', buildToolType: 'webpack' }, { name: 'template-webpack-vue2', description: '这是一个 Webpack 构建的 Vue2 项目', type: 'vue2', buildToolType: 'webpack' }, - { name: 'template-webpack-react', description: '这是一个 Webpack 构建的 React 项目', type: 'react', buildToolType: 'webpack' }, + { name: 'template-webpack-react', description: '这是一个 Webpack 构建的 React 项目', type: 'react', buildToolType: 'webpack' } ]; /** 各构建工具对应的配置重写规则 */ @@ -162,37 +163,35 @@ const BUILD_TOOL_CONFIGS: BuildToolConfig[] = [ { templateDirPattern: /^template-vite/, configFilePattern: /^vite\.config/, - generateConfig: (content, template) => replaceContent(content, [ - { match: 'defineConfig({', replacement: `defineConfig({\n base: '/${template}',` }, - { match: 'export default {', replacement: `export default {\n base: '/${template}',` }, - ]), + generateConfig: (content, template) => + replaceContent(content, [ + { match: 'defineConfig({', replacement: `defineConfig({\n base: '/${template}',` }, + { match: 'export default {', replacement: `export default {\n base: '/${template}',` } + ]) }, { templateDirPattern: /^template-farm/, configFilePattern: /^farm\.config/, - generateConfig: (content, template) => replaceContent(content, [ - { match: 'defineConfig({', replacement: `defineConfig({\n compilation: {\n output: {\n publicPath: '/${template}/',\n },\n },\n` }, - ]), + generateConfig: (content, template) => + replaceContent(content, [ + { match: 'defineConfig({', replacement: `defineConfig({\n compilation: {\n output: {\n publicPath: '/${template}/',\n },\n },\n` } + ]) }, { templateDirPattern: /^template-webpack/, configFilePattern: /^vue\.config/, - generateConfig: (content, template) => replaceContent(content, [ - { match: 'module.exports = {', replacement: `module.exports = {\n publicPath: '/${template}',\n` }, - ]), + generateConfig: (content, template) => + replaceContent(content, [{ match: 'module.exports = {', replacement: `module.exports = {\n publicPath: '/${template}',\n` }]), // webpack-react 额外需要处理 webpack.config.js rewriteExtraConfig: (templateDir, template) => { const webpackConfigPath = path.join(templateDir, 'webpack.config.js'); if (!fs.existsSync(webpackConfigPath)) return; const content = fs.readFileSync(webpackConfigPath, 'utf-8'); - fs.writeFileSync(webpackConfigPath, content.replace( - /publicPath:\s*['"]\/['"]/, - `publicPath: '/${template}/'`, - )); + fs.writeFileSync(webpackConfigPath, content.replace(/publicPath:\s*['"]\/['"]/, `publicPath: '/${template}/'`)); console.log(` 额外配置已更新: webpack.config.js`); }, - outputDir: (template) => template.includes('react') ? 'build' : 'dist', - }, + outputDir: (template) => (template.includes('react') ? 'build' : 'dist') + } ]; // ==================== 入口 ==================== @@ -211,7 +210,7 @@ const main = async () => { // 2. 并行初始化所有模板项目 console.log(`开始初始化 ${TEMPLATES.length} 个模板...`); - await Promise.all(TEMPLATES.map(t => initTemplate(t, cliBinPath))); + await Promise.all(TEMPLATES.map((t) => initTemplate(t, cliBinPath))); console.log(`所有模板初始化完成`); // 3. 按构建工具分组,串行处理(每组内串行构建) @@ -225,6 +224,15 @@ const main = async () => { } } + // 重命名 dist → _site,适配上游 CI 工作流(TDesignOteam/workflows)对 _site 目录的约定 + const distDir = path.join(cwd, 'dist'); + const siteDir = path.join(cwd, '_site'); + if (fs.existsSync(siteDir)) { + fs.rmSync(siteDir, { recursive: true }); + } + fs.renameSync(distDir, siteDir); + console.log(`\n产物目录已重命名: dist/ → _site/`); + console.log('\n✅ 全部部署完成'); }; From c200899c27cf920a493e4f1104bf5a5a484b0ff3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Wed, 8 Apr 2026 16:59:38 +0800 Subject: [PATCH 11/16] =?UTF-8?q?feat(deploy):=20=E7=94=9F=E6=88=90?= =?UTF-8?q?=E6=A0=B9=20index.html=20=E5=AF=BC=E8=88=AA=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/deploy.ts | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 307d52c..d087680 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -224,6 +224,44 @@ const main = async () => { } } + // 4. 生成根 index.html 导航页 + const templateDirs = fs + .readdirSync(path.join(cwd, 'dist'), { withFileTypes: true }) + .filter((d) => d.isDirectory()) + .map((d) => d.name) + .sort(); + + const indexHtml = ` + + + + + TDesign Starter Templates + + + +
+

TDesign Starter Templates

+

Select a template to preview

+
+${templateDirs.map((name) => ` ${name}`).join('\n')} +
+
+ +`; + + fs.writeFileSync(path.join(cwd, 'dist', 'index.html'), indexHtml); + console.log(`\n导航页已生成: dist/index.html (${templateDirs.length} 个模板)`); + // 重命名 dist → _site,适配上游 CI 工作流(TDesignOteam/workflows)对 _site 目录的约定 const distDir = path.join(cwd, 'dist'); const siteDir = path.join(cwd, '_site'); From 4687c1132bc523df16fb9866e770adec299ce13c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Wed, 8 Apr 2026 17:23:48 +0800 Subject: [PATCH 12/16] =?UTF-8?q?feat(deploy):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=AF=BC=E8=88=AA=E9=A1=B5=E6=A0=B7=E5=BC=8F=E5=B9=B6=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=8C=89=E6=9E=84=E5=BB=BA=E5=B7=A5=E5=85=B7=E5=88=86?= =?UTF-8?q?=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/deploy.ts | 115 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 102 insertions(+), 13 deletions(-) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index d087680..b42cc52 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -224,13 +224,49 @@ const main = async () => { } } - // 4. 生成根 index.html 导航页 + // 4. 生成根 index.html 导航页(按构建工具分组) const templateDirs = fs .readdirSync(path.join(cwd, 'dist'), { withFileTypes: true }) .filter((d) => d.isDirectory()) .map((d) => d.name) .sort(); + // 按构建工具分组 + const groups: Record = {}; + for (const name of templateDirs) { + const match = name.match(/^template-(\w+)-/); + const tool = match ? match[1] : 'other'; + (groups[tool] ??= []).push(name); + } + + const toolDisplayName: Record = { vite: 'Vite', webpack: 'Webpack', farm: 'Farm' }; + const frameworkIcon: Record = { vue3: 'Vue 3', vue2: 'Vue 2', react: 'React' }; + + const getFramework = (name: string): string => { + if (name.includes('vue3')) return 'vue3'; + if (name.includes('vue2')) return 'vue2'; + if (name.includes('react')) return 'react'; + return ''; + }; + + const groupsHtml = Object.entries(groups) + .map( + ([tool, names]) => ` +
+

${toolDisplayName[tool] || tool}

+
+${names + .map((name) => { + const fw = getFramework(name); + const label = frameworkIcon[fw] || name; + return ` ${label}`; + }) + .join('\n')} +
+
` + ) + .join('\n'); + const indexHtml = ` @@ -239,28 +275,81 @@ const main = async () => { TDesign Starter Templates
-

TDesign Starter Templates

-

Select a template to preview

-
-${templateDirs.map((name) => ` ${name}`).join('\n')} +
+ +
Select a template to preview
+${groupsHtml} +
`; fs.writeFileSync(path.join(cwd, 'dist', 'index.html'), indexHtml); - console.log(`\n导航页已生成: dist/index.html (${templateDirs.length} 个模板)`); + console.log(`\n导航页已生成: dist/index.html (${templateDirs.length} 个模板, ${Object.keys(groups).length} 个分组)`); // 重命名 dist → _site,适配上游 CI 工作流(TDesignOteam/workflows)对 _site 目录的约定 const distDir = path.join(cwd, 'dist'); From 0b09b1946d8fef973fb652df42227ff9482c8ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Wed, 8 Apr 2026 18:08:57 +0800 Subject: [PATCH 13/16] =?UTF-8?q?refactor(deploy):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E5=AF=BC=E8=88=AA=E9=A1=B5=E7=94=9F=E6=88=90=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E6=A8=A1=E6=9D=BF=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/deploy.ts | 108 ++++--------------------- scripts/index-template.html | 157 ++++++++++++++++++++++++++++++++++++ 2 files changed, 174 insertions(+), 91 deletions(-) create mode 100644 scripts/index-template.html diff --git a/scripts/deploy.ts b/scripts/deploy.ts index b42cc52..6a1b5cd 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -5,9 +5,12 @@ */ import { exec } from 'child_process'; import { promisify } from 'util'; +import { fileURLToPath } from 'url'; import fs from 'fs'; import path from 'path'; +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const execAsync = promisify(exec); // ==================== 类型定义 ==================== @@ -224,14 +227,13 @@ const main = async () => { } } - // 4. 生成根 index.html 导航页(按构建工具分组) + // 4. 生成根 index.html 导航页(读取模板 + 注入侧边栏按钮) const templateDirs = fs .readdirSync(path.join(cwd, 'dist'), { withFileTypes: true }) .filter((d) => d.isDirectory()) .map((d) => d.name) .sort(); - // 按构建工具分组 const groups: Record = {}; for (const name of templateDirs) { const match = name.match(/^template-(\w+)-/); @@ -240,7 +242,8 @@ const main = async () => { } const toolDisplayName: Record = { vite: 'Vite', webpack: 'Webpack', farm: 'Farm' }; - const frameworkIcon: Record = { vue3: 'Vue 3', vue2: 'Vue 2', react: 'React' }; + const frameworkLabel: Record = { vue3: 'Vue 3', vue2: 'Vue 2', react: 'React' }; + const fwIconLetter: Record = { vue3: 'V', vue2: 'V', react: 'R' }; const getFramework = (name: string): string => { if (name.includes('vue3')) return 'vue3'; @@ -249,104 +252,27 @@ const main = async () => { return ''; }; - const groupsHtml = Object.entries(groups) + const sidebarHtml = Object.entries(groups) .map( ([tool, names]) => ` -
-

${toolDisplayName[tool] || tool}

-
+
+
${toolDisplayName[tool] || tool}
+
${names .map((name) => { const fw = getFramework(name); - const label = frameworkIcon[fw] || name; - return ` ${label}`; + const label = frameworkLabel[fw] || name; + const letter = fwIconLetter[fw] || '?'; + return ` `; }) .join('\n')} -
-
` +
+
` ) .join('\n'); - const indexHtml = ` - - - - - TDesign Starter Templates - - - -
-
- -
Select a template to preview
-
-${groupsHtml} - -
- -`; + const templateHtml = fs.readFileSync(path.join(__dirname, 'index-template.html'), 'utf-8'); + const indexHtml = templateHtml.replace('{{SIDEBAR_CONTENT}}', sidebarHtml); fs.writeFileSync(path.join(cwd, 'dist', 'index.html'), indexHtml); console.log(`\n导航页已生成: dist/index.html (${templateDirs.length} 个模板, ${Object.keys(groups).length} 个分组)`); diff --git a/scripts/index-template.html b/scripts/index-template.html new file mode 100644 index 0000000..845ca00 --- /dev/null +++ b/scripts/index-template.html @@ -0,0 +1,157 @@ + + + + + + TDesign Starter Templates + + + + +
+
+ Select a template + +
+
← Choose a template from the left to preview
+ +
+ + + From 368e9ce1ca650a89b05b77ae9e6e9359aaad0971 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=98=BF=E8=8F=9C=20Cai?= Date: Wed, 8 Apr 2026 18:21:25 +0800 Subject: [PATCH 14/16] =?UTF-8?q?feat(preview):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E7=8A=B6=E6=80=81=E5=92=8C=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/index-template.html | 79 ++++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/scripts/index-template.html b/scripts/index-template.html index 845ca00..a1b2f81 100644 --- a/scripts/index-template.html +++ b/scripts/index-template.html @@ -115,6 +115,49 @@ .open-link:hover { text-decoration: underline; } .preview-frame { flex: 1; width: 100%; border: none; } .empty-state { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--td-text-color-placeholder); font-size: 14px; } + .loading-overlay { + position: absolute; + inset: 0; + display: none; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 12px; + background: #fff; + z-index: 10; + } + .loading-overlay.visible { display: flex; } + .spinner { + width: 28px; height: 28px; + border: 3px solid var(--td-brand-color-1); + border-top-color: var(--td-brand-color-7); + border-radius: 50%; + animation: spin .7s linear infinite; + } + .loading-text { font-size: 13px; color: var(--td-text-color-placeholder); } + .loading-error { + display: none; + align-items: center; + justify-content: center; + flex-direction: column; + gap: 12px; + } + .loading-error.visible { display: flex; } + .loading-error .error-icon { font-size: 36px; line-height: 1; } + .loading-error .error-text { font-size: 13px; color: var(--td-text-color-secondary); } + .retry-btn { + padding: 6px 16px; + font-size: 13px; + font-weight: 500; + color: var(--td-brand-color-7); + background: var(--td-brand-color-1); + border: 1px solid var(--td-brand-color-6); + border-radius: 4px; + cursor: pointer; + transition: all .15s ease; + } + .retry-btn:hover { background: var(--td-brand-color-7); color: #fff; } + @keyframes spin { to { transform: rotate(360deg); } } @@ -130,7 +173,18 @@
← Choose a template from the left to preview
- +
+ +
+
+
Loading template...
+
+
+
Failed to load template
+ +
+
+