iles
Version:
Vite & Vue powered static site generator with partial hydration
128 lines (125 loc) • 4.17 kB
JavaScript
import {
resolveComponent,
resolveImportPath
} from "./chunk-CZOBZ67L.js";
import {
debug,
isString
} from "./chunk-ROUSHGC2.js";
import {
importModule
} from "./chunk-HKPEBJDI.js";
// src/node/plugin/remarkWrapIslands.ts
var remarkWrapIslands_default = ({ config }) => async (ast, file) => {
let components = config.namedPlugins.components.api;
let imports;
let componentPromises = [];
let componentCounter = 0;
const unistUtilVisit = await importModule("unist-util-visit");
const visit = unistUtilVisit.visit || unistUtilVisit;
const SKIP = unistUtilVisit.SKIP;
visit(ast, (node) => {
const strategy = isJsxElement(node) && node.attributes.find(isClientDirective)?.name;
if (strategy) {
wrapWithIsland(strategy, node, resolveComponentImport);
return SKIP;
}
});
const componentsToImport = await Promise.all(componentPromises);
if (componentsToImport.length > 0)
ast.children.unshift(defineImports(componentsToImport));
async function resolveComponentImport(strategy, tagName) {
debug.detect(`<${tagName} ${strategy}>`);
if (!imports) imports = extractImports(ast.children.filter((node) => node.type === "mdxjsEsm"));
if (imports[tagName]) return await resolveImportPath(config, imports[tagName], file.path);
const info = resolveComponent(components, tagName, file.path, componentCounter++);
if (strategy !== "client:only") componentPromises.push(info);
return await info;
}
};
function isJsxElement(node) {
return node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement";
}
function isClientDirective(attr) {
return "name" in attr && attr.name.startsWith("client:");
}
function isImport(statement) {
return statement.type === "ImportDeclaration";
}
async function wrapWithIsland(strategy, node, resolveComponentImport) {
const tagName = node.name;
if (!tagName) return;
node.name = "Island";
const importMeta = await resolveComponentImport(strategy, tagName);
node.attributes.unshift(...jsxAttributes({
component: jsxExpression(strategy === "client:only" ? { type: "Literal", value: null, raw: "null" } : { type: "Identifier", name: importMeta.as }),
componentName: tagName,
importName: importMeta.name,
importFrom: importMeta.from
}));
}
function extractImports(nodes) {
const imports = /* @__PURE__ */ Object.create(null);
const declarations = nodes.flatMap((node) => node.data?.estree?.body?.filter(isImport));
declarations.forEach(({ specifiers, source: { value: from } }) => {
if (isString(from)) {
specifiers.forEach((specifier) => {
const as = specifier.local.name;
imports[as] = { as, name: importedName(specifier), from };
});
}
});
return imports;
}
function importedName(specifier) {
switch (specifier.type) {
case "ImportDefaultSpecifier":
return "default";
case "ImportNamespaceSpecifier":
return "*";
default:
if ("name" in specifier.imported) return specifier.imported.name;
throw new Error(`Unpexected literal in import declaration: ${specifier.imported}`);
}
}
function jsxExpression(expression) {
return {
type: "mdxJsxAttributeValueExpression",
value: expression.name || expression.raw,
data: {
estree: {
type: "Program",
sourceType: "module",
body: [{ type: "ExpressionStatement", expression }]
}
}
};
}
function jsxAttributes(val) {
return Object.entries(val).map(([name, value]) => ({ type: "mdxJsxAttribute", name, value }));
}
function defineImports(components) {
return {
type: "mdxjsEsm",
data: {
estree: {
type: "Program",
sourceType: "module",
body: components.map((component) => ({
type: "ImportDeclaration",
specifiers: [
{
type: "ImportSpecifier",
imported: { type: "Identifier", name: component.name },
local: { type: "Identifier", name: component.as }
}
],
source: { type: "Literal", value: component.from, raw: `'${component.from}'` }
}))
}
}
};
}
export {
remarkWrapIslands_default
};