Skip to content

Commit

Permalink
Merge pull request #105 from Rezact/add-config-and-builder-plugins
Browse files Browse the repository at this point in the history
Add config and builder plugins
  • Loading branch information
zachlankton committed Jan 26, 2024
2 parents 75d58d3 + c30eead commit bd8d706
Show file tree
Hide file tree
Showing 7 changed files with 275 additions and 10 deletions.
3 changes: 3 additions & 0 deletions copy-static-build-files.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ echo "Copying Static Build Files"
cp package.json dist/
cp README.md dist/
cp src/lib/rezact/rezact.d.ts dist/
cp src/lib/rezact/vite-build-plugin.js dist/
cp src/lib/rezact/config.js dist/
cp src/lib/rezact/config.d.ts dist/
echo "Finished"
51 changes: 44 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@rezact/rezact",
"version": "1.0.15-beta.52",
"version": "1.0.15-beta.53",
"type": "module",
"description": "Intuitive Reactivity, Simplified State. Embrace a modern UI framework that encourages direct data mutations, offers module-level reactivity, and simplifies component design. With Rezact, you get the power of reactivity without the boilerplate. Dive into a seamless development experience tailored for today's web.",
"main": "index.cjs",
Expand Down Expand Up @@ -31,11 +31,11 @@
"devDependencies": {
"@testing-library/dom": "^9.3.4",
"@testing-library/user-event": "^14.5.2",
"@types/node": "^20.11.7",
"@vitest/coverage-v8": "1.2.1",
"acorn": "8.11.3",
"acorn-jsx": "5.3.2",
"acorn-walk": "8.3.2",
"happy-dom": "npm:zachlankton-happy-dom@13.3.2",
"magic-string": "0.30.5",
"path": "0.12.7",
"typescript": "5.3.3",
Expand All @@ -46,7 +46,9 @@
"vitest": "1.2.1"
},
"dependencies": {
"@happy-dom/global-registrator": "^13.3.1",
"@mdx-js/rollup": "3.0.0",
"happy-dom": "npm:zachlankton-happy-dom@13.3.2",
"rehype-highlight": "7.0.0",
"remark-frontmatter": "5.0.0",
"remark-mdx-frontmatter": "4.0.0"
Expand Down
10 changes: 10 additions & 0 deletions src/lib/rezact/config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { UserConfig } from "vite";

interface defOptions {
routes?: any[];
options?: {
useMdx?: boolean;
};
}

export declare const configRezact: (item: defOptions) => UserConfig;
60 changes: 60 additions & 0 deletions src/lib/rezact/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { rezact } from "./vite-plugin.js";
import { rezact_mdx } from "./vite-mdx-plugin.js";
import remarkFrontmatter from "remark-frontmatter";
import remarkMdxFrontmatter from "remark-mdx-frontmatter";
import rehypeHighlight from "rehype-highlight";
import mdx from "@mdx-js/rollup";
import { addRoutesToInput, rezactBuild } from "./vite-build-plugin.js";
import { defineConfig } from "vite";

const config = defineConfig({
resolve: {
alias: {
src: "/src",
rezact: "@rezact/rezact",
},
},
build: {
target: "esnext",
modulePreload: {
polyfill: false,
},
rollupOptions: {},
},
esbuild: {
jsxFactory: "xCreateElement",
jsxFragment: "xFragment",
},
plugins: [],
});

export function configRezact({ routes, options }) {
const useMdx = options?.useMdx ?? true;
if (!config.plugins) config.plugins = [];

if (useMdx)
config.plugins.push(
mdx({
pragma: "r.xCreateElement",
pragmaFrag: "r.xFragment",
jsxRuntime: "classic",
pragmaImportSource: "@rezact/rezact/mdx",
remarkPlugins: [
remarkFrontmatter,
[remarkMdxFrontmatter, { name: "fm" }],
],
rehypePlugins: [rehypeHighlight],
})
);

config.plugins.push(rezact());

if (useMdx) config.plugins.push(rezact_mdx());

if (routes) {
addRoutesToInput(routes, config);
config.plugins.push(rezactBuild({ routes }));
}

return config;
}
152 changes: 152 additions & 0 deletions src/lib/rezact/vite-build-plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { dirname, resolve } from "path";
import * as fs from "fs";
import { GlobalRegistrator } from "@happy-dom/global-registrator";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename).replace(
"/node_modules/@rezact/rezact",
""
);

