copy-webpack-plugin
Advanced tools
Comparing version 0.1.0 to 0.2.0
120
index.js
@@ -11,40 +11,85 @@ var _ = require('lodash'); | ||
var baseDir = compiler.options.context; | ||
var fileDependencies = []; | ||
var contextDependencies = []; | ||
var lastGlobalUpdate = 0; | ||
compiler.plugin('emit', function(compilation, cb) { | ||
Promise.each(patterns, function(pattern) { | ||
var relSrc = pattern.from; | ||
var absSrc = path.resolve(baseDir, relSrc); | ||
var relDest = pattern.to || ''; | ||
var relSrc = pattern.from; | ||
var absSrc = path.resolve(baseDir, relSrc); | ||
var relDest = pattern.to || ''; | ||
var forceWrite = !!pattern.force; | ||
return fs.statAsync(absSrc) | ||
.then(function(stat) { | ||
if (stat.isDirectory()) { | ||
return writeDirectoryToAssets(compilation, | ||
absSrc, | ||
relDest); | ||
return fs.statAsync(absSrc) | ||
.then(function(stat) { | ||
if (stat.isDirectory()) { | ||
contextDependencies.push(absSrc); | ||
return writeDirectoryToAssets({ | ||
compilation: compilation, | ||
absDirSrc: absSrc, | ||
relDirDest: relDest, | ||
forceWrite: forceWrite, | ||
lastGlobalUpdate: lastGlobalUpdate | ||
}); | ||
} else { | ||
fileDependencies.push(absSrc); | ||
if ((path.extname(relDest) === '' || // doesn't have an extension | ||
_.last(relDest) === '/' || // doesn't end in a slash | ||
pattern.toType === 'dir') && // is explicitly a dir | ||
pattern.toType !== 'file') { // is not explicitly a file | ||
relDest = path.join(relDest, path.basename(relSrc)); | ||
} else { | ||
if ((path.extname(relDest) === '' || // doesn't have an extension | ||
_.last(relDest) === '/' || // doesn't end in a slash | ||
pattern.toType === 'dir') && // is explicitly a dir | ||
pattern.toType !== 'file') { // is not explicitly a file | ||
relDest = path.join(relDest, path.basename(relSrc)); | ||
} else { | ||
relDest = relDest || path.basename(relSrc); | ||
} | ||
return writeFileToAssets(compilation, | ||
absSrc, | ||
relDest); | ||
relDest = relDest || path.basename(relSrc); | ||
} | ||
}); | ||
}) | ||
.catch(function(err) { | ||
compilation.errors.push(err); | ||
}) | ||
.finally(cb); | ||
return writeFileToAssets({ | ||
compilation: compilation, | ||
absFileSrc: absSrc, | ||
relFileDest: relDest, | ||
forceWrite: forceWrite, | ||
lastGlobalUpdate: lastGlobalUpdate | ||
}); | ||
} | ||
}); | ||
}) | ||
.then(function() { | ||
lastGlobalUpdate = _.now(); | ||
}) | ||
.catch(function(err) { | ||
compilation.errors.push(err); | ||
}) | ||
.finally(cb); | ||
}); | ||
compiler.plugin("after-emit", function(compilation, cb) { | ||
var trackedFiles = compilation.fileDependencies; | ||
_.each(fileDependencies, function(file) { | ||
if (!_.contains(trackedFiles, file)) { | ||
trackedFiles.push(file); | ||
} | ||
}); | ||
var trackedDirs = compilation.contextDependencies; | ||
_.each(contextDependencies, function(context) { | ||
if (!_.contains(trackedDirs, context)) { | ||
trackedDirs.push(context); | ||
} | ||
}); | ||
cb(); | ||
}); | ||
} | ||
function writeFileToAssets(compilation, absFileSrc, relFileDest) { | ||
function writeFileToAssets(opts) { | ||
var compilation = opts.compilation; | ||
var relFileDest = opts.relFileDest; | ||
var absFileSrc = opts.absFileSrc; | ||
var forceWrite = opts.forceWrite; | ||
var lastGlobalUpdate = opts.lastGlobalUpdate; | ||
if (compilation.assets[relFileDest] && !forceWrite) { | ||
return Promise.resolve(); | ||
} | ||
return fs.statAsync(absFileSrc) | ||
.then(function(stat) { | ||
.then(function(stat) { | ||
if (stat.mtime.getTime() > lastGlobalUpdate) { | ||
compilation.assets[relFileDest] = { | ||
@@ -58,6 +103,13 @@ size: function() { | ||
}; | ||
}); | ||
} | ||
}); | ||
} | ||
function writeDirectoryToAssets(compilation, absDirSrc, relDirDest) { | ||
function writeDirectoryToAssets(opts) { | ||
var compilation = opts.compilation; | ||
var absDirSrc = opts.absDirSrc; | ||
var relDirDest = opts.relDirDest; | ||
var forceWrite = opts.forceWrite; | ||
var lastGlobalUpdate = opts.lastGlobalUpdate; | ||
return dir.filesAsync(absDirSrc) | ||
@@ -73,3 +125,9 @@ .each(function(absFileSrc) { | ||
return writeFileToAssets(compilation, absFileSrc, relFileDest); | ||
return writeFileToAssets({ | ||
compilation: compilation, | ||
absFileSrc: absFileSrc, | ||
relFileDest: relFileDest, | ||
forceWrite: forceWrite, | ||
lastGlobalUpdate: lastGlobalUpdate | ||
}); | ||
}); | ||
@@ -76,0 +134,0 @@ } |
{ | ||
"name": "copy-webpack-plugin", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "Copy files and directories in webpack", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -69,2 +69,7 @@ ## Copy Webpack Plugin | ||
* Overwrite existing file. By default, assets that are staged by previous plugins aren't overwritten. Provide `force` to override this behavior. | ||
{ from: 'path/to/file.txt', force: true } | ||
{ from: 'path/to/directory', force: true } | ||
### Testing | ||
@@ -71,0 +76,0 @@ |
var expect = require('chai').expect; | ||
var CopyWebpackPlugin = require('../index'); | ||
var path = require('path'); | ||
var _ = require('lodash'); | ||
var Promise = require('bluebird'); | ||
var fs = Promise.promisifyAll(require('fs')); | ||
var HELPER_DIR = path.join(__dirname, 'helpers'); | ||
function MockCompiler() { | ||
this.options = { | ||
context: path.join(__dirname, 'helpers') | ||
context: HELPER_DIR | ||
}; | ||
@@ -12,3 +18,10 @@ } | ||
MockCompiler.prototype.plugin = function(type, fn) { | ||
this.emitFn = fn; | ||
switch(type) { | ||
case 'emit': | ||
this.emitFn = fn; | ||
break; | ||
case 'after-emit': | ||
this.afterEmitFn = fn; | ||
break; | ||
} | ||
}; | ||
@@ -19,29 +32,100 @@ | ||
// Ideally we pass in patterns and confirm the resulting assets | ||
function run(patterns, expectedAssetKeys, done) { | ||
var plugin = new CopyWebpackPlugin(patterns); | ||
function run(opts) { | ||
return new Promise(function(resolve, reject) { | ||
var plugin = new CopyWebpackPlugin(opts.patterns); | ||
// Get a mock compiler to pass to plugin.apply | ||
// Get a mock compiler to pass to plugin.apply | ||
var compiler = opts.compiler || new MockCompiler(); | ||
plugin.apply(compiler); | ||
// Call the registered function with a mock compilation and callback | ||
var compilation = _.extend({ | ||
errors: [], | ||
assets: {}, | ||
fileDependencies: [], | ||
contextDependencies: [] | ||
}, opts.compilation); | ||
// Execute the functions in series | ||
Promise.each([compiler.emitFn, compiler.afterEmitFn], function(fn) { | ||
return new Promise(function(res, rej) { | ||
try { | ||
fn(compilation, res); | ||
} catch(e) { | ||
rej(e); | ||
} | ||
}); | ||
}) | ||
.then(function() { | ||
if (compilation.errors.length > 0) { | ||
throw compilation.errors[0]; | ||
} | ||
resolve(compilation); | ||
}) | ||
.catch(reject); | ||
}); | ||
} | ||
function runEmit(opts) { | ||
return run(opts) | ||
.then(function(compilation) { | ||
if (opts.expectedAssetKeys && opts.expectedAssetKeys.length > 0) { | ||
expect(compilation.assets).to.have.all.keys(opts.expectedAssetKeys); | ||
} else { | ||
expect(compilation.assets).to.be.empty; | ||
} | ||
}); | ||
} | ||
function runForce(opts) { | ||
opts.compilation = { | ||
assets: {} | ||
}; | ||
opts.compilation.assets[opts.existingAsset] = { | ||
source: function() { | ||
return 'existing'; | ||
} | ||
}; | ||
return run(opts) | ||
.then(function(compilation) { | ||
var assetContent = compilation.assets[opts.existingAsset].source().toString(); | ||
expect(assetContent).to.equal(opts.expectedAssetContent); | ||
}); | ||
} | ||
function runChange(opts) { | ||
// Create two test files | ||
fs.writeFileSync(opts.newFileLoc1); | ||
fs.writeFileSync(opts.newFileLoc2); | ||
// Create a reference to the compiler | ||
var compiler = new MockCompiler(); | ||
plugin.apply(compiler); | ||
// Call the registered function with a mock compilation and callback | ||
var compilation = { | ||
errors: [], | ||
assets: {} | ||
assets: {}, | ||
fileDependencies: [], | ||
contextDependencies: [] | ||
}; | ||
return run({ | ||
compiler: compiler, | ||
patterns: opts.patterns | ||
}) | ||
// mtime is only measured in whole seconds | ||
.delay(1000) | ||
.then(function() { | ||
compiler.emitFn(compilation, function() { | ||
// After the callback has been called, check the compilation assets | ||
try { | ||
if (expectedAssetKeys.length > 0) { | ||
expect(compilation.assets).to.have.all.keys(expectedAssetKeys); | ||
} else { | ||
expect(compilation.assets).to.be.empty; | ||
} | ||
// Change a file | ||
fs.appendFileSync(opts.newFileLoc1, 'extra'); | ||
expect(compilation.errors).to.be.empty; | ||
done(); | ||
} catch(e) { | ||
done(e); | ||
} | ||
// Trigger another compile | ||
return new Promise(function(res, rej) { | ||
compiler.emitFn(compilation, res); | ||
}); | ||
}) | ||
.then(function() { | ||
return compilation; | ||
}) | ||
.finally(function() { | ||
fs.unlinkSync(opts.newFileLoc1); | ||
fs.unlinkSync(opts.newFileLoc2); | ||
}); | ||
@@ -52,5 +136,8 @@ } | ||
it('doesn\'t throw an error if no patterns are passed', function(done) { | ||
run(undefined, | ||
[], | ||
done); | ||
runEmit({ | ||
patterns: undefined, | ||
expectedAssetKeys: [] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
@@ -74,56 +161,128 @@ | ||
describe('with file in from', function() { | ||
it('can move a file to the root directory', function(done) { | ||
run([{ from: 'file.txt' }], | ||
['file.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'file.txt' }], | ||
expectedAssetKeys: ['file.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a file to a new directory without a forward slash', function(done) { | ||
run([{ from: 'file.txt', to: 'newdirectory' }], | ||
['newdirectory/file.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'file.txt', to: 'newdirectory' }], | ||
expectedAssetKeys: ['newdirectory/file.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a file to a new directory with a forward slash', function(done) { | ||
run([{ from: 'file.txt', to: 'newdirectory/' }], | ||
['newdirectory/file.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'file.txt', to: 'newdirectory/' }], | ||
expectedAssetKeys: ['newdirectory/file.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a file to a new directory with an extension', function(done) { | ||
run([{ from: 'file.txt', to: 'newdirectory.ext', toType: 'dir' }], | ||
['newdirectory.ext/file.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'file.txt', to: 'newdirectory.ext', toType: 'dir' }], | ||
expectedAssetKeys: ['newdirectory.ext/file.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a file to a new directory with an extension and forward slash', function(done) { | ||
run([{ from: 'file.txt', to: 'newdirectory.ext/' }], | ||
['newdirectory.ext/file.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'file.txt', to: 'newdirectory.ext/' }], | ||
expectedAssetKeys: ['newdirectory.ext/file.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a file to a new file with a different name', function(done) { | ||
run([{ from: 'file.txt', to: 'newname.txt' }], | ||
['newname.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'file.txt', to: 'newname.txt' }], | ||
expectedAssetKeys: ['newname.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a file to a new file with no extension', function(done) { | ||
run([{ from: 'file.txt', to: 'newname', toType: 'file' }], | ||
['newname'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'file.txt', to: 'newname', toType: 'file' }], | ||
expectedAssetKeys: ['newname'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a nested file to the root directory', function(done) { | ||
run([{ from: 'directory/directoryfile.txt' }], | ||
['directoryfile.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'directory/directoryfile.txt' }], | ||
expectedAssetKeys: ['directoryfile.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a nested file to a new directory', function(done) { | ||
run([{ from: 'directory/directoryfile.txt', to: 'newdirectory' }], | ||
['newdirectory/directoryfile.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'directory/directoryfile.txt', to: 'newdirectory' }], | ||
expectedAssetKeys: ['newdirectory/directoryfile.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('won\'t overwrite a file already in the compilation', function(done) { | ||
runForce({ | ||
patterns: [{ from: 'file.txt' }], | ||
existingAsset: 'file.txt', | ||
expectedAssetContent: 'existing' | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can force overwrite of a file already in the compilation', function(done) { | ||
runForce({ | ||
patterns: [{ from: 'file.txt', force: true }], | ||
existingAsset: 'file.txt', | ||
expectedAssetContent: 'new' | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('adds the file to the watch list', function(done) { | ||
run({ | ||
patterns: [{ from: 'file.txt' }] | ||
}) | ||
.then(function(compilation) { | ||
var absFrom = path.join(HELPER_DIR, 'file.txt'); | ||
expect(compilation.fileDependencies).to.have.members([absFrom]); | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('only include files that have changed', function(done) { | ||
runChange({ | ||
patterns: [{ from: 'tempfile1.txt' }, { from: 'tempfile2.txt' }], | ||
newFileLoc1: path.join(HELPER_DIR, 'tempfile1.txt'), | ||
newFileLoc2: path.join(HELPER_DIR, 'tempfile2.txt') | ||
}) | ||
.then(function(compilation) { | ||
expect(compilation.assets).to.have.key('tempfile1.txt'); | ||
expect(compilation.assets).not.to.have.key('tempfile2.txt'); | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
}); | ||
@@ -133,25 +292,83 @@ | ||
it('can move a directory\'s contents to the root directory', function(done) { | ||
run([{ from: 'directory' }], | ||
['directoryfile.txt', 'nested/nestedfile.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'directory' }], | ||
expectedAssetKeys: ['directoryfile.txt', 'nested/nestedfile.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a directory\'s contents to a new directory', function(done) { | ||
run([{ from: 'directory', to: 'newdirectory' }], | ||
['newdirectory/directoryfile.txt','newdirectory/nested/nestedfile.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'directory', to: 'newdirectory' }], | ||
expectedAssetKeys: ['newdirectory/directoryfile.txt','newdirectory/nested/nestedfile.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a nested directory\'s contents to the root directory', function(done) { | ||
run([{ from: 'directory/nested' }], | ||
['nestedfile.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'directory/nested' }], | ||
expectedAssetKeys: ['nestedfile.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can move a nested directory\'s contents to a new directory', function(done) { | ||
run([{ from: 'directory/nested', to: 'newdirectory' }], | ||
['newdirectory/nestedfile.txt'], | ||
done); | ||
runEmit({ | ||
patterns: [{ from: 'directory/nested', to: 'newdirectory' }], | ||
expectedAssetKeys: ['newdirectory/nestedfile.txt'] | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('won\'t overwrite a file already in the compilation', function(done) { | ||
runForce({ | ||
patterns: [{ from: 'directory' }], | ||
existingAsset: 'directoryfile.txt', | ||
expectedAssetContent: 'existing' | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('can force overwrite of a file already in the compilation', function(done) { | ||
runForce({ | ||
patterns: [{ from: 'directory', force: true }], | ||
existingAsset: 'directoryfile.txt', | ||
expectedAssetContent: 'new' | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('adds the directory to the watch list', function(done) { | ||
run({ | ||
patterns: [{ from: 'directory' }] | ||
}) | ||
.then(function(compilation) { | ||
var absFrom = path.join(HELPER_DIR, 'directory'); | ||
expect(compilation.contextDependencies).to.have.members([absFrom]); | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
it('only include files that have changed', function(done) { | ||
runChange({ | ||
patterns: [{ from: 'directory' }], | ||
newFileLoc1: path.join(HELPER_DIR, 'directory', 'tempfile1.txt'), | ||
newFileLoc2: path.join(HELPER_DIR, 'directory', 'tempfile2.txt') | ||
}) | ||
.then(function(compilation) { | ||
expect(compilation.assets).to.have.key('tempfile1.txt'); | ||
expect(compilation.assets).not.to.have.key('tempfile2.txt'); | ||
}) | ||
.then(done) | ||
.catch(done); | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
18046
10
457
80
2
1