Refine babel core types (#11544)
Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
This commit is contained in:
parent
30835f14db
commit
601c824873
@ -28,3 +28,4 @@ esproposal.export_star_as=enable
|
||||
esproposal.optional_chaining=enable
|
||||
esproposal.nullish_coalescing=enable
|
||||
module.name_mapper='^@babel\/\([a-zA-Z0-9_\-]+\)$' -> '<PROJECT_ROOT>/packages/babel-\1/src/index'
|
||||
module.ignore_non_literal_requires=true
|
||||
|
||||
@ -2,6 +2,10 @@
|
||||
* Basic declarations for the npm modules we use.
|
||||
*/
|
||||
|
||||
declare module "debug" {
|
||||
declare export default (namespace: string) => (formatter: string, ...args: any[]) => void;
|
||||
}
|
||||
|
||||
declare module "resolve" {
|
||||
declare export default {
|
||||
(string, {| basedir: string |}, (err: ?Error, res: string) => void): void;
|
||||
@ -27,6 +31,10 @@ declare module "lodash/merge" {
|
||||
declare export default <T: Object>(T, Object) => T;
|
||||
}
|
||||
|
||||
declare module "lodash/escapeRegExp" {
|
||||
declare export default (toEscape?: string) => string;
|
||||
}
|
||||
|
||||
declare module "semver" {
|
||||
declare class SemVer {
|
||||
build: Array<string>;
|
||||
|
||||
@ -108,7 +108,7 @@ function makeCachedFunction<ArgT, ResultT, SideChannel, Cache: *>(
|
||||
const asyncContext = yield* isAsync();
|
||||
const callCache = asyncContext ? callCacheAsync : callCacheSync;
|
||||
|
||||
const cached = yield* getCachedValueOrWait(
|
||||
const cached = yield* getCachedValueOrWait<ArgT, ResultT, SideChannel>(
|
||||
asyncContext,
|
||||
callCache,
|
||||
futureCache,
|
||||
@ -119,7 +119,7 @@ function makeCachedFunction<ArgT, ResultT, SideChannel, Cache: *>(
|
||||
|
||||
const cache = new CacheConfigurator(data);
|
||||
|
||||
const handlerResult = handler(arg, cache);
|
||||
const handlerResult: Handler<ResultT> | ResultT = handler(arg, cache);
|
||||
|
||||
let finishLock: ?Lock<ResultT>;
|
||||
let value: ResultT;
|
||||
@ -313,7 +313,7 @@ class CacheConfigurator<SideChannel = void> {
|
||||
);
|
||||
|
||||
if (isThenable(key)) {
|
||||
return key.then(key => {
|
||||
return key.then((key: mixed) => {
|
||||
this._pairs.push([key, fn]);
|
||||
return key;
|
||||
});
|
||||
@ -369,7 +369,13 @@ function makeSimpleConfigurator(
|
||||
|
||||
// Types are limited here so that in the future these values can be used
|
||||
// as part of Babel's caching logic.
|
||||
type SimpleType = string | boolean | number | null | void | Promise<SimpleType>;
|
||||
export type SimpleType =
|
||||
| string
|
||||
| boolean
|
||||
| number
|
||||
| null
|
||||
| void
|
||||
| Promise<SimpleType>;
|
||||
export function assertSimpleType(value: mixed): SimpleType {
|
||||
if (isThenable(value)) {
|
||||
throw new Error(
|
||||
|
||||
@ -76,7 +76,6 @@ export const buildPresetChainWalker: (
|
||||
arg: PresetInstance,
|
||||
context: *,
|
||||
) => * = makeChainWalker({
|
||||
init: arg => arg,
|
||||
root: preset => loadPresetDescriptors(preset),
|
||||
env: (preset, envName) => loadPresetEnvDescriptors(preset)(envName),
|
||||
overrides: (preset, index) => loadPresetOverridesDescriptors(preset)(index),
|
||||
@ -419,12 +418,12 @@ function makeChainWalker<ArgT: { options: ValidatedOptions, dirname: string }>({
|
||||
env,
|
||||
overrides,
|
||||
overridesEnv,
|
||||
}: {
|
||||
}: {|
|
||||
root: ArgT => OptionsAndDescriptors,
|
||||
env: (ArgT, string) => OptionsAndDescriptors | null,
|
||||
overrides: (ArgT, number) => OptionsAndDescriptors,
|
||||
overridesEnv: (ArgT, number, string) => OptionsAndDescriptors | null,
|
||||
}): (
|
||||
|}): (
|
||||
ArgT,
|
||||
ConfigContext,
|
||||
Set<ConfigFile> | void,
|
||||
|
||||
@ -151,7 +151,7 @@ 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) {
|
||||
function readConfig(filepath, envName, caller): Handler<ConfigFile | null> {
|
||||
const ext = path.extname(filepath);
|
||||
return ext === ".js" || ext === ".cjs" || ext === ".mjs"
|
||||
? readConfigJS(filepath, { envName, caller })
|
||||
@ -236,7 +236,7 @@ const readConfigJS = makeStrongCache(function* readConfigJS(
|
||||
|
||||
const packageToBabelConfig = makeWeakCacheSync(
|
||||
(file: ConfigFile): ConfigFile | null => {
|
||||
const babel = file.options[("babel": string)];
|
||||
const babel: mixed = file.options[("babel": string)];
|
||||
|
||||
if (typeof babel === "undefined") return null;
|
||||
|
||||
@ -252,7 +252,7 @@ const packageToBabelConfig = makeWeakCacheSync(
|
||||
},
|
||||
);
|
||||
|
||||
const readConfigJSON5 = makeStaticFileCache((filepath, content) => {
|
||||
const readConfigJSON5 = makeStaticFileCache((filepath, content): ConfigFile => {
|
||||
let options;
|
||||
try {
|
||||
options = json5.parse(content);
|
||||
@ -281,7 +281,7 @@ const readIgnoreConfig = makeStaticFileCache((filepath, content) => {
|
||||
const ignoreDir = path.dirname(filepath);
|
||||
const ignorePatterns = content
|
||||
.split("\n")
|
||||
.map(line => line.replace(/#(.*?)$/, "").trim())
|
||||
.map<string>(line => line.replace(/#(.*?)$/, "").trim())
|
||||
.filter(line => !!line);
|
||||
|
||||
for (const pattern of ignorePatterns) {
|
||||
@ -299,7 +299,7 @@ const readIgnoreConfig = makeStaticFileCache((filepath, content) => {
|
||||
};
|
||||
});
|
||||
|
||||
function throwConfigError() {
|
||||
function throwConfigError(): empty {
|
||||
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:
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
// @flow
|
||||
// We keep this in a seprate file so that in older node versions, where
|
||||
// import() isn't supported, we can try/catch around the require() call
|
||||
// when loading this file.
|
||||
|
||||
@ -39,12 +39,14 @@ const readConfigPackage = makeStaticFileCache(
|
||||
(filepath, content): ConfigFile => {
|
||||
let options;
|
||||
try {
|
||||
options = JSON.parse(content);
|
||||
options = (JSON.parse(content): mixed);
|
||||
} catch (err) {
|
||||
err.message = `${filepath}: Error while parsing JSON - ${err.message}`;
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!options) throw new Error(`${filepath}: No config detected`);
|
||||
|
||||
if (typeof options !== "object") {
|
||||
throw new Error(`${filepath}: Config returned typeof ${typeof options}`);
|
||||
}
|
||||
|
||||
@ -105,7 +105,7 @@ function resolveStandardizedName(
|
||||
try {
|
||||
resolve.sync(name, { basedir: dirname });
|
||||
resolvedOriginal = true;
|
||||
} catch (e2) {}
|
||||
} catch {}
|
||||
|
||||
if (resolvedOriginal) {
|
||||
e.message += `\n- If you want to resolve "${name}", use "module:${name}"`;
|
||||
@ -118,7 +118,7 @@ function resolveStandardizedName(
|
||||
basedir: dirname,
|
||||
});
|
||||
resolvedBabel = true;
|
||||
} catch (e2) {}
|
||||
} catch {}
|
||||
|
||||
if (resolvedBabel) {
|
||||
e.message += `\n- Did you mean "@babel/${name}"?`;
|
||||
@ -129,7 +129,7 @@ function resolveStandardizedName(
|
||||
try {
|
||||
resolve.sync(standardizeName(oppositeType, name), { basedir: dirname });
|
||||
resolvedOppositeType = true;
|
||||
} catch (e2) {}
|
||||
} catch {}
|
||||
|
||||
if (resolvedOppositeType) {
|
||||
e.message += `\n- Did you accidentally pass a ${oppositeType} as a ${type}?`;
|
||||
@ -151,7 +151,6 @@ function requireModule(type: string, name: string): mixed {
|
||||
|
||||
try {
|
||||
LOADING_MODULES.add(name);
|
||||
// $FlowIssue
|
||||
return require(name);
|
||||
} finally {
|
||||
LOADING_MODULES.delete(name);
|
||||
|
||||
@ -66,7 +66,7 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
|
||||
const { options, context } = result;
|
||||
|
||||
const optionDefaults = {};
|
||||
const passes = [[]];
|
||||
const passes: Array<Array<Plugin>> = [[]];
|
||||
try {
|
||||
const { plugins, presets } = options;
|
||||
|
||||
@ -74,14 +74,8 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
|
||||
throw new Error("Assertion failure - plugins and presets exist");
|
||||
}
|
||||
|
||||
const ignored = yield* (function* recurseDescriptors(
|
||||
config: {
|
||||
plugins: Array<UnloadedDescriptor>,
|
||||
presets: Array<UnloadedDescriptor>,
|
||||
},
|
||||
pass: Array<Plugin>,
|
||||
) {
|
||||
const plugins = [];
|
||||
const ignored = yield* (function* recurseDescriptors(config, pass) {
|
||||
const plugins: Array<Plugin> = [];
|
||||
for (let i = 0; i < config.plugins.length; i++) {
|
||||
const descriptor = config.plugins[i];
|
||||
if (descriptor.options !== false) {
|
||||
@ -103,7 +97,10 @@ export default gensync<[any], ResolvedConfig | null>(function* loadFullConfig(
|
||||
}
|
||||
}
|
||||
|
||||
const presets = [];
|
||||
const presets: Array<{|
|
||||
preset: ConfigChain | null,
|
||||
pass: Array<Plugin>,
|
||||
|}> = [];
|
||||
for (let i = 0; i < config.presets.length; i++) {
|
||||
const descriptor = config.presets[i];
|
||||
if (descriptor.options !== false) {
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
assertSimpleType,
|
||||
type CacheConfigurator,
|
||||
type SimpleCacheConfigurator,
|
||||
type SimpleType,
|
||||
} from "../caching";
|
||||
|
||||
import type { CallerMetadata } from "../validation/options";
|
||||
@ -17,13 +18,15 @@ type EnvFunction = {
|
||||
(Array<string>): boolean,
|
||||
};
|
||||
|
||||
type CallerFactory = ((CallerMetadata | void) => mixed) => SimpleType;
|
||||
|
||||
export type PluginAPI = {|
|
||||
version: string,
|
||||
cache: SimpleCacheConfigurator,
|
||||
env: EnvFunction,
|
||||
async: () => boolean,
|
||||
assertVersion: typeof assertVersion,
|
||||
caller?: any,
|
||||
caller?: CallerFactory,
|
||||
|};
|
||||
|
||||
export default function makeAPI(
|
||||
@ -37,7 +40,7 @@ export default function makeAPI(
|
||||
}
|
||||
if (!Array.isArray(value)) value = [value];
|
||||
|
||||
return value.some(entry => {
|
||||
return value.some((entry: mixed) => {
|
||||
if (typeof entry !== "string") {
|
||||
throw new Error("Unexpected non-string value");
|
||||
}
|
||||
@ -45,8 +48,7 @@ export default function makeAPI(
|
||||
});
|
||||
});
|
||||
|
||||
const caller: any = cb =>
|
||||
cache.using(data => assertSimpleType(cb(data.caller)));
|
||||
const caller = cb => cache.using(data => assertSimpleType(cb(data.caller)));
|
||||
|
||||
return {
|
||||
version: coreVersion,
|
||||
|
||||
@ -103,7 +103,7 @@ class ConfigItem {
|
||||
// programmatically, and also make sure that if people happen to
|
||||
// pass the item through JSON.stringify, it doesn't show up.
|
||||
this._descriptor = descriptor;
|
||||
Object.defineProperty(this, "_descriptor", ({ enumerable: false }: any));
|
||||
Object.defineProperty(this, "_descriptor", { enumerable: false });
|
||||
|
||||
this.value = this._descriptor.value;
|
||||
this.options = this._descriptor.options;
|
||||
|
||||
@ -4,7 +4,7 @@ import type { PluginObject } from "./validation/plugins";
|
||||
|
||||
export default class Plugin {
|
||||
key: ?string;
|
||||
manipulateOptions: Function | void;
|
||||
manipulateOptions: ((options: mixed, parserOpts: mixed) => void) | void;
|
||||
post: Function | void;
|
||||
pre: Function | void;
|
||||
visitor: {};
|
||||
|
||||
@ -18,6 +18,8 @@ import type {
|
||||
RootMode,
|
||||
} from "./options";
|
||||
|
||||
export type { RootPath } from "./options";
|
||||
|
||||
export type ValidatorSet = {
|
||||
[string]: Validator<any>,
|
||||
};
|
||||
@ -192,7 +194,10 @@ export function assertBoolean(loc: GeneralPath, value: mixed): boolean | void {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function assertObject(loc: GeneralPath, value: mixed): {} | void {
|
||||
export function assertObject(
|
||||
loc: GeneralPath,
|
||||
value: mixed,
|
||||
): { +[string]: mixed } | void {
|
||||
if (
|
||||
value !== undefined &&
|
||||
(typeof value !== "object" || Array.isArray(value) || !value)
|
||||
|
||||
@ -276,7 +276,7 @@ export type OptionsSource =
|
||||
| "preset"
|
||||
| "plugin";
|
||||
|
||||
type RootPath = $ReadOnly<{
|
||||
export type RootPath = $ReadOnly<{
|
||||
type: "root",
|
||||
source: OptionsSource,
|
||||
}>;
|
||||
@ -311,7 +311,7 @@ function validateNested(loc: NestingPath, opts: {}) {
|
||||
|
||||
assertNoDuplicateSourcemap(opts);
|
||||
|
||||
Object.keys(opts).forEach(key => {
|
||||
Object.keys(opts).forEach((key: string) => {
|
||||
const optLoc = {
|
||||
type: "option",
|
||||
name: key,
|
||||
@ -364,7 +364,10 @@ function throwUnknownError(loc: OptionPath) {
|
||||
const key = loc.name;
|
||||
|
||||
if (removed[key]) {
|
||||
const { message, version = 5 } = removed[key];
|
||||
const {
|
||||
message,
|
||||
version = 5,
|
||||
}: { message: string, version?: number } = removed[key];
|
||||
|
||||
throw new Error(
|
||||
`Using removed Babel ${version} option: ${msg(loc)} - ${message}`,
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
// @flow
|
||||
import {
|
||||
assertString,
|
||||
assertFunction,
|
||||
assertObject,
|
||||
msg,
|
||||
type ValidatorSet,
|
||||
type Validator,
|
||||
type OptionPath,
|
||||
type RootPath,
|
||||
} from "./option-assertions";
|
||||
|
||||
// Note: The casts here are just meant to be static assertions to make sure
|
||||
@ -31,14 +35,16 @@ const VALIDATORS: ValidatorSet = {
|
||||
>),
|
||||
};
|
||||
|
||||
function assertVisitorMap(key: string, value: mixed): VisitorMap {
|
||||
const obj = assertObject(key, value);
|
||||
function assertVisitorMap(loc: OptionPath, value: mixed): VisitorMap {
|
||||
const obj = assertObject(loc, value);
|
||||
if (obj) {
|
||||
Object.keys(obj).forEach(prop => assertVisitorHandler(prop, obj[prop]));
|
||||
|
||||
if (obj.enter || obj.exit) {
|
||||
throw new Error(
|
||||
`.${key} cannot contain catch-all "enter" or "exit" handlers. Please target individual nodes.`,
|
||||
`${msg(
|
||||
loc,
|
||||
)} cannot contain catch-all "enter" or "exit" handlers. Please target individual nodes.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -50,7 +56,7 @@ function assertVisitorHandler(
|
||||
value: mixed,
|
||||
): VisitorHandler | void {
|
||||
if (value && typeof value === "object") {
|
||||
Object.keys(value).forEach(handler => {
|
||||
Object.keys(value).forEach((handler: string) => {
|
||||
if (handler !== "enter" && handler !== "exit") {
|
||||
throw new Error(
|
||||
`.visitor["${key}"] may only have .enter and/or .exit handlers.`,
|
||||
@ -71,7 +77,7 @@ export type VisitorMap = {
|
||||
|
||||
export type PluginObject = {
|
||||
name?: string,
|
||||
manipulateOptions?: Function,
|
||||
manipulateOptions?: (options: mixed, parserOpts: mixed) => void,
|
||||
|
||||
pre?: Function,
|
||||
post?: Function,
|
||||
@ -88,16 +94,17 @@ export function validatePluginObject(obj: {}): PluginObject {
|
||||
type: "root",
|
||||
source: "plugin",
|
||||
};
|
||||
Object.keys(obj).forEach(key => {
|
||||
Object.keys(obj).forEach((key: string) => {
|
||||
const validator = VALIDATORS[key];
|
||||
const optLoc = {
|
||||
type: "option",
|
||||
name: key,
|
||||
parent: rootPath,
|
||||
};
|
||||
|
||||
if (validator) validator(optLoc, obj[key]);
|
||||
else {
|
||||
if (validator) {
|
||||
const optLoc: OptionPath = {
|
||||
type: "option",
|
||||
name: key,
|
||||
parent: rootPath,
|
||||
};
|
||||
validator(optLoc, obj[key]);
|
||||
} else {
|
||||
const invalidPluginPropertyError = new Error(
|
||||
`.${key} is not a valid Plugin property`,
|
||||
);
|
||||
|
||||
@ -339,6 +339,23 @@ describe("@babel/core config loading", () => {
|
||||
/\.inherits must be a function, or undefined/,
|
||||
);
|
||||
});
|
||||
|
||||
it("should throw when plugin contains `enter` handler", () => {
|
||||
const fooPlugin = {
|
||||
visitor: {
|
||||
enter() {},
|
||||
},
|
||||
};
|
||||
const opts = {
|
||||
cwd: path.dirname(FILEPATH),
|
||||
filename: FILEPATH,
|
||||
plugins: [fooPlugin],
|
||||
};
|
||||
|
||||
expect(() => loadConfig(opts)).toThrow(
|
||||
/\.visitor cannot contain catch-all "enter" or "exit" handlers\. Please target individual nodes\./,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("caller metadata", () => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user