corsa-oxlint 0.47.1 → 0.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -24,7 +24,8 @@ declare namespace ESTree {
24
24
  } ? Kind extends CandidateKind ? Candidate & {
25
25
  readonly type: Kind;
26
26
  } : never : never;
27
- type RemapNodeField<Value> = Value extends ESTree$1.BindingIdentifier ? BindingIdentifier : Value extends OxlintNode ? NodeByType<Extract<Value["type"], string>> : Value extends readonly (infer Item)[] ? readonly RemapNodeField<Item>[] : Value extends (infer Item)[] ? RemapNodeField<Item>[] : Value;
27
+ type RemapTuple<Value extends readonly unknown[]> = Value extends [] ? [] : Value extends [infer Head, ...infer Tail] ? [RemapNodeField<Head>, ...RemapTuple<Tail>] : Value extends readonly [infer Head, ...infer Tail] ? readonly [RemapNodeField<Head>, ...RemapTuple<Tail>] : never;
28
+ type RemapNodeField<Value> = Value extends ESTree$1.BindingIdentifier ? BindingIdentifier : Value extends readonly [unknown, ...unknown[]] ? RemapTuple<Value> : Value extends OxlintNode ? NodeByType<Extract<Value["type"], string>> : Value extends readonly (infer Item)[] ? readonly RemapNodeField<Item>[] : Value extends (infer Item)[] ? RemapNodeField<Item>[] : Value;
28
29
  type RemapNodeShape<Candidate> = Candidate extends object ? { [Key in keyof Candidate]: RemapNodeField<Candidate[Key]> } : Candidate;
29
30
  export type NodeByType<Kind extends string> = Kind extends string ? RemapNodeShape<NarrowNode<OxlintNode, Kind>> : never;
30
31
  export type Node = { [Kind in CorsaAstNodeType]: NodeByType<Kind> }[CorsaAstNodeType] | BindingIdentifier;
@@ -1,7 +1,6 @@
1
1
  import { ContextWithParserOptions, ParserServices } from "./types.js";
2
2
  import { getParserServices } from "./parser_services.js";
3
- import { Rule as Rule$1, RuleMetaWithMessages } from "./plugin.js";
4
- import { Visitor } from "@oxlint/plugins";
3
+ import { Rule, RuleMetaWithMessages, Visitor } from "./plugin.js";
5
4
 
6
5
  //#region src/bindings/nodejs/corsa_oxlint/ts/oxlint_utils.d.ts
7
6
  type RuleCreatorRule<TOptions extends readonly unknown[] = readonly unknown[], TMessageIds extends string = string> = {
@@ -11,7 +10,7 @@ type RuleCreatorRule<TOptions extends readonly unknown[] = readonly unknown[], T
11
10
  readonly create: (context: ContextWithParserOptions) => Visitor;
12
11
  };
13
12
  type RuleCreatorMeta<TMessageIds extends string = string> = RuleMetaWithMessages<TMessageIds>;
