Skip to content

Commit

Permalink
docs(changeset): new CLI infra using citty and server-function fix
Browse files Browse the repository at this point in the history
nksaraf committed Nov 4, 2023
1 parent fa87a10 commit a4512f5
Showing 13 changed files with 337 additions and 160 deletions.
6 changes: 6 additions & 0 deletions .changeset/loud-cobras-smoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@vinxi/server-functions": patch
"vinxi": patch
---

new CLI infra using citty and server-function fix
1 change: 1 addition & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
"^node:(.*)$",
"^[./]"
],
"importAttributes": true,
"importOrderSeparation": true,
"importOrderSortSpecifiers": true,
"overrides": [
4 changes: 2 additions & 2 deletions examples/vanilla/spa/package.json
Original file line number Diff line number Diff line change
@@ -10,9 +10,9 @@
},
"dependencies": {
"@picocss/pico": "^1.5.10",
"@vinxi/server-functions": "0.0.35",
"@vinxi/server-functions": "0.0.37",
"autoprefixer": "^10.4.15",
"tailwindcss": "^3.3.3",
"vinxi": "0.0.43"
"vinxi": "0.0.45"
}
}
5 changes: 3 additions & 2 deletions examples/vanilla/stack/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "example-vanilla-spa",
"name": "example-vanilla-stack",
"type": "module",
"private": true,
"version": null,
@@ -10,7 +10,8 @@
},
"dependencies": {
"@picocss/pico": "^1.5.10",
"@vinxi/server-functions": "workspace:^",
"@vinxi/server-functions": "0.0.37",
"@vitejs/plugin-react": "^4.0.4",
"autoprefixer": "^10.4.15",
"tailwindcss": "^3.3.3",
"vinxi": "0.0.45"
11 changes: 2 additions & 9 deletions packages/vinxi-server-functions/index.js
Original file line number Diff line number Diff line change
@@ -4,15 +4,8 @@ import { serverFunctions } from "./plugin.js";

export default function serverFns(app) {
app.addRouter(serverFunctions.router());
const clientRouters = app.config.routers.filter(
app.addRouterPlugins(
(router) => router.target === "browser",
() => [serverFunctions.client()],
);

clientRouters.forEach((router) => {
if (router.plugins) {
router.plugins = () => [serverFunctions.client(), ...router.plugins()];
} else if (router.plugins === undefined) {
router.plugins = () => [serverFunctions.client()];
}
});
}
7 changes: 4 additions & 3 deletions packages/vinxi-server-functions/server-handler.js
Original file line number Diff line number Diff line change
@@ -6,7 +6,8 @@ async function loadModule(id) {
if (import.meta.env.DEV) {
const mod = await import(
/* @vite-ignore */
import.meta.env.MANIFEST["server"].chunks[id].output.path
import.meta.env.MANIFEST[import.meta.env.ROUTER_NAME].chunks[id].output
.path
);
return mod;
}
@@ -16,7 +17,7 @@ async function loadModule(id) {
}
return await import(
/* @vite-ignore */
import.meta.env.MANIFEST["server"].chunks[id].output.path
import.meta.env.MANIFEST[import.meta.env.ROUTER_NAME].chunks[id].output.path
);
}

@@ -45,7 +46,7 @@ export async function handleServerAction(event) {
// Wait for any mutations
const response = await result;
event.node.res.setHeader("Content-Type", "application/json");
event.node.res.setHeader("Router", "server");
event.node.res.setHeader("Router", "server-fns");

return JSON.stringify(response ?? null);
} catch (x) {
227 changes: 149 additions & 78 deletions packages/vinxi/bin/cli.mjs
Original file line number Diff line number Diff line change
@@ -1,94 +1,165 @@
#!/usr/bin/env node
import chokidar from "chokidar";
import mri from "mri";
import { defineCommand, runMain } from "citty";
import fs from "fs";
// import mri from "mri";
import { fileURLToPath } from "url";

import { exec } from "node:child_process";

import { loadApp } from "../lib/load-app.js";
import { log } from "../lib/logger.js";
import { resolve } from "../lib/path.js";

async function main() {
const args = mri(process.argv.slice(2));
const command = args._[0];
const rootDir = resolve(args._[1] || ".");
const packageJson = JSON.parse(
fs.readFileSync(
fileURLToPath(new URL("../package.json", import.meta.url)),
"utf-8",
),
);

const configFile = args.config;
globalThis.MANIFEST = {};
const app = await loadApp(configFile, args);
const command = defineCommand({
meta: {
name: "vinxi",
version: packageJson.version,
description: "Vinxi: The JavaScript/TypeScript Server SDK",
},
args: {
config: {
type: "string",
description: "Path to config file (default: app.config.js)",
},
},
subCommands: () => ({
dev: {
meta: {
name: "dev",
version: packageJson.version,
description: "Start a Vinxi development server",
},
args: {
config: {
type: "string",
description: "Path to config file (default: app.config.js)",
},
force: {
type: "boolean",
description: "Force optimize deps (default: false)",
},
devtools: {
type: "boolean",
description: "Enable devtools (default: false)",
},
port: {
type: "number",
description: "Port to listen on (default: 3000)",
},
host: {
type: "boolean",
description: "Expose to host (default: false)",
},
stack: {
type: "string",
description: "Stacks",
alias: "s",
},
},
async run({ args }) {
const configFile = args.config;
globalThis.MANIFEST = {};
const app = await loadApp(configFile, args);

if (command === "dev") {
let devServer;
/** @type {import('chokidar').FSWatcher} */
let watcher;
let devServer;
/** @type {import('chokidar').FSWatcher} */
let watcher;

function createWatcher() {
watcher = chokidar.watch(
["app.config.*", "vite.config.*", configFile].filter(Boolean),
{
ignoreInitial: true,
},
);
watcher.on("all", async (ctx, path) => {
log("change detected in", path);
log("reloading app");
const newApp = await loadApp(configFile, args);
if (!newApp) return;
restartDevServer(newApp);
});
}
function createWatcher() {
watcher = chokidar.watch(
["app.config.*", "vite.config.*", configFile].filter(Boolean),
{
ignoreInitial: true,
},
);
watcher.on("all", async (ctx, path) => {
log("change detected in", path);
log("reloading app");
const newApp = await loadApp(configFile, args);
if (!newApp) return;
restartDevServer(newApp);
});
}

async function restartDevServer(newApp) {
const { createDevServer } = await import("../lib/dev-server.js");
await devServer?.close();
devServer = await createDevServer(newApp, {
force: args.force,
devtools: args.devtools || Boolean(process.env.DEVTOOLS),
port: Number(process.env.PORT ?? 3000),
});
log("restarting dev server");
devServer.listen();
}
async function restartDevServer(newApp) {
const { createDevServer } = await import("../lib/dev-server.js");
await devServer?.close();
devServer = await createDevServer(newApp, {
force: args.force,
devtools: args.devtools || Boolean(process.env.DEVTOOLS),
port: Number(process.env.PORT ?? 3000),
});
log("restarting dev server");
devServer.listen();
}

if (!app) {
let fsWatcher = (watcher = chokidar.watch(
["app.config.*", "vite.config.*", configFile].filter(Boolean),
{
ignoreInitial: true,
persistent: true,
},
));
fsWatcher.on("all", async (path) => {
log("change detected in", path);
log("reloading app");
const newApp = await loadApp(configFile, args);
if (!newApp) return;
if (!app) {
let fsWatcher = (watcher = chokidar.watch(
["app.config.*", "vite.config.*", configFile].filter(Boolean),
{
ignoreInitial: true,
persistent: true,
},
));
fsWatcher.on("all", async (path) => {
log("change detected in", path);
log("reloading app");
const newApp = await loadApp(configFile, args);
if (!newApp) return;

fsWatcher.close();
fsWatcher.close();
createWatcher();
restartDevServer(newApp);
});
return;
}
createWatcher();
restartDevServer(newApp);
});
return;
}
createWatcher();
const { createDevServer } = await import("../lib/dev-server.js");
devServer = await createDevServer(app, {
force: args.force,
port: Number(process.env.PORT ?? 3000),
devtools: args.devtools || Boolean(process.env.DEVTOOLS),
});
devServer.listen();
} else if (command === "build") {
process.env.NODE_ENV = "production";
const { createBuild } = await import("../lib/build.js");
await createBuild(app, {});
} else if (command === "start") {
await exec(`node .output/server/index.mjs`);
} else {
throw new Error(`Unknown command ${command}`);
}
}
main().catch((err) => {
console.error(err);
process.exit(1);
const { createDevServer } = await import("../lib/dev-server.js");
devServer = await createDevServer(app, {
force: args.force,
port: Number(process.env.PORT ?? 3000),
devtools: args.devtools || Boolean(process.env.DEVTOOLS),
});
devServer.listen();
},
},
build: {
meta: {
name: "build",
version: packageJson.version,
description: "Build your Vinxi app",
},
args: {
config: {
type: "string",
description: "Path to config file (default: app.config.js)",
},
stack: {
type: "string",
description: "Stacks",
},
preset: {
type: "string",
description: "Server preset (default: node-server)",
},
},
async run({ args }) {
const configFile = args.config;
globalThis.MANIFEST = {};
const app = await loadApp(configFile, args);
process.env.NODE_ENV = "production";
const { createBuild } = await import("../lib/build.js");
await createBuild(app, { preset: args.preset });
},
},
}),
});

runMain(command);
26 changes: 26 additions & 0 deletions packages/vinxi/lib/app.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-ignore
import { createHooks } from "hookable";
import resolve from "resolve";
import { isMainThread } from "worker_threads";

import invariant, { InvariantError } from "./invariant.js";
@@ -24,7 +25,10 @@ import { resolveRouterConfig, routerSchema } from "./router-modes.js";
root: string;
};
addRouter: (router: any) => App;
addRouterPlugins: (apply: (router: import("./router-mode.js").Router) => boolean, plugins: () => any[]) => void;
getRouter: (name: string) => import("./router-mode.js").Router;
resolveSync: (mod: string) => string;
import: (mod: string) => Promise<any>;
stack: (stack: (app: App) => void | Promise<void>) => Promise<App>;
dev(): Promise<void>;
build(): Promise<void>;
@@ -121,6 +125,28 @@ export function createApp({
config.routers.push(resolvedRouter);
return app;
},
addRouterPlugins(apply, plugins) {
const routers = app.config.routers.filter(apply);

routers.forEach((router) => {
if (router.plugins) {
let prevPlugins = router.plugins;
router.plugins = () => [
...(plugins?.() ?? []),
...(prevPlugins() ?? []),
];
} else if (router.plugins === undefined) {
router.plugins = plugins;
}
});
},
resolveSync(mod) {
return resolve.sync(mod, { basedir: config.root });
},
async import(mod) {
const resolved = app.resolveSync(mod);
return await import(resolved);
},
getRouter(/** @type {string} */ name) {
const router = config.routers.find((router) => router.name === name);
if (!router) {
2 changes: 2 additions & 0 deletions packages/vinxi/lib/build.js
Original file line number Diff line number Diff line change
@@ -76,7 +76,9 @@ export async function createBuild(app, buildConfig) {
dev: false,

preset:
buildConfig.preset ??
process.env.TARGET ??
process.env.SERVER_PRESET ??
process.env.NITRO_PRESET ??
app.config.server.preset,
alias: {
2 changes: 1 addition & 1 deletion packages/vinxi/lib/load-app.js
Original file line number Diff line number Diff line change
@@ -87,7 +87,7 @@ async function applyStacks(app, s) {
const stacks = await Promise.all(
s.map(async (stack) => {
if (stack.includes("/") || stack.includes("@")) {
var res = resolve.sync(stack, { basedir: app.config.root });
var res = app.resolveSync(stack);
const mod = await import(res);
return mod.default;
}
Loading

0 comments on commit a4512f5

Please sign in to comment.