diff --git a/packages/babel-core/src/config/full.js b/packages/babel-core/src/config/full.js index 88c65ff882be..d38aec4ba698 100644 --- a/packages/babel-core/src/config/full.js +++ b/packages/babel-core/src/config/full.js @@ -18,6 +18,7 @@ import { validatePluginObject } from "./validation/plugins"; import makeAPI from "./helpers/config-api"; import loadPrivatePartialConfig from "./partial"; +import type { ValidatedOptions } from "./validation/options"; type LoadedDescriptor = { value: {}, @@ -278,6 +279,42 @@ const instantiatePlugin = makeWeakCache( }, ); +const validateIfOptionNeedsFilename = ( + options: ValidatedOptions, + descriptor: UnloadedDescriptor, +): void => { + if (options.test || options.include || options.exclude) { + const formattedPresetName = descriptor.name + ? `"${descriptor.name}"` + : "/* your preset */"; + throw new Error( + [ + `Preset ${formattedPresetName} requires a filename to be set when babel is called directly,`, + `\`\`\``, + `babel.transform(code, { filename: 'file.ts', presets: [${formattedPresetName}] });`, + `\`\`\``, + `See https://babeljs.io/docs/en/options#filename for more information.`, + ].join("\n"), + ); + } +}; + +const validatePreset = ( + preset: PresetInstance, + context: ConfigContext, + descriptor: UnloadedDescriptor, +): void => { + if (!context.filename) { + const { options } = preset; + validateIfOptionNeedsFilename(options, descriptor); + if (options.overrides) { + options.overrides.forEach(overrideOptions => + validateIfOptionNeedsFilename(overrideOptions, descriptor), + ); + } + } +}; + /** * Generate a config object that will act as the root of a new nested config. */ @@ -285,10 +322,9 @@ const loadPresetDescriptor = ( descriptor: UnloadedDescriptor, context: ConfigContext, ): ConfigChain | null => { - return buildPresetChain( - instantiatePreset(loadDescriptor(descriptor, context)), - context, - ); + const preset = instantiatePreset(loadDescriptor(descriptor, context)); + validatePreset(preset, context, descriptor); + return buildPresetChain(preset, context); }; const instantiatePreset = makeWeakCache( diff --git a/packages/babel-core/test/config-chain.js b/packages/babel-core/test/config-chain.js index 849da38cfc02..32ee13867568 100644 --- a/packages/babel-core/test/config-chain.js +++ b/packages/babel-core/test/config-chain.js @@ -1049,5 +1049,27 @@ describe("buildConfigChain", function() { loadOptions({ filename, cwd: path.dirname(filename) }), ).toThrow(/Error while parsing JSON - /); }); + + it("should throw when `test` presents but `filename` is not passed", () => { + expect(() => loadOptions({ test: /\.ts$/, plugins: [] })).toThrow( + /Configuration contains string\/RegExp pattern/, + ); + }); + + it("should throw when `preset` requires `filename` but it was not passed", () => { + expect(() => { + loadOptions({ + presets: [require("./fixtures/config-loading/preset4")], + }); + }).toThrow(/Preset \/\* your preset \*\/ requires a filename/); + }); + + it("should throw when `preset.overrides` requires `filename` but it was not passed", () => { + expect(() => { + loadOptions({ + presets: [require("./fixtures/config-loading/preset5")], + }); + }).toThrow(/Preset \/\* your preset \*\/ requires a filename/); + }); }); }); diff --git a/packages/babel-core/test/fixtures/config-loading/preset4.js b/packages/babel-core/test/fixtures/config-loading/preset4.js new file mode 100644 index 000000000000..1698ff05edd1 --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/preset4.js @@ -0,0 +1,6 @@ +module.exports = function() { + return { + test: /\.ts$/, + plugins: [] + } +}; diff --git a/packages/babel-core/test/fixtures/config-loading/preset5.js b/packages/babel-core/test/fixtures/config-loading/preset5.js new file mode 100644 index 000000000000..65ecb47f93c8 --- /dev/null +++ b/packages/babel-core/test/fixtures/config-loading/preset5.js @@ -0,0 +1,8 @@ +module.exports = function() { + return { + overrides: [{ + test: /\.ts$/, + plugins: [] + }] + } +};