hw-weapp-compiler 1.0.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/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # weapp-compiler
2
+
3
+
4
+ ## 版本改动
5
+
6
+ 1. **OpenSSL 3.0 兼容**: Node.js >= 17 时自动注入 `--openssl-legacy-provider`
7
+ 2. **wxml-loader 空值守卫**: `loadModule` 失败时不再崩溃
8
+
9
+ ## 安装
10
+
11
+ ```bash
12
+ npm i weapp-compiler@2.x -D
13
+ ```
14
+
15
+ ## 使用
16
+
17
+ ```bash
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 # 生产环境
27
+ ```
28
+
29
+ ## 配置
30
+
31
+ 项目根目录下创建 `.weapp.js`:
32
+
33
+ ```js
34
+ const path = require('path');
35
+
36
+ module.exports = {
37
+ // 路径别名
38
+ alias: {
39
+ '@utils': path.resolve(__dirname, 'src/utils'),
40
+ '@config': path.resolve(__dirname, 'src/config'),
41
+ },
42
+ // 资源公共路径
43
+ publicPath: 'https://cdn.example.com/weapp/',
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
+ },
64
+ };
65
+ ```
66
+
67
+ 在 `project.config.json` 中忽略构建产物:
68
+
69
+ ```json
70
+ "packOptions": {
71
+ "ignore": [
72
+ { "type": "folder", "value": "assets" },
73
+ { "type": "regexp", "value": "\\.map$" }
74
+ ]
75
+ }
76
+ ```
@@ -0,0 +1,7 @@
1
+ const ENV = {
2
+ DEV: 'development',
3
+ PROD: 'production',
4
+ SIMULATION: 'simulation',
5
+ UNKOWN: 'unkown',
6
+ };
7
+ module.exports = ENV;
@@ -0,0 +1,11 @@
1
+ const path = require('path');
2
+ const fse = require('fs-extra');
3
+ const getContext = require('./getContext');
4
+
5
+ const appConfig = fse.readJSONSync(path.resolve(getContext(), 'app.json'));
6
+
7
+ function getAppConfig() {
8
+ return appConfig;
9
+ }
10
+
11
+ module.exports = getAppConfig;
@@ -0,0 +1 @@
1
+ module.exports = () => 'assets';
@@ -0,0 +1,73 @@
1
+ const chalk = require('chalk');
2
+ const fse = require('fs-extra');
3
+ const path = require('path');
4
+
5
+ const weapp = path.resolve(process.cwd(), '.weapp.js');
6
+
7
+ let config = {};
8
+
9
+ if (fse.existsSync(weapp)) {
10
+ // eslint-disable-next-line
11
+ config = require(weapp);
12
+ } else {
13
+ throw Error(
14
+ chalk.green(`
15
+ 请在项目根目录新建 .weapp.js 配置文件
16
+
17
+ 内容如下:
18
+
19
+ const path = require('path');
20
+
21
+ module.exports = {
22
+ // 路径别名
23
+ alias: {
24
+ '@utils': path.resolve(__dirname, 'src/utils'),
25
+ '@config': path.resolve(__dirname, 'src/config'),
26
+ '@template': path.resolve(__dirname, 'src/template'),
27
+ '@images': path.resolve(__dirname, 'src/images'),
28
+ '@obsimage': path.resolve(__dirname, 'src/wxs_fila/images'),
29
+ '@obs': path.resolve(__dirname, 'src/wxs_fila'),
30
+ '@obsjson': path.resolve(__dirname, 'src/wxs_fila/json'),
31
+ },
32
+ // 资源公共路径
33
+ publicPath: 'https://img.test.com/weapp-compiler-test/',
34
+ // 要同步的目录
35
+ copyFiles: [{
36
+ from: 'images',
37
+ to: 'images',
38
+ }],
39
+ // 华为OBS配置
40
+ obsConfig: {
41
+ access_key_id: 'XXXXXXXX',
42
+ secret_access_key: 'XXXXXXXX',
43
+ server: 'XXXXXXXX',
44
+ bucket: 'XXXXXXXX',
45
+ dir: 'weapp-compiler-test',
46
+ },
47
+ // 阿里OSS配置
48
+ ossConfig: {
49
+ region: '<Your region>',
50
+ accessKeyId: '<Your AccessKeyId>',
51
+ accessKeySecret: '<Your AccessKeySecret>',
52
+ bucket: '<Your Bucket>',
53
+ dir: 'weapp-compiler-test',
54
+ },
55
+ };
56
+ `),
57
+ );
58
+ }
59
+ const alias = {
60
+ ...(config.alias || {}),
61
+ };
62
+ Object.keys(config.alias || {}).forEach((key) => {
63
+ alias[`alias(${key})`] = alias[key];
64
+ });
65
+
66
+ function getConfig() {
67
+ return {
68
+ ...config,
69
+ alias,
70
+ };
71
+ }
72
+
73
+ module.exports = getConfig;
@@ -0,0 +1,5 @@
1
+ const path = require('path');
2
+
3
+ const context = path.resolve(process.cwd(), 'src');
4
+
5
+ module.exports = () => context;
@@ -0,0 +1,90 @@
1
+ const path = require('path');
2
+ const fse = require('fs-extra');
3
+ const getContext = require('./getContext');
4
+ const { addNodeModulesUsingComponent } = require('../utils/isNodeModulesUsingComponent');
5
+ const compatiblePath = require('../utils/compatiblePath');
6
+ const traverseDir = require('../utils/traverseDir');
7
+ const getAppConfig = require('./getAppConfig');
8
+
9
+ const context = getContext();
10
+ const appConfig = getAppConfig();
11
+
12
+ const entrys = {
13
+ app: path.resolve(context, 'app'),
14
+ };
15
+ const addUsingComponents = (components, parent) => {
16
+ Object.keys(components).forEach((key) => {
17
+ let filePath = components[key];
18
+
19
+ if (!/^(plugin:|plugin-private:|weui-miniprogram)/g.test(filePath)) {
20
+ if (/^\//g.test(filePath)) {
21
+ filePath = path.resolve(context, filePath.replace(/^\//g, ''));
22
+ }
23
+
24
+ if (fse.existsSync(`${path.resolve(parent, filePath)}.js`)) {
25
+ filePath = path.resolve(parent, filePath);
26
+ } else {
27
+ try {
28
+ filePath = require.resolve(filePath);
29
+ filePath = filePath.replace(/(\.js)$/g, '');
30
+ } catch (error) {
31
+ console.error(error);
32
+ return;
33
+ }
34
+ }
35
+
36
+ if (fse.existsSync(`${filePath}.json`)) {
37
+ // eslint-disable-next-line
38
+ readUsingComponents(`${filePath}.json`);
39
+ }
40
+
41
+ let entryKey = filePath;
42
+
43
+ if (/\/node_modules\//g.test(filePath) || /\\node_modules\\/g.test(filePath)) {
44
+ // eslint-disable-next-line
45
+ entryKey = compatiblePath(filePath).split('/node_modules/')[1];
46
+
47
+ addNodeModulesUsingComponent(entryKey);
48
+ } else {
49
+ entryKey = path.relative(context, filePath);
50
+ }
51
+
52
+ entryKey = compatiblePath(entryKey);
53
+
54
+ entrys[entryKey] = filePath;
55
+ }
56
+ });
57
+ };
58
+ const readUsingComponents = (file) => {
59
+ const json = fse.readJSONSync(file);
60
+
61
+ if (json.usingComponents) {
62
+ addUsingComponents(json.usingComponents, path.parse(file).dir);
63
+ }
64
+ };
65
+
66
+ // pages
67
+ (appConfig.pages || []).forEach((page) => {
68
+ entrys[page] = path.resolve(context, page);
69
+ });
70
+
71
+ // tabbar
72
+ if (fse.existsSync(path.resolve(context, 'custom-tab-bar/index.js'))) {
73
+ entrys['custom-tab-bar/index'] = path.resolve(context, 'custom-tab-bar/index');
74
+ }
75
+
76
+ // subpackages
77
+ (appConfig.subpackages || []).forEach((pkg) => {
78
+ (pkg.pages || []).forEach((page) => {
79
+ entrys[compatiblePath(path.join(pkg.root, page))] = path.resolve(context, pkg.root, page);
80
+ });
81
+ });
82
+
83
+ // usingComponents
84
+ traverseDir(context).forEach((item) => {
85
+ if (/(\.json)$/g.test(item)) {
86
+ readUsingComponents(item);
87
+ }
88
+ });
89
+
90
+ module.exports = () => entrys;
@@ -0,0 +1,5 @@
1
+ module.exports = () => ({
2
+ DEV: 'development',
3
+ PROD: 'production',
4
+ SIMULATION: 'simulation',
5
+ });
@@ -0,0 +1,7 @@
1
+ const path = require('path');
2
+
3
+ function getOutput() {
4
+ return path.resolve(process.cwd(), 'dist');
5
+ }
6
+
7
+ module.exports = getOutput;
@@ -0,0 +1 @@
1
+ module.exports = () => /\.(png|jpg|gif|jpeg|svg|ttf|woff|eot|woff2|otf|mp3|mp4|wav|json|html)$/i;
package/build/dev.js ADDED
@@ -0,0 +1,34 @@
1
+ const chalk = require('chalk');
2
+ const webpack = require('webpack');
3
+ const ENV = require('./config/env');
4
+ const webpackConfig = require('./webpack.config');
5
+
6
+ module.exports = (opts) => {
7
+ const compiler = webpack(
8
+ webpackConfig(
9
+ {
10
+ mode: ENV.DEV,
11
+ devtool: 'cheap-module-source-map',
12
+ },
13
+ opts || {},
14
+ ),
15
+ );
16
+
17
+ compiler.watch({}, (err, stats) => {
18
+ if (err) {
19
+ console.error(err);
20
+ return;
21
+ }
22
+
23
+ if (stats.hasErrors()) {
24
+ console.log(
25
+ stats.toString({
26
+ chunks: false, // Makes the build much quieter
27
+ colors: true, // Shows colors in the console
28
+ }),
29
+ );
30
+ }
31
+
32
+ console.log(chalk.green(`completed in ${(stats.endTime - stats.startTime) / 1000} seconds`));
33
+ });
34
+ };
@@ -0,0 +1,37 @@
1
+ const path = require('path');
2
+ const fse = require('fs-extra');
3
+ const getEntrys = require('../config/getEntrys');
4
+ const loadModule = require('../utils/loadModule');
5
+
6
+ const entrys = getEntrys();
7
+
8
+ module.exports = async function loader(source) {
9
+ this.cacheable(true);
10
+ const callback = this.async();
11
+ const filePath = this.resourcePath;
12
+ const fileInfo = path.parse(filePath);
13
+ const sourceStr = source.toString();
14
+
15
+ const imports = [sourceStr];
16
+
17
+ if (Object.values(entrys).indexOf(path.resolve(fileInfo.dir, fileInfo.name)) !== -1) {
18
+ const exts = ['.less', '.wxss', '.css', '.json', '.wxml'];
19
+
20
+ for (let index = 0; index < exts.length; index += 1) {
21
+ const ext = exts[index];
22
+ const otherFilePath = path.resolve(fileInfo.dir, fileInfo.name + ext);
23
+
24
+ if (await fse.pathExists(otherFilePath)) {
25
+ if (['.less', '.wxss', '.css'].indexOf(ext) !== -1) {
26
+ imports.unshift(`import './${fileInfo.name}${ext}'\n`);
27
+ } else {
28
+ await loadModule.call(this, otherFilePath);
29
+ }
30
+ }
31
+ }
32
+ }
33
+
34
+ callback(null, imports.join(''));
35
+ };
36
+
37
+ module.exports.raw = true;
@@ -0,0 +1,44 @@
1
+ const getWxmlAssets = require('../utils/getWxmlAssets');
2
+ const loadModule = require('../utils/loadModule');
3
+
4
+ module.exports = async function loader(source) {
5
+ this.cacheable(true);
6
+ const callback = this.async();
7
+ const filePath = this.resourcePath;
8
+ let content = source.toString();
9
+ const {
10
+ _compiler: {
11
+ options: {
12
+ output: { publicPath },
13
+ },
14
+ },
15
+ } = this;
16
+ const withPublicPath = (str) => {
17
+ if (!str) return '';
18
+ const match = str.match(/"(.*)"/g);
19
+ if (!match) return str;
20
+ return `${publicPath === 'auto' ? '' : publicPath}${match[0].replace(/"/g, '')}`;
21
+ };
22
+
23
+ const [assets, wxmls] = await getWxmlAssets.call(this, filePath, content);
24
+
25
+ // 资源文件
26
+ for (let index = 0; index < assets.length; index += 1) {
27
+ const [attr, file] = assets[index];
28
+ const src = await loadModule.call(this, file);
29
+
30
+ if (src) {
31
+ content = content.replace(attr, withPublicPath(src, publicPath));
32
+ }
33
+ }
34
+
35
+ // wxml文件
36
+ for (let index = 0; index < wxmls.length; index += 1) {
37
+ const [, file] = wxmls[index];
38
+
39
+ await loadModule.call(this, file);
40
+ }
41
+
42
+ callback(null, content);
43
+ };
44
+ module.exports.raw = true;
@@ -0,0 +1,25 @@
1
+ const path = require('path');
2
+ const loadModule = require('../utils/loadModule');
3
+
4
+ module.exports = async function loader(source) {
5
+ this.cacheable(true);
6
+ const callback = this.async();
7
+ const filePath = this.resourcePath;
8
+ const content = source.toString();
9
+ const fileInfo = path.parse(filePath);
10
+
11
+ const requires = content.match(/require\(("|').*("|')\)/g) || [];
12
+
13
+ for (let index = 0; index < requires.length; index += 1) {
14
+ const item = requires[index];
15
+
16
+ const file = path.resolve(
17
+ fileInfo.dir,
18
+ item.replace(/^((require\(')|(require\("))/g, '').replace(/(('\))|("\)))$/g, ''),
19
+ );
20
+ await loadModule.bind(this)(file);
21
+ }
22
+
23
+ callback(null, content);
24
+ };
25
+ module.exports.raw = true;
@@ -0,0 +1,231 @@
1
+ const path = require('path');
2
+ const UglifyJS = require('uglify-js');
3
+ const hasha = require('hasha');
4
+ const { RawSource } = require('webpack-sources');
5
+
6
+ const { isNodeModulesUsingComponent } = require('../utils/isNodeModulesUsingComponent');
7
+ const getAssets = require('../config/getAssets');
8
+ const { addToUploadQueue } = require('../utils/upload');
9
+ const getResourceAccept = require('../config/getResourceAccept');
10
+ const compatiblePath = require('../utils/compatiblePath');
11
+ const ENV = require('../config/env');
12
+
13
+ const assetsDir = getAssets();
14
+ const pluginName = 'WeappCompilerPlugin';
15
+ const contentCache = {};
16
+
17
+ class WeappPlugin {
18
+ // eslint-disable-next-line
19
+ constructor() {}
20
+
21
+ // eslint-disable-next-line
22
+ apply(compiler) {
23
+ // const { RawSource } = compiler.webpack.sources;
24
+
25
+ let obsAssets = [];
26
+
27
+ compiler.hooks.done.tap(pluginName, () => {
28
+ addToUploadQueue([...obsAssets]);
29
+ obsAssets = [];
30
+ });
31
+
32
+ compiler.hooks.compilation.tap(pluginName, (compilation) => {
33
+ compilation.hooks.afterProcessAssets.tap(
34
+ {
35
+ name: pluginName,
36
+ },
37
+ (assets) => {
38
+ obsAssets = obsAssets.concat(
39
+ Object.keys(assets).filter((key) => {
40
+ return getResourceAccept().test(key) && new RegExp(`${assetsDir}/`, 'g').test(key);
41
+ }),
42
+ );
43
+ },
44
+ );
45
+
46
+ compilation.hooks.processAssets.tap(
47
+ {
48
+ name: pluginName,
49
+ stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS,
50
+ },
51
+ async (assets) => {
52
+ // this callback will run against assets added later by plugins.
53
+ const items = Object.entries(assets)
54
+ .map(([name, source]) => {
55
+ return {
56
+ name,
57
+ source,
58
+ };
59
+ })
60
+ .filter((item) => {
61
+ return /(\.(js|wxss))$/g.test(item.name);
62
+ });
63
+
64
+ let hasCommonWxss = false;
65
+ let hasVendorWxss = false;
66
+ let hasCommonJs = false;
67
+ let hasRuntimeJs = false;
68
+ let hasVendorJs = false;
69
+ const subpackages = {};
70
+
71
+ // 检测公共模块
72
+ for (let index = 0; index < items.length; index += 1) {
73
+ const item = items[index];
74
+
75
+ if (hasCommonWxss === false && item.name === 'commons.wxss') {
76
+ hasCommonWxss = true;
77
+ }
78
+ if (hasVendorWxss === false && item.name === 'vendors_wxss.wxss') {
79
+ hasVendorWxss = true;
80
+ }
81
+ if (hasCommonJs === false && item.name === 'commons.js') {
82
+ hasCommonJs = true;
83
+ }
84
+ if (hasVendorJs === false && item.name === 'vendors.js') {
85
+ hasVendorJs = true;
86
+ }
87
+ if (hasRuntimeJs === false && item.name === 'runtime.js') {
88
+ hasRuntimeJs = true;
89
+ }
90
+ if (/subpackage_common/g.test(item.name)) {
91
+ const pathInfo = path.parse(item.name);
92
+
93
+ if (!subpackages[compatiblePath(pathInfo.dir)]) {
94
+ subpackages[compatiblePath(pathInfo.dir)] = {
95
+ js: '',
96
+ wxss: '',
97
+ };
98
+ }
99
+ if (/(\.js)$/g.test(item.name)) {
100
+ subpackages[compatiblePath(pathInfo.dir)].js = compatiblePath(item.name);
101
+ }
102
+ if (/(\.wxss)$/g.test(item.name)) {
103
+ subpackages[compatiblePath(pathInfo.dir)].wxss = compatiblePath(item.name);
104
+ }
105
+ }
106
+ }
107
+
108
+ for (let index = 0; index < items.length; index += 1) {
109
+ const asset = items[index];
110
+
111
+ const assetName = compatiblePath(asset.name);
112
+ const isJs = /(\.(js))$/g.test(assetName);
113
+ const { source } = asset;
114
+ let content = source.source();
115
+
116
+ if (content.toString) {
117
+ content = content.toString();
118
+ }
119
+
120
+ // 注入全局引用模块
121
+ if (isJs) {
122
+ if (!/(var self = global;)/g.test(content)) {
123
+ content = `var self = global; \n${content}`;
124
+ }
125
+ }
126
+
127
+ // runtime 暴露到全局
128
+ if (assetName === 'runtime.js') {
129
+ content = content.replace(
130
+ 'var __webpack_module_cache__ = {};',
131
+ `
132
+ if (!global.__webpack_module_cache__) {
133
+ global.__webpack_module_cache__ = {};
134
+ }
135
+ var __webpack_module_cache__ = global.__webpack_module_cache__;
136
+ global.__webpack_require__ = __webpack_require__;
137
+ `,
138
+ );
139
+ }
140
+
141
+ // 注入公共模块 js
142
+ if (assetName === 'app.js') {
143
+ // sdk2.17.3 window 下没有 regeneratorRuntime
144
+ content = content.replace(
145
+ 'Function("r", "regeneratorRuntime = r")(runtime);',
146
+ 'global.regeneratorRuntime = runtime',
147
+ );
148
+ if (!/require('\.\/commons\.js')/g.test(content) && hasCommonJs) {
149
+ content = `require('./commons.js');\n${content}`;
150
+ }
151
+ if (!/require('\.\/vendors\.js')/g.test(content) && hasVendorJs) {
152
+ content = `require('./vendors.js');\n${content}`;
153
+ }
154
+ if (!/require('\.\/runtime\.js')/g.test(content) && hasRuntimeJs) {
155
+ content = `require('./runtime.js');\n${content}`;
156
+ }
157
+ }
158
+
159
+ // 注入公共模块 css
160
+ if (assetName === 'app.wxss') {
161
+ if (!/@import '\.\/commons\.wxss'/g.test(content) && hasCommonWxss) {
162
+ content = `@import './commons.wxss';\n${content}`;
163
+ }
164
+ if (!/@import '\.\/vendors_wxss\.wxss'/g.test(content) && hasVendorWxss) {
165
+ content = `@import './vendors_wxss.wxss';\n${content}`;
166
+ }
167
+ }
168
+
169
+ // 分包注入公共模块
170
+ Object.keys(subpackages).forEach((subpackage) => {
171
+ const res = subpackages[subpackage];
172
+
173
+ if (
174
+ assetName.startsWith(subpackage) &&
175
+ !/(subpackage_common\.(js|wxss))$/g.test(assetName)
176
+ ) {
177
+ if (
178
+ res.js &&
179
+ /(\.js)$/g.test(assetName) &&
180
+ !/'subpackage_common\.js'\);/g.test(content)
181
+ ) {
182
+ content = `require('${compatiblePath(
183
+ path.relative(path.parse(assetName).dir, res.js),
184
+ )}');\n${content}`;
185
+ }
186
+ if (
187
+ res.wxss &&
188
+ /(\.wxss)$/g.test(assetName) &&
189
+ !/'subpackage_common\.wxss';/g.test(content)
190
+ ) {
191
+ content = `@import '${compatiblePath(
192
+ path.relative(path.parse(assetName).dir, res.wxss),
193
+ )}';\n${content}`;
194
+ }
195
+ }
196
+ });
197
+
198
+ // 压缩 公共模块
199
+ if (
200
+ (assetName === 'commons.js' ||
201
+ assetName === 'vendors.js' ||
202
+ isNodeModulesUsingComponent(asset.name)) &&
203
+ compiler.options.mode === ENV.DEV
204
+ ) {
205
+ const hash = hasha(content);
206
+ if (contentCache[assetName] && contentCache[assetName].hash === hash) {
207
+ content = contentCache[assetName].content;
208
+ } else if (isJs) {
209
+ content = UglifyJS.minify(content, {
210
+ mangle: false,
211
+ compress: {
212
+ drop_console: false,
213
+ drop_debugger: false,
214
+ },
215
+ sourceMap: false,
216
+ }).code;
217
+ contentCache[assetName] = {
218
+ hash,
219
+ content,
220
+ };
221
+ }
222
+ }
223
+ compilation.updateAsset(asset.name, new RawSource(content));
224
+ }
225
+ },
226
+ );
227
+ });
228
+ }
229
+ }
230
+
231
+ module.exports = WeappPlugin;