UNPKG

parcel-plugin-static-files-copy

Version:

ParcelJS plugin to copy static files from static dir to bundle directory.

210 lines (182 loc) 8.36 kB
'use strict'; const fs = require('fs'); const minimatch = require('minimatch'); const path = require('path'); const DEFAULT_CONFIG = { 'staticPath': ['static'], 'watcherGlob': null, 'excludeGlob': null, 'globOptions': {} }; module.exports = bundler => { bundler.on('bundled', async (bundle) => { // main asset and package dir, depending on version of parcel-bundler let mainAsset = bundler.mainAsset || // parcel < 1.8 bundler.mainBundle.entryAsset || // parcel >= 1.8 single entry point bundler.mainBundle.childBundles.values().next().value.entryAsset; // parcel >= 1.8 multiple entry points let pkg; if (typeof mainAsset.getPackage === 'function') { // parcel > 1.8 pkg = (await mainAsset.getPackage()); } else { // parcel <= 1.8 pkg = mainAsset.package; } // config let config = Object.assign({}, DEFAULT_CONFIG, pkg.staticFiles); if (pkg.staticPath) { // parcel-plugin-static-files-copy<1.2.5 config.staticPath = pkg.staticPath; } if (!Array.isArray(config.staticPath)) { // ensure array config.staticPath = [config.staticPath]; } if (config.excludeGlob && !Array.isArray(config.excludeGlob)) { config.excludeGlob = [config.excludeGlob]; } // poor-man's logger const logLevel = parseInt(bundler.options.logLevel); const pmLog = (level, ...msgs) => { if (logLevel >= level) { console.log(...msgs); } }; // static paths are usually just a string can be specified as // an object to make them conditional on the output directory // by specifying them in the form // {"outDirPattern":"dist1", "staticPath":"static1"}, // {"outDirPattern":"dist2", "staticPath":"static2"} config.staticPath = config.staticPath.map(path => { if (typeof path === 'object') { if (!path.staticPath) { console.error(`Error: parcel-plugin-static-files-copy: When staticPath is an object, expecting it to have at least the 'staticPath' key, but found: ${path}`); return null; } if (path.outDirPattern) { if (minimatch(bundler.options.outDir, path.outDirPattern, config.globOptions)) { pmLog(4, `outDir matches '${path.outDirPattern}' so copying static files from '${path.staticPath}'`); } else { pmLog(4, `outDir does not match '${path.outDirPattern}' so not copying static files from '${path.staticPath}'`); return null; } } return path; } else { return {staticPath: path}; } }).filter(path => path != null); // recursive copy function let numWatches = 0; /** * Recurse into directory and execute callback function for each file and folder. * * Based on https://github.com/douzi8/file-system/blob/master/file-system.js#L254 * * @param dirpath directory to start from * @param callback function to be run on every file/directory */ const recurseSync = (dirpath, callback) => { const rootpath = dirpath; function recurse(dirpath) { fs.readdirSync(dirpath).forEach(function (filename) { const filepath = path.join(dirpath, filename); const stats = fs.statSync(filepath); const relative = path.relative(rootpath, filepath); if (stats.isDirectory()) { callback(filepath, relative); recurse(filepath); } else { callback(filepath, relative, filename); } }); } recurse(dirpath); }; function copySingleFile(bundleDir, dest, filepath) { if (fs.existsSync(dest)) { const destStat = fs.statSync(dest); const srcStat = fs.statSync(filepath); if (destStat.mtime < srcStat.mtime) { // File was modified - let's copy it and inform about overwriting. pmLog(3, `Static file '${filepath}' already exists in '${bundleDir}'. Overwriting.`); fs.copyFileSync(filepath, dest); } } else { fs.copyFileSync(filepath, dest); } // watch for changes? if (config.watcherGlob && bundler.watcher && minimatch(filepath, config.watcherGlob, config.globOptions)) { numWatches++; bundler.watch(filepath, mainAsset); } } const shouldBeExcluded = (file, staticPath, excludeGlob) => { return !!excludeGlob.find(excludeGlob => minimatch(file, path.join(staticPath, excludeGlob), config.globOptions) ); }; const copyFile = (filepath, bundleDir, excludeGlob) => { if (shouldBeExcluded(filepath, path.dirname(filepath), excludeGlob)) { return; } const dest = path.join(bundleDir, path.basename(filepath)); copySingleFile(bundleDir, dest, filepath); }; const copyDir = (staticDir, bundleDir, excludeGlob) => { const copy = (filepath, relative, filename) => { if (shouldBeExcluded(filepath, staticDir, excludeGlob)) { return; } const dest = filepath.replace(staticDir, bundleDir); if (!filename) { if (!fs.existsSync(dest)) { fs.mkdirSync(dest, {recursive: true}); } } else { copySingleFile(bundleDir, dest, filepath); } }; recurseSync(staticDir, copy); }; const outDir = bundler.options.outDir; const currentEnv = process.env.NODE_ENV; function processStaticFiles(singleBundle) { for (let dir of config.staticPath) { if (dir.env && dir.env !== currentEnv) { continue; } const copyTo = dir.staticOutDir && dir.staticOutDir.startsWith('/') ? path.join(outDir, dir.staticOutDir) : path.join(path.dirname(singleBundle.name), dir.staticOutDir ? dir.staticOutDir : ''); // merge global exclude glob with static path exclude glob const excludeGlob = (config.excludeGlob || []).concat((dir.excludeGlob || [])); if (!fs.existsSync(copyTo)) { fs.mkdirSync(copyTo, {recursive: true}); } var paths = dir.staticPath; if (!Array.isArray(paths)) { paths = [paths]; } for (let singlePath of paths) { let staticPath = path.join(pkg.pkgdir, singlePath); if (!fs.existsSync(staticPath)) { pmLog(2, `Static path (file or directory) '${staticPath}' does not exist. Skipping.`); continue; } if (fs.statSync(staticPath).isDirectory()) { copyDir(staticPath, copyTo, excludeGlob); } else { copyFile(staticPath, copyTo, excludeGlob); } } } } if (!bundle.name) { // multiple entry points for (let singleBundle of bundler.mainBundle.childBundles.values()) { processStaticFiles(singleBundle); } } else { processStaticFiles(bundle); } if (config.watcherGlob && bundler.watcher) { pmLog(3, `Watching for changes in ${numWatches} static files.`); } }); };