14
- type RuleCreatorCreatedRule<TOptions extends readonly unknown[] = readonly [], TMeta extends RuleMetaWithMessages = RuleMetaWithMessages> = Rule$1 & {
13
+ type RuleCreatorCreatedRule<TOptions extends readonly unknown[] = readonly [], TMeta extends RuleMetaWithMessages = RuleMetaWithMessages> = Rule & {
15
14
  readonly defaultOptions: TOptions;
16
15
  readonly meta: TMeta & {
17
16
  readonly docs: NonNullable<TMeta["docs"]> & {
@@ -43,7 +42,7 @@ declare const NullThrowsReasons: Readonly<{
43
42
  MissingToken: (token: string, thing: string) => string;
44
43
  }>;
45
44
  declare const RuleCreator: ((urlCreator: (ruleName: string) => string) => RuleCreatorFactory) & {
46
- withoutDocs<TRule extends Omit<RuleCreatorRule, "name">>(rule: TRule): Rule$1 & {
45
+ withoutDocs<TRule extends Omit<RuleCreatorRule, "name">>(rule: TRule): Rule & {
47
46
  readonly defaultOptions: TRule extends {
48
47
  readonly defaultOptions: infer TOptions;
49
48
  } ? TOptions : readonly [];
@@ -59,7 +58,7 @@ declare const ESLintUtils: Readonly<{
59
58
  MissingToken: (token: string, thing: string) => string;
60
59
  }>;
61
60
  RuleCreator: ((urlCreator: (ruleName: string) => string) => RuleCreatorFactory) & {
62
- withoutDocs<TRule extends Omit<RuleCreatorRule, "name">>(rule: TRule): Rule$1 & {
61
+ withoutDocs<TRule extends Omit<RuleCreatorRule, "name">>(rule: TRule): Rule & {
63
62
  readonly defaultOptions: TRule extends {
64
63
  readonly defaultOptions: infer TOptions;
65
64
  } ? TOptions : readonly [];
@@ -1 +1 @@
1
- {"version":3,"file":"oxlint_utils.js","names":[],"sources":["../ts/oxlint_utils.ts"],"sourcesContent":["import type { Visitor } from \"@oxlint/plugins\";\n\nimport { getParserServices } from \"./parser_services\";\nimport { decorateRule } from \"./plugin\";\nimport type { Rule, RuleMetaWithMessages } 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: RuleCreatorMeta<TMessageIds>;\n readonly defaultOptions?: TOptions;\n readonly create: (context: ContextWithParserOptions) => Visitor;\n};\n\nexport type RuleCreatorMeta<TMessageIds extends string = string> =\n RuleMetaWithMessages<TMessageIds>;\n\nexport type RuleCreatorCreatedRule<\n TOptions extends readonly unknown[] = readonly [],\n TMeta extends RuleMetaWithMessages = RuleMetaWithMessages,\n> = Rule & {\n readonly defaultOptions: TOptions;\n readonly meta: TMeta & {\n readonly docs: NonNullable<TMeta[\"docs\"]> & {\n readonly url: string;\n };\n };\n};\n\ntype RuleCreatorInput<\n TOptions extends readonly unknown[],\n TMessageIds extends string,\n TMeta extends RuleCreatorMeta<TMessageIds>,\n> = Omit<RuleCreatorRule<TOptions, TMessageIds>, \"defaultOptions\" | \"meta\"> & {\n readonly defaultOptions?: TOptions;\n readonly meta: TMeta;\n};\n\nexport interface RuleCreatorFactory {\n <\n TOptions extends readonly unknown[],\n TMessageIds extends string,\n TMeta extends RuleCreatorMeta<TMessageIds>,\n >(\n rule: RuleCreatorInput<TOptions, TMessageIds, TMeta> & {\n readonly defaultOptions: TOptions;\n },\n ): RuleCreatorCreatedRule<TOptions, TMeta>;\n <TMessageIds extends string, TMeta extends RuleCreatorMeta<TMessageIds>>(\n rule: RuleCreatorInput<readonly [], TMessageIds, TMeta> & {\n readonly defaultOptions?: undefined;\n },\n ): RuleCreatorCreatedRule<readonly [], TMeta>;\n}\n\n/**\n * Self-hosted type-aware utilities for Oxlint rules backed by Corsa.\n */\nexport const OxlintUtils = Object.freeze({\n RuleCreator(urlCreator: (ruleName: string) => string): RuleCreatorFactory {\n return ((rule: RuleCreatorRule) => {\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 never;\n }) as RuleCreatorFactory;\n },\n getParserServices(context: ContextWithParserOptions, allowWithoutFullTypeInformation = false) {\n return getParserServices(context, allowWithoutFullTypeInformation);\n },\n});\n\nexport const NullThrowsReasons = Object.freeze({\n MissingParent: \"Expected node to have a parent.\",\n MissingToken: (token: string, thing: string) => `Expected to find a ${token} for the ${thing}.`,\n});\n\nexport const RuleCreator = Object.assign(OxlintUtils.RuleCreator, {\n withoutDocs<TRule extends Omit<RuleCreatorRule, \"name\">>(\n rule: TRule,\n ): Rule & {\n readonly defaultOptions: TRule extends { readonly defaultOptions: infer TOptions }\n ? TOptions\n : readonly [];\n } {\n return decorateRule({\n ...rule,\n defaultOptions: rule.defaultOptions ?? [],\n } as unknown as Rule) as never;\n },\n});\nexport { getParserServices } from \"./parser_services\";\n\nexport function applyDefault<User extends readonly unknown[], Defaults extends readonly unknown[]>(\n defaultOptions: Defaults,\n userOptions: User | null | undefined,\n): readonly unknown[] {\n const options = structuredClone(defaultOptions) as unknown as unknown[];\n if (userOptions == null) {\n return options;\n }\n options.forEach((option, index) => {\n if (userOptions[index] === undefined) {\n return;\n }\n const userOption = userOptions[index];\n options[index] =\n isObjectNotArray(option) && isObjectNotArray(userOption)\n ? deepMerge(option, userOption)\n : userOption;\n });\n return options;\n}\n\nexport function deepMerge<T>(base: T, override: unknown): T {\n if (isObject(base) && isObject(override)) {\n return Object.fromEntries(\n [...new Set([...Object.keys(base), ...Object.keys(override)])].map((key) => [\n key,\n key in base && key in override\n ? deepMerge((base as any)[key], (override as any)[key])\n : key in base\n ? (base as any)[key]\n : (override as any)[key],\n ]),\n ) as T;\n }\n return (override === undefined ? base : override) 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(`Non-null Assertion Failed: ${message}`);\n }\n return value;\n}\n\nexport function isObjectNotArray(value: unknown): value is Record<string, unknown> {\n return isObject(value);\n}\n\nexport const ESLintUtils = Object.freeze({\n NullThrowsReasons,\n RuleCreator,\n applyDefault,\n deepMerge,\n getParserServices,\n isObjectNotArray,\n nullThrows,\n});\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],"mappings":";;;;;;AA6DA,MAAa,cAAc,OAAO,OAAO;CACvC,YAAY,YAA8D;EACxE,SAAS,SAA0B;GACjC,MAAM,OAAO,KAAK,MAAM;GACxB,OAAO,aAAa;IAClB,GAAG;IACH,MAAM;KACJ,GAAG,KAAK;KACR,MAAM;MACJ,GAAG;MACH,KAAK,WAAW,KAAK,IAAI;KAC3B;IACF;IACA,gBAAgB,KAAK,kBAAkB,CAAC;GAC1C,CAAoB;EACtB;CACF;CACA,kBAAkB,SAAmC,kCAAkC,OAAO;EAC5F,OAAO,kBAAkB,SAAS,+BAA+B;CACnE;AACF,CAAC;AAED,MAAa,oBAAoB,OAAO,OAAO;CAC7C,eAAe;CACf,eAAe,OAAe,UAAkB,sBAAsB,MAAM,WAAW,MAAM;AAC/F,CAAC;AAED,MAAa,cAAc,OAAO,OAAO,YAAY,aAAa,EAChE,YACE,MAKA;CACA,OAAO,aAAa;EAClB,GAAG;EACH,gBAAgB,KAAK,kBAAkB,CAAC;CAC1C,CAAoB;AACtB,EACF,CAAC;AAGD,SAAgB,aACd,gBACA,aACoB;CACpB,MAAM,UAAU,gBAAgB,cAAc;CAC9C,IAAI,eAAe,MACjB,OAAO;CAET,QAAQ,SAAS,QAAQ,UAAU;EACjC,IAAI,YAAY,WAAW,KAAA,GACzB;EAEF,MAAM,aAAa,YAAY;EAC/B,QAAQ,SACN,iBAAiB,MAAM,KAAK,iBAAiB,UAAU,IACnD,UAAU,QAAQ,UAAU,IAC5B;CACR,CAAC;CACD,OAAO;AACT;AAEA,SAAgB,UAAa,MAAS,UAAsB;CAC1D,IAAI,SAAS,IAAI,KAAK,SAAS,QAAQ,GACrC,OAAO,OAAO,YACZ,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,IAAI,GAAG,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAC1E,KACA,OAAO,QAAQ,OAAO,WAClB,UAAW,KAAa,MAAO,SAAiB,IAAI,IACpD,OAAO,OACJ,KAAa,OACb,SAAiB,IAC1B,CAAC,CACH;CAEF,OAAQ,aAAa,KAAA,IAAY,OAAO;AAC1C;AAEA,SAAgB,WACd,OACA,UAAU,gCACP;CACH,IAAI,SAAS,MACX,MAAM,IAAI,MAAM,8BAA8B,SAAS;CAEzD,OAAO;AACT;AAEA,SAAgB,iBAAiB,OAAkD;CACjF,OAAO,SAAS,KAAK;AACvB;AAEA,MAAa,cAAc,OAAO,OAAO;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E"}
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 { Rule, RuleMetaWithMessages, Visitor } 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: RuleCreatorMeta<TMessageIds>;\n readonly defaultOptions?: TOptions;\n readonly create: (context: ContextWithParserOptions) => Visitor;\n};\n\nexport type RuleCreatorMeta<TMessageIds extends string = string> =\n RuleMetaWithMessages<TMessageIds>;\n\nexport type RuleCreatorCreatedRule<\n TOptions extends readonly unknown[] = readonly [],\n TMeta extends RuleMetaWithMessages = RuleMetaWithMessages,\n> = Rule & {\n readonly defaultOptions: TOptions;\n readonly meta: TMeta & {\n readonly docs: NonNullable<TMeta[\"docs\"]> & {\n readonly url: string;\n };\n };\n};\n\ntype RuleCreatorInput<\n TOptions extends readonly unknown[],\n TMessageIds extends string,\n TMeta extends RuleCreatorMeta<TMessageIds>,\n> = Omit<RuleCreatorRule<TOptions, TMessageIds>, \"defaultOptions\" | \"meta\"> & {\n readonly defaultOptions?: TOptions;\n readonly meta: TMeta;\n};\n\nexport interface RuleCreatorFactory {\n <\n TOptions extends readonly unknown[],\n TMessageIds extends string,\n TMeta extends RuleCreatorMeta<TMessageIds>,\n >(\n rule: RuleCreatorInput<TOptions, TMessageIds, TMeta> & {\n readonly defaultOptions: TOptions;\n },\n ): RuleCreatorCreatedRule<TOptions, TMeta>;\n <TMessageIds extends string, TMeta extends RuleCreatorMeta<TMessageIds>>(\n rule: RuleCreatorInput<readonly [], TMessageIds, TMeta> & {\n readonly defaultOptions?: undefined;\n },\n ): RuleCreatorCreatedRule<readonly [], TMeta>;\n}\n\n/**\n * Self-hosted type-aware utilities for Oxlint rules backed by Corsa.\n */\nexport const OxlintUtils = Object.freeze({\n RuleCreator(urlCreator: (ruleName: string) => string): RuleCreatorFactory {\n return ((rule: RuleCreatorRule) => {\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 never;\n }) as RuleCreatorFactory;\n },\n getParserServices(context: ContextWithParserOptions, allowWithoutFullTypeInformation = false) {\n return getParserServices(context, allowWithoutFullTypeInformation);\n },\n});\n\nexport const NullThrowsReasons = Object.freeze({\n MissingParent: \"Expected node to have a parent.\",\n MissingToken: (token: string, thing: string) => `Expected to find a ${token} for the ${thing}.`,\n});\n\nexport const RuleCreator = Object.assign(OxlintUtils.RuleCreator, {\n withoutDocs<TRule extends Omit<RuleCreatorRule, \"name\">>(\n rule: TRule,\n ): Rule & {\n readonly defaultOptions: TRule extends { readonly defaultOptions: infer TOptions }\n ? TOptions\n : readonly [];\n } {\n return decorateRule({\n ...rule,\n defaultOptions: rule.defaultOptions ?? [],\n } as unknown as Rule) as never;\n },\n});\nexport { getParserServices } from \"./parser_services\";\n\nexport function applyDefault<User extends readonly unknown[], Defaults extends readonly unknown[]>(\n defaultOptions: Defaults,\n userOptions: User | null | undefined,\n): readonly unknown[] {\n const options = structuredClone(defaultOptions) as unknown as unknown[];\n if (userOptions == null) {\n return options;\n }\n options.forEach((option, index) => {\n if (userOptions[index] === undefined) {\n return;\n }\n const userOption = userOptions[index];\n options[index] =\n isObjectNotArray(option) && isObjectNotArray(userOption)\n ? deepMerge(option, userOption)\n : userOption;\n });\n return options;\n}\n\nexport function deepMerge<T>(base: T, override: unknown): T {\n if (isObject(base) && isObject(override)) {\n return Object.fromEntries(\n [...new Set([...Object.keys(base), ...Object.keys(override)])].map((key) => [\n key,\n key in base && key in override\n ? deepMerge((base as any)[key], (override as any)[key])\n : key in base\n ? (base as any)[key]\n : (override as any)[key],\n ]),\n ) as T;\n }\n return (override === undefined ? base : override) 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(`Non-null Assertion Failed: ${message}`);\n }\n return value;\n}\n\nexport function isObjectNotArray(value: unknown): value is Record<string, unknown> {\n return isObject(value);\n}\n\nexport const ESLintUtils = Object.freeze({\n NullThrowsReasons,\n RuleCreator,\n applyDefault,\n deepMerge,\n getParserServices,\n isObjectNotArray,\n nullThrows,\n});\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n"],"mappings":";;;;;;AA2DA,MAAa,cAAc,OAAO,OAAO;CACvC,YAAY,YAA8D;EACxE,SAAS,SAA0B;GACjC,MAAM,OAAO,KAAK,MAAM;GACxB,OAAO,aAAa;IAClB,GAAG;IACH,MAAM;KACJ,GAAG,KAAK;KACR,MAAM;MACJ,GAAG;MACH,KAAK,WAAW,KAAK,IAAI;KAC3B;IACF;IACA,gBAAgB,KAAK,kBAAkB,CAAC;GAC1C,CAAoB;EACtB;CACF;CACA,kBAAkB,SAAmC,kCAAkC,OAAO;EAC5F,OAAO,kBAAkB,SAAS,+BAA+B;CACnE;AACF,CAAC;AAED,MAAa,oBAAoB,OAAO,OAAO;CAC7C,eAAe;CACf,eAAe,OAAe,UAAkB,sBAAsB,MAAM,WAAW,MAAM;AAC/F,CAAC;AAED,MAAa,cAAc,OAAO,OAAO,YAAY,aAAa,EAChE,YACE,MAKA;CACA,OAAO,aAAa;EAClB,GAAG;EACH,gBAAgB,KAAK,kBAAkB,CAAC;CAC1C,CAAoB;AACtB,EACF,CAAC;AAGD,SAAgB,aACd,gBACA,aACoB;CACpB,MAAM,UAAU,gBAAgB,cAAc;CAC9C,IAAI,eAAe,MACjB,OAAO;CAET,QAAQ,SAAS,QAAQ,UAAU;EACjC,IAAI,YAAY,WAAW,KAAA,GACzB;EAEF,MAAM,aAAa,YAAY;EAC/B,QAAQ,SACN,iBAAiB,MAAM,KAAK,iBAAiB,UAAU,IACnD,UAAU,QAAQ,UAAU,IAC5B;CACR,CAAC;CACD,OAAO;AACT;AAEA,SAAgB,UAAa,MAAS,UAAsB;CAC1D,IAAI,SAAS,IAAI,KAAK,SAAS,QAAQ,GACrC,OAAO,OAAO,YACZ,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,OAAO,KAAK,IAAI,GAAG,GAAG,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAC1E,KACA,OAAO,QAAQ,OAAO,WAClB,UAAW,KAAa,MAAO,SAAiB,IAAI,IACpD,OAAO,OACJ,KAAa,OACb,SAAiB,IAC1B,CAAC,CACH;CAEF,OAAQ,aAAa,KAAA,IAAY,OAAO;AAC1C;AAEA,SAAgB,WACd,OACA,UAAU,gCACP;CACH,IAAI,SAAS,MACX,MAAM,IAAI,MAAM,8BAA8B,SAAS;CAEzD,OAAO;AACT;AAEA,SAAgB,iBAAiB,OAAkD;CACjF,OAAO,SAAS,KAAK;AACvB;AAEA,MAAa,cAAc,OAAO,OAAO;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E"}
@@ -1,6 +1,9 @@
1
1
  import { ContextWithParserOptions, ParserServices } from "./types.js";
2
2
 
3
3
  //#region src/bindings/nodejs/corsa_oxlint/ts/parser_services.d.ts
4
+ type ParserServicesContext = Omit<ContextWithParserOptions, "options"> & {
5
+ readonly options?: readonly unknown[];
6
+ };
4
7
  /**
5
8
  * Returns type-aware parser services backed by Corsa.
6
9
  *
@@ -10,7 +13,7 @@ import { ContextWithParserOptions, ParserServices } from "./types.js";
10
13
  * const checker = services.program.getTypeChecker();
11
14
  * ```
12
15
  */
13
- declare function getParserServices(context: ContextWithParserOptions, allowWithoutFullTypeInformation?: boolean): ParserServices;
16
+ declare function getParserServices(context: ParserServicesContext, allowWithoutFullTypeInformation?: boolean): ParserServices;
14
17
  //#endregion
15
18
  export { getParserServices };
16
19
  //# sourceMappingURL=parser_services.d.ts.map
@@ -15,24 +15,25 @@ const parserServices = /* @__PURE__ */ new WeakMap();
15
15
  function getParserServices(context, allowWithoutFullTypeInformation = false) {
16
16
  const current = parserServices.get(context);
17
17
  if (current) return current;
18
- const parserOptions = resolveTypeAwareParserOptions(context);
19
- const eslintParserServices = resolveEslintParserServices(context);
18
+ const typedContext = context;
19
+ const parserOptions = resolveTypeAwareParserOptions(typedContext);
20
+ const eslintParserServices = resolveEslintParserServices(typedContext);
20
21
  if (!parserOptions.corsa && eslintParserServices) {
21
22
  const services = createEslintParserServices(eslintParserServices);
22
23
  parserServices.set(context, services);
23
24
  return services;
24
25
  }
25
26
  try {
26
- const maps = createNodeMaps(context);
27
+ const maps = createNodeMaps(typedContext);
27
28
  const services = {
28
- program: createProgram(context),
29
+ program: createProgram(typedContext),
29
30
  ...maps,
30
31
  hasFullTypeInformation: true,
31
32
  getTypeAtLocation(node) {
32
- return createTypeChecker(context).getTypeAtLocation(node);
33
+ return createTypeChecker(typedContext).getTypeAtLocation(node);
33
34
  },
34
35
  getSymbolAtLocation(node) {
35
- return createTypeChecker(context).getSymbolAtLocation(node);
36
+ return createTypeChecker(typedContext).getSymbolAtLocation(node);
36
37
  }
37
38
  };
38
39
  parserServices.set(context, services);
@@ -40,8 +41,8 @@ function getParserServices(context, allowWithoutFullTypeInformation = false) {
40
41
  } catch (error) {
41
42
  if (!allowWithoutFullTypeInformation) throw error;
42
43
  const fallback = {
43
- program: createProgram(context),
44
- ...createNodeMaps(context),
44
+ program: createProgram(typedContext),
45
+ ...createNodeMaps(typedContext),
45
46
  hasFullTypeInformation: false,
46
47
  getTypeAtLocation() {},
47
48
  getSymbolAtLocation() {}
@@ -1 +1 @@
1
- {"version":3,"file":"parser_services.js","names":[],"sources":["../ts/parser_services.ts"],"sourcesContent":["import { createProgram, createTypeChecker } from \"./checker\";\nimport { resolveTypeAwareParserOptions } from \"./context\";\nimport { createNodeMaps } from \"./node_map\";\nimport type {\n ContextWithParserOptions,\n CorsaTypeCheckerShape,\n ParserServices,\n ParserServicesWithTypeInformation,\n} from \"./types\";\n\nconst parserServices = new WeakMap<object, ParserServices>();\n\n/**\n * Returns type-aware parser services backed by Corsa.\n *\n * @example\n * ```ts\n * const services = getParserServices(context);\n * const checker = services.program.getTypeChecker();\n * ```\n */\nexport function getParserServices(\n context: ContextWithParserOptions,\n allowWithoutFullTypeInformation = false,\n): ParserServices {\n const current = parserServices.get(context);\n if (current) {\n return current;\n }\n const parserOptions = resolveTypeAwareParserOptions(context);\n const eslintParserServices = resolveEslintParserServices(context);\n if (!parserOptions.corsa && eslintParserServices) {\n const services = createEslintParserServices(eslintParserServices);\n parserServices.set(context, services);\n return services;\n }\n try {\n const maps = createNodeMaps(context);\n const program = createProgram(context);\n const services: ParserServicesWithTypeInformation = {\n program,\n ...maps,\n hasFullTypeInformation: true,\n getTypeAtLocation(node) {\n return createTypeChecker(context).getTypeAtLocation(node);\n },\n getSymbolAtLocation(node) {\n return createTypeChecker(context).getSymbolAtLocation(node);\n },\n };\n parserServices.set(context, services);\n return services;\n } catch (error) {\n if (!allowWithoutFullTypeInformation) {\n throw error;\n }\n const fallback: ParserServices = {\n program: createProgram(context),\n ...createNodeMaps(context),\n hasFullTypeInformation: false,\n getTypeAtLocation() {\n return undefined;\n },\n getSymbolAtLocation() {\n return undefined;\n },\n };\n parserServices.set(context, fallback);\n return fallback;\n }\n}\n\nfunction createEslintParserServices(\n parserServices: ParserServices,\n): ParserServicesWithTypeInformation {\n const checker = createEslintTypeChecker(\n parserServices.program.getTypeChecker(),\n parserServices.esTreeNodeToTSNodeMap,\n );\n return {\n program: createEslintProgram(parserServices.program, checker),\n esTreeNodeToTSNodeMap: parserServices.esTreeNodeToTSNodeMap,\n tsNodeToESTreeNodeMap: parserServices.tsNodeToESTreeNodeMap,\n hasFullTypeInformation: true,\n getTypeAtLocation(node) {\n const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);\n return tsNode ? checker.getTypeAtLocation(tsNode) : undefined;\n },\n getSymbolAtLocation(node) {\n const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);\n return tsNode ? checker.getSymbolAtLocation(tsNode) : undefined;\n },\n };\n}\n\nfunction createEslintProgram(\n program: ParserServices[\"program\"],\n checker: CorsaTypeCheckerShape,\n): ParserServices[\"program\"] {\n return Object.assign(Object.create(program), {\n getTypeChecker() {\n return checker;\n },\n });\n}\n\nfunction createEslintTypeChecker(\n checker: CorsaTypeCheckerShape,\n esTreeNodeToTSNodeMap: ParserServices[\"esTreeNodeToTSNodeMap\"],\n): CorsaTypeCheckerShape {\n const source = checker as unknown as Record<string, unknown>;\n return {\n ...checker,\n getTypeAtLocation(node) {\n return callChecker(source, \"getTypeAtLocation\", tsNodeFor(node, esTreeNodeToTSNodeMap));\n },\n getContextualType(node) {\n return (\n callChecker(source, \"getContextualType\", tsNodeFor(node, esTreeNodeToTSNodeMap)) ??\n this.getTypeAtLocation(node)\n );\n },\n getSymbolAtLocation(node) {\n return callChecker(source, \"getSymbolAtLocation\", tsNodeFor(node, esTreeNodeToTSNodeMap));\n },\n getSymbol(symbol) {\n return typeof symbol === \"object\" ? symbol : undefined;\n },\n getSymbolById(id) {\n return typeof id === \"object\" ? id : undefined;\n },\n getSymbolOfType(type) {\n return (\n callChecker(source, \"getSymbolOfType\", type) ??\n ((type as { readonly symbol?: unknown }).symbol as never)\n );\n },\n getNode(node) {\n return typeof node === \"object\" ? node : undefined;\n },\n getNodeById(id) {\n return typeof id === \"object\" ? id : undefined;\n },\n getTypeOfSymbol(symbol) {\n return callChecker(source, \"getTypeOfSymbol\", symbol);\n },\n getTypeOfSymbolById() {\n return undefined;\n },\n getDeclaredTypeOfSymbol(symbol) {\n return callChecker(source, \"getDeclaredTypeOfSymbol\", symbol);\n },\n getDeclaredTypeOfSymbolById() {\n return undefined;\n },\n getTypeOfSymbolAtLocation(symbol, node) {\n return (\n callChecker(\n source,\n \"getTypeOfSymbolAtLocation\",\n symbol,\n tsNodeFor(node, esTreeNodeToTSNodeMap),\n ) ??\n this.getTypeOfSymbol(symbol) ??\n this.getDeclaredTypeOfSymbol(symbol)\n );\n },\n typeToString(type, enclosingDeclaration, flags) {\n return (\n callChecker(\n source,\n \"typeToString\",\n type,\n enclosingDeclaration ? tsNodeFor(enclosingDeclaration, esTreeNodeToTSNodeMap) : undefined,\n flags,\n ) ?? \"\"\n );\n },\n getBaseTypeOfLiteralType(type) {\n return callChecker(source, \"getBaseTypeOfLiteralType\", type) ?? type;\n },\n getPropertiesOfType(type) {\n return asReadonlyArray(callChecker(source, \"getPropertiesOfType\", type));\n },\n getSignaturesOfType(type, kind) {\n return asReadonlyArray(callChecker(source, \"getSignaturesOfType\", type, kind));\n },\n getCallSignatureFacts() {\n return {};\n },\n getReturnTypeOfSignature(signature) {\n return callChecker(source, \"getReturnTypeOfSignature\", signature);\n },\n getTypePredicateOfSignature(signature) {\n return callChecker(source, \"getTypePredicateOfSignature\", signature);\n },\n getBaseTypes(type) {\n return asReadonlyArray(callChecker(source, \"getBaseTypes\", type));\n },\n getImplementedTypes(node) {\n return asReadonlyArray(\n callChecker(source, \"getImplementedTypes\", tsNodeFor(node, esTreeNodeToTSNodeMap)),\n );\n },\n getImplementedTypesOfType(type) {\n return asReadonlyArray(callChecker(source, \"getImplementedTypesOfType\", type));\n },\n getTypeArguments(type) {\n return asReadonlyArray(callChecker(source, \"getTypeArguments\", type));\n },\n getTypesOfType(type) {\n return asReadonlyArray((type as { readonly types?: unknown }).types);\n },\n getTargetOfType(type) {\n return (type as { readonly target?: unknown }).target as never;\n },\n getTypeParametersOfType(type) {\n return asReadonlyArray((type as { readonly typeParameters?: unknown }).typeParameters);\n },\n getOuterTypeParametersOfType(type) {\n return asReadonlyArray(\n (type as { readonly outerTypeParameters?: unknown }).outerTypeParameters,\n );\n },\n getLocalTypeParametersOfType(type) {\n return asReadonlyArray(\n (type as { readonly localTypeParameters?: unknown }).localTypeParameters,\n );\n },\n getObjectTypeOfType(type) {\n return (type as { readonly objectType?: unknown }).objectType as never;\n },\n getIndexTypeOfType(type) {\n return (type as { readonly indexType?: unknown }).indexType as never;\n },\n getCheckTypeOfType(type) {\n return (type as { readonly checkType?: unknown }).checkType as never;\n },\n getExtendsTypeOfType(type) {\n return (type as { readonly extendsType?: unknown }).extendsType as never;\n },\n getBaseTypeOfType(type) {\n return (type as { readonly baseType?: unknown }).baseType as never;\n },\n getConstraintOfType(type) {\n return callChecker(source, \"getBaseConstraintOfType\", type);\n },\n isUnionType(type) {\n const value = type as { readonly isUnion?: unknown };\n return typeof value.isUnion === \"function\" ? Boolean(value.isUnion()) : false;\n },\n isIntersectionType(type) {\n const value = type as { readonly isIntersection?: unknown };\n return typeof value.isIntersection === \"function\" ? Boolean(value.isIntersection()) : false;\n },\n } as CorsaTypeCheckerShape;\n}\n\nfunction callChecker(\n checker: Record<string, unknown>,\n method: string,\n ...args: readonly unknown[]\n): never | undefined {\n const candidate = checker[method];\n return typeof candidate === \"function\" ? candidate.apply(checker, args) : undefined;\n}\n\nfunction asReadonlyArray<T>(value: unknown): readonly T[] {\n return Array.isArray(value) ? value : [];\n}\n\nfunction tsNodeFor(\n node: unknown,\n esTreeNodeToTSNodeMap: ParserServices[\"esTreeNodeToTSNodeMap\"],\n): unknown {\n return hasNode(esTreeNodeToTSNodeMap, node) ? esTreeNodeToTSNodeMap.get(node as never) : node;\n}\n\nfunction hasNode(\n esTreeNodeToTSNodeMap: ParserServices[\"esTreeNodeToTSNodeMap\"],\n node: unknown,\n): boolean {\n return typeof node === \"object\" && node !== null && esTreeNodeToTSNodeMap.has(node as never);\n}\n\nfunction resolveEslintParserServices(\n context: ContextWithParserOptions,\n): ParserServices | undefined {\n const candidates = [context.parserServices, context.sourceCode.parserServices] as const;\n for (const candidate of candidates) {\n if (hasEslintParserServices(candidate)) {\n return candidate;\n }\n }\n return undefined;\n}\n\nfunction hasEslintParserServices(value: unknown): value is ParserServices {\n return Boolean(\n value &&\n typeof value === \"object\" &&\n \"program\" in value &&\n \"esTreeNodeToTSNodeMap\" in value &&\n \"tsNodeToESTreeNodeMap\" in value,\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"parser_services.js","names":[],"sources":["../ts/parser_services.ts"],"sourcesContent":["import { createProgram, createTypeChecker } from \"./checker\";\nimport { resolveTypeAwareParserOptions } from \"./context\";\nimport { createNodeMaps } from \"./node_map\";\nimport type {\n ContextWithParserOptions,\n CorsaTypeCheckerShape,\n ParserServices,\n ParserServicesWithTypeInformation,\n} from \"./types\";\n\nconst parserServices = new WeakMap<object, ParserServices>();\ntype ParserServicesContext = Omit<ContextWithParserOptions, \"options\"> & {\n readonly options?: readonly unknown[];\n};\n\n/**\n * Returns type-aware parser services backed by Corsa.\n *\n * @example\n * ```ts\n * const services = getParserServices(context);\n * const checker = services.program.getTypeChecker();\n * ```\n */\nexport function getParserServices(\n context: ParserServicesContext,\n allowWithoutFullTypeInformation = false,\n): ParserServices {\n const current = parserServices.get(context);\n if (current) {\n return current;\n }\n const typedContext = context as ContextWithParserOptions;\n const parserOptions = resolveTypeAwareParserOptions(typedContext);\n const eslintParserServices = resolveEslintParserServices(typedContext);\n if (!parserOptions.corsa && eslintParserServices) {\n const services = createEslintParserServices(eslintParserServices);\n parserServices.set(context, services);\n return services;\n }\n try {\n const maps = createNodeMaps(typedContext);\n const program = createProgram(typedContext);\n const services: ParserServicesWithTypeInformation = {\n program,\n ...maps,\n hasFullTypeInformation: true,\n getTypeAtLocation(node) {\n return createTypeChecker(typedContext).getTypeAtLocation(node);\n },\n getSymbolAtLocation(node) {\n return createTypeChecker(typedContext).getSymbolAtLocation(node);\n },\n };\n parserServices.set(context, services);\n return services;\n } catch (error) {\n if (!allowWithoutFullTypeInformation) {\n throw error;\n }\n const fallback: ParserServices = {\n program: createProgram(typedContext),\n ...createNodeMaps(typedContext),\n hasFullTypeInformation: false,\n getTypeAtLocation() {\n return undefined;\n },\n getSymbolAtLocation() {\n return undefined;\n },\n };\n parserServices.set(context, fallback);\n return fallback;\n }\n}\n\nfunction createEslintParserServices(\n parserServices: ParserServices,\n): ParserServicesWithTypeInformation {\n const checker = createEslintTypeChecker(\n parserServices.program.getTypeChecker(),\n parserServices.esTreeNodeToTSNodeMap,\n );\n return {\n program: createEslintProgram(parserServices.program, checker),\n esTreeNodeToTSNodeMap: parserServices.esTreeNodeToTSNodeMap,\n tsNodeToESTreeNodeMap: parserServices.tsNodeToESTreeNodeMap,\n hasFullTypeInformation: true,\n getTypeAtLocation(node) {\n const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);\n return tsNode ? checker.getTypeAtLocation(tsNode) : undefined;\n },\n getSymbolAtLocation(node) {\n const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node);\n return tsNode ? checker.getSymbolAtLocation(tsNode) : undefined;\n },\n };\n}\n\nfunction createEslintProgram(\n program: ParserServices[\"program\"],\n checker: CorsaTypeCheckerShape,\n): ParserServices[\"program\"] {\n return Object.assign(Object.create(program), {\n getTypeChecker() {\n return checker;\n },\n });\n}\n\nfunction createEslintTypeChecker(\n checker: CorsaTypeCheckerShape,\n esTreeNodeToTSNodeMap: ParserServices[\"esTreeNodeToTSNodeMap\"],\n): CorsaTypeCheckerShape {\n const source = checker as unknown as Record<string, unknown>;\n return {\n ...checker,\n getTypeAtLocation(node) {\n return callChecker(source, \"getTypeAtLocation\", tsNodeFor(node, esTreeNodeToTSNodeMap));\n },\n getContextualType(node) {\n return (\n callChecker(source, \"getContextualType\", tsNodeFor(node, esTreeNodeToTSNodeMap)) ??\n this.getTypeAtLocation(node)\n );\n },\n getSymbolAtLocation(node) {\n return callChecker(source, \"getSymbolAtLocation\", tsNodeFor(node, esTreeNodeToTSNodeMap));\n },\n getSymbol(symbol) {\n return typeof symbol === \"object\" ? symbol : undefined;\n },\n getSymbolById(id) {\n return typeof id === \"object\" ? id : undefined;\n },\n getSymbolOfType(type) {\n return (\n callChecker(source, \"getSymbolOfType\", type) ??\n ((type as { readonly symbol?: unknown }).symbol as never)\n );\n },\n getNode(node) {\n return typeof node === \"object\" ? node : undefined;\n },\n getNodeById(id) {\n return typeof id === \"object\" ? id : undefined;\n },\n getTypeOfSymbol(symbol) {\n return callChecker(source, \"getTypeOfSymbol\", symbol);\n },\n getTypeOfSymbolById() {\n return undefined;\n },\n getDeclaredTypeOfSymbol(symbol) {\n return callChecker(source, \"getDeclaredTypeOfSymbol\", symbol);\n },\n getDeclaredTypeOfSymbolById() {\n return undefined;\n },\n getTypeOfSymbolAtLocation(symbol, node) {\n return (\n callChecker(\n source,\n \"getTypeOfSymbolAtLocation\",\n symbol,\n tsNodeFor(node, esTreeNodeToTSNodeMap),\n ) ??\n this.getTypeOfSymbol(symbol) ??\n this.getDeclaredTypeOfSymbol(symbol)\n );\n },\n typeToString(type, enclosingDeclaration, flags) {\n return (\n callChecker(\n source,\n \"typeToString\",\n type,\n enclosingDeclaration ? tsNodeFor(enclosingDeclaration, esTreeNodeToTSNodeMap) : undefined,\n flags,\n ) ?? \"\"\n );\n },\n getBaseTypeOfLiteralType(type) {\n return callChecker(source, \"getBaseTypeOfLiteralType\", type) ?? type;\n },\n getPropertiesOfType(type) {\n return asReadonlyArray(callChecker(source, \"getPropertiesOfType\", type));\n },\n getSignaturesOfType(type, kind) {\n return asReadonlyArray(callChecker(source, \"getSignaturesOfType\", type, kind));\n },\n getCallSignatureFacts() {\n return {};\n },\n getReturnTypeOfSignature(signature) {\n return callChecker(source, \"getReturnTypeOfSignature\", signature);\n },\n getTypePredicateOfSignature(signature) {\n return callChecker(source, \"getTypePredicateOfSignature\", signature);\n },\n getBaseTypes(type) {\n return asReadonlyArray(callChecker(source, \"getBaseTypes\", type));\n },\n getImplementedTypes(node) {\n return asReadonlyArray(\n callChecker(source, \"getImplementedTypes\", tsNodeFor(node, esTreeNodeToTSNodeMap)),\n );\n },\n getImplementedTypesOfType(type) {\n return asReadonlyArray(callChecker(source, \"getImplementedTypesOfType\", type));\n },\n getTypeArguments(type) {\n return asReadonlyArray(callChecker(source, \"getTypeArguments\", type));\n },\n getTypesOfType(type) {\n return asReadonlyArray((type as { readonly types?: unknown }).types);\n },\n getTargetOfType(type) {\n return (type as { readonly target?: unknown }).target as never;\n },\n getTypeParametersOfType(type) {\n return asReadonlyArray((type as { readonly typeParameters?: unknown }).typeParameters);\n },\n getOuterTypeParametersOfType(type) {\n return asReadonlyArray(\n (type as { readonly outerTypeParameters?: unknown }).outerTypeParameters,\n );\n },\n getLocalTypeParametersOfType(type) {\n return asReadonlyArray(\n (type as { readonly localTypeParameters?: unknown }).localTypeParameters,\n );\n },\n getObjectTypeOfType(type) {\n return (type as { readonly objectType?: unknown }).objectType as never;\n },\n getIndexTypeOfType(type) {\n return (type as { readonly indexType?: unknown }).indexType as never;\n },\n getCheckTypeOfType(type) {\n return (type as { readonly checkType?: unknown }).checkType as never;\n },\n getExtendsTypeOfType(type) {\n return (type as { readonly extendsType?: unknown }).extendsType as never;\n },\n getBaseTypeOfType(type) {\n return (type as { readonly baseType?: unknown }).baseType as never;\n },\n getConstraintOfType(type) {\n return callChecker(source, \"getBaseConstraintOfType\", type);\n },\n isUnionType(type) {\n const value = type as { readonly isUnion?: unknown };\n return typeof value.isUnion === \"function\" ? Boolean(value.isUnion()) : false;\n },\n isIntersectionType(type) {\n const value = type as { readonly isIntersection?: unknown };\n return typeof value.isIntersection === \"function\" ? Boolean(value.isIntersection()) : false;\n },\n } as CorsaTypeCheckerShape;\n}\n\nfunction callChecker(\n checker: Record<string, unknown>,\n method: string,\n ...args: readonly unknown[]\n): never | undefined {\n const candidate = checker[method];\n return typeof candidate === \"function\" ? candidate.apply(checker, args) : undefined;\n}\n\nfunction asReadonlyArray<T>(value: unknown): readonly T[] {\n return Array.isArray(value) ? value : [];\n}\n\nfunction tsNodeFor(\n node: unknown,\n esTreeNodeToTSNodeMap: ParserServices[\"esTreeNodeToTSNodeMap\"],\n): unknown {\n return hasNode(esTreeNodeToTSNodeMap, node) ? esTreeNodeToTSNodeMap.get(node as never) : node;\n}\n\nfunction hasNode(\n esTreeNodeToTSNodeMap: ParserServices[\"esTreeNodeToTSNodeMap\"],\n node: unknown,\n): boolean {\n return typeof node === \"object\" && node !== null && esTreeNodeToTSNodeMap.has(node as never);\n}\n\nfunction resolveEslintParserServices(\n context: ContextWithParserOptions,\n): ParserServices | undefined {\n const candidates = [context.parserServices, context.sourceCode.parserServices] as const;\n for (const candidate of candidates) {\n if (hasEslintParserServices(candidate)) {\n return candidate;\n }\n }\n return undefined;\n}\n\nfunction hasEslintParserServices(value: unknown): value is ParserServices {\n return Boolean(\n value &&\n typeof value === \"object\" &&\n \"program\" in value &&\n \"esTreeNodeToTSNodeMap\" in value &&\n \"tsNodeToESTreeNodeMap\" in value,\n );\n}\n"],"mappings":"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"}
package/dist/plugin.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { ContextWithParserOptions } from "./types.js";
2
- import { Diagnostic, Plugin as Plugin$1, Rule as Rule$1, RuleMeta, Visitor, VisitorWithHooks } from "@oxlint/plugins";
2
+ import { AST_NODE_TYPES } from "./compat.js";
3
+ import { ESTree as ESTree$1 } from "./index.js";
4
+ import { Diagnostic, Plugin as Plugin$1, Rule as Rule$1, RuleMeta } from "@oxlint/plugins";
3
5
 
4
6
  //#region src/bindings/nodejs/corsa_oxlint/ts/plugin.d.ts
5
7
  type Plugin = Omit<Plugin$1, "rules"> & {
@@ -28,6 +30,16 @@ type RuleMetaWithMessages<MessageId extends string = string> = Omit<RuleMeta, "d
28
30
  readonly docs?: RuleDocs;
29
31
  readonly messages?: Record<MessageId, string>;
30
32
  };
33
+ type CorsaAstNodeType = keyof typeof AST_NODE_TYPES;
34
+ type BivariantVisitorHandler<Handler extends (...args: any[]) => any> = {
35
+ bivarianceHack(...args: Parameters<Handler>): ReturnType<Handler>;
36
+ }["bivarianceHack"];
37
+ type VisitorNode<Kind extends CorsaAstNodeType> = ESTree$1[Kind];
38
+ type Visitor = { [Kind in CorsaAstNodeType]?: BivariantVisitorHandler<(node: VisitorNode<Kind>) => void> } & { [Kind in CorsaAstNodeType as `${Kind}:exit`]?: BivariantVisitorHandler<(node: VisitorNode<Kind>) => void> } & Record<string, BivariantVisitorHandler<(node: ESTree$1.Node) => void> | undefined>;
39
+ type VisitorWithHooks = Visitor & {
40
+ readonly before?: () => boolean | void;
41
+ readonly after?: () => void;
42
+ };
31
43
  type RuleDefinition<MessageId extends string = string, Options extends readonly unknown[] = readonly unknown[]> = Record<string, unknown> & {
32
44
  readonly defaultOptions?: Options;
33
45
  readonly meta?: RuleMetaWithMessages<MessageId>;
@@ -57,5 +69,5 @@ declare function defineRule<MessageId extends string = string, const Options ext
57
69
  declare function compatPlugin(plugin: Plugin): Plugin;
58
70
  declare function decorateRule(rule: Rule): Rule;
59
71
  //#endregion
60
- export { Plugin, Rule, RuleContext, RuleDefinition, RuleDiagnostic, RuleDocs, RuleMetaWithMessages, compatPlugin, decorateRule, definePlugin, defineRule };
72
+ export { Plugin, Rule, RuleContext, RuleDefinition, RuleDiagnostic, RuleDocs, RuleMetaWithMessages, Visitor, VisitorWithHooks, compatPlugin, decorateRule, definePlugin, defineRule };
61
73
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","names":[],"sources":["../ts/plugin.ts"],"sourcesContent":["import * as oxlintPluginApi from \"@oxlint/plugins\";\nimport type {\n Context as OxlintContext,\n Diagnostic,\n Plugin as OxlintPlugin,\n Rule as OxlintRule,\n RuleMeta as OxlintRuleMeta,\n Visitor,\n VisitorWithHooks,\n} from \"@oxlint/plugins\";\n\nimport { resolveTypeAwareParserOptions } from \"./context\";\nimport { getParserServices } from \"./parser_services\";\nimport type { ContextWithParserOptions, ParserServices } from \"./types\";\n\nexport type Plugin = Omit<OxlintPlugin, \"rules\"> & {\n readonly rules: Record<string, Rule>;\n} & Record<string, unknown>;\nexport type Rule = OxlintRule & Record<string, unknown>;\nexport type RuleDiagnostic<MessageId extends string = string> = Diagnostic & {\n readonly messageId?: MessageId | null | undefined;\n};\nexport type RuleContext<\n MessageId extends string = string,\n Options extends readonly unknown[] = readonly unknown[],\n> = Omit<ContextWithParserOptions, \"options\" | \"report\"> & {\n readonly options: Readonly<Options>;\n report(this: void, diagnostic: RuleDiagnostic<MessageId>): void;\n};\nexport type RuleDocs = {\n readonly description?: string;\n readonly recommended?: unknown;\n readonly url?: string;\n /**\n * Enables corsa-oxlint's type-aware parser service defaults for this rule.\n *\n * @default false\n */\n readonly requiresTypeChecking?: boolean;\n};\nexport type RuleMetaWithMessages<MessageId extends string = string> = Omit<\n OxlintRuleMeta,\n \"docs\" | \"messages\"\n> & {\n readonly docs?: RuleDocs;\n readonly messages?: Record<MessageId, string>;\n};\nexport type RuleDefinition<\n MessageId extends string = string,\n Options extends readonly unknown[] = readonly unknown[],\n> = Record<string, unknown> & {\n readonly defaultOptions?: Options;\n readonly meta?: RuleMetaWithMessages<MessageId>;\n} & (\n | {\n readonly create: (context: RuleContext<MessageId, Options>) => Visitor;\n readonly createOnce?: never;\n }\n | {\n readonly create?: (context: RuleContext<MessageId, Options>) => Visitor;\n readonly createOnce: (context: RuleContext<MessageId, Options>) => VisitorWithHooks;\n }\n );\n\nconst defineOxlintPlugin = oxlintPluginApi.definePlugin;\nconst defineOxlintRule = oxlintPluginApi.defineRule;\nconst baseCompatPlugin = Reflect.get(\n oxlintPluginApi as object,\n [\"es\", \"lintCompatPlugin\"].join(\"\"),\n) as typeof oxlintPluginApi.definePlugin;\n\nexport function definePlugin(plugin: Plugin): Plugin {\n return defineOxlintPlugin({\n ...plugin,\n rules: wrapRules(plugin.rules ?? {}),\n } as OxlintPlugin) as Plugin;\n}\n\n/**\n * Defines a single Oxlint rule with type-aware parser services.\n *\n * @example\n * ```ts\n * export default defineRule({\n * meta: { schema: [], messages: { demo: \"demo\" } },\n * create(context) {\n * const services = context.parserServices;\n * return {};\n * },\n * });\n * ```\n */\nexport function defineRule<\n MessageId extends string = string,\n const Options extends readonly unknown[] = readonly unknown[],\n>(rule: RuleDefinition<MessageId, Options>): Rule;\nexport function defineRule(rule: Rule): Rule {\n return defineOxlintRule(decorateRule(rule) as OxlintRule) as Rule;\n}\n\nexport function compatPlugin(plugin: Plugin): Plugin {\n return baseCompatPlugin(definePlugin(plugin)) as Plugin;\n}\n\nexport function decorateRule(rule: Rule): Rule {\n if (rule.create) {\n return {\n ...rule,\n create(context) {\n return rule.create!(decorateContext(context, rule));\n },\n } as Rule;\n }\n if (\"createOnce\" in rule && typeof (rule as any).createOnce === \"function\") {\n return {\n ...rule,\n createOnce(context) {\n return (rule as any).createOnce(decorateContext(context, rule));\n },\n } as Rule;\n }\n return rule;\n}\n\nfunction wrapRules(rules: Record<string, Rule>): Record<string, Rule> {\n return Object.fromEntries(\n Object.entries(rules).map(([name, rule]) => [name, decorateRule(rule)]),\n );\n}\n\nfunction decorateContext(context: ContextWithParserOptions, rule: Rule): ContextWithParserOptions {\n const typeAware = requiresTypeChecking(rule);\n const parserOptions = Object.freeze(\n resolveTypeAwareParserOptions(context, {\n corsa: typeAware,\n projectService: typeAware,\n }),\n );\n const baseLanguageOptions = context.languageOptions;\n const languageOptions = Object.freeze({\n ...baseLanguageOptions,\n parserOptions,\n });\n return Object.create(context as OxlintContext, {\n languageOptions: {\n configurable: true,\n enumerable: true,\n get() {\n return languageOptions;\n },\n },\n parserOptions: {\n configurable: true,\n enumerable: false,\n get() {\n return parserOptions;\n },\n },\n parserServices: {\n configurable: true,\n enumerable: false,\n get(): ParserServices {\n return getParserServices(context);\n },\n },\n }) as ContextWithParserOptions;\n}\n\nfunction requiresTypeChecking(rule: Rule): boolean {\n return (\n (rule.meta as { readonly docs?: { readonly requiresTypeChecking?: unknown } } | undefined)?.docs\n ?.requiresTypeChecking === true\n );\n}\n"],"mappings":";;;;AAgEA,MAAM,qBAAqB,gBAAgB;AAC3C,MAAM,mBAAmB,gBAAgB;AACzC,MAAM,mBAAmB,QAAQ,IAC/B,iBACA,CAAC,MAAM,kBAAkB,EAAE,KAAK,EAAE,CACpC;AAEA,SAAgB,aAAa,QAAwB;CACnD,OAAO,mBAAmB;EACxB,GAAG;EACH,OAAO,UAAU,OAAO,SAAS,CAAC,CAAC;CACrC,CAAiB;AACnB;AAoBA,SAAgB,WAAW,MAAkB;CAC3C,OAAO,iBAAiB,aAAa,IAAI,CAAe;AAC1D;AAEA,SAAgB,aAAa,QAAwB;CACnD,OAAO,iBAAiB,aAAa,MAAM,CAAC;AAC9C;AAEA,SAAgB,aAAa,MAAkB;CAC7C,IAAI,KAAK,QACP,OAAO;EACL,GAAG;EACH,OAAO,SAAS;GACd,OAAO,KAAK,OAAQ,gBAAgB,SAAS,IAAI,CAAC;EACpD;CACF;CAEF,IAAI,gBAAgB,QAAQ,OAAQ,KAAa,eAAe,YAC9D,OAAO;EACL,GAAG;EACH,WAAW,SAAS;GAClB,OAAQ,KAAa,WAAW,gBAAgB,SAAS,IAAI,CAAC;EAChE;CACF;CAEF,OAAO;AACT;AAEA,SAAS,UAAU,OAAmD;CACpE,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,EAAE,KAAK,CAAC,MAAM,UAAU,CAAC,MAAM,aAAa,IAAI,CAAC,CAAC,CACxE;AACF;AAEA,SAAS,gBAAgB,SAAmC,MAAsC;CAChG,MAAM,YAAY,qBAAqB,IAAI;CAC3C,MAAM,gBAAgB,OAAO,OAC3B,8BAA8B,SAAS;EACrC,OAAO;EACP,gBAAgB;CAClB,CAAC,CACH;CACA,MAAM,sBAAsB,QAAQ;CACpC,MAAM,kBAAkB,OAAO,OAAO;EACpC,GAAG;EACH;CACF,CAAC;CACD,OAAO,OAAO,OAAO,SAA0B;EAC7C,iBAAiB;GACf,cAAc;GACd,YAAY;GACZ,MAAM;IACJ,OAAO;GACT;EACF;EACA,eAAe;GACb,cAAc;GACd,YAAY;GACZ,MAAM;IACJ,OAAO;GACT;EACF;EACA,gBAAgB;GACd,cAAc;GACd,YAAY;GACZ,MAAsB;IACpB,OAAO,kBAAkB,OAAO;GAClC;EACF;CACF,CAAC;AACH;AAEA,SAAS,qBAAqB,MAAqB;CACjD,OACG,KAAK,MAAsF,MACxF,yBAAyB;AAEjC"}
1
+ {"version":3,"file":"plugin.js","names":[],"sources":["../ts/plugin.ts"],"sourcesContent":["import * as oxlintPluginApi from \"@oxlint/plugins\";\nimport type {\n Context as OxlintContext,\n Diagnostic,\n Plugin as OxlintPlugin,\n Rule as OxlintRule,\n RuleMeta as OxlintRuleMeta,\n} from \"@oxlint/plugins\";\n\nimport { AST_NODE_TYPES } from \"./compat\";\nimport { resolveTypeAwareParserOptions } from \"./context\";\nimport type { ESTree as PublicESTree } from \"./index\";\nimport { getParserServices } from \"./parser_services\";\nimport type { ContextWithParserOptions, ParserServices } from \"./types\";\n\nexport type Plugin = Omit<OxlintPlugin, \"rules\"> & {\n readonly rules: Record<string, Rule>;\n} & Record<string, unknown>;\nexport type Rule = OxlintRule & Record<string, unknown>;\nexport type RuleDiagnostic<MessageId extends string = string> = Diagnostic & {\n readonly messageId?: MessageId | null | undefined;\n};\nexport type RuleContext<\n MessageId extends string = string,\n Options extends readonly unknown[] = readonly unknown[],\n> = Omit<ContextWithParserOptions, \"options\" | \"report\"> & {\n readonly options: Readonly<Options>;\n report(this: void, diagnostic: RuleDiagnostic<MessageId>): void;\n};\nexport type RuleDocs = {\n readonly description?: string;\n readonly recommended?: unknown;\n readonly url?: string;\n /**\n * Enables corsa-oxlint's type-aware parser service defaults for this rule.\n *\n * @default false\n */\n readonly requiresTypeChecking?: boolean;\n};\nexport type RuleMetaWithMessages<MessageId extends string = string> = Omit<\n OxlintRuleMeta,\n \"docs\" | \"messages\"\n> & {\n readonly docs?: RuleDocs;\n readonly messages?: Record<MessageId, string>;\n};\ntype CorsaAstNodeType = keyof typeof AST_NODE_TYPES;\ntype BivariantVisitorHandler<Handler extends (...args: any[]) => any> = {\n bivarianceHack(...args: Parameters<Handler>): ReturnType<Handler>;\n}[\"bivarianceHack\"];\ntype VisitorNode<Kind extends CorsaAstNodeType> = PublicESTree[Kind];\n\nexport type Visitor = {\n [Kind in CorsaAstNodeType]?: BivariantVisitorHandler<(node: VisitorNode<Kind>) => void>;\n} & {\n [Kind in CorsaAstNodeType as `${Kind}:exit`]?: BivariantVisitorHandler<\n (node: VisitorNode<Kind>) => void\n >;\n} & Record<string, BivariantVisitorHandler<(node: PublicESTree.Node) => void> | undefined>;\n\nexport type VisitorWithHooks = Visitor & {\n readonly before?: () => boolean | void;\n readonly after?: () => void;\n};\nexport type RuleDefinition<\n MessageId extends string = string,\n Options extends readonly unknown[] = readonly unknown[],\n> = Record<string, unknown> & {\n readonly defaultOptions?: Options;\n readonly meta?: RuleMetaWithMessages<MessageId>;\n} & (\n | {\n readonly create: (context: RuleContext<MessageId, Options>) => Visitor;\n readonly createOnce?: never;\n }\n | {\n readonly create?: (context: RuleContext<MessageId, Options>) => Visitor;\n readonly createOnce: (context: RuleContext<MessageId, Options>) => VisitorWithHooks;\n }\n );\n\nconst defineOxlintPlugin = oxlintPluginApi.definePlugin;\nconst defineOxlintRule = oxlintPluginApi.defineRule;\nconst baseCompatPlugin = Reflect.get(\n oxlintPluginApi as object,\n [\"es\", \"lintCompatPlugin\"].join(\"\"),\n) as typeof oxlintPluginApi.definePlugin;\n\nexport function definePlugin(plugin: Plugin): Plugin {\n return defineOxlintPlugin({\n ...plugin,\n rules: wrapRules(plugin.rules ?? {}),\n } as OxlintPlugin) as Plugin;\n}\n\n/**\n * Defines a single Oxlint rule with type-aware parser services.\n *\n * @example\n * ```ts\n * export default defineRule({\n * meta: { schema: [], messages: { demo: \"demo\" } },\n * create(context) {\n * const services = context.parserServices;\n * return {};\n * },\n * });\n * ```\n */\nexport function defineRule<\n MessageId extends string = string,\n const Options extends readonly unknown[] = readonly unknown[],\n>(rule: RuleDefinition<MessageId, Options>): Rule;\nexport function defineRule(rule: RuleDefinition): Rule {\n return defineOxlintRule(decorateRule(rule as unknown as Rule) as OxlintRule) as Rule;\n}\n\nexport function compatPlugin(plugin: Plugin): Plugin {\n return baseCompatPlugin(definePlugin(plugin)) as Plugin;\n}\n\nexport function decorateRule(rule: Rule): Rule {\n if (rule.create) {\n return {\n ...rule,\n create(context) {\n return rule.create!(decorateContext(context, rule));\n },\n } as Rule;\n }\n if (\"createOnce\" in rule && typeof (rule as any).createOnce === \"function\") {\n return {\n ...rule,\n createOnce(context) {\n return (rule as any).createOnce(decorateContext(context, rule));\n },\n } as Rule;\n }\n return rule;\n}\n\nfunction wrapRules(rules: Record<string, Rule>): Record<string, Rule> {\n return Object.fromEntries(\n Object.entries(rules).map(([name, rule]) => [name, decorateRule(rule)]),\n );\n}\n\nfunction decorateContext(context: ContextWithParserOptions, rule: Rule): ContextWithParserOptions {\n const typeAware = requiresTypeChecking(rule);\n const parserOptions = Object.freeze(\n resolveTypeAwareParserOptions(context, {\n corsa: typeAware,\n projectService: typeAware,\n }),\n );\n const baseLanguageOptions = context.languageOptions;\n const languageOptions = Object.freeze({\n ...baseLanguageOptions,\n parserOptions,\n });\n return Object.create(context as OxlintContext, {\n languageOptions: {\n configurable: true,\n enumerable: true,\n get() {\n return languageOptions;\n },\n },\n parserOptions: {\n configurable: true,\n enumerable: false,\n get() {\n return parserOptions;\n },\n },\n parserServices: {\n configurable: true,\n enumerable: false,\n get(): ParserServices {\n return getParserServices(context);\n },\n },\n }) as ContextWithParserOptions;\n}\n\nfunction requiresTypeChecking(rule: Rule): boolean {\n return (\n (rule.meta as { readonly docs?: { readonly requiresTypeChecking?: unknown } } | undefined)?.docs\n ?.requiresTypeChecking === true\n );\n}\n"],"mappings":";;;;AAkFA,MAAM,qBAAqB,gBAAgB;AAC3C,MAAM,mBAAmB,gBAAgB;AACzC,MAAM,mBAAmB,QAAQ,IAC/B,iBACA,CAAC,MAAM,kBAAkB,EAAE,KAAK,EAAE,CACpC;AAEA,SAAgB,aAAa,QAAwB;CACnD,OAAO,mBAAmB;EACxB,GAAG;EACH,OAAO,UAAU,OAAO,SAAS,CAAC,CAAC;CACrC,CAAiB;AACnB;AAoBA,SAAgB,WAAW,MAA4B;CACrD,OAAO,iBAAiB,aAAa,IAAuB,CAAe;AAC7E;AAEA,SAAgB,aAAa,QAAwB;CACnD,OAAO,iBAAiB,aAAa,MAAM,CAAC;AAC9C;AAEA,SAAgB,aAAa,MAAkB;CAC7C,IAAI,KAAK,QACP,OAAO;EACL,GAAG;EACH,OAAO,SAAS;GACd,OAAO,KAAK,OAAQ,gBAAgB,SAAS,IAAI,CAAC;EACpD;CACF;CAEF,IAAI,gBAAgB,QAAQ,OAAQ,KAAa,eAAe,YAC9D,OAAO;EACL,GAAG;EACH,WAAW,SAAS;GAClB,OAAQ,KAAa,WAAW,gBAAgB,SAAS,IAAI,CAAC;EAChE;CACF;CAEF,OAAO;AACT;AAEA,SAAS,UAAU,OAAmD;CACpE,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,EAAE,KAAK,CAAC,MAAM,UAAU,CAAC,MAAM,aAAa,IAAI,CAAC,CAAC,CACxE;AACF;AAEA,SAAS,gBAAgB,SAAmC,MAAsC;CAChG,MAAM,YAAY,qBAAqB,IAAI;CAC3C,MAAM,gBAAgB,OAAO,OAC3B,8BAA8B,SAAS;EACrC,OAAO;EACP,gBAAgB;CAClB,CAAC,CACH;CACA,MAAM,sBAAsB,QAAQ;CACpC,MAAM,kBAAkB,OAAO,OAAO;EACpC,GAAG;EACH;CACF,CAAC;CACD,OAAO,OAAO,OAAO,SAA0B;EAC7C,iBAAiB;GACf,cAAc;GACd,YAAY;GACZ,MAAM;IACJ,OAAO;GACT;EACF;EACA,eAAe;GACb,cAAc;GACd,YAAY;GACZ,MAAM;IACJ,OAAO;GACT;EACF;EACA,gBAAgB;GACd,cAAc;GACd,YAAY;GACZ,MAAsB;IACpB,OAAO,kBAAkB,OAAO;GAClC;EACF;CACF,CAAC;AACH;AAEA,SAAS,qBAAqB,MAAqB;CACjD,OACG,KAAK,MAAsF,MACxF,yBAAyB;AAEjC"}
@@ -45,7 +45,6 @@ var RuleTester = class {
45
45
  }
46
46
  run(ruleName, rule, tests) {
47
47
  const workspace = createWorkspace();
48
- writeWorkspaceConfig(workspace);
49
48
  const transformed = {
50
49
  valid: tests.valid.map((test, index) => prepareTestCase(workspace, test, this.#config, "valid", index)),
51
50
  invalid: tests.invalid.map((test, index) => prepareTestCase(workspace, test, this.#config, "invalid", index))
@@ -62,7 +61,7 @@ function prepareTestCase(workspace, test, config, group, index) {
62
61
  const caseWorkspace = resolve(workspace, `${group}-${index}`);
63
62
  const normalized = typeof test === "string" ? { code: test } : test;
64
63
  const filename = resolveCaseFilename(caseWorkspace, normalized.filename, "case.ts");
65
- const projectRoot = isAbsolute(normalized.filename ?? "") ? dirname(filename) : workspace;
64
+ const projectRoot = isAbsolute(normalized.filename ?? "") ? dirname(filename) : caseWorkspace;
66
65
  writeFixture(filename, normalized.code, projectRoot);
67
66
  const testerConfig = config;
68
67
  const baseSettings = testerConfig?.settings?.corsaOxlint;
@@ -113,9 +112,6 @@ function optionalDefaultCorsaExecutable(rootDir) {
113
112
  return;
114
113
  }
115
114
  }
116
- function writeWorkspaceConfig(workspace) {
117
- writeProjectConfig(resolve(workspace, "tsconfig.json"));
118
- }
119
115
  function writeFixture(filename, code, projectRoot) {
120
116
  mkdirSync(dirname(filename), { recursive: true });
121
117
  writeFileSync(filename, code);
@@ -1 +1 @@
1
- {"version":3,"file":"rule_tester.js","names":["OxlintRuleTester","#inner","#config"],"sources":["../ts/rule_tester.ts"],"sourcesContent":["import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\n\nimport { RuleTester as OxlintRuleTester } from \"oxlint/plugins-dev\";\n\nimport { defaultCorsaExecutable, mergeTypeAwareParserOptions } from \"./context\";\nimport { decorateRule } from \"./plugin\";\nimport type { CorsaOxlintSettings, TypeAwareParserOptions } from \"./types\";\n\ntype TesterConfig = import(\"oxlint/plugins-dev\").RuleTester.Config;\ntype TestCase = import(\"oxlint/plugins-dev\").RuleTester.ValidTestCase &\n Partial<import(\"oxlint/plugins-dev\").RuleTester.InvalidTestCase>;\ntype TestCases = import(\"oxlint/plugins-dev\").RuleTester.TestCases;\nexport type RuleTesterConfig = TesterConfig & {\n readonly settings?: {\n readonly corsaOxlint?: CorsaOxlintSettings;\n readonly [key: string]: unknown;\n };\n};\ntype TestLifecycleGlobal = typeof globalThis & {\n after?: (callback: () => void) => unknown;\n afterAll?: (callback: () => void) => unknown;\n};\n\nconst cleanupDirs = new Set<string>();\nlet cleanupInstalled = false;\n\nexport class RuleTester {\n /**\n * A thin Oxlint `RuleTester` wrapper that injects\n * `settings.corsaOxlint`\n * settings, temporary fixtures, and a default project service.\n *\n * @example\n * ```ts\n * const tester = new RuleTester();\n * tester.run(\"demo\", rule, {\n * valid: [{ code: \"const answer = 42;\" }],\n * invalid: [],\n * });\n * ```\n */\n static get describe() {\n return OxlintRuleTester.describe;\n }\n\n static set describe(value) {\n OxlintRuleTester.describe = value;\n }\n\n static get it() {\n return OxlintRuleTester.it;\n }\n\n static set it(value) {\n OxlintRuleTester.it = value;\n }\n\n static only(item: string | TestCase): TestCase {\n return OxlintRuleTester.only(item);\n }\n\n readonly #inner: OxlintRuleTester;\n readonly #config?: RuleTesterConfig;\n\n constructor(config?: RuleTesterConfig) {\n this.#config = config;\n this.#inner = new OxlintRuleTester(config);\n }\n\n run(ruleName: string, rule: Record<string, unknown>, tests: TestCases): void {\n const workspace = createWorkspace();\n writeWorkspaceConfig(workspace);\n const transformed = {\n valid: tests.valid.map((test, index) =>\n prepareTestCase(workspace, test, this.#config, \"valid\", index),\n ),\n invalid: tests.invalid.map((test, index) =>\n prepareTestCase(workspace, test, this.#config, \"invalid\", index),\n ),\n };\n this.#inner.run(ruleName, decorateRule(rule as never) as never, transformed as TestCases);\n }\n}\n\nfunction createWorkspace(): string {\n const root = process.env.CORSA_OXLINT_RULE_TESTER_TMPDIR ?? tmpdir();\n const workspace = mkdtempSync(join(root, \"corsa-oxlint-\"));\n registerCleanup(workspace);\n return workspace;\n}\n\nfunction prepareTestCase(\n workspace: string,\n test: string | TestCase,\n config: RuleTesterConfig | undefined,\n group: \"valid\" | \"invalid\",\n index: number,\n): TestCase {\n const caseWorkspace = resolve(workspace, `${group}-${index}`);\n const normalized = typeof test === \"string\" ? ({ code: test } as TestCase) : test;\n const filename = resolveCaseFilename(caseWorkspace, normalized.filename, \"case.ts\");\n const projectRoot = isAbsolute(normalized.filename ?? \"\") ? dirname(filename) : workspace;\n writeFixture(filename, normalized.code, projectRoot);\n const testerConfig = config;\n const baseSettings = testerConfig?.settings?.corsaOxlint;\n const caseSettings = (\n normalized.settings as {\n corsaOxlint?: CorsaOxlintSettings;\n }\n )?.corsaOxlint;\n const parserOptions = mergeTypeAwareParserOptions(\n mergeTypeAwareParserOptions(\n mergeTypeAwareParserOptions(\n mergeTypeAwareParserOptions(baseSettings, baseSettings?.parserOptions),\n mergeTypeAwareParserOptions(caseSettings, caseSettings?.parserOptions),\n ),\n {\n tsconfigRootDir: projectRoot,\n projectService: {\n allowDefaultProject: [\"*.ts\", \"*.tsx\", \"*.js\", \"*.jsx\"],\n },\n },\n ),\n mergeTypeAwareParserOptions(\n config?.languageOptions?.parserOptions as TypeAwareParserOptions | undefined,\n normalized.languageOptions?.parserOptions as TypeAwareParserOptions | undefined,\n ),\n );\n const parserOptionsWithRuntime = applyRuleTesterRuntimeDefaults(\n parserOptions,\n normalized,\n config,\n );\n return {\n ...normalized,\n filename,\n settings: {\n ...testerConfig?.settings,\n ...normalized.settings,\n corsaOxlint: {\n ...testerConfig?.settings?.corsaOxlint,\n ...(normalized.settings as { corsaOxlint?: CorsaOxlintSettings })?.corsaOxlint,\n parserOptions: parserOptionsWithRuntime,\n },\n } as never,\n languageOptions: {\n ...config?.languageOptions,\n ...normalized.languageOptions,\n parserOptions: {\n ...parserOptionsWithRuntime,\n } as never,\n },\n };\n}\n\nfunction resolveCaseFilename(\n caseWorkspace: string,\n filename: string | undefined,\n fallback: string,\n): string {\n if (!filename) {\n return resolve(caseWorkspace, fallback);\n }\n return isAbsolute(filename) ? filename : resolve(caseWorkspace, filename);\n}\n\nfunction applyRuleTesterRuntimeDefaults(\n parserOptions: TypeAwareParserOptions,\n test: TestCase,\n config: RuleTesterConfig | undefined,\n): TypeAwareParserOptions {\n if (parserOptions.corsa?.executable !== undefined) {\n return parserOptions;\n }\n const rootDir = resolve(test.cwd ?? config?.cwd ?? process.cwd());\n const executable = process.env.CORSA_EXECUTABLE ?? optionalDefaultCorsaExecutable(rootDir);\n if (!executable) {\n return parserOptions;\n }\n return mergeTypeAwareParserOptions(parserOptions, {\n corsa: {\n executable,\n },\n });\n}\n\nfunction optionalDefaultCorsaExecutable(rootDir: string): string | undefined {\n try {\n return defaultCorsaExecutable(rootDir);\n } catch {\n return undefined;\n }\n}\n\nfunction writeWorkspaceConfig(workspace: string): void {\n writeProjectConfig(resolve(workspace, \"tsconfig.json\"));\n}\n\nfunction writeFixture(filename: string, code: string, projectRoot: string): void {\n mkdirSync(dirname(filename), { recursive: true });\n writeFileSync(filename, code);\n writeProjectConfig(resolve(projectRoot, \"tsconfig.json\"));\n}\n\nfunction writeProjectConfig(configPath: string): void {\n mkdirSync(dirname(configPath), { recursive: true });\n writeFileSync(\n configPath,\n JSON.stringify(\n {\n compilerOptions: {\n module: \"esnext\",\n target: \"es2022\",\n strict: true,\n },\n include: [\"**/*\"],\n },\n null,\n 2,\n ),\n );\n}\n\nfunction registerCleanup(workspace: string): void {\n cleanupDirs.add(workspace);\n const afterAll = lifecycleCleanup();\n if (afterAll) {\n afterAll(() => cleanupWorkspace(workspace));\n }\n if (cleanupInstalled) {\n return;\n }\n cleanupInstalled = true;\n process.once(\"beforeExit\", cleanupAllWorkspaces);\n process.once(\"exit\", cleanupAllWorkspaces);\n}\n\nfunction lifecycleCleanup(): ((callback: () => void) => unknown) | undefined {\n const testGlobal = globalThis as TestLifecycleGlobal;\n return typeof testGlobal.afterAll === \"function\"\n ? testGlobal.afterAll\n : typeof testGlobal.after === \"function\"\n ? testGlobal.after\n : undefined;\n}\n\nfunction cleanupWorkspace(workspace: string): void {\n if (!cleanupDirs.delete(workspace)) {\n return;\n }\n rmSync(workspace, { force: true, recursive: true });\n}\n\nfunction cleanupAllWorkspaces(): void {\n for (const dir of cleanupDirs) {\n cleanupWorkspace(dir);\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"rule_tester.js","names":["OxlintRuleTester","#inner","#config"],"sources":["../ts/rule_tester.ts"],"sourcesContent":["import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, isAbsolute, join, resolve } from \"node:path\";\n\nimport { RuleTester as OxlintRuleTester } from \"oxlint/plugins-dev\";\n\nimport { defaultCorsaExecutable, mergeTypeAwareParserOptions } from \"./context\";\nimport { decorateRule } from \"./plugin\";\nimport type { CorsaOxlintSettings, TypeAwareParserOptions } from \"./types\";\n\ntype TesterConfig = import(\"oxlint/plugins-dev\").RuleTester.Config;\ntype TestCase = import(\"oxlint/plugins-dev\").RuleTester.ValidTestCase &\n Partial<import(\"oxlint/plugins-dev\").RuleTester.InvalidTestCase>;\ntype TestCases = import(\"oxlint/plugins-dev\").RuleTester.TestCases;\nexport type RuleTesterConfig = TesterConfig & {\n readonly settings?: {\n readonly corsaOxlint?: CorsaOxlintSettings;\n readonly [key: string]: unknown;\n };\n};\ntype TestLifecycleGlobal = typeof globalThis & {\n after?: (callback: () => void) => unknown;\n afterAll?: (callback: () => void) => unknown;\n};\n\nconst cleanupDirs = new Set<string>();\nlet cleanupInstalled = false;\n\nexport class RuleTester {\n /**\n * A thin Oxlint `RuleTester` wrapper that injects\n * `settings.corsaOxlint`\n * settings, temporary fixtures, and a default project service.\n *\n * @example\n * ```ts\n * const tester = new RuleTester();\n * tester.run(\"demo\", rule, {\n * valid: [{ code: \"const answer = 42;\" }],\n * invalid: [],\n * });\n * ```\n */\n static get describe() {\n return OxlintRuleTester.describe;\n }\n\n static set describe(value) {\n OxlintRuleTester.describe = value;\n }\n\n static get it() {\n return OxlintRuleTester.it;\n }\n\n static set it(value) {\n OxlintRuleTester.it = value;\n }\n\n static only(item: string | TestCase): TestCase {\n return OxlintRuleTester.only(item);\n }\n\n readonly #inner: OxlintRuleTester;\n readonly #config?: RuleTesterConfig;\n\n constructor(config?: RuleTesterConfig) {\n this.#config = config;\n this.#inner = new OxlintRuleTester(config);\n }\n\n run(ruleName: string, rule: Record<string, unknown>, tests: TestCases): void {\n const workspace = createWorkspace();\n const transformed = {\n valid: tests.valid.map((test, index) =>\n prepareTestCase(workspace, test, this.#config, \"valid\", index),\n ),\n invalid: tests.invalid.map((test, index) =>\n prepareTestCase(workspace, test, this.#config, \"invalid\", index),\n ),\n };\n this.#inner.run(ruleName, decorateRule(rule as never) as never, transformed as TestCases);\n }\n}\n\nfunction createWorkspace(): string {\n const root = process.env.CORSA_OXLINT_RULE_TESTER_TMPDIR ?? tmpdir();\n const workspace = mkdtempSync(join(root, \"corsa-oxlint-\"));\n registerCleanup(workspace);\n return workspace;\n}\n\nfunction prepareTestCase(\n workspace: string,\n test: string | TestCase,\n config: RuleTesterConfig | undefined,\n group: \"valid\" | \"invalid\",\n index: number,\n): TestCase {\n const caseWorkspace = resolve(workspace, `${group}-${index}`);\n const normalized = typeof test === \"string\" ? ({ code: test } as TestCase) : test;\n const filename = resolveCaseFilename(caseWorkspace, normalized.filename, \"case.ts\");\n const projectRoot = isAbsolute(normalized.filename ?? \"\") ? dirname(filename) : caseWorkspace;\n writeFixture(filename, normalized.code, projectRoot);\n const testerConfig = config;\n const baseSettings = testerConfig?.settings?.corsaOxlint;\n const caseSettings = (\n normalized.settings as {\n corsaOxlint?: CorsaOxlintSettings;\n }\n )?.corsaOxlint;\n const parserOptions = mergeTypeAwareParserOptions(\n mergeTypeAwareParserOptions(\n mergeTypeAwareParserOptions(\n mergeTypeAwareParserOptions(baseSettings, baseSettings?.parserOptions),\n mergeTypeAwareParserOptions(caseSettings, caseSettings?.parserOptions),\n ),\n {\n tsconfigRootDir: projectRoot,\n projectService: {\n allowDefaultProject: [\"*.ts\", \"*.tsx\", \"*.js\", \"*.jsx\"],\n },\n },\n ),\n mergeTypeAwareParserOptions(\n config?.languageOptions?.parserOptions as TypeAwareParserOptions | undefined,\n normalized.languageOptions?.parserOptions as TypeAwareParserOptions | undefined,\n ),\n );\n const parserOptionsWithRuntime = applyRuleTesterRuntimeDefaults(\n parserOptions,\n normalized,\n config,\n );\n return {\n ...normalized,\n filename,\n settings: {\n ...testerConfig?.settings,\n ...normalized.settings,\n corsaOxlint: {\n ...testerConfig?.settings?.corsaOxlint,\n ...(normalized.settings as { corsaOxlint?: CorsaOxlintSettings })?.corsaOxlint,\n parserOptions: parserOptionsWithRuntime,\n },\n } as never,\n languageOptions: {\n ...config?.languageOptions,\n ...normalized.languageOptions,\n parserOptions: {\n ...parserOptionsWithRuntime,\n } as never,\n },\n };\n}\n\nfunction resolveCaseFilename(\n caseWorkspace: string,\n filename: string | undefined,\n fallback: string,\n): string {\n if (!filename) {\n return resolve(caseWorkspace, fallback);\n }\n return isAbsolute(filename) ? filename : resolve(caseWorkspace, filename);\n}\n\nfunction applyRuleTesterRuntimeDefaults(\n parserOptions: TypeAwareParserOptions,\n test: TestCase,\n config: RuleTesterConfig | undefined,\n): TypeAwareParserOptions {\n if (parserOptions.corsa?.executable !== undefined) {\n return parserOptions;\n }\n const rootDir = resolve(test.cwd ?? config?.cwd ?? process.cwd());\n const executable = process.env.CORSA_EXECUTABLE ?? optionalDefaultCorsaExecutable(rootDir);\n if (!executable) {\n return parserOptions;\n }\n return mergeTypeAwareParserOptions(parserOptions, {\n corsa: {\n executable,\n },\n });\n}\n\nfunction optionalDefaultCorsaExecutable(rootDir: string): string | undefined {\n try {\n return defaultCorsaExecutable(rootDir);\n } catch {\n return undefined;\n }\n}\n\nfunction writeFixture(filename: string, code: string, projectRoot: string): void {\n mkdirSync(dirname(filename), { recursive: true });\n writeFileSync(filename, code);\n writeProjectConfig(resolve(projectRoot, \"tsconfig.json\"));\n}\n\nfunction writeProjectConfig(configPath: string): void {\n mkdirSync(dirname(configPath), { recursive: true });\n writeFileSync(\n configPath,\n JSON.stringify(\n {\n compilerOptions: {\n module: \"esnext\",\n target: \"es2022\",\n strict: true,\n },\n include: [\"**/*\"],\n },\n null,\n 2,\n ),\n );\n}\n\nfunction registerCleanup(workspace: string): void {\n cleanupDirs.add(workspace);\n const afterAll = lifecycleCleanup();\n if (afterAll) {\n afterAll(() => cleanupWorkspace(workspace));\n }\n if (cleanupInstalled) {\n return;\n }\n cleanupInstalled = true;\n process.once(\"beforeExit\", cleanupAllWorkspaces);\n process.once(\"exit\", cleanupAllWorkspaces);\n}\n\nfunction lifecycleCleanup(): ((callback: () => void) => unknown) | undefined {\n const testGlobal = globalThis as TestLifecycleGlobal;\n return typeof testGlobal.afterAll === \"function\"\n ? testGlobal.afterAll\n : typeof testGlobal.after === \"function\"\n ? testGlobal.after\n : undefined;\n}\n\nfunction cleanupWorkspace(workspace: string): void {\n if (!cleanupDirs.delete(workspace)) {\n return;\n }\n rmSync(workspace, { force: true, recursive: true });\n}\n\nfunction cleanupAllWorkspaces(): void {\n for (const dir of cleanupDirs) {\n cleanupWorkspace(dir);\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "corsa-oxlint",
3
- "version": "0.47.1",
3
+ "version": "0.49.0",
4
4
  "description": "Type-aware Oxlint helpers powered by Corsa",
5
5
  "homepage": "https://github.com/ubugeeei-prod/corsa-bind/tree/main/src/bindings/nodejs/corsa_oxlint",
6
6
  "bugs": {
@@ -101,7 +101,7 @@
101
101
  "./package.json": "./package.json"
102
102
  },
103
103
  "dependencies": {
104
- "@corsa-bind/napi": "0.47.1"
104
+ "@corsa-bind/napi": "0.49.0"
105
105
  },
106
106
  "devDependencies": {
107
107
  "@oxlint/plugins": "1.69.0",