From c66124853017148366aa665429dafd37b2f6ab75 Mon Sep 17 00:00:00 2001 From: horvbalint Date: Sat, 15 Feb 2025 17:26:29 +0100 Subject: [PATCH 1/3] poc --- src/rollup/plugins/handlers-meta.ts | 82 +++++++++++++---------------- 1 file changed, 38 insertions(+), 44 deletions(-) diff --git a/src/rollup/plugins/handlers-meta.ts b/src/rollup/plugins/handlers-meta.ts index f2f3ddb8c1..b752172c67 100644 --- a/src/rollup/plugins/handlers-meta.ts +++ b/src/rollup/plugins/handlers-meta.ts @@ -1,9 +1,9 @@ import { readFile } from "node:fs/promises"; import { transform } from "esbuild"; -import type { Expression, Literal } from "estree"; -import type { Nitro, NitroEventHandler } from "nitropack/types"; +import type { Nitro } from "nitropack/types"; import { extname } from "pathe"; import type { Plugin } from "rollup"; +import MagicString from "magic-string"; const virtualPrefix = "\0nitro-handler-meta:"; @@ -28,9 +28,8 @@ export function handlersMeta(nitro: Nitro) { importer, resolveOpts ); - if (!resolved) { - return; - } + if (!resolved) return; + return virtualPrefix + resolved.id; } }, @@ -41,19 +40,27 @@ export function handlersMeta(nitro: Nitro) { } }, async transform(code, id) { - if (!id.startsWith(virtualPrefix)) { - return; - } - - let meta: NitroEventHandler["meta"] | null = null; + if (!id.startsWith(virtualPrefix)) return; try { const ext = extname(id) as keyof typeof esbuildLoaders; - const jsCode = await transform(code, { + const { code: jsCode } = await transform(code, { loader: esbuildLoaders[ext], - }).then((r) => r.code); + }); const ast = this.parse(jsCode); + const s = new MagicString(jsCode); + for (const node of ast.body) { + // if its a relative import, we remove it, since it won't be in place + if ( + node.type === "ImportDeclaration" && + typeof node.source.value === "string" && + node.source.value?.startsWith(".") + ) { + s.remove(node.start!, node.end!); + } + + // if its the macro call, we remove the code after it and replace it with the export if ( node.type === "ExpressionStatement" && node.expression.type === "CallExpression" && @@ -61,42 +68,29 @@ export function handlersMeta(nitro: Nitro) { node.expression.callee.name === "defineRouteMeta" && node.expression.arguments.length === 1 ) { - meta = astToObject(node.expression.arguments[0] as any); - break; + s.remove(node.end!, jsCode.length); + + const arg = jsCode.slice( + node.expression.arguments[0].start!, + node.expression.arguments[0].end! + ); + s.overwrite(node.start!, node.end!, `export default ${arg}`); + + return { + code: s.toString(), + map: s.generateMap(), + }; } } + + return { + code: "export default null", + map: null, + }; } catch (error) { - console.warn( - `[nitro] [handlers-meta] Cannot extra route meta for: ${id}: ${error}` - ); + console.error(error); + return { code, map: null }; } - - return { - code: `export default ${JSON.stringify(meta)};`, - map: null, - }; }, } satisfies Plugin; } - -function astToObject(node: Expression | Literal): any { - switch (node.type) { - case "ObjectExpression": { - const obj: Record = {}; - for (const prop of node.properties) { - if (prop.type === "Property") { - const key = (prop.key as any).name ?? (prop.key as any).value; - obj[key] = astToObject(prop.value as any); - } - } - return obj; - } - case "ArrayExpression": { - return node.elements.map((el) => astToObject(el as any)).filter(Boolean); - } - case "Literal": { - return node.value; - } - // No default - } -} From deb5b6f3bb4a47c14d5c000dc47973a29b9bc306 Mon Sep 17 00:00:00 2001 From: horvbalint Date: Sat, 15 Feb 2025 22:56:17 +0100 Subject: [PATCH 2/3] support relative imports --- src/rollup/plugins/handlers-meta.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/rollup/plugins/handlers-meta.ts b/src/rollup/plugins/handlers-meta.ts index b752172c67..b61d426556 100644 --- a/src/rollup/plugins/handlers-meta.ts +++ b/src/rollup/plugins/handlers-meta.ts @@ -1,7 +1,7 @@ import { readFile } from "node:fs/promises"; import { transform } from "esbuild"; import type { Nitro } from "nitropack/types"; -import { extname } from "pathe"; +import { extname, resolve, dirname } from "pathe"; import type { Plugin } from "rollup"; import MagicString from "magic-string"; @@ -43,6 +43,8 @@ export function handlersMeta(nitro: Nitro) { if (!id.startsWith(virtualPrefix)) return; try { + const dirPath = dirname(id.slice(virtualPrefix.length)); + const ext = extname(id) as keyof typeof esbuildLoaders; const { code: jsCode } = await transform(code, { loader: esbuildLoaders[ext], @@ -57,7 +59,12 @@ export function handlersMeta(nitro: Nitro) { typeof node.source.value === "string" && node.source.value?.startsWith(".") ) { - s.remove(node.start!, node.end!); + const absolutePath = resolve(dirPath, node.source.value); + s.overwrite( + node.source.start!, + node.source.end!, + `"${absolutePath}"` + ); } // if its the macro call, we remove the code after it and replace it with the export From f0be518d1ef5c43f523a05121b162d2c00a9c91f Mon Sep 17 00:00:00 2001 From: horvbalint Date: Sun, 16 Feb 2025 09:39:23 +0100 Subject: [PATCH 3/3] type fixes --- package.json | 1 + pnpm-lock.yaml | 3 +++ src/rollup/plugins/handlers-meta.ts | 20 ++++++++++++-------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 417509f05a..f1626fe895 100644 --- a/package.json +++ b/package.json @@ -107,6 +107,7 @@ "@rollup/plugin-terser": "^0.4.4", "@types/http-proxy": "^1.17.15", "@vercel/nft": "^0.29.1", + "acorn": "^8.14.0", "archiver": "^7.0.1", "c12": "^2.0.2", "chokidar": "^3.6.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0674ee0c2..0539fd580c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: '@vercel/nft': specifier: ^0.29.1 version: 0.29.1(rollup@4.34.6) + acorn: + specifier: ^8.14.0 + version: 8.14.0 archiver: specifier: ^7.0.1 version: 7.0.1 diff --git a/src/rollup/plugins/handlers-meta.ts b/src/rollup/plugins/handlers-meta.ts index b61d426556..31378122d5 100644 --- a/src/rollup/plugins/handlers-meta.ts +++ b/src/rollup/plugins/handlers-meta.ts @@ -4,6 +4,7 @@ import type { Nitro } from "nitropack/types"; import { extname, resolve, dirname } from "pathe"; import type { Plugin } from "rollup"; import MagicString from "magic-string"; +import { parse } from "acorn"; const virtualPrefix = "\0nitro-handler-meta:"; @@ -49,11 +50,14 @@ export function handlersMeta(nitro: Nitro) { const { code: jsCode } = await transform(code, { loader: esbuildLoaders[ext], }); - const ast = this.parse(jsCode); + const ast = parse(jsCode, { + sourceType: "module", + ecmaVersion: "latest", + }); // TODO: what are the desired options? const s = new MagicString(jsCode); for (const node of ast.body) { - // if its a relative import, we remove it, since it won't be in place + // if its a relative import, we update it to the absolute path if ( node.type === "ImportDeclaration" && typeof node.source.value === "string" && @@ -61,8 +65,8 @@ export function handlersMeta(nitro: Nitro) { ) { const absolutePath = resolve(dirPath, node.source.value); s.overwrite( - node.source.start!, - node.source.end!, + node.source.start, + node.source.end, `"${absolutePath}"` ); } @@ -75,13 +79,13 @@ export function handlersMeta(nitro: Nitro) { node.expression.callee.name === "defineRouteMeta" && node.expression.arguments.length === 1 ) { - s.remove(node.end!, jsCode.length); + s.remove(node.end, jsCode.length); const arg = jsCode.slice( - node.expression.arguments[0].start!, - node.expression.arguments[0].end! + node.expression.arguments[0].start, + node.expression.arguments[0].end ); - s.overwrite(node.start!, node.end!, `export default ${arg}`); + s.overwrite(node.start, node.end, `export default ${arg}`); return { code: s.toString(),