Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide normalized options #3597

Merged
merged 27 commits into from
May 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d00a0d4
Add test for #3467
lukastaegert May 11, 2020
ab64733
Extract multi-chunk option validation from output option parsing
lukastaegert May 11, 2020
7d853df
Reorder rollup entry by usage
lukastaegert May 11, 2020
6733df5
Get rid of chunk generation parameters
lukastaegert May 11, 2020
89eb875
Introduce Bundle class
lukastaegert May 13, 2020
b68e8f8
Make generate a little nicer
lukastaegert May 13, 2020
5d80c01
Split option parsing
lukastaegert May 13, 2020
f5fb86f
Split merging from parsing
lukastaegert May 14, 2020
3e9dcb4
Start implementing normalized input options
lukastaegert May 15, 2020
7609b6b
Normalize treeshaking options and warnings
lukastaegert May 20, 2020
862da04
Normalize input, manualChunks, signatures and inline validation
lukastaegert May 20, 2020
17cef26
Normalize remaining options
lukastaegert May 20, 2020
22a5cca
Fix CLI object option handling
lukastaegert May 21, 2020
5c5db5f
Start normalizing output options
lukastaegert May 21, 2020
1496f58
Normalize file name options
lukastaegert May 25, 2020
a24f064
Normalize addons
lukastaegert May 25, 2020
beac0df
Normalize flags
lukastaegert May 25, 2020
3b4d1aa
Normalize dynamicImportFunction and exports
lukastaegert May 25, 2020
ec1a6bc
Normalize globals, name and paths
lukastaegert May 26, 2020
dac5fcc
Make options private in Graph
lukastaegert May 26, 2020
1bd2f36
Refactor option usage
lukastaegert May 26, 2020
4353ad6
Fix types
lukastaegert May 26, 2020
d942a46
Remove unrelated TODOs
lukastaegert May 26, 2020
90c5613
Update docs
lukastaegert May 26, 2020
7948eb6
Improve coverage
lukastaegert May 26, 2020
b232e58
Fix url in builtin warning
lukastaegert May 26, 2020
0eb883c
Merge branch 'master' into normalize-options
lukastaegert May 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion cli/cli.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import help from 'help.md';
import { version } from 'package.json';
import argParser from 'yargs-parser';
import { commandAliases } from '../src/utils/mergeOptions';
import { commandAliases } from '../src/utils/options/mergeOptions';
import run from './run/index';

