@qse/edu-scripts 2.0.1 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # 更新日志
2
2
 
3
+ ## 2.0.3 (2926-03-10)
4
+
5
+ - feat: 增加 cache 配置项
6
+ - feat: 增加 override 迁移提示
7
+ - fix: 修复 dll 在 pnpm 环境下失效
8
+
9
+ ## 2.0.2 (2926-03-10)
10
+
11
+ - fix: 修复 react 兼容问题
12
+ - fix: 修复 mock 失效
13
+
3
14
  ## 2.0.1 (2026-03-09)
4
15
 
5
16
  - fix: 修复 pnpm 启动失败
package/README.md CHANGED
@@ -1,12 +1,10 @@
1
1
  # 教育工程化基础框架
2
2
 
3
- svn://192.168.10.168/edu/code/A0.New-system/0A2.front-end-component/edu-scripts/trunk
4
-
5
- <Alert>内网里的 @qsb/edu-scripts 已经弃用,请更换成公网的 @qse/edu-scripts </Alert>
3
+ svn://192.168.10.168/qsedu/code/00.common/02.front-end-component/06.edu-scripts/trunk
6
4
 
7
5
  ## 运行条件
8
6
 
9
- nodejs 版本至少 12+
7
+ nodejs 版本至少 18+
10
8
 
11
9
  ## 快速体验
12
10
 
@@ -95,7 +93,7 @@ npx edu-scripts g override
95
93
  根目录生成 `theme.js` 或 `theme.json`
96
94
 
97
95
  ```js
98
- module.exports = {
96
+ export default {
99
97
  '@primary-color': '#99f',
100
98
  }
101
99
  ```
package/dist/cli.mjs CHANGED
@@ -49,13 +49,16 @@ function resolveOwn(...filePath) {
49
49
  }
50
50
  return path.resolve(current, ...filePath);
51
51
  }
52
+ function resolveExistPaths(...paths) {
53
+ return paths.filter((p) => fs.existsSync(p));
54
+ }
52
55
  function getExistPath(...paths) {
53
- for (const path of paths) if (fs.existsSync(path)) return path;
54
- return paths[0];
56
+ return resolveExistPaths(...paths)[0] || paths[0];
55
57
  }
56
58
  const paths = {
57
59
  resolveApp,
58
60
  resolveOwn,
61
+ resolveExistPaths,
59
62
  eduAppEnv: resolveApp("src", "edu-app-env.d.ts"),
60
63
  dist: resolveApp("dist"),
61
64
  sshSftp: resolveApp(".sftprc.json"),
@@ -129,7 +132,7 @@ if (!(appConfig.single || appConfig.mainProject)) {
129
132
  console.log(chalk.bgYellow("教育集成工程不能含有 public/static, 现已自动删除"));
130
133
  try {
131
134
  fs.rmSync(paths.static, { recursive: true });
132
- } catch (e) {}
135
+ } catch {}
133
136
  }
134
137
  }
