Urara-Blog/node_modules/.pnpm-store/v3/files/fc/6f72d42be8215caafd837026d8a8f5cd576f31b064e9efda409d5f10a4662f39c75db541e025baecab5cd166724dc88efe1d5497aae564de0b8905963cbb0f
2022-08-14 01:14:53 +08:00

192 lines
6.5 KiB
Text

import * as t from "../../lib/index.js";
import * as definitions from "../../lib/definitions/index.js";
import formatBuilderName from "../utils/formatBuilderName.js";
import lowerFirst from "../utils/lowerFirst.js";
import stringifyValidator from "../utils/stringifyValidator.js";
function areAllRemainingFieldsNullable(fieldName, fieldNames, fields) {
const index = fieldNames.indexOf(fieldName);
return fieldNames.slice(index).every(_ => isNullable(fields[_]));
}
function hasDefault(field) {
return field.default != null;
}
function isNullable(field) {
return field.optional || hasDefault(field);
}
function sortFieldNames(fields, type) {
return fields.sort((fieldA, fieldB) => {
const indexA = t.BUILDER_KEYS[type].indexOf(fieldA);
const indexB = t.BUILDER_KEYS[type].indexOf(fieldB);
if (indexA === indexB) return fieldA < fieldB ? -1 : 1;
if (indexA === -1) return 1;
if (indexB === -1) return -1;
return indexA - indexB;
});
}
function generateBuilderArgs(type) {
const fields = t.NODE_FIELDS[type];
const fieldNames = sortFieldNames(Object.keys(t.NODE_FIELDS[type]), type);
const builderNames = t.BUILDER_KEYS[type];
const args = [];
fieldNames.forEach(fieldName => {
const field = fields[fieldName];
// Future / annoying TODO:
// MemberExpression.property, ObjectProperty.key and ObjectMethod.key need special cases; either:
// - convert the declaration to chain() like ClassProperty.key and ClassMethod.key,
// - declare an alias type for valid keys, detect the case and reuse it here,
// - declare a disjoint union with, for example, ObjectPropertyBase,
// ObjectPropertyLiteralKey and ObjectPropertyComputedKey, and declare ObjectProperty
// as "ObjectPropertyBase & (ObjectPropertyLiteralKey | ObjectPropertyComputedKey)"
let typeAnnotation = stringifyValidator(field.validate, "t.");
if (isNullable(field) && !hasDefault(field)) {
typeAnnotation += " | null";
}
if (builderNames.includes(fieldName)) {
const field = definitions.NODE_FIELDS[type][fieldName];
const def = JSON.stringify(field.default);
const bindingIdentifierName = t.toBindingIdentifierName(fieldName);
let arg;
if (areAllRemainingFieldsNullable(fieldName, builderNames, fields)) {
arg = `${bindingIdentifierName}${
isNullable(field) && !def ? "?:" : ":"
} ${typeAnnotation}`;
} else {
arg = `${bindingIdentifierName}: ${typeAnnotation}${
isNullable(field) ? " | undefined" : ""
}`;
}
if (def !== "null" || isNullable(field)) {
arg += `= ${def}`;
}
args.push(arg);
}
});
return args;
}
export default function generateBuilders(kind) {
return kind === "uppercase.js"
? generateUppercaseBuilders()
: generateLowercaseBuilders();
}
function generateLowercaseBuilders() {
let output = `/*
* This file is auto-generated! Do not modify it directly.
* To re-generate run 'make build'
*/
import validateNode from "../validateNode";
import type * as t from "../..";
`;
const reservedNames = new Set(["super", "import"]);
Object.keys(definitions.BUILDER_KEYS).forEach(type => {
const defArgs = generateBuilderArgs(type);
const formatedBuilderName = formatBuilderName(type);
const formatedBuilderNameLocal = reservedNames.has(formatedBuilderName)
? `_${formatedBuilderName}`
: formatedBuilderName;
const fieldNames = sortFieldNames(
Object.keys(definitions.NODE_FIELDS[type]),
type
);
const builderNames = definitions.BUILDER_KEYS[type];
const objectFields = [["type", JSON.stringify(type)]];
fieldNames.forEach(fieldName => {
const field = definitions.NODE_FIELDS[type][fieldName];
if (builderNames.includes(fieldName)) {
const bindingIdentifierName = t.toBindingIdentifierName(fieldName);
objectFields.push([fieldName, bindingIdentifierName]);
} else if (!field.optional) {
const def = JSON.stringify(field.default);
objectFields.push([fieldName, def]);
}
});
output += `${
formatedBuilderNameLocal === formatedBuilderName ? "export " : ""
}function ${formatedBuilderNameLocal}(${defArgs.join(", ")}): t.${type} {`;
const nodeObjectExpression = `{\n${objectFields
.map(([k, v]) => (k === v ? ` ${k},` : ` ${k}: ${v},`))
.join("\n")}\n }`;
if (builderNames.length > 0) {
output += `\n return validateNode<t.${type}>(${nodeObjectExpression});`;
} else {
output += `\n return ${nodeObjectExpression};`;
}
output += `\n}\n`;
if (formatedBuilderNameLocal !== formatedBuilderName) {
output += `export { ${formatedBuilderNameLocal} as ${formatedBuilderName} };\n`;
}
// This is needed for backwards compatibility.
// It should be removed in the next major version.
// JSXIdentifier -> jSXIdentifier
if (/^[A-Z]{2}/.test(type)) {
output += `export { ${formatedBuilderNameLocal} as ${lowerFirst(
type
)} }\n`;
}
});
Object.keys(definitions.DEPRECATED_KEYS).forEach(type => {
const newType = definitions.DEPRECATED_KEYS[type];
const formatedBuilderName = formatBuilderName(type);
const formatedNewBuilderName = formatBuilderName(newType);
output += `/** @deprecated */
function ${type}(${generateBuilderArgs(newType).join(", ")}) {
console.trace("The node type ${type} has been renamed to ${newType}");
return ${formatedNewBuilderName}(${t.BUILDER_KEYS[newType].join(", ")});
}
export { ${type} as ${formatedBuilderName} };\n`;
// This is needed for backwards compatibility.
// It should be removed in the next major version.
// JSXIdentifier -> jSXIdentifier
if (/^[A-Z]{2}/.test(type)) {
output += `export { ${type} as ${lowerFirst(type)} }\n`;
}
});
return output;
}
function generateUppercaseBuilders() {
let output = `/*
* This file is auto-generated! Do not modify it directly.
* To re-generate run 'make build'
*/
/**
* This file is written in JavaScript and not TypeScript because uppercase builders
* conflict with AST types. TypeScript reads the uppercase.d.ts file instead.
*/
export {\n`;
Object.keys(definitions.BUILDER_KEYS).forEach(type => {
const formatedBuilderName = formatBuilderName(type);
output += ` ${formatedBuilderName} as ${type},\n`;
});
Object.keys(definitions.DEPRECATED_KEYS).forEach(type => {
const formatedBuilderName = formatBuilderName(type);
output += ` ${formatedBuilderName} as ${type},\n`;
});
output += ` } from './index';\n`;
return output;
}