const command = argParser(process.argv.slice(2), {
Expand Down
13 changes: 9 additions & 4 deletions cli/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,15 @@ Basic options:
--strictDeprecations Throw errors for deprecated features
--no-treeshake Disable tree-shaking optimisations
--no-treeshake.annotations Ignore pure call annotations
--no-treeshake.no-moduleSideEffects Assume modules have no side-effects
--no-treeshake.no-propertyReadSideEffects Ignore property access side-effects
--no-treeshake.no-tryCatchDeoptimization Do not turn off try-catch-tree-shaking
--no-treeshake.no-unknownGlobalSideEffects Assume unknown globals do not throw
--no-treeshake.moduleSideEffects Assume modules have no side-effects
--no-treeshake.propertyReadSideEffects Ignore property access side-effects
--no-treeshake.tryCatchDeoptimization Do not turn off try-catch-tree-shaking
--no-treeshake.unknownGlobalSideEffects Assume unknown globals do not throw
--watch.buildDelay <number> Throttle watch rebuilds
--no-watch.clearScreen Do not clear the screen when rebuilding
--watch.skipWrite Do not write files to disk when watching
--watch.exclude <files> Exclude files from being watched
--watch.include <files> Limit watching to specified files

Examples:

Expand Down
2 changes: 1 addition & 1 deletion cli/run/batchWarnings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const immediateHandlers: {
.map((name: string) => `'${name}'`)
.join(', ')} and '${warning.modules!.slice(-1)}'`;
stderr(
`Creating a browser bundle that depends on ${detail}. You might need to include https://www.npmjs.com/package/rollup-plugin-node-builtins`
`Creating a browser bundle that depends on ${detail}. You might need to include https://github.com/ionic-team/rollup-plugin-node-polyfills`
);
}
};
Expand Down
4 changes: 2 additions & 2 deletions cli/run/loadConfigFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { pathToFileURL } from 'url';
import * as rollup from '../../src/node-entry';
import { MergedRollupOptions } from '../../src/rollup/types';
import { error } from '../../src/utils/error';
import { mergeOptions } from '../../src/utils/mergeOptions';
import { GenericConfigObject } from '../../src/utils/parseOptions';
import { mergeOptions } from '../../src/utils/options/mergeOptions';
import { GenericConfigObject } from '../../src/utils/options/options';
import relativeId from '../../src/utils/relativeId';
import { stderr } from '../logging';
import batchWarnings, { BatchWarnings } from './batchWarnings';
Expand Down
2 changes: 1 addition & 1 deletion cli/run/loadConfigFromCommand.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { MergedRollupOptions } from '../../src/rollup/types';
import { mergeOptions } from '../../src/utils/mergeOptions';
import { mergeOptions } from '../../src/utils/options/mergeOptions';
import batchWarnings, { BatchWarnings } from './batchWarnings';
import { addCommandPluginsToInputOptions } from './commandPlugins';
import { stdinName } from './stdin';
Expand Down
13 changes: 9 additions & 4 deletions docs/01-command-line-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,10 +312,15 @@ Many options have command line equivalents. In those cases, any arguments passed
--strictDeprecations Throw errors for deprecated features
--no-treeshake Disable tree-shaking optimisations
--no-treeshake.annotations Ignore pure call annotations
--no-treeshake.no-moduleSideEffects Assume modules have no side-effects
--no-treeshake.no-propertyReadSideEffects Ignore property access side-effects
--no-treeshake.no-tryCatchDeoptimization Do not turn off try-catch-tree-shaking
--no-treeshake.no-unknownGlobalSideEffects Assume unknown globals do not throw
--no-treeshake.moduleSideEffects Assume modules have no side-effects
--no-treeshake.propertyReadSideEffects Ignore property access side-effects
--no-treeshake.tryCatchDeoptimization Do not turn off try-catch-tree-shaking
--no-treeshake.unknownGlobalSideEffects Assume unknown globals do not throw
--watch.buildDelay <number> Throttle watch rebuilds
--no-watch.clearScreen Do not clear the screen when rebuilding
--watch.skipWrite Do not write files to disk when watching
--watch.exclude <files> Exclude files from being watched
--watch.include <files> Limit watching to specified files
```

The flags listed below are only available via the command line interface. All other flags correspond to and override their config file equivalents, see the [big list of options](guide/en/#big-list-of-options) for details.
Expand Down
4 changes: 2 additions & 2 deletions docs/05-plugin-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ Kind: `async, parallel`<br>
Previous Hook: [`options`](guide/en/#options)<br>
Next Hook: [`resolveId`](guide/en/#resolveid) to resolve each entry point in parallel.

Called on each `rollup.rollup` build. This is the recommended hook to use when you need access to the options passed to `rollup.rollup()` as it will take the transformations by all [`options`](guide/en/#options) hooks into account.
Called on each `rollup.rollup` build. This is the recommended hook to use when you need access to the options passed to `rollup.rollup()` as it takes the transformations by all [`options`](guide/en/#options) hooks into account and also contains the right default values for unset options.

#### `load`
Type: `(id: string) => string | null | { code: string, map?: string | SourceMap, ast? : ESTree.Program, moduleSideEffects?: boolean | null, syntheticNamedExports?: boolean | null }`<br>
Expand Down Expand Up @@ -409,7 +409,7 @@ Kind: `async, parallel`<br>
Previous Hook: [`outputOptions`](guide/en/#outputoptions)<br>
Next Hook: [`banner`](guide/en/#banner), [`footer`](guide/en/#footer), [`intro`](guide/en/#intro) and [`outro`](guide/en/#outro) run in parallel.

Called initially each time `bundle.generate()` or `bundle.write()` is called. To get notified when generation has completed, use the `generateBundle` and `renderError` hooks. This is the recommended hook to use when you need access to the output options passed to `bundle.generate()` or `bundle.write()` as it will take the transformations by all [`outputOptions`](guide/en/#outputoptions) hooks into account. It also receives the input options passed to `rollup.rollup()` so that plugins that can be used as output plugins, i.e. plugins that only use `generate` phase hooks, can get access to them.
Called initially each time `bundle.generate()` or `bundle.write()` is called. To get notified when generation has completed, use the `generateBundle` and `renderError` hooks. This is the recommended hook to use when you need access to the output options passed to `bundle.generate()` or `bundle.write()` as it takes the transformations by all [`outputOptions`](guide/en/#outputoptions) hooks into account and also contains the right default values for unset options. It also receives the input options passed to `rollup.rollup()` so that plugins that can be used as output plugins, i.e. plugins that only use `generate` phase hooks, can get access to them.

#### `resolveFileUrl`
Type: `({chunkId: string, fileName: string, format: string, moduleId: string, referenceId: string, relativePath: string}) => string | null`<br>
Expand Down
13 changes: 9 additions & 4 deletions docs/999-big-list-of-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -1108,7 +1108,7 @@ class Impure {

**treeshake.moduleSideEffects**<br>
Type: `boolean | "no-external" | string[] | (id: string, external: boolean) => boolean`<br>
CLI: `--treeshake.moduleSideEffects`/`--no-treeshake.moduleSideEffects`<br>
CLI: `--treeshake.moduleSideEffects`/`--no-treeshake.moduleSideEffects`/`--treeshake.moduleSideEffects no-external`<br>
Default: `true`

If `false`, assume modules and external dependencies from which nothing is imported do not have other side-effects like mutating global variables or logging without checking. For external dependencies, this will suppress empty imports:
Expand Down Expand Up @@ -1276,6 +1276,7 @@ These options only take effect when running Rollup with the `--watch` flag, or u

#### watch.buildDelay
Type: `number`<br>
CLI: `--watch.buildDelay <number>`<br>
Default: `0`

Configures how long Rollup will wait for further changes until it triggers a rebuild in milliseconds. By default, Rollup does not wait but there is a small debounce timeout configured in the chokidar instance. Setting this to a value greater than `0` will mean that Rollup will only triger a rebuild if there was no change for the configured number of milliseconds. If several configurations are watched, Rollup will use the largest configured build delay.
Expand All @@ -1287,18 +1288,21 @@ An optional object of watch options that will be passed to the bundled [chokidar

#### watch.clearScreen
Type: `boolean`<br>
CLI: `--watch.clearScreen`/`--no-watch.clearScreen`<br>
Default: `true`

Whether to clear the screen when a rebuild is triggered.

#### watch.skipWrite
Type: `boolean`<br>
CLI: `--watch.skipWrite`/`--no-watch.skipWrite`<br>
Default: `false`

Whether to skip the `bundle.write()` step when a rebuild is triggered.

#### watch.exclude
Type: `string`
Type: `string`<br>
CLI: `--watch.exclude <files>`

Prevent files from being watched:

Expand All @@ -1313,9 +1317,10 @@ export default {
```

