@acusti/vite-plugin-react-compiler 0.7.0 → 0.9.0
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/README.md +24 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -97,7 +97,8 @@ The plugin takes an optional options object with four properties:
|
|
|
97
97
|
`enableTreatRefLikeIdentifiersAsRefs`, and so on. Callback-valued options
|
|
98
98
|
(e.g. `logger`, function-valued `sources`) don’t cross the native
|
|
99
99
|
boundary and aren’t supported; `sources` accepts an array of filename
|
|
100
|
-
substrings instead
|
|
100
|
+
substrings instead, and `reportDiagnostics` (below) covers the common use
|
|
101
|
+
of `logger`, seeing what the compiler skipped.
|
|
101
102
|
- **`memoize`**: cache transform results per module id, keyed by a content
|
|
102
103
|
hash (bounded to one entry per file). Multi-environment builds (e.g.
|
|
103
104
|
React Router apps building client + SSR environments) run every transform
|
|
@@ -113,11 +114,28 @@ reactCompiler({
|
|
|
113
114
|
});
|
|
114
115
|
```
|
|
115
116
|
|
|
116
|
-
If the compiler reports a fatal error (a parse failure
|
|
117
|
-
|
|
118
|
-
Babel syntax error would. Nonfatal
|
|
119
|
-
|
|
120
|
-
is left uncompiled and the
|
|
117
|
+
If the compiler reports a fatal error (a parse failure, rejected options,
|
|
118
|
+
or a diagnostic escalated by `panicThreshold`), the plugin fails the build
|
|
119
|
+
via `this.error(...)`, just like a Babel syntax error would. Nonfatal
|
|
120
|
+
compiler bail-outs behave as they do with the Babel plugin under
|
|
121
|
+
`panicThreshold: 'none'`: the affected function is left uncompiled and the
|
|
122
|
+
build continues.
|
|
123
|
+
|
|
124
|
+
Those bail-outs are silent by default. To see what the compiler skipped and
|
|
125
|
+
why, set `compiler: { reportDiagnostics: true }`: each recoverable
|
|
126
|
+
diagnostic (a bail-out, or a react-hooks lint suppression that opts a
|
|
127
|
+
function out) is printed as a warning in your build or dev server output,
|
|
128
|
+
with the compiler’s code frame and its position in the file, and the build
|
|
129
|
+
continues.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
reactCompiler({ compiler: { reportDiagnostics: true } });
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
With `memoize` on, a file’s diagnostics are reported once per distinct
|
|
136
|
+
content, not once per environment. With `panicThreshold` set to
|
|
137
|
+
`'critical_errors'` or `'all_errors'`, the diagnostic that escalates fails
|
|
138
|
+
the build through `this.error(...)` as described above instead of warning.
|
|
121
139
|
|
|
122
140
|
[compiler options]:
|
|
123
141
|
https://react.dev/reference/react-compiler/configuration
|
package/dist/index.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export type Options = {
|
|
|
23
23
|
* Options passed verbatim to React Compiler, using the same names as
|
|
24
24
|
* babel-plugin-react-compiler (compilationMode, panicThreshold
|
|
25
25
|
* (defaults to 'none'), target (defaults to '19'), environment, etc.).
|
|
26
|
+
* Set reportDiagnostics to true to have the compiler’s recoverable
|
|
27
|
+
* diagnostics (e.g. bail-outs) reported as build warnings.
|
|
26
28
|
*/
|
|
27
29
|
compiler?: ReactCompilerOptions;
|
|
28
30
|
};
|
package/dist/index.js
CHANGED
|
@@ -21,9 +21,10 @@ function vitePluginReactCompiler(options = {}) {
|
|
|
21
21
|
reactCompiler: options.compiler ?? {},
|
|
22
22
|
sourcemap: true
|
|
23
23
|
});
|
|
24
|
-
if (transformed.fatal)
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
if (transformed.fatal) this.error(transformed.errors.map(formatDiagnostic).join("\n"), getPosition(Buffer.from(code, "utf8"), transformed.errors[0]));
|
|
25
|
+
if (transformed.errors.length > 0) {
|
|
26
|
+
const utf8Code = Buffer.from(code, "utf8");
|
|
27
|
+
for (const error of transformed.errors) this.warn(formatDiagnostic(error), getPosition(utf8Code, error));
|
|
27
28
|
}
|
|
28
29
|
return {
|
|
29
30
|
code: transformed.code,
|
|
@@ -46,6 +47,13 @@ function vitePluginReactCompiler(options = {}) {
|
|
|
46
47
|
}
|
|
47
48
|
};
|
|
48
49
|
}
|
|
50
|
+
function formatDiagnostic(error) {
|
|
51
|
+
return error.codeframe ? `${error.message}\n${error.codeframe}` : error.message;
|
|
52
|
+
}
|
|
53
|
+
function getPosition(utf8Code, error) {
|
|
54
|
+
const byteStart = error?.labels[0]?.start;
|
|
55
|
+
return byteStart == null ? void 0 : utf8Code.subarray(0, byteStart).toString("utf8").length;
|
|
56
|
+
}
|
|
49
57
|
//#endregion
|
|
50
58
|
export { vitePluginReactCompiler as default };
|
|
51
59
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport { createHash } from 'node:crypto';\n\nimport {\n type ReactCompilerOptions,\n type SourceMap,\n transform,\n} from 'oxc-transform-react';\nimport { createFilter, type FilterPattern, type Plugin } from 'vite';\n\nexport type Options = {\n /**\n * Modules to exclude from the compiler pass (takes precedence over\n * include).\n * @default /[/\\\\]node_modules[/\\\\]/\n */\n exclude?: FilterPattern;\n /**\n * Modules to run the compiler pass on.\n * @default /\\.[jt]sx?$/\n */\n include?: FilterPattern;\n /**\n * Cache transform results by module id + content hash so repeated\n * transforms of identical content (e.g. one per environment in\n * multi-environment builds) only run the compiler once per file.\n * @default true\n */\n memoize?: boolean;\n /**\n * Options passed verbatim to React Compiler, using the same names as\n * babel-plugin-react-compiler (compilationMode, panicThreshold\n * (defaults to 'none'), target (defaults to '19'), environment, etc.).\n */\n compiler?: ReactCompilerOptions;\n};\n\ntype CachedTransform = {\n hash: string;\n result: Promise<TransformOutput>;\n};\n\ntype TransformOutput = { code: string; map?: SourceMap };\n\nconst defaultExclude = /[/\\\\]node_modules[/\\\\]/;\nconst defaultInclude = /\\.[jt]sx?$/;\n\nexport default function vitePluginReactCompiler(options: Options = {}): Plugin {\n const filter = createFilter(\n options.include ?? defaultInclude,\n options.exclude ?? defaultExclude,\n );\n const isMemoizing = options.memoize !== false;\n // one entry per module id, replaced whenever the content hash changes;\n // holds the promise so overlapping transforms of identical content\n // (e.g. concurrent client + SSR environments) share a single run\n const cache = new Map<string, CachedTransform>();\n\n return {\n enforce: 'pre',\n name: 'vite-plugin-react-compiler',\n transform(code, id) {\n // vite module ids can carry a query suffix (e.g. sub-request\n // queries from other plugins); filter, cache, and compile by\n // the underlying file path — like @vitejs/plugin-react — so\n // the cache stays one-entry-per-file and sourcemap sources\n // don’t include vite queries\n const [filepath] = id.split('?');\n if (!filter(filepath)) return null;\n\n const run = async (): Promise<TransformOutput> => {\n const transformed = await transform(filepath, code, {\n // leave JSX for vite’s own pipeline (refresh, dev runtime)\n jsx: 'preserve',\n reactCompiler: options.compiler ?? {},\n sourcemap: true,\n });\n\n // fatal means a parse failure or
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { Buffer } from 'node:buffer';\nimport { createHash } from 'node:crypto';\n\nimport {\n type OxcError,\n type ReactCompilerOptions,\n type SourceMap,\n transform,\n} from 'oxc-transform-react';\nimport { createFilter, type FilterPattern, type Plugin } from 'vite';\n\nexport type Options = {\n /**\n * Modules to exclude from the compiler pass (takes precedence over\n * include).\n * @default /[/\\\\]node_modules[/\\\\]/\n */\n exclude?: FilterPattern;\n /**\n * Modules to run the compiler pass on.\n * @default /\\.[jt]sx?$/\n */\n include?: FilterPattern;\n /**\n * Cache transform results by module id + content hash so repeated\n * transforms of identical content (e.g. one per environment in\n * multi-environment builds) only run the compiler once per file.\n * @default true\n */\n memoize?: boolean;\n /**\n * Options passed verbatim to React Compiler, using the same names as\n * babel-plugin-react-compiler (compilationMode, panicThreshold\n * (defaults to 'none'), target (defaults to '19'), environment, etc.).\n * Set reportDiagnostics to true to have the compiler’s recoverable\n * diagnostics (e.g. bail-outs) reported as build warnings.\n */\n compiler?: ReactCompilerOptions;\n};\n\ntype CachedTransform = {\n hash: string;\n result: Promise<TransformOutput>;\n};\n\ntype TransformOutput = { code: string; map?: SourceMap };\n\nconst defaultExclude = /[/\\\\]node_modules[/\\\\]/;\nconst defaultInclude = /\\.[jt]sx?$/;\n\nexport default function vitePluginReactCompiler(options: Options = {}): Plugin {\n const filter = createFilter(\n options.include ?? defaultInclude,\n options.exclude ?? defaultExclude,\n );\n const isMemoizing = options.memoize !== false;\n // one entry per module id, replaced whenever the content hash changes;\n // holds the promise so overlapping transforms of identical content\n // (e.g. concurrent client + SSR environments) share a single run\n const cache = new Map<string, CachedTransform>();\n\n return {\n enforce: 'pre',\n name: 'vite-plugin-react-compiler',\n transform(code, id) {\n // vite module ids can carry a query suffix (e.g. sub-request\n // queries from other plugins); filter, cache, and compile by\n // the underlying file path — like @vitejs/plugin-react — so\n // the cache stays one-entry-per-file and sourcemap sources\n // don’t include vite queries\n const [filepath] = id.split('?');\n if (!filter(filepath)) return null;\n\n const run = async (): Promise<TransformOutput> => {\n const transformed = await transform(filepath, code, {\n // leave JSX for vite’s own pipeline (refresh, dev runtime)\n jsx: 'preserve',\n reactCompiler: options.compiler ?? {},\n sourcemap: true,\n });\n\n // fatal means a parse failure, rejected options, or a\n // diagnostic escalated by panicThreshold: no code was\n // emitted, so break the build like a Babel syntax error\n // would, forwarding the first error’s position so vite can\n // report the location alongside oxc’s own code frames\n if (transformed.fatal) {\n this.error(\n transformed.errors.map(formatDiagnostic).join('\\n'),\n getPosition(Buffer.from(code, 'utf8'), transformed.errors[0]),\n );\n }\n\n // anything left in errors is recoverable (the code was\n // emitted): the compiler’s bail-outs and rule suppressions,\n // present only once reportDiagnostics is on, and oxc’s own\n // non-fatal diagnostics (e.g. a partially supported\n // typescript namespace), which vite’s oxc pass never sees\n // because the typescript is already stripped; oxc labels\n // the compiler’s diagnostics severity 'Error', so warn\n // about each one at its own position rather than failing\n // the build\n if (transformed.errors.length > 0) {\n const utf8Code = Buffer.from(code, 'utf8');\n for (const error of transformed.errors) {\n this.warn(formatDiagnostic(error), getPosition(utf8Code, error));\n }\n }\n\n return { code: transformed.code, map: transformed.map };\n };\n\n if (!isMemoizing) return run();\n\n const hash = createHash('sha1').update(code).digest('hex');\n const cached = cache.get(filepath);\n if (cached?.hash === hash) return cached.result;\n\n const result = run();\n cache.set(filepath, { hash, result });\n // drop failed transforms so the next request retries them\n result.catch(() => {\n if (cache.get(filepath)?.result === result) cache.delete(filepath);\n });\n return result;\n },\n };\n}\n\n// oxc’s message followed by its code frame (when it has one)\nfunction formatDiagnostic(error: OxcError): string {\n return error.codeframe ? `${error.message}\\n${error.codeframe}` : error.message;\n}\n\n// oxc label offsets are utf-8 byte offsets, but rollup’s pos argument\n// indexes the (utf-16) code string; takes the code already encoded as utf-8\n// so callers converting several positions encode it once\nfunction getPosition(utf8Code: Buffer, error: OxcError | undefined): number | undefined {\n const byteStart = error?.labels[0]?.start;\n return byteStart == null\n ? undefined\n : utf8Code.subarray(0, byteStart).toString('utf8').length;\n}\n"],"mappings":";;;;;AA+CA,IAAM,iBAAiB;AACvB,IAAM,iBAAiB;AAEvB,SAAwB,wBAAwB,UAAmB,CAAC,GAAW;CAC3E,MAAM,SAAS,aACX,QAAQ,WAAW,gBACnB,QAAQ,WAAW,cACvB;CACA,MAAM,cAAc,QAAQ,YAAY;CAIxC,MAAM,wBAAQ,IAAI,IAA6B;CAE/C,OAAO;EACH,SAAS;EACT,MAAM;EACN,UAAU,MAAM,IAAI;GAMhB,MAAM,CAAC,YAAY,GAAG,MAAM,GAAG;GAC/B,IAAI,CAAC,OAAO,QAAQ,GAAG,OAAO;GAE9B,MAAM,MAAM,YAAsC;IAC9C,MAAM,cAAc,MAAM,UAAU,UAAU,MAAM;KAEhD,KAAK;KACL,eAAe,QAAQ,YAAY,CAAC;KACpC,WAAW;IACf,CAAC;IAOD,IAAI,YAAY,OACZ,KAAK,MACD,YAAY,OAAO,IAAI,gBAAgB,CAAC,CAAC,KAAK,IAAI,GAClD,YAAY,OAAO,KAAK,MAAM,MAAM,GAAG,YAAY,OAAO,EAAE,CAChE;IAYJ,IAAI,YAAY,OAAO,SAAS,GAAG;KAC/B,MAAM,WAAW,OAAO,KAAK,MAAM,MAAM;KACzC,KAAK,MAAM,SAAS,YAAY,QAC5B,KAAK,KAAK,iBAAiB,KAAK,GAAG,YAAY,UAAU,KAAK,CAAC;IAEvE;IAEA,OAAO;KAAE,MAAM,YAAY;KAAM,KAAK,YAAY;IAAI;GAC1D;GAEA,IAAI,CAAC,aAAa,OAAO,IAAI;GAE7B,MAAM,OAAO,WAAW,MAAM,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,OAAO,KAAK;GACzD,MAAM,SAAS,MAAM,IAAI,QAAQ;GACjC,IAAI,QAAQ,SAAS,MAAM,OAAO,OAAO;GAEzC,MAAM,SAAS,IAAI;GACnB,MAAM,IAAI,UAAU;IAAE;IAAM;GAAO,CAAC;GAEpC,OAAO,YAAY;IACf,IAAI,MAAM,IAAI,QAAQ,CAAC,EAAE,WAAW,QAAQ,MAAM,OAAO,QAAQ;GACrE,CAAC;GACD,OAAO;EACX;CACJ;AACJ;AAGA,SAAS,iBAAiB,OAAyB;CAC/C,OAAO,MAAM,YAAY,GAAG,MAAM,QAAQ,IAAI,MAAM,cAAc,MAAM;AAC5E;AAKA,SAAS,YAAY,UAAkB,OAAiD;CACpF,MAAM,YAAY,OAAO,OAAO,EAAE,EAAE;CACpC,OAAO,aAAa,OACd,KAAA,IACA,SAAS,SAAS,GAAG,SAAS,CAAC,CAAC,SAAS,MAAM,CAAC,CAAC;AAC3D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@acusti/vite-plugin-react-compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Vite plugin that runs React Compiler via oxc-transform-react, the native Rust port’s Node bindings, instead of Babel",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"memoization",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"tsc": "tsc --noEmit"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"oxc-transform-react": "0.
|
|
44
|
+
"oxc-transform-react": "0.154.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@types/node": "^26.1.0",
|