135
138
  if (!fs.existsSync(paths.public) && !process.argv.includes("auto-refactor")) {
@@ -180,6 +183,14 @@ const PostcssSafeAreaPlugin = () => {
180
183
  //#region src/config/webpackConfig.ts
181
184
  const require$2 = createRequire(import.meta.url);
182
185
  const appPkg$1 = fs.readJsonSync(paths.package);
186
+ const hasJsxRuntime = (() => {
187
+ try {
188
+ require$2.resolve("react/jsx-runtime");
189
+ return appConfig.single;
190
+ } catch {
191
+ return false;
192
+ }
193
+ })();
183
194
  const jsMainPath = appConfig.grayscale ? `${appPkg$1.name}/beta/${appPkg$1.name}` : `${appPkg$1.name}/${appPkg$1.name}`;
184
195
  const assetPath = appConfig.grayscale ? `${appPkg$1.name}/beta/${appPkg$1.version}` : `${appPkg$1.name}/${appPkg$1.version}`;
185
196
  const cssRegex = /\.css$/;
@@ -202,6 +213,23 @@ const qseCDN = (() => {
202
213
  isUseQsbSchemeRender: include("qsb-scheme-render.min.js")
203
214
  };
204
215
  })();
216
+ function getPnpmCompatibleDllManifest(manifestPath) {
217
+ const manifest = fs.readJsonSync(manifestPath);
218
+ const normalizedContent = { ...manifest.content };
219
+ for (const [key, value] of Object.entries(manifest.content)) {
220
+ if (!key.startsWith("./node_modules/")) continue;
221
+ const request = key.replace("./node_modules/", "");
222
+ try {
223
+ const resolvedPath = require$2.resolve(request);
224
+ const relativePath = `./${path$1.relative(process.cwd(), resolvedPath).replace(/\\/g, "/")}`;
225
+ if (!normalizedContent[relativePath]) normalizedContent[relativePath] = value;
226
+ } catch {}
227
+ }
228
+ return {
229
+ ...manifest,
230
+ content: normalizedContent
231
+ };
232
+ }
205
233
  function getWebpackConfig(args, override) {
206
234
  const isDev = process.env.NODE_ENV === "development";
207
235
  const isProd = process.env.NODE_ENV === "production";
@@ -251,7 +279,7 @@ function getWebpackConfig(args, override) {
251
279
  });
252
280
  return loaders;
253
281
  };
254
- return {
282
+ const config = {
255
283
  context: process.cwd(),
256
284
  mode: process.env.NODE_ENV,
257
285
  entry: "./src/index",
@@ -263,6 +291,7 @@ function getWebpackConfig(args, override) {
263
291
  uniqueName: appPkg$1.name,
264
292
  publicPath: ""
265
293
  },
294
+ name: appPkg$1.name,
266
295
  externals: Object.assign({}, qseCDN.isUseCommon && {
267
296
  react: "React",
268
297
  "react-dom": "ReactDOM",
@@ -348,7 +377,7 @@ function getWebpackConfig(args, override) {
348
377
  transform: {
349
378
  legacyDecorator: override.decorators,
350
379
  react: {
351
- runtime: "automatic",
380
+ runtime: hasJsxRuntime ? "automatic" : "classic",
352
381
  development: isDev,
353
382
  refresh: isDev
354
383
  }
@@ -458,7 +487,7 @@ function getWebpackConfig(args, override) {
458
487
  }
459
488
  ].filter(Boolean) }] },
460
489
  plugins: [
461
- qseCDN.isUseCommon && new rspack.DllReferencePlugin({ manifest: fs.readJsonSync(paths.resolveOwn("asset", "dll", "libcommon3-manifest.json")) }),
490
+ qseCDN.isUseCommon && new rspack.DllReferencePlugin({ manifest: getPnpmCompatibleDllManifest(paths.resolveOwn("asset", "dll", "libcommon3-manifest.json")) }),
462
491
  new rspack.NormalModuleReplacementPlugin(/@rspack\/dev-server\/client\/index\.js/, (resource) => {
463
492
  const myClientPath = paths.resolveOwn("asset", "rspack-dev-server-client.js");
464
493
  resource.request = resource.request.replace(/.*dev-server\/client\/index\.js/, myClientPath);
@@ -535,6 +564,17 @@ function getWebpackConfig(args, override) {
535
564
  maxAssetSize: 2 * 1024 * 1024
536
565
  }
537
566
  };
567
+ if (override.cache) {
568
+ config.cache = true;
569
+ config.experiments = {
570
+ ...config.experiments,
571
+ cache: {
572
+ type: "persistent",
573
+ buildDependencies: paths.resolveExistPaths(fileURLToPath(import.meta.url), paths.package, paths.tsconfig, paths.override)
574
+ }
575
+ };
576
+ }
577
+ return config;
538
578
  }
539
579
 
540
580
  //#endregion
@@ -565,9 +605,9 @@ const setupMock = debounce(function setupMock() {
565
605
  }
566
606
  }, 100);
567
607
  const getPathReAndKeys = memoize((path) => {
568
- const keys = [];
608
+ const { regexp, keys } = pathToRegexp(path);
569
609
  return {
570
- re: pathToRegexp(path, keys),
610
+ re: regexp,
571
611
  keys
572
612
  };
573
613
  });
@@ -714,6 +754,15 @@ const defaultOverride = {
714
754
  import: [],
715
755
  pure_funcs: ["console.log"]
716
756
  };
757
+ function migrateOverride(override) {
758
+ if ("transformNodeModules" in override) console.warn("transformNodeModules 配置已废弃,请删除这个配置项");
759
+ if ("minifyImage" in override) console.warn("minifyImage 配置已废弃,请删除这个配置项");
760
+ if (typeof override.minify !== "boolean") {
761
+ console.warn("minify 类型错误,请使用boolean类型,已自动设置为 true");
762
+ override.minify = true;
763
+ }
764
+ if ("babel" in override) throw new Error("babel 配置已废弃,请删除这个配置项。如果有 babel-plugin-import 相关的配置,请直接使用 import 选项");
765
+ }
717
766
  let override = null;
718
767
  function getOverride() {
719
768
  if (override) return override;
@@ -722,6 +771,7 @@ function getOverride() {
722
771
  const userOverride = resolveModule(require(paths.override));
723
772
  if (typeof userOverride !== "object") throw new Error("格式错误,请使用 npx edu g override 生成文件");
724
773
  Object.assign(override, userOverride);
774
+ migrateOverride(override);
725
775
  }
726
776
  return override;
727
777
  }
@@ -894,14 +944,15 @@ async function build(args) {
894
944
  }));
