@lunora/rspack 1.0.0-alpha.4 → 1.0.0-alpha.6

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
@@ -68,6 +68,8 @@ export default defineConfig({
68
68
 
69
69
  `rsbuild dev` now starts the client dev server **and** the Lunora Worker, and routes `/_lunora/*` to it. Nothing else to wire — no hand-written proxy, no second terminal, no `wrangler.dev.jsonc`. One entry registers codegen, binding provisioning, wrangler validation, the Worker, and the proxy.
70
70
 
71
+ To start from a working app, `lunora init -t rspack-react` scaffolds a React SPA and `lunora init -t tanstack-start-react-rspack` a TanStack Start app that deploys as one Worker.
72
+
71
73
  On bare Rspack (or webpack 5), the plugin half works on its own — codegen and config only, no Worker:
72
74
 
73
75
  ```js
@@ -90,6 +92,7 @@ lunoraRsbuild({
90
92
  validateWrangler: true, // set false to skip the wrangler.jsonc check
91
93
 
92
94
  // Rsbuild only:
95
+ studio: true, // Lunora Studio at /__lunora (needs @lunora/studio)
93
96
  worker: true, // false to run the Worker yourself (the proxy is still injected)
94
97
  workerPort: 8787, // defaults to the wrangler config's `dev.port`, then 8787
95
98
  wranglerArgs: [], // extra arguments appended to `wrangler dev`
@@ -110,7 +113,7 @@ So `lunoraRsbuild` spawns `wrangler dev` and proxies to it. From your seat that
110
113
  | Binding / cron / compatibility-date sync | ✅ | — |
111
114
  | `.dev.vars` scaffolding + `WORKER_ENV` | ✅ (watch mode) | — |
112
115
  | Worker running on `dev` | ✅ (Rsbuild) | — |
113
- | Studio at `/_lunora/__studio` | ✅ (via proxy) | — |
116
+ | Studio at `/__lunora` | ✅ (Rsbuild) | — |
114
117
  | Browser error overlay | ❌ | `@lunora/vite` |
115
118
  | Per-module HMR inside the Worker | ❌ (restarts) | `@lunora/vite` |
116
119
  | Remote-binding dev (`LUNORA_REMOTE`) | ❌ | `lunora dev` |
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { R as ResolvedLunoraRspackOptions, L as LunoraRspackOptions } from "./packem_shared/types.d-CcVYk-6N.mjs";
1
+ import { R as ResolvedLunoraRspackOptions, L as LunoraRspackOptions } from "./packem_shared/types.d-DcuVeaxB.mjs";
2
2
  import '@lunora/codegen';
3
3
  /**
4
4
  * The slice of the Rspack compiler this plugin touches, projected structurally.
@@ -93,6 +93,17 @@ declare class LunoraRspackPlugin {
93
93
  * Idempotent: the pass below sees `#started` and skips its own call.
94
94
  */
95
95
  prepareDevSession(): Promise<void>;
96
+ /**
97
+ * Run a watch-mode codegen pass now, ahead of the first compilation.
98
+ *
99
+ * Public for the same ordering reason as {@link prepareDevSession}: the
100
+ * Worker `@lunora/rspack/rsbuild` spawns is bundled by `wrangler dev` at
101
+ * startup, and it imports `_generated/app` — which, on a fresh clone, only
102
+ * this pass writes. Spawned first, wrangler dies on an unresolvable import
103
+ * before the first compilation ever generates it. The first compilation then
104
+ * finds the fingerprint unchanged and skips its own pass.
105
+ */
106
+ generateForDev(): Promise<void>;
96
107
  /** Rspack's plugin entry point. */
97
108
  apply(compiler: CompilerLike): void;
98
109
  }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { R as ResolvedLunoraRspackOptions, L as LunoraRspackOptions } from "./packem_shared/types.d-CcVYk-6N.js";
1
+ import { R as ResolvedLunoraRspackOptions, L as LunoraRspackOptions } from "./packem_shared/types.d-DcuVeaxB.js";
2
2
  import '@lunora/codegen';
3
3
  /**
4
4
  * The slice of the Rspack compiler this plugin touches, projected structurally.
@@ -93,6 +93,17 @@ declare class LunoraRspackPlugin {
93
93
  * Idempotent: the pass below sees `#started` and skips its own call.
94
94
  */
95
95
  prepareDevSession(): Promise<void>;
96
+ /**
97
+ * Run a watch-mode codegen pass now, ahead of the first compilation.
98
+ *
99
+ * Public for the same ordering reason as {@link prepareDevSession}: the
100
+ * Worker `@lunora/rspack/rsbuild` spawns is bundled by `wrangler dev` at
101
+ * startup, and it imports `_generated/app` — which, on a fresh clone, only
102
+ * this pass writes. Spawned first, wrangler dies on an unresolvable import
103
+ * before the first compilation ever generates it. The first compilation then
104
+ * finds the fingerprint unchanged and skips its own pass.
105
+ */
106
+ generateForDev(): Promise<void>;
96
107
  /** Rspack's plugin entry point. */
97
108
  apply(compiler: CompilerLike): void;
98
109
  }
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{createRequire as r}from"node:module";import{r as e}from"./packem_shared/options-rWtSt-oe.mjs";import{LunoraRspackPlugin as t}from"./packem_shared/LunoraRspackPlugin-BM3r07nu.mjs";const s=o=>new t(e(o)),i=r(import.meta.url)("../package.json").version;export{t as LunoraRspackPlugin,i as VERSION,s as lunoraRspack};
1
+ import{createRequire as r}from"node:module";import{r as e}from"./packem_shared/options-rWtSt-oe.mjs";import{LunoraRspackPlugin as t}from"./packem_shared/LunoraRspackPlugin-DPrjGG-k.mjs";const s=o=>new t(e(o)),i=r(import.meta.url)("../package.json").version;export{t as LunoraRspackPlugin,i as VERSION,s as lunoraRspack};
@@ -0,0 +1,3 @@
1
+ import{createHash as g}from"node:crypto";import{existsSync as u,readFileSync as p}from"node:fs";import{join as m,resolve as v}from"node:path";import{createCodegenProject as w,refreshCodegenProject as R,runCodegen as j,findTsconfig as a,fingerprintSchemaSources as S}from"@lunora/codegen";import{LUNORA_TAG as y,reportCodegenFindings as E,blockingFindingsMessage as D,isCodegenDisabled as P,CODEGEN_ENV as c,lunoraLine as n,runPostCodegenHook as b,ensureDevVariables as k,createConfirm as C,fillDevSecrets as F,ensureDevWorkerEnv as A,detectAgentRules as L,claimAgentRulesHint as T,AGENT_RULES_HINT as N}from"@lunora/config";import{collectWranglerSecretVariables as _,reconcileWranglerExtras as W,findWranglerFile as h,reconcileBindingsSafely as x,assertWranglerSatisfiesSchema as M}from"@lunora/config/cloudflare";const G=(t,e,r)=>{const i=m(t.projectRoot,t.schemaDir,"schema.ts");if(!u(i)){e.warn(`${y} no schema found at ${i} — run \`lunora init\` or create it, then rebuild.`);return}const s=j({apiSpec:t.apiSpec,lunoraDirectory:t.schemaDir,project:r,projectRoot:t.projectRoot,target:t.target,wranglerVariables:_(t.projectRoot)});return W(t.projectRoot,s.cronTriggers,e),E(s,e),D(s)},H=t=>{let e;return{drop:()=>{e=void 0},get:()=>(e===void 0?e=w(t):R(e,t),e)}},d="LunoraRspackPlugin",I=2,l=t=>{try{return p(t,"utf8")}catch{return""}},o={error:t=>{console.error(t)},info:t=>{console.info(t)},warn:t=>{console.warn(t)}},f=async t=>{const e=r=>{o.info(n(r))};await k({confirm:C("[lunora] "),cwd:t.projectRoot,info:e}),F({cwd:t.projectRoot,info:e}),A(t.projectRoot,e),!L(t.projectRoot).installed&&T()&&o.warn(`
2
+ ${n(N)}
3
+ `)};class q{#e;#r;#c;#o;#i=!1;#n;#h;#s=[];#t;#a;constructor(e){this.#e=e,this.#r=v(e.projectRoot,e.schemaDir),this.#c=P(process.env[c]),this.#o=H(this.#r)}async prepareDevSession(){this.#i||(this.#i=!0,await f(this.#e))}async generateForDev(){await this.#d(!0)}apply(e){e.hooks.beforeCompile.tapPromise(d,async()=>{await this.#d(e.watchMode===!0)}),e.hooks.afterCompile.tapPromise(d,r=>{r.contextDependencies.add(this.#r);for(const i of[a(this.#r),h(this.#e.projectRoot)])i!==void 0&&r.fileDependencies.add(i);for(const i of this.#s)r.errors.push(i);return Promise.resolve()})}async#d(e){this.#a??=this.#f(e).finally(()=>{this.#a=void 0}),await this.#a}async#f(e){if(e&&this.#c){this.#i||(this.#i=!0,o.info(n(`codegen disabled via ${c} — skipping generation and the wrangler checks.`)));return}const r=this.#m();r!==this.#h&&(this.#o.drop(),this.#h=r,this.#n=void 0);const i=this.#u(r);if(!this.#g(i)){this.#s=[];try{!this.#i&&e&&await f(this.#e),this.#i=!0,await this.#p(e),this.#s.length===0?(this.#n=i,this.#t=void 0):this.#l(i)}catch(s){this.#o.drop(),this.#n=void 0,this.#s.push(s instanceof Error?s:new Error(String(s))),this.#l(i),o.error(s instanceof Error?s.message:String(s))}}}#g(e){return e===this.#n?!0:this.#t!==void 0&&this.#t.fingerprint===e&&this.#t.attempts>=I}#l(e){this.#t=this.#t?.fingerprint===e?{attempts:this.#t.attempts+1,fingerprint:e}:{attempts:1,fingerprint:e}}#u(e){const r=h(this.#e.projectRoot);return[S(this.#r),e,r===void 0?"":l(r)].map(s=>g("sha256").update(s).digest("hex")).join("")}async#p(e){await x(this.#e,o),this.#e.validateWrangler&&M(this.#e,o.warn,"Update your wrangler.jsonc and rebuild.");const r=G(this.#e,o,this.#o.get()),i=await b({cwd:this.#e.projectRoot,logger:o});i.error!==void 0&&this.#s.push(new Error(i.error)),!e&&r!==void 0&&this.#s.push(new Error(r))}#m(){const e=a(this.#r);return e===void 0?"":l(e)}}export{q as LunoraRspackPlugin,d as PLUGIN_NAME};
@@ -3,9 +3,10 @@ import { CodegenOptions } from '@lunora/codegen';
3
3
  * Options for the `lunoraRspack()` plugin factory.
4
4
  *
5
5
  * Deliberately a subset of `@lunora/vite`'s. Everything absent here is
6
- * Vite-dev-server machinery with no Rspack counterpart (`overlay`, `studio`,
6
+ * Vite-dev-server machinery with no Rspack counterpart (`overlay`,
7
7
  * `cloudflare`, `shard`) — see the package README for which of those the
8
- * wrangler side of the stack covers instead.
8
+ * wrangler side of the stack covers instead. `studio` lives on the Rsbuild
9
+ * options, since it needs a dev server to mount on.
9
10
  */
10
11
  interface LunoraRspackOptions {
11
12
  /**
@@ -3,9 +3,10 @@ import { CodegenOptions } from '@lunora/codegen';
3
3
  * Options for the `lunoraRspack()` plugin factory.
4
4
  *
5
5
  * Deliberately a subset of `@lunora/vite`'s. Everything absent here is
6
- * Vite-dev-server machinery with no Rspack counterpart (`overlay`, `studio`,
6
+ * Vite-dev-server machinery with no Rspack counterpart (`overlay`,
7
7
  * `cloudflare`, `shard`) — see the package README for which of those the
8
- * wrangler side of the stack covers instead.
8
+ * wrangler side of the stack covers instead. `studio` lives on the Rsbuild
9
+ * options, since it needs a dev server to mount on.
9
10
  */
10
11
  interface LunoraRspackOptions {
11
12
  /**
@@ -1,10 +1,19 @@
1
- import { L as LunoraRspackOptions } from "./packem_shared/types.d-CcVYk-6N.mjs";
1
+ import { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { L as LunoraRspackOptions } from "./packem_shared/types.d-DcuVeaxB.mjs";
2
3
  import '@lunora/codegen';
3
4
  /** Path prefix the Lunora Worker serves — RPC, the WebSocket, and the studio. */
4
5
  declare const LUNORA_PATH = "/_lunora";
5
6
  /** Name Rsbuild lists this plugin under. */
6
7
  declare const RSBUILD_PLUGIN_NAME = "lunora:rsbuild";
7
8
  interface LunoraRsbuildOptions extends LunoraRspackOptions {
9
+ /**
10
+ * Serve Lunora Studio at `/__lunora` on the dev server — the same URL, and
11
+ * the same app, as `@lunora/vite`. Needs `@lunora/studio` installed.
12
+ * `false` opts out.
13
+ *
14
+ * Defaults to `true`.
15
+ */
16
+ studio?: boolean;
8
17
  /**
9
18
  * Run `wrangler dev` alongside the dev server and proxy {@link LUNORA_PATH}
10
19
  * to it. `false` opts out — for a project that starts the Worker itself, or
@@ -27,11 +36,23 @@ interface LunoraRsbuildOptions extends LunoraRspackOptions {
27
36
  * OPTIONAL peer, so its types must not reach this package's published `.d.ts`.
28
37
  */
29
38
  interface RsbuildApiLike {
39
+ getRsbuildConfig: () => Readonly<RsbuildConfigLike>;
30
40
  modifyRsbuildConfig: (callback: (config: RsbuildConfigLike) => RsbuildConfigLike) => void;
31
41
  modifyRspackConfig: (callback: (config: RspackConfigLike) => RspackConfigLike) => void;
32
- onBeforeStartDevServer: (callback: () => Promise<void> | void) => void;
42
+ onAfterStartDevServer: (callback: (params: {
43
+ port: number;
44
+ }) => Promise<void> | void) => void;
45
+ onBeforeStartDevServer: (callback: (params: {
46
+ server: DevServerLike;
47
+ }) => Promise<void> | void) => void;
33
48
  onCloseDevServer: (callback: () => Promise<void> | void) => void;
34
49
  }
50
+ /** The slice of Rsbuild's dev server this uses: its Connect middleware stack. */
51
+ interface DevServerLike {
52
+ middlewares: {
53
+ use: (handler: (request: IncomingMessage, response: ServerResponse, next: () => void) => void) => unknown;
54
+ };
55
+ }
35
56
  /** A single entry of Rsbuild's array-form `server.proxy`. */
36
57
  type ProxyArrayEntry = Record<string, unknown> & {
37
58
  pathFilter?: unknown;
@@ -50,6 +71,8 @@ interface RspackConfigLike {
50
71
  /** The slice of Rsbuild's config this plugin writes. */
51
72
  interface RsbuildConfigLike {
52
73
  server?: {
74
+ base?: string;
75
+ host?: string;
53
76
  proxy?: ProxyConfigLike;
54
77
  };
55
78
  }
@@ -96,4 +119,4 @@ declare const withLunoraProxy: (existing: ProxyConfigLike | undefined, port: num
96
119
  * misconfigured by hand.
97
120
  */
98
121
  declare const lunoraRsbuild: (options?: LunoraRsbuildOptions) => RsbuildPluginLike;
99
- export { LUNORA_PATH, type LunoraRsbuildOptions, type ProxyConfigLike, RSBUILD_PLUGIN_NAME, type RsbuildApiLike, type RsbuildConfigLike, type RsbuildPluginLike, type RspackConfigLike, lunoraRsbuild, withLunoraProxy };
122
+ export { type DevServerLike, LUNORA_PATH, type LunoraRsbuildOptions, type ProxyConfigLike, RSBUILD_PLUGIN_NAME, type RsbuildApiLike, type RsbuildConfigLike, type RsbuildPluginLike, type RspackConfigLike, lunoraRsbuild, withLunoraProxy };
package/dist/rsbuild.d.ts CHANGED
@@ -1,10 +1,19 @@
1
- import { L as LunoraRspackOptions } from "./packem_shared/types.d-CcVYk-6N.js";
1
+ import { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { L as LunoraRspackOptions } from "./packem_shared/types.d-DcuVeaxB.js";
2
3
  import '@lunora/codegen';
3
4
  /** Path prefix the Lunora Worker serves — RPC, the WebSocket, and the studio. */
4
5
  declare const LUNORA_PATH = "/_lunora";
5
6
  /** Name Rsbuild lists this plugin under. */
6
7
  declare const RSBUILD_PLUGIN_NAME = "lunora:rsbuild";
7
8
  interface LunoraRsbuildOptions extends LunoraRspackOptions {
9
+ /**
10
+ * Serve Lunora Studio at `/__lunora` on the dev server — the same URL, and
11
+ * the same app, as `@lunora/vite`. Needs `@lunora/studio` installed.
12
+ * `false` opts out.
13
+ *
14
+ * Defaults to `true`.
15
+ */
16
+ studio?: boolean;
8
17
  /**
9
18
  * Run `wrangler dev` alongside the dev server and proxy {@link LUNORA_PATH}
10
19
  * to it. `false` opts out — for a project that starts the Worker itself, or
@@ -27,11 +36,23 @@ interface LunoraRsbuildOptions extends LunoraRspackOptions {
27
36
  * OPTIONAL peer, so its types must not reach this package's published `.d.ts`.
28
37
  */
29
38
  interface RsbuildApiLike {
39
+ getRsbuildConfig: () => Readonly<RsbuildConfigLike>;
30
40
  modifyRsbuildConfig: (callback: (config: RsbuildConfigLike) => RsbuildConfigLike) => void;
31
41
  modifyRspackConfig: (callback: (config: RspackConfigLike) => RspackConfigLike) => void;
32
- onBeforeStartDevServer: (callback: () => Promise<void> | void) => void;
42
+ onAfterStartDevServer: (callback: (params: {
43
+ port: number;
44
+ }) => Promise<void> | void) => void;
45
+ onBeforeStartDevServer: (callback: (params: {
46
+ server: DevServerLike;
47
+ }) => Promise<void> | void) => void;
33
48
  onCloseDevServer: (callback: () => Promise<void> | void) => void;
34
49
  }
50
+ /** The slice of Rsbuild's dev server this uses: its Connect middleware stack. */
51
+ interface DevServerLike {
52
+ middlewares: {
53
+ use: (handler: (request: IncomingMessage, response: ServerResponse, next: () => void) => void) => unknown;
54
+ };
55
+ }
35
56
  /** A single entry of Rsbuild's array-form `server.proxy`. */
36
57
  type ProxyArrayEntry = Record<string, unknown> & {
37
58
  pathFilter?: unknown;
@@ -50,6 +71,8 @@ interface RspackConfigLike {
50
71
  /** The slice of Rsbuild's config this plugin writes. */
51
72
  interface RsbuildConfigLike {
52
73
  server?: {
74
+ base?: string;
75
+ host?: string;
53
76
  proxy?: ProxyConfigLike;
54
77
  };
55
78
  }
@@ -96,4 +119,4 @@ declare const withLunoraProxy: (existing: ProxyConfigLike | undefined, port: num
96
119
  * misconfigured by hand.
97
120
  */
98
121
  declare const lunoraRsbuild: (options?: LunoraRsbuildOptions) => RsbuildPluginLike;
99
- export { LUNORA_PATH, type LunoraRsbuildOptions, type ProxyConfigLike, RSBUILD_PLUGIN_NAME, type RsbuildApiLike, type RsbuildConfigLike, type RsbuildPluginLike, type RspackConfigLike, lunoraRsbuild, withLunoraProxy };
122
+ export { type DevServerLike, LUNORA_PATH, type LunoraRsbuildOptions, type ProxyConfigLike, RSBUILD_PLUGIN_NAME, type RsbuildApiLike, type RsbuildConfigLike, type RsbuildPluginLike, type RspackConfigLike, lunoraRsbuild, withLunoraProxy };
package/dist/rsbuild.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{formatLunoraEvent as h,lunoraLine as g}from"@lunora/config";import{r as m}from"./packem_shared/options-rWtSt-oe.mjs";import{LunoraRspackPlugin as S}from"./packem_shared/LunoraRspackPlugin-BM3r07nu.mjs";import{spawn as k}from"node:child_process";import{connect as E}from"node:net";import{findWranglerFile as y,readWranglerJsonc as L}from"@lunora/config/cloudflare";const R=8787,c=3e4,A=150,P=5e3,x=(r,t)=>{if(t!==void 0)return t;const o=y(r);if(o!==void 0){const{parsed:e}=L(o);if(typeof e?.dev?.port=="number")return e.dev.port}return R},w=async r=>new Promise(t=>{const o=E({host:"127.0.0.1",port:r}),e=s=>{o.destroy(),t(s)};o.once("connect",()=>{e(!0)}),o.once("error",()=>{e(!1)})}),d=r=>{const t=h(r);if(t===void 0){r.trim()!==""&&console.log(r);return}const o=g(t.text);t.level==="error"?console.error(o):t.level==="warn"?console.warn(o):console.info(o)},u=r=>{let t="";r?.setEncoding("utf8"),r?.on("data",o=>{t+=o;const e=t.split(`
2
- `);t=e.pop()??"";for(const s of e)d(s)}),r?.once("end",()=>{t!==""&&(d(t),t="")})},O=async r=>{if(await w(r.port))throw new Error(`could not start the worker: port ${String(r.port)} is already in use. Stop whatever is serving there (another \`rsbuild dev\` or \`lunora dev\`?), or set \`workerPort\`.`);const t=["dev","--port",String(r.port),"--var","WORKER_ENV:development",...r.wranglerArgs??[]],o=process.platform==="win32",e=k(o?"wrangler.cmd":"wrangler",t,{cwd:r.projectRoot,shell:o,stdio:["ignore","pipe","pipe"]});u(e.stdout),u(e.stderr);let s;e.once("error",i=>{s=i.code==="ENOENT"?new Error("could not start the worker: `wrangler` was not found on PATH. Add it to the project (`npm install -D wrangler`), or pass `worker: false` to run the worker yourself."):i});const a=()=>{e.kill("SIGKILL")};process.once("exit",a);const l=async()=>{process.removeListener("exit",a),!(e.exitCode!==null||e.signalCode!==null)&&await new Promise(i=>{const v=setTimeout(()=>{e.kill("SIGKILL")},P);e.once("exit",()=>{clearTimeout(v),i()}),e.kill("SIGTERM")})};let n;e.once("exit",i=>{n??=new Error(`wrangler dev exited with code ${String(i)} before it was ready`)});const f=Date.now()+c;for(;Date.now()<f;){if(s!==void 0)throw s;if(n!==void 0)throw n;if(await w(r.port)){if(e.exitCode!==null||e.signalCode!==null)throw await l(),new Error(`the worker on port ${String(r.port)} is not the one this plugin started — it exited while something else took the port.`);return{port:r.port,stop:l}}await new Promise(i=>{setTimeout(i,A)})}throw await l(),new Error(`wrangler dev did not start listening on port ${String(r.port)} within ${String(c/1e3)}s`)},p="/_lunora",T="lunora:rsbuild",_=(r,t)=>{const o=`http://127.0.0.1:${String(t)}`;return Array.isArray(r)?[...r,{changeOrigin:!0,pathFilter:p,target:o,ws:!0}]:{[p]:{changeOrigin:!0,target:o,ws:!0},...r}},$=r=>{const t=m(r),o=r?.worker!==!1,e=x(t.projectRoot,r?.workerPort),s=new S(t);return{name:T,setup:a=>{if(a.modifyRspackConfig(n=>({...n,plugins:[...Array.isArray(n.plugins)?n.plugins:[],s]})),a.modifyRsbuildConfig(n=>({...n,server:{...n.server,proxy:_(n.server?.proxy,e)}})),!o)return;let l;a.onBeforeStartDevServer(async()=>{await s.prepareDevSession(),console.info(g(`starting the worker on http://127.0.0.1:${String(e)} …`)),l=await O({port:e,projectRoot:t.projectRoot,wranglerArgs:r?.wranglerArgs})}),a.onCloseDevServer(async()=>{await l?.stop(),l=void 0})}}};export{p as LUNORA_PATH,T as RSBUILD_PLUGIN_NAME,$ as lunoraRsbuild,_ as withLunoraProxy};
1
+ import{formatLunoraEvent as y,lunoraLine as l}from"@lunora/config";import{createStudioMiddleware as k,isNonLoopbackHost as A,studioMountPath as R}from"@lunora/config/studio-host";import{r as E}from"./packem_shared/options-rWtSt-oe.mjs";import{LunoraRspackPlugin as L}from"./packem_shared/LunoraRspackPlugin-DPrjGG-k.mjs";import{spawn as x}from"node:child_process";import{existsSync as D,mkdirSync as P}from"node:fs";import{connect as T}from"node:net";import{resolve as v,dirname as b}from"node:path";import{findWranglerFile as O,readWranglerJsonc as _}from"@lunora/config/cloudflare";const C=8787,d=3e4,W=150,$=5e3,u=(e,r,t)=>{let o;for(const[n,s]of e.entries())s.startsWith(`${r}=`)?o=s.slice(r.length+1):(s===r||s===t)&&n+1<e.length&&(o=e[n+1]);return o},h=(e,r=[])=>{const t=u(r,"--config","-c"),o=t===void 0?O(e):v(e,t);if(o===void 0||!D(o))return;const{parsed:n}=_(o);return n===void 0?void 0:{config:n,env:u(r,"--env","-e"),path:o}},I=(e,r,t)=>{if(r!==void 0)return r;const o=h(e,t)?.config.dev?.port;return typeof o=="number"?o:C},N=(e,r)=>{const t=h(e,r);if(t===void 0)return;const n=((t.env===void 0?void 0:t.config.env?.[t.env]?.assets)??t.config.assets)?.directory;typeof n=="string"&&P(v(b(t.path),n),{recursive:!0})},w=async e=>new Promise(r=>{const t=T({host:"127.0.0.1",port:e}),o=n=>{t.destroy(),r(n)};t.once("connect",()=>{o(!0)}),t.once("error",()=>{o(!1)})}),f=e=>{const r=y(e);if(r===void 0){e.trim()!==""&&console.log(e);return}const t=l(r.text);r.level==="error"?console.error(t):r.level==="warn"?console.warn(t):console.info(t)},p=e=>{let r="";e?.setEncoding("utf8"),e?.on("data",t=>{r+=t;const o=r.split(`
2
+ `);r=o.pop()??"";for(const n of o)f(n)}),e?.once("end",()=>{r!==""&&(f(r),r="")})},M=async e=>{if(await w(e.port))throw new Error(`could not start the worker: port ${String(e.port)} is already in use. Stop whatever is serving there (another \`rsbuild dev\` or \`lunora dev\`?), or set \`workerPort\`.`);N(e.projectRoot,e.wranglerArgs);const r=["dev","--port",String(e.port),"--var","WORKER_ENV:development",...e.wranglerArgs??[]],t=process.platform==="win32",o=x(t?"wrangler.cmd":"wrangler",r,{cwd:e.projectRoot,shell:t,stdio:["ignore","pipe","pipe"]});p(o.stdout),p(o.stderr);let n;o.once("error",a=>{n=a.code==="ENOENT"?new Error("could not start the worker: `wrangler` was not found on PATH. Add it to the project (`npm install -D wrangler`), or pass `worker: false` to run the worker yourself."):a});const s=()=>{o.kill("SIGKILL")};process.once("exit",s);const i=async()=>{process.removeListener("exit",s),!(o.exitCode!==null||o.signalCode!==null)&&await new Promise(a=>{const S=setTimeout(()=>{o.kill("SIGKILL")},$);o.once("exit",()=>{clearTimeout(S),a()}),o.kill("SIGTERM")})};let c;o.once("exit",a=>{c??=new Error(`wrangler dev exited with code ${String(a)} before it was ready`)});const m=Date.now()+d;for(;Date.now()<m;){if(n!==void 0)throw n;if(c!==void 0)throw c;if(await w(e.port)){if(o.exitCode!==null||o.signalCode!==null)throw await i(),new Error(`the worker on port ${String(e.port)} is not the one this plugin started — it exited while something else took the port.`);return{port:e.port,stop:i}}await new Promise(a=>{setTimeout(a,W)})}throw await i(),new Error(`wrangler dev did not start listening on port ${String(e.port)} within ${String(d/1e3)}s`)},g="/_lunora",j="lunora:rsbuild",F=(e,r)=>{const t=`http://127.0.0.1:${String(r)}`;return Array.isArray(e)?[...e,{changeOrigin:!0,pathFilter:g,target:t,ws:!0}]:{[g]:{changeOrigin:!0,target:t,ws:!0},...e}},U=(e,r)=>{e.onBeforeStartDevServer(({server:t})=>{const{base:o,host:n}=e.getRsbuildConfig().server??{};t.middlewares.use(k({apiSpec:r.apiSpec,base:o,isNonLoopbackBind:A(n),projectRoot:r.projectRoot,schemaDirectory:r.schemaDir}))}),e.onAfterStartDevServer(({port:t})=>{console.info(l(`studio on http://localhost:${String(t)}${R(e.getRsbuildConfig().server?.base)}`))})},Q=e=>{const r=E(e),t=I(r.projectRoot,e?.workerPort,e?.wranglerArgs),o=new L(r);return{name:j,setup:n=>{if(n.modifyRspackConfig(i=>({...i,plugins:[...Array.isArray(i.plugins)?i.plugins:[],o]})),n.modifyRsbuildConfig(i=>({...i,server:{...i.server,proxy:F(i.server?.proxy,t)}})),e?.studio!==!1&&U(n,r),e?.worker===!1)return;let s;n.onBeforeStartDevServer(async()=>{await o.prepareDevSession(),await o.generateForDev(),console.info(l(`starting the worker on http://127.0.0.1:${String(t)} …`)),s=await M({port:t,projectRoot:r.projectRoot,wranglerArgs:e?.wranglerArgs})}),n.onCloseDevServer(async()=>{await s?.stop(),s=void 0})}}};export{g as LUNORA_PATH,j as RSBUILD_PLUGIN_NAME,Q as lunoraRsbuild,F as withLunoraProxy};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/rspack",
3
- "version": "1.0.0-alpha.4",
3
+ "version": "1.0.0-alpha.6",
4
4
  "description": "The Lunora Rspack plugin: codegen, binding provisioning, and wrangler validation for Rspack projects",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -51,14 +51,18 @@
51
51
  "access": "public"
52
52
  },
53
53
  "dependencies": {
54
- "@lunora/codegen": "1.0.0-alpha.238",
55
- "@lunora/config": "1.0.0-alpha.283"
54
+ "@lunora/codegen": "1.0.0-alpha.240",
55
+ "@lunora/config": "1.0.0-alpha.285"
56
56
  },
57
57
  "peerDependencies": {
58
+ "@lunora/studio": ">=1.0.0-alpha.50 <2.0.0-0",
58
59
  "@rsbuild/core": "^2.0.0",
59
60
  "@rspack/core": "^1.5.0 || ^2.0.0"
60
61
  },
61
62
  "peerDependenciesMeta": {
63
+ "@lunora/studio": {
64
+ "optional": true
65
+ },
62
66
  "@rsbuild/core": {
63
67
  "optional": true
64
68
  },
@@ -1,3 +0,0 @@
1
- import{createHash as g}from"node:crypto";import{existsSync as p,readFileSync as u}from"node:fs";import{join as m,resolve as v}from"node:path";import{createCodegenProject as R,refreshCodegenProject as j,runCodegen as w,findTsconfig as a,fingerprintSchemaSources as S}from"@lunora/codegen";import{LUNORA_TAG as E,reportCodegenFindings as y,blockingFindingsMessage as D,isCodegenDisabled as P,CODEGEN_ENV as c,lunoraLine as n,runPostCodegenHook as b,ensureDevVariables as k,createConfirm as C,fillDevSecrets as F,ensureDevWorkerEnv as A,detectAgentRules as L,claimAgentRulesHint as T,AGENT_RULES_HINT as N}from"@lunora/config";import{collectWranglerSecretVariables as _,reconcileWranglerExtras as W,findWranglerFile as h,reconcileBindingsSafely as x,assertWranglerSatisfiesSchema as M}from"@lunora/config/cloudflare";const G=(t,e,r)=>{const i=m(t.projectRoot,t.schemaDir,"schema.ts");if(!p(i)){e.warn(`${E} no schema found at ${i} — run \`lunora init\` or create it, then rebuild.`);return}const s=w({apiSpec:t.apiSpec,lunoraDirectory:t.schemaDir,project:r,projectRoot:t.projectRoot,target:t.target,wranglerVariables:_(t.projectRoot)});return W(t.projectRoot,s.cronTriggers,e),y(s,e),D(s)},H=t=>{let e;return{drop:()=>{e=void 0},get:()=>(e===void 0?e=R(t):j(e,t),e)}},d="LunoraRspackPlugin",I=2,l=t=>{try{return u(t,"utf8")}catch{return""}},o={error:t=>{console.error(t)},info:t=>{console.info(t)},warn:t=>{console.warn(t)}},f=async t=>{const e=r=>{o.info(n(r))};await k({confirm:C("[lunora] "),cwd:t.projectRoot,info:e}),F({cwd:t.projectRoot,info:e}),A(t.projectRoot,e),!L(t.projectRoot).installed&&T()&&o.warn(`
2
- ${n(N)}
3
- `)};class q{#e;#r;#c;#o;#i=!1;#n;#h;#s=[];#t;#a;constructor(e){this.#e=e,this.#r=v(e.projectRoot,e.schemaDir),this.#c=P(process.env[c]),this.#o=H(this.#r)}async prepareDevSession(){this.#i||(this.#i=!0,await f(this.#e))}apply(e){e.hooks.beforeCompile.tapPromise(d,async()=>{this.#a??=this.#l(e.watchMode===!0).finally(()=>{this.#a=void 0}),await this.#a}),e.hooks.afterCompile.tapPromise(d,r=>{r.contextDependencies.add(this.#r);for(const i of[a(this.#r),h(this.#e.projectRoot)])i!==void 0&&r.fileDependencies.add(i);for(const i of this.#s)r.errors.push(i);return Promise.resolve()})}async#l(e){if(e&&this.#c){this.#i||(this.#i=!0,o.info(n(`codegen disabled via ${c} — skipping generation and the wrangler checks.`)));return}const r=this.#u();r!==this.#h&&(this.#o.drop(),this.#h=r,this.#n=void 0);const i=this.#g(r);if(!this.#f(i)){this.#s=[];try{!this.#i&&e&&await f(this.#e),this.#i=!0,await this.#p(e),this.#s.length===0?(this.#n=i,this.#t=void 0):this.#d(i)}catch(s){this.#o.drop(),this.#n=void 0,this.#s.push(s instanceof Error?s:new Error(String(s))),this.#d(i),o.error(s instanceof Error?s.message:String(s))}}}#f(e){return e===this.#n?!0:this.#t!==void 0&&this.#t.fingerprint===e&&this.#t.attempts>=I}#d(e){this.#t=this.#t?.fingerprint===e?{attempts:this.#t.attempts+1,fingerprint:e}:{attempts:1,fingerprint:e}}#g(e){const r=h(this.#e.projectRoot);return[S(this.#r),e,r===void 0?"":l(r)].map(s=>g("sha256").update(s).digest("hex")).join("")}async#p(e){await x(this.#e,o),this.#e.validateWrangler&&M(this.#e,o.warn,"Update your wrangler.jsonc and rebuild.");const r=G(this.#e,o,this.#o.get()),i=await b({cwd:this.#e.projectRoot,logger:o});i.error!==void 0&&this.#s.push(new Error(i.error)),!e&&r!==void 0&&this.#s.push(new Error(r))}#u(){const e=a(this.#r);return e===void 0?"":l(e)}}export{q as LunoraRspackPlugin,d as PLUGIN_NAME};