From f316eb1843913422c812eb6dc57659c7879e8e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Mon, 19 Oct 2020 19:20:58 +0200 Subject: [PATCH 1/4] Add @babel/core support for the new "assumptions" option --- .../babel-core/src/config/cache-contexts.js | 32 +++ packages/babel-core/src/config/full.js | 128 ++++++------ .../src/config/helpers/config-api.js | 35 ++-- packages/babel-core/src/config/partial.js | 7 +- packages/babel-core/src/config/util.js | 13 +- .../config/validation/option-assertions.js | 25 +++ .../src/config/validation/options.js | 28 +++ packages/babel-core/test/assumptions.js | 183 ++++++++++++++++++ packages/babel-core/test/config-chain.js | 1 + .../babel-helper-plugin-utils/src/index.js | 5 + 10 files changed, 374 insertions(+), 83 deletions(-) create mode 100644 packages/babel-core/src/config/cache-contexts.js create mode 100644 packages/babel-core/test/assumptions.js diff --git a/packages/babel-core/src/config/cache-contexts.js b/packages/babel-core/src/config/cache-contexts.js new file mode 100644 index 000000000000..969362c0c42c --- /dev/null +++ b/packages/babel-core/src/config/cache-contexts.js @@ -0,0 +1,32 @@ +// @flow + +import type { Targets } from "@babel/helper-compilation-targets"; + +import type { ConfigContext } from "./config-chain"; +import type { CallerMetadata } from "./validation/options"; + +export type { ConfigContext as FullConfig }; + +export type FullPreset = { + ...ConfigContext, + targets: Targets, +}; +export type FullPlugin = { + ...FullPreset, + assumptions: { [name: string]: boolean }, +}; + +// Context not including filename since it is used in places that cannot +// process 'ignore'/'only' and other filename-based logic. +export type SimpleConfig = { + envName: string, + caller: CallerMetadata | void, +}; +export type SimplePreset = { + ...SimpleConfig, + targets: Targets, +}; +export type SimplePlugin = { + ...SimplePreset, + assumptions: { [name: string]: boolean }, +}; diff --git a/packages/babel-core/src/config/full.js b/packages/babel-core/src/config/full.js index bc15858339cf..1e1855184e10 100644 --- a/packages/babel-core/src/config/full.js +++ b/packages/babel-core/src/config/full.js @@ -14,7 +14,6 @@ import { type PresetInstance, } from "./config-chain"; import type { UnloadedDescriptor } from "./config-descriptors"; -import type { Targets } from "@babel/helper-compilation-targets"; import traverse from "@babel/traverse"; import { makeWeakCache, @@ -23,16 +22,17 @@ import { } from "./caching"; import { validate, - type CallerMetadata, checkNoUnwrappedItemOptionPairs, type PluginItem, } from "./validation/options"; import { validatePluginObject } from "./validation/plugins"; -import { makePluginAPI } from "./helpers/config-api"; +import { makePluginAPI, makePresetAPI } from "./helpers/config-api"; import loadPrivatePartialConfig from "./partial"; import type { ValidatedOptions } from "./validation/options"; +import * as Context from "./cache-contexts"; + type LoadedDescriptor = { value: {}, options: {}, @@ -40,11 +40,6 @@ type LoadedDescriptor = { alias: string, }; -type PluginContext = { - ...ConfigContext, - targets: Targets, -}; - export type { InputOptions } from "./validation/options"; export type ResolvedConfig = { @@ -56,14 +51,6 @@ export type { Plugin }; export type PluginPassList = Array; export type PluginPasses = Array; -// Context not including filename since it is used in places that cannot -// process 'ignore'/'only' and other filename-based logic. -type SimpleContext = { - envName: string, - caller: CallerMetadata | void, - targets: Targets, -}; - export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig( inputOpts: mixed, ): Handler { @@ -85,9 +72,10 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig( throw new Error("Assertion failure - plugins and presets exist"); } - const pluginContext: PluginContext = { + const pluginContext: Context.FullPlugin = { ...context, targets: options.targets, + assumptions: options.assumptions ?? {}, }; const toDescriptor = (item: PluginItem) => { @@ -229,62 +217,72 @@ function enhanceError(context, fn: T): T { /** * Load a generic plugin/preset from the given descriptor loaded from the config object. */ -const loadDescriptor = makeWeakCache(function* ( - { value, options, dirname, alias }: UnloadedDescriptor, - cache: CacheConfigurator, -): Handler { - // Disabled presets should already have been filtered out - if (options === false) throw new Error("Assertion failure"); - - options = options || {}; - - let item = value; - if (typeof value === "function") { - const factory = maybeAsync( - value, - `You appear to be using an async plugin/preset, but Babel has been called synchronously`, - ); +const makeDescriptorLoader = ( + apiFactory: (cache: CacheConfigurator) => API, +): ((d: UnloadedDescriptor, c: Context) => Handler) => + makeWeakCache(function* ( + { value, options, dirname, alias }: UnloadedDescriptor, + cache: CacheConfigurator, + ): Handler { + // Disabled presets should already have been filtered out + if (options === false) throw new Error("Assertion failure"); + + options = options || {}; + + let item = value; + if (typeof value === "function") { + const factory = maybeAsync( + value, + `You appear to be using an async plugin/preset, but Babel has been called synchronously`, + ); - const api = { - ...context, - ...makePluginAPI(cache), - }; - try { - item = yield* factory(api, options, dirname); - } catch (e) { - if (alias) { - e.message += ` (While processing: ${JSON.stringify(alias)})`; + const api = { + ...context, + ...apiFactory(cache), + }; + try { + item = yield* factory(api, options, dirname); + } catch (e) { + if (alias) { + e.message += ` (While processing: ${JSON.stringify(alias)})`; + } + throw e; } - throw e; } - } - if (!item || typeof item !== "object") { - throw new Error("Plugin/Preset did not return an object."); - } + if (!item || typeof item !== "object") { + throw new Error("Plugin/Preset did not return an object."); + } - if (isThenable(item)) { - yield* []; // if we want to support async plugins + if (isThenable(item)) { + yield* []; // if we want to support async plugins - throw new Error( - `You appear to be using a promise as a plugin, ` + - `which your current version of Babel does not support. ` + - `If you're using a published plugin, ` + - `you may need to upgrade your @babel/core version. ` + - `As an alternative, you can prefix the promise with "await". ` + - `(While processing: ${JSON.stringify(alias)})`, - ); - } + throw new Error( + `You appear to be using a promise as a plugin, ` + + `which your current version of Babel does not support. ` + + `If you're using a published plugin, ` + + `you may need to upgrade your @babel/core version. ` + + `As an alternative, you can prefix the promise with "await". ` + + `(While processing: ${JSON.stringify(alias)})`, + ); + } - return { value: item, options, dirname, alias }; -}); + return { value: item, options, dirname, alias }; + }); + +const pluginDescriptorLoader = makeDescriptorLoader( + makePluginAPI, +); +const presetDescriptorLoader = makeDescriptorLoader( + makePresetAPI, +); /** * Instantiate a plugin for the given descriptor, returning the plugin/options pair. */ function* loadPluginDescriptor( descriptor: UnloadedDescriptor, - context: SimpleContext, + context: Context.SimplePlugin, ): Handler { if (descriptor.value instanceof Plugin) { if (descriptor.options) { @@ -297,14 +295,14 @@ function* loadPluginDescriptor( } return yield* instantiatePlugin( - yield* loadDescriptor(descriptor, context), + yield* pluginDescriptorLoader(descriptor, context), context, ); } const instantiatePlugin = makeWeakCache(function* ( { value, options, dirname, alias }: LoadedDescriptor, - cache: CacheConfigurator, + cache: CacheConfigurator, ): Handler { const pluginObj = validatePluginObject(value); @@ -387,9 +385,11 @@ const validatePreset = ( */ function* loadPresetDescriptor( descriptor: UnloadedDescriptor, - context: PluginContext, + context: Context.FullPreset, ): Handler { - const preset = instantiatePreset(yield* loadDescriptor(descriptor, context)); + const preset = instantiatePreset( + yield* presetDescriptorLoader(descriptor, context), + ); validatePreset(preset, context, descriptor); return yield* buildPresetChain(preset, context); } diff --git a/packages/babel-core/src/config/helpers/config-api.js b/packages/babel-core/src/config/helpers/config-api.js index 70d793995d1a..a10ccbbde274 100644 --- a/packages/babel-core/src/config/helpers/config-api.js +++ b/packages/babel-core/src/config/helpers/config-api.js @@ -13,6 +13,8 @@ import { import type { CallerMetadata } from "../validation/options"; +import * as Context from "../cache-contexts"; + type EnvFunction = { (): string, ((string) => T): T, @@ -24,6 +26,8 @@ type CallerFactory = ((CallerMetadata | void) => mixed) => SimpleType; type TargetsFunction = () => Targets; +type AssumptionFunction = (name: string) => boolean; + export type ConfigAPI = {| version: string, cache: SimpleCacheConfigurator, @@ -33,14 +37,19 @@ export type ConfigAPI = {| caller?: CallerFactory, |}; -export type PluginAPI = {| +export type PresetAPI = {| ...ConfigAPI, targets: TargetsFunction, |}; -export function makeConfigAPI< - SideChannel: { envName: string, caller: CallerMetadata | void }, ->(cache: CacheConfigurator): ConfigAPI { +export type PluginAPI = {| + ...PresetAPI, + assumption: AssumptionFunction, +|}; + +export function makeConfigAPI( + cache: CacheConfigurator, +): ConfigAPI { const env: any = value => cache.using(data => { if (typeof value === "undefined") return data.envName; @@ -70,13 +79,9 @@ export function makeConfigAPI< }; } -export function makePluginAPI( - cache: CacheConfigurator<{ - envName: string, - caller: CallerMetadata | void, - targets: Targets, - }>, -): PluginAPI { +export function makePresetAPI( + cache: CacheConfigurator, +): PresetAPI { const targets = () => // We are using JSON.parse/JSON.stringify because it's only possible to cache // primitive values. We can safely stringify the targets object because it @@ -86,6 +91,14 @@ export function makePluginAPI( return { ...makeConfigAPI(cache), targets }; } +export function makePluginAPI( + cache: CacheConfigurator, +): PluginAPI { + const assumption = name => cache.using(data => !!data.assumptions[name]); + + return { ...makePresetAPI(cache), assumption }; +} + function assertVersion(range: string | number): void { if (typeof range === "number") { if (!Number.isInteger(range)) { diff --git a/packages/babel-core/src/config/partial.js b/packages/babel-core/src/config/partial.js index 4bc40e3249db..ca610dce3587 100644 --- a/packages/babel-core/src/config/partial.js +++ b/packages/babel-core/src/config/partial.js @@ -117,7 +117,12 @@ export default function* loadPrivatePartialConfig( const configChain = yield* buildRootChain(args, context); if (!configChain) return null; - const merged: ValidatedOptions = {}; + const merged: ValidatedOptions = { + // TODO(Babel 8): everything should default to false. Remove this object. + assumptions: { + newableArrowFunctions: true, + }, + }; configChain.options.forEach(opts => { mergeOptions((merged: any), opts); }); diff --git a/packages/babel-core/src/config/util.js b/packages/babel-core/src/config/util.js index 491a2ea950fa..891e09c90975 100644 --- a/packages/babel-core/src/config/util.js +++ b/packages/babel-core/src/config/util.js @@ -7,14 +7,13 @@ export function mergeOptions( source: ValidatedOptions | NormalizedOptions, ): void { for (const k of Object.keys(source)) { - if (k === "parserOpts" && source.parserOpts) { - const parserOpts = source.parserOpts; - const targetObj = (target.parserOpts = target.parserOpts || {}); + if ( + (k === "parserOpts" || k === "generatorOpts" || k === "assumptions") && + source[k] + ) { + const parserOpts = source[k]; + const targetObj = target[k] || (target[k] = {}); mergeDefaultFields(targetObj, parserOpts); - } else if (k === "generatorOpts" && source.generatorOpts) { - const generatorOpts = source.generatorOpts; - const targetObj = (target.generatorOpts = target.generatorOpts || {}); - mergeDefaultFields(targetObj, generatorOpts); } else { const val = source[k]; if (val !== undefined) target[k] = (val: any); diff --git a/packages/babel-core/src/config/validation/option-assertions.js b/packages/babel-core/src/config/validation/option-assertions.js index fefda1437759..116c3df90071 100644 --- a/packages/babel-core/src/config/validation/option-assertions.js +++ b/packages/babel-core/src/config/validation/option-assertions.js @@ -24,6 +24,8 @@ import type { TargetsListOrObject, } from "./options"; +import { assumptionsNames } from "./options"; + export type { RootPath } from "./options"; export type ValidatorSet = { @@ -431,3 +433,26 @@ function assertBrowserVersion(loc: GeneralPath, value: mixed) { throw new Error(`${msg(loc)} must be a string or an integer number`); } + +export function assertAssumptions( + loc: GeneralPath, + value: mixed, +): { [name: string]: boolean } | void { + if (value === undefined) return; + + if (typeof value !== "object" || value === null) { + throw new Error(`${msg(loc)} must be an object or undefined.`); + } + + for (const name of Object.keys(value)) { + const subLoc = access(loc, name); + if (!assumptionsNames.has(name)) { + throw new Error(`${msg(subLoc)} is not a supported assumption.`); + } + if (typeof value[name] !== "boolean") { + throw new Error(`${msg(subLoc)} must be a boolean.`); + } + } + + return (value: any); +} diff --git a/packages/babel-core/src/config/validation/options.js b/packages/babel-core/src/config/validation/options.js index 56b7655583e4..69d25f1db6f7 100644 --- a/packages/babel-core/src/config/validation/options.js +++ b/packages/babel-core/src/config/validation/options.js @@ -29,6 +29,7 @@ import { type ValidatorSet, type Validator, type OptionPath, + assertAssumptions, } from "./option-assertions"; import type { UnloadedDescriptor } from "../config-descriptors"; @@ -108,6 +109,9 @@ const COMMON_VALIDATORS: ValidatorSet = { passPerPreset: (assertBoolean: Validator< $PropertyType, >), + assumptions: (assertAssumptions: Validator< + $PropertyType, + >), env: (assertEnvSet: Validator<$PropertyType>), overrides: (assertOverridesList: Validator< @@ -221,6 +225,8 @@ export type ValidatedOptions = { plugins?: PluginList, passPerPreset?: boolean, + assumptions?: { [name: string]: boolean }, + // browserslists-related options targets?: TargetsListOrObject, browserslistConfigFile?: ConfigFileSearch, @@ -325,6 +331,28 @@ type EnvPath = $ReadOnly<{ }>; export type NestingPath = RootPath | OverridesPath | EnvPath; +export const assumptionsNames = new Set([ + "arrayLikeIsIterable", + "arrayIndexedIteration", + "copyReexports", + "ignoreFunctionLength", + "ignoreToPrimitiveHint", + "inheritsAsObjectCreate", + "iterableIsArray", + "mutableTemplateObject", + "newableArrowFunctions", + "noDocumentAll", + "objectRestNoSymbols", + "privateFieldsAsPublic", + "setClassMethods", + "setComputedProperties", + "setModuleMeta", + "setPublicClassFields", + "setSpreadProperties", + "skipForOfIterationClosing", + "superAsFunctionCall", +]); + function getSource(loc: NestingPath): OptionsSource { return loc.type === "root" ? loc.source : getSource(loc.parent); } diff --git a/packages/babel-core/test/assumptions.js b/packages/babel-core/test/assumptions.js new file mode 100644 index 000000000000..a4e62b481f51 --- /dev/null +++ b/packages/babel-core/test/assumptions.js @@ -0,0 +1,183 @@ +import { loadOptions as loadOptionsOrig, transformSync } from "../lib"; + +function loadOptions(opts) { + return loadOptionsOrig({ cwd: __dirname, ...opts }); +} + +function withAssumptions(assumptions) { + return loadOptions({ assumptions }); +} + +describe("assumptions", () => { + it("throws if invalid name", () => { + expect(() => withAssumptions({ foo: true })).toThrow( + `.assumptions["foo"] is not a supported assumption.`, + ); + + expect(() => withAssumptions({ setPublicClassFields: true })).not.toThrow(); + }); + + it("throws if not boolean", () => { + expect(() => withAssumptions({ setPublicClassFields: "yes" })).toThrow( + `.assumptions["setPublicClassFields"] must be a boolean.`, + ); + + expect(() => withAssumptions({ setPublicClassFields: true })).not.toThrow(); + expect(() => + withAssumptions({ setPublicClassFields: false }), + ).not.toThrow(); + }); + + it("can be set by presets", () => { + expect( + loadOptions({ + assumptions: { + setPublicClassFields: true, + }, + presets: [() => ({ assumptions: { setClassMethods: true } })], + }).assumptions, + ).toEqual({ + setPublicClassFields: true, + setClassMethods: true, + // This is enabled by default + newableArrowFunctions: true, + }); + }); + + it("can be queried from plugins", () => { + let setPublicClassFields; + let unknownAssumption; + + transformSync("", { + configFile: false, + browserslistConfigFile: false, + assumptions: { + setPublicClassFields: true, + }, + plugins: [ + api => { + setPublicClassFields = api.assumption("setPublicClassFields"); + + // Unknown assumptions default to "false" and don't throw, so + // that plugins can keep compat with older @babel/core versions + // when they introduce support for a new assumption. + unknownAssumption = api.assumption("unknownAssumption"); + + return {}; + }, + ], + }); + + expect(setPublicClassFields).toBe(true); + expect(unknownAssumption).toBe(false); + }); + + it("cannot be queried from presets", () => { + let assumptionFn; + + transformSync("", { + configFile: false, + browserslistConfigFile: false, + presets: [ + api => { + assumptionFn = api.assumption; + return {}; + }, + ], + }); + + expect(assumptionFn).toBeUndefined(); + }); + + describe("plugin cache", () => { + const makePlugin = () => + jest.fn(api => { + api.assumption("setPublicClassFields"); + api.assumption("mutableTemplateObject"); + return {}; + }); + + const run = (plugin, assumptions) => + transformSync("", { + assumptions, + configFile: false, + browserslistConfigFile: false, + plugins: [plugin], + }); + + it("is not invalidated when assumptions don't change", () => { + const plugin = makePlugin(); + + run(plugin, { + setPublicClassFields: true, + setClassMethods: false, + }); + run(plugin, { + setPublicClassFields: true, + setClassMethods: false, + }); + + expect(plugin).toHaveBeenCalledTimes(1); + }); + + it("is not invalidated when unused assumptions change", () => { + const plugin = makePlugin(); + + run(plugin, { + setPublicClassFields: true, + setClassMethods: false, + }); + run(plugin, { + setPublicClassFields: true, + setClassMethods: true, + }); + + expect(plugin).toHaveBeenCalledTimes(1); + }); + + it("is invalidated when used assumptions change", () => { + const plugin = makePlugin(); + + run(plugin, { + setPublicClassFields: true, + setClassMethods: false, + }); + run(plugin, { + setPublicClassFields: false, + setClassMethods: true, + }); + + expect(plugin).toHaveBeenCalledTimes(2); + }); + + it("is invalidated when used assumptions are added", () => { + const plugin = makePlugin(); + + run(plugin, { + setPublicClassFields: true, + setClassMethods: false, + }); + run(plugin, { + setPublicClassFields: false, + setClassMethods: true, + mutableTemplateObject: true, + }); + + expect(plugin).toHaveBeenCalledTimes(2); + }); + + it("is invalidated when used assumptions are removed", () => { + const plugin = makePlugin(); + + run(plugin, { + setPublicClassFields: true, + setClassMethods: false, + }); + run(plugin, { + setClassMethods: true, + }); + + expect(plugin).toHaveBeenCalledTimes(2); + }); + }); +}); diff --git a/packages/babel-core/test/config-chain.js b/packages/babel-core/test/config-chain.js index 653688e9ada8..d99a6da63068 100644 --- a/packages/babel-core/test/config-chain.js +++ b/packages/babel-core/test/config-chain.js @@ -985,6 +985,7 @@ describe("buildConfigChain", function () { presets: [], cloneInputAst: true, targets: {}, + assumptions: { newableArrowFunctions: true }, }); const realEnv = process.env.NODE_ENV; const realBabelEnv = process.env.BABEL_ENV; diff --git a/packages/babel-helper-plugin-utils/src/index.js b/packages/babel-helper-plugin-utils/src/index.js index 55fc7d6761af..8f90cae836e7 100644 --- a/packages/babel-helper-plugin-utils/src/index.js +++ b/packages/babel-helper-plugin-utils/src/index.js @@ -26,6 +26,11 @@ const apiPolyfills = { targets: () => () => { return {}; }, + // This is supported starting from Babel 7.13 + // TODO(Babel 8): Remove this polyfill + assumption: () => () => { + return false; + }, }; function copyApiObject(api) { From 0e4261647152ffbb33cb5b6114a9a0578c12f200 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Fri, 27 Nov 2020 15:19:05 +0000 Subject: [PATCH 2/4] Only keep assumptions used in the tests, everything else will be in other PRs --- .../src/config/validation/options.js | 16 ---------- packages/babel-core/test/assumptions.js | 29 +++++++++---------- 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/packages/babel-core/src/config/validation/options.js b/packages/babel-core/src/config/validation/options.js index 69d25f1db6f7..507b183fa07a 100644 --- a/packages/babel-core/src/config/validation/options.js +++ b/packages/babel-core/src/config/validation/options.js @@ -332,25 +332,9 @@ type EnvPath = $ReadOnly<{ export type NestingPath = RootPath | OverridesPath | EnvPath; export const assumptionsNames = new Set([ - "arrayLikeIsIterable", - "arrayIndexedIteration", - "copyReexports", - "ignoreFunctionLength", - "ignoreToPrimitiveHint", - "inheritsAsObjectCreate", - "iterableIsArray", "mutableTemplateObject", "newableArrowFunctions", - "noDocumentAll", - "objectRestNoSymbols", - "privateFieldsAsPublic", - "setClassMethods", - "setComputedProperties", - "setModuleMeta", "setPublicClassFields", - "setSpreadProperties", - "skipForOfIterationClosing", - "superAsFunctionCall", ]); function getSource(loc: NestingPath): OptionsSource { diff --git a/packages/babel-core/test/assumptions.js b/packages/babel-core/test/assumptions.js index a4e62b481f51..ad23adae50a7 100644 --- a/packages/babel-core/test/assumptions.js +++ b/packages/babel-core/test/assumptions.js @@ -34,11 +34,11 @@ describe("assumptions", () => { assumptions: { setPublicClassFields: true, }, - presets: [() => ({ assumptions: { setClassMethods: true } })], + presets: [() => ({ assumptions: { mutableTemplateObject: true } })], }).assumptions, ).toEqual({ setPublicClassFields: true, - setClassMethods: true, + mutableTemplateObject: true, // This is enabled by default newableArrowFunctions: true, }); @@ -93,7 +93,6 @@ describe("assumptions", () => { const makePlugin = () => jest.fn(api => { api.assumption("setPublicClassFields"); - api.assumption("mutableTemplateObject"); return {}; }); @@ -110,11 +109,11 @@ describe("assumptions", () => { run(plugin, { setPublicClassFields: true, - setClassMethods: false, + mutableTemplateObject: false, }); run(plugin, { setPublicClassFields: true, - setClassMethods: false, + mutableTemplateObject: false, }); expect(plugin).toHaveBeenCalledTimes(1); @@ -125,11 +124,11 @@ describe("assumptions", () => { run(plugin, { setPublicClassFields: true, - setClassMethods: false, + mutableTemplateObject: false, }); run(plugin, { setPublicClassFields: true, - setClassMethods: true, + mutableTemplateObject: true, }); expect(plugin).toHaveBeenCalledTimes(1); @@ -140,11 +139,11 @@ describe("assumptions", () => { run(plugin, { setPublicClassFields: true, - setClassMethods: false, + mutableTemplateObject: false, }); run(plugin, { setPublicClassFields: false, - setClassMethods: true, + mutableTemplateObject: true, }); expect(plugin).toHaveBeenCalledTimes(2); @@ -154,13 +153,11 @@ describe("assumptions", () => { const plugin = makePlugin(); run(plugin, { - setPublicClassFields: true, - setClassMethods: false, + mutableTemplateObject: false, }); run(plugin, { - setPublicClassFields: false, - setClassMethods: true, - mutableTemplateObject: true, + mutableTemplateObject: false, + setPublicClassFields: true, }); expect(plugin).toHaveBeenCalledTimes(2); @@ -171,10 +168,10 @@ describe("assumptions", () => { run(plugin, { setPublicClassFields: true, - setClassMethods: false, + mutableTemplateObject: false, }); run(plugin, { - setClassMethods: true, + mutableTemplateObject: true, }); expect(plugin).toHaveBeenCalledTimes(2); From 2ec0fa60b7f086b9bb18cff930213cbfda0f6f4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Fri, 27 Nov 2020 16:13:03 +0000 Subject: [PATCH 3/4] [preset-env] Don't pass "loose: false" to plugins, so that it doesn't override assumptions --- packages/babel-preset-env/src/normalize-options.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/babel-preset-env/src/normalize-options.js b/packages/babel-preset-env/src/normalize-options.js index 0f730afabaea..98b1754a237f 100644 --- a/packages/babel-preset-env/src/normalize-options.js +++ b/packages/babel-preset-env/src/normalize-options.js @@ -245,7 +245,7 @@ export default function normalizeOptions(opts: Options) { opts.ignoreBrowserslistConfig, false, ), - loose: v.validateBooleanOption(TopLevelOptions.loose, opts.loose, false), + loose: v.validateBooleanOption(TopLevelOptions.loose, opts.loose), modules: validateModulesOption(opts.modules), shippedProposals: v.validateBooleanOption( TopLevelOptions.shippedProposals, From 8affe9b6d9892fd567187057237f3d028adec825 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Thu, 10 Dec 2020 12:58:27 +0100 Subject: [PATCH 4/4] Make unknown assumptions default to `undefined` --- packages/babel-core/src/config/helpers/config-api.js | 4 ++-- packages/babel-core/src/config/partial.js | 5 +---- packages/babel-core/src/config/validation/options.js | 1 - packages/babel-core/test/assumptions.js | 10 ++++------ packages/babel-core/test/config-chain.js | 2 +- packages/babel-helper-plugin-utils/src/index.js | 4 +--- 6 files changed, 9 insertions(+), 17 deletions(-) diff --git a/packages/babel-core/src/config/helpers/config-api.js b/packages/babel-core/src/config/helpers/config-api.js index a10ccbbde274..24dfedc9f817 100644 --- a/packages/babel-core/src/config/helpers/config-api.js +++ b/packages/babel-core/src/config/helpers/config-api.js @@ -26,7 +26,7 @@ type CallerFactory = ((CallerMetadata | void) => mixed) => SimpleType; type TargetsFunction = () => Targets; -type AssumptionFunction = (name: string) => boolean; +type AssumptionFunction = (name: string) => boolean | void; export type ConfigAPI = {| version: string, @@ -94,7 +94,7 @@ export function makePresetAPI( export function makePluginAPI( cache: CacheConfigurator, ): PluginAPI { - const assumption = name => cache.using(data => !!data.assumptions[name]); + const assumption = name => cache.using(data => data.assumptions[name]); return { ...makePresetAPI(cache), assumption }; } diff --git a/packages/babel-core/src/config/partial.js b/packages/babel-core/src/config/partial.js index ca610dce3587..5d74daa00ec0 100644 --- a/packages/babel-core/src/config/partial.js +++ b/packages/babel-core/src/config/partial.js @@ -118,10 +118,7 @@ export default function* loadPrivatePartialConfig( if (!configChain) return null; const merged: ValidatedOptions = { - // TODO(Babel 8): everything should default to false. Remove this object. - assumptions: { - newableArrowFunctions: true, - }, + assumptions: {}, }; configChain.options.forEach(opts => { mergeOptions((merged: any), opts); diff --git a/packages/babel-core/src/config/validation/options.js b/packages/babel-core/src/config/validation/options.js index 507b183fa07a..c5e99846b974 100644 --- a/packages/babel-core/src/config/validation/options.js +++ b/packages/babel-core/src/config/validation/options.js @@ -333,7 +333,6 @@ export type NestingPath = RootPath | OverridesPath | EnvPath; export const assumptionsNames = new Set([ "mutableTemplateObject", - "newableArrowFunctions", "setPublicClassFields", ]); diff --git a/packages/babel-core/test/assumptions.js b/packages/babel-core/test/assumptions.js index ad23adae50a7..3f6e58005eb1 100644 --- a/packages/babel-core/test/assumptions.js +++ b/packages/babel-core/test/assumptions.js @@ -39,8 +39,6 @@ describe("assumptions", () => { ).toEqual({ setPublicClassFields: true, mutableTemplateObject: true, - // This is enabled by default - newableArrowFunctions: true, }); }); @@ -58,9 +56,9 @@ describe("assumptions", () => { api => { setPublicClassFields = api.assumption("setPublicClassFields"); - // Unknown assumptions default to "false" and don't throw, so - // that plugins can keep compat with older @babel/core versions - // when they introduce support for a new assumption. + // Unknown assumptions don't throw, so that plugins can keep compat + // with older @babel/core versions when they introduce support for + // a new assumption. unknownAssumption = api.assumption("unknownAssumption"); return {}; @@ -69,7 +67,7 @@ describe("assumptions", () => { }); expect(setPublicClassFields).toBe(true); - expect(unknownAssumption).toBe(false); + expect(unknownAssumption).toBe(undefined); }); it("cannot be queried from presets", () => { diff --git a/packages/babel-core/test/config-chain.js b/packages/babel-core/test/config-chain.js index d99a6da63068..f710d347ed9e 100644 --- a/packages/babel-core/test/config-chain.js +++ b/packages/babel-core/test/config-chain.js @@ -985,7 +985,7 @@ describe("buildConfigChain", function () { presets: [], cloneInputAst: true, targets: {}, - assumptions: { newableArrowFunctions: true }, + assumptions: {}, }); const realEnv = process.env.NODE_ENV; const realBabelEnv = process.env.BABEL_ENV; diff --git a/packages/babel-helper-plugin-utils/src/index.js b/packages/babel-helper-plugin-utils/src/index.js index 8f90cae836e7..5347c2470fa7 100644 --- a/packages/babel-helper-plugin-utils/src/index.js +++ b/packages/babel-helper-plugin-utils/src/index.js @@ -28,9 +28,7 @@ const apiPolyfills = { }, // This is supported starting from Babel 7.13 // TODO(Babel 8): Remove this polyfill - assumption: () => () => { - return false; - }, + assumption: () => () => {}, }; function copyApiObject(api) {