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

Convert @babel/parser to TypeScript #14783

Merged
merged 9 commits into from Jul 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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