vite-plugin-react-server
Version:
Vite plugin for React Server Components (RSC)
127 lines (124 loc) • 3.77 kB
JavaScript
/**
* vite-plugin-react-server
* Copyright (c) Nico Brinkkemper
* MIT License
*/
import { DEFAULT_CONFIG } from '../config/defaults.js';
import { basename } from 'path';
const REACT_DIRECTIVES = /* @__PURE__ */ new Set(["use client", "use server"]);
function createSourceMap(id, code, mappings) {
return {
version: 3,
file: basename(id),
sources: [id],
sourcesContent: [code],
names: [],
mappings,
sourceRoot: ""
};
}
function removeRanges(code, ranges) {
ranges.sort((a, b) => b.start - a.start);
let result = code;
for (const range of ranges) {
result = result.slice(0, range.start) + result.slice(range.end);
}
return result;
}
function countLines(str) {
let count = 1;
for (let i = 0; i < str.length; i++) {
if (str[i] === "\n") count++;
}
return count;
}
function reactPreservePlugin(_options) {
const meta = {};
return {
name: "vite-plugin-react-server:preserve-directives",
enforce: "post",
transform: {
order: "post",
// Ensure this runs last in transform phase
handler(code, id) {
if (id.includes("node_modules") || id.includes("vite/dist") || !id.match(DEFAULT_CONFIG.FILE_REGEX)) {
return null;
}
let ast;
try {
ast = this.parse(code, {
allowReturnOutsideFunction: true,
jsx: true
});
} catch (e) {
console.warn(`[PreservePlugin] Failed to parse ${id}`, e);
return null;
}
if (ast.type !== "Program") {
return null;
}
const rangesToRemove = [];
let hasChanged = false;
let mappings = "AAAA";
for (const node of ast.body) {
if (node.type !== "ExpressionStatement") {
break;
}
let directive = null;
if ("directive" in node) {
directive = node.directive;
} else if (node.expression.type === "Literal" && typeof node.expression.value === "string" && REACT_DIRECTIVES.has(node.expression.value)) {
directive = node.expression.value;
}
if (directive && "start" in node && "end" in node) {
meta[id] ||= /* @__PURE__ */ new Set();
meta[id].add(directive);
rangesToRemove.push({
start: node.start,
end: node.end
});
hasChanged = true;
const removedLines = code.slice(node.start, node.end).split("\n").length - 1;
for (let i = 0; i < removedLines; i++) {
mappings += ";AACA";
}
}
}
if (!hasChanged) {
return null;
}
const newCode = removeRanges(code, rangesToRemove);
const sourceMap = createSourceMap(id, code, mappings);
return {
code: newCode,
map: sourceMap,
meta: {
directives: Array.from(meta[id] || [])
}
};
}
},
renderChunk(code, chunk) {
const chunkDirectives = /* @__PURE__ */ new Set();
for (const id of chunk.moduleIds) {
if (meta[id]) {
meta[id].forEach((d) => chunkDirectives.add(d));
}
}
if (chunkDirectives.size) {
const directivesCode = Array.from(chunkDirectives).map((d) => `"${d}";`).join("\n") + "\n";
const newCode = directivesCode + code;
const lineCount = countLines(directivesCode);
const mappings = "AAAA" + ";AACA".repeat(lineCount - 1);
const sourceMap = createSourceMap(chunk.fileName, code, mappings);
return {
code: newCode,
map: sourceMap
};
}
return null;
}
};
}
export { reactPreservePlugin };
//# sourceMappingURL=plugin.js.map