Skip to content

Commit

Permalink
Convert @babel/parser to TypeScript (#14783)
Browse files Browse the repository at this point in the history
* flowts convert

yarn dlx flowts --no-allow-js --interactive-rename -i "./src/**/*.js" "./packages/babel-parser/" && yarn eslint packages/babel-parser '**/*.ts' --fix

* fix parser integration typing errors

* chore: remove unused $FlowIgnore

* fix parser-error typing errors

* fix tokenizer types optimizer

* fix parser core typing errors

* fix parser plugin typing errors

* code review

* Minor updates

Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
  • Loading branch information
JLHwung and nicolo-ribaudo committed Jul 23, 2022
1 parent b081a57 commit a483aa2
Show file tree
Hide file tree
Showing 51 changed files with 3,328 additions and 2,372 deletions.
4 changes: 2 additions & 2 deletions babel.config.js
Expand Up @@ -742,7 +742,7 @@ function pluginAddImportExtension() {
}

const tokenTypesMapping = new Map();
const tokenTypeSourcePath = "./packages/babel-parser/src/tokenizer/types.js";
const tokenTypeSourcePath = "./packages/babel-parser/src/tokenizer/types.ts";

function pluginBabelParserTokenType({
types: { isIdentifier, numericLiteral },
Expand Down Expand Up @@ -777,7 +777,7 @@ function pluginBabelParserTokenType({
}),
{
configFile: false,
parserOpts: { attachComments: false, plugins: ["flow"] },
parserOpts: { attachComments: false, plugins: ["typescript"] },
}
);

Expand Down
1 change: 0 additions & 1 deletion packages/babel-cli/src/babel/file.ts
Expand Up @@ -116,7 +116,6 @@ export default async function ({

process.stdin.on("readable", function () {
const chunk = process.stdin.read();
// $FlowIgnore
if (chunk !== null) code += chunk;
});

Expand Down
1 change: 0 additions & 1 deletion packages/babel-cli/src/babel/options.ts
Expand Up @@ -311,7 +311,6 @@ export default function parseArgv(args: Array<string>): CmdOptions | null {
};

if (!process.env.BABEL_8_BREAKING) {
// $FlowIgnore
Object.assign(babelOptions, {
moduleRoot: opts.moduleRoot,
moduleIds: opts.moduleIds,
Expand Down
3 changes: 2 additions & 1 deletion packages/babel-core/src/transformation/normalize-file.ts
Expand Up @@ -39,6 +39,7 @@ export default function* normalizeFile(
ast = cloneDeep(ast) as t.File;
}
} else {
// @ts-expect-error todo: use babel-types ast typings in Babel parser
ast = yield* parser(pluginPasses, options, code);
}

Expand Down Expand Up @@ -91,7 +92,7 @@ export default function* normalizeFile(

return new File(options, {
code,
ast,
ast: ast as t.File,
inputMap,
});
}
Expand Down
@@ -1,5 +1,3 @@
// @flow

import { type Options } from "./options";
import {
hasPlugin,
Expand All @@ -8,9 +6,20 @@ import {
mixinPlugins,
type PluginList,
} from "./plugin-utils";
import type {
PluginConfig as ParserPlugin,
FlowPluginOptions,
RecordAndTuplePluginOptions,
PipelineOperatorPluginOptions,
} from "./typings";
import Parser from "./parser";

import { getExportedToken, tt as internalTokenTypes } from "./tokenizer/types";
import {
ExportedTokenType,
getExportedToken,
tt as internalTokenTypes,
type InternalTokenTypes,
} from "./tokenizer/types";
import "./tokenizer/context";

import type { Expression, File } from "./types";
Expand Down Expand Up @@ -67,8 +76,10 @@ export function parseExpression(input: string, options?: Options): Expression {
return parser.getExpression();
}

function generateExportedTokenTypes(internalTokenTypes) {
const tokenTypes = {};
function generateExportedTokenTypes(
internalTokenTypes: InternalTokenTypes,
): Record<string, ExportedTokenType> {
const tokenTypes: Record<string, ExportedTokenType> = {};
for (const typeName of Object.keys(internalTokenTypes)) {
tokenTypes[typeName] = getExportedToken(internalTokenTypes[typeName]);
}
Expand All @@ -77,7 +88,7 @@ function generateExportedTokenTypes(internalTokenTypes) {

export const tokTypes = generateExportedTokenTypes(internalTokenTypes);

function getParser(options: ?Options, input: string): Parser {
function getParser(options: Options | undefined | null, input: string): Parser {
let cls = Parser;
if (options?.plugins) {
validatePlugins(options.plugins);
Expand All @@ -87,10 +98,12 @@ function getParser(options: ?Options, input: string): Parser {
return new cls(options, input);
}

const parserClassCache: { [key: string]: Class<Parser> } = {};
const parserClassCache: { [key: string]: { new (...args: any): Parser } } = {};

/** Get a Parser class with plugins applied. */
function getParserClass(pluginsFromOptions: PluginList): Class<Parser> {
function getParserClass(pluginsFromOptions: PluginList): {
new (...args: any): Parser;
} {
const pluginList = mixinPluginNames.filter(name =>
hasPlugin(pluginsFromOptions, name),
);
Expand All @@ -106,3 +119,11 @@ function getParserClass(pluginsFromOptions: PluginList): Class<Parser> {
}
return cls;
}

export type {
FlowPluginOptions,
ParserPlugin,
PipelineOperatorPluginOptions,
RecordAndTuplePluginOptions,
};
export type ParserOptions = Partial<Options>;
@@ -1,5 +1,3 @@
// @flow

import type { PluginList } from "./plugin-utils";

// A second optional argument can be given to further configure
Expand All @@ -8,22 +6,22 @@ import type { PluginList } from "./plugin-utils";
export type SourceType = "script" | "module" | "unambiguous";

export type Options = {
sourceType: SourceType,
sourceFilename?: string,
startColumn: number,
startLine: number,
allowAwaitOutsideFunction: boolean,
allowReturnOutsideFunction: boolean,
allowImportExportEverywhere: boolean,
allowSuperOutsideMethod: boolean,
allowUndeclaredExports: boolean,
plugins: PluginList,
strictMode: ?boolean,
ranges: boolean,
tokens: boolean,
createParenthesizedExpressions: boolean,
errorRecovery: boolean,
attachComment: boolean,
sourceType: SourceType;
sourceFilename?: string;
startColumn: number;
startLine: number;
allowAwaitOutsideFunction: boolean;
allowReturnOutsideFunction: boolean;
allowImportExportEverywhere: boolean;
allowSuperOutsideMethod: boolean;
allowUndeclaredExports: boolean;
plugins: PluginList;
strictMode: boolean | undefined | null;
ranges: boolean;
tokens: boolean;
createParenthesizedExpressions: boolean;
errorRecovery: boolean;
attachComment: boolean;
};

export const defaultOptions: Options = {
Expand Down Expand Up @@ -80,9 +78,10 @@ export const defaultOptions: Options = {

// Interpret and default an options object

export function getOptions(opts: ?Options): Options {
export function getOptions(opts?: Options | null): Options {
const options: any = {};
for (const key of Object.keys(defaultOptions)) {
// @ts-expect-error key may not exist in opts
options[key] = opts && opts[key] != null ? opts[key] : defaultOptions[key];
}
return options;
Expand Down
@@ -1,5 +1,3 @@
// @flow

import { Position } from "./util/location";
import type { NodeBase } from "./types";
import {
Expand All @@ -8,6 +6,7 @@ import {
ParseErrorCodes,
type ParseErrorCredentials,
} from "./parse-error/credentials";
import type { Undone } from "../src/parser/node";

// Babel uses "normal" SyntaxErrors for it's errors, but adds some extra
// functionality. This functionality is defined in the
Expand All @@ -25,9 +24,7 @@ interface ParseErrorSpecification<ErrorDetails> {
code: ParseErrorCode;
reasonCode: string;
syntaxPlugin?: string;

missingPlugin?: string | string[];

loc: Position;
details: ErrorDetails;

Expand All @@ -44,35 +41,47 @@ export type ParseError<ErrorDetails> = SyntaxError &
// separate classes from `SyntaxError`'s.
//
// 1. https://github.com/microsoft/TypeScript/blob/v4.5.5/lib/lib.es5.d.ts#L1027
export type ParseErrorConstructor<ErrorDetails> = ({
loc: Position,
details: ErrorDetails,
export type ParseErrorConstructor<ErrorDetails> = (a: {
loc: Position;
details: ErrorDetails;
}) => ParseError<ErrorDetails>;

function toParseErrorConstructor<ErrorDetails: Object>({
function toParseErrorConstructor<ErrorDetails>({
toMessage,
...properties
}: ParseErrorCredentials<ErrorDetails>): ParseErrorConstructor<ErrorDetails> {
type ConstructorArgument = { loc: Position, details: ErrorDetails };
type ConstructorArgument = {
loc: Position;
details: ErrorDetails;
};

return function constructor({ loc, details }: ConstructorArgument) {
return instantiate<ParseError<ErrorDetails>>(
return instantiate<SyntaxError, ParseError<ErrorDetails>>(
SyntaxError,
{ ...properties, loc },
{
clone(overrides: { loc?: Position, details?: ErrorDetails } = {}) {
clone(
overrides: {
loc?: Position;
details?: ErrorDetails;
} = {},
) {
const loc = overrides.loc || {};
return constructor({
loc: new Position(
// @ts-expect-error line has been guarded
"line" in loc ? loc.line : this.loc.line,
// @ts-expect-error column has been guarded
"column" in loc ? loc.column : this.loc.column,
// @ts-expect-error index has been guarded
"index" in loc ? loc.index : this.loc.index,
),
details: { ...this.details, ...overrides.details },
});
},
details: { value: details, enumerable: false },
message: {
get() {
get(this: ConstructorArgument): string {
return `${toMessage(this.details)} (${this.loc.line}:${
this.loc.column
})`;
Expand Down Expand Up @@ -103,23 +112,20 @@ function toParseErrorConstructor<ErrorDetails: Object>({
// to the type system, avoiding the need to do so with $ObjMap (which doesn't
// work) in `ParseErrorEnum`. This hack won't be necessary when we switch to
// Typescript.
declare function toParseErrorCredentials<T: string>(
T,
?{ code?: ParseErrorCode, reasonCode?: string } | boolean,
): ParseErrorConstructor<{||}>;

// ESLint seems to erroneously think that Flow's overloading syntax is an
// accidental redeclaration of the function:
// https://github.com/babel/eslint-plugin-babel/issues/162
// eslint-disable-next-line no-redeclare
declare function toParseErrorCredentials<ErrorDetails>(
(ErrorDetails) => string,
?{ code?: ParseErrorCode, reasonCode?: string } | boolean,
export function toParseErrorCredentials(
message: string,
credentials?: { code?: ParseErrorCode; reasonCode?: string },
): ParseErrorConstructor<{}>;

export function toParseErrorCredentials<ErrorDetails>(
toMessage: (details: ErrorDetails) => string,
credentials?: { code?: ParseErrorCode; reasonCode?: string },
): ParseErrorConstructor<ErrorDetails>;

// See comment about eslint and Flow overloading above.
// eslint-disable-next-line no-redeclare
export function toParseErrorCredentials(toMessageOrMessage, credentials) {
export function toParseErrorCredentials(
toMessageOrMessage: string | ((details: unknown) => string),
credentials?: any,
) {
return {
toMessage:
typeof toMessageOrMessage === "string"
Expand All @@ -130,14 +136,11 @@ export function toParseErrorCredentials(toMessageOrMessage, credentials) {
}

// This is the templated form.
declare function ParseErrorEnum(string[]): typeof ParseErrorEnum;
export function ParseErrorEnum(a: TemplateStringsArray): typeof ParseErrorEnum;

// See comment about eslint and Flow overloading above.
// eslint-disable-next-line no-redeclare
declare function ParseErrorEnum<T>(
toParseErrorCredentials: (typeof toParseErrorCredentials) => T,
syntaxPlugin?: string,
): T;
export function ParseErrorEnum<
T extends (a: typeof toParseErrorCredentials) => unknown,
>(toParseErrorCredentials: T, syntaxPlugin?: string): ReturnType<T>;

// You call `ParseErrorEnum` with a mapping from `ReasonCode`'s to either error
// messages, or `toMessage` functions that define additional necessary `details`
Expand All @@ -148,19 +151,20 @@ declare function ParseErrorEnum<T>(
// ErrorWithDynamicMessage: _<{ type: string }>(({ type }) => `${type}`),
// });
//
// See comment about eslint and Flow overloading above.
// eslint-disable-next-line no-redeclare
export function ParseErrorEnum(argument, syntaxPlugin) {
export function ParseErrorEnum(argument: any, syntaxPlugin?: string) {
// If the first parameter is an array, that means we were called with a tagged
// template literal. Extract the syntaxPlugin from this, and call again in
// the "normalized" form.
if (Array.isArray(argument)) {
return toParseErrorCredentialsMap =>
return (toParseErrorCredentialsMap: any) =>
ParseErrorEnum(toParseErrorCredentialsMap, argument[0]);
}

const partialCredentials = argument(toParseErrorCredentials);
const ParseErrorConstructors = {};
const ParseErrorConstructors = {} as Record<
string,
ParseErrorConstructor<unknown>
>;

for (const reasonCode of Object.keys(partialCredentials)) {
ParseErrorConstructors[reasonCode] = toParseErrorConstructor({
Expand All @@ -174,10 +178,9 @@ export function ParseErrorEnum(argument, syntaxPlugin) {
return ParseErrorConstructors;
}

export type RaiseProperties<ErrorDetails> = {|
...ErrorDetails,
at: Position | NodeBase,
|};
export type RaiseProperties<ErrorDetails> = {
at: Position | Undone<NodeBase>;
} & ErrorDetails;

import ModuleErrors from "./parse-error/module-errors";
import StandardErrors from "./parse-error/standard-errors";
Expand Down

0 comments on commit a483aa2

Please sign in to comment.