Reuse arrays in visitor keys (#18386)
This commit is contained in:
1 parent
c45fef106e
commit
d3eb2b2d08
21 files changed
+273
-60
No files matched your search
@@ -598,7 +598,10 @@ const pluginFiles = [
|
||||
"const LOCAL_DEBUG = false &&",
|
||||
);
|
||||
|
||||
text = text.replace(/(?<=\n)export .*?;/u, "export { preprocess };");
|
||||
text = text.replace(
|
||||
/(?<=\n)export .*?;/u,
|
||||
"export { preprocess, getVoidTags, visitorKeys };",
|
||||
);
|
||||
|
||||
return text;
|
||||
},
|
||||
|
||||
@@ -1,33 +1,89 @@
|
||||
import assert from "node:assert";
|
||||
import path from "node:path";
|
||||
import url from "node:url";
|
||||
import { isValidIdentifier } from "@babel/types";
|
||||
import { outdent } from "outdent";
|
||||
import serialize from "serialize-javascript";
|
||||
|
||||
function serializeModule(module) {
|
||||
return Object.entries(module)
|
||||
.map(([specifier, value]) => {
|
||||
const code =
|
||||
value instanceof RegExp
|
||||
? `/${value.source}/${value.flags}`
|
||||
: serialize(value, { space: 2, unsafe: true });
|
||||
if (specifier === "default") {
|
||||
return `export default ${code};`;
|
||||
}
|
||||
|
||||
if (!isValidIdentifier(specifier)) {
|
||||
throw new Error(`${specifier} is not a valid specifier`);
|
||||
}
|
||||
|
||||
return `export const ${specifier} = ${code};`;
|
||||
})
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
function serializeVisitorKeys(module) {
|
||||
const specifiers = Object.keys(module);
|
||||
assert.deepEqual(specifiers, ["default"]);
|
||||
|
||||
const references = new Map();
|
||||
const properties = Object.entries(module.default);
|
||||
for (const [, keys] of properties) {
|
||||
if (!references.has(keys)) {
|
||||
references.set(keys, 1);
|
||||
} else {
|
||||
references.set(keys, references.get(keys) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const variables = [];
|
||||
const propertiesText = [];
|
||||
for (const [type, keys] of properties) {
|
||||
const isSingleReference = references.get(keys) === 1;
|
||||
const key = isValidIdentifier(type) ? type : JSON.stringify(type);
|
||||
if (isSingleReference) {
|
||||
propertiesText.push(` ${key}: ${JSON.stringify(keys)},`);
|
||||
} else {
|
||||
if (!variables.includes(keys)) {
|
||||
variables.push(keys);
|
||||
}
|
||||
const index = variables.indexOf(keys);
|
||||
propertiesText.push(` ${key}: vk[${index}],`);
|
||||
}
|
||||
}
|
||||
|
||||
const code = outdent`
|
||||
const vk = [
|
||||
${variables.map((keys) => ` ${JSON.stringify(keys)},`).join("\n")}
|
||||
];
|
||||
|
||||
export default {
|
||||
${propertiesText.join("\n")}
|
||||
};
|
||||
`;
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
export default function esbuildPluginEvaluate() {
|
||||
return {
|
||||
name: "evaluate",
|
||||
setup(build) {
|
||||
build.onLoad(
|
||||
{ filter: /\.evaluate\.[cm]?js$/, namespace: "file" },
|
||||
async ({ path }) => {
|
||||
const module = await import(url.pathToFileURL(path));
|
||||
const text = Object.entries(module)
|
||||
.map(([specifier, value]) => {
|
||||
const code =
|
||||
value instanceof RegExp
|
||||
? `/${value.source}/${value.flags}`
|
||||
: serialize(value, { space: 2 });
|
||||
async ({ path: filePath }) => {
|
||||
const module = await import(url.pathToFileURL(filePath));
|
||||
const isVisitorKeys =
|
||||
path.basename(filePath, path.extname(filePath)) ===
|
||||
"visitor-keys.evaluate";
|
||||
|
||||
if (specifier === "default") {
|
||||
return `export default ${code};`;
|
||||
}
|
||||
const text = isVisitorKeys
|
||||
? serializeVisitorKeys(module)
|
||||
: serializeModule(module);
|
||||
|
||||
if (!isValidIdentifier(specifier)) {
|
||||
throw new Error(`${specifier} is not a valid specifier`);
|
||||
}
|
||||
|
||||
return `export const ${specifier} = ${code};`;
|
||||
})
|
||||
.join("\n");
|
||||
return { contents: text };
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.evaluate.js";
|
||||
|
||||
const getVisitorKeys = createGetVisitorKeys(visitorKeys);
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
const visitorKeys = {
|
||||
import { generateReferenceSharedVisitorKeys } from "../utilities/visitor-keys.js";
|
||||
|
||||
const visitorKeys = generateReferenceSharedVisitorKeys({
|
||||
"css-root": ["frontMatter", "nodes"],
|
||||
"css-comment": [],
|
||||
"css-rule": ["selector", "nodes"],
|
||||
@@ -43,6 +45,6 @@ const visitorKeys = {
|
||||
"value-atword": [],
|
||||
"value-unicode-range": [],
|
||||
"value-unknown": [],
|
||||
};
|
||||
});
|
||||
|
||||
export default visitorKeys;
|
||||
@@ -1,5 +1,5 @@
|
||||
import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.evaluate.js";
|
||||
import visitorKeys from "./visitor-keys.js";
|
||||
|
||||
const getVisitorKeys = createGetVisitorKeys(visitorKeys);
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { getVoidTags } from "@glimmer/syntax";
|
||||
|
||||
const htmlVoidElements = new Set(getVoidTags());
|
||||
|
||||
export default htmlVoidElements;
|
||||
@@ -1,4 +1,7 @@
|
||||
import htmlVoidElements from "./html-void-elements.evaluate.js";
|
||||
// Do not use void tags from other package, won't match
|
||||
import { getVoidTags } from "@glimmer/syntax";
|
||||
|
||||
const htmlVoidElements = new Set(getVoidTags());
|
||||
|
||||
function isUppercase(string) {
|
||||
return string.toUpperCase() === string;
|
||||
|
||||
File renamed without changes.
@@ -1,5 +1,5 @@
|
||||
import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.evaluate.js";
|
||||
|
||||
const getVisitorKeys = createGetVisitorKeys(visitorKeys, "kind");
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
const visitorKeys = {
|
||||
import { generateReferenceSharedVisitorKeys } from "../utilities/visitor-keys.js";
|
||||
|
||||
const visitorKeys = generateReferenceSharedVisitorKeys({
|
||||
root: ["children"],
|
||||
element: ["attrs", "children"],
|
||||
ieConditionalComment: ["children"],
|
||||
@@ -17,6 +19,6 @@ const visitorKeys = {
|
||||
angularLetDeclarationInitializer: [],
|
||||
angularIcuExpression: ["cases"],
|
||||
angularIcuCase: ["expression"],
|
||||
};
|
||||
});
|
||||
|
||||
export default visitorKeys;
|
||||
@@ -3,10 +3,11 @@ import { visitorKeys as tsVisitorKeys } from "@typescript-eslint/visitor-keys";
|
||||
import { visitorKeys as angularVisitorKeys } from "angular-estree-parser";
|
||||
import flowVisitorKeys from "hermes-parser/dist/generated/ESTreeVisitorKeys.js";
|
||||
import {
|
||||
generateReferenceSharedVisitorKeys,
|
||||
removeNodeTypes,
|
||||
removeVisitorKeys,
|
||||
unionVisitorKeys,
|
||||
} from "./utilities.js";
|
||||
} from "../../utilities/visitor-keys.js";
|
||||
|
||||
const additionalVisitorKeys = {
|
||||
// Prettier
|
||||
@@ -70,4 +71,7 @@ let visitorKeys = unionVisitorKeys(
|
||||
visitorKeys = removeNodeTypes(visitorKeys, excludeNodeTypes);
|
||||
visitorKeys = removeVisitorKeys(visitorKeys, excludeVisitorKeys);
|
||||
|
||||
// This should be the last step
|
||||
visitorKeys = generateReferenceSharedVisitorKeys(visitorKeys);
|
||||
|
||||
export default visitorKeys;
|
||||
@@ -1,5 +1,5 @@
|
||||
import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.evaluate.js";
|
||||
|
||||
const getVisitorKeys = createGetVisitorKeys(visitorKeys);
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
const visitorKeys = {
|
||||
import { generateReferenceSharedVisitorKeys } from "../utilities/visitor-keys.js";
|
||||
|
||||
const visitorKeys = generateReferenceSharedVisitorKeys({
|
||||
JsonRoot: ["node"],
|
||||
ArrayExpression: ["elements"],
|
||||
ObjectExpression: ["properties"],
|
||||
@@ -11,6 +13,6 @@ const visitorKeys = {
|
||||
Identifier: [],
|
||||
TemplateLiteral: ["quasis"],
|
||||
TemplateElement: [],
|
||||
};
|
||||
});
|
||||
|
||||
export default visitorKeys;
|
||||
@@ -1,5 +1,5 @@
|
||||
import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.evaluate.js";
|
||||
|
||||
const getVisitorKeys = createGetVisitorKeys(visitorKeys);
|
||||
|
||||
|
||||
+4
-2
@@ -1,4 +1,6 @@
|
||||
const visitorKeys = {
|
||||
import { generateReferenceSharedVisitorKeys } from "../utilities/visitor-keys.js";
|
||||
|
||||
const visitorKeys = generateReferenceSharedVisitorKeys({
|
||||
root: ["children"],
|
||||
paragraph: ["children"],
|
||||
sentence: ["children"],
|
||||
@@ -36,6 +38,6 @@ const visitorKeys = {
|
||||
tableRow: ["children"],
|
||||
listItem: ["children"],
|
||||
text: [],
|
||||
};
|
||||
});
|
||||
|
||||
export default visitorKeys;
|
||||
@@ -1,5 +1,5 @@
|
||||
import createGetVisitorKeys from "../utilities/create-get-visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.js";
|
||||
import visitorKeys from "./visitor-keys.evaluate.js";
|
||||
|
||||
const getVisitorKeys = createGetVisitorKeys(visitorKeys);
|
||||
|
||||
|
||||
@@ -1,4 +1,21 @@
|
||||
const visitorKeys = Object.fromEntries(
|
||||
import { generateReferenceSharedVisitorKeys } from "../utilities/visitor-keys.js";
|
||||
|
||||
/**
|
||||
@import {VisitorKeys} from "../utilities/visitor-keys.js";
|
||||
*/
|
||||
|
||||
const commentsKeys = [
|
||||
"indicatorComment",
|
||||
"leadingComments",
|
||||
"middleComments",
|
||||
"trailingComment",
|
||||
"endComments",
|
||||
];
|
||||
|
||||
const tagAndAnchor = ["anchor", "tag"];
|
||||
|
||||
/** @type {VisitorKeys} */
|
||||
let visitorKeys = Object.fromEntries(
|
||||
Object.entries({
|
||||
root: ["children"],
|
||||
document: ["head", "body", "children"],
|
||||
@@ -28,15 +45,12 @@ const visitorKeys = Object.fromEntries(
|
||||
type,
|
||||
[
|
||||
...keys,
|
||||
"anchor",
|
||||
"tag",
|
||||
"indicatorComment",
|
||||
"leadingComments",
|
||||
"middleComments",
|
||||
"trailingComment",
|
||||
"endComments",
|
||||
...(type !== "tag" && type !== "anchor" ? tagAndAnchor : []),
|
||||
...commentsKeys,
|
||||
],
|
||||
]),
|
||||
);
|
||||
|
||||
visitorKeys = generateReferenceSharedVisitorKeys(visitorKeys);
|
||||
|
||||
export default visitorKeys;
|
||||
@@ -1,4 +1,6 @@
|
||||
/** @typedef {Record<string, readonly string[]>} VisitorKeys */
|
||||
/**
|
||||
@typedef {readonly string[]} Keys
|
||||
@typedef {Record<string, Keys>} VisitorKeys */
|
||||
|
||||
const unique = (values) => [...new Set(values)];
|
||||
|
||||
@@ -64,4 +66,30 @@ function removeNodeTypes(visitorKeys, nodeTypesToRemove) {
|
||||
return result;
|
||||
}
|
||||
|
||||
export { removeNodeTypes, removeVisitorKeys, unionVisitorKeys };
|
||||
/**
|
||||
@param {VisitorKeys} visitorKeys
|
||||
@returns {VisitorKeys}
|
||||
*/
|
||||
function generateReferenceSharedVisitorKeys(visitorKeys) {
|
||||
/** @type {Map<string, Keys>} */
|
||||
const cache = new Map();
|
||||
/** @type {VisitorKeys} */
|
||||
const result = {};
|
||||
|
||||
for (const [type, keys] of Object.entries(visitorKeys)) {
|
||||
const cacheKey = keys.toSorted().join("\0");
|
||||
if (!cache.has(cacheKey)) {
|
||||
cache.set(cacheKey, keys);
|
||||
}
|
||||
result[type] = cache.get(cacheKey);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export {
|
||||
generateReferenceSharedVisitorKeys,
|
||||
removeNodeTypes,
|
||||
removeVisitorKeys,
|
||||
unionVisitorKeys,
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { inspect } from "node:util";
|
||||
import createEsmUtils from "esm-utils";
|
||||
import fastGlob from "fast-glob";
|
||||
import coreOptions from "../../../src/main/core-options.evaluate.js";
|
||||
@@ -70,6 +71,111 @@ describe("standalone", () => {
|
||||
expect(esmOutput).toBe(umdOutput);
|
||||
});
|
||||
}
|
||||
|
||||
const printerVisitorKeysSettings = new Map([
|
||||
[
|
||||
"estree",
|
||||
{
|
||||
sharedVisitorKeys: true,
|
||||
nodes: [
|
||||
{ type: "FunctionDeclaration" },
|
||||
{ type: "FunctionExpression" },
|
||||
],
|
||||
},
|
||||
],
|
||||
[
|
||||
"estree-json",
|
||||
{
|
||||
sharedVisitorKeys: true,
|
||||
nodes: [{ type: "StringLiteral" }, { type: "Identifier" }],
|
||||
},
|
||||
],
|
||||
[
|
||||
"glimmer",
|
||||
{
|
||||
sharedVisitorKeys: false,
|
||||
nodes: [{ type: "Template" }, { type: "Block" }],
|
||||
},
|
||||
],
|
||||
[
|
||||
"graphql",
|
||||
{
|
||||
sharedVisitorKeys: false,
|
||||
nodes: [{ kind: "StringValue" }, { kind: "BooleanValue" }],
|
||||
},
|
||||
],
|
||||
[
|
||||
"html",
|
||||
{
|
||||
sharedVisitorKeys: true,
|
||||
nodes: [{ kind: "comment" }, { kind: "cdata" }],
|
||||
},
|
||||
],
|
||||
[
|
||||
"mdast",
|
||||
{
|
||||
sharedVisitorKeys: true,
|
||||
nodes: [{ type: "code" }, { type: "image" }],
|
||||
},
|
||||
],
|
||||
[
|
||||
"postcss",
|
||||
{
|
||||
sharedVisitorKeys: true,
|
||||
nodes: [{ type: "media-query-list" }, { type: "selector-pseudo" }],
|
||||
},
|
||||
],
|
||||
[
|
||||
"yaml",
|
||||
{
|
||||
sharedVisitorKeys: true,
|
||||
nodes: [{ type: "blockLiteral" }, { type: "quoteSingle" }],
|
||||
},
|
||||
],
|
||||
]);
|
||||
|
||||
test("visitor keys with shared reference", () => {
|
||||
for (const [name, printer] of esmPlugins.flatMap((plugin) =>
|
||||
Object.entries(plugin.printers ?? {}),
|
||||
)) {
|
||||
try {
|
||||
expect(printerVisitorKeysSettings.has(name)).toBe(true);
|
||||
} catch {
|
||||
throw new Error(`Missing settings for printer '${name}'.`);
|
||||
}
|
||||
const { getVisitorKeys } = printer;
|
||||
expect(typeof getVisitorKeys).toBe("function");
|
||||
const { sharedVisitorKeys, nodes } = printerVisitorKeysSettings.get(name);
|
||||
expect(
|
||||
typeof sharedVisitorKeys === "boolean" &&
|
||||
Array.isArray(nodes) &&
|
||||
nodes.length > 1,
|
||||
).toBe(true);
|
||||
const keys = nodes.map((node) => getVisitorKeys(node));
|
||||
|
||||
try {
|
||||
expect(keys.every((keys) => Array.isArray(keys))).toBe(true);
|
||||
} catch {
|
||||
throw new Error(
|
||||
`Missing visitor keys for '${name}' nodes: ${inspect(nodes)}.`,
|
||||
);
|
||||
}
|
||||
const [firstNodeKeys, ...restNodeKeys] = keys;
|
||||
if (sharedVisitorKeys) {
|
||||
// Should be same reference
|
||||
expect(restNodeKeys.every((keys) => keys === firstNodeKeys)).toBe(true);
|
||||
} else {
|
||||
// Should be same, but not same reference
|
||||
expect(
|
||||
restNodeKeys.every(
|
||||
(keys) =>
|
||||
keys !== firstNodeKeys &&
|
||||
JSON.stringify(keys) === JSON.stringify(firstNodeKeys),
|
||||
),
|
||||
).toBe(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test("global objects", async () => {
|
||||
|
||||
@@ -1668,12 +1668,10 @@ exports[`visitor keys yaml 1`] = `
|
||||
"trailingComment",
|
||||
],
|
||||
"anchor": [
|
||||
"anchor",
|
||||
"endComments",
|
||||
"indicatorComment",
|
||||
"leadingComments",
|
||||
"middleComments",
|
||||
"tag",
|
||||
"trailingComment",
|
||||
],
|
||||
"blockFolded": [
|
||||
@@ -1892,12 +1890,10 @@ exports[`visitor keys yaml 1`] = `
|
||||
"trailingComment",
|
||||
],
|
||||
"tag": [
|
||||
"anchor",
|
||||
"endComments",
|
||||
"indicatorComment",
|
||||
"leadingComments",
|
||||
"middleComments",
|
||||
"tag",
|
||||
"trailingComment",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import postcssVisitorKeys from "../../src/language-css/visitor-keys.js";
|
||||
import postcssVisitorKeys from "../../src/language-css/visitor-keys.evaluate.js";
|
||||
import graphqlVisitorKeys from "../../src/language-graphql/visitor-keys.js";
|
||||
import glimmerVisitorKeys from "../../src/language-handlebars/visitor-keys.evaluate.js";
|
||||
import htmlVisitorKeys from "../../src/language-html/visitor-keys.js";
|
||||
import glimmerVisitorKeys from "../../src/language-handlebars/visitor-keys.js";
|
||||
import htmlVisitorKeys from "../../src/language-html/visitor-keys.evaluate.js";
|
||||
import estreeVisitorKeys from "../../src/language-js/traverse/visitor-keys.evaluate.js";
|
||||
import jsonVisitorKeys from "../../src/language-json/visitor-keys.js";
|
||||
import remarkVisitorKeys from "../../src/language-markdown/visitor-keys.js";
|
||||
import yamlVisitorKeys from "../../src/language-yaml/visitor-keys.js";
|
||||
import jsonVisitorKeys from "../../src/language-json/visitor-keys.evaluate.js";
|
||||
import remarkVisitorKeys from "../../src/language-markdown/visitor-keys.evaluate.js";
|
||||
import yamlVisitorKeys from "../../src/language-yaml/visitor-keys.evaluate.js";
|
||||
|
||||
// Keep eye on package change
|
||||
describe("visitor keys", () => {
|
||||
|
||||
Reference in new issue
Block a user