resultar-check 1.0.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 +169 -0
- package/dist/cli.cjs +32 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +33 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.cjs +95 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/lint-D-D2dmLi.cjs +1030 -0
- package/dist/lint-D-D2dmLi.cjs.map +1 -0
- package/dist/lint-FMWf8UEv.js +1001 -0
- package/dist/lint-FMWf8UEv.js.map +1 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 Giorgio Delgado
|
|
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,169 @@
|
|
|
1
|
+
# Resultar Check
|
|
2
|
+
|
|
3
|
+
TypeScript 7 diagnostics for Resultar.
|
|
4
|
+
|
|
5
|
+
`resultar-check` is the canonical Resultar diagnostics package and command. It runs TypeScript 7
|
|
6
|
+
first, then runs Resultar diagnostics over the same `tsconfig.json`. Oxlint integration and TS6
|
|
7
|
+
patching are no longer shipped as public lint surfaces.
|
|
8
|
+
|
|
9
|
+
Until TypeScript 7 exposes the stable programmatic type-checker API Resultar needs, the package keeps
|
|
10
|
+
a TypeScript 6 diagnostics API as an internal bridge. Projects should still standardize on the
|
|
11
|
+
TypeScript 7 `resultar-check` command.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
For editor diagnostics and CLI checks, install `resultar-check` with a project-local TypeScript RC:
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
pnpm add -D resultar-check typescript@rc
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Add one check script:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"scripts": {
|
|
26
|
+
"check": "resultar-check -p tsconfig.json --noEmit"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Configure Resultar rules in `tsconfig.json`:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"compilerOptions": {
|
|
36
|
+
"plugins": [
|
|
37
|
+
{
|
|
38
|
+
"name": "resultar-check",
|
|
39
|
+
"noDiscard": "error",
|
|
40
|
+
"preferMapErr": "error",
|
|
41
|
+
"preferAndThen": "error"
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`resultar-check` resolves a project-local `typescript@7` first, then `typescript-7`. If a project
|
|
49
|
+
cannot replace its `typescript` package yet and only needs the CLI path, use the compatibility alias:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
pnpm add -D resultar-check typescript-7@npm:typescript@rc
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Editor Integration
|
|
56
|
+
|
|
57
|
+
`resultar-check` is a TypeScript language-service plugin. Editors must run a TypeScript server that
|
|
58
|
+
can resolve the local `resultar-check` package and see the `compilerOptions.plugins` entry above.
|
|
59
|
+
|
|
60
|
+
### Zed
|
|
61
|
+
|
|
62
|
+
Zed uses `vtsls` for TypeScript by default. Add this to your Zed settings when working in pnpm
|
|
63
|
+
monorepos or when the bundled TypeScript server does not activate the plugin:
|
|
64
|
+
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"lsp": {
|
|
68
|
+
"vtsls": {
|
|
69
|
+
"settings": {
|
|
70
|
+
"vtsls": {
|
|
71
|
+
"autoUseWorkspaceTsdk": true
|
|
72
|
+
},
|
|
73
|
+
"typescript": {
|
|
74
|
+
"tsserver": {
|
|
75
|
+
"pluginPaths": ["./node_modules"]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
If diagnostics do not appear, temporarily add `"log": "normal"` under `typescript.tsserver`, restart
|
|
85
|
+
the TypeScript server, and check the Zed log. A working setup reports diagnostics whose source is
|
|
86
|
+
`resultar` and whose message starts with `[resultar/noDiscard]`.
|
|
87
|
+
|
|
88
|
+
If you use `typescript-language-server` in Zed instead of `vtsls`, pass the plugin through
|
|
89
|
+
`initialization_options.plugins` and set `location` to the project root, not `./node_modules`.
|
|
90
|
+
`typescript-language-server` resolves plugin packages from `${location}/node_modules`.
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"lsp": {
|
|
95
|
+
"typescript-language-server": {
|
|
96
|
+
"initialization_options": {
|
|
97
|
+
"plugins": [
|
|
98
|
+
{
|
|
99
|
+
"name": "resultar-check",
|
|
100
|
+
"location": "/absolute/path/to/project"
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### VS Code
|
|
110
|
+
|
|
111
|
+
Configure VS Code to use the workspace TypeScript version:
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"typescript.tsdk": "node_modules/typescript/lib",
|
|
116
|
+
"typescript.enablePromptUseWorkspaceTsdk": true
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Then run `TypeScript: Select TypeScript Version` and choose the workspace version. In pnpm monorepos
|
|
121
|
+
where the plugin still does not activate, add this setting:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{
|
|
125
|
+
"typescript.tsserver.pluginPaths": ["./node_modules"]
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Rules
|
|
130
|
+
|
|
131
|
+
| Rule | tsconfig option | Purpose |
|
|
132
|
+
| ----------------------------------------------- | ---------------------------------- | ---------------------------------------------------------------------------------- |
|
|
133
|
+
| `resultar/no-discard` | `noDiscard` | Require `Result` and `ResultAsync` values to be handled or explicitly discarded. |
|
|
134
|
+
| `resultar/prefer-map-err` | `preferMapErr` | Prefer `mapErr` when `orElse` only returns another `Err`. |
|
|
135
|
+
| `resultar/prefer-and-then` | `preferAndThen` | Prefer `andThen` / `asyncAndThen` when `map` returns a Resultar value. |
|
|
136
|
+
| `resultar/typed-catch-mapper` | `typedCatchMapper` | Require catch conversion helpers to map caught values to typed errors. |
|
|
137
|
+
| `resultar/no-unsafe-await` | `noUnsafeAwait` | Require raw Promise awaits in Resultar async contexts to use Resultar boundaries. |
|
|
138
|
+
| `resultar/no-try-catch-in-safe-try` | `noTryCatchInSafeTry` | Avoid raw `try/catch` inside `safeTry` generators. |
|
|
139
|
+
| `resultar/yield-star-in-safe-try` | `yieldStarInSafeTry` | Require `yield*` for Resultar values inside `safeTry`. |
|
|
140
|
+
| `resultar/unsafe-result-type-assertion` | `unsafeResultTypeAssertion` | Prevent assertions that narrow Resultar error channels. |
|
|
141
|
+
| `resultar/prefer-tagged-error` | `preferTaggedError` | Prefer `createTaggedError` over plain `Error` subclasses or `err(new Error(...))`. |
|
|
142
|
+
| `resultar/tagged-error-name-match` | `taggedErrorNameMatch` | Require `createTaggedError({ name })` to match the class name. |
|
|
143
|
+
| `resultar/no-tagged-error-constructor-override` | `noTaggedErrorConstructorOverride` | Keep the generated tagged-error constructor intact. |
|
|
144
|
+
| `resultar/no-useless-recovery` | `noUselessRecovery` | Flag recovery calls on `Result<T, never>` / `ResultAsync<T, never>`. |
|
|
145
|
+
|
|
146
|
+
Each option accepts `"error"`, `"warning"`, `"suggestion"`, `"message"`, or `"off"`.
|
|
147
|
+
`noUnsafeAwait` defaults to `"off"` because it is an architectural rule and may require migration in
|
|
148
|
+
existing async code. Enable it explicitly when a project is ready to enforce Resultar async
|
|
149
|
+
boundaries. By default, `noUnsafeAwait` uses `noUnsafeAwaitMode: "resultar-context"` and checks
|
|
150
|
+
functions returning `ResultAsync` or `Promise<Result>`, plus `safeTry` bodies. Raw Promise awaits are
|
|
151
|
+
allowed in async catch helpers such as `tryAsync`, `tryResultAsync`, `tryCatchAsync`, and
|
|
152
|
+
`fromThrowableAsync`; inside `safeTry`, prefer `yield*` with Resultar values. Use
|
|
153
|
+
`noUnsafeAwaitMode: "all"` to also report framework/bootstrap awaits such as Fastify plugin
|
|
154
|
+
registration.
|
|
155
|
+
|
|
156
|
+
The default `noDiscard` mode is neverthrow-style `must-use`: it reports discarded Resultar
|
|
157
|
+
expressions and assigned `Result` values that are passed around but never consumed with `match`,
|
|
158
|
+
`unwrapOr`, `_unsafeUnwrap`, `isOk`, `isErr`, returned, or explicitly discarded. Use
|
|
159
|
+
`--mode direct` for the lower-noise expression-only check.
|
|
160
|
+
|
|
161
|
+
```sh
|
|
162
|
+
pnpm exec resultar-check -p tsconfig.json --noEmit
|
|
163
|
+
pnpm exec resultar-check -p tsconfig.json --noEmit --mode direct
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Deprecated Packages
|
|
167
|
+
|
|
168
|
+
`resultar-lint` and `resultar-tsgo` are compatibility wrappers. New projects should install
|
|
169
|
+
`resultar-check` directly and use plugin name `"resultar-check"`.
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const require_lint = require("./lint-D-D2dmLi.cjs");
|
|
3
|
+
//#region src/cli.ts
|
|
4
|
+
const usage = `Usage: resultar-check -p tsconfig.json --noEmit
|
|
5
|
+
|
|
6
|
+
Runs TypeScript 7 first, then all enabled Resultar diagnostics from tsconfig plugin options.
|
|
7
|
+
`;
|
|
8
|
+
const run = (args = process.argv.slice(2)) => {
|
|
9
|
+
const [command] = args;
|
|
10
|
+
if (command === "check") {
|
|
11
|
+
process.stderr.write("The check subcommand was removed. Use resultar-check -p tsconfig.json --noEmit.\n");
|
|
12
|
+
return 1;
|
|
13
|
+
}
|
|
14
|
+
if (command === "help" || command === "--help" || command === "-h" || command === void 0) {
|
|
15
|
+
process.stdout.write(usage);
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
if (command === "patch" || command === "doctor" || command === "unpatch") {
|
|
19
|
+
process.stderr.write(`${command} was removed. Use resultar-check with TypeScript 7.\n`);
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
return require_lint.runResultarCheckCli(args);
|
|
23
|
+
};
|
|
24
|
+
try {
|
|
25
|
+
process.exitCode = run();
|
|
26
|
+
} catch (error) {
|
|
27
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.cjs","names":["runResultarCheckCli"],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runResultarCheckCli } from \"./lint.js\";\n\nconst usage = `Usage: resultar-check -p tsconfig.json --noEmit\n\nRuns TypeScript 7 first, then all enabled Resultar diagnostics from tsconfig plugin options.\n`;\n\nconst run = (args: readonly string[] = process.argv.slice(2)): number => {\n const [command] = args;\n\n if (command === \"check\") {\n process.stderr.write(\n \"The check subcommand was removed. Use resultar-check -p tsconfig.json --noEmit.\\n\",\n );\n return 1;\n }\n\n if (command === \"help\" || command === \"--help\" || command === \"-h\" || command === undefined) {\n process.stdout.write(usage);\n return 0;\n }\n\n if (command === \"patch\" || command === \"doctor\" || command === \"unpatch\") {\n process.stderr.write(`${command} was removed. Use resultar-check with TypeScript 7.\\n`);\n return 1;\n }\n\n return runResultarCheckCli(args);\n};\n\ntry {\n process.exitCode = run();\n} catch (error: unknown) {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n}\n"],"mappings":";;;AAGA,MAAM,QAAQ;;;;AAKd,MAAM,OAAO,OAA0B,QAAQ,KAAK,MAAM,CAAC,MAAc;CACvE,MAAM,CAAC,WAAW;CAElB,IAAI,YAAY,SAAS;EACvB,QAAQ,OAAO,MACb,mFACF;EACA,OAAO;CACT;CAEA,IAAI,YAAY,UAAU,YAAY,YAAY,YAAY,QAAQ,YAAY,KAAA,GAAW;EAC3F,QAAQ,OAAO,MAAM,KAAK;EAC1B,OAAO;CACT;CAEA,IAAI,YAAY,WAAW,YAAY,YAAY,YAAY,WAAW;EACxE,QAAQ,OAAO,MAAM,GAAG,QAAQ,sDAAsD;EACtF,OAAO;CACT;CAEA,OAAOA,aAAAA,oBAAoB,IAAI;AACjC;AAEA,IAAI;CACF,QAAQ,WAAW,IAAI;AACzB,SAAS,OAAgB;CACvB,QAAQ,OAAO,MAAM,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GAAG;CAClF,QAAQ,WAAW;AACrB"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as runResultarCheckCli } from "./lint-FMWf8UEv.js";
|
|
3
|
+
//#region src/cli.ts
|
|
4
|
+
const usage = `Usage: resultar-check -p tsconfig.json --noEmit
|
|
5
|
+
|
|
6
|
+
Runs TypeScript 7 first, then all enabled Resultar diagnostics from tsconfig plugin options.
|
|
7
|
+
`;
|
|
8
|
+
const run = (args = process.argv.slice(2)) => {
|
|
9
|
+
const [command] = args;
|
|
10
|
+
if (command === "check") {
|
|
11
|
+
process.stderr.write("The check subcommand was removed. Use resultar-check -p tsconfig.json --noEmit.\n");
|
|
12
|
+
return 1;
|
|
13
|
+
}
|
|
14
|
+
if (command === "help" || command === "--help" || command === "-h" || command === void 0) {
|
|
15
|
+
process.stdout.write(usage);
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
if (command === "patch" || command === "doctor" || command === "unpatch") {
|
|
19
|
+
process.stderr.write(`${command} was removed. Use resultar-check with TypeScript 7.\n`);
|
|
20
|
+
return 1;
|
|
21
|
+
}
|
|
22
|
+
return runResultarCheckCli(args);
|
|
23
|
+
};
|
|
24
|
+
try {
|
|
25
|
+
process.exitCode = run();
|
|
26
|
+
} catch (error) {
|
|
27
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
28
|
+
process.exitCode = 1;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export {};
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { runResultarCheckCli } from \"./lint.js\";\n\nconst usage = `Usage: resultar-check -p tsconfig.json --noEmit\n\nRuns TypeScript 7 first, then all enabled Resultar diagnostics from tsconfig plugin options.\n`;\n\nconst run = (args: readonly string[] = process.argv.slice(2)): number => {\n const [command] = args;\n\n if (command === \"check\") {\n process.stderr.write(\n \"The check subcommand was removed. Use resultar-check -p tsconfig.json --noEmit.\\n\",\n );\n return 1;\n }\n\n if (command === \"help\" || command === \"--help\" || command === \"-h\" || command === undefined) {\n process.stdout.write(usage);\n return 0;\n }\n\n if (command === \"patch\" || command === \"doctor\" || command === \"unpatch\") {\n process.stderr.write(`${command} was removed. Use resultar-check with TypeScript 7.\\n`);\n return 1;\n }\n\n return runResultarCheckCli(args);\n};\n\ntry {\n process.exitCode = run();\n} catch (error: unknown) {\n process.stderr.write(`${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 1;\n}\n"],"mappings":";;;AAGA,MAAM,QAAQ;;;;AAKd,MAAM,OAAO,OAA0B,QAAQ,KAAK,MAAM,CAAC,MAAc;CACvE,MAAM,CAAC,WAAW;CAElB,IAAI,YAAY,SAAS;EACvB,QAAQ,OAAO,MACb,mFACF;EACA,OAAO;CACT;CAEA,IAAI,YAAY,UAAU,YAAY,YAAY,YAAY,QAAQ,YAAY,KAAA,GAAW;EAC3F,QAAQ,OAAO,MAAM,KAAK;EAC1B,OAAO;CACT;CAEA,IAAI,YAAY,WAAW,YAAY,YAAY,YAAY,WAAW;EACxE,QAAQ,OAAO,MAAM,GAAG,QAAQ,sDAAsD;EACtF,OAAO;CACT;CAEA,OAAO,oBAAoB,IAAI;AACjC;AAEA,IAAI;CACF,QAAQ,WAAW,IAAI;AACzB,SAAS,OAAgB;CACvB,QAAQ,OAAO,MAAM,GAAG,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,EAAE,GAAG;CAClF,QAAQ,WAAW;AACrB"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const require_lint = require("./lint-D-D2dmLi.cjs");
|
|
2
|
+
//#region src/diagnostics.ts
|
|
3
|
+
const RESULTAR_DIAGNOSTIC_SOURCE = "resultar";
|
|
4
|
+
const RESULTAR_RULE_DIAGNOSTIC_CODES = {
|
|
5
|
+
"no-discard": 91001,
|
|
6
|
+
"no-tagged-error-constructor-override": 91010,
|
|
7
|
+
"no-try-catch-in-safe-try": 91006,
|
|
8
|
+
"no-unsafe-await": 91012,
|
|
9
|
+
"no-useless-recovery": 91011,
|
|
10
|
+
"prefer-and-then": 91003,
|
|
11
|
+
"prefer-map-err": 91002,
|
|
12
|
+
"prefer-tagged-error": 91008,
|
|
13
|
+
"tagged-error-name-match": 91009,
|
|
14
|
+
"typed-catch-mapper": 91004,
|
|
15
|
+
"unsafe-result-type-assertion": 91007,
|
|
16
|
+
"yield-star-in-safe-try": 91005
|
|
17
|
+
};
|
|
18
|
+
const isExternalSourceFile = (sourceFile) => sourceFile.isDeclarationFile || sourceFile.fileName.includes("/node_modules/") || sourceFile.fileName.includes("\\node_modules\\");
|
|
19
|
+
const getDiagnosticCategory = (tsApi, severity) => {
|
|
20
|
+
if (severity === "error") return tsApi.DiagnosticCategory.Error;
|
|
21
|
+
if (severity === "message") return tsApi.DiagnosticCategory.Message;
|
|
22
|
+
if (severity === "suggestion") return tsApi.DiagnosticCategory.Suggestion;
|
|
23
|
+
return tsApi.DiagnosticCategory.Warning;
|
|
24
|
+
};
|
|
25
|
+
const createResultarDiagnostic = (context, finding) => {
|
|
26
|
+
const diagnosticRuleName = finding.rule === "no-discard" ? "noDiscard" : finding.rule;
|
|
27
|
+
return {
|
|
28
|
+
category: getDiagnosticCategory(context.tsApi, finding.severity),
|
|
29
|
+
code: RESULTAR_RULE_DIAGNOSTIC_CODES[finding.rule],
|
|
30
|
+
file: context.sourceFile,
|
|
31
|
+
length: finding.length,
|
|
32
|
+
messageText: `[resultar/${diagnosticRuleName}] ${finding.message}`,
|
|
33
|
+
source: RESULTAR_DIAGNOSTIC_SOURCE,
|
|
34
|
+
start: finding.start
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const getResultarDiagnostics = (input) => {
|
|
38
|
+
const options = require_lint.normalizeResultarRulesOptions(input.options);
|
|
39
|
+
if (isExternalSourceFile(input.sourceFile)) return [];
|
|
40
|
+
const context = {
|
|
41
|
+
sourceFile: input.sourceFile,
|
|
42
|
+
tsApi: input.tsApi
|
|
43
|
+
};
|
|
44
|
+
return require_lint.getSourceFileResultarFindings(input.tsApi, input.program.getTypeChecker(), input.sourceFile, options).map((finding) => createResultarDiagnostic(context, finding));
|
|
45
|
+
};
|
|
46
|
+
const getProgramResultarDiagnostics = (tsApi, program, options = {}) => program.getSourceFiles().flatMap((sourceFile) => getResultarDiagnostics({
|
|
47
|
+
options,
|
|
48
|
+
program,
|
|
49
|
+
sourceFile,
|
|
50
|
+
tsApi
|
|
51
|
+
}));
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/plugin.ts
|
|
54
|
+
const pluginMarker = "__resultarLanguageServicePlugin";
|
|
55
|
+
const createLanguageServicePlugin = (modules) => {
|
|
56
|
+
const tsApi = modules.typescript;
|
|
57
|
+
const create = (info) => {
|
|
58
|
+
if (info.languageService[pluginMarker] === true) return info.languageService;
|
|
59
|
+
const options = require_lint.parsePluginOptions(info.config);
|
|
60
|
+
const proxy = Object.create(null);
|
|
61
|
+
const proxyRecord = proxy;
|
|
62
|
+
const serviceRecord = info.languageService;
|
|
63
|
+
proxy[pluginMarker] = true;
|
|
64
|
+
for (const key of Object.keys(serviceRecord)) {
|
|
65
|
+
const property = serviceRecord[key];
|
|
66
|
+
if (typeof property === "function") proxyRecord[key] = (...args) => Reflect.apply(property, info.languageService, args);
|
|
67
|
+
}
|
|
68
|
+
proxy.getSemanticDiagnostics = (fileName, ...args) => {
|
|
69
|
+
const diagnostics = info.languageService.getSemanticDiagnostics(fileName, ...args);
|
|
70
|
+
const program = info.languageService.getProgram();
|
|
71
|
+
const sourceFile = program?.getSourceFile(fileName);
|
|
72
|
+
if (program === void 0 || sourceFile === void 0) return diagnostics;
|
|
73
|
+
return [...diagnostics, ...getResultarDiagnostics({
|
|
74
|
+
options,
|
|
75
|
+
program,
|
|
76
|
+
sourceFile,
|
|
77
|
+
tsApi
|
|
78
|
+
})];
|
|
79
|
+
};
|
|
80
|
+
return proxy;
|
|
81
|
+
};
|
|
82
|
+
return { create };
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/index.ts
|
|
86
|
+
const plugin = Object.assign(createLanguageServicePlugin, {
|
|
87
|
+
createLanguageServicePlugin,
|
|
88
|
+
findResultarLintFindings: require_lint.findResultarLintFindings,
|
|
89
|
+
getProgramResultarDiagnostics,
|
|
90
|
+
runResultarCheckCli: require_lint.runResultarCheckCli
|
|
91
|
+
});
|
|
92
|
+
//#endregion
|
|
93
|
+
module.exports = plugin;
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["normalizeResultarRulesOptions","getSourceFileResultarFindings","parsePluginOptions"],"sources":["../src/diagnostics.ts","../src/plugin.ts","../src/index.ts"],"sourcesContent":["import type * as ts from \"typescript\";\n\nimport type { ResultarLintFinding, ResultarRuleName, ResultarRuleSeverity } from \"./finding.js\";\nimport {\n type ResultarRulesOptions,\n getSourceFileResultarFindings,\n normalizeResultarRulesOptions,\n onlyResultarRule,\n} from \"./rules-core.js\";\n\nexport const RESULTAR_DIAGNOSTIC_SOURCE = \"resultar\";\nexport const RESULTAR_NO_DISCARD_DIAGNOSTIC_CODE = 91_001;\nexport const RESULTAR_RULE_DIAGNOSTIC_CODES: Record<ResultarRuleName, number> = {\n \"no-discard\": RESULTAR_NO_DISCARD_DIAGNOSTIC_CODE,\n \"no-tagged-error-constructor-override\": 91_010,\n \"no-try-catch-in-safe-try\": 91_006,\n \"no-unsafe-await\": 91_012,\n \"no-useless-recovery\": 91_011,\n \"prefer-and-then\": 91_003,\n \"prefer-map-err\": 91_002,\n \"prefer-tagged-error\": 91_008,\n \"tagged-error-name-match\": 91_009,\n \"typed-catch-mapper\": 91_004,\n \"unsafe-result-type-assertion\": 91_007,\n \"yield-star-in-safe-try\": 91_005,\n};\n\ntype TypeScriptApi = typeof ts;\n\ntype ResultarLanguageServiceOptions = Partial<ResultarRulesOptions>;\n\ninterface DiagnosticContext {\n readonly sourceFile: ts.SourceFile;\n readonly tsApi: TypeScriptApi;\n}\n\nexport interface ResultarDiagnosticInput {\n readonly options?: ResultarLanguageServiceOptions;\n readonly program: ts.Program;\n readonly sourceFile: ts.SourceFile;\n readonly tsApi: TypeScriptApi;\n}\n\nconst isExternalSourceFile = (sourceFile: ts.SourceFile): boolean =>\n sourceFile.isDeclarationFile ||\n sourceFile.fileName.includes(\"/node_modules/\") ||\n sourceFile.fileName.includes(\"\\\\node_modules\\\\\");\n\nconst getDiagnosticCategory = (\n tsApi: TypeScriptApi,\n severity: Exclude<ResultarRuleSeverity, \"off\">,\n): ts.DiagnosticCategory => {\n if (severity === \"error\") {\n return tsApi.DiagnosticCategory.Error;\n }\n\n if (severity === \"message\") {\n return tsApi.DiagnosticCategory.Message;\n }\n\n if (severity === \"suggestion\") {\n return tsApi.DiagnosticCategory.Suggestion;\n }\n\n return tsApi.DiagnosticCategory.Warning;\n};\n\nconst createResultarDiagnostic = (\n context: DiagnosticContext,\n finding: ResultarLintFinding,\n): ts.Diagnostic => {\n const diagnosticRuleName = finding.rule === \"no-discard\" ? \"noDiscard\" : finding.rule;\n\n return {\n category: getDiagnosticCategory(context.tsApi, finding.severity),\n code: RESULTAR_RULE_DIAGNOSTIC_CODES[finding.rule],\n file: context.sourceFile,\n length: finding.length,\n messageText: `[resultar/${diagnosticRuleName}] ${finding.message}`,\n source: RESULTAR_DIAGNOSTIC_SOURCE,\n start: finding.start,\n };\n};\n\nexport const getResultarDiagnostics = (\n input: ResultarDiagnosticInput,\n): readonly ts.Diagnostic[] => {\n const options = normalizeResultarRulesOptions(input.options);\n\n if (isExternalSourceFile(input.sourceFile)) {\n return [];\n }\n\n const context: DiagnosticContext = { sourceFile: input.sourceFile, tsApi: input.tsApi };\n const diagnostics = getSourceFileResultarFindings(\n input.tsApi,\n input.program.getTypeChecker(),\n input.sourceFile,\n options,\n ).map((finding) => createResultarDiagnostic(context, finding));\n\n return diagnostics;\n};\n\nexport const getProgramResultarDiagnostics = (\n tsApi: TypeScriptApi,\n program: ts.Program,\n options: ResultarLanguageServiceOptions = {},\n): readonly ts.Diagnostic[] =>\n program\n .getSourceFiles()\n .flatMap((sourceFile) => getResultarDiagnostics({ options, program, sourceFile, tsApi }));\n\nexport const getNoDiscardDiagnostics = (input: ResultarDiagnosticInput): readonly ts.Diagnostic[] =>\n getResultarDiagnostics({\n ...input,\n options: {\n ...onlyResultarRule(\"no-discard\", input.options?.noDiscard ?? \"error\"),\n noDiscardMode: input.options?.noDiscardMode,\n },\n });\n\nexport const getProgramNoDiscardDiagnostics = (\n tsApi: TypeScriptApi,\n program: ts.Program,\n options: ResultarLanguageServiceOptions = {},\n): readonly ts.Diagnostic[] =>\n getProgramResultarDiagnostics(tsApi, program, {\n ...onlyResultarRule(\"no-discard\", options.noDiscard ?? \"error\"),\n noDiscardMode: options.noDiscardMode,\n });\n","import type * as ts from \"typescript\";\n\nimport { getResultarDiagnostics } from \"./diagnostics.js\";\nimport { parsePluginOptions } from \"./plugin-options.js\";\n\nconst pluginMarker = \"__resultarLanguageServicePlugin\";\n\nexport const createLanguageServicePlugin = (modules: {\n readonly typescript: typeof ts;\n}): ts.server.PluginModule => {\n const tsApi = modules.typescript;\n\n const create = (info: ts.server.PluginCreateInfo): ts.LanguageService => {\n if ((info.languageService as unknown as Record<string, unknown>)[pluginMarker] === true) {\n return info.languageService;\n }\n\n const options = parsePluginOptions(info.config);\n const proxy = Object.create(null) as ts.LanguageService & Record<string, unknown>;\n const proxyRecord = proxy as unknown as Record<string, unknown>;\n const serviceRecord = info.languageService as unknown as Record<string, unknown>;\n proxy[pluginMarker] = true;\n\n for (const key of Object.keys(serviceRecord)) {\n const property = serviceRecord[key];\n\n if (typeof property === \"function\") {\n proxyRecord[key] = (...args: readonly unknown[]) =>\n Reflect.apply(property, info.languageService, args) as unknown;\n }\n }\n\n proxy.getSemanticDiagnostics = (fileName, ...args) => {\n const diagnostics = info.languageService.getSemanticDiagnostics(fileName, ...args);\n\n const program = info.languageService.getProgram();\n const sourceFile = program?.getSourceFile(fileName);\n\n if (program === undefined || sourceFile === undefined) {\n return diagnostics;\n }\n\n return [...diagnostics, ...getResultarDiagnostics({ options, program, sourceFile, tsApi })];\n };\n\n return proxy;\n };\n\n return { create };\n};\n","import { getProgramResultarDiagnostics } from \"./diagnostics.js\";\nimport { findResultarLintFindings, runResultarCheckCli } from \"./lint.js\";\nimport { createLanguageServicePlugin } from \"./plugin.js\";\n\ntype ResultarLanguageServicePlugin = typeof createLanguageServicePlugin & {\n readonly createLanguageServicePlugin: typeof createLanguageServicePlugin;\n readonly findResultarLintFindings: typeof findResultarLintFindings;\n readonly getProgramResultarDiagnostics: typeof getProgramResultarDiagnostics;\n readonly runResultarCheckCli: typeof runResultarCheckCli;\n};\n\nconst plugin: ResultarLanguageServicePlugin = Object.assign(createLanguageServicePlugin, {\n createLanguageServicePlugin,\n findResultarLintFindings,\n getProgramResultarDiagnostics,\n runResultarCheckCli,\n});\n\nexport default plugin;\n"],"mappings":";;AAUA,MAAa,6BAA6B;AAE1C,MAAa,iCAAmE;CAC9E,cAAc;CACd,wCAAwC;CACxC,4BAA4B;CAC5B,mBAAmB;CACnB,uBAAuB;CACvB,mBAAmB;CACnB,kBAAkB;CAClB,uBAAuB;CACvB,2BAA2B;CAC3B,sBAAsB;CACtB,gCAAgC;CAChC,0BAA0B;AAC5B;AAkBA,MAAM,wBAAwB,eAC5B,WAAW,qBACX,WAAW,SAAS,SAAS,gBAAgB,KAC7C,WAAW,SAAS,SAAS,kBAAkB;AAEjD,MAAM,yBACJ,OACA,aAC0B;CAC1B,IAAI,aAAa,SACf,OAAO,MAAM,mBAAmB;CAGlC,IAAI,aAAa,WACf,OAAO,MAAM,mBAAmB;CAGlC,IAAI,aAAa,cACf,OAAO,MAAM,mBAAmB;CAGlC,OAAO,MAAM,mBAAmB;AAClC;AAEA,MAAM,4BACJ,SACA,YACkB;CAClB,MAAM,qBAAqB,QAAQ,SAAS,eAAe,cAAc,QAAQ;CAEjF,OAAO;EACL,UAAU,sBAAsB,QAAQ,OAAO,QAAQ,QAAQ;EAC/D,MAAM,+BAA+B,QAAQ;EAC7C,MAAM,QAAQ;EACd,QAAQ,QAAQ;EAChB,aAAa,aAAa,mBAAmB,IAAI,QAAQ;EACzD,QAAQ;EACR,OAAO,QAAQ;CACjB;AACF;AAEA,MAAa,0BACX,UAC6B;CAC7B,MAAM,UAAUA,aAAAA,8BAA8B,MAAM,OAAO;CAE3D,IAAI,qBAAqB,MAAM,UAAU,GACvC,OAAO,CAAC;CAGV,MAAM,UAA6B;EAAE,YAAY,MAAM;EAAY,OAAO,MAAM;CAAM;CAQtF,OAPoBC,aAAAA,8BAClB,MAAM,OACN,MAAM,QAAQ,eAAe,GAC7B,MAAM,YACN,OACF,EAAE,KAAK,YAAY,yBAAyB,SAAS,OAAO,CAE3C;AACnB;AAEA,MAAa,iCACX,OACA,SACA,UAA0C,CAAC,MAE3C,QACG,eAAe,EACf,SAAS,eAAe,uBAAuB;CAAE;CAAS;CAAS;CAAY;AAAM,CAAC,CAAC;;;AC1G5F,MAAM,eAAe;AAErB,MAAa,+BAA+B,YAEd;CAC5B,MAAM,QAAQ,QAAQ;CAEtB,MAAM,UAAU,SAAyD;EACvE,IAAK,KAAK,gBAAuD,kBAAkB,MACjF,OAAO,KAAK;EAGd,MAAM,UAAUC,aAAAA,mBAAmB,KAAK,MAAM;EAC9C,MAAM,QAAQ,OAAO,OAAO,IAAI;EAChC,MAAM,cAAc;EACpB,MAAM,gBAAgB,KAAK;EAC3B,MAAM,gBAAgB;EAEtB,KAAK,MAAM,OAAO,OAAO,KAAK,aAAa,GAAG;GAC5C,MAAM,WAAW,cAAc;GAE/B,IAAI,OAAO,aAAa,YACtB,YAAY,QAAQ,GAAG,SACrB,QAAQ,MAAM,UAAU,KAAK,iBAAiB,IAAI;EAExD;EAEA,MAAM,0BAA0B,UAAU,GAAG,SAAS;GACpD,MAAM,cAAc,KAAK,gBAAgB,uBAAuB,UAAU,GAAG,IAAI;GAEjF,MAAM,UAAU,KAAK,gBAAgB,WAAW;GAChD,MAAM,aAAa,SAAS,cAAc,QAAQ;GAElD,IAAI,YAAY,KAAA,KAAa,eAAe,KAAA,GAC1C,OAAO;GAGT,OAAO,CAAC,GAAG,aAAa,GAAG,uBAAuB;IAAE;IAAS;IAAS;IAAY;GAAM,CAAC,CAAC;EAC5F;EAEA,OAAO;CACT;CAEA,OAAO,EAAE,OAAO;AAClB;;;ACtCA,MAAM,SAAwC,OAAO,OAAO,6BAA6B;CACvF;CACA,0BAAA,aAAA;CACA;CACA,qBAAA,aAAA;AACF,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
|
|
3
|
+
//#region src/finding.d.ts
|
|
4
|
+
type ResultarRuleName = "no-discard" | "no-tagged-error-constructor-override" | "no-try-catch-in-safe-try" | "no-unsafe-await" | "no-useless-recovery" | "prefer-and-then" | "prefer-map-err" | "prefer-tagged-error" | "tagged-error-name-match" | "typed-catch-mapper" | "unsafe-result-type-assertion" | "yield-star-in-safe-try";
|
|
5
|
+
type ResultarRuleSeverity = "error" | "message" | "off" | "suggestion" | "warning";
|
|
6
|
+
interface ResultarLintFinding {
|
|
7
|
+
readonly column: number;
|
|
8
|
+
readonly file: string;
|
|
9
|
+
readonly length: number;
|
|
10
|
+
readonly line: number;
|
|
11
|
+
readonly message: string;
|
|
12
|
+
readonly rule: ResultarRuleName;
|
|
13
|
+
readonly severity: Exclude<ResultarRuleSeverity, "off">;
|
|
14
|
+
readonly start: number;
|
|
15
|
+
readonly type?: string;
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/result-usage-core.d.ts
|
|
19
|
+
type NoDiscardMode = "direct" | "must-use";
|
|
20
|
+
//#endregion
|
|
21
|
+
//#region src/rules-core.d.ts
|
|
22
|
+
type NoUnsafeAwaitMode = "all" | "resultar-context";
|
|
23
|
+
interface ResultarRulesOptions {
|
|
24
|
+
readonly noDiscard: ResultarRuleSeverity;
|
|
25
|
+
readonly noDiscardMode: NoDiscardMode;
|
|
26
|
+
readonly noTaggedErrorConstructorOverride: ResultarRuleSeverity;
|
|
27
|
+
readonly noTryCatchInSafeTry: ResultarRuleSeverity;
|
|
28
|
+
readonly noUnsafeAwait: ResultarRuleSeverity;
|
|
29
|
+
readonly noUnsafeAwaitMode: NoUnsafeAwaitMode;
|
|
30
|
+
readonly noUselessRecovery: ResultarRuleSeverity;
|
|
31
|
+
readonly preferAndThen: ResultarRuleSeverity;
|
|
32
|
+
readonly preferMapErr: ResultarRuleSeverity;
|
|
33
|
+
readonly preferTaggedError: ResultarRuleSeverity;
|
|
34
|
+
readonly taggedErrorNameMatch: ResultarRuleSeverity;
|
|
35
|
+
readonly typedCatchMapper: ResultarRuleSeverity;
|
|
36
|
+
readonly unsafeResultTypeAssertion: ResultarRuleSeverity;
|
|
37
|
+
readonly yieldStarInSafeTry: ResultarRuleSeverity;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/diagnostics.d.ts
|
|
41
|
+
type TypeScriptApi = typeof ts;
|
|
42
|
+
type ResultarLanguageServiceOptions = Partial<ResultarRulesOptions>;
|
|
43
|
+
declare const getProgramResultarDiagnostics: (tsApi: TypeScriptApi, program: ts.Program, options?: ResultarLanguageServiceOptions) => readonly ts.Diagnostic[];
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/lint.d.ts
|
|
46
|
+
interface NoDiscardOptions {
|
|
47
|
+
readonly mode?: NoDiscardMode;
|
|
48
|
+
readonly project?: string;
|
|
49
|
+
readonly rootDir?: string;
|
|
50
|
+
}
|
|
51
|
+
type NoDiscardFailure = {
|
|
52
|
+
readonly error: Error;
|
|
53
|
+
readonly ok: false;
|
|
54
|
+
};
|
|
55
|
+
interface ResultarLintOptions extends NoDiscardOptions {
|
|
56
|
+
readonly rules?: Partial<ResultarRulesOptions>;
|
|
57
|
+
}
|
|
58
|
+
type ResultarLintResult = NoDiscardFailure | {
|
|
59
|
+
readonly findings: readonly ResultarLintFinding[];
|
|
60
|
+
readonly ok: true;
|
|
61
|
+
};
|
|
62
|
+
declare const findResultarLintFindings: (options?: ResultarLintOptions) => ResultarLintResult;
|
|
63
|
+
declare const runResultarCheckCli: (args?: readonly string[]) => number;
|
|
64
|
+
//#endregion
|
|
65
|
+
//#region src/plugin.d.ts
|
|
66
|
+
declare const createLanguageServicePlugin: (modules: {
|
|
67
|
+
readonly typescript: typeof ts;
|
|
68
|
+
}) => ts.server.PluginModule;
|
|
69
|
+
//#endregion
|
|
70
|
+
//#region src/index.d.ts
|
|
71
|
+
type ResultarLanguageServicePlugin = typeof createLanguageServicePlugin & {
|
|
72
|
+
readonly createLanguageServicePlugin: typeof createLanguageServicePlugin;
|
|
73
|
+
readonly findResultarLintFindings: typeof findResultarLintFindings;
|
|
74
|
+
readonly getProgramResultarDiagnostics: typeof getProgramResultarDiagnostics;
|
|
75
|
+
readonly runResultarCheckCli: typeof runResultarCheckCli;
|
|
76
|
+
};
|
|
77
|
+
declare const plugin: ResultarLanguageServicePlugin;
|
|
78
|
+
//#endregion
|
|
79
|
+
export { plugin as default };
|
|
80
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { a as normalizeResultarRulesOptions, i as getSourceFileResultarFindings, n as runResultarCheckCli, r as parsePluginOptions, t as findResultarLintFindings } from "./lint-FMWf8UEv.js";
|
|
2
|
+
//#region src/diagnostics.ts
|
|
3
|
+
const RESULTAR_DIAGNOSTIC_SOURCE = "resultar";
|
|
4
|
+
const RESULTAR_RULE_DIAGNOSTIC_CODES = {
|
|
5
|
+
"no-discard": 91001,
|
|
6
|
+
"no-tagged-error-constructor-override": 91010,
|
|
7
|
+
"no-try-catch-in-safe-try": 91006,
|
|
8
|
+
"no-unsafe-await": 91012,
|
|
9
|
+
"no-useless-recovery": 91011,
|
|
10
|
+
"prefer-and-then": 91003,
|
|
11
|
+
"prefer-map-err": 91002,
|
|
12
|
+
"prefer-tagged-error": 91008,
|
|
13
|
+
"tagged-error-name-match": 91009,
|
|
14
|
+
"typed-catch-mapper": 91004,
|
|
15
|
+
"unsafe-result-type-assertion": 91007,
|
|
16
|
+
"yield-star-in-safe-try": 91005
|
|
17
|
+
};
|
|
18
|
+
const isExternalSourceFile = (sourceFile) => sourceFile.isDeclarationFile || sourceFile.fileName.includes("/node_modules/") || sourceFile.fileName.includes("\\node_modules\\");
|
|
19
|
+
const getDiagnosticCategory = (tsApi, severity) => {
|
|
20
|
+
if (severity === "error") return tsApi.DiagnosticCategory.Error;
|
|
21
|
+
if (severity === "message") return tsApi.DiagnosticCategory.Message;
|
|
22
|
+
if (severity === "suggestion") return tsApi.DiagnosticCategory.Suggestion;
|
|
23
|
+
return tsApi.DiagnosticCategory.Warning;
|
|
24
|
+
};
|
|
25
|
+
const createResultarDiagnostic = (context, finding) => {
|
|
26
|
+
const diagnosticRuleName = finding.rule === "no-discard" ? "noDiscard" : finding.rule;
|
|
27
|
+
return {
|
|
28
|
+
category: getDiagnosticCategory(context.tsApi, finding.severity),
|
|
29
|
+
code: RESULTAR_RULE_DIAGNOSTIC_CODES[finding.rule],
|
|
30
|
+
file: context.sourceFile,
|
|
31
|
+
length: finding.length,
|
|
32
|
+
messageText: `[resultar/${diagnosticRuleName}] ${finding.message}`,
|
|
33
|
+
source: RESULTAR_DIAGNOSTIC_SOURCE,
|
|
34
|
+
start: finding.start
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const getResultarDiagnostics = (input) => {
|
|
38
|
+
const options = normalizeResultarRulesOptions(input.options);
|
|
39
|
+
if (isExternalSourceFile(input.sourceFile)) return [];
|
|
40
|
+
const context = {
|
|
41
|
+
sourceFile: input.sourceFile,
|
|
42
|
+
tsApi: input.tsApi
|
|
43
|
+
};
|
|
44
|
+
return getSourceFileResultarFindings(input.tsApi, input.program.getTypeChecker(), input.sourceFile, options).map((finding) => createResultarDiagnostic(context, finding));
|
|
45
|
+
};
|
|
46
|
+
const getProgramResultarDiagnostics = (tsApi, program, options = {}) => program.getSourceFiles().flatMap((sourceFile) => getResultarDiagnostics({
|
|
47
|
+
options,
|
|
48
|
+
program,
|
|
49
|
+
sourceFile,
|
|
50
|
+
tsApi
|
|
51
|
+
}));
|
|
52
|
+
//#endregion
|
|
53
|
+
//#region src/plugin.ts
|
|
54
|
+
const pluginMarker = "__resultarLanguageServicePlugin";
|
|
55
|
+
const createLanguageServicePlugin = (modules) => {
|
|
56
|
+
const tsApi = modules.typescript;
|
|
57
|
+
const create = (info) => {
|
|
58
|
+
if (info.languageService[pluginMarker] === true) return info.languageService;
|
|
59
|
+
const options = parsePluginOptions(info.config);
|
|
60
|
+
const proxy = Object.create(null);
|
|
61
|
+
const proxyRecord = proxy;
|
|
62
|
+
const serviceRecord = info.languageService;
|
|
63
|
+
proxy[pluginMarker] = true;
|
|
64
|
+
for (const key of Object.keys(serviceRecord)) {
|
|
65
|
+
const property = serviceRecord[key];
|
|
66
|
+
if (typeof property === "function") proxyRecord[key] = (...args) => Reflect.apply(property, info.languageService, args);
|
|
67
|
+
}
|
|
68
|
+
proxy.getSemanticDiagnostics = (fileName, ...args) => {
|
|
69
|
+
const diagnostics = info.languageService.getSemanticDiagnostics(fileName, ...args);
|
|
70
|
+
const program = info.languageService.getProgram();
|
|
71
|
+
const sourceFile = program?.getSourceFile(fileName);
|
|
72
|
+
if (program === void 0 || sourceFile === void 0) return diagnostics;
|
|
73
|
+
return [...diagnostics, ...getResultarDiagnostics({
|
|
74
|
+
options,
|
|
75
|
+
program,
|
|
76
|
+
sourceFile,
|
|
77
|
+
tsApi
|
|
78
|
+
})];
|
|
79
|
+
};
|
|
80
|
+
return proxy;
|
|
81
|
+
};
|
|
82
|
+
return { create };
|
|
83
|
+
};
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/index.ts
|
|
86
|
+
const plugin = Object.assign(createLanguageServicePlugin, {
|
|
87
|
+
createLanguageServicePlugin,
|
|
88
|
+
findResultarLintFindings,
|
|
89
|
+
getProgramResultarDiagnostics,
|
|
90
|
+
runResultarCheckCli
|
|
91
|
+
});
|
|
92
|
+
//#endregion
|
|
93
|
+
export { plugin as default };
|
|
94
|
+
|
|
95
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/diagnostics.ts","../src/plugin.ts","../src/index.ts"],"sourcesContent":["import type * as ts from \"typescript\";\n\nimport type { ResultarLintFinding, ResultarRuleName, ResultarRuleSeverity } from \"./finding.js\";\nimport {\n type ResultarRulesOptions,\n getSourceFileResultarFindings,\n normalizeResultarRulesOptions,\n onlyResultarRule,\n} from \"./rules-core.js\";\n\nexport const RESULTAR_DIAGNOSTIC_SOURCE = \"resultar\";\nexport const RESULTAR_NO_DISCARD_DIAGNOSTIC_CODE = 91_001;\nexport const RESULTAR_RULE_DIAGNOSTIC_CODES: Record<ResultarRuleName, number> = {\n \"no-discard\": RESULTAR_NO_DISCARD_DIAGNOSTIC_CODE,\n \"no-tagged-error-constructor-override\": 91_010,\n \"no-try-catch-in-safe-try\": 91_006,\n \"no-unsafe-await\": 91_012,\n \"no-useless-recovery\": 91_011,\n \"prefer-and-then\": 91_003,\n \"prefer-map-err\": 91_002,\n \"prefer-tagged-error\": 91_008,\n \"tagged-error-name-match\": 91_009,\n \"typed-catch-mapper\": 91_004,\n \"unsafe-result-type-assertion\": 91_007,\n \"yield-star-in-safe-try\": 91_005,\n};\n\ntype TypeScriptApi = typeof ts;\n\ntype ResultarLanguageServiceOptions = Partial<ResultarRulesOptions>;\n\ninterface DiagnosticContext {\n readonly sourceFile: ts.SourceFile;\n readonly tsApi: TypeScriptApi;\n}\n\nexport interface ResultarDiagnosticInput {\n readonly options?: ResultarLanguageServiceOptions;\n readonly program: ts.Program;\n readonly sourceFile: ts.SourceFile;\n readonly tsApi: TypeScriptApi;\n}\n\nconst isExternalSourceFile = (sourceFile: ts.SourceFile): boolean =>\n sourceFile.isDeclarationFile ||\n sourceFile.fileName.includes(\"/node_modules/\") ||\n sourceFile.fileName.includes(\"\\\\node_modules\\\\\");\n\nconst getDiagnosticCategory = (\n tsApi: TypeScriptApi,\n severity: Exclude<ResultarRuleSeverity, \"off\">,\n): ts.DiagnosticCategory => {\n if (severity === \"error\") {\n return tsApi.DiagnosticCategory.Error;\n }\n\n if (severity === \"message\") {\n return tsApi.DiagnosticCategory.Message;\n }\n\n if (severity === \"suggestion\") {\n return tsApi.DiagnosticCategory.Suggestion;\n }\n\n return tsApi.DiagnosticCategory.Warning;\n};\n\nconst createResultarDiagnostic = (\n context: DiagnosticContext,\n finding: ResultarLintFinding,\n): ts.Diagnostic => {\n const diagnosticRuleName = finding.rule === \"no-discard\" ? \"noDiscard\" : finding.rule;\n\n return {\n category: getDiagnosticCategory(context.tsApi, finding.severity),\n code: RESULTAR_RULE_DIAGNOSTIC_CODES[finding.rule],\n file: context.sourceFile,\n length: finding.length,\n messageText: `[resultar/${diagnosticRuleName}] ${finding.message}`,\n source: RESULTAR_DIAGNOSTIC_SOURCE,\n start: finding.start,\n };\n};\n\nexport const getResultarDiagnostics = (\n input: ResultarDiagnosticInput,\n): readonly ts.Diagnostic[] => {\n const options = normalizeResultarRulesOptions(input.options);\n\n if (isExternalSourceFile(input.sourceFile)) {\n return [];\n }\n\n const context: DiagnosticContext = { sourceFile: input.sourceFile, tsApi: input.tsApi };\n const diagnostics = getSourceFileResultarFindings(\n input.tsApi,\n input.program.getTypeChecker(),\n input.sourceFile,\n options,\n ).map((finding) => createResultarDiagnostic(context, finding));\n\n return diagnostics;\n};\n\nexport const getProgramResultarDiagnostics = (\n tsApi: TypeScriptApi,\n program: ts.Program,\n options: ResultarLanguageServiceOptions = {},\n): readonly ts.Diagnostic[] =>\n program\n .getSourceFiles()\n .flatMap((sourceFile) => getResultarDiagnostics({ options, program, sourceFile, tsApi }));\n\nexport const getNoDiscardDiagnostics = (input: ResultarDiagnosticInput): readonly ts.Diagnostic[] =>\n getResultarDiagnostics({\n ...input,\n options: {\n ...onlyResultarRule(\"no-discard\", input.options?.noDiscard ?? \"error\"),\n noDiscardMode: input.options?.noDiscardMode,\n },\n });\n\nexport const getProgramNoDiscardDiagnostics = (\n tsApi: TypeScriptApi,\n program: ts.Program,\n options: ResultarLanguageServiceOptions = {},\n): readonly ts.Diagnostic[] =>\n getProgramResultarDiagnostics(tsApi, program, {\n ...onlyResultarRule(\"no-discard\", options.noDiscard ?? \"error\"),\n noDiscardMode: options.noDiscardMode,\n });\n","import type * as ts from \"typescript\";\n\nimport { getResultarDiagnostics } from \"./diagnostics.js\";\nimport { parsePluginOptions } from \"./plugin-options.js\";\n\nconst pluginMarker = \"__resultarLanguageServicePlugin\";\n\nexport const createLanguageServicePlugin = (modules: {\n readonly typescript: typeof ts;\n}): ts.server.PluginModule => {\n const tsApi = modules.typescript;\n\n const create = (info: ts.server.PluginCreateInfo): ts.LanguageService => {\n if ((info.languageService as unknown as Record<string, unknown>)[pluginMarker] === true) {\n return info.languageService;\n }\n\n const options = parsePluginOptions(info.config);\n const proxy = Object.create(null) as ts.LanguageService & Record<string, unknown>;\n const proxyRecord = proxy as unknown as Record<string, unknown>;\n const serviceRecord = info.languageService as unknown as Record<string, unknown>;\n proxy[pluginMarker] = true;\n\n for (const key of Object.keys(serviceRecord)) {\n const property = serviceRecord[key];\n\n if (typeof property === \"function\") {\n proxyRecord[key] = (...args: readonly unknown[]) =>\n Reflect.apply(property, info.languageService, args) as unknown;\n }\n }\n\n proxy.getSemanticDiagnostics = (fileName, ...args) => {\n const diagnostics = info.languageService.getSemanticDiagnostics(fileName, ...args);\n\n const program = info.languageService.getProgram();\n const sourceFile = program?.getSourceFile(fileName);\n\n if (program === undefined || sourceFile === undefined) {\n return diagnostics;\n }\n\n return [...diagnostics, ...getResultarDiagnostics({ options, program, sourceFile, tsApi })];\n };\n\n return proxy;\n };\n\n return { create };\n};\n","import { getProgramResultarDiagnostics } from \"./diagnostics.js\";\nimport { findResultarLintFindings, runResultarCheckCli } from \"./lint.js\";\nimport { createLanguageServicePlugin } from \"./plugin.js\";\n\ntype ResultarLanguageServicePlugin = typeof createLanguageServicePlugin & {\n readonly createLanguageServicePlugin: typeof createLanguageServicePlugin;\n readonly findResultarLintFindings: typeof findResultarLintFindings;\n readonly getProgramResultarDiagnostics: typeof getProgramResultarDiagnostics;\n readonly runResultarCheckCli: typeof runResultarCheckCli;\n};\n\nconst plugin: ResultarLanguageServicePlugin = Object.assign(createLanguageServicePlugin, {\n createLanguageServicePlugin,\n findResultarLintFindings,\n getProgramResultarDiagnostics,\n runResultarCheckCli,\n});\n\nexport default plugin;\n"],"mappings":";;AAUA,MAAa,6BAA6B;AAE1C,MAAa,iCAAmE;CAC9E,cAAc;CACd,wCAAwC;CACxC,4BAA4B;CAC5B,mBAAmB;CACnB,uBAAuB;CACvB,mBAAmB;CACnB,kBAAkB;CAClB,uBAAuB;CACvB,2BAA2B;CAC3B,sBAAsB;CACtB,gCAAgC;CAChC,0BAA0B;AAC5B;AAkBA,MAAM,wBAAwB,eAC5B,WAAW,qBACX,WAAW,SAAS,SAAS,gBAAgB,KAC7C,WAAW,SAAS,SAAS,kBAAkB;AAEjD,MAAM,yBACJ,OACA,aAC0B;CAC1B,IAAI,aAAa,SACf,OAAO,MAAM,mBAAmB;CAGlC,IAAI,aAAa,WACf,OAAO,MAAM,mBAAmB;CAGlC,IAAI,aAAa,cACf,OAAO,MAAM,mBAAmB;CAGlC,OAAO,MAAM,mBAAmB;AAClC;AAEA,MAAM,4BACJ,SACA,YACkB;CAClB,MAAM,qBAAqB,QAAQ,SAAS,eAAe,cAAc,QAAQ;CAEjF,OAAO;EACL,UAAU,sBAAsB,QAAQ,OAAO,QAAQ,QAAQ;EAC/D,MAAM,+BAA+B,QAAQ;EAC7C,MAAM,QAAQ;EACd,QAAQ,QAAQ;EAChB,aAAa,aAAa,mBAAmB,IAAI,QAAQ;EACzD,QAAQ;EACR,OAAO,QAAQ;CACjB;AACF;AAEA,MAAa,0BACX,UAC6B;CAC7B,MAAM,UAAU,8BAA8B,MAAM,OAAO;CAE3D,IAAI,qBAAqB,MAAM,UAAU,GACvC,OAAO,CAAC;CAGV,MAAM,UAA6B;EAAE,YAAY,MAAM;EAAY,OAAO,MAAM;CAAM;CAQtF,OAPoB,8BAClB,MAAM,OACN,MAAM,QAAQ,eAAe,GAC7B,MAAM,YACN,OACF,EAAE,KAAK,YAAY,yBAAyB,SAAS,OAAO,CAE3C;AACnB;AAEA,MAAa,iCACX,OACA,SACA,UAA0C,CAAC,MAE3C,QACG,eAAe,EACf,SAAS,eAAe,uBAAuB;CAAE;CAAS;CAAS;CAAY;AAAM,CAAC,CAAC;;;AC1G5F,MAAM,eAAe;AAErB,MAAa,+BAA+B,YAEd;CAC5B,MAAM,QAAQ,QAAQ;CAEtB,MAAM,UAAU,SAAyD;EACvE,IAAK,KAAK,gBAAuD,kBAAkB,MACjF,OAAO,KAAK;EAGd,MAAM,UAAU,mBAAmB,KAAK,MAAM;EAC9C,MAAM,QAAQ,OAAO,OAAO,IAAI;EAChC,MAAM,cAAc;EACpB,MAAM,gBAAgB,KAAK;EAC3B,MAAM,gBAAgB;EAEtB,KAAK,MAAM,OAAO,OAAO,KAAK,aAAa,GAAG;GAC5C,MAAM,WAAW,cAAc;GAE/B,IAAI,OAAO,aAAa,YACtB,YAAY,QAAQ,GAAG,SACrB,QAAQ,MAAM,UAAU,KAAK,iBAAiB,IAAI;EAExD;EAEA,MAAM,0BAA0B,UAAU,GAAG,SAAS;GACpD,MAAM,cAAc,KAAK,gBAAgB,uBAAuB,UAAU,GAAG,IAAI;GAEjF,MAAM,UAAU,KAAK,gBAAgB,WAAW;GAChD,MAAM,aAAa,SAAS,cAAc,QAAQ;GAElD,IAAI,YAAY,KAAA,KAAa,eAAe,KAAA,GAC1C,OAAO;GAGT,OAAO,CAAC,GAAG,aAAa,GAAG,uBAAuB;IAAE;IAAS;IAAS;IAAY;GAAM,CAAC,CAAC;EAC5F;EAEA,OAAO;CACT;CAEA,OAAO,EAAE,OAAO;AAClB;;;ACtCA,MAAM,SAAwC,OAAO,OAAO,6BAA6B;CACvF;CACA;CACA;CACA;AACF,CAAC"}
|