@vercube/vite 1.1.2 → 1.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -79,17 +79,19 @@ node dist/index.mjs # runs the built server
79
79
  | `scanDirs` | `string[]` | `['src']` | Directories scanned (recursively) for `@Controller` and `@Injectable` classes. |
80
80
  | `setupFile` | `string` | `undefined` | Module whose default export `(app: App) => void \| Promise<void>` runs as `createApp`'s setup hook (before init). |
81
81
  | `runner` | `string` | `'node-worker'` | The `env-runner` runner used to execute server code in dev. |
82
- | `noExternal` | `(string \| RegExp)[]` | `undefined` | Extra dev `resolve.noExternal` patterns merged after `@vercube/*` (see below). |
82
+ | `noExternal` | `(string \| RegExp)[]` | `undefined` | Extra package patterns merged after `@vercube/*` for dev and production bundling (see below). |
83
83
 
84
- ### Dev `noExternal`
84
+ ### `noExternal`
85
85
 
86
- In dev, Vercube keeps `@vercube/*` inside Vite's module graph so class-reference DI tokens are not loaded twice. If a dependency imports `@vercube/core` from its published `dist` while your app imports it through Vite, inject tokens such as `RequestContext` will not match.
86
+ Vercube keeps `@vercube/*` in a single module graph so class-reference DI tokens are not loaded twice. If a dependency imports `@vercube/core` from its published `dist` while your app resolves a different copy (common with pnpm), inject tokens such as `RequestContext` will not match — auth middleware writes to one instance, your services read from another.
87
87
 
88
- Add those packages via `noExternal`:
88
+ The same patterns apply in **dev** (`resolve.noExternal`). In **production** the server bundle keeps `@vercube/*` and plugin packages as runtime imports — only app source (`@/…`) is inlined. Use **one** `@vercube/core` version across the app and dependencies (e.g. pnpm `overrides`), otherwise class-reference DI tokens such as `RequestContext` will not match between `@enp/auth` and your services.
89
+
90
+ Add packages that use Vercube DI tokens:
89
91
 
90
92
  ```ts
91
93
  vercube({
92
- noExternal: [/^@org\//],
94
+ noExternal: [/^@enp\//],
93
95
  });
94
96
  ```
95
97
 
