release: WRNexusJS 0.4.0
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
* the running process while the HMR socket morphs fresh HTML into the browser.
|
||||
*/
|
||||
|
||||
import { resolve, dirname, join } from "node:path";
|
||||
import { resolve, dirname, isAbsolute, join } from "node:path";
|
||||
import type { Middleware, Mode, SecurityConfig, SeoConfig } from "@wrnexus/core";
|
||||
import { buildRouter, type Router } from "@wrnexus/router";
|
||||
import {
|
||||
@@ -19,20 +19,22 @@ import {
|
||||
import { uiComponentsDir, uiCss } from "@wrnexus/ui";
|
||||
import { renderSchemasScript, type ObjectSchema, type SchemaDescriptor } from "@wrnexus/validation";
|
||||
import { loadLocales, resolveI18n, type I18nConfig } from "@wrnexus/i18n";
|
||||
import { migrate, setDb, registerDb } from "@wrnexus/db";
|
||||
import { applyMigrations, migrate, setDb, registerDb } from "@wrnexus/db";
|
||||
import { connectFromConfig } from "@wrnexus/db/connect";
|
||||
import { configureStorage, type StorageConfig } from "@wrnexus/uploader";
|
||||
import { realtimeBusFromConfig } from "./realtime-bus.ts";
|
||||
import { invalidateModule, loadModule, setCompileCacheDir } from "./pipeline.ts";
|
||||
import { createHandlers, type WsData } from "./runtime.ts";
|
||||
import { createDevAssetServer } from "./assets.ts";
|
||||
import { pluginAssetsFromContributions } from "./plugin-assets.ts";
|
||||
import { resolvePackageMigrations } from "./plugin-migrations.ts";
|
||||
import { HmrHub } from "./hmr.ts";
|
||||
import { startWatcher } from "./watch.ts";
|
||||
export { RESTART_EXIT_CODE } from "./restart.ts";
|
||||
import { resetDevCache } from "./cache.ts";
|
||||
|
||||
import type { DevToolbarConfig } from "@wrnexus/dev-toolbar/types";
|
||||
import { createPluginRunner, type PluginInput } from "@wrnexus/plugin";
|
||||
import { createPluginRunner, discoverPlugins, type PluginInput } from "@wrnexus/plugin";
|
||||
import type { ObservabilityConfig, TenancyConfig } from "@wrnexus/styles";
|
||||
|
||||
import { createDevToolbarCollector, type DevToolbarCollector } from "@wrnexus/dev-toolbar/server";
|
||||
@@ -160,7 +162,12 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
|
||||
const appDir = resolve(opts.appDir);
|
||||
const appRoot = dirname(appDir);
|
||||
const mode: Mode = opts.mode ?? "development";
|
||||
const pluginRunner = createPluginRunner(opts.plugins, {
|
||||
const discoveredPlugins = await discoverPlugins(appRoot, opts.plugins, {
|
||||
includeDevDependencies: true,
|
||||
strict: true,
|
||||
warn: (message) => console.warn(`[wrnexus:plugin] ${message}`),
|
||||
});
|
||||
const pluginRunner = createPluginRunner(discoveredPlugins, {
|
||||
root: appRoot,
|
||||
mode,
|
||||
command: "dev",
|
||||
@@ -171,13 +178,20 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
|
||||
await pluginRunner.configResolved(
|
||||
Object.freeze({ ...opts }) as Readonly<Record<string, unknown>>,
|
||||
);
|
||||
const pluginContributions = await pluginRunner.contributions();
|
||||
const pluginToolbarPanels = await pluginRunner.devToolbarPanels();
|
||||
const componentDirs = [uiComponentsDir(), ...pluginContributions.componentDirs];
|
||||
|
||||
const hmr = opts.hmr ?? mode === "development";
|
||||
const port = opts.port ?? 3000;
|
||||
const hostname = opts.hostname ?? "::";
|
||||
const displayHost = hostname === "0.0.0.0" || hostname === "::" ? "localhost" : hostname;
|
||||
|
||||
const router = buildRouter(appDir, { componentDirs: [uiComponentsDir()] });
|
||||
const router = buildRouter(appDir, {
|
||||
componentDirs,
|
||||
externalRoutes: pluginContributions.routes,
|
||||
middlewareFiles: pluginContributions.middleware,
|
||||
});
|
||||
const styleEntry = opts.styleEntry ?? null;
|
||||
|
||||
const devToolbarConfig = resolveDevToolbarConfig(mode, opts.devToolbar);
|
||||
@@ -215,7 +229,12 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
|
||||
? registerDb(name, connectFromConfig(cfg, appRoot))
|
||||
: setDb(connectFromConfig(cfg, appRoot));
|
||||
const dir = name ? join(appDir, "db", name, "migrations") : join(appDir, "db", "migrations");
|
||||
const applied = await migrate(db, dir);
|
||||
const appApplied = await migrate(db, dir);
|
||||
const packageApplied = await applyMigrations(
|
||||
db,
|
||||
resolvePackageMigrations(pluginContributions.migrations, name ?? undefined),
|
||||
);
|
||||
const applied = [...appApplied, ...packageApplied];
|
||||
if (applied.length) {
|
||||
console.log(
|
||||
`[wrnexus] applied ${applied.length} migration(s)${name ? ` to '${name}'` : ""}`,
|
||||
@@ -242,10 +261,16 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
|
||||
config: opts.stylesConfig,
|
||||
appRoot,
|
||||
publicDir: join(appRoot, "public"),
|
||||
sources: [
|
||||
...componentDirs,
|
||||
...pluginContributions.styles.flatMap((style) => (style.source ? [style.source] : [])),
|
||||
],
|
||||
entries: pluginContributions.styles.flatMap((style) => (style.entry ? [style.entry] : [])),
|
||||
},
|
||||
theme,
|
||||
uiStyles,
|
||||
schemasJs,
|
||||
pluginAssetsFromContributions(pluginContributions),
|
||||
);
|
||||
|
||||
const hub = hmr ? new HmrHub() : undefined;
|
||||
@@ -264,7 +289,7 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
|
||||
loadModule,
|
||||
getMiddleware: middleware.load,
|
||||
assets,
|
||||
hasStyles: !!styleEntry,
|
||||
hasStyles: !!styleEntry || pluginContributions.styles.some((style) => !!style.entry),
|
||||
hasUi: true,
|
||||
theme,
|
||||
i18n,
|
||||
@@ -275,6 +300,7 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
|
||||
security: opts.security,
|
||||
observability: opts.observability,
|
||||
tenancy: opts.tenancy,
|
||||
clientRuntimes: pluginContributions.clientRuntimes,
|
||||
hub,
|
||||
realtimeBus: realtimeBusFromConfig(opts.realtime),
|
||||
devToolbar:
|
||||
@@ -283,6 +309,31 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
|
||||
config: devToolbarConfig,
|
||||
collector: devToolbarCollector,
|
||||
root: appRoot,
|
||||
panels: pluginToolbarPanels,
|
||||
platform: {
|
||||
plugins: pluginRunner.plugins.map((plugin) => ({
|
||||
name: plugin.name,
|
||||
version: plugin.version,
|
||||
})),
|
||||
runtimes: pluginContributions.clientRuntimes.map((runtime) => ({
|
||||
id: runtime.id,
|
||||
publicPath: runtime.publicPath,
|
||||
type: runtime.type,
|
||||
load: runtime.load,
|
||||
})),
|
||||
assets: pluginContributions.assets.map((asset) => ({
|
||||
id: asset.id,
|
||||
publicPath: asset.publicPath,
|
||||
contentType: asset.contentType,
|
||||
})),
|
||||
componentDirs,
|
||||
styles: pluginContributions.styles,
|
||||
routes: {
|
||||
pages: router.pages.length,
|
||||
api: router.api.length,
|
||||
realtime: router.realtime.length,
|
||||
},
|
||||
},
|
||||
}
|
||||
: undefined,
|
||||
};
|
||||
@@ -304,6 +355,7 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
|
||||
handlers,
|
||||
assets,
|
||||
devToolbarCollector,
|
||||
pluginContributions,
|
||||
});
|
||||
} catch (error) {
|
||||
server.stop();
|
||||
@@ -319,22 +371,26 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
|
||||
// Allow VS Code/Bun to finish writing pasted content.
|
||||
await new Promise((resolvePromise) => setTimeout(resolvePromise, 50));
|
||||
|
||||
for (const relative of files) {
|
||||
invalidateModule(resolve(appDir, relative));
|
||||
for (const file of files) {
|
||||
invalidateModule(isAbsolute(file) ? file : resolve(appDir, file));
|
||||
}
|
||||
if (files.some((file) => file.endsWith(".wrn"))) assets.invalidateCss();
|
||||
|
||||
Object.assign(
|
||||
router,
|
||||
buildRouter(appDir, {
|
||||
componentDirs: [uiComponentsDir()],
|
||||
componentDirs,
|
||||
externalRoutes: pluginContributions.routes,
|
||||
middlewareFiles: pluginContributions.middleware,
|
||||
}),
|
||||
);
|
||||
middleware.invalidate();
|
||||
|
||||
if (files.some((file) => file === "schemas" || file.startsWith("schemas/"))) {
|
||||
const appFiles = files.filter((file) => !isAbsolute(file));
|
||||
if (appFiles.some((file) => file === "schemas" || file.startsWith("schemas/"))) {
|
||||
assets.updateSchemas(await schemaRuntime(router));
|
||||
}
|
||||
if (files.some((file) => file === "locales" || file.startsWith("locales/"))) {
|
||||
if (appFiles.some((file) => file === "locales" || file.startsWith("locales/"))) {
|
||||
const messages = loadLocales(join(appDir, "locales"));
|
||||
runtimeDeps.i18n = Object.keys(messages).length
|
||||
? resolveI18n(messages, opts.i18n)
|
||||
@@ -350,8 +406,23 @@ export async function startServer(opts: ServeOptions): Promise<RunningServer> {
|
||||
files,
|
||||
});
|
||||
};
|
||||
const packageWatchDirs = [
|
||||
...componentDirs,
|
||||
...pluginContributions.clientRuntimes.flatMap((runtime) =>
|
||||
runtime.entry ? [dirname(runtime.entry)] : [],
|
||||
),
|
||||
...pluginContributions.assets.flatMap((asset) => (asset.entry ? [dirname(asset.entry)] : [])),
|
||||
...pluginContributions.styles.flatMap((style) =>
|
||||
[style.source, style.entry ? dirname(style.entry) : undefined].filter(
|
||||
(value): value is string => !!value,
|
||||
),
|
||||
),
|
||||
...pluginContributions.routes.map((route) => dirname(route.entry)),
|
||||
...pluginContributions.middleware.map((file) => dirname(file)),
|
||||
];
|
||||
watcher = startWatcher({
|
||||
appDir,
|
||||
extraDirs: packageWatchDirs,
|
||||
hub,
|
||||
assets,
|
||||
devToolbarCollector,
|
||||
|
||||
Reference in New Issue
Block a user