Skip to content

Commit

Permalink
feat: add macro
Browse files Browse the repository at this point in the history
  • Loading branch information
aralroca committed Mar 21, 2024
1 parent 84cae11 commit a064acc
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 6 deletions.
79 changes: 73 additions & 6 deletions package/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { BunPlugin } from "bun";
import ts from "typescript";

type PrerenderPluginParams = {
renderToStringPath: string;
Expand All @@ -7,7 +8,6 @@ type PrerenderPluginParams = {
injectStringIntoJSXModuleName: string;
};

const filter = new RegExp(`*.(ts|js)x$`);
const defaultConfig: PrerenderPluginParams = {
renderToStringPath: "brisa/server",
renderToStringModuleName: "renderToString",
Expand All @@ -24,10 +24,17 @@ export default function prerenderPlugin({
return {
name: "prerender-plugin",
setup(build) {
build.onLoad({ filter }, async ({ path, loader }) => ({
contents: prerenderPluginTransformation(await Bun.file(path).text()),
loader,
}));
build.onLoad(
{ filter: /.*/, namespace: "prerender" },
async ({ path, loader }) => {
return {
contents: prerenderPluginTransformation(
await Bun.file(path).text(),
),
loader,
};
},
);
},
} satisfies BunPlugin;
}
Expand All @@ -43,5 +50,65 @@ export default function prerenderPlugin({
*
*/
export function prerenderPluginTransformation(code: string) {
return code;
const result = ts.transpileModule(code, {
compilerOptions: {
module: ts.ModuleKind.ESNext,
target: ts.ScriptTarget.ESNext,
jsx: ts.JsxEmit.React,
},
});

const ast = ts.createSourceFile(
"file.tsx",
result.outputText,
ts.ScriptTarget.ESNext,
true,
ts.ScriptKind.TSX,
);
const imports = ast.statements.filter(ts.isImportDeclaration);
const importsWithPrerender = imports.filter(
(node) =>
node.attributes?.elements?.some(
(element: any) =>
element.name.escapedText === "type" &&
element.value.text === "prerender",
),
);

if (!importsWithPrerender.length) return code;

const importPrerenderMacro = ts.factory.createImportDeclaration(
undefined,
ts.factory.createImportClause(
false,
undefined,
ts.factory.createNamedImports([
ts.factory.createImportSpecifier(
false,
ts.factory.createIdentifier("prerender"),
ts.factory.createIdentifier("__prerender__macro"),
),
]),
),
ts.factory.createStringLiteral("prerender-macro/prerender"),
ts.factory.createImportAttributes(
ts.factory.createNodeArray([
ts.factory.createImportAttribute(
ts.factory.createStringLiteral("type"),
ts.factory.createStringLiteral("macro"),
),
]),
),
);

return ts
.createPrinter()
.printNode(
ts.EmitHint.Unspecified,
ts.factory.updateSourceFile(ast, [
importPrerenderMacro,
...ast.statements,
]),
ast,
);
}
61 changes: 61 additions & 0 deletions tests/brisa/plugin.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { describe, it, expect } from "bun:test";
import { prerenderPluginTransformation } from "prerender-macro";

export const toInline = (s: string) => s.replace(/\s*\n\s*/g, "");
export const normalizeQuotes = (s: string) => toInline(s).replaceAll("'", '"');

describe("Brisa", () => {
describe("plugin", () => {
it('should not transform if there is not an import attribute with type "prerender"', () => {
const input = `
import StaticComponent from "@/components/static";
import DynamicComponent from "@/components/dynamic";
export default function Test() {
return (
<div>
<StaticComponent />
<DynamicComponent />
</div>
);
}
`;
const output = normalizeQuotes(prerenderPluginTransformation(input));
const expected = normalizeQuotes(input);

expect(output).toBe(expected);
});
it("should transform a static component without props", () => {
const input = `
import StaticComponent from "@/components/static" with { type: "prerender" };
import DynamicComponent from "@/components/dynamic";
export default function Test() {
return (
<div>
<StaticComponent />
<DynamicComponent />
</div>
);
}
`;
const output = normalizeQuotes(prerenderPluginTransformation(input));
const expected = normalizeQuotes(`
import { prerender as __prerender_macro } from "prerender-macro/prerender" with { "type": "macro" };
import StaticComponent from "@/components/static" with { type: "prerender" };
import DynamicComponent from "@/components/dynamic";
export default function Test() {
return (
<div>
{__prerender_macro({ componentPath: "@/components/static", componentModuleName: "default", componentProps: {} })}
<DynamicComponent />
</div>
);
}
`);

expect(output).toBe(expected);

Check failure on line 58 in tests/brisa/plugin.test.tsx

View workflow job for this annotation

GitHub Actions / build

error: expect(received).toBe(expected)

Expected: "import { prerender as __prerender_macro } from \"prerender-macro/prerender\" with { \"type\": \"macro\" };import StaticComponent from \"@/components/static\" with { type: \"prerender\" };import DynamicComponent from \"@/components/dynamic\";export default function Test() {return (<div>{__prerender_macro({ componentPath: \"@/components/static\", componentModuleName: \"default\", componentProps: {} })}<DynamicComponent /></div>);}" Received: "import { prerender as __prerender__macro } from \"prerender-macro/prerender\" with { \"type\": \"macro\" };import StaticComponent from \"@/components/static\" with { type: \"prerender\" };import DynamicComponent from \"@/components/dynamic\";export default function Test() {return (React.createElement(\"div\", null, React.createElement(StaticComponent, null), React.createElement(DynamicComponent, null)));}" at /home/runner/work/prerender-macro/prerender-macro/tests/brisa/plugin.test.tsx:58:7
});
});
});

0 comments on commit a064acc

Please sign in to comment.