diff --git a/scripts/rollup/packaging.js b/scripts/rollup/packaging.js old mode 100644 new mode 100755 index be641502a1545..7a40d07633c16 --- a/scripts/rollup/packaging.js +++ b/scripts/rollup/packaging.js @@ -1,6 +1,7 @@ 'use strict'; const basename = require('path').basename; +const dirname = require('path').dirname; const fs = require('fs'); const join = require('path').join; const resolve = require('path').resolve; @@ -157,7 +158,7 @@ function copyBundleIntoNodePackage(packageName, filename, bundleType) { function copyNodePackageTemplate(packageName) { const from = resolve(`./packages/${packageName}`); const to = resolve(`./build/packages/${packageName}`); - const npmFrom = resolve(`${from}/npm`); + const npmFrom = `${from}/npm`; if (!fs.existsSync(npmFrom)) { // The package is not meant for npm consumption. return Promise.resolve(); @@ -166,53 +167,81 @@ function copyNodePackageTemplate(packageName) { // We already created this package (e.g. due to another entry point). return Promise.resolve(); } - fs.mkdirSync(to); - const packageJson = resolve(`${from}/package.json`); - const whitelistedFiles = (fs.existsSync(packageJson) && - require(packageJson).files) || []; - const npmRootFiles = fs.readdirSync(npmFrom); // Get all entry points in the package root // Exceptions: *.fb.js - const packageRootEntries = glob.sync('!(*.fb).js', { + const packageEntries = glob.sync('!(*.fb).js', { cwd: from, }); - packageRootEntries.forEach(entry => { - // Terminate the build if any entry point in the package root - // does not have an equivalent in ./npm. - if (!npmRootFiles.includes(entry)) { + const npmFiles = fs.readdirSync(npmFrom); + packageEntries.forEach(entry => { + if (!npmFiles.includes(entry)) { + // Terminate the build if any entry point(Exception: *.fb.js) + // does not have an equivalent in ./npm. console.error( `Entry point ${entry} in package ${packageName} does not have an equivalent in ./npm` ); process.exit(1); } }); - if (whitelistedFiles.length > 0) { - // Looping through files and folders in ./npm root, - // if whitelisted in package.json, copy to build package root, - // return an array of the promises generated by async copy. - const promisesForForwardingModules = npmRootFiles.reduce( - (promises, file) => { - if ( - whitelistedFiles.includes(file) || - whitelistedFiles.includes(`${file}/`) - ) { - promises.push( - asyncCopyTo(resolve(`${npmFrom}/${file}`), `${to}/${file}`) - ); - } - return promises; - }, - [] + const packageJson = `${from}/package.json`; + const whitelist = fs.existsSync(packageJson) && require(packageJson).files; + let promisesForForwardingModules = []; + if (!whitelist) { + // Terminate the build if 'files' field is missing from package.json. + console.error( + `'files' field is missing from package.json in package ${packageName}` ); - return Promise.all([ - ...promisesForForwardingModules, - asyncCopyTo(resolve(`${from}/package.json`), `${to}/package.json`), - asyncCopyTo(resolve(`${from}/README.md`), `${to}/README.md`), - asyncCopyTo(resolve('./LICENSE'), `${to}/LICENSE`), - ]); + process.exit(1); } else { - return Promise.resolve(); + // 'files' field exists in package.json, + fs.mkdirSync(to); + let existingDirCache = {}; + // looping through entries(single file / directory / pattern) in `files` + const whitelistedFiles = whitelist.reduce((list, pattern) => { + const matchedFiles = glob.sync(pattern, { + cwd: npmFrom, + }); + // copy matching files/directories from './npm' to build package. + matchedFiles.forEach(file => { + if (fs.lstatSync(`${npmFrom}/${file}`).isFile()) { + // copy source is a file, create all nesting directories ahead + // to avoid 'No such file or directory' error + const fileDirnameSegments = dirname(file).split('/'); + fileDirnameSegments.reduce((prevPath, currentPath) => { + const fullPath = `${prevPath}/${currentPath}`; + if (!existingDirCache[fullPath] && !fs.existsSync(fullPath)) { + existingDirCache[fullPath] = true; + fs.mkdirSync(`${to}/${fullPath}`); + } + return fullPath; + }, ''); + } + promisesForForwardingModules.push( + asyncCopyTo(`${npmFrom}/${file}`, `${to}/${file}`) + ); + }); + // return an array of whitelisted files + // for entry point check next. + // All patterns have been parsed to file/directory + return list.concat(matchedFiles); + }, []); + // terminate the build if any entry point(Exception: *.fb.js) + // is not whitelisted in the 'files' field in package.json. + packageEntries.forEach(entry => { + if (!whitelistedFiles.includes(entry)) { + console.error( + `Entry point ${entry} in package ${packageName} is not listed in the 'files' field in package.json` + ); + process.exit(1); + } + }); } + return Promise.all([ + ...promisesForForwardingModules, + asyncCopyTo(resolve(`${from}/package.json`), `${to}/package.json`), + asyncCopyTo(resolve(`${from}/README.md`), `${to}/README.md`), + asyncCopyTo(resolve('./LICENSE'), `${to}/LICENSE`), + ]); } function createNodePackage(bundleType, packageName, filename) {