diff --git a/.changeset/ninety-seas-dig.md b/.changeset/ninety-seas-dig.md
new file mode 100644
index 0000000000..853d812bb3
--- /dev/null
+++ b/.changeset/ninety-seas-dig.md
@@ -0,0 +1,3 @@
+---
+
+---
diff --git a/packages/react/transform/index.d.ts b/packages/react/transform/index.d.ts
index 8f75070879..a021924527 100644
--- a/packages/react/transform/index.d.ts
+++ b/packages/react/transform/index.d.ts
@@ -339,8 +339,57 @@ export interface CssScopeVisitorConfig {
/** @public */
filename: string
}
+/**
+ * {@inheritdoc PluginReactLynxOptions.defineDCE}
+ * @public
+ */
export interface DefineDceVisitorConfig {
- /** @public */
+ /**
+ * @public
+ * Replaces variables in your code with other values or expressions at compile time.
+ *
+ * @remarks
+ * Caveat: differences between `source.define`
+ *
+ * `defineDCE` happens before transforming `background-only` directives.
+ * So it's useful for eliminating code that is only used in the background from main-thread.
+ *
+ * @example
+ *
+ * ```js
+ * import { defineConfig } from '@lynx-js/rspeedy'
+ * import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin'
+ *
+ * export default defineConfig({
+ * plugins: [
+ * pluginReactLynx({
+ * defineDCE: {
+ * define: {
+ * __FOO__: 'false',
+ * 'process.env.PLATFORM': '"lynx"',
+ * },
+ * },
+ * })
+ * ],
+ * })
+ * ```
+ *
+ * Then, `__FOO__` and `process.env.PLATFORM` could be used in source code.
+ *
+ * ```
+ * if (process.env.PLATFORM === 'lynx') {
+ * console.log('lynx')
+ * }
+ *
+ * function FooOrBar() {
+ * if (__FOO__) {
+ * return foo
+ * } else {
+ * return bar
+ * }
+ * }
+ * ```
+ */
define: Record
}
export interface DirectiveDceVisitorConfig {
@@ -475,10 +524,15 @@ export interface ShakeVisitorConfig {
*/
removeCallParams: Array
}
+/** @internal */
export interface JsxTransformerConfig {
+ /** @internal */
preserveJsx: boolean
+ /** @internal */
runtimePkg: string
+ /** @internal */
jsxImportSource?: string
+ /** @internal */
filename: string
/** @internal */
target: 'LEPUS' | 'JS' | 'MIXED'
diff --git a/packages/react/transform/src/swc_plugin_define_dce/mod.rs b/packages/react/transform/src/swc_plugin_define_dce/mod.rs
index 6bad046e10..770231e680 100644
--- a/packages/react/transform/src/swc_plugin_define_dce/mod.rs
+++ b/packages/react/transform/src/swc_plugin_define_dce/mod.rs
@@ -2,10 +2,55 @@ use std::{collections::HashMap, fmt::Debug};
use napi_derive::napi;
+/// {@inheritdoc PluginReactLynxOptions.defineDCE}
+/// @public
#[napi(object)]
#[derive(Clone, Debug)]
pub struct DefineDCEVisitorConfig {
/// @public
+ /// Replaces variables in your code with other values or expressions at compile time.
+ ///
+ /// @remarks
+ /// Caveat: differences between `source.define`
+ ///
+ /// `defineDCE` happens before transforming `background-only` directives.
+ /// So it's useful for eliminating code that is only used in the background from main-thread.
+ ///
+ /// @example
+ ///
+ /// ```js
+ /// import { defineConfig } from '@lynx-js/rspeedy'
+ /// import { pluginReactLynx } from '@lynx-js/react-rsbuild-plugin'
+ ///
+ /// export default defineConfig({
+ /// plugins: [
+ /// pluginReactLynx({
+ /// defineDCE: {
+ /// define: {
+ /// __FOO__: 'false',
+ /// 'process.env.PLATFORM': '"lynx"',
+ /// },
+ /// },
+ /// })
+ /// ],
+ /// })
+ /// ```
+ ///
+ /// Then, `__FOO__` and `process.env.PLATFORM` could be used in source code.
+ ///
+ /// ```
+ /// if (process.env.PLATFORM === 'lynx') {
+ /// console.log('lynx')
+ /// }
+ ///
+ /// function FooOrBar() {
+ /// if (__FOO__) {
+ /// return foo
+ /// } else {
+ /// return bar
+ /// }
+ /// }
+ /// ```
pub define: HashMap,
}
diff --git a/packages/react/transform/src/swc_plugin_snapshot/mod.rs b/packages/react/transform/src/swc_plugin_snapshot/mod.rs
index 86f80ae751..76444f2d7e 100644
--- a/packages/react/transform/src/swc_plugin_snapshot/mod.rs
+++ b/packages/react/transform/src/swc_plugin_snapshot/mod.rs
@@ -1083,12 +1083,17 @@ where
}
}
+/// @internal
#[napi(object)]
#[derive(Clone, Debug)]
pub struct JSXTransformerConfig {
+ /// @internal
pub preserve_jsx: bool,
+ /// @internal
pub runtime_pkg: String,
+ /// @internal
pub jsx_import_source: Option,
+ /// @internal
pub filename: String,
/// @internal
#[napi(ts_type = "'LEPUS' | 'JS' | 'MIXED'")]
diff --git a/packages/rspeedy/plugin-react/src/validate.ts b/packages/rspeedy/plugin-react/src/validate.ts
index 825e68213d..d0ba6cec3a 100644
--- a/packages/rspeedy/plugin-react/src/validate.ts
+++ b/packages/rspeedy/plugin-react/src/validate.ts
@@ -6,28 +6,51 @@ import * as typia from 'typia'
import type { PluginReactLynxOptions } from './pluginReactLynx.js'
+const validate: (
+ input: unknown,
+) => typia.IValidation = typia
+ .createValidateEquals()
+
export const validateConfig: (
input: unknown,
-) => PluginReactLynxOptions | undefined = typia.createAssertEquals<
- PluginReactLynxOptions | undefined
->(({ path, expected, value }) => {
- if (expected === 'undefined') {
- const errorMessage =
- `Unknown property: \`${path}\` in the configuration of pluginReactLynx`
-
- // Unknown properties
- return new Error(errorMessage)
+) => PluginReactLynxOptions | undefined = (input: unknown) => {
+ const result = validate(input)
+
+ if (result.success) {
+ return result.data
+ }
+
+ const messages: string[] = result
+ .errors
+ .flatMap(({ expected, path, value }) => {
+ // Ignore the internal options
+ // See: #846
+ if (
+ path
+ && (path === '$input.jsx' || path.startsWith('$input.jsx.'))
+ && expected === 'undefined'
+ ) {
+ return null
+ }
+
+ if (expected === 'undefined') {
+ return `Unknown property: \`${path}\` in the configuration of pluginReactLynx`
+ }
+ return [
+ `Invalid config on pluginReactLynx: \`${path}\`.`,
+ ` - Expect to be ${expected}`,
+ ` - Got: ${whatIs(value)}`,
+ '',
+ ]
+ })
+ .filter(message => message !== null)
+
+ if (messages.length === 0) {
+ return result.data as PluginReactLynxOptions | undefined
}
- return new Error(
- [
- `Invalid config on pluginReactLynx: \`${path}\`.`,
- ` - Expect to be ${expected}`,
- ` - Got: ${whatIs(value)}`,
- '',
- ].join('\n'),
- )
-})
+ throw new Error(messages.join('\n'))
+}
function whatIs(value: unknown): string {
return Object.prototype.toString.call(value)
diff --git a/packages/rspeedy/plugin-react/test/validate.test.ts b/packages/rspeedy/plugin-react/test/validate.test.ts
index 16d7a4e614..4fa4fb9125 100644
--- a/packages/rspeedy/plugin-react/test/validate.test.ts
+++ b/packages/rspeedy/plugin-react/test/validate.test.ts
@@ -3,7 +3,11 @@
// LICENSE file in the root directory of this source tree.
import { describe, expect, test } from 'vitest'
-import type { CompatVisitorConfig } from '@lynx-js/react/transform'
+import type {
+ CompatVisitorConfig,
+ DefineDceVisitorConfig,
+ JsxTransformerConfig,
+} from '@lynx-js/react/transform'
import { validateConfig } from '../src/validate.js'
@@ -55,6 +59,83 @@ describe('Validation', () => {
})
})
+ test('defineDCE', () => {
+ const cases: Partial[] = [
+ {},
+ { define: {} },
+ { define: { foo: 'bar' } },
+ ]
+
+ cases.forEach(defineDCE => {
+ expect(validateConfig({ defineDCE })).toStrictEqual({ defineDCE })
+ })
+ })
+
+ test('invalid defineDCE', () => {
+ expect(() => validateConfig({ defineDCE: 1 }))
+ .toThrowErrorMatchingInlineSnapshot(`
+ [Error: Invalid config on pluginReactLynx: \`$input.defineDCE\`.
+ - Expect to be (Partial | undefined)
+ - Got: number
+ ]
+ `)
+
+ expect(() => validateConfig({ defineDCE: [] }))
+ .toThrowErrorMatchingInlineSnapshot(`
+ [Error: Invalid config on pluginReactLynx: \`$input.defineDCE\`.
+ - Expect to be (Partial | undefined)
+ - Got: array
+ ]
+ `)
+
+ expect(() => validateConfig({ defineDCEs: {} }))
+ .toThrowErrorMatchingInlineSnapshot(
+ `[Error: Unknown property: \`$input.defineDCEs\` in the configuration of pluginReactLynx]`,
+ )
+
+ expect(() =>
+ validateConfig({
+ defineDCE: {
+ define: {
+ foo: 1,
+ bar: null,
+ },
+ },
+ })
+ )
+ .toThrowErrorMatchingInlineSnapshot(`
+ [Error: Invalid config on pluginReactLynx: \`$input.defineDCE.define.foo\`.
+ - Expect to be string
+ - Got: number
+
+ Invalid config on pluginReactLynx: \`$input.defineDCE.define.bar\`.
+ - Expect to be string
+ - Got: null
+ ]
+ `)
+ })
+
+ test('jsx', () => {
+ const cases: Partial[] = [
+ {},
+ { filename: '1' },
+ { preserveJsx: false },
+ // @ts-expect-error We bypass the internal type check.
+ { preserveJsx: 'foo' },
+ ]
+
+ cases.forEach(jsx => {
+ expect(validateConfig({ jsx })).toStrictEqual({ jsx })
+ })
+
+ // cSpell:disable
+ expect(() => validateConfig({ jsxes: 1 }))
+ .toThrowErrorMatchingInlineSnapshot(
+ `[Error: Unknown property: \`$input.jsxes\` in the configuration of pluginReactLynx]`,
+ )
+ // cSpell:enable
+ })
+
test('targetSdkVersion', () => {
expect(validateConfig({ targetSdkVersion: '3.2' })).toStrictEqual({
targetSdkVersion: '3.2',