Skip to content

Commit

Permalink
fix
Browse files Browse the repository at this point in the history
  • Loading branch information
liuxingbaoyu committed Mar 31, 2023
1 parent 936e649 commit 37b4f08
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 59 deletions.
52 changes: 22 additions & 30 deletions packages/babel-template/src/literal.ts
Expand Up @@ -45,41 +45,33 @@ function buildLiteralData<T>(
tpl: Array<string>,
opts: TemplateOpts,
) {
let names;
let nameSet: Set<string>;
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 };
}
Expand Down
45 changes: 16 additions & 29 deletions packages/babel-template/src/parse.ts
Expand Up @@ -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";
Expand Down Expand Up @@ -54,28 +54,19 @@ export default function parseAndBuildMetadata<T>(

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<any>, {
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),
};
}

Expand All @@ -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
Expand All @@ -121,7 +111,7 @@ function placeholderVisitorHandler(
}

if (
state.isLegacyRef.value &&
!hasSyntacticPlaceholders &&
(state.placeholderPattern === false ||
!(state.placeholderPattern || PATTERN).test(name)) &&
!state.placeholderWhitelist?.has(name)
Expand Down Expand Up @@ -155,7 +145,7 @@ function placeholderVisitorHandler(
type = "other";
}

const { placeholders, placeholderNames } = state.isLegacyRef.value
const { placeholders, placeholderNames } = !hasSyntacticPlaceholders
? state.legacy
: state.syntactic;

Expand Down Expand Up @@ -194,9 +184,6 @@ type MetadataState = {
placeholders: Array<Placeholder>;
placeholderNames: Set<string>;
};
isLegacyRef: {
value?: boolean;
};
placeholderWhitelist?: Set<string>;
placeholderPattern?: RegExp | false;
syntacticPlaceholders?: boolean;
Expand Down
58 changes: 58 additions & 0 deletions packages/babel-template/test/index.js
Expand Up @@ -271,6 +271,64 @@ describe("@babel/template", function () {

expect(generator(result).code).toEqual("<div>{'content'}</div>");
});

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", () => {
Expand Down

0 comments on commit 37b4f08

Please sign in to comment.