#### watch.include
Type: `string`
Type: `string`<br>
CLI: `--watch.include <files>`

Limit the file-watching to certain files:
Limit the file-watching to certain files. Note that this only filters the module graph but does not allow to add additional watch files:

```js
// rollup.config.js
Expand Down
172 changes: 172 additions & 0 deletions src/Bundle.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import Chunk from './Chunk';
import {
NormalizedInputOptions,
NormalizedOutputOptions,
OutputBundle,
OutputBundleWithPlaceholders,
OutputChunk
} from './rollup/types';
import { Addons, createAddons } from './utils/addons';
import commondir from './utils/commondir';
import { error, warnDeprecation } from './utils/error';
import { FILE_PLACEHOLDER } from './utils/FileEmitter';
import { basename, isAbsolute } from './utils/path';
import { PluginDriver } from './utils/PluginDriver';
import { timeEnd, timeStart } from './utils/timers';

export default class Bundle {
constructor(
private readonly outputOptions: NormalizedOutputOptions,
private readonly unsetOptions: Set<string>,
private readonly inputOptions: NormalizedInputOptions,
private readonly pluginDriver: PluginDriver,
private readonly chunks: Chunk[]
) {}

async generate(isWrite: boolean): Promise<OutputBundle> {
timeStart('GENERATE', 1);
const inputBase = commondir(getAbsoluteEntryModulePaths(this.chunks));
const outputBundle: OutputBundleWithPlaceholders = Object.create(null);
this.pluginDriver.setOutputBundle(outputBundle, this.outputOptions.assetFileNames);
try {
await this.pluginDriver.hookParallel('renderStart', [this.outputOptions, this.inputOptions]);
if (this.chunks.length > 1) {
validateOptionsForMultiChunkOutput(this.outputOptions);
}

const addons = await createAddons(this.outputOptions, this.pluginDriver);
for (const chunk of this.chunks) {
chunk.generateExports(this.outputOptions);
}
for (const chunk of this.chunks) {
chunk.preRender(this.outputOptions, inputBase, this.pluginDriver);
}
this.assignChunkIds(inputBase, addons, outputBundle);
assignChunksToBundle(this.chunks, outputBundle);

await Promise.all(
this.chunks.map(chunk => {
const outputChunk = outputBundle[chunk.id!] as OutputChunk;
return chunk
.render(this.outputOptions, addons, outputChunk, this.pluginDriver)
.then(rendered => {
outputChunk.code = rendered.code;
outputChunk.map = rendered.map;
});
})
);
} catch (error) {
await this.pluginDriver.hookParallel('renderError', [error]);
throw error;
}
await this.pluginDriver.hookSeq('generateBundle', [
this.outputOptions,
outputBundle as OutputBundle,
isWrite
]);
for (const key of Object.keys(outputBundle)) {
const file = outputBundle[key] as any;
if (!file.type) {
warnDeprecation(
'A plugin is directly adding properties to the bundle object in the "generateBundle" hook. This is deprecated and will be removed in a future Rollup version, please use "this.emitFile" instead.',
true,
this.inputOptions
);
file.type = 'asset';
}
}
this.pluginDriver.finaliseAssets();

timeEnd('GENERATE', 1);
return outputBundle as OutputBundle;
}

private assignChunkIds(inputBase: string, addons: Addons, bundle: OutputBundleWithPlaceholders) {
const entryChunks: Chunk[] = [];
const otherChunks: Chunk[] = [];
for (const chunk of this.chunks) {
(chunk.facadeModule && chunk.facadeModule.isUserDefinedEntryPoint
? entryChunks
: otherChunks
).push(chunk);
}

// make sure entry chunk names take precedence with regard to deconflicting
const chunksForNaming: Chunk[] = entryChunks.concat(otherChunks);
for (const chunk of chunksForNaming) {
if (this.outputOptions.file) {
chunk.id = basename(this.outputOptions.file);
} else if (this.inputOptions.preserveModules) {
chunk.id = chunk.generateIdPreserveModules(
inputBase,
this.outputOptions,
bundle,
this.unsetOptions
);
} else {
chunk.id = chunk.generateId(addons, this.outputOptions, bundle, true, this.pluginDriver);
}
bundle[chunk.id] = FILE_PLACEHOLDER;
}
}
}

function getAbsoluteEntryModulePaths(chunks: Chunk[]): string[] {
const absoluteEntryModulePaths: string[] = [];
for (const chunk of chunks) {
for (const entryModule of chunk.entryModules) {
if (isAbsolute(entryModule.id)) {
absoluteEntryModulePaths.push(entryModule.id);
}
}
}
return absoluteEntryModulePaths;
}

function validateOptionsForMultiChunkOutput(outputOptions: NormalizedOutputOptions) {
if (outputOptions.format === 'umd' || outputOptions.format === 'iife')
return error({
code: 'INVALID_OPTION',
message: 'UMD and IIFE output formats are not supported for code-splitting builds.'
});
if (typeof outputOptions.file === 'string')
return error({
code: 'INVALID_OPTION',
message:
'When building multiple chunks, the "output.dir" option must be used, not "output.file". ' +
'To inline dynamic imports, set the "inlineDynamicImports" option.'
});
if (outputOptions.sourcemapFile)
return error({
code: 'INVALID_OPTION',
message: '"output.sourcemapFile" is only supported for single-file builds.'
});
}

function assignChunksToBundle(
chunks: Chunk[],
outputBundle: OutputBundleWithPlaceholders
): OutputBundle {
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const facadeModule = chunk.facadeModule;

outputBundle[chunk.id!] = {
code: undefined as any,
dynamicImports: chunk.getDynamicImportIds(),
exports: chunk.getExportNames(),
facadeModuleId: facadeModule && facadeModule.id,
fileName: chunk.id,
imports: chunk.getImportIds(),
isDynamicEntry: chunk.isDynamicEntry,
isEntry: facadeModule !== null && facadeModule.isEntryPoint,
map: undefined,
modules: chunk.renderedModules,
get name() {
return chunk.getChunkName();
},
type: 'chunk'
} as OutputChunk;
}
return outputBundle as OutputBundle;
}