From a647b9ea6bdd510f0d80b58dbea66828016ffe00 Mon Sep 17 00:00:00 2001 From: Bogdan Savluk Date: Sat, 27 Mar 2021 02:03:15 +0100 Subject: [PATCH] Convert `@babel/core` to TypeScript (#12929) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Nicolò Ribaudo --- packages/babel-core/package.json | 16 +- .../babel-core/src/config/cache-contexts.ts | 28 +- packages/babel-core/src/config/caching.ts | 127 +++--- .../babel-core/src/config/config-chain.ts | 139 ++++--- .../src/config/config-descriptors.ts | 50 +-- .../src/config/files/configuration.ts | 90 ++-- .../babel-core/src/config/files/import.ts | 1 - .../src/config/files/index-browser.ts | 48 ++- packages/babel-core/src/config/files/index.ts | 8 +- .../src/config/files/module-types.ts | 4 +- .../babel-core/src/config/files/package.ts | 4 +- .../babel-core/src/config/files/plugins.ts | 17 +- packages/babel-core/src/config/files/types.ts | 30 +- packages/babel-core/src/config/files/utils.ts | 17 +- packages/babel-core/src/config/full.ts | 329 +++++++-------- .../src/config/helpers/config-api.ts | 83 ++-- .../src/config/helpers/environment.ts | 2 - packages/babel-core/src/config/index.ts | 16 +- packages/babel-core/src/config/item.ts | 28 +- packages/babel-core/src/config/partial.ts | 119 +++--- .../babel-core/src/config/pattern-to-regex.ts | 1 - packages/babel-core/src/config/plugin.ts | 6 +- packages/babel-core/src/config/printer.ts | 43 +- .../src/config/resolve-targets-browser.ts | 18 +- .../babel-core/src/config/resolve-targets.ts | 20 +- packages/babel-core/src/config/util.ts | 13 +- .../config/validation/option-assertions.ts | 115 +++--- .../src/config/validation/options.ts | 384 ++++++++---------- .../src/config/validation/plugins.ts | 84 ++-- .../src/config/validation/removed.ts | 6 +- .../babel-core/src/gensync-utils/async.ts | 54 ++- packages/babel-core/src/gensync-utils/fs.ts | 16 +- .../babel-core/src/gensync-utils/gensync.d.ts | 37 ++ packages/babel-core/src/index.ts | 5 +- packages/babel-core/src/parse.ts | 44 +- packages/babel-core/src/parser/index.ts | 9 +- .../src/parser/util/missing-plugin-helper.ts | 7 +- .../src/tools/build-external-helpers.ts | 2 +- packages/babel-core/src/transform-ast.ts | 39 +- .../babel-core/src/transform-file-browser.ts | 14 +- packages/babel-core/src/transform-file.ts | 46 +-- packages/babel-core/src/transform.ts | 44 +- .../src/transformation/block-hoist-plugin.ts | 4 +- .../src/transformation/file/file.ts | 65 +-- .../src/transformation/file/generate.ts | 9 +- .../src/transformation/file/merge-map.ts | 82 ++-- .../babel-core/src/transformation/index.ts | 27 +- .../src/transformation/normalize-file.ts | 29 +- .../src/transformation/normalize-opts.ts | 2 - .../src/transformation/plugin-pass.ts | 31 +- packages/babel-parser/tsconfig.json | 17 + .../babel-parser/typings/babel-parser.d.ts | 5 + yarn.lock | 35 ++ 53 files changed, 1267 insertions(+), 1202 deletions(-) create mode 100644 packages/babel-core/src/gensync-utils/gensync.d.ts create mode 100644 packages/babel-parser/tsconfig.json diff --git a/packages/babel-core/package.json b/packages/babel-core/package.json index b99baee4bd2b..f2e9998b0c09 100644 --- a/packages/babel-core/package.json +++ b/packages/babel-core/package.json @@ -42,10 +42,10 @@ "./lib/config/resolve-targets.js": "./lib/config/resolve-targets-browser.js", "./lib/transform-file.js": "./lib/transform-file-browser.js", "./lib/transformation/util/clone-deep.js": "./lib/transformation/util/clone-deep-browser.js", - "./src/config/files/index.js": "./src/config/files/index-browser.js", - "./src/config/resolve-targets.js": "./src/config/resolve-targets-browser.js", - "./src/transform-file.js": "./src/transform-file-browser.js", - "./src/transformation/util/clone-deep.js": "./src/transformation/util/clone-deep-browser.js" + "./src/config/files/index.ts": "./src/config/files/index-browser.ts", + "./src/config/resolve-targets.ts": "./src/config/resolve-targets-browser.ts", + "./src/transform-file.ts": "./src/transform-file-browser.ts", + "./src/transformation/util/clone-deep.ts": "./src/transformation/util/clone-deep-browser.ts" }, "dependencies": { "@babel/code-frame": "workspace:^7.12.13", @@ -65,6 +65,12 @@ "source-map": "^0.5.0" }, "devDependencies": { - "@babel/helper-transform-fixture-test-runner": "workspace:*" + "@babel/helper-transform-fixture-test-runner": "workspace:*", + "@types/convert-source-map": "^1.5.1", + "@types/debug": "^4.1.0", + "@types/lodash": "^4.14.150", + "@types/resolve": "^1.3.2", + "@types/semver": "^5.4.0", + "@types/source-map": "^0.5.0" } } diff --git a/packages/babel-core/src/config/cache-contexts.ts b/packages/babel-core/src/config/cache-contexts.ts index 969362c0c42c..b487d22dfcb3 100644 --- a/packages/babel-core/src/config/cache-contexts.ts +++ b/packages/babel-core/src/config/cache-contexts.ts @@ -1,5 +1,3 @@ -// @flow - import type { Targets } from "@babel/helper-compilation-targets"; import type { ConfigContext } from "./config-chain"; @@ -8,25 +6,23 @@ import type { CallerMetadata } from "./validation/options"; export type { ConfigContext as FullConfig }; export type FullPreset = { - ...ConfigContext, - targets: Targets, -}; + targets: Targets; +} & ConfigContext; export type FullPlugin = { - ...FullPreset, - assumptions: { [name: string]: boolean }, -}; + assumptions: { [name: string]: boolean }; +} & FullPreset; // 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, + envName: string; + caller: CallerMetadata | void; }; export type SimplePreset = { - ...SimpleConfig, - targets: Targets, -}; + targets: Targets; +} & SimpleConfig; export type SimplePlugin = { - ...SimplePreset, - assumptions: { [name: string]: boolean }, -}; + assumptions: { + [name: string]: boolean; + }; +} & SimplePreset; diff --git a/packages/babel-core/src/config/caching.ts b/packages/babel-core/src/config/caching.ts index 050c59d856dc..c459679ceeff 100644 --- a/packages/babel-core/src/config/caching.ts +++ b/packages/babel-core/src/config/caching.ts @@ -1,6 +1,5 @@ -// @flow - -import gensync, { type Handler } from "gensync"; +import gensync from "gensync"; +import type { Handler } from "gensync"; import { maybeAsync, isAsync, @@ -12,60 +11,61 @@ import { isIterableIterator } from "./util"; export type { CacheConfigurator }; -export type SimpleCacheConfigurator = SimpleCacheConfiguratorFn & - SimpleCacheConfiguratorObj; +export type SimpleCacheConfigurator = { + (forever: boolean): void; + (handler: () => T): T; -type SimpleCacheConfiguratorFn = { - (boolean): void, - (handler: () => T): T, -}; -type SimpleCacheConfiguratorObj = { - forever: () => void, - never: () => void, - using: (handler: () => T) => T, - invalidate: (handler: () => T) => T, + forever: () => void; + never: () => void; + using: (handler: () => T) => T; + invalidate: (handler: () => T) => T; }; export type CacheEntry = Array<{ - value: ResultT, - valid: SideChannel => Handler, + value: ResultT; + valid: (channel: SideChannel) => Handler; }>; -const synchronize = ( - gen: (...ArgsT) => Handler, - // $FlowIssue https://github.com/facebook/flow/issues/7279 +const synchronize = ( + gen: (...args: ArgsT) => Handler, ): ((...args: ArgsT) => ResultT) => { return gensync(gen).sync; }; -// eslint-disable-next-line require-yield, no-unused-vars -function* genTrue(data: any) { +// eslint-disable-next-line require-yield +function* genTrue() { return true; } -export function makeWeakCache( - handler: (ArgT, CacheConfigurator) => Handler | ResultT, -): (ArgT, SideChannel) => Handler { - return makeCachedFunction(WeakMap, handler); +export function makeWeakCache( + handler: ( + arg: ArgT, + cache: CacheConfigurator, + ) => Handler | ResultT, +): (arg: ArgT, data: SideChannel) => Handler { + return makeCachedFunction(WeakMap, handler); } -export function makeWeakCacheSync( - handler: (ArgT, CacheConfigurator) => ResultT, -): (ArgT, SideChannel) => ResultT { +export function makeWeakCacheSync( + handler: (arg: ArgT, cache?: CacheConfigurator) => ResultT, +): (arg: ArgT, data?: SideChannel) => ResultT { return synchronize<[ArgT, SideChannel], ResultT>( makeWeakCache(handler), ); } export function makeStrongCache( - handler: (ArgT, CacheConfigurator) => Handler | ResultT, -): (ArgT, SideChannel) => Handler { - return makeCachedFunction(Map, handler); + handler: ( + arg: ArgT, + cache: CacheConfigurator, + ) => Handler | ResultT, +): (arg: ArgT, data: SideChannel) => Handler { + return makeCachedFunction(Map, handler); } export function makeStrongCacheSync( - handler: (ArgT, CacheConfigurator) => ResultT, -): (ArgT, SideChannel) => ResultT { + handler: (arg: ArgT, cache?: CacheConfigurator) => ResultT, +): (arg: ArgT, data?: SideChannel) => ResultT { return synchronize<[ArgT, SideChannel], ResultT>( makeStrongCache(handler), ); @@ -96,13 +96,16 @@ export function makeStrongCacheSync( * 6. Store the result in the cache * 7. RETURN the result */ -function makeCachedFunction( - CallCache: Class, - handler: (ArgT, CacheConfigurator) => Handler | ResultT, -): (ArgT, SideChannel) => Handler { - const callCacheSync = new CallCache(); - const callCacheAsync = new CallCache(); - const futureCache = new CallCache(); +function makeCachedFunction( + CallCache: new () => CacheMap, + handler: ( + arg: ArgT, + cache: CacheConfigurator, + ) => Handler | ResultT, +): (arg: ArgT, data: SideChannel) => Handler { + const callCacheSync = new CallCache(); + const callCacheAsync = new CallCache(); + const futureCache = new CallCache>(); return function* cachedFunction(arg: ArgT, data: SideChannel) { const asyncContext = yield* isAsync(); @@ -121,19 +124,17 @@ function makeCachedFunction( const handlerResult: Handler | ResultT = handler(arg, cache); - let finishLock: ?Lock; + let finishLock: Lock; let value: ResultT; if (isIterableIterator(handlerResult)) { - // Flow refines handlerResult to Generator - const gen = (handlerResult: Generator<*, ResultT, *>); + const gen = handlerResult as Generator; value = yield* onFirstPause(gen, () => { finishLock = setupAsyncLocks(cache, futureCache, arg); }); } else { - // $FlowIgnore doesn't refine handlerResult to ResultT - value = (handlerResult: ResultT); + value = handlerResult; } updateFunctionCache(callCache, cache, arg, value); @@ -149,19 +150,14 @@ function makeCachedFunction( type CacheMap = | Map> + // @ts-expect-error todo(flow->ts): add `extends object` constraint to ArgT | WeakMap>; -function* getCachedValue< - ArgT, - ResultT, - SideChannel, - // $FlowIssue https://github.com/facebook/flow/issues/4528 - Cache: CacheMap, ->( - cache: Cache, +function* getCachedValue( + cache: CacheMap, arg: ArgT, data: SideChannel, -): Handler<{ valid: true, value: ResultT } | { valid: false, value: null }> { +): Handler<{ valid: true; value: ResultT } | { valid: false; value: null }> { const cachedValue: CacheEntry | void = cache.get(arg); if (cachedValue) { @@ -179,7 +175,7 @@ function* getCachedValueOrWait( futureCache: CacheMap, SideChannel>, arg: ArgT, data: SideChannel, -): Handler<{ valid: true, value: ResultT } | { valid: false, value: null }> { +): Handler<{ valid: true; value: ResultT } | { valid: false; value: null }> { const cached = yield* getCachedValue(callCache, arg, data); if (cached.valid) { return cached; @@ -212,8 +208,7 @@ function updateFunctionCache< ArgT, ResultT, SideChannel, - // $FlowIssue https://github.com/facebook/flow/issues/4528 - Cache: CacheMap, + Cache extends CacheMap >( cache: Cache, config: CacheConfigurator, @@ -253,7 +248,9 @@ class CacheConfigurator { _configured: boolean = false; - _pairs: Array<[mixed, (SideChannel) => Handler]> = []; + _pairs: Array< + [cachedValue: unknown, handler: (data: SideChannel) => Handler] + > = []; _data: SideChannel; @@ -294,7 +291,7 @@ class CacheConfigurator { this._configured = true; } - using(handler: SideChannel => T): T { + using(handler: (data: SideChannel) => T): T { if (!this._active) { throw new Error("Cannot change caching after evaluation has completed."); } @@ -313,7 +310,8 @@ class CacheConfigurator { ); if (isThenable(key)) { - return key.then((key: mixed) => { + // @ts-expect-error todo(flow->ts): improve function return type annotation + return key.then((key: unknown) => { this._pairs.push([key, fn]); return key; }); @@ -323,12 +321,12 @@ class CacheConfigurator { return key; } - invalidate(handler: SideChannel => T): T { + invalidate(handler: (data: SideChannel) => T): T { this._invalidate = true; return this.using(handler); } - validator(): SideChannel => Handler { + validator(): (data: SideChannel) => Handler { const pairs = this._pairs; return function* (data: SideChannel) { for (const [key, fn] of pairs) { @@ -364,7 +362,7 @@ function makeSimpleConfigurator( cacheFn.using = cb => cache.using(() => assertSimpleType(cb())); cacheFn.invalidate = cb => cache.invalidate(() => assertSimpleType(cb())); - return (cacheFn: any); + return cacheFn as any; } // Types are limited here so that in the future these values can be used @@ -376,7 +374,7 @@ export type SimpleType = | null | void | Promise; -export function assertSimpleType(value: mixed): SimpleType { +export function assertSimpleType(value: unknown): SimpleType { if (isThenable(value)) { throw new Error( `You appear to be using an async cache handler, ` + @@ -397,6 +395,7 @@ export function assertSimpleType(value: mixed): SimpleType { "Cache keys must be either string, boolean, number, null, or undefined.", ); } + // @ts-expect-error todo(flow->ts) value is still typed as unknown, also assert function typically should not return a value return value; } diff --git a/packages/babel-core/src/config/config-chain.ts b/packages/babel-core/src/config/config-chain.ts index 5aa472ccfa40..3d5fdaa2a276 100644 --- a/packages/babel-core/src/config/config-chain.ts +++ b/packages/babel-core/src/config/config-chain.ts @@ -1,15 +1,14 @@ -// @flow - import path from "path"; import buildDebug from "debug"; import type { Handler } from "gensync"; -import { - validate, - type ValidatedOptions, - type IgnoreList, - type ConfigApplicableTest, - type BabelrcSearch, - type CallerMetadata, +import { validate } from "./validation/options"; +import type { + ValidatedOptions, + IgnoreList, + ConfigApplicableTest, + BabelrcSearch, + CallerMetadata, + IgnoreItem, } from "./validation/options"; import pathPatternToRegex from "./pattern-to-regex"; import { ConfigPrinter, ChainFormatter } from "./printer"; @@ -21,41 +20,41 @@ import { findRelativeConfig, findRootConfig, loadConfig, - type ConfigFile, - type IgnoreFile, - type FilePackageData, } from "./files"; +import type { ConfigFile, IgnoreFile, FilePackageData } from "./files"; import { makeWeakCacheSync, makeStrongCacheSync } from "./caching"; import { createCachedDescriptors, createUncachedDescriptors, - type UnloadedDescriptor, - type OptionsAndDescriptors, - type ValidatedFile, +} from "./config-descriptors"; +import type { + UnloadedDescriptor, + OptionsAndDescriptors, + ValidatedFile, } from "./config-descriptors"; export type ConfigChain = { - plugins: Array, - presets: Array, - options: Array, - files: Set, + plugins: Array; + presets: Array; + options: Array; + files: Set; }; export type PresetInstance = { - options: ValidatedOptions, - alias: string, - dirname: string, + options: ValidatedOptions; + alias: string; + dirname: string; }; export type ConfigContext = { - filename: string | void, - cwd: string, - root: string, - envName: string, - caller: CallerMetadata | void, - showConfig: boolean, + filename: string | void; + cwd: string; + root: string; + envName: string; + caller: CallerMetadata | void; + showConfig: boolean; }; /** @@ -63,7 +62,7 @@ export type ConfigContext = { */ export function* buildPresetChain( arg: PresetInstance, - context: *, + context: any, ): Handler { const chain = yield* buildPresetChainWalker(arg, context); if (!chain) return null; @@ -76,10 +75,7 @@ export function* buildPresetChain( }; } -export const buildPresetChainWalker: ( - arg: PresetInstance, - context: *, -) => * = makeChainWalker({ +export const buildPresetChainWalker = makeChainWalker({ root: preset => loadPresetDescriptors(preset), env: (preset, envName) => loadPresetEnvDescriptors(preset)(envName), overrides: (preset, index) => loadPresetOverridesDescriptors(preset)(index), @@ -128,11 +124,11 @@ const loadPresetOverridesEnvDescriptors = makeWeakCacheSync( export type FileHandling = "transpile" | "ignored" | "unsupported"; export type RootConfigChain = ConfigChain & { - babelrc: ConfigFile | void, - config: ConfigFile | void, - ignore: IgnoreFile | void, - fileHandling: FileHandling, - files: Set, + babelrc: ConfigFile | void; + config: ConfigFile | void; + ignore: IgnoreFile | void; + fileHandling: FileHandling; + files: Set; }; /** @@ -257,7 +253,6 @@ export function* buildRootChain( if (context.showConfig) { console.log( - // $FlowIgnore: context.showConfig implies context.filename is not null `Babel configs on "${context.filename}" (ascending priority):\n` + // print config by the order of ascending priority [configReport, babelRcReport, programmaticReport] @@ -288,7 +283,7 @@ export function* buildRootChain( function babelrcLoadEnabled( context: ConfigContext, pkgData: FilePackageData, - babelrcRoots: BabelrcSearch | void, + babelrcRoots: BabelrcSearch | undefined, babelrcRootsDirectory: string, ): boolean { if (typeof babelrcRoots === "boolean") return babelrcRoots; @@ -302,7 +297,9 @@ function babelrcLoadEnabled( } let babelrcPatterns = babelrcRoots; - if (!Array.isArray(babelrcPatterns)) babelrcPatterns = [babelrcPatterns]; + if (!Array.isArray(babelrcPatterns)) { + babelrcPatterns = [babelrcPatterns as IgnoreItem]; + } babelrcPatterns = babelrcPatterns.map(pat => { return typeof pat === "string" ? path.resolve(babelrcRootsDirectory, pat) @@ -374,7 +371,7 @@ const loadProgrammaticChain = makeChainWalker({ /** * Build a config chain for a given file. */ -const loadFileChainWalker = makeChainWalker({ +const loadFileChainWalker = makeChainWalker({ root: file => loadFileDescriptors(file), env: (file, envName) => loadFileEnvDescriptors(file)(envName), overrides: (file, index) => loadFileOverridesDescriptors(file)(index), @@ -499,36 +496,46 @@ function buildOverrideEnvDescriptors( : null; } -function makeChainWalker({ +function makeChainWalker< + ArgT extends { options: ValidatedOptions; dirname: string } +>({ root, env, overrides, overridesEnv, createLogger, -}: {| - root: ArgT => OptionsAndDescriptors, - env: (ArgT, string) => OptionsAndDescriptors | null, - overrides: (ArgT, number) => OptionsAndDescriptors, - overridesEnv: (ArgT, number, string) => OptionsAndDescriptors | null, +}: { + root: (configEntry: ArgT) => OptionsAndDescriptors; + env: (configEntry: ArgT, env: string) => OptionsAndDescriptors | null; + overrides: (configEntry: ArgT, index: number) => OptionsAndDescriptors; + overridesEnv: ( + configEntry: ArgT, + index: number, + env: string, + ) => OptionsAndDescriptors | null; createLogger: ( - ArgT, - ConfigContext, - ConfigPrinter | void, - ) => (OptionsAndDescriptors, ?number, ?string) => void, -|}): ( - ArgT, - ConfigContext, - files?: Set | void, - baseLogger: ConfigPrinter | void, + configEntry: ArgT, + context: ConfigContext, + printer: ConfigPrinter | void, + ) => ( + opts: OptionsAndDescriptors, + index?: number | null, + env?: string | null, + ) => void; +}): ( + configEntry: ArgT, + context: ConfigContext, + files?: Set, + baseLogger?: ConfigPrinter, ) => Handler { return function* (input, context, files = new Set(), baseLogger) { const { dirname } = input; - const flattenedConfigs: Array<{| - config: OptionsAndDescriptors, - index: ?number, - envName: ?string, - |}> = []; + const flattenedConfigs: Array<{ + config: OptionsAndDescriptors; + index: number | undefined | null; + envName: string | undefined | null; + }> = []; const rootOpts = root(input); if (configIsApplicable(rootOpts, dirname, context)) { @@ -612,7 +619,7 @@ function* mergeExtendsChain( dirname: string, context: ConfigContext, files: Set, - baseLogger: ConfigPrinter | void, + baseLogger?: ConfigPrinter, ): Handler { if (opts.extends === undefined) return true; @@ -708,7 +715,7 @@ function dedupDescriptors( ): Array { const map: Map< Function, - Map, + Map > = new Map(); const descriptors = []; @@ -773,8 +780,8 @@ function configFieldIsApplicable( */ function shouldIgnore( context: ConfigContext, - ignore: ?IgnoreList, - only: ?IgnoreList, + ignore: IgnoreList | undefined | null, + only: IgnoreList | undefined | null, dirname: string, ): boolean { if (ignore && matchesPatterns(context, ignore, dirname)) { diff --git a/packages/babel-core/src/config/config-descriptors.ts b/packages/babel-core/src/config/config-descriptors.ts index 2ae3c4344b29..9fe9df54a027 100644 --- a/packages/babel-core/src/config/config-descriptors.ts +++ b/packages/babel-core/src/config/config-descriptors.ts @@ -1,6 +1,6 @@ -// @flow +import gensync from "gensync"; -import gensync, { type Handler } from "gensync"; +import type { Handler } from "gensync"; import { loadPlugin, loadPreset } from "./files"; @@ -10,8 +10,8 @@ import { makeWeakCacheSync, makeStrongCacheSync, makeStrongCache, - type CacheConfigurator, } from "./caching"; +import type { CacheConfigurator } from "./caching"; import type { ValidatedOptions, @@ -25,25 +25,25 @@ import { resolveBrowserslistConfigFile } from "./resolve-targets"; // for the plugins and presets so we don't load the plugins/presets unless // the options object actually ends up being applicable. export type OptionsAndDescriptors = { - options: ValidatedOptions, - plugins: () => Handler>, - presets: () => Handler>, + options: ValidatedOptions; + plugins: () => Handler>; + presets: () => Handler>; }; // Represents a plugin or presets at a given location in a config object. // At this point these have been resolved to a specific object or function, // but have not yet been executed to call functions with options. export type UnloadedDescriptor = { - name: string | void, - value: {} | Function, - options: {} | void | false, - dirname: string, - alias: string, - ownPass?: boolean, + name: string | undefined; + value: any | Function; + options: {} | undefined | false; + dirname: string; + alias: string; + ownPass?: boolean; file?: { - request: string, - resolved: string, - } | void, + request: string; + resolved: string; + }; }; function isEqualDescriptor( @@ -63,9 +63,9 @@ function isEqualDescriptor( } export type ValidatedFile = { - filepath: string, - dirname: string, - options: ValidatedOptions, + filepath: string; + dirname: string; + options: ValidatedOptions; }; // eslint-disable-next-line require-yield @@ -100,10 +100,13 @@ export function createCachedDescriptors( return { options: optionsWithResolvedBrowserslistConfigFile(options, dirname), plugins: plugins - ? () => createCachedPluginDescriptors(plugins, dirname)(alias) + ? () => + // @ts-expect-error todo(flow->ts) ts complains about incorrect arguments + createCachedPluginDescriptors(plugins, dirname)(alias) : () => handlerOf([]), presets: presets ? () => + // @ts-expect-error todo(flow->ts) ts complains about incorrect arguments createCachedPresetDescriptors(presets, dirname)(alias)( !!passPerPreset, ) @@ -295,9 +298,9 @@ export function* createDescriptor( alias, ownPass, }: { - type?: "plugin" | "preset", - alias: string, - ownPass?: boolean, + type?: "plugin" | "preset"; + alias: string; + ownPass?: boolean; }, ): Handler { const desc = getItemDescriptor(pair); @@ -307,7 +310,8 @@ export function* createDescriptor( let name; let options; - let value = pair; + // todo(flow->ts) better type annotation + let value: any = pair; if (Array.isArray(value)) { if (value.length === 3) { [value, options, name] = value; diff --git a/packages/babel-core/src/config/files/configuration.ts b/packages/babel-core/src/config/files/configuration.ts index fe3da4593ad3..df06057cbc78 100644 --- a/packages/babel-core/src/config/files/configuration.ts +++ b/packages/babel-core/src/config/files/configuration.ts @@ -1,15 +1,12 @@ -// @flow - import buildDebug from "debug"; import path from "path"; import json5 from "json5"; -import gensync, { type Handler } from "gensync"; -import { - makeStrongCache, - makeWeakCacheSync, - type CacheConfigurator, -} from "../caching"; -import { makeConfigAPI, type ConfigAPI } from "../helpers/config-api"; +import gensync from "gensync"; +import type { Handler } from "gensync"; +import { makeStrongCache, makeWeakCacheSync } from "../caching"; +import type { CacheConfigurator } from "../caching"; +import { makeConfigAPI } from "../helpers/config-api"; +import type { ConfigAPI } from "../helpers/config-api"; import { makeStaticFileCache } from "./utils"; import loadCjsOrMjsDefault from "./module-types"; import pathPatternToRegex from "../pattern-to-regex"; @@ -19,8 +16,7 @@ import type { CallerMetadata } from "../validation/options"; import * as fs from "../../gensync-utils/fs"; import { createRequire } from "module"; -// $FlowIgnore - https://github.com/facebook/flow/issues/6913#issuecomment-662787504 -const require = createRequire(import /*::("")*/.meta.url); +const require = createRequire(import.meta.url); const debug = buildDebug("babel:config:loading:files:configuration"); @@ -75,8 +71,7 @@ export function* findRelativeConfig( envName, caller, packageData.pkg?.dirname === loc - ? // $FlowIgnore - packageData.pkg is not null - packageToBabelConfig((packageData.pkg: ConfigFile)) + ? packageToBabelConfig(packageData.pkg as ConfigFile) : null, ); } @@ -107,7 +102,7 @@ function* loadOneConfig( dirname: string, envName: string, caller: CallerMetadata | void, - previousConfig?: ConfigFile | null = null, + previousConfig: ConfigFile | null = null, ): Handler { const configs = yield* gensync.all( names.map(filename => @@ -166,8 +161,8 @@ const LOADING_CONFIGS = new Set(); const readConfigJS = makeStrongCache(function* readConfigJS( filepath: string, cache: CacheConfigurator<{ - envName: string, - caller: CallerMetadata | void, + envName: string; + caller: CallerMetadata | void; }>, ): Handler { if (!fs.exists.sync(filepath)) { @@ -189,14 +184,14 @@ const readConfigJS = makeStrongCache(function* readConfigJS( }; } - let options: mixed; + let options: unknown; try { LOADING_CONFIGS.add(filepath); - options = (yield* loadCjsOrMjsDefault( + options = yield* loadCjsOrMjsDefault( filepath, "You appear to be using a native ECMAScript module configuration " + "file, which is only supported when running Babel asynchronously.", - ): mixed); + ); } catch (err) { err.message = `${filepath}: Error while loading config - ${err.message}`; throw err; @@ -206,8 +201,12 @@ const readConfigJS = makeStrongCache(function* readConfigJS( let assertCache = false; if (typeof options === "function") { - yield* []; // if we want to make it possible to use async configs - options = ((options: any): (api: ConfigAPI) => {})(makeConfigAPI(cache)); + // @ts-expect-error - if we want to make it possible to use async configs + yield* []; + + options = ((options as any) as (api: ConfigAPI) => {})( + makeConfigAPI(cache), + ); assertCache = true; } @@ -218,6 +217,7 @@ const readConfigJS = makeStrongCache(function* readConfigJS( ); } + // @ts-expect-error todo(flow->ts) if (typeof options.then === "function") { throw new Error( `You appear to be using an async configuration, ` + @@ -239,7 +239,7 @@ const readConfigJS = makeStrongCache(function* readConfigJS( const packageToBabelConfig = makeWeakCacheSync( (file: ConfigFile): ConfigFile | null => { - const babel: mixed = file.options[("babel": string)]; + const babel: unknown = file.options["babel"]; if (typeof babel === "undefined") return null; @@ -255,30 +255,32 @@ const packageToBabelConfig = makeWeakCacheSync( }, ); -const readConfigJSON5 = makeStaticFileCache((filepath, content): ConfigFile => { - let options; - try { - options = json5.parse(content); - } catch (err) { - err.message = `${filepath}: Error while parsing config - ${err.message}`; - throw err; - } +const readConfigJSON5 = makeStaticFileCache( + (filepath, content): ConfigFile => { + let options; + try { + options = json5.parse(content); + } catch (err) { + err.message = `${filepath}: Error while parsing config - ${err.message}`; + throw err; + } - if (!options) throw new Error(`${filepath}: No config detected`); + if (!options) throw new Error(`${filepath}: No config detected`); - if (typeof options !== "object") { - throw new Error(`${filepath}: Config returned typeof ${typeof options}`); - } - if (Array.isArray(options)) { - throw new Error(`${filepath}: Expected config object but found array`); - } + if (typeof options !== "object") { + throw new Error(`${filepath}: Config returned typeof ${typeof options}`); + } + if (Array.isArray(options)) { + throw new Error(`${filepath}: Expected config object but found array`); + } - return { - filepath, - dirname: path.dirname(filepath), - options, - }; -}); + return { + filepath, + dirname: path.dirname(filepath), + options, + }; + }, +); const readIgnoreConfig = makeStaticFileCache((filepath, content) => { const ignoreDir = path.dirname(filepath); @@ -319,7 +321,7 @@ export function* resolveShowConfigPath( return null; } -function throwConfigError(): empty { +function throwConfigError(): never { throw new Error(`\ Caching was left unconfigured. Babel's plugins, presets, and .babelrc.js files can be configured for various types of caching, using the first param of their handler functions: diff --git a/packages/babel-core/src/config/files/import.ts b/packages/babel-core/src/config/files/import.ts index 3db10a4e9e1b..460873bec19f 100644 --- a/packages/babel-core/src/config/files/import.ts +++ b/packages/babel-core/src/config/files/import.ts @@ -1,4 +1,3 @@ -// @flow // We keep this in a separate file so that in older node versions, where // import() isn't supported, we can try/catch around the require() call // when loading this file. diff --git a/packages/babel-core/src/config/files/index-browser.ts b/packages/babel-core/src/config/files/index-browser.ts index cdac53a626c2..5740f8210e40 100644 --- a/packages/babel-core/src/config/files/index-browser.ts +++ b/packages/babel-core/src/config/files/index-browser.ts @@ -1,5 +1,3 @@ -// @flow - import type { Handler } from "gensync"; import type { @@ -15,7 +13,8 @@ export type { ConfigFile, IgnoreFile, RelativeConfig, FilePackageData }; // eslint-disable-next-line require-yield export function* findConfigUpwards( - rootDir: string, // eslint-disable-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars + rootDir: string, ): Handler { return null; } @@ -32,18 +31,24 @@ export function* findPackageData(filepath: string): Handler { // eslint-disable-next-line require-yield export function* findRelativeConfig( - pkgData: FilePackageData, // eslint-disable-line no-unused-vars - envName: string, // eslint-disable-line no-unused-vars - caller: CallerMetadata | void, // eslint-disable-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars + pkgData: FilePackageData, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + envName: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + caller: CallerMetadata | void, ): Handler { - return { pkg: null, config: null, ignore: null }; + return { config: null, ignore: null }; } // eslint-disable-next-line require-yield export function* findRootConfig( - dirname: string, // eslint-disable-line no-unused-vars - envName: string, // eslint-disable-line no-unused-vars - caller: CallerMetadata | void, // eslint-disable-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars + dirname: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + envName: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + caller: CallerMetadata | void, ): Handler { return null; } @@ -52,27 +57,30 @@ export function* findRootConfig( export function* loadConfig( name: string, dirname: string, - envName: string, // eslint-disable-line no-unused-vars - caller: CallerMetadata | void, // eslint-disable-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars + envName: string, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + caller: CallerMetadata | void, ): Handler { throw new Error(`Cannot load ${name} relative to ${dirname} in a browser`); } // eslint-disable-next-line require-yield export function* resolveShowConfigPath( - dirname: string, // eslint-disable-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars + dirname: string, ): Handler { return null; } export const ROOT_CONFIG_FILENAMES = []; -// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line @typescript-eslint/no-unused-vars export function resolvePlugin(name: string, dirname: string): string | null { return null; } -// eslint-disable-next-line no-unused-vars +// eslint-disable-next-line @typescript-eslint/no-unused-vars export function resolvePreset(name: string, dirname: string): string | null { return null; } @@ -80,7 +88,10 @@ export function resolvePreset(name: string, dirname: string): string | null { export function loadPlugin( name: string, dirname: string, -): Handler<{ filepath: string, value: mixed }> { +): Handler<{ + filepath: string; + value: unknown; +}> { throw new Error( `Cannot load plugin ${name} relative to ${dirname} in a browser`, ); @@ -89,7 +100,10 @@ export function loadPlugin( export function loadPreset( name: string, dirname: string, -): Handler<{ filepath: string, value: mixed }> { +): Handler<{ + filepath: string; + value: unknown; +}> { throw new Error( `Cannot load preset ${name} relative to ${dirname} in a browser`, ); diff --git a/packages/babel-core/src/config/files/index.ts b/packages/babel-core/src/config/files/index.ts index 2321fb7605d9..c1dfb76e447b 100644 --- a/packages/babel-core/src/config/files/index.ts +++ b/packages/babel-core/src/config/files/index.ts @@ -1,11 +1,9 @@ -// @flow - -import typeof * as indexBrowserType from "./index-browser"; -import typeof * as indexType from "./index"; +type indexBrowserType = typeof import("./index-browser"); +type indexType = typeof import("./index"); // Kind of gross, but essentially asserting that the exports of this module are the same as the // exports of index-browser, since this file may be replaced at bundle time with index-browser. -((({}: any): $Exact): $Exact); +((({} as any) as indexBrowserType) as indexType); export { findPackageData } from "./package"; diff --git a/packages/babel-core/src/config/files/module-types.ts b/packages/babel-core/src/config/files/module-types.ts index de75ada49337..68c7049f30e3 100644 --- a/packages/babel-core/src/config/files/module-types.ts +++ b/packages/babel-core/src/config/files/module-types.ts @@ -17,7 +17,7 @@ export default function* loadCjsOrMjsDefault( asyncError: string, // TODO(Babel 8): Remove this fallbackToTranspiledModule: boolean = false, -): Handler { +): Handler { switch (guessJSModuleType(filepath)) { case "cjs": return loadCjsDefault(filepath, fallbackToTranspiledModule); @@ -48,7 +48,7 @@ function guessJSModuleType(filename: string): "cjs" | "mjs" | "unknown" { } function loadCjsDefault(filepath: string, fallbackToTranspiledModule: boolean) { - const module = (require(filepath): mixed); + const module = require(filepath) as any; return module?.__esModule ? // TODO (Babel 8): Remove "module" and "undefined" fallback module.default || (fallbackToTranspiledModule ? module : undefined) diff --git a/packages/babel-core/src/config/files/package.ts b/packages/babel-core/src/config/files/package.ts index 99ac13364e77..c583db3e38b8 100644 --- a/packages/babel-core/src/config/files/package.ts +++ b/packages/babel-core/src/config/files/package.ts @@ -1,5 +1,3 @@ -// @flow - import path from "path"; import type { Handler } from "gensync"; import { makeStaticFileCache } from "./utils"; @@ -39,7 +37,7 @@ const readConfigPackage = makeStaticFileCache( (filepath, content): ConfigFile => { let options; try { - options = (JSON.parse(content): mixed); + options = JSON.parse(content) as unknown; } catch (err) { err.message = `${filepath}: Error while parsing JSON - ${err.message}`; throw err; diff --git a/packages/babel-core/src/config/files/plugins.ts b/packages/babel-core/src/config/files/plugins.ts index 4e2f4ba88e27..1483c7d2a421 100644 --- a/packages/babel-core/src/config/files/plugins.ts +++ b/packages/babel-core/src/config/files/plugins.ts @@ -1,17 +1,14 @@ -// @flow - /** * This file handles all logic for converting string-based configuration references into loaded objects. */ import buildDebug from "debug"; import path from "path"; -import { type Handler } from "gensync"; +import type { Handler } from "gensync"; import loadCjsOrMjsDefault from "./module-types"; import { createRequire } from "module"; -// $FlowIgnore - https://github.com/facebook/flow/issues/6913#issuecomment-662787504 -const require = createRequire(import /*::("")*/.meta.url); +const require = createRequire(import.meta.url); const debug = buildDebug("babel:config:loading:files:plugins"); @@ -35,7 +32,7 @@ export function resolvePreset(name: string, dirname: string): string | null { export function* loadPlugin( name: string, dirname: string, -): Handler<{ filepath: string, value: mixed }> { +): Handler<{ filepath: string; value: unknown }> { const filepath = resolvePlugin(name, dirname); if (!filepath) { throw new Error(`Plugin ${name} not found relative to ${dirname}`); @@ -50,7 +47,7 @@ export function* loadPlugin( export function* loadPreset( name: string, dirname: string, -): Handler<{ filepath: string, value: mixed }> { +): Handler<{ filepath: string; value: unknown }> { const filepath = resolvePreset(name, dirname); if (!filepath) { throw new Error(`Preset ${name} not found relative to ${dirname}`); @@ -151,7 +148,7 @@ function resolveStandardizedName( } const LOADING_MODULES = new Set(); -function* requireModule(type: string, name: string): Handler { +function* requireModule(type: string, name: string): Handler { if (LOADING_MODULES.has(name)) { throw new Error( `Reentrant ${type} detected trying to load "${name}". This module is not ignored ` + @@ -162,7 +159,7 @@ function* requireModule(type: string, name: string): Handler { try { LOADING_MODULES.add(name); - return (yield* loadCjsOrMjsDefault( + return yield* loadCjsOrMjsDefault( name, `You appear to be using a native ECMAScript module ${type}, ` + "which is only supported when running Babel asynchronously.", @@ -171,7 +168,7 @@ function* requireModule(type: string, name: string): Handler { // export. // See packages/babel-core/test/fixtures/option-manager/presets/es2015_named.js true, - ): mixed); + ); } catch (err) { err.message = `[BABEL]: ${err.message} (While processing: ${name})`; throw err; diff --git a/packages/babel-core/src/config/files/types.ts b/packages/babel-core/src/config/files/types.ts index 24649d010fc7..a2e61f7ef7e5 100644 --- a/packages/babel-core/src/config/files/types.ts +++ b/packages/babel-core/src/config/files/types.ts @@ -1,38 +1,32 @@ -// @flow - export type ConfigFile = { - filepath: string, - dirname: string, - options: {}, + filepath: string; + dirname: string; + options: {}; }; export type IgnoreFile = { - filepath: string, - dirname: string, - ignore: Array, + filepath: string; + dirname: string; + ignore: Array; }; export type RelativeConfig = { // The actual config, either from package.json#babel, .babelrc, or // .babelrc.js, if there was one. - config: ConfigFile | null, - + config: ConfigFile | null; // The .babelignore, if there was one. - ignore: IgnoreFile | null, + ignore: IgnoreFile | null; }; export type FilePackageData = { // The file in the package. - filepath: string, - + filepath: string; // Any ancestor directories of the file that are within the package. - directories: Array, - + directories: Array; // The contents of the package.json. May not be found if the package just // terminated at a node_modules folder without finding one. - pkg: ConfigFile | null, - + pkg: ConfigFile | null; // True if a package.json or node_modules folder was found while traversing // the directory structure. - isPackage: boolean, + isPackage: boolean; }; diff --git a/packages/babel-core/src/config/files/utils.ts b/packages/babel-core/src/config/files/utils.ts index 57b60ec4914c..9511a995e8a4 100644 --- a/packages/babel-core/src/config/files/utils.ts +++ b/packages/babel-core/src/config/files/utils.ts @@ -1,17 +1,16 @@ -// @flow +import type { Handler } from "gensync"; -import type { Gensync, Handler } from "gensync"; - -import { makeStrongCache, type CacheConfigurator } from "../caching"; +import { makeStrongCache } from "../caching"; +import type { CacheConfigurator } from "../caching"; import * as fs from "../../gensync-utils/fs"; import nodeFs from "fs"; export function makeStaticFileCache( - fn: (string, string) => T, -): Gensync<[string], T | null> { - return (makeStrongCache(function* ( + fn: (filepath: string, contents: string) => T, +) { + return makeStrongCache(function* ( filepath: string, - cache: CacheConfigurator, + cache: CacheConfigurator, ): Handler { const cached = cache.invalidate(() => fileMtime(filepath)); @@ -20,7 +19,7 @@ export function makeStaticFileCache( } return fn(filepath, yield* fs.readFile(filepath, "utf8")); - }): Gensync); + }); } function fileMtime(filepath: string): number | null { diff --git a/packages/babel-core/src/config/full.ts b/packages/babel-core/src/config/full.ts index 1e1855184e10..c4b20aebd24d 100644 --- a/packages/babel-core/src/config/full.ts +++ b/packages/babel-core/src/config/full.ts @@ -1,32 +1,29 @@ -// @flow - -import gensync, { type Handler } from "gensync"; +import gensync from "gensync"; +import type { Handler } from "gensync"; import { forwardAsync, maybeAsync, isThenable } from "../gensync-utils/async"; import { mergeOptions } from "./util"; import * as context from "../index"; import Plugin from "./plugin"; import { getItemDescriptor } from "./item"; -import { - buildPresetChain, - type ConfigContext, - type ConfigChain, - type PresetInstance, +import { buildPresetChain } from "./config-chain"; +import type { + ConfigContext, + ConfigChain, + PresetInstance, } from "./config-chain"; import type { UnloadedDescriptor } from "./config-descriptors"; import traverse from "@babel/traverse"; -import { - makeWeakCache, - makeWeakCacheSync, - type CacheConfigurator, -} from "./caching"; +import { makeWeakCache, makeWeakCacheSync } from "./caching"; +import type { CacheConfigurator } from "./caching"; import { validate, checkNoUnwrappedItemOptionPairs, - type PluginItem, } from "./validation/options"; +import type { PluginItem } from "./validation/options"; import { validatePluginObject } from "./validation/plugins"; import { makePluginAPI, makePresetAPI } from "./helpers/config-api"; +import type { PluginAPI, PresetAPI } from "./helpers/config-api"; import loadPrivatePartialConfig from "./partial"; import type { ValidatedOptions } from "./validation/options"; @@ -34,172 +31,183 @@ import type { ValidatedOptions } from "./validation/options"; import * as Context from "./cache-contexts"; type LoadedDescriptor = { - value: {}, - options: {}, - dirname: string, - alias: string, + value: {}; + options: {}; + dirname: string; + alias: string; }; export type { InputOptions } from "./validation/options"; export type ResolvedConfig = { - options: Object, - passes: PluginPasses, + options: any; + passes: PluginPasses; }; export type { Plugin }; export type PluginPassList = Array; export type PluginPasses = Array; -export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig( - inputOpts: mixed, -): Handler { - const result = yield* loadPrivatePartialConfig(inputOpts); - if (!result) { - return null; - } - const { options, context, fileHandling } = result; - - if (fileHandling === "ignored") { - return null; - } - - const optionDefaults = {}; +export default gensync<(inputOpts: unknown) => ResolvedConfig | null>( + function* loadFullConfig(inputOpts) { + const result = yield* loadPrivatePartialConfig(inputOpts); + if (!result) { + return null; + } + const { options, context, fileHandling } = result; - const { plugins, presets } = options; + if (fileHandling === "ignored") { + return null; + } - if (!plugins || !presets) { - throw new Error("Assertion failure - plugins and presets exist"); - } + const optionDefaults = {}; - const pluginContext: Context.FullPlugin = { - ...context, - targets: options.targets, - assumptions: options.assumptions ?? {}, - }; + const { plugins, presets } = options; - const toDescriptor = (item: PluginItem) => { - const desc = getItemDescriptor(item); - if (!desc) { - throw new Error("Assertion failure - must be config item"); + if (!plugins || !presets) { + throw new Error("Assertion failure - plugins and presets exist"); } - return desc; - }; + const pluginContext: Context.FullPlugin = { + ...context, + targets: options.targets, + assumptions: options.assumptions ?? {}, + }; - const presetsDescriptors = presets.map(toDescriptor); - const initialPluginsDescriptors = plugins.map(toDescriptor); - const pluginDescriptorsByPass: Array> = [[]]; - const passes: Array> = []; + const toDescriptor = (item: PluginItem) => { + const desc = getItemDescriptor(item); + if (!desc) { + throw new Error("Assertion failure - must be config item"); + } - const ignored = yield* enhanceError( - context, - function* recursePresetDescriptors( - rawPresets: Array, - pluginDescriptorsPass: Array, - ) { - const presets: Array<{| - preset: ConfigChain | null, - pass: Array, - |}> = []; - - for (let i = 0; i < rawPresets.length; i++) { - const descriptor = rawPresets[i]; - if (descriptor.options !== false) { - try { - // Presets normally run in reverse order, but if they - // have their own pass they run after the presets - // in the previous pass. - if (descriptor.ownPass) { - presets.push({ - preset: yield* loadPresetDescriptor(descriptor, pluginContext), - pass: [], - }); - } else { - presets.unshift({ - preset: yield* loadPresetDescriptor(descriptor, pluginContext), - pass: pluginDescriptorsPass, - }); - } - } catch (e) { - if (e.code === "BABEL_UNKNOWN_OPTION") { - checkNoUnwrappedItemOptionPairs(rawPresets, i, "preset", e); + return desc; + }; + + const presetsDescriptors = presets.map(toDescriptor); + const initialPluginsDescriptors = plugins.map(toDescriptor); + const pluginDescriptorsByPass: Array> = [[]]; + const passes: Array> = []; + + const ignored = yield* enhanceError( + context, + function* recursePresetDescriptors( + rawPresets: Array, + pluginDescriptorsPass: Array, + ) { + const presets: Array<{ + preset: ConfigChain | null; + pass: Array; + }> = []; + + for (let i = 0; i < rawPresets.length; i++) { + const descriptor = rawPresets[i]; + if (descriptor.options !== false) { + try { + // Presets normally run in reverse order, but if they + // have their own pass they run after the presets + // in the previous pass. + if (descriptor.ownPass) { + presets.push({ + preset: yield* loadPresetDescriptor( + descriptor, + pluginContext, + ), + pass: [], + }); + } else { + presets.unshift({ + preset: yield* loadPresetDescriptor( + descriptor, + pluginContext, + ), + pass: pluginDescriptorsPass, + }); + } + } catch (e) { + if (e.code === "BABEL_UNKNOWN_OPTION") { + checkNoUnwrappedItemOptionPairs(rawPresets, i, "preset", e); + } + throw e; } - throw e; } } - } - - // resolve presets - if (presets.length > 0) { - // The passes are created in the same order as the preset list, but are inserted before any - // existing additional passes. - pluginDescriptorsByPass.splice( - 1, - 0, - ...presets.map(o => o.pass).filter(p => p !== pluginDescriptorsPass), - ); - - for (const { preset, pass } of presets) { - if (!preset) return true; - - pass.push(...preset.plugins); - - const ignored = yield* recursePresetDescriptors(preset.presets, pass); - if (ignored) return true; - preset.options.forEach(opts => { - mergeOptions(optionDefaults, opts); - }); + // resolve presets + if (presets.length > 0) { + // The passes are created in the same order as the preset list, but are inserted before any + // existing additional passes. + pluginDescriptorsByPass.splice( + 1, + 0, + ...presets + .map(o => o.pass) + .filter(p => p !== pluginDescriptorsPass), + ); + + for (const { preset, pass } of presets) { + if (!preset) return true; + + pass.push(...preset.plugins); + + const ignored = yield* recursePresetDescriptors( + preset.presets, + pass, + ); + if (ignored) return true; + + preset.options.forEach(opts => { + mergeOptions(optionDefaults, opts); + }); + } } - } - }, - )(presetsDescriptors, pluginDescriptorsByPass[0]); - - if (ignored) return null; - - const opts: Object = optionDefaults; - mergeOptions(opts, options); - - yield* enhanceError(context, function* loadPluginDescriptors() { - pluginDescriptorsByPass[0].unshift(...initialPluginsDescriptors); - - for (const descs of pluginDescriptorsByPass) { - const pass = []; - passes.push(pass); - - for (let i = 0; i < descs.length; i++) { - const descriptor: UnloadedDescriptor = descs[i]; - if (descriptor.options !== false) { - try { - pass.push(yield* loadPluginDescriptor(descriptor, pluginContext)); - } catch (e) { - if (e.code === "BABEL_UNKNOWN_PLUGIN_PROPERTY") { - // print special message for `plugins: ["@babel/foo", { foo: "option" }]` - checkNoUnwrappedItemOptionPairs(descs, i, "plugin", e); + }, + )(presetsDescriptors, pluginDescriptorsByPass[0]); + + if (ignored) return null; + + const opts: any = optionDefaults; + mergeOptions(opts, options); + + yield* enhanceError(context, function* loadPluginDescriptors() { + pluginDescriptorsByPass[0].unshift(...initialPluginsDescriptors); + + for (const descs of pluginDescriptorsByPass) { + const pass = []; + passes.push(pass); + + for (let i = 0; i < descs.length; i++) { + const descriptor: UnloadedDescriptor = descs[i]; + if (descriptor.options !== false) { + try { + pass.push(yield* loadPluginDescriptor(descriptor, pluginContext)); + } catch (e) { + if (e.code === "BABEL_UNKNOWN_PLUGIN_PROPERTY") { + // print special message for `plugins: ["@babel/foo", { foo: "option" }]` + checkNoUnwrappedItemOptionPairs(descs, i, "plugin", e); + } + throw e; } - throw e; } } } - } - })(); - - opts.plugins = passes[0]; - opts.presets = passes - .slice(1) - .filter(plugins => plugins.length > 0) - .map(plugins => ({ plugins })); - opts.passPerPreset = opts.presets.length > 0; - - return { - options: opts, - passes: passes, - }; -}); + })(); + + opts.plugins = passes[0]; + opts.presets = passes + .slice(1) + .filter(plugins => plugins.length > 0) + .map(plugins => ({ plugins })); + opts.passPerPreset = opts.presets.length > 0; -function enhanceError(context, fn: T): T { - return (function* (arg1, arg2) { + return { + options: opts, + passes: passes, + }; + }, +); + +function enhanceError(context, fn: T): T { + return function* (arg1, arg2) { try { return yield* fn(arg1, arg2); } catch (e) { @@ -211,7 +219,7 @@ function enhanceError(context, fn: T): T { throw e; } - }: any); + } as any; } /** @@ -255,7 +263,8 @@ const makeDescriptorLoader = ( } if (isThenable(item)) { - yield* []; // if we want to support async plugins + // @ts-expect-error - if we want to support async plugins + yield* []; throw new Error( `You appear to be using a promise as a plugin, ` + @@ -270,12 +279,14 @@ const makeDescriptorLoader = ( return { value: item, options, dirname, alias }; }); -const pluginDescriptorLoader = makeDescriptorLoader( - makePluginAPI, -); -const presetDescriptorLoader = makeDescriptorLoader( - makePresetAPI, -); +const pluginDescriptorLoader = makeDescriptorLoader< + Context.SimplePlugin, + PluginAPI +>(makePluginAPI); +const presetDescriptorLoader = makeDescriptorLoader< + Context.SimplePreset, + PresetAPI +>(makePresetAPI); /** * Instantiate a plugin for the given descriptor, returning the plugin/options pair. diff --git a/packages/babel-core/src/config/helpers/config-api.ts b/packages/babel-core/src/config/helpers/config-api.ts index 24dfedc9f817..16fe7040a28e 100644 --- a/packages/babel-core/src/config/helpers/config-api.ts +++ b/packages/babel-core/src/config/helpers/config-api.ts @@ -1,14 +1,12 @@ -// @flow - import semver from "semver"; import type { Targets } from "@babel/helper-compilation-targets"; import { version as coreVersion } from "../../"; -import { - assertSimpleType, - type CacheConfigurator, - type SimpleCacheConfigurator, - type SimpleType, +import { assertSimpleType } from "../caching"; +import type { + CacheConfigurator, + SimpleCacheConfigurator, + SimpleType, } from "../caching"; import type { CallerMetadata } from "../validation/options"; @@ -16,38 +14,36 @@ import type { CallerMetadata } from "../validation/options"; import * as Context from "../cache-contexts"; type EnvFunction = { - (): string, - ((string) => T): T, - (string): boolean, - (Array): boolean, + (): string; + (extractor: (babelEnv: string) => T): T; + (envVar: string): boolean; + (envVars: Array): boolean; }; -type CallerFactory = ((CallerMetadata | void) => mixed) => SimpleType; - +type CallerFactory = ( + extractor: (callerMetadata: CallerMetadata | void) => unknown, +) => SimpleType; type TargetsFunction = () => Targets; - type AssumptionFunction = (name: string) => boolean | void; -export type ConfigAPI = {| - version: string, - cache: SimpleCacheConfigurator, - env: EnvFunction, - async: () => boolean, - assertVersion: typeof assertVersion, - caller?: CallerFactory, -|}; - -export type PresetAPI = {| - ...ConfigAPI, - targets: TargetsFunction, -|}; - -export type PluginAPI = {| - ...PresetAPI, - assumption: AssumptionFunction, -|}; - -export function makeConfigAPI( +export type ConfigAPI = { + version: string; + cache: SimpleCacheConfigurator; + env: EnvFunction; + async: () => boolean; + assertVersion: typeof assertVersion; + caller?: CallerFactory; +}; + +export type PresetAPI = { + targets: TargetsFunction; +} & ConfigAPI; + +export type PluginAPI = { + assumption: AssumptionFunction; +} & PresetAPI; + +export function makeConfigAPI( cache: CacheConfigurator, ): ConfigAPI { const env: any = value => @@ -58,7 +54,7 @@ export function makeConfigAPI( } if (!Array.isArray(value)) value = [value]; - return value.some((entry: mixed) => { + return value.some((entry: unknown) => { if (typeof entry !== "string") { throw new Error("Unexpected non-string value"); } @@ -79,7 +75,7 @@ export function makeConfigAPI( }; } -export function makePresetAPI( +export function makePresetAPI( cache: CacheConfigurator, ): PresetAPI { const targets = () => @@ -91,7 +87,7 @@ export function makePresetAPI( return { ...makeConfigAPI(cache), targets }; } -export function makePluginAPI( +export function makePluginAPI( cache: CacheConfigurator, ): PluginAPI { const assumption = name => cache.using(data => data.assumptions[name]); @@ -133,12 +129,9 @@ function assertVersion(range: string | number): void { Error.stackTraceLimit = limit; } - throw Object.assign( - err, - ({ - code: "BABEL_VERSION_UNSUPPORTED", - version: coreVersion, - range, - }: any), - ); + throw Object.assign(err, { + code: "BABEL_VERSION_UNSUPPORTED", + version: coreVersion, + range, + }); } diff --git a/packages/babel-core/src/config/helpers/environment.ts b/packages/babel-core/src/config/helpers/environment.ts index 06bd33484a4d..525bbddef250 100644 --- a/packages/babel-core/src/config/helpers/environment.ts +++ b/packages/babel-core/src/config/helpers/environment.ts @@ -1,5 +1,3 @@ -// @flow - export function getEnv(defaultValue: string = "development"): string { return process.env.BABEL_ENV || process.env.NODE_ENV || defaultValue; } diff --git a/packages/babel-core/src/config/index.ts b/packages/babel-core/src/config/index.ts index ef331f45ca51..8cab46d6977b 100644 --- a/packages/babel-core/src/config/index.ts +++ b/packages/babel-core/src/config/index.ts @@ -1,5 +1,3 @@ -// @flow - import gensync from "gensync"; export type { @@ -18,19 +16,19 @@ export { loadFullConfig as default }; export type { PartialConfig } from "./partial"; import { createConfigItem as createConfigItemImpl } from "./item"; +import type { ConfigItem } from "./item"; -const loadOptionsRunner = gensync<[mixed], Object | null>(function* (opts) { +const loadOptionsRunner = gensync<(opts: unknown) => any>(function* (opts) { const config = yield* loadFullConfig(opts); // NOTE: We want to return "null" explicitly, while ?. alone returns undefined return config?.options ?? null; }); -const createConfigItemRunner = gensync<[PluginTarget, any], Object | null>( - // $FlowIgnore - createConfigItemImpl, -); +const createConfigItemRunner = gensync< + (...args: Parameters) => ConfigItem +>(createConfigItemImpl); -const maybeErrback = runner => (opts: mixed, callback: Function) => { +const maybeErrback = runner => (opts: unknown, callback?: Function) => { if (callback === undefined && typeof opts === "function") { callback = opts; opts = undefined; @@ -51,7 +49,7 @@ export const createConfigItemAsync = createConfigItemRunner.async; export function createConfigItem( target: PluginTarget, options: any, - callback?: Function, + callback?: (err: Error, val: ConfigItem | null) => void, ) { if (callback !== undefined) { return createConfigItemRunner.errback(target, options, callback); diff --git a/packages/babel-core/src/config/item.ts b/packages/babel-core/src/config/item.ts index cf04f1bc40d3..a85a94f2be25 100644 --- a/packages/babel-core/src/config/item.ts +++ b/packages/babel-core/src/config/item.ts @@ -1,15 +1,10 @@ -// @flow - -/*:: declare var invariant; */ - import type { Handler } from "gensync"; import type { PluginTarget, PluginOptions } from "./validation/options"; import path from "path"; -import { - createDescriptor, - type UnloadedDescriptor, -} from "./config-descriptors"; +import { createDescriptor } from "./config-descriptors"; + +import type { UnloadedDescriptor } from "./config-descriptors"; export function createItemFromDescriptor(desc: UnloadedDescriptor): ConfigItem { return new ConfigItem(desc); @@ -30,8 +25,8 @@ export function* createConfigItem( dirname = ".", type, }: { - dirname?: string, - type?: "preset" | "plugin", + dirname?: string; + type?: "preset" | "plugin"; } = {}, ): Handler { const descriptor = yield* createDescriptor(value, path.resolve(dirname), { @@ -42,10 +37,9 @@ export function* createConfigItem( return createItemFromDescriptor(descriptor); } -export function getItemDescriptor(item: mixed): UnloadedDescriptor | void { - if ((item: any)?.[CONFIG_ITEM_BRAND]) { - /*:: invariant(item instanceof ConfigItem) */ - return item._descriptor; +export function getItemDescriptor(item: unknown): UnloadedDescriptor | void { + if ((item as any)?.[CONFIG_ITEM_BRAND]) { + return (item as ConfigItem)._descriptor; } return undefined; @@ -74,7 +68,6 @@ class ConfigItem { /** * Used to detect ConfigItem instances from other Babel instances. */ - // $FlowIgnore [CONFIG_ITEM_BRAND] = true; /** @@ -105,10 +98,9 @@ class ConfigItem { */ file: { // The requested path, e.g. "@babel/env". - request: string, - + request: string; // The resolved absolute path of the file. - resolved: string, + resolved: string; } | void; constructor(descriptor: UnloadedDescriptor) { diff --git a/packages/babel-core/src/config/partial.ts b/packages/babel-core/src/config/partial.ts index 11c0c3d9fccb..84dffade417d 100644 --- a/packages/babel-core/src/config/partial.ts +++ b/packages/babel-core/src/config/partial.ts @@ -1,30 +1,26 @@ -// @flow - import path from "path"; -import gensync, { type Handler } from "gensync"; +import gensync from "gensync"; +import type { Handler } from "gensync"; import Plugin from "./plugin"; import { mergeOptions } from "./util"; import { createItemFromDescriptor } from "./item"; -import { - buildRootChain, - type ConfigContext, - type FileHandling, -} from "./config-chain"; +import { buildRootChain } from "./config-chain"; +import type { ConfigContext, FileHandling } from "./config-chain"; import { getEnv } from "./helpers/environment"; -import { - validate, - type ValidatedOptions, - type NormalizedOptions, - type RootMode, +import { validate } from "./validation/options"; + +import type { + ValidatedOptions, + NormalizedOptions, + RootMode, } from "./validation/options"; import { findConfigUpwards, resolveShowConfigPath, ROOT_CONFIG_FILENAMES, - type ConfigFile, - type IgnoreFile, } from "./files"; +import type { ConfigFile, IgnoreFile } from "./files"; import { resolveTargets } from "./resolve-targets"; function* resolveRootMode( @@ -45,12 +41,12 @@ function* resolveRootMode( if (upwardRootDir !== null) return upwardRootDir; throw Object.assign( - (new Error( + new Error( `Babel was run with rootMode:"upward" but a root could not ` + `be found when searching upward from "${rootDir}".\n` + `One of the following config files must be in the directory tree: ` + `"${ROOT_CONFIG_FILENAMES.join(", ")}".`, - ): any), + ) as any, { code: "BABEL_ROOT_NOT_FOUND", dirname: rootDir, @@ -63,17 +59,17 @@ function* resolveRootMode( } type PrivPartialConfig = { - options: NormalizedOptions, - context: ConfigContext, - fileHandling: FileHandling, - ignore: IgnoreFile | void, - babelrc: ConfigFile | void, - config: ConfigFile | void, - files: Set, + options: NormalizedOptions; + context: ConfigContext; + fileHandling: FileHandling; + ignore: IgnoreFile | void; + babelrc: ConfigFile | void; + config: ConfigFile | void; + files: Set; }; export default function* loadPrivatePartialConfig( - inputOpts: mixed, + inputOpts: unknown, ): Handler { if ( inputOpts != null && @@ -121,7 +117,7 @@ export default function* loadPrivatePartialConfig( assumptions: {}, }; configChain.options.forEach(opts => { - mergeOptions((merged: any), opts); + mergeOptions(merged as any, opts); }); const options: NormalizedOptions = { @@ -163,47 +159,50 @@ export default function* loadPrivatePartialConfig( } type LoadPartialConfigOpts = { - showIgnoredFiles?: boolean, - ... + showIgnoredFiles?: boolean; }; -export const loadPartialConfig = gensync<[any], PartialConfig | null>( - function* (opts?: LoadPartialConfigOpts): Handler { - let showIgnoredFiles = false; - // We only extract showIgnoredFiles if opts is an object, so that - // loadPrivatePartialConfig can throw the appropriate error if it's not. - if (typeof opts === "object" && opts !== null && !Array.isArray(opts)) { - ({ showIgnoredFiles, ...opts } = opts); - } +export const loadPartialConfig = gensync< + (opts?: LoadPartialConfigOpts) => PartialConfig | null +>(function* (opts) { + let showIgnoredFiles = false; + // We only extract showIgnoredFiles if opts is an object, so that + // loadPrivatePartialConfig can throw the appropriate error if it's not. + if (typeof opts === "object" && opts !== null && !Array.isArray(opts)) { + ({ showIgnoredFiles, ...opts } = opts); + } + + const result: + | PrivPartialConfig + | undefined + | null = yield* loadPrivatePartialConfig(opts); + if (!result) return null; - const result: ?PrivPartialConfig = yield* loadPrivatePartialConfig(opts); - if (!result) return null; + const { options, babelrc, ignore, config, fileHandling, files } = result; - const { options, babelrc, ignore, config, fileHandling, files } = result; + if (fileHandling === "ignored" && !showIgnoredFiles) { + return null; + } - if (fileHandling === "ignored" && !showIgnoredFiles) { - return null; + (options.plugins || []).forEach(item => { + // @ts-expect-error todo(flow->ts): better type annotation for `item.value` + if (item.value instanceof Plugin) { + throw new Error( + "Passing cached plugin instances is not supported in " + + "babel.loadPartialConfig()", + ); } + }); - (options.plugins || []).forEach(item => { - if (item.value instanceof Plugin) { - throw new Error( - "Passing cached plugin instances is not supported in " + - "babel.loadPartialConfig()", - ); - } - }); - - return new PartialConfig( - options, - babelrc ? babelrc.filepath : undefined, - ignore ? ignore.filepath : undefined, - config ? config.filepath : undefined, - fileHandling, - files, - ); - }, -); + return new PartialConfig( + options, + babelrc ? babelrc.filepath : undefined, + ignore ? ignore.filepath : undefined, + config ? config.filepath : undefined, + fileHandling, + files, + ); +}); export type { PartialConfig }; diff --git a/packages/babel-core/src/config/pattern-to-regex.ts b/packages/babel-core/src/config/pattern-to-regex.ts index 8e868b3c6ffd..a5078365fdcd 100644 --- a/packages/babel-core/src/config/pattern-to-regex.ts +++ b/packages/babel-core/src/config/pattern-to-regex.ts @@ -1,4 +1,3 @@ -// @flow import path from "path"; const sep = `\\${path.sep}`; diff --git a/packages/babel-core/src/config/plugin.ts b/packages/babel-core/src/config/plugin.ts index c034a0976c31..2f958faa70ce 100644 --- a/packages/babel-core/src/config/plugin.ts +++ b/packages/babel-core/src/config/plugin.ts @@ -1,10 +1,8 @@ -// @flow - import type { PluginObject } from "./validation/plugins"; export default class Plugin { - key: ?string; - manipulateOptions: ((options: mixed, parserOpts: mixed) => void) | void; + key: string | undefined | null; + manipulateOptions: ((options: unknown, parserOpts: unknown) => void) | void; post: Function | void; pre: Function | void; visitor: {}; diff --git a/packages/babel-core/src/config/printer.ts b/packages/babel-core/src/config/printer.ts index b1f1072eddda..e052abf504df 100644 --- a/packages/babel-core/src/config/printer.ts +++ b/packages/babel-core/src/config/printer.ts @@ -1,6 +1,6 @@ -// @flow +import gensync from "gensync"; -import gensync, { type Handler } from "gensync"; +import type { Handler } from "gensync"; import type { OptionsAndDescriptors, @@ -14,19 +14,19 @@ export const ChainFormatter = { }; type PrintableConfig = { - content: OptionsAndDescriptors, - type: $Values, - callerName: ?string, - filepath: ?string, - index: ?number, - envName: ?string, + content: OptionsAndDescriptors; + type: typeof ChainFormatter[keyof typeof ChainFormatter]; + callerName: string | undefined | null; + filepath: string | undefined | null; + index: number | undefined | null; + envName: string | undefined | null; }; const Formatter = { title( - type: $Values, - callerName: ?string, - filepath: ?string, + type: typeof ChainFormatter[keyof typeof ChainFormatter], + callerName?: string | null, + filepath?: string | null, ): string { let title = ""; if (type === ChainFormatter.Programmatic) { @@ -35,12 +35,11 @@ const Formatter = { title += " from " + callerName; } } else { - // $FlowIgnore title = "config " + filepath; } return title; }, - loc(index: ?number, envName: ?string): string { + loc(index?: number | null, envName?: string | null): string { let loc = ""; if (index != null) { loc += `.overrides[${index}]`; @@ -69,7 +68,9 @@ const Formatter = { }, }; -function descriptorToConfig(d: UnloadedDescriptor): string | {} | Array { +function descriptorToConfig( + d: UnloadedDescriptor, +): string | {} | Array { let name = d.file?.request; if (name == null) { if (typeof d.value === "object") { @@ -97,14 +98,20 @@ export class ConfigPrinter { _stack: Array = []; configure( enabled: boolean, - type: $Values, - { callerName, filepath }: { callerName?: string, filepath?: string }, + type: typeof ChainFormatter[keyof typeof ChainFormatter], + { + callerName, + filepath, + }: { + callerName?: string; + filepath?: string; + }, ) { if (!enabled) return () => {}; return ( content: OptionsAndDescriptors, - index: ?number, - envName: ?string, + index?: number | null, + envName?: string | null, ) => { this._stack.push({ type, diff --git a/packages/babel-core/src/config/resolve-targets-browser.ts b/packages/babel-core/src/config/resolve-targets-browser.ts index 1b8c43429071..2d91c9216975 100644 --- a/packages/babel-core/src/config/resolve-targets-browser.ts +++ b/packages/babel-core/src/config/resolve-targets-browser.ts @@ -1,12 +1,12 @@ -// @flow - import type { ValidatedOptions } from "./validation/options"; -import getTargets, { type Targets } from "@babel/helper-compilation-targets"; +import getTargets from "@babel/helper-compilation-targets"; + +import type { Targets } from "@babel/helper-compilation-targets"; export function resolveBrowserslistConfigFile( - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars browserslistConfigFile: string, - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars configFilePath: string, ): string | void { return undefined; @@ -14,19 +14,19 @@ export function resolveBrowserslistConfigFile( export function resolveTargets( options: ValidatedOptions, - // eslint-disable-next-line no-unused-vars + // eslint-disable-next-line @typescript-eslint/no-unused-vars root: string, ): Targets { - let { targets } = options; + // todo(flow->ts) remove any and refactor to not assign different types into same variable + let targets: any = options.targets; if (typeof targets === "string" || Array.isArray(targets)) { targets = { browsers: targets }; } - // $FlowIgnore it thinks that targets.esmodules doesn't exist. if (targets && targets.esmodules) { targets = { ...targets, esmodules: "intersect" }; } - return getTargets((targets: any), { + return getTargets(targets, { ignoreBrowserslistConfig: true, browserslistEnv: options.browserslistEnv, }); diff --git a/packages/babel-core/src/config/resolve-targets.ts b/packages/babel-core/src/config/resolve-targets.ts index 72361f27abff..085292de4aa0 100644 --- a/packages/babel-core/src/config/resolve-targets.ts +++ b/packages/babel-core/src/config/resolve-targets.ts @@ -1,20 +1,20 @@ -// @flow - -import typeof * as browserType from "./resolve-targets-browser"; -import typeof * as nodeType from "./resolve-targets"; +type browserType = typeof import("./resolve-targets-browser"); +type nodeType = typeof import("./resolve-targets"); // Kind of gross, but essentially asserting that the exports of this module are the same as the // exports of index-browser, since this file may be replaced at bundle time with index-browser. -((({}: any): $Exact): $Exact); +((({} as any) as browserType) as nodeType); import type { ValidatedOptions } from "./validation/options"; import path from "path"; -import getTargets, { type Targets } from "@babel/helper-compilation-targets"; +import getTargets from "@babel/helper-compilation-targets"; + +import type { Targets } from "@babel/helper-compilation-targets"; export function resolveBrowserslistConfigFile( browserslistConfigFile: string, configFileDir: string, -): string | void { +): string | undefined { return path.resolve(configFileDir, browserslistConfigFile); } @@ -22,11 +22,11 @@ export function resolveTargets( options: ValidatedOptions, root: string, ): Targets { - let { targets } = options; + // todo(flow->ts) remove any and refactor to not assign different types into same variable + let targets: any = options.targets; if (typeof targets === "string" || Array.isArray(targets)) { targets = { browsers: targets }; } - // $FlowIgnore it thinks that targets.esmodules doesn't exist. if (targets && targets.esmodules) { targets = { ...targets, esmodules: "intersect" }; } @@ -40,7 +40,7 @@ export function resolveTargets( ignoreBrowserslistConfig = browserslistConfigFile === false; } - return getTargets((targets: any), { + return getTargets(targets, { ignoreBrowserslistConfig, configFile, configPath: root, diff --git a/packages/babel-core/src/config/util.ts b/packages/babel-core/src/config/util.ts index 891e09c90975..2bdce13b7071 100644 --- a/packages/babel-core/src/config/util.ts +++ b/packages/babel-core/src/config/util.ts @@ -1,5 +1,3 @@ -// @flow - import type { ValidatedOptions, NormalizedOptions } from "./validation/options"; export function mergeOptions( @@ -16,25 +14,22 @@ export function mergeOptions( mergeDefaultFields(targetObj, parserOpts); } else { const val = source[k]; - if (val !== undefined) target[k] = (val: any); + if (val !== undefined) target[k] = val as any; } } } -function mergeDefaultFields(target: T, source: T) { +function mergeDefaultFields(target: T, source: T) { for (const k of Object.keys(source)) { const val = source[k]; - if (val !== undefined) target[k] = (val: any); + if (val !== undefined) target[k] = val as any; } } -export function isIterableIterator(value: mixed): boolean %checks { +export function isIterableIterator(value: any): value is IterableIterator { return ( - /*:: value instanceof Generator && */ - // /*:: "@@iterator" in value && */ !!value && typeof value.next === "function" && - // $FlowIgnore typeof value[Symbol.iterator] === "function" ); } diff --git a/packages/babel-core/src/config/validation/option-assertions.ts b/packages/babel-core/src/config/validation/option-assertions.ts index 4a3d5f7410dc..e53aff72890c 100644 --- a/packages/babel-core/src/config/validation/option-assertions.ts +++ b/packages/babel-core/src/config/validation/option-assertions.ts @@ -1,5 +1,3 @@ -// @flow - import { isBrowsersQueryValid, TargetNames, @@ -29,10 +27,10 @@ import { assumptionsNames } from "./options"; export type { RootPath } from "./options"; export type ValidatorSet = { - [string]: Validator, + [name: string]: Validator; }; -export type Validator = (OptionPath, mixed) => T; +export type Validator = (loc: OptionPath, value: unknown) => T; export function msg(loc: NestingPath | GeneralPath) { switch (loc.type) { @@ -47,6 +45,7 @@ export function msg(loc: NestingPath | GeneralPath) { case "access": return `${msg(loc.parent)}[${JSON.stringify(loc.name)}]`; default: + // @ts-ignore should not happen when code is type checked throw new Error(`Assertion failure: Unknown type ${loc.type}`); } } @@ -59,19 +58,22 @@ export function access(loc: GeneralPath, name: string | number): AccessPath { }; } -export type OptionPath = $ReadOnly<{ - type: "option", - name: string, - parent: NestingPath, +export type OptionPath = Readonly<{ + type: "option"; + name: string; + parent: NestingPath; }>; -type AccessPath = $ReadOnly<{ - type: "access", - name: string | number, - parent: GeneralPath, +type AccessPath = Readonly<{ + type: "access"; + name: string | number; + parent: GeneralPath; }>; type GeneralPath = OptionPath | AccessPath; -export function assertRootMode(loc: OptionPath, value: mixed): RootMode | void { +export function assertRootMode( + loc: OptionPath, + value: unknown, +): RootMode | void { if ( value !== undefined && value !== "root" && @@ -87,7 +89,7 @@ export function assertRootMode(loc: OptionPath, value: mixed): RootMode | void { export function assertSourceMaps( loc: OptionPath, - value: mixed, + value: unknown, ): SourceMapsOption | void { if ( value !== undefined && @@ -104,7 +106,7 @@ export function assertSourceMaps( export function assertCompact( loc: OptionPath, - value: mixed, + value: unknown, ): CompactOption | void { if (value !== undefined && typeof value !== "boolean" && value !== "auto") { throw new Error(`${msg(loc)} must be a boolean, "auto", or undefined`); @@ -114,7 +116,7 @@ export function assertCompact( export function assertSourceType( loc: OptionPath, - value: mixed, + value: unknown, ): SourceTypeOption | void { if ( value !== undefined && @@ -131,11 +133,11 @@ export function assertSourceType( export function assertCallerMetadata( loc: OptionPath, - value: mixed, + value: unknown, ): CallerMetadata | void { const obj = assertObject(loc, value); if (obj) { - if (typeof obj[("name": string)] !== "string") { + if (typeof obj.name !== "string") { throw new Error( `${msg(loc)} set but does not contain "name" property string`, ); @@ -161,12 +163,13 @@ export function assertCallerMetadata( } } } - return (value: any); + // @ts-expect-error todo(flow->ts) + return value; } export function assertInputSourceMap( loc: OptionPath, - value: mixed, + value: unknown, ): RootInputSourceMapOption | void { if ( value !== undefined && @@ -178,7 +181,7 @@ export function assertInputSourceMap( return value; } -export function assertString(loc: GeneralPath, value: mixed): string | void { +export function assertString(loc: GeneralPath, value: unknown): string | void { if (value !== undefined && typeof value !== "string") { throw new Error(`${msg(loc)} must be a string, or undefined`); } @@ -187,7 +190,7 @@ export function assertString(loc: GeneralPath, value: mixed): string | void { export function assertFunction( loc: GeneralPath, - value: mixed, + value: unknown, ): Function | void { if (value !== undefined && typeof value !== "function") { throw new Error(`${msg(loc)} must be a function, or undefined`); @@ -195,7 +198,10 @@ export function assertFunction( return value; } -export function assertBoolean(loc: GeneralPath, value: mixed): boolean | void { +export function assertBoolean( + loc: GeneralPath, + value: unknown, +): boolean | void { if (value !== undefined && typeof value !== "boolean") { throw new Error(`${msg(loc)} must be a boolean, or undefined`); } @@ -204,21 +210,22 @@ export function assertBoolean(loc: GeneralPath, value: mixed): boolean | void { export function assertObject( loc: GeneralPath, - value: mixed, -): { +[string]: mixed } | void { + value: unknown, +): { readonly [key: string]: unknown } | void { if ( value !== undefined && (typeof value !== "object" || Array.isArray(value) || !value) ) { throw new Error(`${msg(loc)} must be an object, or undefined`); } + // @ts-expect-error todo(flow->ts) value is still typed as unknown, also assert function typically should not return a value return value; } -export function assertArray( +export function assertArray( loc: GeneralPath, - value: mixed, -): ?$ReadOnlyArray { + value: Array | undefined | null, +): ReadonlyArray | undefined | null { if (value != null && !Array.isArray(value)) { throw new Error(`${msg(loc)} must be an array, or undefined`); } @@ -227,15 +234,16 @@ export function assertArray( export function assertIgnoreList( loc: OptionPath, - value: mixed, + value: unknown[] | undefined, ): IgnoreList | void { const arr = assertArray(loc, value); if (arr) { arr.forEach((item, i) => assertIgnoreItem(access(loc, i), item)); } - return (arr: any); + // @ts-expect-error todo(flow->ts) + return arr; } -function assertIgnoreItem(loc: GeneralPath, value: mixed): IgnoreItem { +function assertIgnoreItem(loc: GeneralPath, value: unknown): IgnoreItem { if ( typeof value !== "string" && typeof value !== "function" && @@ -252,7 +260,7 @@ function assertIgnoreItem(loc: GeneralPath, value: mixed): IgnoreItem { export function assertConfigApplicableTest( loc: OptionPath, - value: mixed, + value: unknown, ): ConfigApplicableTest | void { if (value === undefined) return value; @@ -269,10 +277,10 @@ export function assertConfigApplicableTest( `${msg(loc)} must be a string/Function/RegExp, or an array of those`, ); } - return (value: any); + return value; } -function checkValidTest(value: mixed): boolean { +function checkValidTest(value: unknown): value is string | Function | RegExp { return ( typeof value === "string" || typeof value === "function" || @@ -282,7 +290,7 @@ function checkValidTest(value: mixed): boolean { export function assertConfigFileSearch( loc: OptionPath, - value: mixed, + value: unknown, ): ConfigFileSearch | void { if ( value !== undefined && @@ -291,7 +299,7 @@ export function assertConfigFileSearch( ) { throw new Error( `${msg(loc)} must be a undefined, a boolean, a string, ` + - `got ${JSON.stringify((value: any))}`, + `got ${JSON.stringify(value)}`, ); } @@ -300,7 +308,7 @@ export function assertConfigFileSearch( export function assertBabelrcSearch( loc: OptionPath, - value: mixed, + value: unknown, ): BabelrcSearch | void { if (value === undefined || typeof value === "boolean") return value; @@ -315,15 +323,15 @@ export function assertBabelrcSearch( } else if (!checkValidTest(value)) { throw new Error( `${msg(loc)} must be a undefined, a boolean, a string/Function/RegExp ` + - `or an array of those, got ${JSON.stringify((value: any))}`, + `or an array of those, got ${JSON.stringify(value as any)}`, ); } - return (value: any); + return value; } export function assertPluginList( loc: OptionPath, - value: mixed, + value: unknown[] | null | undefined, ): PluginList | void { const arr = assertArray(loc, value); if (arr) { @@ -331,9 +339,9 @@ export function assertPluginList( // for plugin array for use during config chain processing. arr.forEach((item, i) => assertPluginItem(access(loc, i), item)); } - return (arr: any); + return arr as any; } -function assertPluginItem(loc: GeneralPath, value: mixed): PluginItem { +function assertPluginItem(loc: GeneralPath, value: unknown): PluginItem { if (Array.isArray(value)) { if (value.length === 0) { throw new Error(`${msg(loc)} must include an object`); @@ -369,9 +377,10 @@ function assertPluginItem(loc: GeneralPath, value: mixed): PluginItem { assertPluginTarget(loc, value); } - return (value: any); + // @ts-expect-error todo(flow->ts) + return value; } -function assertPluginTarget(loc: GeneralPath, value: mixed): PluginTarget { +function assertPluginTarget(loc: GeneralPath, value: unknown): PluginTarget { if ( (typeof value !== "object" || !value) && typeof value !== "string" && @@ -384,9 +393,9 @@ function assertPluginTarget(loc: GeneralPath, value: mixed): PluginTarget { export function assertTargets( loc: GeneralPath, - value: mixed, + value: any, ): TargetsListOrObject { - if (isBrowsersQueryValid(value)) return (value: any); + if (isBrowsersQueryValid(value)) return value; if (typeof value !== "object" || !value || Array.isArray(value)) { throw new Error( @@ -416,10 +425,10 @@ export function assertTargets( } else assertBrowserVersion(subLoc, val); } - return (value: any); + return value; } -function assertBrowsersList(loc: GeneralPath, value: mixed) { +function assertBrowsersList(loc: GeneralPath, value: unknown) { if (value !== undefined && !isBrowsersQueryValid(value)) { throw new Error( `${msg(loc)} must be undefined, a string or an array of strings`, @@ -427,7 +436,7 @@ function assertBrowsersList(loc: GeneralPath, value: mixed) { } } -function assertBrowserVersion(loc: GeneralPath, value: mixed) { +function assertBrowserVersion(loc: GeneralPath, value: unknown) { if (typeof value === "number" && Math.round(value) === value) return; if (typeof value === "string") return; @@ -436,7 +445,7 @@ function assertBrowserVersion(loc: GeneralPath, value: mixed) { export function assertAssumptions( loc: GeneralPath, - value: mixed, + value: unknown, ): { [name: string]: boolean } | void { if (value === undefined) return; @@ -444,7 +453,8 @@ export function assertAssumptions( throw new Error(`${msg(loc)} must be an object or undefined.`); } - let root = loc; + // todo(flow->ts): remove any + let root: any = loc; do { root = root.parent; } while (root.type !== "root"); @@ -465,5 +475,6 @@ export function assertAssumptions( } } - return (value: any); + // @ts-expect-error todo(flow->ts) + return value; } diff --git a/packages/babel-core/src/config/validation/options.ts b/packages/babel-core/src/config/validation/options.ts index bf9cafcecda0..bb73935335ff 100644 --- a/packages/babel-core/src/config/validation/options.ts +++ b/packages/babel-core/src/config/validation/options.ts @@ -1,5 +1,3 @@ -// @flow - import type { InputTargets, Targets } from "@babel/helper-compilation-targets"; import type { ConfigItem } from "../item"; @@ -26,159 +24,99 @@ import { assertCompact, assertSourceType, assertTargets, - type ValidatorSet, - type Validator, - type OptionPath, assertAssumptions, } from "./option-assertions"; +import type { ValidatorSet, Validator, OptionPath } from "./option-assertions"; import type { UnloadedDescriptor } from "../config-descriptors"; const ROOT_VALIDATORS: ValidatorSet = { - cwd: (assertString: Validator<$PropertyType>), - root: (assertString: Validator<$PropertyType>), - rootMode: (assertRootMode: Validator< - $PropertyType, - >), - configFile: (assertConfigFileSearch: Validator< - $PropertyType, - >), - - caller: (assertCallerMetadata: Validator< - $PropertyType, - >), - filename: (assertString: Validator< - $PropertyType, - >), - filenameRelative: (assertString: Validator< - $PropertyType, - >), - code: (assertBoolean: Validator<$PropertyType>), - ast: (assertBoolean: Validator<$PropertyType>), - - cloneInputAst: (assertBoolean: Validator< - $PropertyType, - >), - - envName: (assertString: Validator< - $PropertyType, - >), + cwd: assertString as Validator, + root: assertString as Validator, + rootMode: assertRootMode as Validator, + configFile: assertConfigFileSearch as Validator< + ValidatedOptions["configFile"] + >, + + caller: assertCallerMetadata as Validator, + filename: assertString as Validator, + filenameRelative: assertString as Validator< + ValidatedOptions["filenameRelative"] + >, + code: assertBoolean as Validator, + ast: assertBoolean as Validator, + + cloneInputAst: assertBoolean as Validator, + + envName: assertString as Validator, }; const BABELRC_VALIDATORS: ValidatorSet = { - babelrc: (assertBoolean: Validator< - $PropertyType, - >), - babelrcRoots: (assertBabelrcSearch: Validator< - $PropertyType, - >), + babelrc: assertBoolean as Validator, + babelrcRoots: assertBabelrcSearch as Validator< + ValidatedOptions["babelrcRoots"] + >, }; const NONPRESET_VALIDATORS: ValidatorSet = { - extends: (assertString: Validator< - $PropertyType, - >), - ignore: (assertIgnoreList: Validator< - $PropertyType, - >), - only: (assertIgnoreList: Validator<$PropertyType>), - - targets: (assertTargets: Validator< - $PropertyType, - >), - browserslistConfigFile: (assertConfigFileSearch: Validator< - $PropertyType, - >), - browserslistEnv: (assertString: Validator< - $PropertyType, - >), + extends: assertString as Validator, + ignore: assertIgnoreList as Validator, + only: assertIgnoreList as Validator, + + targets: assertTargets as Validator, + browserslistConfigFile: assertConfigFileSearch as Validator< + ValidatedOptions["browserslistConfigFile"] + >, + browserslistEnv: assertString as Validator< + ValidatedOptions["browserslistEnv"] + >, }; const COMMON_VALIDATORS: ValidatorSet = { // TODO: Should 'inputSourceMap' be moved to be a root-only option? // We may want a boolean-only version to be a common option, with the // object only allowed as a root config argument. - inputSourceMap: (assertInputSourceMap: Validator< - $PropertyType, - >), - presets: (assertPluginList: Validator< - $PropertyType, - >), - plugins: (assertPluginList: Validator< - $PropertyType, - >), - passPerPreset: (assertBoolean: Validator< - $PropertyType, - >), - assumptions: (assertAssumptions: Validator< - $PropertyType, - >), - - env: (assertEnvSet: Validator<$PropertyType>), - overrides: (assertOverridesList: Validator< - $PropertyType, - >), + inputSourceMap: assertInputSourceMap as Validator< + ValidatedOptions["inputSourceMap"] + >, + presets: assertPluginList as Validator, + plugins: assertPluginList as Validator, + passPerPreset: assertBoolean as Validator, + assumptions: assertAssumptions as Validator, + + env: assertEnvSet as Validator, + overrides: assertOverridesList as Validator, // We could limit these to 'overrides' blocks, but it's not clear why we'd // bother, when the ability to limit a config to a specific set of files // is a fairly general useful feature. - test: (assertConfigApplicableTest: Validator< - $PropertyType, - >), - include: (assertConfigApplicableTest: Validator< - $PropertyType, - >), - exclude: (assertConfigApplicableTest: Validator< - $PropertyType, - >), - - retainLines: (assertBoolean: Validator< - $PropertyType, - >), - comments: (assertBoolean: Validator< - $PropertyType, - >), - shouldPrintComment: (assertFunction: Validator< - $PropertyType, - >), - compact: (assertCompact: Validator< - $PropertyType, - >), - minified: (assertBoolean: Validator< - $PropertyType, - >), - auxiliaryCommentBefore: (assertString: Validator< - $PropertyType, - >), - auxiliaryCommentAfter: (assertString: Validator< - $PropertyType, - >), - sourceType: (assertSourceType: Validator< - $PropertyType, - >), - wrapPluginVisitorMethod: (assertFunction: Validator< - $PropertyType, - >), - highlightCode: (assertBoolean: Validator< - $PropertyType, - >), - sourceMaps: (assertSourceMaps: Validator< - $PropertyType, - >), - sourceMap: (assertSourceMaps: Validator< - $PropertyType, - >), - sourceFileName: (assertString: Validator< - $PropertyType, - >), - sourceRoot: (assertString: Validator< - $PropertyType, - >), - parserOpts: (assertObject: Validator< - $PropertyType, - >), - generatorOpts: (assertObject: Validator< - $PropertyType, - >), + test: assertConfigApplicableTest as Validator, + include: assertConfigApplicableTest as Validator, + exclude: assertConfigApplicableTest as Validator, + + retainLines: assertBoolean as Validator, + comments: assertBoolean as Validator, + shouldPrintComment: assertFunction as Validator< + ValidatedOptions["shouldPrintComment"] + >, + compact: assertCompact as Validator, + minified: assertBoolean as Validator, + auxiliaryCommentBefore: assertString as Validator< + ValidatedOptions["auxiliaryCommentBefore"] + >, + auxiliaryCommentAfter: assertString as Validator< + ValidatedOptions["auxiliaryCommentAfter"] + >, + sourceType: assertSourceType as Validator, + wrapPluginVisitorMethod: assertFunction as Validator< + ValidatedOptions["wrapPluginVisitorMethod"] + >, + highlightCode: assertBoolean as Validator, + sourceMaps: assertSourceMaps as Validator, + sourceMap: assertSourceMaps as Validator, + sourceFileName: assertString as Validator, + sourceRoot: assertString as Validator, + parserOpts: assertObject as Validator, + generatorOpts: assertObject as Validator, }; if (!process.env.BABEL_8_BREAKING) { Object.assign(COMMON_VALIDATORS, { @@ -192,95 +130,86 @@ if (!process.env.BABEL_8_BREAKING) { export type InputOptions = ValidatedOptions; export type ValidatedOptions = { - cwd?: string, - filename?: string, - filenameRelative?: string, - babelrc?: boolean, - babelrcRoots?: BabelrcSearch, - configFile?: ConfigFileSearch, - root?: string, - rootMode?: RootMode, - code?: boolean, - ast?: boolean, - cloneInputAst?: boolean, - inputSourceMap?: RootInputSourceMapOption, - envName?: string, - caller?: CallerMetadata, - - extends?: string, - env?: EnvSet, - ignore?: IgnoreList, - only?: IgnoreList, - overrides?: OverridesList, - + cwd?: string; + filename?: string; + filenameRelative?: string; + babelrc?: boolean; + babelrcRoots?: BabelrcSearch; + configFile?: ConfigFileSearch; + root?: string; + rootMode?: RootMode; + code?: boolean; + ast?: boolean; + cloneInputAst?: boolean; + inputSourceMap?: RootInputSourceMapOption; + envName?: string; + caller?: CallerMetadata; + extends?: string; + env?: EnvSet; + ignore?: IgnoreList; + only?: IgnoreList; + overrides?: OverridesList; // Generally verify if a given config object should be applied to the given file. - test?: ConfigApplicableTest, - include?: ConfigApplicableTest, - exclude?: ConfigApplicableTest, - - presets?: PluginList, - plugins?: PluginList, - passPerPreset?: boolean, - - assumptions?: { [name: string]: boolean }, - + test?: ConfigApplicableTest; + include?: ConfigApplicableTest; + exclude?: ConfigApplicableTest; + presets?: PluginList; + plugins?: PluginList; + passPerPreset?: boolean; + assumptions?: { + [name: string]: boolean; + }; // browserslists-related options - targets?: TargetsListOrObject, - browserslistConfigFile?: ConfigFileSearch, - browserslistEnv?: string, - + targets?: TargetsListOrObject; + browserslistConfigFile?: ConfigFileSearch; + browserslistEnv?: string; // Options for @babel/generator - retainLines?: boolean, - comments?: boolean, - shouldPrintComment?: Function, - compact?: CompactOption, - minified?: boolean, - auxiliaryCommentBefore?: string, - auxiliaryCommentAfter?: string, - + retainLines?: boolean; + comments?: boolean; + shouldPrintComment?: Function; + compact?: CompactOption; + minified?: boolean; + auxiliaryCommentBefore?: string; + auxiliaryCommentAfter?: string; // Parser - sourceType?: SourceTypeOption, - - wrapPluginVisitorMethod?: Function, - highlightCode?: boolean, - + sourceType?: SourceTypeOption; + wrapPluginVisitorMethod?: Function; + highlightCode?: boolean; // Sourcemap generation options. - sourceMaps?: SourceMapsOption, - sourceMap?: SourceMapsOption, - sourceFileName?: string, - sourceRoot?: string, - + sourceMaps?: SourceMapsOption; + sourceMap?: SourceMapsOption; + sourceFileName?: string; + sourceRoot?: string; // Deprecate top level parserOpts - parserOpts?: {}, + parserOpts?: {}; // Deprecate top level generatorOpts - generatorOpts?: {}, + generatorOpts?: {}; }; export type NormalizedOptions = { - ...$Diff, - +targets: Targets, -}; + readonly targets: Targets; +} & Omit; export type CallerMetadata = { // If 'caller' is specified, require that the name is given for debugging // messages. - name: string, + name: string; }; export type EnvSet = { - [string]: ?T, + [x: string]: T; }; export type IgnoreItem = string | Function | RegExp; -export type IgnoreList = $ReadOnlyArray; +export type IgnoreList = ReadonlyArray; -export type PluginOptions = {} | void | false; -export type PluginTarget = string | {} | Function; +export type PluginOptions = object | void | false; +export type PluginTarget = string | object | Function; export type PluginItem = | ConfigItem | Plugin | PluginTarget | [PluginTarget, PluginOptions] | [PluginTarget, PluginOptions, string | void]; -export type PluginList = $ReadOnlyArray; +export type PluginList = ReadonlyArray; export type OverridesList = Array; export type ConfigApplicableTest = IgnoreItem | Array; @@ -296,7 +225,7 @@ export type RootMode = "root" | "upward" | "upward-optional"; export type TargetsListOrObject = | Targets | InputTargets - | $PropertyType; + | InputTargets["browsers"]; export type OptionsSource = | "arguments" @@ -306,20 +235,23 @@ export type OptionsSource = | "preset" | "plugin"; -export type RootPath = $ReadOnly<{ - type: "root", - source: OptionsSource, +export type RootPath = Readonly<{ + type: "root"; + source: OptionsSource; }>; -type OverridesPath = $ReadOnly<{ - type: "overrides", - index: number, - parent: RootPath, + +type OverridesPath = Readonly<{ + type: "overrides"; + index: number; + parent: RootPath; }>; -type EnvPath = $ReadOnly<{ - type: "env", - name: string, - parent: RootPath | OverridesPath, + +type EnvPath = Readonly<{ + type: "env"; + name: string; + parent: RootPath | OverridesPath; }>; + export type NestingPath = RootPath | OverridesPath | EnvPath; export const assumptionsNames = new Set([ @@ -369,7 +301,7 @@ function validateNested(loc: NestingPath, opts: {}) { type: "option", name: key, parent: loc, - }; + } as const; if (type === "preset" && NONPRESET_VALIDATORS[key]) { throw new Error(`${msg(optLoc)} is not allowed in preset options`); @@ -405,22 +337,19 @@ function validateNested(loc: NestingPath, opts: {}) { NONPRESET_VALIDATORS[key] || BABELRC_VALIDATORS[key] || ROOT_VALIDATORS[key] || - (throwUnknownError: Validator); + (throwUnknownError as Validator); validator(optLoc, opts[key]); }); - return (opts: any); + return opts; } function throwUnknownError(loc: OptionPath) { const key = loc.name; if (removed[key]) { - const { - message, - version = 5, - }: { message: string, version?: number } = removed[key]; + const { message, version = 5 } = removed[key]; throw new Error( `Using removed Babel ${version} option: ${msg(loc)} - ${message}`, @@ -432,7 +361,7 @@ function throwUnknownError(loc: OptionPath) { loc, )}. Check out https://babeljs.io/docs/en/babel-core/#options for more information about options.`, ); - // $FlowIgnore + // @ts-expect-error todo(flow->ts): consider creating something like BabelConfigError with code field in it unknownOptErr.code = "BABEL_UNKNOWN_OPTION"; throw unknownOptErr; @@ -449,7 +378,10 @@ function assertNoDuplicateSourcemap(opts: {}): void { } } -function assertEnvSet(loc: OptionPath, value: mixed): EnvSet { +function assertEnvSet( + loc: OptionPath, + value: unknown, +): void | EnvSet { if (loc.parent.type === "env") { throw new Error(`${msg(loc)} is not allowed inside of another .env block`); } @@ -467,14 +399,17 @@ function assertEnvSet(loc: OptionPath, value: mixed): EnvSet { type: "env", name: envName, parent, - }; + } as const; validateNested(envLoc, env); } } - return (obj: any); + return obj; } -function assertOverridesList(loc: OptionPath, value: mixed): OverridesList { +function assertOverridesList( + loc: OptionPath, + value: unknown[], +): undefined | OverridesList { if (loc.parent.type === "env") { throw new Error(`${msg(loc)} is not allowed inside an .env block`); } @@ -494,11 +429,12 @@ function assertOverridesList(loc: OptionPath, value: mixed): OverridesList { type: "overrides", index, parent, - }; + } as const; validateNested(overridesLoc, env); } } - return (arr: any); + // @ts-expect-error + return arr; } export function checkNoUnwrappedItemOptionPairs( diff --git a/packages/babel-core/src/config/validation/plugins.ts b/packages/babel-core/src/config/validation/plugins.ts index 63510dd13c25..9731b4b36dc2 100644 --- a/packages/babel-core/src/config/validation/plugins.ts +++ b/packages/babel-core/src/config/validation/plugins.ts @@ -1,45 +1,42 @@ -// @flow import { assertString, assertFunction, assertObject, msg, - type ValidatorSet, - type Validator, - type OptionPath, - type RootPath, +} from "./option-assertions"; + +import type { + ValidatorSet, + Validator, + OptionPath, + RootPath, } from "./option-assertions"; // Note: The casts here are just meant to be static assertions to make sure // that the assertion functions actually assert that the value's type matches // the declared types. const VALIDATORS: ValidatorSet = { - name: (assertString: Validator<$PropertyType>), - manipulateOptions: (assertFunction: Validator< - $PropertyType, - >), - pre: (assertFunction: Validator<$PropertyType>), - post: (assertFunction: Validator<$PropertyType>), - inherits: (assertFunction: Validator< - $PropertyType, - >), - visitor: (assertVisitorMap: Validator< - $PropertyType, - >), + name: assertString as Validator, + manipulateOptions: assertFunction as Validator< + PluginObject["manipulateOptions"] + >, + pre: assertFunction as Validator, + post: assertFunction as Validator, + inherits: assertFunction as Validator, + visitor: assertVisitorMap as Validator, - parserOverride: (assertFunction: Validator< - $PropertyType, - >), - generatorOverride: (assertFunction: Validator< - $PropertyType, - >), + parserOverride: assertFunction as Validator, + generatorOverride: assertFunction as Validator< + PluginObject["generatorOverride"] + >, }; -function assertVisitorMap(loc: OptionPath, value: mixed): VisitorMap { +function assertVisitorMap(loc: OptionPath, value: unknown): VisitorMap { const obj = assertObject(loc, value); if (obj) { Object.keys(obj).forEach(prop => assertVisitorHandler(prop, obj[prop])); + // @ts-ignore if (obj.enter || obj.exit) { throw new Error( `${msg( @@ -48,12 +45,12 @@ function assertVisitorMap(loc: OptionPath, value: mixed): VisitorMap { ); } } - return (obj: any); + return obj as VisitorMap; } function assertVisitorHandler( key: string, - value: mixed, + value: unknown, ): VisitorHandler | void { if (value && typeof value === "object") { Object.keys(value).forEach((handler: string) => { @@ -67,26 +64,29 @@ function assertVisitorHandler( throw new Error(`.visitor["${key}"] must be a function`); } - return (value: any); + return value as any; } -type VisitorHandler = Function | { enter?: Function, exit?: Function }; +type VisitorHandler = + | Function + | { + enter?: Function; + exit?: Function; + }; + export type VisitorMap = { - [string]: VisitorHandler, + [x: string]: VisitorHandler; }; export type PluginObject = { - name?: string, - manipulateOptions?: (options: mixed, parserOpts: mixed) => void, - - pre?: Function, - post?: Function, - - inherits?: Function, - visitor?: VisitorMap, - - parserOverride?: Function, - generatorOverride?: Function, + name?: string; + manipulateOptions?: (options: unknown, parserOpts: unknown) => void; + pre?: Function; + post?: Function; + inherits?: Function; + visitor?: VisitorMap; + parserOverride?: Function; + generatorOverride?: Function; }; export function validatePluginObject(obj: {}): PluginObject { @@ -108,11 +108,11 @@ export function validatePluginObject(obj: {}): PluginObject { const invalidPluginPropertyError = new Error( `.${key} is not a valid Plugin property`, ); - // $FlowIgnore + // @ts-expect-error todo(flow->ts) consider additing BabelConfigError with code field invalidPluginPropertyError.code = "BABEL_UNKNOWN_PLUGIN_PROPERTY"; throw invalidPluginPropertyError; } }); - return (obj: any); + return obj as any; } diff --git a/packages/babel-core/src/config/validation/removed.ts b/packages/babel-core/src/config/validation/removed.ts index 79a321917af2..ff49f2706f59 100644 --- a/packages/babel-core/src/config/validation/removed.ts +++ b/packages/babel-core/src/config/validation/removed.ts @@ -1,5 +1,3 @@ -// @flow - export default { auxiliaryComment: { message: "Use `auxiliaryCommentBefore` or `auxiliaryCommentAfter`", @@ -64,17 +62,15 @@ export default { version: 6, message: "Use `babel-plugin-module-resolver@3`'s 'resolvePath' options", }, - metadata: { version: 6, message: "Generated plugin metadata is always included in the output result", }, - sourceMapTarget: { version: 6, message: "The `sourceMapTarget` option has been removed because it makes more sense for the tooling " + "that calls Babel to assign `map.file` themselves.", }, -}; +} as { [name: string]: { version?: number; message: string } }; diff --git a/packages/babel-core/src/gensync-utils/async.ts b/packages/babel-core/src/gensync-utils/async.ts index 2b474aeed692..d0d9c16c3134 100644 --- a/packages/babel-core/src/gensync-utils/async.ts +++ b/packages/babel-core/src/gensync-utils/async.ts @@ -1,18 +1,26 @@ -// @flow - -import gensync, { type Gensync, type Handler } from "gensync"; +import gensync from "gensync"; +import type { Gensync, Handler } from "gensync"; type MaybePromise = T | Promise; const id = x => x; -const runGenerator = gensync(function* (item) { +const runGenerator: { + sync(gen: Generator): Return; + async(gen: Generator): Promise; + errback( + gen: Generator, + cb: (err: Error, val: Return) => void, + ): void; +} = gensync<(item: Generator) => any>(function* ( + item: Generator, +) { return yield* item; }); // This Gensync returns true if the current execution context is // asynchronous, otherwise it returns false. -export const isAsync = gensync<[], boolean>({ +export const isAsync = gensync<() => boolean>({ sync: () => false, errback: cb => cb(null, true), }); @@ -22,13 +30,13 @@ export const isAsync = gensync<[], boolean>({ // but the current execution context is synchronous, it will throw the // provided error. // This is used to handle user-provided functions which could be asynchronous. -export function maybeAsync( - fn: (...args: Args) => T, +export function maybeAsync any>( + fn: Fn, message: string, -): Gensync { +): Gensync { return gensync({ sync(...args) { - const result = fn.apply(this, args); + const result = fn.apply(this, args) as ReturnType; if (isThenable(result)) throw new Error(message); return result; }, @@ -38,10 +46,10 @@ export function maybeAsync( }); } -const withKind = (gensync<[any], any>({ +const withKind = gensync<(cb: (kind: "sync" | "async") => any) => any>({ sync: cb => cb("sync"), async: cb => cb("async"), -}): (cb: (kind: "sync" | "async") => MaybePromise) => Handler); +}) as (cb: (kind: "sync" | "async") => MaybePromise) => Handler; // This function wraps a generator (or a Gensync) into another function which, // when called, will run the provided generator in a sync or async way, depending @@ -57,14 +65,17 @@ const withKind = (gensync<[any], any>({ // return wrappedFn(x); // }) // ) -export function forwardAsync( - action: (...args: ActionArgs) => Handler, +export function forwardAsync< + Action extends (...args: unknown[]) => any, + Return +>( + action: (...args: Parameters) => Handler>, cb: ( - adapted: (...args: ActionArgs) => MaybePromise, + adapted: (...args: Parameters) => MaybePromise>, ) => MaybePromise, ): Handler { - const g = gensync(action); - return withKind(kind => { + const g = gensync(action); + return withKind(kind => { const adapted = g[kind]; return cb(adapted); }); @@ -73,7 +84,7 @@ export function forwardAsync( // If the given generator is executed asynchronously, the first time that it // is paused (i.e. When it yields a gensync generator which can't be run // synchronously), call the "firstPause" callback. -export const onFirstPause = (gensync<[any, any], any>({ +export const onFirstPause = gensync<(gen: Generator, cb: Function) => any>({ name: "onFirstPause", arity: 2, sync: function (item) { @@ -91,17 +102,16 @@ export const onFirstPause = (gensync<[any, any], any>({ firstPause(); } }, -}): (gen: Generator<*, T, *>, cb: Function) => Handler); +}) as (gen: Generator, cb: Function) => Handler; // Wait for the given promise to be resolved -export const waitFor = (gensync<[any], any>({ +export const waitFor = gensync({ sync: id, async: id, -}): (p: T | Promise) => Handler); +}) as (p: T | Promise) => Handler; -export function isThenable(val: mixed): boolean %checks { +export function isThenable(val: any): val is PromiseLike { return ( - /*:: val instanceof Promise && */ !!val && (typeof val === "object" || typeof val === "function") && !!val.then && diff --git a/packages/babel-core/src/gensync-utils/fs.ts b/packages/babel-core/src/gensync-utils/fs.ts index 172fa6a9d024..83681472f276 100644 --- a/packages/babel-core/src/gensync-utils/fs.ts +++ b/packages/babel-core/src/gensync-utils/fs.ts @@ -1,14 +1,14 @@ -// @flow - import fs from "fs"; import gensync from "gensync"; -export const readFile = gensync<[string, "utf8"], string>({ - sync: fs.readFileSync, - errback: fs.readFile, -}); +export const readFile = gensync<(filepath: string, encoding: "utf8") => string>( + { + sync: fs.readFileSync, + errback: fs.readFile, + }, +); -export const exists = gensync<[string], boolean>({ +export const exists = gensync<(filepath: string) => boolean>({ sync(path) { try { fs.accessSync(path); @@ -20,7 +20,7 @@ export const exists = gensync<[string], boolean>({ errback: (path, cb) => fs.access(path, undefined, err => cb(null, !err)), }); -export const stat = gensync<[string], *>({ +export const stat = gensync({ sync: fs.statSync, errback: fs.stat, }); diff --git a/packages/babel-core/src/gensync-utils/gensync.d.ts b/packages/babel-core/src/gensync-utils/gensync.d.ts new file mode 100644 index 000000000000..9e84f0d471d2 --- /dev/null +++ b/packages/babel-core/src/gensync-utils/gensync.d.ts @@ -0,0 +1,37 @@ +declare module "gensync" { + declare type Next = undefined | Function; + declare type Yield = mixed; + + declare type Callback = + | ((err: Error, val: Return) => void) + | ((err: unknown) => void); + + export type Gensync any> = { + (...args: Parameters): Handler>; + sync(...args: Parameters): ReturnType; + async(...args: Parameters): Promise>; + errback(...args: [...Parameters, Callback>]): void; + }; + + export type Handler = Generator; + export type Options any> = { + sync(...args: Parameters): ReturnType; + arity?: number; + name?: string; + } & ( + | { async?: (...args: Parameters) => Promise> } + | { + errback(...args: [...Parameters, Callback>]): void; + } + ); + + declare const gensync: { + any>( + _: Options | ((...args: Parameters) => Handler>), + ): Gensync; + + all(gensyncs: Array>): Handler; + race(gensyncs: Array>): Handler; + }; + export default gensync; +} diff --git a/packages/babel-core/src/index.ts b/packages/babel-core/src/index.ts index 7cabc44ae6b2..f938b3c7d3d4 100644 --- a/packages/babel-core/src/index.ts +++ b/packages/babel-core/src/index.ts @@ -1,5 +1,4 @@ -// @flow - +declare const PACKAGE_JSON: { name: string; version: string }; export const version = PACKAGE_JSON.version; export { default as File } from "./transformation/file/file"; @@ -53,7 +52,7 @@ export const DEFAULT_EXTENSIONS = Object.freeze([ ".es", ".mjs", ".cjs", -]); +] as const); // For easier backward-compatibility, provide an API like the one we exposed in Babel 6. import { loadOptions } from "./config"; diff --git a/packages/babel-core/src/parse.ts b/packages/babel-core/src/parse.ts index 3609df763530..79fd13ffe209 100644 --- a/packages/babel-core/src/parse.ts +++ b/packages/babel-core/src/parse.ts @@ -1,39 +1,39 @@ -// @flow - import gensync from "gensync"; -import loadConfig, { type InputOptions } from "./config"; +import loadConfig from "./config"; +import type { InputOptions } from "./config"; import parser from "./parser"; import type { ParseResult } from "./parser"; import normalizeOptions from "./transformation/normalize-opts"; type FileParseCallback = { - (Error, null): any, - (null, ParseResult | null): any, + (err: Error, ast: null): any; + (err: null, ast: ParseResult | null): any; }; type Parse = { - (code: string, callback: FileParseCallback): void, - (code: string, opts: ?InputOptions, callback: FileParseCallback): void, - - // Here for backward-compatibility. Ideally use ".parseSync" if you want - // a synchronous API. - (code: string, opts: ?InputOptions): ParseResult | null, + (code: string, callback: FileParseCallback): void; + ( + code: string, + opts: InputOptions | undefined | null, + callback: FileParseCallback, + ): void; + (code: string, opts?: InputOptions | null): ParseResult | null; }; -const parseRunner = gensync<[string, ?InputOptions], ParseResult | null>( - function* parse(code, opts) { - const config = yield* loadConfig(opts); +const parseRunner = gensync< + (code: string, opts: InputOptions | undefined | null) => ParseResult | null +>(function* parse(code, opts) { + const config = yield* loadConfig(opts); - if (config === null) { - return null; - } + if (config === null) { + return null; + } - return yield* parser(config.passes, normalizeOptions(config), code); - }, -); + return yield* parser(config.passes, normalizeOptions(config), code); +}); -export const parse: Parse = (function parse(code, opts, callback) { +export const parse: Parse = function parse(code, opts?, callback?) { if (typeof opts === "function") { callback = opts; opts = undefined; @@ -44,7 +44,7 @@ export const parse: Parse = (function parse(code, opts, callback) { if (callback === undefined) return parseRunner.sync(code, opts); parseRunner.errback(code, opts, callback); -}: Function); +}; export const parseSync = parseRunner.sync; export const parseAsync = parseRunner.async; diff --git a/packages/babel-core/src/parser/index.ts b/packages/babel-core/src/parser/index.ts index 3ddf5ca4b64d..44c5b684a151 100644 --- a/packages/babel-core/src/parser/index.ts +++ b/packages/babel-core/src/parser/index.ts @@ -1,15 +1,17 @@ import type { Handler } from "gensync"; import { parse } from "@babel/parser"; +import type * as t from "@babel/types"; import { codeFrameColumns } from "@babel/code-frame"; import generateMissingPluginMessage from "./util/missing-plugin-helper"; +import type { PluginPasses } from "../config"; -type AstRoot = BabelNodeFile | BabelNodeProgram; +type AstRoot = t.File | t.Program; export type ParseResult = AstRoot; export default function* parser( pluginPasses: PluginPasses, - { parserOpts, highlightCode = true, filename = "unknown" }: Object, + { parserOpts, highlightCode = true, filename = "unknown" }: any, code: string, ): Handler { try { @@ -28,7 +30,8 @@ export default function* parser( if (results.length === 0) { return parse(code, parserOpts); } else if (results.length === 1) { - yield* []; // If we want to allow async parsers + // @ts-expect-error - If we want to allow async parsers + yield* []; if (typeof results[0].then === "function") { throw new Error( `You appear to be using an async parser plugin, ` + diff --git a/packages/babel-core/src/parser/util/missing-plugin-helper.ts b/packages/babel-core/src/parser/util/missing-plugin-helper.ts index b1a949746d51..93ed711c5c14 100644 --- a/packages/babel-core/src/parser/util/missing-plugin-helper.ts +++ b/packages/babel-core/src/parser/util/missing-plugin-helper.ts @@ -1,5 +1,3 @@ -// @flow - const pluginNameMap = { classProperties: { syntax: { @@ -290,7 +288,10 @@ to enable [parsing|transformation]. */ export default function generateMissingPluginMessage( missingPluginName: string, - loc: { line: number, column: number }, + loc: { + line: number; + column: number; + }, codeFrame: string, ): string { let helpMessage = diff --git a/packages/babel-core/src/tools/build-external-helpers.ts b/packages/babel-core/src/tools/build-external-helpers.ts index ef9120229aaa..0e9cfa9a1cf5 100644 --- a/packages/babel-core/src/tools/build-external-helpers.ts +++ b/packages/babel-core/src/tools/build-external-helpers.ts @@ -105,7 +105,7 @@ function buildUmd(allowlist) { AMD_ARGUMENTS: t.arrayExpression([t.stringLiteral("exports")]), FACTORY_BODY: body, UMD_ROOT: t.identifier("this"), - }), + }) as t.Statement, ]); } diff --git a/packages/babel-core/src/transform-ast.ts b/packages/babel-core/src/transform-ast.ts index 5a6f0993d898..5a482dead628 100644 --- a/packages/babel-core/src/transform-ast.ts +++ b/packages/babel-core/src/transform-ast.ts @@ -1,33 +1,30 @@ -// @flow - import gensync from "gensync"; -import loadConfig, { type InputOptions, type ResolvedConfig } from "./config"; -import { - run, - type FileResult, - type FileResultCallback, -} from "./transformation"; +import loadConfig from "./config"; +import type { InputOptions, ResolvedConfig } from "./config"; +import { run } from "./transformation"; +import type * as t from "@babel/types"; -type AstRoot = BabelNodeFile | BabelNodeProgram; +import type { FileResult, FileResultCallback } from "./transformation"; +type AstRoot = t.File | t.Program; type TransformFromAst = { - (ast: AstRoot, code: string, callback: FileResultCallback): void, + (ast: AstRoot, code: string, callback: FileResultCallback): void; ( ast: AstRoot, code: string, - opts: ?InputOptions, + opts: InputOptions | undefined | null, callback: FileResultCallback, - ): void, - - // Here for backward-compatibility. Ideally use ".transformSync" if you want - // a synchronous API. - (ast: AstRoot, code: string, opts: ?InputOptions): FileResult | null, + ): void; + (ast: AstRoot, code: string, opts?: InputOptions | null): FileResult | null; }; const transformFromAstRunner = gensync< - [AstRoot, string, ?InputOptions], - FileResult | null, + ( + ast: AstRoot, + code: string, + opts: InputOptions | undefined | null, + ) => FileResult | null >(function* (ast, code, opts) { const config: ResolvedConfig | null = yield* loadConfig(opts); if (config === null) return null; @@ -37,11 +34,11 @@ const transformFromAstRunner = gensync< return yield* run(config, code, ast); }); -export const transformFromAst: TransformFromAst = (function transformFromAst( +export const transformFromAst: TransformFromAst = function transformFromAst( ast, code, opts, - callback, + callback?, ) { if (typeof opts === "function") { callback = opts; @@ -55,7 +52,7 @@ export const transformFromAst: TransformFromAst = (function transformFromAst( } transformFromAstRunner.errback(ast, code, opts, callback); -}: Function); +}; export const transformFromAstSync = transformFromAstRunner.sync; export const transformFromAstAsync = transformFromAstRunner.async; diff --git a/packages/babel-core/src/transform-file-browser.ts b/packages/babel-core/src/transform-file-browser.ts index fc1b12af0e50..1adbcd649b52 100644 --- a/packages/babel-core/src/transform-file-browser.ts +++ b/packages/babel-core/src/transform-file-browser.ts @@ -1,24 +1,22 @@ -// @flow - // duplicated from transform-file so we do not have to import anything here type TransformFile = { - (filename: string, callback: Function): void, - (filename: string, opts: ?Object, callback: Function): void, + (filename: string, callback: Function): void; + (filename: string, opts: any, callback: Function): void; }; -export const transformFile: TransformFile = (function transformFile( +export const transformFile: TransformFile = function transformFile( filename, opts, - callback, + callback?, ) { if (typeof opts === "function") { callback = opts; } callback(new Error("Transforming files is not supported in browsers"), null); -}: Function); +}; -export function transformFileSync() { +export function transformFileSync(): never { throw new Error("Transforming files is not supported in browsers"); } diff --git a/packages/babel-core/src/transform-file.ts b/packages/babel-core/src/transform-file.ts index 480afc1cf6f7..be27260c84ef 100644 --- a/packages/babel-core/src/transform-file.ts +++ b/packages/babel-core/src/transform-file.ts @@ -1,40 +1,40 @@ -// @flow - import gensync from "gensync"; -import loadConfig, { type InputOptions, type ResolvedConfig } from "./config"; -import { - run, - type FileResult, - type FileResultCallback, -} from "./transformation"; +import loadConfig from "./config"; +import type { InputOptions, ResolvedConfig } from "./config"; +import { run } from "./transformation"; +import type { FileResult, FileResultCallback } from "./transformation"; import * as fs from "./gensync-utils/fs"; -import typeof * as transformFileBrowserType from "./transform-file-browser"; -import typeof * as transformFileType from "./transform-file"; +type transformFileBrowserType = typeof import("./transform-file-browser"); +type transformFileType = typeof import("./transform-file"); // Kind of gross, but essentially asserting that the exports of this module are the same as the // exports of transform-file-browser, since this file may be replaced at bundle time with // transform-file-browser. -((({}: any): $Exact): $Exact); +((({} as any) as transformFileBrowserType) as transformFileType); type TransformFile = { - (filename: string, callback: FileResultCallback): void, - (filename: string, opts: ?InputOptions, callback: FileResultCallback): void, + (filename: string, callback: FileResultCallback): void; + ( + filename: string, + opts: InputOptions | undefined | null, + callback: FileResultCallback, + ): void; }; -const transformFileRunner = gensync<[string, ?InputOptions], FileResult | null>( - function* (filename, opts) { - const options = { ...opts, filename }; +const transformFileRunner = gensync< + (filename: string, opts?: InputOptions) => FileResult | null +>(function* (filename, opts: InputOptions) { + const options = { ...opts, filename }; - const config: ResolvedConfig | null = yield* loadConfig(options); - if (config === null) return null; + const config: ResolvedConfig | null = yield* loadConfig(options); + if (config === null) return null; - const code = yield* fs.readFile(filename, "utf8"); - return yield* run(config, code); - }, -); + const code = yield* fs.readFile(filename, "utf8"); + return yield* run(config, code); +}); -export const transformFile: TransformFile = transformFileRunner.errback; +export const transformFile = transformFileRunner.errback as TransformFile; export const transformFileSync = transformFileRunner.sync; export const transformFileAsync = transformFileRunner.async; diff --git a/packages/babel-core/src/transform.ts b/packages/babel-core/src/transform.ts index 0ace438c29fa..5bc4fca5ab4a 100644 --- a/packages/babel-core/src/transform.ts +++ b/packages/babel-core/src/transform.ts @@ -1,33 +1,31 @@ -// @flow - import gensync from "gensync"; -import loadConfig, { type InputOptions, type ResolvedConfig } from "./config"; -import { - run, - type FileResult, - type FileResultCallback, -} from "./transformation"; +import loadConfig from "./config"; +import type { InputOptions, ResolvedConfig } from "./config"; +import { run } from "./transformation"; -type Transform = { - (code: string, callback: FileResultCallback): void, - (code: string, opts: ?InputOptions, callback: FileResultCallback): void, +import type { FileResult, FileResultCallback } from "./transformation"; - // Here for backward-compatibility. Ideally use ".transformSync" if you want - // a synchronous API. - (code: string, opts: ?InputOptions): FileResult | null, +type Transform = { + (code: string, callback: FileResultCallback): void; + ( + code: string, + opts: InputOptions | undefined | null, + callback: FileResultCallback, + ): void; + (code: string, opts?: InputOptions | null): FileResult | null; }; -const transformRunner = gensync<[string, ?InputOptions], FileResult | null>( - function* transform(code, opts) { - const config: ResolvedConfig | null = yield* loadConfig(opts); - if (config === null) return null; +const transformRunner = gensync< + (code: string, opts?: InputOptions) => FileResult | null +>(function* transform(code, opts) { + const config: ResolvedConfig | null = yield* loadConfig(opts); + if (config === null) return null; - return yield* run(config, code); - }, -); + return yield* run(config, code); +}); -export const transform: Transform = (function transform(code, opts, callback) { +export const transform: Transform = function transform(code, opts?, callback?) { if (typeof opts === "function") { callback = opts; opts = undefined; @@ -38,7 +36,7 @@ export const transform: Transform = (function transform(code, opts, callback) { if (callback === undefined) return transformRunner.sync(code, opts); transformRunner.errback(code, opts, callback); -}: Function); +}; export const transformSync = transformRunner.sync; export const transformAsync = transformRunner.async; diff --git a/packages/babel-core/src/transformation/block-hoist-plugin.ts b/packages/babel-core/src/transformation/block-hoist-plugin.ts index 788b0e0a35dc..a2215ae17488 100644 --- a/packages/babel-core/src/transformation/block-hoist-plugin.ts +++ b/packages/babel-core/src/transformation/block-hoist-plugin.ts @@ -1,6 +1,6 @@ -// @flow +import loadConfig from "../config"; -import loadConfig, { type Plugin } from "../config"; +import type { Plugin } from "../config"; let LOADED_PLUGIN: Plugin | void; diff --git a/packages/babel-core/src/transformation/file/file.ts b/packages/babel-core/src/transformation/file/file.ts index 7037465248dd..703afe204e1e 100644 --- a/packages/babel-core/src/transformation/file/file.ts +++ b/packages/babel-core/src/transformation/file/file.ts @@ -1,7 +1,6 @@ -// @flow - import * as helpers from "@babel/helpers"; -import { NodePath, Scope, type HubInterface } from "@babel/traverse"; +import { NodePath, Scope } from "@babel/traverse"; +import type { HubInterface } from "@babel/traverse"; import { codeFrameColumns } from "@babel/code-frame"; import traverse from "@babel/traverse"; import * as t from "@babel/types"; @@ -22,27 +21,39 @@ const errorVisitor = { export type NodeLocation = { loc?: { - end?: { line: number, column: number }, - start: { line: number, column: number }, - }, + end?: { + line: number; + column: number; + }; + start: { + line: number; + column: number; + }; + }; _loc?: { - end?: { line: number, column: number }, - start: { line: number, column: number }, - }, + end?: { + line: number; + column: number; + }; + start: { + line: number; + column: number; + }; + }; }; export default class File { _map: Map = new Map(); - opts: Object; - declarations: Object = {}; - path: NodePath = null; - ast: Object = {}; + opts: any; + declarations: any = {}; + path: NodePath = null; + ast: any = {}; scope: Scope; metadata: {} = {}; code: string = ""; - inputMap: Object | null = null; + inputMap: any | null = null; - hub: HubInterface = { + hub: HubInterface & { file: File } = { // keep it for the usage in babel-core, ex: path.hub.file.opts.filename file: this, getCode: () => this.code, @@ -63,7 +74,7 @@ export default class File { parent: this.ast, container: this.ast, key: "program", - }).setContext(); + }).setContext() as NodePath; this.scope = this.path.scope; } @@ -76,7 +87,7 @@ export default class File { const { interpreter } = this.path.node; return interpreter ? interpreter.value : ""; } - set shebang(value: string): void { + set shebang(value: string) { if (value) { this.path.get("interpreter").replaceWith(t.interpreterDirective(value)); } else { @@ -84,7 +95,7 @@ export default class File { } } - set(key: mixed, val: mixed) { + set(key: unknown, val: unknown) { if (key === "helpersNamespace") { throw new Error( "Babel 7.0.0-beta.56 has dropped support for the 'helpersNamespace' utility." + @@ -98,15 +109,15 @@ export default class File { this._map.set(key, val); } - get(key: mixed): any { + get(key: unknown): any { return this._map.get(key); } - has(key: mixed): boolean { + has(key: unknown): boolean { return this._map.has(key); } - getModuleName(): ?string { + getModuleName(): string | undefined | null { return getModuleName(this.opts, this.opts); } @@ -126,7 +137,7 @@ export default class File { * helper exists, but was not available for the full given range, it will be * considered unavailable. */ - availableHelper(name: string, versionRange: ?string): boolean { + availableHelper(name: string, versionRange?: string | null): boolean { let minVersion; try { minVersion = helpers.minVersion(name); @@ -163,7 +174,7 @@ export default class File { ); } - addHelper(name: string): Object { + addHelper(name: string): any { const declar = this.declarations[name]; if (declar) return t.cloneNode(declar); @@ -220,9 +231,9 @@ export default class File { } buildCodeFrameError( - node: ?NodeLocation, + node: NodeLocation | undefined | null, msg: string, - Error: typeof Error = SyntaxError, + _Error: typeof Error = SyntaxError, ): Error { let loc = node && (node.loc || node._loc); @@ -230,7 +241,7 @@ export default class File { const state = { loc: null, }; - traverse(node, errorVisitor, this.scope, state); + traverse(node as t.Node, errorVisitor, this.scope, state); loc = state.loc; let txt = @@ -264,6 +275,6 @@ export default class File { ); } - return new Error(msg); + return new _Error(msg); } } diff --git a/packages/babel-core/src/transformation/file/generate.ts b/packages/babel-core/src/transformation/file/generate.ts index b07851fe9cba..7248d2509961 100644 --- a/packages/babel-core/src/transformation/file/generate.ts +++ b/packages/babel-core/src/transformation/file/generate.ts @@ -1,7 +1,6 @@ -// @flow - import type { PluginPasses } from "../../config"; -import convertSourceMap, { typeof SourceMap } from "convert-source-map"; +import convertSourceMap from "convert-source-map"; +type SourceMap = any; import generate from "@babel/generator"; import type File from "./file"; @@ -11,8 +10,8 @@ export default function generateCode( pluginPasses: PluginPasses, file: File, ): { - outputCode: string, - outputMap: SourceMap | null, + outputCode: string; + outputMap: SourceMap | null; } { const { opts, ast, code, inputMap } = file; diff --git a/packages/babel-core/src/transformation/file/merge-map.ts b/packages/babel-core/src/transformation/file/merge-map.ts index 911867d96890..4ffdb0d5b9df 100644 --- a/packages/babel-core/src/transformation/file/merge-map.ts +++ b/packages/babel-core/src/transformation/file/merge-map.ts @@ -1,6 +1,4 @@ -// @flow - -import typeof { SourceMap } from "convert-source-map"; +type SourceMap = any; import sourceMap from "source-map"; export default function mergeSourceMap( @@ -74,6 +72,7 @@ export default function mergeSourceMap( // Insert mappings with no original position to terminate any mappings // that were found above, so that they don't expand beyond their correct // range. + // @ts-expect-error todo(flow->ts) original and source field are missing mergedGenerator.addMapping({ generated: { line: clearItem.line, @@ -93,14 +92,14 @@ export default function mergeSourceMap( return result; } -function makeMappingKey(item: { line: number, columnStart: number }) { +function makeMappingKey(item: { line: number; columnStart: number }) { return `${item.line}/${item.columnStart}`; } function eachOverlappingGeneratedOutputRange( outputFile: ResolvedFileMappings, inputGeneratedRange: ResolvedGeneratedRange, - callback: ResolvedGeneratedRange => mixed, + callback: (range: ResolvedGeneratedRange) => unknown, ) { // Find the Babel-generated mappings that overlap with this range in the // input sourcemap. Generated locations within the input sourcemap @@ -137,10 +136,10 @@ function filterApplicableOriginalRanges( function eachInputGeneratedRange( map: ResolvedMappings, callback: ( - ResolvedGeneratedRange, - ResolvedOriginalRange, - ResolvedSource, - ) => mixed, + c: ResolvedGeneratedRange, + b: ResolvedOriginalRange, + a: ResolvedSource, + ) => unknown, ) { for (const { source, mappings } of map.sources) { for (const { original, generated } of mappings) { @@ -151,34 +150,39 @@ function eachInputGeneratedRange( } } -type ResolvedMappings = {| - file: ?string, - sourceRoot: ?string, - sources: Array, -|}; -type ResolvedFileMappings = {| - source: ResolvedSource, - mappings: OriginalMappings, -|}; -type OriginalMappings = Array<{| - original: ResolvedOriginalRange, - generated: Array, -|}>; -type ResolvedSource = {| - path: string, - content: string | null, -|}; -type ResolvedOriginalRange = {| - line: number, - columnStart: number, - columnEnd: number, - name: string | null, -|}; -type ResolvedGeneratedRange = {| - line: number, - columnStart: number, - columnEnd: number, -|}; +type ResolvedMappings = { + file: string | undefined | null; + sourceRoot: string | undefined | null; + sources: Array; +}; + +type ResolvedFileMappings = { + source: ResolvedSource; + mappings: OriginalMappings; +}; + +type OriginalMappings = Array<{ + original: ResolvedOriginalRange; + generated: Array; +}>; + +type ResolvedSource = { + path: string; + content: string | null; +}; + +type ResolvedOriginalRange = { + line: number; + columnStart: number; + columnEnd: number; + name: string | null; +}; + +type ResolvedGeneratedRange = { + line: number; + columnStart: number; + columnEnd: number; +}; function buildMappingData(map: SourceMap): ResolvedMappings { const consumer = new sourceMap.SourceMapConsumer({ @@ -270,7 +274,7 @@ function buildMappingData(map: SourceMap): ResolvedMappings { function findInsertionLocation( array: Array, - callback: T => number, + callback: (item: T) => number, ): number { let left = 0; let right = array.length; @@ -304,7 +308,7 @@ function findInsertionLocation( function filterSortedArray( array: Array, - callback: T => number, + callback: (item: T) => number, ): Array { const start = findInsertionLocation(array, callback); diff --git a/packages/babel-core/src/transformation/index.ts b/packages/babel-core/src/transformation/index.ts index 970549f9d94e..bcae18c09937 100644 --- a/packages/babel-core/src/transformation/index.ts +++ b/packages/babel-core/src/transformation/index.ts @@ -1,6 +1,6 @@ -// @flow import traverse from "@babel/traverse"; -import typeof { SourceMap } from "convert-source-map"; +import type * as t from "@babel/types"; +type SourceMap = any; import type { Handler } from "gensync"; import type { ResolvedConfig, PluginPasses } from "../config"; @@ -14,22 +14,23 @@ import generateCode from "./file/generate"; import type File from "./file/file"; export type FileResultCallback = { - (Error, null): any, - (null, FileResult | null): any, + (err: Error, file: null): any; + (err: null, file: FileResult | null): any; }; export type FileResult = { - metadata: {}, - options: {}, - ast: {} | null, - code: string | null, - map: SourceMap | null, + metadata: {}; + options: {}; + ast: {} | null; + code: string | null; + map: SourceMap | null; + sourceType: "string" | "module"; }; export function* run( config: ResolvedConfig, code: string, - ast: ?(BabelNodeFile | BabelNodeProgram), + ast?: t.File | t.Program | null, ): Handler { const file = yield* normalizeFile( config.passes, @@ -91,7 +92,9 @@ function* transformFile(file: File, pluginPasses: PluginPasses): Handler { if (fn) { const result = fn.call(pass, file); + // @ts-expect-error - If we want to support async .pre yield* []; + if (isThenable(result)) { throw new Error( `You appear to be using an plugin with an async .pre, ` + @@ -116,7 +119,9 @@ function* transformFile(file: File, pluginPasses: PluginPasses): Handler { if (fn) { const result = fn.call(pass, file); + // @ts-expect-error - If we want to support async .post yield* []; + if (isThenable(result)) { throw new Error( `You appear to be using an plugin with an async .post, ` + @@ -130,7 +135,7 @@ function* transformFile(file: File, pluginPasses: PluginPasses): Handler { } } -function isThenable(val: mixed): boolean { +function isThenable>(val: any): val is T { return ( !!val && (typeof val === "object" || typeof val === "function") && diff --git a/packages/babel-core/src/transformation/normalize-file.ts b/packages/babel-core/src/transformation/normalize-file.ts index b5fd21de258d..2a834cd9d494 100644 --- a/packages/babel-core/src/transformation/normalize-file.ts +++ b/packages/babel-core/src/transformation/normalize-file.ts @@ -1,12 +1,11 @@ -// @flow - import fs from "fs"; import path from "path"; import buildDebug from "debug"; import type { Handler } from "gensync"; import * as t from "@babel/types"; import type { PluginPasses } from "../config"; -import convertSourceMap, { typeof Converter } from "convert-source-map"; +import convertSourceMap from "convert-source-map"; +import type { SourceMapConverter as Converter } from "convert-source-map"; import File from "./file/file"; import parser from "../parser"; import cloneDeep from "./util/clone-deep"; @@ -15,16 +14,16 @@ const debug = buildDebug("babel:transform:file"); const LARGE_INPUT_SOURCEMAP_THRESHOLD = 1_000_000; export type NormalizedFile = { - code: string, - ast: {}, - inputMap: Converter | null, + code: string; + ast: {}; + inputMap: Converter | null; }; export default function* normalizeFile( pluginPasses: PluginPasses, - options: Object, + options: any, code: string, - ast: ?(BabelNodeFile | BabelNodeProgram), + ast?: t.File | t.Program | null, ): Handler { code = `${code || ""}`; @@ -66,16 +65,19 @@ export default function* normalizeFile( if (typeof options.filename === "string" && lastComment) { try { // when `lastComment` is non-null, EXTERNAL_SOURCEMAP_REGEX must have matches - const match: [string, string] = (EXTERNAL_SOURCEMAP_REGEX.exec( + const match: [string, string] = EXTERNAL_SOURCEMAP_REGEX.exec( lastComment, - ): any); - const inputMapContent: Buffer = fs.readFileSync( + ) as any; + const inputMapContent = fs.readFileSync( path.resolve(path.dirname(options.filename), match[1]), ); if (inputMapContent.length > LARGE_INPUT_SOURCEMAP_THRESHOLD) { debug("skip merging input map > 1 MB"); } else { - inputMap = convertSourceMap.fromJSON(inputMapContent); + inputMap = convertSourceMap.fromJSON( + // todo: + (inputMapContent as unknown) as string, + ); } } catch (err) { debug("discarding unknown file input sourcemap", err); @@ -116,19 +118,16 @@ function extractCommentsFromList(regex, comments, lastComment) { function extractComments(regex, ast) { let lastComment = null; t.traverseFast(ast, node => { - // $FlowIgnore destructuring with expressions is not supported [node.leadingComments, lastComment] = extractCommentsFromList( regex, node.leadingComments, lastComment, ); - // $FlowIgnore destructuring with expressions is not supported [node.innerComments, lastComment] = extractCommentsFromList( regex, node.innerComments, lastComment, ); - // $FlowIgnore destructuring with expressions is not supported [node.trailingComments, lastComment] = extractCommentsFromList( regex, node.trailingComments, diff --git a/packages/babel-core/src/transformation/normalize-opts.ts b/packages/babel-core/src/transformation/normalize-opts.ts index 41108888177d..bf6cda2d5193 100644 --- a/packages/babel-core/src/transformation/normalize-opts.ts +++ b/packages/babel-core/src/transformation/normalize-opts.ts @@ -1,5 +1,3 @@ -// @flow - import path from "path"; import type { ResolvedConfig } from "../config"; diff --git a/packages/babel-core/src/transformation/plugin-pass.ts b/packages/babel-core/src/transformation/plugin-pass.ts index 7f5f8a992b3a..5b06282a8321 100644 --- a/packages/babel-core/src/transformation/plugin-pass.ts +++ b/packages/babel-core/src/transformation/plugin-pass.ts @@ -1,13 +1,11 @@ -// @flow - import type File from "./file/file"; -import type NodeLocation from "./file/file"; +import type { NodeLocation } from "./file/file"; export default class PluginPass { - _map: Map = new Map(); - key: ?string; + _map: Map = new Map(); + key: string | undefined | null; file: File; - opts: Object; + opts: any; // The working directory that Babel's programmatic options are loaded // relative to. @@ -16,7 +14,7 @@ export default class PluginPass { // The absolute path of the file being compiled. filename: string | void; - constructor(file: File, key: ?string, options: ?Object) { + constructor(file: File, key?: string | null, options?: any | null) { this.key = key; this.file = file; this.opts = options || {}; @@ -24,15 +22,15 @@ export default class PluginPass { this.filename = file.opts.filename; } - set(key: mixed, val: mixed) { + set(key: unknown, val: unknown) { this._map.set(key, val); } - get(key: mixed): any { + get(key: unknown): any { return this._map.get(key); } - availableHelper(name: string, versionRange: ?string) { + availableHelper(name: string, versionRange?: string | null) { return this.file.availableHelper(name, versionRange); } @@ -44,14 +42,19 @@ export default class PluginPass { return this.file.addImport(); } - buildCodeFrameError(node: ?NodeLocation, msg: string, Error?: typeof Error) { - return this.file.buildCodeFrameError(node, msg, Error); + buildCodeFrameError( + node: NodeLocation | undefined | null, + msg: string, + _Error?: typeof Error, + ) { + return this.file.buildCodeFrameError(node, msg, _Error); } } if (!process.env.BABEL_8_BREAKING) { - // $FlowIgnore - PluginPass.prototype.getModuleName = function getModuleName(): ?string { + (PluginPass as any).prototype.getModuleName = function getModuleName(): + | string + | undefined { return this.file.getModuleName(); }; } diff --git a/packages/babel-parser/tsconfig.json b/packages/babel-parser/tsconfig.json new file mode 100644 index 000000000000..d1a436e76874 --- /dev/null +++ b/packages/babel-parser/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "include": [ + "./typings" + ], + "references": [ + { + "path": "../babel-code-frame" + }, + { + "path": "../babel-helper-fixtures" + }, + { + "path": "../babel-helper-validator-identifier" + } + ] +} diff --git a/packages/babel-parser/typings/babel-parser.d.ts b/packages/babel-parser/typings/babel-parser.d.ts index 76f0f327e0ab..4f22e6656d2b 100644 --- a/packages/babel-parser/typings/babel-parser.d.ts +++ b/packages/babel-parser/typings/babel-parser.d.ts @@ -167,3 +167,8 @@ export interface RecordAndTuplePluginOptions { export interface FlowPluginOptions { all?: boolean; } + +export const tokTypes: { + // todo(flow->ts) real token type + [name: string]: any; +}; diff --git a/yarn.lock b/yarn.lock index 494c17641c16..aab2cbd2300e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -211,6 +211,12 @@ __metadata: "@babel/template": "workspace:^7.12.13" "@babel/traverse": "workspace:^7.13.13" "@babel/types": "workspace:^7.13.14" + "@types/convert-source-map": ^1.5.1 + "@types/debug": ^4.1.0 + "@types/lodash": ^4.14.150 + "@types/resolve": ^1.3.2 + "@types/semver": ^5.4.0 + "@types/source-map": ^0.5.0 convert-source-map: ^1.7.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 @@ -659,6 +665,7 @@ __metadata: resolution: "@babel/helper-module-transforms@condition:BABEL_8_BREAKING?:workspace:^7.13.14#f57fb3" dependencies: "@babel/helper-module-transforms-BABEL_8_BREAKING-false": "npm:@babel/helper-module-transforms@workspace:^7.13.14" + checksum: 82d133091e69e2b2c742cfeb03c3f9acb3d0a00391d3ab2624154aa537dc714eedc5d8145c6c671bacd63d5314de09982a36b216b520c22466e4af3e375aff2d languageName: node linkType: hard @@ -3992,6 +3999,20 @@ __metadata: languageName: node linkType: hard +"@types/convert-source-map@npm:^1.5.1": + version: 1.5.1 + resolution: "@types/convert-source-map@npm:1.5.1" + checksum: 36cd50ea42b5e5c41db2415e4f42bdbc426e6b963a7d0d0a511164b789d4140629fbc0b3c5a29306ad3731ab41708088bb63e95972060cd3983af1f22b33f414 + languageName: node + linkType: hard + +"@types/debug@npm:^4.1.0": + version: 4.1.5 + resolution: "@types/debug@npm:4.1.5" + checksum: 416ad24bc589be0fb8c78bea972aa7d4ffdf6b136239701b1792674463b2dbf8c6707f6055ec484f79ec1f2b528ffef90c87e55df7e4a0f458184cad5bf0cfc8 + languageName: node + linkType: hard + "@types/eslint-scope@npm:^3.7.0": version: 3.7.0 resolution: "@types/eslint-scope@npm:3.7.0" @@ -4163,6 +4184,20 @@ __metadata: languageName: node linkType: hard +"@types/resolve@npm:^1.3.2": + version: 1.20.0 + resolution: "@types/resolve@npm:1.20.0" + checksum: 3c75135d5cf3652453ef8f099b109f7fec5e82fe67bf38048226614dbcd11a943affee383e5d28c12c5f03b049281a3e486395326b9810297f9649c7b00f41fd + languageName: node + linkType: hard + +"@types/semver@npm:^5.4.0": + version: 5.5.0 + resolution: "@types/semver@npm:5.5.0" + checksum: df74589466e171c36dd868b760609e518830f212134c238674ddd6eb83653368c59f4510aa6523b7692ec99c5d8ab40b818e30f9d65e0df97c56bdbacef06661 + languageName: node + linkType: hard + "@types/semver@npm:^7.3.4": version: 7.3.4 resolution: "@types/semver@npm:7.3.4"