├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .travis.yml ├── LICENSE.md ├── README.md ├── npm-shrinkwrap.json ├── package.json ├── src └── index.js └── test ├── node_modules └── npm-component │ ├── npm-component.js │ ├── npm-component.json │ ├── npm-component.wxml │ └── npm-component.wxss ├── project.config.json ├── src ├── js │ ├── app.js │ ├── app.json │ ├── app.wxss │ ├── components │ │ ├── app-component │ │ │ ├── app-component.js │ │ │ ├── app-component.json │ │ │ ├── app-component.wxml │ │ │ └── app-component.wxss │ │ ├── common-component │ │ │ ├── common-component.js │ │ │ ├── common-component.json │ │ │ ├── common-component.wxml │ │ │ └── common-component.wxss │ │ ├── index-component │ │ │ ├── index-component.js │ │ │ ├── index-component.json │ │ │ └── index-component.wxml │ │ └── log-component │ │ │ ├── log-component.js │ │ │ ├── log-component.json │ │ │ └── log-component.wxml │ ├── images │ │ ├── twitter.png │ │ ├── twitter_selected.png │ │ ├── wechat.png │ │ └── wechat_selected.png │ ├── pages │ │ ├── index │ │ │ ├── index.js │ │ │ ├── index.json │ │ │ ├── index.wxml │ │ │ └── index.wxss │ │ └── logs │ │ │ ├── logs.js │ │ │ ├── logs.json │ │ │ ├── logs.wxml │ │ │ └── logs.wxss │ ├── subPackages │ │ ├── independent │ │ │ ├── route.js │ │ │ ├── route.wxml │ │ │ └── route.wxss │ │ └── product │ │ │ ├── images │ │ │ └── test.png │ │ │ ├── product.service.js │ │ │ ├── productDetail.js │ │ │ ├── productDetail.json │ │ │ ├── productDetail.wxml │ │ │ ├── productDetail.wxss │ │ │ ├── productList.js │ │ │ ├── productList.json │ │ │ ├── productList.wxml │ │ │ └── productList.wxss │ └── utils │ │ ├── bomPolyfill.js │ │ └── util.js └── ts │ ├── app.json │ ├── app.ts │ ├── app.wxss │ ├── components │ ├── common-component │ │ ├── common-component.json │ │ ├── common-component.ts │ │ ├── common-component.wxml │ │ └── common-component.wxss │ ├── index-component │ │ ├── index-component.json │ │ ├── index-component.ts │ │ └── index-component.wxml │ └── log-component │ │ ├── log-component.json │ │ ├── log-component.ts │ │ └── log-component.wxml │ ├── images │ ├── twitter.png │ ├── twitter_selected.png │ ├── wechat.png │ └── wechat_selected.png │ ├── pages │ ├── index │ │ ├── index.json │ │ ├── index.ts │ │ ├── index.wxml │ │ └── index.wxss │ ├── logs │ │ ├── logs.json │ │ ├── logs.ts │ │ ├── logs.wxml │ │ └── logs.wxss │ └── product │ │ ├── productDetail.json │ │ ├── productDetail.ts │ │ ├── productDetail.wxml │ │ ├── productDetail.wxss │ │ ├── productList.json │ │ ├── productList.ts │ │ ├── productList.wxml │ │ └── productList.wxss │ ├── subPackages │ └── product │ │ ├── folder │ │ └── test.js │ │ ├── images │ │ └── test.png │ │ ├── product.service.js │ │ ├── productDetail.js │ │ ├── productDetail.json │ │ ├── productDetail.wxml │ │ ├── productDetail.wxss │ │ ├── productList.js │ │ ├── productList.json │ │ ├── productList.wxml │ │ └── productList.wxss │ └── utils │ ├── bomPolyfill.js │ └── util.ts ├── test.js └── webpack.config.babel.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env" 5 | ] 6 | ], 7 | "plugins": [ 8 | "@babel/plugin-transform-runtime", 9 | [ 10 | "@babel/plugin-transform-destructuring", 11 | { 12 | "loose": false 13 | } 14 | ], 15 | "@babel/plugin-transform-modules-commonjs", 16 | "@babel/plugin-transform-regenerator", 17 | "@babel/plugin-transform-async-to-generator", 18 | "@babel/plugin-transform-parameters", 19 | "@babel/plugin-transform-spread", 20 | "@babel/plugin-proposal-class-properties", 21 | "@babel/plugin-proposal-json-strings", 22 | [ 23 | "@babel/plugin-proposal-decorators", 24 | { 25 | "legacy": true 26 | } 27 | ], 28 | "@babel/plugin-proposal-function-sent", 29 | "@babel/plugin-proposal-numeric-separator", 30 | "@babel/plugin-proposal-throw-expressions", 31 | "@babel/plugin-proposal-logical-assignment-operators", 32 | "@babel/plugin-proposal-optional-chaining", 33 | [ 34 | "@babel/plugin-proposal-pipeline-operator", 35 | { 36 | "proposal": "minimal" 37 | } 38 | ], 39 | "@babel/plugin-proposal-nullish-coalescing-operator", 40 | "@babel/plugin-proposal-do-expressions", 41 | "@babel/plugin-proposal-function-bind" 42 | ] 43 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | 9 | [*.{js,jsx,css,less,scss}] 10 | indent_style = tab 11 | tab_width = 2 12 | trim_trailing_whitespace = true 13 | 14 | [*.{html,hbr,rt,sass}] 15 | indent_style = space 16 | indent_size = 2 17 | trim_trailing_whitespace = true 18 | 19 | [*.json] 20 | indent_style = space 21 | indent_size = 2 22 | trim_trailing_whitespace = true 23 | 24 | [*.md] 25 | trim_trailing_whitespace = false 26 | indent_size = 2 27 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "plugin:promise/recommended", 6 | "plugin:import/recommended" 7 | ], 8 | "env": { 9 | "browser": true, 10 | "node": true, 11 | "es6": true, 12 | "mocha": true, 13 | "jest": true, 14 | }, 15 | "rules": { 16 | "import/no-absolute-path": 2, 17 | "import/no-extraneous-dependencies": 2, 18 | "import/no-mutable-exports": 2, 19 | "import/newline-after-import": 1, 20 | "import/unambiguous": 0, 21 | 22 | "promise/avoid-new": 0, 23 | "promise/no-callback-in-promise": 0, 24 | "promise/always-return": 0, 25 | 26 | "semi": [1, "always"], 27 | "no-tabs": 0, 28 | "comma-dangle": 0, 29 | "indent": [2, "tab", { 30 | "SwitchCase": 1 31 | }], 32 | "padded-blocks": 0, 33 | "space-before-function-paren": [1, { 34 | "anonymous": "always", 35 | "named": "never" 36 | }], 37 | "max-len": [1, { 38 | "code": 120, 39 | "tabWidth": 2, 40 | "ignoreComments": true, 41 | "ignoreStrings": true, 42 | "ignoreUrls": true, 43 | "ignoreRegExpLiterals": true 44 | }], 45 | "brace-style": 0, 46 | "operator-linebreak": [1, "after"], 47 | "camelcase": 0, 48 | "no-multiple-empty-lines": [1, { 49 | "max": 2 50 | }], 51 | "no-unused-vars": [1, { 52 | "vars": "all", 53 | "args": "after-used", 54 | "caughtErrors": "none", 55 | "ignoreRestSiblings": true 56 | }], 57 | "spaced-comment": 0 58 | }, 59 | "globals": { 60 | "__DEV__": true, 61 | "App": true, 62 | "Page": true, 63 | "Component": true, 64 | "wx": true, 65 | "getApp": true 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .DS_Store? 3 | ._* 4 | .Spotlight-V100 5 | .Trashes 6 | ehthumbs.db 7 | Thumbs.db 8 | .log 9 | *.log 10 | node_modules 11 | dist 12 | lib 13 | .idea 14 | .vscode 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | language: node_js 7 | 8 | os: 9 | - osx 10 | - linux 11 | - windows 12 | 13 | node_js: 14 | - 8 15 | 16 | cache: 17 | directories: 18 | - "node_modules" 19 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Webb Peng 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-webpack-plugin 2 | 3 | 微信小程序 webpack 插件 4 | 5 | 6 | ###### 为什么要使用 webpack 7 | 8 | - 支持通过 `yarn` 或 `npm` 引入和使用 `node_modules` 模块 9 | - 支持丰富且灵活的 `loaders` 和 `plugins` 10 | - 支持 `alias` 11 | - 支持 `sass、less` 12 | - 支持引入npm自定义组件 13 | 14 | 15 | ###### 为什么要使用这个插件 16 | 17 | - 微信小程序开发需要有多个入口文件(如 `app.js`, `app.json`, `pages/index/index.js` 等等),使用这个插件只需要引入 `app.js` 即可,其余文件将会被自动引入 18 | - 若多个入口文件(如 `pages/index/index.js` 和 `pages/logs/logs.js`)引入有相同的模块,这个插件能避免重复打包相同模块 19 | - 支持自动复制 `app.json` 上的 `tabbar` 图片`json`文件 20 | - 支持分包加载,自动提取分析`common.js`到分包root目录下 21 | 22 | 23 | ## 使用方法 24 | 25 | #### 安装 26 | 27 | ```bash 28 | npm i -S miniprogram-webpack-plugin 29 | ``` 30 | 31 | #### 配置 webpack 32 | 33 | 1. 在 `entry` 上引入 `{ app: './src/app.js' }`, 这里的 `./src/app.js` 为微信小程序开发所需的 `app.js`。**注意** `key` 必须为 `app`,`value`为`app.js`文件) 34 | 35 | 2. 在 `output` 上设置 `filename: '[name].js'。` **注意** 这里 `[name].js` 是因为 `webpack` 将会打包生成多个文件,文件名称将以 `[name]` 规则来输出 36 | 37 | 3. 添加 `new MiniProgramWebpackPlugin()` 到 `plugins` 38 | 39 | **完整的项目开发脚手架,请查看 [quickstart-miniprogram](https://github.com/fupengl/quickstart-miniprogram.git)** 40 | 41 | 42 | ## API 43 | 44 | #### MiniProgramWebpackPlugin 45 | 46 | ###### 用法 47 | 48 | webpack.config.babel.js 49 | 50 | ```js 51 | import MiniProgramWebpackPlugin from 'miniprogram-webpack-plugin'; 52 | export default { 53 | // ...configs, 54 | plugins: [ 55 | // ...other, 56 | new MiniProgramWebpackPlugin(options) 57 | ], 58 | }; 59 | ``` 60 | 61 | 62 | ###### Options 63 | 64 | 所有 `Options` 均为可选 65 | 66 | - `clear` (\): 在启动 `webpack` 时清空 `output` 目录。默认为 `true` 67 | - `extensions` (\\>): 脚本文件后缀名。默认为 `['.js','ts']` 68 | - `include` (\\>): 静态资源目录。eg:\[" /assets/**/\* "\] 69 | - `exclude` (\\>): 排除静态资源目录。eg:\[" /doc/**/\* "\] 70 | 71 | 72 | ## 提示 73 | 74 | - 程序的开发方式与 [微信小程序开发文档](https://mp.weixin.qq.com/debug/wxadoc/dev/) 一样,开发者需要在 `src` (源)目录创建 `app.js`、`app.json`、`app.wxss`、`pages/index/index.js` 之类的文件进行开发 75 | - 引入node_modules的包,只需要`usingComponents`中对应组件增加`/npm-components`这个前缀,打包出来会提取`node_modules`中的组定义组件到输出目录`npm-components`文件夹 76 | 77 | **** 78 | 79 | ## License 80 | 81 | MIT © fupengl 82 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-webpack-plugin", 3 | "version": "0.1.24", 4 | "description": "微信小程序 Webpack 插件", 5 | "scripts": { 6 | "start": "jest test --watch", 7 | "test": "jest test", 8 | "test:webpack": "cd test && webpack --mode=development --progress", 9 | "test:webpack:prod": "cd test && webpack --mode=production --progress", 10 | "test:webpack:ts": "cd test && TEST_EXT=ts webpack --progress", 11 | "lint": "eslint --ext .js src/ --fix", 12 | "prebuild": "rimraf ./lib && mkdirp ./lib", 13 | "build:watch": "babel src -o lib/index.js -w", 14 | "build": "npx babel src --out-dir lib", 15 | "preversion": "npm test && npm build", 16 | "prepublish": "npm run build" 17 | }, 18 | "main": "lib/index.js", 19 | "files": [ 20 | "lib", 21 | "*.md" 22 | ], 23 | "keywords": [ 24 | "miniprogram", 25 | "weapp", 26 | "webpack", 27 | "webpack-plugin", 28 | "miniprogram-webpack", 29 | "wxapp-webpack" 30 | ], 31 | "author": "fupengl", 32 | "repository": "fupengl/miniprogram-webpack-plugin", 33 | "license": "MIT", 34 | "devDependencies": { 35 | "@babel/cli": "^7.1.5", 36 | "@babel/core": "^7.1.6", 37 | "@babel/plugin-proposal-class-properties": "^7.1.0", 38 | "@babel/plugin-proposal-decorators": "^7.1.6", 39 | "@babel/plugin-proposal-do-expressions": "^7.0.0", 40 | "@babel/plugin-proposal-function-bind": "^7.0.0", 41 | "@babel/plugin-proposal-function-sent": "^7.1.0", 42 | "@babel/plugin-proposal-logical-assignment-operators": "^7.0.0", 43 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", 44 | "@babel/plugin-proposal-numeric-separator": "^7.0.0", 45 | "@babel/plugin-proposal-optional-chaining": "^7.0.0", 46 | "@babel/plugin-proposal-pipeline-operator": "^7.0.0", 47 | "@babel/plugin-proposal-throw-expressions": "^7.0.0", 48 | "@babel/plugin-syntax-dynamic-import": "^7.0.0", 49 | "@babel/plugin-syntax-import-meta": "^7.0.0", 50 | "@babel/plugin-transform-async-to-generator": "^7.1.0", 51 | "@babel/plugin-transform-destructuring": "^7.1.3", 52 | "@babel/plugin-transform-modules-commonjs": "^7.1.0", 53 | "@babel/plugin-transform-regenerator": "^7.0.0", 54 | "@babel/plugin-transform-runtime": "^7.1.0", 55 | "@babel/preset-env": "^7.1.6", 56 | "babel-eslint": "^10.0.1", 57 | "babel-loader": "^8.0.4", 58 | "eslint": "^5.9.0", 59 | "eslint-config-standard": "^12.0.0", 60 | "eslint-plugin-import": "^2.14.0", 61 | "eslint-plugin-node": "^8.0.0", 62 | "eslint-plugin-promise": "^4.0.1", 63 | "eslint-plugin-standard": "^4.0.0", 64 | "file-loader": "^3.0.1", 65 | "url-loader": "^1.1.2" 66 | }, 67 | "dependencies": { 68 | "@babel/register": "^7.0.0", 69 | "@babel/runtime": "^7.4.4", 70 | "copy-webpack-plugin": "^5.0.3", 71 | "fs-extra": "^7.0.1", 72 | "globby": "^8.0.1", 73 | "webpack": "^4.26.1", 74 | "webpack-cli": "^3.1.2", 75 | "webpack-sources": "^1.3.0" 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fsExtra = require("fs-extra"); 3 | const globby = require("globby"); 4 | const SingleEntryPlugin = require("webpack/lib/SingleEntryPlugin"); 5 | const MultiEntryPlugin = require("webpack/lib/MultiEntryPlugin"); 6 | const { optimize } = require("webpack"); 7 | const LoaderTargetPlugin = require("webpack/lib/LoaderTargetPlugin"); 8 | const FunctionModulePlugin = require("webpack/lib/FunctionModulePlugin"); 9 | const NodeSourcePlugin = require("webpack/lib/node/NodeSourcePlugin"); 10 | const JsonpTemplatePlugin = require("webpack/lib/web/JsonpTemplatePlugin"); 11 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 12 | const { ConcatSource } = require("webpack-sources"); 13 | 14 | const pluginName = "MiniProgramWebpackPlugin"; 15 | module.exports = class MiniProgramWebpackPlugin { 16 | constructor(options = {}) { 17 | this.options = Object.assign( 18 | {}, 19 | { 20 | clear: true, 21 | extensions: [".js", ".ts"], // script ext 22 | include: [], // include assets file 23 | exclude: [], // ignore assets file 24 | assetsChunkName: "__assets_chunk__", 25 | commonsChunkName: "commons", 26 | vendorChunkName: "vendor", 27 | runtimeChunkName: "runtime" 28 | }, 29 | options 30 | ); 31 | } 32 | 33 | apply(compiler) { 34 | this.setBasePath(compiler); 35 | this.enforceTarget(compiler); 36 | 37 | const catchError = handler => async arg => { 38 | try { 39 | await handler(arg); 40 | } catch (err) { 41 | console.warn(err); 42 | } 43 | }; 44 | 45 | compiler.hooks.run.tapPromise( 46 | pluginName, 47 | catchError(compiler => this.setAppEntries(compiler)) 48 | ); 49 | 50 | compiler.hooks.watchRun.tapPromise( 51 | pluginName, 52 | catchError(compiler => this.setAppEntries(compiler)) 53 | ); 54 | 55 | compiler.hooks.compilation.tap( 56 | pluginName, 57 | catchError(compilation => this.compilationHooks(compilation)) 58 | ); 59 | 60 | compiler.hooks.emit.tapPromise( 61 | pluginName, 62 | catchError(async compilation => { 63 | const { clear } = this.options; 64 | if (clear && !this.firstClean) { 65 | this.firstClean = true; 66 | await MiniProgramWebpackPlugin.clearOutPut(compilation); 67 | } 68 | await this.emitAssetsFile(compilation); 69 | }) 70 | ); 71 | } 72 | 73 | compilationHooks(compilation) { 74 | compilation.chunkTemplate.hooks.renderWithEntry.tap( 75 | pluginName, 76 | (modules, chunk) => { 77 | const children = modules.listMap().children; 78 | const generatedCode = 79 | children[Object.keys(children).pop()].generatedCode; 80 | const requireModule = JSON.parse( 81 | generatedCode.substring( 82 | generatedCode.indexOf(",") + 2, 83 | generatedCode.length - 3 84 | ) 85 | ).slice(1); 86 | const source = new ConcatSource(modules); 87 | requireModule.forEach(module => { 88 | if (this.chunkMap[module]) { 89 | const chunkName = chunk.name; 90 | source.add( 91 | `;require("${path 92 | .relative(path.dirname(chunkName), this.chunkMap[module]) 93 | .replace(/\\/g, "/")}")` 94 | ); 95 | } 96 | }); 97 | return source; 98 | } 99 | ); 100 | 101 | compilation.hooks.afterOptimizeChunkIds.tap(pluginName, chunks => { 102 | this.chunkMap = chunks.reduce((acc, item) => { 103 | acc[item.id] = item.name; 104 | return acc; 105 | }, {}); 106 | }); 107 | 108 | // splice assets module 109 | compilation.hooks.beforeChunkAssets.tap(pluginName, () => { 110 | const assetsChunkIndex = compilation.chunks.findIndex( 111 | ({ name }) => name === this.options.assetsChunkName 112 | ); 113 | if (assetsChunkIndex > -1) { 114 | compilation.chunks.splice(assetsChunkIndex, 1); 115 | } 116 | }); 117 | } 118 | 119 | setBasePath(compiler) { 120 | const appEntry = compiler.options.entry.app; 121 | if (!appEntry) { 122 | throw new TypeError("Entry invalid."); 123 | } 124 | this.basePath = path.resolve(path.dirname(appEntry)); 125 | } 126 | 127 | async enforceTarget(compiler) { 128 | const { options } = compiler; 129 | // set jsonp obj motuned obj 130 | options.output.globalObject = "global"; 131 | options.node = { 132 | ...(options.node || {}), 133 | global: false 134 | }; 135 | 136 | // set target to web 137 | options.target = compiler => { 138 | new JsonpTemplatePlugin(options.output).apply(compiler); 139 | new FunctionModulePlugin(options.output).apply(compiler); 140 | new NodeSourcePlugin(options.node).apply(compiler); 141 | new LoaderTargetPlugin("web").apply(compiler); 142 | }; 143 | } 144 | 145 | async setAppEntries(compiler) { 146 | this.npmComponts = new Set(); 147 | this.appEntries = await this.resolveAppEntries(); 148 | await Promise.all([ 149 | this.addScriptEntry(compiler), 150 | this.addAssetsEntries(compiler) 151 | ]); 152 | this.applyPlugin(compiler); 153 | } 154 | 155 | // resolve tabbar page compoments 156 | async resolveAppEntries() { 157 | const { tabBar = {}, pages = [], subpackages = [] } = fsExtra.readJSONSync( 158 | path.resolve(this.basePath, "app.json") 159 | ); 160 | 161 | let tabBarAssets = new Set(); 162 | let components = new Set(); 163 | let subPageRoots = []; 164 | let independentPageRoots = []; 165 | this.subpackRoot = []; 166 | 167 | for (const { iconPath, selectedIconPath } of tabBar.list || []) { 168 | if (iconPath) { 169 | tabBarAssets.add(iconPath); 170 | } 171 | if (selectedIconPath) { 172 | tabBarAssets.add(selectedIconPath); 173 | } 174 | } 175 | 176 | // parse subpage 177 | for (const subPage of subpackages) { 178 | subPageRoots.push(subPage.root); 179 | if (subPage.independent) { 180 | independentPageRoots.push(subPage.root); 181 | } 182 | for (const page of subPage.pages || []) { 183 | pages.push(path.join(subPage.root, page)); 184 | } 185 | } 186 | 187 | // add app.[ts/js] 188 | pages.push("app"); 189 | 190 | // resolve page components 191 | for (const page of pages) { 192 | await this.getComponents(components, path.resolve(this.basePath, page)); 193 | } 194 | 195 | components = Array.from(components) || []; 196 | tabBarAssets = Array.from(tabBarAssets) || []; 197 | 198 | const ret = [...pages, ...components]; 199 | Object.defineProperties(ret, { 200 | pages: { 201 | get: () => pages 202 | }, 203 | components: { 204 | get: () => components 205 | }, 206 | tabBarAssets: { 207 | get: () => tabBarAssets 208 | }, 209 | subPageRoots: { 210 | get: () => subPageRoots 211 | }, 212 | independentPageRoots: { 213 | get: () => independentPageRoots 214 | } 215 | }); 216 | return ret; 217 | } 218 | 219 | // code splite 220 | applyPlugin(compiler) { 221 | const { 222 | runtimeChunkName, 223 | commonsChunkName, 224 | vendorChunkName 225 | } = this.options; 226 | const subpackRoots = this.appEntries.subPageRoots; 227 | const independentPageRoots = this.appEntries.independentPageRoots; 228 | 229 | new optimize.RuntimeChunkPlugin({ 230 | name({ name }) { 231 | const index = independentPageRoots.findIndex(item => 232 | name.includes(item) 233 | ); 234 | if (index !== -1) { 235 | return path.join(independentPageRoots[index], runtimeChunkName); 236 | } 237 | return runtimeChunkName; 238 | } 239 | }).apply(compiler); 240 | 241 | new optimize.SplitChunksPlugin({ 242 | hidePathInfo: false, 243 | chunks: "async", 244 | minSize: 10000, 245 | minChunks: 1, 246 | maxAsyncRequests: Infinity, 247 | automaticNameDelimiter: "~", 248 | maxInitialRequests: Infinity, 249 | name: true, 250 | cacheGroups: { 251 | default: false, 252 | // node_modules 253 | vendor: { 254 | chunks: "all", 255 | test: /[\\/]node_modules[\\/]/, 256 | name: vendorChunkName, 257 | minChunks: 0 258 | }, 259 | // 其他公用代码 260 | common: { 261 | chunks: "all", 262 | test: /[\\/]src[\\/]/, 263 | minChunks: 2, 264 | name({ context }) { 265 | const index = subpackRoots.findIndex(item => 266 | context.includes(item) 267 | ); 268 | if (index !== -1) { 269 | return path.join(subpackRoots[index], commonsChunkName); 270 | } 271 | return commonsChunkName; 272 | }, 273 | minSize: 0 274 | } 275 | } 276 | }).apply(compiler); 277 | } 278 | 279 | // add script entry 280 | async addScriptEntry(compiler) { 281 | this.appEntries 282 | .filter(resource => resource !== "app") 283 | .forEach(resource => { 284 | if (this.npmComponts.has(resource)) { 285 | new SingleEntryPlugin( 286 | this.basePath, 287 | path.join(process.cwd(), resource), 288 | resource.replace(/node_modules/, "npm-components") 289 | ).apply(compiler); 290 | } else { 291 | const fullPath = this.getFullScriptPath(resource); 292 | if (fullPath) { 293 | new SingleEntryPlugin(this.basePath, fullPath, resource).apply( 294 | compiler 295 | ); 296 | } else { 297 | console.warn(`file ${resource} is exists`); 298 | } 299 | } 300 | }); 301 | } 302 | 303 | // add assets entry 304 | async addAssetsEntries(compiler) { 305 | const { include, exclude, extensions, assetsChunkName } = this.options; 306 | const patterns = this.appEntries 307 | .map(resource => `${resource}.*`) 308 | .concat(include); 309 | const entries = await globby(patterns, { 310 | cwd: this.basePath, 311 | nodir: true, 312 | realpath: false, 313 | ignore: [...extensions.map(ext => `**/*${ext}`), ...exclude], 314 | dot: false 315 | }); 316 | 317 | this.assetsEntry = [...entries, ...this.appEntries.tabBarAssets]; 318 | new MultiEntryPlugin( 319 | this.basePath, 320 | this.assetsEntry, 321 | assetsChunkName 322 | ).apply(compiler); 323 | 324 | const npmAssetsEntry = await globby( 325 | [...this.npmComponts] 326 | .map(resource => `${path.parse(resource).dir}/**/*.*`) 327 | .concat(include), 328 | { 329 | cwd: process.cwd(), 330 | nodir: true, 331 | realpath: false, 332 | ignore: [...extensions.map(ext => `**/*${ext}`), ...exclude], 333 | dot: false 334 | } 335 | ); 336 | new CopyWebpackPlugin( 337 | [ 338 | ...npmAssetsEntry.map(resource => { 339 | return { 340 | from: path.resolve(process.cwd().replace(/\\/g, "/"), resource), 341 | to: resource.replace(/node_modules/, "npm-components") 342 | }; 343 | }) 344 | ], 345 | { 346 | ignore: [...extensions.map(ext => `**/*${ext}`), ...exclude] 347 | } 348 | ).apply(compiler); 349 | } 350 | 351 | async emitAssetsFile(compilation) { 352 | const emitAssets = []; 353 | for (let entry of this.assetsEntry) { 354 | const assets = path.resolve(this.basePath, entry); 355 | if (/\.(sass|scss|css|less|styl)$/.test(assets)) { 356 | continue; 357 | } 358 | const toTmit = async () => { 359 | const stat = await fsExtra.stat(assets); 360 | const source = await fsExtra.readFile(assets); 361 | compilation.assets[entry] = { 362 | size: () => stat.size, 363 | source: () => source 364 | }; 365 | }; 366 | emitAssets.push(toTmit()); 367 | } 368 | await Promise.all(emitAssets); 369 | } 370 | 371 | // parse components 372 | async getComponents(components, instance) { 373 | try { 374 | const { usingComponents = {} } = fsExtra.readJSONSync(`${instance}.json`); 375 | const instanceDir = path.parse(instance).dir; 376 | for (const c of Object.values(usingComponents)) { 377 | if (c.indexOf("plugin://") === 0) { 378 | break; 379 | } 380 | if (c.indexOf("/npm-components") === 0) { 381 | const component = c.replace(/\/npm-components/, "node_modules"); 382 | if (!this.npmComponts.has(component)) { 383 | this.npmComponts.add(component); 384 | components.add(component); 385 | this.getComponents( 386 | components, 387 | path.resolve(process.cwd(), component) 388 | ); 389 | } 390 | break; 391 | } 392 | const component = path.resolve(instanceDir, c); 393 | if (!components.has(component)) { 394 | components.add(path.relative(this.basePath, component)); 395 | await this.getComponents(components, component); 396 | } 397 | } 398 | } catch (error) {} 399 | } 400 | 401 | // script full path 402 | getFullScriptPath(script) { 403 | const { 404 | basePath, 405 | options: { extensions } 406 | } = this; 407 | for (const ext of extensions) { 408 | const fullPath = path.resolve(basePath, script + ext); 409 | if (fsExtra.existsSync(fullPath)) { 410 | return fullPath; 411 | } 412 | } 413 | } 414 | 415 | static async clearOutPut(compilation) { 416 | const { path } = compilation.options.output; 417 | await fsExtra.remove(path); 418 | } 419 | }; 420 | -------------------------------------------------------------------------------- /test/node_modules/npm-component/npm-component.js: -------------------------------------------------------------------------------- 1 | Component({}); 2 | -------------------------------------------------------------------------------- /test/node_modules/npm-component/npm-component.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } 4 | -------------------------------------------------------------------------------- /test/node_modules/npm-component/npm-component.wxml: -------------------------------------------------------------------------------- 1 | npm-component 2 | -------------------------------------------------------------------------------- /test/node_modules/npm-component/npm-component.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/node_modules/npm-component/npm-component.wxss -------------------------------------------------------------------------------- /test/project.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "description": "Project configuration file", 3 | "packOptions": { 4 | "ignore": [] 5 | }, 6 | "setting": { 7 | "urlCheck": true, 8 | "es6": true, 9 | "postcss": false, 10 | "minified": false, 11 | "newFeature": true, 12 | "nodeModules": false, 13 | "checkInvalidKey": true 14 | }, 15 | "compileType": "miniprogram", 16 | "libVersion": "2.4.1", 17 | "appid": "wxe8440a52dab9bd67", 18 | "projectname": "miniprogran-webpack-plugin", 19 | "miniprogramRoot": "dist/js/", 20 | "debugOptions": { 21 | "hidedInDevtools": [] 22 | }, 23 | "scripts": {}, 24 | "simulatorType": "wechat", 25 | "simulatorPluginLibVersion": {}, 26 | "condition": { 27 | "search": { 28 | "current": -1, 29 | "list": [] 30 | }, 31 | "conversation": { 32 | "current": -1, 33 | "list": [] 34 | }, 35 | "plugin": { 36 | "current": -1, 37 | "list": [] 38 | }, 39 | "game": { 40 | "list": [] 41 | }, 42 | "miniprogram": { 43 | "current": -1, 44 | "list": [] 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /test/src/js/app.js: -------------------------------------------------------------------------------- 1 | import { formatTime } from './utils/util'; 2 | 3 | App({ 4 | onLaunch() { 5 | 6 | console.log(formatTime(new Date())); 7 | 8 | //调用API从本地缓存中获取数据 9 | var logs = wx.getStorageSync('logs') || []; 10 | logs.unshift(Date.now()); 11 | wx.setStorageSync('logs', logs); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /test/src/js/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages":[ 3 | "pages/index/index", 4 | "pages/logs/logs" 5 | ], 6 | "tabBar": { 7 | "list": [{ 8 | "pagePath": "pages/index/index", 9 | "iconPath": "images/wechat.png", 10 | "selectedIconPath": "images/wechat_selected.png", 11 | "text": "首页" 12 | }, { 13 | "pagePath": "pages/logs/logs", 14 | "iconPath": "images/twitter.png", 15 | "selectedIconPath": "images/twitter_selected.png", 16 | "text": "日志" 17 | }] 18 | }, 19 | "subpackages": [ 20 | { 21 | "root": "subPackages/product", 22 | "pages": [ 23 | "productDetail", 24 | "productList" 25 | ] 26 | }, 27 | { 28 | "root": "subPackages/independent", 29 | "pages": [ 30 | "route" 31 | ], 32 | "independent": true 33 | } 34 | ], 35 | "window":{ 36 | "backgroundTextStyle":"light", 37 | "navigationBarBackgroundColor": "#fff", 38 | "navigationBarTitleText": "WeChat", 39 | "navigationBarTextStyle":"black" 40 | }, 41 | "usingComponents": { 42 | "app-component": "./components/app-component/app-component" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/src/js/app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | .container { 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: space-between; 8 | padding: 200rpx 0; 9 | box-sizing: border-box; 10 | } 11 | -------------------------------------------------------------------------------- /test/src/js/components/app-component/app-component.js: -------------------------------------------------------------------------------- 1 | Component({}); 2 | -------------------------------------------------------------------------------- /test/src/js/components/app-component/app-component.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } -------------------------------------------------------------------------------- /test/src/js/components/app-component/app-component.wxml: -------------------------------------------------------------------------------- 1 | app-component -------------------------------------------------------------------------------- /test/src/js/components/app-component/app-component.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/js/components/app-component/app-component.wxss -------------------------------------------------------------------------------- /test/src/js/components/common-component/common-component.js: -------------------------------------------------------------------------------- 1 | Component({}) 2 | -------------------------------------------------------------------------------- /test/src/js/components/common-component/common-component.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } 4 | -------------------------------------------------------------------------------- /test/src/js/components/common-component/common-component.wxml: -------------------------------------------------------------------------------- 1 | Common Component loaded. 2 | -------------------------------------------------------------------------------- /test/src/js/components/common-component/common-component.wxss: -------------------------------------------------------------------------------- 1 | .common-text { 2 | color: grey; 3 | } 4 | -------------------------------------------------------------------------------- /test/src/js/components/index-component/index-component.js: -------------------------------------------------------------------------------- 1 | Component({}) 2 | -------------------------------------------------------------------------------- /test/src/js/components/index-component/index-component.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "common-component": "../common-component/common-component" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/src/js/components/index-component/index-component.wxml: -------------------------------------------------------------------------------- 1 | 2 | 'Index Component loaded.' 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/src/js/components/log-component/log-component.js: -------------------------------------------------------------------------------- 1 | Component({}) 2 | -------------------------------------------------------------------------------- /test/src/js/components/log-component/log-component.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "common-component": "../common-component/common-component" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/src/js/components/log-component/log-component.wxml: -------------------------------------------------------------------------------- 1 | 2 | 'Log Component loaded.' 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/src/js/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/js/images/twitter.png -------------------------------------------------------------------------------- /test/src/js/images/twitter_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/js/images/twitter_selected.png -------------------------------------------------------------------------------- /test/src/js/images/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/js/images/wechat.png -------------------------------------------------------------------------------- /test/src/js/images/wechat_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/js/images/wechat_selected.png -------------------------------------------------------------------------------- /test/src/js/pages/index/index.js: -------------------------------------------------------------------------------- 1 | import { formatTime } from '../../utils/util'; 2 | 3 | Page({ 4 | data: { 5 | motto: 'Hello World', 6 | userInfo: {}, 7 | }, 8 | onLoad() { 9 | console.log(formatTime(new Date())); 10 | }, 11 | bindViewTap() { 12 | wx.navigateTo({ 13 | url: '../../subPackages/logs/logs', 14 | }); 15 | }, 16 | goToSubList() { 17 | wx.navigateTo({ 18 | url: '../../subPackages/product/productList', 19 | }); 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /test/src/js/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "index-component": "../../components/index-component/index-component", 4 | "npm-component": "/npm-components/npm-component/npm-component" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/src/js/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{userInfo.nickName}} 6 | 7 | 8 | {{motto}} 9 | go to subPackages 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/src/js/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | .userinfo { 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | } 7 | 8 | .userinfo-avatar { 9 | width: 128rpx; 10 | height: 128rpx; 11 | margin: 20rpx; 12 | border-radius: 50%; 13 | } 14 | 15 | .userinfo-nickname { 16 | color: #aaa; 17 | } 18 | 19 | .usermotto { 20 | margin-top: 200px; 21 | } -------------------------------------------------------------------------------- /test/src/js/pages/logs/logs.js: -------------------------------------------------------------------------------- 1 | import { formatTime } from '../../utils/util'; 2 | 3 | Page({ 4 | data: { 5 | logs: [], 6 | }, 7 | onLoad() { 8 | console.log(formatTime(new Date())); 9 | 10 | this.setData({ 11 | logs: (wx.getStorageSync('logs') || []).map(function (log) { 12 | return formatTime(new Date(log)); 13 | }), 14 | }); 15 | } 16 | }); 17 | -------------------------------------------------------------------------------- /test/src/js/pages/logs/logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "查看启动日志", 3 | "usingComponents": { 4 | "log-component": "../../components/log-component/log-component" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/src/js/pages/logs/logs.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{index + 1}}. {{log}} 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/src/js/pages/logs/logs.wxss: -------------------------------------------------------------------------------- 1 | .log-list { 2 | display: flex; 3 | flex-direction: column; 4 | padding: 40rpx; 5 | } 6 | .log-item { 7 | margin: 10rpx; 8 | } 9 | -------------------------------------------------------------------------------- /test/src/js/subPackages/independent/route.js: -------------------------------------------------------------------------------- 1 | Page({ 2 | onLoad() { 3 | console.log('route'); 4 | } 5 | }); 6 | -------------------------------------------------------------------------------- /test/src/js/subPackages/independent/route.wxml: -------------------------------------------------------------------------------- 1 | route 2 | -------------------------------------------------------------------------------- /test/src/js/subPackages/independent/route.wxss: -------------------------------------------------------------------------------- 1 | view { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /test/src/js/subPackages/product/images/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/js/subPackages/product/images/test.png -------------------------------------------------------------------------------- /test/src/js/subPackages/product/product.service.js: -------------------------------------------------------------------------------- 1 | export default class Product { 2 | constructor() { 3 | this.name = 'product'; 4 | } 5 | 6 | getProductName() { 7 | return this.name; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /test/src/js/subPackages/product/productDetail.js: -------------------------------------------------------------------------------- 1 | 2 | import { formatTime } from '../../utils/util'; 3 | import Product from './product.service'; 4 | 5 | console.log(Product, '!!!!!!!'); 6 | const productService = new Product(); 7 | 8 | Page({ 9 | data: { 10 | logs: [] 11 | }, 12 | onLoad() { 13 | this.setData({ 14 | logs: (wx.getStorageSync('logs') || []).map(function (log) { 15 | return formatTime(new Date(log)); 16 | }), 17 | productName: productService.getProductName() 18 | }); 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /test/src/js/subPackages/product/productDetail.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "分包明细", 3 | "usingComponents": { 4 | "log-component": "../../components/log-component/log-component" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/src/js/subPackages/product/productDetail.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{index + 1}}. {{log}} 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/src/js/subPackages/product/productDetail.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/js/subPackages/product/productDetail.wxss -------------------------------------------------------------------------------- /test/src/js/subPackages/product/productList.js: -------------------------------------------------------------------------------- 1 | import Product from './product.service'; 2 | 3 | console.log(Product, '!!!!!!!'); 4 | const productService = new Product(); 5 | 6 | require('./images/test.png'); 7 | 8 | Page({ 9 | data: { 10 | motto: 'Hello List', 11 | userInfo: {}, 12 | }, 13 | //事件处理函数 14 | bindViewTap() { 15 | wx.navigateTo({ 16 | url: './productDetail', 17 | }); 18 | }, 19 | onLoad() { 20 | this.setData({ 21 | productName: productService.getProductName() 22 | }); 23 | }, 24 | }); 25 | -------------------------------------------------------------------------------- /test/src/js/subPackages/product/productList.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "index-component": "../../components/index-component/index-component" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/src/js/subPackages/product/productList.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | click me and go to detail page 5 | 6 | {{userInfo.nickName}} 7 | 8 | 9 | {{motto}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | click me and go to detail page 17 | 18 | {{userInfo.nickName}} 19 | 20 | 21 | {{motto}} 22 | 23 | 24 | 25 | 26 | 27 | 28 | click me and go to detail page 29 | 30 | {{userInfo.nickName}} 31 | 32 | 33 | {{motto}} 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/src/js/subPackages/product/productList.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/js/subPackages/product/productList.wxss -------------------------------------------------------------------------------- /test/src/js/utils/bomPolyfill.js: -------------------------------------------------------------------------------- 1 | 2 | if (typeof global === 'object') { 3 | global.Array = Array; 4 | global.DataView = DataView; 5 | global.Date = Date; 6 | global.Error = Error; 7 | global.Float32Array = Float32Array; 8 | global.Float64Array = Float64Array; 9 | global.Function = Function; 10 | global.Int8Array = Int8Array; 11 | global.Int16Array = Int16Array; 12 | global.Int32Array = Int32Array; 13 | global.Math = Math; 14 | global.Object = Object; 15 | global.Promise = Promise; 16 | global.RegExp = RegExp; 17 | global.String = String; 18 | global.TypeError = TypeError; 19 | global.Uint8Array = Uint8Array; 20 | global.Uint8ClampedArray = Uint8ClampedArray; 21 | global.Uint16Array = Uint16Array; 22 | global.Uint32Array = Uint32Array; 23 | global.WeakMap = WeakMap; 24 | global.clearTimeout = clearTimeout; 25 | global.isFinite = isFinite; 26 | global.parseInt = parseInt; 27 | global.setTimeout = setTimeout; 28 | 29 | if (typeof window === 'undefined') { 30 | global.window = global; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/src/js/utils/util.js: -------------------------------------------------------------------------------- 1 | 2 | function formatNumber(n) { 3 | n = n.toString(); 4 | return n[1] ? n : '0' + n; 5 | } 6 | 7 | export function formatTime(date) { 8 | const year = date.getFullYear(); 9 | const month = date.getMonth() + 1; 10 | const day = date.getDate(); 11 | 12 | const hour = date.getHours(); 13 | const minute = date.getMinutes(); 14 | const second = date.getSeconds(); 15 | 16 | return [year, month, day] 17 | .map(formatNumber) 18 | .join('/') + ' ' + 19 | [hour, minute, second].map(formatNumber).join(':') 20 | ; 21 | } 22 | -------------------------------------------------------------------------------- /test/src/ts/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages":[ 3 | "pages/index/index", 4 | "pages/logs/logs" 5 | ], 6 | "tabBar": { 7 | "list": [{ 8 | "pagePath": "pages/index/index", 9 | "iconPath": "images/wechat.png", 10 | "selectedIconPath": "images/wechat_selected.png", 11 | "text": "首页" 12 | }, { 13 | "pagePath": "pages/logs/logs", 14 | "iconPath": "images/twitter.png", 15 | "selectedIconPath": "images/twitter_selected.png", 16 | "text": "日志" 17 | }] 18 | }, 19 | "subPackages": [ 20 | { 21 | "root": "subPackages/product", 22 | "pages": [ 23 | "productDetail", 24 | "productList" 25 | ] 26 | } 27 | ], 28 | "window":{ 29 | "backgroundTextStyle":"light", 30 | "navigationBarBackgroundColor": "#fff", 31 | "navigationBarTitleText": "WeChat", 32 | "navigationBarTextStyle":"black" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test/src/ts/app.ts: -------------------------------------------------------------------------------- 1 | 2 | import { flow } from 'lodash'; 3 | import { formatTime } from 'utils/util'; 4 | 5 | App({ 6 | onLaunch() { 7 | 8 | flow(() => console.log('typeof formatTime', typeof formatTime))(); 9 | 10 | //调用API从本地缓存中获取数据 11 | var logs = wx.getStorageSync('logs') || []; 12 | logs.unshift(Date.now()); 13 | wx.setStorageSync('logs', logs); 14 | }, 15 | getUserInfo(cb) { 16 | if (this.globalData.userInfo) { 17 | typeof cb == 'function' && cb(this.globalData.userInfo); 18 | } else { 19 | //调用登录接口 20 | wx.login({ 21 | success: () => { 22 | wx.getUserInfo({ 23 | success: (res) => { 24 | this.globalData.userInfo = res.userInfo; 25 | typeof cb == 'function' && cb(this.globalData.userInfo); 26 | }, 27 | }); 28 | }, 29 | }); 30 | } 31 | }, 32 | globalData: { 33 | userInfo: null, 34 | }, 35 | }); 36 | -------------------------------------------------------------------------------- /test/src/ts/app.wxss: -------------------------------------------------------------------------------- 1 | /**app.wxss**/ 2 | .container { 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | align-items: center; 7 | justify-content: space-between; 8 | padding: 200rpx 0; 9 | box-sizing: border-box; 10 | } 11 | -------------------------------------------------------------------------------- /test/src/ts/components/common-component/common-component.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true 3 | } 4 | -------------------------------------------------------------------------------- /test/src/ts/components/common-component/common-component.ts: -------------------------------------------------------------------------------- 1 | Component({}) 2 | -------------------------------------------------------------------------------- /test/src/ts/components/common-component/common-component.wxml: -------------------------------------------------------------------------------- 1 | Common Component loaded. 2 | -------------------------------------------------------------------------------- /test/src/ts/components/common-component/common-component.wxss: -------------------------------------------------------------------------------- 1 | .common-text { 2 | color: grey; 3 | } 4 | -------------------------------------------------------------------------------- /test/src/ts/components/index-component/index-component.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "common-component": "../common-component/common-component" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/src/ts/components/index-component/index-component.ts: -------------------------------------------------------------------------------- 1 | Component({}) 2 | -------------------------------------------------------------------------------- /test/src/ts/components/index-component/index-component.wxml: -------------------------------------------------------------------------------- 1 | 2 | 'Index Component loaded.' 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/src/ts/components/log-component/log-component.json: -------------------------------------------------------------------------------- 1 | { 2 | "component": true, 3 | "usingComponents": { 4 | "common-component": "../common-component/common-component" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/src/ts/components/log-component/log-component.ts: -------------------------------------------------------------------------------- 1 | Component({}) 2 | -------------------------------------------------------------------------------- /test/src/ts/components/log-component/log-component.wxml: -------------------------------------------------------------------------------- 1 | 2 | 'Log Component loaded.' 3 | 4 | 5 | -------------------------------------------------------------------------------- /test/src/ts/images/twitter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/ts/images/twitter.png -------------------------------------------------------------------------------- /test/src/ts/images/twitter_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/ts/images/twitter_selected.png -------------------------------------------------------------------------------- /test/src/ts/images/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/ts/images/wechat.png -------------------------------------------------------------------------------- /test/src/ts/images/wechat_selected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/ts/images/wechat_selected.png -------------------------------------------------------------------------------- /test/src/ts/pages/index/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "index-component": "../../components/index-component/index-component" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/src/ts/pages/index/index.ts: -------------------------------------------------------------------------------- 1 | 2 | // import { flow } from 'lodash'; 3 | 4 | // const delay = (t = 0) => new Promise((resolve) => setTimeout(resolve, t)); 5 | 6 | //获取应用实例 7 | const app = getApp(); // eslint-disable-line no-undef 8 | 9 | Page({ 10 | data: { 11 | motto: 'Hello World', 12 | userInfo: {}, 13 | }, 14 | //事件处理函数 15 | bindViewTap() { 16 | wx.navigateTo({ 17 | url: '../logs/logs', 18 | }); 19 | }, 20 | goToSubList() { 21 | wx.navigateTo({ 22 | url: '../product/productList', 23 | }); 24 | }, 25 | onLoad() { 26 | 27 | // await delay(); 28 | 29 | // const log = flow(() => { 30 | // console.log('onLoad'); 31 | // }); 32 | 33 | // log(); 34 | 35 | //调用应用实例的方法获取全局数据 36 | app.getUserInfo((userInfo) => { 37 | //更新数据 38 | this.setData({ userInfo }); 39 | }); 40 | }, 41 | }); 42 | -------------------------------------------------------------------------------- /test/src/ts/pages/index/index.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{userInfo.nickName}} 6 | 7 | 8 | {{motto}} 9 | go to subPackages 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/src/ts/pages/index/index.wxss: -------------------------------------------------------------------------------- 1 | /**index.wxss**/ 2 | .userinfo { 3 | display: flex; 4 | flex-direction: column; 5 | align-items: center; 6 | } 7 | 8 | .userinfo-avatar { 9 | width: 128rpx; 10 | height: 128rpx; 11 | margin: 20rpx; 12 | border-radius: 50%; 13 | } 14 | 15 | .userinfo-nickname { 16 | color: #aaa; 17 | } 18 | 19 | .usermotto { 20 | margin-top: 200px; 21 | } -------------------------------------------------------------------------------- /test/src/ts/pages/logs/logs.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "查看启动日志", 3 | "usingComponents": { 4 | "log-component": "../../components/log-component/log-component" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/src/ts/pages/logs/logs.ts: -------------------------------------------------------------------------------- 1 | 2 | import { formatTime } from '../../utils/util'; 3 | 4 | Page({ 5 | data: { 6 | logs: [], 7 | }, 8 | onLoad() { 9 | this.setData({ 10 | logs: (wx.getStorageSync('logs') || []).map(function (log) { 11 | return formatTime(new Date(log)); 12 | }), 13 | }); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /test/src/ts/pages/logs/logs.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{index + 1}}. {{log}} 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/src/ts/pages/logs/logs.wxss: -------------------------------------------------------------------------------- 1 | .log-list { 2 | display: flex; 3 | flex-direction: column; 4 | padding: 40rpx; 5 | } 6 | .log-item { 7 | margin: 10rpx; 8 | } 9 | -------------------------------------------------------------------------------- /test/src/ts/pages/product/productDetail.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "分包明细", 3 | "usingComponents": { 4 | "log-component": "../../components/log-component/log-component" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/src/ts/pages/product/productDetail.ts: -------------------------------------------------------------------------------- 1 | 2 | import { formatTime } from '../../utils/util'; 3 | 4 | Page({ 5 | data: { 6 | logs: [], 7 | }, 8 | onLoad() { 9 | this.setData({ 10 | logs: (wx.getStorageSync('logs') || []).map(function (log) { 11 | return formatTime(new Date(log)); 12 | }), 13 | }); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /test/src/ts/pages/product/productDetail.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{index + 1}}. {{log}} 5 | 6 | 7 | -------------------------------------------------------------------------------- /test/src/ts/pages/product/productDetail.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/ts/pages/product/productDetail.wxss -------------------------------------------------------------------------------- /test/src/ts/pages/product/productList.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "index-component": "../../components/index-component/index-component" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/src/ts/pages/product/productList.ts: -------------------------------------------------------------------------------- 1 | 2 | // import { flow } from 'lodash'; 3 | 4 | // const delay = (t = 0) => new Promise((resolve) => setTimeout(resolve, t)); 5 | 6 | //获取应用实例 7 | const app = getApp(); // eslint-disable-line no-undef 8 | 9 | Page({ 10 | data: { 11 | motto: 'Hello List', 12 | userInfo: {}, 13 | }, 14 | //事件处理函数 15 | bindViewTap() { 16 | wx.navigateTo({ 17 | url: './productDetail', 18 | }); 19 | }, 20 | onLoad() { 21 | 22 | // await delay(); 23 | 24 | // const log = flow(() => { 25 | // console.log('onLoad'); 26 | // }); 27 | 28 | // log(); 29 | 30 | //调用应用实例的方法获取全局数据 31 | app.getUserInfo((userInfo) => { 32 | //更新数据 33 | this.setData({ userInfo }); 34 | }); 35 | }, 36 | }); 37 | -------------------------------------------------------------------------------- /test/src/ts/pages/product/productList.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | click me and go to detail page 5 | 6 | {{userInfo.nickName}} 7 | 8 | 9 | {{motto}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | click me and go to detail page 17 | 18 | {{userInfo.nickName}} 19 | 20 | 21 | {{motto}} 22 | 23 | 24 | 25 | 26 | 27 | 28 | click me and go to detail page 29 | 30 | {{userInfo.nickName}} 31 | 32 | 33 | {{motto}} 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/src/ts/pages/product/productList.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/ts/pages/product/productList.wxss -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/folder/test.js: -------------------------------------------------------------------------------- 1 | a = 1; -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/images/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/ts/subPackages/product/images/test.png -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/product.service.js: -------------------------------------------------------------------------------- 1 | class Product { 2 | constructor() { 3 | this.name = 'aki product' 4 | } 5 | 6 | getProductName() { 7 | return this.name 8 | } 9 | } -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/productDetail.js: -------------------------------------------------------------------------------- 1 | 2 | import { formatTime } from '../../utils/util'; 3 | import Product from './product.service'; 4 | 5 | console.log(Product, '!!!!!!!') 6 | const productService = new Product(); 7 | 8 | Page({ 9 | data: { 10 | logs: [], 11 | testImage: require('./images/test.png') 12 | }, 13 | onLoad() { 14 | this.setData({ 15 | logs: (wx.getStorageSync('logs') || []).map(function (log) { 16 | return formatTime(new Date(log)); 17 | }), 18 | productName: productService.getProductName() 19 | }); 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/productDetail.json: -------------------------------------------------------------------------------- 1 | { 2 | "navigationBarTitleText": "分包明细", 3 | "usingComponents": { 4 | "log-component": "../../components/log-component/log-component" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/productDetail.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{index + 1}}. {{log}} 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/productDetail.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/ts/subPackages/product/productDetail.wxss -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/productList.js: -------------------------------------------------------------------------------- 1 | 2 | // import { flow } from 'lodash'; 3 | 4 | // const delay = (t = 0) => new Promise((resolve) => setTimeout(resolve, t)); 5 | import Product from './product.service'; 6 | 7 | console.log(Product, '!!!!!!!') 8 | const productService = new Product(); 9 | //获取应用实例 10 | const app = getApp(); // eslint-disable-line no-undef 11 | 12 | Page({ 13 | data: { 14 | motto: 'Hello List', 15 | userInfo: {}, 16 | }, 17 | //事件处理函数 18 | bindViewTap() { 19 | wx.navigateTo({ 20 | url: './productDetail', 21 | }); 22 | }, 23 | onLoad() { 24 | 25 | // await delay(); 26 | 27 | // const log = flow(() => { 28 | // console.log('onLoad'); 29 | // }); 30 | 31 | // log(); 32 | this.setData({ 33 | productName: productService.getProductName() 34 | }) 35 | 36 | //调用应用实例的方法获取全局数据 37 | app.getUserInfo((userInfo) => { 38 | //更新数据 39 | this.setData({ userInfo }); 40 | }); 41 | }, 42 | }); 43 | -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/productList.json: -------------------------------------------------------------------------------- 1 | { 2 | "usingComponents": { 3 | "index-component": "../../components/index-component/index-component" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/productList.wxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | click me and go to detail page 5 | 6 | {{userInfo.nickName}} 7 | 8 | 9 | {{motto}} 10 | 11 | 12 | 13 | 14 | 15 | 16 | click me and go to detail page 17 | 18 | {{userInfo.nickName}} 19 | 20 | 21 | {{motto}} 22 | 23 | 24 | 25 | 26 | 27 | 28 | click me and go to detail page 29 | 30 | {{userInfo.nickName}} 31 | 32 | 33 | {{motto}} 34 | 35 | 36 | -------------------------------------------------------------------------------- /test/src/ts/subPackages/product/productList.wxss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fupengl/miniprogram-webpack-plugin/745f018ed0baca1aa913b3bead52769650b657e1/test/src/ts/subPackages/product/productList.wxss -------------------------------------------------------------------------------- /test/src/ts/utils/bomPolyfill.js: -------------------------------------------------------------------------------- 1 | 2 | if (typeof global === 'object') { 3 | global.Array = Array; 4 | global.DataView = DataView; 5 | global.Date = Date; 6 | global.Error = Error; 7 | global.Float32Array = Float32Array; 8 | global.Float64Array = Float64Array; 9 | global.Function = Function; 10 | global.Int8Array = Int8Array; 11 | global.Int16Array = Int16Array; 12 | global.Int32Array = Int32Array; 13 | global.Math = Math; 14 | global.Object = Object; 15 | global.Promise = Promise; 16 | global.RegExp = RegExp; 17 | global.String = String; 18 | global.TypeError = TypeError; 19 | global.Uint8Array = Uint8Array; 20 | global.Uint8ClampedArray = Uint8ClampedArray; 21 | global.Uint16Array = Uint16Array; 22 | global.Uint32Array = Uint32Array; 23 | global.WeakMap = WeakMap; 24 | global.clearTimeout = clearTimeout; 25 | global.isFinite = isFinite; 26 | global.parseInt = parseInt; 27 | global.setTimeout = setTimeout; 28 | 29 | if (typeof window === 'undefined') { 30 | global.window = global; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/src/ts/utils/util.ts: -------------------------------------------------------------------------------- 1 | 2 | function formatNumber(n) { 3 | n = n.toString(); 4 | return n[1] ? n : '0' + n; 5 | } 6 | 7 | export function formatTime(date) { 8 | const year = date.getFullYear(); 9 | const month = date.getMonth() + 1; 10 | const day = date.getDate(); 11 | 12 | const hour = date.getHours(); 13 | const minute = date.getMinutes(); 14 | const second = date.getSeconds(); 15 | 16 | return [year, month, day] 17 | .map(formatNumber) 18 | .join('/') + ' ' + 19 | [hour, minute, second].map(formatNumber).join(':') 20 | ; 21 | } 22 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 2 | import rimraf from 'rimraf'; 3 | import { execSync } from 'child_process'; 4 | import { resolve } from 'path'; 5 | import { existsSync } from 'fs-extra'; 6 | 7 | const createTest = function createTest(ext) { 8 | try { 9 | const stdout = execSync('webpack', { 10 | cwd: __dirname, 11 | encoding: 'utf8', 12 | env: { 13 | ...process.env, 14 | TEST_EXT: ext, 15 | }, 16 | }); 17 | stdout && console.log(stdout); 18 | } catch (err) { 19 | err.stdout && console.error(err.stdout); 20 | expect(err).toBe(undefined); 21 | } 22 | 23 | global.wx = {}; 24 | global.getApp = jest.fn(); 25 | global.App = jest.fn(); 26 | global.Page = jest.fn(); 27 | 28 | require(`./dist/${ext}/app`); 29 | require(`./dist/${ext}/pages/index/index`); 30 | require(`./dist/${ext}/pages/logs/logs`); 31 | 32 | expect(global.App.mock.calls.length).toBe(1); 33 | expect(global.Page.mock.calls.length).toBe(2); 34 | 35 | const inImagesDir = (name) => resolve(__dirname, `dist/${ext}/images`, name); 36 | expect(existsSync(inImagesDir('wechat.png'))).toBe(true); 37 | expect(existsSync(inImagesDir('wechat_selected.png'))).toBe(true); 38 | expect(existsSync(inImagesDir('twitter.png'))).toBe(true); 39 | expect(existsSync(inImagesDir('twitter_selected.png'))).toBe(true); 40 | }; 41 | 42 | afterEach(() => { 43 | rimraf.sync(resolve('test/dist')); 44 | delete global.wx; 45 | delete global.getApp;q 46 | delete global.App; 47 | delete global.Page; 48 | }); 49 | 50 | test('js', () => { 51 | createTest('js'); 52 | }); 53 | 54 | test('ts', () => { 55 | createTest('ts'); 56 | }); 57 | -------------------------------------------------------------------------------- /test/webpack.config.babel.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const MiniProgramWebpackPlugin = require("../src"); 3 | 4 | const ext = process.env.TEST_EXT || "js"; 5 | 6 | const include = new RegExp("src"); 7 | 8 | module.exports = { 9 | entry: { 10 | app: `./src/${ext}/app.${ext}` 11 | }, 12 | output: { 13 | filename: "[name].js", 14 | path: path.resolve(__dirname, "dist", ext) 15 | }, 16 | module: { 17 | rules: [ 18 | { 19 | test: /\.(ts|js)$/, 20 | include, 21 | loader: "babel-loader", 22 | options: { 23 | presets: ["@babel/preset-env"], 24 | babelrc: false 25 | } 26 | }, 27 | { 28 | test: /\.(woff|woff2|eot|ttf|svg|png|gif|jpeg|jpg|wxs|wxml|wxss)\??.*$/, 29 | include, 30 | type: 'javascript/auto', 31 | use: [ 32 | { 33 | loader: "url-loader", 34 | options: { 35 | limit: 50000 36 | } 37 | } 38 | ] 39 | } 40 | ] 41 | }, 42 | plugins: [new MiniProgramWebpackPlugin()], 43 | devtool: "none", 44 | resolve: { 45 | modules: [`src/${ext}`, "node_modules"], 46 | extensions: [".js", ".ts"], 47 | alias: { 48 | "@": path.resolve(`./src/${ext}`) 49 | } 50 | } 51 | }; 52 | --------------------------------------------------------------------------------