@qse/edu-scripts 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/CHANGELOG.md +333 -0
  2. package/README.md +105 -0
  3. package/app.d.ts +68 -0
  4. package/docs/changelog.md +5 -0
  5. package/docs/deploy.md +53 -0
  6. package/docs/feat.md +74 -0
  7. package/docs/grayscale.md +31 -0
  8. package/docs/index.md +5 -0
  9. package/docs/mode.md +42 -0
  10. package/docs/override.md +165 -0
  11. package/docs/refactor-react-16.md +40 -0
  12. package/docs/refactor.md +139 -0
  13. package/jest.config.js +195 -0
  14. package/lib/asset/dll/libcommon3-manifest.json +181 -0
  15. package/lib/asset/template/edu-app-env.d.ts.tpl +15 -0
  16. package/lib/asset/template/edu-scripts.override.js.tpl +14 -0
  17. package/lib/asset/template/page/index.class.js.tpl +24 -0
  18. package/lib/asset/template/page/index.class.tsx.tpl +10 -0
  19. package/lib/asset/template/page/index.fc.js.tpl +16 -0
  20. package/lib/asset/template/page/index.fc.tsx.tpl +9 -0
  21. package/lib/asset/template/page/index.less.tpl +3 -0
  22. package/lib/asset/template/page/logic.js.tpl +4 -0
  23. package/lib/asset/template/page/route.js.tpl +12 -0
  24. package/lib/asset/template/tailwind.config.js.tpl +11 -0
  25. package/lib/asset/template/tsconfig.json.tpl +24 -0
  26. package/lib/auto-refactor.js +175 -0
  27. package/lib/build.js +87 -0
  28. package/lib/cli.js +63 -0
  29. package/lib/commit-dist.js +117 -0
  30. package/lib/config/babel.dependencies.js +56 -0
  31. package/lib/config/babel.js +82 -0
  32. package/lib/config/paths.js +44 -0
  33. package/lib/config/plugins/postcss-safe-area.js +22 -0
  34. package/lib/config/webpackConfig.js +384 -0
  35. package/lib/config/webpackDevServerConfig.js +47 -0
  36. package/lib/deploy.js +186 -0
  37. package/lib/generator.js +155 -0
  38. package/lib/index.d.ts +1 -0
  39. package/lib/index.js +13 -0
  40. package/lib/start.js +51 -0
  41. package/lib/utils/FileSizeReporter.js +131 -0
  42. package/lib/utils/appConfig.js +44 -0
  43. package/lib/utils/beforeStart.js +73 -0
  44. package/lib/utils/changeDeployVersion.js +125 -0
  45. package/lib/utils/defineConfig.d.ts +59 -0
  46. package/lib/utils/defineConfig.js +10 -0
  47. package/lib/utils/exec.js +13 -0
  48. package/lib/utils/getConfig.js +30 -0
  49. package/lib/utils/getOverride.js +33 -0
  50. package/package.json +102 -0
  51. package/src/asset/dll/libcommon3-manifest.json +181 -0
  52. package/src/asset/template/edu-app-env.d.ts.tpl +15 -0
  53. package/src/asset/template/edu-scripts.override.js.tpl +14 -0
  54. package/src/asset/template/page/index.class.js.tpl +24 -0
  55. package/src/asset/template/page/index.class.tsx.tpl +10 -0
  56. package/src/asset/template/page/index.fc.js.tpl +16 -0
  57. package/src/asset/template/page/index.fc.tsx.tpl +9 -0
  58. package/src/asset/template/page/index.less.tpl +3 -0
  59. package/src/asset/template/page/logic.js.tpl +4 -0
  60. package/src/asset/template/page/route.js.tpl +12 -0
  61. package/src/asset/template/tailwind.config.js.tpl +11 -0
  62. package/src/asset/template/tsconfig.json.tpl +24 -0
  63. package/src/auto-refactor.js +172 -0
  64. package/src/build.js +74 -0
  65. package/src/cli.js +91 -0
  66. package/src/commit-dist.js +103 -0
  67. package/src/config/babel.dependencies.js +65 -0
  68. package/src/config/babel.js +88 -0
  69. package/src/config/paths.js +37 -0
  70. package/src/config/plugins/postcss-safe-area.js +21 -0
  71. package/src/config/webpackConfig.js +410 -0
  72. package/src/config/webpackDevServerConfig.js +44 -0
  73. package/src/deploy.js +158 -0
  74. package/src/generator.js +138 -0
  75. package/src/index.ts +1 -0
  76. package/src/start.js +46 -0
  77. package/src/utils/FileSizeReporter.js +144 -0
  78. package/src/utils/appConfig.js +35 -0
  79. package/src/utils/beforeStart.js +66 -0
  80. package/src/utils/changeDeployVersion.js +115 -0
  81. package/src/utils/defineConfig.ts +63 -0
  82. package/src/utils/exec.js +7 -0
  83. package/src/utils/getConfig.js +26 -0
  84. package/src/utils/getOverride.js +32 -0
  85. package/tsconfig.json +31 -0
