hw-weapp-compiler 1.0.21 → 1.0.23
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/README.md +44 -87
- package/build/config/getConfig.js +0 -3
- package/build/dev.js +1 -7
- package/build/plugin/index.js +6 -32
- package/build/prod.js +2 -2
- package/build/utils/createCompilerHandler.js +1 -21
- package/build/utils/getWxmlAssets.js +56 -65
- package/build/utils/upload.js +80 -105
- package/build/webpack.config.js +16 -18
- package/index.js +24 -13
- package/package.json +6 -8
- package/test-support/mini-program.js +0 -49
- package/test-support/project.js +0 -25
- package/test-support/webpack.js +0 -80
package/README.md
CHANGED
|
@@ -1,119 +1,76 @@
|
|
|
1
|
-
#
|
|
1
|
+
# weapp-compiler
|
|
2
2
|
|
|
3
|
-
基于 webpack 5 的原生微信小程序构建工具,支持 JavaScript、Less、WXML、WXS、分包、npm 组件和可选的 OBS/OSS 静态资源上传。
|
|
4
3
|
|
|
5
|
-
##
|
|
4
|
+
## 版本改动
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
- 必须存在 `src/app.json` 和 `src/app.js`
|
|
6
|
+
1. **OpenSSL 3.0 兼容**: Node.js >= 17 时自动注入 `--openssl-legacy-provider`
|
|
7
|
+
2. **wxml-loader 空值守卫**: `loadModule` 失败时不再崩溃
|
|
10
8
|
|
|
11
9
|
## 安装
|
|
12
10
|
|
|
13
11
|
```bash
|
|
14
|
-
npm
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
升级版本后如果出现 `Cannot find module`,请删除业务项目中旧的安装产物并重新执行 `npm install`,确保传递依赖与 lockfile 同步。
|
|
18
|
-
|
|
19
|
-
在业务项目的 `package.json` 中添加:
|
|
20
|
-
|
|
21
|
-
```json
|
|
22
|
-
{
|
|
23
|
-
"scripts": {
|
|
24
|
-
"dev": "hw-weapp dev",
|
|
25
|
-
"build": "hw-weapp build"
|
|
26
|
-
}
|
|
27
|
-
}
|
|
12
|
+
npm i weapp-compiler@2.x -D
|
|
28
13
|
```
|
|
29
14
|
|
|
30
15
|
## 使用
|
|
31
16
|
|
|
32
17
|
```bash
|
|
33
|
-
#
|
|
34
|
-
npm run dev
|
|
35
|
-
|
|
36
|
-
#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
#
|
|
41
|
-
npm run build
|
|
42
|
-
npm run build -- --development
|
|
43
|
-
npm run build -- --simulation
|
|
18
|
+
# 开发调试
|
|
19
|
+
npm run dev # 测试环境
|
|
20
|
+
npm run dev -s # 预发环境
|
|
21
|
+
npm run dev -p # 生产环境
|
|
22
|
+
|
|
23
|
+
# 生产发布
|
|
24
|
+
npm run build -d # 测试环境
|
|
25
|
+
npm run build -s # 预发环境
|
|
26
|
+
npm run build # 生产环境
|
|
44
27
|
```
|
|
45
28
|
|
|
46
|
-
可用选项:
|
|
47
|
-
|
|
48
|
-
- `-d, --development`:测试环境
|
|
49
|
-
- `-s, --simulation`:预发环境
|
|
50
|
-
- `-p, --production`:生产环境
|
|
51
|
-
- `-a, --analyzer`:打开 webpack bundle analyzer
|
|
52
|
-
- `-q, --quiet`:关闭 webpack 进度输出
|
|
53
|
-
|
|
54
|
-
最终环境会通过 `process.env.BUILD_ENV` 注入代码,并记录到 `dist/weapp.env.json`。
|
|
55
|
-
|
|
56
29
|
## 配置
|
|
57
30
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```js
|
|
61
|
-
module.exports = {};
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
完整示例:
|
|
31
|
+
项目根目录下创建 `.weapp.js`:
|
|
65
32
|
|
|
66
33
|
```js
|
|
67
34
|
const path = require('path');
|
|
68
35
|
|
|
69
36
|
module.exports = {
|
|
37
|
+
// 路径别名
|
|
70
38
|
alias: {
|
|
71
39
|
'@utils': path.resolve(__dirname, 'src/utils'),
|
|
40
|
+
'@config': path.resolve(__dirname, 'src/config'),
|
|
72
41
|
},
|
|
42
|
+
// 资源公共路径
|
|
73
43
|
publicPath: 'https://cdn.example.com/weapp/',
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
dir: 'weapp',
|
|
95
|
-
}
|
|
96
|
-
: undefined,
|
|
44
|
+
// 要同步的目录
|
|
45
|
+
copyFiles: [{
|
|
46
|
+
from: 'images',
|
|
47
|
+
to: 'images',
|
|
48
|
+
}],
|
|
49
|
+
// OBS 或 OSS 配置(二选一)
|
|
50
|
+
obsConfig: {
|
|
51
|
+
access_key_id: 'XXX',
|
|
52
|
+
secret_access_key: 'XXX',
|
|
53
|
+
server: 'XXX',
|
|
54
|
+
bucket: 'XXX',
|
|
55
|
+
dir: 'weapp',
|
|
56
|
+
},
|
|
57
|
+
ossConfig: {
|
|
58
|
+
region: '<Your region>',
|
|
59
|
+
accessKeyId: '<Your AccessKeyId>',
|
|
60
|
+
accessKeySecret: '<Your AccessKeySecret>',
|
|
61
|
+
bucket: '<Your Bucket>',
|
|
62
|
+
dir: 'weapp',
|
|
63
|
+
},
|
|
97
64
|
};
|
|
98
65
|
```
|
|
99
66
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
## 开发监听限制
|
|
103
|
-
|
|
104
|
-
修改已有文件可以正常增量构建。开发监听期间新增页面、组件、分包,或后创建同名 `.less`、`.wxss`、`.wxml`、`.json` 文件后,需要重新启动 `dev`。生产 `build` 每次都会重新读取完整项目配置。
|
|
105
|
-
|
|
106
|
-
## 微信开发者工具配置
|
|
107
|
-
|
|
108
|
-
如果资源使用 CDN 上传,可以在 `src/project.config.json` 中忽略本地产物:
|
|
67
|
+
在 `project.config.json` 中忽略构建产物:
|
|
109
68
|
|
|
110
69
|
```json
|
|
111
|
-
{
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
]
|
|
117
|
-
}
|
|
70
|
+
"packOptions": {
|
|
71
|
+
"ignore": [
|
|
72
|
+
{ "type": "folder", "value": "assets" },
|
|
73
|
+
{ "type": "regexp", "value": "\\.map$" }
|
|
74
|
+
]
|
|
118
75
|
}
|
|
119
76
|
```
|
package/build/dev.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
const webpack = require('webpack');
|
|
2
|
-
const chalk = require('chalk');
|
|
3
2
|
const { ENV } = require('./config/constants');
|
|
4
3
|
const webpackConfig = require('./webpack.config');
|
|
5
4
|
const { createCompilerHandler } = require('./utils/createCompilerHandler');
|
|
@@ -21,7 +20,7 @@ module.exports = async (opts) => {
|
|
|
21
20
|
),
|
|
22
21
|
);
|
|
23
22
|
|
|
24
|
-
|
|
23
|
+
compiler.watch(
|
|
25
24
|
{
|
|
26
25
|
aggregateTimeout: 300,
|
|
27
26
|
poll: false,
|
|
@@ -29,9 +28,4 @@ module.exports = async (opts) => {
|
|
|
29
28
|
},
|
|
30
29
|
createCompilerHandler('dev'),
|
|
31
30
|
);
|
|
32
|
-
|
|
33
|
-
if (!opts || opts.quiet !== true) {
|
|
34
|
-
console.log(chalk.cyan('dev 正在监听文件变化'));
|
|
35
|
-
}
|
|
36
|
-
return watching;
|
|
37
31
|
};
|
package/build/plugin/index.js
CHANGED
|
@@ -9,25 +9,10 @@ const { RawSource } = require('webpack-sources');
|
|
|
9
9
|
|
|
10
10
|
const { addToUploadQueue } = require('../utils/upload');
|
|
11
11
|
const { compatiblePath } = require('../utils/compatiblePath');
|
|
12
|
-
const {
|
|
12
|
+
const { PATTERNS, ASSET_EXT_PATTERN, ASSETS_DIR } = require('../config/constants');
|
|
13
13
|
|
|
14
14
|
const pluginName = 'WeappCompilerPlugin';
|
|
15
15
|
|
|
16
|
-
async function uploadCompilationAssets(stats, compiler, assets, upload = addToUploadQueue) {
|
|
17
|
-
if (stats.hasErrors() || assets.length === 0) {
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
try {
|
|
22
|
-
await upload(assets);
|
|
23
|
-
} catch (error) {
|
|
24
|
-
if (compiler.options.mode === ENV.PROD) {
|
|
25
|
-
throw error;
|
|
26
|
-
}
|
|
27
|
-
compiler.getInfrastructureLogger(pluginName).error(error);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
16
|
/**
|
|
32
17
|
* 检测产物中存在哪些公共模块和分包模块
|
|
33
18
|
*/
|
|
@@ -91,19 +76,13 @@ function injectGlobalModules(content, assetName, flags) {
|
|
|
91
76
|
|
|
92
77
|
// runtime 暴露到全局
|
|
93
78
|
if (assetName === 'runtime.js') {
|
|
94
|
-
const runtimeCachePattern = /\b(var|let|const)\s+__webpack_module_cache__\s*=\s*\{\};/;
|
|
95
|
-
if (!runtimeCachePattern.test(result)) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
'无法注入微信小程序 runtime:当前 webpack 产物中缺少 module cache 标记,请检查 webpack 版本',
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
79
|
result = result.replace(
|
|
101
|
-
|
|
102
|
-
|
|
80
|
+
'var __webpack_module_cache__ = {};',
|
|
81
|
+
`
|
|
103
82
|
if (!global.__webpack_module_cache__) {
|
|
104
83
|
global.__webpack_module_cache__ = {};
|
|
105
84
|
}
|
|
106
|
-
|
|
85
|
+
var __webpack_module_cache__ = global.__webpack_module_cache__;
|
|
107
86
|
global.__webpack_require__ = __webpack_require__;
|
|
108
87
|
`,
|
|
109
88
|
);
|
|
@@ -188,10 +167,9 @@ class WeappPlugin {
|
|
|
188
167
|
apply(compiler) {
|
|
189
168
|
let obsAssets = [];
|
|
190
169
|
|
|
191
|
-
compiler.hooks.done.
|
|
192
|
-
|
|
170
|
+
compiler.hooks.done.tap(pluginName, () => {
|
|
171
|
+
addToUploadQueue([...obsAssets]);
|
|
193
172
|
obsAssets = [];
|
|
194
|
-
await uploadCompilationAssets(stats, compiler, assets);
|
|
195
173
|
});
|
|
196
174
|
|
|
197
175
|
compiler.hooks.compilation.tap(pluginName, (compilation) => {
|
|
@@ -250,7 +228,3 @@ class WeappPlugin {
|
|
|
250
228
|
}
|
|
251
229
|
|
|
252
230
|
module.exports = WeappPlugin;
|
|
253
|
-
module.exports._internals = {
|
|
254
|
-
injectGlobalModules,
|
|
255
|
-
uploadCompilationAssets,
|
|
256
|
-
};
|
package/build/prod.js
CHANGED
|
@@ -3,7 +3,7 @@ const TerserPlugin = require('terser-webpack-plugin');
|
|
|
3
3
|
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
|
4
4
|
const { ENV } = require('./config/constants');
|
|
5
5
|
const webpackConfig = require('./webpack.config');
|
|
6
|
-
const { createCompilerHandler
|
|
6
|
+
const { createCompilerHandler } = require('./utils/createCompilerHandler');
|
|
7
7
|
|
|
8
8
|
module.exports = async (opts) => {
|
|
9
9
|
// 生产环境禁用 filesystem cache,与 dev 同源:output.clean 后复用旧缓存会导致副作用产物漏生成,
|
|
@@ -34,5 +34,5 @@ module.exports = async (opts) => {
|
|
|
34
34
|
),
|
|
35
35
|
);
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
compiler.run(createCompilerHandler('build'));
|
|
38
38
|
};
|
|
@@ -48,26 +48,6 @@ function createCompilerHandler(label, { failOnError = label === 'build' } = {})
|
|
|
48
48
|
};
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
/**
|
|
52
|
-
* 运行一次 webpack 编译,并等待异步 done 钩子及 compiler.close 完成。
|
|
53
|
-
* 上传任务挂在 done.tapPromise 上,生产命令必须等待这里结束,才能可靠输出上传错误。
|
|
54
|
-
*/
|
|
55
|
-
function runCompilerOnce(compiler, handler) {
|
|
56
|
-
return new Promise((resolve, reject) => {
|
|
57
|
-
compiler.run((runError, stats) => {
|
|
58
|
-
compiler.close((closeError) => {
|
|
59
|
-
try {
|
|
60
|
-
handler(runError || closeError, stats);
|
|
61
|
-
resolve(stats);
|
|
62
|
-
} catch (error) {
|
|
63
|
-
reject(error);
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
51
|
module.exports = {
|
|
71
52
|
createCompilerHandler,
|
|
72
|
-
|
|
73
|
-
};
|
|
53
|
+
};
|
|
@@ -3,43 +3,27 @@ const path = require('path');
|
|
|
3
3
|
const fse = require('fs-extra');
|
|
4
4
|
const { ASSET_EXT_PATTERN, PATTERNS, SRC_DIR } = require('../config/constants');
|
|
5
5
|
|
|
6
|
-
function extractStyleUrls(style) {
|
|
7
|
-
const urls = [];
|
|
8
|
-
const urlPattern = /url\(\s*(?:(['"])(.*?)\1|([^)]*?))\s*\)/gi;
|
|
9
|
-
let match;
|
|
10
|
-
|
|
11
|
-
// eslint-disable-next-line no-cond-assign
|
|
12
|
-
while ((match = urlPattern.exec(style)) !== null) {
|
|
13
|
-
const value = (match[2] !== undefined ? match[2] : match[3]).trim();
|
|
14
|
-
if (value) {
|
|
15
|
-
urls.push(value);
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return urls;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
6
|
function getWxmlAssets(filePath, content) {
|
|
22
7
|
const resolvePath = (attr, dir) => {
|
|
23
|
-
return new Promise((resolve
|
|
8
|
+
return new Promise((resolve) => {
|
|
24
9
|
const isAbsolutePath = PATTERNS.ABSOLUTE_PATH.test(attr);
|
|
25
10
|
const request = isAbsolutePath ? attr.replace(PATTERNS.ABSOLUTE_PATH, '') : attr;
|
|
26
11
|
const resolveContext = isAbsolutePath ? SRC_DIR : dir;
|
|
27
12
|
|
|
28
13
|
this.resolve(resolveContext, request, async (err, result) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const fallbackPath = path.resolve(resolveContext, request);
|
|
14
|
+
let res = result;
|
|
15
|
+
if (err) {
|
|
16
|
+
const fallbackPath = path.resolve(resolveContext, request);
|
|
33
17
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
18
|
+
if (await fse.pathExists(fallbackPath)) {
|
|
19
|
+
res = fallbackPath;
|
|
20
|
+
resolve(res);
|
|
21
|
+
} else {
|
|
22
|
+
this.emitError(err);
|
|
23
|
+
resolve(res);
|
|
39
24
|
}
|
|
25
|
+
} else {
|
|
40
26
|
resolve(res);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
reject(error);
|
|
43
27
|
}
|
|
44
28
|
});
|
|
45
29
|
});
|
|
@@ -47,7 +31,8 @@ function getWxmlAssets(filePath, content) {
|
|
|
47
31
|
return new Promise((resolve, reject) => {
|
|
48
32
|
let allAttrs = [];
|
|
49
33
|
const parser = new htmlparser2.Parser({
|
|
50
|
-
onopentag: (name, attributes) => {
|
|
34
|
+
onopentag: async (name, attributes) => {
|
|
35
|
+
const reg = /url\(.*\)/g;
|
|
51
36
|
const { style } = attributes;
|
|
52
37
|
|
|
53
38
|
allAttrs = allAttrs.concat(
|
|
@@ -58,53 +43,60 @@ function getWxmlAssets(filePath, content) {
|
|
|
58
43
|
);
|
|
59
44
|
|
|
60
45
|
if (style) {
|
|
61
|
-
|
|
46
|
+
const styles = attributes.style.split(';');
|
|
47
|
+
styles.forEach((styleItem) => {
|
|
48
|
+
(styleItem.match(reg) || []).forEach((item) => {
|
|
49
|
+
allAttrs.push(
|
|
50
|
+
item
|
|
51
|
+
.replace(/^(url\(”)/g, '')
|
|
52
|
+
.replace(/^(url\(')/g, '')
|
|
53
|
+
.replace(/^(url\()/g, '')
|
|
54
|
+
.replace(/(“\)|'\)|\))$/g, ''),
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
62
58
|
}
|
|
63
59
|
},
|
|
64
60
|
onerror: (err) => {
|
|
65
61
|
reject(err);
|
|
66
62
|
},
|
|
67
63
|
onend: async () => {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
.filter((item) => !/\+.*\+/.test(item));
|
|
64
|
+
const filteredAttrs = allAttrs
|
|
65
|
+
.filter((item) => !!item)
|
|
66
|
+
.map((item) => item.split('?')[0])
|
|
67
|
+
.filter((item) => !/^(http:|https:)/.test(item))
|
|
68
|
+
.filter((item) => !/{{.*}}/.test(item))
|
|
69
|
+
.filter((item) => !/\+.*\+/.test(item));
|
|
75
70
|
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
const assets = filteredAttrs.filter((item) => ASSET_EXT_PATTERN.test(item));
|
|
72
|
+
const wxmls = filteredAttrs.filter((item) => PATTERNS.WXS_EXT.test(item) || PATTERNS.WXML_EXT.test(item));
|
|
78
73
|
|
|
79
|
-
|
|
80
|
-
|
|
74
|
+
const assetsImports = [];
|
|
75
|
+
const wxmlsImports = [];
|
|
81
76
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
// 并行解析 asset 路径
|
|
94
|
-
const assetResults = await Promise.all(
|
|
95
|
-
assets.map(async (attr) => {
|
|
96
|
-
const result = await resolvePath(attr, path.parse(filePath).dir);
|
|
97
|
-
return result ? [attr, result] : null;
|
|
98
|
-
}),
|
|
99
|
-
);
|
|
100
|
-
for (let index = 0; index < assetResults.length; index += 1) {
|
|
101
|
-
if (assetResults[index]) assetsImports.push(assetResults[index]);
|
|
102
|
-
}
|
|
77
|
+
// 并行解析 wxml 路径
|
|
78
|
+
const wxmlResults = await Promise.all(
|
|
79
|
+
wxmls.map(async (attr) => {
|
|
80
|
+
const result = await resolvePath(attr, path.parse(filePath).dir);
|
|
81
|
+
return result ? [attr, result] : null;
|
|
82
|
+
}),
|
|
83
|
+
);
|
|
84
|
+
for (let index = 0; index < wxmlResults.length; index += 1) {
|
|
85
|
+
if (wxmlResults[index]) wxmlsImports.push(wxmlResults[index]);
|
|
86
|
+
}
|
|
103
87
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
88
|
+
// 并行解析 asset 路径
|
|
89
|
+
const assetResults = await Promise.all(
|
|
90
|
+
assets.map(async (attr) => {
|
|
91
|
+
const result = await resolvePath(attr, path.parse(filePath).dir);
|
|
92
|
+
return result ? [attr, result] : null;
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
for (let index = 0; index < assetResults.length; index += 1) {
|
|
96
|
+
if (assetResults[index]) assetsImports.push(assetResults[index]);
|
|
107
97
|
}
|
|
98
|
+
|
|
99
|
+
resolve([assetsImports, wxmlsImports]);
|
|
108
100
|
},
|
|
109
101
|
});
|
|
110
102
|
parser.write(content);
|
|
@@ -114,5 +106,4 @@ function getWxmlAssets(filePath, content) {
|
|
|
114
106
|
|
|
115
107
|
module.exports = {
|
|
116
108
|
getWxmlAssets,
|
|
117
|
-
extractStyleUrls,
|
|
118
109
|
};
|
package/build/utils/upload.js
CHANGED
|
@@ -16,70 +16,18 @@ const { DIST_DIR } = require('../config/constants');
|
|
|
16
16
|
|
|
17
17
|
const { obsConfig, ossConfig } = getConfig();
|
|
18
18
|
|
|
19
|
-
let
|
|
19
|
+
let obsClient;
|
|
20
20
|
let ossClient;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const commonMsg = result && result.CommonMsg;
|
|
24
|
-
const rawStatus = commonMsg && commonMsg.Status;
|
|
25
|
-
const status = Number(rawStatus);
|
|
26
|
-
|
|
27
|
-
if (
|
|
28
|
-
rawStatus === undefined ||
|
|
29
|
-
rawStatus === null ||
|
|
30
|
-
(typeof rawStatus === 'string' && rawStatus.trim() === '') ||
|
|
31
|
-
!Number.isFinite(status)
|
|
32
|
-
) {
|
|
33
|
-
return new Error(`OBS ${action} failed: invalid response`);
|
|
34
|
-
}
|
|
35
|
-
if (status >= 200 && status < 300) {
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const details = [
|
|
40
|
-
`status ${status}`,
|
|
41
|
-
commonMsg.Code && `code ${commonMsg.Code}`,
|
|
42
|
-
commonMsg.Message && commonMsg.Message,
|
|
43
|
-
].filter(Boolean);
|
|
44
|
-
return new Error(`OBS ${action} failed with ${details.join(', ')}`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function createObsCallback(action, resolve, reject) {
|
|
48
|
-
return (error, result) => {
|
|
49
|
-
if (error) {
|
|
50
|
-
reject(error);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const responseError = getObsResponseError(action, result);
|
|
55
|
-
if (responseError) {
|
|
56
|
-
reject(responseError);
|
|
57
|
-
} else {
|
|
58
|
-
resolve(result);
|
|
59
|
-
}
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function initializeObsClient(Client = OBSClient, config = obsConfig) {
|
|
64
|
-
const client = new Client({ ...config });
|
|
65
|
-
|
|
66
|
-
// esdk-obs-nodejs 3.26.x 的构造函数不会等待异步 initFactory()。
|
|
67
|
-
// 静态 AK/SK 初始化会在下一个微任务完成,立即调用 API 时 signatureContext 仍为 null。
|
|
68
|
-
await Promise.resolve();
|
|
69
|
-
if (!client.util || !client.util.signatureContext) {
|
|
70
|
-
throw new Error('OBS client initialization failed');
|
|
71
|
-
}
|
|
72
|
-
return client;
|
|
73
|
-
}
|
|
21
|
+
let progress;
|
|
22
|
+
let uploadQueue = {};
|
|
74
23
|
|
|
75
24
|
function getObsClient() {
|
|
76
|
-
if (!
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
throw error;
|
|
25
|
+
if (!obsClient) {
|
|
26
|
+
obsClient = new OBSClient({
|
|
27
|
+
...obsConfig,
|
|
80
28
|
});
|
|
81
29
|
}
|
|
82
|
-
return
|
|
30
|
+
return obsClient;
|
|
83
31
|
}
|
|
84
32
|
|
|
85
33
|
function getOssClient() {
|
|
@@ -102,28 +50,42 @@ function getOssStat(file) {
|
|
|
102
50
|
return getOssClient().head(compatiblePath(path.join(ossConfig.dir, path.relative(DIST_DIR, file))));
|
|
103
51
|
}
|
|
104
52
|
|
|
105
|
-
|
|
106
|
-
const client = await getObsClient();
|
|
53
|
+
function doObsUpload(file) {
|
|
107
54
|
return new Promise((resolve, reject) => {
|
|
108
|
-
|
|
55
|
+
getObsClient().putObject(
|
|
109
56
|
{
|
|
110
57
|
Bucket: obsConfig.bucket,
|
|
111
58
|
Key: compatiblePath(path.join(obsConfig.dir, path.relative(DIST_DIR, file))),
|
|
112
59
|
SourceFile: file,
|
|
113
60
|
},
|
|
114
|
-
|
|
61
|
+
(err, result) => {
|
|
62
|
+
if (err) {
|
|
63
|
+
reject(err);
|
|
64
|
+
} else if (result.CommonMsg.Status >= 300) {
|
|
65
|
+
reject(new Error(`OBS upload failed with status ${result.CommonMsg.Status}`));
|
|
66
|
+
} else {
|
|
67
|
+
resolve(result);
|
|
68
|
+
}
|
|
69
|
+
},
|
|
115
70
|
);
|
|
116
71
|
});
|
|
117
72
|
}
|
|
118
73
|
async function getObsStat(file) {
|
|
119
|
-
const client = await getObsClient();
|
|
120
74
|
return new Promise((resolve, reject) => {
|
|
121
|
-
|
|
75
|
+
getObsClient().getObjectMetadata(
|
|
122
76
|
{
|
|
123
77
|
Bucket: obsConfig.bucket,
|
|
124
78
|
Key: compatiblePath(path.join(obsConfig.dir, path.relative(DIST_DIR, file))),
|
|
125
79
|
},
|
|
126
|
-
|
|
80
|
+
(err, result) => {
|
|
81
|
+
if (err) {
|
|
82
|
+
reject(err);
|
|
83
|
+
} else if (result.CommonMsg.Status < 300) {
|
|
84
|
+
resolve(result);
|
|
85
|
+
} else {
|
|
86
|
+
reject(result);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
127
89
|
);
|
|
128
90
|
});
|
|
129
91
|
}
|
|
@@ -148,64 +110,77 @@ function doUpload(file) {
|
|
|
148
110
|
return doObsUpload(file);
|
|
149
111
|
}
|
|
150
112
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
113
|
+
function updateProgress() {
|
|
114
|
+
const completed =
|
|
115
|
+
Object.entries(uploadQueue).filter((item) => item[1] === 'completed').length + 1;
|
|
116
|
+
const total = Object.keys(uploadQueue).length;
|
|
117
|
+
|
|
118
|
+
if (!progress) {
|
|
119
|
+
progress = new Progress('uploading assets [:bar] :current/:total', {
|
|
120
|
+
total,
|
|
121
|
+
width: 40,
|
|
122
|
+
clear: true,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
progress.tick();
|
|
127
|
+
|
|
128
|
+
if (completed === total) {
|
|
129
|
+
progress.tick({
|
|
130
|
+
current: total,
|
|
131
|
+
});
|
|
132
|
+
progress = null;
|
|
133
|
+
uploadQueue = {};
|
|
156
134
|
}
|
|
157
|
-
|
|
135
|
+
|
|
136
|
+
return `[${completed}/${total}]`;
|
|
158
137
|
}
|
|
159
138
|
|
|
160
|
-
async function
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
width: 40,
|
|
165
|
-
clear: true,
|
|
166
|
-
});
|
|
139
|
+
async function checkUpload() {
|
|
140
|
+
const files = Object.entries(uploadQueue)
|
|
141
|
+
.filter((item) => item[1] === false)
|
|
142
|
+
.splice(0, 10);
|
|
167
143
|
|
|
168
|
-
|
|
169
|
-
const batch = files.slice(index, index + 10);
|
|
144
|
+
if (files && files.length) {
|
|
170
145
|
await Promise.all(
|
|
171
|
-
|
|
146
|
+
files.map(async (file) => {
|
|
147
|
+
uploadQueue[file[0]] = 'uploading';
|
|
172
148
|
try {
|
|
173
|
-
await
|
|
149
|
+
await getStat(file[0]);
|
|
150
|
+
setStorage(file[0], true);
|
|
151
|
+
updateProgress();
|
|
174
152
|
} catch (error) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
153
|
+
try {
|
|
154
|
+
await doUpload(file[0]);
|
|
155
|
+
setStorage(file[0], true);
|
|
156
|
+
} catch (uploadError) {
|
|
157
|
+
console.error(`Failed to upload ${file[0]}:`, uploadError);
|
|
158
|
+
}
|
|
159
|
+
updateProgress();
|
|
178
160
|
}
|
|
161
|
+
uploadQueue[file[0]] = 'completed';
|
|
179
162
|
}),
|
|
180
163
|
);
|
|
181
|
-
}
|
|
182
164
|
|
|
183
|
-
|
|
184
|
-
const details = failures
|
|
185
|
-
.map(({ file, error }) => `${file}: ${error && error.message ? error.message : error}`)
|
|
186
|
-
.join('\n');
|
|
187
|
-
const error = new Error(`Failed to upload ${failures.length} asset(s):\n${details}`);
|
|
188
|
-
error.failures = failures;
|
|
189
|
-
throw error;
|
|
165
|
+
checkUpload();
|
|
190
166
|
}
|
|
191
167
|
}
|
|
192
168
|
|
|
193
|
-
|
|
194
|
-
if (
|
|
169
|
+
function addToUploadQueue(assets) {
|
|
170
|
+
if (!obsConfig && !ossConfig) {
|
|
171
|
+
console.warn('请配置obsConfig 或 ossConfig,否则无法上传文件到obs 或 oss');
|
|
195
172
|
return;
|
|
196
173
|
}
|
|
197
174
|
|
|
198
|
-
|
|
199
|
-
|
|
175
|
+
assets.forEach((asset) => {
|
|
176
|
+
const file = path.resolve(DIST_DIR, asset);
|
|
177
|
+
if (uploadQueue[file] === undefined) {
|
|
178
|
+
uploadQueue[file] = false;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
checkUpload();
|
|
200
182
|
}
|
|
201
183
|
|
|
202
184
|
module.exports = {
|
|
203
185
|
addToUploadQueue,
|
|
204
|
-
_internals: {
|
|
205
|
-
createObsCallback,
|
|
206
|
-
getObsResponseError,
|
|
207
|
-
initializeObsClient,
|
|
208
|
-
uploadFile,
|
|
209
|
-
uploadFiles,
|
|
210
|
-
},
|
|
211
186
|
};
|
package/build/webpack.config.js
CHANGED
|
@@ -180,13 +180,13 @@ module.exports = async (options, { analyzer, quiet } = {}) => {
|
|
|
180
180
|
loader: MiniCssExtractPlugin.loader,
|
|
181
181
|
},
|
|
182
182
|
{
|
|
183
|
-
loader:
|
|
183
|
+
loader: 'css-loader',
|
|
184
184
|
options: {
|
|
185
185
|
importLoaders: use.length + 1,
|
|
186
186
|
},
|
|
187
187
|
},
|
|
188
188
|
{
|
|
189
|
-
loader:
|
|
189
|
+
loader: 'postcss-loader',
|
|
190
190
|
options: {
|
|
191
191
|
postcssOptions: {
|
|
192
192
|
plugins: [
|
|
@@ -239,16 +239,14 @@ module.exports = async (options, { analyzer, quiet } = {}) => {
|
|
|
239
239
|
});
|
|
240
240
|
});
|
|
241
241
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
);
|
|
251
|
-
}
|
|
242
|
+
plugins.push(
|
|
243
|
+
new CopyPlugin({
|
|
244
|
+
patterns,
|
|
245
|
+
options: {
|
|
246
|
+
concurrency: 100,
|
|
247
|
+
},
|
|
248
|
+
}),
|
|
249
|
+
);
|
|
252
250
|
|
|
253
251
|
return merge(
|
|
254
252
|
{
|
|
@@ -277,12 +275,12 @@ module.exports = async (options, { analyzer, quiet } = {}) => {
|
|
|
277
275
|
test: ASSET_EXT_PATTERN,
|
|
278
276
|
use: [
|
|
279
277
|
{
|
|
280
|
-
loader:
|
|
278
|
+
loader: 'url-loader',
|
|
281
279
|
options: {
|
|
282
280
|
limit: 0,
|
|
283
281
|
esModule: false,
|
|
284
282
|
fallback: {
|
|
285
|
-
loader:
|
|
283
|
+
loader: 'file-loader',
|
|
286
284
|
options: {
|
|
287
285
|
name: `${ASSETS_DIR}/[name].[hash].[ext]`,
|
|
288
286
|
},
|
|
@@ -303,7 +301,7 @@ module.exports = async (options, { analyzer, quiet } = {}) => {
|
|
|
303
301
|
...getCssLoader({
|
|
304
302
|
use: [
|
|
305
303
|
{
|
|
306
|
-
loader:
|
|
304
|
+
loader: 'less-loader',
|
|
307
305
|
options: {
|
|
308
306
|
lessOptions() {
|
|
309
307
|
return {
|
|
@@ -321,7 +319,7 @@ module.exports = async (options, { analyzer, quiet } = {}) => {
|
|
|
321
319
|
type: 'javascript/auto',
|
|
322
320
|
use: [
|
|
323
321
|
{
|
|
324
|
-
loader:
|
|
322
|
+
loader: 'file-loader',
|
|
325
323
|
options: {
|
|
326
324
|
name: assetsName,
|
|
327
325
|
},
|
|
@@ -333,7 +331,7 @@ module.exports = async (options, { analyzer, quiet } = {}) => {
|
|
|
333
331
|
type: 'javascript/auto',
|
|
334
332
|
use: [
|
|
335
333
|
{
|
|
336
|
-
loader:
|
|
334
|
+
loader: 'file-loader',
|
|
337
335
|
options: {
|
|
338
336
|
name: assetsName,
|
|
339
337
|
},
|
|
@@ -347,7 +345,7 @@ module.exports = async (options, { analyzer, quiet } = {}) => {
|
|
|
347
345
|
test: /\.(wxml)$/i,
|
|
348
346
|
use: [
|
|
349
347
|
{
|
|
350
|
-
loader:
|
|
348
|
+
loader: 'file-loader',
|
|
351
349
|
options: {
|
|
352
350
|
name: assetsName,
|
|
353
351
|
},
|
package/index.js
CHANGED
|
@@ -1,14 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// 高版本 Node.js (>=17) 兼容 OpenSSL 3.0
|
|
4
|
+
const nodeMajor = process.versions.node.split('.')[0];
|
|
5
|
+
if (nodeMajor >= 17) {
|
|
6
|
+
process.env.NODE_OPTIONS = `${process.env.NODE_OPTIONS || ''} --openssl-legacy-provider`.trim();
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
const { program } = require('commander');
|
|
4
10
|
const path = require('path');
|
|
5
11
|
const fs = require('fs');
|
|
12
|
+
const dev = require('./build/dev');
|
|
13
|
+
const prod = require('./build/prod');
|
|
6
14
|
const { setBuildEnv } = require('./build/utils/buildEnv');
|
|
7
15
|
const { ENV } = require('./build/config/constants');
|
|
8
16
|
|
|
9
17
|
const { version } = JSON.parse(
|
|
10
18
|
fs.readFileSync(path.resolve(__dirname, './package.json')).toString(),
|
|
11
19
|
);
|
|
20
|
+
const compiler = {
|
|
21
|
+
dev,
|
|
22
|
+
prod,
|
|
23
|
+
};
|
|
24
|
+
|
|
12
25
|
program.version(version);
|
|
13
26
|
program.option('-a, --analyzer', 'webpack-bundle-analyzer');
|
|
14
27
|
program.option('-s, --simulation', 'process.env.BUILD_ENV = simulation');
|
|
@@ -16,27 +29,25 @@ program.option('-d, --development', 'process.env.BUILD_ENV = development');
|
|
|
16
29
|
program.option('-p, --production', 'process.env.BUILD_ENV = production');
|
|
17
30
|
program.option('-q, --quiet', '安静模式,打印减少');
|
|
18
31
|
|
|
19
|
-
program.command('dev').action(
|
|
32
|
+
program.command('dev').action(() => {
|
|
20
33
|
setBuildEnv({
|
|
21
34
|
mode: ENV.DEV,
|
|
22
35
|
...program.opts(),
|
|
23
36
|
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
37
|
+
compiler.dev(program.opts()).catch((err) => {
|
|
38
|
+
console.error(err);
|
|
39
|
+
process.exitCode = 1;
|
|
40
|
+
});
|
|
28
41
|
});
|
|
29
|
-
program.command('build').action(
|
|
42
|
+
program.command('build').action(() => {
|
|
30
43
|
setBuildEnv({
|
|
31
44
|
mode: ENV.PROD,
|
|
32
45
|
...program.opts(),
|
|
33
46
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
47
|
+
compiler.prod(program.opts()).catch((err) => {
|
|
48
|
+
console.error(err);
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
});
|
|
37
51
|
});
|
|
38
52
|
|
|
39
|
-
program.
|
|
40
|
-
console.error(err);
|
|
41
|
-
process.exitCode = 1;
|
|
42
|
-
});
|
|
53
|
+
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hw-weapp-compiler",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "基于 webpack5 的小程序构建工具,兼容 Node.js
|
|
3
|
+
"version": "1.0.23",
|
|
4
|
+
"description": "基于 webpack5 的小程序构建工具,兼容 Node.js 14~24",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"hw-weapp": "index.js"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"registry": "https://registry.npmjs.org"
|
|
13
13
|
},
|
|
14
14
|
"engines": {
|
|
15
|
-
"node": ">=
|
|
15
|
+
"node": ">=14.17.1"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@swc/core": "^1.15.43",
|
|
@@ -30,8 +30,7 @@
|
|
|
30
30
|
"file-loader": "^6.2.0",
|
|
31
31
|
"fs-extra": "^9.1.0",
|
|
32
32
|
"htmlparser2": "^6.0.1",
|
|
33
|
-
"
|
|
34
|
-
"less": "4.6.7",
|
|
33
|
+
"less": "^4.1.1",
|
|
35
34
|
"less-loader": "^8.0.0",
|
|
36
35
|
"loader-utils": "^2.0.0",
|
|
37
36
|
"mini-css-extract-plugin": "^1.6.0",
|
|
@@ -42,7 +41,7 @@
|
|
|
42
41
|
"terser-webpack-plugin": "^5.6.1",
|
|
43
42
|
"throttle-debounce": "^3.0.1",
|
|
44
43
|
"url-loader": "^4.1.1",
|
|
45
|
-
"webpack": "5.
|
|
44
|
+
"webpack": "^5.61.0",
|
|
46
45
|
"webpack-bundle-analyzer": "^4.4.0",
|
|
47
46
|
"webpack-merge": "^5.7.3",
|
|
48
47
|
"webpack-sources": "^2.2.0"
|
|
@@ -53,7 +52,6 @@
|
|
|
53
52
|
"scripts": {
|
|
54
53
|
"build": "node ./index.js build",
|
|
55
54
|
"dev": "nodemon --inspect --trace-deprecation --watch build ./index.js dev",
|
|
56
|
-
"simulation": "node ./index.js build -s"
|
|
57
|
-
"test": "node --test"
|
|
55
|
+
"simulation": "node ./index.js build -s"
|
|
58
56
|
}
|
|
59
57
|
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
const fs = require('node:fs');
|
|
2
|
-
const path = require('node:path');
|
|
3
|
-
const vm = require('node:vm');
|
|
4
|
-
|
|
5
|
-
function executeMiniProgramDist(dist, entries = ['app.js', 'pages/index/index.js']) {
|
|
6
|
-
const sandbox = {
|
|
7
|
-
console,
|
|
8
|
-
appConfig: null,
|
|
9
|
-
pageConfig: null,
|
|
10
|
-
};
|
|
11
|
-
sandbox.global = sandbox;
|
|
12
|
-
sandbox.App = (config) => {
|
|
13
|
-
sandbox.appConfig = config;
|
|
14
|
-
};
|
|
15
|
-
sandbox.Page = (config) => {
|
|
16
|
-
sandbox.pageConfig = config;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const context = vm.createContext(sandbox);
|
|
20
|
-
const moduleCache = new Map();
|
|
21
|
-
|
|
22
|
-
const load = (request, from = 'app.js') => {
|
|
23
|
-
let filename = path.posix.normalize(path.posix.join(path.posix.dirname(from), request));
|
|
24
|
-
if (!path.posix.extname(filename)) {
|
|
25
|
-
filename += '.js';
|
|
26
|
-
}
|
|
27
|
-
if (moduleCache.has(filename)) {
|
|
28
|
-
return moduleCache.get(filename).exports;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const loadedModule = { exports: {} };
|
|
32
|
-
moduleCache.set(filename, loadedModule);
|
|
33
|
-
const source = fs.readFileSync(path.join(dist, ...filename.split('/')), 'utf8');
|
|
34
|
-
const wrapper = vm.runInContext(
|
|
35
|
-
`(function (require, module, exports) { ${source}\n })`,
|
|
36
|
-
context,
|
|
37
|
-
{ filename },
|
|
38
|
-
);
|
|
39
|
-
wrapper((child) => load(child, filename), loadedModule, loadedModule.exports);
|
|
40
|
-
return loadedModule.exports;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
for (const entry of entries) {
|
|
44
|
-
load(`./${entry}`);
|
|
45
|
-
}
|
|
46
|
-
return sandbox;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
module.exports = { executeMiniProgramDist };
|
package/test-support/project.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
const fs = require('node:fs');
|
|
2
|
-
const os = require('node:os');
|
|
3
|
-
const path = require('node:path');
|
|
4
|
-
|
|
5
|
-
function createTempProject(prefix = 'hw-weapp-test-') {
|
|
6
|
-
const root = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
|
7
|
-
|
|
8
|
-
return {
|
|
9
|
-
root,
|
|
10
|
-
resolve(...segments) {
|
|
11
|
-
return path.join(root, ...segments);
|
|
12
|
-
},
|
|
13
|
-
write(relativePath, content) {
|
|
14
|
-
const filename = path.join(root, relativePath);
|
|
15
|
-
fs.mkdirSync(path.dirname(filename), { recursive: true });
|
|
16
|
-
fs.writeFileSync(filename, content);
|
|
17
|
-
return filename;
|
|
18
|
-
},
|
|
19
|
-
cleanup() {
|
|
20
|
-
fs.rmSync(root, { recursive: true, force: true });
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
module.exports = { createTempProject };
|
package/test-support/webpack.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
const Module = require('node:module');
|
|
2
|
-
const webpack = require('webpack');
|
|
3
|
-
|
|
4
|
-
function runCompiler(config) {
|
|
5
|
-
const compiler = webpack(config);
|
|
6
|
-
|
|
7
|
-
return new Promise((resolve, reject) => {
|
|
8
|
-
compiler.run((runError, stats) => {
|
|
9
|
-
compiler.close((closeError) => {
|
|
10
|
-
if (runError || closeError) {
|
|
11
|
-
reject(runError || closeError);
|
|
12
|
-
} else {
|
|
13
|
-
resolve(stats);
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function installMissingLoaderFallbacks(fallbacks) {
|
|
21
|
-
const originalResolveFilename = Module._resolveFilename;
|
|
22
|
-
const missingFallbacks = new Map();
|
|
23
|
-
|
|
24
|
-
for (const [request, fallback] of Object.entries(fallbacks)) {
|
|
25
|
-
try {
|
|
26
|
-
originalResolveFilename.call(Module, request, module, false);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
missingFallbacks.set(request, fallback);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
Module._resolveFilename = function resolveFilename(request, parent, isMain, options) {
|
|
33
|
-
if (missingFallbacks.has(request)) {
|
|
34
|
-
return missingFallbacks.get(request);
|
|
35
|
-
}
|
|
36
|
-
return originalResolveFilename.call(this, request, parent, isMain, options);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
return () => {
|
|
40
|
-
Module._resolveFilename = originalResolveFilename;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function createAssetLoaderFallbacks(project) {
|
|
45
|
-
const fileLoader = project.write(
|
|
46
|
-
'test-file-loader.js',
|
|
47
|
-
[
|
|
48
|
-
"const path = require('path');",
|
|
49
|
-
'module.exports = function loader(content) {',
|
|
50
|
-
" const name = path.relative(this.rootContext, this.resourcePath).replace(/\\\\/g, '/');",
|
|
51
|
-
' this.emitFile(name, content);',
|
|
52
|
-
' return `module.exports = ${JSON.stringify(name)};`;',
|
|
53
|
-
'};',
|
|
54
|
-
'module.exports.raw = true;',
|
|
55
|
-
].join('\n'),
|
|
56
|
-
);
|
|
57
|
-
const urlLoader = project.write(
|
|
58
|
-
'test-url-loader.js',
|
|
59
|
-
[
|
|
60
|
-
"const crypto = require('crypto');",
|
|
61
|
-
"const path = require('path');",
|
|
62
|
-
'module.exports = function loader(content) {',
|
|
63
|
-
" const hash = crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);",
|
|
64
|
-
' const info = path.parse(this.resourcePath);',
|
|
65
|
-
' const name = `assets/${info.name}.${hash}${info.ext}`;',
|
|
66
|
-
' this.emitFile(name, content);',
|
|
67
|
-
' return `module.exports = ${JSON.stringify(name)};`;',
|
|
68
|
-
'};',
|
|
69
|
-
'module.exports.raw = true;',
|
|
70
|
-
].join('\n'),
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
return { 'file-loader': fileLoader, 'url-loader': urlLoader };
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
module.exports = {
|
|
77
|
-
createAssetLoaderFallbacks,
|
|
78
|
-
installMissingLoaderFallbacks,
|
|
79
|
-
runCompiler,
|
|
80
|
-
};
|