Skip to content

Commit

Permalink
Use export mode "auto" by default when preserving modules (#3265)
Browse files Browse the repository at this point in the history
* Treat all modules as entry modules with regard to the export mode when preserving modules

* Test export mode warnings with preserveModules

* Aggregate warnings

* Add __esModule attribute to named export chunks when preserving modules

* Improve coverage
  • Loading branch information
lukastaegert committed Nov 30, 2019
1 parent c547361 commit bdd1e03
Show file tree
Hide file tree
Showing 168 changed files with 875 additions and 216 deletions.
2 changes: 1 addition & 1 deletion LICENSE.md
Expand Up @@ -382,7 +382,7 @@ Repository: sindresorhus/pretty-bytes
## pretty-ms
License: MIT
By: Sindre Sorhus
Repository: git+https://github.com/sindresorhus/pretty-ms.git
Repository: sindresorhus/pretty-ms

---------------------------------------

Expand Down
151 changes: 81 additions & 70 deletions cli/run/batchWarnings.ts
Expand Up @@ -82,13 +82,6 @@ const immediateHandlers: {
stderr(
`Creating a browser bundle that depends on ${detail}. You might need to include https://www.npmjs.com/package/rollup-plugin-node-builtins`
);
},

MIXED_EXPORTS: () => {
title('Mixing named and default exports');
stderr(
`Consumers of your bundle will have to use bundle['default'] to access the default export, which may not be what you want. Use \`output.exports: 'named'\` to disable this warning`
);
}
};

Expand All @@ -106,27 +99,19 @@ const deferredHandlers: {
}
},