895
945
  process.exit(1);
896
946
  }
947
+ printFileSizesAfterBuild(stats, previousSizeMap, paths.dist, WARN_AFTER_BUNDLE_GZIP_SIZE, WARN_AFTER_CHUNK_GZIP_SIZE);
948
+ if (appConfig.single) console.log(`打包完成,可以使用 ${chalk.green("@qse/ssh-sftp")} 自动部署代码到 v1`);
949
+ else console.log(`打包完成,可以运行 ${chalk.green("npx edu-scripts deploy")} 部署代码到 v1`);
950
+ console.log();
897
951
  console.log(stats.toString({
898
952
  colors: true,
899
953
  preset: "errors-warnings",
900
954
  timings: true
901
955
  }));
902
- printFileSizesAfterBuild(stats, previousSizeMap, paths.dist, WARN_AFTER_BUNDLE_GZIP_SIZE, WARN_AFTER_CHUNK_GZIP_SIZE);
903
- if (appConfig.single) console.log(`打包完成,可以使用 ${chalk.green("@qse/ssh-sftp")} 自动部署代码到 v1`);
904
- else console.log(`打包完成,可以运行 ${chalk.green("npx edu-scripts deploy")} 部署代码到 v1`);
905
956
  console.log();
906
957
  });
907
958
  }
package/dist/index.d.mts CHANGED
@@ -71,6 +71,13 @@ type Configuration$2 = {
71
71
  * @default [{libraryName: 'lodash',libraryDirectory: '',camelToDashComponentName: false}]
72
72
  */
73
73
  import?: NonNullable<SwcLoaderOptions['rspackExperiments']>['import'];
74
+ /**
75
+ * 该选项可以开启 Rspack 构建过程中对快照及中间产物的缓存,可以使用它们来提升构建的速度。\
76
+ * 这会增加磁盘空间的使用,但能显著提升构建性能,特别是在大型项目中。建议在开发环境中开启,在生产环境中根据需要选择是否开启。\
77
+ * 项目会自动清理7天未使用的缓存文件,确保磁盘空间不会被过度占用。
78
+ * @default false
79
+ */
80
+ cache?: boolean;
74
81
  };
75
82
  declare function defineConfig(config: Configuration$2): Configuration$2;
76
83
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qse/edu-scripts",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "author": "Kinoko",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,10 +14,10 @@
14
14
  "docs:build": "vitepress build docs",
