Skip to content

Commit

Permalink
Strip rollup-plugin prefix to find named plugin exports, throw when e…
Browse files Browse the repository at this point in the history
…xport cannot be found (#3647)

* Strip rollup-plugin prefix to find named plugin exports, throw when export cannot be found

* Also ignore anything after the first "."

* Update dependencies

* Cover more cases
  • Loading branch information
lukastaegert committed Jun 26, 2020
1 parent 981b11c commit b8013f8
Show file tree
Hide file tree
Showing 19 changed files with 118 additions and 15 deletions.
26 changes: 20 additions & 6 deletions cli/run/commandPlugins.ts
Expand Up @@ -3,10 +3,7 @@ import { InputOptions } from '../../src/rollup/types';
import { stdinPlugin } from './stdin';
import { waitForInputPlugin } from './waitForInput';

export function addCommandPluginsToInputOptions(
inputOptions: InputOptions,
command: any
) {
export function addCommandPluginsToInputOptions(inputOptions: InputOptions, command: any) {
if (command.stdin !== false) {
inputOptions.plugins!.push(stdinPlugin());
}
Expand Down Expand Up @@ -63,16 +60,33 @@ function loadAndRegisterPlugin(inputOptions: InputOptions, pluginText: string) {
if (pluginText[0] == '.') pluginText = path.resolve(pluginText);
plugin = require(pluginText);
} catch (ex) {
throw new Error(`Cannot load plugin "${pluginText}"`);
throw new Error(`Cannot load plugin "${pluginText}": ${ex.message}.`);
}
}
}
// some plugins do not use `module.exports` for their entry point,
// in which case we try the named default export and the plugin name
if (typeof plugin === 'object') {
plugin = plugin.default || plugin[pluginText]
plugin = plugin.default || plugin[getCamelizedPluginBaseName(pluginText)];
}
if (!plugin) {
throw new Error(
`Cannot find entry for plugin "${pluginText}". The plugin needs to export a function either as "default" or "${getCamelizedPluginBaseName(
pluginText
)}" for Rollup to recognize it.`
);
}
inputOptions.plugins!.push(
typeof plugin === 'function' ? plugin.call(plugin, pluginArg) : plugin
);
}

function getCamelizedPluginBaseName(pluginText: string): string {
return (pluginText.match(/(@rollup\/plugin-|rollup-plugin-)(.+)$/)?.[2] || pluginText)
.split(/[\\/]/)
.slice(-1)[0]
.split('.')[0]
.split('-')
.map((part, index) => (index === 0 || !part ? part : part[0].toUpperCase() + part.slice(1)))
.join('');
}
4 changes: 2 additions & 2 deletions docs/01-command-line-reference.md
Expand Up @@ -342,7 +342,7 @@ Use the specified plugin. There are several ways to specify plugins here:
rollup -i input.js -f es -p ./my-plugin.js
```

The file should export a plugin object or a function returning such an object.
The file should export a function returning a plugin object.
- Via the name of a plugin that is installed in a local or global `node_modules` folder:

```
Expand All @@ -367,7 +367,7 @@ If you want to load more than one plugin, you can repeat the option or supply a
rollup -i input.js -f es -p node-resolve -p commonjs,json
```

By default, plugins that export functions will be called with no argument to create the plugin. You can however pass a custom argument as well:
By default, plugin functions be called with no argument to create the plugin. You can however pass a custom argument as well:

```
rollup -i input.js -f es -p 'terser={output: {beautify: true, indent_level: 2}}'
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 @@ -110,7 +110,7 @@
"pretty-ms": "^7.0.0",
"require-relative": "^0.8.7",
"requirejs": "^2.3.6",
"rollup": "^2.14.0",
"rollup": "^2.18.0",
"rollup-plugin-license": "^2.1.0",
"rollup-plugin-string": "^3.0.0",
"rollup-plugin-terser": "^6.1.0",
Expand Down
5 changes: 2 additions & 3 deletions rollup.config.js
Expand Up @@ -98,9 +98,7 @@ export default command => {
'util'
],
treeshake,
manualChunks: { rollup: ['src/node-entry.ts'] },
// TODO set this to `true` again once manualChunks has been moved to the output
strictDeprecations: false,
strictDeprecations: true,
output: {
banner,
chunkFileNames: 'shared/[name].js',
Expand All @@ -110,6 +108,7 @@ export default command => {
format: 'cjs',
freeze: false,
interop: false,
manualChunks: { rollup: ['src/node-entry.ts'] },
sourcemap: true
}
};
Expand Down
17 changes: 17 additions & 0 deletions test/cli/samples/plugin/export-not-found/_config.js
@@ -0,0 +1,17 @@
const { assertIncludes } = require('../../../../utils.js');
const path = require('path');

