Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve babel-core typings #14622

Merged
merged 14 commits into from Jun 11, 2022
8 changes: 5 additions & 3 deletions packages/babel-core/src/config/caching.ts
Expand Up @@ -348,7 +348,7 @@ class CacheConfigurator<SideChannel = void> {
function makeSimpleConfigurator(
cache: CacheConfigurator<any>,
): SimpleCacheConfigurator {
function cacheFn(val) {
function cacheFn(val: any) {
if (typeof val === "boolean") {
if (val) cache.forever();
else cache.never();
Expand All @@ -359,8 +359,10 @@ function makeSimpleConfigurator(
}
cacheFn.forever = () => cache.forever();
cacheFn.never = () => cache.never();
cacheFn.using = cb => cache.using(() => assertSimpleType(cb()));
cacheFn.invalidate = cb => cache.invalidate(() => assertSimpleType(cb()));
cacheFn.using = (cb: { (): SimpleType }) =>
cache.using(() => assertSimpleType(cb()));
cacheFn.invalidate = (cb: { (): SimpleType }) =>
cache.invalidate(() => assertSimpleType(cb()));

return cacheFn as any;
}
Expand Down
67 changes: 48 additions & 19 deletions packages/babel-core/src/config/config-chain.ts
Expand Up @@ -383,7 +383,12 @@ const loadFileChainWalker = makeChainWalker<ValidatedFile>({
buildFileLogger(file.filepath, context, baseLogger),
});

function* loadFileChain(input, context, files, baseLogger) {
function* loadFileChain(
input: ValidatedFile,
context: ConfigContext,
files: Set<ConfigFile>,
baseLogger: ConfigPrinter,
) {
const chain = yield* loadFileChainWalker(input, context, files, baseLogger);
if (chain) {
chain.files.add(input.filepath);
Expand Down Expand Up @@ -443,11 +448,23 @@ function buildFileLogger(
});
}

function buildRootDescriptors({ dirname, options }, alias, descriptors) {
function buildRootDescriptors(
{ dirname, options }: Partial<ValidatedFile>,
alias: string,
descriptors: (
dirname: string,
options: ValidatedOptions,
alias: string,
) => OptionsAndDescriptors,
) {
return descriptors(dirname, options, alias);
}

function buildProgrammaticLogger(_, context, baseLogger: ConfigPrinter | void) {
function buildProgrammaticLogger(
_: unknown,
context: ConfigContext,
baseLogger: ConfigPrinter | void,
) {
if (!baseLogger) {
return () => {};
}
Expand All @@ -457,20 +474,28 @@ function buildProgrammaticLogger(_, context, baseLogger: ConfigPrinter | void) {
}

function buildEnvDescriptors(
{ dirname, options },
alias,
descriptors,
envName,
{ dirname, options }: Partial<ValidatedFile>,
alias: string,
descriptors: (
dirname: string,
options: ValidatedOptions,
alias: string,
) => OptionsAndDescriptors,
envName: string,
) {
const opts = options.env && options.env[envName];
return opts ? descriptors(dirname, opts, `${alias}.env["${envName}"]`) : null;
}

function buildOverrideDescriptors(
{ dirname, options },
alias,
descriptors,
index,
{ dirname, options }: Partial<ValidatedFile>,
alias: string,
descriptors: (
dirname: string,
options: ValidatedOptions,
alias: string,
) => OptionsAndDescriptors,
index: number,
) {
const opts = options.overrides && options.overrides[index];
if (!opts) throw new Error("Assertion failure - missing override");
Expand All @@ -479,11 +504,15 @@ function buildOverrideDescriptors(
}

function buildOverrideEnvDescriptors(
{ dirname, options },
alias,
descriptors,
index,
envName,
{ dirname, options }: Partial<ValidatedFile>,
alias: string,
descriptors: (
dirname: string,
options: ValidatedOptions,
alias: string,
) => OptionsAndDescriptors,
index: number,
envName: string,
) {
const override = options.overrides && options.overrides[index];
if (!override) throw new Error("Assertion failure - missing override");
Expand Down Expand Up @@ -850,9 +879,9 @@ function matchesPatterns(
}

function matchPattern(
pattern,
dirname,
pathToTest,
pattern: IgnoreItem,
dirname: string,
pathToTest: unknown,
context: ConfigContext,
): boolean {
if (typeof pattern === "function") {
Expand Down
4 changes: 2 additions & 2 deletions packages/babel-core/src/config/config-descriptors.ts
Expand Up @@ -126,8 +126,8 @@ export function createUncachedDescriptors(
// The returned result here is cached to represent a config object in
// memory, so we build and memoize the descriptors to ensure the same
// values are returned consistently.
let plugins;
let presets;
let plugins: UnloadedDescriptor[];
let presets: UnloadedDescriptor[];

return {
options: optionsWithResolvedBrowserslistConfigFile(options, dirname),
Expand Down
6 changes: 5 additions & 1 deletion packages/babel-core/src/config/files/configuration.ts
Expand Up @@ -150,7 +150,11 @@ export function* loadConfig(
* Read the given config file, returning the result. Returns null if no config was found, but will
* throw if there are parsing errors while loading a config.
*/
function readConfig(filepath, envName, caller): Handler<ConfigFile | null> {
function readConfig(
filepath: string,
envName: string,
caller: CallerMetadata | undefined,
): Handler<ConfigFile | null> {
const ext = path.extname(filepath);
return ext === ".js" || ext === ".cjs" || ext === ".mjs"
? readConfigJS(filepath, { envName, caller })
Expand Down
3 changes: 2 additions & 1 deletion packages/babel-core/src/config/files/import-meta-resolve.ts
Expand Up @@ -34,7 +34,8 @@ const importMetaResolveP: Promise<ImportMeta["resolve"]> =
// it throws because it's a module, will fallback to import().
process.execArgv.includes("--experimental-import-meta-resolve")
? import_("data:text/javascript,export default import.meta.resolve").then(
m => m.default || polyfill,
(m: { default: ImportMeta["resolve"] | undefined }) =>
m.default || polyfill,
() => polyfill,
)
: Promise.resolve(polyfill);
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-core/src/config/files/index-browser.ts
Expand Up @@ -72,7 +72,7 @@ export function* resolveShowConfigPath(
return null;
}

export const ROOT_CONFIG_FILENAMES = [];
export const ROOT_CONFIG_FILENAMES: string[] = [];

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function resolvePlugin(name: string, dirname: string): string | null {
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-core/src/config/files/module-types.ts
Expand Up @@ -6,7 +6,7 @@ import { createRequire } from "module";

const require = createRequire(import.meta.url);

let import_;
let import_: ((specifier: string | URL) => any) | undefined;
try {
// Node < 13.3 doesn't support import() syntax.
import_ = require("./import").default;
Expand Down
4 changes: 3 additions & 1 deletion packages/babel-core/src/config/files/types.ts
@@ -1,7 +1,9 @@
import type { InputOptions } from "..";

export type ConfigFile = {
filepath: string;
dirname: string;
options: {};
options: InputOptions & { babel?: unknown };
};

export type IgnoreFile = {
Expand Down
15 changes: 9 additions & 6 deletions packages/babel-core/src/config/full.ts
Expand Up @@ -181,7 +181,7 @@ export default gensync<(inputOpts: unknown) => ResolvedConfig | null>(
pluginDescriptorsByPass[0].unshift(...initialPluginsDescriptors);

for (const descs of pluginDescriptorsByPass) {
const pass = [];
const pass: Plugin[] = [];
passes.push(pass);

for (let i = 0; i < descs.length; i++) {
Expand Down Expand Up @@ -223,8 +223,8 @@ export default gensync<(inputOpts: unknown) => ResolvedConfig | null>(
},
);

function enhanceError<T extends Function>(context, fn: T): T {
return function* (arg1, arg2) {
function enhanceError<T extends Function>(context: ConfigContext, fn: T): T {
return function* (arg1: unknown, arg2: unknown) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can use Parameters<T>[0] and Parameters<T>[1]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't seem to be, it can't be passed into the Function.

Copy link
Contributor

@armano2 armano2 Jun 6, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should be able to use Parameters in here if you define proper correct type of T

i'm not sure what should be in here, but as simple example you can do something like this:

function enhanceError<T extends (arg1?: unknown, arg2?: unknown) => Generator<unknown>>(
  context: ConfigContext,
  fn: T,
): T {
  return function* (arg1: Parameters<T>[0], arg2: Parameters<T>[1]) {

see https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html

try {
return yield* fn(arg1, arg2);
} catch (e) {
Expand Down Expand Up @@ -376,7 +376,7 @@ const instantiatePlugin = makeWeakCache(function* (
}

if (plugin.inherits) {
const inheritsDescriptor = {
const inheritsDescriptor: UnloadedDescriptor = {
name: undefined,
alias: `${alias}$inherits`,
value: plugin.inherits,
Expand Down Expand Up @@ -490,11 +490,14 @@ const instantiatePreset = makeWeakCacheSync(
},
);

function chain(a, b) {
function chain<Args extends any[]>(
a: undefined | ((...args: Args) => void),
b: undefined | ((...args: Args) => void),
) {
const fns = [a, b].filter(Boolean);
if (fns.length <= 1) return fns[0];

return function (this: unknown, ...args) {
return function (this: unknown, ...args: unknown[]) {
for (const fn of fns) {
fn.apply(this, args);
}
Expand Down
13 changes: 7 additions & 6 deletions packages/babel-core/src/config/helpers/config-api.ts
Expand Up @@ -47,23 +47,23 @@ export type PluginAPI = {
export function makeConfigAPI<SideChannel extends Context.SimpleConfig>(
cache: CacheConfigurator<SideChannel>,
): ConfigAPI {
const env: any = value =>
const env: EnvFunction = value =>
cache.using(data => {
if (typeof value === "undefined") return data.envName;
if (typeof value === "function") {
return assertSimpleType(value(data.envName));
}
if (!Array.isArray(value)) value = [value];

return value.some((entry: unknown) => {
return (Array.isArray(value) ? value : [value]).some(entry => {
if (typeof entry !== "string") {
throw new Error("Unexpected non-string value");
}
return entry === data.envName;
});
});

const caller = cb => cache.using(data => assertSimpleType(cb(data.caller)));
const caller = (cb: {
(CallerMetadata: CallerMetadata | undefined): SimpleType;
}) => cache.using(data => assertSimpleType(cb(data.caller)));

return {
version: coreVersion,
Expand Down Expand Up @@ -98,7 +98,8 @@ export function makePluginAPI<SideChannel extends Context.SimplePlugin>(
cache: CacheConfigurator<SideChannel>,
externalDependencies: Array<string>,
): PluginAPI {
const assumption = name => cache.using(data => data.assumptions[name]);
const assumption = (name: string) =>
cache.using(data => data.assumptions[name]);

return { ...makePresetAPI(cache, externalDependencies), assumption };
}
Expand Down
33 changes: 19 additions & 14 deletions packages/babel-core/src/config/index.ts
@@ -1,4 +1,5 @@
import gensync from "gensync";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Todo (for myself): Continue reviewing from this file.

import type { Gensync } from "gensync";

export type {
ResolvedConfig,
Expand All @@ -23,7 +24,7 @@ export type {
ValidatedOptions as PresetObject,
} from "./validation/options";

import loadFullConfig from "./full";
import loadFullConfig, { type ResolvedConfig } from "./full";
import { loadPartialConfig as loadPartialConfigRunner } from "./partial";

export { loadFullConfig as default };
Expand All @@ -32,24 +33,28 @@ export type { PartialConfig } from "./partial";
import { createConfigItem as createConfigItemImpl } from "./item";
import type { ConfigItem } from "./item";

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 loadOptionsRunner = gensync<(opts: unknown) => ResolvedConfig | null>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future note: we will be able to drop our type definitions for gensync and use DefinitelyTyped/DefinitelyTyped#60125

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<(...args: Parameters<typeof createConfigItemImpl>) => ConfigItem>(
createConfigItemImpl,
);

const maybeErrback = runner => (opts: unknown, callback?: Function) => {
if (callback === undefined && typeof opts === "function") {
callback = opts;
opts = undefined;
}
return callback ? runner.errback(opts, callback) : runner.sync(opts);
};
const maybeErrback =
(runner: Gensync<(...args: any) => any>) =>
(opts: unknown, callback?: any) => {
if (callback === undefined && typeof opts === "function") {
callback = opts;
opts = undefined;
}
return callback ? runner.errback(opts, callback) : runner.sync(opts);
};

export const loadPartialConfig = maybeErrback(loadPartialConfigRunner);
export const loadPartialConfigSync = loadPartialConfigRunner.sync;
Expand All @@ -63,7 +68,7 @@ export const createConfigItemSync = createConfigItemRunner.sync;
export const createConfigItemAsync = createConfigItemRunner.async;
export function createConfigItem(
target: PluginTarget,
options: any,
options: Parameters<typeof createConfigItemImpl>[1],
callback?: (err: Error, val: ConfigItem | null) => void,
) {
if (callback !== undefined) {
Expand Down
2 changes: 1 addition & 1 deletion packages/babel-core/src/config/pattern-to-regex.ts
Expand Up @@ -11,7 +11,7 @@ const starPatLast = `(?:${substitution}${endSep})`;
const starStarPat = `${starPat}*?`;
const starStarPatLast = `${starPat}*?${starPatLast}?`;

function escapeRegExp(string) {
function escapeRegExp(string: string) {
return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
}

Expand Down
6 changes: 3 additions & 3 deletions packages/babel-core/src/config/plugin.ts
Expand Up @@ -5,9 +5,9 @@ import type { PluginObject } from "./validation/plugins";
export default class Plugin {
key: string | undefined | null;
manipulateOptions?: (options: unknown, parserOpts: unknown) => void;
post?: Function;
pre?: Function;
visitor: {};
post?: PluginObject["post"];
pre?: PluginObject["pre"];
visitor: PluginObject["visitor"];

parserOverride?: Function;
generatorOverride?: Function;
Expand Down
13 changes: 8 additions & 5 deletions packages/babel-core/src/config/resolve-targets-browser.ts
@@ -1,5 +1,7 @@
import type { ValidatedOptions } from "./validation/options";
import getTargets from "@babel/helper-compilation-targets";
import getTargets, {
type InputTargets,
} from "@babel/helper-compilation-targets";

import type { Targets } from "@babel/helper-compilation-targets";

Expand All @@ -17,10 +19,11 @@ export function resolveTargets(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
root: string,
): Targets {
// 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 };
const optTargets = options.targets;
let targets: InputTargets;
liuxingbaoyu marked this conversation as resolved.
Show resolved Hide resolved

if (typeof optTargets === "string" || Array.isArray(optTargets)) {
targets = { browsers: optTargets };
}
if (targets && targets.esmodules) {
targets = { ...targets, esmodules: "intersect" };
Expand Down