@octanejs/rspack-plugin 0.1.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/LICENSE +21 -0
- package/README.md +84 -0
- package/package.json +59 -0
- package/src/index.js +2 -0
- package/src/loader.js +95 -0
- package/src/plugin.js +124 -0
- package/src/shared.js +110 -0
- package/types/index.d.ts +49 -0
- package/types/loader.d.ts +5 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Dominic Gannaway
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# `@octanejs/rspack-plugin`
|
|
2
|
+
|
|
3
|
+
Low-level Rspack 2.x integration for Octane. It compiles `.tsrx`, eligible
|
|
4
|
+
Octane `.tsx`, and raw `.ts`/`.js` hook sources; the full routing and SSR app
|
|
5
|
+
integration lives in `@octanejs/rsbuild-plugin`.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
pnpm add octane
|
|
11
|
+
pnpm add -D @rspack/core @octanejs/rspack-plugin
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Rspack plugin
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
// rspack.config.mjs
|
|
18
|
+
import { OctaneRspackPlugin } from '@octanejs/rspack-plugin';
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
entry: './src/main.tsrx',
|
|
22
|
+
plugins: [new OctaneRspackPlugin()],
|
|
23
|
+
};
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The plugin:
|
|
27
|
+
|
|
28
|
+
- adds `.tsrx`, `.tsx`, and `.ts` resolution;
|
|
29
|
+
- installs the Octane pre-loader for local and linked/raw dependency sources;
|
|
30
|
+
- selects client or server codegen from Rspack's `target` (or an explicit
|
|
31
|
+
`environment` option);
|
|
32
|
+
- resolves every exact bare `octane` import to one client runtime, or to
|
|
33
|
+
`octane/server` in server compilations;
|
|
34
|
+
- forwards compiler source maps and registers consulted and missing manifests
|
|
35
|
+
with Rspack's cache and watcher;
|
|
36
|
+
- emits the webpack/Rspack HMR dialect when the compilation is hot; and
|
|
37
|
+
- strips TypeScript with `builtin:swc-loader` after Octane by default.
|
|
38
|
+
|
|
39
|
+
Use an explicit environment for targets which do not identify their consumer:
|
|
40
|
+
|
|
41
|
+
```js
|
|
42
|
+
new OctaneRspackPlugin({ environment: 'server' });
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Set `transpile: false` when an existing rule already strips TypeScript. Set
|
|
46
|
+
`hmr: false` to disable Octane HMR codegen even when Rspack HMR is active.
|
|
47
|
+
Options contain only serializable strings, booleans, and string arrays, so the
|
|
48
|
+
same configuration is safe to reuse across compiler environments and caches.
|
|
49
|
+
|
|
50
|
+
Rspack's dev server enables the loader's hot context. If you run a custom dev
|
|
51
|
+
server, add Rspack's `HotModuleReplacementPlugin` as usual.
|
|
52
|
+
|
|
53
|
+
## Loader only
|
|
54
|
+
|
|
55
|
+
The ESM loader is exported for custom rule composition:
|
|
56
|
+
|
|
57
|
+
```js
|
|
58
|
+
export default {
|
|
59
|
+
module: {
|
|
60
|
+
rules: [
|
|
61
|
+
{
|
|
62
|
+
test: /\.(?:tsrx|tsx|ts|js)$/,
|
|
63
|
+
enforce: 'pre',
|
|
64
|
+
type: 'javascript/auto',
|
|
65
|
+
use: {
|
|
66
|
+
loader: '@octanejs/rspack-plugin/loader',
|
|
67
|
+
options: { environment: 'client' },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
With the loader-only form, configure `.tsrx` resolution, TypeScript stripping,
|
|
76
|
+
and the exact server runtime alias yourself. The class plugin is recommended
|
|
77
|
+
unless another integration owns those concerns.
|
|
78
|
+
|
|
79
|
+
## App-level metadata
|
|
80
|
+
|
|
81
|
+
Transformed Rspack modules receive a serializable `buildInfo.octane` record
|
|
82
|
+
containing `canonicalId`, `transformKind`, and `serverRpc`. App integrations can
|
|
83
|
+
read the validated value with `getOctaneRspackBuildInfo(module)` without
|
|
84
|
+
depending on compiler output parsing for module identity.
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@octanejs/rspack-plugin",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=22"
|
|
8
|
+
},
|
|
9
|
+
"description": "Rspack loader and compiler plugin for Octane TSRX source",
|
|
10
|
+
"author": {
|
|
11
|
+
"name": "Dominic Gannaway",
|
|
12
|
+
"email": "dg@domgan.com"
|
|
13
|
+
},
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/octanejs/octane.git",
|
|
20
|
+
"directory": "packages/rspack-plugin-octane"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"src",
|
|
24
|
+
"types",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"module": "src/index.js",
|
|
29
|
+
"main": "src/index.js",
|
|
30
|
+
"types": "types/index.d.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./types/index.d.ts",
|
|
34
|
+
"import": "./src/index.js",
|
|
35
|
+
"default": "./src/index.js"
|
|
36
|
+
},
|
|
37
|
+
"./loader": {
|
|
38
|
+
"types": "./types/loader.d.ts",
|
|
39
|
+
"import": "./src/loader.js",
|
|
40
|
+
"default": "./src/loader.js"
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@jridgewell/remapping": "^2.3.5"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@rspack/core": "^2.0.0",
|
|
48
|
+
"octane": "0.1.5"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@rspack/core": "^2.1.3",
|
|
52
|
+
"@types/node": "^24.3.0",
|
|
53
|
+
"vitest": "^4.1.9",
|
|
54
|
+
"octane": "0.1.5"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"test": "vitest run --config vitest.config.js"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/index.js
ADDED
package/src/loader.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { isAbsolute, resolve } from 'node:path';
|
|
2
|
+
import remapping from '@jridgewell/remapping';
|
|
3
|
+
import { canonicalModuleId, createOctaneCompiler } from 'octane/compiler/bundler';
|
|
4
|
+
import { inferRspackEnvironment, normalizeLoaderOptions } from './shared.js';
|
|
5
|
+
|
|
6
|
+
function clearBuildInfo(module) {
|
|
7
|
+
if (module?.buildInfo && typeof module.buildInfo === 'object') {
|
|
8
|
+
delete module.buildInfo.octane;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function setBuildInfo(module, value) {
|
|
13
|
+
if (!module || typeof module !== 'object') return;
|
|
14
|
+
if (!module.buildInfo || typeof module.buildInfo !== 'object') module.buildInfo = {};
|
|
15
|
+
module.buildInfo.octane = value;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function registerDependencies(context, result) {
|
|
19
|
+
for (const dependency of new Set(result.dependencies ?? [])) {
|
|
20
|
+
context.addDependency?.(dependency);
|
|
21
|
+
}
|
|
22
|
+
for (const dependency of new Set(result.missingDependencies ?? [])) {
|
|
23
|
+
context.addMissingDependency?.(dependency);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function composeSourceMaps(outputMap, inputSourceMap) {
|
|
28
|
+
if (!outputMap || !inputSourceMap) return outputMap ?? inputSourceMap;
|
|
29
|
+
const input = typeof inputSourceMap === 'string' ? JSON.parse(inputSourceMap) : inputSourceMap;
|
|
30
|
+
const chained = remapping([outputMap, input], () => null);
|
|
31
|
+
return String(chained.mappings).length > 0 ? chained : outputMap;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Rspack's ESM loader entry. A compiler instance is intentionally scoped to
|
|
36
|
+
* one invocation: Rspack owns output caching and invalidates it from the file
|
|
37
|
+
* and missing-file dependencies registered below, while a fresh neutral
|
|
38
|
+
* compiler instance cannot retain stale manifest discovery across rebuilds.
|
|
39
|
+
*/
|
|
40
|
+
export default function octaneLoader(source, inputSourceMap) {
|
|
41
|
+
this.cacheable?.(true);
|
|
42
|
+
clearBuildInfo(this._module);
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const options = normalizeLoaderOptions(this.getOptions?.() ?? {});
|
|
46
|
+
const loaderRoot = this.rootContext ?? process.cwd();
|
|
47
|
+
const root = options.root
|
|
48
|
+
? isAbsolute(options.root)
|
|
49
|
+
? options.root
|
|
50
|
+
: resolve(loaderRoot, options.root)
|
|
51
|
+
: loaderRoot;
|
|
52
|
+
const environment = options.environment ?? inferRspackEnvironment(this.target);
|
|
53
|
+
const hmr =
|
|
54
|
+
environment === 'client' && this.hot === true && options.hmr !== false ? 'webpack' : false;
|
|
55
|
+
const dev =
|
|
56
|
+
environment === 'client' &&
|
|
57
|
+
(options.dev ?? (this.mode === undefined || this.mode !== 'production'));
|
|
58
|
+
const compiler = createOctaneCompiler({
|
|
59
|
+
root,
|
|
60
|
+
...(options.exclude === undefined ? null : { exclude: options.exclude }),
|
|
61
|
+
...(options.parallelUse === undefined ? null : { parallelUse: options.parallelUse }),
|
|
62
|
+
});
|
|
63
|
+
const id = this.resource ?? this.resourcePath;
|
|
64
|
+
const result = compiler.transform(String(source), id, {
|
|
65
|
+
environment,
|
|
66
|
+
hmr,
|
|
67
|
+
dev,
|
|
68
|
+
...(options.parallelUse === undefined ? null : { parallelUse: options.parallelUse }),
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
if (result === null) {
|
|
72
|
+
this.callback(null, source, this.sourceMap === false ? undefined : inputSourceMap);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
registerDependencies(this, result);
|
|
77
|
+
if (result.kind === 'none') {
|
|
78
|
+
this.callback(null, source, this.sourceMap === false ? undefined : inputSourceMap);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
setBuildInfo(this._module, {
|
|
82
|
+
canonicalId: canonicalModuleId(id, root),
|
|
83
|
+
transformKind: result.kind,
|
|
84
|
+
serverRpc:
|
|
85
|
+
result.kind === 'compile' &&
|
|
86
|
+
(result.code.includes('_$__serverRpc(') ||
|
|
87
|
+
result.code.includes('export const _$_server_$_')),
|
|
88
|
+
});
|
|
89
|
+
const sourceMap =
|
|
90
|
+
this.sourceMap === false ? undefined : composeSourceMaps(result.map, inputSourceMap);
|
|
91
|
+
this.callback(null, result.code, sourceMap);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
this.callback(error instanceof Error ? error : new Error(String(error)));
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/plugin.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
2
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { createOctaneCompiler } from 'octane/compiler/bundler';
|
|
5
|
+
import { inferRspackEnvironment, normalizePluginOptions } from './shared.js';
|
|
6
|
+
|
|
7
|
+
const PLUGIN_NAME = 'OctaneRspackPlugin';
|
|
8
|
+
const loaderPath = fileURLToPath(new URL('./loader.js', import.meta.url));
|
|
9
|
+
const OCTANE_RULE = /\.(?:tsrx|tsx|ts|js)$/i;
|
|
10
|
+
const TYPESCRIPT_RULE = /\.(?:tsrx|tsx|ts)$/i;
|
|
11
|
+
|
|
12
|
+
function addUniqueExtensions(resolveOptions) {
|
|
13
|
+
const extensions = resolveOptions.extensions ?? ['.js', '.json', '.wasm'];
|
|
14
|
+
resolveOptions.extensions = [
|
|
15
|
+
...['.tsrx', '.tsx', '.ts'].filter((extension) => !extensions.includes(extension)),
|
|
16
|
+
...extensions,
|
|
17
|
+
];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function resolveRuntimeModule(request, root) {
|
|
21
|
+
if (isAbsolute(request)) return request;
|
|
22
|
+
try {
|
|
23
|
+
return createRequire(join(root, 'package.json')).resolve(request);
|
|
24
|
+
} catch {
|
|
25
|
+
// Let Rspack produce its normal resolution diagnostic. This fallback also
|
|
26
|
+
// keeps config inspection usable before peer dependencies are installed.
|
|
27
|
+
return request;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function addRuntimeAlias(resolveOptions, request, root) {
|
|
32
|
+
const aliases = resolveOptions.alias === false ? {} : (resolveOptions.alias ?? {});
|
|
33
|
+
resolveOptions.alias = {
|
|
34
|
+
...aliases,
|
|
35
|
+
octane$: resolveRuntimeModule(request, root),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function addDependencies(collection, values) {
|
|
40
|
+
if (!collection?.add) return;
|
|
41
|
+
for (const value of values ?? []) collection.add(value);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export class OctaneRspackPlugin {
|
|
45
|
+
constructor(options = {}) {
|
|
46
|
+
this.options = normalizePluginOptions(options);
|
|
47
|
+
this.sourceDependencies = [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
apply(compiler) {
|
|
51
|
+
const configuredRoot = this.options.root;
|
|
52
|
+
const compilerRoot = compiler.options.context ?? process.cwd();
|
|
53
|
+
const root = configuredRoot
|
|
54
|
+
? isAbsolute(configuredRoot)
|
|
55
|
+
? configuredRoot
|
|
56
|
+
: resolve(compilerRoot, configuredRoot)
|
|
57
|
+
: compilerRoot;
|
|
58
|
+
const environment = this.options.environment ?? inferRspackEnvironment(compiler.options.target);
|
|
59
|
+
const neutralCompiler = createOctaneCompiler({
|
|
60
|
+
root,
|
|
61
|
+
...(this.options.exclude === undefined ? null : { exclude: this.options.exclude }),
|
|
62
|
+
...(this.options.parallelUse === undefined
|
|
63
|
+
? null
|
|
64
|
+
: { parallelUse: this.options.parallelUse }),
|
|
65
|
+
});
|
|
66
|
+
const runtimeRequest = neutralCompiler.resolveRuntimeRequest('octane', environment);
|
|
67
|
+
|
|
68
|
+
compiler.options.resolve ??= {};
|
|
69
|
+
addUniqueExtensions(compiler.options.resolve);
|
|
70
|
+
addRuntimeAlias(compiler.options.resolve, runtimeRequest, root);
|
|
71
|
+
|
|
72
|
+
compiler.options.module ??= {};
|
|
73
|
+
compiler.options.module.rules ??= [];
|
|
74
|
+
const loaderOptions = {
|
|
75
|
+
root,
|
|
76
|
+
environment,
|
|
77
|
+
...(this.options.hmr === undefined ? null : { hmr: this.options.hmr }),
|
|
78
|
+
...(this.options.dev === undefined ? null : { dev: this.options.dev }),
|
|
79
|
+
...(this.options.parallelUse === undefined
|
|
80
|
+
? null
|
|
81
|
+
: { parallelUse: this.options.parallelUse }),
|
|
82
|
+
...(this.options.exclude === undefined ? null : { exclude: this.options.exclude }),
|
|
83
|
+
};
|
|
84
|
+
compiler.options.module.rules.push({
|
|
85
|
+
test: OCTANE_RULE,
|
|
86
|
+
type: 'javascript/auto',
|
|
87
|
+
enforce: 'pre',
|
|
88
|
+
use: [{ loader: loaderPath, options: loaderOptions }],
|
|
89
|
+
});
|
|
90
|
+
if (this.options.transpile !== false) {
|
|
91
|
+
compiler.options.module.rules.push({
|
|
92
|
+
test: TYPESCRIPT_RULE,
|
|
93
|
+
type: 'javascript/auto',
|
|
94
|
+
use: [{ loader: 'builtin:swc-loader', options: { detectSyntax: 'auto' } }],
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
let discovery;
|
|
99
|
+
const discover = () => {
|
|
100
|
+
if (discovery === undefined) {
|
|
101
|
+
discovery = neutralCompiler.discoverSourceDependencies();
|
|
102
|
+
this.sourceDependencies = Object.freeze([...(discovery.packages ?? [])]);
|
|
103
|
+
}
|
|
104
|
+
return discovery;
|
|
105
|
+
};
|
|
106
|
+
compiler.hooks.invalid?.tap(PLUGIN_NAME, (filename) => {
|
|
107
|
+
neutralCompiler.invalidate(filename);
|
|
108
|
+
discovery = undefined;
|
|
109
|
+
});
|
|
110
|
+
compiler.hooks.watchRun?.tap(PLUGIN_NAME, () => {
|
|
111
|
+
neutralCompiler.invalidate();
|
|
112
|
+
discovery = undefined;
|
|
113
|
+
});
|
|
114
|
+
compiler.hooks.thisCompilation?.tap(PLUGIN_NAME, (compilation) => {
|
|
115
|
+
const current = discover();
|
|
116
|
+
addDependencies(compilation.fileDependencies, current.dependencies);
|
|
117
|
+
addDependencies(compilation.missingDependencies, current.missingDependencies);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function octaneRspack(options) {
|
|
123
|
+
return new OctaneRspackPlugin(options);
|
|
124
|
+
}
|
package/src/shared.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const CLIENT_TARGETS = new Set(['web', 'webworker', 'electron-renderer', 'browserslist']);
|
|
2
|
+
|
|
3
|
+
function targetValues(target) {
|
|
4
|
+
if (Array.isArray(target)) return target.flatMap(targetValues);
|
|
5
|
+
return typeof target === 'string' ? [target.toLowerCase()] : [];
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Infer which Octane runtime a Rspack compilation consumes. Explicit plugin
|
|
10
|
+
* options remain authoritative; this helper covers Rspack's standard target
|
|
11
|
+
* names when the environment is omitted.
|
|
12
|
+
*/
|
|
13
|
+
export function inferRspackEnvironment(target) {
|
|
14
|
+
const values = targetValues(target);
|
|
15
|
+
for (const value of values) {
|
|
16
|
+
if (
|
|
17
|
+
value === 'node' ||
|
|
18
|
+
value.startsWith('node') ||
|
|
19
|
+
value === 'async-node' ||
|
|
20
|
+
value.startsWith('async-node') ||
|
|
21
|
+
value === 'electron-main' ||
|
|
22
|
+
value === 'electron-preload' ||
|
|
23
|
+
value === 'nwjs' ||
|
|
24
|
+
value.startsWith('nwjs')
|
|
25
|
+
) {
|
|
26
|
+
return 'server';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const value of values) {
|
|
30
|
+
if (CLIENT_TARGETS.has(value) || value.startsWith('web')) return 'client';
|
|
31
|
+
}
|
|
32
|
+
return 'client';
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const LOADER_OPTION_KEYS = new Set(['root', 'environment', 'hmr', 'dev', 'parallelUse', 'exclude']);
|
|
36
|
+
const PLUGIN_OPTION_KEYS = new Set([...LOADER_OPTION_KEYS, 'transpile']);
|
|
37
|
+
|
|
38
|
+
function assertBooleanOption(options, key) {
|
|
39
|
+
if (options[key] !== undefined && typeof options[key] !== 'boolean') {
|
|
40
|
+
throw new TypeError(`@octanejs/rspack-plugin: \`${key}\` must be a boolean.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeOptions(value, plugin) {
|
|
45
|
+
const options = value ?? {};
|
|
46
|
+
if (typeof options !== 'object' || Array.isArray(options)) {
|
|
47
|
+
throw new TypeError('@octanejs/rspack-plugin: options must be an object.');
|
|
48
|
+
}
|
|
49
|
+
const allowed = plugin ? PLUGIN_OPTION_KEYS : LOADER_OPTION_KEYS;
|
|
50
|
+
for (const key of Object.keys(options)) {
|
|
51
|
+
if (!allowed.has(key)) {
|
|
52
|
+
throw new TypeError(`@octanejs/rspack-plugin: unknown option \`${key}\`.`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (options.root !== undefined && typeof options.root !== 'string') {
|
|
56
|
+
throw new TypeError('@octanejs/rspack-plugin: `root` must be a path string.');
|
|
57
|
+
}
|
|
58
|
+
if (
|
|
59
|
+
options.environment !== undefined &&
|
|
60
|
+
options.environment !== 'client' &&
|
|
61
|
+
options.environment !== 'server'
|
|
62
|
+
) {
|
|
63
|
+
throw new TypeError('@octanejs/rspack-plugin: `environment` must be "client" or "server".');
|
|
64
|
+
}
|
|
65
|
+
assertBooleanOption(options, 'hmr');
|
|
66
|
+
assertBooleanOption(options, 'dev');
|
|
67
|
+
assertBooleanOption(options, 'parallelUse');
|
|
68
|
+
if (
|
|
69
|
+
options.exclude !== undefined &&
|
|
70
|
+
(!Array.isArray(options.exclude) || options.exclude.some((entry) => typeof entry !== 'string'))
|
|
71
|
+
) {
|
|
72
|
+
throw new TypeError('@octanejs/rspack-plugin: `exclude` must be an array of path strings.');
|
|
73
|
+
}
|
|
74
|
+
if (plugin) assertBooleanOption(options, 'transpile');
|
|
75
|
+
|
|
76
|
+
const normalized = {
|
|
77
|
+
...(options.root === undefined ? null : { root: options.root }),
|
|
78
|
+
...(options.environment === undefined ? null : { environment: options.environment }),
|
|
79
|
+
...(options.hmr === undefined ? null : { hmr: options.hmr }),
|
|
80
|
+
...(options.dev === undefined ? null : { dev: options.dev }),
|
|
81
|
+
...(options.parallelUse === undefined ? null : { parallelUse: options.parallelUse }),
|
|
82
|
+
...(options.exclude === undefined ? null : { exclude: [...options.exclude] }),
|
|
83
|
+
...(plugin && options.transpile !== undefined ? { transpile: options.transpile } : null),
|
|
84
|
+
};
|
|
85
|
+
if (normalized.exclude) Object.freeze(normalized.exclude);
|
|
86
|
+
return Object.freeze(normalized);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function normalizeLoaderOptions(value) {
|
|
90
|
+
return normalizeOptions(value, false);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function normalizePluginOptions(value) {
|
|
94
|
+
return normalizeOptions(value, true);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Read the serializable metadata attached to an Octane-transformed module. */
|
|
98
|
+
export function getOctaneRspackBuildInfo(module) {
|
|
99
|
+
const value = module?.buildInfo?.octane;
|
|
100
|
+
if (
|
|
101
|
+
value &&
|
|
102
|
+
typeof value === 'object' &&
|
|
103
|
+
typeof value.canonicalId === 'string' &&
|
|
104
|
+
(value.transformKind === 'compile' || value.transformKind === 'slots') &&
|
|
105
|
+
typeof value.serverRpc === 'boolean'
|
|
106
|
+
) {
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Compiler, RspackPluginInstance } from '@rspack/core';
|
|
2
|
+
|
|
3
|
+
export type OctaneRspackEnvironment = 'client' | 'server';
|
|
4
|
+
|
|
5
|
+
export interface OctaneRspackLoaderOptions {
|
|
6
|
+
/** Project root used to canonicalize module IDs and discover package manifests. */
|
|
7
|
+
root?: string;
|
|
8
|
+
/** Explicit compiler target. Standard Rspack web/node targets are inferred when omitted. */
|
|
9
|
+
environment?: OctaneRspackEnvironment;
|
|
10
|
+
/** Allow webpack-dialect HMR codegen when the loader context is hot. Default `true`. */
|
|
11
|
+
hmr?: boolean;
|
|
12
|
+
/** Emit client development metadata. Defaults to Rspack's non-production mode. */
|
|
13
|
+
dev?: boolean;
|
|
14
|
+
/** Enable Octane's parallel `use()` compilation pipeline. Default `true`. */
|
|
15
|
+
parallelUse?: boolean;
|
|
16
|
+
/** Path fragments excluded from the plain `.ts`/`.js` hook-slot pass. */
|
|
17
|
+
exclude?: string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface OctaneRspackPluginOptions extends OctaneRspackLoaderOptions {
|
|
21
|
+
/**
|
|
22
|
+
* Add Rspack's built-in SWC loader to strip TypeScript after Octane runs.
|
|
23
|
+
* Disable this when another rule already owns TypeScript transpilation.
|
|
24
|
+
* @default true
|
|
25
|
+
*/
|
|
26
|
+
transpile?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface OctaneRspackBuildInfo {
|
|
30
|
+
canonicalId: string;
|
|
31
|
+
transformKind: 'compile' | 'slots';
|
|
32
|
+
serverRpc: boolean;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export declare class OctaneRspackPlugin implements RspackPluginInstance {
|
|
36
|
+
constructor(options?: OctaneRspackPluginOptions);
|
|
37
|
+
readonly options: Readonly<OctaneRspackPluginOptions>;
|
|
38
|
+
/** Raw installed packages discovered from Octane package manifests after compilation starts. */
|
|
39
|
+
sourceDependencies: readonly string[];
|
|
40
|
+
apply(compiler: Compiler): void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export declare function octaneRspack(options?: OctaneRspackPluginOptions): OctaneRspackPlugin;
|
|
44
|
+
|
|
45
|
+
/** Infer client/server compilation from standard Rspack target names. */
|
|
46
|
+
export declare function inferRspackEnvironment(target: unknown): OctaneRspackEnvironment;
|
|
47
|
+
|
|
48
|
+
/** Read the serializable metadata emitted by the loader for app-level collectors. */
|
|
49
|
+
export declare function getOctaneRspackBuildInfo(module: unknown): OctaneRspackBuildInfo | null;
|