Convert @babel/template from Flow to TS (#12317)
Co-authored-by: Nicolò Ribaudo <nicolo.ribaudo@gmail.com>
This commit is contained in:
parent
f80478c06d
commit
089c200c8b
@ -0,0 +1,36 @@
|
||||
declare module "@babel/template" {
|
||||
declare type PublicOpts = {
|
||||
placeholderWhitelist?: ?Set<string>,
|
||||
placeholderPattern?: ?(RegExp | false),
|
||||
preserveComments?: ?boolean,
|
||||
syntacticPlaceholders?: ?boolean,
|
||||
};
|
||||
|
||||
declare type PublicReplacements = { [string]: mixed } | Array<mixed>;
|
||||
|
||||
declare type TemplateBuilder<T> = {
|
||||
ast: {
|
||||
(tpl: string, opts: ?PublicOpts): T,
|
||||
(tpl: Array<string>, ...args: Array<mixed>): T,
|
||||
},
|
||||
(opts: PublicOpts): TemplateBuilder<T>,
|
||||
(tpl: string, opts: ?PublicOpts): (?PublicReplacements) => T,
|
||||
(tpl: Array<string>, ...args: Array<mixed>): (?PublicReplacements) => T,
|
||||
};
|
||||
|
||||
declare type Smart = TemplateBuilder<
|
||||
Array<BabelNodeStatement> | BabelNodeStatement
|
||||
>;
|
||||
declare type Statement = TemplateBuilder<BabelNodeStatement>;
|
||||
declare type Statements = TemplateBuilder<Array<BabelNodeStatement>>;
|
||||
declare type Expression = TemplateBuilder<BabelNodeExpression>;
|
||||
declare type Program = TemplateBuilder<BabelNodeProgram>;
|
||||
|
||||
declare export default Smart & {
|
||||
smart: Smart,
|
||||
statement: Statement,
|
||||
statements: Statements,
|
||||
expression: Expression,
|
||||
program: Program,
|
||||
};
|
||||
}
|
||||
@ -1,12 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import {
|
||||
merge,
|
||||
validate,
|
||||
type TemplateOpts,
|
||||
type PublicOpts,
|
||||
type PublicReplacements,
|
||||
} from "./options";
|
||||
import { merge, validate } from "./options";
|
||||
import type { TemplateOpts, PublicOpts, PublicReplacements } from "./options";
|
||||
import type { Formatter } from "./formatters";
|
||||
|
||||
import stringTemplate from "./string";
|
||||
@ -14,20 +7,22 @@ import literalTemplate from "./literal";
|
||||
|
||||
export type TemplateBuilder<T> = {
|
||||
// Build a new builder, merging the given options with the previous ones.
|
||||
(opts: PublicOpts): TemplateBuilder<T>,
|
||||
(opts: PublicOpts): TemplateBuilder<T>;
|
||||
|
||||
// Building from a string produces an AST builder function by default.
|
||||
(tpl: string, opts: ?PublicOpts): (?PublicReplacements) => T,
|
||||
(tpl: string, opts?: PublicOpts): (replacements?: PublicReplacements) => T;
|
||||
|
||||
// Building from a template literal produces an AST builder function by default.
|
||||
(tpl: Array<string>, ...args: Array<mixed>): (?PublicReplacements) => T,
|
||||
(tpl: TemplateStringsArray, ...args: Array<any>): (
|
||||
replacements?: PublicReplacements,
|
||||
) => T;
|
||||
|
||||
// Allow users to explicitly create templates that produce ASTs, skipping
|
||||
// the need for an intermediate function.
|
||||
ast: {
|
||||
(tpl: string, opts: ?PublicOpts): T,
|
||||
(tpl: Array<string>, ...args: Array<mixed>): T,
|
||||
},
|
||||
(tpl: string, opts?: PublicOpts): T;
|
||||
(tpl: TemplateStringsArray, ...args: Array<any>): T;
|
||||
};
|
||||
};
|
||||
|
||||
// Prebuild the options that will be used when parsing a `.ast` template.
|
||||
@ -69,7 +64,7 @@ export default function createTemplateBuilder<T>(
|
||||
);
|
||||
}
|
||||
throw new Error(`Unexpected template param ${typeof tpl}`);
|
||||
}: Function),
|
||||
}) as TemplateBuilder<T>,
|
||||
{
|
||||
ast: (tpl, ...args) => {
|
||||
if (typeof tpl === "string") {
|
||||
@ -98,7 +93,9 @@ export default function createTemplateBuilder<T>(
|
||||
);
|
||||
}
|
||||
|
||||
function extendedTrace<Arg, Result>(fn: Arg => Result): Arg => Result {
|
||||
function extendedTrace<Arg, Result>(
|
||||
fn: (_: Arg) => Result,
|
||||
): (_: Arg) => Result {
|
||||
// Since we lazy parse the template, we get the current stack so we have the
|
||||
// original stack to append if it errors when parsing
|
||||
let rootStack = "";
|
||||
@ -1,75 +0,0 @@
|
||||
// @flow
|
||||
|
||||
export type Formatter<T> = {
|
||||
code: string => string,
|
||||
validate: BabelNodeFile => void,
|
||||
unwrap: BabelNodeFile => T,
|
||||
};
|
||||
|
||||
function makeStatementFormatter<T>(
|
||||
fn: (Array<BabelNodeStatement>) => T,
|
||||
): Formatter<T> {
|
||||
return {
|
||||
// We need to prepend a ";" to force statement parsing so that
|
||||
// ExpressionStatement strings won't be parsed as directives.
|
||||
// Alongside that, we also prepend a comment so that when a syntax error
|
||||
// is encountered, the user will be less likely to get confused about
|
||||
// where the random semicolon came from.
|
||||
code: str => `/* @babel/template */;\n${str}`,
|
||||
validate: () => {},
|
||||
unwrap: (ast: BabelNodeFile): T => {
|
||||
return fn(ast.program.body.slice(1));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const smart: Formatter<
|
||||
Array<BabelNodeStatement> | BabelNodeStatement,
|
||||
> = makeStatementFormatter(body => {
|
||||
if (body.length > 1) {
|
||||
return body;
|
||||
} else {
|
||||
return body[0];
|
||||
}
|
||||
});
|
||||
|
||||
export const statements: Formatter<
|
||||
Array<BabelNodeStatement>,
|
||||
> = makeStatementFormatter(body => body);
|
||||
|
||||
export const statement: Formatter<BabelNodeStatement> = makeStatementFormatter(
|
||||
body => {
|
||||
// We do this validation when unwrapping since the replacement process
|
||||
// could have added or removed statements.
|
||||
if (body.length === 0) {
|
||||
throw new Error("Found nothing to return.");
|
||||
}
|
||||
if (body.length > 1) {
|
||||
throw new Error("Found multiple statements but wanted one");
|
||||
}
|
||||
|
||||
return body[0];
|
||||
},
|
||||
);
|
||||
|
||||
export const expression: Formatter<BabelNodeExpression> = {
|
||||
code: str => `(\n${str}\n)`,
|
||||
validate: ({ program }) => {
|
||||
if (program.body.length > 1) {
|
||||
throw new Error("Found multiple statements but wanted one");
|
||||
}
|
||||
// $FlowFixMe
|
||||
const expression = program.body[0].expression;
|
||||
if (expression.start === 0) {
|
||||
throw new Error("Parse result included parens.");
|
||||
}
|
||||
},
|
||||
// $FlowFixMe
|
||||
unwrap: ast => ast.program.body[0].expression,
|
||||
};
|
||||
|
||||
export const program: Formatter<BabelNodeProgram> = {
|
||||
code: str => str,
|
||||
validate: () => {},
|
||||
unwrap: ast => ast.program,
|
||||
};
|
||||
70
packages/babel-template/src/formatters.ts
Normal file
70
packages/babel-template/src/formatters.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import * as t from "@babel/types";
|
||||
|
||||
export type Formatter<T> = {
|
||||
code: (source: string) => string;
|
||||
validate: (ast: t.File) => void;
|
||||
unwrap: (ast: t.File) => T;
|
||||
};
|
||||
|
||||
function makeStatementFormatter<T>(
|
||||
fn: (statements: Array<t.Statement>) => T,
|
||||
): Formatter<T> {
|
||||
return {
|
||||
// We need to prepend a ";" to force statement parsing so that
|
||||
// ExpressionStatement strings won't be parsed as directives.
|
||||
// Alongside that, we also prepend a comment so that when a syntax error
|
||||
// is encountered, the user will be less likely to get confused about
|
||||
// where the random semicolon came from.
|
||||
code: str => `/* @babel/template */;\n${str}`,
|
||||
validate: () => {},
|
||||
unwrap: (ast: t.File): T => {
|
||||
return fn(ast.program.body.slice(1));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const smart = makeStatementFormatter(body => {
|
||||
if (body.length > 1) {
|
||||
return body;
|
||||
} else {
|
||||
return body[0];
|
||||
}
|
||||
});
|
||||
|
||||
export const statements = makeStatementFormatter(body => body);
|
||||
|
||||
export const statement = makeStatementFormatter(body => {
|
||||
// We do this validation when unwrapping since the replacement process
|
||||
// could have added or removed statements.
|
||||
if (body.length === 0) {
|
||||
throw new Error("Found nothing to return.");
|
||||
}
|
||||
if (body.length > 1) {
|
||||
throw new Error("Found multiple statements but wanted one");
|
||||
}
|
||||
|
||||
return body[0];
|
||||
});
|
||||
|
||||
export const expression: Formatter<t.Expression> = {
|
||||
code: str => `(\n${str}\n)`,
|
||||
validate: ast => {
|
||||
if (ast.program.body.length > 1) {
|
||||
throw new Error("Found multiple statements but wanted one");
|
||||
}
|
||||
if (expression.unwrap(ast).start === 0) {
|
||||
throw new Error("Parse result included parens.");
|
||||
}
|
||||
},
|
||||
unwrap: ({ program }) => {
|
||||
const [stmt] = program.body;
|
||||
t.assertExpressionStatement(stmt);
|
||||
return stmt.expression;
|
||||
},
|
||||
};
|
||||
|
||||
export const program: Formatter<t.Program> = {
|
||||
code: str => str,
|
||||
validate: () => {},
|
||||
unwrap: ast => ast.program,
|
||||
};
|
||||
@ -1,31 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import * as formatters from "./formatters";
|
||||
import createTemplateBuilder from "./builder";
|
||||
|
||||
export const smart = createTemplateBuilder<*>(formatters.smart);
|
||||
export const statement = createTemplateBuilder<*>(formatters.statement);
|
||||
export const statements = createTemplateBuilder<*>(formatters.statements);
|
||||
export const expression = createTemplateBuilder<*>(formatters.expression);
|
||||
export const program = createTemplateBuilder<*>(formatters.program);
|
||||
|
||||
type DefaultTemplateBuilder = typeof smart & {
|
||||
smart: typeof smart,
|
||||
statement: typeof statement,
|
||||
statements: typeof statements,
|
||||
expression: typeof expression,
|
||||
program: typeof program,
|
||||
ast: typeof smart.ast,
|
||||
};
|
||||
|
||||
export default Object.assign(
|
||||
((smart.bind(undefined): any): DefaultTemplateBuilder),
|
||||
{
|
||||
smart,
|
||||
statement,
|
||||
statements,
|
||||
expression,
|
||||
program,
|
||||
ast: smart.ast,
|
||||
},
|
||||
);
|
||||
25
packages/babel-template/src/index.ts
Normal file
25
packages/babel-template/src/index.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import * as formatters from "./formatters";
|
||||
import createTemplateBuilder from "./builder";
|
||||
|
||||
export const smart = createTemplateBuilder(formatters.smart);
|
||||
export const statement = createTemplateBuilder(formatters.statement);
|
||||
export const statements = createTemplateBuilder(formatters.statements);
|
||||
export const expression = createTemplateBuilder(formatters.expression);
|
||||
export const program = createTemplateBuilder(formatters.program);
|
||||
|
||||
type DefaultTemplateBuilder = typeof smart & {
|
||||
smart: typeof smart;
|
||||
statement: typeof statement;
|
||||
statements: typeof statements;
|
||||
expression: typeof expression;
|
||||
program: typeof program;
|
||||
};
|
||||
|
||||
export default Object.assign(smart.bind(undefined) as DefaultTemplateBuilder, {
|
||||
smart,
|
||||
statement,
|
||||
statements,
|
||||
expression,
|
||||
program,
|
||||
ast: smart.ast,
|
||||
});
|
||||
@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import type { Formatter } from "./formatters";
|
||||
import { normalizeReplacements, type TemplateOpts } from "./options";
|
||||
import type { TemplateReplacements, TemplateOpts } from "./options";
|
||||
import { normalizeReplacements } from "./options";
|
||||
import parseAndBuildMetadata from "./parse";
|
||||
import populatePlaceholders from "./populate";
|
||||
|
||||
@ -9,16 +8,16 @@ export default function literalTemplate<T>(
|
||||
formatter: Formatter<T>,
|
||||
tpl: Array<string>,
|
||||
opts: TemplateOpts,
|
||||
): (Array<mixed>) => mixed => T {
|
||||
): (_: Array<unknown>) => (_: unknown) => T {
|
||||
const { metadata, names } = buildLiteralData(formatter, tpl, opts);
|
||||
|
||||
return (arg: Array<mixed>) => {
|
||||
const defaultReplacements = arg.reduce((acc, replacement, i) => {
|
||||
acc[names[i]] = replacement;
|
||||
return acc;
|
||||
}, {});
|
||||
return arg => {
|
||||
const defaultReplacements: TemplateReplacements = {};
|
||||
arg.forEach((replacement, i) => {
|
||||
defaultReplacements[names[i]] = replacement;
|
||||
});
|
||||
|
||||
return (arg: mixed) => {
|
||||
return (arg: unknown) => {
|
||||
const replacements = normalizeReplacements(arg);
|
||||
|
||||
if (replacements) {
|
||||
@ -88,7 +87,7 @@ function buildLiteralData<T>(
|
||||
function buildTemplateCode(
|
||||
tpl: Array<string>,
|
||||
prefix: string,
|
||||
): { names: Array<string>, code: string } {
|
||||
): { names: Array<string>; code: string } {
|
||||
const names = [];
|
||||
|
||||
let code = tpl[0];
|
||||
@ -1,6 +1,4 @@
|
||||
// @flow
|
||||
|
||||
import type { Options as ParserOpts } from "@babel/parser/src/options";
|
||||
import type { ParserOptions as ParserOpts } from "@babel/parser";
|
||||
|
||||
export type { ParserOpts };
|
||||
|
||||
@ -15,8 +13,7 @@ export type PublicOpts = {
|
||||
*
|
||||
* This option can be used when using %%foo%% style placeholders.
|
||||
*/
|
||||
placeholderWhitelist?: ?Set<string>,
|
||||
|
||||
placeholderWhitelist?: Set<string>;
|
||||
/**
|
||||
* A pattern to search for when looking for Identifier and StringLiteral
|
||||
* nodes that can be replaced.
|
||||
@ -28,30 +25,28 @@ export type PublicOpts = {
|
||||
*
|
||||
* This option can be used when using %%foo%% style placeholders.
|
||||
*/
|
||||
placeholderPattern?: ?(RegExp | false),
|
||||
|
||||
placeholderPattern?: RegExp | false;
|
||||
/**
|
||||
* 'true' to pass through comments from the template into the resulting AST,
|
||||
* or 'false' to automatically discard comments. Defaults to 'false'.
|
||||
*/
|
||||
preserveComments?: ?boolean,
|
||||
|
||||
preserveComments?: boolean;
|
||||
/**
|
||||
* 'true' to use %%foo%% style placeholders, 'false' to use legacy placeholders
|
||||
* described by placeholderPattern or placeholderWhitelist.
|
||||
* When it is not set, it behaves as 'true' if there are syntactic placeholders,
|
||||
* otherwise as 'false'.
|
||||
*/
|
||||
syntacticPlaceholders?: ?boolean,
|
||||
syntacticPlaceholders?: boolean | null;
|
||||
};
|
||||
|
||||
export type TemplateOpts = {|
|
||||
parser: ParserOpts,
|
||||
placeholderWhitelist: Set<string> | void,
|
||||
placeholderPattern: RegExp | false | void,
|
||||
preserveComments: boolean | void,
|
||||
syntacticPlaceholders: boolean | void,
|
||||
|};
|
||||
export type TemplateOpts = {
|
||||
parser: ParserOpts;
|
||||
placeholderWhitelist?: Set<string>;
|
||||
placeholderPattern?: RegExp | false;
|
||||
preserveComments?: boolean;
|
||||
syntacticPlaceholders?: boolean;
|
||||
};
|
||||
|
||||
export function merge(a: TemplateOpts, b: TemplateOpts): TemplateOpts {
|
||||
const {
|
||||
@ -73,7 +68,7 @@ export function merge(a: TemplateOpts, b: TemplateOpts): TemplateOpts {
|
||||
};
|
||||
}
|
||||
|
||||
export function validate(opts: mixed): TemplateOpts {
|
||||
export function validate(opts: unknown): TemplateOpts {
|
||||
if (opts != null && typeof opts !== "object") {
|
||||
throw new Error("Unknown template options.");
|
||||
}
|
||||
@ -84,7 +79,7 @@ export function validate(opts: mixed): TemplateOpts {
|
||||
preserveComments,
|
||||
syntacticPlaceholders,
|
||||
...parser
|
||||
} = opts || {};
|
||||
} = opts || ({} as any);
|
||||
|
||||
if (placeholderWhitelist != null && !(placeholderWhitelist instanceof Set)) {
|
||||
throw new Error(
|
||||
@ -137,11 +132,11 @@ export function validate(opts: mixed): TemplateOpts {
|
||||
};
|
||||
}
|
||||
|
||||
export type PublicReplacements = { [string]: mixed } | Array<mixed>;
|
||||
export type TemplateReplacements = { [string]: mixed } | void;
|
||||
export type PublicReplacements = { [x: string]: unknown } | Array<unknown>;
|
||||
export type TemplateReplacements = { [x: string]: unknown } | void;
|
||||
|
||||
export function normalizeReplacements(
|
||||
replacements: mixed,
|
||||
replacements: unknown,
|
||||
): TemplateReplacements {
|
||||
if (Array.isArray(replacements)) {
|
||||
return replacements.reduce((acc, replacement, i) => {
|
||||
@ -149,7 +144,7 @@ export function normalizeReplacements(
|
||||
return acc;
|
||||
}, {});
|
||||
} else if (typeof replacements === "object" || replacements == null) {
|
||||
return (replacements: any) || undefined;
|
||||
return (replacements as any) || undefined;
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
@ -1,4 +1,3 @@
|
||||
// @flow
|
||||
import * as t from "@babel/types";
|
||||
import type { TraversalAncestors, TraversalHandler } from "@babel/types";
|
||||
import { parse } from "@babel/parser";
|
||||
@ -7,18 +6,18 @@ import type { TemplateOpts, ParserOpts } from "./options";
|
||||
import type { Formatter } from "./formatters";
|
||||
|
||||
export type Metadata = {
|
||||
ast: BabelNodeFile,
|
||||
placeholders: Array<Placeholder>,
|
||||
placeholderNames: Set<string>,
|
||||
ast: t.File;
|
||||
placeholders: Array<Placeholder>;
|
||||
placeholderNames: Set<string>;
|
||||
};
|
||||
|
||||
type PlaceholderType = "string" | "param" | "statement" | "other";
|
||||
export type Placeholder = {|
|
||||
name: string,
|
||||
resolve: BabelNodeFile => { parent: BabelNode, key: string, index?: number },
|
||||
type: PlaceholderType,
|
||||
isDuplicate: boolean,
|
||||
|};
|
||||
export type Placeholder = {
|
||||
name: string;
|
||||
resolve: (a: t.File) => { parent: t.Node; key: string; index?: number };
|
||||
type: PlaceholderType;
|
||||
isDuplicate: boolean;
|
||||
};
|
||||
|
||||
const PATTERN = /^[_$A-Z0-9]+$/;
|
||||
|
||||
@ -44,15 +43,15 @@ export default function parseAndBuildMetadata<T>(
|
||||
|
||||
const syntactic = {
|
||||
placeholders: [],
|
||||
placeholderNames: new Set(),
|
||||
placeholderNames: new Set<string>(),
|
||||
};
|
||||
const legacy = {
|
||||
placeholders: [],
|
||||
placeholderNames: new Set(),
|
||||
placeholderNames: new Set<string>(),
|
||||
};
|
||||
const isLegacyRef = { value: undefined };
|
||||
|
||||
t.traverse(ast, (placeholderVisitorHandler: TraversalHandler<*>), {
|
||||
t.traverse(ast, placeholderVisitorHandler as TraversalHandler<any>, {
|
||||
syntactic,
|
||||
legacy,
|
||||
isLegacyRef,
|
||||
@ -68,11 +67,11 @@ export default function parseAndBuildMetadata<T>(
|
||||
}
|
||||
|
||||
function placeholderVisitorHandler(
|
||||
node: BabelNode,
|
||||
node: t.Node,
|
||||
ancestors: TraversalAncestors,
|
||||
state: MetadataState,
|
||||
) {
|
||||
let name;
|
||||
let name: string;
|
||||
|
||||
if (t.isPlaceholder(node)) {
|
||||
if (state.syntacticPlaceholders === false) {
|
||||
@ -81,16 +80,16 @@ function placeholderVisitorHandler(
|
||||
"'.syntacticPlaceholders' is false.",
|
||||
);
|
||||
} else {
|
||||
name = ((node: any).name: BabelNodeIdentifier).name;
|
||||
name = node.name.name;
|
||||
state.isLegacyRef.value = false;
|
||||
}
|
||||
} else if (state.isLegacyRef.value === false || state.syntacticPlaceholders) {
|
||||
return;
|
||||
} else if (t.isIdentifier(node) || t.isJSXIdentifier(node)) {
|
||||
name = ((node: any): BabelNodeIdentifier).name;
|
||||
name = node.name;
|
||||
state.isLegacyRef.value = true;
|
||||
} else if (t.isStringLiteral(node)) {
|
||||
name = ((node: any): BabelNodeStringLiteral).value;
|
||||
name = node.value;
|
||||
state.isLegacyRef.value = true;
|
||||
} else {
|
||||
return;
|
||||
@ -156,15 +155,15 @@ function placeholderVisitorHandler(
|
||||
placeholderNames.add(name);
|
||||
}
|
||||
|
||||
function resolveAncestors(ast: BabelNodeFile, ancestors: TraversalAncestors) {
|
||||
let parent: BabelNode = ast;
|
||||
function resolveAncestors(ast: t.File, ancestors: TraversalAncestors) {
|
||||
let parent: t.Node = ast;
|
||||
for (let i = 0; i < ancestors.length - 1; i++) {
|
||||
const { key, index } = ancestors[i];
|
||||
|
||||
if (index === undefined) {
|
||||
parent = (parent: any)[key];
|
||||
parent = (parent as any)[key];
|
||||
} else {
|
||||
parent = (parent: any)[key][index];
|
||||
parent = (parent as any)[key][index];
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,24 +174,26 @@ function resolveAncestors(ast: BabelNodeFile, ancestors: TraversalAncestors) {
|
||||
|
||||
type MetadataState = {
|
||||
syntactic: {
|
||||
placeholders: Array<Placeholder>,
|
||||
placeholderNames: Set<string>,
|
||||
},
|
||||
placeholders: Array<Placeholder>;
|
||||
placeholderNames: Set<string>;
|
||||
};
|
||||
legacy: {
|
||||
placeholders: Array<Placeholder>,
|
||||
placeholderNames: Set<string>,
|
||||
},
|
||||
isLegacyRef: { value: boolean | void },
|
||||
placeholderWhitelist: Set<string> | void,
|
||||
placeholderPattern: RegExp | false | void,
|
||||
syntacticPlaceholders: boolean | void,
|
||||
placeholders: Array<Placeholder>;
|
||||
placeholderNames: Set<string>;
|
||||
};
|
||||
isLegacyRef: {
|
||||
value?: boolean;
|
||||
};
|
||||
placeholderWhitelist?: Set<string>;
|
||||
placeholderPattern?: RegExp | false;
|
||||
syntacticPlaceholders?: boolean;
|
||||
};
|
||||
|
||||
function parseWithCodeFrame(
|
||||
code: string,
|
||||
parserOpts: ParserOpts,
|
||||
syntacticPlaceholders?: boolean,
|
||||
): BabelNodeFile {
|
||||
): t.File {
|
||||
const plugins = (parserOpts.plugins || []).slice();
|
||||
if (syntacticPlaceholders !== false) {
|
||||
plugins.push("placeholders");
|
||||
@ -207,7 +208,6 @@ function parseWithCodeFrame(
|
||||
};
|
||||
|
||||
try {
|
||||
// $FlowFixMe - The parser AST is not the same type as the babel-types type.
|
||||
return parse(code, parserOpts);
|
||||
} catch (err) {
|
||||
const loc = err.loc;
|
||||
@ -1,4 +1,3 @@
|
||||
// @flow
|
||||
import * as t from "@babel/types";
|
||||
|
||||
import type { TemplateReplacements } from "./options";
|
||||
@ -7,7 +6,7 @@ import type { Metadata, Placeholder } from "./parse";
|
||||
export default function populatePlaceholders(
|
||||
metadata: Metadata,
|
||||
replacements: TemplateReplacements,
|
||||
): BabelNodeFile {
|
||||
): t.File {
|
||||
const ast = t.cloneNode(metadata.ast);
|
||||
|
||||
if (replacements) {
|
||||
@ -55,7 +54,7 @@ export default function populatePlaceholders(
|
||||
|
||||
function applyReplacement(
|
||||
placeholder: Placeholder,
|
||||
ast: BabelNodeFile,
|
||||
ast: t.File,
|
||||
replacement: any,
|
||||
) {
|
||||
// Track inserted nodes and clone them if they are inserted more than
|
||||
@ -86,7 +85,7 @@ function applyReplacement(
|
||||
} else if (typeof replacement === "string") {
|
||||
replacement = t.expressionStatement(t.identifier(replacement));
|
||||
} else if (!t.isStatement(replacement)) {
|
||||
replacement = t.expressionStatement((replacement: any));
|
||||
replacement = t.expressionStatement(replacement as any);
|
||||
}
|
||||
} else {
|
||||
if (replacement && !Array.isArray(replacement)) {
|
||||
@ -94,7 +93,7 @@ function applyReplacement(
|
||||
replacement = t.identifier(replacement);
|
||||
}
|
||||
if (!t.isStatement(replacement)) {
|
||||
replacement = t.expressionStatement((replacement: any));
|
||||
replacement = t.expressionStatement(replacement as any);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,9 +115,9 @@ function applyReplacement(
|
||||
if (index === undefined) {
|
||||
t.validate(parent, key, replacement);
|
||||
|
||||
(parent: any)[key] = replacement;
|
||||
(parent as any)[key] = replacement;
|
||||
} else {
|
||||
const items: Array<BabelNode> = (parent: any)[key].slice();
|
||||
const items: Array<t.Node> = (parent as any)[key].slice();
|
||||
|
||||
if (placeholder.type === "statement" || placeholder.type === "param") {
|
||||
if (replacement == null) {
|
||||
@ -133,6 +132,6 @@ function applyReplacement(
|
||||
}
|
||||
|
||||
t.validate(parent, key, items);
|
||||
(parent: any)[key] = items;
|
||||
(parent as any)[key] = items;
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
import type { Formatter } from "./formatters";
|
||||
import { normalizeReplacements, type TemplateOpts } from "./options";
|
||||
import type { TemplateOpts } from "./options";
|
||||
import { normalizeReplacements } from "./options";
|
||||
import parseAndBuildMetadata from "./parse";
|
||||
import populatePlaceholders from "./populate";
|
||||
|
||||
@ -8,12 +8,12 @@ export default function stringTemplate<T>(
|
||||
formatter: Formatter<T>,
|
||||
code: string,
|
||||
opts: TemplateOpts,
|
||||
): mixed => T {
|
||||
): (arg?: unknown) => T {
|
||||
code = formatter.code(code);
|
||||
|
||||
let metadata;
|
||||
|
||||
return (arg?: mixed) => {
|
||||
return (arg?: unknown) => {
|
||||
const replacements = normalizeReplacements(arg);
|
||||
|
||||
if (!metadata) metadata = parseAndBuildMetadata(formatter, code, opts);
|
||||
Loading…
x
Reference in New Issue
Block a user