@sigx/lynx-plugin 0.2.7 → 0.4.1

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/dist/index.js CHANGED
@@ -1,319 +1,398 @@
1
- import { networkInterfaces as e } from "node:os";
2
- import t from "node:path";
3
- import { fileURLToPath as n } from "node:url";
4
- //#region src/layers.ts
5
- var r = {
6
- BACKGROUND: "sigx:background",
7
- MAIN_THREAD: "sigx:main-thread"
8
- }, i = t.dirname(n(import.meta.url));
9
- function a(e, n) {
10
- let { enableCSSSelector: a, enableCSSInvalidation: s } = n;
11
- e.modifyRsbuildConfig((e, { mergeRsbuildConfig: t }) => t(e, { output: { injectStyles: !1 } })), e.modifyBundlerChain(async function(n, { CHAIN_ID: c }) {
12
- let { CssExtractRspackPlugin: l, CssExtractWebpackPlugin: u } = await import("@lynx-js/css-extract-webpack-plugin"), d = e.context.bundlerType === "rspack" ? l : u;
13
- [
14
- c.RULE.CSS,
15
- c.RULE.SASS,
16
- c.RULE.LESS,
17
- c.RULE.STYLUS
18
- ].filter((e) => n.module.rules.has(e)).forEach((e) => {
19
- let a = n.module.rule(e);
20
- p(a, c), a.issuerLayer(r.BACKGROUND).use(c.USE.MINI_CSS_EXTRACT).loader(d.loader).end();
21
- let s = a.uses.entries(), l = a.entries(), u = s[c.USE.CSS]?.entries();
22
- n.module.rule(`${e}:${r.MAIN_THREAD}`).merge(l).issuerLayer(r.MAIN_THREAD).use(c.USE.IGNORE_CSS).loader(t.resolve(i, "./loaders/ignore-css-loader")).end().uses.merge(s).delete(c.USE.MINI_CSS_EXTRACT).delete(c.USE.LIGHTNINGCSS).delete(c.USE.CSS).end(), u && n.module.rule(`${e}:${r.MAIN_THREAD}`).use(c.USE.CSS).after(c.USE.IGNORE_CSS).merge(u).options(o(u.options, !0)).end();
23
- });
24
- let f = c.RULE;
25
- [
26
- "CSS_INLINE",
27
- "SASS_INLINE",
28
- "LESS_INLINE",
29
- "STYLUS_INLINE"
30
- ].map((e) => f[e]).filter((e) => !!e && n.module.rules.has(e)).forEach((e) => {
31
- p(n.module.rule(e), c);
32
- }), n.plugin(c.PLUGIN.MINI_CSS_EXTRACT).tap((e) => {
33
- let [t] = e;
34
- return [{
35
- ...t,
36
- enableRemoveCSSScope: !0,
37
- enableCSSSelector: a,
38
- enableCSSInvalidation: s,
39
- cssPlugins: []
40
- }];
41
- }).init((e, t) => new d(...t)).end().end();
42
- function p(e, t) {
43
- e.uses.has(t.USE.LIGHTNINGCSS) && e.uses.delete(t.USE.LIGHTNINGCSS);
44
- }
45
- });
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * An rsbuild / rspeedy plugin that integrates SignalX with Lynx's dual-thread
5
+ * architecture (Background Thread renderer + Main Thread PAPI executor).
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * // lynx.config.ts
10
+ * import { defineConfig } from '@lynx-js/rspeedy'
11
+ * import { pluginSigxLynx } from '@sigx/lynx-plugin'
12
+ *
13
+ * export default defineConfig({
14
+ * plugins: [pluginSigxLynx()],
15
+ * })
16
+ * ```
17
+ */
18
+ import { networkInterfaces } from 'node:os';
19
+ import path from 'node:path';
20
+ import { fileURLToPath } from 'node:url';
21
+ import { applyCSS } from './css.js';
22
+ import { applyEntry } from './entry.js';
23
+ import { applyIcons } from './icons.js';
24
+ import { LAYERS } from './layers.js';
25
+ import { createLogWebSocketServer, LOG_ENDPOINT_PATH } from './log-server.js';
26
+ export { LAYERS, applyEntry };
27
+ const _pluginDirname = path.dirname(fileURLToPath(import.meta.url));
28
+ const _sigxLynxRoot = path.resolve(_pluginDirname, '../..');
29
+ /** Wildcard addresses that bind to all interfaces but aren't routable from other devices. */
30
+ const WILDCARD_HOSTS = new Set(['0.0.0.0', '::', '0:0:0:0:0:0:0:0']);
31
+ /**
32
+ * Interface names that are virtual adapters (Hyper-V, WSL, Docker, VPN, etc.)
33
+ * and should be skipped when looking for the real LAN address.
34
+ */
35
+ const VIRTUAL_IF_PATTERNS = /^(vEthernet|veth|docker|br-|virbr|vmnet|VirtualBox|Hyper-V|WSL|ham\d)/i;
36
+ /**
37
+ * Detect the real LAN IPv4 address on this machine.
38
+ * Skips virtual/container adapters (Hyper-V, WSL, Docker) and prefers
39
+ * physical interfaces like Wi-Fi or Ethernet.
40
+ * Falls back to the first external IPv4 if no physical match is found,
41
+ * and ultimately to `'127.0.0.1'`.
42
+ */
43
+ function detectLanIPv4() {
44
+ const ifaces = networkInterfaces();
45
+ let fallback;
46
+ for (const [name, nets] of Object.entries(ifaces)) {
47
+ for (const net of nets ?? []) {
48
+ if (net.family !== 'IPv4' || net.internal || !net.address)
49
+ continue;
50
+ // Remember first external address as fallback
51
+ if (!fallback)
52
+ fallback = net.address;
53
+ // Skip virtual adapters
54
+ if (VIRTUAL_IF_PATTERNS.test(name))
55
+ continue;
56
+ return net.address;
57
+ }
58
+ }
59
+ return fallback ?? '127.0.0.1';
46
60
  }
47
- var o = (e, t) => {
48
- if (e.modules && t) {
49
- let { modules: t } = e;
50
- return t = t === !0 ? { exportOnlyLocals: !0 } : typeof t == "string" ? {
51
- mode: t,
52
- exportOnlyLocals: !0
53
- } : {
54
- ...t,
55
- exportOnlyLocals: !0
56
- }, {
57
- ...e,
58
- modules: t
59
- };
60
- }
61
- return e;
62
- }, s = "lynx:sigx-template", c = "lynx:sigx-mark-main-thread", l = "lynx:sigx-encode", u = ".rspeedy", d = t.dirname(n(import.meta.url));
63
- t.resolve(d, "..");
64
- var f = class {
65
- mainThreadFilenames;
66
- constructor(e) {
67
- this.mainThreadFilenames = e;
68
- }
69
- apply(e) {
70
- let { RuntimeGlobals: t } = e.webpack;
71
- e.hooks.thisCompilation.tap(c, (n) => {
72
- n.hooks.additionalTreeRuntimeRequirements.tap(c, (e, n) => {
73
- e.getEntryOptions()?.layer === r.MAIN_THREAD && (n.add(t.startup), n.add(t.require));
74
- }), n.hooks.processAssets.tap({
75
- name: c,
76
- stage: e.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL
77
- }, () => {
78
- for (let e of this.mainThreadFilenames) {
79
- let t = n.getAsset(e);
80
- t && n.updateAsset(e, t.source, {
81
- ...t.info,
82
- "lynx:main-thread": !0
83
- });
84
- }
85
- });
86
- });
87
- }
88
- };
89
- async function p(e, n = {}) {
90
- let i;
91
- try {
92
- i = await import("@lynx-js/template-webpack-plugin");
93
- } catch {}
94
- let a;
95
- try {
96
- a = await import("@lynx-js/runtime-wrapper-webpack-plugin");
97
- } catch {}
98
- e.modifyRsbuildConfig((t, { mergeRsbuildConfig: n }) => e.getRsbuildConfig("original").performance?.chunkSplit?.strategy ? t : n(t, { performance: { chunkSplit: { strategy: "all-in-one" } } })), e.modifyRspackConfig((e) => {
99
- if (!e.optimization) return e;
100
- if (e.optimization.splitChunks === !1 && (e.optimization.splitChunks = {}), e.optimization.splitChunks) {
101
- let t = e.optimization.splitChunks.chunks;
102
- e.optimization.splitChunks.chunks = (e) => e.name?.includes("__main-thread") ? !1 : typeof t == "function" ? t(e) : t === "all" || t === "initial";
103
- }
104
- return e;
105
- }), e.modifyBundlerChain((o, { environment: p, isProd: m }) => {
106
- if (e.context.callerName !== "rspeedy") return;
107
- let h = !m, g = p.name === "lynx" || p.name.startsWith("lynx-"), _ = p.name === "web" || p.name.startsWith("web-"), { hmr: v, liveReload: y } = p.config.dev ?? {}, b = h && !_ && v !== !1, x = h && !_ && y !== !1, S = o.entryPoints.entries() ?? {};
108
- o.entryPoints.clear();
109
- let C = [];
110
- for (let [e, a] of Object.entries(S)) {
111
- let c = [], l = a;
112
- for (let e of l.values()) if (typeof e == "string") c.push(e);
113
- else if (typeof e == "object" && e && "import" in e) {
114
- let t = e.import;
115
- Array.isArray(t) ? c.push(...t) : t && c.push(t);
116
- }
117
- let d = g ? u : "", f = `${e}__main-thread`, h = t.posix.join(d, `${e}/main-thread.js`), v = t.posix.join(d, `${e}/background${m ? ".[contenthash:8]" : ""}.js`);
118
- (g || _) && C.push(h);
119
- let y = b ? ["@lynx-js/css-extract-webpack-plugin/runtime/hotModuleReplacement.lepus.cjs", ...c] : [...c];
120
- o.entry(f).add({
121
- layer: r.MAIN_THREAD,
122
- import: y,
123
- filename: h
124
- }).end();
125
- let S = [];
126
- S.push(...c);
127
- let w = o.entry(e).add({
128
- layer: r.BACKGROUND,
129
- import: S,
130
- filename: v
131
- });
132
- if (b && (w.prepend({
133
- layer: r.BACKGROUND,
134
- import: "@rspack/core/hot/dev-server"
135
- }), w.prepend({
136
- layer: r.BACKGROUND,
137
- import: "@sigx/lynx-runtime/mt-hmr-bridge"
138
- })), (b || x) && w.prepend({
139
- layer: r.BACKGROUND,
140
- import: "@lynx-js/webpack-dev-transport/client"
141
- }), w.end(), (g || _) && i) {
142
- let { LynxTemplatePlugin: r } = i, a = (typeof p.config.output.filename == "object" ? p.config.output.filename.bundle : p.config.output.filename) ?? "[name].[platform].bundle";
143
- o.plugin(`${s}-${e}`).use(r, [{
144
- ...r.defaultOptions,
145
- dsl: "react_nodiff",
146
- chunks: [f, e],
147
- filename: a.replaceAll("[name]", e).replaceAll("[platform]", p.name),
148
- intermediate: t.posix.join(d, e),
149
- debugInfoOutside: n.debugInfoOutside ?? !0,
150
- enableCSSSelector: n.enableCSSSelector ?? !0,
151
- enableCSSInvalidation: n.enableCSSSelector ?? !0,
152
- enableCSSInheritance: n.enableCSSInheritance ?? !1,
153
- customCSSInheritanceList: n.customCSSInheritanceList,
154
- enableRemoveCSSScope: !0,
155
- enableNewGesture: !0,
156
- removeDescendantSelectorScope: !0,
157
- cssPlugins: []
158
- }]).end();
159
- }
160
- }
161
- if ((g || _) && C.length > 0 && o.plugin(c).use(f, [C]).end(), g && a) {
162
- let { RuntimeWrapperWebpackPlugin: e } = a;
163
- o.plugin("lynx:sigx-runtime-wrapper").use(e, [{ test: /^(?!.*main-thread(?:\.[A-Fa-f0-9]*)?\.js$).*\.js$/ }]).end();
164
- }
165
- if (g && i) {
166
- let { LynxEncodePlugin: e } = i;
167
- o.plugin(l).use(e, [{}]).end();
168
- }
169
- b && o.module.rule("sigx-hmr").test(/\.[jt]sx?$/).issuerLayer(r.BACKGROUND).exclude.add(/node_modules/).add(/dist/).end().enforce("pre").use("sigx-hmr-loader").loader(t.resolve(d, "./loaders/hmr-loader")).end(), o.module.rule("sigx-worklet").test(/\.[jt]sx?$/).issuerLayer(r.BACKGROUND).exclude.add(/node_modules/).add(/dist/).end().enforce("pre").use("sigx-worklet-loader").loader(t.resolve(d, "./loaders/worklet-loader")).end(), o.module.rule("sigx-worklet-mt").test(/\.[jt]sx?$/).issuerLayer(r.MAIN_THREAD).exclude.add(/node_modules/).add(/dist/).end().enforce("pre").use("sigx-worklet-mt-loader").loader(t.resolve(d, "./loaders/worklet-loader-mt")).end(), o.output.set("iife", !1);
170
- });
61
+ /** Extract the hostname from a URL string (may be inside JSON quotes). */
62
+ function extractHost(s) {
63
+ const m = s.match(/\/\/([^:/]+)/);
64
+ return m ? m[1] : '';
171
65
  }
172
- //#endregion
173
- //#region src/index.ts
174
- var m = t.dirname(n(import.meta.url));
175
- t.resolve(m, "../..");
176
- var h = new Set([
177
- "0.0.0.0",
178
- "::",
179
- "0:0:0:0:0:0:0:0"
180
- ]), g = /^(vEthernet|veth|docker|br-|virbr|vmnet|VirtualBox|Hyper-V|WSL|ham\d)/i;
181
- function _() {
182
- let t = e(), n;
183
- for (let [e, r] of Object.entries(t)) for (let t of r ?? []) if (!(t.family !== "IPv4" || t.internal || !t.address) && (n ||= t.address, !g.test(e))) return t.address;
184
- return n ?? "127.0.0.1";
66
+ /**
67
+ * Create an rsbuild / rspeedy plugin for SignalX-Lynx dual-thread rendering.
68
+ *
69
+ * @public
70
+ */
71
+ export function pluginSigxLynx(options = {}) {
72
+ const { enableCSSSelector: _enableCSSSelector = true, enableCSSInheritance: _enableCSSInheritance = false, customCSSInheritanceList: _customCSSInheritanceList, debugInfoOutside: _debugInfoOutside = true, } = options;
73
+ return {
74
+ name: 'lynx:sigx',
75
+ // Must run after rspeedy's own config plugins (including pluginDev for URL fixes)
76
+ pre: ['lynx:rsbuild:plugin-api', 'lynx:config', 'lynx:rsbuild:dev'],
77
+ async setup(api) {
78
+ api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => {
79
+ // Compile all JS files (including node_modules) for ES2019 compat
80
+ // with the Lynx JS engine, unless user explicitly sets source.include.
81
+ const userConfig = api.getRsbuildConfig('original');
82
+ if (typeof userConfig.source?.include === 'undefined') {
83
+ config = mergeRsbuildConfig(config, {
84
+ source: {
85
+ include: [/\.(?:js|mjs|cjs)$/],
86
+ },
87
+ });
88
+ }
89
+ // Honour `SIGX_LYNX_DEV_PORT` set by `@sigx/lynx-cli`. Rspeedy's CLI
90
+ // has no `--port` flag, so the CLI plumbs its computed port (from
91
+ // `sigx dev --port N` or the lynx-cli default) through this env var
92
+ // and we override `server.port` here. Without this, `serverState.port`
93
+ // on the lynx-cli side (used to build the device-launch URL) could
94
+ // diverge from whatever the user's `lynx.config.ts` set — and the
95
+ // device would boot pointing at a server that isn't there.
96
+ const envPort = process.env['SIGX_LYNX_DEV_PORT'];
97
+ const portOverride = envPort && Number.isFinite(Number(envPort))
98
+ ? Number(envPort)
99
+ : undefined;
100
+ if (portOverride !== undefined) {
101
+ config = mergeRsbuildConfig(config, {
102
+ server: { port: portOverride },
103
+ });
104
+ }
105
+ return mergeRsbuildConfig(config, {
106
+ source: {
107
+ define: {
108
+ __DEV__: 'process.env.NODE_ENV !== \'production\'',
109
+ },
110
+ },
111
+ tools: {
112
+ rspack: {
113
+ output: {
114
+ iife: false,
115
+ },
116
+ },
117
+ swc: {
118
+ jsc: {
119
+ target: 'es2019',
120
+ transform: {
121
+ react: {
122
+ runtime: 'automatic',
123
+ importSource: '@sigx/lynx',
124
+ throwIfNamespace: false,
125
+ },
126
+ },
127
+ },
128
+ },
129
+ },
130
+ });
131
+ });
132
+ // -------------------------------------------------------------------
133
+ // Dev-only: console log streaming. Two pieces:
134
+ // 1. `onAfterStartDevServer` boots a tiny `ws` server on
135
+ // `devServerPort + 1` that receives batched log entries from
136
+ // devices and emits them on stdout for the CLI to pretty-print.
137
+ // 2. `source.define` bakes the ws URL into the BG bundle so
138
+ // `@sigx/lynx-dev-client/install` can pick it up at runtime.
139
+ // Both are gated on NODE_ENV !== 'production'.
140
+ //
141
+ // The Lynx BG runtime on Android has no `fetch` / `XHR` / `lynx.fetch`,
142
+ // so HTTP isn't an option for the device side. WebSocket is shipped by
143
+ // `@sigx/lynx-websocket` (URLSessionWebSocketTask on iOS, OkHttp on
144
+ // Android), which the dev-client imports as a side-effect.
145
+ // -------------------------------------------------------------------
146
+ const isDevServer = process.env['NODE_ENV'] !== 'production';
147
+ if (isDevServer) {
148
+ const lanIP = detectLanIPv4();
149
+ let logServer;
150
+ let wsPort = 0;
151
+ api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => {
152
+ const envPort = process.env['SIGX_LYNX_DEV_PORT'];
153
+ const httpPort = envPort && Number.isFinite(Number(envPort))
154
+ ? Number(envPort)
155
+ : (config.server?.port ?? 3000);
156
+ wsPort = httpPort + 1;
157
+ const logUrl = `ws://${lanIP}:${wsPort}${LOG_ENDPOINT_PATH}`;
158
+ return mergeRsbuildConfig(config, {
159
+ source: {
160
+ define: {
161
+ __SIGX_DEV_LOG_URL__: JSON.stringify(logUrl),
162
+ },
163
+ },
164
+ });
165
+ });
166
+ api.onAfterStartDevServer(async () => {
167
+ if (logServer)
168
+ return;
169
+ try {
170
+ logServer = await createLogWebSocketServer({ port: wsPort });
171
+ api.logger.info(`[sigx-lynx] device log ws → ws://${lanIP}:${logServer.port}${LOG_ENDPOINT_PATH}`);
172
+ }
173
+ catch (err) {
174
+ api.logger.warn(`[sigx-lynx] device log ws failed to start on port ${wsPort}: ${err.message}`);
175
+ }
176
+ });
177
+ const stopLogServer = async () => {
178
+ if (!logServer)
179
+ return;
180
+ const s = logServer;
181
+ logServer = undefined;
182
+ await s.close();
183
+ };
184
+ api.onCloseDevServer(stopLogServer);
185
+ api.onExit(() => { void stopLogServer(); });
186
+ }
187
+ api.modifyBundlerChain((chain) => {
188
+ chain.resolve.alias.set('@sigx/runtime-dom', '@sigx/lynx-runtime');
189
+ });
190
+ // rspeedy's pluginDev uses `server.host` as the hostname for HMR
191
+ // client URLs (publicPath, WebSocket URL, printUrls). When the user
192
+ // sets server.host to '0.0.0.0' (bind all interfaces), those URLs
193
+ // become unreachable from external devices (phones, emulators).
194
+ //
195
+ // Fix at the rsbuild config level (runs AFTER rspeedy's hooks thanks
196
+ // to 'lynx:rsbuild:dev' in `pre`): replace wildcard hosts with the
197
+ // actual LAN IP in dev.assetPrefix, dev.client.host, output.assetPrefix,
198
+ // and the server.printUrls function.
199
+ api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => {
200
+ const devAssetPrefix = config.dev?.assetPrefix;
201
+ if (typeof devAssetPrefix !== 'string')
202
+ return config;
203
+ // Only fix if the assetPrefix contains a wildcard host
204
+ let needsFix = false;
205
+ for (const wh of WILDCARD_HOSTS) {
206
+ if (devAssetPrefix.includes(`//${wh}:`)) {
207
+ needsFix = true;
208
+ break;
209
+ }
210
+ }
211
+ if (!needsFix)
212
+ return config;
213
+ const lanIP = detectLanIPv4();
214
+ const replaceWildcard = (s) => {
215
+ for (const wh of WILDCARD_HOSTS) {
216
+ s = s.replaceAll(`//${wh}:`, `//${lanIP}:`);
217
+ }
218
+ return s;
219
+ };
220
+ const fixedAssetPrefix = replaceWildcard(devAssetPrefix);
221
+ // Override printUrls to show the correct LAN IP URL.
222
+ // rspeedy's printUrls uses closure variables that still hold '0.0.0.0',
223
+ // so we must replace the function entirely.
224
+ const existingPrintUrls = config.server?.printUrls;
225
+ const printUrlsFn = typeof existingPrintUrls === 'function'
226
+ ? (param) => {
227
+ // Call rspeedy's original printUrls to get the URL list,
228
+ // then fix the hostnames in each URL.
229
+ const result = existingPrintUrls(param);
230
+ if (Array.isArray(result)) {
231
+ return result.map((item) => typeof item === 'string'
232
+ ? replaceWildcard(item)
233
+ : { ...item, url: replaceWildcard(item.url) });
234
+ }
235
+ return result;
236
+ }
237
+ : undefined;
238
+ const merged = mergeRsbuildConfig(config, {
239
+ dev: {
240
+ assetPrefix: fixedAssetPrefix,
241
+ client: {
242
+ host: lanIP,
243
+ },
244
+ },
245
+ output: {
246
+ assetPrefix: fixedAssetPrefix,
247
+ },
248
+ });
249
+ // Direct assignment — mergeRsbuildConfig can't reliably merge functions
250
+ if (printUrlsFn) {
251
+ merged.server = { ...merged.server, printUrls: printUrlsFn };
252
+ }
253
+ return merged;
254
+ });
255
+ // Rspack's default watcher-ignore is only /node_modules|\.git/. In Lynx
256
+ // app layouts the ios/ Pods tree and dist/ output drown macOS FSEvents,
257
+ // causing edits to src/*.tsx to silently not fire rebuilds. Narrow the
258
+ // watched set and stop chasing symlinks through pnpm's .pnpm/ store.
259
+ //
260
+ // Upstream: fixed in Rspack 2.0 (`fix(watcher): filter stale FSEvents
261
+ // with mtime baseline comparison`). Rspeedy 0.14.2 still pins Rspack
262
+ // 1.7.10, so we can't adopt the real fix yet — revisit when rspeedy
263
+ // bumps to Rspack 2.0 and we can drop this hook.
264
+ api.modifyRspackConfig((rspackConfig) => {
265
+ const existing = rspackConfig.watchOptions ?? {};
266
+ const existingIgnored = Array.isArray(existing.ignored)
267
+ ? existing.ignored
268
+ : typeof existing.ignored === 'string'
269
+ ? [existing.ignored]
270
+ : [];
271
+ rspackConfig.watchOptions = {
272
+ ...existing,
273
+ ignored: [
274
+ '**/node_modules/**',
275
+ '**/.git/**',
276
+ '**/dist/**',
277
+ '**/ios/**',
278
+ '**/android/**',
279
+ '**/Pods/**',
280
+ '**/.rspeedy/**',
281
+ ...existingIgnored,
282
+ ],
283
+ followSymlinks: existing.followSymlinks ?? false,
284
+ poll: existing.poll
285
+ ?? (process.env.SIGX_LYNX_WATCH_POLL
286
+ ? Number(process.env.SIGX_LYNX_WATCH_POLL) || true
287
+ : undefined),
288
+ };
289
+ });
290
+ // Belt-and-suspenders: also patch at the rspack config level in case
291
+ // the rsbuild-level fix didn't propagate everywhere (e.g. resolve
292
+ // aliases set by rspeedy's modifyBundlerChain using closure variables).
293
+ api.modifyRspackConfig((rspackConfig) => {
294
+ // Check if publicPath or any resolve alias contains a wildcard host
295
+ let needsFix = false;
296
+ const publicPath = rspackConfig.output?.publicPath;
297
+ if (typeof publicPath === 'string') {
298
+ for (const wh of WILDCARD_HOSTS) {
299
+ if (publicPath.includes(`//${wh}:`)) {
300
+ needsFix = true;
301
+ break;
302
+ }
303
+ }
304
+ }
305
+ if (!needsFix) {
306
+ const aliases = rspackConfig.resolve?.alias;
307
+ if (aliases && typeof aliases === 'object' && !Array.isArray(aliases)) {
308
+ for (const val of Object.values(aliases)) {
309
+ if (typeof val === 'string') {
310
+ for (const wh of WILDCARD_HOSTS) {
311
+ if (val.includes(`hostname=${wh}`)) {
312
+ needsFix = true;
313
+ break;
314
+ }
315
+ }
316
+ }
317
+ if (needsFix)
318
+ break;
319
+ }
320
+ }
321
+ }
322
+ if (!needsFix)
323
+ return;
324
+ const lanIP = detectLanIPv4();
325
+ const replaceWildcard = (s) => {
326
+ for (const wh of WILDCARD_HOSTS) {
327
+ s = s.replaceAll(`//${wh}:`, `//${lanIP}:`);
328
+ s = s.replaceAll(`hostname=${wh}`, `hostname=${lanIP}`);
329
+ }
330
+ return s;
331
+ };
332
+ // Fix output.publicPath (used for hot-update fetch URLs)
333
+ if (rspackConfig.output) {
334
+ rspackConfig.output.publicPath = replaceWildcard(rspackConfig.output.publicPath);
335
+ }
336
+ // Fix the resolve alias for @lynx-js/webpack-dev-transport/client
337
+ // which embeds hostname=0.0.0.0 in query params for the WebSocket URL
338
+ const aliases = rspackConfig.resolve?.alias;
339
+ if (aliases && typeof aliases === 'object' && !Array.isArray(aliases)) {
340
+ for (const [key, val] of Object.entries(aliases)) {
341
+ if (typeof val === 'string') {
342
+ const fixed = replaceWildcard(val);
343
+ if (fixed !== val) {
344
+ aliases[key] = fixed;
345
+ }
346
+ }
347
+ }
348
+ }
349
+ // Fix ASSET_PREFIX in DefinePlugin definitions — these are stringified
350
+ // JSON values so the wildcard appears inside quoted strings.
351
+ for (const plugin of rspackConfig.plugins ?? []) {
352
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
353
+ const defs = plugin?.definitions ?? plugin?._args?.[0];
354
+ if (!defs || typeof defs !== 'object')
355
+ continue;
356
+ for (const [k, v] of Object.entries(defs)) {
357
+ if (typeof v === 'string' && WILDCARD_HOSTS.has(extractHost(v))) {
358
+ defs[k] = replaceWildcard(v);
359
+ }
360
+ else if (typeof v === 'object' && v !== null) {
361
+ for (const [k2, v2] of Object.entries(v)) {
362
+ if (typeof v2 === 'string' && WILDCARD_HOSTS.has(extractHost(v2))) {
363
+ v[k2] = replaceWildcard(v2);
364
+ }
365
+ }
366
+ }
367
+ }
368
+ }
369
+ // Fix SourceMapDevToolPlugin publicPath
370
+ for (const plugin of rspackConfig.plugins ?? []) {
371
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
372
+ const opts = plugin?._options ?? plugin?._args?.[0];
373
+ if (opts && typeof opts.publicPath === 'string') {
374
+ opts.publicPath = replaceWildcard(opts.publicPath);
375
+ }
376
+ }
377
+ });
378
+ // Wire CSS handling — forces extraction via @lynx-js/css-extract-webpack-plugin,
379
+ // strips lightningcss, and configures ignore-css-loader for main-thread layer.
380
+ applyCSS(api, {
381
+ enableCSSSelector: _enableCSSSelector,
382
+ enableCSSInvalidation: _enableCSSInheritance,
383
+ });
384
+ // Wire dual-thread entry splitting (worklets skipped in v1)
385
+ await applyEntry(api, {
386
+ debugInfoOutside: _debugInfoOutside,
387
+ enableCSSInheritance: _enableCSSInheritance,
388
+ customCSSInheritanceList: _customCSSInheritanceList,
389
+ });
390
+ // Wire @sigx/lynx-icons — reads iconSets from signalx.config.ts,
391
+ // scans the project for <Icon> usage, and aliases the runtime's
392
+ // virtual-module subpaths to generated codepoint / SVG maps.
393
+ // Safe to call unconditionally; bails out when no iconSets are
394
+ // configured or @sigx/lynx-cli isn't installed.
395
+ await applyIcons(api);
396
+ },
397
+ };
185
398
  }
