@golar-rstack/rspack 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 +61 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +159 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Oskar Lebuda
|
|
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,61 @@
|
|
|
1
|
+
# @golar-rstack/rspack
|
|
2
|
+
|
|
3
|
+
Run [golar](https://golar.dev) type checking and type-aware linting in a
|
|
4
|
+
separate process as an Rspack plugin.
|
|
5
|
+
|
|
6
|
+
Because golar is built on `typescript-go` and understands embedded languages,
|
|
7
|
+
type errors inside `.vue`, `.svelte`, `.astro` and `.gts` files are reported at
|
|
8
|
+
their real position in the source file.
|
|
9
|
+
|
|
10
|
+
Using Rsbuild? Use [`@golar-rstack/rsbuild`](https://www.npmjs.com/package/@golar-rstack/rsbuild)
|
|
11
|
+
instead.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add -D @golar-rstack/rspack golar
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Usage
|
|
20
|
+
|
|
21
|
+
Create `golar.config.ts` in your project root. golar discovers it relative to
|
|
22
|
+
its working directory:
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { defineConfig } from 'golar/unstable'
|
|
26
|
+
import '@golar/vue'
|
|
27
|
+
|
|
28
|
+
export default defineConfig({})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
// rspack.config.ts
|
|
33
|
+
import { GolarRspackPlugin } from '@golar-rstack/rspack'
|
|
34
|
+
|
|
35
|
+
export default {
|
|
36
|
+
plugins: [new GolarRspackPlugin()],
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
In watch mode the check runs asynchronously and is pushed to the dev server's
|
|
41
|
+
error overlay. In a one-off build the compilation waits for golar, so type
|
|
42
|
+
errors fail the build.
|
|
43
|
+
|
|
44
|
+
## Options
|
|
45
|
+
|
|
46
|
+
See the [repository README](https://github.com/OskarLebuda/golar-rstack-plugin#options)
|
|
47
|
+
for the full list. Two worth knowing up front:
|
|
48
|
+
|
|
49
|
+
- `lintSeverity` defaults to `'warning'`, because golar exits 0 when only lint
|
|
50
|
+
rules fire.
|
|
51
|
+
- `cwd` defaults to the compiler `context` and is what selects the golar config,
|
|
52
|
+
since golar has no `--config` flag.
|
|
53
|
+
|
|
54
|
+
## Requirements
|
|
55
|
+
|
|
56
|
+
- Node.js >= 22.12
|
|
57
|
+
- `golar` installed in the project
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import type { Compiler } from '@rspack/core';
|
|
2
|
+
import { GolarMode } from '@golar-rstack/core';
|
|
3
|
+
import { GolarOptions } from '@golar-rstack/core';
|
|
4
|
+
import { Issue } from '@golar-rstack/core';
|
|
5
|
+
import { IssueFilter } from '@golar-rstack/core';
|
|
6
|
+
import { IssueOrigin } from '@golar-rstack/core';
|
|
7
|
+
import { IssueSeverity } from '@golar-rstack/core';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* An error shaped the way Rspack expects.
|
|
11
|
+
*
|
|
12
|
+
* Rspack only renders `loc` for errors carrying a real `NormalModule`, which
|
|
13
|
+
* this plugin has no way to supply, so the position is folded into `file`
|
|
14
|
+
* instead. This is the same workaround ts-checker-rspack-plugin uses.
|
|
15
|
+
*/
|
|
16
|
+
export declare class GolarIssueError extends Error {
|
|
17
|
+
readonly issue: Issue;
|
|
18
|
+
readonly hideStack = true;
|
|
19
|
+
file?: string;
|
|
20
|
+
/**
|
|
21
|
+
* @param message The formatted text Rspack will display.
|
|
22
|
+
* @param issue The issue this error was built from, kept so consumers can
|
|
23
|
+
* inspect the original diagnostic.
|
|
24
|
+
*/
|
|
25
|
+
constructor(message: string, issue: Issue);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { GolarMode }
|
|
29
|
+
|
|
30
|
+
export { GolarOptions }
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Runs [golar](https://golar.dev), which type checks and lints TypeScript and
|
|
34
|
+
* embedded languages such as Vue, Svelte and Astro, in a separate process, and
|
|
35
|
+
* reports what it finds as Rspack errors and warnings.
|
|
36
|
+
*/
|
|
37
|
+
export declare class GolarRspackPlugin {
|
|
38
|
+
private readonly options;
|
|
39
|
+
/**
|
|
40
|
+
* @param options Plugin options. See the package README for the defaults.
|
|
41
|
+
*/
|
|
42
|
+
constructor(options?: GolarRspackPluginOptions);
|
|
43
|
+
/**
|
|
44
|
+
* Registers the plugin on a compiler. Called by Rspack.
|
|
45
|
+
*
|
|
46
|
+
* @param compiler The compiler to attach to.
|
|
47
|
+
*/
|
|
48
|
+
apply(compiler: Compiler): void;
|
|
49
|
+
/**
|
|
50
|
+
* Records the dev server's `done` tap so async results can be replayed into
|
|
51
|
+
* the browser overlay.
|
|
52
|
+
*
|
|
53
|
+
* Installed from `apply` so the interceptor is in place before the dev server
|
|
54
|
+
* registers its own tap.
|
|
55
|
+
*
|
|
56
|
+
* @param compiler The compiler being set up.
|
|
57
|
+
* @param state State to store the captured tap on.
|
|
58
|
+
*/
|
|
59
|
+
private interceptDevServerTap;
|
|
60
|
+
/**
|
|
61
|
+
* Resolves options and installs the reporting hooks on the first run.
|
|
62
|
+
*
|
|
63
|
+
* This is deferred because the default for `async` depends on whether this is
|
|
64
|
+
* a build or a watch, which is not known when `apply` runs.
|
|
65
|
+
*
|
|
66
|
+
* @param compiler The compiler being set up.
|
|
67
|
+
* @param state State to mark as initialized.
|
|
68
|
+
*/
|
|
69
|
+
private tapInitialization;
|
|
70
|
+
/**
|
|
71
|
+
* Starts a golar run for each compilation, superseding any run still going.
|
|
72
|
+
*
|
|
73
|
+
* Runs are chained rather than overlapped, because golar is a whole project
|
|
74
|
+
* check backed by a native process and concurrency would only contend for
|
|
75
|
+
* CPU.
|
|
76
|
+
*
|
|
77
|
+
* @param compiler The compiler being set up.
|
|
78
|
+
* @param state State holding the current run and its abort controller.
|
|
79
|
+
* @param logger Infrastructure logger used for debug output.
|
|
80
|
+
*/
|
|
81
|
+
private tapCompilationToRunGolar;
|
|
82
|
+
/**
|
|
83
|
+
* Installs blocking reporting, where the compilation waits for golar and owns
|
|
84
|
+
* the issues it found.
|
|
85
|
+
*
|
|
86
|
+
* @param compiler The compiler being set up.
|
|
87
|
+
* @param state State holding the current run.
|
|
88
|
+
*/
|
|
89
|
+
private tapAfterCompileToReport;
|
|
90
|
+
/**
|
|
91
|
+
* Installs async reporting, where the build is never held up.
|
|
92
|
+
*
|
|
93
|
+
* Issues are logged when they arrive and, if a dev server is listening, its
|
|
94
|
+
* `done` tap is replayed with the issues attached. That re-sends the stats
|
|
95
|
+
* over the HMR socket, which is what makes the browser overlay show them.
|
|
96
|
+
*
|
|
97
|
+
* @param compiler The compiler being set up.
|
|
98
|
+
* @param state State holding the current run and the captured dev server tap.
|
|
99
|
+
*/
|
|
100
|
+
private tapDoneToReportAsync;
|
|
101
|
+
/**
|
|
102
|
+
* Aborts any run still going when the compiler shuts down.
|
|
103
|
+
*
|
|
104
|
+
* @param compiler The compiler being set up.
|
|
105
|
+
* @param state State holding the abort controller.
|
|
106
|
+
*/
|
|
107
|
+
private tapCloseToAbort;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export declare interface GolarRspackPluginOptions extends GolarOptions {
|
|
111
|
+
devServer?: boolean;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export { Issue }
|
|
115
|
+
|
|
116
|
+
export { IssueFilter }
|
|
117
|
+
|
|
118
|
+
export { IssueOrigin }
|
|
119
|
+
|
|
120
|
+
export { IssueSeverity }
|
|
121
|
+
|
|
122
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import node_path from "node:path";
|
|
2
|
+
import node_process from "node:process";
|
|
3
|
+
import { GolarAbortError, formatIssue, formatIssueSummary, resolveGolarOptions, runGolar } from "@golar-rstack/core";
|
|
4
|
+
class GolarIssueError extends Error {
|
|
5
|
+
issue;
|
|
6
|
+
hideStack = true;
|
|
7
|
+
file;
|
|
8
|
+
constructor(message, issue){
|
|
9
|
+
super(message), this.issue = issue;
|
|
10
|
+
this.name = 'GolarIssueError';
|
|
11
|
+
if (issue.file) {
|
|
12
|
+
const relative = node_path.relative(node_process.cwd(), issue.file);
|
|
13
|
+
this.file = relative.startsWith('..') ? issue.file : relative;
|
|
14
|
+
if (issue.location) this.file += `:${issue.location.line}:${issue.location.column}`;
|
|
15
|
+
}
|
|
16
|
+
Error.captureStackTrace(this, this.constructor);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function prepareIssues(issues, options) {
|
|
20
|
+
const filtered = options.filter ? issues.filter((issue)=>options.filter(issue)) : issues;
|
|
21
|
+
if (options.failOnError) return filtered;
|
|
22
|
+
return filtered.map((issue)=>'error' === issue.severity ? {
|
|
23
|
+
...issue,
|
|
24
|
+
severity: 'warning'
|
|
25
|
+
} : issue);
|
|
26
|
+
}
|
|
27
|
+
function pushIssues(compilation, issues, options) {
|
|
28
|
+
for (const issue of issues){
|
|
29
|
+
const error = new GolarIssueError(formatIssue(issue, {
|
|
30
|
+
cwd: options.cwd,
|
|
31
|
+
formatter: options.formatter
|
|
32
|
+
}), issue);
|
|
33
|
+
if ('warning' === issue.severity) compilation.warnings.push(error);
|
|
34
|
+
else compilation.errors.push(error);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function createPluginState() {
|
|
38
|
+
return {
|
|
39
|
+
initialized: false,
|
|
40
|
+
watching: false,
|
|
41
|
+
iteration: 0
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const PLUGIN_NAME = 'GolarRspackPlugin';
|
|
45
|
+
const DEV_SERVER_TAPS = [
|
|
46
|
+
'webpack-dev-server',
|
|
47
|
+
'rspack-dev-server',
|
|
48
|
+
'rsbuild-dev-server'
|
|
49
|
+
];
|
|
50
|
+
class GolarRspackPlugin {
|
|
51
|
+
options;
|
|
52
|
+
constructor(options = {}){
|
|
53
|
+
this.options = options;
|
|
54
|
+
}
|
|
55
|
+
apply(compiler) {
|
|
56
|
+
const state = createPluginState();
|
|
57
|
+
const logger = compiler.getInfrastructureLogger(PLUGIN_NAME);
|
|
58
|
+
this.interceptDevServerTap(compiler, state);
|
|
59
|
+
this.tapInitialization(compiler, state);
|
|
60
|
+
this.tapCompilationToRunGolar(compiler, state, logger);
|
|
61
|
+
this.tapCloseToAbort(compiler, state);
|
|
62
|
+
}
|
|
63
|
+
interceptDevServerTap(compiler, state) {
|
|
64
|
+
if (false === this.options.devServer) return;
|
|
65
|
+
compiler.hooks.done.intercept({
|
|
66
|
+
register: (tap)=>{
|
|
67
|
+
const candidate = tap;
|
|
68
|
+
if (DEV_SERVER_TAPS.includes(candidate.name) && 'sync' === candidate.type) state.devServerDoneTap = candidate;
|
|
69
|
+
return tap;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
tapInitialization(compiler, state) {
|
|
74
|
+
const initialize = (watching)=>{
|
|
75
|
+
if (state.initialized) return;
|
|
76
|
+
state.initialized = true;
|
|
77
|
+
state.watching = watching;
|
|
78
|
+
state.options = resolveGolarOptions(this.options, {
|
|
79
|
+
cwd: compiler.context ?? process.cwd(),
|
|
80
|
+
watch: watching
|
|
81
|
+
});
|
|
82
|
+
if (state.options.async) this.tapDoneToReportAsync(compiler, state);
|
|
83
|
+
else this.tapAfterCompileToReport(compiler, state);
|
|
84
|
+
};
|
|
85
|
+
compiler.hooks.run.tap(PLUGIN_NAME, ()=>initialize(false));
|
|
86
|
+
compiler.hooks.watchRun.tap(PLUGIN_NAME, ()=>initialize(true));
|
|
87
|
+
}
|
|
88
|
+
tapCompilationToRunGolar(compiler, state, logger) {
|
|
89
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation)=>{
|
|
90
|
+
if (compilation.compiler !== compiler) return;
|
|
91
|
+
const options = state.options;
|
|
92
|
+
if (!options) return;
|
|
93
|
+
const iteration = ++state.iteration;
|
|
94
|
+
state.abortController?.abort();
|
|
95
|
+
const abortController = new AbortController();
|
|
96
|
+
state.abortController = abortController;
|
|
97
|
+
state.issuesPromise = (state.issuesPromise ?? Promise.resolve(void 0)).catch(()=>void 0).then(async ()=>{
|
|
98
|
+
if (abortController.signal.aborted) return;
|
|
99
|
+
logger.debug(`Running golar, iteration ${iteration}.`);
|
|
100
|
+
try {
|
|
101
|
+
const result = await runGolar(options, abortController.signal);
|
|
102
|
+
logger.debug(`golar iteration ${iteration} found ${result.issues.length} issue(s).`);
|
|
103
|
+
return result.issues;
|
|
104
|
+
} catch (error) {
|
|
105
|
+
if (error instanceof GolarAbortError) return void logger.debug(`golar iteration ${iteration} aborted.`);
|
|
106
|
+
compilation.errors.push(error);
|
|
107
|
+
return;
|
|
108
|
+
} finally{
|
|
109
|
+
if (state.abortController === abortController) state.abortController = void 0;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
tapAfterCompileToReport(compiler, state) {
|
|
115
|
+
compiler.hooks.afterCompile.tapPromise(PLUGIN_NAME, async (compilation)=>{
|
|
116
|
+
if (compilation.compiler !== compiler || !state.options) return;
|
|
117
|
+
const issues = await state.issuesPromise;
|
|
118
|
+
if (!issues) return;
|
|
119
|
+
pushIssues(compilation, prepareIssues(issues, state.options), state.options);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
tapDoneToReportAsync(compiler, state) {
|
|
123
|
+
const logger = compiler.getInfrastructureLogger(PLUGIN_NAME);
|
|
124
|
+
compiler.hooks.done.tap(PLUGIN_NAME, (stats)=>{
|
|
125
|
+
if (stats.compilation.compiler !== compiler || !state.options) return;
|
|
126
|
+
const options = state.options;
|
|
127
|
+
const issuesPromise = state.issuesPromise;
|
|
128
|
+
(async ()=>{
|
|
129
|
+
let issues;
|
|
130
|
+
try {
|
|
131
|
+
issues = await issuesPromise;
|
|
132
|
+
} catch {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
if (!issues || state.issuesPromise !== issuesPromise) return;
|
|
136
|
+
const prepared = prepareIssues(issues, options);
|
|
137
|
+
if (0 === prepared.length) return void logger.info('No golar issues found.');
|
|
138
|
+
logger.error(formatIssueSummary(prepared));
|
|
139
|
+
for (const issue of prepared)logger.error(formatIssue(issue, {
|
|
140
|
+
cwd: options.cwd,
|
|
141
|
+
formatter: options.formatter
|
|
142
|
+
}));
|
|
143
|
+
if (state.devServerDoneTap) {
|
|
144
|
+
pushIssues(stats.compilation, prepared, options);
|
|
145
|
+
state.devServerDoneTap.fn(stats);
|
|
146
|
+
}
|
|
147
|
+
})();
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
tapCloseToAbort(compiler, state) {
|
|
151
|
+
const abort = ()=>{
|
|
152
|
+
state.abortController?.abort();
|
|
153
|
+
state.abortController = void 0;
|
|
154
|
+
};
|
|
155
|
+
compiler.hooks.watchClose.tap(PLUGIN_NAME, abort);
|
|
156
|
+
compiler.hooks.shutdown?.tap(PLUGIN_NAME, abort);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
export { GolarIssueError, GolarRspackPlugin };
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@golar-rstack/rspack",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"description": "Run golar type checking and linting in a separate process as an Rspack plugin",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Oskar Lebuda",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"golar",
|
|
10
|
+
"rspack",
|
|
11
|
+
"rspack-plugin",
|
|
12
|
+
"typecheck",
|
|
13
|
+
"typescript",
|
|
14
|
+
"vue",
|
|
15
|
+
"svelte",
|
|
16
|
+
"astro",
|
|
17
|
+
"rstack"
|
|
18
|
+
],
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"default": "./dist/index.js"
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"module": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"files": [
|
|
28
|
+
"dist"
|
|
29
|
+
],
|
|
30
|
+
"sideEffects": false,
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@golar-rstack/core": "0.1.0"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@rspack/core": "^1.0.0 || ^2.0.0",
|
|
36
|
+
"golar": ">=0.1.0"
|
|
37
|
+
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"@rspack/core": {
|
|
40
|
+
"optional": true
|
|
41
|
+
},
|
|
42
|
+
"golar": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@rslib/core": "^0.23.2",
|
|
48
|
+
"@rspack/core": "^2.1.5",
|
|
49
|
+
"@rstest/core": "^0.11.4",
|
|
50
|
+
"@types/node": "^24.13.3",
|
|
51
|
+
"typescript": "^6.0.3"
|
|
52
|
+
},
|
|
53
|
+
"publishConfig": {
|
|
54
|
+
"access": "public"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=22.12"
|
|
58
|
+
},
|
|
59
|
+
"scripts": {
|
|
60
|
+
"build": "rslib build",
|
|
61
|
+
"dev": "rslib build --watch",
|
|
62
|
+
"test": "rstest run",
|
|
63
|
+
"check:publish": "publint && attw --pack ."
|
|
64
|
+
}
|
|
65
|
+
}
|