@@ -35,7 +35,8 @@ function generateServerEntry(ctx) {
35
35
  else lines.push("const app = await createApp();", "");
36
36
  lines.push(...[...imports.keys()].map((name) => `app.container.bind(${name});`), "");
37
37
  lines.push("app.container.flushQueue();", "");
38
- lines.push("if (import.meta.main) {", " await app.listen();", "}", "");
38
+ if (ctx.hasClient) lines.push("if (import.meta.main) {", " const dir = fileURLToPath(new URL('./public', import.meta.url));", " app.container.get(HttpServer).enableSpaFallback(dir);", " await app.listen();", "}", "");
39
+ else lines.push("if (import.meta.main) {", " await app.listen();", "}", "");
39
40
  lines.push("export const fetch = app.fetch.bind(app);", "");
40
41
  lines.push("export const handleUpgrade = (req, socket, head) => globalThis.__vercube_ws_upgrade__?.(req, socket, head);", "");
41
42
  return lines.join("\n");
@@ -1,4 +1,4 @@
1
- import { r as scanProject } from "./context-CfzvWZrR.mjs";
1
+ import { r as scanProject } from "./context-sG04QQCS.mjs";
2
2
  import { t as VERCUBE_ENV } from "./types-Cpd32Lkt.mjs";
3
3
  import { createViteHotChannel } from "env-runner/vite";
4
4
  import { watch } from "chokidar";
package/dist/index.d.mts CHANGED
@@ -34,15 +34,15 @@ interface VercubePluginConfig {
34
34
  */
35
35
  runner?: string;
36
36
  /**
37
- * Additional package patterns passed to the Vercube environment's dev
38
- * `resolve.noExternal`. They are merged after the built-in `@vercube/*`
39
- * defaults so class-reference DI tokens are not duplicated in the module graph.
37
+ * Package patterns merged after the built-in `@vercube/*` defaults. In dev they
38
+ * are passed to `resolve.noExternal`; in production they are bundled into the
39
+ * server output so class-reference DI tokens are not duplicated at runtime.
40
40
  *
41
- * Use this for workspace packages (for example `@org/*`) that import
42
- * `@vercube/core` and register Vercube DI tokens.
41
+ * Use this for packages (for example `@enp/*`) that import `@vercube/core` and
42
+ * participate in request-scoped injection (`RequestContext`, etc.).
43
43
  *
44
44
  * @example
45
- * vercube({ noExternal: [/^@org\//] })
45
+ * vercube({ noExternal: [/^@enp\//] })
46
46
  */
47
47
  noExternal?: (string | RegExp)[];
48
48
  }
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
- import { i as setupContext, n as createContext, t as CLIENT_OUT_DIR } from "./context-CfzvWZrR.mjs";
1
+ import { i as setupContext, n as createContext, t as CLIENT_OUT_DIR } from "./context-sG04QQCS.mjs";
2
2
  import { t as VERCUBE_ENV } from "./types-Cpd32Lkt.mjs";
3
+ import { createRequire } from "node:module";
3
4
  import { defu } from "defu";
4
5
  import { dirname, isAbsolute, resolve } from "pathe";
5
6
  import { RunnerManager, loadRunner } from "env-runner";
@@ -8,25 +9,51 @@ import { fileURLToPath } from "node:url";
8
9
  * Absolute path to the worker bootstrap loaded by env-runner.
9
10
  */
10
11
  const devWorker = resolve(resolve(dirname(fileURLToPath(import.meta.url)), "runtime"), "dev-worker.mjs");
11
- //#endregion
12
- //#region src/env.ts
13
12
  /**
14
- * Default dev-only `resolve.noExternal` patterns. `@vercube/*` must stay in
15
- * Vite's module graph so class-reference DI tokens are not duplicated; other
16
- * deps (e.g. CJS-only `dotenv`) are left external and loaded via native
17
- * `import()`.
13
+ * Default `resolve.noExternal` / production bundle patterns. `@vercube/*` must stay in
14
+ * a single module graph so class-reference DI tokens (e.g. `RequestContext`) are not
15
+ * duplicated between the app, plugins, and framework.
16
+ */
17
+ const DEFAULT_NO_EXTERNAL = [/^@vercube\//, ...[
18
+ /^srvx/,
19
+ "rou3",
20
+ "c12",
21
+ "defu",
22
+ "pathe",
23
+ "evlog",
24
+ /^evlog\//,
25
+ "@standard-schema/spec"
26
+ ]];
27
+ /** Vercube packages that must resolve to a single module id (class-reference DI tokens). */
28
+ const VERCUBE_PACKAGES = [
29
+ "@vercube/core",
30
+ "@vercube/di",
31
+ "@vercube/auth",
32
+ "@vercube/logger",
33
+ "@vercube/schema"
34
+ ];
35
+ /**
36
+ * Canonical entry paths for `@vercube/*` packages from the consuming app's root.
37
+ * Prevents the production bundler from inlining duplicate copies when workspace
38
+ * links and `node_modules` paths both appear in the graph.
18
39
  */
