@qse/edu-scripts 0.0.0-beta.4 → 0.0.0-beta.5

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.
@@ -1,6 +1,6 @@
1
- const { defineConfig } = require('@qse/edu-scripts')
1
+ import { defineConfig } from '@qse/edu-scripts'
2
2
 
3
- module.exports = defineConfig({
3
+ export default defineConfig({
4
4
  startup: ({ logger, chalk }) => {
5
5
  logger.info('本地免登录开发地址:', chalk.cyan(`http://127.0.0.1:${process.env.PORT}/index.html`))
6
6
  },
@@ -1,5 +1,5 @@
1
1
  /** @type {import('tailwindcss').Config} */
2
- module.exports = {
2
+ export default {
3
3
  content: ['./src/**/*.{js,jsx,ts,tsx}'],
4
4
  theme: {
5
5
  extend: {},
package/dist/cli.mjs CHANGED
@@ -166,7 +166,13 @@ const PostcssSafeAreaPlugin = () => {
166
166
  };
167
167
 
168
168
  //#endregion
169
- //#region src/config/webpackConfig.js
169
+ //#region src/utils/resolveModule.ts
170
+ function resolveModule(mod) {
171
+ return mod.default ? mod.default : mod;
172
+ }
173
+
174
+ //#endregion
175
+ //#region src/config/webpackConfig.ts
170
176
  const require$2 = createRequire(import.meta.url);
171
177
  const appPkg$1 = fs.readJsonSync(paths.package);
172
178
  const jsMainPath = appConfig.grayscale ? `${appPkg$1.name}/beta/${appPkg$1.name}` : `${appPkg$1.name}/${appPkg$1.name}`;
@@ -191,10 +197,6 @@ const qseCDN = (() => {
191
197
  isUseQsbSchemeRender: include("qsb-scheme-render.min.js")
192
198
  };
193
199
  })();
194
- /**
195
- * @param {*} args
196
- * @param {import('../utils/defineConfig.js').Configuration} override
197
- */
198
200
  function getWebpackConfig(args, override) {
199
201
  const isDev = process.env.NODE_ENV === "development";
200
202
  const isProd = process.env.NODE_ENV === "production";
@@ -225,7 +227,7 @@ function getWebpackConfig(args, override) {
225
227
  isProd && PostcssSafeAreaPlugin(),
226
228
  isProd && [require$2.resolve("postcss-momentum-scrolling"), ["scroll", "auto"]],
227
229
  require$2.resolve("postcss-normalize"),
228
- ...override.extraPostCSSPlugins
230
+ ...override.extraPostCSSPlugins || []
229
231
  ].filter(Boolean)
230
232
  },
231
233
  sourceMap: isDev
@@ -237,7 +239,7 @@ function getWebpackConfig(args, override) {
237
239
  options: {
238
240
  lessOptions: {
239
241
  javascriptEnabled: true,
240
- modifyVars: fs.existsSync(paths.theme) ? require$2(paths.theme) : void 0
242
+ modifyVars: fs.existsSync(paths.theme) ? resolveModule(require$2(paths.theme)) : void 0
241
243
  },
242
244
  sourceMap: true
243
245
  }
@@ -304,7 +306,11 @@ function getWebpackConfig(args, override) {
304
306
  ".tsx",
305
307
  ".json",
306
308
  ".wasm"
307
- ]
309
+ ],
310
+ tsConfig: {
311
+ configFile: paths.tsconfig,
312
+ references: "auto"
313
+ }
308
314
  },
309
315
  stats: false,
310
316
  devtool: isDev ? "cheap-module-source-map" : false,
@@ -322,8 +328,9 @@ function getWebpackConfig(args, override) {
322
328
  env: { targets: process.env.BROWSERSLIST },
323
329
  rspackExperiments: { import: [{
324
330
  libraryName: "lodash",
325
- customName: "lodash/{{member}}"
326
- }, ...override.import] },
331
+ libraryDirectory: "",
332
+ camelToDashComponentName: false
333
+ }, ...override.import || []] },
327
334
  isModule: "unknown",
328
335
  jsc: {
329
336
  parser: {
@@ -537,8 +544,7 @@ const setupMock = debounce(function setupMock() {
537
544
  for (const file of files) {
538
545
  delete require$1.cache[require$1.resolve(file)];
539
546
  try {
540
- let mock = require$1(file);
541
- mock = mock.default || mock;
547
+ const mock = resolveModule(require$1(file));
542
548
  for (const key in mock) {
543
549
  const [method, path] = key.split(" ");
544
550
  mockCache[key] = {
@@ -633,7 +639,7 @@ function setupMockServer(middelwaves, _devServer) {
633
639
  }
634
640
 
635
641
  //#endregion
636
- //#region src/config/webpackDevServerConfig.js
642
+ //#region src/config/webpackDevServerConfig.ts
637
643
  function createProxy(context, target, origin) {
638
644
  const url = new URL(origin || target);
639
645
  return {
@@ -647,13 +653,8 @@ function createProxy(context, target, origin) {
647
653
  }
648
654
  };
649
655
  }
650
- /**
651
- * @param {*} args
652
- * @param {import('../utils/defineConfig.js').Configuration} override
653
- */
654
656
  function getWebpackDevServerConfig(args, override) {
655
657
  const host = process.env.HOST || "0.0.0.0";
656
- /** @type {import('@rspack/dev-server').Configuration} */
657
658
  const devServer = {
658
659
  allowedHosts: "all",
659
660
  historyApiFallback: true,
@@ -680,10 +681,10 @@ function getWebpackDevServerConfig(args, override) {
680
681
  compress: true
681
682
  };
682
683
  if (override.proxy) {
683
- if (Array.isArray(override.proxy)) devServer.proxy = [...override.proxy, ...devServer.proxy];
684
+ if (Array.isArray(override.proxy)) devServer.proxy = [...override.proxy, ...devServer.proxy || []];
684
685
  else if (typeof override.proxy === "object") devServer.proxy = [...Object.entries(override.proxy).map(([context, target]) => {
685
686
  return createProxy(context, target);
686
- }), ...devServer.proxy];
687
+ }), ...devServer.proxy || []];
687
688
  else throw new Error("proxy 必须是数组或对象");
688
689
  const proxyMap = /* @__PURE__ */ new Map();
689
690
  devServer.proxy = devServer.proxy.filter((item) => {
@@ -713,7 +714,7 @@ function getOverride() {
713
714
  if (override) return override;
714
715
  override = Object.assign({}, defaultOverride);
715
716
  if (fs.existsSync(paths.override)) {
716
- const userOverride = require(paths.override);
717
+ const userOverride = resolveModule(require(paths.override));
717
718
  if (typeof userOverride !== "object") throw new Error("格式错误,请使用 npx edu g override 生成文件");
718
719
  Object.assign(override, userOverride);
719
720
  }
@@ -770,7 +771,7 @@ async function start(args) {
770
771
  }
771
772
 
772
773
  //#endregion
773
- //#region src/utils/FileSizeReporter.js
774
+ //#region src/utils/FileSizeReporter.ts
774
775
  /**
775
776
  * Copyright (c) 2015-present, Facebook, Inc.
776
777
  *
@@ -781,15 +782,15 @@ function canReadAsset(asset) {
781
782
  return /\.(js|css)$/.test(asset) && !/service-worker\.js/.test(asset) && !/precache-manifest\.[0-9a-f]+\.js/.test(asset);
782
783
  }
783
784
  function printFileSizesAfterBuild(webpackStats, previousSizeMap, buildFolder, maxBundleGzipSize, maxChunkGzipSize) {
784
- var root = previousSizeMap.root;
785
- var sizes = previousSizeMap.sizes;
786
- var assets = (webpackStats.stats || [webpackStats]).map((stats) => stats.toJson({
785
+ const root = previousSizeMap.root;
786
+ const sizes = previousSizeMap.sizes;
787
+ const assets = (webpackStats.stats || [webpackStats]).map((stats) => stats.toJson({
787
788
  all: false,
788
789
  assets: true
789
790
  }).assets.filter((asset) => canReadAsset(asset.name)).map((asset) => {
790
- var size = gzipSizeSync(fs.readFileSync(path.join(root, asset.name)));
791
- var previousSize = sizes[removeFileNameHash(root, asset.name)];
792
- var difference = getDifferenceLabel(size, previousSize);
791
+ const size = gzipSizeSync(fs.readFileSync(path.join(root, asset.name)));
792
+ const previousSize = sizes[removeFileNameHash(root, asset.name)];
793
+ const difference = getDifferenceLabel(size, previousSize);
793
794
  return {
794
795
  folder: path.join(path.basename(buildFolder), path.dirname(asset.name)),
795
796
  name: path.basename(asset.name),
@@ -800,20 +801,20 @@ function printFileSizesAfterBuild(webpackStats, previousSizeMap, buildFolder, ma
800
801
  if (assets.length === 0) return;
801
802
  console.log("\ngzip 后文件大小:\n");
802
803
  assets.sort((a, b) => b.size - a.size);
803
- var mainAssetIdx = assets.findIndex((asset) => /_\d+\.\d+\.\d+/.test(asset.name));
804
+ const mainAssetIdx = assets.findIndex((asset) => /_\d+\.\d+\.\d+/.test(asset.name));
804
805
  assets.unshift(assets.splice(mainAssetIdx, 1)[0]);
805
- var longestSizeLabelLength = Math.max.apply(null, assets.map((a) => stripAnsi(a.sizeLabel).length));
806
- var suggestBundleSplitting = false;
806
+ const longestSizeLabelLength = Math.max.apply(null, assets.map((a) => stripAnsi(a.sizeLabel).length));
807
+ let suggestBundleSplitting = false;
807
808
  assets.forEach((asset) => {
808
- var sizeLabel = asset.sizeLabel;
809
- var sizeLength = stripAnsi(sizeLabel).length;
809
+ let sizeLabel = asset.sizeLabel;
810
+ const sizeLength = stripAnsi(sizeLabel).length;
810
811
  if (sizeLength < longestSizeLabelLength) {
811
- var rightPadding = " ".repeat(longestSizeLabelLength - sizeLength);
812
+ const rightPadding = " ".repeat(longestSizeLabelLength - sizeLength);
812
813
  sizeLabel += rightPadding;
813
814
  }
814
- var isMainBundle = /_\d+\.\d+\.\d+\./.test(asset.name);
815
- var maxRecommendedSize = isMainBundle ? maxBundleGzipSize : maxChunkGzipSize;
816
- var isLarge = maxRecommendedSize && asset.size > maxRecommendedSize;
815
+ const isMainBundle = /_\d+\.\d+\.\d+\./.test(asset.name);
816
+ const maxRecommendedSize = isMainBundle ? maxBundleGzipSize : maxChunkGzipSize;
817
+ const isLarge = maxRecommendedSize && asset.size > maxRecommendedSize;
817
818
  if (isLarge && path.extname(asset.name) === ".js") suggestBundleSplitting = true;
818
819
  console.log(" " + (isLarge ? chalk.yellow(sizeLabel) : sizeLabel) + " " + chalk.dim(asset.folder + path.sep) + chalk.cyan(asset.name));
819
820
  if (isMainBundle) console.log("");
@@ -830,9 +831,9 @@ function removeFileNameHash(buildFolder, fileName) {
830
831
  return fileName.replace(buildFolder, "").replace(/\\/g, "/").replace(/\/\d+\.\d+\.\d+\//, "/").replace(/\/?(.*)(\.[0-9a-f]+)(\.chunk)?(\.js|\.css)/, (match, p1, p2, p3, p4) => p1 + p4);
831
832
  }
832
833
  function getDifferenceLabel(currentSize, previousSize) {
833
- var FIFTY_KILOBYTES = 1024 * 50;
834
- var difference = currentSize - previousSize;
835
- var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0;
834
+ const FIFTY_KILOBYTES = 1024 * 50;
835
+ const difference = currentSize - previousSize;
836
+ const fileSize = !Number.isNaN(difference) ? filesize(difference) : 0;
836
837
  if (difference >= FIFTY_KILOBYTES) return chalk.red("+" + fileSize);
837
838
  else if (difference < FIFTY_KILOBYTES && difference > 0) return chalk.yellow("+" + fileSize);
838
839
  else if (difference < 0) return chalk.green(fileSize);
@@ -841,10 +842,10 @@ function getDifferenceLabel(currentSize, previousSize) {
841
842
  function measureFileSizesBeforeBuild(buildFolder) {
842
843
  return new Promise((resolve) => {
843
844
  recursive(buildFolder, (err, fileNames) => {
844
- var sizes;
845
+ let sizes;
845
846
  if (!err && fileNames) sizes = fileNames.filter(canReadAsset).reduce((memo, fileName) => {
846
- var contents = fs.readFileSync(fileName);
847
- var key = removeFileNameHash(buildFolder, fileName);
847
+ const contents = fs.readFileSync(fileName);
848
+ const key = removeFileNameHash(buildFolder, fileName);
848
849
  memo[key] = gzipSizeSync(contents);
849
850
  return memo;
850
851
  }, {});
@@ -899,7 +900,7 @@ async function build(args) {
899
900
  }
900
901
 
901
902
  //#endregion
902
- //#region src/utils/changeDeployVersion.js
903
+ //#region src/utils/changeDeployVersion.ts
903
904
  const TARGET_IDENTIFIER_NAME = "project_apiArr";
904
905
  const MODULE_IDENTIFIER_NAME = "module";
905
906
  function changeDeployVersion(code, pkg) {
@@ -911,9 +912,6 @@ function changeDeployVersion(code, pkg) {
911
912
  throw new Error(`代码解析错误: ${error.message}`, { cause: error });
912
913
  }
913
914
  const keyName = grayscale ? "grayscale" : "main";
914
- /**
915
- * @return {babel.NodePath<t.VariableDeclarator> | undefined}
916
- */
917
915
  function findTargetDeclarator(ast) {
918
916
  let res;
919
917
  traverse(ast, { VariableDeclarator(path) {
@@ -921,44 +919,33 @@ function changeDeployVersion(code, pkg) {
921
919
  } });
922
920
  return res;
923
921
  }
924
- /**
925
- * @param {babel.NodePath<t.VariableDeclarator>} path
926
- * @return {babel.NodePath<t.ObjectExpression> | undefined}
927
- */
928
922
  function findModuleObject(path) {
929
923
  let res;
930
924
  path.traverse({ ObjectExpression(path) {
931
- if (path.node.properties.some((node) => types.isIdentifier(node.key, { name: MODULE_IDENTIFIER_NAME }) && types.isLiteral(node.value, { value: name }))) res = path;
925
+ if (path.node.properties.some((node) => types.isObjectProperty(node) && types.isIdentifier(node.key, { name: MODULE_IDENTIFIER_NAME }) && types.isLiteral(node.value, { value: name }))) res = path;
932
926
  } });
933
927
  return res;
934
928
  }
935
- /**
936
- * @param {babel.NodePath<t.ObjectExpression>} path
937
- */
938
929
  function modifyModuleVersion(path) {
939
930
  let hasModify = false;
940
931
  path.traverse({ Property(path) {
941
- if (path.node.key.name === keyName) {
942
- path.node.value.value = version;
932
+ if (types.isObjectProperty(path.node) && types.isIdentifier(path.node.key, { name: keyName })) {
933
+ if (types.isStringLiteral(path.node.value)) path.node.value.value = version;
943
934
  hasModify = true;
944
935
  }
945
936
  } });
946
937
  if (!hasModify) path.node.properties.push(types.objectProperty(types.identifier(keyName), types.stringLiteral(version)));
947
938
  }
948
- /**
949
- * @param {babel.NodePath<t.ObjectExpression>} path
950
- */
951
939
  function deleteModuleComments(path) {
952
940
  path.traverse({ ObjectExpression(path) {
953
941
  delete path.node.leadingComments;
954
942
  } });
955
943
  }
956
- /**
957
- * @param {babel.NodePath<t.VariableDeclarator>} path
958
- */
959
944
  function addModuleToTarget(path) {
960
- const elements = path.node.init.elements;
961
- elements.splice(elements.length - 2, 0, types.objectExpression([types.objectProperty(types.identifier(MODULE_IDENTIFIER_NAME), types.stringLiteral(name)), types.objectProperty(types.identifier(keyName), types.stringLiteral(version))]));
945
+ if (types.isArrayExpression(path.node.init)) {
946
+ const elements = path.node.init.elements;
947
+ elements.splice(elements.length - 2, 0, types.objectExpression([types.objectProperty(types.identifier(MODULE_IDENTIFIER_NAME), types.stringLiteral(name)), types.objectProperty(types.identifier(keyName), types.stringLiteral(version))]));
948
+ }
962
949
  }
963
950
  const targetPath = findTargetDeclarator(ast);
964
951
  if (!targetPath) throw new Error(`ver.js 不合规范,未找到参数 ${TARGET_IDENTIFIER_NAME}`);
@@ -966,10 +953,12 @@ function changeDeployVersion(code, pkg) {
966
953
  if (moduleObjPath) modifyModuleVersion(moduleObjPath);
967
954
  else addModuleToTarget(targetPath);
968
955
  deleteModuleComments(targetPath);
969
- return transformFromAstSync(ast, void 0, {
956
+ const result = transformFromAstSync(ast, void 0, {
970
957
  filename: "ver.js",
971
958
  minified: true
972
- }).code;
959
+ });
960
+ if (!result || !result.code) throw new Error("代码转换失败");
961
+ return result.code;
973
962
  }
974
963
 
975
964
  //#endregion
package/dist/index.d.mts CHANGED
@@ -1,22 +1,18 @@
1
1
  import { Chalk } from "chalk";
2
- import { Compiler, Configuration } from "@rspack/core";
2
+ import { Compiler, Configuration, SwcLoaderOptions } from "@rspack/core";
3
3
  import { Configuration as Configuration$1 } from "@rspack/dev-server";
4
4
  import { RequestHandler } from "express";
5
5
 
6
6
  //#region src/utils/defineConfig.d.ts
7
7
  type ProxyConfigArray = NonNullable<Configuration$1['proxy']>;
8
- type BabelImportPlugin = {
9
- libraryName: string;
10
- libraryDirectory?: string;
11
- customName?: string;
12
- style?: boolean | 'css' | string;
13
- };
14
8
  type Configuration$2 = {
15
9
  webpack?: (config: Configuration) => Configuration | undefined;
16
10
  devServer?: (config: Configuration$1) => Configuration$1 | undefined;
17
11
  /**
18
12
  * webpack alias 配置,会与内置 alias 合并
19
13
  *
14
+ * 会自动读取 tsconfig 里 baseUrl + paths 的配置
15
+ *
20
16
  * @default
21
17
  * { '@': './src' }
22
18
  */
@@ -71,12 +67,10 @@ type Configuration$2 = {
71
67
  /**
72
68
  * 与 babel-plugin-import 类似,但是内部是 rspack 重构,libraryDirectory 默认值是 lib
73
69
  *
74
- * 如果 libraryDirectory 传 ’‘ 则必须使用 customName 来写
75
- *
76
70
  * @see https://rspack.rs/zh/guide/features/builtin-swc-loader#rspackexperimentsimport
77
- * @default [{libraryName: 'lodash', customName: 'lodash/{{member}}'}]
71
+ * @default [{libraryName: 'lodash',libraryDirectory: '',camelToDashComponentName: false}]
78
72
  */
79
- import?: BabelImportPlugin[];
73
+ import?: NonNullable<SwcLoaderOptions['rspackExperiments']>['import'];
80
74
  };
81
75
  declare function defineConfig(config: Configuration$2): Configuration$2;
82
76
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qse/edu-scripts",
3
- "version": "0.0.0-beta.4",
3
+ "version": "0.0.0-beta.5",
4
4
  "author": "Kinoko",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -15,7 +15,7 @@
15
15
  "docs:deploy": "ssh-sftp",
16
16
  "build": "tsdown",
17
17
  "deploy": "node scripts/deploy.js",
18
- "release": "npm run test && npm run build && npm publish && rimraf es",
18
+ "release": "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
20
  "lint": "eslint --fix src",
21
21
  "postversion": "npm run release",
@@ -9,6 +9,7 @@ import cookieParser from 'cookie-parser'
9
9
  import multer from 'multer'
10
10
  import { pathToRegexp, type Key } from 'path-to-regexp'
11
11
  import { createRequire } from 'node:module'
12
+ import { resolveModule } from '@/utils/resolveModule'
12
13
  const require = createRequire(import.meta.url)
13
14
  const { register } = require('@swc-node/register/register')
14
15
 
@@ -31,8 +32,7 @@ const setupMock = debounce(function setupMock() {
31
32
  for (const file of files) {
32
33
  delete require.cache[require.resolve(file)]
33
34
  try {
34
- let mock = require(file)
35
- mock = mock.default || mock
35
+ const mock = resolveModule(require(file))
36
36
 
37
37
  for (const key in mock) {
38
38
  const [method, path] = key.split(' ')
@@ -1,15 +1,19 @@
1
1
  import { RsdoctorRspackPlugin } from '@rsdoctor/rspack-plugin'
2
2
  import { rspack } from '@rspack/core'
3
+ import type { Configuration, Compiler, RuleSetRule, SwcLoaderOptions } from '@rspack/core'
3
4
  import ReactRefreshPlugin from '@rspack/plugin-react-refresh'
4
5
  import chalk from 'chalk'
5
6
  import fs from 'fs-extra'
6
7
  import HtmlWebpackPlugin from 'html-webpack-plugin'
7
8
  import { createRequire } from 'node:module'
8
- import appConfig from '../utils/appConfig.js'
9
- import paths from './paths.js'
10
- import PostcssSafeAreaPlugin from './plugins/postcss-safe-area.js'
9
+ import appConfig from '../utils/appConfig'
10
+ import type { Configuration as CustomConfiguration } from '../utils/defineConfig'
11
+ import paths from './paths'
12
+ import PostcssSafeAreaPlugin from './plugins/postcss-safe-area'
13
+ import { resolveModule } from '@/utils/resolveModule'
14
+
11
15
  const require = createRequire(import.meta.url)
12
- const appPkg = fs.readJsonSync(paths.package)
16
+ const appPkg = fs.readJsonSync(paths.package) as { name: string; version: string }
13
17
 
14
18
  const jsMainPath = appConfig.grayscale
15
19
  ? `${appPkg.name}/beta/${appPkg.name}`
@@ -25,10 +29,19 @@ const lessModuleRegex = /\.module\.less$/
25
29
 
26
30
  const imageInlineSizeLimit = 10 * 1024
27
31
 
28
- const qseCDN = (() => {
32
+ interface QseCDN {
33
+ isUseCommon: boolean
34
+ isUseAxios: boolean
35
+ isUseMoment: boolean
36
+ isUseAntd: boolean
37
+ isUseQsbAntd: boolean
38
+ isUseQsbSchemeRender: boolean
39
+ }
40
+
41
+ const qseCDN: QseCDN = (() => {
29
42
  const contents = paths.indexHTML.map((url) => fs.readFileSync(url, 'utf-8'))
30
43
 
31
- function include(pattern) {
44
+ function include(pattern: string | RegExp): boolean {
32
45
  const regexp = new RegExp(pattern)
33
46
  return contents.some((content) => regexp.test(content))
34
47
  }
@@ -43,16 +56,21 @@ const qseCDN = (() => {
43
56
  }
44
57
  })()
45
58
 
46
- /**
47
- * @param {*} args
48
- * @param {import('../utils/defineConfig.js').Configuration} override
49
- */
50
- export default function getWebpackConfig(args, override) {
59
+ interface CSSLoaderOptions {
60
+ importLoaders: number
61
+ sourceMap: boolean
62
+ modules: {
63
+ mode: 'global' | 'local'
64
+ localIdentName: string
65
+ }
66
+ }
67
+
68
+ export default function getWebpackConfig(args: any, override: CustomConfiguration): Configuration {
51
69
  const isDev = process.env.NODE_ENV === 'development'
52
70
  const isProd = process.env.NODE_ENV === 'production'
53
71
 
54
72
  // common function to get style loaders
55
- const getStyleLoaders = (cssOptions, preProcessor) => {
73
+ const getStyleLoaders = (cssOptions: CSSLoaderOptions, preProcessor?: string) => {
56
74
  const loaders = [
57
75
  {
58
76
  loader: require.resolve('style-loader'),
@@ -89,7 +107,7 @@ export default function getWebpackConfig(args, override) {
89
107
  isProd && PostcssSafeAreaPlugin(),
90
108
  isProd && [require.resolve('postcss-momentum-scrolling'), ['scroll', 'auto']],
91
109
  require.resolve('postcss-normalize'),
92
- ...override.extraPostCSSPlugins,
110
+ ...(override.extraPostCSSPlugins || []),
93
111
  ].filter(Boolean),
94
112
  },
95
113
  sourceMap: isDev,
@@ -102,19 +120,20 @@ export default function getWebpackConfig(args, override) {
102
120
  options: {
103
121
  lessOptions: {
104
122
  javascriptEnabled: true,
105
- modifyVars: fs.existsSync(paths.theme) ? require(paths.theme) : undefined,
123
+ modifyVars: fs.existsSync(paths.theme)
124
+ ? resolveModule(require(paths.theme))
125
+ : undefined,
106
126
  },
107
127
  sourceMap: true,
108
128
  },
109
- })
129
+ } as any)
110
130
  }
111
131
  return loaders
112
132
  }
113
133
 
114
- /** @type {import('@rspack/core').Configuration} */
115
- const config = {
134
+ const config: Configuration = {
116
135
  context: process.cwd(),
117
- mode: process.env.NODE_ENV,
136
+ mode: process.env.NODE_ENV as 'development' | 'production' | 'none',
118
137
  entry: './src/index',
119
138
  target: 'browserslist',
120
139
  output: {
@@ -184,6 +203,10 @@ export default function getWebpackConfig(args, override) {
184
203
  ...override.alias,
185
204
  },
186
205
  extensions: ['.web.js', '.web.mjs', '.js', '.mjs', '.jsx', '.ts', '.tsx', '.json', '.wasm'],
206
+ tsConfig: {
207
+ configFile: paths.tsconfig,
208
+ references: 'auto',
209
+ },
187
210
  },
188
211
  stats: false,
189
212
  devtool: isDev ? 'cheap-module-source-map' : false,
@@ -207,9 +230,10 @@ export default function getWebpackConfig(args, override) {
207
230
  import: [
208
231
  {
209
232
  libraryName: 'lodash',
210
- customName: 'lodash/{{member}}',
233
+ libraryDirectory: '',
234
+ camelToDashComponentName: false,
211
235
  },
212
- ...override.import,
236
+ ...(override.import || []),
213
237
  ],
214
238
  },
215
239
  isModule: 'unknown',
@@ -229,7 +253,7 @@ export default function getWebpackConfig(args, override) {
229
253
  },
230
254
  },
231
255
  },
232
- },
256
+ } satisfies SwcLoaderOptions,
233
257
  },
234
258
  ],
235
259
  },
@@ -365,7 +389,7 @@ export default function getWebpackConfig(args, override) {
365
389
  test: /\.(?!(?:js|mjs|jsx|ts|tsx|html|json)$)[^.]+$/,
366
390
  type: 'asset/resource',
367
391
  },
368
- ].filter(Boolean),
392
+ ].filter(Boolean) as RuleSetRule[],
369
393
  },
370
394
  ],
371
395
  },
@@ -417,7 +441,7 @@ export default function getWebpackConfig(args, override) {
417
441
  : []),
418
442
  process.env.ANALYZE && isProd && new RsdoctorRspackPlugin(),
419
443
  isDev &&
420
- ((compiler) => {
444
+ ((compiler: Compiler) => {
421
445
  let isFirst = true
422
446
  compiler.hooks.afterDone.tap('edu-scripts-startup', (stats) => {
423
447
  if (!isFirst) console.clear()
@@ -1,6 +1,15 @@
1
+ import type { Configuration } from '@rspack/dev-server'
2
+ import type { Configuration as CustomConfiguration } from '../utils/defineConfig'
1
3
  import setupMockServer from './plugins/mock-server'
2
4
 
3
- function createProxy(context, target, origin) {
5
+ interface ProxyConfig {
6
+ context: string[]
7
+ target: string
8
+ changeOrigin: boolean
9
+ onProxyReq: (proxyReq: any) => void
10
+ }
11
+
12
+ function createProxy(context: string, target: string, origin?: string): ProxyConfig {
4
13
  const url = new URL(origin || target)
5
14
 
6
15
  return {
@@ -15,15 +24,10 @@ function createProxy(context, target, origin) {
15
24
  }
16
25
  }
17
26
 
18
- /**
19
- * @param {*} args
20
- * @param {import('../utils/defineConfig.js').Configuration} override
21
- */
22
- export default function getWebpackDevServerConfig(args, override) {
27
+ export default function getWebpackDevServerConfig(args: any, override: CustomConfiguration): Configuration {
23
28
  const host = process.env.HOST || '0.0.0.0'
24
29
 
25
- /** @type {import('@rspack/dev-server').Configuration} */
26
- const devServer = {
30
+ const devServer: Configuration = {
27
31
  allowedHosts: 'all',
28
32
  historyApiFallback: true,
29
33
  port: args.port,
@@ -56,19 +60,19 @@ export default function getWebpackDevServerConfig(args, override) {
56
60
 
57
61
  if (override.proxy) {
58
62
  if (Array.isArray(override.proxy)) {
59
- devServer.proxy = [...override.proxy, ...devServer.proxy]
63
+ devServer.proxy = [...override.proxy, ...(devServer.proxy || [])]
60
64
  } else if (typeof override.proxy === 'object') {
61
65
  const proxies = Object.entries(override.proxy).map(([context, target]) => {
62
- return createProxy(context, target)
66
+ return createProxy(context, target as string)
63
67
  })
64
- devServer.proxy = [...proxies, ...devServer.proxy]
68
+ devServer.proxy = [...proxies, ...(devServer.proxy || [])]
65
69
  } else {
66
70
  throw new Error('proxy 必须是数组或对象')
67
71
  }
68
72
 
69
73
  // 清理重复的代理配置
70
- const proxyMap = new Map()
71
- devServer.proxy = devServer.proxy.filter((item) => {
74
+ const proxyMap = new Map<string, boolean>()
75
+ devServer.proxy = (devServer.proxy as ProxyConfig[]).filter((item) => {
72
76
  const key = JSON.stringify([...item.context].sort())
73
77
  if (!proxyMap.has(key)) {
74
78
  proxyMap.set(key, true)
@@ -9,11 +9,24 @@ import fs from 'fs-extra'
9
9
  import path from 'path'
10
10
  import chalk from 'chalk'
11
11
  import filesize from 'filesize'
12
+ // @ts-ignore
12
13
  import recursive from 'recursive-readdir'
13
14
  import stripAnsi from 'strip-ansi'
14
15
  import { gzipSizeSync } from 'gzip-size'
15
16
 
16
- function canReadAsset(asset) {
17
+ interface SizeMap {
18
+ root: string
19
+ sizes: Record<string, number>
20
+ }
21
+
22
+ interface WebpackStats {
23
+ stats?: WebpackStats[]
24
+ toJson: (options: { all: boolean; assets: boolean }) => {
25
+ assets: Array<{ name: string }>
26
+ }
27
+ }
28
+
29
+ function canReadAsset(asset: string): boolean {
17
30
  return (
18
31
  /\.(js|css)$/.test(asset) &&
19
32
  !/service-worker\.js/.test(asset) &&
@@ -23,24 +36,24 @@ function canReadAsset(asset) {
23
36
 
24
37
  // Prints a detailed summary of build files.
25
38
  function printFileSizesAfterBuild(
26
- webpackStats,
27
- previousSizeMap,
28
- buildFolder,
29
- maxBundleGzipSize,
30
- maxChunkGzipSize
31
- ) {
32
- var root = previousSizeMap.root
33
- var sizes = previousSizeMap.sizes
34
- var assets = (webpackStats.stats || [webpackStats])
39
+ webpackStats: WebpackStats,
40
+ previousSizeMap: SizeMap,
41
+ buildFolder: string,
42
+ maxBundleGzipSize: number,
43
+ maxChunkGzipSize: number
44
+ ): void {
45
+ const root = previousSizeMap.root
46
+ const sizes = previousSizeMap.sizes
47
+ const assets = (webpackStats.stats || [webpackStats])
35
48
  .map((stats) =>
36
49
  stats
37
50
  .toJson({ all: false, assets: true })
38
51
  .assets.filter((asset) => canReadAsset(asset.name))
39
52
  .map((asset) => {
40
- var fileContents = fs.readFileSync(path.join(root, asset.name))
41
- var size = gzipSizeSync(fileContents)
42
- var previousSize = sizes[removeFileNameHash(root, asset.name)]
43
- var difference = getDifferenceLabel(size, previousSize)
53
+ const fileContents = fs.readFileSync(path.join(root, asset.name))
54
+ const size = gzipSizeSync(fileContents)
55
+ const previousSize = sizes[removeFileNameHash(root, asset.name)]
56
+ const difference = getDifferenceLabel(size, previousSize)
44
57
  return {
45
58
  folder: path.join(path.basename(buildFolder), path.dirname(asset.name)),
46
59
  name: path.basename(asset.name),
@@ -58,24 +71,24 @@ function printFileSizesAfterBuild(
58
71
  assets.sort((a, b) => b.size - a.size)
59
72
 
60
73
  // move main file to first
61
- var mainAssetIdx = assets.findIndex((asset) => /_\d+\.\d+\.\d+/.test(asset.name))
74
+ const mainAssetIdx = assets.findIndex((asset) => /_\d+\.\d+\.\d+/.test(asset.name))
62
75
  assets.unshift(assets.splice(mainAssetIdx, 1)[0])
63
76
 
64
- var longestSizeLabelLength = Math.max.apply(
77
+ const longestSizeLabelLength = Math.max.apply(
65
78
  null,
66
79
  assets.map((a) => stripAnsi(a.sizeLabel).length)
67
80
  )
68
- var suggestBundleSplitting = false
81
+ let suggestBundleSplitting = false
69
82
  assets.forEach((asset) => {
70
- var sizeLabel = asset.sizeLabel
71
- var sizeLength = stripAnsi(sizeLabel).length
83
+ let sizeLabel = asset.sizeLabel
84
+ const sizeLength = stripAnsi(sizeLabel).length
72
85
  if (sizeLength < longestSizeLabelLength) {
73
- var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength)
86
+ const rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength)
74
87
  sizeLabel += rightPadding
75
88
  }
76
- var isMainBundle = /_\d+\.\d+\.\d+\./.test(asset.name)
77
- var maxRecommendedSize = isMainBundle ? maxBundleGzipSize : maxChunkGzipSize
78
- var isLarge = maxRecommendedSize && asset.size > maxRecommendedSize
89
+ const isMainBundle = /_\d+\.\d+\.\d+\./.test(asset.name)
90
+ const maxRecommendedSize = isMainBundle ? maxBundleGzipSize : maxChunkGzipSize
91
+ const isLarge = maxRecommendedSize && asset.size > maxRecommendedSize
79
92
  if (isLarge && path.extname(asset.name) === '.js') {
80
93
  suggestBundleSplitting = true
81
94
  }
@@ -100,7 +113,7 @@ function printFileSizesAfterBuild(
100
113
  console.log()
101
114
  }
102
115
 
103
- function removeFileNameHash(buildFolder, fileName) {
116
+ function removeFileNameHash(buildFolder: string, fileName: string): string {
104
117
  return fileName
105
118
  .replace(buildFolder, '')
106
119
  .replace(/\\/g, '/')
@@ -110,10 +123,10 @@ function removeFileNameHash(buildFolder, fileName) {
110
123
 
111
124
  // Input: 1024, 2048
112
125
  // Output: "(+1 KB)"
113
- function getDifferenceLabel(currentSize, previousSize) {
114
- var FIFTY_KILOBYTES = 1024 * 50
115
- var difference = currentSize - previousSize
116
- var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0
126
+ function getDifferenceLabel(currentSize: number, previousSize: number): string {
127
+ const FIFTY_KILOBYTES = 1024 * 50
128
+ const difference = currentSize - previousSize
129
+ const fileSize = !Number.isNaN(difference) ? filesize(difference) : 0
117
130
  if (difference >= FIFTY_KILOBYTES) {
118
131
  return chalk.red('+' + fileSize)
119
132
  } else if (difference < FIFTY_KILOBYTES && difference > 0) {
@@ -125,17 +138,17 @@ function getDifferenceLabel(currentSize, previousSize) {
125
138
  }
126
139
  }
127
140
 
128
- function measureFileSizesBeforeBuild(buildFolder) {
141
+ function measureFileSizesBeforeBuild(buildFolder: string): Promise<SizeMap> {
129
142
  return new Promise((resolve) => {
130
- recursive(buildFolder, (err, fileNames) => {
131
- var sizes
143
+ recursive(buildFolder, (err: any, fileNames: any) => {
144
+ let sizes: Record<string, number> | undefined
132
145
  if (!err && fileNames) {
133
- sizes = fileNames.filter(canReadAsset).reduce((memo, fileName) => {
134
- var contents = fs.readFileSync(fileName)
135
- var key = removeFileNameHash(buildFolder, fileName)
146
+ sizes = fileNames.filter(canReadAsset).reduce((memo: any, fileName: any) => {
147
+ const contents = fs.readFileSync(fileName)
148
+ const key = removeFileNameHash(buildFolder, fileName)
136
149
  memo[key] = gzipSizeSync(contents)
137
150
  return memo
138
- }, {})
151
+ }, {} as Record<string, number>)
139
152
  }
140
153
  resolve({
141
154
  root: buildFolder,
@@ -1,24 +1,28 @@
1
+ // @ts-nocheck
1
2
  import { parse, traverse, types as t, transformFromAstSync } from '@babel/core'
2
3
 
3
4
  // ver.js 中定义模块的数组
4
5
  const TARGET_IDENTIFIER_NAME = 'project_apiArr'
5
6
  const MODULE_IDENTIFIER_NAME = 'module'
6
7
 
7
- function changeDeployVersion(code, pkg) {
8
+ interface PackageInfo {
9
+ name: string
10
+ version: string
11
+ grayscale?: boolean
12
+ }
13
+
14
+ function changeDeployVersion(code: string, pkg: PackageInfo): string {
8
15
  const { name, version, grayscale } = pkg
9
16
  let ast
10
17
  try {
11
18
  ast = parse(code, { filename: 'ver.js' })
12
- } catch (error) {
19
+ } catch (error: any) {
13
20
  throw new Error(`代码解析错误: ${error.message}`, { cause: error })
14
21
  }
15
22
  const keyName = grayscale ? 'grayscale' : 'main'
16
23
 
17
- /**
18
- * @return {babel.NodePath<t.VariableDeclarator> | undefined}
19
- */
20
- function findTargetDeclarator(ast) {
21
- let res
24
+ function findTargetDeclarator(ast: any): any | undefined {
25
+ let res: any | undefined
22
26
 
23
27
  traverse(ast, {
24
28
  VariableDeclarator(path) {
@@ -34,18 +38,15 @@ function changeDeployVersion(code, pkg) {
34
38
  return res
35
39
  }
36
40
 
37
- /**
38
- * @param {babel.NodePath<t.VariableDeclarator>} path
39
- * @return {babel.NodePath<t.ObjectExpression> | undefined}
40
- */
41
- function findModuleObject(path) {
42
- let res
41
+ function findModuleObject(path: any): any | undefined {
42
+ let res: any | undefined
43
43
 
44
44
  path.traverse({
45
45
  ObjectExpression(path) {
46
46
  if (
47
47
  path.node.properties.some(
48
48
  (node) =>
49
+ t.isObjectProperty(node) &&
49
50
  t.isIdentifier(node.key, { name: MODULE_IDENTIFIER_NAME }) &&
50
51
  t.isLiteral(node.value, { value: name })
51
52
  )
@@ -58,15 +59,14 @@ function changeDeployVersion(code, pkg) {
58
59
  return res
59
60
  }
60
61
 
61
- /**
62
- * @param {babel.NodePath<t.ObjectExpression>} path
63
- */
64
- function modifyModuleVersion(path) {
62
+ function modifyModuleVersion(path: any): void {
65
63
  let hasModify = false
66
64
  path.traverse({
67
65
  Property(path) {
68
- if (path.node.key.name === keyName) {
69
- path.node.value.value = version
66
+ if (t.isObjectProperty(path.node) && t.isIdentifier(path.node.key, { name: keyName })) {
67
+ if (t.isStringLiteral(path.node.value)) {
68
+ path.node.value.value = version
69
+ }
70
70
  hasModify = true
71
71
  }
72
72
  },
@@ -76,10 +76,8 @@ function changeDeployVersion(code, pkg) {
76
76
  path.node.properties.push(t.objectProperty(t.identifier(keyName), t.stringLiteral(version)))
77
77
  }
78
78
  }
79
- /**
80
- * @param {babel.NodePath<t.ObjectExpression>} path
81
- */
82
- function deleteModuleComments(path) {
79
+
80
+ function deleteModuleComments(path: any): void {
83
81
  path.traverse({
84
82
  ObjectExpression(path) {
85
83
  delete path.node.leadingComments
@@ -87,19 +85,18 @@ function changeDeployVersion(code, pkg) {
87
85
  })
88
86
  }
89
87
 
90
- /**
91
- * @param {babel.NodePath<t.VariableDeclarator>} path
92
- */
93
- function addModuleToTarget(path) {
94
- const elements = path.node.init.elements
95
- elements.splice(
96
- elements.length - 2,
97
- 0,
98
- t.objectExpression([
99
- t.objectProperty(t.identifier(MODULE_IDENTIFIER_NAME), t.stringLiteral(name)),
100
- t.objectProperty(t.identifier(keyName), t.stringLiteral(version)),
101
- ])
102
- )
88
+ function addModuleToTarget(path: any): void {
89
+ if (t.isArrayExpression(path.node.init)) {
90
+ const elements = path.node.init.elements
91
+ elements.splice(
92
+ elements.length - 2,
93
+ 0,
94
+ t.objectExpression([
95
+ t.objectProperty(t.identifier(MODULE_IDENTIFIER_NAME), t.stringLiteral(name)),
96
+ t.objectProperty(t.identifier(keyName), t.stringLiteral(version)),
97
+ ])
98
+ )
99
+ }
103
100
  }
104
101
 
105
102
  const targetPath = findTargetDeclarator(ast)
@@ -113,7 +110,12 @@ function changeDeployVersion(code, pkg) {
113
110
  }
114
111
  deleteModuleComments(targetPath)
115
112
 
116
- return transformFromAstSync(ast, undefined, { filename: 'ver.js', minified: true }).code
113
+ const result = transformFromAstSync(ast, undefined, { filename: 'ver.js', minified: true })
114
+ if (!result || !result.code) {
115
+ throw new Error('代码转换失败')
116
+ }
117
+
118
+ return result.code
117
119
  }
118
120
 
119
121
  export default changeDeployVersion
@@ -1,22 +1,17 @@
1
1
  import type { Chalk } from 'chalk'
2
- import type { Compiler, Configuration as RspackConfiguration } from '@rspack/core'
2
+ import type { Compiler, Configuration as RspackConfiguration, SwcLoaderOptions } from '@rspack/core'
3
3
  import type { Configuration as DevServerConfiguration } from '@rspack/dev-server'
4
4
 
5
5
  type ProxyConfigArray = NonNullable<DevServerConfiguration['proxy']>
6
6
 
7
- export type BabelImportPlugin = {
8
- libraryName: string
9
- libraryDirectory?: string
10
- customName?: string
11
- style?: boolean | 'css' | string
12
- }
13
-
14
7
  export type Configuration = {
15
8
  webpack?: (config: RspackConfiguration) => RspackConfiguration | undefined
16
9
  devServer?: (config: DevServerConfiguration) => DevServerConfiguration | undefined
17
10
  /**
18
11
  * webpack alias 配置,会与内置 alias 合并
19
12
  *
13
+ * 会自动读取 tsconfig 里 baseUrl + paths 的配置
14
+ *
20
15
  * @default
21
16
  * { '@': './src' }
22
17
  */
@@ -76,12 +71,10 @@ export type Configuration = {
76
71
  /**
77
72
  * 与 babel-plugin-import 类似,但是内部是 rspack 重构,libraryDirectory 默认值是 lib
78
73
  *
79
- * 如果 libraryDirectory 传 ’‘ 则必须使用 customName 来写
80
- *
81
74
  * @see https://rspack.rs/zh/guide/features/builtin-swc-loader#rspackexperimentsimport
82
- * @default [{libraryName: 'lodash', customName: 'lodash/{{member}}'}]
75
+ * @default [{libraryName: 'lodash',libraryDirectory: '',camelToDashComponentName: false}]
83
76
  */
84
- import?: BabelImportPlugin[]
77
+ import?: NonNullable<SwcLoaderOptions['rspackExperiments']>['import']
85
78
  }
86
79
  export function defineConfig(config: Configuration) {
87
80
  return config
@@ -2,6 +2,7 @@ import fs from 'fs-extra'
2
2
  import paths from '../config/paths'
3
3
  import { createRequire } from 'node:module'
4
4
  import { type Configuration } from './defineConfig'
5
+ import { resolveModule } from './resolveModule'
5
6
 
6
7
  const require = createRequire(import.meta.url)
7
8
 
@@ -21,7 +22,7 @@ export default function getOverride(): Configuration {
21
22
  override = Object.assign({}, defaultOverride)
22
23
 
23
24
  if (fs.existsSync(paths.override)) {
24
- const userOverride = require(paths.override)
25
+ const userOverride = resolveModule(require(paths.override))
25
26
  if (typeof userOverride !== 'object')
26
27
  throw new Error('格式错误,请使用 npx edu g override 生成文件')
27
28
  Object.assign(override, userOverride)
@@ -0,0 +1,3 @@
1
+ export function resolveModule(mod: any) {
2
+ return mod.default ? mod.default : mod
3
+ }