Skip to content

Commit

Permalink
Add option to handle use of features marked for deprecation as errors (
Browse files Browse the repository at this point in the history
…#2950)

* Unify deprecations for plugin hooks

* Deprecate plugin context functions

* Deprecate options

* Use new deprecation API for transform dependencies

* Adjust documentation
  • Loading branch information
lukastaegert committed Jun 21, 2019
1 parent bae092c commit 1739459
Show file tree
Hide file tree
Showing 140 changed files with 567 additions and 263 deletions.
2 changes: 1 addition & 1 deletion bin/src/logging.ts
Expand Up @@ -10,7 +10,7 @@ export function handleError(err: RollupError, recover = false) {
if (err.name) description = `${err.name}: ${description}`;
const message =
((err as { plugin?: string }).plugin
? `(${(err as { plugin?: string }).plugin} plugin) ${description}`
? `(plugin ${(err as { plugin?: string }).plugin}) ${description}`
: description) || err;

stderr(tc.bold.red(`[!] ${tc.bold(message.toString())}`));
Expand Down
9 changes: 9 additions & 0 deletions docs/999-big-list-of-options.md
Expand Up @@ -533,6 +533,15 @@ Default: `false`

Instead of creating as few chunks as possible, this mode will create separate chunks for all modules using the original module names as file names. Requires the [`output.dir`](guide/en#output-dir) option. Tree-shaking will still be applied, suppressing files that are not used by the provided entry points or do not have side-effects when executed. This mode can be used to transform a file structure to a different module format.

#### strictDeprecations
Type: `boolean`<br>
CLI: `--strictDeprecations`/`--no-strictDeprecations`<br>
Default: `false`

When this flag is enabled, Rollup will throw an error instead of showing a warning when a deprecated feature is used. Furthermore, features that are marked to receive a deprecation warning with the next major version will also throw an error when used.

This flag is intended to be used by e.g. plugin authors to be able to adjust their plugins for upcoming major releases as early as possible.

### Danger zone

You probably don't need to use these options unless you know what you are doing!
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -121,7 +121,7 @@
"systemjs": "^3.1.6",
"terser": "^4.0.0",
"tslib": "^1.10.0",
"tslint": "^5.17.0",
"tslint": "^5.18.0",
"turbocolor": "^2.6.1",
"typescript": "^3.5.2",
"url-parse": "^1.4.7"
Expand Down
21 changes: 9 additions & 12 deletions rollup.config.js
Expand Up @@ -74,6 +74,12 @@ const moduleAliases = {
'package.json': path.resolve('package.json')
};

const treeshake = {
moduleSideEffects: false,
propertyReadSideEffects: false,
tryCatchDeoptimization: false
};

export default command => {
const nodeBuilds = [
/* Rollup core node builds */
Expand All @@ -90,10 +96,7 @@ export default command => {
],
// acorn needs to be external as some plugins rely on a shared acorn instance
external: ['fs', 'path', 'events', 'module', 'util', 'acorn'],
treeshake: {
moduleSideEffects: false,
propertyReadSideEffects: false
},
treeshake,
output: [
{ file: 'dist/rollup.js', format: 'cjs', sourcemap: true, banner },
{ file: 'dist/rollup.es.js', format: 'esm', banner }
Expand All @@ -112,10 +115,7 @@ export default command => {
typescript({include: '**/*.{ts,js}'}),
],
external: ['fs', 'path', 'module', 'assert', 'events', 'rollup'],
treeshake: {
moduleSideEffects: false,
propertyReadSideEffects: false
},
treeshake,
output: {
file: 'bin/rollup',
format: 'cjs',
Expand Down Expand Up @@ -149,10 +149,7 @@ export default command => {
typescript({include: '**/*.{ts,js}'}),
terser({ module: true, output: { comments: 'some' } })
],
treeshake: {
moduleSideEffects: false,
propertyReadSideEffects: false
},
treeshake,
output: [
{ file: 'dist/rollup.browser.js', format: 'umd', name: 'rollup', banner },
{ file: 'dist/rollup.browser.es.js', format: 'esm', banner }
Expand Down
22 changes: 20 additions & 2 deletions src/Graph.ts
Expand Up @@ -30,6 +30,7 @@ import { finaliseAsset } from './utils/assetHooks';
import { BuildPhase } from './utils/buildPhase';
import { assignChunkColouringHashes } from './utils/chunkColouring';
import { Uint8ArrayToHexString } from './utils/entryHashing';
import { errDeprecation, error } from './utils/error';
import { analyseModuleExecution, sortByExecutionOrder } from './utils/executionOrder';
import { resolve } from './utils/path';
import { createPluginDriver, PluginDriver } from './utils/pluginDriver';
Expand Down Expand Up @@ -88,8 +89,10 @@ export default class Graph {
private modules: Module[] = [];
private onwarn: WarningHandler;
private pluginCache?: Record<string, SerializablePluginCache>;
private strictDeprecations: boolean;

constructor(options: InputOptions, watcher?: RollupWatcher) {
this.onwarn = (options.onwarn as WarningHandler) || makeOnwarn();
this.curChunkIndex = 0;
this.deoptimizationTracker = new EntityPathTracker();
this.cachedModules = new Map();
Expand All @@ -107,6 +110,7 @@ export default class Graph {
}
}
this.preserveModules = options.preserveModules as boolean;
this.strictDeprecations = options.strictDeprecations as boolean;

this.cacheExpiry = options.experimentalCacheExpiry as number;

Expand All @@ -125,9 +129,14 @@ export default class Graph {
annotations: true,
moduleSideEffects: true,
propertyReadSideEffects: true,
pureExternalModules: false,
tryCatchDeoptimization: true
};
if (typeof this.treeshakingOptions.pureExternalModules !== 'undefined') {
this.warnDeprecation(
`The "treeshake.pureExternalModules" option is deprecated. The "treeshake.moduleSideEffects" option should be used instead. "treeshake.pureExternalModules: true" is equivalent to "treeshake.moduleSideEffects: 'no-external'"`,
false
);
}
}

this.contextParse = (code: string, options: acorn.Options = {}) =>
Expand Down Expand Up @@ -164,7 +173,6 @@ export default class Graph {
this.getModuleContext = () => this.context;
}

this.onwarn = (options.onwarn as WarningHandler) || makeOnwarn();
this.acornOptions = options.acorn || {};
const acornPluginsToInject = [];

Expand Down Expand Up @@ -379,6 +387,16 @@ export default class Graph {
this.onwarn(warning);
}

warnDeprecation(deprecation: string | RollupWarning, activeDeprecation: boolean): void {
if (activeDeprecation || this.strictDeprecations) {
const warning = errDeprecation(deprecation);
if (this.strictDeprecations) {
return error(warning);
}
this.warn(warning);
}
}

private link(entryModules: Module[]) {
for (const module of this.modules) {
module.linkDependencies();
Expand Down
1 change: 0 additions & 1 deletion src/ast/nodes/MetaProperty.ts
Expand Up @@ -73,7 +73,6 @@ export default class MetaProperty extends NodeBase {
const relativePath = normalize(relative(dirname(chunkId), fileName));
let replacement;
if (assetReferenceId !== null) {
// deprecated hook for assets
replacement = pluginDriver.hookFirstSync('resolveAssetUrl', [
{
assetFileName: fileName,
Expand Down
39 changes: 26 additions & 13 deletions src/rollup/index.ts
Expand Up @@ -6,12 +6,12 @@ import { createAddons } from '../utils/addons';
import { createAssetPluginHooks, finaliseAsset } from '../utils/assetHooks';
import { assignChunkIds } from '../utils/assignChunkIds';
import commondir from '../utils/commondir';
import { error } from '../utils/error';
import { errDeprecation, error } from '../utils/error';
import { writeFile } from '../utils/fs';
import getExportMode from '../utils/getExportMode';
import mergeOptions, { GenericConfigObject } from '../utils/mergeOptions';
import { basename, dirname, isAbsolute, resolve } from '../utils/path';
import { PluginDriver } from '../utils/pluginDriver';
import { ANONYMOUS_PLUGIN_PREFIX, PluginDriver } from '../utils/pluginDriver';
import { SOURCEMAPPING_URL } from '../utils/sourceMappingURL';
import { getTimings, initialiseTimers, timeEnd, timeStart } from '../utils/timers';
import {
Expand All @@ -31,10 +31,12 @@ import {

function checkOutputOptions(options: OutputOptions) {
if ((options.format as string) === 'es6') {
error({
message: 'The "es6" output format is deprecated – use "esm" instead',
url: `https://rollupjs.org/guide/en#output-format`
});
error(
errDeprecation({
message: 'The "es6" output format is deprecated – use "esm" instead',
url: `https://rollupjs.org/guide/en#output-format`
})
);
}

if (['amd', 'cjs', 'system', 'es', 'iife', 'umd'].indexOf(options.format as string) < 0) {
Expand Down Expand Up @@ -70,6 +72,16 @@ function applyOptionHook(inputOptions: InputOptions, plugin: Plugin) {
return inputOptions;
}

function ensureArray<T>(items: (T | null | undefined)[] | T | null | undefined): T[] {
if (Array.isArray(items)) {
return items.filter(Boolean) as T[];
}
if (items) {
return [items];
}
return [];
}

function getInputOptions(rawInputOptions: GenericConfigObject): InputOptions {
if (!rawInputOptions) {
throw new Error('You must supply an options object to rollup');
Expand All @@ -81,13 +93,14 @@ function getInputOptions(rawInputOptions: GenericConfigObject): InputOptions {
if (optionError)
(inputOptions.onwarn as WarningHandler)({ message: optionError, code: 'UNKNOWN_OPTION' });

const plugins = inputOptions.plugins;
inputOptions.plugins = Array.isArray(plugins)
? plugins.filter(Boolean)
: plugins
? [plugins]
: [];
inputOptions = inputOptions.plugins.reduce(applyOptionHook, inputOptions);
inputOptions = ensureArray(inputOptions.plugins).reduce(applyOptionHook, inputOptions);
inputOptions.plugins = ensureArray(inputOptions.plugins);
for (let pluginIndex = 0; pluginIndex < inputOptions.plugins.length; pluginIndex++) {
const plugin = inputOptions.plugins[pluginIndex];
if (!plugin.name) {
plugin.name = `${ANONYMOUS_PLUGIN_PREFIX}${pluginIndex + 1}`;
}
}

if (inputOptions.inlineDynamicImports) {
if (inputOptions.preserveModules)
Expand Down
2 changes: 1 addition & 1 deletion src/rollup/types.d.ts
Expand Up @@ -374,6 +374,7 @@ export interface InputOptions {
preserveModules?: boolean;
preserveSymlinks?: boolean;
shimMissingExports?: boolean;
strictDeprecations?: boolean;
treeshake?: boolean | TreeshakingOptions;
watch?: WatcherOptions;
}
Expand Down Expand Up @@ -480,7 +481,6 @@ export interface SerializablePluginCache {
}

export interface RollupCache {
// to be deprecated
modules?: ModuleJSON[];
plugins?: Record<string, SerializablePluginCache>;
}
Expand Down
14 changes: 6 additions & 8 deletions src/utils/addons.ts
Expand Up @@ -41,13 +41,11 @@ export function createAddons(graph: Graph, options: OutputOptions): Promise<Addo

return { intro, outro, banner, footer };
})
.catch(
(err): any => {
error({
code: 'ADDON_ERROR',
message: `Could not retrieve ${err.hook}. Check configuration of ${err.plugin}.
.catch((err): any => {
error({
code: 'ADDON_ERROR',
message: `Could not retrieve ${err.hook}. Check configuration of plugin ${err.plugin}.
\tError Message: ${err.message}`
});
}
);
});
});
}
20 changes: 11 additions & 9 deletions src/utils/error.ts
Expand Up @@ -39,6 +39,7 @@ export enum Errors {
BAD_LOADER = 'BAD_LOADER',
CHUNK_NOT_FOUND = 'CHUNK_NOT_FOUND',
CHUNK_NOT_GENERATED = 'CHUNK_NOT_GENERATED',
DEPRECATED_FEATURE = 'DEPRECATED_FEATURE',
INVALID_ASSET_NAME = 'INVALID_ASSET_NAME',
INVALID_CHUNK = 'INVALID_CHUNK',
INVALID_EXTERNAL_ID = 'INVALID_EXTERNAL_ID',
Expand All @@ -53,18 +54,14 @@ export enum Errors {
export function errAssetNotFinalisedForFileName(asset: Asset) {
return {
code: Errors.ASSET_NOT_FINALISED,
message: `Plugin error - Unable to get file name for asset "${
asset.name
}". Ensure that the source is set and that generate is called first.`
message: `Plugin error - Unable to get file name for asset "${asset.name}". Ensure that the source is set and that generate is called first.`
};
}

export function errChunkNotGeneratedForFileName(entry: { name: string }) {
return {
code: Errors.CHUNK_NOT_GENERATED,
message: `Plugin error - Unable to get file name for chunk "${
entry.name
}". Ensure that generate is called first.`
message: `Plugin error - Unable to get file name for chunk "${entry.name}". Ensure that generate is called first.`
};
}

Expand All @@ -85,9 +82,7 @@ export function errAssetReferenceIdNotFoundForSetSource(assetReferenceId: string
export function errAssetSourceAlreadySet(asset: Asset) {
return {
code: Errors.ASSET_SOURCE_ALREADY_SET,
message: `Plugin error - Unable to set the source for asset "${
asset.name
}", source already set.`
message: `Plugin error - Unable to set the source for asset "${asset.name}", source already set.`
};
}

Expand Down Expand Up @@ -121,6 +116,13 @@ export function errChunkReferenceIdNotFoundForFilename(chunkReferenceId: string)
};
}

export function errDeprecation(deprecation: string | RollupWarning) {
return {
code: Errors.DEPRECATED_FEATURE,
...(typeof deprecation === 'string' ? { message: deprecation } : deprecation)
};
}

export function errInvalidAssetName(name: string) {
return {
code: Errors.INVALID_ASSET_NAME,
Expand Down
1 change: 1 addition & 0 deletions src/utils/mergeOptions.ts
Expand Up @@ -231,6 +231,7 @@ function getInputOptions(
preserveModules: getOption('preserveModules'),
preserveSymlinks: getOption('preserveSymlinks'),
shimMissingExports: getOption('shimMissingExports'),
strictDeprecations: getOption('strictDeprecations', false),
treeshake: getObjectOption(config, command, 'treeshake'),
watch: config.watch as any
};
Expand Down

0 comments on commit 1739459

Please sign in to comment.