better formatting
This commit is contained in:
parent
ffbe14e164
commit
49561f7c95
2 changed files with 109 additions and 31 deletions
116
index.ts
116
index.ts
|
|
@ -7,23 +7,51 @@ import { experimental_AstroContainer as AstroContainer } from "astro/container";
|
|||
import ts from "typescript";
|
||||
import type { Integration, IntegrationContext } from "muddle/contracts";
|
||||
|
||||
export interface AstroIntegrationOptions { dir?: string; partials?: boolean }
|
||||
export interface AstroIntegrationOptions {
|
||||
dir?: string;
|
||||
partials?: boolean;
|
||||
}
|
||||
|
||||
function routeFor(root: string, file: string): string {
|
||||
const path = relative(root, file).replace(/\\/g, "/").replace(/\.astro$/, "");
|
||||
const path = relative(root, file)
|
||||
.replace(/\\/g, "/")
|
||||
.replace(/\.astro$/, "");
|
||||
return `/${path.replace(/index$/, "").replace(/\/+/g, "/") || ""}` || "/";
|
||||
}
|
||||
|
||||
export function astroIntegration(options: AstroIntegrationOptions = {}): Integration {
|
||||
export function astroIntegration(
|
||||
options: AstroIntegrationOptions = {},
|
||||
): Integration {
|
||||
return {
|
||||
name: "astro",
|
||||
async setup(context: IntegrationContext) {
|
||||
context.loader("astro", { name: "astro", extensions: [".astro"], async load(file) { return { data: {}, content: await renderAstro(file.source, undefined, file.path) }; } });
|
||||
context.loader("astro", {
|
||||
name: "astro",
|
||||
extensions: [".astro"],
|
||||
async load(file) {
|
||||
return {
|
||||
data: {},
|
||||
content: await renderAstro(file.source, undefined, file.path),
|
||||
};
|
||||
},
|
||||
});
|
||||
const dir = resolve(context.config.root, options.dir ?? "src/pages");
|
||||
if (!existsSync(dir)) return;
|
||||
for (const file of await astroFiles(dir)) {
|
||||
const route = routeFor(dir, file);
|
||||
context.route("GET", route, async (request) => new Response(await renderAstro(await readFile(file, "utf8"), request, file), { headers: { "content-type": "text/html; charset=utf-8" } }));
|
||||
context.route(
|
||||
"GET",
|
||||
route,
|
||||
async (request) =>
|
||||
new Response(
|
||||
await renderAstro(await readFile(file, "utf8"), request, file),
|
||||
{
|
||||
headers: {
|
||||
"content-type": "text/html; charset=utf-8",
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -39,42 +67,92 @@ async function astroFiles(dir: string): Promise<string[]> {
|
|||
return files;
|
||||
}
|
||||
|
||||
export async function renderAstro(source: string, request = new Request("http://localhost/"), filename = resolve("inline.astro")): Promise<string> {
|
||||
export async function renderAstro(
|
||||
source: string,
|
||||
request = new Request("http://localhost/"),
|
||||
filename = resolve("inline.astro"),
|
||||
): Promise<string> {
|
||||
const component = await compileAstro(source, filename);
|
||||
const container = await AstroContainer.create();
|
||||
return container.renderToString(component, { request });
|
||||
}
|
||||
|
||||
async function compileAstro(source: string, filename: string, ancestors: string[] = []): Promise<any> {
|
||||
if (ancestors.includes(filename)) throw new Error(`Circular Astro import ${filename}`);
|
||||
const result = await transform(source, { filename, internalURL: "astro/runtime/server/index.js" });
|
||||
let code = result.code.replace(/,\s*createMetadata as \$\$createMetadata/, "").replace(/export const \$\$metadata = [^\n]+\n/g, "");
|
||||
async function compileAstro(
|
||||
source: string,
|
||||
filename: string,
|
||||
ancestors: string[] = [],
|
||||
): Promise<any> {
|
||||
if (ancestors.includes(filename)) {
|
||||
throw new Error(`Circular Astro import ${filename}`);
|
||||
}
|
||||
const result = await transform(source, {
|
||||
filename,
|
||||
internalURL: "astro/runtime/server/index.js",
|
||||
});
|
||||
let code = result.code
|
||||
.replace(/,\s*createMetadata as \$\$createMetadata/, "")
|
||||
.replace(/export const \$\$metadata = [^\n]+\n/g, "");
|
||||
for (const match of [...code.matchAll(/from\s+["']([^"']+)["']/g)]) {
|
||||
const specifier = match[1]!;
|
||||
let url: string;
|
||||
if (specifier.endsWith(".astro")) {
|
||||
const file = resolve(filename, "..", specifier);
|
||||
const child = await compileAstro(await readFile(file, "utf8"), file, [...ancestors, filename]);
|
||||
const child = await compileAstro(await readFile(file, "utf8"), file, [
|
||||
...ancestors,
|
||||
filename,
|
||||
]);
|
||||
const key = `__muddle_astro_${crypto.randomUUID().replaceAll("-", "")}`;
|
||||
(globalThis as Record<string, unknown>)[key] = child;
|
||||
url = `data:text/javascript;base64,${Buffer.from(`export default globalThis[${JSON.stringify(key)}]`).toString("base64")}`;
|
||||
} else url = specifier.startsWith(".") ? pathToFileURL(resolve(filename, "..", specifier)).href : import.meta.resolve(specifier);
|
||||
code = code.replaceAll(`"${specifier}"`, JSON.stringify(url)).replaceAll(`'${specifier}'`, JSON.stringify(url));
|
||||
url = `data:text/javascript;base64,${Buffer.from(
|
||||
`export default globalThis[${JSON.stringify(key)}]`,
|
||||
).toString("base64")}`;
|
||||
} else {
|
||||
url = specifier.startsWith(".")
|
||||
? pathToFileURL(resolve(filename, "..", specifier)).href
|
||||
: import.meta.resolve(specifier);
|
||||
}
|
||||
code = code
|
||||
.replaceAll(`"${specifier}"`, JSON.stringify(url))
|
||||
.replaceAll(`'${specifier}'`, JSON.stringify(url));
|
||||
}
|
||||
code = ts.transpileModule(code, { compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 } }).outputText;
|
||||
return (await import(`data:text/javascript;base64,${Buffer.from(code).toString("base64")}`)).default;
|
||||
code = ts.transpileModule(code, {
|
||||
compilerOptions: {
|
||||
module: ts.ModuleKind.ESNext,
|
||||
target: ts.ScriptTarget.ES2022,
|
||||
},
|
||||
}).outputText;
|
||||
return (
|
||||
await import(
|
||||
`data:text/javascript;base64,${Buffer.from(code).toString("base64")}`
|
||||
)
|
||||
).default;
|
||||
}
|
||||
|
||||
/** Import into TemplateContext with `.import("Card", astroComponent("./Card.astro"))`. */
|
||||
export function astroComponent(file: string) {
|
||||
return {
|
||||
async render(props: Record<string, unknown>, context: { params: Record<string, string>; locals: Record<string, unknown> }) {
|
||||
async render(
|
||||
props: Record<string, unknown>,
|
||||
context: {
|
||||
params: Record<string, string>;
|
||||
locals: Record<string, unknown>;
|
||||
},
|
||||
) {
|
||||
const path = resolve(file);
|
||||
const component = await compileAstro(await readFile(path, "utf8"), path);
|
||||
const container = await AstroContainer.create();
|
||||
const slots: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(props)) if (typeof value === "function") slots[key === "content" ? "default" : key] = await value();
|
||||
return container.renderToString(component, { props, params: context.params, locals: context.locals, slots });
|
||||
for (const [key, value] of Object.entries(props)) {
|
||||
if (typeof value === "function") {
|
||||
slots[key === "content" ? "default" : key] = await value();
|
||||
}
|
||||
}
|
||||
return container.renderToString(component, {
|
||||
props,
|
||||
params: context.params,
|
||||
locals: context.locals,
|
||||
slots,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
24
package.json
24
package.json
|
|
@ -1,14 +1,14 @@
|
|||
{
|
||||
"name": "@muddle/astro",
|
||||
"type": "module",
|
||||
"exports": "./index.ts",
|
||||
"version": "0.1.0",
|
||||
"peerDependencies": {
|
||||
"muddle": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "2",
|
||||
"astro": "5",
|
||||
"typescript": "5.9.3"
|
||||
}
|
||||
"name": "@muddle/astro",
|
||||
"type": "module",
|
||||
"exports": "./index.ts",
|
||||
"version": "0.1.0",
|
||||
"peerDependencies": {
|
||||
"muddle": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/compiler": "2",
|
||||
"astro": "5",
|
||||
"typescript": "5.9.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue