flowbite-react
Version:
Official React components built for Flowbite and Tailwind CSS
383 lines (380 loc) • 13 kB
JavaScript
import MagicString from 'magic-string';
import { parseSync, Visitor } from 'oxc-parser';
const builders = {
stringLiteral: (value) => JSON.stringify(value),
identifier: (name) => name,
callExpression: (callee, args) => args.length > 0 ? `${callee}(${args.join(", ")})` : `${callee}()`
};
function addToConfig({
content,
targetPath,
valueGenerator
}) {
const parseResult = parseSync("config.ts", content);
if (parseResult.errors.length > 0) {
return content;
}
const valueToAdd = valueGenerator(builders);
const pathParts = targetPath.split(".");
const configObjects = /* @__PURE__ */ new Map();
let targetObjectStart = null;
let targetObjectEnd = null;
const collectVisitor = new Visitor({
VariableDeclarator(node) {
if (node.id?.type !== "Identifier" || !node.init) return;
const varName = node.id.name;
if (node.init.type === "ObjectExpression") {
configObjects.set(varName, { start: node.init.start, end: node.init.end });
} else if (node.init.type === "CallExpression" && node.init.arguments?.length > 0 && node.init.arguments[0]?.type === "ObjectExpression") {
configObjects.set(varName, {
start: node.init.arguments[0].start,
end: node.init.arguments[0].end
});
}
}
});
collectVisitor.visit(parseResult.program);
const findExportVisitor = new Visitor({
ExportDefaultDeclaration(node) {
const decl = node.declaration;
if (!decl) return;
if (decl.type === "ObjectExpression") {
targetObjectStart = decl.start;
targetObjectEnd = decl.end;
} else if (decl.type === "Identifier") {
const obj = configObjects.get(decl.name);
if (obj) {
targetObjectStart = obj.start;
targetObjectEnd = obj.end;
}
} else if ((decl.type === "TSAsExpression" || decl.type === "TSSatisfiesExpression") && decl.expression?.type === "ObjectExpression") {
targetObjectStart = decl.expression.start;
targetObjectEnd = decl.expression.end;
} else if (decl.type === "CallExpression" && decl.arguments?.length > 0) {
const arg = decl.arguments[0];
if (arg?.type === "ObjectExpression") {
targetObjectStart = arg.start;
targetObjectEnd = arg.end;
} else if (arg?.type === "Identifier") {
const obj = configObjects.get(arg.name);
if (obj) {
targetObjectStart = obj.start;
targetObjectEnd = obj.end;
}
}
}
},
AssignmentExpression(node) {
const left = node.left;
if (left?.type === "MemberExpression" && left.object?.type === "Identifier" && left.object.name === "module" && left.property?.type === "Identifier" && left.property.name === "exports") {
if (node.right?.type === "ObjectExpression") {
targetObjectStart = node.right.start;
targetObjectEnd = node.right.end;
} else if (node.right?.type === "Identifier") {
const obj = configObjects.get(node.right.name);
if (obj) {
targetObjectStart = obj.start;
targetObjectEnd = obj.end;
}
} else if (node.right?.type === "CallExpression" && node.right.arguments?.length > 0 && node.right.arguments[0]?.type === "ObjectExpression") {
targetObjectStart = node.right.arguments[0].start;
targetObjectEnd = node.right.arguments[0].end;
}
}
}
});
findExportVisitor.visit(parseResult.program);
if (targetObjectStart === null || targetObjectEnd === null) {
return content;
}
const result = findTargetArrayOrCreationPoint(content, targetObjectStart, targetObjectEnd, pathParts);
const s = new MagicString(content);
if (result.type === "found") {
const targetArrayLocation = result.array;
const exists = checkValueExists(targetArrayLocation, valueToAdd);
if (exists) {
return content;
}
const arrayStyle = detectArrayStyle(content, targetArrayLocation);
if (targetArrayLocation.elements.length > 0) {
const lastElement = targetArrayLocation.elements[targetArrayLocation.elements.length - 1];
if (arrayStyle.isMultiLine) {
const afterLastElement = content.slice(lastElement.end, targetArrayLocation.end);
const hasTrailingComma = /^[\s]*,/.test(afterLastElement);
if (hasTrailingComma) {
const commaOffset = afterLastElement.indexOf(",") + 1;
s.appendLeft(lastElement.end + commaOffset, `
${arrayStyle.elementIndent}${valueToAdd}`);
} else {
s.appendLeft(lastElement.end, `,
${arrayStyle.elementIndent}${valueToAdd}`);
}
} else {
s.appendLeft(lastElement.end, `, ${valueToAdd}`);
}
} else {
if (arrayStyle.isMultiLine) {
s.appendLeft(targetArrayLocation.start + 1, `
${arrayStyle.elementIndent}${valueToAdd}
${arrayStyle.indent}`);
} else {
s.appendLeft(targetArrayLocation.start + 1, valueToAdd);
}
}
} else if (result.type === "create") {
const { insertPosition, propertyPath, needsComma, indent } = result.creation;
const indentUnit = detectIndentUnit(content);
let newContent = "";
const lastIndex = propertyPath.length - 1;
for (let i = 0; i < propertyPath.length; i++) {
const key = propertyPath[i];
const currentIndent = indent + indentUnit.repeat(i);
if (i === lastIndex) {
newContent += `${currentIndent}${key}: [${valueToAdd}]`;
} else {
newContent += `${currentIndent}${key}: {
`;
}
}
for (let i = propertyPath.length - 2; i >= 0; i--) {
const currentIndent = indent + indentUnit.repeat(i);
newContent += `
${currentIndent}}`;
}
const prefix = needsComma ? ",\n" : "\n";
s.appendLeft(insertPosition, prefix + newContent);
} else {
return content;
}
return s.toString();
}
function findTargetArrayOrCreationPoint(content, objectStart, objectEnd, pathParts) {
const objectContent = content.slice(objectStart, objectEnd);
const wrappedContent = `(${objectContent})`;
const parseResult = parseSync("temp.ts", wrappedContent);
if (parseResult.errors.length > 0) {
return { type: "none" };
}
const baseOffset = objectStart - 1;
function extractObjectInfo(node) {
const obj = node;
const props = [];
for (const prop of obj.properties || []) {
const p = prop;
if (p.type !== "ObjectProperty" && p.type !== "Property") continue;
let propKey;
if (p.key.type === "Identifier") {
propKey = p.key.name;
} else if (p.key.type === "StringLiteral" || p.key.type === "Literal") {
propKey = String(p.key.value);
}
if (propKey) {
props.push({
key: propKey,
start: baseOffset + p.start,
end: baseOffset + p.end,
valueType: p.value.type,
value: p.value
});
}
}
return {
start: baseOffset + obj.start,
end: baseOffset + obj.end,
properties: props
};
}
function processPath(objInfo, remainingPath, depth) {
if (remainingPath.length === 0) {
return { type: "none" };
}
const key = remainingPath[0];
const prop = objInfo.properties.find((p) => p.key === key);
if (!prop) {
const lastProp = objInfo.properties[objInfo.properties.length - 1];
const insertPosition = lastProp ? lastProp.end : objInfo.start + 1;
const needsComma = objInfo.properties.length > 0;
const beforeObj = content.slice(Math.max(0, objInfo.start - 50), objInfo.start);
const indentMatch = beforeObj.match(/\n([ \t]*)$/);
const indentUnit = detectIndentUnit(content);
const baseIndent = indentMatch ? indentMatch[1] : indentUnit;
const indent = baseIndent + indentUnit.repeat(depth);
return {
type: "create",
creation: {
insertPosition,
propertyPath: remainingPath,
needsComma,
indent
}
};
}
if (remainingPath.length === 1) {
const value2 = prop.value;
if (value2.type === "ArrayExpression") {
return {
type: "found",
array: extractArrayLocation(value2, baseOffset)
};
} else if (value2.type === "CallExpression") {
const arr = extractArrayFromCallExpression(value2, baseOffset);
if (arr) {
return { type: "found", array: arr };
}
}
return { type: "none" };
}
const value = prop.value;
if (value.type !== "ObjectExpression") {
return { type: "none" };
}
const nestedObjInfo = extractObjectInfo(prop.value);
return processPath(nestedObjInfo, remainingPath.slice(1), depth + 1);
}
let result = { type: "none" };
const visitor = new Visitor({
ObjectExpression(node) {
if (result.type === "none") {
const objInfo = extractObjectInfo(node);
result = processPath(objInfo, pathParts, 0);
}
}
});
visitor.visit(parseResult.program);
return result;
}
function extractArrayLocation(node, offset) {
const arr = node;
const elements = [];
for (const el of arr.elements || []) {
if (!el) continue;
const e = el;
const elemInfo = {
start: offset + e.start,
end: offset + e.end,
type: e.type
};
if (e.type === "StringLiteral" || e.type === "Literal") {
elemInfo.value = String(e.value);
}
if (e.type === "Identifier") {
elemInfo.name = e.name;
}
if (e.type === "CallExpression" && e.callee?.type === "Identifier") {
elemInfo.calleeName = e.callee.name;
}
elements.push(elemInfo);
}
return {
start: offset + arr.start,
end: offset + arr.end,
elements
};
}
function extractArrayFromCallExpression(node, offset) {
const call = node;
if (call.callee?.type === "MemberExpression") {
const obj = call.callee.object;
if (obj?.type === "ArrayExpression") {
return extractArrayLocation(obj, offset);
}
}
return null;
}
function detectIndentUnit(content) {
const lines = content.split("\n");
const indentCounts = /* @__PURE__ */ new Map();
for (const line of lines) {
const match = line.match(/^(\s+)\S/);
if (match) {
const indent = match[1];
if (indent === " ") {
indentCounts.set(" ", (indentCounts.get(" ") || 0) + 1);
} else if (/^ +$/.test(indent)) {
const len = indent.length;
if (len <= 4) {
indentCounts.set(indent, (indentCounts.get(indent) || 0) + 1);
}
}
}
}
let bestIndent = " ";
let bestCount = 0;
for (const [indent, count] of indentCounts) {
if (count > bestCount) {
bestCount = count;
bestIndent = indent;
}
}
if (bestIndent === " ") {
return " ";
}
const spaceCounts = Array.from(indentCounts.entries()).filter(([k]) => /^ +$/.test(k)).map(([k, v]) => ({ len: k.length, count: v }));
if (spaceCounts.length > 0) {
const lengths = spaceCounts.map((s) => s.len);
const gcd = lengths.reduce((a, b) => {
while (b) {
[a, b] = [b, a % b];
}
return a;
});
if (gcd === 2 || gcd === 4) {
return " ".repeat(gcd);
}
}
return bestIndent;
}
function detectArrayStyle(content, arrayLocation) {
const arrayContent = content.slice(arrayLocation.start, arrayLocation.end);
const indentUnit = detectIndentUnit(content);
const hasNewlines = arrayContent.includes("\n");
if (!hasNewlines || arrayLocation.elements.length === 0) {
return {
isMultiLine: false,
indent: "",
elementIndent: ""
};
}
const firstElement = arrayLocation.elements[0];
const contentBeforeFirst = content.slice(arrayLocation.start, firstElement.start);
const indentMatch = contentBeforeFirst.match(/\n([ \t]*)$/);
const elementIndent = indentMatch ? indentMatch[1] : indentUnit;
let arrayIndent = "";
if (elementIndent.startsWith(" ")) {
arrayIndent = elementIndent.slice(0, -1);
} else {
const unitLen = indentUnit.length;
if (elementIndent.length >= unitLen) {
arrayIndent = elementIndent.slice(0, -unitLen);
}
}
return {
isMultiLine: true,
indent: arrayIndent,
elementIndent
};
}
function checkValueExists(arrayLocation, valueToAdd) {
return arrayLocation.elements.some((el) => {
if ((el.type === "StringLiteral" || el.type === "Literal") && el.value !== void 0) {
try {
const parsed = JSON.parse(valueToAdd);
return el.value === parsed;
} catch {
return false;
}
}
if (el.type === "Identifier" && el.name) {
return el.name === valueToAdd;
}
if (el.type === "CallExpression" && el.calleeName) {
const callMatch = valueToAdd.match(/^(\w+)\(/);
if (callMatch) {
return el.calleeName === callMatch[1];
}
}
return false;
});
}
export { addToConfig, builders };
//# sourceMappingURL=add-to-config.js.map