diff --git a/.changeset/eleven-birds-return.md b/.changeset/eleven-birds-return.md new file mode 100644 index 00000000..ec7e8357 --- /dev/null +++ b/.changeset/eleven-birds-return.md @@ -0,0 +1,8 @@ +--- +"@vinxi/server-components": patch +"@vinxi/server-functions": patch +"@vinxi/plugin-directives": patch +"vinxi": patch +--- + +added a stacks feature to quickly build up app config from the CLI (experimental) diff --git a/examples/react/rsc/spa/CHANGELOG.md b/examples/react/rsc/spa/CHANGELOG.md index b3fa89e3..82ad0027 100644 --- a/examples/react/rsc/spa/CHANGELOG.md +++ b/examples/react/rsc/spa/CHANGELOG.md @@ -6,8 +6,8 @@ - Updated dependencies [978b04a] - vinxi@0.0.43 - - @vinxi/plugin-server-components@0.0.35 - - @vinxi/plugin-server-functions@0.0.35 + - @vinxi/server-components@0.0.35 + - @vinxi/server-functions@0.0.35 ## null @@ -16,26 +16,26 @@ - Updated dependencies [e8fdad4] - Updated dependencies [291a5cf] - vinxi@0.0.42 - - @vinxi/plugin-server-components@0.0.34 - - @vinxi/plugin-server-functions@0.0.34 + - @vinxi/server-components@0.0.34 + - @vinxi/server-functions@0.0.34 ## null ### Patch Changes - Updated dependencies [2e52d87] - - @vinxi/plugin-server-functions@0.0.33 + - @vinxi/server-functions@0.0.33 - vinxi@0.0.41 - - @vinxi/plugin-server-components@0.0.33 + - @vinxi/server-components@0.0.33 ## null ### Patch Changes - Updated dependencies [0335776] - - @vinxi/plugin-server-components@0.0.32 + - @vinxi/server-components@0.0.32 - vinxi@0.0.40 - - @vinxi/plugin-server-functions@0.0.32 + - @vinxi/server-functions@0.0.32 ## null @@ -44,8 +44,8 @@ - Updated dependencies [f237894] - Updated dependencies [d620072] - vinxi@0.0.39 - - @vinxi/plugin-server-components@0.0.31 - - @vinxi/plugin-server-functions@0.0.31 + - @vinxi/server-components@0.0.31 + - @vinxi/server-functions@0.0.31 ## null @@ -53,9 +53,9 @@ - Updated dependencies [1284791] - Updated dependencies [930b2f2] - - @vinxi/plugin-server-functions@0.0.30 + - @vinxi/server-functions@0.0.30 - vinxi@0.0.38 - - @vinxi/plugin-server-components@0.0.30 + - @vinxi/server-components@0.0.30 ## null diff --git a/examples/react/rsc/spa/app.config.js b/examples/react/rsc/spa/app.config.js index 4403f60e..34ceb924 100644 --- a/examples/react/rsc/spa/app.config.js +++ b/examples/react/rsc/spa/app.config.js @@ -1,5 +1,5 @@ -import { serverComponents } from "@vinxi/plugin-server-components"; -import { serverFunctions } from "@vinxi/plugin-server-functions"; +import { serverComponents } from "@vinxi/server-components/plugin"; +import { serverFunctions } from "@vinxi/server-functions/plugin"; import reactRefresh from "@vitejs/plugin-react"; import { createApp } from "vinxi"; diff --git a/examples/react/rsc/spa/app/server-action.tsx b/examples/react/rsc/spa/app/server-action.tsx index f10927de..3dab1713 100644 --- a/examples/react/rsc/spa/app/server-action.tsx +++ b/examples/react/rsc/spa/app/server-action.tsx @@ -1,19 +1,19 @@ -import { eventHandler, readRawBody, setHeader } from "vinxi/server"; +import { eventHandler, sendStream } from "vinxi/server"; export default eventHandler(async (event) => { - if (event.method === "POST") { + if (event.node.req.method === "POST") { const { renderToPipeableStream, decodeReply, decodeReplyFromBusboy, decodeAction, } = await import("@vinxi/react-server-dom/server"); - const serverReference = event.headers.get("server-action"); + const serverReference = event.node.req.headers["server-action"]; if (serverReference) { // This is the client-side case const [filepath, name] = serverReference.split("#"); const action = ( - await import.meta.env.MANIFEST["server"].chunks[filepath].import() + await import.meta.env.MANIFEST["rsc"].chunks[filepath].import() )[name]; // Validate that this is actually a function we intended to expose and // not the client trying to invoke arbitrary functions. In a real app, @@ -23,14 +23,16 @@ export default eventHandler(async (event) => { } let args; - // if (req.is('multipart/form-data')) { - // // Use busboy to streamingly parse the reply from form-data. - // const bb = busboy({headers: req.headers}); - // const reply = decodeReplyFromBusboy(bb, moduleBasePath); - // req.pipe(bb); - // args = await reply; - // } else { - const text = await readRawBody(event); + const text = await new Promise((resolve) => { + const requestBody = []; + event.node.req.on("data", (chunks) => { + console.log(chunks); + requestBody.push(chunks); + }); + event.node.req.on("end", () => { + resolve(requestBody.join("")); + }); + }); console.log(text); args = await decodeReply(text); @@ -50,12 +52,11 @@ export default eventHandler(async (event) => { events[event] = listener; }; - setHeader(event, "Content-Type", "application/json"); - setHeader(event, "Router", "server"); + event.node.res.setHeader("Content-Type", "application/json"); + event.node.res.setHeader("Router", "server"); return stream; } catch (x) { - console.error(x); // We handle the error on the client } // Refresh the client and return the value diff --git a/examples/react/rsc/spa/package.json b/examples/react/rsc/spa/package.json index 0ce50333..3061c4ae 100644 --- a/examples/react/rsc/spa/package.json +++ b/examples/react/rsc/spa/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@picocss/pico": "^1.5.10", - "@vinxi/plugin-server-components": "0.0.35", - "@vinxi/plugin-server-functions": "0.0.35", + "@vinxi/server-components": "0.0.35", + "@vinxi/server-functions": "0.0.35", "@vinxi/react": "0.0.11", "@vinxi/react-server-dom": "0.0.3", "@vitejs/plugin-react": "^4.0.4", diff --git a/examples/react/rsc/spa/tmp/count b/examples/react/rsc/spa/tmp/count index 301160a9..2edeafb0 100644 --- a/examples/react/rsc/spa/tmp/count +++ b/examples/react/rsc/spa/tmp/count @@ -1 +1 @@ -8 \ No newline at end of file +20 \ No newline at end of file diff --git a/examples/vanilla/spa/app.config.js b/examples/vanilla/spa/app.config.js index 9d7d1db1..d19ad803 100644 --- a/examples/vanilla/spa/app.config.js +++ b/examples/vanilla/spa/app.config.js @@ -1,4 +1,4 @@ -import { serverFunctions } from "@vinxi/plugin-server-functions"; +import { serverFunctions } from "@vinxi/server-functions/plugin"; import { createApp } from "vinxi"; export default createApp({ @@ -12,7 +12,6 @@ export default createApp({ name: "client", mode: "spa", handler: "./index.html", - target: "browser", plugins: () => [serverFunctions.client()], }, serverFunctions.router({ diff --git a/examples/vanilla/spa/app/client.tsx b/examples/vanilla/spa/app/client.tsx index 4323a8f4..670d7756 100644 --- a/examples/vanilla/spa/app/client.tsx +++ b/examples/vanilla/spa/app/client.tsx @@ -20,12 +20,18 @@ const world4 = async (x, y) => { console.log("hello world 4", x, y); }; +const world5 = async (x) => { + "use server"; + console.log("hello world 4", x); +}; + console.log( await world(), await world2(1), await world3(2, 3), await world4(5, 6), await sayHello(), + await world5({ hello: 1, world: "aasd", x: true }), ); console.log("Hello world!"); diff --git a/examples/vanilla/spa/package.json b/examples/vanilla/spa/package.json index b1409cff..05eb069a 100644 --- a/examples/vanilla/spa/package.json +++ b/examples/vanilla/spa/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "@picocss/pico": "^1.5.10", - "@vinxi/plugin-server-functions": "workspace:^", + "@vinxi/server-functions": "0.0.35", "autoprefixer": "^10.4.15", "tailwindcss": "^3.3.3", "vinxi": "0.0.43" diff --git a/examples/vanilla/stack/.gitignore b/examples/vanilla/stack/.gitignore new file mode 100644 index 00000000..e985853e --- /dev/null +++ b/examples/vanilla/stack/.gitignore @@ -0,0 +1 @@ +.vercel diff --git a/examples/vanilla/stack/CHANGELOG.md b/examples/vanilla/stack/CHANGELOG.md new file mode 100644 index 00000000..493713fd --- /dev/null +++ b/examples/vanilla/stack/CHANGELOG.md @@ -0,0 +1,104 @@ +# example-vanilla + +## null + +### Patch Changes + +- Updated dependencies [355daea] + - @vinxi/plugin-references@0.0.24 + - vinxi@0.0.34 + +## null + +### Patch Changes + +- Updated dependencies [1dec590] + - vinxi@0.0.33 + - @vinxi/plugin-references@0.0.23 + +## null + +### Patch Changes + +- Updated dependencies [982147a] + - vinxi@0.0.32 + - @vinxi/plugin-references@0.0.22 + +## null + +### Patch Changes + +- Updated dependencies [94f59aa] +- Updated dependencies [bc82d8e] +- Updated dependencies [1d8b542] +- Updated dependencies [5bf5e03] +- Updated dependencies [4c7fd35] +- Updated dependencies [0f4b3ee] +- Updated dependencies [ad62318] +- Updated dependencies [c223ab6] +- Updated dependencies [94f59aa] + - @vinxi/plugin-references@0.0.21 + - vinxi@0.0.31 + +## null + +### Patch Changes + +- Updated dependencies [fd06048] +- Updated dependencies [0f14555] +- Updated dependencies [82267c5] + - vinxi@0.0.30 + - @vinxi/plugin-references@0.0.20 + +## null + +### Patch Changes + +- Updated dependencies [8058084] + - vinxi@0.0.29 + - @vinxi/plugin-references@0.0.19 + +## null + +### Patch Changes + +- Updated dependencies [b934e84] +- Updated dependencies [17693dc] +- Updated dependencies [d6305b8] +- Updated dependencies [cb91c48] +- Updated dependencies [085116d] +- Updated dependencies [f1ee5b8] + - vinxi@0.0.28 + - @vinxi/plugin-references@0.0.18 + +## null + +### Patch Changes + +- Updated dependencies [7803042] + - vinxi@0.0.27 + - @vinxi/plugin-references@0.0.17 + +## null + +### Patch Changes + +- Updated dependencies [2b17e0d] + - vinxi@0.0.26 + - @vinxi/plugin-references@0.0.16 + +## null + +### Patch Changes + +- Updated dependencies [552d8ca] + - vinxi@0.0.25 + - @vinxi/plugin-references@0.0.15 + +## null + +### Patch Changes + +- Updated dependencies [47abc3c] + - @vinxi/plugin-references@0.0.14 + - vinxi@0.0.24 diff --git a/examples/vanilla/stack/app/actions.tsx b/examples/vanilla/stack/app/actions.tsx new file mode 100644 index 00000000..a843ead4 --- /dev/null +++ b/examples/vanilla/stack/app/actions.tsx @@ -0,0 +1,12 @@ +"use server"; + +let store = { count: 0 }; +export function sayHello() { + console.log("Hello World"); + store.count++; + return store.count; +} + +export function getStore() { + return store.count; +} diff --git a/examples/vanilla/stack/app/client.tsx b/examples/vanilla/stack/app/client.tsx new file mode 100644 index 00000000..670d7756 --- /dev/null +++ b/examples/vanilla/stack/app/client.tsx @@ -0,0 +1,38 @@ +/// +import "vinxi/client"; + +import { sayHello } from "./actions"; +import { world } from "./server-functions"; +import "./style.css"; + +export async function world2(x) { + "use server"; + console.log("hello world 2", x); +} + +async function world3(x, y) { + "use server"; + console.log("hello world 3", x, y); +} + +const world4 = async (x, y) => { + "use server"; + console.log("hello world 4", x, y); +}; + +const world5 = async (x) => { + "use server"; + console.log("hello world 4", x); +}; + +console.log( + await world(), + await world2(1), + await world3(2, 3), + await world4(5, 6), + await sayHello(), + await world5({ hello: 1, world: "aasd", x: true }), +); +console.log("Hello world!"); + +document.getElementById("app").innerHTML = `Hello World`; diff --git a/examples/vanilla/stack/app/middleware.tsx b/examples/vanilla/stack/app/middleware.tsx new file mode 100644 index 00000000..e59c832b --- /dev/null +++ b/examples/vanilla/stack/app/middleware.tsx @@ -0,0 +1,10 @@ +import { defineMiddleware, getContext, setContext } from "vinxi/server"; + +export default defineMiddleware({ + onRequest: (event) => { + setContext(event, "help", { foo: "bar" }); + }, + onBeforeResponse: (event) => { + console.log(getContext(event, "help")); + }, +}); diff --git a/examples/vanilla/stack/app/server-functions.tsx b/examples/vanilla/stack/app/server-functions.tsx new file mode 100644 index 00000000..7b352f32 --- /dev/null +++ b/examples/vanilla/stack/app/server-functions.tsx @@ -0,0 +1,4 @@ +export async function world() { + "use server"; + console.log("hello"); +} diff --git a/examples/vanilla/stack/app/style.css b/examples/vanilla/stack/app/style.css new file mode 100644 index 00000000..b1284a35 --- /dev/null +++ b/examples/vanilla/stack/app/style.css @@ -0,0 +1,7 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +* { + color: red; +} \ No newline at end of file diff --git a/examples/vanilla/stack/index.html b/examples/vanilla/stack/index.html new file mode 100644 index 00000000..e5ae1ec0 --- /dev/null +++ b/examples/vanilla/stack/index.html @@ -0,0 +1,13 @@ + + + + + + Document + + + +
+ + + diff --git a/examples/vanilla/stack/index.ts b/examples/vanilla/stack/index.ts new file mode 100644 index 00000000..8785b577 --- /dev/null +++ b/examples/vanilla/stack/index.ts @@ -0,0 +1,6 @@ +import { eventHandler } from "vinxi/server"; + +export default eventHandler((event) => { + console.log(event.path); + return "Hello world"; +}); diff --git a/examples/vanilla/stack/package.json b/examples/vanilla/stack/package.json new file mode 100644 index 00000000..bc4d64fb --- /dev/null +++ b/examples/vanilla/stack/package.json @@ -0,0 +1,21 @@ +{ + "name": "example-vanilla-spa", + "type": "module", + "private": true, + "version": null, + "scripts": { + "dev": "vinxi dev", + "build": "vinxi build", + "start": "vinxi start" + }, + "dependencies": { + "@picocss/pico": "^1.5.10", + "@vinxi/server-functions": "workspace:^", + "autoprefixer": "^10.4.15", + "tailwindcss": "^3.3.3", + "vinxi": "0.0.43" + }, + "devDependencies": { + "@types/resolve": "^1.20.4" + } +} diff --git a/examples/vanilla/stack/postcss.config.cjs b/examples/vanilla/stack/postcss.config.cjs new file mode 100644 index 00000000..33ad091d --- /dev/null +++ b/examples/vanilla/stack/postcss.config.cjs @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/examples/vanilla/stack/public/favicon.ico b/examples/vanilla/stack/public/favicon.ico new file mode 100644 index 00000000..129ee136 Binary files /dev/null and b/examples/vanilla/stack/public/favicon.ico differ diff --git a/examples/vanilla/stack/tailwind.config.cjs b/examples/vanilla/stack/tailwind.config.cjs new file mode 100644 index 00000000..e1e70f61 --- /dev/null +++ b/examples/vanilla/stack/tailwind.config.cjs @@ -0,0 +1,8 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: ["./app/**/*.tsx", "./app/**/*.ts", "./app/**/*.js"], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/packages/vinxi-directives/plugin.js b/packages/vinxi-directives/plugin.js index 4a975fc4..b537dee8 100644 --- a/packages/vinxi-directives/plugin.js +++ b/packages/vinxi-directives/plugin.js @@ -1,5 +1,3 @@ -import { decorateExportsPlugin, shimExportsPlugin } from "./transform"; - /** * * @param {*} param0 diff --git a/packages/vinxi-directives/transform.js b/packages/vinxi-directives/transform.js index 25b2fadd..5a3ce609 100644 --- a/packages/vinxi-directives/transform.js +++ b/packages/vinxi-directives/transform.js @@ -9,7 +9,7 @@ import { readFileSync } from "fs"; import { print, types, visit } from "recast"; -import { parseAdvanced, parseLoose } from "./parse"; +import { parseAdvanced, parseLoose } from "./parse.js"; /** * diff --git a/packages/vinxi-server-components/package.json b/packages/vinxi-server-components/package.json index 6db311f0..ac64d8c5 100644 --- a/packages/vinxi-server-components/package.json +++ b/packages/vinxi-server-components/package.json @@ -1,12 +1,9 @@ { - "name": "@vinxi/plugin-server-components", + "name": "@vinxi/server-components", "version": "0.0.35", "type": "module", "author": "Nikhil Saraf ", - "files": [ - "*.js", - "*.d.ts" - ], + "files": ["*.js", "*.d.ts"], "types": "./index.d.ts", "exports": { ".": "./index.js", @@ -14,9 +11,7 @@ }, "typesVersions": { "*": { - ".": [ - "./index.d.ts" - ] + ".": ["./index.d.ts"] } }, "devDependencies": { diff --git a/packages/vinxi-server-components/plugin.js b/packages/vinxi-server-components/plugin.js new file mode 100644 index 00000000..4fe996c5 --- /dev/null +++ b/packages/vinxi-server-components/plugin.js @@ -0,0 +1,20 @@ +import { fileURLToPath } from "url"; + +import { client } from "./client.js"; +import { serverActions } from "./server-action.js"; +import { server } from "./server.js"; + +export const serverComponents = { + client: client, + server: server, + serverActions: serverActions, + serverRouter: (overrides) => ({ + name: "server", + mode: "handler", + base: "/_server", + handler: fileURLToPath(new URL("./server-handler.js", import.meta.url)), + target: "server", + ...(overrides ?? {}), + plugins: () => [server(), ...(overrides?.plugins?.() ?? [])], + }), +}; diff --git a/packages/vinxi-server-functions/index.js b/packages/vinxi-server-functions/index.js index 0931cd04..cb23bf8f 100644 --- a/packages/vinxi-server-functions/index.js +++ b/packages/vinxi-server-functions/index.js @@ -1,18 +1,18 @@ -import { fileURLToPath } from "url"; +import { createStack } from "vinxi/stack"; -import { client } from "./client.js"; -import { server } from "./server.js"; +import { serverFunctions } from "./plugin.js"; -export const serverFunctions = { - client: client, - server: server, - router: (overrides) => ({ - name: "server", - mode: "handler", - base: "/_server", - handler: fileURLToPath(new URL("./server-handler.js", import.meta.url)), - target: "server", - ...(overrides ?? {}), - plugins: () => [server(), ...(overrides?.plugins?.() ?? [])], - }), -}; +export default function serverFns(app) { + app.addRouter(serverFunctions.router()); + const clientRouters = app.config.routers.filter( + (router) => router.target === "browser", + ); + + clientRouters.forEach((router) => { + if (router.plugins) { + router.plugins = () => [serverFunctions.client(), ...router.plugins()]; + } else if (router.plugins === undefined) { + router.plugins = () => [serverFunctions.client()]; + } + }); +} diff --git a/packages/vinxi-server-functions/package.json b/packages/vinxi-server-functions/package.json index 2526a91e..e917a200 100644 --- a/packages/vinxi-server-functions/package.json +++ b/packages/vinxi-server-functions/package.json @@ -1,5 +1,5 @@ { - "name": "@vinxi/plugin-server-functions", + "name": "@vinxi/server-functions", "version": "0.0.35", "type": "module", "author": "Nikhil Saraf ", diff --git a/packages/vinxi-server-functions/plugin.js b/packages/vinxi-server-functions/plugin.js new file mode 100644 index 00000000..5399f740 --- /dev/null +++ b/packages/vinxi-server-functions/plugin.js @@ -0,0 +1,18 @@ +import { fileURLToPath } from "url"; + +import { client } from "./client.js"; +import { server } from "./server.js"; + +export const serverFunctions = { + client: client, + server: server, + router: (overrides) => ({ + name: "server-fns", + mode: "handler", + base: "/_server", + handler: fileURLToPath(new URL("./server-handler.js", import.meta.url)), + target: "server", + ...(overrides ?? {}), + plugins: () => [server(), ...(overrides?.plugins?.() ?? [])], + }), +}; diff --git a/packages/vinxi-server-functions/server-handler.js b/packages/vinxi-server-functions/server-handler.js index ca180701..35c8de09 100644 --- a/packages/vinxi-server-functions/server-handler.js +++ b/packages/vinxi-server-functions/server-handler.js @@ -29,32 +29,28 @@ export async function handleServerAction(event) { // This is the client-side case const [filepath, name] = serverReference.split("#"); const action = (await loadModule(filepath))[name]; - // Validate that this is actually a function we intended to expose and - // not the client trying to invoke arbitrary functions. In a real app, - // you'd have a manifest verifying this before even importing it. - // if (action.$$typeof !== Symbol.for("react.server.reference")) { - // throw new Error("Invalid action"); - // } - const result = action.apply(null, await toWebRequest(event).json()); + const text = await new Promise((resolve) => { + const requestBody = []; + event.node.req.on("data", (chunks) => { + console.log(chunks); + requestBody.push(chunks); + }); + event.node.req.on("end", () => { + resolve(requestBody.join("")); + }); + }); + const json = JSON.parse(text); + const result = action.apply(null, json); try { // Wait for any mutations const response = await result; - // const stream = renderToPipeableStream(result); - // // @ts-ignore - // stream._read = () => {}; - // // @ts-ignore - // stream.on = (event, listener) => { - // events[event] = listener; - // }; event.node.res.setHeader("Content-Type", "application/json"); event.node.res.setHeader("Router", "server"); return JSON.stringify(response ?? null); } catch (x) { - // We handle the error on the client + console.error(x); } - // Refresh the client and return the value - // return {}; } else { throw new Error("Invalid request"); } diff --git a/packages/vinxi/bin/cli.mjs b/packages/vinxi/bin/cli.mjs index ceaeedbf..8ec62faf 100755 --- a/packages/vinxi/bin/cli.mjs +++ b/packages/vinxi/bin/cli.mjs @@ -13,9 +13,11 @@ async function main() { const command = args._[0]; const rootDir = resolve(args._[1] || "."); + console.log(args); + const configFile = args.config; globalThis.MANIFEST = {}; - const app = await loadApp(configFile); + const app = await loadApp(configFile, args); if (command === "dev") { let devServer; @@ -32,7 +34,7 @@ async function main() { watcher.on("all", async (ctx, path) => { log("change detected in", path); log("reloading app"); - const newApp = await loadApp(configFile); + const newApp = await loadApp(configFile, args); if (!newApp) return; restartDevServer(newApp); }); @@ -61,7 +63,7 @@ async function main() { fsWatcher.on("all", async (path) => { log("change detected in", path); log("reloading app"); - const newApp = await loadApp(configFile); + const newApp = await loadApp(configFile, args); if (!newApp) return; fsWatcher.close(); @@ -83,7 +85,7 @@ async function main() { const { createBuild } = await import("../lib/build.js"); await createBuild(app, {}); } else if (command === "start") { - exec(`node .output/dist/server.js`); + await exec(`node .output/server/index.mjs`); } else { throw new Error(`Unknown command ${command}`); } diff --git a/packages/vinxi/lib/app.d.ts b/packages/vinxi/lib/app.d.ts new file mode 100644 index 00000000..25add0fa --- /dev/null +++ b/packages/vinxi/lib/app.d.ts @@ -0,0 +1,15 @@ +export type App = { + config: { + name: string; + devtools: boolean; + server: import("nitropack").NitroConfig; + routers: import("./router-mode.js").Router[]; + root: string; + }; + addRouter: (router: any) => App; + getRouter: (name: string) => import("./router-mode.js").Router; + stack: (stack: any) => App; + dev(): Promise; + build(): Promise; + hooks: import("hookable").Hookable; +}; diff --git a/packages/vinxi/lib/app.js b/packages/vinxi/lib/app.js index 81a33bbb..c17f2e0c 100644 --- a/packages/vinxi/lib/app.js +++ b/packages/vinxi/lib/app.js @@ -1,5 +1,4 @@ // @ts-ignore -import { devtoolsClient, devtoolsRpc } from "@vinxi/devtools"; import { createHooks } from "hookable"; import { isMainThread } from "worker_threads"; @@ -7,8 +6,30 @@ import invariant, { InvariantError } from "./invariant.js"; import { c, consola, log, withLogger } from "./logger.js"; import { resolveRouterConfig, routerSchema } from "./router-modes.js"; -/** @typedef {{ devtools?: boolean; routers?: import("./router-modes.js").RouterSchemaInput[]; name?: string; server?: import('nitropack').NitroConfig; root?: string }} AppOptions */ -/** @typedef {{ config: { name: string; devtools: boolean; server: import('nitropack').NitroConfig; routers: import("./router-mode.js").Router[]; root: string; }; addRouter: (router: any) => App, getRouter: (name: string) => import("./router-mode.js").Router; dev(): Promise; build(): Promise; hooks: import('hookable').Hookable}} App */ +/** @typedef {{ + devtools?: boolean; + routers?: import("./router-modes.js").RouterSchemaInput[]; + name?: + string; + server?: import('nitropack').NitroConfig; + root?: string +}} AppOptions */ + +/** @typedef {{ + config: { + name: string; + devtools: boolean; + server: import("nitropack").NitroConfig; + routers: import("./router-mode.js").Router[]; + root: string; + }; + addRouter: (router: any) => App; + getRouter: (name: string) => import("./router-mode.js").Router; + stack: (stack: (app: App) => void | Promise) => Promise; + dev(): Promise; + build(): Promise; + hooks: import("hookable").Hookable; +}} App */ /** * @@ -20,7 +41,7 @@ export function createApp({ name = "vinxi", server = {}, root = process.cwd(), -}) { +} = {}) { const hooks = createHooks(); hooks.afterEach((result) => { const output = result.args[0].router @@ -103,6 +124,10 @@ export function createApp({ } return router; }, + async stack(stack) { + await stack(app); + return app; + }, async dev() { if (isMainThread) { const { createDevServer } = await import("./dev-server.js"); diff --git a/packages/vinxi/lib/build.js b/packages/vinxi/lib/build.js index 0110282b..5714170a 100644 --- a/packages/vinxi/lib/build.js +++ b/packages/vinxi/lib/build.js @@ -67,6 +67,7 @@ export async function createBuild(app, buildConfig) { const nitro = await createNitro({ ...app.config.server, dev: false, + preset: process.env.TARGET ?? process.env.NITRO_PRESET ?? @@ -93,6 +94,7 @@ export async function createBuild(app, buildConfig) { "#vinxi/chunks", ...(app.config.server.plugins ?? []), ], + buildDir: ".vinxi", handlers: [ ...[...app.config.routers] .sort((a, b) => a.base.length - b.base.length) diff --git a/packages/vinxi/lib/load-app.js b/packages/vinxi/lib/load-app.js index fb642ffb..1c3a16b5 100644 --- a/packages/vinxi/lib/load-app.js +++ b/packages/vinxi/lib/load-app.js @@ -1,5 +1,6 @@ import { loadConfig } from "c12"; import chokidar from "chokidar"; +import { fileURLToPath } from "url"; import { createApp } from "./app.js"; @@ -8,7 +9,9 @@ import { createApp } from "./app.js"; * @param {string | undefined} configFile * @returns {Promise} */ -export async function loadApp(configFile = undefined) { +export async function loadApp(configFile = undefined, args = {}) { + const stacks = typeof args.s === "string" ? [args.s] : args.s ?? []; + console.log(stacks); /** @type {{ config: import("./app.js").App }}*/ try { let { config: app } = await loadConfig( @@ -41,10 +44,16 @@ export async function loadApp(configFile = undefined) { if (config.config) { console.warn("Found vite.config.js with app config"); // @ts-expect-error trying to send c12's config as app + // return config; } else { console.warn("No app config found. Assuming SPA app."); - return createApp({ + + if (stacks.length) { + return applyStacks(createApp({}), stacks); + } + + const app = createApp({ routers: [ { name: "public", @@ -60,13 +69,40 @@ export async function loadApp(configFile = undefined) { }, ], }); + + return applyStacks(app, stacks); } } - return app; + return applyStacks(app, stacks); } catch (e) { console.error(e); return undefined; } } + +async function applyStacks(app, s) { + const { default: resolve } = await import("resolve"); + const stacks = await Promise.all( + s.map(async (stack) => { + if (stack.includes("/") || stack.includes("@")) { + var res = resolve.sync(stack, { basedir: app.config.root }); + const mod = await import(res); + return mod.default; + } + const mod = await import( + fileURLToPath(new URL(`../stack/${stack}.js`, import.meta.url)) + ); + return mod.default; + }), + ); + + console.log(stacks); + + for (const stack of stacks) { + await app.stack(stack); + } + + return app; +} diff --git a/packages/vinxi/package.json b/packages/vinxi/package.json index 05b63c86..9f20e1fe 100644 --- a/packages/vinxi/package.json +++ b/packages/vinxi/package.json @@ -3,15 +3,7 @@ "version": "0.0.43", "type": "module", "author": "Nikhil Saraf ", - "files": [ - "bin", - "lib", - "public", - "runtime", - "types", - "dist/types", - "*.d.ts" - ], + "files": ["bin", "lib", "public", "runtime", "types", "dist/types", "*.d.ts"], "scripts": { "types": "npm run tsc", "tsc": "rm -rf dist/types && tsc; cp -r types dist/types/types" @@ -27,6 +19,9 @@ "import": "./lib/dev-server.js", "types": "./dist/types/lib/dev-server.d.ts" }, + "./stack": { + "import": "./stack/index.js" + }, "./lib/doc": { "import": "./lib/doc.js" }, @@ -83,48 +78,20 @@ }, "typesVersions": { "*": { - ".": [ - "./dist/types/lib/index.d.ts" - ], - "dev-server": [ - "./dist/types/lib/dev-server.d.ts" - ], - "server": [ - "./dist/types/runtime/server.d.ts" - ], - "party": [ - "./dist/types/runtime/party.d.ts" - ], - "css": [ - "./dist/types/runtime/style.d.ts" - ], - "client": [ - "./dist/types/runtime/client.d.ts" - ], - "fs-router": [ - "./dist/types/lib/fs-router.d.ts" - ], - "types/server": [ - "./types/server.d.ts" - ], - "types/client": [ - "./types/client.d.ts" - ], - "routes": [ - "./types/routes.d.ts" - ], - "lib/invariant": [ - "./dist/types/lib/invariant.d.ts" - ], - "plugins/config": [ - "./dist/types/lib/plugins/config.d.ts" - ], - "plugins/virtual": [ - "./dist/types/lib/plugins/virtual.d.ts" - ], - "lib/path": [ - "./dist/types/lib/path.d.ts" - ] + ".": ["./dist/types/lib/index.d.ts"], + "dev-server": ["./dist/types/lib/dev-server.d.ts"], + "server": ["./dist/types/runtime/server.d.ts"], + "party": ["./dist/types/runtime/party.d.ts"], + "css": ["./dist/types/runtime/style.d.ts"], + "client": ["./dist/types/runtime/client.d.ts"], + "fs-router": ["./dist/types/lib/fs-router.d.ts"], + "types/server": ["./types/server.d.ts"], + "types/client": ["./types/client.d.ts"], + "routes": ["./types/routes.d.ts"], + "lib/invariant": ["./dist/types/lib/invariant.d.ts"], + "plugins/config": ["./dist/types/lib/plugins/config.d.ts"], + "plugins/virtual": ["./dist/types/lib/plugins/virtual.d.ts"], + "lib/path": ["./dist/types/lib/path.d.ts"] } }, "dependencies": { diff --git a/packages/vinxi/stack/assets.js b/packages/vinxi/stack/assets.js new file mode 100644 index 00000000..381a6b87 --- /dev/null +++ b/packages/vinxi/stack/assets.js @@ -0,0 +1,9 @@ +import { createStack } from "./index.js"; + +export default createStack((app) => { + app.addRouter({ + name: "public", + mode: "static", + dir: "./public", + }); +}); diff --git a/packages/vinxi/stack/index.js b/packages/vinxi/stack/index.js new file mode 100644 index 00000000..3cc601b9 --- /dev/null +++ b/packages/vinxi/stack/index.js @@ -0,0 +1,5 @@ +export function createStack( + /** @type {(app: import('./app.js').App) => import('./app.js').App | void}*/ stack, +) { + return stack; +} diff --git a/packages/vinxi/stack/server.js b/packages/vinxi/stack/server.js new file mode 100644 index 00000000..3fe6aefd --- /dev/null +++ b/packages/vinxi/stack/server.js @@ -0,0 +1,10 @@ +import { createStack } from "./index.js"; + +export default createStack((app) => { + app.addRouter({ + name: "server", + mode: "handler", + handler: "./index.ts", + target: "server", + }); +}); diff --git a/packages/vinxi/stack/spa.js b/packages/vinxi/stack/spa.js new file mode 100644 index 00000000..f1eb2350 --- /dev/null +++ b/packages/vinxi/stack/spa.js @@ -0,0 +1,10 @@ +import { createStack } from "./index.js"; + +export default createStack((app) => { + app.addRouter({ + name: "client", + mode: "spa", + handler: "./index.html", + target: "browser", + }); +}); diff --git a/packages/vinxi/tsconfig.json b/packages/vinxi/tsconfig.json index e68e6f67..5a968ff3 100644 --- a/packages/vinxi/tsconfig.json +++ b/packages/vinxi/tsconfig.json @@ -9,6 +9,7 @@ "strict": true, "checkJs": true, "noEmit": false, + "rootDir": "./", "isolatedModules": true, "declarationMap": true, "declaration": true, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0aaf7caa..5e916817 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -152,18 +152,18 @@ importers: '@picocss/pico': specifier: ^1.5.10 version: 1.5.10 - '@vinxi/plugin-server-components': + '@vinxi/server-components': specifier: 0.0.35 version: link:../../../../packages/vinxi-server-components - '@vinxi/plugin-server-functions': - specifier: 0.0.35 - version: link:../../../../packages/vinxi-server-functions '@vinxi/react': specifier: 0.0.11 version: link:../../../../packages/vinxi-react '@vinxi/react-server-dom': specifier: 0.0.3 version: 0.0.3(react-dom@0.0.0-experimental-035a41c4e-20230704)(react@0.0.0-experimental-035a41c4e-20230704)(vite@4.5.0) + '@vinxi/server-functions': + specifier: 0.0.35 + version: link:../../../../packages/vinxi-server-functions '@vitejs/plugin-react': specifier: ^4.0.4 version: 4.1.0(vite@4.5.0) @@ -761,7 +761,7 @@ importers: '@picocss/pico': specifier: ^1.5.10 version: 1.5.10 - '@vinxi/plugin-server-functions': + '@vinxi/server-functions': specifier: workspace:^ version: link:../../../packages/vinxi-server-functions autoprefixer: @@ -774,6 +774,28 @@ importers: specifier: 0.0.43 version: link:../../../packages/vinxi + examples/vanilla/stack: + dependencies: + '@picocss/pico': + specifier: ^1.5.10 + version: 1.5.10 + '@vinxi/server-functions': + specifier: workspace:^ + version: link:../../../packages/vinxi-server-functions + autoprefixer: + specifier: ^10.4.15 + version: 10.4.16(postcss@8.4.31) + tailwindcss: + specifier: ^3.3.3 + version: 3.3.5 + vinxi: + specifier: 0.0.43 + version: link:../../../packages/vinxi + devDependencies: + '@types/resolve': + specifier: ^1.20.4 + version: 1.20.4 + examples/vanilla/trpc: dependencies: '@picocss/pico': @@ -1354,18 +1376,18 @@ importers: '@picocss/pico': specifier: ^1.5.10 version: 1.5.10 - '@vinxi/plugin-server-components': + '@vinxi/server-components': specifier: 0.0.35 version: link:../../../packages/vinxi-server-components - '@vinxi/plugin-server-functions': - specifier: 0.0.35 - version: link:../../../packages/vinxi-server-functions '@vinxi/react': specifier: 0.0.11 version: link:../../../packages/vinxi-react '@vinxi/react-server-dom': specifier: 0.0.3 version: 0.0.3(react-dom@0.0.0-experimental-035a41c4e-20230704)(react@0.0.0-experimental-035a41c4e-20230704)(vite@4.5.0) + '@vinxi/server-functions': + specifier: 0.0.35 + version: link:../../../packages/vinxi-server-functions '@vitejs/plugin-react': specifier: ^4.0.4 version: 4.1.0(vite@4.5.0) diff --git a/test/templates/react-rsc/CHANGELOG.md b/test/templates/react-rsc/CHANGELOG.md index b3fa89e3..82ad0027 100644 --- a/test/templates/react-rsc/CHANGELOG.md +++ b/test/templates/react-rsc/CHANGELOG.md @@ -6,8 +6,8 @@ - Updated dependencies [978b04a] - vinxi@0.0.43 - - @vinxi/plugin-server-components@0.0.35 - - @vinxi/plugin-server-functions@0.0.35 + - @vinxi/server-components@0.0.35 + - @vinxi/server-functions@0.0.35 ## null @@ -16,26 +16,26 @@ - Updated dependencies [e8fdad4] - Updated dependencies [291a5cf] - vinxi@0.0.42 - - @vinxi/plugin-server-components@0.0.34 - - @vinxi/plugin-server-functions@0.0.34 + - @vinxi/server-components@0.0.34 + - @vinxi/server-functions@0.0.34 ## null ### Patch Changes - Updated dependencies [2e52d87] - - @vinxi/plugin-server-functions@0.0.33 + - @vinxi/server-functions@0.0.33 - vinxi@0.0.41 - - @vinxi/plugin-server-components@0.0.33 + - @vinxi/server-components@0.0.33 ## null ### Patch Changes - Updated dependencies [0335776] - - @vinxi/plugin-server-components@0.0.32 + - @vinxi/server-components@0.0.32 - vinxi@0.0.40 - - @vinxi/plugin-server-functions@0.0.32 + - @vinxi/server-functions@0.0.32 ## null @@ -44,8 +44,8 @@ - Updated dependencies [f237894] - Updated dependencies [d620072] - vinxi@0.0.39 - - @vinxi/plugin-server-components@0.0.31 - - @vinxi/plugin-server-functions@0.0.31 + - @vinxi/server-components@0.0.31 + - @vinxi/server-functions@0.0.31 ## null @@ -53,9 +53,9 @@ - Updated dependencies [1284791] - Updated dependencies [930b2f2] - - @vinxi/plugin-server-functions@0.0.30 + - @vinxi/server-functions@0.0.30 - vinxi@0.0.38 - - @vinxi/plugin-server-components@0.0.30 + - @vinxi/server-components@0.0.30 ## null diff --git a/test/templates/react-rsc/app.config.js b/test/templates/react-rsc/app.config.js index 9157d134..263510f3 100644 --- a/test/templates/react-rsc/app.config.js +++ b/test/templates/react-rsc/app.config.js @@ -1,5 +1,5 @@ -import { serverComponents } from "@vinxi/plugin-server-components"; -import { serverFunctions } from "@vinxi/plugin-server-functions"; +import { serverComponents } from "@vinxi/server-components/plugin"; +import { serverFunctions } from "@vinxi/server-functions/plugin"; import reactRefresh from "@vitejs/plugin-react"; import { createApp } from "vinxi"; diff --git a/test/templates/react-rsc/package.json b/test/templates/react-rsc/package.json index 14c3c7f4..d01076be 100644 --- a/test/templates/react-rsc/package.json +++ b/test/templates/react-rsc/package.json @@ -10,8 +10,8 @@ }, "dependencies": { "@picocss/pico": "^1.5.10", - "@vinxi/plugin-server-functions": "0.0.35", - "@vinxi/plugin-server-components": "0.0.35", + "@vinxi/server-functions": "0.0.35", + "@vinxi/server-components": "0.0.35", "@vinxi/react": "0.0.11", "@vinxi/react-server-dom": "0.0.3", "@vitejs/plugin-react": "^4.0.4",