@@ -0,0 +1,138 @@
1
+ const path = require('path')
2
+ const fs = require('fs-extra')
3
+ const { upperFirst, camelCase, has } = require('lodash')
4
+ const paths = require('./config/paths')
5
+ const chalk = require('chalk')
6
+
7
+ /**
8
+ * @param {string[]} args
9
+ * @returns {string}
10
+ */
11
+ const getTmpPath = (...args) => path.resolve(__dirname, 'asset', 'template', ...args)
12
+
13
+ function genFile({ source, target, modulePath, replace }) {
14
+ let content = fs.readFileSync(source, 'utf-8')
15
+
16
+ if (typeof replace === 'object') {
17
+ Object.entries(replace).forEach(([searchValue, replaceValue]) => {
18
+ content = content.replaceAll(searchValue, replaceValue)
19
+ })
20
+ }
21
+
22
+ fs.writeFileSync(path.resolve(modulePath, target), content)
23
+ }
24
+
25
+ /**
26
+ * @typedef {Object} PageArgs
27
+ * @property {string} name
28
+ * @property {boolean} [fc=false]
29
+ * @property {boolean} [ts=true]
30
+ * @property {boolean} [route=true]
31
+ *
32
+ * @param {PageArgs} args
33
+ */
34
+ async function generatorPage(args) {
35
+ const tmpPath = getTmpPath('page', 'index.[type].[ext].tpl')
36
+ const lessPath = tmpPath.replace('[type].[ext]', 'less')
37
+
38
+ const moduleName = camelCase(args.name)
39
+ const ModuleName = upperFirst(moduleName)
40
+ const type = args.fc ? 'fc' : 'class'
41
+ const ext = fs.existsSync(paths.tsconfig) ? (args.ts ? 'tsx' : 'js') : 'js'
42
+
43
+ const jsPath = tmpPath.replace('[type]', type).replace('[ext]', ext)
44
+ const routePath = tmpPath.replace('index.[type].[ext]', 'route.js')
45
+ const logicPath = tmpPath.replace('index.[type].[ext]', 'logic.js')
46
+
47
+ const modulePath = path.resolve(paths.pages, moduleName)
48
+
49
+ const ModuleFileName = args.route ? ModuleName : 'index'
50
+ const ModuleLogicName = args.route ? ModuleName + 'Logic' : 'logic'
51
+
52
+ const replace = { ModuleName, moduleName, ModuleFileName, ModuleLogicName }
53
+
54
+ fs.ensureDirSync(modulePath)
55
+ genFile({ source: jsPath, target: `${ModuleFileName}.${ext}`, modulePath, replace })
56
+ genFile({ source: lessPath, target: `${ModuleFileName}.less`, modulePath, replace })
57
+ if (type === 'class' && ext === 'js') {
58
+ genFile({
59
+ source: logicPath,
60
+ target: `${ModuleLogicName}.${ext.slice(0, 2)}`,
61
+ modulePath,
62
+ replace,
63
+ })
64
+ }
65
+ if (args.route) {
66
+ genFile({ source: routePath, target: `index.${ext.slice(0, 2)}`, modulePath, replace })
67
+ }
68
+
69
+ console.log(
70
+ chalk.green(
71
+ [
72
+ `生成完毕:`,
73
+ `import ${ModuleName} from '@/pages/${moduleName}'`,
74
+ `const ${ModuleName} = lazy(() => import(/* webpackChunkName: '${moduleName}' */ '@/pages/${moduleName}'))`,
75
+ `{ path: '/${moduleName}', element: <${ModuleName} /> },`,
76
+ ].join('\n')
77
+ )
78
+ )
79
+ }
80
+
81
+ async function generatorOverride() {
82
+ if (fs.existsSync(paths.override)) {
83
+ console.log(chalk.red(`文件已存在 ${paths.override}`))
84
+ process.exit(0)
85
+ }
86
+
87
+ fs.copySync(getTmpPath('edu-scripts.override.js.tpl'), paths.override)
88
+
89
+ console.log(chalk.green(`成功生成 ${paths.override}`))
90
+ }
91
+
92
+ async function generatorTsconfig() {
93
+ if (fs.existsSync(paths.tsconfig)) {
94
+ console.log(chalk.red(`文件已存在 ${paths.tsconfig}`))
95
+ process.exit(0)
96
+ }
97
+
98
+ fs.copySync(getTmpPath('tsconfig.json.tpl'), paths.tsconfig)
99
+ fs.copySync(getTmpPath('edu-app-env.d.ts.tpl'), paths.eduAppEnv)
100
+
101
+ console.log(chalk.green(`成功生成 ${paths.tsconfig}`))
102
+ }
103
+
104
+ async function generatorTailwind() {
105
+ if (fs.existsSync(paths.tailwind)) {
106
+ console.log(chalk.red(`文件已存在 ${paths.tailwind}`))
107
+ process.exit(0)
108
+ }
109
+
110
+ fs.copySync(getTmpPath('tailwind.config.js.tpl'), paths.tailwind)
111
+
112
+ const code = ['@tailwind base;', '@tailwind components;', '@tailwind utilities;'].join('\n')
113
+
114
+ const globalLessFile = paths.resolveApp('src', 'index.less')
115
+ if (fs.existsSync(globalLessFile)) {
116
+ const content = fs.readFileSync(globalLessFile, 'utf-8')
117
+ if (!content.includes('@tailwind base')) {
118
+ fs.writeFileSync(globalLessFile, [code, content].join('\n'))
119
+ }
120
+
121
+ console.log(chalk.green(`成功生成 ${paths.tailwind}`))
122
+
123
+ return
124
+ }
125
+
126
+ console.log(
127
+ chalk.green(
128
+ [`成功生成 ${paths.tailwind}`, '', '添加以下代码到入口处的 index.less 中', code].join('\n')
129
+ )
130
+ )
131
+ }
132
+
133
+ module.exports = {
134
+ page: generatorPage,
135
+ override: generatorOverride,
136
+ ts: generatorTsconfig,
137
+ tailwind: generatorTailwind,
138
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { defineConfig } from './utils/defineConfig'
package/src/start.js ADDED
@@ -0,0 +1,46 @@
1
+ process.env.NODE_ENV = 'development'
2
+ process.env.BABEL_ENV = 'development'
3
+ process.env.BROWSERSLIST = 'chrome >= 70'
4
+ process.env.WEBPACK_DEV_SERVER_BASE_PORT = '3000'
5
+
6
+ // Makes the script crash on unhandled rejections instead of silently
7
+ // ignoring them. In the future, promise rejections that are not handled will
8
+ // terminate the Node.js process with a non-zero exit code.
9
+ process.on('unhandledRejection', (err) => {
10
+ throw err
11
+ })
12
+
13
+ const WebpackDevServer = require('webpack-dev-server')
14
+ const webpack = require('webpack')
15
+ const getConfig = require('./utils/getConfig')
16
+ const chalk = require('chalk')
17
+
18
+ process.env.WDS_SOCKET_HOST = WebpackDevServer.internalIPSync('v4') || '127.0.0.1'
19
+
20
+ module.exports = async function start(args) {
21
+ const basePort = process.env.WEBPACK_DEV_SERVER_BASE_PORT
22
+ const port = await WebpackDevServer.getFreePort(args.port || process.env.PORT)
23
+ if (!(args.port || process.env.PORT) && +port !== +basePort) {
24
+ console.log(chalk.bgYellow(`${basePort} 端口已被占用,现切换到 ${port} 端口运行`))
25
+ }
26
+ process.env.WDS_SOCKET_PORT = port
27
+ args.port = port
28
+
29
+ const compiler = webpack(getConfig(args))
30
+ const devServer = new WebpackDevServer(compiler.options.devServer, compiler)
31
+ devServer.start()
32
+ ;[('SIGINT', 'SIGTERM')].forEach(function (sig) {
33
+ process.on(sig, function () {
34
+ devServer.stop()
35
+ process.exit()
36
+ })
37
+ })
38
+
39
+ if (process.env.CI !== 'true') {
40
+ // Gracefully exit when stdin ends
41
+ process.stdin.on('end', function () {
42
+ devServer.stop()
43
+ process.exit()
44
+ })
45
+ }
46
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Copyright (c) 2015-present, Facebook, Inc.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+ var fs = require('fs')
9
+ var path = require('path')
10
+ var chalk = require('chalk')
11
+ var filesize = require('filesize')
12
+ var recursive = require('recursive-readdir')
13
+ var stripAnsi = require('strip-ansi')
14
+ var gzipSize = require('gzip-size').sync
15
+
16
+ function canReadAsset(asset) {
17
+ return (
18
+ /\.(js|css)$/.test(asset) &&
19
+ !/service-worker\.js/.test(asset) &&
20
+ !/precache-manifest\.[0-9a-f]+\.js/.test(asset)
21
+ )
22
+ }
23
+
24
+ // Prints a detailed summary of build files.
25
+ 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])
35
+ .map((stats) =>
36
+ stats
37
+ .toJson({ all: false, assets: true })
38
+ .assets.filter((asset) => canReadAsset(asset.name))
39
+ .map((asset) => {
40
+ var fileContents = fs.readFileSync(path.join(root, asset.name))
41
+ var size = gzipSize(fileContents)
42
+ var previousSize = sizes[removeFileNameHash(root, asset.name)]
43
+ var difference = getDifferenceLabel(size, previousSize)
44
+ return {
45
+ folder: path.join(path.basename(buildFolder), path.dirname(asset.name)),
46
+ name: path.basename(asset.name),
47
+ size: size,
48
+ sizeLabel: filesize(size) + (difference ? ' (' + difference + ')' : ''),
49
+ }
50
+ })
51
+ )
52
+ .reduce((single, all) => all.concat(single), [])
53
+ assets.sort((a, b) => b.size - a.size)
54
+
55
+ // move main file to first
56
+ var mainAssetIdx = assets.findIndex((asset) => /_\d+\.\d+\.\d+\./.test(asset.name))
57
+ assets.unshift(assets.splice(mainAssetIdx, 1)[0])
58
+
59
+ var longestSizeLabelLength = Math.max.apply(
60
+ null,
61
+ assets.map((a) => stripAnsi(a.sizeLabel).length)
62
+ )
63
+ var suggestBundleSplitting = false
64
+ assets.forEach((asset) => {
65
+ var sizeLabel = asset.sizeLabel
66
+ var sizeLength = stripAnsi(sizeLabel).length
67
+ if (sizeLength < longestSizeLabelLength) {
68
+ var rightPadding = ' '.repeat(longestSizeLabelLength - sizeLength)
69
+ sizeLabel += rightPadding
70
+ }
71
+ var isMainBundle = /_\d+\.\d+\.\d+\./.test(asset.name)
72
+ var maxRecommendedSize = isMainBundle ? maxBundleGzipSize : maxChunkGzipSize
73
+ var isLarge = maxRecommendedSize && asset.size > maxRecommendedSize
74
+ if (isLarge && path.extname(asset.name) === '.js') {
75
+ suggestBundleSplitting = true
76
+ }
77
+ console.log(
78
+ ' ' +
79
+ (isLarge ? chalk.yellow(sizeLabel) : sizeLabel) +
80
+ ' ' +
81
+ chalk.dim(asset.folder + path.sep) +
82
+ chalk.cyan(asset.name)
83
+ )
84
+ if (isMainBundle) {
85
+ console.log('')
86
+ }
87
+ })
88
+ if (suggestBundleSplitting) {
89
+ console.log()
90
+ console.log(chalk.yellow('产物大小明显大于推荐的大小 (主文件 30k, chunk 1M, 黄色标注为偏大)'))
91
+ console.log(chalk.yellow('考虑下使用代码分割解决'))
92
+ console.log(chalk.yellow('也可以使用 npm run analyze 命令分析产物'))
93
+ }
94
+ }
95
+
96
+ function removeFileNameHash(buildFolder, fileName) {
97
+ return fileName
98
+ .replace(buildFolder, '')
99
+ .replace(/\\/g, '/')
100
+ .replace(/\/\d+\.\d+\.\d+\//, '/')
101
+ .replace(/\/?(.*)(\.[0-9a-f]+)(\.chunk)?(\.js|\.css)/, (match, p1, p2, p3, p4) => p1 + p4)
102
+ }
103
+
104
+ // Input: 1024, 2048
105
+ // Output: "(+1 KB)"
106
+ function getDifferenceLabel(currentSize, previousSize) {
107
+ var FIFTY_KILOBYTES = 1024 * 50
108
+ var difference = currentSize - previousSize
109
+ var fileSize = !Number.isNaN(difference) ? filesize(difference) : 0
110
+ if (difference >= FIFTY_KILOBYTES) {
111
+ return chalk.red('+' + fileSize)
112
+ } else if (difference < FIFTY_KILOBYTES && difference > 0) {
113
+ return chalk.yellow('+' + fileSize)
114
+ } else if (difference < 0) {
115
+ return chalk.green(fileSize)
116
+ } else {
117
+ return ''
118
+ }
119
+ }
120
+
121
+ function measureFileSizesBeforeBuild(buildFolder) {
122
+ return new Promise((resolve) => {
123
+ recursive(buildFolder, (err, fileNames) => {
124
+ var sizes
125
+ if (!err && fileNames) {
126
+ sizes = fileNames.filter(canReadAsset).reduce((memo, fileName) => {
127
+ var contents = fs.readFileSync(fileName)
128
+ var key = removeFileNameHash(buildFolder, fileName)
129
+ memo[key] = gzipSize(contents)
130
+ return memo
131
+ }, {})
132
+ }
133
+ resolve({
134
+ root: buildFolder,
135
+ sizes: sizes || {},
136
+ })
137
+ })
138
+ })
139
+ }
140
+
141
+ module.exports = {
142
+ measureFileSizesBeforeBuild: measureFileSizesBeforeBuild,
143
+ printFileSizesAfterBuild: printFileSizesAfterBuild,
144
+ }
@@ -0,0 +1,35 @@
1
+ const paths = require('../config/paths')
2
+ const appPkg = require(paths.package)
3
+ const fs = require('fs')
4
+
5
+ const edu = appPkg.edu || {}
6
+
7
+ if (edu.single || edu.mainProject) {
8
+ if (edu.single) {
9
+ edu.mode = 'single'
10
+ delete edu.single
11
+ }
12
+ if (edu.mainProject) {
13
+ edu.mode = 'main'
14
+ delete edu.mainProject
15
+ }
16
+ fs.writeFileSync(paths.package, JSON.stringify(appPkg, null, 2), 'utf-8')
17
+ }
18
+
19
+ const appConfig = {
20
+ /** @type {'main'|'single'} */
21
+ get mode() {
22
+ return edu.mode
23
+ },
24
+ get mainProject() {
25
+ return this.mode === 'main'
26
+ },
27
+ get single() {
28
+ return this.mode === 'single'
29
+ },
30
+ get grayscale() {
31
+ return !!edu.grayscale
32
+ },
33
+ }
34
+
35
+ module.exports = appConfig
@@ -0,0 +1,66 @@
1
+ const fs = require('fs')
2
+ const paths = require('../config/paths')
3
+ const chalk = require('chalk')
4
+ const pkg = require('../../package.json')
5
+ const updateNotifier = require('update-notifier')
6
+
7
+ const notifier = updateNotifier({
8
+ pkg,
9
+ shouldNotifyInNpmScript: true,
10
+ updateCheckInterval: 1000 * 60, // 时刻保持更新
11
+ })
12
+ if (notifier.update && ['minor', 'major'].includes(notifier.update.type)) {
13
+ const message =
14
+ '发现新版本 ' +
15
+ chalk.dim('{currentVersion}') +
16
+ chalk.reset(' → ') +
17
+ chalk.green('{latestVersion}') +
18
+ ' \n运行 ' +
19
+ chalk.cyan('{updateCommand}') +
20
+ ' 进行更新\n\n' +
21
+ chalk.bold.red('请始终保持最新版本\n') +
22
+ '更新日志: ' +
23
+ chalk.green(`${pkg.homepage}#/changelog`)
24
+ notifier.notify({ message })
25
+ process.exit(1)
26
+ }
27
+
28
+ const appPkg = require(paths.package)
29
+ const appConfig = require('./appConfig')
30
+ switch (appConfig.mode) {
31
+ case 'main':
32
+ console.log(chalk.bgMagenta('正在使用教育主工程模式'))
33
+ break
34
+ case 'single':
35
+ console.log(chalk.bgMagenta('正在使用独立项目模式'))
36
+ break
37
+ default:
38
+ console.log(chalk.bgMagenta('正在使用教育集成模式'))
39
+ break
40
+ }
41
+
42
+ if (appConfig.grayscale) {
43
+ console.log(chalk.bgYellow('正在使用灰度测试模式'))
44
+ }
45
+
46
+ if (!(appConfig.single || appConfig.mainProject)) {
47
+ if (fs.existsSync(paths.static)) {
48
+ console.log(chalk.bgYellow('教育集成工程不能含有 public/static, 现已自动删除'))
49
+ try {
50
+ fs.rmSync(paths.static, { recursive: true })
51
+ } catch (e) {}
52
+ }
53
+ }
54
+
55
+ if (!fs.existsSync(paths.public) && !process.argv.includes('auto-refactor')) {
56
+ console.log(chalk.red(`public 文件夹不存在,请先按文档改造项目`))
57
+ console.log(`文档: ${chalk.underline(pkg.homepage)}`)
58
+ console.log(`\n使用 ${chalk.green('npx edu-scripts auto-refactor')} 自动改造\n`)
59
+ process.exit(0)
60
+ }
61
+
62
+ if (appPkg.browserslist) {
63
+ console.log(chalk.yellow('已删除 package.json 中 browserslist,该值由内部自动控制\n'))
64
+ delete appPkg.browserslist
65
+ fs.writeFileSync(paths.package, JSON.stringify(appPkg, null, 2), 'utf-8')
66
+ }
@@ -0,0 +1,115 @@
1
+ const t = require('@babel/types')
2
+ const { parse } = require('@babel/parser')
3
+ const generate = require('@babel/generator').default
4
+ const traverse = require('@babel/traverse').default
5
+ const { format } = require('prettier')
6
+
7
+ // ver.js 中定义模块的数组
8
+ const TARGET_IDENTIFIER_NAME = 'project_apiArr'
9
+ const MODULE_IDENTIFIER_NAME = 'module'
10
+
11
+ function changeDeployVersion(code, pkg) {
12
+ const { name, version, grayscale } = pkg
13
+ let ast
14
+ try {
15
+ ast = parse(code)
16
+ } catch (error) {
17
+ throw new Error(`代码解析错误: ${error.message}`)
18
+ }
19
+ const keyName = grayscale ? 'grayscale' : 'main'
20
+
21
+ /**
22
+ * @return {babel.NodePath<t.VariableDeclarator> | undefined}
23
+ */
24
+ function findTargetDeclarator(ast) {
25
+ let res
26
+
27
+ traverse(ast, {
28
+ VariableDeclarator(path) {
29
+ if (
30
+ t.isIdentifier(path.node.id, { name: TARGET_IDENTIFIER_NAME }) &&
31
+ t.isArrayExpression(path.node.init)
32
+ ) {
33
+ res = path
34
+ }
35
+ },
36
+ })
37
+
38
+ return res
39
+ }
40
+
41
+ /**
42
+ * @param {babel.NodePath<t.VariableDeclarator>} path
43
+ * @return {babel.NodePath<t.ObjectExpression> | undefined}
44
+ */
45
+ function findModuleObject(path) {
46
+ let res
47
+
48
+ path.traverse({
49
+ ObjectExpression(path) {
50
+ if (
51
+ path.node.properties.some(
52
+ (node) =>
53
+ t.isIdentifier(node.key, { name: MODULE_IDENTIFIER_NAME }) &&
54
+ t.isLiteral(node.value, { value: name })
55
+ )
56
+ ) {
57
+ res = path
58
+ }
59
+ },
60
+ })
61
+
62
+ return res
63
+ }
64
+
65
+ /**
66
+ * @param {babel.NodePath<t.ObjectExpression>} path
67
+ */
68
+ function modifyModuleVersion(path) {
69
+ let hasModify = false
70
+ path.traverse({
71
+ Property(path) {
72
+ if (path.node.key.name === keyName) {
73
+ path.node.value.value = version
74
+ hasModify = true
75
+ }
76
+ },
77
+ })
78
+
79
+ if (!hasModify) {
80
+ path.node.properties.push(t.objectProperty(t.identifier(keyName), t.stringLiteral(version)))
81
+ }
82
+ }
83
+
84
+ /**
85
+ * @param {babel.NodePath<t.VariableDeclarator>} path
86
+ */
87
+ function addModuleToTarget(path) {
88
+ const elements = path.node.init.elements
89
+ elements.splice(
90
+ elements.length - 2,
91
+ 0,
92
+ t.objectExpression([
93
+ t.objectProperty(t.identifier(MODULE_IDENTIFIER_NAME), t.stringLiteral(name)),
94
+ t.objectProperty(t.identifier(keyName), t.stringLiteral(version)),
95
+ ])
96
+ )
97
+ }
98
+
99
+ const targetPath = findTargetDeclarator(ast)
100
+ if (!targetPath) throw new Error(`ver.js 不合规范,未找到参数 ${TARGET_IDENTIFIER_NAME}`)
101
+
102
+ const moduleObjPath = findModuleObject(targetPath)
103
+ if (moduleObjPath) {
104
+ modifyModuleVersion(moduleObjPath)
105
+ } else {
106
+ addModuleToTarget(targetPath)
107
+ }
108
+
109
+ return format(generate(ast, { minified: true }).code, {
110
+ parser: 'babel',
111
+ printWidth: 120,
112
+ })
113
+ }
114
+
115
+ module.exports = changeDeployVersion
@@ -0,0 +1,63 @@
1
+ import type { Configuration } from 'webpack'
2
+ import type { Configuration as DevServerConfiguration, ProxyConfigArray } from 'webpack-dev-server'
3
+
4
+ export type BabelImportPlugin = [
5
+ 'import',
6
+ {
7
+ libraryName: string
8
+ libraryDirectory: string
9
+ camel2DashComponentName?: boolean
10
+ style?: boolean | 'css' | ((name: string) => string)
11
+ },
12
+ string
13
+ ]
14
+ export type BabelConfig = {
15
+ presets: any[]
16
+ plugins: (string | BabelImportPlugin | any[])[]
17
+ }
18
+
19
+ export type Config = {
20
+ webpack?: (config: Configuration) => Configuration | undefined
21
+ devServer?: (config: DevServerConfiguration) => DevServerConfiguration | undefined
22
+ babel?: (config: BabelConfig, type: 'src' | 'node_modules') => any | undefined
23
+ /**
24
+ * webpack alias 配置,会与内置 alias 合并
25
+ *
26
+ * @default
27
+ * { '@': './src' }
28
+ */
29
+ alias?: Record<string, string>
30
+ /**
31
+ * webpack externals 配置,会与内置 externals 合并
32
+ */
33
+ externals?: Record<string, string>
34
+ /**
35
+ * terser pure_funcs,esbuild pure 配置,传 [] 可以不清空 console.log
36
+ * @default ['console.log']
37
+ */
38
+ pure_funcs?: string[]
39
+ /**
40
+ * 编译 node_modules 下文件
41
+ *
42
+ * 仅在 development 环境下生效。production 一定会编译 node_modules
43
+ * @default true
44
+ */
45
+ transformNodeModules?: boolean
46
+ /**
47
+ * 指定压缩工具,esbuild 比 terser 快 20-40 倍,实际压缩率差 10%左右
48
+ *
49
+ * @default 'esbuild'
50
+ */
51
+ minify?: boolean | 'terser' | 'esbuild'
52
+ /** 自定义全局参数 */
53
+ define?: Record<string, any>
54
+ /**
55
+ * webpack-dev-server proxy
56
+ * @default /api -> /qsxxwapdev/api
57
+ */
58
+ proxy?: ProxyConfigArray
59
+ extraPostCSSPlugins?: any[]
60
+ }
61
+ export function defineConfig(config: Config) {
62
+ return config
63
+ }
@@ -0,0 +1,7 @@
1
+ const cp = require('child_process')
2
+ const exec = (cmd) => {
3
+ console.log(`> ${cmd}`)
4
+ return cp.execSync(cmd, { stdio: 'inherit', encoding: 'utf-8' })
5
+ }
6
+
7
+ module.exports = exec
@@ -0,0 +1,26 @@
1
+ const getWebpackConfig = require('../config/webpackConfig')
2
+ const getWebpackDevServerConfig = require('../config/webpackDevServerConfig')
3
+ const getOverride = require('./getOverride')
4
+
5
+ function getConfig(args) {
6
+ const override = getOverride()
7
+
8
+ let webpackConfig = getWebpackConfig(args, override)
9
+
10
+ if (override.webpack) {
11
+ webpackConfig = override.webpack(webpackConfig) || webpackConfig
12
+ }
13
+
14
+ if (process.env.NODE_ENV === 'development') {
15
+ let devServerConfig = getWebpackDevServerConfig(args, override)
16
+
17
+ if (override.devServer) {
18
+ devServerConfig = override.devServer(devServerConfig) || devServerConfig
19
+ }
20
+ webpackConfig.devServer = devServerConfig
21
+ }
22
+
23
+ return webpackConfig
24
+ }
25
+
26
+ module.exports = getConfig
@@ -0,0 +1,32 @@
1
+ const fs = require('fs')
2
+ const paths = require('../config/paths')
3
+
4
+ /** @type {import('./defineConfig').Config} */
5
+ const defaultOverride = {
6
+ transformNodeModules: true,
7
+ minify: 'esbuild',
8
+ proxy: [],
9
+ extraPostCSSPlugins: [],
10
+ }
11
+
12
+ let override = null
13
+
14
+ /**
15
+ * @return {import('./defineConfig').Config}
16
+ */
17
+ module.exports = function getOverride() {
18
+ if (override) return override
19
+
20
+ override = Object.assign({}, defaultOverride)
21
+
22
+ if (fs.existsSync(paths.override)) {
23
+ const userOverride = require(paths.override)
24
+ if (typeof userOverride !== 'object')
25
+ throw new Error('格式错误,请使用 npx edu g override 生成文件')
26
+ Object.assign(override, userOverride)
27
+ }
28
+
29
+ if (override.minify === true) override.minify = defaultOverride.minify
30
+
31
+ return override
32
+ }