diff --git a/cli/run/index.ts b/cli/run/index.ts index 6ff743c79f8..ed4478fa6d4 100644 --- a/cli/run/index.ts +++ b/cli/run/index.ts @@ -1,4 +1,5 @@ import { MergedRollupOptions } from '../../src/rollup/types'; +import { isWatchEnabled } from '../../src/utils/options/mergeOptions'; import { getAliasName } from '../../src/utils/relativeId'; import { loadFsEvents } from '../../src/watch/fsevents-importer'; import { handleError } from '../logging'; @@ -56,7 +57,7 @@ export default async function runRollup(command: Record): Promise - // type: 'chunk', // signifies that this is a chunk - // } - console.log('Chunk', chunkOrAsset.modules); - } - } +// you can create multiple outputs from the same input to generate e.g. +// different formats like CommonJS and ESM +const outputOptionsList = [{...}, {...}]; - // or write the bundle to disk - await bundle.write(outputOptions); +build(); - // closes the bundle - await bundle.close(); +async function build() { + let bundle; + let buildFailed = false; + try { + // create a bundle + const bundle = await rollup(inputOptions); + + // an array of file names this bundle depends on + console.log(bundle.watchFiles); + + await generateOutputs(bundle); + } catch (error) { + buildFailed = true; + // do some error reporting + console.error(error); + } + if (bundle) { + // closes the bundle + await bundle.close(); + } + process.exit(buildFailed ? 1 : 0); } -build(); +async function generateOutputs(bundle) { + for (const outputOptions of outputOptionsList) { + // generate output specific code in-memory + // you can call this function multiple times on the same bundle object + // replace bundle.generate with bundle.write to directly write to disk + const { output } = await bundle.generate(outputOptions); + + for (const chunkOrAsset of output) { + if (chunkOrAsset.type === 'asset') { + // For assets, this contains + // { + // fileName: string, // the asset file name + // source: string | Uint8Array // the asset source + // type: 'asset' // signifies that this is an asset + // } + console.log('Asset', chunkOrAsset); + } else { + // For chunks, this contains + // { + // code: string, // the generated JS code + // dynamicImports: string[], // external modules imported dynamically by the chunk + // exports: string[], // exported variable names + // facadeModuleId: string | null, // the id of a module that this chunk corresponds to + // fileName: string, // the chunk file name + // implicitlyLoadedBefore: string[]; // entries that should only be loaded after this chunk + // imports: string[], // external modules imported statically by the chunk + // importedBindings: {[imported: string]: string[]} // imported bindings per dependency + // isDynamicEntry: boolean, // is this chunk a dynamic entry point + // isEntry: boolean, // is this chunk a static entry point + // isImplicitEntry: boolean, // should this chunk only be loaded after other chunks + // map: string | null, // sourcemaps if present + // modules: { // information about the modules in this chunk + // [id: string]: { + // renderedExports: string[]; // exported variable names that were included + // removedExports: string[]; // exported variable names that were removed + // renderedLength: number; // the length of the remaining code in this module + // originalLength: number; // the original length of the code in this module + // code: string | null; // remaining code in this module + // }; + // }, + // name: string // the name of this chunk as used in naming patterns + // referencedFiles: string[] // files referenced via import.meta.ROLLUP_FILE_URL_ + // type: 'chunk', // signifies that this is a chunk + // } + console.log('Chunk', chunkOrAsset.modules); + } + } + } +} ``` #### inputOptions object diff --git a/src/ast/nodes/shared/ArrayPrototype.ts b/src/ast/nodes/shared/ArrayPrototype.ts index 65efb82ea17..a1a1d3d6912 100644 --- a/src/ast/nodes/shared/ArrayPrototype.ts +++ b/src/ast/nodes/shared/ArrayPrototype.ts @@ -128,6 +128,8 @@ export const ARRAY_PROTOTYPE = new ObjectEntity( filter: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_NEW_ARRAY, find: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_UNKNOWN, findIndex: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_NUMBER, + findLast: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_UNKNOWN, + findLastIndex: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_NUMBER, flat: METHOD_DEOPTS_SELF_RETURNS_NEW_ARRAY, flatMap: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_NEW_ARRAY, forEach: METHOD_CALLS_ARG_DEOPTS_SELF_RETURNS_UNKNOWN, diff --git a/src/utils/options/mergeOptions.ts b/src/utils/options/mergeOptions.ts index 9b92961d4b4..472090b1847 100644 --- a/src/utils/options/mergeOptions.ts +++ b/src/utils/options/mergeOptions.ts @@ -13,6 +13,7 @@ import { defaultOnWarn, generatedCodePresets, GenericConfigObject, + objectifyOption, objectifyOptionWithPresets, treeshakePresets, warnUnknownOptions @@ -135,7 +136,7 @@ function mergeInputOptions( 'treeshake', objectifyOptionWithPresets(treeshakePresets, 'treeshake', 'false, true, ') ), - watch: getWatch(config, overrides, 'watch') + watch: getWatch(config, overrides) }; warnUnknownOptions( @@ -171,8 +172,7 @@ const getObjectOption = ( config: GenericConfigObject, overrides: GenericConfigObject, name: string, - objectifyValue: (value: unknown) => Record | undefined = value => - (typeof value === 'object' ? value : {}) as Record | undefined + objectifyValue = objectifyOption ) => { const commandOption = normalizeObjectOptionValue(overrides[name], objectifyValue); const configOption = normalizeObjectOptionValue(config[name], objectifyValue); @@ -182,8 +182,18 @@ const getObjectOption = ( return configOption; }; -const getWatch = (config: GenericConfigObject, overrides: GenericConfigObject, name: string) => - config.watch !== false && getObjectOption(config, overrides, name); +export const getWatch = (config: GenericConfigObject, overrides: GenericConfigObject) => + config.watch !== false && getObjectOption(config, overrides, 'watch'); + +export const isWatchEnabled = (optionValue: unknown): boolean => { + if (Array.isArray(optionValue)) { + return optionValue.reduce( + (result, value) => (typeof value === 'boolean' ? value : result), + false + ); + } + return optionValue === true; +}; export const normalizeObjectOptionValue = ( optionValue: unknown, diff --git a/src/utils/options/options.ts b/src/utils/options/options.ts index d6555064c63..cfcf75b9306 100644 --- a/src/utils/options/options.ts +++ b/src/utils/options/options.ts @@ -94,6 +94,9 @@ type ObjectOptionWithPresets = | Partial | Partial; +export const objectifyOption = (value: unknown): Record => + value && typeof value === 'object' ? (value as Record) : {}; + export const objectifyOptionWithPresets = ( presets: Record, @@ -117,7 +120,7 @@ export const objectifyOptionWithPresets = ) ); } - return value && typeof value === 'object' ? (value as Record) : {}; + return objectifyOption(value); }; export const getOptionWithPreset = ( diff --git a/src/utils/transform.ts b/src/utils/transform.ts index 5821d636a07..16ea7de1319 100644 --- a/src/utils/transform.ts +++ b/src/utils/transform.ts @@ -21,7 +21,7 @@ import { decodedSourcemap } from './decodedSourcemap'; import { augmentCodeLocation, errNoTransformMapOrAstWithoutCode } from './error'; import { throwPluginError } from './pluginUtils'; -export default function transform( +export default async function transform( source: SourceDescription, module: Module, pluginDriver: PluginDriver, @@ -37,7 +37,7 @@ export default function transform( const emittedFiles: EmittedFile[] = []; let customTransformCache = false; const useCustomTransformCache = () => (customTransformCache = true); - let curPlugin: Plugin; + let pluginName = ''; const curSource: string = source.code; function transformReducer( @@ -77,13 +77,15 @@ export default function transform( return code; } - return pluginDriver - .hookReduceArg0( + let code: string; + + try { + code = await pluginDriver.hookReduceArg0( 'transform', [curSource, id], transformReducer, (pluginContext, plugin): TransformPluginContext => { - curPlugin = plugin; + pluginName = plugin.name; return { ...pluginContext, addWatchFile(id: string) { @@ -149,23 +151,24 @@ export default function transform( } }; } - ) - .catch(err => throwPluginError(err, curPlugin.name, { hook: 'transform', id })) - .then(code => { - if (!customTransformCache) { - // files emitted by a transform hook need to be emitted again if the hook is skipped - if (emittedFiles.length) module.transformFiles = emittedFiles; - } + ); + } catch (err: any) { + throwPluginError(err, pluginName, { hook: 'transform', id }); + } + + if (!customTransformCache) { + // files emitted by a transform hook need to be emitted again if the hook is skipped + if (emittedFiles.length) module.transformFiles = emittedFiles; + } - return { - ast, - code, - customTransformCache, - meta: module.info.meta, - originalCode, - originalSourcemap, - sourcemapChain, - transformDependencies - }; - }); + return { + ast, + code, + customTransformCache, + meta: module.info.meta, + originalCode, + originalSourcemap, + sourcemapChain, + transformDependencies + }; } diff --git a/test/cli/samples/watch/no-watch-by-default/_config.js b/test/cli/samples/watch/no-watch-by-default/_config.js new file mode 100644 index 00000000000..b43ee5fbb19 --- /dev/null +++ b/test/cli/samples/watch/no-watch-by-default/_config.js @@ -0,0 +1,12 @@ +const { assertIncludes } = require('../../../../utils'); + +module.exports = { + description: 'does not watch if --watch is missing', + command: 'node wrapper.js -c --no-watch.clearScreen', + stderr: stderr => assertIncludes(stderr, 'main.js → _actual.js...\ncreated _actual.js in'), + abortOnStderr(data) { + if (data.includes('waiting for changes')) { + throw new Error('Watch initiated'); + } + } +}; diff --git a/test/cli/samples/watch/no-watch-by-default/main.js b/test/cli/samples/watch/no-watch-by-default/main.js new file mode 100644 index 00000000000..7a4e8a723a4 --- /dev/null +++ b/test/cli/samples/watch/no-watch-by-default/main.js @@ -0,0 +1 @@ +export default 42; diff --git a/test/cli/samples/watch/no-watch-by-default/rollup.config.js b/test/cli/samples/watch/no-watch-by-default/rollup.config.js new file mode 100644 index 00000000000..161653a6b43 --- /dev/null +++ b/test/cli/samples/watch/no-watch-by-default/rollup.config.js @@ -0,0 +1,9 @@ +export default [ + { + input: 'main.js', + output: { + file: '_actual.js', + format: 'es', + }, + } +]; diff --git a/test/cli/samples/watch/no-watch-by-default/wrapper.js b/test/cli/samples/watch/no-watch-by-default/wrapper.js new file mode 100755 index 00000000000..8f733cc8815 --- /dev/null +++ b/test/cli/samples/watch/no-watch-by-default/wrapper.js @@ -0,0 +1,5 @@ +#!/usr/bin/env node + +process.stdout.isTTY = true; +process.stderr.isTTY = true; +require('../../../../../dist/bin/rollup'); diff --git a/test/form/samples/builtin-prototypes/array-expression/_expected.js b/test/form/samples/builtin-prototypes/array-expression/_expected.js index f6f62d2d5fe..4fea44f0ad9 100644 --- a/test/form/samples/builtin-prototypes/array-expression/_expected.js +++ b/test/form/samples/builtin-prototypes/array-expression/_expected.js @@ -35,10 +35,18 @@ _filterArray[0].effect(); const _findArray = [{ effect() {} }]; _findArray.find(element => (element.effect = () => console.log(1))); _findArray[0].effect(); +[1].findLast(() => console.log(1) || true); +const _findLastArray = [{ effect() {} }]; +_findLastArray.findLast(element => (element.effect = () => console.log(1))); +_findLastArray[0].effect(); [1].findIndex(() => console.log(1) || true); const _findIndexArray = [{ effect() {} }]; _findIndexArray.findIndex(element => (element.effect = () => console.log(1))); _findIndexArray[0].effect(); +[1].findLastIndex(() => console.log(1) || true); +const _findLastIndexArray = [{ effect() {} }]; +_findLastIndexArray.findLastIndex(element => (element.effect = () => console.log(1))); +_findLastIndexArray[0].effect(); [1].flatMap(() => console.log(1) || 1); const _flatMapArray = [{ effect() {} }]; _flatMapArray.flatMap(element => (element.effect = () => console.log(1))); diff --git a/test/form/samples/builtin-prototypes/array-expression/main.js b/test/form/samples/builtin-prototypes/array-expression/main.js index ae082e12d64..3cf1b47ca43 100644 --- a/test/form/samples/builtin-prototypes/array-expression/main.js +++ b/test/form/samples/builtin-prototypes/array-expression/main.js @@ -74,12 +74,24 @@ const _findArray = [{ effect() {} }]; _findArray.find(element => (element.effect = () => console.log(1))); _findArray[0].effect(); +const _findLast = [1].findLast(() => true); +const _findLastEffect = [1].findLast(() => console.log(1) || true); +const _findLastArray = [{ effect() {} }]; +_findLastArray.findLast(element => (element.effect = () => console.log(1))); +_findLastArray[0].effect(); + const _findIndex = [1].findIndex(() => true).toPrecision(1); const _findIndexEffect = [1].findIndex(() => console.log(1) || true); const _findIndexArray = [{ effect() {} }]; _findIndexArray.findIndex(element => (element.effect = () => console.log(1))); _findIndexArray[0].effect(); +const _findLastIndex = [1].findLastIndex(() => true).toPrecision(1); +const _findLastIndexEffect = [1].findLastIndex(() => console.log(1) || true); +const _findLastIndexArray = [{ effect() {} }]; +_findLastIndexArray.findLastIndex(element => (element.effect = () => console.log(1))); +_findLastIndexArray[0].effect(); + const _flatMap = [1].flatMap(() => 1).join(','); const _flatMapEffect = [1].flatMap(() => console.log(1) || 1); const _flatMapArray = [{ effect() {} }];