UNPKG

one

Version:

One is a new React Framework that makes Vite serve both native and web.

154 lines (153 loc) 5.38 kB
import { extname, relative } from "node:path"; import BabelGenerate from "@babel/generator"; import { parse } from "@babel/parser"; import BabelTraverse from "@babel/traverse"; import { deadCodeElimination, findReferencedIdentifiers } from "babel-dead-code-elimination"; import { EMPTY_LOADER_STRING, makeLoaderRouteIdStub } from "../constants.mjs"; const traverse = BabelTraverse["default"] || BabelTraverse; const generate = BabelGenerate["default"] || BabelGenerate; function collectTypeImports(ast) { const typeImports = []; traverse(ast, { ImportDeclaration(path) { if (path.node.importKind === "type") { typeImports.push(path.node); } } }); return typeImports; } function restoreTypeImports(ast, typeImports) { if (typeImports.length === 0) return; const existingSources = /* @__PURE__ */new Set(); traverse(ast, { ImportDeclaration(path) { if (path.node.importKind === "type") { existingSources.add(path.node.source.value); } } }); for (const typeImport of typeImports) { if (!existingSources.has(typeImport.source.value)) { ast.program.body.unshift(typeImport); } } } const clientTreeShakePlugin = opts => { const runtime = opts?.runtime ?? "vite"; return { name: "one-client-tree-shake", enforce: "pre", ...(runtime === "vite" && { applyToEnvironment(env) { return env.name === "client" || env.name === "ios" || env.name === "android"; } }), transform: { order: "pre", async handler(code, id, settings) { if (runtime === "vite" && this.environment?.name === "ssr") { return; } if (!/\.(js|jsx|ts|tsx)/.test(extname(id))) { return; } if (/node_modules/.test(id)) { return; } const out = await transformTreeShakeClient(code, id, process.cwd()); return out; } } }; }; async function transformTreeShakeClient(code, id, root) { if (!/generateStaticParams|loader/.test(code)) { return; } let ast; try { ast = parse(code, { sourceType: "module", plugins: ["typescript", "jsx"] }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.warn(`[one] Skipping tree shaking for ${id} due to syntax error:`, errorMessage); return; } let referenced; try { referenced = findReferencedIdentifiers(ast); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.warn(`[one] Skipping tree shaking for ${id} due to identifier analysis error:`, errorMessage); return; } const removed = { loader: false, generateStaticParams: false }; try { traverse(ast, { ExportNamedDeclaration(path) { if (path.node.declaration && path.node.declaration.type === "FunctionDeclaration") { if (!path.node.declaration.id) return; const functionName = path.node.declaration.id.name; if (functionName === "loader" || functionName === "generateStaticParams") { path.remove(); removed[functionName] = true; } } else if (path.node.declaration && path.node.declaration.type === "VariableDeclaration") { path.node.declaration.declarations.forEach((declarator, index) => { if (declarator.id.type === "Identifier" && (declarator.id.name === "loader" || declarator.id.name === "generateStaticParams")) { const declaration = path.get("declaration.declarations." + index); if (!Array.isArray(declaration) && declaration) { ; declaration.remove(); removed[declarator.id.name] = true; } } }); } } }); } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.warn(`[one] Skipping tree shaking for ${id} due to traversal error:`, errorMessage); return; } const removedFunctions = Object.keys(removed).filter(key => removed[key]); if (removedFunctions.length) { try { const typeImports = collectTypeImports(ast); deadCodeElimination(ast, referenced); restoreTypeImports(ast, typeImports); const out = generate(ast, { retainLines: true }); const codeOut = out.code + "\n\n" + removedFunctions.map(key => { if (key === "loader") { if (root) { const fromRoot = relative(root, id).replace(/\\/g, "/"); const routeId = "./" + fromRoot.replace(/^app\//, ""); return makeLoaderRouteIdStub(routeId); } return EMPTY_LOADER_STRING; } return `export function generateStaticParams() {};`; }).join("\n"); console.info(` \u{1F9F9} [one] ${relative(process.cwd(), id)} removed ${removedFunctions.length} server-only exports`); return { code: codeOut, map: out.map }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.warn(`[one] Skipping tree shaking for ${id} due to code generation error:`, errorMessage); return; } } } export { clientTreeShakePlugin, transformTreeShakeClient }; //# sourceMappingURL=clientTreeShakePlugin.mjs.map