From f72043a36903a18703168a1ebfe271c150f27330 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 6 Dec 2017 20:11:32 +0000 Subject: [PATCH] Refactor the build scripts (#11787) * Rewrite the build scripts * Don't crash when doing FB-only builds * Group sync imports under Sync.* * Don't print known errors twice * Use an exclamation that aligns vertically --- package.json | 2 +- scripts/rollup/build.js | 372 +++++++++++++++++------------------- scripts/rollup/packaging.js | 303 ++++++++++++----------------- scripts/rollup/utils.js | 99 ++++++---- yarn.lock | 4 - 5 files changed, 363 insertions(+), 417 deletions(-) diff --git a/package.json b/package.json index af185c2b533d5..9b5e0cbad9a72 100644 --- a/package.json +++ b/package.json @@ -73,6 +73,7 @@ "jest-runtime": "^21.3.0-beta.4", "merge-stream": "^1.0.0", "minimist": "^1.2.0", + "mkdirp": "^0.5.1", "ncp": "^2.0.0", "object-assign": "^4.1.1", "platform": "^1.1.0", @@ -93,7 +94,6 @@ "through2": "^2.0.0", "tmp": "~0.0.28", "typescript": "~1.8.10", - "uuid": "^3.1.0", "yargs": "^6.3.0" }, "devEngines": { diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index cef05623fda9e..bc58d8678a46d 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -1,6 +1,6 @@ 'use strict'; -const rollup = require('rollup').rollup; +const {rollup} = require('rollup'); const babel = require('rollup-plugin-babel'); const closure = require('rollup-plugin-closure-compiler-js'); const commonjs = require('rollup-plugin-commonjs'); @@ -10,33 +10,40 @@ const stripBanner = require('rollup-plugin-strip-banner'); const chalk = require('chalk'); const path = require('path'); const resolve = require('rollup-plugin-node-resolve'); -const os = require('os'); const fs = require('fs'); -const rimraf = require('rimraf'); const argv = require('minimist')(process.argv.slice(2)); const Modules = require('./modules'); const Bundles = require('./bundles'); +const Stats = require('./stats'); +const Sync = require('./sync'); const sizes = require('./plugins/sizes-plugin'); const useForks = require('./plugins/use-forks-plugin'); -const Stats = require('./stats'); const extractErrorCodes = require('../error-codes/extract-errors'); -const syncReactDom = require('./sync').syncReactDom; -const syncReactNative = require('./sync').syncReactNative; -const syncReactNativeRT = require('./sync').syncReactNativeRT; -const syncReactNativeCS = require('./sync').syncReactNativeCS; const Packaging = require('./packaging'); +const {asyncCopyTo, asyncRimRaf} = require('./utils'); const codeFrame = require('babel-code-frame'); const Wrappers = require('./wrappers'); -const uuidv1 = require('uuid/v1'); -const UMD_DEV = Bundles.bundleTypes.UMD_DEV; -const UMD_PROD = Bundles.bundleTypes.UMD_PROD; -const NODE_DEV = Bundles.bundleTypes.NODE_DEV; -const NODE_PROD = Bundles.bundleTypes.NODE_PROD; -const FB_DEV = Bundles.bundleTypes.FB_DEV; -const FB_PROD = Bundles.bundleTypes.FB_PROD; -const RN_DEV = Bundles.bundleTypes.RN_DEV; -const RN_PROD = Bundles.bundleTypes.RN_PROD; +// Errors in promises should be fatal. +let loggedErrors = new Set(); +process.on('unhandledRejection', err => { + if (loggedErrors.has(err)) { + // No need to print it twice. + process.exit(1); + } + throw err; +}); + +const { + UMD_DEV, + UMD_PROD, + NODE_DEV, + NODE_PROD, + FB_DEV, + FB_PROD, + RN_DEV, + RN_PROD, +} = Bundles.bundleTypes; const requestedBundleTypes = (argv.type || '') .split(',') @@ -44,16 +51,12 @@ const requestedBundleTypes = (argv.type || '') const requestedBundleNames = (argv._[0] || '') .split(',') .map(type => type.toLowerCase()); -const syncFbsource = argv['sync-fbsource']; -const syncWww = argv['sync-www']; +const syncFBSourcePath = argv['sync-fbsource']; +const syncWWWPath = argv['sync-www']; const shouldExtractErrors = argv['extract-errors']; const errorCodeOpts = { errorMapFilePath: 'scripts/error-codes/codes.json', }; -const npmPackagesTmpDir = path.join( - os.tmpdir(), - `react-npm-packages-${uuidv1()}` -); const closureOptions = { compilationLevel: 'SIMPLE', @@ -105,54 +108,11 @@ function getBabelConfig(updateBabelOptions, bundleType, filename) { } } -function handleRollupWarnings(warning) { - if (warning.code === 'UNRESOLVED_IMPORT') { - console.error(warning.message); - process.exit(1); - } - if (warning.code === 'UNUSED_EXTERNAL_IMPORT') { - const match = warning.message.match(/external module '([^']+)'/); - if (!match || typeof match[1] !== 'string') { - throw new Error( - 'Could not parse a Rollup warning. ' + 'Fix this method.' - ); - } - const importSideEffects = Modules.getImportSideEffects(); - const externalModule = match[1]; - if (typeof importSideEffects[externalModule] !== 'boolean') { - throw new Error( - 'An external module "' + - externalModule + - '" is used in a DEV-only code path ' + - 'but we do not know if it is safe to omit an unused require() to it in production. ' + - 'Please add it to the `importSideEffects` list in `scripts/rollup/modules.js`.' - ); - } - // Don't warn. We will remove side effectless require() in a later pass. - return; - } - console.warn(warning.message || warning); -} - -function getRollupOutputOptions( - filename, - format, - bundleType, - globals, - globalName, - moduleType -) { +function getRollupOutputOptions(outputPath, format, globals, globalName) { return Object.assign( {}, { - destDir: 'build/', - file: - 'build/' + - Packaging.getOutputPathRelativeToBuildFolder( - bundleType, - filename, - globalName - ), + file: outputPath, format, globals, interop: false, @@ -312,17 +272,17 @@ function getPlugins( ].filter(Boolean); } -async function createBundle(bundle, bundleType) { +function shouldSkipBundle(bundle, bundleType) { const shouldSkipBundleType = bundle.bundleTypes.indexOf(bundleType) === -1; if (shouldSkipBundleType) { - return; + return true; } if (requestedBundleTypes.length > 0) { const isAskingForDifferentType = requestedBundleTypes.every( requestedType => bundleType.indexOf(requestedType) === -1 ); if (isAskingForDifferentType) { - return; + return true; } } if (requestedBundleNames.length > 0) { @@ -330,9 +290,16 @@ async function createBundle(bundle, bundleType) { requestedName => bundle.label.indexOf(requestedName) === -1 ); if (isAskingForDifferentNames) { - return; + return true; } } + return false; +} + +async function createBundle(bundle, bundleType) { + if (shouldSkipBundle(bundle, bundleType)) { + return; + } const filename = getFilename(bundle.entry, bundle.global, bundleType); const logKey = @@ -365,139 +332,154 @@ async function createBundle(bundle, bundleType) { module => !importSideEffects[module] ); + const rollupConfig = { + input: resolvedEntry, + pureExternalModules, + external(id) { + const containsThisModule = pkg => id === pkg || id.startsWith(pkg + '/'); + const isProvidedByDependency = externals.some(containsThisModule); + if (!shouldBundleDependencies && isProvidedByDependency) { + return true; + } + return !!peerGlobals[id]; + }, + onwarn: handleRollupWarning, + plugins: getPlugins( + bundle.entry, + externals, + bundle.babel, + filename, + bundleType, + bundle.global, + bundle.moduleType, + bundle.modulesToStub + ), + // We can't use getters in www. + legacy: bundleType === FB_DEV || bundleType === FB_PROD, + }; + const [mainOutputPath, ...otherOutputPaths] = Packaging.getBundleOutputPaths( + bundleType, + filename, + packageName + ); + const rollupOutputOptions = getRollupOutputOptions( + mainOutputPath, + format, + peerGlobals, + bundle.global + ); + console.log(`${chalk.bgYellow.black(' BUILDING ')} ${logKey}`); try { - const result = await rollup({ - input: resolvedEntry, - pureExternalModules, - external(id) { - const containsThisModule = pkg => - id === pkg || id.startsWith(pkg + '/'); - const isProvidedByDependency = externals.some(containsThisModule); - if (!shouldBundleDependencies && isProvidedByDependency) { - return true; - } - return !!peerGlobals[id]; - }, - onwarn: handleRollupWarnings, - plugins: getPlugins( - bundle.entry, - externals, - bundle.babel, - filename, - bundleType, - bundle.global, - bundle.moduleType, - bundle.modulesToStub - ), - // We can't use getters in www. - legacy: bundleType === FB_DEV || bundleType === FB_PROD, - }); - await result.write( - getRollupOutputOptions( - filename, - format, - bundleType, - peerGlobals, - bundle.global, - bundle.moduleType - ) - ); - await Packaging.createNodePackage( - bundleType, - packageName, - filename, - npmPackagesTmpDir - ); - console.log(`${chalk.bgGreen.black(' COMPLETE ')} ${logKey}\n`); + const result = await rollup(rollupConfig); + await result.write(rollupOutputOptions); } catch (error) { - if (error.code) { - console.error( - `\x1b[31m-- ${error.code}${error.plugin ? ` (${error.plugin})` : ''} --` + console.log(`${chalk.bgRed.black(' OH NOES! ')} ${logKey}\n`); + handleRollupError(error); + throw error; + } + for (let i = 0; i < otherOutputPaths.length; i++) { + await asyncCopyTo(mainOutputPath, otherOutputPaths[i]); + } + console.log(`${chalk.bgGreen.black(' COMPLETE ')} ${logKey}\n`); +} + +function handleRollupWarning(warning) { + if (warning.code === 'UNRESOLVED_IMPORT') { + console.error(warning.message); + process.exit(1); + } + if (warning.code === 'UNUSED_EXTERNAL_IMPORT') { + const match = warning.message.match(/external module '([^']+)'/); + if (!match || typeof match[1] !== 'string') { + throw new Error( + 'Could not parse a Rollup warning. ' + 'Fix this method.' ); - console.error(error.message); - const {file, line, column} = error.loc; - if (file) { - // This looks like an error from Rollup, e.g. missing export. - // We'll use the accurate line numbers provided by Rollup but - // use Babel code frame because it looks nicer. - const rawLines = fs.readFileSync(file, 'utf-8'); - // column + 1 is required due to rollup counting column start position from 0 - // whereas babel-code-frame counts from 1 - const frame = codeFrame(rawLines, line, column + 1, { - highlightCode: true, - }); - console.error(frame); - } else { - // This looks like an error from a plugin (e.g. Babel). - // In this case we'll resort to displaying the provided code frame - // because we can't be sure the reported location is accurate. - console.error(error.codeFrame); - } - } else { - console.error(error); } - process.exit(1); + const importSideEffects = Modules.getImportSideEffects(); + const externalModule = match[1]; + if (typeof importSideEffects[externalModule] !== 'boolean') { + throw new Error( + 'An external module "' + + externalModule + + '" is used in a DEV-only code path ' + + 'but we do not know if it is safe to omit an unused require() to it in production. ' + + 'Please add it to the `importSideEffects` list in `scripts/rollup/modules.js`.' + ); + } + // Don't warn. We will remove side effectless require() in a later pass. + return; } + console.warn(warning.message || warning); } -// clear the build directory -rimraf('build', async () => { - try { - // create a new build directory - fs.mkdirSync('build'); - // create the temp directory for local npm packing and unpacking - // in operating system's default temporary directory - fs.mkdirSync(npmPackagesTmpDir); - // create the packages folder for NODE+UMD bundles - fs.mkdirSync(path.join('build', 'packages')); - // create the dist folder for UMD bundles - fs.mkdirSync(path.join('build', 'dist')); +function handleRollupError(error) { + loggedErrors.add(error); + if (!error.code) { + console.error(error); + return; + } + console.error( + `\x1b[31m-- ${error.code}${error.plugin ? ` (${error.plugin})` : ''} --` + ); + console.error(error.message); + const {file, line, column} = error.loc; + if (file) { + // This looks like an error from Rollup, e.g. missing export. + // We'll use the accurate line numbers provided by Rollup but + // use Babel code frame because it looks nicer. + const rawLines = fs.readFileSync(file, 'utf-8'); + // column + 1 is required due to rollup counting column start position from 0 + // whereas babel-code-frame counts from 1 + const frame = codeFrame(rawLines, line, column + 1, { + highlightCode: true, + }); + console.error(frame); + } else { + // This looks like an error from a plugin (e.g. Babel). + // In this case we'll resort to displaying the provided code frame + // because we can't be sure the reported location is accurate. + console.error(error.codeFrame); + } +} - await Packaging.createFacebookWWWBuild(); - await Packaging.createReactNativeBuild(); - await Packaging.createReactNativeRTBuild(); - await Packaging.createReactNativeCSBuild(); +async function buildEverything() { + await asyncRimRaf('build'); - // Run them serially for better console output - // and to avoid any potential race conditions. - for (const bundle of Bundles.bundles) { - await createBundle(bundle, UMD_DEV); - await createBundle(bundle, UMD_PROD); - await createBundle(bundle, NODE_DEV); - await createBundle(bundle, NODE_PROD); - await createBundle(bundle, FB_DEV); - await createBundle(bundle, FB_PROD); - await createBundle(bundle, RN_DEV); - await createBundle(bundle, RN_PROD); - } + // Run them serially for better console output + // and to avoid any potential race conditions. + for (const bundle of Bundles.bundles) { + await createBundle(bundle, UMD_DEV); + await createBundle(bundle, UMD_PROD); + await createBundle(bundle, NODE_DEV); + await createBundle(bundle, NODE_PROD); + await createBundle(bundle, FB_DEV); + await createBundle(bundle, FB_PROD); + await createBundle(bundle, RN_DEV); + await createBundle(bundle, RN_PROD); + } - if (syncFbsource) { - await syncReactNative(path.join('build', 'react-native'), syncFbsource); - await syncReactNativeRT(path.join('build', 'react-rt'), syncFbsource); - await syncReactNativeCS(path.join('build', 'react-cs'), syncFbsource); - } else if (syncWww) { - await syncReactDom(path.join('build', 'facebook-www'), syncWww); - } + await Packaging.copyAllShims(); + await Packaging.prepareNpmPackages(); - console.log(Stats.printResults()); - // save the results for next run - Stats.saveResults(); - if (shouldExtractErrors) { - console.warn( - '\nWarning: this build was created with --extract-errors enabled.\n' + - 'this will result in extremely slow builds and should only be\n' + - 'used when the error map needs to be rebuilt.\n' - ); - } - rimraf(npmPackagesTmpDir, err => { - if (err) { - console.error(err); - process.exit(1); - } - }); - } catch (err) { - console.error(err); - process.exit(1); + if (syncFBSourcePath) { + await Sync.syncReactNative('build/react-native', syncFBSourcePath); + await Sync.syncReactNativeRT('build/react-rt', syncFBSourcePath); + await Sync.syncReactNativeCS('build/react-cs', syncFBSourcePath); + } else if (syncWWWPath) { + await Sync.syncReactDom('build/facebook-www', syncWWWPath); } -}); + + console.log(Stats.printResults()); + Stats.saveResults(); + + if (shouldExtractErrors) { + console.warn( + '\nWarning: this build was created with --extract-errors enabled.\n' + + 'this will result in extremely slow builds and should only be\n' + + 'used when the error map needs to be rebuilt.\n' + ); + } +} + +buildEverything(); diff --git a/scripts/rollup/packaging.js b/scripts/rollup/packaging.js index 0d7c05572d74b..2705e5c01429d 100644 --- a/scripts/rollup/packaging.js +++ b/scripts/rollup/packaging.js @@ -1,40 +1,24 @@ 'use strict'; -const basename = require('path').basename; -const fs = require('fs'); -const join = require('path').join; -const resolve = require('path').resolve; +const {existsSync, readdirSync, unlinkSync} = require('fs'); const Bundles = require('./bundles'); -const asyncCopyTo = require('./utils').asyncCopyTo; -const asyncExecuteCommand = require('./utils').asyncExecuteCommand; -const asyncExtractTar = require('./utils').asyncExtractTar; - -const UMD_DEV = Bundles.bundleTypes.UMD_DEV; -const UMD_PROD = Bundles.bundleTypes.UMD_PROD; -const NODE_DEV = Bundles.bundleTypes.NODE_DEV; -const NODE_PROD = Bundles.bundleTypes.NODE_PROD; -const FB_DEV = Bundles.bundleTypes.FB_DEV; -const FB_PROD = Bundles.bundleTypes.FB_PROD; -const RN_DEV = Bundles.bundleTypes.RN_DEV; -const RN_PROD = Bundles.bundleTypes.RN_PROD; - -const facebookWWW = 'facebook-www'; - -// these files need to be copied to the react-native build -const reactNativeSrcDependencies = [ - 'packages/shared/ReactTypes.js', - 'packages/react-native-renderer/src/ReactNativeTypes.js', -]; - -// these files need to be copied to the react-rt build -const reactNativeRTSrcDependencies = [ - 'packages/react-rt-renderer/src/ReactNativeRTTypes.js', -]; - -// these files need to be copied to the react-cs build -const reactNativeCSSrcDependencies = [ - 'packages/react-cs-renderer/src/ReactNativeCSTypes.js', -]; +const { + asyncCopyTo, + asyncExecuteCommand, + asyncExtractTar, + asyncRimRaf, +} = require('./utils'); + +const { + UMD_DEV, + UMD_PROD, + NODE_DEV, + NODE_PROD, + FB_DEV, + FB_PROD, + RN_DEV, + RN_PROD, +} = Bundles.bundleTypes; function getPackageName(name) { if (name.indexOf('/') !== -1) { @@ -43,172 +27,127 @@ function getPackageName(name) { return name; } -async function createReactNativeBuild() { - fs.mkdirSync(join('build', 'react-native')); - fs.mkdirSync(join('build', 'react-native', 'shims')); - const from = join('scripts', 'rollup', 'shims', 'react-native'); - const to = join('build', 'react-native', 'shims'); - await asyncCopyTo(from, to); - await Promise.all( - reactNativeSrcDependencies.map(srcDependency => - asyncCopyTo(resolve(srcDependency), join(to, basename(srcDependency))) - ) - ); -} - -async function createReactNativeRTBuild() { - fs.mkdirSync(join('build', 'react-rt')); - fs.mkdirSync(join('build', 'react-rt', 'shims')); - const to = join('build', 'react-rt', 'shims'); - await Promise.all( - reactNativeRTSrcDependencies.map(srcDependency => - asyncCopyTo(resolve(srcDependency), join(to, basename(srcDependency))) - ) - ); +function getBundleOutputPaths(bundleType, filename, packageName) { + switch (bundleType) { + case NODE_DEV: + case NODE_PROD: + return [`build/packages/${packageName}/cjs/${filename}`]; + case UMD_DEV: + case UMD_PROD: + return [ + `build/packages/${packageName}/umd/${filename}`, + `build/dist/${filename}`, + ]; + case FB_DEV: + case FB_PROD: + return [`build/facebook-www/${filename}`]; + case RN_DEV: + case RN_PROD: + switch (packageName) { + case 'react-rt-renderer': + return [`build/react-rt/${filename}`]; + case 'react-cs-renderer': + return [`build/react-cs/${filename}`]; + case 'react-native-renderer': + return [`build/react-native/${filename}`]; + default: + throw new Error('Unknown RN package.'); + } + default: + throw new Error('Unknown bundle type.'); + } } -async function createReactNativeCSBuild() { - fs.mkdirSync(join('build', 'react-cs')); - fs.mkdirSync(join('build', 'react-cs', 'shims')); - const to = join('build', 'react-cs', 'shims'); - await Promise.all( - reactNativeCSSrcDependencies.map(srcDependency => - asyncCopyTo(resolve(srcDependency), join(to, basename(srcDependency))) - ) +async function copyWWWShims() { + await asyncCopyTo( + `${__dirname}/shims/facebook-www`, + 'build/facebook-www/shims' ); } -async function createFacebookWWWBuild() { - fs.mkdirSync(join('build', facebookWWW)); - fs.mkdirSync(join('build', facebookWWW, 'shims')); - const from = join('scripts', 'rollup', 'shims', facebookWWW); - const to = join('build', facebookWWW, 'shims'); - await asyncCopyTo(from, to); +async function copyRNShims() { + await Promise.all([ + // React Native + asyncCopyTo(`${__dirname}/shims/react-native`, 'build/react-native/shims'), + asyncCopyTo( + require.resolve('shared/ReactTypes.js'), + 'build/react-native/shims/ReactTypes.js' + ), + asyncCopyTo( + require.resolve('react-native-renderer/src/ReactNativeTypes.js'), + 'build/react-native/shims/ReactNativeTypes.js' + ), + // React Native CS + asyncCopyTo( + require.resolve('react-cs-renderer/src/ReactNativeCSTypes.js'), + 'build/react-cs/shims/ReactNativeCSTypes.js' + ), + // React Native RT + asyncCopyTo( + require.resolve('react-rt-renderer/src/ReactNativeRTTypes.js'), + 'build/react-rt/shims/ReactNativeRTTypes.js' + ), + ]); } -async function copyBundleIntoNodePackage( - packageName, - filename, - bundleType, - npmPackagesTmpDir -) { - const packageDirectory = resolve(`${npmPackagesTmpDir}/${packageName}`); - if (!fs.existsSync(packageDirectory)) { - return; - } - let from = resolve(`./build/${filename}`); - let to = `${packageDirectory}/${filename}`; - // for UMD bundles we have to move the files into a umd directory - // within the package directory. we also need to set the from - // to be the root build from directory - if (bundleType === UMD_DEV || bundleType === UMD_PROD) { - const distDirectory = `${packageDirectory}/umd`; - // create a dist directory if not created - if (!fs.existsSync(distDirectory)) { - fs.mkdirSync(distDirectory); - } - from = resolve(`./build/dist/${filename}`); - to = `${packageDirectory}/umd/${filename}`; - } - // for NODE bundles we have to move the files into a cjs directory - // within the package directory. we also need to set the from - // to be the root build from directory - if (bundleType === NODE_DEV || bundleType === NODE_PROD) { - const distDirectory = `${packageDirectory}/cjs`; - // create a dist directory if not created - if (!fs.existsSync(distDirectory)) { - fs.mkdirSync(distDirectory); - } - to = `${packageDirectory}/cjs/${filename}`; - } - await asyncCopyTo(from, to); - // delete the old file if this is a not a UMD bundle - if (bundleType !== UMD_DEV && bundleType !== UMD_PROD) { - fs.unlinkSync(from); - } +async function copyAllShims() { + await Promise.all([copyWWWShims(), copyRNShims()]); } -async function copyNodePackageTemplate(packageName, npmPackagesTmpDir) { - const from = resolve(`./packages/${packageName}`); - const to = resolve(`${npmPackagesTmpDir}/${packageName}`); - const npmFrom = resolve(`${from}/npm`); - if (!fs.existsSync(npmFrom)) { - // The package is not meant for npm consumption. - return; - } - if (fs.existsSync(to)) { - // We already created this package (e.g. due to another entry point). - return; - } - await asyncCopyTo(npmFrom, to); - await asyncCopyTo(resolve(`${from}/package.json`), `${to}/package.json`); - await asyncCopyTo(resolve(`${from}/README.md`), `${to}/README.md`); - await asyncCopyTo(resolve('./LICENSE'), `${to}/LICENSE`); +function getTarOptions(tgzName, packageName) { + // Files inside the `npm pack`ed archive start + // with "package/" in their paths. We'll undo + // this during extraction. + const CONTENTS_FOLDER = 'package'; + return { + src: tgzName, + dest: `build/packages/${packageName}`, + tar: { + entries: [CONTENTS_FOLDER], + map(header) { + if (header.name.indexOf(CONTENTS_FOLDER + '/') === 0) { + header.name = header.name.substring(CONTENTS_FOLDER.length + 1); + } + }, + }, + }; } -async function packForNpmAndUnpack(packageName, npmPackagesTmpDir) { - const packageTmpDir = resolve(`${npmPackagesTmpDir}/${packageName}`); - const extractTmpDir = resolve(`${packageTmpDir}/extract`); - const build = resolve(`./build/packages/${packageName}`); - const npmFrom = resolve(`./packages/${packageName}/npm`); - if (!fs.existsSync(npmFrom)) { - return; - } - let tgzName = await asyncExecuteCommand(`cd ${packageTmpDir} && npm pack`); - // In npm packages, files are grouped into a root directory(named 'package'). - // We only copy the packed files instead of extract the root 'package' directly to build directory - await asyncExtractTar({ - src: `${packageTmpDir}/${tgzName.trim()}`, - dest: extractTmpDir, - }); - await asyncCopyTo(`${extractTmpDir}/package`, build); +async function prepareNpmPackage(name) { + await Promise.all([ + asyncCopyTo('LICENSE', `build/packages/${name}/LICENSE`), + asyncCopyTo( + `packages/${name}/package.json`, + `build/packages/${name}/package.json` + ), + asyncCopyTo( + `packages/${name}/README.md`, + `build/packages/${name}/README.md` + ), + asyncCopyTo(`packages/${name}/npm`, `build/packages/${name}`), + ]); + const tgzName = (await asyncExecuteCommand( + `npm pack build/packages/${name}` + )).trim(); + await asyncRimRaf(`build/packages/${name}`); + await asyncExtractTar(getTarOptions(tgzName, name)); + unlinkSync(tgzName); } -async function createNodePackage( - bundleType, - packageName, - filename, - npmPackagesTmpDir -) { - // the only case where we don't want to copy the package is for FB bundles - if (bundleType === FB_DEV || bundleType === FB_PROD) { +async function prepareNpmPackages() { + if (!existsSync('build/packages')) { + // We didn't build any npm packages. return; } - await copyNodePackageTemplate(packageName, npmPackagesTmpDir); - await copyBundleIntoNodePackage( - packageName, - filename, - bundleType, - npmPackagesTmpDir + const builtPackageFolders = readdirSync('build/packages').filter( + dir => dir.charAt(0) !== '.' ); - // Packing packages locally, simulate npm publish, - // Then unpacking generated packages to build directory - await packForNpmAndUnpack(packageName, npmPackagesTmpDir); -} - -function getOutputPathRelativeToBuildFolder(bundleType, filename, hasteName) { - if (bundleType === FB_DEV || bundleType === FB_PROD) { - return `${facebookWWW}/${filename}`; - } else if (bundleType === UMD_DEV || bundleType === UMD_PROD) { - return `dist/${filename}`; - } else if (bundleType === RN_DEV || bundleType === RN_PROD) { - if (hasteName === 'ReactRTRenderer') { - return `react-rt/${filename}`; - } else if (hasteName === 'ReactCSRenderer') { - return `react-cs/${filename}`; - } else { - return `react-native/${filename}`; - } - } - return filename; + await Promise.all(builtPackageFolders.map(prepareNpmPackage)); } module.exports = { - getOutputPathRelativeToBuildFolder, - createNodePackage, + copyAllShims, getPackageName, - createFacebookWWWBuild, - createReactNativeBuild, - createReactNativeRTBuild, - createReactNativeCSBuild, + getBundleOutputPaths, + prepareNpmPackages, }; diff --git a/scripts/rollup/utils.js b/scripts/rollup/utils.js index fc7fc9dc1490d..24eeed192feb3 100644 --- a/scripts/rollup/utils.js +++ b/scripts/rollup/utils.js @@ -1,60 +1,89 @@ 'use strict'; const ncp = require('ncp').ncp; -const join = require('path').join; -const resolve = require('path').resolve; +const path = require('path'); +const mkdirp = require('mkdirp'); +const rimraf = require('rimraf'); const exec = require('child_process').exec; const targz = require('targz'); function asyncCopyTo(from, to) { - return new Promise(_resolve => { - ncp(from, to, error => { - if (error) { - console.error(error); - process.exit(1); - } - _resolve(); - }); - }); -} - -function resolvePath(path) { - if (path[0] === '~') { - return join(process.env.HOME, path.slice(1)); - } else { - return resolve(path); - } + return asyncMkDirP(path.dirname(to)).then( + () => + new Promise((resolve, reject) => { + ncp(from, to, error => { + if (error) { + // Wrap to have a useful stack trace. + reject(new Error(error)); + return; + } + resolve(); + }); + }) + ); } function asyncExecuteCommand(command) { - return new Promise(_resolve => + return new Promise((resolve, reject) => exec(command, (error, stdout) => { - if (!error) { - _resolve(stdout); - } else { - console.error(error); - process.exit(1); + if (error) { + reject(error); + return; } + resolve(stdout); }) ); } function asyncExtractTar(options) { - return new Promise(_resolve => + return new Promise((resolve, reject) => targz.decompress(options, error => { - if (!error) { - _resolve(); - } else { - console.error(error); - process.exit(1); + if (error) { + reject(error); + return; } + resolve(); }) ); } +function asyncMkDirP(filepath) { + return new Promise((resolve, reject) => + mkdirp(filepath, error => { + if (error) { + reject(error); + return; + } + resolve(); + }) + ); +} + +function asyncRimRaf(filepath) { + return new Promise((resolve, reject) => + rimraf(filepath, error => { + if (error) { + reject(error); + return; + } + resolve(); + }) + ); +} + +function resolvePath(filepath) { + if (filepath[0] === '~') { + return path.join(process.env.HOME, filepath.slice(1)); + } else { + return path.resolve(filepath); + } +} + module.exports = { - asyncCopyTo: asyncCopyTo, - resolvePath: resolvePath, - asyncExecuteCommand: asyncExecuteCommand, - asyncExtractTar: asyncExtractTar, + asyncCopyTo, + resolvePath, + asyncExecuteCommand, + asyncExtractTar, + asyncMkDirP, + asyncRimRaf, }; diff --git a/yarn.lock b/yarn.lock index 13d2f9180bd98..df11eb2e0b92c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4788,10 +4788,6 @@ uuid@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" -uuid@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04" - v8flags@^2.0.10: version "2.1.1" resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4"