export function writeToFileSync(filePath, content) {
fs.mkdirSync(dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, content);
}

export async function rezactRendered() {
return new Promise((resolve) => {
const renderedFunc = (e) => {
document.removeEventListener("rezact-rendered", renderedFunc);
resolve(e);
};

document.addEventListener("rezact-rendered", renderedFunc);
});
}

export function addRoutesToInput(routes, config) {
const isBuild = process.env.npm_lifecycle_event === "build";

let rollupInput = { main: resolve(__dirname, "index.html") };
if (isBuild) {
const indexFilePath = resolve(__dirname, "index.html");
const _idxFileText = fs.readFileSync(indexFilePath, "utf-8");

const mappedRoutes = routes.map((route) => {
const htmlFilePath = __dirname + route.path + ".html";
if (route.path !== "/" && !route.path.includes(":")) {
let idxFileText = _idxFileText;
writeToFileSync(htmlFilePath, idxFileText);
rollupInput[route.path.replace(/\//g, "")] = resolve(htmlFilePath);
}
});
}

config.build.rollupOptions.input = { ...rollupInput };
}

function deleteFileAndEmptyDirs(filePath) {
// Delete the file
fs.unlinkSync(filePath);

// Recursively remove parent directories if empty
let dirPath = dirname(filePath);
while (dirPath !== resolve(dirPath, "..")) {
if (fs.readdirSync(dirPath).length === 0) {
fs.rmdirSync(dirPath);
dirPath = resolve(dirPath, "..");
} else {
break;
}
}
}

async function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

// function findAllNestedImports(filePath) {}

export function rezactBuild({ routes }) {
return {
name: "rezact-build",

async writeBundle(options, bundle) {
GlobalRegistrator.register({
url: `http://localhost:3000/`,
});
const { happyDOM } = window;
window.scrollTo = () => {};
document.body.innerHTML = `<div id="app"></div>`;

let mainEntryJS = "";
const bundleMapped = {};
Object.keys(bundle).forEach((key) => {
const path = bundle[key].facadeModuleId?.replace(/\.(tsx|jsx)$/, "");
if (path) bundleMapped[path] = bundle[key];
});

let mainModule = null;

// convert this map into a for loop
for (const route of routes) {
console.log("rendering", route.path);
const path = route.component.toString().split(/["'`]/)[1];
const resolvedPath = resolve(__dirname, path);
const htmlFilePath = __dirname + route.path + ".html";
if (route.path !== "/" && !route.path.includes(":")) {
deleteFileAndEmptyDirs(htmlFilePath);
}

const routeChunk = bundleMapped[resolvedPath];
const routePathMapName = (route.path.slice(1) || "index") + ".html";
let preloads = "";
const dedupePreload = {};
const preload = `<link rel="modulepreload" href="/${routeChunk.fileName}" />\n`;
preloads += preload;
routeChunk.imports.forEach((importPath) => {
if (importPath.startsWith("assets/main-"))
return (mainEntryJS = importPath);
if (dedupePreload[importPath]) return;
dedupePreload[importPath] = true;
const preload = `<link rel="modulepreload" href="/${importPath}" />\n`;
preloads += preload;
});

const _modifiedSource = bundle[routePathMapName].source.replace(
"<!-- PRELOAD HERE -->",
preloads
);

if (mainModule === null) {
const fullMainPath = __dirname + "/dist/" + mainEntryJS;
mainModule = await import(fullMainPath);
await rezactRendered();
} else {
const anchor = document.createElement("a");
anchor.href = route.path;
document.body.appendChild(anchor);
anchor.click();
await rezactRendered();
await delay(100);
}

const app = document.getElementById("app");
const modifiedSource = _modifiedSource.replace(
"<!-- PRE RENDER HERE -->",
app.innerHTML
);

const outRoutePath = route.path === "/" ? "/index" : route.path;
const outputHtmlFilePath = __dirname + "/dist" + outRoutePath + ".html";

writeToFileSync(outputHtmlFilePath, modifiedSource);
}
happyDOM.abort();
GlobalRegistrator.unregister();
// fs.writeFileSync("bundle.json", JSON.stringify(bundle, null, 2));
return;
},
};
}

0 comments on commit bd8d706

Please sign in to comment.