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

Support native ESM configs in Node 13, support untranspiled configs #3445

Merged
merged 14 commits into from Mar 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 18 additions & 2 deletions .circleci/config.yml
Expand Up @@ -31,16 +31,26 @@ jobs:
- run:
name: Running tests
command: npm run ci:test
node-v12-latest:
docker:
- image: rollupcabal/circleci-node-v12:latest
steps:
- checkout
- restore_cache:
key: dependency-cache-{{ checksum "package-lock.json" }}
- run:
name: Running tests
command: npm run ci:test:only
- store_artifacts:
name: Storing browser build for REPL
path: /home/circleci/project/dist/rollup.browser.js
destination: rollup.browser.js
- run:
name: Post REPL comment
command: ./scripts/post-comment.js
node-v12-latest:
node-v13-latest:
docker:
- image: rollupcabal/circleci-node-v12:latest
- image: lukastaegert/circleci-node-v13:latest
steps:
- checkout
- restore_cache:
Expand Down Expand Up @@ -70,3 +80,9 @@ workflows:
filters:
tags:
only: /.*/
- node-v13-latest:
requires:
- analysis
filters:
tags:
only: /.*/
2 changes: 1 addition & 1 deletion .github/workflows/node-windows.yml
Expand Up @@ -8,7 +8,7 @@ jobs:

strategy:
matrix:
node: ['12', '10']
node: ['13', '12', '10']

