Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d9c0752
feat: support bundle and load css in external bundle
upupming Jan 26, 2026
8cab401
Merge branch 'main' into feat/rslib-css
upupming Jan 30, 2026
872c04a
test: add test case for external bundle css
upupming Feb 4, 2026
655fd14
Merge origin/main into feat/rslib-css
upupming Feb 4, 2026
9fd7b20
Merge remote-tracking branch 'origin/main' into feat/rslib-css
upupming Feb 24, 2026
39c3de2
feat: upgrade @lynx-js/tasm and remove css import in consumer
upupming Feb 24, 2026
7075e4a
feat: move `cssChunksToMap` implementation to `@lynx-js/css-serializer`
upupming Feb 24, 2026
a44e2d6
Merge remote-tracking branch 'origin/feat/cssChunksToMap' into feat/r…
upupming Feb 24, 2026
79a3613
feat: use cssChunksToMap from `@lynx-js/css-serializer`
upupming Feb 24, 2026
bd1dfa6
test: css loading in external bundle
upupming Feb 25, 2026
94da61e
Merge branch 'main' into feat/rslib-css
upupming Feb 25, 2026
3e900fe
chore: upgrade @lynx-js/tasm to 0.0.25
upupming Feb 25, 2026
f69be35
chore: upgrade @lynx-js/tasm to 0.0.25
upupming Feb 25, 2026
1c41029
Merge branches 'feat/rslib-css' and 'feat/rslib-css' of github.com:ly…
upupming Feb 25, 2026
d6d75a5
chore: upgrade @lynx-js/tasm to 0.0.26
upupming Mar 2, 2026
0bd3ba9
Merge remote-tracking branch 'origin/main' into feat/rslib-css
upupming Mar 3, 2026
30a3f51
Merge remote-tracking branch 'origin/main' into feat/rslib-css
upupming Mar 3, 2026
c07a9a2
fix: cr of rabbit
upupming Mar 3, 2026
102ab4b
chore: remove unused deps
upupming Mar 4, 2026
a80ede6
feat: use target: 'web' instead modify css loaders
upupming Mar 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/thick-needles-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@lynx-js/externals-loading-webpack-plugin": patch
"@lynx-js/lynx-bundle-rslib-config": patch
"@lynx-js/react-rsbuild-plugin": patch
---

Support bundle and load css in external bundle
8 changes: 8 additions & 0 deletions examples/react-externals/src/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/* At least a css rule is needed in consumer
* to make sure the css engine is loaded
* so that the css in external bundles can be applied
*
* This is a limitation of Lynx engine
* we can remove this limitation in the future
*/
.dummy {}
Comment thread
upupming marked this conversation as resolved.
5 changes: 1 addition & 4 deletions examples/react-externals/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@ import { root } from '@lynx-js/react';

import { App } from './App.js';

// We have to manually import the css now
// TODO: load css from external bundle
// when it is supported in Lynx engine
import './App.css';
import './index.css';