module.exports = {
description: 'Throws when the plugin export cannot be found',
skipIfWindows: true,
command: `echo 'console.log("ignored");' | rollup -p "./my-missing-plugin"`,
error(err) {
assertIncludes(
err.message,
`[!] Error: Cannot find entry for plugin "${path.join(
__dirname,
'my-missing-plugin'
)}". The plugin needs to export a function either as "default" or "myMissingPlugin" for Rollup to recognize it.`
);
}
};
1 change: 1 addition & 0 deletions test/cli/samples/plugin/export-not-found/_expected.js
@@ -0,0 +1 @@
console.log('transformed');
Empty file.
5 changes: 5 additions & 0 deletions test/cli/samples/plugin/named-export/_config.js
@@ -0,0 +1,5 @@
module.exports = {
description: 'supports stripping "rollup-config" prefix to find named plugin export',
skipIfWindows: true,
command: `rollup -c -p rollup-plugin-terser`
};
1 change: 1 addition & 0 deletions test/cli/samples/plugin/named-export/_expected/cjs.js
@@ -0,0 +1 @@
"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=function(t){this.x=t};t.prototype.output=function(){console.log(this.x)},new t(123).output(),exports.Bar=t;
1 change: 1 addition & 0 deletions test/cli/samples/plugin/named-export/_expected/es.js
@@ -0,0 +1 @@
var t=function(t){this.x=t};t.prototype.output=function(){console.log(this.x)},new t(123).output();export{t as Bar};
12 changes: 12 additions & 0 deletions test/cli/samples/plugin/named-export/main.js
@@ -0,0 +1,12 @@
class Foo {
constructor(x) {
this.x = x;
}
output() {
console.log(this.x);
}
}

const foo = new Foo(123);
foo.output();
export { Foo as Bar };
18 changes: 18 additions & 0 deletions test/cli/samples/plugin/named-export/rollup.config.js
@@ -0,0 +1,18 @@
const buble = require('@rollup/plugin-buble');

export default {
input: 'main.js',
plugins: [
buble()
],
output: [
{
file: '_actual/cjs.js',
format: 'cjs'
},
{
file: '_actual/es.js',
format: 'esm'
}
]
};
10 changes: 10 additions & 0 deletions test/cli/samples/plugin/relative-camelized/_config.js
@@ -0,0 +1,10 @@
module.exports = {
description: 'handles plugins where the export name is the camelized file name',
skipIfWindows: true,
command:
`echo 'console.log("initial");' | rollup ` +
'-p "./plugins/my-super-plugin1.js" ' +
'-p "./plugins/rollup-plugin-my-super-plugin2.js" ' +
'-p "./plugins/rollup-plugin-" ' +
'-p "./plugins/@rollup/plugin-supreme"'
};
5 changes: 5 additions & 0 deletions test/cli/samples/plugin/relative-camelized/_expected.js
@@ -0,0 +1,5 @@
console.log("initial");
console.log("plugin1");
console.log("plugin2");
console.log("plugin3");
console.log("plugin4");
@@ -0,0 +1,5 @@
exports.supreme = () => ({
transform(code) {
return `${code}console.log("plugin4");\n`;
}
});
@@ -0,0 +1,5 @@
exports.mySuperPlugin1 = () => ({
transform(code) {
return `${code}console.log("plugin1");\n`;
}
});
@@ -0,0 +1,5 @@
exports.rollupPlugin = () => ({
transform(code) {
return `${code}console.log("plugin3");\n`;
}
});
@@ -0,0 +1,5 @@
exports.mySuperPlugin2 = () => ({
transform(code) {
return `${code}console.log("plugin2");\n`;
}
});

0 comments on commit b8013f8

Please sign in to comment.