19
- const DEV_NO_EXTERNAL = [/^@vercube\//];
40
+ function createVercubeResolveAliases(root) {
41
+ const require = createRequire(resolve(root, "package.json"));
42
+ const aliases = {};
43
+ for (const name of VERCUBE_PACKAGES) try {
44
+ aliases[name] = require.resolve(name);
45
+ } catch {}
46
+ return aliases;
47
+ }
20
48
  /**
21
- * Resolves the full dev `noExternal` list for the Vercube environment by
22
- * merging {@link DEV_NO_EXTERNAL} with optional plugin patterns.
49
+ * Merges built-in {@link DEFAULT_NO_EXTERNAL} with optional plugin patterns.
23
50
  *
24
51
  * @param extra - Additional patterns from {@link VercubePluginConfig.noExternal}.
25
- * @returns Patterns for `resolve.noExternal` in dev.
52
+ * @returns Patterns for dev `resolve.noExternal` and production bundling.
26
53
  */
27
- function resolveDevNoExternal(extra) {
28
- if (!extra?.length) return DEV_NO_EXTERNAL;
29
- return [...DEV_NO_EXTERNAL, ...extra];
54
+ function resolveNoExternalPatterns(extra) {
55
+ if (!extra?.length) return DEFAULT_NO_EXTERNAL;
56
+ return [...DEFAULT_NO_EXTERNAL, ...extra];
30
57
  }
31
58
  /**
32
59
  * The custom message event used to tell the worker which entry an environment loads.
@@ -43,9 +70,10 @@ const VITE_ENV_EVENT = "vercube:vite-env";
43
70
  * @returns The environment options for the `vercube` environment.
44
71
  */
45
72
  function createVercubeEnvironment(ctx) {
73
+ const noExternal = resolveNoExternalPatterns(ctx.pluginConfig.noExternal);
46
74
  return {
47
75
  consumer: "server",
48
- resolve: ctx.dev ? { noExternal: resolveDevNoExternal(ctx.pluginConfig.noExternal) } : {},
76
+ resolve: ctx.dev ? { noExternal } : {},
49
77
  build: {
50
78
  outDir: resolve(ctx.root, "dist"),
51
79
  emptyOutDir: false,
@@ -61,7 +89,7 @@ function createVercubeEnvironment(ctx) {
61
89
  }
62
90
  },
63
91
  dev: { createEnvironment: async (envName, envConfig) => {
64
- const { createFetchableDevEnvironment } = await import("./dev-Bl0akm-q.mjs");
92
+ const { createFetchableDevEnvironment } = await import("./dev-CKXS10LI.mjs");
65
93
  const env = createFetchableDevEnvironment(envName, envConfig, getEnvRunner(ctx), ctx.serverEntry);
66
94
  (ctx._viteEnvs ??= /* @__PURE__ */ new Map()).set(envName, ctx.serverEntry);
67
95
  return env;
@@ -153,7 +181,8 @@ async function loadRunnerInto(ctx, manager) {
153
181
  * @returns The array of Vite plugins composing the integration.
154
182
  */
155
183
  function vercube(pluginConfig = {}) {
156
- return [vercubeMain(createContext(pluginConfig))];
184
+ const ctx = createContext(pluginConfig);
185
+ return [vercubeMain(ctx), vercubeResolveDedupe(ctx)];
157
186
  }
158
187
  /**
159
188
  * The primary sub-plugin: defines the Vercube environment, warms the dev worker,
@@ -176,6 +205,7 @@ function vercubeMain(ctx) {
176
205
  }
177
206
  return {
178
207
  builder: { sharedConfigBuild: true },
208
+ resolve: { alias: createVercubeResolveAliases(ctx.root) },
179
209
  environments: {
180
210
  client: { build: { outDir: resolve(ctx.root, CLIENT_OUT_DIR) } },
181
211
  [VERCUBE_ENV]: createVercubeEnvironment(ctx)
@@ -190,8 +220,8 @@ function vercubeMain(ctx) {
190
220
  }
191
221
  },
192
222
  configureServer: async (server) => {
193
- const { configureViteDevServer } = await import("./dev-Bl0akm-q.mjs");
194
- return configureViteDevServer(ctx, server);
223
+ const { configureViteDevServer } = await import("./dev-CKXS10LI.mjs");
224
+ await configureViteDevServer(ctx, server);
195
225
  },
196
226
  hotUpdate() {
197
227
  if (this.environment.name !== "vercube") return;
@@ -200,5 +230,22 @@ function vercubeMain(ctx) {
200
230
  }
201
231
  };
202
232
  }
233
+ /**
234
+ * Forces every `@vercube/*` import in the server environment to the same resolved
235
+ * entry path so class-reference DI tokens are not duplicated in production bundles.
236
+ */
237
+ function vercubeResolveDedupe(ctx) {
238
+ return {
239
+ name: "vercube:resolve-dedupe",
240
+ apply: "build",
241
+ resolveId: {
242
+ filter: { id: /^@vercube\// },
243
+ handler(id) {
244
+ if (this.environment.name !== "vercube") return;
245
+ return createVercubeResolveAliases(ctx.root)[id] ?? null;
246
+ }
247
+ }
248
+ };
249
+ }
203
250
  //#endregion
204
251
  export { VERCUBE_ENV, vercube };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vercube/vite",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "Vite plugin integrating Vercube with the Vite Environment API",
5
5
  "repository": {
6
6
  "type": "git",
@@ -36,7 +36,7 @@
36
36
  "pathe": "2.0.3",
37
37
  "rou3": "0.8.1",
38
38
  "srvx": "0.11.17",
39
- "@vercube/scan": "1.1.2"
39
+ "@vercube/scan": "1.1.4"
40
40
  },
41
41
  "peerDependencies": {
42
42
  "vite": "^8.0.0"