UNUSED_EXTERNAL_IMPORT(warnings) {
title('Unused external imports');
for (const warning of warnings) {
stderr(`${warning.names} imported from external module '${warning.source}' but never used`);
}
EMPTY_BUNDLE(warnings) {
title(
`Generated${warnings.length === 1 ? ' an' : ''} empty ${
warnings.length > 1 ? 'chunks' : 'chunk'
}`
);
stderr(warnings.map(warning => warning.chunkName!).join(', '));
},

UNRESOLVED_IMPORT(warnings) {
title('Unresolved dependencies');
info('https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency');

const dependencies = new Map();
for (const warning of warnings) {
if (!dependencies.has(warning.source)) dependencies.set(warning.source, []);
dependencies.get(warning.source).push(warning.importer);
}

for (const dependency of dependencies.keys()) {
const importers = dependencies.get(dependency);
stderr(`${tc.bold(dependency)} (imported by ${importers.join(', ')})`);
}
EVAL(warnings) {
title('Use of eval is strongly discouraged');
info('https://rollupjs.org/guide/en/#avoiding-eval');
showTruncatedWarnings(warnings);
},

MISSING_EXPORT(warnings) {
Expand All @@ -140,21 +125,32 @@ const deferredHandlers: {
}
},

THIS_IS_UNDEFINED(warnings) {
title('`this` has been rewritten to `undefined`');
info('https://rollupjs.org/guide/en/#error-this-is-undefined');
showTruncatedWarnings(warnings);
},

EVAL(warnings) {
title('Use of eval is strongly discouraged');
info('https://rollupjs.org/guide/en/#avoiding-eval');
showTruncatedWarnings(warnings);
MISSING_GLOBAL_NAME(warnings) {
title(`Missing global variable ${warnings.length > 1 ? 'names' : 'name'}`);
stderr(
`Use output.globals to specify browser global variable names corresponding to external modules`
);
for (const warning of warnings) {
stderr(`${tc.bold(warning.source!)} (guessing '${warning.guess}')`);
}
},

NON_EXISTENT_EXPORT(warnings) {
title(`Import of non-existent ${warnings.length > 1 ? 'exports' : 'export'}`);
showTruncatedWarnings(warnings);
MIXED_EXPORTS: (warnings) => {
title('Mixing named and default exports');
info(`https://rollupjs.org/guide/en/#output-exports`);
stderr(
tc.bold('The following entry modules are using named and default exports together:')
);
const displayedWarnings = warnings.length > 5 ? warnings.slice(0, 3) : warnings;
for (const warning of displayedWarnings) {
stderr(relativeId(warning.id!));
}
if (displayedWarnings.length < warnings.length) {
stderr(`...and ${warnings.length - displayedWarnings.length} other entry modules`);
}
stderr(
`\nConsumers of your bundle will have to use chunk['default'] to access their default export, which may not be what you want. Use \`output.exports: 'named'\` to disable this warning`
);
},

NAMESPACE_CONFLICT(warnings) {
Expand All @@ -170,30 +166,9 @@ const deferredHandlers: {
}
},

MISSING_GLOBAL_NAME(warnings) {
title(`Missing global variable ${warnings.length > 1 ? 'names' : 'name'}`);
stderr(
`Use output.globals to specify browser global variable names corresponding to external modules`
);
for (const warning of warnings) {
stderr(`${tc.bold(warning.source!)} (guessing '${warning.guess}')`);
}
},

SOURCEMAP_BROKEN(warnings) {
title(`Broken sourcemap`);
info('https://rollupjs.org/guide/en/#warning-sourcemap-is-likely-to-be-incorrect');

const plugins = Array.from(new Set(warnings.map(w => w.plugin).filter(Boolean)));
const detail =
plugins.length > 1
? ` (such as ${plugins
.slice(0, -1)
.map(p => `'${p}'`)
.join(', ')} and '${plugins.slice(-1)}')`
: ` (such as '${plugins[0]}')`;

stderr(`Plugins that transform code${detail} should generate accompanying sourcemaps`);
NON_EXISTENT_EXPORT(warnings) {
title(`Import of non-existent ${warnings.length > 1 ? 'exports' : 'export'}`);
showTruncatedWarnings(warnings);
},

PLUGIN_WARNING(warnings) {
Expand Down Expand Up @@ -222,13 +197,49 @@ const deferredHandlers: {
}
},

EMPTY_BUNDLE(warnings) {
title(
`Generated${warnings.length === 1 ? ' an' : ''} empty ${
warnings.length > 1 ? 'chunks' : 'chunk'
}`
);
stderr(warnings.map(warning => warning.chunkName!).join(', '));
SOURCEMAP_BROKEN(warnings) {
title(`Broken sourcemap`);
info('https://rollupjs.org/guide/en/#warning-sourcemap-is-likely-to-be-incorrect');

const plugins = Array.from(new Set(warnings.map(w => w.plugin).filter(Boolean)));
const detail =
plugins.length > 1
? ` (such as ${plugins
.slice(0, -1)
.map(p => `'${p}'`)
.join(', ')} and '${plugins.slice(-1)}')`
: ` (such as '${plugins[0]}')`;

stderr(`Plugins that transform code${detail} should generate accompanying sourcemaps`);
},

THIS_IS_UNDEFINED(warnings) {
title('`this` has been rewritten to `undefined`');
info('https://rollupjs.org/guide/en/#error-this-is-undefined');
showTruncatedWarnings(warnings);
},

UNRESOLVED_IMPORT(warnings) {
title('Unresolved dependencies');
info('https://rollupjs.org/guide/en/#warning-treating-module-as-external-dependency');

const dependencies = new Map();
for (const warning of warnings) {
if (!dependencies.has(warning.source)) dependencies.set(warning.source, []);
dependencies.get(warning.source).push(warning.importer);
}

for (const dependency of dependencies.keys()) {
const importers = dependencies.get(dependency);
stderr(`${tc.bold(dependency)} (imported by ${importers.join(', ')})`);
}
},

UNUSED_EXTERNAL_IMPORT(warnings) {
title('Unused external imports');
for (const warning of warnings) {
stderr(`${warning.names} imported from external module '${warning.source}' but never used`);
}
}
};

Expand Down
16 changes: 16 additions & 0 deletions docs/01-command-line-reference.md
Expand Up @@ -187,6 +187,22 @@ export default commandLineArgs => {

If you now run `rollup --config --configDebug`, the debug configuration will be used.

By default, command line arguments will always override the respective values exported from a config file. If you want to change this behaviour, you can make Rollup ignore command line arguments by deleting them from the `commandLineArgs` object:

```javascript
// rollup.config.js
export default commandLineArgs => {
const inputBase = commandLineArgs.input || 'main.js';

// this will make Rollup ignore the CLI argument
delete commandLineArgs.input;
return {
input: 'src/entries/' + inputBase,
output: {...}
}
}
```


### Command line flags

Expand Down
42 changes: 42 additions & 0 deletions docs/999-big-list-of-options.md
Expand Up @@ -572,6 +572,48 @@ 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/#outputdir) 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.

Note that when transforming to `cjs` or `amd` format, each file will by default be treated as an entry point with [`output.exports`](guide/en/#outputexports) set to `auto`. This means that e.g. for `cjs`, a file that only contains a default export will be rendered as

```js
// input main.js
export default 42;

// output main.js
'use strict';

var main = 42;

module.exports = main;
```

assigning the value directly to `module.exports`. If someone imports this file, they will get access to the default export via

```js
const main = require('./main.js');
console.log(main); // 42
```

As with regular entry points, files that mix default and named exports will produce warnings. You can avoid the warnings by forcing all files to use named export mode via `output.exports: "named"`. In that case, the default export needs to be accessed via the `.default` property of the export:

```js
// input main.js
export default 42;

// output main.js
'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var main = 42;

exports.default = main;

// consuming file
const main = require('./main.js');
console.log(main.default); // 42
```


#### strictDeprecations
Type: `boolean`<br>
CLI: `--strictDeprecations`/`--no-strictDeprecations`<br>
Expand Down
6 changes: 4 additions & 2 deletions src/Chunk.ts
Expand Up @@ -619,7 +619,7 @@ export default class Chunk {
this.renderedSourceLength = undefined as any;
this.renderedHash = undefined as any;

if (this.getExportNames().length === 0 && this.getImportIds().length === 0 && this.isEmpty) {
if (this.isEmpty && this.getExportNames().length === 0 && this.dependencies.length === 0) {
const chunkName = this.getChunkName();
this.graph.warn({
chunkName,
Expand Down Expand Up @@ -708,7 +708,9 @@ export default class Chunk {
hasExports,
indentString: this.indentString,
intro: addons.intro as string,
isEntryModuleFacade: this.facadeModule !== null && this.facadeModule.isEntryPoint,
isEntryModuleFacade:
this.graph.preserveModules ||
(this.facadeModule !== null && this.facadeModule.isEntryPoint),
namedExportsMode: this.exportMode !== 'default',
outro: addons.outro as string,
usesTopLevelAwait,
Expand Down
1 change: 1 addition & 0 deletions src/finalisers/shared/getExportBlock.ts
Expand Up @@ -23,6 +23,7 @@ export default function getExportBlock(
dep.namedExportsMode && expt.imported !== '*' && expt.imported !== 'default'
? `${dep.name}.${expt.imported}`
: dep.name;
break;
}
}
}
Expand Down
18 changes: 14 additions & 4 deletions src/rollup/index.ts
Expand Up @@ -5,7 +5,12 @@ import Graph from '../Graph';
import { createAddons } from '../utils/addons';
import { assignChunkIds } from '../utils/assignChunkIds';
import commondir from '../utils/commondir';
import { errCannotEmitFromOptionsHook, errDeprecation, error } from '../utils/error';
import {
errCannotEmitFromOptionsHook,
errDeprecation,
errInvalidExportOptionValue,
error
} from '../utils/error';
import { writeFile } from '../utils/fs';
import getExportMode from '../utils/getExportMode';
import mergeOptions, { GenericConfigObject } from '../utils/mergeOptions';
Expand Down Expand Up @@ -45,6 +50,10 @@ function checkOutputOptions(options: OutputOptions) {
url: `https://rollupjs.org/guide/en/#output-format`
});
}

if (options.exports && !['default', 'named', 'none', 'auto'].includes(options.exports)) {
error(errInvalidExportOptionValue(options.exports));
}
}

function getAbsoluteEntryModulePaths(chunks: Chunk[]): string[] {
Expand Down Expand Up @@ -259,8 +268,8 @@ export default async function rollup(rawInputOptions: GenericConfigObject): Prom
const addons = await createAddons(outputOptions, outputPluginDriver);
for (const chunk of chunks) {
if (!inputOptions.preserveModules) chunk.generateInternalExports(outputOptions);
if (chunk.facadeModule && chunk.facadeModule.isEntryPoint)
chunk.exportMode = getExportMode(chunk, outputOptions);
if (inputOptions.preserveModules || (chunk.facadeModule && chunk.facadeModule.isEntryPoint))
chunk.exportMode = getExportMode(chunk, outputOptions, chunk.facadeModule!.id);
}
for (const chunk of chunks) {
chunk.preRender(outputOptions, inputBase);
Expand Down Expand Up @@ -403,7 +412,8 @@ function createOutput(outputBundle: Record<string, OutputChunk | OutputAsset | {
.map(fileName => outputBundle[fileName])
.filter(outputFile => Object.keys(outputFile).length > 0) as (
| OutputChunk
| OutputAsset)[]).sort((outputFileA, outputFileB) => {
| OutputAsset
)[]).sort((outputFileA, outputFileB) => {
const fileTypeA = getSortingFileType(outputFileA);
const fileTypeB = getSortingFileType(outputFileB);
if (fileTypeA === fileTypeB) return 0;
Expand Down

0 comments on commit bdd1e03

Please sign in to comment.