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