@sigx/lynx-plugin 0.4.0 → 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/css.js +150 -0
- package/dist/entry.js +443 -0
- package/dist/icons.js +226 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +395 -434
- package/dist/layers.js +5 -0
- package/dist/loaders/hmr-loader.js +70 -19
- package/dist/loaders/ignore-css-loader.js +16 -7
- package/dist/loaders/worklet-loader-mt.js +142 -62
- package/dist/loaders/worklet-loader.js +69 -31
- package/dist/loaders/worklet-utils.d.ts +0 -15
- package/dist/loaders/worklet-utils.js +116 -0
- package/dist/log-server.d.ts +0 -0
- package/dist/log-server.js +0 -0
- package/package.json +13 -11
- package/dist/index.js.map +0 -1
- package/dist/loaders/hmr-loader.js.map +0 -1
- package/dist/loaders/ignore-css-loader.js.map +0 -1
- package/dist/loaders/worklet-loader-mt.js.map +0 -1
- package/dist/loaders/worklet-loader.js.map +0 -1
package/dist/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/layers.ts","../src/css.ts","../src/entry.ts","../src/icons.ts","../src/index.ts"],"sourcesContent":["/** Webpack module layers used to separate the dual-thread bundles. */\nexport const LAYERS = {\n BACKGROUND: 'sigx:background',\n MAIN_THREAD: 'sigx:main-thread',\n} as const;\n","/**\n * CSS extraction pipeline for SignalX Lynx.\n *\n * Mirrors the behaviour of `@lynx-js/react-rsbuild-plugin`'s `applyCSS()`:\n * 1. Disables `style-loader` (forces CSS extraction via CssExtractPlugin).\n * 2. Replaces the rsbuild-default CssExtract plugin with\n * `@lynx-js/css-extract-webpack-plugin` which emits Lynx-compatible CSS.\n * 3. Removes `lightningcss-loader` (Lynx has its own CSS processor).\n * 4. Configures the Main-Thread layer to ignore CSS entirely.\n */\n\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { CSSLoaderOptions, RsbuildPluginAPI } from '@rsbuild/core';\n\nimport type {\n CssExtractRspackPluginOptions,\n CssExtractWebpackPluginOptions,\n} from '@lynx-js/css-extract-webpack-plugin';\n\nimport { LAYERS } from './layers';\n\nexport interface ApplyCSSOptions {\n enableCSSSelector: boolean;\n enableCSSInvalidation: boolean;\n}\n\nconst _dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport function applyCSS(\n api: RsbuildPluginAPI,\n options: ApplyCSSOptions,\n): void {\n const { enableCSSSelector, enableCSSInvalidation } = options;\n\n // ① Force CSS extraction (disable style-loader, enable CssExtractPlugin).\n // Without this, rsbuild injects CSS via JS — useless in Lynx's native env.\n api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => {\n return mergeRsbuildConfig(config, {\n output: { injectStyles: false },\n });\n });\n\n // ② Replace the rsbuild-default CSS extraction plugin with the Lynx-aware\n // one, configure loaders per layer, and remove lightningcss.\n api.modifyBundlerChain(\n async function handler(chain, { CHAIN_ID }) {\n const { CssExtractRspackPlugin, CssExtractWebpackPlugin } = await import(\n '@lynx-js/css-extract-webpack-plugin'\n );\n const CssExtractPlugin = api.context.bundlerType === 'rspack'\n ? CssExtractRspackPlugin\n : CssExtractWebpackPlugin;\n\n const cssRules = [\n CHAIN_ID.RULE.CSS,\n CHAIN_ID.RULE.SASS,\n CHAIN_ID.RULE.LESS,\n CHAIN_ID.RULE.STYLUS,\n ] as const;\n\n cssRules\n .filter((rule) => chain.module.rules.has(rule))\n .forEach((ruleName) => {\n const rule = chain.module.rule(ruleName);\n\n // Remove lightningcss-loader — Lynx processes CSS natively.\n removeLightningCSS(rule, CHAIN_ID);\n\n // Use the Lynx CssExtract loader for the Background layer.\n rule\n .issuerLayer(LAYERS.BACKGROUND)\n .use(CHAIN_ID.USE.MINI_CSS_EXTRACT)\n .loader(CssExtractPlugin.loader)\n .end();\n\n // Clone the existing CSS rule chain for the Main-Thread layer.\n // Main-Thread bundles never contain user CSS — only the PAPI\n // bootstrap code. We replace all loaders with ignore-css + a\n // css-loader configured for `exportOnlyLocals: true`.\n const uses = rule.uses.entries();\n const ruleEntries = rule.entries() as Record<string, any>;\n const cssLoaderRule = uses[CHAIN_ID.USE.CSS]?.entries() as\n | Record<string, any>\n | undefined;\n\n chain.module\n .rule(`${ruleName}:${LAYERS.MAIN_THREAD}`)\n .merge(ruleEntries)\n .issuerLayer(LAYERS.MAIN_THREAD)\n .use(CHAIN_ID.USE.IGNORE_CSS)\n .loader(path.resolve(_dirname, './loaders/ignore-css-loader'))\n .end()\n .uses.merge(uses)\n .delete(CHAIN_ID.USE.MINI_CSS_EXTRACT)\n .delete(CHAIN_ID.USE.LIGHTNINGCSS)\n .delete(CHAIN_ID.USE.CSS)\n .end();\n\n // Re-add css-loader with exportOnlyLocals for main-thread\n if (cssLoaderRule) {\n chain.module\n .rule(`${ruleName}:${LAYERS.MAIN_THREAD}`)\n .use(CHAIN_ID.USE.CSS)\n .after(CHAIN_ID.USE.IGNORE_CSS)\n .merge(cssLoaderRule)\n .options(\n normalizeCssLoaderOptions(\n cssLoaderRule.options as CSSLoaderOptions,\n true,\n ),\n )\n .end();\n }\n });\n\n // Also strip lightningcss from inline CSS rules (Rsbuild ≥1.3.0).\n const RULE = CHAIN_ID.RULE as Record<string, string | undefined>;\n const inlineCSSRuleNames = [\n 'CSS_INLINE',\n 'SASS_INLINE',\n 'LESS_INLINE',\n 'STYLUS_INLINE',\n ] as const;\n\n inlineCSSRuleNames\n .map((key) => RULE[key])\n .filter(\n (ruleName): ruleName is string =>\n !!ruleName && chain.module.rules.has(ruleName),\n )\n .forEach((ruleName) => {\n removeLightningCSS(chain.module.rule(ruleName), CHAIN_ID);\n });\n\n // ③ Replace the CssExtract plugin instance with the Lynx-aware one\n // and pass through the CSS selector / invalidation options.\n chain\n .plugin(CHAIN_ID.PLUGIN.MINI_CSS_EXTRACT)\n .tap((args: any[]) => {\n const [pluginOptions] = args;\n return [\n {\n ...pluginOptions,\n enableRemoveCSSScope: true,\n enableCSSSelector,\n enableCSSInvalidation,\n cssPlugins: [],\n } as\n | CssExtractWebpackPluginOptions\n | CssExtractRspackPluginOptions,\n ];\n })\n .init((_: any, args: unknown[]) => {\n return new CssExtractPlugin(\n ...(args as [\n options:\n & CssExtractWebpackPluginOptions\n & CssExtractRspackPluginOptions,\n ]),\n );\n })\n .end()\n .end();\n\n function removeLightningCSS(\n rule: ReturnType<typeof chain.module.rule>,\n ids: typeof CHAIN_ID,\n ): void {\n if (rule.uses.has(ids.USE.LIGHTNINGCSS)) {\n rule.uses.delete(ids.USE.LIGHTNINGCSS);\n }\n }\n },\n );\n}\n\n/**\n * Force `exportOnlyLocals: true` on the css-loader modules config.\n * Copied from rsbuild internals — required when the target is not `web`\n * and CSS modules are enabled.\n */\nconst normalizeCssLoaderOptions = (\n options: CSSLoaderOptions,\n exportOnlyLocals: boolean,\n): CSSLoaderOptions => {\n if (options.modules && exportOnlyLocals) {\n let { modules } = options;\n if (modules === true) {\n modules = { exportOnlyLocals: true };\n } else if (typeof modules === 'string') {\n modules = {\n mode: modules as 'local',\n exportOnlyLocals: true,\n };\n } else {\n modules = {\n ...modules,\n exportOnlyLocals: true,\n };\n }\n\n return {\n ...options,\n modules,\n };\n }\n\n return options;\n};\n","/**\n * Dual-thread entry splitting for SignalX Lynx.\n *\n * For each user-defined rsbuild entry, creates two webpack entries:\n * - `<name>__main-thread` on the MAIN_THREAD layer (PAPI bootstrap via @sigx/lynx-runtime-main)\n * - `<name>` on the BACKGROUND layer (sigx renderer + user app)\n *\n * Then registers @lynx-js/template-webpack-plugin to stitch both bundles\n * into a single .lynx template.\n */\n\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { RsbuildPluginAPI } from '@rsbuild/core';\n\nimport { LAYERS } from './layers';\n\nconst PLUGIN_TEMPLATE = 'lynx:sigx-template';\nconst PLUGIN_MARK_MAIN_THREAD = 'lynx:sigx-mark-main-thread';\nconst PLUGIN_ENCODE = 'lynx:sigx-encode';\n\nconst DEFAULT_INTERMEDIATE = '.rspeedy';\n\nconst _dirname = path.dirname(fileURLToPath(import.meta.url));\n\n// sigx lynx-plugin package root — the plugin lives at <pkgRoot>/dist/,\n// so we resolve one level up from _dirname.\nconst sigxLynxRoot = path.resolve(_dirname, '..');\n\n/** Minimal typing for a webpack Chunk (avoids importing @rspack/core). */\ninterface WebpackChunk {\n getEntryOptions(): { layer?: string } | undefined;\n}\n\n/** Minimal typing for the webpack Compilation object. */\ninterface WebpackCompilation {\n hooks: {\n processAssets: {\n tap(\n options: { name: string; stage: number },\n callback: () => void,\n ): void;\n };\n additionalTreeRuntimeRequirements: {\n tap(\n name: string,\n callback: (chunk: WebpackChunk, set: Set<string>) => void,\n ): void;\n };\n };\n getAsset(\n filename: string,\n ): { source: unknown; info: Record<string, unknown> } | undefined;\n updateAsset(\n filename: string,\n source: unknown,\n info: Record<string, unknown>,\n ): void;\n}\n\n/** Minimal typing for the webpack Compiler object. */\ninterface WebpackCompiler {\n webpack: {\n Compilation: {\n PROCESS_ASSETS_STAGE_ADDITIONAL: number;\n };\n RuntimeGlobals: { startup: string; require: string };\n sources: { RawSource: new (source: string) => unknown };\n };\n hooks: {\n thisCompilation: {\n tap(\n name: string,\n callback: (compilation: WebpackCompilation) => void,\n ): void;\n };\n };\n}\n\n/**\n * SigxMarkMainThreadPlugin forces webpack to generate startup code for MT\n * entry chunks and marks their assets with `lynx:main-thread: true` so\n * LynxTemplatePlugin routes them to lepusCode.root (Lepus bytecode).\n */\nclass SigxMarkMainThreadPlugin {\n constructor(private readonly mainThreadFilenames: string[]) {}\n\n apply(compiler: WebpackCompiler): void {\n const { RuntimeGlobals } = compiler.webpack;\n\n compiler.hooks.thisCompilation.tap(\n PLUGIN_MARK_MAIN_THREAD,\n (compilation) => {\n // Force startup code generation for MT entry chunks.\n compilation.hooks.additionalTreeRuntimeRequirements.tap(\n PLUGIN_MARK_MAIN_THREAD,\n (chunk, set) => {\n const entryOptions = chunk.getEntryOptions();\n if (entryOptions?.layer === LAYERS.MAIN_THREAD) {\n set.add(RuntimeGlobals.startup);\n set.add(RuntimeGlobals.require);\n }\n },\n );\n\n // Mark MT assets with lynx:main-thread: true for LynxTemplatePlugin.\n compilation.hooks.processAssets.tap(\n {\n name: PLUGIN_MARK_MAIN_THREAD,\n stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL,\n },\n () => {\n for (const filename of this.mainThreadFilenames) {\n const asset = compilation.getAsset(filename);\n if (asset) {\n compilation.updateAsset(filename, asset.source, {\n ...asset.info,\n 'lynx:main-thread': true,\n });\n }\n }\n },\n );\n },\n );\n }\n}\n\nexport interface ApplyEntryOptions {\n enableCSSSelector?: boolean;\n enableCSSInheritance?: boolean;\n customCSSInheritanceList?: string[];\n debugInfoOutside?: boolean;\n}\n\nexport async function applyEntry(\n api: RsbuildPluginAPI,\n opts: ApplyEntryOptions = {},\n): Promise<void> {\n // Preload @lynx-js/template-webpack-plugin via dynamic ESM import.\n // rsbuild bundlerChain callbacks are sync, and template-webpack-plugin\n // is pure-ESM (no \"require\" condition in its exports map), so createRequire\n // fails. Stash the module in closure scope for the sync callback below.\n let templateMod:\n | typeof import('@lynx-js/template-webpack-plugin')\n | undefined;\n try {\n templateMod = await import('@lynx-js/template-webpack-plugin');\n } catch {\n // Optional peer — if missing, we'll still emit the two JS bundles.\n }\n\n // Preload @lynx-js/runtime-wrapper-webpack-plugin. This wraps the BG bundle\n // in `__init_card_bundle__(lynxCoreInject, lynx, ...)` so user code inside\n // can reference `lynx` and `lynxCoreInject` as bare identifiers — that's\n // how the BG transport (lynx.getNativeApp().callLepusMethod) and the event\n // dispatcher (lynxCoreInject.tt.publishEvent) get installed properly.\n // Without this wrapper we'd be forced to spelunk through globalThis.multiApps.\n let wrapperMod:\n | { RuntimeWrapperWebpackPlugin: new (opts: { test: RegExp; targetSdk?: string }) => unknown }\n | undefined;\n try {\n wrapperMod = (await import('@lynx-js/runtime-wrapper-webpack-plugin')) as typeof wrapperMod;\n } catch {\n // Optional peer — if missing, lynx-runtime will still attempt the\n // multiApps[appId]._nativeApp fallback, but proper hosts need the wrapper.\n }\n\n // Default to all-in-one chunk splitting to avoid async chunks that break\n // Lynx's single-file bundle requirement.\n api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => {\n const userConfig = api.getRsbuildConfig('original');\n if (!userConfig.performance?.chunkSplit?.strategy) {\n return mergeRsbuildConfig(config, {\n performance: { chunkSplit: { strategy: 'all-in-one' } },\n });\n }\n return config;\n });\n\n // Exclude main-thread chunks from chunk splitting so each remains\n // self-contained.\n api.modifyRspackConfig((rspackConfig) => {\n if (!rspackConfig.optimization) return rspackConfig;\n\n if (rspackConfig.optimization.splitChunks === false) {\n rspackConfig.optimization.splitChunks = {};\n }\n\n if (rspackConfig.optimization.splitChunks) {\n const prev = rspackConfig.optimization.splitChunks.chunks;\n // biome-ignore lint/suspicious/noExplicitAny: rspack Chunk type not importable\n rspackConfig.optimization.splitChunks.chunks = (chunk: any) => {\n if (chunk.name?.includes('__main-thread')) return false;\n if (typeof prev === 'function') return prev(chunk);\n if (prev === 'all') return true;\n if (prev === 'initial') return true;\n return false;\n };\n }\n\n return rspackConfig;\n });\n\n api.modifyBundlerChain((chain, { environment, isProd }) => {\n const isRspeedy = api.context.callerName === 'rspeedy';\n if (!isRspeedy) return;\n\n const isDev = !isProd;\n const isLynx =\n environment.name === 'lynx' || environment.name.startsWith('lynx-');\n const isWeb =\n environment.name === 'web' || environment.name.startsWith('web-');\n\n // HMR / Live Reload flags (same logic as vue-lynx / React plugin)\n const { hmr, liveReload } = environment.config.dev ?? {};\n const enabledHMR = isDev && !isWeb && hmr !== false;\n const enabledLiveReload = isDev && !isWeb && liveReload !== false;\n\n const entries = chain.entryPoints.entries() ?? {};\n\n chain.entryPoints.clear();\n\n // Collect all main-thread filenames to mark with lynx:main-thread\n const mainThreadFilenames: string[] = [];\n\n for (const [entryName, entryPoint] of Object.entries(entries)) {\n // Collect user imports from the original entry\n const imports: string[] = [];\n const ep = entryPoint as { values(): Iterable<unknown> };\n for (const val of ep.values()) {\n if (typeof val === 'string') {\n imports.push(val);\n } else if (typeof val === 'object' && val !== null && 'import' in val) {\n const imp = (val as { import?: string | string[] }).import;\n if (Array.isArray(imp)) imports.push(...imp);\n else if (imp) imports.push(imp);\n }\n }\n\n // ----------------------------------------------------------------\n // Filenames\n // ----------------------------------------------------------------\n const intermediate = isLynx ? DEFAULT_INTERMEDIATE : '';\n const mainThreadEntry = `${entryName}__main-thread`;\n const mainThreadName = path.posix.join(\n intermediate,\n `${entryName}/main-thread.js`,\n );\n const backgroundName = path.posix.join(\n intermediate,\n `${entryName}/background${isProd ? '.[contenthash:8]' : ''}.js`,\n );\n\n if (isLynx || isWeb) {\n mainThreadFilenames.push(mainThreadName);\n }\n\n // ----------------------------------------------------------------\n // Main Thread bundle – PAPI bootstrap only\n // ----------------------------------------------------------------\n // The MT entry ONLY imports @sigx/lynx-runtime-main, which registers\n // globalThis.renderPage, processData, sigxPatchUpdate and bridges\n // ops from the background thread.\n //\n // MT bundle evaluation order (critical):\n // The bootstrap (entry-main → worklet-runtime → install-hybrid-worklet)\n // is prepended to every user file by `worklet-loader-mt.ts` using\n // absolute paths resolved from the loader's install location. That\n // means we DON'T list those modules here as entry imports — the dep\n // graph that the loader-emitted preamble creates pulls them in, in\n // the right order, without forcing the user's app package.json to\n // declare @lynx-js/react as a direct dep.\n //\n // So the MT entry list is just: user imports. (CSS HMR runtime in\n // dev mode only.) Worklet registrations land via the dep graph.\n const mainThreadImports = !enabledHMR\n ? [...imports]\n : [\n '@lynx-js/css-extract-webpack-plugin/runtime/hotModuleReplacement.lepus.cjs',\n ...imports,\n ];\n\n chain\n .entry(mainThreadEntry)\n .add({\n layer: LAYERS.MAIN_THREAD,\n import: mainThreadImports,\n filename: mainThreadName,\n })\n .end();\n\n // ----------------------------------------------------------------\n // Background bundle – sigx renderer + user app\n // ----------------------------------------------------------------\n const bgImports: string[] = [];\n bgImports.push(...imports);\n\n const bgEntry = chain\n .entry(entryName)\n .add({\n layer: LAYERS.BACKGROUND,\n import: bgImports,\n filename: backgroundName,\n });\n\n // Inject standard rspack HMR client + Lynx WebSocket transport into\n // the BG entry (matching vue-lynx's approach). These must be prepended\n // so they initialise before user code.\n if (enabledHMR) {\n bgEntry.prepend({\n layer: LAYERS.BACKGROUND,\n import: '@rspack/core/hot/dev-server',\n });\n // BG → MT hot-update bridge. Subscribes to the same `webpackHotUpdate`\n // emitter event as `@rspack/core/hot/dev-server`, fetches the matching\n // `main__main-thread.<hash>.hot-update.js`, and forwards extracted\n // `registerWorkletInternal` calls to MT via `callLepusMethod`. Without\n // this, MT's `_workletMap` keeps the old worklet IDs from the static\n // bundle while BG sends ops referencing new content-hash IDs after a\n // save → bind-of-undefined on tap.\n bgEntry.prepend({\n layer: LAYERS.BACKGROUND,\n import: '@sigx/lynx-runtime/mt-hmr-bridge',\n });\n }\n if (enabledHMR || enabledLiveReload) {\n bgEntry.prepend({\n layer: LAYERS.BACKGROUND,\n import: '@lynx-js/webpack-dev-transport/client',\n });\n }\n\n bgEntry.end();\n\n // ----------------------------------------------------------------\n // LynxTemplatePlugin – packages both bundles into .lynx template\n // ----------------------------------------------------------------\n if ((isLynx || isWeb) && templateMod) {\n {\n const { LynxTemplatePlugin } = templateMod;\n\n const templateFilename =\n (typeof environment.config.output.filename === 'object'\n ? (environment.config.output.filename as { bundle?: string })\n .bundle\n : environment.config.output.filename) ??\n '[name].[platform].bundle';\n\n chain\n .plugin(`${PLUGIN_TEMPLATE}-${entryName}`)\n .use(LynxTemplatePlugin, [\n {\n ...LynxTemplatePlugin.defaultOptions,\n dsl: 'react_nodiff',\n chunks: [mainThreadEntry, entryName],\n filename: templateFilename\n .replaceAll('[name]', entryName)\n .replaceAll('[platform]', environment.name),\n intermediate: path.posix.join(intermediate, entryName),\n debugInfoOutside: opts.debugInfoOutside ?? true,\n enableCSSSelector: opts.enableCSSSelector ?? true,\n enableCSSInvalidation: opts.enableCSSSelector ?? true,\n enableCSSInheritance: opts.enableCSSInheritance ?? false,\n customCSSInheritanceList: opts.customCSSInheritanceList,\n enableRemoveCSSScope: true,\n enableNewGesture: true,\n removeDescendantSelectorScope: true,\n cssPlugins: [],\n },\n ])\n .end();\n }\n }\n }\n\n // ------------------------------------------------------------------\n // SigxMarkMainThreadPlugin – mark MT assets for LynxTemplatePlugin\n // ------------------------------------------------------------------\n if ((isLynx || isWeb) && mainThreadFilenames.length > 0) {\n chain\n .plugin(PLUGIN_MARK_MAIN_THREAD)\n .use(SigxMarkMainThreadPlugin, [mainThreadFilenames])\n .end();\n }\n\n // ------------------------------------------------------------------\n // RuntimeWrapperWebpackPlugin – wrap BG bundle (NOT main-thread.js)\n // in __init_card_bundle__(lynxCoreInject, lynx, ...). Inside the\n // wrapper, lynx-runtime code can reference `lynx` and `lynxCoreInject`\n // as bare identifiers, giving us the official BG → MT bridge and\n // event dispatch hooks.\n // ------------------------------------------------------------------\n if (isLynx && wrapperMod) {\n const { RuntimeWrapperWebpackPlugin } = wrapperMod;\n chain\n .plugin('lynx:sigx-runtime-wrapper')\n .use(RuntimeWrapperWebpackPlugin, [\n {\n // Wrap everything except main-thread.js (and main-thread.[hash].js).\n test: /^(?!.*main-thread(?:\\.[A-Fa-f0-9]*)?\\.js$).*\\.js$/,\n },\n ])\n .end();\n }\n\n // ------------------------------------------------------------------\n // LynxEncodePlugin – binary-encode the .lynx template\n // ------------------------------------------------------------------\n if (isLynx && templateMod) {\n const { LynxEncodePlugin } = templateMod;\n chain\n .plugin(PLUGIN_ENCODE)\n .use(LynxEncodePlugin, [{}])\n .end();\n }\n\n // ------------------------------------------------------------------\n // HMR loader – inject registerHMRModule() + module.hot.accept()\n // into component files on the BG layer so they self-accept hot\n // updates and patch instances in-place (no structural tree ops).\n // ------------------------------------------------------------------\n if (enabledHMR) {\n chain.module\n .rule('sigx-hmr')\n .test(/\\.[jt]sx?$/)\n .issuerLayer(LAYERS.BACKGROUND)\n .exclude\n .add(/node_modules/)\n .add(/dist/)\n .end()\n .enforce('pre')\n .use('sigx-hmr-loader')\n .loader(path.resolve(_dirname, './loaders/hmr-loader'))\n .end();\n }\n\n // ------------------------------------------------------------------\n // Worklet loaders — both layers run @lynx-js/react/transform.\n // BG layer: target='JS' replaces 'main thread' functions with\n // { _wkltId, _c? } placeholders shipped via SET_WORKLET_EVENT.\n // MT layer: target='LEPUS' produces registerWorkletInternal(...) calls;\n // the loader extracts those + local-import edges.\n //\n // Rules run on every JS/TS file in their respective layer — no\n // package allowlist and no `node_modules`/`dist` rule exclude. The\n // loaders gate themselves on directive presence (cheap regex\n // pre-filter, then SWC). The MT loader additionally branches on the\n // file's path because rspack shares module identity across BG/MT\n // layers — see the decision table in `worklet-loader-mt.ts` — so an\n // MT-side body strip of a library file would wipe its named exports\n // for BG consumers too. That MT-side preservation keeps\n // `@sigx/lynx-runtime-main`'s MT globals (`processData`,\n // `updateGlobalProps`, `sigxRunOnMT`) and lets cross-package\n // consumers like `@sigx/lynx-daisyui` resolve named imports\n // (`useTabs`, `useScreenChrome`) from worklet-shipping packages.\n //\n // The BG loader has no path branch; for directive-bearing files\n // (user or library) it returns the JS-target transform output,\n // which preserves exports while replacing worklet bodies with\n // `{ _wkltId }` placeholders. New packages that ship `'main thread'`\n // directives in their dist are picked up automatically — no\n // manual opt-in.\n chain.module\n .rule('sigx-worklet')\n .test(/\\.[jt]sx?$/)\n .issuerLayer(LAYERS.BACKGROUND)\n .enforce('pre')\n .use('sigx-worklet-loader')\n .loader(path.resolve(_dirname, './loaders/worklet-loader'))\n .end();\n\n chain.module\n .rule('sigx-worklet-mt')\n .test(/\\.[jt]sx?$/)\n .issuerLayer(LAYERS.MAIN_THREAD)\n .enforce('pre')\n .use('sigx-worklet-mt-loader')\n .loader(path.resolve(_dirname, './loaders/worklet-loader-mt'))\n .end();\n\n // Disable IIFE wrapping – Lynx handles module scoping itself\n chain.output.set('iife', false);\n });\n}\n","/**\n * Icon-set integration for @sigx/lynx-icons.\n *\n * At plugin setup time this slice:\n *\n * 1. Loads `signalx.config.ts` and reads the `iconSets: [...]` field.\n * 2. Statically scans every `.tsx` / `.jsx` / `.ts` / `.js` file under the\n * project root for `<Icon set=\"…\" name=\"…\" />` usages.\n * 3. Dynamically imports each adapter package (e.g. `@sigx/lynx-icons-fa-free`)\n * and resolves the used glyphs to `{ codepoint, svg }` records.\n * 4. Writes three generated files into `node_modules/.cache/sigx-lynx-icons/`\n * and aliases the `@sigx/lynx-icons/__codepoints` / `__svgs` / `__font-face.css`\n * subpath imports to them, so Rspack tree-shakes everything else away.\n *\n * v1 emits SVG-mode artefacts only. Font-mode (build-time TTF subsetting +\n * base64-inlined @font-face) is a v1.1 follow-up; for now the generated\n * font-face.css is empty.\n *\n * The scanner is a one-shot regex pass at plugin start — adding a new icon\n * during `pnpm dev` requires a dev-server restart in v1. A real SWC-AST\n * Rspack loader is the planned upgrade.\n */\n\nimport { createRequire } from 'node:module';\nimport { promises as fs, existsSync } from 'node:fs';\nimport { join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { RsbuildPluginAPI } from '@rsbuild/core';\nimport type { IconAdapter } from '@sigx/lynx-icons';\nimport type { ResolvedConfig, ResolvedIconSet } from '@sigx/lynx-cli';\n\nconst SCAN_REGEX_SET_FIRST =\n /<Icon\\s+[^>]*?\\bset\\s*=\\s*[\"']([\\w-]+)[\"'][^>]*?\\bname\\s*=\\s*[\"']([\\w-]+)[\"']/g;\nconst SCAN_REGEX_NAME_FIRST =\n /<Icon\\s+[^>]*?\\bname\\s*=\\s*[\"']([\\w-]+)[\"'][^>]*?\\bset\\s*=\\s*[\"']([\\w-]+)[\"']/g;\n\n/** Directories to skip when walking the project. */\nconst SKIP_DIRS = new Set(['node_modules', 'dist', 'ios', 'android', 'Pods', '.git', '.cache', '.rspeedy']);\n\n/** File extensions worth scanning. */\nconst SOURCE_EXT = /\\.(?:tsx?|jsx?)$/;\n\nasync function walkSourceFiles(root: string): Promise<string[]> {\n const out: string[] = [];\n async function walk(dir: string): Promise<void> {\n let entries: Array<{ name: string; isDirectory(): boolean; isFile(): boolean }>;\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n await Promise.all(\n entries.map(async (entry) => {\n if (entry.name.startsWith('.')) return;\n if (SKIP_DIRS.has(entry.name)) return;\n const full = join(dir, entry.name);\n if (entry.isDirectory()) return walk(full);\n if (entry.isFile() && SOURCE_EXT.test(entry.name)) out.push(full);\n }),\n );\n }\n await walk(root);\n return out;\n}\n\nfunction addUsage(used: Map<string, Set<string>>, set: string, name: string): void {\n let bucket = used.get(set);\n if (!bucket) {\n bucket = new Set();\n used.set(set, bucket);\n }\n bucket.add(name);\n}\n\n/**\n * Extract `<Icon set=\"…\" name=\"…\" />` (and the name-first variant) usages\n * from a single source string. Exported for unit testing — the prod path\n * calls this once per file from {@link scanProject}.\n */\nexport function scanContent(content: string): Array<{ set: string; name: string }> {\n if (!content.includes('<Icon')) return [];\n const seen = new Set<string>();\n const out: Array<{ set: string; name: string }> = [];\n const push = (set: string, name: string): void => {\n const key = `${set}\\0${name}`;\n if (seen.has(key)) return;\n seen.add(key);\n out.push({ set, name });\n };\n for (const m of content.matchAll(SCAN_REGEX_SET_FIRST)) push(m[1], m[2]);\n for (const m of content.matchAll(SCAN_REGEX_NAME_FIRST)) push(m[2], m[1]);\n return out;\n}\n\nasync function scanProject(cwd: string): Promise<Map<string, Set<string>>> {\n const used = new Map<string, Set<string>>();\n const files = await walkSourceFiles(cwd);\n await Promise.all(\n files.map(async (file) => {\n const content = await fs.readFile(file, 'utf8').catch(() => '');\n for (const { set, name } of scanContent(content)) addUsage(used, set, name);\n }),\n );\n return used;\n}\n\nasync function loadAdapter(cwd: string, source: string): Promise<IconAdapter | null> {\n try {\n const cwdRequire = createRequire(join(cwd, 'noop.js'));\n const adapterPath = cwdRequire.resolve(source);\n // Wrap with file:// — Windows ESM rejects bare absolute paths in dynamic\n // import(). Same pattern as packages/lynx-cli/src/prebuild.ts loadConfig.\n const mod = (await import(pathToFileURL(adapterPath).href)) as { default?: IconAdapter } & IconAdapter;\n return mod.default ?? (mod as IconAdapter);\n } catch (err) {\n // Adapter not installed — silently skip; consumer will see missing-icon placeholders.\n if (process.env['SIGX_DEBUG_ICONS']) {\n // eslint-disable-next-line no-console\n console.warn(`[@sigx/lynx-plugin] icons: failed to load adapter \"${source}\":`, err);\n }\n return null;\n }\n}\n\ninterface GlyphResult {\n codepoint?: number;\n svg: string;\n}\n\nfunction collectGlyphsForSet(\n adapter: IconAdapter,\n setConfig: ResolvedIconSet,\n usedNames: ReadonlySet<string>,\n): { codepoints: Record<string, number>; svgs: Record<string, GlyphResult> } {\n const codepoints: Record<string, number> = {};\n const svgs: Record<string, GlyphResult> = {};\n const stylesToTry = setConfig.styles ?? adapter.styles;\n\n // v1 contract: codepoint and svg are MUTUALLY EXCLUSIVE per set so the\n // runtime can pick a render strategy without ambiguity. v1 only ships\n // SVG mode (no @font-face CSS generation yet), so we never emit\n // codepoints — even when the adapter returns them. v1.1's font-mode work\n // flips the switch by emitting codepoints + matching @font-face CSS for\n // sets whose `mode` is 'font'.\n const emitCodepoints = false;\n\n for (const name of usedNames) {\n let glyph = null;\n for (const style of stylesToTry) {\n glyph = adapter.getGlyph(style, name);\n if (glyph) break;\n }\n if (!glyph) continue;\n if (emitCodepoints && glyph.codepoint !== undefined) {\n codepoints[name] = glyph.codepoint;\n } else {\n svgs[name] = { svg: glyph.svg };\n }\n }\n return { codepoints, svgs };\n}\n\n/**\n * Wire `@sigx/lynx-icons` adapter packages declared in `signalx.config.ts`.\n * Called from {@link pluginSigxLynx}'s `setup()` after the dev/asset patches.\n */\nexport async function applyIcons(\n api: RsbuildPluginAPI,\n opts: { cwd?: string } = {},\n): Promise<void> {\n const cwd = opts.cwd ?? process.cwd();\n\n // Two layered concerns:\n // 1. No config / lynx-cli not installed → silent no-op (genuine non-Lynx context).\n // 2. Config exists but loadConfig / resolveConfig throws → surface the error so a\n // typo in iconSets (duplicate ids, unknown styles/modes) doesn't silently\n // swallow itself and leave the user wondering why icons render as placeholders.\n const configCandidates = [\n 'signalx.config.ts',\n 'signalx.config.js',\n 'signalx.config.mjs',\n ];\n const hasConfig = configCandidates.some((f) => existsSync(join(cwd, f)));\n if (!hasConfig) return;\n\n let cli: typeof import('@sigx/lynx-cli');\n try {\n cli = (await import('@sigx/lynx-cli')) as typeof import('@sigx/lynx-cli');\n } catch {\n // @sigx/lynx-cli is an optional peer dep; consumer outside a sigx-lynx app — skip.\n return;\n }\n\n // From here errors are real (bad config / failed validation) — let them throw\n // so the build fails loudly. eslint-disable + console.error keeps the message\n // visible even when the throw is wrapped by rsbuild.\n const raw = await cli.loadConfig(cwd);\n const config: ResolvedConfig = cli.resolveConfig(raw);\n\n if (!config.iconSets || config.iconSets.length === 0) return;\n\n const used = await scanProject(cwd);\n\n const codepointsMap: Record<string, Record<string, number>> = {};\n const svgsMap: Record<string, Record<string, GlyphResult>> = {};\n\n for (const setConfig of config.iconSets) {\n const adapter = await loadAdapter(cwd, setConfig.source);\n if (!adapter) continue;\n\n const setUsed = new Set(used.get(setConfig.id) ?? []);\n for (const forced of setConfig.include) setUsed.add(forced);\n\n // `include: ['*']` → ship the full glyph catalog for each configured\n // style. Required for JSON-driven UIs where icon names are unknown\n // at build time. Trade-off: bundle grows by hundreds of KB.\n if (setConfig.include.includes('*')) {\n setUsed.delete('*');\n const stylesToTry = setConfig.styles ?? adapter.styles;\n for (const style of stylesToTry) {\n for (const name of adapter.listGlyphs(style)) setUsed.add(name);\n }\n // eslint-disable-next-line no-console\n console.log(\n `[@sigx/lynx-plugin] icons: ${setConfig.id} bundling ${setUsed.size} glyphs (include: ['*'])`,\n );\n }\n\n if (setUsed.size === 0) continue;\n\n const { codepoints, svgs } = collectGlyphsForSet(adapter, setConfig, setUsed);\n if (Object.keys(codepoints).length > 0) codepointsMap[setConfig.id] = codepoints;\n if (Object.keys(svgs).length > 0) svgsMap[setConfig.id] = svgs;\n }\n\n // Persist generated modules into the project's pnpm cache dir.\n const cacheDir = join(cwd, 'node_modules', '.cache', 'sigx-lynx-icons');\n await fs.mkdir(cacheDir, { recursive: true });\n const codepointsPath = join(cacheDir, 'codepoints.mjs');\n const svgsPath = join(cacheDir, 'svgs.mjs');\n const fontFacePath = join(cacheDir, 'font-face.css');\n\n await fs.writeFile(\n codepointsPath,\n `// Auto-generated by @sigx/lynx-plugin — do not edit.\\nexport const codepoints = ${JSON.stringify(codepointsMap)};\\n`,\n );\n await fs.writeFile(\n svgsPath,\n `// Auto-generated by @sigx/lynx-plugin — do not edit.\\nexport const svgs = ${JSON.stringify(svgsMap)};\\n`,\n );\n await fs.writeFile(\n fontFacePath,\n '/* Auto-generated by @sigx/lynx-plugin — font mode lands in v1.1. */\\n',\n );\n\n // Alias the three subpath imports to the generated files.\n api.modifyBundlerChain((chain) => {\n chain.resolve.alias.set('@sigx/lynx-icons/__codepoints', codepointsPath);\n chain.resolve.alias.set('@sigx/lynx-icons/__svgs', svgsPath);\n chain.resolve.alias.set('@sigx/lynx-icons/__font-face.css', fontFacePath);\n });\n}\n","/**\n * @packageDocumentation\n *\n * An rsbuild / rspeedy plugin that integrates SignalX with Lynx's dual-thread\n * architecture (Background Thread renderer + Main Thread PAPI executor).\n *\n * @example\n * ```ts\n * // lynx.config.ts\n * import { defineConfig } from '@lynx-js/rspeedy'\n * import { pluginSigxLynx } from '@sigx/lynx-plugin'\n *\n * export default defineConfig({\n * plugins: [pluginSigxLynx()],\n * })\n * ```\n */\n\nimport { networkInterfaces } from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { RsbuildPlugin } from '@rsbuild/core';\n\nimport { applyCSS } from './css';\nimport { applyEntry } from './entry';\nimport { applyIcons } from './icons';\nimport { LAYERS } from './layers';\n\nexport { LAYERS, applyEntry };\n\nconst _pluginDirname = path.dirname(fileURLToPath(import.meta.url));\nconst _sigxLynxRoot = path.resolve(_pluginDirname, '../..');\n\n/** Wildcard addresses that bind to all interfaces but aren't routable from other devices. */\nconst WILDCARD_HOSTS = new Set(['0.0.0.0', '::', '0:0:0:0:0:0:0:0']);\n\n/**\n * Interface names that are virtual adapters (Hyper-V, WSL, Docker, VPN, etc.)\n * and should be skipped when looking for the real LAN address.\n */\nconst VIRTUAL_IF_PATTERNS = /^(vEthernet|veth|docker|br-|virbr|vmnet|VirtualBox|Hyper-V|WSL|ham\\d)/i;\n\n/**\n * Detect the real LAN IPv4 address on this machine.\n * Skips virtual/container adapters (Hyper-V, WSL, Docker) and prefers\n * physical interfaces like Wi-Fi or Ethernet.\n * Falls back to the first external IPv4 if no physical match is found,\n * and ultimately to `'127.0.0.1'`.\n */\nfunction detectLanIPv4(): string {\n const ifaces = networkInterfaces();\n let fallback: string | undefined;\n\n for (const [name, nets] of Object.entries(ifaces)) {\n for (const net of nets ?? []) {\n if (net.family !== 'IPv4' || net.internal || !net.address) continue;\n // Remember first external address as fallback\n if (!fallback) fallback = net.address;\n // Skip virtual adapters\n if (VIRTUAL_IF_PATTERNS.test(name)) continue;\n return net.address;\n }\n }\n return fallback ?? '127.0.0.1';\n}\n\n/** Extract the hostname from a URL string (may be inside JSON quotes). */\nfunction extractHost(s: string): string {\n const m = s.match(/\\/\\/([^:/]+)/);\n return m ? m[1] : '';\n}\n\n/**\n * Options for {@link pluginSigxLynx}.\n * @public\n */\nexport interface PluginSigxLynxOptions {\n /**\n * Whether to enable CSS selector support in the Lynx template.\n * @defaultValue true\n */\n enableCSSSelector?: boolean;\n\n /**\n * Whether to enable CSS inheritance in the Lynx engine.\n * @defaultValue false\n */\n enableCSSInheritance?: boolean;\n\n /**\n * A list of additional CSS properties to inherit beyond the engine defaults.\n * Only effective when {@link enableCSSInheritance} is `true`.\n */\n customCSSInheritanceList?: string[];\n\n /**\n * Whether to place debug info outside the template bundle.\n * @defaultValue true\n */\n debugInfoOutside?: boolean;\n}\n\n/**\n * Create an rsbuild / rspeedy plugin for SignalX-Lynx dual-thread rendering.\n *\n * @public\n */\nexport function pluginSigxLynx(\n options: PluginSigxLynxOptions = {},\n): RsbuildPlugin {\n const {\n enableCSSSelector: _enableCSSSelector = true,\n enableCSSInheritance: _enableCSSInheritance = false,\n customCSSInheritanceList: _customCSSInheritanceList,\n debugInfoOutside: _debugInfoOutside = true,\n } = options;\n\n return {\n name: 'lynx:sigx',\n // Must run after rspeedy's own config plugins (including pluginDev for URL fixes)\n pre: ['lynx:rsbuild:plugin-api', 'lynx:config', 'lynx:rsbuild:dev'],\n\n async setup(api) {\n api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => {\n // Compile all JS files (including node_modules) for ES2019 compat\n // with the Lynx JS engine, unless user explicitly sets source.include.\n const userConfig = api.getRsbuildConfig('original');\n if (typeof userConfig.source?.include === 'undefined') {\n config = mergeRsbuildConfig(config, {\n source: {\n include: [/\\.(?:js|mjs|cjs)$/],\n },\n });\n }\n\n // Honour `SIGX_LYNX_DEV_PORT` set by `@sigx/lynx-cli`. Rspeedy's CLI\n // has no `--port` flag, so the CLI plumbs its computed port (from\n // `sigx dev --port N` or the lynx-cli default) through this env var\n // and we override `server.port` here. Without this, `serverState.port`\n // on the lynx-cli side (used to build the device-launch URL) could\n // diverge from whatever the user's `lynx.config.ts` set — and the\n // device would boot pointing at a server that isn't there.\n const envPort = process.env['SIGX_LYNX_DEV_PORT'];\n const portOverride = envPort && Number.isFinite(Number(envPort))\n ? Number(envPort)\n : undefined;\n if (portOverride !== undefined) {\n config = mergeRsbuildConfig(config, {\n server: { port: portOverride },\n });\n }\n\n return mergeRsbuildConfig(config, {\n source: {\n define: {\n __DEV__: 'process.env.NODE_ENV !== \\'production\\'',\n },\n },\n tools: {\n rspack: {\n output: {\n iife: false,\n },\n },\n swc: {\n jsc: {\n target: 'es2019',\n transform: {\n react: {\n runtime: 'automatic',\n importSource: '@sigx/lynx',\n throwIfNamespace: false,\n },\n },\n },\n },\n },\n });\n });\n\n api.modifyBundlerChain((chain) => {\n chain.resolve.alias.set(\n '@sigx/runtime-dom',\n '@sigx/lynx-runtime',\n );\n });\n\n // rspeedy's pluginDev uses `server.host` as the hostname for HMR\n // client URLs (publicPath, WebSocket URL, printUrls). When the user\n // sets server.host to '0.0.0.0' (bind all interfaces), those URLs\n // become unreachable from external devices (phones, emulators).\n //\n // Fix at the rsbuild config level (runs AFTER rspeedy's hooks thanks\n // to 'lynx:rsbuild:dev' in `pre`): replace wildcard hosts with the\n // actual LAN IP in dev.assetPrefix, dev.client.host, output.assetPrefix,\n // and the server.printUrls function.\n api.modifyRsbuildConfig((config, { mergeRsbuildConfig }) => {\n const devAssetPrefix = config.dev?.assetPrefix;\n if (typeof devAssetPrefix !== 'string') return config;\n\n // Only fix if the assetPrefix contains a wildcard host\n let needsFix = false;\n for (const wh of WILDCARD_HOSTS) {\n if (devAssetPrefix.includes(`//${wh}:`)) {\n needsFix = true;\n break;\n }\n }\n if (!needsFix) return config;\n\n const lanIP = detectLanIPv4();\n const replaceWildcard = (s: string): string => {\n for (const wh of WILDCARD_HOSTS) {\n s = s.replaceAll(`//${wh}:`, `//${lanIP}:`);\n }\n return s;\n };\n\n const fixedAssetPrefix = replaceWildcard(devAssetPrefix);\n\n // Override printUrls to show the correct LAN IP URL.\n // rspeedy's printUrls uses closure variables that still hold '0.0.0.0',\n // so we must replace the function entirely.\n const existingPrintUrls = config.server?.printUrls;\n const printUrlsFn = typeof existingPrintUrls === 'function'\n ? (param: Parameters<typeof existingPrintUrls>[0]) => {\n // Call rspeedy's original printUrls to get the URL list,\n // then fix the hostnames in each URL.\n const result = existingPrintUrls(param);\n if (Array.isArray(result)) {\n return result.map((item: any) =>\n typeof item === 'string'\n ? replaceWildcard(item)\n : { ...item, url: replaceWildcard(item.url) }\n );\n }\n return result;\n }\n : undefined;\n\n const merged = mergeRsbuildConfig(config, {\n dev: {\n assetPrefix: fixedAssetPrefix,\n client: {\n host: lanIP,\n },\n },\n output: {\n assetPrefix: fixedAssetPrefix,\n },\n });\n\n // Direct assignment — mergeRsbuildConfig can't reliably merge functions\n if (printUrlsFn) {\n merged.server = { ...merged.server, printUrls: printUrlsFn };\n }\n\n return merged;\n });\n\n // Rspack's default watcher-ignore is only /node_modules|\\.git/. In Lynx\n // app layouts the ios/ Pods tree and dist/ output drown macOS FSEvents,\n // causing edits to src/*.tsx to silently not fire rebuilds. Narrow the\n // watched set and stop chasing symlinks through pnpm's .pnpm/ store.\n //\n // Upstream: fixed in Rspack 2.0 (`fix(watcher): filter stale FSEvents\n // with mtime baseline comparison`). Rspeedy 0.14.2 still pins Rspack\n // 1.7.10, so we can't adopt the real fix yet — revisit when rspeedy\n // bumps to Rspack 2.0 and we can drop this hook.\n api.modifyRspackConfig((rspackConfig) => {\n const existing = rspackConfig.watchOptions ?? {};\n const existingIgnored = Array.isArray(existing.ignored)\n ? existing.ignored\n : typeof existing.ignored === 'string'\n ? [existing.ignored]\n : [];\n\n rspackConfig.watchOptions = {\n ...existing,\n ignored: [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/ios/**',\n '**/android/**',\n '**/Pods/**',\n '**/.rspeedy/**',\n ...existingIgnored,\n ],\n followSymlinks: existing.followSymlinks ?? false,\n poll: existing.poll\n ?? (process.env.SIGX_LYNX_WATCH_POLL\n ? Number(process.env.SIGX_LYNX_WATCH_POLL) || true\n : undefined),\n };\n });\n\n // Belt-and-suspenders: also patch at the rspack config level in case\n // the rsbuild-level fix didn't propagate everywhere (e.g. resolve\n // aliases set by rspeedy's modifyBundlerChain using closure variables).\n api.modifyRspackConfig((rspackConfig) => {\n // Check if publicPath or any resolve alias contains a wildcard host\n let needsFix = false;\n const publicPath = rspackConfig.output?.publicPath;\n if (typeof publicPath === 'string') {\n for (const wh of WILDCARD_HOSTS) {\n if (publicPath.includes(`//${wh}:`)) { needsFix = true; break; }\n }\n }\n if (!needsFix) {\n const aliases = rspackConfig.resolve?.alias;\n if (aliases && typeof aliases === 'object' && !Array.isArray(aliases)) {\n for (const val of Object.values(aliases)) {\n if (typeof val === 'string') {\n for (const wh of WILDCARD_HOSTS) {\n if (val.includes(`hostname=${wh}`)) { needsFix = true; break; }\n }\n }\n if (needsFix) break;\n }\n }\n }\n if (!needsFix) return;\n\n const lanIP = detectLanIPv4();\n const replaceWildcard = (s: string): string => {\n for (const wh of WILDCARD_HOSTS) {\n s = s.replaceAll(`//${wh}:`, `//${lanIP}:`);\n s = s.replaceAll(`hostname=${wh}`, `hostname=${lanIP}`);\n }\n return s;\n };\n\n // Fix output.publicPath (used for hot-update fetch URLs)\n if (rspackConfig.output) {\n rspackConfig.output.publicPath = replaceWildcard(\n rspackConfig.output.publicPath as string,\n );\n }\n\n // Fix the resolve alias for @lynx-js/webpack-dev-transport/client\n // which embeds hostname=0.0.0.0 in query params for the WebSocket URL\n const aliases = rspackConfig.resolve?.alias;\n if (aliases && typeof aliases === 'object' && !Array.isArray(aliases)) {\n for (const [key, val] of Object.entries(aliases)) {\n if (typeof val === 'string') {\n const fixed = replaceWildcard(val);\n if (fixed !== val) {\n (aliases as Record<string, string>)[key] = fixed;\n }\n }\n }\n }\n\n // Fix ASSET_PREFIX in DefinePlugin definitions — these are stringified\n // JSON values so the wildcard appears inside quoted strings.\n for (const plugin of rspackConfig.plugins ?? []) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const defs = (plugin as any)?.definitions ?? (plugin as any)?._args?.[0];\n if (!defs || typeof defs !== 'object') continue;\n for (const [k, v] of Object.entries(defs)) {\n if (typeof v === 'string' && WILDCARD_HOSTS.has(extractHost(v))) {\n (defs as Record<string, string>)[k] = replaceWildcard(v);\n } else if (typeof v === 'object' && v !== null) {\n for (const [k2, v2] of Object.entries(v as Record<string, string>)) {\n if (typeof v2 === 'string' && WILDCARD_HOSTS.has(extractHost(v2))) {\n (v as Record<string, string>)[k2] = replaceWildcard(v2);\n }\n }\n }\n }\n }\n\n // Fix SourceMapDevToolPlugin publicPath\n for (const plugin of rspackConfig.plugins ?? []) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const opts = (plugin as any)?._options ?? (plugin as any)?._args?.[0];\n if (opts && typeof opts.publicPath === 'string') {\n opts.publicPath = replaceWildcard(opts.publicPath);\n }\n }\n });\n\n // Wire CSS handling — forces extraction via @lynx-js/css-extract-webpack-plugin,\n // strips lightningcss, and configures ignore-css-loader for main-thread layer.\n applyCSS(api, {\n enableCSSSelector: _enableCSSSelector,\n enableCSSInvalidation: _enableCSSInheritance,\n });\n\n // Wire dual-thread entry splitting (worklets skipped in v1)\n await applyEntry(api, {\n debugInfoOutside: _debugInfoOutside,\n enableCSSInheritance: _enableCSSInheritance,\n customCSSInheritanceList: _customCSSInheritanceList,\n });\n\n // Wire @sigx/lynx-icons — reads iconSets from signalx.config.ts,\n // scans the project for <Icon> usage, and aliases the runtime's\n // virtual-module subpaths to generated codepoint / SVG maps.\n // Safe to call unconditionally; bails out when no iconSets are\n // configured or @sigx/lynx-cli isn't installed.\n await applyIcons(api);\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hmr-loader.js","names":[],"sources":["../../src/loaders/hmr-loader.ts"],"sourcesContent":["/**\n * Rspack loader that injects HMR self-acceptance into component files.\n *\n * For every file that contains a `component(` or `component<` call, the\n * loader prepends:\n * 1. An import of `__registerComponentPlugin` from `@sigx/lynx` — this\n * is the *same* bundle the app uses, so the plugin array is shared.\n * 2. An import of `initHMR` + `registerHMRModule` from `@sigx/lynx-runtime/hmr`.\n * 3. Calls to wire them up: `initHMR(__registerComponentPlugin)` (idempotent)\n * and `registerHMRModule(moduleId)`.\n * 4. (If `signal` is imported) A wrapper that preserves signal objects\n * across HMR cycles via `module.hot.data`, so module-level reactive\n * state (e.g., a route signal) doesn't reset and cause structural tree\n * mutations that crash the Lynx native engine.\n *\n * And appends `module.hot.accept()` + a dispose handler that snapshots\n * preserved signals into `module.hot.data` for the next execution.\n *\n * On re-execution the HMR runtime patches existing component instances\n * in-place — only property-level ops are emitted, avoiding the structural\n * tree mutations that crash Lynx's native engine.\n */\n\nimport type { Rspack } from '@rsbuild/core';\n\n// Matches `component(` or `component<`\nconst COMPONENT_RE = /\\bcomponent\\s*[<(]/;\n\n// Matches an import statement from @sigx/lynx that destructures `signal`.\n// Captures: (1) before-signal names, (2) after-signal names, (3) quote char\n// e.g. import { signal, component } from '@sigx/lynx';\n// group 1: \"\" group 2: \", component \" group 3: \"'\"\nconst SIGNAL_IMPORT_RE =\n /import\\s*\\{([^}]*)\\bsignal\\b([^}]*)\\}\\s*from\\s*(['\"])@sigx\\/lynx\\3/;\n\nexport default function hmrLoader(\n this: Rspack.LoaderContext,\n source: string,\n): string {\n this.cacheable(false);\n\n if (!COMPONENT_RE.test(source)) {\n return source;\n }\n\n const moduleId = this.resourcePath\n .replace(/\\\\/g, '/')\n .replace(/\\.(tsx?|jsx)$/, '');\n\n // Check if the file imports `signal` from @sigx/lynx\n const signalImportMatch = source.match(SIGNAL_IMPORT_RE);\n const hasSignalImport = !!signalImportMatch;\n\n let transformedSource = source;\n\n if (hasSignalImport) {\n // Rename `signal` → `__origSignal` in the import statement so we can\n // shadow it with a preserving wrapper. All call-sites automatically\n // use the wrapper because the local binding name stays `signal`.\n transformedSource = source.replace(\n SIGNAL_IMPORT_RE,\n (_match, before, after, quote) =>\n `import {${before}signal as __origSignal${after}} from ${quote}@sigx/lynx${quote}`,\n );\n }\n\n const header: string[] = [\n `import { __registerComponentPlugin, __setCurrentInstanceForHMR } from '@sigx/lynx';`,\n `import { initHMR, registerHMRModule } from '@sigx/lynx-runtime/hmr';`,\n `initHMR(__registerComponentPlugin, __setCurrentInstanceForHMR);`,\n `registerHMRModule(${JSON.stringify(moduleId)});`,\n ];\n\n if (hasSignalImport) {\n // Inject a `signal` wrapper that returns the cached signal object from\n // the previous HMR cycle when one exists, or delegates to the real\n // `signal()` for new signals. This preserves module-level reactive\n // state (and component-level state inside setup — matching React Fast\n // Refresh behaviour where hooks state is preserved across HMR).\n header.push(\n `var __hmrSigPrev = (typeof module !== 'undefined' && module.hot && module.hot.data && module.hot.data.__hmrSigs) || {};`,\n `var __hmrSigStore = {};`,\n `var __hmrSigIdx = 0;`,\n `function signal() {`,\n ` var k = 's' + __hmrSigIdx++;`,\n ` if (k in __hmrSigPrev) { __hmrSigStore[k] = __hmrSigPrev[k]; return __hmrSigPrev[k]; }`,\n ` var s = __origSignal.apply(null, arguments);`,\n ` __hmrSigStore[k] = s;`,\n ` return s;`,\n `}`,\n );\n }\n\n header.push('');\n\n const footer: string[] = [''];\n\n if (hasSignalImport) {\n footer.push(\n `if (typeof module !== 'undefined' && module.hot) {`,\n ` module.hot.dispose(function(data) { data.__hmrSigs = __hmrSigStore; });`,\n ` module.hot.accept();`,\n `}`,\n );\n } else {\n footer.push(\n `if (typeof module !== 'undefined' && module.hot) { module.hot.accept(); }`,\n );\n }\n\n return header.join('\\n') + transformedSource + footer.join('\\n');\n}\n"],"mappings":";AA0BA,IAAM,IAAe,sBAMf,IACJ;AAEF,SAAwB,EAEtB,GACQ;CAGR,IAFA,KAAK,UAAU,GAAM,EAEjB,CAAC,EAAa,KAAK,EAAO,EAC5B,OAAO;CAGT,IAAM,IAAW,KAAK,aACnB,QAAQ,OAAO,IAAI,CACnB,QAAQ,iBAAiB,GAAG,EAIzB,IAAkB,CAAC,CADC,EAAO,MAAM,EACb,EAEtB,IAAoB;CAExB,AAAI,MAIF,IAAoB,EAAO,QACzB,IACC,GAAQ,GAAQ,GAAO,MACtB,WAAW,EAAO,wBAAwB,EAAM,SAAS,EAAM,YAAY,IAC9E;CAGH,IAAM,IAAmB;EACvB;EACA;EACA;EACA,qBAAqB,KAAK,UAAU,EAAS,CAAC;EAC/C;CAsBD,AApBI,KAMF,EAAO,KACL,2HACA,2BACA,wBACA,uBACA,kCACA,4FACA,kDACA,2BACA,eACA,IACD,EAGH,EAAO,KAAK,GAAG;CAEf,IAAM,IAAmB,CAAC,GAAG;CAe7B,OAbI,IACF,EAAO,KACL,sDACA,6EACA,0BACA,IACD,GAED,EAAO,KACL,4EACD,EAGI,EAAO,KAAK,KAAK,GAAG,IAAoB,EAAO,KAAK,KAAK"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ignore-css-loader.js","names":[],"sources":["../../src/loaders/ignore-css-loader.ts"],"sourcesContent":["/**\n * Webpack/Rspack loader that strips CSS from Main-Thread bundles.\n *\n * In the Lynx dual-thread model, only the Background Thread needs CSS.\n * The Main-Thread layer uses `exportOnlyLocals` from css-loader to get\n * CSS module class name mappings without actual styles.\n */\n\nimport type { Rspack } from '@rsbuild/core';\n\nexport default function ignoreCssLoader(\n this: Rspack.LoaderContext,\n source: string,\n): string {\n this.cacheable(true);\n\n // If the source contains ___CSS_LOADER_EXPORT___, it is not a CSS Modules\n // file (exportOnlyLocals is enabled), so we don't need to preserve it.\n if (source.includes('___CSS_LOADER_EXPORT___')) {\n return 'export {}';\n }\n\n // Preserve CSS modules export for background layer.\n return source;\n}\n"],"mappings":";AAUA,SAAwB,EAEtB,GACQ;CAUR,OATA,KAAK,UAAU,GAAK,EAIhB,EAAO,SAAS,0BAA0B,GACrC,cAIF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worklet-loader-mt.js","names":[],"sources":["../../src/loaders/worklet-utils.ts","../../src/loaders/worklet-loader-mt.ts"],"sourcesContent":["/**\n * Helpers shared by worklet-loader (BG) and worklet-loader-mt (MT).\n *\n * Mirrors vue-lynx's `worklet-utils.ts` minus the `?vue` sub-module logic\n * (sigx has no Vue Single-File-Component pipeline) and minus the\n * `extractSharedImports` path (deferred to Phase 1c — see plan).\n */\n\n/**\n * Extract import statements whose target may contain `'main thread'` worklets\n * the MT bundle needs to register.\n *\n * Two import flavours qualify:\n * - Relative paths (`./foo`, `../bar`) — user code split across files.\n * - `@sigx/*` package paths — workspace component packages like\n * `@sigx/gestures` that ship `<Pressable>`/`<Draggable>` etc. These resolve\n * to workspace `src/` (not node_modules) so the MT loader's rule still\n * processes them; we just need to preserve the edge so rspack walks there.\n *\n * Converts named/default/namespace imports to side-effect-only imports\n * (`import '@sigx/gestures';`) so webpack still follows the dependency graph\n * without executing user code on the MT layer.\n *\n * Critical for the MT loader: an entry like `main.tsx` may not contain any\n * `'main thread'` directives itself, but it imports route/component files\n * that do. Without preserving these edges, webpack would never reach the\n * files with worklet registrations and MT-side hydration of BG worklet ctxs\n * would throw \"cannot read property bind of undefined\" when the unregistered\n * _wkltId is looked up in `lynxWorkletImpl._workletMap`.\n */\nexport function extractLocalImports(source: string): string {\n // Strip line and block comments so docstring examples like\n // `* import X from './foo'` don't get parsed as real imports.\n // String contents are left intact (we still catch `import './foo'` inside\n // a string template), but real source rarely encodes import-statement\n // shapes inside strings, and the false-positive rate with comments stripped\n // is acceptable for this preserve-edges-only loader.\n const stripped = source\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/(^|[^:])\\/\\/[^\\n]*/g, '$1');\n\n const specifiers = new Set<string>();\n\n // Relative paths (./foo, ../bar) and @sigx/* package paths.\n const re = /(?:from|import)\\s+['\"]((?:\\.[^'\"]+)|(?:@sigx\\/[^'\"]+))['\"]/g;\n let match: RegExpExecArray | null;\n while ((match = re.exec(stripped)) !== null) {\n specifiers.add(match[1]!);\n }\n\n if (specifiers.size === 0) return '';\n return [...specifiers].map((s) => `import '${s}';`).join('\\n');\n}\n\n/**\n * Extract `registerWorkletInternal(...)` calls from a LEPUS-target transform output.\n *\n * Upstream's LEPUS output includes:\n * - `import { loadWorkletRuntime as __loadWorkletRuntime } from \"<runtimePkg>\";`\n * - `var loadWorkletRuntime = __loadWorkletRuntime;`\n * - the original component code (which we drop — never invoked on MT)\n * - `const __workletRuntimeLoaded = loadWorkletRuntime(...);`\n * - `__workletRuntimeLoaded && registerWorkletInternal(\"main-thread\", \"<id>\", function(...) { ... });`\n *\n * We only need the `registerWorkletInternal(...)` calls — the loadWorkletRuntime\n * gating is redundant because we only inject the calls when the MT bundle is\n * actually being built. Bracket-depth counting handles nested braces in the\n * function body.\n */\nexport function extractRegistrations(lepusCode: string): string {\n const out: string[] = [];\n const marker = 'registerWorkletInternal(';\n let from = 0;\n\n while (true) {\n const idx = lepusCode.indexOf(marker, from);\n if (idx === -1) break;\n\n let depth = 0;\n let i = idx + marker.length - 1; // points at the opening '('\n for (; i < lepusCode.length; i++) {\n const ch = lepusCode[i];\n if (ch === '(') depth++;\n else if (ch === ')') {\n depth--;\n if (depth === 0) break;\n }\n }\n\n let end = i + 1;\n if (end < lepusCode.length && lepusCode[end] === ';') end++;\n out.push(lepusCode.slice(idx, end));\n from = end;\n }\n\n return out.join('\\n');\n}\n","/**\n * MT-layer rspack loader for the `'main thread'` worklet directive.\n *\n * Runs on every JS/TS file in the MT layer — no rule-level exclude.\n * Decides per-file what to emit based on directive presence and whether\n * the file is library code (`node_modules/` or `dist/`) or user code:\n *\n * | Origin | Directive? | Output |\n * |--------------|------------|------------------------------------------------|\n * | user code | no | bootstrap preamble + side-effect imports |\n * | | | (drops body — sigx's Lepus never executes |\n * | | | React components; `entry-main.ts`'s |\n * | | | `renderPage` builds a placeholder and ops |\n * | | | arrive from BG via `sigxPatchUpdate`) |\n * | user code | yes | bootstrap preamble + side-effect imports |\n * | | | + extracted `registerWorkletInternal(...)` |\n * | library | no | source unchanged |\n * | | | (preserves `@sigx/lynx-runtime-main`'s MT |\n * | | | globals — `processData`, `updateGlobalProps`,|\n * | | | `sigxRunOnMT` — and barrel re-exports that |\n * | | | BG-side consumers like daisyui import by |\n * | | | name; rspack shares module identity across |\n * | | | BG/MT layers) |\n * | library | yes | source unchanged + appended registrations |\n * | | | (keeps named exports AND registers worklets) |\n *\n * Library files skip the bootstrap preamble — the user entry's\n * preamble has already pulled runtime-main in before any library code\n * evaluates.\n *\n * For files WITH a directive, the LEPUS transform's output is sliced\n * via `extractRegistrations` so only the `registerWorkletInternal(...)`\n * calls are kept. The `loadWorkletRuntime` import that upstream emits\n * is dropped — `registerWorkletInternal` is installed as a global by\n * `entry-main.ts`.\n *\n * Mirrors vue-lynx's `worklet-loader-mt.ts`, minus the `?vue` sub-module\n * branch (sigx has no Vue SFC pipeline) and minus the shared-imports path\n * (deferred to Phase 1c).\n */\n\nimport type { Rspack } from '@rsbuild/core';\nimport { transformReactLynxSync } from '@lynx-js/react/transform';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport { extractLocalImports, extractRegistrations } from './worklet-utils';\n\nconst RUNTIME_PKG = '@sigx/lynx-runtime-main';\n\n/**\n * Prepended to every MT-layer file so webpack's dep graph guarantees the\n * bootstrap modules evaluate BEFORE any user code that calls\n * `registerWorkletInternal(...)`. Listing these as separate entries in\n * `entry.ts` doesn't suffice — rspack walks the chunk graph, which can\n * eval user code first when nothing else explicitly depends on bootstrap.\n *\n * Order matters: entry-main's module body sets globalThis.SystemInfo before\n * worklet-runtime's IIFE reads it.\n *\n * Paths are resolved to absolute file URIs at loader-init time so user apps\n * don't need to declare @lynx-js/react as a direct dep — pnpm hoists it into\n * our package's node_modules and we hand rspack the resolved path. We resolve\n * the package's `package.json` (always reachable, no exports-map games), read\n * the dist subpath relative to it. createRequire(import.meta.url) lets us\n * walk up from the loader's install location via Node's CJS resolver.\n */\nconst _req = createRequire(import.meta.url);\n\nfunction resolvePackageSubpath(pkgName: string, subpath: string): string {\n const pkgJsonPath = _req.resolve(`${pkgName}/package.json`);\n return join(dirname(pkgJsonPath), subpath);\n}\n\nconst ENTRY_MAIN_PATH = resolvePackageSubpath(\n '@sigx/lynx-runtime-main',\n 'dist/entry-main.js',\n);\nconst WORKLET_RUNTIME_PATH = resolvePackageSubpath(\n '@lynx-js/react',\n 'runtime/worklet-runtime/main.js',\n);\nconst INSTALL_HYBRID_PATH = resolvePackageSubpath(\n '@sigx/lynx-runtime-main',\n 'dist/install-hybrid-worklet.js',\n);\n\nconst BOOTSTRAP_PREAMBLE =\n `import ${JSON.stringify(ENTRY_MAIN_PATH)};\\n`\n + `import ${JSON.stringify(WORKLET_RUNTIME_PATH)};\\n`\n + `import ${JSON.stringify(INSTALL_HYBRID_PATH)};\\n`;\n\n// Cheap pre-filter to skip the SWC parse for files that obviously don't\n// contain a worklet directive. A directive is always followed immediately\n// by a statement terminator — either `;` or a newline (ASI). Requiring\n// one of those after the closing quote rejects substrings inside\n// single-line error strings like\n// `\"...inside 'main thread' functions...\"` from `@sigx/lynx-runtime`'s\n// dist, where the next char is a space then `functions`.\n//\n// This is not a parser. A truly adversarial input (e.g. the literal\n// string `\"'main thread';\"`) can slip through, and SWC is the final\n// arbiter — for such files it produces no registrations and the MT\n// output reduces to the library branch's source pass-through (for\n// library paths) or to the no-directive user branch's strip (for user\n// paths). Either way: correct, just with the parse work done.\nconst DIRECTIVE_RE = /['\"]main thread['\"]\\s*(?:;|\\n)/;\n\n// Library paths (`node_modules/` and any `dist/`) get the body-preserve\n// branches above. Rspack shares module identity across BG/MT layers, so\n// stripping the MT-side body of a library file would wipe its named\n// exports for BG consumers too — daisyui couldn't resolve `useTabs` from\n// lynx-navigation, runtime-main's MT globals would disappear, etc.\nconst LIBRARY_PATH_RE = /[\\\\/](?:node_modules|dist)[\\\\/]/;\n\nexport default function workletLoaderMT(\n this: Rspack.LoaderContext,\n source: string,\n): string {\n this.cacheable(true);\n\n const localImports = extractLocalImports(source);\n\n if (!DIRECTIVE_RE.test(source)) {\n if (LIBRARY_PATH_RE.test(this.resourcePath)) {\n return source;\n }\n return BOOTSTRAP_PREAMBLE + localImports;\n }\n\n const filename = this.resourcePath;\n const result = transformReactLynxSync(source, {\n pluginName: 'sigx:worklet-mt',\n filename,\n sourcemap: false,\n cssScope: false,\n shake: false,\n compat: false,\n refresh: false,\n defineDCE: false,\n directiveDCE: false,\n snapshot: false,\n worklet: { target: 'LEPUS', filename, runtimePkg: RUNTIME_PKG },\n });\n\n if (result.errors && result.errors.length > 0) {\n for (const err of result.errors) {\n this.emitError(new Error(`[sigx-worklet-mt] LEPUS transform: ${err.text}`));\n }\n return localImports;\n }\n\n const registrations = extractRegistrations(result.code);\n\n // Library files with a directive (e.g. lynx-navigation's `EdgeBackHandle.js`,\n // `navigator/core.js`): preserve the original source so its named exports\n // survive cross-layer module identity (BG-side consumers like daisyui\n // import them by name), and append the `registerWorkletInternal` calls so\n // the directives are still registered on the MT side. No bootstrap\n // preamble — user entry code's preamble has already pulled runtime-main\n // in before any library code is evaluated.\n if (LIBRARY_PATH_RE.test(this.resourcePath)) {\n return registrations ? `${source}\\n${registrations}` : source;\n }\n\n return BOOTSTRAP_PREAMBLE + [localImports, registrations].filter(Boolean).join('\\n');\n}\n"],"mappings":";;;;AA8BA,SAAgB,EAAoB,GAAwB;CAO1D,IAAM,IAAW,EACd,QAAQ,qBAAqB,GAAG,CAChC,QAAQ,uBAAuB,KAAK,EAEjC,oBAAa,IAAI,KAAa,EAG9B,IAAK,+DACP;CACJ,QAAQ,IAAQ,EAAG,KAAK,EAAS,MAAM,OACrC,EAAW,IAAI,EAAM,GAAI;CAI3B,OADI,EAAW,SAAS,IAAU,KAC3B,CAAC,GAAG,EAAW,CAAC,KAAK,MAAM,WAAW,EAAE,IAAI,CAAC,KAAK,KAAK;;AAkBhE,SAAgB,EAAqB,GAA2B;CAC9D,IAAM,IAAgB,EAAE,EAEpB,IAAO;CAEX,SAAa;EACX,IAAM,IAAM,EAAU,QAAQ,4BAAQ,EAAK;EAC3C,IAAI,MAAQ,IAAI;EAEhB,IAAI,IAAQ,GACR,IAAI,IAAM,KAAgB;EAC9B,OAAO,IAAI,EAAU,QAAQ,KAAK;GAChC,IAAM,IAAK,EAAU;GACrB,IAAI,MAAO,KAAK;QACX,IAAI,MAAO,QACd,KACI,MAAU,IAAG;;EAIrB,IAAI,IAAM,IAAI;EAGd,AAFI,IAAM,EAAU,UAAU,EAAU,OAAS,OAAK,KACtD,EAAI,KAAK,EAAU,MAAM,GAAK,EAAI,CAAC,EACnC,IAAO;;CAGT,OAAO,EAAI,KAAK,KAAK;;;;AChDvB,IAAM,IAAc,2BAmBd,IAAO,EAAc,OAAO,KAAK,IAAI;AAE3C,SAAS,EAAsB,GAAiB,GAAyB;CAEvE,OAAO,EAAK,EADQ,EAAK,QAAQ,GAAG,EAAQ,eACxB,CAAY,EAAE,EAAQ;;AAG5C,IAAM,IAAkB,EACtB,2BACA,qBACD,EACK,IAAuB,EAC3B,kBACA,kCACD,EACK,IAAsB,EAC1B,2BACA,iCACD,EAEK,IACJ,UAAU,KAAK,UAAU,EAAgB,CAAC,YAC9B,KAAK,UAAU,EAAqB,CAAC,YACrC,KAAK,UAAU,EAAoB,CAAC,MAgB5C,IAAe,kCAOf,IAAkB;AAExB,SAAwB,EAEtB,GACQ;CACR,KAAK,UAAU,GAAK;CAEpB,IAAM,IAAe,EAAoB,EAAO;CAEhD,IAAI,CAAC,EAAa,KAAK,EAAO,EAI5B,OAHI,EAAgB,KAAK,KAAK,aAAa,GAClC,IAEF,IAAqB;CAG9B,IAAM,IAAW,KAAK,cAChB,IAAS,EAAuB,GAAQ;EAC5C,YAAY;EACZ;EACA,WAAW;EACX,UAAU;EACV,OAAO;EACP,QAAQ;EACR,SAAS;EACT,WAAW;EACX,cAAc;EACd,UAAU;EACV,SAAS;GAAE,QAAQ;GAAS;GAAU,YAAY;GAAa;EAChE,CAAC;CAEF,IAAI,EAAO,UAAU,EAAO,OAAO,SAAS,GAAG;EAC7C,KAAK,IAAM,KAAO,EAAO,QACvB,KAAK,UAAU,gBAAI,MAAM,sCAAsC,EAAI,OAAO,CAAC;EAE7E,OAAO;;CAGT,IAAM,IAAgB,EAAqB,EAAO,KAAK;CAavD,OAJI,EAAgB,KAAK,KAAK,aAAa,GAClC,IAAgB,GAAG,EAAO,IAAI,MAAkB,IAGlD,IAAqB,CAAC,GAAc,EAAc,CAAC,OAAO,QAAQ,CAAC,KAAK,KAAK"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worklet-loader.js","names":[],"sources":["../../src/loaders/worklet-loader.ts"],"sourcesContent":["/**\n * BG-layer rspack loader for the `'main thread'` worklet directive.\n *\n * Delegates to upstream's `transformReactLynxSync` (a framework-agnostic SWC\n * worklet transform shared with vue-lynx and react-lynx — see\n * docs/mts-upstream-spike.md). With `target: 'JS'`, each `'main thread'`-marked\n * function is replaced by a `{ _wkltId, _c? }` placeholder object that the\n * sigx BG runtime ships through SET_WORKLET_EVENT to MT.\n *\n * Files without the directive are passed through unchanged. The MT side is\n * handled by the sibling `worklet-loader-mt.ts`.\n */\n\nimport type { Rspack } from '@rsbuild/core';\nimport { transformReactLynxSync } from '@lynx-js/react/transform';\n\n// runtimePkg controls where SWC emits `import { transformToWorklet }` for the\n// BG bundle (used when `runOnBackground(fn)` appears inline inside a\n// `'main thread'` body). Point at @sigx/lynx-runtime — the BG runtime —\n// because that's where transformToWorklet is exported. The MT loader uses\n// @sigx/lynx-runtime-main; identifier matching for runOnMainThread /\n// runOnBackground is by name and works regardless of source.\nconst RUNTIME_PKG = '@sigx/lynx-runtime';\n\n// Cheap pre-filter to skip the SWC parse for files that obviously don't\n// contain a worklet directive. A directive is always followed immediately\n// by a statement terminator — either `;` or a newline (ASI). Requiring\n// one of those after the closing quote rejects substrings inside\n// single-line error strings like\n// `\"...inside 'main thread' functions...\"` from `@sigx/lynx-runtime`'s\n// dist, where the next char is a space then `functions`.\n//\n// This is not a parser. A truly adversarial input (e.g. the literal\n// string `\"'main thread';\"`) can slip through, and the SWC transform\n// is the final arbiter — for such files it produces no registrations\n// and the BG output is identical to the no-directive path, just with\n// the parse work done.\nconst DIRECTIVE_RE = /['\"]main thread['\"]\\s*(?:;|\\n)/;\n\nexport default function workletLoader(\n this: Rspack.LoaderContext,\n source: string,\n): string {\n this.cacheable(true);\n\n if (!DIRECTIVE_RE.test(source)) {\n return source;\n }\n\n const filename = this.resourcePath;\n const result = transformReactLynxSync(source, {\n pluginName: 'sigx:worklet',\n filename,\n sourcemap: false,\n cssScope: false,\n shake: false,\n compat: false,\n refresh: false,\n defineDCE: false,\n directiveDCE: false,\n snapshot: false,\n worklet: { target: 'JS', filename, runtimePkg: RUNTIME_PKG },\n });\n\n if (result.errors && result.errors.length > 0) {\n for (const err of result.errors) {\n this.emitError(new Error(`[sigx-worklet] JS transform: ${err.text}`));\n }\n return source;\n }\n\n return result.code;\n}\n"],"mappings":";;AAsBA,IAAM,IAAc,sBAed,IAAe;AAErB,SAAwB,EAEtB,GACQ;CAGR,IAFA,KAAK,UAAU,GAAK,EAEhB,CAAC,EAAa,KAAK,EAAO,EAC5B,OAAO;CAGT,IAAM,IAAW,KAAK,cAChB,IAAS,EAAuB,GAAQ;EAC5C,YAAY;EACZ;EACA,WAAW;EACX,UAAU;EACV,OAAO;EACP,QAAQ;EACR,SAAS;EACT,WAAW;EACX,cAAc;EACd,UAAU;EACV,SAAS;GAAE,QAAQ;GAAM;GAAU,YAAY;GAAa;EAC7D,CAAC;CAEF,IAAI,EAAO,UAAU,EAAO,OAAO,SAAS,GAAG;EAC7C,KAAK,IAAM,KAAO,EAAO,QACvB,KAAK,UAAU,gBAAI,MAAM,gCAAgC,EAAI,OAAO,CAAC;EAEvE,OAAO;;CAGT,OAAO,EAAO"}
|