├── .babelrc
├── .eslintrc.json
├── .gitattributes
├── .gitignore
├── .travis.yml
├── CHANGELOG.md
├── LICENSE
├── README.md
├── appveyor.yml
├── package-lock.json
├── package.json
├── scripts
└── createSpecialDirectory.js
├── src
├── copyWebpackPlugin.js
├── extractComponent.js
├── fetchModules.js
├── index.js
├── preProcessPattern.js
├── processPattern.js
├── utils
│ ├── escape.js
│ ├── isObject.js
│ └── promisify.js
└── writeFile.js
├── tests
├── components.test.js
├── helpers
│ ├── [!]
│ │ └── hello.txt
│ ├── binextension.bin
│ ├── directory
│ │ ├── .dottedfile
│ │ ├── directoryfile.txt
│ │ └── nested
│ │ │ └── nestedfile.txt
│ ├── file.txt
│ ├── file.txt.gz
│ └── noextension
├── index.js
├── utils
│ └── removeIllegalCharacterForWindows.js
└── weappHelpers
│ ├── components
│ ├── base
│ │ ├── Dep
│ │ │ └── index.js
│ │ └── Page.js
│ ├── button
│ │ ├── index.js
│ │ ├── index.json
│ │ └── index.wxml
│ ├── comp1
│ │ ├── index.js
│ │ ├── index.json
│ │ └── index.wxml
│ └── panel
│ │ ├── index.js
│ │ ├── index.json
│ │ └── index.wxml
│ ├── entries.js
│ └── pages
│ ├── native
│ ├── index.js
│ ├── index.json
│ └── index.wxml
│ ├── normal
│ └── index.json
│ ├── notexist
│ └── index.json
│ └── self
│ └── index.json
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"]
3 | }
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "node": true,
5 | "mocha": true
6 | },
7 | "extends": "eslint:recommended",
8 | "parserOptions": {
9 | "sourceType": "module"
10 | },
11 | "rules": {
12 | "indent": [
13 | "error",
14 | 4
15 | ],
16 | "linebreak-style": [
17 | "error",
18 | "unix"
19 | ],
20 | "quotes": [
21 | "error",
22 | "single"
23 | ],
24 | "semi": [
25 | "error",
26 | "always"
27 | ]
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
3 | package-lock.json -diff
4 | bin/* eol=lf
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | coverage
3 | dist
4 | *.log
5 | .*
6 | !.gitignore
7 | !.npmignore
8 | !.babelrc
9 | !.travis.yml
10 | !.eslintrc.json
11 | !tests/helpers/directory/.dottedfile
12 | compiled_tests
13 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 |
3 | node_js:
4 | - stable
5 | - lts/*
6 | - 6
7 | - 4
8 |
9 | notifications:
10 | email: false
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 |
6 | ## [4.7.3](https://github.com/JJJYY/import-weapp-component/compare/v4.7.2...v4.7.3) (2018-11-04)
7 |
8 |
9 | ### Features
10 |
11 | * support copy dependencies file ([f557296](https://github.com/JJJYY/import-weapp-component/commit/f557296))
12 |
13 |
14 |
15 |
16 | ## [4.7.2](https://github.com/JJJYY/import-weapp-component/compare/v4.7.0...v4.7.2) (2018-09-16)
17 |
18 |
19 | ### Features
20 |
21 | * 支持 mpvue-entry([#4](https://github.com/JJJYY/import-weapp-component/issues/4)) ([5b0f619](https://github.com/JJJYY/import-weapp-component/commit/5b0f619))
22 |
23 |
24 |
25 |
26 | # [4.7.0](https://github.com/JJJYY/import-weapp-component/compare/v4.5.5...v4.7.0) (2018-08-26)
27 |
28 |
29 | ### Features
30 |
31 | * support mpvue 1.0.12 and native page import ([312aab0](https://github.com/JJJYY/import-weapp-component/commit/312aab0))
32 |
33 |
34 |
35 |
36 | # [4.6.0](https://github.com/JJJYY/import-weapp-component/compare/v4.5.5...v4.6.0) (2018-08-26)
37 |
38 |
39 | ### Features
40 |
41 | * support mpvue 1.0.12 and native page import ([312aab0](https://github.com/JJJYY/import-weapp-component/commit/312aab0))
42 |
43 |
44 |
45 |
46 | ## [4.5.5](https://github.com/JJJYY/import-weapp-component/compare/v4.5.1...v4.5.5) (2018-07-06)
47 |
48 |
49 | ### Bug Fixes
50 |
51 | * [#2](https://github.com/JJJYY/import-weapp-component/issues/2) native dependencies not bundle ([84a67e1](https://github.com/JJJYY/import-weapp-component/commit/84a67e1))
52 | * [#3](https://github.com/JJJYY/import-weapp-component/issues/3) relative path import ([93aa211](https://github.com/JJJYY/import-weapp-component/commit/93aa211))
53 | * add import component entry ([499c755](https://github.com/JJJYY/import-weapp-component/commit/499c755))
54 | * allow square brackets in path ([#264](https://github.com/JJJYY/import-weapp-component/issues/264)) ([3ef5b6c](https://github.com/JJJYY/import-weapp-component/commit/3ef5b6c)), closes [#231](https://github.com/JJJYY/import-weapp-component/issues/231)
55 | * fix copy webpack test ([331c667](https://github.com/JJJYY/import-weapp-component/commit/331c667))
56 | * keep faces together with weapp import component ([5ab2bd7](https://github.com/JJJYY/import-weapp-component/commit/5ab2bd7))
57 | * windows get file dir from path ([deaa923](https://github.com/JJJYY/import-weapp-component/commit/deaa923))
58 |
59 |
60 | ### Features
61 |
62 | * add auto copy component to output ([f3e48fe](https://github.com/JJJYY/import-weapp-component/commit/f3e48fe))
63 | * throw error when path not exist ([94aa056](https://github.com/JJJYY/import-weapp-component/commit/94aa056))
64 |
65 |
66 |
67 |
68 | ## [4.5.1](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.5.0...v4.5.1) (2018-03-09)
69 |
70 |
71 | ### Bug Fixes
72 |
73 | * **package:** update `cacache` v10.0.1...10.0.4 (`dependencies`) ([#238](https://github.com/webpack-contrib/copy-webpack-plugin/issues/238)) ([0b288f9](https://github.com/webpack-contrib/copy-webpack-plugin/commit/0b288f9))
74 |
75 |
76 | ### Performance Improvements
77 |
78 | * **index:** switch to `md4` for content hashing ([#239](https://github.com/webpack-contrib/copy-webpack-plugin/issues/239)) ([2be8191](https://github.com/webpack-contrib/copy-webpack-plugin/commit/2be8191))
79 |
80 |
81 |
82 |
83 | # [4.5.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.4.3...v4.5.0) (2018-03-02)
84 |
85 |
86 | ### Features
87 |
88 | * **processPattern:** add support for `{RegExp)` matches (`pattern.test`) ([#235](https://github.com/webpack-contrib/copy-webpack-plugin/issues/235)) ([1861730](https://github.com/webpack-contrib/copy-webpack-plugin/commit/1861730))
89 |
90 |
91 |
92 |
93 | ## [4.4.3](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.4.2...v4.4.3) (2018-03-01)
94 |
95 |
96 | ### Bug Fixes
97 |
98 | * **index:** `tapable` deprecation warnings (`webpack >= v4.0.0`) ([#234](https://github.com/webpack-contrib/copy-webpack-plugin/issues/234)) ([445d548](https://github.com/webpack-contrib/copy-webpack-plugin/commit/445d548))
99 |
100 |
101 |
102 |
103 | ## [4.4.2](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.4.1...v4.4.2) (2018-02-23)
104 |
105 |
106 | ### Bug Fixes
107 |
108 | * **src/:** don't escape non-glob patterns ([#230](https://github.com/webpack-contrib/copy-webpack-plugin/issues/230)) ([0eb2cd5](https://github.com/webpack-contrib/copy-webpack-plugin/commit/0eb2cd5))
109 |
110 |
111 |
112 |
113 | ## [4.4.1](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.4.0...v4.4.1) (2018-02-08)
114 |
115 |
116 | ### Bug Fixes
117 |
118 | * replace `pify` with simpler promise helpers ([#221](https://github.com/webpack-contrib/copy-webpack-plugin/issues/221)) ([dadac24](https://github.com/webpack-contrib/copy-webpack-plugin/commit/dadac24))
119 |
120 |
121 |
122 |
123 | # [4.4.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.3.1...v4.4.0) (2018-02-08)
124 |
125 |
126 | ### Bug Fixes
127 |
128 | * **package:** add `prepare` script ([9bf0d99](https://github.com/webpack-contrib/copy-webpack-plugin/commit/9bf0d99))
129 | * **preProcessPatterns:** support glob context paths with special characters ([#208](https://github.com/webpack-contrib/copy-webpack-plugin/issues/208)) ([ea0c05f](https://github.com/webpack-contrib/copy-webpack-plugin/commit/ea0c05f))
130 | * support `webpack >= v4.0.0` ([6a16b3c](https://github.com/webpack-contrib/copy-webpack-plugin/commit/6a16b3c))
131 |
132 |
133 | ### Features
134 |
135 | * use `compiler.inputFileSystem` instead `fs` ([#205](https://github.com/webpack-contrib/copy-webpack-plugin/issues/205)) ([158f821](https://github.com/webpack-contrib/copy-webpack-plugin/commit/158f821))
136 |
137 |
138 |
139 |
140 | ## [4.3.1](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.3.0...v4.3.1) (2017-12-22)
141 |
142 |
143 | ### Bug Fixes
144 |
145 | * `cache` behaviour ([#196](https://github.com/webpack-contrib/copy-webpack-plugin/issues/196)) ([6beb89e](https://github.com/webpack-contrib/copy-webpack-plugin/commit/6beb89e))
146 | * `cache` option behaviour ([3b088d0](https://github.com/webpack-contrib/copy-webpack-plugin/commit/3b088d0))
147 |
148 |
149 |
150 |
151 | # [4.3.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.2.4...v4.3.0) (2017-12-14)
152 |
153 |
154 | ### Features
155 |
156 | * add option to cache `pattern.transform` (`pattern.cache`) ([#176](https://github.com/webpack-contrib/copy-webpack-plugin/issues/176)) ([20c143b](https://github.com/webpack-contrib/copy-webpack-plugin/commit/20c143b))
157 | * option for caching `transform` function ([48c19ff](https://github.com/webpack-contrib/copy-webpack-plugin/commit/48c19ff))
158 |
159 |
160 |
161 |
162 | ## [4.2.4](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.2.3...v4.2.4) (2017-12-14)
163 |
164 |
165 | ### Refactoring
166 |
167 | * refactor: use native `{Promise}` instead of `bluebird` ([#178](https://github.com/webpack-contrib/copy-webpack-plugin/issues/178)) ([a508f14](https://github.com/webpack-contrib/copy-webpack-plugin/commit/a508f14))
168 |
169 |
170 |
171 |
172 | ## [4.2.3](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.2.2...v4.2.3) (2017-11-23)
173 |
174 |
175 |
176 |
177 | ## [4.2.2](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.2.0...v4.2.2) (2017-11-23)
178 |
179 |
180 | ### Bug Fixes
181 |
182 | * copying same file to multiple targets ([#165](https://github.com/webpack-contrib/copy-webpack-plugin/issues/165)) ([43a9870](https://github.com/webpack-contrib/copy-webpack-plugin/commit/43a9870))
183 |
184 |
185 |
186 |
187 | # [4.2.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.1.1...v4.2.0) (2017-10-19)
188 |
189 |
190 | ### Features
191 |
192 | * add `context` option (`options.context`) ([#149](https://github.com/webpack-contrib/copy-webpack-plugin/issues/149)) ([10cd1a2](https://github.com/webpack-contrib/copy-webpack-plugin/commit/10cd1a2))
193 | * allow async transforms ([#111](https://github.com/webpack-contrib/copy-webpack-plugin/issues/111)) ([8794e5f](https://github.com/webpack-contrib/copy-webpack-plugin/commit/8794e5f))
194 | * Plugin context option ([5c54e92](https://github.com/webpack-contrib/copy-webpack-plugin/commit/5c54e92)), closes [#148](https://github.com/webpack-contrib/copy-webpack-plugin/issues/148)
195 | * support `{String}` patterns ([#155](https://github.com/webpack-contrib/copy-webpack-plugin/issues/155)) ([b6c2e66](https://github.com/webpack-contrib/copy-webpack-plugin/commit/b6c2e66))
196 | * Support simple string patterns ([056a60b](https://github.com/webpack-contrib/copy-webpack-plugin/commit/056a60b)), closes [#150](https://github.com/webpack-contrib/copy-webpack-plugin/issues/150)
197 |
198 |
199 |
200 |
201 | ## [4.1.1](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.1.0...v4.1.1) (2017-10-05)
202 |
203 |
204 | ### Chore
205 |
206 | * Update dependencies for NSP security advisory ([#151](https://github.com/webpack-contrib/copy-webpack-plugin/issues/151)) ([6d4346e](https://github.com/webpack-contrib/copy-webpack-plugin/commit/6d4346e))
207 |
208 | - Reference issue: https://nodesecurity.io/advisories/minimatch_regular-expression-denial-of-service
209 |
210 |
211 |
212 |
213 | # [4.1.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v2.1.3...v4.1.0) (2017-09-29)
214 |
215 |
216 | ### Bug Fixes
217 |
218 | * Changed default ignore glob to ignore dot files ([#80](https://github.com/webpack-contrib/copy-webpack-plugin/issues/80)) ([08b69a4](https://github.com/webpack-contrib/copy-webpack-plugin/commit/08b69a4))
219 | * Fixed glob as object ([1b2c21a](https://github.com/webpack-contrib/copy-webpack-plugin/commit/1b2c21a))
220 | * Improved Windows compatibility ([#85](https://github.com/webpack-contrib/copy-webpack-plugin/issues/85)) ([ad62899](https://github.com/webpack-contrib/copy-webpack-plugin/commit/ad62899))
221 | * Memory leak in watch mode and use Set for performance ([#130](https://github.com/webpack-contrib/copy-webpack-plugin/issues/130)) ([de46fde](https://github.com/webpack-contrib/copy-webpack-plugin/commit/de46fde))
222 | * subdirectory errors in blob patterns ([c2720d0](https://github.com/webpack-contrib/copy-webpack-plugin/commit/c2720d0))
223 |
224 |
225 | ### Features
226 |
227 | * Added non-wildcard glob support ([405d1ec](https://github.com/webpack-contrib/copy-webpack-plugin/commit/405d1ec))
228 | * Added transform method to patterns ([#77](https://github.com/webpack-contrib/copy-webpack-plugin/issues/77)) ([6371eb1](https://github.com/webpack-contrib/copy-webpack-plugin/commit/6371eb1))
229 |
230 |
231 |
232 |
233 | ## [4.0.1](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v2.1.3...v4.0.1) (2017-09-29)
234 |
235 |
236 | ### Bug Fixes
237 |
238 | * Fixed glob as object ([1b2c21a](https://github.com/webpack-contrib/copy-webpack-plugin/commit/1b2c21a))
239 | * Improved Windows compatibility ([#85](https://github.com/webpack-contrib/copy-webpack-plugin/issues/85)) ([ad62899](https://github.com/webpack-contrib/copy-webpack-plugin/commit/ad62899))
240 | * subdirectory errors in blob patterns ([c2720d0](https://github.com/webpack-contrib/copy-webpack-plugin/commit/c2720d0))
241 |
242 |
243 | ### Features
244 |
245 | * Added non-wildcard glob support ([405d1ec](https://github.com/webpack-contrib/copy-webpack-plugin/commit/405d1ec))
246 | * Added transform method to patterns ([#77](https://github.com/webpack-contrib/copy-webpack-plugin/issues/77)) ([6371eb1](https://github.com/webpack-contrib/copy-webpack-plugin/commit/6371eb1))
247 |
248 |
249 |
250 |
251 | ## [4.0.0](https://github.com/webpack-contrib/copy-webpack-plugin/compare/v4.0.0...v3.0.1) (2016-10-23)
252 |
253 |
254 | ### Bug Fixes
255 |
256 | * Changed default ignore glob to ignore dot files ([#80](https://github.com/webpack-contrib/copy-webpack-plugin/issues/80)) ([08b69a4](https://github.com/webpack-contrib/copy-webpack-plugin/commit/08b69a4))
257 |
258 | ### Features
259 |
260 | * Added transform method to patterns ([6371eb1](https://github.com/webpack-contrib/copy-webpack-plugin/commit/6371eb1))
261 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright JS Foundation and other contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | 'Software'), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Forked from [webpack-contrib/copy-webpack-plugin](https://github.com/webpack-contrib/copy-webpack-plugin)
2 |
3 | Author: Len Boyette
4 |
5 | License: MIT
6 |
7 | 简介:根据`json`文件`usingComponents`自动引入组件,兼容[mpvue-entry](https://github.com/F-loat/mpvue-entry)
8 |
9 | 支持最新版本`mpvue`, 请按照如下用法进行配置
10 |
11 | -----------
12 |
13 |
14 |
15 |
17 |
18 |
Webpack Plugin Import Weapp Component
19 |
Import wechat app native component
20 |
21 |
22 | Install
23 |
24 | ```bash
25 | npm i -D import-weapp-component
26 | ```
27 |
28 | Usage
29 |
30 | **webpack.config.js**
31 | ```js
32 | const ImportComponent = require('import-weapp-component')
33 |
34 | const config = {
35 | plugins: [
36 | new ImportComponent({
37 | src: path.resolve(__dirname, '../src'), // 引用组件或原生页面的目录
38 | native: true, // 将 src 目录中的原生 page 复制到 dist
39 | usingComponents: path.resolve(__dirname, '../src/page.js') // mpvue-entry 配置路径
40 | })
41 | ]
42 | }
43 | ```
44 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | matrix:
3 | - nodejs_version: '6'
4 | - nodejs_version: '4'
5 | install:
6 | - ps: Install-Product node $env:nodejs_version
7 | - npm install
8 | matrix:
9 | fast_finish: true
10 | build: off
11 | shallow_clone: true
12 | test_script:
13 | - npm run test
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "import-weapp-component",
3 | "version": "4.7.3",
4 | "description": "import weapp native component for mpvue",
5 | "author": "Jesse Yang",
6 | "license": "MIT",
7 | "main": "dist/index.js",
8 | "engines": {
9 | "node": ">= 6"
10 | },
11 | "files": [
12 | "dist"
13 | ],
14 | "scripts": {
15 | "lint": "eslint src/ tests/",
16 | "prepare": "npm run build",
17 | "release": "standard-version",
18 | "pretest": "npm run lint && npm run build && npm run build:tests",
19 | "test": "mocha compiled_tests/",
20 | "build": "babel src/ --out-dir dist/",
21 | "build:tests": "babel tests/ --out-dir compiled_tests/ && rimraf compiled_tests/helpers && ncp tests/helpers compiled_tests/helpers && ncp tests/weappHelpers compiled_tests/weappHelpers && node scripts/createSpecialDirectory.js"
22 | },
23 | "dependencies": {
24 | "acorn": "^6.0.2",
25 | "cacache": "^10.0.4",
26 | "estree-walker": "^0.5.2",
27 | "find-cache-dir": "^1.0.0",
28 | "globby": "^7.1.1",
29 | "is-glob": "^4.0.0",
30 | "loader-utils": "^1.1.0",
31 | "minimatch": "^3.0.4",
32 | "p-limit": "^1.0.0",
33 | "serialize-javascript": "^1.4.0"
34 | },
35 | "devDependencies": {
36 | "babel-cli": "^6.8.0",
37 | "babel-preset-es2015": "^6.6.0",
38 | "chai": "^3.4.0",
39 | "enhanced-resolve": "^3.4.1",
40 | "eslint": "^2.9.0",
41 | "is-gzip": "^2.0.0",
42 | "mkdirp": "^0.5.1",
43 | "mocha": "^2.4.5",
44 | "ncp": "^2.0.0",
45 | "rimraf": "^2.6.2",
46 | "standard-version": "^4.2.0"
47 | },
48 | "homepage": "https://github.com/JJJYY/import-weapp-component",
49 | "bugs": "https://github.com/JJJYY/import-weapp-component/issues",
50 | "repository": "git@github.com:JJJYY/import-weapp-component.git",
51 | "keywords": [
52 | "webpack",
53 | "plugin",
54 | "import",
55 | "weapp",
56 | "wechat",
57 | "component"
58 | ]
59 | }
60 |
--------------------------------------------------------------------------------
/scripts/createSpecialDirectory.js:
--------------------------------------------------------------------------------
1 | const mkdirp = require('mkdirp');
2 | const path = require('path');
3 | const fs = require('fs');
4 | const removeIllegalCharacterForWindows = require('../tests/utils/removeIllegalCharacterForWindows');
5 |
6 | const baseDir = 'compiled_tests/helpers';
7 |
8 | const specialFiles = {
9 | '[special?directory]/nested/nestedfile.txt': '',
10 | '[special?directory]/(special-*file).txt': 'special',
11 | '[special?directory]/directoryfile.txt': 'new'
12 | };
13 |
14 | Object.keys(specialFiles).forEach(function (originFile) {
15 | const file = removeIllegalCharacterForWindows(originFile);
16 | const dir = path.dirname(file);
17 | mkdirp.sync(path.join(baseDir, dir));
18 | fs.writeFileSync(path.join(baseDir, file), specialFiles[originFile]);
19 | });
20 |
21 |
--------------------------------------------------------------------------------
/src/copyWebpackPlugin.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import preProcessPattern from './preProcessPattern';
3 | import processPattern from './processPattern';
4 | import extractComponent from './extractComponent';
5 |
6 | function CopyWebpackPlugin(patterns = [], options = {}, componentConfig = {}) {
7 | if (!Array.isArray(patterns)) {
8 | throw new Error('[copy-webpack-plugin] patterns must be an array');
9 | }
10 |
11 | // Defaults debug level to 'warning'
12 | options.debug = options.debug || 'warning';
13 |
14 | // Defaults debugging to info if only true is specified
15 | if (options.debug === true) {
16 | options.debug = 'info';
17 | }
18 |
19 | const debugLevels = ['warning', 'info', 'debug'];
20 | const debugLevelIndex = debugLevels.indexOf(options.debug);
21 | function log(msg, level) {
22 | if (level === 0) {
23 | msg = `WARNING - ${msg}`;
24 | } else {
25 | level = level || 1;
26 | }
27 | if (level <= debugLevelIndex) {
28 | console.log('[copy-webpack-plugin] ' + msg); // eslint-disable-line no-console
29 | }
30 | }
31 |
32 | function warning(msg) {
33 | log(msg, 0);
34 | }
35 |
36 | function info(msg) {
37 | log(msg, 1);
38 | }
39 |
40 | function debug(msg) {
41 | log(msg, 2);
42 | }
43 |
44 | const apply = (compiler) => {
45 | let fileDependencies;
46 | let contextDependencies;
47 | const written = {};
48 |
49 | let context;
50 |
51 | if (!options.context) {
52 | context = compiler.options.context;
53 | } else if (!path.isAbsolute(options.context)) {
54 | context = path.join(compiler.options.context, options.context);
55 | } else {
56 | context = options.context;
57 | }
58 |
59 | const emit = (compilation, cb) => {
60 | debug('starting emit');
61 | const callback = () => {
62 | debug('finishing emit');
63 | cb();
64 | };
65 |
66 | fileDependencies = [];
67 | contextDependencies = [];
68 |
69 | const globalRef = {
70 | info,
71 | debug,
72 | warning,
73 | compilation,
74 | written,
75 | fileDependencies,
76 | contextDependencies,
77 | context,
78 | inputFileSystem: compiler.inputFileSystem,
79 | output: compiler.options.output.path,
80 | ignore: options.ignore || [],
81 | copyUnmodified: options.copyUnmodified,
82 | concurrency: options.concurrency
83 | };
84 |
85 | if (globalRef.output === '/' &&
86 | compiler.options.devServer &&
87 | compiler.options.devServer.outputPath) {
88 | globalRef.output = compiler.options.devServer.outputPath;
89 | }
90 |
91 | const tasks = [];
92 | patterns = patterns.concat(extractComponent(compilation, componentConfig) || []);
93 | patterns.forEach((pattern) => {
94 | tasks.push(
95 | Promise.resolve()
96 | .then(() => preProcessPattern(globalRef, pattern))
97 | // Every source (from) is assumed to exist here
98 | .then((pattern) => processPattern(globalRef, pattern))
99 | );
100 | });
101 |
102 | Promise.all(tasks)
103 | .catch((err) => {
104 | compilation.errors.push(err);
105 | })
106 | .then(() => callback());
107 | };
108 |
109 | const afterEmit = (compilation, cb) => {
110 | debug('starting after-emit');
111 | const callback = () => {
112 | debug('finishing after-emit');
113 | cb();
114 | };
115 |
116 | let compilationFileDependencies;
117 | let addFileDependency;
118 | if (Array.isArray(compilation.fileDependencies)) {
119 | compilationFileDependencies = new Set(compilation.fileDependencies);
120 | addFileDependency = (file) => compilation.fileDependencies.push(file);
121 | } else {
122 | compilationFileDependencies = compilation.fileDependencies;
123 | addFileDependency = (file) => compilation.fileDependencies.add(file);
124 | }
125 |
126 | let compilationContextDependencies;
127 | let addContextDependency;
128 | if (Array.isArray(compilation.contextDependencies)) {
129 | compilationContextDependencies = new Set(compilation.contextDependencies);
130 | addContextDependency = (file) => compilation.contextDependencies.push(file);
131 | } else {
132 | compilationContextDependencies = compilation.contextDependencies;
133 | addContextDependency = (file) => compilation.contextDependencies.add(file);
134 | }
135 |
136 | // Add file dependencies if they're not already tracked
137 | for (const file of fileDependencies) {
138 | if (compilationFileDependencies.has(file)) {
139 | debug(`not adding ${file} to change tracking, because it's already tracked`);
140 | } else {
141 | debug(`adding ${file} to change tracking`);
142 | addFileDependency(file);
143 | }
144 | }
145 |
146 | // Add context dependencies if they're not already tracked
147 | for (const context of contextDependencies) {
148 | if (compilationContextDependencies.has(context)) {
149 | debug(`not adding ${context} to change tracking, because it's already tracked`);
150 | } else {
151 | debug(`adding ${context} to change tracking`);
152 | addContextDependency(context);
153 | }
154 | }
155 |
156 | callback();
157 | };
158 |
159 | if (compiler.hooks) {
160 | const plugin = { name: 'CopyPlugin' };
161 |
162 | compiler.hooks.emit.tapAsync(plugin, emit);
163 | compiler.hooks.afterEmit.tapAsync(plugin, afterEmit);
164 | } else {
165 | compiler.plugin('emit', emit);
166 | compiler.plugin('after-emit', afterEmit);
167 | }
168 | };
169 |
170 | return {
171 | apply
172 | };
173 | }
174 |
175 | CopyWebpackPlugin['default'] = CopyWebpackPlugin;
176 | module.exports = CopyWebpackPlugin;
177 |
--------------------------------------------------------------------------------
/src/extractComponent.js:
--------------------------------------------------------------------------------
1 | import globby from 'globby';
2 | import path, { isAbsolute, resolve, join, relative } from 'path';
3 | import fs from 'fs';
4 | import fetchModules from './fetchModules';
5 |
6 | let globalCompilation; // 主要用于`errors.push`
7 |
8 | const dependencies = new Set();
9 |
10 | function isPlainObject (_any) {
11 | return Object.prototype.toString.call(_any) === '[object Object]';
12 | }
13 |
14 | const jsonRE = /\/.+\.json$/;
15 | function getPathParse (filePath) {
16 | return path.parse(filePath);
17 | }
18 | function getFileDir (path) { // must be json file
19 | return getPathParse(path).dir;
20 | }
21 | // function getFileName (path) {
22 | // return getPathParse(path).name;
23 | // }
24 |
25 | function pushError (err) {
26 | err = typeof err === 'string' ? new Error(err) : err;
27 | globalCompilation.errors.push(err);
28 | }
29 |
30 | function getUsingComponents (content, filePath) {
31 | try {
32 | let json = JSON.parse(content);
33 | return json['usingComponents'] || {}; // 防止返回undefined
34 | } catch (e) {
35 | pushError(`${filePath} is not json`);
36 | return {}; // 解析出错,返回空
37 | }
38 | }
39 |
40 | function addComponents (content, components, parent) {
41 | components.push(...Object.keys(content).map(name => parent ? path.join(parent, content[name]) : content[name]));
42 | }
43 |
44 | function addComponentsFromJson (json, components, parent, filePath) {
45 | const content = getUsingComponents(json, filePath);
46 | addComponents(content, components, parent);
47 | }
48 |
49 | function addComponentsFromPath (path, components, parent) {
50 | if (fs.existsSync(path)) {
51 | let json = fs.readFileSync(path, 'utf8');
52 | addComponentsFromJson(json, components, parent, path);
53 | } else {
54 | pushError(`Component is not found in path "${path}"(not found json)`);
55 | }
56 | }
57 |
58 | function getNativePattern (from, to) {
59 | return {
60 | from: getFileDir(from),
61 | to: getFileDir(to),
62 | ignore: [ '**/*.!(js|json|wxss|wxml)' ]
63 | };
64 | }
65 |
66 | function generatorPattern (from, to, components, parent) {
67 | const { dir: fromDir, name: fromName } = getPathParse(from);
68 | const filePath = `${from}.js`;
69 |
70 | // 为了与小程序保持一致,仅判断`from.js`是否存在
71 | if (fs.existsSync(filePath)) {
72 | // 读取需要复制的 js 文件找出其依赖项,并添加到复制项
73 | dependencies.add(filePath);
74 | addComponentsFromPath(`${fromDir}/${fromName}.json`, components, getFileDir(parent));
75 | return getNativePattern(from, to);
76 | } else {
77 | pushError(`Component is not found in path "${from}"(not found js)`);
78 | }
79 | }
80 |
81 | function getOutputDir (file, path) {
82 | const fileDir = getFileDir(file);
83 | const to = isAbsolute(path)
84 | ? path
85 | : join(`${isAbsolute(fileDir) ? fileDir : ('/' + fileDir)}`, path); // 以输出路径为最上级路径
86 |
87 | return to.slice(1);
88 | }
89 |
90 | function getEntry (name, code, context) {
91 | return {
92 | context,
93 | assets: {
94 | [name]: {
95 | source () {
96 | return code;
97 | }
98 | }
99 | }
100 | };
101 | }
102 |
103 | function path2Entry (_path, context) {
104 | const parse = getPathParse(_path);
105 | const code = fs.readFileSync(_path, { encoding: 'utf8' });
106 |
107 | if (context) {
108 | _path = relative(context, _path);
109 | }
110 | return getEntry(_path, code, parse.dir);
111 | }
112 |
113 | function getAllExtFileFromSrc (src, ext, getEntry) {
114 | if (!Array.isArray(src)) {
115 | src = [ src ];
116 | }
117 | return src.reduce((result, dir) => {
118 | let res = [];
119 | const stat = fs.statSync(dir);
120 | if (stat.isDirectory) {
121 | const jsonPaths = globby.sync(resolve(dir, `**/*.${ext}`));
122 | res = jsonPaths.map((_path) => getEntry ? path2Entry(_path, dir) : _path);
123 | } else {
124 | if (jsonRE.test(dir)) {
125 | res = [
126 | getEntry ? path2Entry(dir) : dir
127 | ];
128 | } else {
129 | pushError(`includes: "${dir}", is not a effective path.`);
130 | }
131 | }
132 |
133 | return result.concat(res);
134 | }, []);
135 | }
136 |
137 | // 从 object 或 array 中获取所有 usingComponent
138 | // allUsingComponents 可能为 string 或 object
139 | function getAllUsing (allUsingComponents, entries = [], context = '') {
140 | if (Array.isArray(allUsingComponents)) {
141 | allUsingComponents.forEach(item => getAllUsing(item, entries, context));
142 | } else if (isPlainObject(allUsingComponents)) {
143 | Object.keys(allUsingComponents).forEach(key => {
144 | const value = allUsingComponents[key];
145 | if (key === 'usingComponents') {
146 | const file = context + '.json';
147 | const parse = getPathParse(file);
148 | entries.push(getEntry(file, JSON.stringify({ usingComponents: value }), parse.dir));
149 | } else if (key === 'path') {
150 | context = value;
151 | } else {
152 | getAllUsing(value, entries, context);
153 | }
154 | });
155 | }
156 |
157 | return entries;
158 | }
159 |
160 | function hadExist (patterns, pattern) {
161 | return patterns.some(p => p.from === pattern.from && p.to === pattern.to);
162 | }
163 |
164 | export default function extractComponent (compilation, componentConfig = {}) {
165 | dependencies.clear();
166 |
167 | globalCompilation = compilation;
168 | let patterns = [];
169 |
170 | let entries = compilation.entries ? compilation.entries.slice(0) : [];
171 |
172 | let projectContext = (compilation.options || {}).context || '';
173 | const { src, native, usingComponents } = componentConfig;
174 |
175 | // 增加对最新版本 mpvue 的支持
176 | if (src) {
177 | entries = entries.concat(getAllExtFileFromSrc(src, 'json', true));
178 | }
179 |
180 | if (usingComponents) {
181 | try {
182 | const allUsingComponents = require(usingComponents);
183 | entries = entries.concat(getAllUsing(allUsingComponents));
184 | // 修改 projectContext
185 | projectContext = getPathParse(usingComponents).dir;
186 | } catch (e) {
187 | pushError(e);
188 | }
189 | }
190 |
191 | if (native && projectContext) {
192 | const nativePages = getAllExtFileFromSrc(src, 'wxml');
193 | nativePages.forEach(dir => {
194 | const parse = getPathParse(dir);
195 | const filePath = `${parse.dir}/${parse.name}.js`;
196 | const jsonPath = `${parse.dir}/${parse.name}.json`;
197 | if (fs.existsSync(filePath)) {
198 | if (fs.existsSync(jsonPath)) {
199 | let json = fs.readFileSync(jsonPath, { encoding: 'utf8' });
200 | if (json) {
201 | json = JSON.parse(json);
202 | // 剔除组件
203 | if (json.component) {
204 | return;
205 | }
206 | }
207 | }
208 | dependencies.add(filePath);
209 | const to = relative(src, dir);
210 |
211 | patterns.push(getNativePattern(dir, to));
212 | }
213 | });
214 | }
215 |
216 | if (entries.length && projectContext) {
217 | for (let i = 0; i < entries.length; i++) {
218 | let context = entries[i].context;
219 | let assets = entries[i].assets;
220 |
221 | // 从 assets 获取所有 components 路径
222 | const file = Object.keys(assets).find(f => jsonRE.test(f));
223 | let components = [];
224 | if (file) {
225 | addComponentsFromJson(assets[file].source(), components, null, file);
226 | }
227 |
228 | for (let j = 0; j < components.length; j++) {
229 | const path = components[j];
230 | if (path) {
231 | const from = isAbsolute(path)
232 | ? join(projectContext, path)
233 | : resolve(projectContext, context, path);
234 |
235 | const to = getOutputDir(file, path);
236 |
237 | const pattern = generatorPattern(from, to, components, components[j]);
238 | // 重复去重
239 | if (pattern && !hadExist(patterns, pattern)) patterns.push(pattern);
240 | }
241 | }
242 | }
243 |
244 | // 根据 dependencies 列表得到需要复制的依赖文件
245 | patterns = patterns.concat(fetchModules(dependencies, projectContext));
246 | }
247 |
248 | return patterns;
249 | }
250 |
--------------------------------------------------------------------------------
/src/fetchModules.js:
--------------------------------------------------------------------------------
1 | import fs from 'fs';
2 | import path from 'path';
3 | import { parse } from 'acorn';
4 | import { walk } from 'estree-walker';
5 |
6 |
7 | function isDependency (node) {
8 | return node.type === 'ImportDeclaration'
9 | || (node.type === 'Identifier' && node.name === 'require');
10 | }
11 |
12 | function getFilePath (origin, filePath, projectContext) {
13 | if (path.isAbsolute(filePath)) {
14 | return path.join(projectContext, filePath);
15 | } else {
16 | const dir = path.parse(origin).dir;
17 | return path.join(dir, filePath);
18 | }
19 | }
20 |
21 | const jsFileReg = /\.js$/;
22 | function fileExist (filePath) {
23 | if (jsFileReg.test(filePath)) return filePath;
24 |
25 | if (fs.existsSync(`${filePath}.js`)) {
26 | return `${filePath}.js`;
27 | }
28 | if (fs.existsSync(`${filePath}/index.js`)) {
29 | return `${filePath}/index.js`;
30 | }
31 | }
32 |
33 | function generatorPatterns (filePaths, projectContext) {
34 | const patterns = [];
35 | for (let i = 0; i < filePaths.length; i++) {
36 | const toPath = path.relative(projectContext, filePaths[i]);
37 | patterns.push({
38 | from: filePaths[i],
39 | to: toPath
40 | });
41 | }
42 |
43 | return patterns;
44 | }
45 |
46 | export default function fetchModules (filePaths, projectContext) {
47 | const filePathArr = [...filePaths];
48 | const patterns = [];
49 | const len = filePathArr.length;
50 |
51 | for (let i = 0; i < filePathArr.length; i++) {
52 | const origin = filePathArr[i] = fileExist(filePathArr[i]);
53 | if (!origin) continue;
54 | const content = fs.readFileSync(origin, { encoding: 'utf8' });
55 | const ast = parse(content, { sourceType: 'module' });
56 | walk(ast, {
57 | enter (node, parent) {
58 | if (isDependency(node)) {
59 | let dep = '';
60 | if (node.type === 'ImportDeclaration') {
61 | dep = node.source.value;
62 | } else {
63 | dep = parent.arguments[0] ? parent.arguments[0].value : '';
64 | }
65 |
66 | dep = getFilePath(origin, dep, projectContext);
67 | filePathArr.push(dep);
68 | }
69 | }
70 | });
71 | }
72 |
73 | const deps = generatorPatterns(filePathArr.slice(len), projectContext);
74 |
75 | return patterns.concat(deps);
76 | }
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import CopyWebpackPlugin from './copyWebpackPlugin';
2 |
3 | let ImportComponent = CopyWebpackPlugin.bind(null, [], {});
4 | ImportComponent['default'] = ImportComponent;
5 | module.exports = ImportComponent;
--------------------------------------------------------------------------------
/src/preProcessPattern.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import isGlob from 'is-glob';
3 | import escape from './utils/escape';
4 | import isObject from './utils/isObject';
5 | import { stat } from './utils/promisify';
6 |
7 | // https://www.debuggex.com/r/VH2yS2mvJOitiyr3
8 | const isTemplateLike = /(\[ext\])|(\[name\])|(\[path\])|(\[folder\])|(\[emoji(:\d+)?\])|(\[(\w+:)?hash(:\w+)?(:\d+)?\])|(\[\d+\])/;
9 |
10 | export default function preProcessPattern(globalRef, pattern) {
11 | const {info, debug, warning, context, inputFileSystem,
12 | fileDependencies, contextDependencies, compilation} = globalRef;
13 |
14 | pattern = typeof pattern === 'string' ? {
15 | from: pattern
16 | } : Object.assign({}, pattern);
17 | pattern.to = pattern.to || '';
18 | pattern.context = pattern.context || context;
19 | if (!path.isAbsolute(pattern.context)) {
20 | pattern.context = path.join(context, pattern.context);
21 | }
22 | pattern.ignore = globalRef.ignore.concat(pattern.ignore || []);
23 |
24 | info(`processing from: '${pattern.from}' to: '${pattern.to}'`);
25 |
26 | switch(true) {
27 | case !!pattern.toType: // if toType already exists
28 | break;
29 | case isTemplateLike.test(pattern.to):
30 | pattern.toType = 'template';
31 | break;
32 | case path.extname(pattern.to) === '' || pattern.to.slice(-1) === '/':
33 | pattern.toType = 'dir';
34 | break;
35 | default:
36 | pattern.toType = 'file';
37 | }
38 |
39 | debug(`determined '${pattern.to}' is a '${pattern.toType}'`);
40 |
41 | // If we know it's a glob, then bail early
42 | if (isObject(pattern.from) && pattern.from.glob) {
43 | pattern.fromType = 'glob';
44 |
45 | const fromArgs = Object.assign({}, pattern.from);
46 | delete fromArgs.glob;
47 |
48 | pattern.fromArgs = fromArgs;
49 | pattern.glob = escape(pattern.context, pattern.from.glob);
50 | pattern.absoluteFrom = path.resolve(pattern.context, pattern.from.glob);
51 | return Promise.resolve(pattern);
52 | }
53 |
54 | if (path.isAbsolute(pattern.from)) {
55 | pattern.absoluteFrom = pattern.from;
56 | } else {
57 | pattern.absoluteFrom = path.resolve(pattern.context, pattern.from);
58 | }
59 |
60 | debug(`determined '${pattern.from}' to be read from '${pattern.absoluteFrom}'`);
61 |
62 | return stat(inputFileSystem, pattern.absoluteFrom)
63 | .catch(() => {
64 | // If from doesn't appear to be a glob, then log a warning
65 | if (isGlob(pattern.from) || pattern.from.indexOf('*') !== -1) {
66 | pattern.fromType = 'glob';
67 | pattern.glob = escape(pattern.context, pattern.from);
68 | } else {
69 | const msg = `unable to locate '${pattern.from}' at '${pattern.absoluteFrom}'`;
70 | warning(msg);
71 | compilation.errors.push(`[copy-webpack-plugin] ${msg}`);
72 | pattern.fromType = 'nonexistent';
73 | }
74 | })
75 | .then((stat) => {
76 | if (!stat) {
77 | return pattern;
78 | }
79 |
80 | if (stat.isDirectory()) {
81 | pattern.fromType = 'dir';
82 | pattern.context = pattern.absoluteFrom;
83 | contextDependencies.push(pattern.absoluteFrom);
84 | pattern.glob = escape(pattern.absoluteFrom, '**/*');
85 | pattern.absoluteFrom = path.join(pattern.absoluteFrom, '**/*');
86 | pattern.fromArgs = {
87 | dot: true
88 | };
89 | } else if(stat.isFile()) {
90 | pattern.fromType = 'file';
91 | pattern.context = path.dirname(pattern.absoluteFrom);
92 | pattern.glob = escape(pattern.absoluteFrom);
93 | pattern.fromArgs = {
94 | dot: true
95 | };
96 | fileDependencies.push(pattern.absoluteFrom);
97 | } else if(!pattern.fromType) {
98 | info(`Unrecognized file type for ${pattern.from}`);
99 | }
100 | return pattern;
101 | });
102 | }
103 |
--------------------------------------------------------------------------------
/src/processPattern.js:
--------------------------------------------------------------------------------
1 | import globby from 'globby';
2 | import pLimit from 'p-limit';
3 | import path from 'path';
4 | import minimatch from 'minimatch';
5 | import writeFile from './writeFile';
6 | import isObject from './utils/isObject';
7 |
8 | export default function processPattern(globalRef, pattern) {
9 | const {info, debug, output, concurrency} = globalRef;
10 | const globArgs = Object.assign({
11 | cwd: pattern.context
12 | }, pattern.fromArgs || {});
13 |
14 | if (pattern.fromType === 'nonexistent') {
15 | return Promise.resolve();
16 | }
17 |
18 | const limit = pLimit(concurrency || 100);
19 |
20 | info(`begin globbing '${pattern.glob}' with a context of '${pattern.context}'`);
21 | return globby(pattern.glob, globArgs)
22 | .then((paths) => Promise.all(paths.map((from) => limit(() => {
23 | const file = {
24 | force: pattern.force,
25 | absoluteFrom: path.resolve(pattern.context, from)
26 | };
27 | file.relativeFrom = path.relative(pattern.context, file.absoluteFrom);
28 |
29 | if (pattern.flatten) {
30 | file.relativeFrom = path.basename(file.relativeFrom);
31 | }
32 |
33 | debug(`found ${from}`);
34 |
35 | // Check the ignore list
36 | let il = pattern.ignore.length;
37 | while (il--) {
38 | const ignoreGlob = pattern.ignore[il];
39 |
40 | let globParams = {
41 | dot: true,
42 | matchBase: true
43 | };
44 |
45 | let glob;
46 | if (typeof ignoreGlob === 'string') {
47 | glob = ignoreGlob;
48 | } else if (isObject(ignoreGlob)) {
49 | glob = ignoreGlob.glob || '';
50 | const ignoreGlobParams = Object.assign({}, ignoreGlob);
51 | delete ignoreGlobParams.glob;
52 |
53 | // Overwrite minimatch defaults
54 | globParams = Object.assign(globParams, ignoreGlobParams);
55 | } else {
56 | glob = '';
57 | }
58 |
59 | debug(`testing ${glob} against ${file.relativeFrom}`);
60 | if (minimatch(file.relativeFrom, glob, globParams)) {
61 | info(`ignoring '${file.relativeFrom}', because it matches the ignore glob '${glob}'`);
62 | return Promise.resolve();
63 | } else {
64 | debug(`${glob} doesn't match ${file.relativeFrom}`);
65 | }
66 | }
67 |
68 | // Change the to path to be relative for webpack
69 | if (pattern.toType === 'dir') {
70 | file.webpackTo = path.join(pattern.to, file.relativeFrom);
71 | } else if (pattern.toType === 'file') {
72 | file.webpackTo = pattern.to || file.relativeFrom;
73 | } else if (pattern.toType === 'template') {
74 | file.webpackTo = pattern.to;
75 | file.webpackToRegExp = pattern.test;
76 | }
77 |
78 | if (path.isAbsolute(file.webpackTo)) {
79 | if (output === '/') {
80 | throw '[copy-webpack-plugin] Using older versions of webpack-dev-server, devServer.outputPath must be defined to write to absolute paths';
81 | }
82 |
83 | file.webpackTo = path.relative(output, file.webpackTo);
84 | }
85 |
86 | // ensure forward slashes
87 | file.webpackTo = file.webpackTo.replace(/\\/g, '/');
88 |
89 | info(`determined that '${from}' should write to '${file.webpackTo}'`);
90 |
91 | return writeFile(globalRef, pattern, file);
92 | }))));
93 | }
94 |
--------------------------------------------------------------------------------
/src/utils/escape.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | export default function escape(context, from) {
4 | if (from && path.isAbsolute(from)) {
5 | return from;
6 | } else {
7 | // Ensure context is escaped before globbing
8 | // Handles special characters in paths
9 | const absoluteContext = path.resolve(context)
10 | .replace(/[\*|\?|\!|\(|\)|\[|\]|\{|\}]/g, (substring) => `[${substring}]`);
11 |
12 | if (!from) {
13 | return absoluteContext;
14 | }
15 |
16 | // Cannot use path.join/resolve as it "fixes" the path separators
17 | if (absoluteContext.endsWith('/')) {
18 | return `${absoluteContext}${from}`;
19 | } else {
20 | return `${absoluteContext}/${from}`;
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/utils/isObject.js:
--------------------------------------------------------------------------------
1 | export default (val) => Object.prototype.toString.call(val) === '[object Object]' ? true : false;
2 |
--------------------------------------------------------------------------------
/src/utils/promisify.js:
--------------------------------------------------------------------------------
1 | export const stat = (inputFileSystem, path) => {
2 | return new Promise((resolve, reject) => {
3 | inputFileSystem.stat(path, (err, stats) => {
4 | if (err) {
5 | reject(err);
6 | }
7 | resolve(stats);
8 | });
9 | });
10 | };
11 |
12 | export const readFile = (inputFileSystem, path) => {
13 | return new Promise((resolve, reject) => {
14 | inputFileSystem.readFile(path, (err, stats) => {
15 | if (err) {
16 | reject(err);
17 | }
18 | resolve(stats);
19 | });
20 | });
21 | };
22 |
--------------------------------------------------------------------------------
/src/writeFile.js:
--------------------------------------------------------------------------------
1 | import loaderUtils from 'loader-utils';
2 | import path from 'path';
3 | import cacache from 'cacache';
4 | import serialize from 'serialize-javascript';
5 | import { name, version } from '../package.json';
6 | import findCacheDir from 'find-cache-dir';
7 | import { stat, readFile } from './utils/promisify';
8 | import crypto from 'crypto';
9 |
10 | export default function writeFile(globalRef, pattern, file) {
11 | const {info, debug, compilation, fileDependencies, written, inputFileSystem, copyUnmodified} = globalRef;
12 |
13 | return stat(inputFileSystem, file.absoluteFrom)
14 | .then((stat) => {
15 | // We don't write empty directories
16 | if (stat.isDirectory()) {
17 | return;
18 | }
19 |
20 | // If this came from a glob, add it to the file watchlist
21 | if (pattern.fromType === 'glob') {
22 | fileDependencies.push(file.absoluteFrom);
23 | }
24 |
25 | info(`reading ${file.absoluteFrom} to write to assets`);
26 | return readFile(inputFileSystem, file.absoluteFrom)
27 | .then((content) => {
28 | if (pattern.transform) {
29 | const transform = (content, absoluteFrom) => {
30 | return pattern.transform(content, absoluteFrom);
31 | };
32 |
33 | if (pattern.cache) {
34 | if (!globalRef.cacheDir) {
35 | globalRef.cacheDir = findCacheDir({ name: 'copy-webpack-plugin' });
36 | }
37 |
38 | const cacheKey = pattern.cache.key
39 | ? pattern.cache.key
40 | : serialize({
41 | name,
42 | version,
43 | pattern,
44 | hash: crypto.createHash('md4').update(content).digest('hex')
45 | });
46 |
47 | return cacache
48 | .get(globalRef.cacheDir, cacheKey)
49 | .then(
50 | (result) => result.data,
51 | () => {
52 | return Promise
53 | .resolve()
54 | .then(() => transform(content, file.absoluteFrom))
55 | .then((content) => cacache.put(globalRef.cacheDir, cacheKey, content)
56 | .then(() => content));
57 | }
58 | );
59 | }
60 |
61 | content = transform(content, file.absoluteFrom);
62 | }
63 |
64 | return content;
65 | }).then((content) => {
66 | const hash = loaderUtils.getHashDigest(content);
67 |
68 | if (pattern.toType === 'template') {
69 | info(`interpolating template '${file.webpackTo}' for '${file.relativeFrom}'`);
70 |
71 | // If it doesn't have an extension, remove it from the pattern
72 | // ie. [name].[ext] or [name][ext] both become [name]
73 | if (!path.extname(file.relativeFrom)) {
74 | file.webpackTo = file.webpackTo.replace(/\.?\[ext\]/g, '');
75 | }
76 |
77 | file.webpackTo = loaderUtils.interpolateName(
78 | {resourcePath: file.absoluteFrom},
79 | file.webpackTo,
80 | {
81 | content,
82 | regExp: file.webpackToRegExp,
83 | context: pattern.context
84 | }
85 | );
86 | }
87 |
88 | if (!copyUnmodified &&
89 | written[file.absoluteFrom] &&
90 | written[file.absoluteFrom]['hash'] === hash &&
91 | written[file.absoluteFrom]['webpackTo'] === file.webpackTo
92 | ) {
93 | info(`skipping '${file.webpackTo}', because it hasn't changed`);
94 | return;
95 | } else {
96 | debug(`added ${hash} to written tracking for '${file.absoluteFrom}'`);
97 | written[file.absoluteFrom] = {
98 | hash: hash,
99 | webpackTo: file.webpackTo
100 | };
101 | }
102 |
103 | if (compilation.assets[file.webpackTo] && !file.force) {
104 | info(`skipping '${file.webpackTo}', because it already exists`);
105 | return;
106 | }
107 |
108 | info(`writing '${file.webpackTo}' to compilation assets from '${file.absoluteFrom}'`);
109 | compilation.assets[file.webpackTo] = {
110 | size: function() {
111 | return stat.size;
112 | },
113 | source: function() {
114 | return content;
115 | }
116 | };
117 | });
118 | });
119 | }
120 |
--------------------------------------------------------------------------------
/tests/components.test.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import path from 'path';
3 | import extractComponent from '../dist/extractComponent';
4 |
5 | function resolve (...paths) {
6 | return path.resolve(__dirname, './weappHelpers', ...paths);
7 | }
8 |
9 | function generatorEntries (pages) {
10 | if (!Array.isArray(pages)) {
11 | pages = [pages];
12 | }
13 |
14 | return pages.map(page => {
15 | // const _path = parse(page.path);
16 | return {
17 | context: page.context,
18 | assets: {
19 | [page.name]: {
20 | source () {
21 | return JSON.stringify(page.content);
22 | }
23 | }
24 | }
25 | };
26 | });
27 | }
28 |
29 | function run (opts) {
30 | return new Promise ((res, rej) => {
31 | const compilation = Object.assign({
32 | assets: {},
33 | contextDependencies: [],
34 | errors: [],
35 | fileDependencies: [],
36 | options: {
37 | context: resolve()
38 | }
39 | }, opts.compilation);
40 |
41 | const patterns = extractComponent(compilation, opts.config);
42 | console.log(patterns);
43 |
44 | let expectPatterns = opts.components.map(comp => {
45 | return {
46 | from: resolve(comp),
47 | to: comp,
48 | ignore: [ '**/*.!(js|json|wxss|wxml)' ]
49 | };
50 | });
51 | if (opts.dependencies) {
52 | expectPatterns = expectPatterns.concat(opts.dependencies);
53 | }
54 | if (expect(patterns).to.have.deep.members(expectPatterns)) {
55 | res();
56 | } else {
57 | rej();
58 | }
59 | });
60 | }
61 |
62 | describe('should get error', () => {
63 | it('component not exist', (done) => {
64 | const components = [
65 | ];
66 | const compilation = {
67 | errors: [],
68 | entries: generatorEntries([
69 | {
70 | name: 'pages/self/index.json',
71 | context: resolve('pages/self'),
72 | content: {
73 | usingComponents: {
74 | none: '/components/none/index'
75 | }
76 | }
77 | }
78 | ])
79 | };
80 | run({
81 | compilation,
82 | components
83 | })
84 | .then(() => {
85 | done(compilation.errors.length !== 0 ? null : new Error('not get error'));
86 | })
87 | .catch(done);
88 | });
89 | });
90 |
91 | // 目录中使用单独的 json 文件来模拟 mpvue-loader 在 emit 生成的 json 文件
92 | // 低版本 mpvue-loader
93 | describe('generator patterns when emit', () => {
94 | describe('single pages', () => {
95 | it('relative path', (done) => {
96 | const components = [ 'components/button' ];
97 | run({
98 | compilation: {
99 | entries: generatorEntries([
100 | {
101 | name: 'pages/self/index.json',
102 | context: resolve('pages/self'),
103 | content: {
104 | usingComponents: {
105 | button: '/components/button/index'
106 | }
107 | }
108 | }
109 | ])
110 | },
111 | components
112 | })
113 | .then(done)
114 | .catch(done);
115 | });
116 |
117 | it('absolute path and import component from other component', (done) => {
118 | const components = [
119 | 'components/button',
120 | 'components/panel'
121 | ];
122 | run({
123 | compilation: {
124 | entries: generatorEntries([
125 | {
126 | name: 'pages/normal/index.json',
127 | context: resolve('pages/normal'),
128 | content: {
129 | usingComponents: {
130 | panel: '/components/panel/index'
131 | }
132 | }
133 | }
134 | ])
135 | },
136 | components
137 | })
138 | .then(done)
139 | .catch(done);
140 | });
141 | });
142 |
143 | describe('multiply pages', () => {
144 | it('repeat import component', (done) => {
145 | const components = [
146 | 'components/button',
147 | 'components/panel'
148 | ];
149 | run({
150 | compilation: {
151 | entries: generatorEntries([
152 | {
153 | name: 'pages/self/index.json',
154 | context: resolve('pages/self'),
155 | content: {
156 | usingComponents: {
157 | button: '/components/button/index'
158 | }
159 | }
160 | },
161 | {
162 | name: 'pages/normal/index.json',
163 | context: resolve('pages/normal'),
164 | content: {
165 | usingComponents: {
166 | panel: '/components/panel/index'
167 | }
168 | }
169 | }
170 | ])
171 | },
172 | components
173 | })
174 | .then(done)
175 | .catch(done);
176 | });
177 | });
178 | });
179 |
180 | // mpvue-loader v1.1.0 以上
181 | describe('generator patterns read json path', () => {
182 | const config = {
183 | src: resolve()
184 | };
185 |
186 | describe('config src path', () => {
187 | it('relative path', (done) => {
188 | const components = [
189 | 'components/button',
190 | 'components/panel'
191 | ];
192 | run({
193 | components,
194 | config
195 | })
196 | .then(done)
197 | .catch(done);
198 | });
199 | });
200 |
201 | describe('copy native page', () => {
202 | it('relative path', (done) => {
203 | const components = [
204 | 'components/button',
205 | 'components/panel',
206 | 'pages/native'
207 | ];
208 | run({
209 | components,
210 | config: {
211 | src: resolve(),
212 | native: true
213 | }
214 | })
215 | .then(done)
216 | .catch(done);
217 | });
218 | });
219 | });
220 |
221 | describe('compatible with', () => {
222 | const config = {
223 | usingComponents: resolve('entries.js')
224 | };
225 |
226 | describe('mpvue-entry', () => {
227 | it('form entries config', (done) => {
228 | const components = [
229 | 'components/button',
230 | 'components/panel'
231 | ];
232 | run({
233 | components,
234 | config
235 | })
236 | .then(done)
237 | .catch(done);
238 | });
239 | });
240 | });
241 |
242 | describe('component depends other', () => {
243 | describe('require function', () => {
244 | it('from component', (done) => {
245 | const components = [
246 | 'components/comp1'
247 | ];
248 | run({
249 | components,
250 | compilation: {
251 | entries: generatorEntries([
252 | {
253 | name: 'pages/normal/index.json',
254 | context: resolve('pages/normal'),
255 | content: {
256 | usingComponents: {
257 | panel: '/components/comp1/index'
258 | }
259 | }
260 | }
261 | ])
262 | },
263 | dependencies: [
264 | {
265 | from: resolve('components/base/Page.js'),
266 | to: 'components/base/Page.js'
267 | },
268 | {
269 | from: resolve('components/base/Dep/index.js'),
270 | to: 'components/base/Dep/index.js'
271 | }
272 | ]
273 | })
274 | .then(done)
275 | .catch(done);
276 | });
277 | });
278 | });
279 |
--------------------------------------------------------------------------------
/tests/helpers/[!]/hello.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/helpers/[!]/hello.txt
--------------------------------------------------------------------------------
/tests/helpers/binextension.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/helpers/binextension.bin
--------------------------------------------------------------------------------
/tests/helpers/directory/.dottedfile:
--------------------------------------------------------------------------------
1 | dottedfile contents
2 |
--------------------------------------------------------------------------------
/tests/helpers/directory/directoryfile.txt:
--------------------------------------------------------------------------------
1 | new
--------------------------------------------------------------------------------
/tests/helpers/directory/nested/nestedfile.txt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/helpers/directory/nested/nestedfile.txt
--------------------------------------------------------------------------------
/tests/helpers/file.txt:
--------------------------------------------------------------------------------
1 | new
--------------------------------------------------------------------------------
/tests/helpers/file.txt.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/helpers/file.txt.gz
--------------------------------------------------------------------------------
/tests/helpers/noextension:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/helpers/noextension
--------------------------------------------------------------------------------
/tests/index.js:
--------------------------------------------------------------------------------
1 | /* globals describe, it, __dirname */
2 | import {
3 | expect
4 | } from 'chai';
5 | import NodeJsInputFileSystem from 'enhanced-resolve/lib/NodeJsInputFileSystem';
6 | import CachedInputFileSystem from 'enhanced-resolve/lib/CachedInputFileSystem';
7 |
8 | // ensure we don't mess up classic imports
9 | const CopyWebpackPlugin = require('./../dist/copyWebpackPlugin');
10 |
11 | import fs from 'fs';
12 | import path from 'path';
13 | import findCacheDir from 'find-cache-dir';
14 | import cacache from 'cacache';
15 | import isGzip from 'is-gzip';
16 | import zlib from 'zlib';
17 |
18 | import removeIllegalCharacterForWindows from './utils/removeIllegalCharacterForWindows';
19 |
20 | const BUILD_DIR = path.join(__dirname, 'build');
21 | const HELPER_DIR = path.join(__dirname, 'helpers');
22 | const TEMP_DIR = path.join(__dirname, 'tempdir');
23 |
24 | class MockCompiler {
25 | constructor (options = {}) {
26 | this.options = {
27 | context: HELPER_DIR,
28 | output: {
29 | path: options.outputPath || BUILD_DIR
30 | }
31 | };
32 |
33 | if (options.devServer && options.devServer.outputPath) {
34 | this.options.devServer = {
35 | outputPath: options.devServer.outputPath
36 | };
37 | }
38 |
39 | this.inputFileSystem = new CachedInputFileSystem(new NodeJsInputFileSystem(), 0);
40 |
41 | this.outputFileSystem = {
42 | constructor: {
43 | name: 'NotMemoryFileSystem'
44 | }
45 | };
46 | }
47 |
48 | plugin (type, fn) {
49 | if (type === 'emit') {
50 | this.emitFn = fn;
51 | }
52 |
53 | if (type === 'after-emit') {
54 | this.afterEmitFn = fn;
55 | }
56 | }
57 | }
58 |
59 | describe('apply function', () => {
60 | // Ideally we pass in patterns and confirm the resulting assets
61 | const run = (opts) => {
62 | return new Promise((resolve, reject) => {
63 | if (Array.isArray(opts.patterns)) {
64 | opts.patterns.forEach(function (pattern) {
65 | if (pattern.context) {
66 | pattern.context = removeIllegalCharacterForWindows(pattern.context);
67 | }
68 | });
69 | }
70 | const plugin = CopyWebpackPlugin(opts.patterns, opts.options);
71 |
72 | // Get a mock compiler to pass to plugin.apply
73 | const compiler = opts.compiler || new MockCompiler();
74 |
75 | plugin.apply(compiler);
76 |
77 | // Call the registered function with a mock compilation and callback
78 | const compilation = Object.assign({
79 | assets: {},
80 | contextDependencies: [],
81 | errors: [],
82 | fileDependencies: []
83 | }, opts.compilation);
84 |
85 | // Execute the functions in series
86 | return Promise.resolve()
87 | .then(() => {
88 | return new Promise((res, rej) => {
89 | try {
90 | compiler.emitFn(compilation, res);
91 | } catch (error) {
92 | rej(error);
93 | }
94 | });
95 | })
96 | .then(() => {
97 | return new Promise((res, rej) => {
98 | try {
99 | compiler.afterEmitFn(compilation, res);
100 | } catch (error) {
101 | rej(error);
102 | }
103 | });
104 | })
105 | .then(() => {
106 | if (opts.expectedErrors) {
107 | expect(compilation.errors).to.deep.equal(opts.expectedErrors);
108 | } else if (compilation.errors.length > 0) {
109 | throw compilation.errors[0];
110 | }
111 | resolve(compilation);
112 | })
113 | .catch(reject);
114 | });
115 | };
116 |
117 | const runEmit = (opts) => {
118 | return run(opts)
119 | .then((compilation) => {
120 | if (opts.expectedAssetKeys && opts.expectedAssetKeys.length > 0) {
121 | expect(compilation.assets).to.have.all.keys(opts.expectedAssetKeys.map(removeIllegalCharacterForWindows));
122 | } else {
123 | expect(compilation.assets).to.deep.equal({});
124 | }
125 |
126 | if (opts.expectedAssetContent) {
127 | for (var key in opts.expectedAssetContent) {
128 | expect(compilation.assets[key]).to.exist;
129 | if (compilation.assets[key]) {
130 | let expectedContent = opts.expectedAssetContent[key];
131 |
132 | if (!Buffer.isBuffer(expectedContent)) {
133 | expectedContent = new Buffer(expectedContent);
134 | }
135 |
136 | let compiledContent = compilation.assets[key].source();
137 |
138 | if (!Buffer.isBuffer(compiledContent)) {
139 | compiledContent = new Buffer(compiledContent);
140 | }
141 |
142 | expect(Buffer.compare(expectedContent, compiledContent)).to.equal(0);
143 | }
144 | }
145 | }
146 | });
147 | };
148 |
149 | const runForce = (opts) => {
150 | opts.compilation = {
151 | assets: {}
152 | };
153 | opts.compilation.assets[opts.existingAsset] = {
154 | source () {
155 | return 'existing';
156 | }
157 | };
158 |
159 | return run(opts).then(() => {});
160 | };
161 |
162 | const runChange = (opts) => {
163 | // Create two test files
164 | fs.writeFileSync(opts.newFileLoc1, 'file1contents');
165 | fs.writeFileSync(opts.newFileLoc2, 'file2contents');
166 |
167 | // Create a reference to the compiler
168 | const compiler = new MockCompiler();
169 | const compilation = {
170 | assets: {},
171 | contextDependencies: [],
172 | errors: [],
173 | fileDependencies: []
174 | };
175 |
176 | return run({
177 | compiler,
178 | options: opts.options,
179 | patterns: opts.patterns
180 | })
181 | .then(() => {
182 | // Change a file
183 | fs.appendFileSync(opts.newFileLoc1, 'extra');
184 |
185 | // Trigger another compile
186 | return new Promise((res) => {
187 | compiler.emitFn(compilation, res);
188 | });
189 | })
190 | .then(() => {
191 | if (opts.expectedAssetKeys && opts.expectedAssetKeys.length > 0) {
192 | expect(compilation.assets).to.have.all.keys(opts.expectedAssetKeys);
193 | } else {
194 | expect(compilation.assets).to.deep.equal({});
195 | }
196 | })
197 | .then(() => {
198 | fs.unlinkSync(opts.newFileLoc1);
199 | fs.unlinkSync(opts.newFileLoc2);
200 | });
201 | };
202 |
203 | // Use then and catch explicitly, so errors
204 | // aren't seen as unhandled exceptions
205 | describe('error handling', () => {
206 | it('doesn\'t throw an error if no patterns are passed', (done) => {
207 | runEmit({
208 | expectedAssetKeys: [],
209 | patterns: undefined // eslint-disable-line no-undefined
210 | })
211 | .then(done)
212 | .catch(done);
213 | });
214 |
215 | it('throws an error if the patterns are an object', () => {
216 | const createPluginWithObject = () => {
217 | CopyWebpackPlugin({});
218 | };
219 |
220 | expect(createPluginWithObject).to.throw(Error);
221 | });
222 |
223 | it('throws an error if the patterns are null', () => {
224 | const createPluginWithNull = () => {
225 | CopyWebpackPlugin(null);
226 | };
227 |
228 | expect(createPluginWithNull).to.throw(Error);
229 | });
230 | });
231 |
232 | describe('with glob in from', () => {
233 | it('can use a glob to move a file to the root directory', (done) => {
234 | runEmit({
235 | expectedAssetKeys: [
236 | 'file.txt'
237 | ],
238 | patterns: [{
239 | from: '*.txt'
240 | }]
241 | })
242 | .then(done)
243 | .catch(done);
244 | });
245 |
246 | it('can use a bracketed glob to move a file to the root directory', (done) => {
247 | runEmit({
248 | expectedAssetKeys: [
249 | 'directory/directoryfile.txt',
250 | 'directory/nested/nestedfile.txt',
251 | 'file.txt',
252 | 'noextension'
253 | ],
254 | patterns: [{
255 | from: '{file.txt,noextension,directory/**/*}'
256 | }]
257 | })
258 | .then(done)
259 | .catch(done);
260 | });
261 |
262 | it('can use a glob object to move a file to the root directory', (done) => {
263 | runEmit({
264 | expectedAssetKeys: [
265 | 'file.txt'
266 | ],
267 | patterns: [{
268 | from: {
269 | glob: '*.txt'
270 | }
271 | }]
272 | })
273 | .then(done)
274 | .catch(done);
275 | });
276 |
277 | it('can use a glob to move multiple files to the root directory', (done) => {
278 | runEmit({
279 | expectedAssetKeys: [
280 | '[!]/hello.txt',
281 | 'binextension.bin',
282 | 'file.txt',
283 | 'file.txt.gz',
284 | 'directory/directoryfile.txt',
285 | 'directory/nested/nestedfile.txt',
286 | '[special?directory]/directoryfile.txt',
287 | '[special?directory]/(special-*file).txt',
288 | '[special?directory]/nested/nestedfile.txt',
289 | 'noextension'
290 | ],
291 | patterns: [{
292 | from: '**/*'
293 | }]
294 | })
295 | .then(done)
296 | .catch(done);
297 | });
298 |
299 | it('can use a glob to move multiple files to a non-root directory', (done) => {
300 | runEmit({
301 | expectedAssetKeys: [
302 | 'nested/[!]/hello.txt',
303 | 'nested/binextension.bin',
304 | 'nested/file.txt',
305 | 'nested/file.txt.gz',
306 | 'nested/directory/directoryfile.txt',
307 | 'nested/directory/nested/nestedfile.txt',
308 | 'nested/[special?directory]/directoryfile.txt',
309 | 'nested/[special?directory]/(special-*file).txt',
310 | 'nested/[special?directory]/nested/nestedfile.txt',
311 | 'nested/noextension'
312 | ],
313 | patterns: [{
314 | from: '**/*',
315 | to: 'nested'
316 | }]
317 | })
318 | .then(done)
319 | .catch(done);
320 | });
321 |
322 | it('can use a glob to move multiple files in a different relative context to a non-root directory', (done) => {
323 | runEmit({
324 | expectedAssetKeys: [
325 | 'nested/directoryfile.txt',
326 | 'nested/nested/nestedfile.txt'
327 | ],
328 | patterns: [{
329 | context: 'directory',
330 | from: '**/*',
331 | to: 'nested'
332 | }]
333 | })
334 | .then(done)
335 | .catch(done);
336 | });
337 |
338 | it('can use a direct glob to move multiple files in a different relative context with special characters', (done) => {
339 | runEmit({
340 | expectedAssetKeys: [
341 | 'directoryfile.txt',
342 | '(special-*file).txt',
343 | 'nested/nestedfile.txt'
344 | ],
345 | patterns: [{
346 | context: '[special?directory]',
347 | from: { glob: '**/*' }
348 | }]
349 | })
350 | .then(done)
351 | .catch(done);
352 | });
353 |
354 | it('can use a glob to move multiple files in a different relative context with special characters', (done) => {
355 | runEmit({
356 | expectedAssetKeys: [
357 | 'directoryfile.txt',
358 | '(special-*file).txt',
359 | 'nested/nestedfile.txt'
360 | ],
361 | patterns: [{
362 | context: '[special?directory]',
363 | from: '**/*'
364 | }]
365 | })
366 | .then(done)
367 | .catch(done);
368 | });
369 |
370 | it('can use a glob to flatten multiple files in a relative context to a non-root directory', (done) => {
371 | runEmit({
372 | expectedAssetKeys: [
373 | 'nested/directoryfile.txt',
374 | 'nested/nestedfile.txt'
375 | ],
376 | patterns: [{
377 | context: 'directory',
378 | flatten: true,
379 | from: '**/*',
380 | to: 'nested'
381 | }]
382 | })
383 | .then(done)
384 | .catch(done);
385 | });
386 |
387 | it('can use a glob to move multiple files in a different absolute context to a non-root directory', (done) => {
388 | runEmit({
389 | expectedAssetKeys: [
390 | 'nested/directoryfile.txt',
391 | 'nested/nested/nestedfile.txt'
392 | ],
393 | patterns: [{
394 | context: path.join(HELPER_DIR, 'directory'),
395 | from: '**/*',
396 | to: 'nested'
397 | }]
398 | })
399 | .then(done)
400 | .catch(done);
401 | });
402 |
403 | it('can use a glob with a full path to move a file to the root directory', (done) => {
404 | runEmit({
405 | expectedAssetKeys: [
406 | 'file.txt'
407 | ],
408 | patterns: [{
409 | from: path.join(HELPER_DIR, '*.txt')
410 | }]
411 | })
412 | .then(done)
413 | .catch(done);
414 | });
415 |
416 | it('can use a glob with a full path to move multiple files to the root directory', (done) => {
417 | runEmit({
418 | expectedAssetKeys: [
419 | '[!]/hello.txt',
420 | 'file.txt',
421 | 'directory/directoryfile.txt',
422 | 'directory/nested/nestedfile.txt',
423 | '[special?directory]/directoryfile.txt',
424 | '[special?directory]/(special-*file).txt',
425 | '[special?directory]/nested/nestedfile.txt'
426 | ],
427 | patterns: [{
428 | from: path.join(HELPER_DIR, '**/*.txt')
429 | }]
430 | })
431 | .then(done)
432 | .catch(done);
433 | });
434 |
435 | it('can use a glob to move multiple files to a non-root directory with name, hash and ext', (done) => {
436 | runEmit({
437 | expectedAssetKeys: [
438 | 'nested/[!]/hello-d41d8c.txt',
439 | 'nested/binextension-d41d8c.bin',
440 | 'nested/file-22af64.txt',
441 | 'nested/file.txt-5b311c.gz',
442 | 'nested/directory/directoryfile-22af64.txt',
443 | 'nested/directory/nested/nestedfile-d41d8c.txt',
444 | 'nested/[special?directory]/(special-*file)-0bd650.txt',
445 | 'nested/[special?directory]/directoryfile-22af64.txt',
446 | 'nested/[special?directory]/nested/nestedfile-d41d8c.txt',
447 | 'nested/noextension-d41d8c'
448 | ],
449 | patterns: [{
450 | from: '**/*',
451 | to: 'nested/[path][name]-[hash:6].[ext]'
452 | }]
453 | })
454 | .then(done)
455 | .catch(done);
456 | });
457 |
458 | it('can flatten or normalize glob matches', (done) => {
459 | runEmit({
460 | expectedAssetKeys: [
461 | '[!]-hello.txt',
462 | '[special?directory]-(special-*file).txt',
463 | '[special?directory]-directoryfile.txt',
464 | 'directory-directoryfile.txt'
465 | ],
466 | patterns: [{
467 | from: '*/*.*',
468 | test: `([^\\${path.sep}]+)\\${path.sep}([^\\${path.sep}]+)\\.\\w+$`,
469 | to: '[1]-[2].[ext]'
470 | }]
471 | })
472 | .then(done)
473 | .catch(done);
474 | });
475 | });
476 |
477 | describe('with file in from', () => {
478 | it('can move a file to the root directory', (done) => {
479 | runEmit({
480 | expectedAssetKeys: [
481 | 'file.txt'
482 | ],
483 | patterns: [{
484 | from: 'file.txt'
485 | }]
486 | })
487 | .then(done)
488 | .catch(done);
489 | });
490 |
491 | it('can transform a file', (done) => {
492 | runEmit({
493 | expectedAssetKeys: [
494 | 'file.txt'
495 | ],
496 | expectedAssetContent: {
497 | 'file.txt': 'newchanged'
498 | },
499 | patterns: [{
500 | from: 'file.txt',
501 | transform: function(content, absoluteFrom) {
502 | expect(absoluteFrom).to.equal(path.join(HELPER_DIR, 'file.txt'));
503 | return content + 'changed';
504 | }
505 | }]
506 | })
507 | .then(done)
508 | .catch(done);
509 | });
510 |
511 | it('warns when file not found', (done) => {
512 | runEmit({
513 | expectedAssetKeys: [],
514 | expectedErrors: [
515 | `[copy-webpack-plugin] unable to locate 'nonexistent.txt' at '${HELPER_DIR}${path.sep}nonexistent.txt'`
516 | ],
517 | patterns: [{
518 | from: 'nonexistent.txt'
519 | }]
520 | })
521 | .then(done)
522 | .catch(done);
523 | });
524 |
525 | it('warns when tranform failed', (done) => {
526 | runEmit({
527 | expectedAssetKeys: [],
528 | expectedErrors: [
529 | 'a failure happened'
530 | ],
531 | patterns: [{
532 | from: 'file.txt',
533 | transform: function() {
534 | throw 'a failure happened';
535 | }
536 | }]
537 | })
538 | .then(done)
539 | .catch(done);
540 | });
541 |
542 | it('can use an absolute path to move a file to the root directory', (done) => {
543 | const absolutePath = path.resolve(HELPER_DIR, 'file.txt');
544 |
545 | runEmit({
546 | expectedAssetKeys: [
547 | 'file.txt'
548 | ],
549 | patterns: [{
550 | from: absolutePath
551 | }]
552 | })
553 | .then(done)
554 | .catch(done);
555 | });
556 |
557 | it('can move a file to a new directory without a forward slash', (done) => {
558 | runEmit({
559 | expectedAssetKeys: [
560 | 'newdirectory/file.txt'
561 | ],
562 | patterns: [{
563 | from: 'file.txt',
564 | to: 'newdirectory'
565 | }]
566 | })
567 | .then(done)
568 | .catch(done);
569 | });
570 |
571 | it('can move a file to the root directory using an absolute to', (done) => {
572 | runEmit({
573 | expectedAssetKeys: [
574 | 'file.txt'
575 | ],
576 | patterns: [{
577 | from: 'file.txt',
578 | to: BUILD_DIR
579 | }]
580 | })
581 | .then(done)
582 | .catch(done);
583 | });
584 |
585 | it('allows absolute to if outpath is defined with webpack-dev-server', (done) => {
586 | runEmit({
587 | compiler: new MockCompiler({
588 | outputPath: '/',
589 | devServer: {
590 | outputPath: BUILD_DIR
591 | }
592 | }),
593 | expectedAssetKeys: [
594 | 'file.txt'
595 | ],
596 | patterns: [{
597 | from: 'file.txt',
598 | to: BUILD_DIR
599 | }]
600 | })
601 | .then(done)
602 | .catch(done);
603 | });
604 |
605 | it('throws an error when output path isn\'t defined with webpack-dev-server', (done) => {
606 | runEmit({
607 | compiler: new MockCompiler({
608 | outputPath: '/'
609 | }),
610 | expectedAssetKeys: [],
611 | expectedErrors: [
612 | '[copy-webpack-plugin] Using older versions of webpack-dev-server, devServer.outputPath must be ' +
613 | 'defined to write to absolute paths'
614 | ],
615 | patterns: [{
616 | from: 'file.txt',
617 | to: BUILD_DIR
618 | }]
619 | })
620 | .then(done)
621 | .catch(done);
622 | });
623 |
624 | it('can move a file to a new directory using an absolute to', (done) => {
625 | runEmit({
626 | expectedAssetKeys: [
627 | '../tempdir/file.txt'
628 | ],
629 | patterns: [{
630 | from: 'file.txt',
631 | to: TEMP_DIR
632 | }]
633 | })
634 | .then(done)
635 | .catch(done);
636 | });
637 |
638 | it('can move a file to a new file using an absolute to', (done) => {
639 | const absolutePath = path.resolve(TEMP_DIR, 'newfile.txt');
640 |
641 | runEmit({
642 | expectedAssetKeys: [
643 | '../tempdir/newfile.txt'
644 | ],
645 | patterns: [{
646 | from: 'file.txt',
647 | to: absolutePath
648 | }]
649 | })
650 | .then(done)
651 | .catch(done);
652 | });
653 |
654 | it('can move a file to a new directory with a forward slash', (done) => {
655 | runEmit({
656 | expectedAssetKeys: [
657 | 'newdirectory/file.txt'
658 | ],
659 | patterns: [{
660 | from: 'file.txt',
661 | to: 'newdirectory/'
662 | }]
663 | })
664 | .then(done)
665 | .catch(done);
666 | });
667 |
668 | it('can move a file with a context containing special characters', (done) => {
669 | runEmit({
670 | expectedAssetKeys: [
671 | 'directoryfile.txt'
672 | ],
673 | patterns: [{
674 | from: 'directoryfile.txt',
675 | context: '[special?directory]'
676 | }]
677 | })
678 | .then(done)
679 | .catch(done);
680 | });
681 |
682 | it('can move a file with special characters with a context containing special characters', (done) => {
683 | runEmit({
684 | expectedAssetKeys: [
685 | '(special-*file).txt'
686 | ],
687 | patterns: [{
688 | from: '(special-*file).txt',
689 | context: '[special?directory]'
690 | }]
691 | })
692 | .then(done)
693 | .catch(done);
694 | });
695 |
696 | it('can move a file to a new directory with an extension', (done) => {
697 | runEmit({
698 | expectedAssetKeys: [
699 | 'newdirectory.ext/file.txt'
700 | ],
701 | patterns: [{
702 | from: 'file.txt',
703 | to: 'newdirectory.ext',
704 | toType: 'dir'
705 | }]
706 | })
707 | .then(done)
708 | .catch(done);
709 | });
710 |
711 | it('can move a file to a new directory with an extension and forward slash', (done) => {
712 | runEmit({
713 | expectedAssetKeys: [
714 | 'newdirectory.ext/file.txt'
715 | ],
716 | patterns: [{
717 | from: 'file.txt',
718 | to: 'newdirectory.ext/'
719 | }]
720 | })
721 | .then(done)
722 | .catch(done);
723 | });
724 |
725 | it('can move a file to a new file with a different name', (done) => {
726 | runEmit({
727 | expectedAssetKeys: [
728 | 'newname.txt'
729 | ],
730 | patterns: [{
731 | from: 'file.txt',
732 | to: 'newname.txt'
733 | }]
734 | })
735 | .then(done)
736 | .catch(done);
737 | });
738 |
739 | it('can move a file to a new file with no extension', (done) => {
740 | runEmit({
741 | expectedAssetKeys: [
742 | 'newname'
743 | ],
744 | patterns: [{
745 | from: 'file.txt',
746 | to: 'newname',
747 | toType: 'file'
748 | }]
749 | })
750 | .then(done)
751 | .catch(done);
752 | });
753 |
754 | it('can move a file without an extension to a file using a template', (done) => {
755 | runEmit({
756 | expectedAssetKeys: [
757 | 'noextension.newext'
758 | ],
759 | patterns: [{
760 | from: 'noextension',
761 | to: '[name][ext].newext'
762 | }]
763 | })
764 | .then(done)
765 | .catch(done);
766 | });
767 |
768 | it('can move a file with a ".bin" extension using a template', (done) => {
769 | runEmit({
770 | expectedAssetKeys: [
771 | 'binextension.bin'
772 | ],
773 | patterns: [{
774 | from: 'binextension.bin',
775 | to: '[name].[ext]'
776 | }]
777 | })
778 | .then(done)
779 | .catch(done);
780 | });
781 |
782 | it('can move a nested file to the root directory', (done) => {
783 | runEmit({
784 | expectedAssetKeys: [
785 | 'directoryfile.txt'
786 | ],
787 | patterns: [{
788 | from: 'directory/directoryfile.txt'
789 | }]
790 | })
791 | .then(done)
792 | .catch(done);
793 | });
794 |
795 | it('can use an absolute path to move a nested file to the root directory', (done) => {
796 | const absolutePath = path.resolve(HELPER_DIR, 'directory', 'directoryfile.txt');
797 |
798 | runEmit({
799 | expectedAssetKeys: [
800 | 'directoryfile.txt'
801 | ],
802 | patterns: [{
803 | from: absolutePath
804 | }]
805 | })
806 | .then(done)
807 | .catch(done);
808 | });
809 |
810 | it('can move a nested file to a new directory', (done) => {
811 | runEmit({
812 | expectedAssetKeys: [
813 | 'newdirectory/directoryfile.txt'
814 | ],
815 | patterns: [{
816 | from: 'directory/directoryfile.txt',
817 | to: 'newdirectory'
818 | }]
819 | })
820 | .then(done)
821 | .catch(done);
822 | });
823 |
824 | it('can use an absolute path to move a nested file to a new directory', (done) => {
825 | const absolutePath = path.resolve(HELPER_DIR, 'directory', 'directoryfile.txt');
826 |
827 | runEmit({
828 | expectedAssetKeys: [
829 | 'newdirectory/directoryfile.txt'
830 | ],
831 | patterns: [{
832 | from: absolutePath,
833 | to: 'newdirectory'
834 | }]
835 | })
836 | .then(done)
837 | .catch(done);
838 | });
839 |
840 | it('won\'t overwrite a file already in the compilation', (done) => {
841 | runForce({
842 | existingAsset: 'file.txt',
843 | expectedAssetContent: {
844 | 'file.txt': 'existing'
845 | },
846 | patterns: [{
847 | from: 'file.txt'
848 | }]
849 | })
850 | .then(done)
851 | .catch(done);
852 | });
853 |
854 | it('can force overwrite of a file already in the compilation', (done) => {
855 | runForce({
856 | existingAsset: 'file.txt',
857 | expectedAssetContent: {
858 | 'file.txt': 'new'
859 | },
860 | patterns: [{
861 | force: true,
862 | from: 'file.txt'
863 | }]
864 | })
865 | .then(done)
866 | .catch(done);
867 | });
868 |
869 | it('adds the file to the watch list', (done) => {
870 | run({
871 | patterns: [{
872 | from: 'file.txt'
873 | }]
874 | })
875 | .then((compilation) => {
876 | const absFrom = path.join(HELPER_DIR, 'file.txt');
877 |
878 | expect(compilation.fileDependencies).to.have.members([absFrom]);
879 | })
880 | .then(done)
881 | .catch(done);
882 | });
883 |
884 | it('only include files that have changed', (done) => {
885 | runChange({
886 | expectedAssetKeys: [
887 | 'tempfile1.txt'
888 | ],
889 | newFileLoc1: path.join(HELPER_DIR, 'tempfile1.txt'),
890 | newFileLoc2: path.join(HELPER_DIR, 'tempfile2.txt'),
891 | patterns: [{
892 | from: 'tempfile1.txt'
893 | }, {
894 | from: 'tempfile2.txt'
895 | }]
896 | })
897 | .then(done)
898 | .catch(done);
899 | });
900 |
901 | it('ignores files in pattern', (done) => {
902 | runEmit({
903 | expectedAssetKeys: [
904 | '[!]/hello.txt',
905 | 'binextension.bin',
906 | 'directory/directoryfile.txt',
907 | 'directory/nested/nestedfile.txt',
908 | '[special?directory]/directoryfile.txt',
909 | '[special?directory]/(special-*file).txt',
910 | '[special?directory]/nested/nestedfile.txt',
911 | 'noextension'
912 | ],
913 | patterns: [{
914 | from: '**/*',
915 | ignore: [
916 | 'file.*'
917 | ]
918 | }]
919 | })
920 | .then(done)
921 | .catch(done);
922 | });
923 |
924 | it('allows pattern to contain name, hash or ext', (done) => {
925 | runEmit({
926 | expectedAssetKeys: [
927 | 'directory/directoryfile-22af64.txt'
928 | ],
929 | patterns: [{
930 | from: 'directory/directoryfile.txt',
931 | to: 'directory/[name]-[hash:6].[ext]'
932 | }]
933 | })
934 | .then(done)
935 | .catch(done);
936 | });
937 |
938 | it('transform with promise', (done) => {
939 | runEmit({
940 | expectedAssetKeys: [
941 | 'file.txt'
942 | ],
943 | expectedAssetContent: {
944 | 'file.txt': 'newchanged!'
945 | },
946 | patterns: [{
947 | from: 'file.txt',
948 | transform: function(content) {
949 | return new Promise((resolve) => {
950 | resolve(content + 'changed!');
951 | });
952 | }
953 | }]
954 | })
955 | .then(done)
956 | .catch(done);
957 | });
958 |
959 | it('same file to multiple targets', (done) => {
960 | runEmit({
961 | expectedAssetKeys: [
962 | 'first/file.txt',
963 | 'second/file.txt'
964 | ],
965 | patterns: [{
966 | from: 'file.txt',
967 | to: 'first/file.txt'
968 | }, {
969 | from: 'file.txt',
970 | to: 'second/file.txt'
971 | }]
972 | })
973 | .then(done)
974 | .catch(done);
975 | });
976 | });
977 |
978 | describe('with directory in from', () => {
979 | it('can move a directory\'s contents to the root directory', (done) => {
980 | runEmit({
981 | expectedAssetKeys: [
982 | '.dottedfile',
983 | 'directoryfile.txt',
984 | 'nested/nestedfile.txt'
985 | ],
986 | patterns: [{
987 | from: 'directory'
988 | }]
989 | })
990 | .then(done)
991 | .catch(done);
992 | });
993 |
994 | it('can move a directory\'s contents to the root directory using from with special characters', (done) => {
995 | runEmit({
996 | expectedAssetKeys: [
997 | 'directoryfile.txt',
998 | '(special-*file).txt',
999 | 'nested/nestedfile.txt'
1000 | ],
1001 | patterns: [{
1002 | from: (path.sep === '/' ? '[special?directory]' : '[specialdirectory]')
1003 | }]
1004 | })
1005 | .then(done)
1006 | .catch(done);
1007 | });
1008 |
1009 | it('can move a directory\'s contents to the root directory using context with special characters', (done) => {
1010 | runEmit({
1011 | expectedAssetKeys: [
1012 | 'directoryfile.txt',
1013 | '(special-*file).txt',
1014 | 'nested/nestedfile.txt'
1015 | ],
1016 | patterns: [{
1017 | from: '.',
1018 | context: '[special?directory]'
1019 | }]
1020 | })
1021 | .then(done)
1022 | .catch(done);
1023 | });
1024 |
1025 | it('warns when directory not found', (done) => {
1026 | runEmit({
1027 | expectedAssetKeys: [],
1028 | expectedErrors: [
1029 | `[copy-webpack-plugin] unable to locate 'nonexistent' at '${HELPER_DIR}${path.sep}nonexistent'`
1030 | ],
1031 | patterns: [{
1032 | from: 'nonexistent'
1033 | }]
1034 | })
1035 | .then(done)
1036 | .catch(done);
1037 | });
1038 |
1039 | it('can use an absolute path to move a directory\'s contents to the root directory', (done) => {
1040 | const absolutePath = path.resolve(HELPER_DIR, 'directory');
1041 |
1042 | runEmit({
1043 | expectedAssetKeys: [
1044 | '.dottedfile',
1045 | 'directoryfile.txt',
1046 | 'nested/nestedfile.txt'
1047 | ],
1048 | patterns: [{
1049 | from: absolutePath
1050 | }]
1051 | })
1052 | .then(done)
1053 | .catch(done);
1054 | });
1055 |
1056 | it('can move a directory\'s contents to a new directory', (done) => {
1057 | runEmit({
1058 | expectedAssetKeys: [
1059 | 'newdirectory/.dottedfile',
1060 | 'newdirectory/directoryfile.txt',
1061 | 'newdirectory/nested/nestedfile.txt'
1062 | ],
1063 | patterns: [{
1064 | from: 'directory',
1065 | to: 'newdirectory'
1066 | }]
1067 | })
1068 | .then(done)
1069 | .catch(done);
1070 | });
1071 |
1072 | it('can move a directory\'s contents to a new directory using a pattern context', (done) => {
1073 | runEmit({
1074 | expectedAssetKeys: [
1075 | 'newdirectory/nestedfile.txt'
1076 | ],
1077 | patterns: [{
1078 | context: 'directory',
1079 | from: 'nested',
1080 | to: 'newdirectory'
1081 | }]
1082 | })
1083 | .then(done)
1084 | .catch(done);
1085 | });
1086 |
1087 | it('can flatten a directory\'s contents to a new directory', (done) => {
1088 | runEmit({
1089 | expectedAssetKeys: [
1090 | 'newdirectory/.dottedfile',
1091 | 'newdirectory/directoryfile.txt',
1092 | 'newdirectory/nestedfile.txt'
1093 | ],
1094 | patterns: [{
1095 | flatten: true,
1096 | from: 'directory',
1097 | to: 'newdirectory'
1098 | }]
1099 | })
1100 | .then(done)
1101 | .catch(done);
1102 | });
1103 |
1104 | it('can move a directory\'s contents to a new directory using an absolute to', (done) => {
1105 | runEmit({
1106 | expectedAssetKeys: [
1107 | '../tempdir/.dottedfile',
1108 | '../tempdir/directoryfile.txt',
1109 | '../tempdir/nested/nestedfile.txt'
1110 | ],
1111 | patterns: [{
1112 | from: 'directory',
1113 | to: TEMP_DIR
1114 | }]
1115 | })
1116 | .then(done)
1117 | .catch(done);
1118 | });
1119 |
1120 | it('can move a nested directory\'s contents to the root directory', (done) => {
1121 | runEmit({
1122 | expectedAssetKeys: [
1123 | 'nestedfile.txt'
1124 | ],
1125 | patterns: [{
1126 | from: 'directory/nested'
1127 | }]
1128 | })
1129 | .then(done)
1130 | .catch(done);
1131 | });
1132 |
1133 | it('can move a nested directory\'s contents to a new directory', (done) => {
1134 | runEmit({
1135 | expectedAssetKeys: [
1136 | 'newdirectory/nestedfile.txt'
1137 | ],
1138 | patterns: [{
1139 | from: 'directory/nested',
1140 | to: 'newdirectory'
1141 | }]
1142 | })
1143 | .then(done)
1144 | .catch(done);
1145 | });
1146 |
1147 | it('can use an absolute path to move a nested directory\'s contents to a new directory', (done) => {
1148 | const absolutePath = path.resolve(HELPER_DIR, 'directory', 'nested');
1149 |
1150 | runEmit({
1151 | expectedAssetKeys: [
1152 | 'newdirectory/nestedfile.txt'
1153 | ],
1154 | patterns: [{
1155 | from: absolutePath,
1156 | to: 'newdirectory'
1157 | }]
1158 | })
1159 | .then(done)
1160 | .catch(done);
1161 | });
1162 |
1163 | it('won\'t overwrite a file already in the compilation', (done) => {
1164 | runForce({
1165 | existingAsset: 'directoryfile.txt',
1166 | expectedAssetContent: {
1167 | 'directoryfile.txt': 'existing'
1168 | },
1169 | patterns: [{
1170 | from: 'directory'
1171 | }]
1172 | })
1173 | .then(done)
1174 | .catch(done);
1175 | });
1176 |
1177 | it('can force overwrite of a file already in the compilation', (done) => {
1178 | runForce({
1179 | existingAsset: 'directoryfile.txt',
1180 | expectedAssetContent: {
1181 | 'directoryfile.txt': 'new'
1182 | },
1183 | patterns: [{
1184 | force: true,
1185 | from: 'directory'
1186 | }]
1187 | })
1188 | .then(done)
1189 | .catch(done);
1190 | });
1191 |
1192 | it('adds the directory to the watch list', (done) => {
1193 | run({
1194 | patterns: [{
1195 | from: 'directory'
1196 | }]
1197 | })
1198 | .then((compilation) => {
1199 | const absFrom = path.join(HELPER_DIR, 'directory');
1200 |
1201 | expect(compilation.contextDependencies).to.have.members([absFrom]);
1202 | })
1203 | .then(done)
1204 | .catch(done);
1205 | });
1206 |
1207 | it('only include files that have changed', (done) => {
1208 | runChange({
1209 | expectedAssetKeys: [
1210 | 'tempfile1.txt'
1211 | ],
1212 | newFileLoc1: path.join(HELPER_DIR, 'directory', 'tempfile1.txt'),
1213 | newFileLoc2: path.join(HELPER_DIR, 'directory', 'tempfile2.txt'),
1214 | patterns: [{
1215 | from: 'directory'
1216 | }]
1217 | })
1218 | .then(done)
1219 | .catch(done);
1220 | });
1221 |
1222 | it('include all files if copyUnmodified is true', (done) => {
1223 | runChange({
1224 | expectedAssetKeys: [
1225 | '.dottedfile',
1226 | 'directoryfile.txt',
1227 | 'nested/nestedfile.txt',
1228 | 'tempfile1.txt',
1229 | 'tempfile2.txt'
1230 | ],
1231 | newFileLoc1: path.join(HELPER_DIR, 'directory', 'tempfile1.txt'),
1232 | newFileLoc2: path.join(HELPER_DIR, 'directory', 'tempfile2.txt'),
1233 | options: {
1234 | copyUnmodified: true
1235 | },
1236 | patterns: [{
1237 | from: 'directory'
1238 | }]
1239 | })
1240 | .then(done)
1241 | .catch(done);
1242 | });
1243 |
1244 | it('can move multiple files to a non-root directory with name, hash and ext', (done) => {
1245 | runEmit({
1246 | expectedAssetKeys: [
1247 | 'nested/.dottedfile-79d39f',
1248 | 'nested/directoryfile-22af64.txt',
1249 | 'nested/nested/nestedfile-d41d8c.txt'
1250 | ],
1251 | patterns: [{
1252 | from: 'directory',
1253 | to: 'nested/[path][name]-[hash:6].[ext]'
1254 | }]
1255 | })
1256 | .then(done)
1257 | .catch(done);
1258 | });
1259 | });
1260 |
1261 | describe('with simple string patterns', () => {
1262 | it('can move multiple files', (done) => {
1263 | runEmit({
1264 | expectedAssetKeys: [
1265 | 'binextension.bin',
1266 | 'file.txt',
1267 | 'noextension'
1268 | ],
1269 | patterns: [
1270 | 'binextension.bin',
1271 | 'file.txt',
1272 | 'noextension'
1273 | ]
1274 | })
1275 | .then(done)
1276 | .catch(done);
1277 | });
1278 | });
1279 |
1280 | describe('options', () => {
1281 | describe('ignore', () => {
1282 | it('ignores files when from is a file', (done) => {
1283 | runEmit({
1284 | expectedAssetKeys: [
1285 | 'directoryfile.txt'
1286 | ],
1287 | options: {
1288 | ignore: [
1289 | 'file.*'
1290 | ]
1291 | },
1292 | patterns: [{
1293 | from: 'file.txt'
1294 | }, {
1295 | from: 'directory/directoryfile.txt'
1296 | }]
1297 | })
1298 | .then(done)
1299 | .catch(done);
1300 | });
1301 |
1302 | it('ignores files when from is a directory', (done) => {
1303 | runEmit({
1304 | expectedAssetKeys: [
1305 | '.dottedfile',
1306 | 'directoryfile.txt'
1307 | ],
1308 | options: {
1309 | ignore: [
1310 | '*/nestedfile.*'
1311 | ]
1312 | },
1313 | patterns: [{
1314 | from: 'directory'
1315 | }]
1316 | })
1317 | .then(done)
1318 | .catch(done);
1319 | });
1320 |
1321 | it('ignores files with a certain extension', (done) => {
1322 | runEmit({
1323 | expectedAssetKeys: [
1324 | '.dottedfile'
1325 | ],
1326 | options: {
1327 | ignore: [
1328 | '*.txt'
1329 | ]
1330 | },
1331 | patterns: [{
1332 | from: 'directory'
1333 | }]
1334 | })
1335 | .then(done)
1336 | .catch(done);
1337 | });
1338 |
1339 | it('ignores files that start with a dot', (done) => {
1340 | runEmit({
1341 | expectedAssetKeys: [
1342 | '[!]/hello.txt',
1343 | 'binextension.bin',
1344 | 'file.txt',
1345 | 'file.txt.gz',
1346 | 'directory/directoryfile.txt',
1347 | 'directory/nested/nestedfile.txt',
1348 | '[special?directory]/directoryfile.txt',
1349 | '[special?directory]/(special-*file).txt',
1350 | '[special?directory]/nested/nestedfile.txt',
1351 | 'noextension'
1352 | ],
1353 | options: {
1354 | ignore: [
1355 | '.dottedfile'
1356 | ]
1357 | },
1358 | patterns: [{
1359 | from: '.'
1360 | }]
1361 | })
1362 | .then(done)
1363 | .catch(done);
1364 | });
1365 |
1366 | it('ignores all files except those with dots', (done) => {
1367 | runEmit({
1368 | expectedAssetKeys: [
1369 | 'directory/.dottedfile'
1370 | ],
1371 | options: {
1372 | ignore: [{
1373 | dot: false,
1374 | glob: '**/*'
1375 | }]
1376 | },
1377 | patterns: [{
1378 | from: '.'
1379 | }]
1380 | })
1381 | .then(done)
1382 | .catch(done);
1383 | });
1384 |
1385 | it('ignores all files even if they start with a dot', (done) => {
1386 | runEmit({
1387 | expectedAssetKeys: [],
1388 | options: {
1389 | ignore: ['**/*']
1390 | },
1391 | patterns: [{
1392 | from: '.'
1393 | }]
1394 |
1395 | })
1396 | .then(done)
1397 | .catch(done);
1398 | });
1399 |
1400 | it('ignores nested directory', (done) => {
1401 | runEmit({
1402 | expectedAssetKeys: [
1403 | '[!]/hello.txt',
1404 | 'binextension.bin',
1405 | 'file.txt',
1406 | 'file.txt.gz',
1407 | 'noextension'
1408 | ],
1409 | options: {
1410 | ignore: ['directory/**/*', `[[]special${process.platform === 'win32' ? '' : '[?]'}directory]/**/*`]
1411 | },
1412 | patterns: [{
1413 | from: '.'
1414 | }]
1415 |
1416 | })
1417 | .then(done)
1418 | .catch(done);
1419 | });
1420 |
1421 | if (path.sep === '/') {
1422 | it('ignores nested directory(can use "\\" to escape if path.sep is "/")', (done) => {
1423 | runEmit({
1424 | expectedAssetKeys: [
1425 | '[!]/hello.txt',
1426 | 'binextension.bin',
1427 | 'file.txt',
1428 | 'file.txt.gz',
1429 | 'noextension'
1430 | ],
1431 | options: {
1432 | ignore: ['directory/**/*', '\\[special\\?directory\\]/**/*']
1433 | },
1434 | patterns: [{
1435 | from: '.'
1436 | }]
1437 |
1438 | })
1439 | .then(done)
1440 | .catch(done);
1441 | });
1442 | }
1443 |
1444 | it('ignores nested directory (glob)', (done) => {
1445 | runEmit({
1446 | expectedAssetKeys: [
1447 | '.dottedfile',
1448 | 'directoryfile.txt'
1449 | ],
1450 | options: {
1451 | ignore: ['nested/**/*']
1452 | },
1453 | patterns: [{
1454 | from: 'directory'
1455 | }]
1456 | })
1457 | .then(done)
1458 | .catch(done);
1459 | });
1460 | });
1461 |
1462 | describe('context', () => {
1463 | it('overrides webpack config context with absolute path', (done) => {
1464 | runEmit({
1465 | expectedAssetKeys: [
1466 | 'newdirectory/nestedfile.txt'
1467 | ],
1468 | options: {
1469 | context: path.resolve(HELPER_DIR, 'directory')
1470 | },
1471 | patterns: [{
1472 | from: 'nested',
1473 | to: 'newdirectory'
1474 | }]
1475 | })
1476 | .then(done)
1477 | .catch(done);
1478 | });
1479 |
1480 | it('overrides webpack config context with relative path', (done) => {
1481 | runEmit({
1482 | expectedAssetKeys: [
1483 | 'newdirectory/nestedfile.txt'
1484 | ],
1485 | options: {
1486 | context: 'directory'
1487 | },
1488 | patterns: [{
1489 | from: 'nested',
1490 | to: 'newdirectory'
1491 | }]
1492 | })
1493 | .then(done)
1494 | .catch(done);
1495 | });
1496 |
1497 | it('is overridden by pattern context', (done) => {
1498 | runEmit({
1499 | expectedAssetKeys: [
1500 | 'newdirectory/nestedfile.txt'
1501 | ],
1502 | options: {
1503 | context: 'directory'
1504 | },
1505 | patterns: [{
1506 | context: 'nested',
1507 | from: '.',
1508 | to: 'newdirectory'
1509 | }]
1510 | })
1511 | .then(done)
1512 | .catch(done);
1513 | });
1514 | });
1515 |
1516 | describe('cache', () => {
1517 | const cacheDir = findCacheDir({ name: 'copy-webpack-plugin' });
1518 |
1519 | beforeEach(() => cacache.rm.all(cacheDir));
1520 |
1521 | it('file should be cached', (done) => {
1522 | const newContent = 'newchanged!';
1523 | const from = 'file.txt';
1524 |
1525 | runEmit({
1526 | expectedAssetKeys: [
1527 | 'file.txt'
1528 | ],
1529 | expectedAssetContent: {
1530 | 'file.txt': newContent
1531 | },
1532 | patterns: [{
1533 | from: from,
1534 | cache: true,
1535 | transform: function(content) {
1536 | return new Promise((resolve) => {
1537 | resolve(content + 'changed!');
1538 | });
1539 | }
1540 | }]
1541 | })
1542 | .then(() => {
1543 | return cacache
1544 | .ls(cacheDir)
1545 | .then((cacheEntries) => {
1546 | const cacheKeys = Object.keys(cacheEntries);
1547 |
1548 | expect(cacheKeys).to.have.lengthOf(1);
1549 |
1550 | cacheKeys.forEach((cacheKey) => {
1551 | const cacheEntry = new Function(`'use strict'\nreturn ${cacheKey}`)();
1552 |
1553 | expect(cacheEntry.pattern.from).to.equal(from);
1554 | });
1555 | });
1556 | })
1557 | .then(done)
1558 | .catch(done);
1559 | });
1560 |
1561 | it('files in directory should be cached', (done) => {
1562 | const from = 'directory';
1563 |
1564 | runEmit({
1565 | expectedAssetKeys: [
1566 | '.dottedfile',
1567 | 'directoryfile.txt',
1568 | 'nested/nestedfile.txt'
1569 | ],
1570 | expectedAssetContent: {
1571 | '.dottedfile': 'dottedfile contents\nchanged!',
1572 | 'directoryfile.txt': 'newchanged!',
1573 | 'nested/nestedfile.txt': 'changed!'
1574 | },
1575 | patterns: [{
1576 | from: from,
1577 | cache: true,
1578 | transform: function(content) {
1579 | return new Promise((resolve) => {
1580 | resolve(content + 'changed!');
1581 | });
1582 | }
1583 | }]
1584 | })
1585 | .then(() => {
1586 | return cacache
1587 | .ls(cacheDir)
1588 | .then((cacheEntries) => {
1589 | const cacheKeys = Object.keys(cacheEntries);
1590 |
1591 | expect(cacheKeys).to.have.lengthOf(3);
1592 |
1593 | cacheKeys.forEach((cacheKey) => {
1594 | const cacheEntry = new Function(`'use strict'\nreturn ${cacheKey}`)();
1595 |
1596 | expect(cacheEntry.pattern.from).to.equal(from);
1597 | });
1598 | });
1599 | })
1600 | .then(done)
1601 | .catch(done);
1602 | });
1603 |
1604 | it('glob should be cached', (done) => {
1605 | const from = '*.txt';
1606 |
1607 | runEmit({
1608 | expectedAssetKeys: [
1609 | 'file.txt'
1610 | ],
1611 | expectedAssetContent: {
1612 | 'file.txt': 'newchanged!'
1613 | },
1614 | patterns: [{
1615 | from: from,
1616 | cache: true,
1617 | transform: function(content) {
1618 | return new Promise((resolve) => {
1619 | resolve(content + 'changed!');
1620 | });
1621 | }
1622 | }]
1623 | })
1624 | .then(() => {
1625 | return cacache
1626 | .ls(cacheDir)
1627 | .then((cacheEntries) => {
1628 | const cacheKeys = Object.keys(cacheEntries);
1629 |
1630 | expect(cacheKeys).to.have.lengthOf(1);
1631 |
1632 | cacheKeys.forEach((cacheKey) => {
1633 | const cacheEntry = new Function(`'use strict'\nreturn ${cacheKey}`)();
1634 |
1635 | expect(cacheEntry.pattern.from).to.equal(from);
1636 | });
1637 | });
1638 | })
1639 | .then(done)
1640 | .catch(done);
1641 | });
1642 |
1643 | it('file should be cached with custom cache key', (done) => {
1644 | const newContent = 'newchanged!';
1645 | const from = 'file.txt';
1646 |
1647 | runEmit({
1648 | expectedAssetKeys: [
1649 | 'file.txt'
1650 | ],
1651 | expectedAssetContent: {
1652 | 'file.txt': newContent
1653 | },
1654 | patterns: [{
1655 | from: from,
1656 | cache: {
1657 | key: 'foobar'
1658 | },
1659 | transform: function(content) {
1660 | return new Promise((resolve) => {
1661 | resolve(content + 'changed!');
1662 | });
1663 | }
1664 | }]
1665 | })
1666 | .then(() => {
1667 | return cacache
1668 | .ls(cacheDir)
1669 | .then((cacheEntries) => {
1670 | const cacheKeys = Object.keys(cacheEntries);
1671 |
1672 | expect(cacheKeys).to.have.lengthOf(1);
1673 |
1674 | cacheKeys.forEach((cacheKey) => {
1675 | expect(cacheKey).to.equal('foobar');
1676 | });
1677 | });
1678 | })
1679 | .then(done)
1680 | .catch(done);
1681 | });
1682 |
1683 | it('binary file should be cached', (done) => {
1684 | const from = 'file.txt.gz';
1685 | const content = fs.readFileSync(path.join(HELPER_DIR, from));
1686 | const expectedNewContent = zlib.gzipSync('newchanged!');
1687 |
1688 | expect(isGzip(content)).to.equal(true);
1689 | expect(isGzip(expectedNewContent)).to.equal(true);
1690 |
1691 | runEmit({
1692 | expectedAssetKeys: [
1693 | 'file.txt.gz'
1694 | ],
1695 | expectedAssetContent: {
1696 | 'file.txt.gz': expectedNewContent
1697 | },
1698 | patterns: [{
1699 | from: from,
1700 | cache: true,
1701 | transform: function(content) {
1702 | expect(isGzip(content)).to.equal(true);
1703 |
1704 | return new Promise((resolve) => {
1705 | zlib.unzip(content, (error, content) => {
1706 | if (error) {
1707 | throw error;
1708 | }
1709 |
1710 | const newContent = new Buffer(content + 'changed!');
1711 |
1712 | zlib.gzip(newContent, (error, compressedData) => {
1713 | if (error) {
1714 | throw error;
1715 | }
1716 |
1717 | expect(isGzip(compressedData)).to.equal(true);
1718 |
1719 | return resolve(compressedData);
1720 | });
1721 | });
1722 | });
1723 | }
1724 | }]
1725 | })
1726 | .then(() => {
1727 | return cacache
1728 | .ls(cacheDir)
1729 | .then((cacheEntries) => {
1730 | const cacheKeys = Object.keys(cacheEntries);
1731 |
1732 | expect(cacheKeys).to.have.lengthOf(1);
1733 |
1734 | cacheKeys.forEach((cacheKey) => {
1735 | const cacheEntry = new Function(`'use strict'\nreturn ${cacheKey}`)();
1736 |
1737 | expect(cacheEntry.pattern.from).to.equal(from);
1738 | });
1739 | });
1740 | })
1741 | .then(done)
1742 | .catch(done);
1743 | });
1744 |
1745 | after(() => cacache.rm.all(cacheDir));
1746 | });
1747 | });
1748 | });
1749 |
--------------------------------------------------------------------------------
/tests/utils/removeIllegalCharacterForWindows.js:
--------------------------------------------------------------------------------
1 | module.exports = function (string) {
2 | return process.platform !== 'win32' ? string : string.replace(/[*?"<>|]/g, '');
3 | };
4 |
5 |
--------------------------------------------------------------------------------
/tests/weappHelpers/components/base/Dep/index.js:
--------------------------------------------------------------------------------
1 | export default function Dep () {}
--------------------------------------------------------------------------------
/tests/weappHelpers/components/base/Page.js:
--------------------------------------------------------------------------------
1 | import Dep from './Dep';
2 |
3 | export default function Page () {
4 | console.log(Dep);
5 | }
--------------------------------------------------------------------------------
/tests/weappHelpers/components/button/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/weappHelpers/components/button/index.js
--------------------------------------------------------------------------------
/tests/weappHelpers/components/button/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/tests/weappHelpers/components/button/index.wxml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/weappHelpers/components/button/index.wxml
--------------------------------------------------------------------------------
/tests/weappHelpers/components/comp1/index.js:
--------------------------------------------------------------------------------
1 | // import Page from '../base/Page';
2 | const Page = require('../base/Page');
3 |
4 | Page();
--------------------------------------------------------------------------------
/tests/weappHelpers/components/comp1/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "component": true
3 | }
--------------------------------------------------------------------------------
/tests/weappHelpers/components/comp1/index.wxml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/weappHelpers/components/comp1/index.wxml
--------------------------------------------------------------------------------
/tests/weappHelpers/components/panel/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/weappHelpers/components/panel/index.js
--------------------------------------------------------------------------------
/tests/weappHelpers/components/panel/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {
3 | "button": "../button/index"
4 | }
5 | }
--------------------------------------------------------------------------------
/tests/weappHelpers/components/panel/index.wxml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/weappHelpers/components/panel/index.wxml
--------------------------------------------------------------------------------
/tests/weappHelpers/entries.js:
--------------------------------------------------------------------------------
1 | module.exports = [
2 | {
3 | path: 'pages/normal',
4 | config: {
5 | usingComponents: {
6 | 'button': '/components/button/index'
7 | }
8 | }
9 | },
10 | {
11 | path: 'pages/self',
12 | config: {
13 | usingComponents: {
14 | 'panel': '../components/panel/index'
15 | }
16 | }
17 | }
18 | ];
--------------------------------------------------------------------------------
/tests/weappHelpers/pages/native/index.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/weappHelpers/pages/native/index.js
--------------------------------------------------------------------------------
/tests/weappHelpers/pages/native/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/tests/weappHelpers/pages/native/index.wxml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jessejyang/import-weapp-component/3e70cd33b6097d155893f7556435a42345c51de8/tests/weappHelpers/pages/native/index.wxml
--------------------------------------------------------------------------------
/tests/weappHelpers/pages/normal/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {
3 | "button": "/components/panel/index"
4 | }
5 | }
--------------------------------------------------------------------------------
/tests/weappHelpers/pages/notexist/index.json:
--------------------------------------------------------------------------------
1 | {}
--------------------------------------------------------------------------------
/tests/weappHelpers/pages/self/index.json:
--------------------------------------------------------------------------------
1 | {
2 | "usingComponents": {
3 | "button": "../../components/button/index"
4 | }
5 | }
--------------------------------------------------------------------------------