corsa-oxlint 0.3.2 → 0.7.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 +19 -0
- package/dist/checker.js +37 -2
- package/dist/checker.js.map +1 -1
- package/dist/oxlint_utils.d.ts +23 -3
- package/dist/oxlint_utils.js +2 -2
- package/dist/oxlint_utils.js.map +1 -1
- package/dist/rules/await_thenable.js +2 -18
- package/dist/rules/await_thenable.js.map +1 -1
- package/dist/rules/native_bridge.d.ts +25 -0
- package/dist/rules/native_bridge.js +120 -0
- package/dist/rules/native_bridge.js.map +1 -0
- package/dist/rules/no_array_delete.js +2 -24
- package/dist/rules/no_array_delete.js.map +1 -1
- package/dist/rules/no_base_to_string.js +1 -1
- package/dist/rules/no_floating_promises.js +1 -1
- package/dist/rules/no_for_in_array.js +2 -11
- package/dist/rules/no_for_in_array.js.map +1 -1
- package/dist/rules/no_implied_eval.js +2 -30
- package/dist/rules/no_implied_eval.js.map +1 -1
- package/dist/rules/no_mixed_enums.js +2 -36
- package/dist/rules/no_mixed_enums.js.map +1 -1
- package/dist/rules/no_unsafe_return.js +1 -1
- package/dist/rules/no_unsafe_unary_minus.js +2 -23
- package/dist/rules/no_unsafe_unary_minus.js.map +1 -1
- package/dist/rules/only_throw_error.js +2 -11
- package/dist/rules/only_throw_error.js.map +1 -1
- package/dist/rules/prefer_find.js +2 -20
- package/dist/rules/prefer_find.js.map +1 -1
- package/dist/rules/prefer_includes.js +2 -16
- package/dist/rules/prefer_includes.js.map +1 -1
- package/dist/rules/prefer_promise_reject_errors.js +1 -1
- package/dist/rules/prefer_regexp_exec.js +2 -15
- package/dist/rules/prefer_regexp_exec.js.map +1 -1
- package/dist/rules/prefer_string_starts_ends_with.js +1 -1
- package/dist/rules/require_array_sort_compare.js +1 -1
- package/dist/rules/use_unknown_in_catch_callback_variable.js +2 -14
- package/dist/rules/use_unknown_in_catch_callback_variable.js.map +1 -1
- package/dist/session.js +5 -27
- package/dist/session.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -140,6 +140,25 @@ The remaining upstream rules stay listed in `pendingNativeRuleNames`, and
|
|
|
140
140
|
`native_rules.test.ts` fails if implemented + pending drift away from the
|
|
141
141
|
tracked upstream rule list.
|
|
142
142
|
|
|
143
|
+
## Rust-Authored Rule Lane
|
|
144
|
+
|
|
145
|
+
General-purpose built-in rules can be implemented as Rust rules and still ship
|
|
146
|
+
as Oxlint JS plugin rules. The bridge is:
|
|
147
|
+
|
|
148
|
+
1. Oxlint visits the ESTree node in JS.
|
|
149
|
+
2. `corsa-oxlint` collects compact node facts and type texts.
|
|
150
|
+
3. `@corsa-bind/napi` calls `corsa::lint::RustLintRule`.
|
|
151
|
+
4. Rust returns Oxlint-shaped diagnostics, suggestions, and fixes.
|
|
152
|
+
5. The JS rule reports them through `context.report()`.
|
|
153
|
+
|
|
154
|
+
The first rules on this path are `await-thenable`, `no-array-delete`,
|
|
155
|
+
`no-for-in-array`, `no-implied-eval`, `no-mixed-enums`,
|
|
156
|
+
`no-unsafe-unary-minus`, `only-throw-error`, `prefer-find`,
|
|
157
|
+
`prefer-includes`, `prefer-regexp-exec`, and
|
|
158
|
+
`use-unknown-in-catch-callback-variable`. Custom project-specific rules can
|
|
159
|
+
still be authored in JS/TS with `OxlintUtils.RuleCreator()`, while hot,
|
|
160
|
+
shared, tsgolint-parity rules can move into Rust incrementally.
|
|
161
|
+
|
|
143
162
|
## Runtime Safety Controls
|
|
144
163
|
|
|
145
164
|
The underlying `@corsa-bind/napi` client now exposes a few production-oriented
|
package/dist/checker.js
CHANGED
|
@@ -27,13 +27,15 @@ function createProgram(context) {
|
|
|
27
27
|
function createTypeChecker(context) {
|
|
28
28
|
return {
|
|
29
29
|
getTypeAtLocation(node) {
|
|
30
|
-
|
|
30
|
+
const lookupNode = nodeForTypeLookup(node);
|
|
31
|
+
return sessionForContext(context).session.getTypeAtPosition(filenameFor(context, lookupNode), toPosition(lookupNode));
|
|
31
32
|
},
|
|
32
33
|
getContextualType(node) {
|
|
33
34
|
return this.getTypeAtLocation(node);
|
|
34
35
|
},
|
|
35
36
|
getSymbolAtLocation(node) {
|
|
36
|
-
|
|
37
|
+
const lookupNode = nodeForTypeLookup(node);
|
|
38
|
+
return sessionForContext(context).session.getSymbolAtPosition(filenameFor(context, lookupNode), toPosition(lookupNode));
|
|
37
39
|
},
|
|
38
40
|
getTypeOfSymbol(symbol) {
|
|
39
41
|
return sessionForContext(context).session.getTypeOfSymbol(symbol);
|
|
@@ -65,11 +67,44 @@ function createTypeChecker(context) {
|
|
|
65
67
|
getBaseTypes(type) {
|
|
66
68
|
return sessionForContext(context).session.getBaseTypes(type);
|
|
67
69
|
},
|
|
70
|
+
getImplementedTypes(node) {
|
|
71
|
+
return implementedClauseNodes(node).map((clause) => {
|
|
72
|
+
const expression = implementedClauseChildNode(clause, "expression") ?? clause;
|
|
73
|
+
const symbol = this.getSymbolAtLocation(expression) ?? this.getSymbolAtLocation(clause);
|
|
74
|
+
return symbol ? this.getDeclaredTypeOfSymbol(symbol) ?? this.getTypeOfSymbol(symbol) : this.getTypeAtLocation(expression) ?? this.getTypeAtLocation(clause);
|
|
75
|
+
}).filter((type) => type !== void 0);
|
|
76
|
+
},
|
|
68
77
|
getTypeArguments(type) {
|
|
69
78
|
return sessionForContext(context).session.getTypeArguments(type);
|
|
70
79
|
}
|
|
71
80
|
};
|
|
72
81
|
}
|
|
82
|
+
function nodeForTypeLookup(node) {
|
|
83
|
+
if ("pos" in node) return node;
|
|
84
|
+
switch (node.type) {
|
|
85
|
+
case "ClassDeclaration":
|
|
86
|
+
case "ClassExpression": return childNode(node, "id") ?? node;
|
|
87
|
+
case "TSPropertySignature": return childNode(node, "key") ?? node;
|
|
88
|
+
default: return node;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function childNode(node, key) {
|
|
92
|
+
const value = node[key];
|
|
93
|
+
if (isNode(value)) return value;
|
|
94
|
+
}
|
|
95
|
+
function implementedClauseNodes(node) {
|
|
96
|
+
if ("pos" in node) return [];
|
|
97
|
+
const clauses = node.implements;
|
|
98
|
+
if (!Array.isArray(clauses)) return [];
|
|
99
|
+
return clauses.filter(isNode);
|
|
100
|
+
}
|
|
101
|
+
function implementedClauseChildNode(node, key) {
|
|
102
|
+
const value = node[key];
|
|
103
|
+
if (isNode(value)) return value;
|
|
104
|
+
}
|
|
105
|
+
function isNode(value) {
|
|
106
|
+
return typeof value === "object" && value !== null && "type" in value && "range" in value;
|
|
107
|
+
}
|
|
73
108
|
function filenameFor(context, node) {
|
|
74
109
|
if ("fileName" in node) return node.fileName;
|
|
75
110
|
return context.filename;
|
package/dist/checker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checker.js","names":[],"sources":["../ts/checker.ts"],"sourcesContent":["import type { Node } from \"@oxlint/plugins\";\n\nimport { createNodeMaps, toPosition } from \"./node_map\";\nimport { sessionForContext } from \"./registry\";\nimport type {\n ContextWithParserOptions,\n TsgoNode,\n TsgoProgramShape,\n TsgoSignature,\n TsgoSymbol,\n TsgoType,\n TsgoTypeCheckerShape,\n} from \"./types\";\n\nexport function createProgram(\n context: ContextWithParserOptions,\n): TsgoProgramShape & { readonly nodeMaps: ReturnType<typeof createNodeMaps> } {\n const nodeMaps = createNodeMaps(context);\n return {\n nodeMaps,\n getCompilerOptions() {\n return sessionForContext(context).session.getCompilerOptions();\n },\n getCurrentDirectory() {\n return sessionForContext(context).project.rootDir;\n },\n getRootFileNames() {\n return sessionForContext(context).session.getRootFileNames();\n },\n getSourceFile(fileName = context.filename) {\n return { fileName, text: context.sourceCode.text };\n },\n getTypeChecker() {\n return createTypeChecker(context);\n },\n };\n}\n\nexport function createTypeChecker(context: ContextWithParserOptions): TsgoTypeCheckerShape {\n return {\n getTypeAtLocation(node) {\n return sessionForContext(context).session.getTypeAtPosition(\n filenameFor(context,
|
|
1
|
+
{"version":3,"file":"checker.js","names":[],"sources":["../ts/checker.ts"],"sourcesContent":["import type { Node } from \"@oxlint/plugins\";\n\nimport { createNodeMaps, toPosition } from \"./node_map\";\nimport { sessionForContext } from \"./registry\";\nimport type {\n ContextWithParserOptions,\n TsgoNode,\n TsgoProgramShape,\n TsgoSignature,\n TsgoSymbol,\n TsgoType,\n TsgoTypeCheckerShape,\n} from \"./types\";\n\nexport function createProgram(\n context: ContextWithParserOptions,\n): TsgoProgramShape & { readonly nodeMaps: ReturnType<typeof createNodeMaps> } {\n const nodeMaps = createNodeMaps(context);\n return {\n nodeMaps,\n getCompilerOptions() {\n return sessionForContext(context).session.getCompilerOptions();\n },\n getCurrentDirectory() {\n return sessionForContext(context).project.rootDir;\n },\n getRootFileNames() {\n return sessionForContext(context).session.getRootFileNames();\n },\n getSourceFile(fileName = context.filename) {\n return { fileName, text: context.sourceCode.text };\n },\n getTypeChecker() {\n return createTypeChecker(context);\n },\n };\n}\n\nexport function createTypeChecker(context: ContextWithParserOptions): TsgoTypeCheckerShape {\n return {\n getTypeAtLocation(node) {\n const lookupNode = nodeForTypeLookup(node);\n return sessionForContext(context).session.getTypeAtPosition(\n filenameFor(context, lookupNode),\n toPosition(lookupNode),\n );\n },\n getContextualType(node) {\n return this.getTypeAtLocation(node);\n },\n getSymbolAtLocation(node) {\n const lookupNode = nodeForTypeLookup(node);\n return sessionForContext(context).session.getSymbolAtPosition(\n filenameFor(context, lookupNode),\n toPosition(lookupNode),\n );\n },\n getTypeOfSymbol(symbol) {\n return sessionForContext(context).session.getTypeOfSymbol(symbol);\n },\n getDeclaredTypeOfSymbol(symbol) {\n return sessionForContext(context).session.getDeclaredTypeOfSymbol(symbol);\n },\n getTypeOfSymbolAtLocation(symbol, node) {\n return this.getTypeAtLocation(node) ?? this.getTypeOfSymbol(symbol);\n },\n typeToString(type, enclosingDeclaration, flags) {\n void enclosingDeclaration;\n return sessionForContext(context).session.typeToString(type, flags);\n },\n getBaseTypeOfLiteralType(type) {\n return sessionForContext(context).session.getBaseTypeOfLiteralType(type);\n },\n getPropertiesOfType(type) {\n return sessionForContext(context).session.getPropertiesOfType(type);\n },\n getSignaturesOfType(type, kind) {\n return sessionForContext(context).session.getSignaturesOfType(type, kind);\n },\n getReturnTypeOfSignature(signature) {\n return sessionForContext(context).session.getReturnTypeOfSignature(signature);\n },\n getTypePredicateOfSignature(signature) {\n return sessionForContext(context).session.getTypePredicateOfSignature(signature);\n },\n getBaseTypes(type) {\n return sessionForContext(context).session.getBaseTypes(type);\n },\n getImplementedTypes(node) {\n return implementedClauseNodes(node)\n .map((clause) => {\n const expression = implementedClauseChildNode(clause, \"expression\") ?? clause;\n const symbol = this.getSymbolAtLocation(expression) ?? this.getSymbolAtLocation(clause);\n return symbol\n ? (this.getDeclaredTypeOfSymbol(symbol) ?? this.getTypeOfSymbol(symbol))\n : (this.getTypeAtLocation(expression) ?? this.getTypeAtLocation(clause));\n })\n .filter((type): type is TsgoType => type !== undefined);\n },\n getTypeArguments(type) {\n return sessionForContext(context).session.getTypeArguments(type);\n },\n };\n}\n\nfunction nodeForTypeLookup(node: Node | TsgoNode): Node | TsgoNode {\n if (\"pos\" in node) {\n return node;\n }\n switch ((node as { readonly type?: string }).type) {\n case \"ClassDeclaration\":\n case \"ClassExpression\":\n return childNode(node, \"id\") ?? node;\n case \"TSPropertySignature\":\n return childNode(node, \"key\") ?? node;\n default:\n return node;\n }\n}\n\nfunction childNode(node: Node, key: string): Node | undefined {\n const value = (node as unknown as Record<string, unknown>)[key];\n if (isNode(value)) {\n return value;\n }\n return undefined;\n}\n\nfunction implementedClauseNodes(node: Node | TsgoNode): readonly Node[] {\n if (\"pos\" in node) {\n return [];\n }\n const clauses = (node as unknown as { readonly implements?: unknown }).implements;\n if (!Array.isArray(clauses)) {\n return [];\n }\n return clauses.filter(isNode);\n}\n\nfunction implementedClauseChildNode(node: Node, key: string): Node | undefined {\n const value = (node as unknown as Record<string, unknown>)[key];\n if (isNode(value)) {\n return value;\n }\n return undefined;\n}\n\nfunction isNode(value: unknown): value is Node {\n return typeof value === \"object\" && value !== null && \"type\" in value && \"range\" in value;\n}\n\nfunction filenameFor(\n context: ContextWithParserOptions,\n node: Node | TsgoNode | TsgoType | TsgoSymbol | TsgoSignature,\n): string {\n if (\"fileName\" in node) {\n return node.fileName;\n }\n return context.filename;\n}\n"],"mappings":"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"}
|
package/dist/oxlint_utils.d.ts
CHANGED
|
@@ -1,18 +1,38 @@
|
|
|
1
1
|
import { ContextWithParserOptions, ParserServices } from "./types.js";
|
|
2
2
|
import { getParserServices } from "./parser_services.js";
|
|
3
|
+
import { Rule, RuleMeta, Visitor } from "@oxlint/plugins";
|
|
3
4
|
|
|
4
5
|
//#region src/bindings/nodejs/typescript_oxlint/ts/oxlint_utils.d.ts
|
|
6
|
+
type RuleCreatorRule<TOptions extends readonly unknown[] = readonly unknown[], TMessageIds extends string = string> = {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly meta: RuleMeta & {
|
|
9
|
+
readonly messages?: Record<TMessageIds, string>;
|
|
10
|
+
};
|
|
11
|
+
readonly defaultOptions?: TOptions;
|
|
12
|
+
readonly create: (context: ContextWithParserOptions) => Visitor;
|
|
13
|
+
};
|
|
14
|
+
type RuleCreatorCreatedRule<TRule extends RuleCreatorRule> = Omit<TRule, "defaultOptions" | "meta"> & {
|
|
15
|
+
readonly defaultOptions: TRule extends {
|
|
16
|
+
readonly defaultOptions: infer TOptions;
|
|
17
|
+
} ? TOptions : readonly [];
|
|
18
|
+
readonly meta: TRule["meta"] & {
|
|
19
|
+
readonly docs: NonNullable<TRule["meta"]["docs"]> & {
|
|
20
|
+
readonly url: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
} & Rule & Record<string, unknown>;
|
|
24
|
+
type RuleCreatorFactory = <TRule extends RuleCreatorRule>(rule: TRule) => RuleCreatorCreatedRule<TRule>;
|
|
5
25
|
/**
|
|
6
26
|
* Self-hosted type-aware utilities for Oxlint rules backed by tsgo.
|
|
7
27
|
*/
|
|
8
28
|
declare const OxlintUtils: Readonly<{
|
|
9
|
-
RuleCreator(urlCreator: (ruleName: string) => string):
|
|
29
|
+
RuleCreator(urlCreator: (ruleName: string) => string): RuleCreatorFactory;
|
|
10
30
|
getParserServices(context: ContextWithParserOptions, allowWithoutFullTypeInformation?: boolean): ParserServices;
|
|
11
31
|
}>;
|
|
12
|
-
declare const RuleCreator: (urlCreator: (ruleName: string) => string) =>
|
|
32
|
+
declare const RuleCreator: (urlCreator: (ruleName: string) => string) => RuleCreatorFactory;
|
|
13
33
|
declare function applyDefault<Values extends readonly unknown[], Defaults extends readonly unknown[]>(values: Values | undefined, defaults: Defaults): readonly unknown[];
|
|
14
34
|
declare function deepMerge<T>(base: T, override: unknown): T;
|
|
15
35
|
declare function nullThrows<T>(value: T | null | undefined, message?: string): T;
|
|
16
36
|
//#endregion
|
|
17
|
-
export { OxlintUtils, RuleCreator, applyDefault, deepMerge, getParserServices, nullThrows };
|
|
37
|
+
export { OxlintUtils, RuleCreator, RuleCreatorCreatedRule, RuleCreatorFactory, RuleCreatorRule, applyDefault, deepMerge, getParserServices, nullThrows };
|
|
18
38
|
//# sourceMappingURL=oxlint_utils.d.ts.map
|
package/dist/oxlint_utils.js
CHANGED
|
@@ -6,7 +6,7 @@ import { decorateRule } from "./plugin.js";
|
|
|
6
6
|
*/
|
|
7
7
|
const OxlintUtils = Object.freeze({
|
|
8
8
|
RuleCreator(urlCreator) {
|
|
9
|
-
return (rule) => {
|
|
9
|
+
return ((rule) => {
|
|
10
10
|
const docs = rule.meta?.docs;
|
|
11
11
|
return decorateRule({
|
|
12
12
|
...rule,
|
|
@@ -19,7 +19,7 @@ const OxlintUtils = Object.freeze({
|
|
|
19
19
|
},
|
|
20
20
|
defaultOptions: rule.defaultOptions ?? []
|
|
21
21
|
});
|
|
22
|
-
};
|
|
22
|
+
});
|
|
23
23
|
},
|
|
24
24
|
getParserServices(context, allowWithoutFullTypeInformation = false) {
|
|
25
25
|
return getParserServices(context, allowWithoutFullTypeInformation);
|
package/dist/oxlint_utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oxlint_utils.js","names":[],"sources":["../ts/oxlint_utils.ts"],"sourcesContent":["import { getParserServices } from \"./parser_services\";\nimport { decorateRule } from \"./plugin\";\nimport type { ContextWithParserOptions } from \"./types\";\n\n/**\n * Self-hosted type-aware utilities for Oxlint rules backed by tsgo.\n */\nexport const OxlintUtils = Object.freeze({\n RuleCreator(urlCreator: (ruleName: string) => string) {\n return (rule
|
|
1
|
+
{"version":3,"file":"oxlint_utils.js","names":[],"sources":["../ts/oxlint_utils.ts"],"sourcesContent":["import type { Rule, RuleMeta, Visitor } from \"@oxlint/plugins\";\n\nimport { getParserServices } from \"./parser_services\";\nimport { decorateRule } from \"./plugin\";\nimport type { ContextWithParserOptions } from \"./types\";\n\nexport type RuleCreatorRule<\n TOptions extends readonly unknown[] = readonly unknown[],\n TMessageIds extends string = string,\n> = {\n readonly name: string;\n readonly meta: RuleMeta & {\n readonly messages?: Record<TMessageIds, string>;\n };\n readonly defaultOptions?: TOptions;\n readonly create: (context: ContextWithParserOptions) => Visitor;\n};\n\nexport type RuleCreatorCreatedRule<TRule extends RuleCreatorRule> = Omit<\n TRule,\n \"defaultOptions\" | \"meta\"\n> & {\n readonly defaultOptions: TRule extends { readonly defaultOptions: infer TOptions }\n ? TOptions\n : readonly [];\n readonly meta: TRule[\"meta\"] & {\n readonly docs: NonNullable<TRule[\"meta\"][\"docs\"]> & {\n readonly url: string;\n };\n };\n} & Rule &\n Record<string, unknown>;\n\nexport type RuleCreatorFactory = <TRule extends RuleCreatorRule>(\n rule: TRule,\n) => RuleCreatorCreatedRule<TRule>;\n\n/**\n * Self-hosted type-aware utilities for Oxlint rules backed by tsgo.\n */\nexport const OxlintUtils = Object.freeze({\n RuleCreator(urlCreator: (ruleName: string) => string): RuleCreatorFactory {\n return ((rule) => {\n const docs = rule.meta?.docs;\n return decorateRule({\n ...rule,\n meta: {\n ...rule.meta,\n docs: {\n ...docs,\n url: urlCreator(rule.name),\n },\n },\n defaultOptions: rule.defaultOptions ?? [],\n } as unknown as Rule) as RuleCreatorCreatedRule<typeof rule>;\n }) as RuleCreatorFactory;\n },\n getParserServices(context: ContextWithParserOptions, allowWithoutFullTypeInformation = false) {\n return getParserServices(context, allowWithoutFullTypeInformation);\n },\n});\n\nexport const RuleCreator = OxlintUtils.RuleCreator;\nexport { getParserServices } from \"./parser_services\";\n\nexport function applyDefault<\n Values extends readonly unknown[],\n Defaults extends readonly unknown[],\n>(values: Values | undefined, defaults: Defaults): readonly unknown[] {\n return deepMerge(defaults, values ?? []) as readonly unknown[];\n}\n\nexport function deepMerge<T>(base: T, override: unknown): T {\n if (Array.isArray(base) && Array.isArray(override)) {\n return base.map((value, index) => deepMerge(value, override[index])) as unknown as T;\n }\n if (isObject(base) && isObject(override)) {\n return Object.fromEntries(\n [...new Set([...Object.keys(base), ...Object.keys(override)])].map((key) => [\n key,\n deepMerge((base as any)[key], (override as any)[key]),\n ]),\n ) as T;\n }\n return (override ?? base) as T;\n}\n\nexport function nullThrows<T>(\n value: T | null | undefined,\n message = \"Expected value to be present\",\n): T {\n if (value == null) {\n throw new Error(message);\n }\n return value;\n}\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],"mappings":";;;;;;AAwCA,MAAa,cAAc,OAAO,OAAO;CACvC,YAAY,YAA8D;AACxE,WAAS,SAAS;GAChB,MAAM,OAAO,KAAK,MAAM;AACxB,UAAO,aAAa;IAClB,GAAG;IACH,MAAM;KACJ,GAAG,KAAK;KACR,MAAM;MACJ,GAAG;MACH,KAAK,WAAW,KAAK,KAAK;MAC3B;KACF;IACD,gBAAgB,KAAK,kBAAkB,EAAE;IAC1C,CAAoB;;;CAGzB,kBAAkB,SAAmC,kCAAkC,OAAO;AAC5F,SAAO,kBAAkB,SAAS,gCAAgC;;CAErE,CAAC;AAEF,MAAa,cAAc,YAAY;AAGvC,SAAgB,aAGd,QAA4B,UAAwC;AACpE,QAAO,UAAU,UAAU,UAAU,EAAE,CAAC;;AAG1C,SAAgB,UAAa,MAAS,UAAsB;AAC1D,KAAI,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,SAAS,CAChD,QAAO,KAAK,KAAK,OAAO,UAAU,UAAU,OAAO,SAAS,OAAO,CAAC;AAEtE,KAAI,SAAS,KAAK,IAAI,SAAS,SAAS,CACtC,QAAO,OAAO,YACZ,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,KAAK,EAAE,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,QAAQ,CAC1E,KACA,UAAW,KAAa,MAAO,SAAiB,KAAK,CACtD,CAAC,CACH;AAEH,QAAQ,YAAY;;AAGtB,SAAgB,WACd,OACA,UAAU,gCACP;AACH,KAAI,SAAS,KACX,OAAM,IAAI,MAAM,QAAQ;AAE1B,QAAO;;AAGT,SAAS,SAAS,OAAkD;AAClE,QAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,MAAM"}
|
|
@@ -1,22 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createNativeRule } from "./rule_creator.js";
|
|
3
|
-
import { isPromiseLikeNode } from "./type_utils.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
4
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/await_thenable.ts
|
|
5
|
-
const awaitThenableRule =
|
|
6
|
-
docs: { description: "Disallow awaiting non-thenable values." },
|
|
7
|
-
messages: { unexpected: "Unexpected await of a non-thenable value." }
|
|
8
|
-
}, (context) => ({ AwaitExpression(node) {
|
|
9
|
-
if (!isPromiseLikeNode(context, node.argument) && !isObviouslyPromiseLike(node.argument)) context.report({
|
|
10
|
-
node,
|
|
11
|
-
messageId: "unexpected"
|
|
12
|
-
});
|
|
13
|
-
} }));
|
|
14
|
-
function isObviouslyPromiseLike(node) {
|
|
15
|
-
const current = stripChainExpression(node);
|
|
16
|
-
if (current?.type === "NewExpression" && isIdentifierNamed(current.callee, "Promise")) return true;
|
|
17
|
-
if (current?.type !== "CallExpression") return false;
|
|
18
|
-
return memberPropertyName(current.callee) === "resolve" && isIdentifierNamed(memberObject(current.callee), "Promise");
|
|
19
|
-
}
|
|
3
|
+
const awaitThenableRule = createRustNativeRule("await-thenable");
|
|
20
4
|
//#endregion
|
|
21
5
|
export { awaitThenableRule };
|
|
22
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"await_thenable.js","names":[],"sources":["../../ts/rules/await_thenable.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"await_thenable.js","names":[],"sources":["../../ts/rules/await_thenable.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const awaitThenableRule = createRustNativeRule(\"await-thenable\");\n"],"mappings":";;AAEA,MAAa,oBAAoB,qBAAqB,iBAAiB"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ContextWithParserOptions } from "../types.js";
|
|
2
|
+
import { NativeLintDiagnostic, NativeLintNode } from "@corsa-bind/napi";
|
|
3
|
+
|
|
4
|
+
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/native_bridge.d.ts
|
|
5
|
+
type RangedNode = {
|
|
6
|
+
readonly type: string;
|
|
7
|
+
readonly range: readonly [number, number];
|
|
8
|
+
};
|
|
9
|
+
declare function createRustNativeRule(ruleName: string): {
|
|
10
|
+
defaultOptions: never[];
|
|
11
|
+
meta: {
|
|
12
|
+
docs: {
|
|
13
|
+
requiresTypeChecking: boolean;
|
|
14
|
+
url: string;
|
|
15
|
+
};
|
|
16
|
+
type: "problem";
|
|
17
|
+
schema: never[];
|
|
18
|
+
};
|
|
19
|
+
create: (context: any) => Record<string, (node: any) => void>;
|
|
20
|
+
};
|
|
21
|
+
declare function toNativeNode(context: ContextWithParserOptions, node: RangedNode, includeTypeTexts?: boolean, maxDepth?: number): NativeLintNode;
|
|
22
|
+
declare function reportNativeDiagnostics(context: ContextWithParserOptions, node: RangedNode, diagnostics: readonly NativeLintDiagnostic[]): void;
|
|
23
|
+
//#endregion
|
|
24
|
+
export { createRustNativeRule, reportNativeDiagnostics, toNativeNode };
|
|
25
|
+
//# sourceMappingURL=native_bridge.d.ts.map
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { createNativeRule } from "./rule_creator.js";
|
|
2
|
+
import { propertyNamesOfNode, typeTextsAtNode } from "./type_utils.js";
|
|
3
|
+
import { nativeLintRuleMetas, runNativeLintRule } from "@corsa-bind/napi";
|
|
4
|
+
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/native_bridge.ts
|
|
5
|
+
const MAX_NATIVE_NODE_DEPTH = 4;
|
|
6
|
+
const nativeRuleMetasByName = new Map(nativeLintRuleMetas().map((meta) => [meta.name, meta]));
|
|
7
|
+
function createRustNativeRule(ruleName) {
|
|
8
|
+
const meta = nativeRuleMeta(ruleName);
|
|
9
|
+
return createNativeRule(ruleName, {
|
|
10
|
+
docs: { description: meta.docsDescription },
|
|
11
|
+
hasSuggestions: meta.hasSuggestions,
|
|
12
|
+
messages: meta.messages
|
|
13
|
+
}, (context) => Object.fromEntries(meta.listeners.map((listener) => [listener, (node) => {
|
|
14
|
+
reportNativeDiagnostics(context, node, runNativeLintRule(ruleName, toNativeNode(context, node, meta.requiresTypeTexts)));
|
|
15
|
+
}])));
|
|
16
|
+
}
|
|
17
|
+
function toNativeNode(context, node, includeTypeTexts = true, maxDepth = MAX_NATIVE_NODE_DEPTH) {
|
|
18
|
+
const fields = {};
|
|
19
|
+
const children = {};
|
|
20
|
+
const childLists = {};
|
|
21
|
+
for (const [key, value] of Object.entries(node)) {
|
|
22
|
+
if (isSkippedField(key)) continue;
|
|
23
|
+
if (isNativeChildNode(value)) {
|
|
24
|
+
if (maxDepth > 0) children[key] = toNativeNode(context, value, includeTypeTexts, maxDepth - 1);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(value)) {
|
|
28
|
+
if (maxDepth > 0 && value.every(isNativeChildNode)) childLists[key] = value.map((child) => toNativeNode(context, child, includeTypeTexts, maxDepth - 1));
|
|
29
|
+
else if (value.every(isJsonPrimitive)) fields[key] = value;
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (isPrimitiveRecord(value)) {
|
|
33
|
+
fields[key] = value;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (isJsonPrimitive(value)) fields[key] = value;
|
|
37
|
+
}
|
|
38
|
+
const nativeNode = {
|
|
39
|
+
kind: node.type,
|
|
40
|
+
range: nativeRange(node.range)
|
|
41
|
+
};
|
|
42
|
+
if (includeTypeTexts) {
|
|
43
|
+
nativeNode.typeTexts = typeTextsAtNode(context, node);
|
|
44
|
+
nativeNode.propertyNames = propertyNamesOfNode(context, node);
|
|
45
|
+
}
|
|
46
|
+
if (Object.keys(fields).length > 0) nativeNode.fields = fields;
|
|
47
|
+
if (Object.keys(children).length > 0) nativeNode.children = children;
|
|
48
|
+
if (Object.keys(childLists).length > 0) nativeNode.childLists = childLists;
|
|
49
|
+
return nativeNode;
|
|
50
|
+
}
|
|
51
|
+
function reportNativeDiagnostics(context, node, diagnostics) {
|
|
52
|
+
for (const diagnostic of diagnostics) context.report({
|
|
53
|
+
node: reportNodeForRange(node, diagnostic.range),
|
|
54
|
+
messageId: diagnostic.messageId,
|
|
55
|
+
...diagnostic.suggestions?.length ? { suggest: diagnostic.suggestions.map((suggestion) => ({
|
|
56
|
+
messageId: suggestion.messageId,
|
|
57
|
+
fix: (fixer) => suggestion.fixes.map((fix) => fixer.replaceTextRange(oxlintRange(fix.range), fix.replacementText))
|
|
58
|
+
})) } : {}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function reportNodeForRange(root, range) {
|
|
62
|
+
return findNodeByRange(root, range) ?? root;
|
|
63
|
+
}
|
|
64
|
+
function findNodeByRange(value, range, seen = /* @__PURE__ */ new Set()) {
|
|
65
|
+
if (typeof value !== "object" || value === null || seen.has(value)) return;
|
|
66
|
+
seen.add(value);
|
|
67
|
+
if (isNativeChildNode(value) && sameRange(value.range, range)) return value;
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
for (const item of value) {
|
|
70
|
+
const match = findNodeByRange(item, range, seen);
|
|
71
|
+
if (match) return match;
|
|
72
|
+
}
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
for (const [key, child] of Object.entries(value)) {
|
|
76
|
+
if (isSkippedField(key)) continue;
|
|
77
|
+
const match = findNodeByRange(child, range, seen);
|
|
78
|
+
if (match) return match;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function nativeRuleMeta(ruleName) {
|
|
82
|
+
const meta = nativeRuleMetasByName.get(ruleName);
|
|
83
|
+
if (!meta) throw new Error(`corsa-oxlint native Rust rule is not registered: ${ruleName}`);
|
|
84
|
+
return meta;
|
|
85
|
+
}
|
|
86
|
+
function nativeRange(range) {
|
|
87
|
+
return {
|
|
88
|
+
start: range[0],
|
|
89
|
+
end: range[1]
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function oxlintRange(range) {
|
|
93
|
+
return [range.start, range.end];
|
|
94
|
+
}
|
|
95
|
+
function sameRange(range, expected) {
|
|
96
|
+
return range[0] === expected.start && range[1] === expected.end;
|
|
97
|
+
}
|
|
98
|
+
function isNativeChildNode(value) {
|
|
99
|
+
return typeof value === "object" && value !== null && typeof value.type === "string" && isRange(value.range);
|
|
100
|
+
}
|
|
101
|
+
function isRange(value) {
|
|
102
|
+
return Array.isArray(value) && value.length === 2 && typeof value[0] === "number" && typeof value[1] === "number";
|
|
103
|
+
}
|
|
104
|
+
function isJsonPrimitive(value) {
|
|
105
|
+
return value === null || [
|
|
106
|
+
"boolean",
|
|
107
|
+
"number",
|
|
108
|
+
"string"
|
|
109
|
+
].includes(typeof value);
|
|
110
|
+
}
|
|
111
|
+
function isPrimitiveRecord(value) {
|
|
112
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every(isJsonPrimitive);
|
|
113
|
+
}
|
|
114
|
+
function isSkippedField(key) {
|
|
115
|
+
return key === "type" || key === "range" || key === "loc" || key === "parent";
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
export { createRustNativeRule, reportNativeDiagnostics, toNativeNode };
|
|
119
|
+
|
|
120
|
+
//# sourceMappingURL=native_bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native_bridge.js","names":[],"sources":["../../ts/rules/native_bridge.ts"],"sourcesContent":["import { nativeLintRuleMetas, runNativeLintRule } from \"@corsa-bind/napi\";\nimport type {\n NativeLintDiagnostic,\n NativeLintNode,\n NativeLintRange,\n NativeLintRuleMeta,\n} from \"@corsa-bind/napi\";\n\nimport { createNativeRule } from \"./rule_creator\";\nimport { propertyNamesOfNode, typeTextsAtNode } from \"./type_utils\";\nimport type { ContextWithParserOptions } from \"../types\";\n\ntype RangedNode = {\n readonly type: string;\n readonly range: readonly [number, number];\n};\n\nconst MAX_NATIVE_NODE_DEPTH = 4;\nconst nativeRuleMetasByName = new Map(nativeLintRuleMetas().map((meta) => [meta.name, meta]));\n\nexport function createRustNativeRule(ruleName: string) {\n const meta = nativeRuleMeta(ruleName);\n return createNativeRule(\n ruleName,\n {\n docs: {\n description: meta.docsDescription,\n },\n hasSuggestions: meta.hasSuggestions,\n messages: meta.messages,\n },\n (context) =>\n Object.fromEntries(\n meta.listeners.map((listener) => [\n listener,\n (node: RangedNode) => {\n reportNativeDiagnostics(\n context,\n node,\n runNativeLintRule(ruleName, toNativeNode(context, node, meta.requiresTypeTexts)),\n );\n },\n ]),\n ),\n );\n}\n\nexport function toNativeNode(\n context: ContextWithParserOptions,\n node: RangedNode,\n includeTypeTexts = true,\n maxDepth = MAX_NATIVE_NODE_DEPTH,\n): NativeLintNode {\n const fields: Record<string, unknown> = {};\n const children: Record<string, NativeLintNode> = {};\n const childLists: Record<string, NativeLintNode[]> = {};\n\n for (const [key, value] of Object.entries(node)) {\n if (isSkippedField(key)) {\n continue;\n }\n if (isNativeChildNode(value)) {\n if (maxDepth > 0) {\n children[key] = toNativeNode(context, value, includeTypeTexts, maxDepth - 1);\n }\n continue;\n }\n if (Array.isArray(value)) {\n if (maxDepth > 0 && value.every(isNativeChildNode)) {\n childLists[key] = value.map((child) =>\n toNativeNode(context, child, includeTypeTexts, maxDepth - 1),\n );\n } else if (value.every(isJsonPrimitive)) {\n fields[key] = value;\n }\n continue;\n }\n if (isPrimitiveRecord(value)) {\n fields[key] = value;\n continue;\n }\n if (isJsonPrimitive(value)) {\n fields[key] = value;\n }\n }\n\n const nativeNode: NativeLintNode = {\n kind: node.type,\n range: nativeRange(node.range),\n };\n if (includeTypeTexts) {\n nativeNode.typeTexts = typeTextsAtNode(context, node);\n nativeNode.propertyNames = propertyNamesOfNode(context, node);\n }\n if (Object.keys(fields).length > 0) {\n nativeNode.fields = fields;\n }\n if (Object.keys(children).length > 0) {\n nativeNode.children = children;\n }\n if (Object.keys(childLists).length > 0) {\n nativeNode.childLists = childLists;\n }\n return nativeNode;\n}\n\nexport function reportNativeDiagnostics(\n context: ContextWithParserOptions,\n node: RangedNode,\n diagnostics: readonly NativeLintDiagnostic[],\n): void {\n for (const diagnostic of diagnostics) {\n context.report({\n node: reportNodeForRange(node, diagnostic.range),\n messageId: diagnostic.messageId,\n ...(diagnostic.suggestions?.length\n ? {\n suggest: diagnostic.suggestions.map((suggestion) => ({\n messageId: suggestion.messageId,\n fix: (fixer: any) =>\n suggestion.fixes.map((fix) =>\n fixer.replaceTextRange(oxlintRange(fix.range), fix.replacementText),\n ),\n })),\n }\n : {}),\n } as never);\n }\n}\n\nfunction reportNodeForRange(root: RangedNode, range: NativeLintRange): RangedNode {\n return findNodeByRange(root, range) ?? root;\n}\n\nfunction findNodeByRange(\n value: unknown,\n range: NativeLintRange,\n seen = new Set<object>(),\n): RangedNode | undefined {\n if (typeof value !== \"object\" || value === null || seen.has(value)) {\n return undefined;\n }\n seen.add(value);\n\n if (isNativeChildNode(value) && sameRange(value.range, range)) {\n return value;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n const match = findNodeByRange(item, range, seen);\n if (match) {\n return match;\n }\n }\n return undefined;\n }\n\n for (const [key, child] of Object.entries(value)) {\n if (isSkippedField(key)) {\n continue;\n }\n const match = findNodeByRange(child, range, seen);\n if (match) {\n return match;\n }\n }\n return undefined;\n}\n\nfunction nativeRuleMeta(ruleName: string): NativeLintRuleMeta {\n const meta = nativeRuleMetasByName.get(ruleName);\n if (!meta) {\n throw new Error(`corsa-oxlint native Rust rule is not registered: ${ruleName}`);\n }\n return meta;\n}\n\nfunction nativeRange(range: readonly [number, number]): NativeLintRange {\n return { start: range[0], end: range[1] };\n}\n\nfunction oxlintRange(range: NativeLintRange): [number, number] {\n return [range.start, range.end];\n}\n\nfunction sameRange(range: readonly [number, number], expected: NativeLintRange): boolean {\n return range[0] === expected.start && range[1] === expected.end;\n}\n\nfunction isNativeChildNode(value: unknown): value is RangedNode {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\" &&\n isRange((value as { range?: unknown }).range)\n );\n}\n\nfunction isRange(value: unknown): value is readonly [number, number] {\n return (\n Array.isArray(value) &&\n value.length === 2 &&\n typeof value[0] === \"number\" &&\n typeof value[1] === \"number\"\n );\n}\n\nfunction isJsonPrimitive(value: unknown): value is string | number | boolean | null {\n return value === null || [\"boolean\", \"number\", \"string\"].includes(typeof value);\n}\n\nfunction isPrimitiveRecord(\n value: unknown,\n): value is Record<string, string | number | boolean | null> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n Object.values(value).every(isJsonPrimitive)\n );\n}\n\nfunction isSkippedField(key: string): boolean {\n return key === \"type\" || key === \"range\" || key === \"loc\" || key === \"parent\";\n}\n"],"mappings":"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"}
|
|
@@ -1,28 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isArrayLikeNode } from "./type_utils.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
3
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/no_array_delete.ts
|
|
4
|
-
const noArrayDeleteRule =
|
|
5
|
-
docs: { description: "Disallow deleting elements from array-like values." },
|
|
6
|
-
hasSuggestions: true,
|
|
7
|
-
messages: {
|
|
8
|
-
unexpected: "Do not delete elements from an array-like value.",
|
|
9
|
-
useSplice: "Use array.splice(index, 1) instead."
|
|
10
|
-
}
|
|
11
|
-
}, (context) => ({ UnaryExpression(node) {
|
|
12
|
-
if (node.operator !== "delete" || node.argument?.type !== "MemberExpression" || !node.argument.computed) return;
|
|
13
|
-
if (isArrayLikeNode(context, node.argument.object)) context.report({
|
|
14
|
-
node,
|
|
15
|
-
messageId: "unexpected",
|
|
16
|
-
suggest: [{
|
|
17
|
-
messageId: "useSplice",
|
|
18
|
-
fix: (fixer) => [
|
|
19
|
-
fixer.removeRange([node.range[0], node.argument.object.range[1]]),
|
|
20
|
-
fixer.replaceTextRange([node.argument.object.range[1], node.argument.property.range[0]], ".splice("),
|
|
21
|
-
fixer.replaceTextRange([node.argument.property.range[1], node.argument.range[1]], ", 1)")
|
|
22
|
-
]
|
|
23
|
-
}]
|
|
24
|
-
});
|
|
25
|
-
} }));
|
|
3
|
+
const noArrayDeleteRule = createRustNativeRule("no-array-delete");
|
|
26
4
|
//#endregion
|
|
27
5
|
export { noArrayDeleteRule };
|
|
28
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no_array_delete.js","names":[],"sources":["../../ts/rules/no_array_delete.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"no_array_delete.js","names":[],"sources":["../../ts/rules/no_array_delete.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const noArrayDeleteRule = createRustNativeRule(\"no-array-delete\");\n"],"mappings":";;AAEA,MAAa,oBAAoB,qBAAqB,kBAAkB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { calleePropertyName, isIdentifierNamed, isLiteralString, stripChainExpression } from "./ast.js";
|
|
2
1
|
import { createNativeRule } from "./rule_creator.js";
|
|
2
|
+
import { calleePropertyName, isIdentifierNamed, isLiteralString, stripChainExpression } from "./ast.js";
|
|
3
3
|
import { classifyTypeText, isStringLikeNode, splitTopLevelTypeText, typeTextsAtNode } from "./type_utils.js";
|
|
4
4
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/no_base_to_string.ts
|
|
5
5
|
const knownSafeObjectTypes = new Set([
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { calleePropertyName, memberObject, nearestFunctionAncestors, stripChainExpression } from "./ast.js";
|
|
2
1
|
import { createNativeRule } from "./rule_creator.js";
|
|
2
|
+
import { calleePropertyName, memberObject, nearestFunctionAncestors, stripChainExpression } from "./ast.js";
|
|
3
3
|
import { isPromiseLikeNode } from "./type_utils.js";
|
|
4
4
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/no_floating_promises.ts
|
|
5
5
|
const noFloatingPromisesRule = createNativeRule("no-floating-promises", {
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isArrayLikeNode } from "./type_utils.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
3
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/no_for_in_array.ts
|
|
4
|
-
const noForInArrayRule =
|
|
5
|
-
docs: { description: "Disallow for-in iteration over array-like values." },
|
|
6
|
-
messages: { unexpected: "Do not iterate over an array with a for-in loop." }
|
|
7
|
-
}, (context) => ({ ForInStatement(node) {
|
|
8
|
-
if (isArrayLikeNode(context, node.right)) context.report({
|
|
9
|
-
node,
|
|
10
|
-
messageId: "unexpected"
|
|
11
|
-
});
|
|
12
|
-
} }));
|
|
3
|
+
const noForInArrayRule = createRustNativeRule("no-for-in-array");
|
|
13
4
|
//#endregion
|
|
14
5
|
export { noForInArrayRule };
|
|
15
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no_for_in_array.js","names":[],"sources":["../../ts/rules/no_for_in_array.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"no_for_in_array.js","names":[],"sources":["../../ts/rules/no_for_in_array.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const noForInArrayRule = createRustNativeRule(\"no-for-in-array\");\n"],"mappings":";;AAEA,MAAa,mBAAmB,qBAAqB,kBAAkB"}
|
|
@@ -1,34 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createNativeRule } from "./rule_creator.js";
|
|
3
|
-
import { isStringLikeNode } from "./type_utils.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
4
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/no_implied_eval.ts
|
|
5
|
-
const
|
|
6
|
-
"execScript",
|
|
7
|
-
"setInterval",
|
|
8
|
-
"setTimeout"
|
|
9
|
-
]);
|
|
10
|
-
const noImpliedEvalRule = createNativeRule("no-implied-eval", {
|
|
11
|
-
docs: { description: "Disallow string-based dynamic code execution APIs." },
|
|
12
|
-
messages: { unexpected: "Do not pass a string to an implied eval API." }
|
|
13
|
-
}, (context) => ({
|
|
14
|
-
CallExpression(node) {
|
|
15
|
-
const callee = stripChainExpression(node.callee);
|
|
16
|
-
const calleeName = memberPropertyName(callee) ?? (callee?.type === "Identifier" ? callee.name : void 0);
|
|
17
|
-
if (!calleeName || !impliedEvalNames.has(calleeName)) return;
|
|
18
|
-
const [firstArgument] = node.arguments;
|
|
19
|
-
if (firstArgument && !firstArgument.type?.includes("Function") && (isLiteralString(firstArgument) || isStringLikeNode(context, firstArgument))) context.report({
|
|
20
|
-
node,
|
|
21
|
-
messageId: "unexpected"
|
|
22
|
-
});
|
|
23
|
-
},
|
|
24
|
-
NewExpression(node) {
|
|
25
|
-
if (!isIdentifierNamed(node.callee, "Function")) return;
|
|
26
|
-
if (node.arguments.some((argument) => isLiteralString(argument))) context.report({
|
|
27
|
-
node,
|
|
28
|
-
messageId: "unexpected"
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
}));
|
|
3
|
+
const noImpliedEvalRule = createRustNativeRule("no-implied-eval");
|
|
32
4
|
//#endregion
|
|
33
5
|
export { noImpliedEvalRule };
|
|
34
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no_implied_eval.js","names":[],"sources":["../../ts/rules/no_implied_eval.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"no_implied_eval.js","names":[],"sources":["../../ts/rules/no_implied_eval.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const noImpliedEvalRule = createRustNativeRule(\"no-implied-eval\");\n"],"mappings":";;AAEA,MAAa,oBAAoB,qBAAqB,kBAAkB"}
|
|
@@ -1,40 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isNumberLikeNode, isStringLikeNode } from "./type_utils.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
3
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/no_mixed_enums.ts
|
|
4
|
-
const noMixedEnumsRule =
|
|
5
|
-
docs: { description: "Disallow mixing string and numeric enum members." },
|
|
6
|
-
messages: { mixed: "Mixing number and string enums can be confusing." }
|
|
7
|
-
}, (context) => ({ TSEnumDeclaration(node) {
|
|
8
|
-
const members = enumMembersOf(node);
|
|
9
|
-
if (members.length === 0) return;
|
|
10
|
-
const desiredKind = enumMemberKind(context, members[0]);
|
|
11
|
-
if (desiredKind === "unknown") return;
|
|
12
|
-
for (const member of members) {
|
|
13
|
-
const currentKind = enumMemberKind(context, member);
|
|
14
|
-
if (currentKind === "unknown") return;
|
|
15
|
-
if (currentKind !== desiredKind) {
|
|
16
|
-
context.report({
|
|
17
|
-
node: member.initializer ?? member,
|
|
18
|
-
messageId: "mixed"
|
|
19
|
-
});
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
} }));
|
|
24
|
-
function enumMembersOf(node) {
|
|
25
|
-
return node.body?.members ?? node.members ?? [];
|
|
26
|
-
}
|
|
27
|
-
function enumMemberKind(context, member) {
|
|
28
|
-
const initializer = member.initializer;
|
|
29
|
-
if (!initializer) return "number";
|
|
30
|
-
if (initializer.type === "Literal") {
|
|
31
|
-
if (typeof initializer.value === "number") return "number";
|
|
32
|
-
if (typeof initializer.value === "string") return "string";
|
|
33
|
-
}
|
|
34
|
-
if (isStringLikeNode(context, initializer)) return "string";
|
|
35
|
-
if (isNumberLikeNode(context, initializer)) return "number";
|
|
36
|
-
return "unknown";
|
|
37
|
-
}
|
|
3
|
+
const noMixedEnumsRule = createRustNativeRule("no-mixed-enums");
|
|
38
4
|
//#endregion
|
|
39
5
|
export { noMixedEnumsRule };
|
|
40
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no_mixed_enums.js","names":[],"sources":["../../ts/rules/no_mixed_enums.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"no_mixed_enums.js","names":[],"sources":["../../ts/rules/no_mixed_enums.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const noMixedEnumsRule = createRustNativeRule(\"no-mixed-enums\");\n"],"mappings":";;AAEA,MAAa,mBAAmB,qBAAqB,iBAAiB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { nearestFunctionAncestors } from "./ast.js";
|
|
2
1
|
import { createNativeRule } from "./rule_creator.js";
|
|
2
|
+
import { nearestFunctionAncestors } from "./ast.js";
|
|
3
3
|
import { checkerFor, typeAtNode, typeTextsAtNode } from "./type_utils.js";
|
|
4
4
|
import { isUnsafeReturn } from "@corsa-bind/napi";
|
|
5
5
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/no_unsafe_return.ts
|
|
@@ -1,27 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { classifyTypeText, splitTypeText, typeTextsAtNode } from "./type_utils.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
3
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/no_unsafe_unary_minus.ts
|
|
4
|
-
const noUnsafeUnaryMinusRule =
|
|
5
|
-
docs: { description: "Disallow unary negation on non-number and non-bigint values." },
|
|
6
|
-
messages: { unaryMinus: "Argument of unary negation should be assignable to number | bigint." }
|
|
7
|
-
}, (context) => ({ UnaryExpression(node) {
|
|
8
|
-
if (node.operator !== "-") return;
|
|
9
|
-
if (isSafeLiteral(node.argument)) return;
|
|
10
|
-
const typeTexts = typeTextsAtNode(context, node.argument);
|
|
11
|
-
if (typeTexts.length > 0 && typeTexts.every((text) => {
|
|
12
|
-
return splitTypeText(text).every((part) => {
|
|
13
|
-
const kind = classifyTypeText(part);
|
|
14
|
-
return kind === "any" || kind === "number" || kind === "bigint";
|
|
15
|
-
});
|
|
16
|
-
})) return;
|
|
17
|
-
context.report({
|
|
18
|
-
node,
|
|
19
|
-
messageId: "unaryMinus"
|
|
20
|
-
});
|
|
21
|
-
} }));
|
|
22
|
-
function isSafeLiteral(node) {
|
|
23
|
-
return node?.type === "Literal" && (typeof node.value === "number" || typeof node.bigint === "string");
|
|
24
|
-
}
|
|
3
|
+
const noUnsafeUnaryMinusRule = createRustNativeRule("no-unsafe-unary-minus");
|
|
25
4
|
//#endregion
|
|
26
5
|
export { noUnsafeUnaryMinusRule };
|
|
27
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no_unsafe_unary_minus.js","names":[],"sources":["../../ts/rules/no_unsafe_unary_minus.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"no_unsafe_unary_minus.js","names":[],"sources":["../../ts/rules/no_unsafe_unary_minus.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const noUnsafeUnaryMinusRule = createRustNativeRule(\"no-unsafe-unary-minus\");\n"],"mappings":";;AAEA,MAAa,yBAAyB,qBAAqB,wBAAwB"}
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { isErrorLikeNode } from "./type_utils.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
3
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/only_throw_error.ts
|
|
4
|
-
const onlyThrowErrorRule =
|
|
5
|
-
docs: { description: "Require thrown values to be Error-like." },
|
|
6
|
-
messages: { unexpected: "Only Error-like values should be thrown." }
|
|
7
|
-
}, (context) => ({ ThrowStatement(node) {
|
|
8
|
-
if (node.argument && !isErrorLikeNode(context, node.argument)) context.report({
|
|
9
|
-
node,
|
|
10
|
-
messageId: "unexpected"
|
|
11
|
-
});
|
|
12
|
-
} }));
|
|
3
|
+
const onlyThrowErrorRule = createRustNativeRule("only-throw-error");
|
|
13
4
|
//#endregion
|
|
14
5
|
export { onlyThrowErrorRule };
|
|
15
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"only_throw_error.js","names":[],"sources":["../../ts/rules/only_throw_error.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"only_throw_error.js","names":[],"sources":["../../ts/rules/only_throw_error.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const onlyThrowErrorRule = createRustNativeRule(\"only-throw-error\");\n"],"mappings":";;AAEA,MAAa,qBAAqB,qBAAqB,mBAAmB"}
|
|
@@ -1,24 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createNativeRule } from "./rule_creator.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
3
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/prefer_find.ts
|
|
4
|
-
const preferFindRule =
|
|
5
|
-
docs: { description: "Prefer find over filtering and taking the first element." },
|
|
6
|
-
messages: { unexpected: "Use .find() instead of filtering and taking the first match." }
|
|
7
|
-
}, (context) => ({
|
|
8
|
-
MemberExpression(node) {
|
|
9
|
-
if (memberPropertyName(node) === "0" && calleePropertyName(node.object) === "filter") context.report({
|
|
10
|
-
node,
|
|
11
|
-
messageId: "unexpected"
|
|
12
|
-
});
|
|
13
|
-
},
|
|
14
|
-
CallExpression(node) {
|
|
15
|
-
if (calleePropertyName(node) !== "at" || node.arguments[0]?.value !== 0) return;
|
|
16
|
-
if (calleePropertyName(memberObject(node.callee)) === "filter") context.report({
|
|
17
|
-
node,
|
|
18
|
-
messageId: "unexpected"
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
}));
|
|
3
|
+
const preferFindRule = createRustNativeRule("prefer-find");
|
|
22
4
|
//#endregion
|
|
23
5
|
export { preferFindRule };
|
|
24
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer_find.js","names":[],"sources":["../../ts/rules/prefer_find.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"prefer_find.js","names":[],"sources":["../../ts/rules/prefer_find.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const preferFindRule = createRustNativeRule(\"prefer-find\");\n"],"mappings":";;AAEA,MAAa,iBAAiB,qBAAqB,cAAc"}
|
|
@@ -1,20 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createNativeRule } from "./rule_creator.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
3
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/prefer_includes.ts
|
|
4
|
-
const preferIncludesRule =
|
|
5
|
-
docs: { description: "Prefer includes over indexOf/lastIndexOf comparisons." },
|
|
6
|
-
messages: { unexpected: "Use .includes() instead of comparing an index result." }
|
|
7
|
-
}, (context) => ({ BinaryExpression(node) {
|
|
8
|
-
if (!isComparableIndexSearch(node.left) && !isComparableIndexSearch(node.right)) return;
|
|
9
|
-
if (isNegativeOneLiteral(node.left) || isNegativeOneLiteral(node.right) || isZeroLiteral(node.left) || isZeroLiteral(node.right)) context.report({
|
|
10
|
-
node,
|
|
11
|
-
messageId: "unexpected"
|
|
12
|
-
});
|
|
13
|
-
} }));
|
|
14
|
-
function isComparableIndexSearch(node) {
|
|
15
|
-
const propertyName = calleePropertyName(node);
|
|
16
|
-
return propertyName === "indexOf" || propertyName === "lastIndexOf";
|
|
17
|
-
}
|
|
3
|
+
const preferIncludesRule = createRustNativeRule("prefer-includes");
|
|
18
4
|
//#endregion
|
|
19
5
|
export { preferIncludesRule };
|
|
20
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer_includes.js","names":[],"sources":["../../ts/rules/prefer_includes.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"prefer_includes.js","names":[],"sources":["../../ts/rules/prefer_includes.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const preferIncludesRule = createRustNativeRule(\"prefer-includes\");\n"],"mappings":";;AAEA,MAAa,qBAAqB,qBAAqB,kBAAkB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { isIdentifierNamed, memberObject, memberPropertyName, nearestFunctionAncestors, stripChainExpression } from "./ast.js";
|
|
2
1
|
import { createNativeRule } from "./rule_creator.js";
|
|
2
|
+
import { isIdentifierNamed, memberObject, memberPropertyName, nearestFunctionAncestors, stripChainExpression } from "./ast.js";
|
|
3
3
|
import { isAnyLikeNode, isErrorLikeNode, isPromiseLikeNode, isUnknownLikeNode } from "./type_utils.js";
|
|
4
4
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/prefer_promise_reject_errors.ts
|
|
5
5
|
const defaults = {
|
|
@@ -1,19 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createNativeRule } from "./rule_creator.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
3
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/prefer_regexp_exec.ts
|
|
4
|
-
const preferRegexpExecRule =
|
|
5
|
-
docs: { description: "Prefer RegExp#exec over String#match for single matches." },
|
|
6
|
-
messages: { unexpected: "Use a RegExp exec() call instead of String match()." }
|
|
7
|
-
}, (context) => ({ CallExpression(node) {
|
|
8
|
-
if (calleePropertyName(node) !== "match") return;
|
|
9
|
-
const [firstArgument] = node.arguments;
|
|
10
|
-
if (!firstArgument) return;
|
|
11
|
-
const flags = regexFlags(firstArgument);
|
|
12
|
-
if (flags !== void 0 && !flags.includes("g")) context.report({
|
|
13
|
-
node,
|
|
14
|
-
messageId: "unexpected"
|
|
15
|
-
});
|
|
16
|
-
} }));
|
|
3
|
+
const preferRegexpExecRule = createRustNativeRule("prefer-regexp-exec");
|
|
17
4
|
//#endregion
|
|
18
5
|
export { preferRegexpExecRule };
|
|
19
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer_regexp_exec.js","names":[],"sources":["../../ts/rules/prefer_regexp_exec.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"prefer_regexp_exec.js","names":[],"sources":["../../ts/rules/prefer_regexp_exec.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const preferRegexpExecRule = createRustNativeRule(\"prefer-regexp-exec\");\n"],"mappings":";;AAEA,MAAa,uBAAuB,qBAAqB,qBAAqB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { calleePropertyName, isZeroLiteral, memberObject, memberPropertyName, stripChainExpression } from "./ast.js";
|
|
2
1
|
import { createNativeRule } from "./rule_creator.js";
|
|
2
|
+
import { calleePropertyName, isZeroLiteral, memberObject, memberPropertyName, stripChainExpression } from "./ast.js";
|
|
3
3
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/prefer_string_starts_ends_with.ts
|
|
4
4
|
const comparableOperators = new Set([
|
|
5
5
|
"==",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { memberObject, memberPropertyName } from "./ast.js";
|
|
2
1
|
import { createNativeRule } from "./rule_creator.js";
|
|
2
|
+
import { memberObject, memberPropertyName } from "./ast.js";
|
|
3
3
|
import { isArrayLikeNode, typeTextsAtNode } from "./type_utils.js";
|
|
4
4
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/require_array_sort_compare.ts
|
|
5
5
|
const defaults = { ignoreStringArrays: true };
|
|
@@ -1,18 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createNativeRule } from "./rule_creator.js";
|
|
1
|
+
import { createRustNativeRule } from "./native_bridge.js";
|
|
3
2
|
//#region src/bindings/nodejs/typescript_oxlint/ts/rules/use_unknown_in_catch_callback_variable.ts
|
|
4
|
-
const useUnknownInCatchCallbackVariableRule =
|
|
5
|
-
docs: { description: "Require Promise catch callback variables to use an explicit unknown annotation." },
|
|
6
|
-
messages: { unexpected: "Catch callback variables should be explicitly typed as unknown." }
|
|
7
|
-
}, (context) => ({ CallExpression(node) {
|
|
8
|
-
const propertyName = calleePropertyName(node);
|
|
9
|
-
const callback = propertyName === "catch" ? node.arguments[0] : propertyName === "then" ? node.arguments[1] : void 0;
|
|
10
|
-
const parameter = callback?.params?.[0];
|
|
11
|
-
if (callback?.type?.includes("Function") && parameter && !hasUnknownTypeAnnotation(parameter)) context.report({
|
|
12
|
-
node: parameter,
|
|
13
|
-
messageId: "unexpected"
|
|
14
|
-
});
|
|
15
|
-
} }));
|
|
3
|
+
const useUnknownInCatchCallbackVariableRule = createRustNativeRule("use-unknown-in-catch-callback-variable");
|
|
16
4
|
//#endregion
|
|
17
5
|
export { useUnknownInCatchCallbackVariableRule };
|
|
18
6
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use_unknown_in_catch_callback_variable.js","names":[],"sources":["../../ts/rules/use_unknown_in_catch_callback_variable.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"use_unknown_in_catch_callback_variable.js","names":[],"sources":["../../ts/rules/use_unknown_in_catch_callback_variable.ts"],"sourcesContent":["import { createRustNativeRule } from \"./native_bridge\";\n\nexport const useUnknownInCatchCallbackVariableRule = createRustNativeRule(\n \"use-unknown-in-catch-callback-variable\",\n);\n"],"mappings":";;AAEA,MAAa,wCAAwC,qBACnD,yCACD"}
|
package/dist/session.js
CHANGED
|
@@ -29,37 +29,19 @@ var TsgoProjectSession = class {
|
|
|
29
29
|
}
|
|
30
30
|
getTypeAtPosition(fileName, position) {
|
|
31
31
|
const state = this.fileState(fileName);
|
|
32
|
-
if (!state.typeByPosition.has(position)) state.typeByPosition.set(position, this.client().
|
|
33
|
-
snapshot: this.#snapshot,
|
|
34
|
-
project: state.projectId,
|
|
35
|
-
file: fileName,
|
|
36
|
-
position
|
|
37
|
-
}));
|
|
32
|
+
if (!state.typeByPosition.has(position)) state.typeByPosition.set(position, this.client().getTypeAtPosition(this.#snapshot, state.projectId, fileName, position));
|
|
38
33
|
return state.typeByPosition.get(position);
|
|
39
34
|
}
|
|
40
35
|
getSymbolAtPosition(fileName, position) {
|
|
41
36
|
const state = this.fileState(fileName);
|
|
42
|
-
if (!state.symbolByPosition.has(position)) state.symbolByPosition.set(position, this.client().
|
|
43
|
-
snapshot: this.#snapshot,
|
|
44
|
-
project: state.projectId,
|
|
45
|
-
file: fileName,
|
|
46
|
-
position
|
|
47
|
-
}));
|
|
37
|
+
if (!state.symbolByPosition.has(position)) state.symbolByPosition.set(position, this.client().getSymbolAtPosition(this.#snapshot, state.projectId, fileName, position));
|
|
48
38
|
return state.symbolByPosition.get(position);
|
|
49
39
|
}
|
|
50
40
|
getTypeOfSymbol(symbol) {
|
|
51
|
-
return this.client().
|
|
52
|
-
snapshot: this.#snapshot,
|
|
53
|
-
project: this.projectId(),
|
|
54
|
-
symbol: symbol.id
|
|
55
|
-
});
|
|
41
|
+
return this.client().getTypeOfSymbol(this.#snapshot, this.projectId(), symbol.id);
|
|
56
42
|
}
|
|
57
43
|
getDeclaredTypeOfSymbol(symbol) {
|
|
58
|
-
return this.client().
|
|
59
|
-
snapshot: this.#snapshot,
|
|
60
|
-
project: this.projectId(),
|
|
61
|
-
symbol: symbol.id
|
|
62
|
-
});
|
|
44
|
+
return this.client().getDeclaredTypeOfSymbol(this.#snapshot, this.projectId(), symbol.id);
|
|
63
45
|
}
|
|
64
46
|
typeToString(type, flags) {
|
|
65
47
|
return this.client().typeToString(this.#snapshot, this.projectId(), type.id, void 0, flags);
|
|
@@ -108,11 +90,7 @@ var TsgoProjectSession = class {
|
|
|
108
90
|
}) ?? [];
|
|
109
91
|
}
|
|
110
92
|
getTypeArguments(type) {
|
|
111
|
-
return this.client().
|
|
112
|
-
snapshot: this.#snapshot,
|
|
113
|
-
project: this.projectId(),
|
|
114
|
-
type: type.id
|
|
115
|
-
}) ?? [];
|
|
93
|
+
return this.client().getTypeArguments(this.#snapshot, this.projectId(), type.id, type.objectFlags);
|
|
116
94
|
}
|
|
117
95
|
client() {
|
|
118
96
|
if (!this.#client) {
|
package/dist/session.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","names":["#snapshot","#client","#files","#config","#lastRefreshMs","#projects"],"sources":["../ts/session.ts"],"sourcesContent":["import { statSync } from \"node:fs\";\n\nimport { type ProjectResponse, TsgoApiClient } from \"@corsa-bind/napi\";\n\nimport type { TsgoSignature, TsgoSymbol, TsgoType, TsgoTypePredicate } from \"./types\";\nimport type { ResolvedProjectConfig, ResolvedRuntimeOptions } from \"./types\";\n\ntype FileCache = {\n mtimeMs: number;\n projectId: string;\n typeByPosition: Map<number, TsgoType | undefined>;\n symbolByPosition: Map<number, TsgoSymbol | undefined>;\n};\n\nexport class TsgoProjectSession {\n #client?: TsgoApiClient;\n #config?: { options: unknown; fileNames: string[] };\n #snapshot?: string;\n #projects: ProjectResponse[] = [];\n #files = new Map<string, FileCache>();\n #lastRefreshMs = 0;\n\n constructor(\n readonly project: ResolvedProjectConfig,\n readonly runtime: ResolvedRuntimeOptions,\n ) {}\n\n close(): void {\n if (this.#snapshot) {\n this.#client?.releaseHandle(this.#snapshot);\n this.#snapshot = undefined;\n }\n this.#client?.close();\n this.#client = undefined;\n this.#files.clear();\n }\n\n getCompilerOptions(): unknown {\n return this.config().options;\n }\n\n getRootFileNames(): readonly string[] {\n return this.config().fileNames;\n }\n\n getTypeAtPosition(fileName: string, position: number): TsgoType | undefined {\n const state = this.fileState(fileName);\n if (!state.typeByPosition.has(position)) {\n state.typeByPosition.set(\n position,\n this.client().callJson(\"getTypeAtPosition\", {\n snapshot: this.#snapshot,\n project: state.projectId,\n file: fileName,\n position,\n }),\n );\n }\n return state.typeByPosition.get(position);\n }\n\n getSymbolAtPosition(fileName: string, position: number): TsgoSymbol | undefined {\n const state = this.fileState(fileName);\n if (!state.symbolByPosition.has(position)) {\n state.symbolByPosition.set(\n position,\n this.client().callJson(\"getSymbolAtPosition\", {\n snapshot: this.#snapshot,\n project: state.projectId,\n file: fileName,\n position,\n }),\n );\n }\n return state.symbolByPosition.get(position);\n }\n\n getTypeOfSymbol(symbol: TsgoSymbol): TsgoType | undefined {\n return this.client().callJson(\"getTypeOfSymbol\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n symbol: symbol.id,\n });\n }\n\n getDeclaredTypeOfSymbol(symbol: TsgoSymbol): TsgoType | undefined {\n return this.client().callJson(\"getDeclaredTypeOfSymbol\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n symbol: symbol.id,\n });\n }\n\n typeToString(type: TsgoType, flags?: number): string {\n return this.client().typeToString(this.#snapshot!, this.projectId(), type.id, undefined, flags);\n }\n\n getBaseTypeOfLiteralType(type: TsgoType): TsgoType | undefined {\n return this.client().callJson(\"getBaseTypeOfLiteralType\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n type: type.id,\n });\n }\n\n getPropertiesOfType(type: TsgoType): readonly TsgoSymbol[] {\n return (\n this.client().callJson(\"getPropertiesOfType\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n type: type.id,\n }) ?? []\n );\n }\n\n getSignaturesOfType(type: TsgoType, kind: number): readonly TsgoSignature[] {\n return this.client().callJson(\"getSignaturesOfType\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n type: type.id,\n kind,\n });\n }\n\n getReturnTypeOfSignature(signature: TsgoSignature): TsgoType | undefined {\n return this.client().callJson(\"getReturnTypeOfSignature\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n signature: signature.id,\n });\n }\n\n getTypePredicateOfSignature(signature: TsgoSignature): TsgoTypePredicate | undefined {\n return this.client().callJson(\"getTypePredicateOfSignature\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n signature: signature.id,\n });\n }\n\n getBaseTypes(type: TsgoType): readonly TsgoType[] {\n return (\n this.client().callJson(\"getBaseTypes\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n type: type.id,\n }) ?? []\n );\n }\n\n getTypeArguments(type: TsgoType): readonly TsgoType[] {\n return (\n this.client().callJson(\"getTypeArguments\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n type: type.id,\n }) ?? []\n );\n }\n\n private client(): TsgoApiClient {\n if (!this.#client) {\n this.#client = TsgoApiClient.spawn({\n executable: this.runtime.executable,\n cwd: this.runtime.cwd,\n mode: this.runtime.mode,\n });\n this.#client.initialize();\n }\n return this.#client;\n }\n\n private config(): { options: unknown; fileNames: string[] } {\n if (!this.#config) {\n this.#config = this.client().parseConfigFile(this.project.configPath);\n }\n const config = this.#config;\n if (!config) {\n throw new Error(`corsa-oxlint could not parse a tsgo config for ${this.project.configPath}`);\n }\n return config;\n }\n\n private fileState(fileName: string): FileCache {\n this.refreshIfNeeded(fileName);\n const current = this.#files.get(fileName);\n if (current) {\n return current;\n }\n const project = this.client().callJson<ProjectResponse | null>(\"getDefaultProjectForFile\", {\n snapshot: this.#snapshot,\n file: fileName,\n });\n const state: FileCache = {\n mtimeMs: statMtimeMs(fileName),\n projectId: project?.id ?? this.projectId(),\n typeByPosition: new Map(),\n symbolByPosition: new Map(),\n };\n this.#files.set(fileName, state);\n return state;\n }\n\n private refreshIfNeeded(fileName: string): void {\n const now = Date.now();\n const expired = now - this.#lastRefreshMs > this.runtime.cacheLifetimeMs;\n const stale =\n !this.#snapshot || statMtimeMs(fileName) !== this.#files.get(fileName)?.mtimeMs || expired;\n if (!stale) {\n return;\n }\n const previous = this.#snapshot;\n const response = this.client().updateSnapshot(\n previous\n ? { fileChanges: { changed: [fileName] } }\n : { openProject: this.project.configPath },\n );\n this.#snapshot = response.snapshot;\n this.#projects = response.projects;\n this.#lastRefreshMs = now;\n this.#files.clear();\n if (previous && previous !== this.#snapshot) {\n this.client().releaseHandle(previous);\n }\n }\n\n private projectId(): string {\n const id = this.#projects[0]?.id;\n if (!id) {\n throw new Error(`corsa-oxlint could not resolve a tsgo project for ${this.project.filename}`);\n }\n return id;\n }\n}\n\nfunction statMtimeMs(fileName: string): number {\n try {\n return statSync(fileName).mtimeMs;\n } catch {\n return 0;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session.js","names":["#snapshot","#client","#files","#config","#lastRefreshMs","#projects"],"sources":["../ts/session.ts"],"sourcesContent":["import { statSync } from \"node:fs\";\n\nimport { type ProjectResponse, TsgoApiClient } from \"@corsa-bind/napi\";\n\nimport type { TsgoSignature, TsgoSymbol, TsgoType, TsgoTypePredicate } from \"./types\";\nimport type { ResolvedProjectConfig, ResolvedRuntimeOptions } from \"./types\";\n\ntype FileCache = {\n mtimeMs: number;\n projectId: string;\n typeByPosition: Map<number, TsgoType | undefined>;\n symbolByPosition: Map<number, TsgoSymbol | undefined>;\n};\n\nexport class TsgoProjectSession {\n #client?: TsgoApiClient;\n #config?: { options: unknown; fileNames: string[] };\n #snapshot?: string;\n #projects: ProjectResponse[] = [];\n #files = new Map<string, FileCache>();\n #lastRefreshMs = 0;\n\n constructor(\n readonly project: ResolvedProjectConfig,\n readonly runtime: ResolvedRuntimeOptions,\n ) {}\n\n close(): void {\n if (this.#snapshot) {\n this.#client?.releaseHandle(this.#snapshot);\n this.#snapshot = undefined;\n }\n this.#client?.close();\n this.#client = undefined;\n this.#files.clear();\n }\n\n getCompilerOptions(): unknown {\n return this.config().options;\n }\n\n getRootFileNames(): readonly string[] {\n return this.config().fileNames;\n }\n\n getTypeAtPosition(fileName: string, position: number): TsgoType | undefined {\n const state = this.fileState(fileName);\n if (!state.typeByPosition.has(position)) {\n state.typeByPosition.set(\n position,\n this.client().getTypeAtPosition(this.#snapshot!, state.projectId, fileName, position) as\n | TsgoType\n | undefined,\n );\n }\n return state.typeByPosition.get(position);\n }\n\n getSymbolAtPosition(fileName: string, position: number): TsgoSymbol | undefined {\n const state = this.fileState(fileName);\n if (!state.symbolByPosition.has(position)) {\n state.symbolByPosition.set(\n position,\n this.client().getSymbolAtPosition(this.#snapshot!, state.projectId, fileName, position) as\n | TsgoSymbol\n | undefined,\n );\n }\n return state.symbolByPosition.get(position);\n }\n\n getTypeOfSymbol(symbol: TsgoSymbol): TsgoType | undefined {\n return this.client().getTypeOfSymbol(this.#snapshot!, this.projectId(), symbol.id) as\n | TsgoType\n | undefined;\n }\n\n getDeclaredTypeOfSymbol(symbol: TsgoSymbol): TsgoType | undefined {\n return this.client().getDeclaredTypeOfSymbol(this.#snapshot!, this.projectId(), symbol.id) as\n | TsgoType\n | undefined;\n }\n\n typeToString(type: TsgoType, flags?: number): string {\n return this.client().typeToString(this.#snapshot!, this.projectId(), type.id, undefined, flags);\n }\n\n getBaseTypeOfLiteralType(type: TsgoType): TsgoType | undefined {\n return this.client().callJson(\"getBaseTypeOfLiteralType\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n type: type.id,\n });\n }\n\n getPropertiesOfType(type: TsgoType): readonly TsgoSymbol[] {\n return (\n this.client().callJson(\"getPropertiesOfType\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n type: type.id,\n }) ?? []\n );\n }\n\n getSignaturesOfType(type: TsgoType, kind: number): readonly TsgoSignature[] {\n return this.client().callJson(\"getSignaturesOfType\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n type: type.id,\n kind,\n });\n }\n\n getReturnTypeOfSignature(signature: TsgoSignature): TsgoType | undefined {\n return this.client().callJson(\"getReturnTypeOfSignature\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n signature: signature.id,\n });\n }\n\n getTypePredicateOfSignature(signature: TsgoSignature): TsgoTypePredicate | undefined {\n return this.client().callJson(\"getTypePredicateOfSignature\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n signature: signature.id,\n });\n }\n\n getBaseTypes(type: TsgoType): readonly TsgoType[] {\n return (\n this.client().callJson(\"getBaseTypes\", {\n snapshot: this.#snapshot,\n project: this.projectId(),\n type: type.id,\n }) ?? []\n );\n }\n\n getTypeArguments(type: TsgoType): readonly TsgoType[] {\n return this.client().getTypeArguments(\n this.#snapshot!,\n this.projectId(),\n type.id,\n type.objectFlags,\n ) as unknown as readonly TsgoType[];\n }\n\n private client(): TsgoApiClient {\n if (!this.#client) {\n this.#client = TsgoApiClient.spawn({\n executable: this.runtime.executable,\n cwd: this.runtime.cwd,\n mode: this.runtime.mode,\n });\n this.#client.initialize();\n }\n return this.#client;\n }\n\n private config(): { options: unknown; fileNames: string[] } {\n if (!this.#config) {\n this.#config = this.client().parseConfigFile(this.project.configPath);\n }\n const config = this.#config;\n if (!config) {\n throw new Error(`corsa-oxlint could not parse a tsgo config for ${this.project.configPath}`);\n }\n return config;\n }\n\n private fileState(fileName: string): FileCache {\n this.refreshIfNeeded(fileName);\n const current = this.#files.get(fileName);\n if (current) {\n return current;\n }\n const project = this.client().callJson<ProjectResponse | null>(\"getDefaultProjectForFile\", {\n snapshot: this.#snapshot,\n file: fileName,\n });\n const state: FileCache = {\n mtimeMs: statMtimeMs(fileName),\n projectId: project?.id ?? this.projectId(),\n typeByPosition: new Map(),\n symbolByPosition: new Map(),\n };\n this.#files.set(fileName, state);\n return state;\n }\n\n private refreshIfNeeded(fileName: string): void {\n const now = Date.now();\n const expired = now - this.#lastRefreshMs > this.runtime.cacheLifetimeMs;\n const stale =\n !this.#snapshot || statMtimeMs(fileName) !== this.#files.get(fileName)?.mtimeMs || expired;\n if (!stale) {\n return;\n }\n const previous = this.#snapshot;\n const response = this.client().updateSnapshot(\n previous\n ? { fileChanges: { changed: [fileName] } }\n : { openProject: this.project.configPath },\n );\n this.#snapshot = response.snapshot;\n this.#projects = response.projects;\n this.#lastRefreshMs = now;\n this.#files.clear();\n if (previous && previous !== this.#snapshot) {\n this.client().releaseHandle(previous);\n }\n }\n\n private projectId(): string {\n const id = this.#projects[0]?.id;\n if (!id) {\n throw new Error(`corsa-oxlint could not resolve a tsgo project for ${this.project.filename}`);\n }\n return id;\n }\n}\n\nfunction statMtimeMs(fileName: string): number {\n try {\n return statSync(fileName).mtimeMs;\n } catch {\n return 0;\n }\n}\n"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -95,6 +95,7 @@ interface TsgoTypeCheckerShape {
|
|
|
95
95
|
getReturnTypeOfSignature(signature: TsgoSignature): TsgoType | undefined;
|
|
96
96
|
getTypePredicateOfSignature(signature: TsgoSignature): TsgoTypePredicate | undefined;
|
|
97
97
|
getBaseTypes(type: TsgoType): readonly TsgoType[];
|
|
98
|
+
getImplementedTypes(node: Node | TsgoNode): readonly TsgoType[];
|
|
98
99
|
getTypeArguments(type: TsgoType): readonly TsgoType[];
|
|
99
100
|
}
|
|
100
101
|
interface ParserServices {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "corsa-oxlint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Type-aware Oxlint helpers powered by corsa and typescript-go",
|
|
5
5
|
"homepage": "https://github.com/ubugeeei/corsa-bind/tree/main/src/bindings/nodejs/typescript_oxlint",
|
|
6
6
|
"bugs": {
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@oxlint/plugins": "1.57.0",
|
|
63
63
|
"oxlint": "1.57.0",
|
|
64
|
-
"@corsa-bind/napi": "0.
|
|
64
|
+
"@corsa-bind/napi": "0.7.0"
|
|
65
65
|
},
|
|
66
66
|
"engines": {
|
|
67
67
|
"node": ">=22"
|