@ecoding/base.build 0.0.1

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/LICENSE.md ADDED
File without changes
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ // 工程通用打包配置
2
+
3
+ # `@ecoding/build`
4
+
5
+ ## 简单快速配置 abc.js
6
+
7
+ ```javascript
8
+ {
9
+ mpa: boolean, // 是否是多页
10
+ dll:boolean, // 是否打dll
11
+ micro:"main" | "child", // 微前端主应用或子应用 默认null
12
+ outputLibrary: string, // micro == child时生效
13
+ publicPath: string,
14
+ outputPath: string, // build bundle 目录,相对命令执行目录
15
+ externals: object;
16
+ /* externals
17
+ {
18
+ "@alilc/lowcode-engine": "var window.AliLowCodeEngine",
19
+ "@alilc/lowcode-engine-ext": "var window.AliLowCodeEngineExt",
20
+ };
21
+ */
22
+ dev:{
23
+ publicPath: string; // devserver 读这个
24
+ port: number, // devserver 端口 默认 8080
25
+ }
26
+ }
27
+ ```
28
+
29
+ ## 复杂自定义打包配置
30
+
31
+ 在工程下新建 build.js,return webpack 的配置即可,注意完全按照 webpack 配置规则,框架无做任何干涉
32
+
33
+ ```javascript
34
+ module.exports = {
35
+ externals: {
36
+ react: "var window.React",
37
+ moment: "var window.moment",
38
+ lodash: "var window._",
39
+ "react-dom": "var window.ReactDOM",
40
+ "prop-types": "var window.PropTypes"
41
+ }
42
+ };
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ ```json
48
+ {
49
+ "@babel/core": "7.18.5", // babel
50
+ "@babel/plugin-proposal-class-properties": "7.17.12", // 会将类中的属性编译,支持私有属性
51
+ "@babel/plugin-proposal-decorators": "7.18.2", // 插件会支持类的装饰器语法, 包括类装饰器, 属性装饰器, 方法装饰器, tsconfig.json -> "experimentalDecorators": true,
52
+ "@babel/plugin-proposal-private-methods": "7.17.12", // 会将类中的方法编译,支持私有方法
53
+ "@babel/plugin-proposal-private-property-in-object": "7.17.12",
54
+ "@babel/plugin-transform-runtime": "7.18.5", // babel 运行时工具集合
55
+ "@babel/preset-env": "7.18.2", // babel插件集合
56
+ "@babel/preset-react": "7.17.12", // babel 支持 react 插件
57
+ "@babel/preset-typescript": "7.17.12", // babel 支持 typescript 插件
58
+ "@ecoding/mock": "0.0.10", // 自己写的mock插件
59
+ "@svgr/webpack": "6.2.1", // webpack svg 插件
60
+ "@typescript-eslint/eslint-plugin": "5.28.0", // eslint ts 插件
61
+ "@typescript-eslint/parser": "5.28.0", // eslint ts 编译器
62
+ "add-asset-html-webpack-plugin": "5.0.2", // 将某个文件打包输出到build目录下,并在html中自动引入该资源
63
+ "autoprefixer": "10.4.7", // 可以自动在样式中添加浏览器厂商前,是postcss插件
64
+ "babel-eslint": "10.1.0",
65
+ "babel-loader": "8.2.5",
66
+ "clean-webpack-plugin": "^4.0.0",
67
+ "core-js": "3.23.1",
68
+ "cross-env": "7.0.3",
69
+ "css-loader": "6.7.1",
70
+ "css-minimizer-webpack-plugin": "4.0.0",
71
+ "eslint": "8.18.0",
72
+ "globby": "^11.0.2",
73
+ "html-webpack-plugin": "5.5.0", // html 模版插件
74
+ "inline-chunk-html-plugin": "1.1.1", // 内联 webpack runtime 代码
75
+ "less": "4.1.3",
76
+ "less-loader": "11.0.0",
77
+ "mini-css-extract-plugin": "2.6.1", // css压缩
78
+ "postcss-loader": "7.0.0", // css向下兼容
79
+ "style-loader": "3.3.1", // 编译 style 内联css
80
+ "terser-webpack-plugin": "5.3.3", // js压缩
81
+ "webpack": "5.73.0",
82
+ "webpack-cli": "4.10.0",
83
+ "webpack-dev-server": "4.9.2",
84
+ "webpack-merge": "5.8.0",
85
+ "webpackbar": "5.0.2"
86
+ }
87
+ ```
@@ -0,0 +1,64 @@
1
+ const mock = require("@ecoding/mock").default;
2
+ const { rootPath, entry } = require("../z-helpers/paths");
3
+ const { customConfig } = require("../z-helpers/config");
4
+
5
+ const entryRewrites = () => {
6
+ const ary = [];
7
+ entry.pages.forEach((pageName) => {
8
+ const reg = new RegExp(`${pageName}\\.*`, "i");
9
+ ary.push({ from: reg, to: `/${pageName}.html` });
10
+ });
11
+ return ary;
12
+ };
13
+
14
+ const getDevServer = () => {
15
+ const dev = Object.assign({}, customConfig.dev);
16
+ delete dev.publicPath;
17
+ const obj = {
18
+ // 配合单页路由 BrowserRouter 使用找寻index
19
+ historyApiFallback: {
20
+ rewrites: customConfig.mpa ? entryRewrites() : [{ from: /\.*/, to: "/index.html" }]
21
+ },
22
+ headers: {
23
+ "Access-Control-Allow-Origin": "*"
24
+ },
25
+ static: {
26
+ directory: rootPath("dist/")
27
+ },
28
+ /**
29
+ * 配置监听端口, 因为8080很常用, 为了避免和其他程序冲突, 我们配个其他的端口号 mac 80端口需要sudo
30
+ */
31
+ port: 8080,
32
+ host: "0.0.0.0",
33
+ // "proxy": {
34
+ // "/api/cxc": {
35
+ // target: "https://nei.netease.com/api/apimock-v2/218e6a7e23485596c6be37252eba11c9/comment/deleteComment?commentId=",
36
+ // changeOrigin: true,
37
+ // secure: false
38
+ // }
39
+ // },
40
+ onBeforeSetupMiddleware: function (devServer) {
41
+ if (!devServer) {
42
+ throw new Error("webpack-dev-server is not defined");
43
+ }
44
+ mock(devServer.app, {
45
+ enable: true,
46
+ type: "local"
47
+ });
48
+ },
49
+ /**
50
+ * config.devServer.proxy用来配置后端api的反向代理, ajax /api/auth/*的请求会被转发到 http://api.example.dev/auth/*, /api/pay/*的请求会被转发到 http://api.example.dev/pay/*.
51
+ * changeOrigin会修改HTTP请求头中的Host为target的域名, 这里会被改为api.example.dev
52
+ * pathRewrite用来改写URL, 这里我们把/api前缀去掉. pathRewrite: { '^/api': '' }
53
+ */
54
+ // proxy: proxy,
55
+ ...dev
56
+ };
57
+ if (customConfig.micro === "child") {
58
+ obj.hot = false;
59
+ obj.liveReload = false;
60
+ }
61
+ return obj;
62
+ };
63
+
64
+ module.exports = getDevServer;
@@ -0,0 +1,13 @@
1
+ const { rootPath, entry } = require("../z-helpers/paths");
2
+ const { customConfig } = require("../z-helpers/config");
3
+
4
+ const getEntries = () => {
5
+ if (customConfig.mpa) {
6
+ return entry.pagesObj;
7
+ }
8
+ return {
9
+ app: rootPath("./src/app.tsx")
10
+ };
11
+ };
12
+
13
+ module.exports = getEntries;
@@ -0,0 +1,11 @@
1
+ const { customConfig } = require("../z-helpers/config");
2
+
3
+ const getExternals = () => {
4
+ return {
5
+ // "@alilc/lowcode-engine": "var window.AliLowCodeEngine",
6
+ // "@alilc/lowcode-engine-ext": "var window.AliLowCodeEngineExt",
7
+ ...customConfig.externals
8
+ };
9
+ };
10
+
11
+ module.exports = getExternals;
@@ -0,0 +1,55 @@
1
+ const MiniCssExtractPlugin = require("mini-css-extract-plugin");
2
+ const { isEnvDevelopment, isEnvProduction } = require("../../z-helpers/util");
3
+
4
+ const loaders = [
5
+ isEnvDevelopment && { loader: "style-loader" },
6
+ isEnvProduction && MiniCssExtractPlugin.loader,
7
+ {
8
+ loader: "css-loader",
9
+ options: {
10
+ modules: undefined // enable CSS modules for all files matching /\.module\.\w+$/i.test(filename) and /\.icss\.\w+$/i.test(filename) regexp.
11
+ }
12
+ },
13
+ {
14
+ loader: "postcss-loader",
15
+ options: {
16
+ postcssOptions: {
17
+ plugins: {
18
+ autoprefixer: {
19
+ overrideBrowserslist: ["IE 10", "iOS >=6", "Android >=4"],
20
+ remove: false
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }
26
+ ].filter(Boolean);
27
+
28
+ const getLessRule = () => {
29
+ return {
30
+ test: /\.less$/,
31
+ use: [
32
+ ...loaders,
33
+ {
34
+ loader: "less-loader",
35
+ options: {
36
+ lessOptions: {
37
+ javascriptEnabled: true
38
+ }
39
+ }
40
+ }
41
+ ]
42
+ };
43
+ };
44
+
45
+ const getCssRule = () => {
46
+ return {
47
+ test: /\.css$/,
48
+ use: [...loaders]
49
+ };
50
+ };
51
+
52
+ module.exports = {
53
+ getCssRule,
54
+ getLessRule
55
+ };
@@ -0,0 +1,14 @@
1
+ // https://github.com/michael-ciniawsky/postcss-load-config
2
+
3
+ module.exports = {
4
+ "plugins": {
5
+ "autoprefixer": {
6
+ overrideBrowserslist:[
7
+ "IE 10",
8
+ "iOS >=6",
9
+ "Android >=4"
10
+ ],
11
+ remove: false
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,18 @@
1
+
2
+ const getFontRule = () => {
3
+ return {
4
+ test: /\.(eot|ttf|woff2?)(\?.+)?$/,
5
+ type: 'asset',
6
+ parser: {
7
+ dataUrlCondition: {
8
+ maxSize: 10 * 1024,
9
+ }
10
+ },
11
+ generator: {
12
+ filename: 'fonts/[base]',
13
+ }
14
+ }
15
+ }
16
+
17
+
18
+ module.exports = getFontRule;
@@ -0,0 +1,19 @@
1
+
2
+ const getImgRule = () => {
3
+ return {
4
+ test: /\.(png|jpg|jpeg|gif)(\?.+)?$/,
5
+ exclude: /favicon\.png$/,
6
+ type: 'asset',
7
+ parser: {
8
+ dataUrlCondition: {
9
+ maxSize: 10 * 1024,
10
+ }
11
+ },
12
+ generator: {
13
+ filename: 'img/[base]',
14
+ }
15
+ }
16
+ }
17
+
18
+
19
+ module.exports = getImgRule;
@@ -0,0 +1,29 @@
1
+ const getTsRule = require('./ts-loader');
2
+ const { getCssRule, getLessRule } = require('./css-loader');
3
+ const getImgRule = require('./img-loader');
4
+ const getSvgRule = require('./svg-loader');
5
+ const getFontRule = require('./font-loader');
6
+
7
+ const getModule = () => {
8
+ const tsRule = getTsRule();
9
+ const lessRule = getLessRule();
10
+ const cssRule = getCssRule();
11
+ const imgRule = getImgRule();
12
+ const svgRule = getSvgRule();
13
+ const fontRule = getFontRule();
14
+
15
+ return {
16
+ // noParse:/test.js$/, 不编译正则匹配上的文件
17
+ rules: [
18
+ tsRule,
19
+ lessRule,
20
+ cssRule,
21
+ imgRule,
22
+ svgRule,
23
+ fontRule
24
+ ]
25
+ }
26
+ }
27
+
28
+
29
+ module.exports = getModule;
@@ -0,0 +1,10 @@
1
+
2
+ const getSvgRule = () => {
3
+ return {
4
+ test: /\.svg$/,
5
+ use: ['@svgr/webpack']
6
+ }
7
+ }
8
+
9
+
10
+ module.exports = getSvgRule;
@@ -0,0 +1,75 @@
1
+ const presets = [
2
+ [
3
+ "@babel/preset-env",
4
+ {
5
+ ignoreBrowserslistConfig: true,
6
+
7
+ /**
8
+ * false 当我们使用preset-env传入useBuiltIns参数时候,默认为false。
9
+ * 它表示仅仅会转化最新的ES语法,并不会转化任何Api和方法。就是关闭垫片
10
+ *
11
+ * 当传入entry时,需要我们在项目入口文件中手动引入一次core-js,
12
+ * 它会根据我们配置的浏览器兼容性列表(browserList)然后全量引入不兼容的polyfill。
13
+ * 项目入口文件中需要额外引入polyfill
14
+ // core-js 2.0中是使用"@babel/polyfill" core-js3.0版本中变化成为了上边两个包
15
+ import "@babel/polyfill" // core-js 2.0
16
+
17
+ import "core-js/stable" // core-js3.0
18
+ import "regenerator-runtime/runtime" // core-js3.0
19
+
20
+ // babel
21
+ {
22
+ "presets": [
23
+ ["@babel/preset-env", {
24
+ "useBuiltIns": "entry"
25
+ }]
26
+ ]
27
+ }
28
+
29
+ * usage时,会根据配置的浏览器兼容,以及代码中 使用到的Api 进行引入polyfill按需添加。
30
+ */
31
+ useBuiltIns: "usage",
32
+ targets: {
33
+ browsers: [
34
+ "last 1 version",
35
+ "> 1%",
36
+ "IE 10",
37
+ "iOS >=6",
38
+ "Android >=4"
39
+ ]
40
+ },
41
+ // 如果设为true,则会编译 es6Module 语法,则会失去tree-shaking
42
+ modules: false,
43
+ corejs: 3,
44
+ // 需要忽略的预编译包
45
+ exclude: [
46
+ // 没使用Symbol,不需要_typeof方法兼容Symbol
47
+ 'transform-typeof-symbol'
48
+ ]
49
+ }
50
+ ],
51
+ ["@babel/preset-react", {
52
+ // automatic 自动模式 不需要引入 import React form 'react';
53
+ // classic 经典模式 需要引入 import React form 'react';
54
+ runtime: 'classic',
55
+ }],
56
+ ["@babel/preset-typescript"]
57
+ ].filter(Boolean);
58
+
59
+ const plugins = [
60
+ ["@babel/plugin-transform-runtime", { corejs: false }],
61
+ ["@babel/plugin-proposal-decorators", { legacy: true }],
62
+ ["@babel/plugin-proposal-class-properties", { loose: true }],
63
+ ["@babel/plugin-proposal-private-methods", { loose: true }],
64
+ ["@babel/plugin-proposal-private-property-in-object", { loose: true }]
65
+ ].filter(Boolean);
66
+
67
+ const babelLoader = {
68
+ loader: 'babel-loader',
69
+ options: {
70
+ presets,
71
+ plugins,
72
+ cacheDirectory: true
73
+ }
74
+ }
75
+ module.exports = babelLoader;
@@ -0,0 +1,16 @@
1
+ const { rootPath } = require("../../z-helpers/paths");
2
+ const babelLoader = require("./babel");
3
+
4
+ const getTsRule = () => {
5
+ return {
6
+ test: /\.tsx?$/,
7
+ use: [babelLoader],
8
+ // 指定要编译的路径
9
+ include: [rootPath("src"), /\/node_modules\/@ecoding.*/],
10
+
11
+ // 忽略要编译的路径
12
+ exclude: [/node_modules/]
13
+ };
14
+ };
15
+
16
+ module.exports = getTsRule;
@@ -0,0 +1,8 @@
1
+ const getMPAOptimization = require('./mpa');
2
+ const getSPAOptimization = require('./spa');
3
+
4
+
5
+ module.exports = {
6
+ getMPAOptimization,
7
+ getSPAOptimization
8
+ };
@@ -0,0 +1,68 @@
1
+ const TerserPlugin = require("terser-webpack-plugin");
2
+ const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
3
+ const { isEnvDevelopment } = require("../z-helpers/util");
4
+ const { customConfig } = require("../z-helpers/config");
5
+
6
+ /** optimization 配置
7
+ * module: 每一个文件其实都可以看成一个module
8
+ * chunk:webpack打包最终生成的代码块,代码块会生成文件,一个文件对应一个chunk
9
+ * chunkIds、moduleIds:natural | named | deterministic(生产环境默认) | size,chunk 命名算法
10
+ * natural:自然数 1,2,3
11
+ * named:文件名
12
+ * deterministic:简短hash
13
+ * size:文件大小
14
+ */
15
+ const getOptimization = () => {
16
+ return {
17
+ chunkIds: isEnvDevelopment ? "named" : "deterministic",
18
+ moduleIds: isEnvDevelopment ? "named" : "deterministic",
19
+ runtimeChunk: {
20
+ // 单独分割运行时代码 webpack 运行时所需代码
21
+ name: (entrypoint) => `wp-runtime-${entrypoint.name}`
22
+ },
23
+ minimize: isEnvDevelopment ? false : true,
24
+ minimizer: [
25
+ new TerserPlugin({ parallel: true }), // parallel:多进程压缩
26
+ new CssMinimizerPlugin()
27
+ ],
28
+ // 这里的体积大小时指压缩前的
29
+ // 如果不想代码分割,设置 splitChunks: false,通常用于微前端或微模块
30
+ splitChunks: customConfig.dll
31
+ ? false
32
+ : {
33
+ chunks: "all", // initial | all | async all 表明了在异步和同步之间共享
34
+ minSize: 10000, // 生成 chunk 的最小体积(以 bytes 为单位) 不限制
35
+ maxSize: 500000, // 封装的模块的最小体积 0 不限制
36
+ minRemainingSize: 10000, // 分割后剩下的模块大小 0 不限制
37
+ minChunks: 1, // 模块的最小被引用次数
38
+ maxAsyncRequests: 30, // 异步模块按需加载的最大并行请求数 import('xxx.js').then
39
+ maxInitialRequests: 30, // 同步模块最大并行请求数
40
+ // enforceSizeThreshold: 1000000000, // 如果代码块体积超过了 50000,则强制打包,无视其他配置
41
+ cacheGroups: {
42
+ // 缓存组
43
+ vendors: {
44
+ name: "vendors",
45
+ test: /[\\/]node_modules[\\/]/,
46
+ // test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom|redux|react-redux|redux-thunk|connected-react-router|hoist-non-react-statics)/,
47
+ // minSize: 0,
48
+ enforce: true, // ignore splitChunks.minSize, splitChunks.minChunks, splitChunks.maxAsyncRequests and splitChunks.maxInitialRequests
49
+ priority: -10
50
+ },
51
+ // vendors2: {
52
+ // name: 'vendors2',
53
+ // test: /[\\/]node_modules[\\/](axios)/,
54
+ // // minSize: 0,
55
+ // priority: -10,
56
+ // },
57
+ default: {
58
+ test: /[\\/]src[\\/]/,
59
+ minChunks: 2, // 如果一个模块被引用2次的话,会单独提取
60
+ priority: -20, // 优先级
61
+ reuseExistingChunk: true // 是否使用其他已被分割出去的代码块
62
+ }
63
+ }
64
+ }
65
+ };
66
+ };
67
+
68
+ module.exports = getOptimization;
@@ -0,0 +1,75 @@
1
+ const TerserPlugin = require("terser-webpack-plugin");
2
+ const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
3
+ const { isEnvDevelopment } = require("../z-helpers/util");
4
+ const { customConfig } = require("../z-helpers/config");
5
+
6
+ /** optimization 配置
7
+ * module: 每一个文件其实都可以看成一个module
8
+ * chunk:webpack打包最终生成的代码块,代码块会生成文件,一个文件对应一个chunk
9
+ * chunkIds、moduleIds:natural | named | deterministic(生产环境默认) | size,chunk 命名算法
10
+ * natural:自然数 1,2,3
11
+ * named:文件名
12
+ * deterministic:简短hash
13
+ * size:文件大小
14
+ */
15
+ const getOptimization = () => {
16
+ const obj = {
17
+ chunkIds: isEnvDevelopment ? "named" : "deterministic",
18
+ moduleIds: isEnvDevelopment ? "named" : "deterministic",
19
+ minimize: isEnvDevelopment ? false : true,
20
+ minimizer: [
21
+ new TerserPlugin({ parallel: true }), // parallel:多进程压缩
22
+ new CssMinimizerPlugin()
23
+ ]
24
+ };
25
+
26
+ if (customConfig.dll) {
27
+ obj.splitChunks = false;
28
+ } else {
29
+ // 这里的体积大小时指压缩前的
30
+ obj.splitChunks = {
31
+ chunks: "all", // initial | all | async all 表明了在异步和同步之间共享
32
+ minSize: 10000, // 生成 chunk 的最小体积(以 bytes 为单位) 不限制
33
+ maxSize: 500000, // 封装的模块的最小体积 0 不限制
34
+ minRemainingSize: 10000, // 分割后剩下的模块大小 0 不限制
35
+ minChunks: 1, // 模块的最小被引用次数
36
+ maxAsyncRequests: 30, // 异步模块按需加载的最大并行请求数 import('xxx.js').then
37
+ maxInitialRequests: 30, // 同步模块最大并行请求数
38
+ // enforceSizeThreshold: 1000000000, // 如果代码块体积超过了 50000,则强制打包,无视其他配置
39
+ cacheGroups: {
40
+ // 缓存组
41
+ vendors: {
42
+ name: "vendors",
43
+ test: /[\\/]node_modules[\\/]/,
44
+ // test: /[\\/]node_modules[\\/](react|react-dom|react-router-dom|redux|react-redux|redux-thunk|connected-react-router|hoist-non-react-statics)/,
45
+ // minSize: 0,
46
+ enforce: true, // ignore splitChunks.minSize, splitChunks.minChunks, splitChunks.maxAsyncRequests and splitChunks.maxInitialRequests
47
+ priority: -10
48
+ },
49
+ // vendors2: {
50
+ // name: 'vendors2',
51
+ // test: /[\\/]node_modules[\\/](axios)/,
52
+ // // minSize: 0,
53
+ // priority: -10,
54
+ // },
55
+ default: {
56
+ test: /[\\/]src[\\/]/,
57
+ minChunks: 2, // 如果一个模块被引用2次的话,会单独提取
58
+ priority: -20, // 优先级
59
+ reuseExistingChunk: true // 是否使用其他已被分割出去的代码块
60
+ }
61
+ }
62
+ };
63
+ }
64
+
65
+ if (customConfig.micro !== "child") {
66
+ obj.runtimeChunk = {
67
+ // 单独分割运行时代码 webpack 运行时所需代码
68
+ name: (entrypoint) => `wp-runtime-${entrypoint.name}`
69
+ };
70
+ }
71
+
72
+ return obj;
73
+ };
74
+
75
+ module.exports = getOptimization;
@@ -0,0 +1,54 @@
1
+ const { rootPath } = require("../z-helpers/paths");
2
+ const { customConfig } = require("../z-helpers/config");
3
+
4
+ const pubPath = () => {
5
+ const dev = customConfig.dev || {};
6
+ switch (process.env.NODE_ENV) {
7
+ case "production":
8
+ return customConfig.publicPath || "";
9
+ default:
10
+ return dev.publicPath || "/";
11
+ }
12
+ };
13
+
14
+ const getOutput = () => {
15
+ return Object.assign(
16
+ {
17
+ // 输出文件目录(将来所有资源输出的公共目录)
18
+ path: rootPath(customConfig.outputPath || "./dist"),
19
+
20
+ /**
21
+ * 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
22
+ * 注意:可在业务代码中 动态修改 output 内的publicPath
23
+ * if(...) {
24
+ __webpack_public_path__ = 'https://cdn.com'
25
+ }
26
+ */
27
+ publicPath: pubPath(),
28
+
29
+ // 入口代码块文件名称(指定名称+目录)
30
+ filename: "[name].[chunkhash:8].min.js",
31
+
32
+ /**
33
+ * 非入口代码文件命名
34
+ * 1. 每一个入口会生成一个chunk
35
+ * 2. 代码分割动态import 引入代码会生成一个chunk
36
+ */
37
+ chunkFilename: "[name].[chunkhash:8].min.js"
38
+ // library: '[name]', // 整个库向外暴露的变量名
39
+ // libraryTarget: 'var' // 默认
40
+ },
41
+ customConfig.micro === "child"
42
+ ? {
43
+ library: {
44
+ name: customConfig.outputLibrary,
45
+ type: "umd"
46
+ },
47
+ chunkLoadingGlobal: `webpackJsonp_${customConfig.outputLibrary}`,
48
+ globalObject: "window"
49
+ }
50
+ : {}
51
+ );
52
+ };
53
+
54
+ module.exports = getOutput;
@@ -0,0 +1,142 @@
1
+ # output
2
+
3
+ ## path
4
+ > 输出文件目录(将来所有资源输出的公共目录)
5
+
6
+ ## publicPath
7
+ > 所有资源引入公共路径前缀 --> 'imgs/a.jpg' --> '/imgs/a.jpg'
8
+ ```javascript
9
+ // 可在业务代码中 动态修改 output 内的publicPath
10
+ if(...) {
11
+ __webpack_public_path__ = 'https://cdn.conm/'
12
+ }
13
+ ```
14
+
15
+ ## filename
16
+ > 入口代码块文件名称(指定名称+目录)
17
+
18
+ ## chunkFilename
19
+ > 代码块文件名称
20
+ * 1. 每一个入口会生成一个chunk
21
+ * 2. 代码分割动态import 引入代码会生成一个chunk
22
+
23
+ ## library
24
+ > 整个库向外暴露的变量名,library: '[name]'
25
+
26
+ ## libraryTarget
27
+ > 导出库的规范方式
28
+
29
+ ## umdNamedDefine
30
+ > 如果在output.libraryTarget被设置成umd形式,并且output.library也被设置时,将该选项设为true将会给命名给amd模块
31
+
32
+
33
+
34
+ #### var - 默认值
35
+ > 当库被加载时,那么库的返回值会被分配到使用用 var 声明的变量上。
36
+ ```javascript
37
+ var myDemo = _entry_return_;
38
+ myDemo();
39
+ ```
40
+
41
+ #### assign
42
+ > 会把库返回值分配给一个没使用var声明的变量中,如果这个变量没有在引入作用域中提前声明过,那么将会挂载在全局作用域中。(注意,这个行为有可能会覆盖全局作用域中的已有变量)
43
+ ```javascript
44
+ myDemo = _entry_return_;
45
+ ```
46
+
47
+ #### this
48
+ > 将库的返回值分配给this对象的由 output.library指定的属性。其中this的意义由用户决定。
49
+ ```javascript
50
+ this["myDemo"] = _entry_return_;
51
+ this.myDemo();
52
+ myDemo(); // if this is window
53
+ ```
54
+
55
+ #### window
56
+ > 将库的返回值分配给 browser window 对象的由output.library指定的属性。
57
+ ```javascript
58
+ window["myDemo"] = _entry_return_;
59
+ window.myDemo.doSomething();
60
+ ```
61
+
62
+ #### global
63
+ > 将库的返回值分配给 node global 对象的由output.library指定的属性。
64
+ ```javascript
65
+ global["myDemo"] = _entry_return_;
66
+ global.myDemo();
67
+ ```
68
+
69
+ #### commonjs
70
+ > 将库的返回值分配给exports对象的由output.library指定的属性。正如名字所指,这个选项可以使用在 CommonJS 环境。
71
+ ```javascript
72
+ exports["myDemo"] = _entry_return_;
73
+ require("myDemo").doSomething();
74
+ ```
75
+
76
+ #### commonjs2
77
+ > 将库的返回值分配给module.exports。正如名字所指,这个选项可以使用在 CommonJS 环境。
78
+ ```javascript
79
+ module.exports = _entry_return_;
80
+ const myDemo = require("myDemo");
81
+ myDemo();
82
+ ```
83
+
84
+ #### amd
85
+ > 这个选项会把库作为 AMD 模块导出。AMD模块要求输入脚本(例如由 script 标签加载的第一个脚本)被定义为具有特定属性,例如通常由 RequireJS 或任何兼容的加载器(诸如almond)提供的require和define属性。否则,直接加载生成的 AMD 捆绑包将导致类似define is not defined的错误。
86
+ ```javascript
87
+ define("myDemo", [], function() {
88
+ return _entry_return_;
89
+ });
90
+ ```
91
+ 以上的代码可以作为script标签引入代码的一部分被包含
92
+
93
+ 然后在通过以下代码调用
94
+ ``` javascript
95
+ require(['myDemo'], function(myDemo) {
96
+ myDemo();
97
+ });
98
+ ```
99
+
100
+ **注意**
101
+
102
+ 如果output.library没有定义有效值,那么生成的代码将如下:
103
+ ``` javascript
104
+ define([], function() {
105
+ return _entry_return_;
106
+ });
107
+ ```
108
+
109
+
110
+ #### umd
111
+ > 这个选项会尝试把库暴露给前使用的模块定义系统,这使其和CommonJS、AMD兼容或者暴露为全局变量。**注意** output.library 选项在这里是必须的。
112
+ ```javascript
113
+ (function webpackUniversalModuleDefinition(root, factory) {
114
+ if(typeof exports === 'object' && typeof module === 'object')
115
+ module.exports = factory();
116
+ else if(typeof define === 'function' && define.amd)
117
+ define([], factory);
118
+ else if(typeof exports === 'object')
119
+ exports["MyLibrary"] = factory();
120
+ else
121
+ root["MyLibrary"] = factory();
122
+ })(typeof self !== 'undefined' ? self : this, function() {
123
+ return _entry_return_;
124
+ });
125
+ ```
126
+ **配置案例**
127
+ ```javascript
128
+ output: {
129
+ library: {
130
+ root: "myDemo",
131
+ amd: "myAmdDemo",
132
+ commonjs: "myCjsDemo"
133
+ },
134
+ libraryTarget: "umd"
135
+ }
136
+ ```
137
+
138
+ #### jsonp
139
+ > 这个方法会使用 jsonp 的方式把结果包裹起来。
140
+ ```javascript
141
+ myDemo(_entry_return_);
142
+ ```
@@ -0,0 +1,113 @@
1
+ const webpack = require("webpack");
2
+ const path = require("path");
3
+ const globby = require("globby");
4
+ const WebpackBar = require("webpackbar");
5
+ const HtmlWebpackPlugin = require("html-webpack-plugin");
6
+ const AddAssetHtmlWebpackPlugin = require("add-asset-html-webpack-plugin");
7
+ const InlineChunkHtmlPlugin = require("inline-chunk-html-plugin");
8
+ const MiniCssExtractPlugin = require("mini-css-extract-plugin"); // 提取分离css
9
+ const { CleanWebpackPlugin } = require("clean-webpack-plugin"); // 提取分离css
10
+ const { isEnvProduction } = require("../z-helpers/util");
11
+ const { customConfig } = require("../z-helpers/config");
12
+ const { rootPath, entry } = require("../z-helpers/paths");
13
+
14
+ let dllJS = "";
15
+ let dllJSON = "";
16
+ if (customConfig.dll) {
17
+ const paths = globby.sync(["*.js", "*.json"], {
18
+ cwd: rootPath("dll")
19
+ });
20
+ paths.forEach((p) => {
21
+ const ext = path.extname(p);
22
+ if (ext === ".js") {
23
+ dllJS = p;
24
+ }
25
+ if (ext === ".json") {
26
+ dllJSON = p;
27
+ }
28
+ });
29
+ }
30
+
31
+ const getHtmlWebpack = () => {
32
+ const arr = [];
33
+ const template = rootPath("./template/index.ejs");
34
+
35
+ if (customConfig.mpa) {
36
+ entry.pages.forEach((pageName) => {
37
+ const filename = `${pageName}.html`;
38
+ const obj = {
39
+ template,
40
+ filename,
41
+ inject: "body",
42
+ inlineSource: "wp-runtime-",
43
+ chunksSortMode: "manual",
44
+ chunks: [pageName]
45
+ // favicon: rootPath('./src/assets/img/favicon.ico')
46
+ };
47
+ if (isEnvProduction) {
48
+ obj.output = rootPath("./dist");
49
+ obj.minify = {
50
+ minimize: true,
51
+ removeConments: true,
52
+ collapseWhitespace: true,
53
+ minifyCSS: true,
54
+ minifyJS: true
55
+ };
56
+ }
57
+ arr.push(new HtmlWebpackPlugin(obj));
58
+ });
59
+ } else {
60
+ const filename = `index.html`;
61
+ const obj = {
62
+ template,
63
+ filename,
64
+ inlineSource: "wp-runtime-",
65
+ inject: "body"
66
+ };
67
+ if (isEnvProduction) {
68
+ obj.output = rootPath("./dist");
69
+ obj.minify = {
70
+ minimize: true,
71
+ removeConments: true,
72
+ collapseWhitespace: true,
73
+ minifyCSS: true,
74
+ minifyJS: true
75
+ };
76
+ }
77
+ arr.push(new HtmlWebpackPlugin(obj));
78
+ }
79
+ return arr;
80
+ };
81
+
82
+ module.exports = () => {
83
+ const plugins = [
84
+ new WebpackBar({
85
+ profile: true
86
+ }),
87
+
88
+ isEnvProduction &&
89
+ new MiniCssExtractPlugin({
90
+ // filename: 'css/[name].css',
91
+ filename: "[id].[chunkhash:5].css"
92
+ }),
93
+
94
+ ...getHtmlWebpack(),
95
+
96
+ // 内联runtime
97
+ customConfig.micro !== "child" && new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/wp-runtime-.*\.js/]),
98
+
99
+ new CleanWebpackPlugin(),
100
+
101
+ // 告诉webpack哪些库不参与打包,同时使用时的名称也得变
102
+ customConfig.dll &&
103
+ new webpack.DllReferencePlugin({
104
+ manifest: rootPath(`dll/${dllJSON}`)
105
+ }),
106
+ // 将某个文件打包输出到build目录下,并在html中自动引入该资源
107
+ customConfig.dll &&
108
+ new AddAssetHtmlWebpackPlugin({
109
+ filepath: rootPath(`dll/${dllJS}`)
110
+ })
111
+ ].filter(Boolean);
112
+ return plugins;
113
+ };
@@ -0,0 +1,44 @@
1
+ const { rootPath } = require('../z-helpers/paths');
2
+
3
+ const getResolve = () => {
4
+ return {
5
+ /**
6
+ Webpack的resolve.modules配置模块库(即 node_modules)所在的位置,
7
+ 在 js 里出现 import 'vue' 这样不是相对、也不是绝对路径的写法时,会去 node_modules 目录下找。
8
+ 但是默认的配置,会采用向上递归搜索的方式去寻找,但通常项目目录里只有一个 node_modules,
9
+ 且是在项目根目录,为了减少搜索范围,可以直接写明 node_modules 的全路径;
10
+ */
11
+ modules: [
12
+ rootPath('./src'),
13
+ rootPath('./node_modules')
14
+ ],
15
+
16
+ // 配置后缀名,方便代码书写时引用不写后缀
17
+ extensions: ['.ts', '.tsx', '.json', '.js', '.jsx'],
18
+
19
+ // 定义别名,在代码里可直接写
20
+ // import xx from '@/'; 缓存src目录为@ 符号,避免重复寻址
21
+ alias: {
22
+ "@": rootPath("./src")
23
+ },
24
+
25
+ // fallback:是模块在root及默认路径下都未找到时的最终查找路径
26
+ // 设为false就是放弃备选的引用,假如第三方里用到了node内置包,此设置可忽略
27
+ fallback: {
28
+ 'crypto': false, // crypto-browserify
29
+ 'stream': false, // stream-browserify
30
+ 'buffer': false // buffer
31
+ }
32
+ }
33
+ }
34
+
35
+
36
+ const getResolveLoader = () => {
37
+ // 先从 node_modules 里找,找不到再从自定的 loaders 文件夹下找
38
+ modules: ['node_modules', rootPath('./deploy/z-custom-loaders')]
39
+ }
40
+
41
+ module.exports = {
42
+ getResolveLoader,
43
+ getResolve
44
+ };
@@ -0,0 +1,13 @@
1
+ const { isEnvDevelopment, shouldUseSourceMap } = require('../z-helpers/util');
2
+
3
+ // source-map 源码映射,单独生成map文件,标示了行和列,文件大而全
4
+ // eval-source-map 不会生成单独map文件,集成在打包后文件,可以显示行和列
5
+ // cheap-module-source-map 生成单独map文件,不会显示列只显示行
6
+ // cheap-module-eval-source-map 不会生成单独map文件,集成在打包后文件,不会显示列
7
+
8
+ const getSourceMap = () => {
9
+ return isEnvDevelopment ? "cheap-module-source-map" : shouldUseSourceMap ? 'source-map' : false
10
+ }
11
+
12
+
13
+ module.exports = getSourceMap;
@@ -0,0 +1,95 @@
1
+ const { merge } = require("webpack-merge");
2
+ const { Production, Development } = require("./z-helpers/const");
3
+ const { isEnvDevelopment } = require("./z-helpers/util");
4
+ const { customConfig, buildConfig } = require("./z-helpers/config");
5
+ const getEntries = require("./entries");
6
+ const getDevTool = require("./source-map");
7
+ const getOutput = require("./output");
8
+ const { getMPAOptimization, getSPAOptimization } = require("./optimization");
9
+ const getModule = require("./module");
10
+ const { getResolve, getResolveLoader } = require("./resolve");
11
+ const getPlugins = require("./plugins");
12
+ const getExternals = require("./externals");
13
+ const getDevServer = require("./dev");
14
+
15
+ /**
16
+ * 事前工作
17
+ */
18
+ function prepare(runtimeOpts) {}
19
+
20
+ /**
21
+ *
22
+ * @param {*} cmdOpts : build时 { WEBPACK_BUNDLE: true, WEBPACK_BUILD: true } | 本地开发dev时 { WEBPACK_SERVE: true }
23
+ * @param {*} runtimeOpts :
24
+ * {
25
+ mode: 'development',
26
+ config: [ './deploy/webpack.config.js' ],
27
+ env: { WEBPACK_SERVE: true }
28
+ }
29
+ * @returns
30
+ */
31
+ const getWebpackConfig = (env) => {
32
+ prepare();
33
+ // 打包环境
34
+ const mode = isEnvDevelopment ? Development : Production;
35
+
36
+ // 配置缓存chunk
37
+ /**
38
+ * 'filesystem', // filesystem(prod默认) | memory(dev默认)
39
+ */
40
+ const cache = {
41
+ type: isEnvDevelopment ? "memory" : "filesystem",
42
+ };
43
+
44
+ // 配置source-map
45
+ const devtool = getDevTool();
46
+
47
+ // 配置入口 entry
48
+ const entry = getEntries();
49
+
50
+ // 配置输出bundle output
51
+ const output = getOutput();
52
+
53
+ // 配置resolveLoader
54
+ const resolveLoader = getResolveLoader();
55
+
56
+ // 配置optimization
57
+ const optimization = customConfig.mpa
58
+ ? getMPAOptimization()
59
+ : getSPAOptimization();
60
+
61
+ // 配置loaders
62
+ const module = getModule();
63
+
64
+ // 配置resolve
65
+ const resolve = getResolve();
66
+
67
+ // 配置plugins
68
+ const plugins = getPlugins();
69
+
70
+ // 配置devServer
71
+ const devServer = getDevServer();
72
+
73
+ // 配置externals
74
+ const externals = getExternals();
75
+
76
+ const config = Object.assign(
77
+ {
78
+ mode,
79
+ cache,
80
+ devtool,
81
+ entry,
82
+ output,
83
+ resolveLoader,
84
+ optimization,
85
+ module,
86
+ resolve,
87
+ plugins,
88
+ externals,
89
+ },
90
+ isEnvDevelopment ? { devServer } : {}
91
+ );
92
+ return merge(config, buildConfig);
93
+ };
94
+
95
+ module.exports = getWebpackConfig;
@@ -0,0 +1,25 @@
1
+ const { rootPath } = require("./z-helpers/paths");
2
+ const webpack = require("webpack");
3
+ const { CleanWebpackPlugin } = require("clean-webpack-plugin");
4
+
5
+ module.exports = {
6
+ entry: {
7
+ dll: ["react", "react-dom", "react-router-dom", "react-redux", "redux", "redux-thunk", "hoist-non-react-statics", "axios"]
8
+ //other:['a','b','c']
9
+ },
10
+ mode: "production",
11
+ output: {
12
+ // 输出出口指定
13
+ filename: "[name]_[fullhash].js", // name就是jquery
14
+ path: rootPath("dll"), // 打包到dll目录下
15
+ library: "[name]_[fullhash]" // 打包的库里面向外暴露出去的内容叫什么名字
16
+ },
17
+ plugins: [
18
+ new CleanWebpackPlugin(),
19
+ // 打包生成一个manifest.json --> 提供jquery的映射关系(告诉webpack:jquery之后不需要再打包和暴露内容的名称)
20
+ new webpack.DllPlugin({
21
+ name: "[name]_[fullhash]", // 映射库的暴露的内容名称
22
+ path: rootPath("dll/manifest_[name]_[fullhash].json") // 输出文件路径
23
+ })
24
+ ]
25
+ };
File without changes
@@ -0,0 +1,19 @@
1
+ const fs = require("fs");
2
+ const { rootPath } = require("./paths");
3
+ const isABC = fs.existsSync(rootPath("abc.js"));
4
+ const isBuild = fs.existsSync(rootPath("build.js"));
5
+ let customConfig = {}; // 简单配置
6
+ let buildConfig = {}; // 复杂完全合并配置
7
+ if (isABC) {
8
+ customConfig = require(rootPath("abc.js"));
9
+ } else {
10
+ throw new Error("not found abc.js");
11
+ }
12
+ if (isBuild) {
13
+ buildConfig = require(rootPath("build.js"));
14
+ }
15
+
16
+ module.exports = {
17
+ customConfig,
18
+ buildConfig
19
+ };
@@ -0,0 +1,8 @@
1
+ const Production = 'production';
2
+ const Development = 'development';
3
+
4
+
5
+ module.exports = {
6
+ Production,
7
+ Development
8
+ }
@@ -0,0 +1,28 @@
1
+ const { resolve } = require('path');
2
+ const fs = require('fs');
3
+ const cwd = process.cwd();
4
+
5
+ const rootPath = (pathname) => {
6
+ return resolve(cwd, pathname)
7
+ }
8
+
9
+ const resolvePath = resolve;
10
+
11
+ const getEntries = () => {
12
+ const dirPath = rootPath('./src/pages')
13
+ const pages = fs.readdirSync(dirPath)
14
+ const pagesObj = {}
15
+ pages.forEach((pageName) => {
16
+ pagesObj[pageName] = rootPath(`./src/pages/${pageName}/main.tsx`);
17
+ });
18
+ return {
19
+ pagesObj,
20
+ pages
21
+ }
22
+ }
23
+
24
+ module.exports = {
25
+ rootPath,
26
+ resolvePath,
27
+ entry: getEntries()
28
+ }
@@ -0,0 +1,14 @@
1
+ const os = require('os');
2
+ const { Production, Development } = require('./const');
3
+
4
+ const isEnvProduction = process.env.NODE_ENV === Production;
5
+ const isEnvDevelopment = process.env.NODE_ENV === Development;
6
+ const workers = os.cpus().length - 1;
7
+ const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP === 'true';
8
+
9
+ module.exports = {
10
+ isEnvProduction,
11
+ isEnvDevelopment,
12
+ shouldUseSourceMap,
13
+ workers
14
+ }
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@ecoding/base.build",
3
+ "version": "0.0.1",
4
+ "description": "tpl building",
5
+ "author": "cxc",
6
+ "license": "MIT",
7
+ "files": [
8
+ "libs"
9
+ ],
10
+ "dependencies": {
11
+ "@babel/core": "7.18.5",
12
+ "@babel/plugin-proposal-class-properties": "7.17.12",
13
+ "@babel/plugin-proposal-decorators": "7.18.2",
14
+ "@babel/plugin-proposal-private-methods": "7.17.12",
15
+ "@babel/plugin-proposal-private-property-in-object": "7.17.12",
16
+ "@babel/plugin-transform-runtime": "7.18.5",
17
+ "@babel/preset-env": "7.18.2",
18
+ "@babel/preset-react": "7.17.12",
19
+ "@babel/preset-typescript": "7.17.12",
20
+ "@ecoding/mock": "0.0.10",
21
+ "@svgr/webpack": "6.2.1",
22
+ "@typescript-eslint/eslint-plugin": "5.28.0",
23
+ "@typescript-eslint/parser": "5.28.0",
24
+ "add-asset-html-webpack-plugin": "5.0.2",
25
+ "autoprefixer": "10.4.7",
26
+ "babel-eslint": "10.1.0",
27
+ "babel-loader": "8.2.5",
28
+ "clean-webpack-plugin": "^4.0.0",
29
+ "concurrently": "^7.3.0",
30
+ "core-js": "3.23.1",
31
+ "cross-env": "7.0.3",
32
+ "css-loader": "6.7.1",
33
+ "css-minimizer-webpack-plugin": "4.0.0",
34
+ "eslint": "8.18.0",
35
+ "globby": "^11.0.2",
36
+ "html-webpack-plugin": "5.5.0",
37
+ "inline-chunk-html-plugin": "1.1.1",
38
+ "less": "4.1.3",
39
+ "less-loader": "11.0.0",
40
+ "mini-css-extract-plugin": "2.6.1",
41
+ "postcss-loader": "7.0.0",
42
+ "style-loader": "3.3.1",
43
+ "terser-webpack-plugin": "5.3.3",
44
+ "typescript": "4.7.4",
45
+ "webpack": "5.73.0",
46
+ "webpack-cli": "4.10.0",
47
+ "webpack-dev-server": "4.9.2",
48
+ "webpack-merge": "5.8.0",
49
+ "webpackbar": "5.0.2"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ },
54
+ "gitHead": "c1b38f181a2441de24a46e530fa1a4d36ad43222"
55
+ }