name: ${{ matrix.node }} (Windows)
steps:
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -13,7 +13,7 @@
* Do not strip `.js` extensions from AMD imports when the import is a user-supplied replacement for a non-resolvable dynamic import target (#3453)

### Pull Requests
* [#3449](https://github.com/rollup/rollup/pull/3449): Avoid the assumption of Buffer in browser envs (@lukastaegert)
* [#3449](https://github.com/rollup/rollup/pull/3449): Add hook to rewrite dynamic import expressions (@lukastaegert)
* [#3452](https://github.com/rollup/rollup/pull/3452): Avoid the assumption of Buffer in browser envs (@JoviDeCroock)
* [#3453](https://github.com/rollup/rollup/pull/3453): fix types since watch accepts single or array config (@lukeed)
* [#3456](https://github.com/rollup/rollup/pull/3456): fix SystemJS url in tutorial (@guybedford)
Expand Down
7 changes: 7 additions & 0 deletions build-plugins/conditional-fsevents-import.js
Expand Up @@ -4,10 +4,12 @@ const FSEVENTS_REQUIRE = "require('fsevents')";
const REPLACEMENT = "require('../../../src/watch/fsevents-importer').getFsEvents()";

export default function conditionalFsEventsImport() {
let transformed = false;
return {
name: 'conditional-fs-events-import',
transform(code, id) {
if (id.endsWith('fsevents-handler.js')) {
transformed = true;
const requireStatementPos = code.indexOf(FSEVENTS_REQUIRE);
if (requireStatementPos < 0) {
throw new Error(`Could not find expected fsevents import "${FSEVENTS_REQUIRE}"`);
Expand All @@ -20,6 +22,11 @@ export default function conditionalFsEventsImport() {
);
return { code: magicString.toString(), map: magicString.generateMap({ hires: true }) };
}
},
buildEnd() {
if (!transformed) {
throw new Error('Could not find "fsevents-handler.js", was the file renamed?');
}
}
};
}
19 changes: 19 additions & 0 deletions build-plugins/esm-dynamic-import.js
@@ -0,0 +1,19 @@
export default function addBinShebangAndEsmImport() {
let importFound = false;
return {
name: 'esm-dynamic-import',
renderDynamicImport({ moduleId }) {
importFound = true;
if (moduleId.endsWith('loadConfigFile.ts')) {
return { left: 'import(', right: ')' };
}
},
generateBundle() {
if (!importFound) {
throw new Error(
'Could not find dynamic import in "loadConfigFile.ts", was the file renamed?'
);
}
}
};
}
23 changes: 0 additions & 23 deletions build-plugins/fix-acorn-esm-import.js

This file was deleted.

94 changes: 46 additions & 48 deletions cli/run/build.ts
@@ -1,23 +1,22 @@
import color from 'colorette';
import ms from 'pretty-ms';
import * as rollup from '../../src/node-entry';
import { InputOptions, OutputOptions, RollupBuild } from '../../src/rollup/types';
import { MergedRollupOptions } from '../../src/rollup/types';
import relativeId from '../../src/utils/relativeId';
import { handleError, stderr } from '../logging';
import SOURCEMAPPING_URL from '../sourceMappingUrl';
import { BatchWarnings } from './batchWarnings';
import { printTimings } from './timings';

export default function build(
inputOptions: InputOptions,
outputOptions: OutputOptions[],
export default async function build(
inputOptions: MergedRollupOptions,
warnings: BatchWarnings,
silent = false
): Promise<unknown> {
const outputOptions = inputOptions.output;
const useStdout = !outputOptions[0].file && !outputOptions[0].dir;

const start = Date.now();
const files = useStdout ? ['stdout'] : outputOptions.map(t => relativeId(t.file || t.dir!));
const files = useStdout ? ['stdout'] : outputOptions.map((t) => relativeId(t.file || t.dir!));
if (!silent) {
let inputFiles: string | undefined;
if (typeof inputOptions.input === 'string') {
Expand All @@ -26,54 +25,53 @@ export default function build(
inputFiles = inputOptions.input.join(', ');
} else if (typeof inputOptions.input === 'object' && inputOptions.input !== null) {
inputFiles = Object.keys(inputOptions.input)
.map(name => (inputOptions.input as Record<string, string>)[name])
.map((name) => (inputOptions.input as Record<string, string>)[name])
.join(', ');
}
stderr(color.cyan(`\n${color.bold(inputFiles!)} → ${color.bold(files.join(', '))}...`));
}

return rollup
.rollup(inputOptions as any)
.then((bundle: RollupBuild) => {
if (useStdout) {
const output = outputOptions[0];
if (output.sourcemap && output.sourcemap !== 'inline') {
handleError({
code: 'MISSING_OUTPUT_OPTION',
message: 'You must specify a --file (-o) option when creating a file with a sourcemap'
});
}

return bundle.generate(output).then(({ output: outputs }) => {
for (const file of outputs) {
let source: string | Uint8Array;
if (file.type === 'asset') {
source = file.source;
} else {
source = file.code;
if (output.sourcemap === 'inline') {
source += `\n//# ${SOURCEMAPPING_URL}=${file.map!.toUrl()}\n`;
}
}
if (outputs.length > 1)
process.stdout.write('\n' + color.cyan(color.bold('//→ ' + file.fileName + ':')) + '\n');
process.stdout.write(source);
}
return null;
});
}
const bundle = await rollup.rollup(inputOptions as any);
if (useStdout) {
const output = outputOptions[0];
if (output.sourcemap && output.sourcemap !== 'inline') {
handleError({
code: 'ONLY_INLINE_SOURCEMAPS',
message: 'Only inline sourcemaps are supported when bundling to stdout.',
});
}

return Promise.all(outputOptions.map(output => bundle.write(output))).then(() => bundle);
})
.then((bundle: RollupBuild | null) => {
if (!silent) {
warnings.flush();
stderr(
color.green(`created ${color.bold(files.join(', '))} in ${color.bold(ms(Date.now() - start))}`)
);
if (bundle && bundle.getTimings) {
printTimings(bundle.getTimings());
const { output: outputs } = await bundle.generate(output);
for (const file of outputs) {
let source: string | Uint8Array;
if (file.type === 'asset') {
source = file.source;
} else {
source = file.code;
if (output.sourcemap === 'inline') {
source += `\n//# ${SOURCEMAPPING_URL}=${file.map!.toUrl()}\n`;
}
}
});
if (outputs.length > 1)
process.stdout.write(`\n${color.cyan(color.bold(`//→ ${file.fileName}:`))}\n`);
process.stdout.write(source);
}
if (!silent) {
warnings.flush();
}
return;
}

await Promise.all(outputOptions.map(bundle.write));
if (!silent) {
warnings.flush();
stderr(
color.green(
`created ${color.bold(files.join(', '))} in ${color.bold(ms(Date.now() - start))}`
)
);
if (bundle && bundle.getTimings) {
printTimings(bundle.getTimings());
}
}
}
74 changes: 74 additions & 0 deletions cli/run/commandPlugins.ts
@@ -0,0 +1,74 @@
import * as path from 'path';
import { InputOptions } from '../../src/rollup/types';
import { stdinPlugin } from './stdin';

export function addCommandPluginsToInputOptions(
inputOptions: InputOptions,
command: any
) {
if (command.stdin !== false) {
inputOptions.plugins!.push(stdinPlugin());
}
const commandPlugin = command.plugin;
if (commandPlugin) {
const plugins = Array.isArray(commandPlugin) ? commandPlugin : [commandPlugin];
for (const plugin of plugins) {
if (/[={}]/.test(plugin)) {
// -p plugin=value
// -p "{transform(c,i){...}}"
loadAndRegisterPlugin(inputOptions, plugin);
} else {
// split out plugins joined by commas
// -p node-resolve,commonjs,buble
plugin.split(',').forEach((plugin: string) => loadAndRegisterPlugin(inputOptions, plugin));
}
}
}
}

function loadAndRegisterPlugin(inputOptions: InputOptions, pluginText: string) {
let plugin: any = null;
let pluginArg: any = undefined;
if (pluginText[0] === '{') {
// -p "{transform(c,i){...}}"
plugin = new Function('return ' + pluginText);
} else {
const match = pluginText.match(/^([@.\/\\\w|^{}|-]+)(=(.*))?$/);
if (match) {
// -p plugin
// -p plugin=arg
pluginText = match[1];
pluginArg = new Function('return ' + match[3])();
} else {
throw new Error(`Invalid --plugin argument format: ${JSON.stringify(pluginText)}`);
}
if (!/^\.|^rollup-plugin-|[@\/\\]/.test(pluginText)) {
// Try using plugin prefix variations first if applicable.
// Prefix order is significant - left has higher precedence.
for (const prefix of ['@rollup/plugin-', 'rollup-plugin-']) {
try {
plugin = require(prefix + pluginText);
break;
} catch (ex) {
// if this does not work, we try requiring the actual name below
}
}
}
if (!plugin) {
try {
if (pluginText[0] == '.') pluginText = path.resolve(pluginText);
plugin = require(pluginText);
} catch (ex) {
throw new Error(`Cannot load plugin "${pluginText}"`);
}
}
}
if (typeof plugin === 'object' && pluginText in plugin) {
// some plugins do not use `export default` for their entry point.
// attempt to use the plugin name as the named import name.
plugin = plugin[pluginText];
}
inputOptions.plugins!.push(
typeof plugin === 'function' ? plugin.call(plugin, pluginArg) : plugin
);
}
41 changes: 41 additions & 0 deletions cli/run/getConfigPath.ts
@@ -0,0 +1,41 @@
import { readdirSync, realpathSync } from 'fs';
import * as path from 'path';
import relative from 'require-relative';
import { handleError } from '../logging';

const DEFAULT_CONFIG_BASE = 'rollup.config';

export function getConfigPath(commandConfig: any): string {
const cwd = process.cwd();
if (commandConfig === true) {
return path.join(cwd, findConfigFileNameInCwd());
}
if (commandConfig.slice(0, 5) === 'node:') {
const pkgName = commandConfig.slice(5);
try {
return relative.resolve(`rollup-config-${pkgName}`, cwd);
} catch (err) {
try {
return relative.resolve(pkgName, cwd);
} catch (err) {
if (err.code === 'MODULE_NOT_FOUND') {
handleError({
code: 'MISSING_EXTERNAL_CONFIG',
message: `Could not resolve config file "${commandConfig}"`
});
}
throw err;
}
}
}
return realpathSync(commandConfig);
}

function findConfigFileNameInCwd(): string {
const filesInWorkingDir = new Set(readdirSync(process.cwd()));
for (const extension of ['mjs', 'cjs']) {
const fileName = `${DEFAULT_CONFIG_BASE}.${extension}`;
if (filesInWorkingDir.has(fileName)) return fileName;
}
return `${DEFAULT_CONFIG_BASE}.js`;
}