15
15
  "docs:deploy": "ssh-sftp",
16
16
  "build": "tsdown",
17
- "deploy": "node scripts/deploy.js",
18
- "release": "npm run test && npm run build && npm publish && rimraf dist",
17
+ "deploy": "node scripts/deploy.js && rimraf .vitepress/dist",
18
+ "release": "npm run lint && npm run test && npm run build && npm publish && rimraf dist",
19
19
  "prettier": "prettier -c -w \"src/**/*.{js,jsx,tsx,ts,less,md,json}\"",
20
- "lint": "eslint --fix src",
20
+ "lint": "eslint --fix src && tsc --noEmit",
21
21
  "postversion": "npm run release",
22
22
  "test": "jest"
23
23
  },
package/src/build.ts CHANGED
@@ -53,8 +53,6 @@ export default async function build(args: BuildArgs) {
53
53
  process.exit(1)
54
54
  }
55
55
 
56
- console.log(stats.toString({ colors: true, preset: 'errors-warnings', timings: true }))
57
-
58
56
  printFileSizesAfterBuild(
59
57
  stats,
60
58
  previousSizeMap,
@@ -69,5 +67,7 @@ export default async function build(args: BuildArgs) {
69
67
  console.log(`打包完成,可以运行 ${chalk.green('npx edu-scripts deploy')} 部署代码到 v1`)
70
68
  }
71
69
  console.log()
70
+ console.log(stats.toString({ colors: true, preset: 'errors-warnings', timings: true }))
71
+ console.log()
72
72
  })
73
73
  }
@@ -20,18 +20,19 @@ function resolveOwn(...filePath: string[]) {
20
20
  return path.resolve(current, ...filePath)
21
21
  }
22
22
 
23
+ function resolveExistPaths(...paths: string[]) {
24
+ return paths.filter((p) => fs.existsSync(p))
25
+ }
26
+
23
27
  function getExistPath(...paths: string[]) {
24
- for (const path of paths) {
25
- if (fs.existsSync(path)) {
26
- return path
27
- }
28
- }
29
- return paths[0]
28
+ const existPaths = resolveExistPaths(...paths)
29
+ return existPaths[0] || paths[0]
30
30
  }
31
31
 
