-
Notifications
You must be signed in to change notification settings - Fork 122
feat: emit debug metadata assets #2402
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
40fc09b
feat(react): add node index metadata to snapshots
upupming 7a3cfbe
chore: update snapshot
upupming 00bc4a0
feat(react): gate snapshot node index collection
upupming ec30d15
chore(react): update plugin react api report
upupming 5f222e2
feat: make enableNodeIndex optional
upupming 9457561
refactor(react): rename node index metadata to ui source map
upupming c3e4572
docs(changeset): align react metadata wording
upupming a33b0a0
fix(react): inject ui source map filenames at transform boundary
upupming e68ceb8
fix(react): clamp ui source map ids to int32
upupming dd44597
fix(react): keep ui source map ids non-negative
upupming 32e9a05
feat(rspeedy): emit node index map assets
upupming 1c75343
fix(example): satisfy node index config types
upupming 068808f
chore(api): update react webpack plugin report
upupming ff735c8
chore(api): update react rsbuild plugin report
upupming 3d21e93
fix(webpack): clean node index intermediate assets
upupming 20d3b62
fix(example): write nodeIndexMapUrl to source content config
upupming 08dc4ef
refactor(webpack): align node index mappings with sourcemap
upupming 9d889c3
docs(changeset): align ui source map wording
upupming 2876cf9
refactor(react): rename node index asset surface to ui source map
upupming 806163f
refactor(webpack): emit debug metadata assets
upupming 5818cee
test(react): update ui source map snapshots
upupming 2f07380
chore: move example node-index to ui-sourcemap
upupming 5812900
Merge remote-tracking branch 'origin/main' into codex/node-index-asset
upupming 48fae34
chore: update changeset
upupming b69867e
fix: test
upupming 2da6869
fix: test
upupming f61fd42
Merge branch 'codex/node-index-asset' of github.com:lynx-family/lynx-…
upupming 8f5182a
Merge remote-tracking branch 'origin/main' into codex/node-index-asset
upupming cf55e95
fix: cr of codex
upupming File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@lynx-js/template-webpack-plugin': patch | ||
| --- | ||
|
|
||
| Introduce `LynxDebugMetadataPlugin` to emit debug-metadata assets. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@lynx-js/react": patch | ||
| --- | ||
|
|
||
| Add `nodeIndex` to generated FiberElement creation calls and expose React transform debug metadata as `uiSourceMapRecords`. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| --- | ||
| '@lynx-js/react-rsbuild-plugin': patch | ||
| '@lynx-js/react-webpack-plugin': patch | ||
| --- | ||
|
|
||
| Add `enableUiSourceMap` option to enable UI source map generation and debug-metadata asset emission. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| applyTo: "packages/webpack/{react-webpack-plugin,template-webpack-plugin}/**/*" | ||
| --- | ||
|
|
||
| Treat `debug-metadata.json` as the final unified debug asset, not as an early intermediate dump. Generate it only after every JS sourcemap that will be shipped or uploaded has already been finalized, including main-thread debug-info remapping and any late `processAssets` code transforms. When one template contains multiple runtimes, store JS sourcemaps as a `jsSourceMaps` collection rather than a single top-level map, and keep `sourceMapRelease` attached to each JS asset entry instead of the document root because Slardar matches sourcemaps per emitted JS file. Keep `uiSourceMap` and bytecode debug payloads as sibling debug documents inside the unified container instead of overloading the standard sourcemap top-level shape. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| applyTo: "packages/react/transform/**/*" | ||
| --- | ||
|
|
||
| When a crate exposes both core Rust structs and `napi` wrapper structs with the same semantic shape, keep internal transform pipelines and shared `Rc<RefCell<...>>` state on the core types and convert to the `napi` types only at the JS boundary. Do not mix `swc_plugin_*::napi::*` record types into internal plugin wiring such as `.with_*_records(...)`, or wasm builds can fail with mismatched type errors. | ||
| When recording source locations from SWC spans, guard `SourceMap::lookup_char_pos` for synthetic spans such as `DUMMY_SP` (`span.lo == 0`). Compat and other transforms may synthesize JSX nodes with default spans, and wasm builds can surface panics from source map lookups on those spans as `RuntimeError: unreachable`. | ||
| Expose recorded columns as 1-based values so `uiSourceMapRecords` can be fed directly into editor locations such as VS Code without an extra offset conversion. | ||
| When compat wraps a component with a synthetic `<view>`, preserve the original component spans on the generated wrapper instead of using `DUMMY_SP` or `Default::default()`. Snapshot ui source map extraction reads `opening.span`, so preserved spans keep `uiSourceMapRecords` file, line, and column data intact. | ||
| Keep `snapshot.filename` stable for snapshot hashing semantics, even when callers want absolute paths in exported debug metadata. If `uiSourceMapRecords.filename` needs to use the top-level transform filename, inject it at the `react/transform/src/lib.rs` boundary instead of changing the snapshot plugin's internal filename. | ||
| If `swc_plugin_snapshot::JSXTransformer::new` gains a new constructor parameter, update every external callsite under `packages/react/transform/**` at the same time, including wrapper crates such as `swc-plugin-reactlynx`, not just the main `packages/react/transform/src/lib.rs` entrypoint. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| applyTo: "packages/rspeedy/core/test/**/*" | ||
| --- | ||
|
|
||
| Some rspeedy core test fixtures intentionally keep git-tracked files under fixture `node_modules` directories. When cleaning caches or build outputs, avoid deleting tracked fixture files under `packages/rspeedy/core/test/**/node_modules`; only remove untracked/generated artifacts. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| --- | ||
| applyTo: "packages/webpack/{react-webpack-plugin,template-webpack-plugin}/**/*" | ||
| --- | ||
|
|
||
| When emitting React UI source map metadata during template generation, emit `debug-metadata.json` into the template plugin `intermediate` directory, not beside `template.js`. The file should keep the sourcemap payload under a top-level `uiSourceMap` field and place auxiliary data such as `templateDebug` and `git` under `meta`, instead of serializing raw `uiSourceMapRecords`. | ||
| Keep UI source map generation opt-in behind `pluginReactLynx({ enableUiSourceMap: true })`. When the flag is off, do not collect `uiSourceMapRecords`, do not emit `debug-metadata.json`, and do not inject `debugMetadataUrl` into encode data. | ||
| Collect `uiSourceMapRecords` from main-thread loader results by storing them on module `buildInfo`, then aggregate them per template entry group before emit. The emitted `uiSourceMap.sources` array should use project-root-relative POSIX paths, `uiSourceMap.mappings` should follow sourcemap-style source locations as `[sourceIndex, line, column]`, and `uiSourceMap.uiMaps` should be a parallel array where `uiMaps[i]` is the runtime `nodeIndex` for `mappings[i]`. Keep the emitted line and column values 0-based even if transform-time records are editor-friendly 1-based. | ||
| If a webpack plugin emits extra intermediate assets during `beforeEncode` such as `debug-metadata.json`, register their asset names on `args.intermediateAssets` so `LynxEncodePlugin` / `WebEncodePlugin` can clean them with the rest of the intermediate encode artifacts after template generation. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,214 @@ | ||
| import { execFileSync } from 'node:child_process'; | ||
| import path from 'node:path'; | ||
| import { fileURLToPath } from 'node:url'; | ||
|
|
||
| import { pluginQRCode } from '@lynx-js/qrcode-rsbuild-plugin'; | ||
| import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin'; | ||
| import { defineConfig } from '@lynx-js/rspeedy'; | ||
| import type { RsbuildPlugin, Rspack } from '@lynx-js/rspeedy'; | ||
| import type { LynxTemplatePlugin } from '@lynx-js/template-webpack-plugin'; | ||
|
|
||
| const DEBUG_METADATA_ASSET = 'debug-metadata.json'; | ||
| const MOCK_UPLOAD_BASE_URL = 'https://mock-debug-metadata-upload.lynx.dev/'; | ||
| const projectRoot = path.dirname(fileURLToPath(import.meta.url)); | ||
|
|
||
| interface GitMetadata { | ||
| branch: string; | ||
| commit: string; | ||
| commitUrl: string | null; | ||
| remoteUrl: string | null; | ||
| } | ||
|
|
||
| function runGit(args: string[]): string | null { | ||
| try { | ||
| return execFileSync('git', args, { | ||
| cwd: projectRoot, | ||
| encoding: 'utf8', | ||
| }).trim(); | ||
| } catch { | ||
| return null; | ||
| } | ||
| } | ||
|
|
||
| function normalizeRepositoryUrl(remoteUrl: string | null): string | null { | ||
| if (!remoteUrl) { | ||
| return null; | ||
| } | ||
|
|
||
| if (remoteUrl.startsWith('git@github.com:')) { | ||
| return `https://github.com/${ | ||
| remoteUrl.slice('git@github.com:'.length).replace(/\.git$/, '') | ||
| }`; | ||
| } | ||
|
|
||
| if (remoteUrl.startsWith('https://github.com/')) { | ||
| return remoteUrl.replace(/\.git$/, ''); | ||
| } | ||
|
|
||
| return remoteUrl; | ||
| } | ||
|
|
||
| function getGitMetadata(): GitMetadata { | ||
| const commit = runGit(['rev-parse', 'HEAD']) ?? 'unknown'; | ||
| const branch = runGit(['rev-parse', '--abbrev-ref', 'HEAD']) ?? 'unknown'; | ||
| const remoteUrl = normalizeRepositoryUrl( | ||
| runGit(['config', '--get', 'remote.origin.url']), | ||
| ); | ||
|
|
||
| return { | ||
| branch, | ||
| commit, | ||
| remoteUrl, | ||
| commitUrl: remoteUrl ? `${remoteUrl}/commit/${commit}` : null, | ||
| }; | ||
| } | ||
|
|
||
| function mockUploadDebugMetadata( | ||
| filenameTemplate: string, | ||
| intermediate: string, | ||
| ): string { | ||
| const normalizedTemplate = filenameTemplate.replaceAll( | ||
| path.win32.sep, | ||
| path.posix.sep, | ||
| ); | ||
| const normalizedIntermediate = intermediate.replaceAll( | ||
| path.win32.sep, | ||
| path.posix.sep, | ||
| ); | ||
| const assetPath = path.posix.join( | ||
| normalizedIntermediate.replace(/^\.\//, ''), | ||
| DEBUG_METADATA_ASSET, | ||
| ); | ||
|
|
||
| return new URL( | ||
| `${assetPath}?template=${encodeURIComponent(normalizedTemplate)}`, | ||
| MOCK_UPLOAD_BASE_URL, | ||
| ).toString(); | ||
| } | ||
|
|
||
| function pluginMockDebugMetadataUpload(): RsbuildPlugin { | ||
| return { | ||
| name: 'example:mock-debug-metadata-upload', | ||
| setup(api) { | ||
| const git = getGitMetadata(); | ||
|
|
||
| api.modifyBundlerChain(chain => { | ||
| const exposed = api.useExposed< | ||
| { LynxTemplatePlugin: typeof LynxTemplatePlugin } | ||
| >(Symbol.for('LynxTemplatePlugin')); | ||
|
|
||
| if (!exposed) { | ||
| throw new Error( | ||
| '[example:mock-debug-metadata-upload] Missing exposed LynxTemplatePlugin', | ||
| ); | ||
| } | ||
|
|
||
| chain.plugin('example:mock-debug-metadata-upload').use({ | ||
| apply(compiler) { | ||
| compiler.hooks.thisCompilation.tap( | ||
| 'example:mock-debug-metadata-upload', | ||
| compilation => { | ||
| const hooks = exposed.LynxTemplatePlugin | ||
| .getLynxTemplatePluginHooks( | ||
| compilation as unknown as Parameters< | ||
| typeof LynxTemplatePlugin.getLynxTemplatePluginHooks | ||
| >[0], | ||
| ); | ||
|
|
||
| hooks.beforeEncode.tapPromise( | ||
| { | ||
| name: 'example:mock-debug-metadata-upload', | ||
| stage: 1000, | ||
| }, | ||
| async args => { | ||
| const assetName = path.posix.format({ | ||
| dir: args.intermediate, | ||
| base: DEBUG_METADATA_ASSET, | ||
| }); | ||
| const debugMetadataAsset = compilation.getAsset(assetName); | ||
|
|
||
| if (debugMetadataAsset) { | ||
| const currentContent = debugMetadataAsset.source | ||
| .source() | ||
| .toString(); | ||
| const debugMetadata = JSON.parse( | ||
| currentContent, | ||
| ) as Record< | ||
| string, | ||
| unknown | ||
| >; | ||
| const currentMeta = | ||
| typeof debugMetadata['meta'] === 'object' | ||
| && debugMetadata['meta'] !== null | ||
| ? debugMetadata['meta'] as Record<string, unknown> | ||
| : {}; | ||
|
|
||
| compilation.updateAsset( | ||
| assetName, | ||
| new compiler.webpack.sources.RawSource( | ||
| JSON.stringify( | ||
| { | ||
| ...debugMetadata, | ||
| meta: { | ||
| ...currentMeta, | ||
| git, | ||
| }, | ||
| }, | ||
| null, | ||
| 2, | ||
| ), | ||
| ), | ||
| ); | ||
| } | ||
|
|
||
| const debugMetadataUrl = await Promise.resolve( | ||
| mockUploadDebugMetadata( | ||
| args.filenameTemplate, | ||
| args.intermediate, | ||
| ), | ||
| ); | ||
|
|
||
| args.encodeData.sourceContent.config = { | ||
| ...args.encodeData.sourceContent.config, | ||
| debugMetadataUrl, | ||
| }; | ||
|
|
||
| return args; | ||
| }, | ||
| ); | ||
| }, | ||
| ); | ||
| }, | ||
| } as Rspack.RspackPluginInstance); | ||
| }); | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export default defineConfig({ | ||
| source: { | ||
| entry: { | ||
| main: path.join(projectRoot, 'src/index.tsx'), | ||
| }, | ||
| }, | ||
| output: { | ||
| distPath: { | ||
| root: path.join(projectRoot, 'dist'), | ||
| }, | ||
| }, | ||
| plugins: [ | ||
| pluginReactLynx({ | ||
| enableUiSourceMap: true, | ||
| }), | ||
| pluginMockDebugMetadataUpload(), | ||
| pluginQRCode({ | ||
| schema(url) { | ||
| return `${url}?fullscreen=true`; | ||
| }, | ||
| }), | ||
| ], | ||
| environments: { | ||
| web: {}, | ||
| lynx: {}, | ||
| }, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| { | ||
| "name": "@lynx-js/example-react-ui-sourcemap", | ||
| "version": "0.0.0", | ||
| "private": true, | ||
| "type": "module", | ||
| "scripts": { | ||
| "build": "rspeedy build", | ||
| "dev": "rspeedy dev" | ||
| }, | ||
| "dependencies": { | ||
| "@lynx-js/react": "workspace:*" | ||
| }, | ||
| "devDependencies": { | ||
| "@lynx-js/preact-devtools": "^5.0.1", | ||
| "@lynx-js/qrcode-rsbuild-plugin": "workspace:*", | ||
| "@lynx-js/react-rsbuild-plugin": "workspace:*", | ||
| "@lynx-js/rspeedy": "workspace:*", | ||
| "@lynx-js/template-webpack-plugin": "workspace:*", | ||
| "@lynx-js/types": "3.7.0", | ||
| "@types/react": "^18.3.28" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| .Screen { | ||
| min-height: 100%; | ||
| padding: 32px 24px; | ||
| background: linear-gradient(180deg, #0c1222 0%, #172544 100%); | ||
| } | ||
|
|
||
| .Hero { | ||
| margin-bottom: 24px; | ||
| } | ||
|
|
||
| .Eyebrow { | ||
| margin-bottom: 8px; | ||
| color: #89b4ff; | ||
| font-size: 24px; | ||
| } | ||
|
|
||
| .Title { | ||
| margin-bottom: 12px; | ||
| color: #ffffff; | ||
| font-size: 42px; | ||
| font-weight: 700; | ||
| } | ||
|
|
||
| .Description { | ||
| color: rgba(255, 255, 255, 0.82); | ||
| font-size: 26px; | ||
| line-height: 36px; | ||
| } | ||
|
|
||
| .Button { | ||
| margin-bottom: 24px; | ||
| padding: 20px 24px; | ||
| border-radius: 999px; | ||
| background-color: #7ee787; | ||
| } | ||
|
|
||
| .ButtonLabel { | ||
| color: #08210d; | ||
| font-size: 24px; | ||
| font-weight: 600; | ||
| } | ||
|
|
||
| .Hint { | ||
| color: rgba(255, 255, 255, 0.7); | ||
| font-size: 24px; | ||
| line-height: 34px; | ||
| } | ||
|
|
||
| .Card { | ||
| padding: 24px; | ||
| border-radius: 24px; | ||
| background-color: rgba(255, 255, 255, 0.12); | ||
| } | ||
|
|
||
| .CardTitle { | ||
| margin-bottom: 12px; | ||
| color: #ffffff; | ||
| font-size: 30px; | ||
| font-weight: 600; | ||
| } | ||
|
|
||
| .CardBody { | ||
| color: rgba(255, 255, 255, 0.78); | ||
| font-size: 24px; | ||
| line-height: 34px; | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.