babel/packages/babel-core/test/option-manager.js
David J. Hamilton fa5a40c8d5 assertNoDuplicates throw with more context (#10419)
When users see errors like

```
Duplicate plugin/preset detected.
If you'd like to use two separate instances of a plugin,
they need separate names, e.g.

  plugins: [
    ['some-plugin', {}],
    ['some-plugin', {}, 'some unique name'],
  ]
```

It can be difficult to determine the source of the conflict, especially
in a larger build system.

This commit outputs what is known about the plugins that actually
conflict, which can be helpful for users to determine the root cause of
the conflict.

Partially addresses #9778
2019-10-02 16:45:03 +02:00

216 lines
6.2 KiB
JavaScript

import { loadOptions as loadOptionsOrig } from "../lib";
import path from "path";
function loadOptions(opts) {
return loadOptionsOrig({
cwd: __dirname,
...opts,
});
}
describe("option-manager", () => {
it("throws for babel 5 plugin", () => {
return expect(() => {
loadOptions({
plugins: [({ Plugin }) => new Plugin("object-assign", {})],
});
}).toThrow(/Babel 5 plugin is being run with an unsupported Babel/);
});
describe("config plugin/preset flattening and overriding", () => {
function makePlugin() {
const calls = [];
const plugin = (api, opts) => {
calls.push(opts);
return {};
};
return { plugin, calls };
}
it("should throw if a plugin is repeated, with information about the repeated plugin", () => {
const { calls, plugin } = makePlugin("my-plugin");
expect(() => {
loadOptions({
plugins: [
[plugin, undefined, "my-plugin"],
[plugin, undefined, "my-plugin"],
],
});
}).toThrow(
/Duplicate plugin\/preset detected.*Duplicates detected are.*my-plugin.*my-plugin/ms,
);
expect(calls).toEqual([]);
});
it("throws for null options", () => {
const { calls, plugin } = makePlugin();
expect(() => {
loadOptions({
plugins: [[plugin, null]],
}).toThrow(/.plugins[0][1] must be an object, false, or undefined/);
});
expect(calls).toEqual([]);
});
it("should not throw if a repeated plugin has a different name", () => {
const { calls: calls1, plugin: plugin1 } = makePlugin();
const { calls: calls2, plugin: plugin2 } = makePlugin();
loadOptions({
plugins: [[plugin1, { arg: 1 }], [plugin2, { arg: 2 }, "some-name"]],
});
expect(calls1).toEqual([{ arg: 1 }]);
expect(calls2).toEqual([{ arg: 2 }]);
});
it("should merge .env[] plugins with parent presets", () => {
const { calls: calls1, plugin: plugin1 } = makePlugin();
const { calls: calls2, plugin: plugin2 } = makePlugin();
loadOptions({
envName: "test",
plugins: [[plugin1, { arg: 1 }]],
env: {
test: {
plugins: [[plugin1, { arg: 3 }], [plugin2, { arg: 2 }]],
},
},
});
expect(calls1).toEqual([{ arg: 3 }]);
expect(calls2).toEqual([{ arg: 2 }]);
});
it("should throw if a preset is repeated", () => {
const { calls, plugin: preset } = makePlugin();
expect(() => {
loadOptions({
presets: [preset, preset],
}).toThrow(/Duplicate plugin\/preset detected/);
});
expect(calls).toEqual([]);
});
it("should not throw if a repeated preset has a different name", () => {
const { calls: calls1, plugin: preset1 } = makePlugin();
const { calls: calls2, plugin: preset2 } = makePlugin();
loadOptions({
presets: [[preset1, { arg: 1 }], [preset2, { arg: 2 }, "some-name"]],
});
expect(calls1).toEqual([{ arg: 1 }]);
expect(calls2).toEqual([{ arg: 2 }]);
});
it("should merge .env[] presets with parent presets", () => {
const { calls: calls1, plugin: preset1 } = makePlugin();
const { calls: calls2, plugin: preset2 } = makePlugin();
loadOptions({
envName: "test",
presets: [[preset1, { arg: 1 }]],
env: {
test: {
presets: [[preset1, { arg: 3 }], [preset2, { arg: 2 }]],
},
},
});
expect(calls1).toEqual([{ arg: 3 }]);
expect(calls2).toEqual([{ arg: 2 }]);
});
it("should not merge .env[] presets with parent presets when passPerPreset", () => {
const { calls: calls1, plugin: preset1 } = makePlugin();
const { calls: calls2, plugin: preset2 } = makePlugin();
loadOptions({
envName: "test",
passPerPreset: true,
presets: [[preset1, { arg: 1 }]],
env: {
test: {
presets: [[preset1, { arg: 3 }], [preset2, { arg: 2 }]],
},
},
});
expect(calls1).toEqual([{ arg: 1 }, { arg: 3 }]);
expect(calls2).toEqual([{ arg: 2 }]);
});
});
describe("mergeOptions", () => {
it("throws for removed babel 5 options", () => {
return expect(() => {
loadOptions({
randomOption: true,
});
}).toThrow(/Unknown option: .randomOption/);
});
it("throws for removed babel 5 options", () => {
return expect(() => {
loadOptions({
auxiliaryComment: true,
blacklist: true,
});
}).toThrow(
// eslint-disable-next-line max-len
/Using removed Babel 5 option: .auxiliaryComment - Use `auxiliaryCommentBefore` or `auxiliaryCommentAfter`/,
);
});
it("throws for resolved but erroring preset", () => {
return expect(() => {
loadOptions({
presets: [
path.join(__dirname, "fixtures/option-manager/not-a-preset"),
],
});
}).toThrow(
/While processing: .*option-manager(?:\/|\\\\)not-a-preset\.js/,
);
});
});
describe("presets", function() {
function presetTest(name) {
it(name, function() {
const options = loadOptions({
presets: [
path.join(__dirname, "fixtures/option-manager/presets", name),
],
});
expect(Array.isArray(options.plugins)).toBe(true);
expect(options.plugins).toHaveLength(1);
expect(options.presets).toHaveLength(0);
});
}
function presetThrowsTest(name, msg) {
it(name, function() {
expect(() =>
loadOptions({
presets: [
path.join(__dirname, "fixtures/option-manager/presets", name),
],
}),
).toThrow(msg);
});
}
presetTest("es5_function");
presetTest("es5_object");
presetTest("es2015_default_function");
presetTest("es2015_default_object");
presetThrowsTest(
"es2015_named",
/Must export a default export when using ES6 modules/,
);
presetThrowsTest("es2015_invalid", /Unsupported format: string/);
presetThrowsTest("es5_invalid", /Unsupported format: string/);
});
});