From 37b4f087738a137d1899a929b1f52392fb3425b1 Mon Sep 17 00:00:00 2001 From: liuxingbaoyu <30521560+liuxingbaoyu@users.noreply.github.com> Date: Fri, 31 Mar 2023 09:59:50 +0800 Subject: [PATCH] fix --- packages/babel-template/src/literal.ts | 52 ++++++++++------------- packages/babel-template/src/parse.ts | 45 +++++++------------- packages/babel-template/test/index.js | 58 ++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 59 deletions(-) diff --git a/packages/babel-template/src/literal.ts b/packages/babel-template/src/literal.ts index c6e79a1fcc8f..93aed9cdaf49 100644 --- a/packages/babel-template/src/literal.ts +++ b/packages/babel-template/src/literal.ts @@ -45,41 +45,33 @@ function buildLiteralData( tpl: Array, opts: TemplateOpts, ) { - let names; - let nameSet: Set; - let metadata; - let prefix = ""; + let prefix = "BABEL_TPL$"; + + const raw = tpl.join(""); do { // If there are cases where the template already contains $0 or any other - // matching pattern, we keep adding "$" characters until a unique prefix + // matching pattern, we keep adding "$$" characters until a unique prefix // is found. - prefix += "$"; - const result = buildTemplateCode(tpl, prefix); - - names = result.names; - nameSet = new Set(names); - metadata = parseAndBuildMetadata(formatter, formatter.code(result.code), { - parser: opts.parser, - - // Explicitly include our generated names in the whitelist so users never - // have to think about whether their placeholder pattern will match. - placeholderWhitelist: new Set( - result.names.concat( - opts.placeholderWhitelist - ? Array.from(opts.placeholderWhitelist) - : [], - ), + prefix = "$$" + prefix; + } while (raw.includes(prefix)); + + const { names, code } = buildTemplateCode(tpl, prefix); + + const metadata = parseAndBuildMetadata(formatter, formatter.code(code), { + parser: opts.parser, + + // Explicitly include our generated names in the whitelist so users never + // have to think about whether their placeholder pattern will match. + placeholderWhitelist: new Set( + names.concat( + opts.placeholderWhitelist ? Array.from(opts.placeholderWhitelist) : [], ), - placeholderPattern: opts.placeholderPattern, - preserveComments: opts.preserveComments, - syntacticPlaceholders: opts.syntacticPlaceholders, - }); - } while ( - metadata.placeholders.some( - placeholder => placeholder.isDuplicate && nameSet.has(placeholder.name), - ) - ); + ), + placeholderPattern: opts.placeholderPattern, + preserveComments: opts.preserveComments, + syntacticPlaceholders: opts.syntacticPlaceholders, + }); return { metadata, names }; } diff --git a/packages/babel-template/src/parse.ts b/packages/babel-template/src/parse.ts index 3e9e958b8d74..913a158873ee 100644 --- a/packages/babel-template/src/parse.ts +++ b/packages/babel-template/src/parse.ts @@ -12,7 +12,7 @@ import { traverse, } from "@babel/types"; import type * as t from "@babel/types"; -import type { TraversalAncestors, TraversalHandler } from "@babel/types"; +import type { TraversalAncestors } from "@babel/types"; import { parse } from "@babel/parser"; import { codeFrameColumns } from "@babel/code-frame"; import type { TemplateOpts, ParserOpts } from "./options"; @@ -54,28 +54,19 @@ export default function parseAndBuildMetadata( formatter.validate(ast); - const syntactic: MetadataState["syntactic"] = { - placeholders: [], - placeholderNames: new Set(), - }; - const legacy: MetadataState["legacy"] = { - placeholders: [], - placeholderNames: new Set(), - }; - const isLegacyRef: MetadataState["isLegacyRef"] = { value: undefined }; - - traverse(ast, placeholderVisitorHandler as TraversalHandler, { - syntactic, - legacy, - isLegacyRef, + const state: MetadataState = { + syntactic: { placeholders: [], placeholderNames: new Set() }, + legacy: { placeholders: [], placeholderNames: new Set() }, placeholderWhitelist, placeholderPattern, syntacticPlaceholders, - }); + }; + + traverse(ast, placeholderVisitorHandler, state); return { ast, - ...(isLegacyRef.value ? legacy : syntactic), + ...(state.syntactic.placeholders.length ? state.syntactic : state.legacy), }; } @@ -86,30 +77,29 @@ function placeholderVisitorHandler( ) { let name: string; + let hasSyntacticPlaceholders = state.syntactic.placeholders.length > 0; + if (isPlaceholder(node)) { if (state.syntacticPlaceholders === false) { throw new Error( "%%foo%%-style placeholders can't be used when " + "'.syntacticPlaceholders' is false.", ); - } else { - name = node.name.name; - state.isLegacyRef.value = false; } - } else if (state.isLegacyRef.value === false || state.syntacticPlaceholders) { + name = node.name.name; + hasSyntacticPlaceholders = true; + } else if (hasSyntacticPlaceholders || state.syntacticPlaceholders) { return; } else if (isIdentifier(node) || isJSXIdentifier(node)) { name = node.name; - state.isLegacyRef.value = true; } else if (isStringLiteral(node)) { name = node.value; - state.isLegacyRef.value = true; } else { return; } if ( - !state.isLegacyRef.value && + hasSyntacticPlaceholders && (state.placeholderPattern != null || state.placeholderWhitelist != null) ) { // This check is also in options.js. We need it there to handle the default @@ -121,7 +111,7 @@ function placeholderVisitorHandler( } if ( - state.isLegacyRef.value && + !hasSyntacticPlaceholders && (state.placeholderPattern === false || !(state.placeholderPattern || PATTERN).test(name)) && !state.placeholderWhitelist?.has(name) @@ -155,7 +145,7 @@ function placeholderVisitorHandler( type = "other"; } - const { placeholders, placeholderNames } = state.isLegacyRef.value + const { placeholders, placeholderNames } = !hasSyntacticPlaceholders ? state.legacy : state.syntactic; @@ -194,9 +184,6 @@ type MetadataState = { placeholders: Array; placeholderNames: Set; }; - isLegacyRef: { - value?: boolean; - }; placeholderWhitelist?: Set; placeholderPattern?: RegExp | false; syntacticPlaceholders?: boolean; diff --git a/packages/babel-template/test/index.js b/packages/babel-template/test/index.js index f185c8d8e9bd..a031419b9708 100644 --- a/packages/babel-template/test/index.js +++ b/packages/babel-template/test/index.js @@ -271,6 +271,64 @@ describe("@babel/template", function () { expect(generator(result).code).toEqual("
{'content'}
"); }); + + it("should work with `export { x }`", () => { + const result = template.ast` + export { ${t.identifier("x")} } + $$$$BABEL_TPL$0; + `; + expect(result).toMatchInlineSnapshot(` + Array [ + Object { + "declaration": null, + "loc": undefined, + "source": null, + "specifiers": Array [ + Object { + "exported": Object { + "name": "x", + "type": "Identifier", + }, + "loc": undefined, + "local": Object { + "name": "x", + "type": "Identifier", + }, + "type": "ExportSpecifier", + }, + ], + "type": "ExportNamedDeclaration", + }, + Object { + "expression": Object { + "loc": undefined, + "name": "$$$$BABEL_TPL$0", + "type": "Identifier", + }, + "loc": undefined, + "type": "ExpressionStatement", + }, + ] + `); + }); + + it("should work with `const a = { x }`", () => { + const result = template.ast` + { + const a = { ${t.identifier("x")} }; + $$$$BABEL_TPL$0; + } + `; + + expect(generator(result).code).toMatchInlineSnapshot(` + "{ + const a = { + x + }; + $$$$BABEL_TPL$0; + }" + `); + }); }); describe(".syntacticPlaceholders", () => {