root.render(
<App />,
Expand Down
3 changes: 2 additions & 1 deletion packages/rspeedy/lynx-bundle-rslib-config/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@
"test": "vitest"
},
"dependencies": {
"@lynx-js/css-serializer": "workspace:*",
"@lynx-js/runtime-wrapper-webpack-plugin": "workspace:*",
"@lynx-js/tasm": "0.0.20"
"@lynx-js/tasm": "0.0.26"
},
"devDependencies": {
"@lynx-js/react": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const defaultExternalBundleLibConfig: LibConfig = {
},
},
},
target: 'web',
},
source: {
include: [/node_modules/],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
// LICENSE file in the root directory of this source tree.
import type { Asset, Compilation, Compiler } from 'webpack'

import { cssChunksToMap } from '@lynx-js/css-serializer'
import type { LynxStyleNode } from '@lynx-js/css-serializer'

/**
* The options for {@link ExternalBundleWebpackPlugin}.
*
Expand Down Expand Up @@ -112,18 +115,49 @@ export class ExternalBundleWebpackPlugin {

async #encode(assets: Readonly<Asset>[]) {
const customSections = assets
.filter(({ name }) => name.endsWith('.js'))
.reduce<Record<string, { content: string }>>((prev, cur) => ({
...prev,
[cur.name.replace(/\.js$/, '')]: {
content: cur.source.source().toString(),
.reduce<
Record<string, {
content: string | {
ruleList: LynxStyleNode[]
}
}>
>(
(prev, cur) => {
switch (cur.info['assetType']) {
case 'javascript':
return ({
...prev,
[cur.name.replace(/\.js$/, '')]: {
content: cur.source.source().toString(),
},
})
case 'extract-css':
return ({
...prev,
[`${cur.name.replace(/\.css$/, '')}:CSS`]: {
'encoding': 'CSS',
content: {
ruleList: cssChunksToMap(
[cur.source.source().toString()],
[],
true,
).cssMap[0] ?? [],
},
},
})
default:
return prev
}
},
}), {})
{},
)

const compilerOptions: Record<string, unknown> = {
enableFiberArch: true,
// `lynx.fetchBundle` and `lynx.loadScript` require engineVersion >= 3.5
targetSdkVersion: this.options.engineVersion ?? '3.5',
enableCSSInvalidation: true,
enableCSSSelector: true,
}

const encodeOptions = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,37 @@ describe('should build external bundle', () => {
)
expect(decodedResult['engine-version']).toBe('3.5')
})

it('should build css into external bundle', async () => {
const fixtureDir = path.join(__dirname, './fixtures/css-lib')
const rslibConfig = defineExternalBundleRslibConfig({
source: {
entry: {
index: path.join(fixtureDir, 'index.ts'),
},
},
id: 'css-bundle',
output: {
distPath: {
root: path.join(fixtureDir, 'dist'),
},
},
plugins: [pluginReactLynx()],
})

await build(rslibConfig)

const decodedResult = await decodeTemplate(
path.join(fixtureDir, 'dist', 'css-bundle.lynx.bundle'),
)

// Check custom-sections for CSS keys
expect(Object.keys(decodedResult['custom-sections']).sort()).toEqual([
'index',
'index:CSS',
'index__main-thread',
])
})
})

describe('debug mode artifacts', () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.container {
color: red;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './index.css'

export const hello = 'world'
4 changes: 1 addition & 3 deletions packages/rspeedy/plugin-react/src/pluginReactLynx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,7 @@ export function pluginReactLynx(
})
}

if (!isRslib) {
applyCSS(api, resolvedOptions)
}
applyCSS(api, resolvedOptions)
applyEntry(api, resolvedOptions)
applyBackgroundOnly(api)
applyGenerator(api, resolvedOptions)
Expand Down
27 changes: 27 additions & 0 deletions packages/webpack/externals-loading-webpack-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@ export class ExternalsLoadingPlugin {
} else {
return '';
}
const isMainThreadLayer = layer === 'mainThread';
for (
const [pkgName, external] of Object.entries(
externalsLoadingPluginOptions.externals,
Expand All @@ -323,8 +324,21 @@ function createLoadExternalAsync(handler, sectionPath) {
if (response.code === 0) {
try {
const result = lynx.loadScript(sectionPath, { bundleName: response.url });
${
isMainThreadLayer
? `
// TODO: Use configured layer suffix instead of hard-coded __main-thread for CSS section lookup.
const styleSheet = __LoadStyleSheet(sectionPath.replace('__main-thread', '') + ':CSS', response.url);
Comment thread
upupming marked this conversation as resolved.
if (styleSheet !== null) {
__AdoptStyleSheet(styleSheet);
__FlushElementTree();
}
`
: ''
}
resolve(result)
} catch (error) {
console.error(error)
reject(new Error('Failed to load script ' + sectionPath + ' in ' + response.url, { cause: error }))
}
} else {
Expand All @@ -338,8 +352,21 @@ function createLoadExternalSync(handler, sectionPath, timeout) {
if (response.code === 0) {
try {
const result = lynx.loadScript(sectionPath, { bundleName: response.url });
${
isMainThreadLayer
? `
// TODO: Use configured layer suffix instead of hard-coded __main-thread for CSS section lookup.
const styleSheet = __LoadStyleSheet(sectionPath.replace('__main-thread', '') + ':CSS', response.url);
if (styleSheet !== null) {
__AdoptStyleSheet(styleSheet);
__FlushElementTree();
}
`
: ''
}
return result
} catch (error) {
console.error(error)
throw new Error('Failed to load script ' + sectionPath + ' in ' + response.url, { cause: error })
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,14 @@ function __injectGlobals(target) {

target.lynxCoreInject = {};
target.lynxCoreInject.tt = {};

target.__LoadStyleSheet = () => {
return {};
};
target.__AdoptStyleSheet = () => {
//
};
target.__FlushElementTree = () => {
//
};
}
2 changes: 1 addition & 1 deletion packages/webpack/template-webpack-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
},
"dependencies": {
"@lynx-js/css-serializer": "workspace:*",
"@lynx-js/tasm": "0.0.20",
"@lynx-js/tasm": "0.0.26",
"@lynx-js/web-core-wasm": "workspace:*",
"@lynx-js/webpack-runtime-globals": "workspace:^",
"@rspack/lite-tapable": "1.1.0",
Expand Down
17 changes: 10 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading