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.
Files changed (41) hide show
  1. package/README.md +19 -0
  2. package/dist/checker.js +37 -2
  3. package/dist/checker.js.map +1 -1
  4. package/dist/oxlint_utils.d.ts +23 -3
  5. package/dist/oxlint_utils.js +2 -2
  6. package/dist/oxlint_utils.js.map +1 -1
  7. package/dist/rules/await_thenable.js +2 -18
  8. package/dist/rules/await_thenable.js.map +1 -1
  9. package/dist/rules/native_bridge.d.ts +25 -0
  10. package/dist/rules/native_bridge.js +120 -0
  11. package/dist/rules/native_bridge.js.map +1 -0
  12. package/dist/rules/no_array_delete.js +2 -24
  13. package/dist/rules/no_array_delete.js.map +1 -1
  14. package/dist/rules/no_base_to_string.js +1 -1
  15. package/dist/rules/no_floating_promises.js +1 -1
  16. package/dist/rules/no_for_in_array.js +2 -11
  17. package/dist/rules/no_for_in_array.js.map +1 -1
  18. package/dist/rules/no_implied_eval.js +2 -30
  19. package/dist/rules/no_implied_eval.js.map +1 -1
  20. package/dist/rules/no_mixed_enums.js +2 -36
  21. package/dist/rules/no_mixed_enums.js.map +1 -1
  22. package/dist/rules/no_unsafe_return.js +1 -1
  23. package/dist/rules/no_unsafe_unary_minus.js +2 -23
  24. package/dist/rules/no_unsafe_unary_minus.js.map +1 -1
  25. package/dist/rules/only_throw_error.js +2 -11
  26. package/dist/rules/only_throw_error.js.map +1 -1
  27. package/dist/rules/prefer_find.js +2 -20
  28. package/dist/rules/prefer_find.js.map +1 -1
  29. package/dist/rules/prefer_includes.js +2 -16
  30. package/dist/rules/prefer_includes.js.map +1 -1
  31. package/dist/rules/prefer_promise_reject_errors.js +1 -1
  32. package/dist/rules/prefer_regexp_exec.js +2 -15
  33. package/dist/rules/prefer_regexp_exec.js.map +1 -1
  34. package/dist/rules/prefer_string_starts_ends_with.js +1 -1
  35. package/dist/rules/require_array_sort_compare.js +1 -1
  36. package/dist/rules/use_unknown_in_catch_callback_variable.js +2 -14
  37. package/dist/rules/use_unknown_in_catch_callback_variable.js.map +1 -1
  38. package/dist/session.js +5 -27
  39. package/dist/session.js.map +1 -1
  40. package/dist/types.d.ts +1 -0
  41. 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
- return sessionForContext(context).session.getTypeAtPosition(filenameFor(context, node), toPosition(node));
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
- return sessionForContext(context).session.getSymbolAtPosition(filenameFor(context, node), toPosition(node));
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;
@@ -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, node),\n toPosition(node),\n );\n },\n getContextualType(node) {\n return this.getTypeAtLocation(node);\n },\n getSymbolAtLocation(node) {\n return sessionForContext(context).session.getSymbolAtPosition(\n filenameFor(context, node),\n toPosition(node),\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 getTypeArguments(type) {\n return sessionForContext(context).session.getTypeArguments(type);\n },\n };\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":";;;AAcA,SAAgB,cACd,SAC6E;AAE7E,QAAO;EACL,UAFe,eAAe,QAAQ;EAGtC,qBAAqB;AACnB,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,oBAAoB;;EAEhE,sBAAsB;AACpB,UAAO,kBAAkB,QAAQ,CAAC,QAAQ;;EAE5C,mBAAmB;AACjB,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,kBAAkB;;EAE9D,cAAc,WAAW,QAAQ,UAAU;AACzC,UAAO;IAAE;IAAU,MAAM,QAAQ,WAAW;IAAM;;EAEpD,iBAAiB;AACf,UAAO,kBAAkB,QAAQ;;EAEpC;;AAGH,SAAgB,kBAAkB,SAAyD;AACzF,QAAO;EACL,kBAAkB,MAAM;AACtB,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,kBACxC,YAAY,SAAS,KAAK,EAC1B,WAAW,KAAK,CACjB;;EAEH,kBAAkB,MAAM;AACtB,UAAO,KAAK,kBAAkB,KAAK;;EAErC,oBAAoB,MAAM;AACxB,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,oBACxC,YAAY,SAAS,KAAK,EAC1B,WAAW,KAAK,CACjB;;EAEH,gBAAgB,QAAQ;AACtB,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,gBAAgB,OAAO;;EAEnE,wBAAwB,QAAQ;AAC9B,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,wBAAwB,OAAO;;EAE3E,0BAA0B,QAAQ,MAAM;AACtC,UAAO,KAAK,kBAAkB,KAAK,IAAI,KAAK,gBAAgB,OAAO;;EAErE,aAAa,MAAM,sBAAsB,OAAO;AAE9C,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,aAAa,MAAM,MAAM;;EAErE,yBAAyB,MAAM;AAC7B,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,yBAAyB,KAAK;;EAE1E,oBAAoB,MAAM;AACxB,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,oBAAoB,KAAK;;EAErE,oBAAoB,MAAM,MAAM;AAC9B,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,oBAAoB,MAAM,KAAK;;EAE3E,yBAAyB,WAAW;AAClC,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,yBAAyB,UAAU;;EAE/E,4BAA4B,WAAW;AACrC,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,4BAA4B,UAAU;;EAElF,aAAa,MAAM;AACjB,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,aAAa,KAAK;;EAE9D,iBAAiB,MAAM;AACrB,UAAO,kBAAkB,QAAQ,CAAC,QAAQ,iBAAiB,KAAK;;EAEnE;;AAGH,SAAS,YACP,SACA,MACQ;AACR,KAAI,cAAc,KAChB,QAAO,KAAK;AAEd,QAAO,QAAQ"}
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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"}
@@ -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): (rule: any) => never;
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) => (rule: any) => never;
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
@@ -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);
@@ -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: any) => {\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 never);\n };\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":";;;;;;AAOA,MAAa,cAAc,OAAO,OAAO;CACvC,YAAY,YAA0C;AACpD,UAAQ,SAAc;GACpB,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,CAAU;;;CAGf,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
+ {"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 { isIdentifierNamed, memberObject, memberPropertyName, stripChainExpression } from "./ast.js";
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 = createNativeRule("await-thenable", {
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 { isIdentifierNamed, memberObject, memberPropertyName, stripChainExpression } from \"./ast\";\nimport { createNativeRule } from \"./rule_creator\";\nimport { isPromiseLikeNode } from \"./type_utils\";\n\nexport const awaitThenableRule = createNativeRule(\n \"await-thenable\",\n {\n docs: {\n description: \"Disallow awaiting non-thenable values.\",\n },\n messages: {\n unexpected: \"Unexpected await of a non-thenable value.\",\n },\n },\n (context) => ({\n AwaitExpression(node: any) {\n if (!isPromiseLikeNode(context, node.argument) && !isObviouslyPromiseLike(node.argument)) {\n context.report({ node, messageId: \"unexpected\" });\n }\n },\n }),\n);\n\nfunction isObviouslyPromiseLike(node: any): boolean {\n const current = stripChainExpression(node);\n if (current?.type === \"NewExpression\" && isIdentifierNamed(current.callee, \"Promise\")) {\n return true;\n }\n if (current?.type !== \"CallExpression\") {\n return false;\n }\n return (\n memberPropertyName(current.callee) === \"resolve\" &&\n isIdentifierNamed(memberObject(current.callee), \"Promise\")\n );\n}\n"],"mappings":";;;;AAIA,MAAa,oBAAoB,iBAC/B,kBACA;CACE,MAAM,EACJ,aAAa,0CACd;CACD,UAAU,EACR,YAAY,6CACb;CACF,GACA,aAAa,EACZ,gBAAgB,MAAW;AACzB,KAAI,CAAC,kBAAkB,SAAS,KAAK,SAAS,IAAI,CAAC,uBAAuB,KAAK,SAAS,CACtF,SAAQ,OAAO;EAAE;EAAM,WAAW;EAAc,CAAC;GAGtD,EACF;AAED,SAAS,uBAAuB,MAAoB;CAClD,MAAM,UAAU,qBAAqB,KAAK;AAC1C,KAAI,SAAS,SAAS,mBAAmB,kBAAkB,QAAQ,QAAQ,UAAU,CACnF,QAAO;AAET,KAAI,SAAS,SAAS,iBACpB,QAAO;AAET,QACE,mBAAmB,QAAQ,OAAO,KAAK,aACvC,kBAAkB,aAAa,QAAQ,OAAO,EAAE,UAAU"}
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 { createNativeRule } from "./rule_creator.js";
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 = createNativeRule("no-array-delete", {
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 { createNativeRule } from \"./rule_creator\";\nimport { isArrayLikeNode } from \"./type_utils\";\n\nexport const noArrayDeleteRule = createNativeRule(\n \"no-array-delete\",\n {\n docs: {\n description: \"Disallow deleting elements from array-like values.\",\n },\n hasSuggestions: true,\n messages: {\n unexpected: \"Do not delete elements from an array-like value.\",\n useSplice: \"Use array.splice(index, 1) instead.\",\n },\n },\n (context) => ({\n UnaryExpression(node: any) {\n if (\n node.operator !== \"delete\" ||\n node.argument?.type !== \"MemberExpression\" ||\n !node.argument.computed\n ) {\n return;\n }\n if (isArrayLikeNode(context, node.argument.object)) {\n context.report({\n node,\n messageId: \"unexpected\",\n suggest: [\n {\n messageId: \"useSplice\",\n fix: (fixer: any) => [\n fixer.removeRange([node.range[0], node.argument.object.range[1]]),\n fixer.replaceTextRange(\n [node.argument.object.range[1], node.argument.property.range[0]],\n \".splice(\",\n ),\n fixer.replaceTextRange(\n [node.argument.property.range[1], node.argument.range[1]],\n \", 1)\",\n ),\n ],\n },\n ],\n });\n }\n },\n }),\n);\n"],"mappings":";;;AAGA,MAAa,oBAAoB,iBAC/B,mBACA;CACE,MAAM,EACJ,aAAa,sDACd;CACD,gBAAgB;CAChB,UAAU;EACR,YAAY;EACZ,WAAW;EACZ;CACF,GACA,aAAa,EACZ,gBAAgB,MAAW;AACzB,KACE,KAAK,aAAa,YAClB,KAAK,UAAU,SAAS,sBACxB,CAAC,KAAK,SAAS,SAEf;AAEF,KAAI,gBAAgB,SAAS,KAAK,SAAS,OAAO,CAChD,SAAQ,OAAO;EACb;EACA,WAAW;EACX,SAAS,CACP;GACE,WAAW;GACX,MAAM,UAAe;IACnB,MAAM,YAAY,CAAC,KAAK,MAAM,IAAI,KAAK,SAAS,OAAO,MAAM,GAAG,CAAC;IACjE,MAAM,iBACJ,CAAC,KAAK,SAAS,OAAO,MAAM,IAAI,KAAK,SAAS,SAAS,MAAM,GAAG,EAChE,WACD;IACD,MAAM,iBACJ,CAAC,KAAK,SAAS,SAAS,MAAM,IAAI,KAAK,SAAS,MAAM,GAAG,EACzD,OACD;IACF;GACF,CACF;EACF,CAAC;GAGP,EACF"}
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 { createNativeRule } from "./rule_creator.js";
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 = createNativeRule("no-for-in-array", {
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 { createNativeRule } from \"./rule_creator\";\nimport { isArrayLikeNode } from \"./type_utils\";\n\nexport const noForInArrayRule = createNativeRule(\n \"no-for-in-array\",\n {\n docs: {\n description: \"Disallow for-in iteration over array-like values.\",\n },\n messages: {\n unexpected: \"Do not iterate over an array with a for-in loop.\",\n },\n },\n (context) => ({\n ForInStatement(node: any) {\n if (isArrayLikeNode(context, node.right)) {\n context.report({ node, messageId: \"unexpected\" });\n }\n },\n }),\n);\n"],"mappings":";;;AAGA,MAAa,mBAAmB,iBAC9B,mBACA;CACE,MAAM,EACJ,aAAa,qDACd;CACD,UAAU,EACR,YAAY,oDACb;CACF,GACA,aAAa,EACZ,eAAe,MAAW;AACxB,KAAI,gBAAgB,SAAS,KAAK,MAAM,CACtC,SAAQ,OAAO;EAAE;EAAM,WAAW;EAAc,CAAC;GAGtD,EACF"}
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 { isIdentifierNamed, isLiteralString, memberPropertyName, stripChainExpression } from "./ast.js";
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 impliedEvalNames = new Set([
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 {\n isIdentifierNamed,\n isLiteralString,\n memberPropertyName,\n stripChainExpression,\n} from \"./ast\";\nimport { createNativeRule } from \"./rule_creator\";\nimport { isStringLikeNode } from \"./type_utils\";\n\nconst impliedEvalNames = new Set([\"execScript\", \"setInterval\", \"setTimeout\"]);\n\nexport const noImpliedEvalRule = createNativeRule(\n \"no-implied-eval\",\n {\n docs: {\n description: \"Disallow string-based dynamic code execution APIs.\",\n },\n messages: {\n unexpected: \"Do not pass a string to an implied eval API.\",\n },\n },\n (context) => ({\n CallExpression(node: any) {\n const callee = stripChainExpression(node.callee);\n const calleeName =\n memberPropertyName(callee) ?? (callee?.type === \"Identifier\" ? callee.name : undefined);\n if (!calleeName || !impliedEvalNames.has(calleeName)) {\n return;\n }\n const [firstArgument] = node.arguments;\n if (\n firstArgument &&\n !firstArgument.type?.includes(\"Function\") &&\n (isLiteralString(firstArgument) || isStringLikeNode(context, firstArgument))\n ) {\n context.report({ node, messageId: \"unexpected\" });\n }\n },\n NewExpression(node: any) {\n if (!isIdentifierNamed(node.callee, \"Function\")) {\n return;\n }\n if (node.arguments.some((argument: any) => isLiteralString(argument))) {\n context.report({ node, messageId: \"unexpected\" });\n }\n },\n }),\n);\n"],"mappings":";;;;AASA,MAAM,mBAAmB,IAAI,IAAI;CAAC;CAAc;CAAe;CAAa,CAAC;AAE7E,MAAa,oBAAoB,iBAC/B,mBACA;CACE,MAAM,EACJ,aAAa,sDACd;CACD,UAAU,EACR,YAAY,gDACb;CACF,GACA,aAAa;CACZ,eAAe,MAAW;EACxB,MAAM,SAAS,qBAAqB,KAAK,OAAO;EAChD,MAAM,aACJ,mBAAmB,OAAO,KAAK,QAAQ,SAAS,eAAe,OAAO,OAAO,KAAA;AAC/E,MAAI,CAAC,cAAc,CAAC,iBAAiB,IAAI,WAAW,CAClD;EAEF,MAAM,CAAC,iBAAiB,KAAK;AAC7B,MACE,iBACA,CAAC,cAAc,MAAM,SAAS,WAAW,KACxC,gBAAgB,cAAc,IAAI,iBAAiB,SAAS,cAAc,EAE3E,SAAQ,OAAO;GAAE;GAAM,WAAW;GAAc,CAAC;;CAGrD,cAAc,MAAW;AACvB,MAAI,CAAC,kBAAkB,KAAK,QAAQ,WAAW,CAC7C;AAEF,MAAI,KAAK,UAAU,MAAM,aAAkB,gBAAgB,SAAS,CAAC,CACnE,SAAQ,OAAO;GAAE;GAAM,WAAW;GAAc,CAAC;;CAGtD,EACF"}
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 { createNativeRule } from "./rule_creator.js";
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 = createNativeRule("no-mixed-enums", {
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 { createNativeRule } from \"./rule_creator\";\nimport { isNumberLikeNode, isStringLikeNode } from \"./type_utils\";\n\nexport const noMixedEnumsRule = createNativeRule(\n \"no-mixed-enums\",\n {\n docs: {\n description: \"Disallow mixing string and numeric enum members.\",\n },\n messages: {\n mixed: \"Mixing number and string enums can be confusing.\",\n },\n },\n (context) => ({\n TSEnumDeclaration(node: any) {\n const members = enumMembersOf(node);\n if (members.length === 0) {\n return;\n }\n const desiredKind = enumMemberKind(context, members[0]);\n if (desiredKind === \"unknown\") {\n return;\n }\n for (const member of members) {\n const currentKind = enumMemberKind(context, member);\n if (currentKind === \"unknown\") {\n return;\n }\n if (currentKind !== desiredKind) {\n context.report({\n node: member.initializer ?? member,\n messageId: \"mixed\",\n });\n return;\n }\n }\n },\n }),\n);\n\nfunction enumMembersOf(node: any): readonly any[] {\n return node.body?.members ?? node.members ?? [];\n}\n\nfunction enumMemberKind(context: any, member: any): \"number\" | \"string\" | \"unknown\" {\n const initializer = member.initializer;\n if (!initializer) {\n return \"number\";\n }\n if (initializer.type === \"Literal\") {\n if (typeof initializer.value === \"number\") {\n return \"number\";\n }\n if (typeof initializer.value === \"string\") {\n return \"string\";\n }\n }\n if (isStringLikeNode(context, initializer)) {\n return \"string\";\n }\n if (isNumberLikeNode(context, initializer)) {\n return \"number\";\n }\n return \"unknown\";\n}\n"],"mappings":";;;AAGA,MAAa,mBAAmB,iBAC9B,kBACA;CACE,MAAM,EACJ,aAAa,oDACd;CACD,UAAU,EACR,OAAO,oDACR;CACF,GACA,aAAa,EACZ,kBAAkB,MAAW;CAC3B,MAAM,UAAU,cAAc,KAAK;AACnC,KAAI,QAAQ,WAAW,EACrB;CAEF,MAAM,cAAc,eAAe,SAAS,QAAQ,GAAG;AACvD,KAAI,gBAAgB,UAClB;AAEF,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,cAAc,eAAe,SAAS,OAAO;AACnD,MAAI,gBAAgB,UAClB;AAEF,MAAI,gBAAgB,aAAa;AAC/B,WAAQ,OAAO;IACb,MAAM,OAAO,eAAe;IAC5B,WAAW;IACZ,CAAC;AACF;;;GAIP,EACF;AAED,SAAS,cAAc,MAA2B;AAChD,QAAO,KAAK,MAAM,WAAW,KAAK,WAAW,EAAE;;AAGjD,SAAS,eAAe,SAAc,QAA8C;CAClF,MAAM,cAAc,OAAO;AAC3B,KAAI,CAAC,YACH,QAAO;AAET,KAAI,YAAY,SAAS,WAAW;AAClC,MAAI,OAAO,YAAY,UAAU,SAC/B,QAAO;AAET,MAAI,OAAO,YAAY,UAAU,SAC/B,QAAO;;AAGX,KAAI,iBAAiB,SAAS,YAAY,CACxC,QAAO;AAET,KAAI,iBAAiB,SAAS,YAAY,CACxC,QAAO;AAET,QAAO"}
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 { createNativeRule } from "./rule_creator.js";
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 = createNativeRule("no-unsafe-unary-minus", {
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 { createNativeRule } from \"./rule_creator\";\nimport { classifyTypeText, splitTypeText, typeTextsAtNode } from \"./type_utils\";\n\nexport const noUnsafeUnaryMinusRule = createNativeRule(\n \"no-unsafe-unary-minus\",\n {\n docs: {\n description: \"Disallow unary negation on non-number and non-bigint values.\",\n },\n messages: {\n unaryMinus: \"Argument of unary negation should be assignable to number | bigint.\",\n },\n },\n (context) => ({\n UnaryExpression(node: any) {\n if (node.operator !== \"-\") {\n return;\n }\n if (isSafeLiteral(node.argument)) {\n return;\n }\n const typeTexts = typeTextsAtNode(context, node.argument);\n if (\n typeTexts.length > 0 &&\n typeTexts.every((text) => {\n return splitTypeText(text).every((part) => {\n const kind = classifyTypeText(part);\n return kind === \"any\" || kind === \"number\" || kind === \"bigint\";\n });\n })\n ) {\n return;\n }\n context.report({ node, messageId: \"unaryMinus\" });\n },\n }),\n);\n\nfunction isSafeLiteral(node: any): boolean {\n return (\n node?.type === \"Literal\" && (typeof node.value === \"number\" || typeof node.bigint === \"string\")\n );\n}\n"],"mappings":";;;AAGA,MAAa,yBAAyB,iBACpC,yBACA;CACE,MAAM,EACJ,aAAa,gEACd;CACD,UAAU,EACR,YAAY,uEACb;CACF,GACA,aAAa,EACZ,gBAAgB,MAAW;AACzB,KAAI,KAAK,aAAa,IACpB;AAEF,KAAI,cAAc,KAAK,SAAS,CAC9B;CAEF,MAAM,YAAY,gBAAgB,SAAS,KAAK,SAAS;AACzD,KACE,UAAU,SAAS,KACnB,UAAU,OAAO,SAAS;AACxB,SAAO,cAAc,KAAK,CAAC,OAAO,SAAS;GACzC,MAAM,OAAO,iBAAiB,KAAK;AACnC,UAAO,SAAS,SAAS,SAAS,YAAY,SAAS;IACvD;GACF,CAEF;AAEF,SAAQ,OAAO;EAAE;EAAM,WAAW;EAAc,CAAC;GAEpD,EACF;AAED,SAAS,cAAc,MAAoB;AACzC,QACE,MAAM,SAAS,cAAc,OAAO,KAAK,UAAU,YAAY,OAAO,KAAK,WAAW"}
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 { createNativeRule } from "./rule_creator.js";
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 = createNativeRule("only-throw-error", {
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 { createNativeRule } from \"./rule_creator\";\nimport { isErrorLikeNode } from \"./type_utils\";\n\nexport const onlyThrowErrorRule = createNativeRule(\n \"only-throw-error\",\n {\n docs: {\n description: \"Require thrown values to be Error-like.\",\n },\n messages: {\n unexpected: \"Only Error-like values should be thrown.\",\n },\n },\n (context) => ({\n ThrowStatement(node: any) {\n if (node.argument && !isErrorLikeNode(context, node.argument)) {\n context.report({ node, messageId: \"unexpected\" });\n }\n },\n }),\n);\n"],"mappings":";;;AAGA,MAAa,qBAAqB,iBAChC,oBACA;CACE,MAAM,EACJ,aAAa,2CACd;CACD,UAAU,EACR,YAAY,4CACb;CACF,GACA,aAAa,EACZ,eAAe,MAAW;AACxB,KAAI,KAAK,YAAY,CAAC,gBAAgB,SAAS,KAAK,SAAS,CAC3D,SAAQ,OAAO;EAAE;EAAM,WAAW;EAAc,CAAC;GAGtD,EACF"}
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 { calleePropertyName, memberObject, memberPropertyName } from "./ast.js";
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 = createNativeRule("prefer-find", {
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 { calleePropertyName, memberObject, memberPropertyName } from \"./ast\";\nimport { createNativeRule } from \"./rule_creator\";\n\nexport const preferFindRule = createNativeRule(\n \"prefer-find\",\n {\n docs: {\n description: \"Prefer find over filtering and taking the first element.\",\n },\n messages: {\n unexpected: \"Use .find() instead of filtering and taking the first match.\",\n },\n },\n (context) => ({\n MemberExpression(node: any) {\n if (memberPropertyName(node) === \"0\" && calleePropertyName(node.object) === \"filter\") {\n context.report({ node, messageId: \"unexpected\" });\n }\n },\n CallExpression(node: any) {\n if (calleePropertyName(node) !== \"at\" || node.arguments[0]?.value !== 0) {\n return;\n }\n if (calleePropertyName(memberObject(node.callee)) === \"filter\") {\n context.report({ node, messageId: \"unexpected\" });\n }\n },\n }),\n);\n"],"mappings":";;;AAGA,MAAa,iBAAiB,iBAC5B,eACA;CACE,MAAM,EACJ,aAAa,4DACd;CACD,UAAU,EACR,YAAY,gEACb;CACF,GACA,aAAa;CACZ,iBAAiB,MAAW;AAC1B,MAAI,mBAAmB,KAAK,KAAK,OAAO,mBAAmB,KAAK,OAAO,KAAK,SAC1E,SAAQ,OAAO;GAAE;GAAM,WAAW;GAAc,CAAC;;CAGrD,eAAe,MAAW;AACxB,MAAI,mBAAmB,KAAK,KAAK,QAAQ,KAAK,UAAU,IAAI,UAAU,EACpE;AAEF,MAAI,mBAAmB,aAAa,KAAK,OAAO,CAAC,KAAK,SACpD,SAAQ,OAAO;GAAE;GAAM,WAAW;GAAc,CAAC;;CAGtD,EACF"}
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 { calleePropertyName, isNegativeOneLiteral, isZeroLiteral } from "./ast.js";
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 = createNativeRule("prefer-includes", {
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 { calleePropertyName, isNegativeOneLiteral, isZeroLiteral } from \"./ast\";\nimport { createNativeRule } from \"./rule_creator\";\n\nexport const preferIncludesRule = createNativeRule(\n \"prefer-includes\",\n {\n docs: {\n description: \"Prefer includes over indexOf/lastIndexOf comparisons.\",\n },\n messages: {\n unexpected: \"Use .includes() instead of comparing an index result.\",\n },\n },\n (context) => ({\n BinaryExpression(node: any) {\n if (!isComparableIndexSearch(node.left) && !isComparableIndexSearch(node.right)) {\n return;\n }\n if (\n isNegativeOneLiteral(node.left) ||\n isNegativeOneLiteral(node.right) ||\n isZeroLiteral(node.left) ||\n isZeroLiteral(node.right)\n ) {\n context.report({ node, messageId: \"unexpected\" });\n }\n },\n }),\n);\n\nfunction isComparableIndexSearch(node: any): boolean {\n const propertyName = calleePropertyName(node);\n return propertyName === \"indexOf\" || propertyName === \"lastIndexOf\";\n}\n"],"mappings":";;;AAGA,MAAa,qBAAqB,iBAChC,mBACA;CACE,MAAM,EACJ,aAAa,yDACd;CACD,UAAU,EACR,YAAY,yDACb;CACF,GACA,aAAa,EACZ,iBAAiB,MAAW;AAC1B,KAAI,CAAC,wBAAwB,KAAK,KAAK,IAAI,CAAC,wBAAwB,KAAK,MAAM,CAC7E;AAEF,KACE,qBAAqB,KAAK,KAAK,IAC/B,qBAAqB,KAAK,MAAM,IAChC,cAAc,KAAK,KAAK,IACxB,cAAc,KAAK,MAAM,CAEzB,SAAQ,OAAO;EAAE;EAAM,WAAW;EAAc,CAAC;GAGtD,EACF;AAED,SAAS,wBAAwB,MAAoB;CACnD,MAAM,eAAe,mBAAmB,KAAK;AAC7C,QAAO,iBAAiB,aAAa,iBAAiB"}
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 { calleePropertyName, regexFlags } from "./ast.js";
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 = createNativeRule("prefer-regexp-exec", {
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 { calleePropertyName, regexFlags } from \"./ast\";\nimport { createNativeRule } from \"./rule_creator\";\n\nexport const preferRegexpExecRule = createNativeRule(\n \"prefer-regexp-exec\",\n {\n docs: {\n description: \"Prefer RegExp#exec over String#match for single matches.\",\n },\n messages: {\n unexpected: \"Use a RegExp exec() call instead of String match().\",\n },\n },\n (context) => ({\n CallExpression(node: any) {\n if (calleePropertyName(node) !== \"match\") {\n return;\n }\n const [firstArgument] = node.arguments;\n if (!firstArgument) {\n return;\n }\n const flags = regexFlags(firstArgument);\n if (flags !== undefined && !flags.includes(\"g\")) {\n context.report({ node, messageId: \"unexpected\" });\n }\n },\n }),\n);\n"],"mappings":";;;AAGA,MAAa,uBAAuB,iBAClC,sBACA;CACE,MAAM,EACJ,aAAa,4DACd;CACD,UAAU,EACR,YAAY,uDACb;CACF,GACA,aAAa,EACZ,eAAe,MAAW;AACxB,KAAI,mBAAmB,KAAK,KAAK,QAC/B;CAEF,MAAM,CAAC,iBAAiB,KAAK;AAC7B,KAAI,CAAC,cACH;CAEF,MAAM,QAAQ,WAAW,cAAc;AACvC,KAAI,UAAU,KAAA,KAAa,CAAC,MAAM,SAAS,IAAI,CAC7C,SAAQ,OAAO;EAAE;EAAM,WAAW;EAAc,CAAC;GAGtD,EACF"}
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 { calleePropertyName, hasUnknownTypeAnnotation } from "./ast.js";
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 = createNativeRule("use-unknown-in-catch-callback-variable", {
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 { calleePropertyName, hasUnknownTypeAnnotation } from \"./ast\";\nimport { createNativeRule } from \"./rule_creator\";\n\nexport const useUnknownInCatchCallbackVariableRule = createNativeRule(\n \"use-unknown-in-catch-callback-variable\",\n {\n docs: {\n description:\n \"Require Promise catch callback variables to use an explicit unknown annotation.\",\n },\n messages: {\n unexpected: \"Catch callback variables should be explicitly typed as unknown.\",\n },\n },\n (context) => ({\n CallExpression(node: any) {\n const propertyName = calleePropertyName(node);\n const callback =\n propertyName === \"catch\"\n ? node.arguments[0]\n : propertyName === \"then\"\n ? node.arguments[1]\n : undefined;\n const parameter = callback?.params?.[0];\n if (\n callback?.type?.includes(\"Function\") &&\n parameter &&\n !hasUnknownTypeAnnotation(parameter)\n ) {\n context.report({ node: parameter, messageId: \"unexpected\" });\n }\n },\n }),\n);\n"],"mappings":";;;AAGA,MAAa,wCAAwC,iBACnD,0CACA;CACE,MAAM,EACJ,aACE,mFACH;CACD,UAAU,EACR,YAAY,mEACb;CACF,GACA,aAAa,EACZ,eAAe,MAAW;CACxB,MAAM,eAAe,mBAAmB,KAAK;CAC7C,MAAM,WACJ,iBAAiB,UACb,KAAK,UAAU,KACf,iBAAiB,SACf,KAAK,UAAU,KACf,KAAA;CACR,MAAM,YAAY,UAAU,SAAS;AACrC,KACE,UAAU,MAAM,SAAS,WAAW,IACpC,aACA,CAAC,yBAAyB,UAAU,CAEpC,SAAQ,OAAO;EAAE,MAAM;EAAW,WAAW;EAAc,CAAC;GAGjE,EACF"}
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().callJson("getTypeAtPosition", {
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().callJson("getSymbolAtPosition", {
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().callJson("getTypeOfSymbol", {
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().callJson("getDeclaredTypeOfSymbol", {
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().callJson("getTypeArguments", {
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) {
@@ -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.2",
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.3.2"
64
+ "@corsa-bind/napi": "0.7.0"
65
65
  },
66
66
  "engines": {
67
67
  "node": ">=22"