-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
270 additions
and
79 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
'use strict'; // eslint-disable-line | ||
|
||
const path = require('path'); | ||
const utils = require('loader-utils'); | ||
|
||
/** | ||
* Generate output name from output pattern | ||
* | ||
* @link https://github.com/kevlened/copy-webpack-plugin/blob/323b1d74ef35ed2221637d8028b1bef854deb523/src/writeFile.js#L31-L65 | ||
* @param {string} pattern | ||
* @param {string} relativeFrom | ||
* @param {binary} content | ||
* @return {string} | ||
*/ | ||
module.exports = (pattern, relativeFrom, content) => { | ||
let webpackTo = pattern; | ||
let resourcePath = relativeFrom; | ||
|
||
/* A hack so .dotted files don't get parsed as extensions */ | ||
const basename = path.basename(resourcePath); | ||
let dotRemoved = false; | ||
if (basename[0] === '.') { | ||
dotRemoved = true; | ||
resourcePath = path.join(path.dirname(resourcePath), basename.slice(1)); | ||
} | ||
|
||
/** | ||
* If it doesn't have an extension, remove it from the pattern | ||
* ie. [name].[ext] or [name][ext] both become [name] | ||
*/ | ||
if (!path.extname(resourcePath)) { | ||
webpackTo = webpackTo.replace(/\.?\[ext]/g, ''); | ||
} | ||
|
||
/** | ||
* A hack because loaderUtils.interpolateName doesn't | ||
* find the right path if no directory is defined | ||
* ie. [path] applied to 'file.txt' would return 'file' | ||
*/ | ||
if (resourcePath.indexOf('/') < 0) { | ||
resourcePath = `/${resourcePath}`; | ||
} | ||
|
||
webpackTo = utils.interpolateName({ resourcePath }, webpackTo, { content }); | ||
|
||
if (dotRemoved) { | ||
webpackTo = path.join(path.dirname(webpackTo), `.${path.basename(webpackTo)}`); | ||
} | ||
return webpackTo; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** | ||
* Node-style asynchronous function. | ||
* | ||
* @callback nodeAsyncCallback | ||
* @param {string|null} err | ||
* @param {*} data | ||
*/ | ||
/** | ||
* Promisify node-style asynchronous functions | ||
* | ||
* @param {nodeAsyncCallback} fn - Function with node-style callback | ||
* @param {this} [scope] - Scope to which the function should be bound. Default: fn | ||
* @returns {Promise} - An instance of Promise | ||
*/ | ||
module.exports = (fn, scope) => function callback() { | ||
const args = [].slice.call(arguments); | ||
return new Promise((resolve, reject) => { | ||
args.push((err, data) => (err === null ? resolve(data) : reject(err))); | ||
return fn.apply(scope || fn, args); | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
'use strict'; // eslint-disable-line | ||
|
||
const fs = require('fs'); | ||
const path = require('path'); | ||
const glob = require('glob'); | ||
const utils = require('loader-utils'); | ||
const includes = require('lodash/includes'); | ||
|
||
const interpolateName = require('./util/interpolateName'); | ||
const promisify = require('./util/promisify'); | ||
|
||
const fixPath = v => v.replace(/\\/g, '/'); | ||
const errorMsg = msg => `\x1b[31m${msg}\x1b[0m`; | ||
|
||
const GLOB_CWD_AUTO = null; | ||
|
||
const globAsync = promisify(glob); | ||
const statAsync = promisify(fs.stat); | ||
const readFileAsync = promisify(fs.readFile); | ||
|
||
class PatternUndefinedError extends Error { | ||
constructor() { | ||
super(errorMsg('[copy-globs] You must provide glob pattern.')); | ||
} | ||
} | ||
|
||
class ArgsArrayError extends TypeError { | ||
constructor() { | ||
super(errorMsg( | ||
'[copy-globs] pattern cannot be an array.\n' + | ||
'For multiple folders, use something like:\n\n' + | ||
' +(images|fonts)/**/*\n\n' + | ||
'See also: https://github.com/isaacs/node-glob#glob-primer\n' | ||
)); | ||
} | ||
} | ||
|
||
/** | ||
* Throws an error if pattern is an array or undefined | ||
* | ||
* @param pattern | ||
*/ | ||
const testPattern = (pattern) => { | ||
if (pattern === undefined) { | ||
throw new PatternUndefinedError(); | ||
} | ||
if (Array.isArray(pattern)) { | ||
throw new ArgsArrayError(); | ||
} | ||
}; | ||
|
||
const normalizeArguments = (input) => { | ||
testPattern(input); | ||
const options = {}; | ||
if (typeof input === 'string') { | ||
options.pattern = input; | ||
} else { | ||
testPattern(input.pattern); | ||
return input; | ||
} | ||
return options; | ||
}; | ||
|
||
module.exports = class { | ||
constructor(o) { | ||
const options = normalizeArguments(o); | ||
this.pattern = options.pattern; | ||
this.disable = options.disable; | ||
this.output = options.output || '[path][name].[ext]'; | ||
this.globOptions = Object.assign(options.globOptions || {}, { cwd: GLOB_CWD_AUTO }); | ||
this.globOptions.nodir = true; | ||
this.manifest = options.manifest || {}; | ||
this.files = []; | ||
} | ||
apply(compiler) { | ||
if (this.disable) { | ||
return; | ||
} | ||
this.compiler = compiler; | ||
this.resolveWorkingDirectory(); | ||
compiler.plugin('emit', this.emitHandler.bind(this)); | ||
compiler.plugin('after-emit', this.afterEmitHandler.bind(this)); | ||
} | ||
emitHandler(compilation, callback) { | ||
this.compilation = compilation; | ||
globAsync(this.pattern, this.globOptions) | ||
.then( | ||
paths => Promise.all(paths.map(this.processAsset.bind(this))), | ||
err => compilation.errors.push(err) | ||
) | ||
.then(() => { | ||
Object.keys(this.files).forEach((absoluteFrom) => { | ||
const file = this.files[absoluteFrom]; | ||
this.manifest[file.relativeFrom] = file.webpackTo; | ||
this.compilation.assets[file.webpackTo] = { | ||
size: () => file.stat.size, | ||
source: () => file.content, | ||
}; | ||
}); | ||
}) | ||
.then(callback); | ||
} | ||
afterEmitHandler(compilation, callback) { | ||
Object.keys(this.files) | ||
.filter(absoluteFrom => !includes(compilation.fileDependencies, absoluteFrom)) | ||
.forEach(absoluteFrom => compilation.fileDependencies.push(absoluteFrom)); | ||
callback(); | ||
} | ||
resolveWorkingDirectory() { | ||
if (this.globOptions.cwd === GLOB_CWD_AUTO) { | ||
this.globOptions.cwd = this.compiler.options.context; | ||
} | ||
this.context = this.globOptions.cwd || this.compiler.options.context; | ||
} | ||
processAsset(relativeFrom) { | ||
if (this.compilation.assets[relativeFrom]) { | ||
return Promise.resolve(); | ||
} | ||
const absoluteFrom = path.resolve(this.context, relativeFrom); | ||
return statAsync(absoluteFrom) | ||
.then(stat => this.buildFileObject(relativeFrom, absoluteFrom, stat)) | ||
.then(this.addAsset.bind(this)); | ||
} | ||
buildFileObject(relativeFrom, absoluteFrom, stat) { | ||
return readFileAsync(absoluteFrom) | ||
.then((content) => { | ||
const hash = utils.getHashDigest(content); | ||
const webpackTo = fixPath(interpolateName(this.output, relativeFrom, content)); | ||
return { relativeFrom, absoluteFrom, stat, content, hash, webpackTo }; | ||
}); | ||
} | ||
addAsset(file) { | ||
const asset = this.getAsset(file.absoluteFrom); | ||
if (asset && asset.hash === file.hash) { | ||
return null; | ||
} | ||
this.files[file.absoluteFrom] = file; | ||
return file; | ||
} | ||
getAsset(absoluteFrom) { | ||
return this.files[absoluteFrom]; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.