32
32
  const paths = {
33
33
  resolveApp,
34
34
  resolveOwn,
35
+ resolveExistPaths,
35
36
  eduAppEnv: resolveApp('src', 'edu-app-env.d.ts'),
36
37
  dist: resolveApp('dist'),
37
38
  sshSftp: resolveApp('.sftprc.json'),
@@ -41,7 +42,10 @@ const paths = {
41
42
  package: resolveApp('package.json'),
42
43
  tailwind: resolveApp('tailwind.config.js'),
43
44
  pages: resolveApp('src', 'pages'),
44
- override: getExistPath(resolveApp('edu-scripts.override.js'), resolveApp('edu-scripts.override.ts')),
45
+ override: getExistPath(
46
+ resolveApp('edu-scripts.override.js'),
47
+ resolveApp('edu-scripts.override.ts')
48
+ ),
45
49
  indexHTML: globbySync('./public/*.html', { absolute: true }),
46
50
  src: resolveApp('src'),
47
51
  public: resolveApp('public'),
@@ -7,7 +7,7 @@ import fs from 'fs-extra'
7
7
  import express from 'express'
8
8
  import cookieParser from 'cookie-parser'
9
9
  import multer from 'multer'
10
- import { pathToRegexp, type Key } from 'path-to-regexp'
10
+ import { pathToRegexp } from 'path-to-regexp'
11
11
  import { createRequire } from 'node:module'
12
12
  import { resolveModule } from '@/utils/resolveModule'
13
13
  const require = createRequire(import.meta.url)
@@ -44,9 +44,8 @@ const setupMock = debounce(function setupMock() {
44
44
  }, 100)
45
45
 
46
46
  const getPathReAndKeys = memoize((path: string) => {
47
- const keys: Key[] = []
48
- const re = pathToRegexp(path, keys)
49
- return { re, keys }
47
+ const { regexp, keys } = pathToRegexp(path)
48
+ return { re: regexp, keys }
50
49
  })
51
50
 
52
51
  function decodeParam(val: string) {
@@ -12,10 +12,20 @@ import appConfig from '../utils/appConfig'
12
12
  import type { Configuration as CustomConfiguration } from '../utils/defineConfig'
13
13
  import paths from './paths'
14
14
  import PostcssSafeAreaPlugin from './plugins/postcss-safe-area'
15
+ import { fileURLToPath } from 'node:url'
15
16
 
16
17
  const require = createRequire(import.meta.url)
17
18
  const appPkg = fs.readJsonSync(paths.package) as { name: string; version: string }
18
19
 
20
+ const hasJsxRuntime = (() => {
21
+ try {
22
+ require.resolve('react/jsx-runtime')
23
+ return appConfig.single
24
+ } catch {
25
+ return false
26
+ }
27
+ })()
28
+
19
29
  const jsMainPath = appConfig.grayscale
20
30
  ? `${appPkg.name}/beta/${appPkg.name}`
21
31
  : `${appPkg.name}/${appPkg.name}`
@@ -66,6 +76,32 @@ interface CSSLoaderOptions {
66
76
  }
67
77
  }
68
78
 
79
+ interface DllManifest {
80
+ name: string
81
+ content: Record<string, { id: number | string }>
82
+ }
83
+
84
+ function getPnpmCompatibleDllManifest(manifestPath: string): DllManifest {
85
+ const manifest = fs.readJsonSync(manifestPath) as DllManifest
86
+ const normalizedContent = { ...manifest.content }
87
+
88
+ for (const [key, value] of Object.entries(manifest.content)) {
89
+ if (!key.startsWith('./node_modules/')) continue
90
+
91
+ const request = key.replace('./node_modules/', '')
92
+ try {
93
+ const resolvedPath = require.resolve(request)
94
+ const relativePath = `./${path.relative(process.cwd(), resolvedPath).replace(/\\/g, '/')}`
95
+
96
+ if (!normalizedContent[relativePath]) {
97
+ normalizedContent[relativePath] = value
98
+ }
99
+ } catch {}
100
+ }
101
+
102
+ return { ...manifest, content: normalizedContent }
103
+ }
104
+
69
105
  export default function getWebpackConfig(args: any, override: CustomConfiguration): Configuration {
70
106
  const isDev = process.env.NODE_ENV === 'development'
71
107
  const isProd = process.env.NODE_ENV === 'production'
@@ -134,7 +170,7 @@ export default function getWebpackConfig(args: any, override: CustomConfiguratio
134
170
 
135
171
  const config: Configuration = {
136
172
  context: process.cwd(),
137
- mode: process.env.NODE_ENV as 'development' | 'production' | 'none',
173
+ mode: process.env.NODE_ENV as 'development' | 'production',
138
174
  entry: './src/index',
139
175
  target: 'browserslist',
140
176
  output: {
@@ -146,6 +182,7 @@ export default function getWebpackConfig(args: any, override: CustomConfiguratio
146
182
  uniqueName: appPkg.name,
147
183
  publicPath: '',
148
184
  },
185
+ name: appPkg.name,
149
186
  externals: Object.assign(
150
187
  {},
151
188
  qseCDN.isUseCommon && {
@@ -249,7 +286,7 @@ export default function getWebpackConfig(args: any, override: CustomConfiguratio
249
286
  transform: {
250
287
  legacyDecorator: override.decorators,
251
288
  react: {
252
- runtime: 'automatic',
289
+ runtime: hasJsxRuntime ? 'automatic' : 'classic',
253
290
  development: isDev,
254
291
  refresh: isDev,
255
292
  },
@@ -398,7 +435,9 @@ export default function getWebpackConfig(args: any, override: CustomConfiguratio
398
435
  plugins: [
399
436
  qseCDN.isUseCommon &&
400
437
  new rspack.DllReferencePlugin({
401
- manifest: fs.readJsonSync(paths.resolveOwn('asset', 'dll', 'libcommon3-manifest.json')),
438
+ manifest: getPnpmCompatibleDllManifest(
439
+ paths.resolveOwn('asset', 'dll', 'libcommon3-manifest.json')
440
+ ),
402
441
  }),
403
442
  new rspack.NormalModuleReplacementPlugin(
404
443
  /@rspack\/dev-server\/client\/index\.js/,
@@ -496,5 +535,21 @@ export default function getWebpackConfig(args: any, override: CustomConfiguratio
496
535
  },
497
536
  }
498
537
 
538
+ if (override.cache) {
539
+ config.cache = true
540
+ config.experiments = {
541
+ ...config.experiments,
542
+ cache: {
543
+ type: 'persistent',
544
+ buildDependencies: paths.resolveExistPaths(
545
+ fileURLToPath(import.meta.url),
546
+ paths.package,
547
+ paths.tsconfig,
548
+ paths.override
549
+ ),
550
+ },
551
+ }
552
+ }
553
+
499
554
  return config
500
555
  }
@@ -35,7 +35,7 @@ if (!(appConfig.single || appConfig.mainProject)) {
35
35
  console.log(chalk.bgYellow('教育集成工程不能含有 public/static, 现已自动删除'))
36
36
  try {
37
37
  fs.rmSync(paths.static, { recursive: true })
38
- } catch (e) {}
38
+ } catch {}
39
39
  }
40
40
  }
41
41
 
@@ -75,6 +75,14 @@ export type Configuration = {
75
75
  * @default [{libraryName: 'lodash',libraryDirectory: '',camelToDashComponentName: false}]
76
76
  */
77
77
  import?: NonNullable<SwcLoaderOptions['rspackExperiments']>['import']
78
+
79
+ /**
80
+ * 该选项可以开启 Rspack 构建过程中对快照及中间产物的缓存,可以使用它们来提升构建的速度。\
81
+ * 这会增加磁盘空间的使用,但能显著提升构建性能,特别是在大型项目中。建议在开发环境中开启,在生产环境中根据需要选择是否开启。\
82
+ * 项目会自动清理7天未使用的缓存文件,确保磁盘空间不会被过度占用。
83
+ * @default false
84
+ */
85
+ cache?: boolean
78
86
  }
79
87
  export function defineConfig(config: Configuration) {
80
88
  return config
@@ -14,6 +14,28 @@ const defaultOverride: Configuration = {
14
14
  pure_funcs: ['console.log'],
15
15
  }
16
16
 
17
+ function migrateOverride(override: Configuration) {
18
+ // 迁移提示 v1 -> v2,后续版本会删除这些废弃的配置项
19
+ if ('transformNodeModules' in override) {
20
+ console.warn('transformNodeModules 配置已废弃,请删除这个配置项')
21
+ }
22
+
23
+ if ('minifyImage' in override) {
24
+ console.warn('minifyImage 配置已废弃,请删除这个配置项')
25
+ }
26
+
27
+ if (typeof override.minify !== 'boolean') {
28
+ console.warn('minify 类型错误,请使用boolean类型,已自动设置为 true')
29
+ override.minify = true
30
+ }
31
+
32
+ if ('babel' in override) {
33
+ throw new Error(
34
+ 'babel 配置已废弃,请删除这个配置项。如果有 babel-plugin-import 相关的配置,请直接使用 import 选项'
35
+ )
36
+ }
37
+ }
38
+
17
39
  let override: Configuration | null = null
18
40
 
19
41
  export default function getOverride(): Configuration {
@@ -26,6 +48,8 @@ export default function getOverride(): Configuration {
26
48
  if (typeof userOverride !== 'object')
27
49
  throw new Error('格式错误,请使用 npx edu g override 生成文件')
28
50
  Object.assign(override, userOverride)
51
+
52
+ migrateOverride(override)
29
53
  }
30
54
 
31
55
  return override