Skip to content

Commit

Permalink
Merge branch 'master' into mutable-meta
Browse files Browse the repository at this point in the history
  • Loading branch information
lukastaegert committed Jan 4, 2022
2 parents f0af4ea + a54b798 commit f5392ea
Show file tree
Hide file tree
Showing 12 changed files with 172 additions and 88 deletions.
3 changes: 2 additions & 1 deletion 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';
Expand Down Expand Up @@ -56,7 +57,7 @@ export default async function runRollup(command: Record<string, any>): Promise<v
});
}

if (command.watch) {
if (isWatchEnabled(command.watch)) {
await loadFsEvents();
const { watch } = await import('./watch-cli');
watch(command);
Expand Down
134 changes: 76 additions & 58 deletions docs/02-javascript-api.md
Expand Up @@ -13,71 +13,89 @@ On a `bundle` object, you can call `bundle.generate` multiple times with differe
Once you're finished with the `bundle` object, you should call `bundle.close()`, which will let plugins clean up their external processes or services via the [`closeBundle`](guide/en/#closebundle) hook.

```javascript
const rollup = require('rollup');
import { rollup } from 'rollup';

// see below for details on the options
// see below for details on these options
const inputOptions = {...};
const outputOptions = {...};

async function build() {
// create a bundle
const bundle = await rollup.rollup(inputOptions);

console.log(bundle.watchFiles); // an array of file names this bundle depends on

// generate output specific code in-memory
// you can call this function multiple times on the same bundle object
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_<id>
// 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_<id>
// type: 'chunk', // signifies that this is a chunk
// }
console.log('Chunk', chunkOrAsset.modules);
}
}
}
}
```

#### inputOptions object
Expand Down
2 changes: 2 additions & 0 deletions src/ast/nodes/shared/ArrayPrototype.ts
Expand Up @@ -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,
Expand Down
20 changes: 15 additions & 5 deletions src/utils/options/mergeOptions.ts
Expand Up @@ -13,6 +13,7 @@ import {
defaultOnWarn,
generatedCodePresets,
GenericConfigObject,
objectifyOption,
objectifyOptionWithPresets,
treeshakePresets,
warnUnknownOptions
Expand Down Expand Up @@ -135,7 +136,7 @@ function mergeInputOptions(
'treeshake',
objectifyOptionWithPresets(treeshakePresets, 'treeshake', 'false, true, ')
),
watch: getWatch(config, overrides, 'watch')
watch: getWatch(config, overrides)
};

warnUnknownOptions(
Expand Down Expand Up @@ -171,8 +172,7 @@ const getObjectOption = (
config: GenericConfigObject,
overrides: GenericConfigObject,
name: string,
objectifyValue: (value: unknown) => Record<string, unknown> | undefined = value =>
(typeof value === 'object' ? value : {}) as Record<string, unknown> | undefined
objectifyValue = objectifyOption
) => {
const commandOption = normalizeObjectOptionValue(overrides[name], objectifyValue);
const configOption = normalizeObjectOptionValue(config[name], objectifyValue);
Expand All @@ -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,
Expand Down
5 changes: 4 additions & 1 deletion src/utils/options/options.ts
Expand Up @@ -94,6 +94,9 @@ type ObjectOptionWithPresets =
| Partial<NormalizedTreeshakingOptions>
| Partial<NormalizedGeneratedCodeOptions>;

export const objectifyOption = (value: unknown): Record<string, unknown> =>
value && typeof value === 'object' ? (value as Record<string, unknown>) : {};

export const objectifyOptionWithPresets =
<T extends ObjectOptionWithPresets>(
presets: Record<string, T>,
Expand All @@ -117,7 +120,7 @@ export const objectifyOptionWithPresets =
)
);
}
return value && typeof value === 'object' ? (value as Record<string, unknown>) : {};
return objectifyOption(value);
};

export const getOptionWithPreset = <T extends ObjectOptionWithPresets>(
Expand Down
49 changes: 26 additions & 23 deletions src/utils/transform.ts
Expand Up @@ -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,
Expand All @@ -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(
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
};
}
12 changes: 12 additions & 0 deletions 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');
}
}
};
1 change: 1 addition & 0 deletions test/cli/samples/watch/no-watch-by-default/main.js
@@ -0,0 +1 @@
export default 42;
9 changes: 9 additions & 0 deletions 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',
},
}
];
5 changes: 5 additions & 0 deletions 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');
Expand Up @@ -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)));
Expand Down
12 changes: 12 additions & 0 deletions test/form/samples/builtin-prototypes/array-expression/main.js
Expand Up @@ -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() {} }];
Expand Down

0 comments on commit f5392ea

Please sign in to comment.