186
- function v(e) {
187
- let t = e.match(/\/\/([^:/]+)/);
188
- return t ? t[1] : "";
189
- }
190
- function y(e = {}) {
191
- let { enableCSSSelector: t = !0, enableCSSInheritance: n = !1, customCSSInheritanceList: r, debugInfoOutside: i = !0 } = e;
192
- return {
193
- name: "lynx:sigx",
194
- pre: [
195
- "lynx:rsbuild:plugin-api",
196
- "lynx:config",
197
- "lynx:rsbuild:dev"
198
- ],
199
- async setup(e) {
200
- e.modifyRsbuildConfig((t, { mergeRsbuildConfig: n }) => (e.getRsbuildConfig("original").source?.include === void 0 && (t = n(t, { source: { include: [/\.(?:js|mjs|cjs)$/] } })), n(t, {
201
- source: { define: { __DEV__: "process.env.NODE_ENV !== 'production'" } },
202
- tools: {
203
- rspack: { output: { iife: !1 } },
204
- swc: { jsc: {
205
- target: "es2019",
206
- transform: { react: {
207
- runtime: "automatic",
208
- importSource: "@sigx/lynx",
209
- throwIfNamespace: !1
210
- } }
211
- } }
212
- }
213
- }))), e.modifyBundlerChain((e) => {
214
- e.resolve.alias.set("@sigx/runtime-dom", "@sigx/lynx-runtime");
215
- }), e.modifyRsbuildConfig((e, { mergeRsbuildConfig: t }) => {
216
- let n = e.dev?.assetPrefix;
217
- if (typeof n != "string") return e;
218
- let r = !1;
219
- for (let e of h) if (n.includes(`//${e}:`)) {
220
- r = !0;
221
- break;
222
- }
223
- if (!r) return e;
224
- let i = _(), a = (e) => {
225
- for (let t of h) e = e.replaceAll(`//${t}:`, `//${i}:`);
226
- return e;
227
- }, o = a(n), s = e.server?.printUrls, c = typeof s == "function" ? (e) => {
228
- let t = s(e);
229
- return Array.isArray(t) ? t.map((e) => typeof e == "string" ? a(e) : {
230
- ...e,
231
- url: a(e.url)
232
- }) : t;
233
- } : void 0, l = t(e, {
234
- dev: {
235
- assetPrefix: o,
236
- client: { host: i }
237
- },
238
- output: { assetPrefix: o }
239
- });
240
- return c && (l.server = {
241
- ...l.server,
242
- printUrls: c
243
- }), l;
244
- }), e.modifyRspackConfig((e) => {
245
- let t = e.watchOptions ?? {}, n = Array.isArray(t.ignored) ? t.ignored : typeof t.ignored == "string" ? [t.ignored] : [];
246
- e.watchOptions = {
247
- ...t,
248
- ignored: [
249
- "**/node_modules/**",
250
- "**/.git/**",
251
- "**/dist/**",
252
- "**/ios/**",
253
- "**/android/**",
254
- "**/Pods/**",
255
- "**/.rspeedy/**",
256
- ...n
257
- ],
258
- followSymlinks: t.followSymlinks ?? !1,
259
- poll: t.poll ?? (process.env.SIGX_LYNX_WATCH_POLL ? Number(process.env.SIGX_LYNX_WATCH_POLL) || !0 : void 0)
260
- };
261
- }), e.modifyRspackConfig((e) => {
262
- let t = !1, n = e.output?.publicPath;
263
- if (typeof n == "string") {
264
- for (let e of h) if (n.includes(`//${e}:`)) {
265
- t = !0;
266
- break;
267
- }
268
- }
269
- if (!t) {
270
- let n = e.resolve?.alias;
271
- if (n && typeof n == "object" && !Array.isArray(n)) for (let e of Object.values(n)) {
272
- if (typeof e == "string") {
273
- for (let n of h) if (e.includes(`hostname=${n}`)) {
274
- t = !0;
275
- break;
276
- }
277
- }
278
- if (t) break;
279
- }
280
- }
281
- if (!t) return;
282
- let r = _(), i = (e) => {
283
- for (let t of h) e = e.replaceAll(`//${t}:`, `//${r}:`), e = e.replaceAll(`hostname=${t}`, `hostname=${r}`);
284
- return e;
285
- };
286
- e.output && (e.output.publicPath = i(e.output.publicPath));
287
- let a = e.resolve?.alias;
288
- if (a && typeof a == "object" && !Array.isArray(a)) {
289
- for (let [e, t] of Object.entries(a)) if (typeof t == "string") {
290
- let n = i(t);
291
- n !== t && (a[e] = n);
292
- }
293
- }
294
- for (let t of e.plugins ?? []) {
295
- let e = t?.definitions ?? t?._args?.[0];
296
- if (!(!e || typeof e != "object")) {
297
- for (let [t, n] of Object.entries(e)) if (typeof n == "string" && h.has(v(n))) e[t] = i(n);
298
- else if (typeof n == "object" && n) for (let [e, t] of Object.entries(n)) typeof t == "string" && h.has(v(t)) && (n[e] = i(t));
299
- }
300
- }
301
- for (let t of e.plugins ?? []) {
302
- let e = t?._options ?? t?._args?.[0];
303
- e && typeof e.publicPath == "string" && (e.publicPath = i(e.publicPath));
304
- }
305
- }), a(e, {
306
- enableCSSSelector: t,
307
- enableCSSInvalidation: n
308
- }), await p(e, {
309
- debugInfoOutside: i,
310
- enableCSSInheritance: n,
311
- customCSSInheritanceList: r
312
- });
313
- }
314
- };
315
- }
316
- //#endregion
317
- export { r as LAYERS, p as applyEntry, y as pluginSigxLynx };
318
-
319
- //# sourceMappingURL=index.js.map