@flint.fyi/ts 0.19.1 → 0.20.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 (36) hide show
  1. package/lib/plugin.d.ts +30 -0
  2. package/lib/plugin.js +4 -0
  3. package/lib/rules/asyncFunctionAwaits.d.ts +10 -0
  4. package/lib/rules/asyncFunctionAwaits.js +55 -0
  5. package/lib/rules/classAssignments.js +4 -7
  6. package/lib/rules/constantAssignments.js +4 -7
  7. package/lib/rules/exceptionAssignments.js +4 -7
  8. package/lib/rules/functionAssignments.js +4 -7
  9. package/lib/rules/nullishCoalescingOperators.js +22 -10
  10. package/lib/rules/parameterReassignments.js +18 -49
  11. package/lib/rules/restrictedImports.d.ts +27 -0
  12. package/lib/rules/restrictedImports.js +166 -0
  13. package/lib/rules/typeImports.js +1 -2
  14. package/lib/rules/unassignedVariables.js +2 -5
  15. package/lib/rules/utils/collectAccessorPairs.d.ts +1 -1
  16. package/lib/rules/utils/discriminateAnyType.d.ts +1 -1
  17. package/lib/rules/utils/equalityOperators.d.ts +1 -1
  18. package/lib/rules/utils/formatReportedType.d.ts +1 -1
  19. package/lib/rules/utils/getRegExpConstruction.d.ts +1 -1
  20. package/lib/rules/utils/isDirectEqualityCheck.d.ts +1 -1
  21. package/lib/rules/utils/isUnsafeAssignment.d.ts +1 -1
  22. package/lib/type-utils/getSpecifierNames.d.ts +3 -0
  23. package/lib/type-utils/getSpecifierNames.js +9 -0
  24. package/lib/type-utils/isDeclaredInModuleBlock.d.ts +3 -0
  25. package/lib/type-utils/isDeclaredInModuleBlock.js +14 -0
  26. package/lib/type-utils/isFromFile.d.ts +3 -0
  27. package/lib/type-utils/isFromFile.js +13 -0
  28. package/lib/type-utils/isFromPackage.d.ts +3 -0
  29. package/lib/type-utils/isFromPackage.js +16 -0
  30. package/lib/type-utils/matchesSpecifier.d.ts +4 -0
  31. package/lib/type-utils/matchesSpecifier.js +20 -0
  32. package/lib/type-utils/schemas.d.ts +15 -0
  33. package/lib/type-utils/schemas.js +25 -0
  34. package/package.json +6 -6
  35. package/lib/rules/utils/forEachChild.d.ts +0 -4
  36. package/lib/rules/utils/forEachChild.js +0 -7
package/lib/plugin.d.ts CHANGED
@@ -255,6 +255,13 @@ declare const ts: _flint_fyi_core0.Plugin<_flint_fyi_core0.RuleAbout<string>, "j
255
255
  readonly pluginId: string;
256
256
  readonly url: string;
257
257
  }, "preferShorthand", undefined>, _flint_fyi_core0.Rule<{
258
+ readonly description: "Reports async functions that do not use await.";
259
+ readonly id: "asyncFunctionAwaits";
260
+ readonly presets: readonly ["logicalStrict"];
261
+ } & {
262
+ readonly pluginId: string;
263
+ readonly url: string;
264
+ }, "missingAwait", undefined>, _flint_fyi_core0.Rule<{
258
265
  readonly description: "Reports using async functions as Promise executor functions.";
259
266
  readonly id: "asyncPromiseExecutors";
260
267
  readonly presets: readonly ["logical", "logicalStrict"];
@@ -1816,6 +1823,29 @@ declare const ts: _flint_fyi_core0.Plugin<_flint_fyi_core0.RuleAbout<string>, "j
1816
1823
  readonly url: string;
1817
1824
  }, "restricted", {
1818
1825
  deny: zod_v40.ZodOptional<zod_v40.ZodArray<zod_v40.ZodString>>;
1826
+ }>, _flint_fyi_core0.Rule<{
1827
+ readonly description: "Restricts specified modules from being imported.";
1828
+ readonly id: "restrictedImports";
1829
+ } & {
1830
+ readonly pluginId: string;
1831
+ readonly url: string;
1832
+ }, "restricted" | "moduleRestricted" | "moduleRestrictedWithMessage" | "namespaceRestricted" | "namespaceRestrictedWithMessage" | "restrictedWithMessage", {
1833
+ restrictions: zod_v40.ZodDefault<zod_v40.ZodArray<zod_v40.ZodObject<{
1834
+ allowTypeImports: zod_v40.ZodOptional<zod_v40.ZodBoolean>;
1835
+ message: zod_v40.ZodOptional<zod_v40.ZodString>;
1836
+ specifier: zod_v40.ZodUnion<readonly [zod_v40.ZodObject<{
1837
+ from: zod_v40.ZodLiteral<"file">;
1838
+ name: zod_v40.ZodOptional<zod_v40.ZodUnion<readonly [zod_v40.ZodString, zod_v40.ZodArray<zod_v40.ZodString>]>>;
1839
+ path: zod_v40.ZodOptional<zod_v40.ZodString>;
1840
+ }, zod_v4_core0.$strip>, zod_v40.ZodObject<{
1841
+ from: zod_v40.ZodLiteral<"lib">;
1842
+ name: zod_v40.ZodOptional<zod_v40.ZodUnion<readonly [zod_v40.ZodString, zod_v40.ZodArray<zod_v40.ZodString>]>>;
1843
+ }, zod_v4_core0.$strip>, zod_v40.ZodObject<{
1844
+ from: zod_v40.ZodLiteral<"package">;
1845
+ name: zod_v40.ZodOptional<zod_v40.ZodUnion<readonly [zod_v40.ZodString, zod_v40.ZodArray<zod_v40.ZodString>]>>;
1846
+ package: zod_v40.ZodString;
1847
+ }, zod_v4_core0.$strip>]>;
1848
+ }, zod_v4_core0.$strip>>>;
1819
1849
  }>, _flint_fyi_core0.Rule<{
1820
1850
  readonly description: "Reports using assignment expressions in return statements.";
1821
1851
  readonly id: "returnAssignments";
package/lib/plugin.js CHANGED
@@ -33,6 +33,7 @@ import arrayTypes_default from "./rules/arrayTypes.js";
33
33
  import arrayUnnecessaryLengthChecks_default from "./rules/arrayUnnecessaryLengthChecks.js";
34
34
  import asConstAssertions_default from "./rules/asConstAssertions.js";
35
35
  import assignmentOperatorShorthands_default from "./rules/assignmentOperatorShorthands.js";
36
+ import asyncFunctionAwaits_default from "./rules/asyncFunctionAwaits.js";
36
37
  import asyncPromiseExecutors_default from "./rules/asyncPromiseExecutors.js";
37
38
  import asyncUnnecessaryPromiseWrappers_default from "./rules/asyncUnnecessaryPromiseWrappers.js";
38
39
  import atAccesses_default from "./rules/atAccesses.js";
@@ -248,6 +249,7 @@ import regexZeroQuantifiers_default from "./rules/regexZeroQuantifiers.js";
248
249
  import requireImports_default from "./rules/requireImports.js";
249
250
  import responseJsonMethods_default from "./rules/responseJsonMethods.js";
250
251
  import restrictedIdentifiers_default from "./rules/restrictedIdentifiers.js";
252
+ import restrictedImports_default from "./rules/restrictedImports.js";
251
253
  import returnAssignments_default from "./rules/returnAssignments.js";
252
254
  import returnThisTypes_default from "./rules/returnThisTypes.js";
253
255
  import selfAssignments_default from "./rules/selfAssignments.js";
@@ -344,6 +346,7 @@ const ts = createPlugin({
344
346
  arrayUnnecessaryLengthChecks_default,
345
347
  asConstAssertions_default,
346
348
  assignmentOperatorShorthands_default,
349
+ asyncFunctionAwaits_default,
347
350
  asyncPromiseExecutors_default,
348
351
  asyncUnnecessaryPromiseWrappers_default,
349
352
  atAccesses_default,
@@ -560,6 +563,7 @@ const ts = createPlugin({
560
563
  requireImports_default,
561
564
  responseJsonMethods_default,
562
565
  restrictedIdentifiers_default,
566
+ restrictedImports_default,
563
567
  returnAssignments_default,
564
568
  returnThisTypes_default,
565
569
  selfAssignments_default,
@@ -0,0 +1,10 @@
1
+ declare const _default: import("@flint.fyi/core").Rule<{
2
+ readonly description: "Reports async functions that do not use await.";
3
+ readonly id: "asyncFunctionAwaits";
4
+ readonly presets: readonly ["logicalStrict"];
5
+ } & {
6
+ readonly pluginId: string;
7
+ readonly url: string;
8
+ }, "missingAwait", undefined>;
9
+ export default _default;
10
+ //# sourceMappingURL=asyncFunctionAwaits.d.ts.map
@@ -0,0 +1,55 @@
1
+ import { ruleCreator } from "./ruleCreator.js";
2
+ import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
3
+ import * as ts$1 from "typescript";
4
+ import * as tsutils from "ts-api-utils";
5
+ //#region src/rules/asyncFunctionAwaits.ts
6
+ var asyncFunctionAwaits_default = ruleCreator.createRule(typescriptLanguage, {
7
+ about: {
8
+ description: "Reports async functions that do not use await.",
9
+ id: "asyncFunctionAwaits",
10
+ presets: ["logicalStrict"]
11
+ },
12
+ messages: { missingAwait: {
13
+ primary: "This function is marked `async` but does not contain an `await` expression or return a Promise.",
14
+ secondary: ["Async functions always wrap their return value in a Promise, which adds overhead if you're not using await.", "This may indicate incomplete implementation or leftover code after refactoring."],
15
+ suggestions: ["Add an `await` expression if you need to wait for asynchronous operations.", "Remove the `async` keyword if the function doesn't need to be asynchronous."]
16
+ } },
17
+ setup(context) {
18
+ function checkFunction(node, { sourceFile, typeChecker }) {
19
+ const asyncModifier = node.modifiers?.find((modifier) => modifier.kind === ts$1.SyntaxKind.AsyncKeyword);
20
+ if (!asyncModifier || node.asteriskToken || !node.body || isEmptyBody(node.body) || checkForAwait(node.body) || bodyReturnsThenable(node.body, typeChecker)) return;
21
+ context.report({
22
+ message: "missingAwait",
23
+ range: getTSNodeRange(asyncModifier, sourceFile)
24
+ });
25
+ }
26
+ return { visitors: {
27
+ ArrowFunction: checkFunction,
28
+ FunctionDeclaration: checkFunction,
29
+ FunctionExpression: checkFunction,
30
+ MethodDeclaration: checkFunction
31
+ } };
32
+ }
33
+ });
34
+ function bodyReturnsThenable(body, typeChecker) {
35
+ if (!ts$1.isBlock(body)) return tsutils.isThenableType(typeChecker, body);
36
+ function checkReturnStatements(node) {
37
+ if (ts$1.isReturnStatement(node) && node.expression && tsutils.isThenableType(typeChecker, node.expression)) return true;
38
+ if (tsutils.isFunctionScopeBoundary(node)) return false;
39
+ return ts$1.forEachChild(node, checkReturnStatements);
40
+ }
41
+ return ts$1.forEachChild(body, checkReturnStatements);
42
+ }
43
+ function checkForAwait(node) {
44
+ if (ts$1.isAwaitExpression(node)) return true;
45
+ if (ts$1.isForOfStatement(node) && node.awaitModifier) return true;
46
+ if (tsutils.isFunctionScopeBoundary(node)) return false;
47
+ return ts$1.forEachChild(node, checkForAwait);
48
+ }
49
+ function isEmptyBody(body) {
50
+ return ts$1.isBlock(body) && !body.statements.length;
51
+ }
52
+ //#endregion
53
+ export { asyncFunctionAwaits_default as default };
54
+
55
+ //# sourceMappingURL=asyncFunctionAwaits.js.map
@@ -1,5 +1,5 @@
1
1
  import { ruleCreator } from "./ruleCreator.js";
2
- import { getModifyingReferences, typescriptLanguage } from "@flint.fyi/typescript-language";
2
+ import { getModifyingReferences, getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
3
3
  //#region src/rules/classAssignments.ts
4
4
  var classAssignments_default = ruleCreator.createRule(typescriptLanguage, {
5
5
  about: {
@@ -13,15 +13,12 @@ var classAssignments_default = ruleCreator.createRule(typescriptLanguage, {
13
13
  suggestions: ["Use a different variable name if you need to store a modified reference to a class."]
14
14
  } },
15
15
  setup(context) {
16
- return { visitors: { ClassDeclaration: (node, { sourceFile, typeChecker }) => {
16
+ return { visitors: { ClassDeclaration: (node, { sourceFile }) => {
17
17
  if (!node.name) return;
18
- const modifyingReferences = getModifyingReferences(node.name, sourceFile, typeChecker);
18
+ const modifyingReferences = getModifyingReferences(node.name, sourceFile);
19
19
  for (const reference of modifyingReferences) context.report({
20
20
  message: "noClassAssign",
21
- range: {
22
- begin: reference.getStart(sourceFile),
23
- end: reference.getEnd()
24
- }
21
+ range: getTSNodeRange(reference, sourceFile)
25
22
  });
26
23
  } } };
27
24
  }
@@ -1,5 +1,5 @@
1
1
  import { ruleCreator } from "./ruleCreator.js";
2
- import { getModifyingReferences, typescriptLanguage } from "@flint.fyi/typescript-language";
2
+ import { getModifyingReferences, getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
3
3
  import ts, { SyntaxKind } from "typescript";
4
4
  //#region src/rules/constantAssignments.ts
5
5
  var constantAssignments_default = ruleCreator.createRule(typescriptLanguage, {
@@ -20,18 +20,15 @@ var constantAssignments_default = ruleCreator.createRule(typescriptLanguage, {
20
20
  for (const element of name.elements) if (element.kind === SyntaxKind.BindingElement) identifiers.push(...collectBindingElements(element.name));
21
21
  return identifiers;
22
22
  }
23
- return { visitors: { VariableDeclarationList: (node, { sourceFile, typeChecker }) => {
23
+ return { visitors: { VariableDeclarationList: (node, { sourceFile }) => {
24
24
  if (!(node.flags & ts.NodeFlags.Const) || !node.declarations.length) return;
25
25
  for (const declaration of node.declarations) {
26
26
  const identifiers = collectBindingElements(declaration.name);
27
27
  for (const identifier of identifiers) {
28
- const modifyingReferences = getModifyingReferences(identifier, sourceFile, typeChecker);
28
+ const modifyingReferences = getModifyingReferences(identifier, sourceFile);
29
29
  for (const reference of modifyingReferences) context.report({
30
30
  message: "noConstAssign",
31
- range: {
32
- begin: reference.getStart(sourceFile),
33
- end: reference.getEnd()
34
- }
31
+ range: getTSNodeRange(reference, sourceFile)
35
32
  });
36
33
  }
37
34
  }
@@ -1,5 +1,5 @@
1
1
  import { ruleCreator } from "./ruleCreator.js";
2
- import { getModifyingReferences, typescriptLanguage } from "@flint.fyi/typescript-language";
2
+ import { getModifyingReferences, getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
3
3
  import { SyntaxKind } from "typescript";
4
4
  //#region src/rules/exceptionAssignments.ts
5
5
  var exceptionAssignments_default = ruleCreator.createRule(typescriptLanguage, {
@@ -20,17 +20,14 @@ var exceptionAssignments_default = ruleCreator.createRule(typescriptLanguage, {
20
20
  else for (const element of name.elements) if (element.kind === SyntaxKind.BindingElement) identifiers.push(...collectBindingElements(element.name));
21
21
  return identifiers;
22
22
  }
23
- return { visitors: { CatchClause: (node, { sourceFile, typeChecker }) => {
23
+ return { visitors: { CatchClause: (node, { sourceFile }) => {
24
24
  if (!node.variableDeclaration?.name) return;
25
25
  const identifiers = collectBindingElements(node.variableDeclaration.name);
26
26
  for (const identifier of identifiers) {
27
- const modifyingReferences = getModifyingReferences(identifier, sourceFile, typeChecker);
27
+ const modifyingReferences = getModifyingReferences(identifier, sourceFile);
28
28
  for (const reference of modifyingReferences) context.report({
29
29
  message: "noExAssign",
30
- range: {
31
- begin: reference.getStart(sourceFile),
32
- end: reference.getEnd()
33
- }
30
+ range: getTSNodeRange(reference, sourceFile)
34
31
  });
35
32
  }
36
33
  } } };
@@ -1,5 +1,5 @@
1
1
  import { ruleCreator } from "./ruleCreator.js";
2
- import { getModifyingReferences, typescriptLanguage } from "@flint.fyi/typescript-language";
2
+ import { getModifyingReferences, getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
3
3
  //#region src/rules/functionAssignments.ts
4
4
  var functionAssignments_default = ruleCreator.createRule(typescriptLanguage, {
5
5
  about: {
@@ -13,15 +13,12 @@ var functionAssignments_default = ruleCreator.createRule(typescriptLanguage, {
13
13
  suggestions: ["Use a function expression or const variable instead if you need to reassign the function."]
14
14
  } },
15
15
  setup(context) {
16
- return { visitors: { FunctionDeclaration: (node, { sourceFile, typeChecker }) => {
16
+ return { visitors: { FunctionDeclaration: (node, { sourceFile }) => {
17
17
  if (!node.name) return;
18
- const modifyingReferences = getModifyingReferences(node.name, sourceFile, typeChecker);
18
+ const modifyingReferences = getModifyingReferences(node.name, sourceFile);
19
19
  for (const reference of modifyingReferences) context.report({
20
20
  message: "noFunctionAssignment",
21
- range: {
22
- begin: reference.getStart(sourceFile),
23
- end: reference.getEnd()
24
- }
21
+ range: getTSNodeRange(reference, sourceFile)
25
22
  });
26
23
  } } };
27
24
  }
@@ -111,6 +111,17 @@ function getComparisonOperator(node) {
111
111
  default: return "";
112
112
  }
113
113
  }
114
+ function getIfStatementNullishCheckValue(node) {
115
+ switch (node.expression.kind) {
116
+ case ts.SyntaxKind.BinaryExpression:
117
+ if (isNullLikeComparison(node.expression)) return extractValueFromComparison(node.expression).value ?? void 0;
118
+ return;
119
+ case ts.SyntaxKind.PrefixUnaryExpression:
120
+ if (node.expression.operator === ts.SyntaxKind.ExclamationToken) return node.expression.operand;
121
+ return;
122
+ default: return;
123
+ }
124
+ }
114
125
  function getTypeFlags(type) {
115
126
  let flags = 0;
116
127
  for (const constituent of tsutils.unionConstituents(type)) for (const subConstituent of tsutils.intersectionConstituents(constituent)) flags |= subConstituent.getFlags();
@@ -143,13 +154,6 @@ function isFalsyLiteralType(part) {
143
154
  }
144
155
  return false;
145
156
  }
146
- function isIfStatementNullishCheck(node) {
147
- switch (node.expression.kind) {
148
- case ts.SyntaxKind.BinaryExpression: return isNullLikeComparison(node.expression);
149
- case ts.SyntaxKind.PrefixUnaryExpression: return node.expression.operator === ts.SyntaxKind.ExclamationToken;
150
- default: return false;
151
- }
152
- }
153
157
  function isMixedLogicalExpression(node) {
154
158
  const seen = /* @__PURE__ */ new Set();
155
159
  const queue = [
@@ -266,6 +270,13 @@ var nullishCoalescingOperators_default = ruleCreator.createRule(typescriptLangua
266
270
  text: `${leftText} ?? ${getText(alternate)}`
267
271
  };
268
272
  }
273
+ function createNullishAssignmentFix(left, right, sourceFile, range) {
274
+ const getText = (node) => sourceFile.text.substring(node.getStart(sourceFile), node.getEnd());
275
+ return {
276
+ range,
277
+ text: `${getText(left)} ??= ${getText(right)};`
278
+ };
279
+ }
269
280
  return { visitors: {
270
281
  BinaryExpression: (node, { options, sourceFile, typeChecker }) => {
271
282
  if (options.ignoreConditionalTests && isConditionalTest(node) || options.ignoreMixedLogicalExpressions && isMixedLogicalExpression(node) || ![ts.SyntaxKind.BarBarEqualsToken, ts.SyntaxKind.BarBarToken].includes(node.operatorToken.kind) || shouldIgnoreNode(node.left, options.ignorePrimitives, typeChecker)) return;
@@ -292,12 +303,13 @@ var nullishCoalescingOperators_default = ruleCreator.createRule(typescriptLangua
292
303
  });
293
304
  },
294
305
  IfStatement: (node, { options, sourceFile, typeChecker }) => {
295
- if (options.ignoreIfStatements || node.elseStatement || !isIfStatementNullishCheck(node)) return;
306
+ const checkedValue = getIfStatementNullishCheckValue(node);
307
+ if (options.ignoreIfStatements || node.elseStatement || !checkedValue) return;
296
308
  const assignmentExpression = extractAssignmentFromIfStatement(node);
297
- if (!assignmentExpression || shouldIgnoreNode(assignmentExpression.left, options.ignorePrimitives, typeChecker)) return;
309
+ if (!assignmentExpression || !hasSameTokens(assignmentExpression.left, checkedValue, sourceFile) || shouldIgnoreNode(assignmentExpression.left, options.ignorePrimitives, typeChecker)) return;
298
310
  const range = getTSNodeRange(node, sourceFile);
299
311
  context.report({
300
- fix: createNullishNodesFix(assignmentExpression.left, assignmentExpression.right, sourceFile, range),
312
+ fix: createNullishAssignmentFix(assignmentExpression.left, assignmentExpression.right, sourceFile, range),
301
313
  message: "preferNullishAssignment",
302
314
  range
303
315
  });
@@ -1,20 +1,6 @@
1
1
  import { ruleCreator } from "./ruleCreator.js";
2
- import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
3
- import ts from "typescript";
2
+ import { getScopeManager, getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
4
3
  //#region src/rules/parameterReassignments.ts
5
- function collectParameterNames(parameters) {
6
- const names = /* @__PURE__ */ new Set();
7
- const collectNames = (node) => {
8
- if (node.kind === ts.SyntaxKind.Identifier) names.add(node.text);
9
- else if (ts.isObjectBindingPattern(node)) {
10
- for (const element of node.elements) if (!ts.isOmittedExpression(element)) collectNames(element.name);
11
- } else if (ts.isArrayBindingPattern(node)) {
12
- for (const element of node.elements) if (!ts.isOmittedExpression(element)) collectNames(element.name);
13
- }
14
- };
15
- for (const parameter of parameters) collectNames(parameter.name);
16
- return names;
17
- }
18
4
  var parameterReassignments_default = ruleCreator.createRule(typescriptLanguage, {
19
5
  about: {
20
6
  description: "Disallow reassignment of function parameters.",
@@ -31,42 +17,25 @@ var parameterReassignments_default = ruleCreator.createRule(typescriptLanguage,
31
17
  suggestions: ["Use a new variable if you need to modify the value.", "Use a different pattern such as a helper function."]
32
18
  } },
33
19
  setup(context) {
34
- const scopes = /* @__PURE__ */ new Map();
35
- const checkParameterAssignment = (name, node, sourceFile) => {
36
- let currentNode = node;
37
- while (currentNode !== void 0) {
38
- if (ts.isFunctionDeclaration(currentNode) || ts.isFunctionExpression(currentNode) || ts.isArrowFunction(currentNode)) {
39
- if (scopes.get(currentNode)?.has(name)) context.report({
40
- message: "parameterReassignment",
41
- range: getTSNodeRange(node, sourceFile)
42
- });
43
- return;
44
- }
45
- currentNode = currentNode.parent;
20
+ function reportParameterReassignments(node, { sourceFile }) {
21
+ const scopeManager = getScopeManager(sourceFile);
22
+ const parameterVariables = new Set(node.parameters.flatMap((parameter) => scopeManager.getDeclaredVariables(parameter)));
23
+ for (const reference of scopeManager.getReferencesInScope(node)) {
24
+ if (!reference.isWrite || !reference.variable || !parameterVariables.has(reference.variable)) continue;
25
+ context.report({
26
+ message: "parameterReassignment",
27
+ range: getTSNodeRange(reference.identifier, sourceFile)
28
+ });
46
29
  }
47
- };
48
- const handleUnaryExpression = (node, { sourceFile }) => {
49
- if ((node.operator === ts.SyntaxKind.PlusPlusToken || node.operator === ts.SyntaxKind.MinusMinusToken) && ts.isIdentifier(node.operand)) checkParameterAssignment(node.operand.text, node.operand, sourceFile);
50
- };
30
+ }
51
31
  return { visitors: {
52
- ArrowFunction: (node) => {
53
- scopes.set(node, collectParameterNames(node.parameters));
54
- },
55
- BinaryExpression: (node, { sourceFile }) => {
56
- if (!(node.operatorToken.kind === ts.SyntaxKind.EqualsToken || node.operatorToken.kind === ts.SyntaxKind.PlusEqualsToken || node.operatorToken.kind === ts.SyntaxKind.MinusEqualsToken || node.operatorToken.kind === ts.SyntaxKind.AsteriskEqualsToken || node.operatorToken.kind === ts.SyntaxKind.SlashEqualsToken) || !ts.isIdentifier(node.left)) return;
57
- checkParameterAssignment(node.left.text, node.left, sourceFile);
58
- },
59
- FunctionDeclaration: (node) => {
60
- scopes.set(node, collectParameterNames(node.parameters));
61
- },
62
- FunctionExpression: (node) => {
63
- scopes.set(node, collectParameterNames(node.parameters));
64
- },
65
- PostfixUnaryExpression: handleUnaryExpression,
66
- PrefixUnaryExpression: handleUnaryExpression,
67
- Program: () => {
68
- scopes.clear();
69
- }
32
+ ArrowFunction: reportParameterReassignments,
33
+ Constructor: reportParameterReassignments,
34
+ FunctionDeclaration: reportParameterReassignments,
35
+ FunctionExpression: reportParameterReassignments,
36
+ GetAccessor: reportParameterReassignments,
37
+ MethodDeclaration: reportParameterReassignments,
38
+ SetAccessor: reportParameterReassignments
70
39
  } };
71
40
  }
72
41
  });
@@ -0,0 +1,27 @@
1
+ import { z } from "zod/v4";
2
+ declare const _default: import("@flint.fyi/core").Rule<{
3
+ readonly description: "Restricts specified modules from being imported.";
4
+ readonly id: "restrictedImports";
5
+ } & {
6
+ readonly pluginId: string;
7
+ readonly url: string;
8
+ }, "restricted" | "moduleRestricted" | "moduleRestrictedWithMessage" | "namespaceRestricted" | "namespaceRestrictedWithMessage" | "restrictedWithMessage", {
9
+ restrictions: z.ZodDefault<z.ZodArray<z.ZodObject<{
10
+ allowTypeImports: z.ZodOptional<z.ZodBoolean>;
11
+ message: z.ZodOptional<z.ZodString>;
12
+ specifier: z.ZodUnion<readonly [z.ZodObject<{
13
+ from: z.ZodLiteral<"file">;
14
+ name: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
15
+ path: z.ZodOptional<z.ZodString>;
16
+ }, z.core.$strip>, z.ZodObject<{
17
+ from: z.ZodLiteral<"lib">;
18
+ name: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
19
+ }, z.core.$strip>, z.ZodObject<{
20
+ from: z.ZodLiteral<"package">;
21
+ name: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
22
+ package: z.ZodString;
23
+ }, z.core.$strip>]>;
24
+ }, z.core.$strip>>>;
25
+ }>;
26
+ export default _default;
27
+ //# sourceMappingURL=restrictedImports.d.ts.map
@@ -0,0 +1,166 @@
1
+ import { ruleCreator } from "./ruleCreator.js";
2
+ import { getSpecifierNames } from "../type-utils/getSpecifierNames.js";
3
+ import { matchesSpecifier } from "../type-utils/matchesSpecifier.js";
4
+ import { typeOrValueSpecifierSchema } from "../type-utils/schemas.js";
5
+ import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
6
+ import ts, { SyntaxKind } from "typescript";
7
+ import { z } from "zod/v4";
8
+ //#region src/rules/restrictedImports.ts
9
+ const restrictionSchema = z.object({
10
+ allowTypeImports: z.boolean().optional().describe("Whether type-only imports are exempt from the restriction."),
11
+ message: z.string().optional().describe("A custom message to display when the restriction is triggered."),
12
+ specifier: typeOrValueSpecifierSchema.describe("A TypeOrValueSpecifier identifying the restricted import.")
13
+ });
14
+ function resolveModuleDeclarations(moduleSpecifier, typeChecker) {
15
+ return typeChecker.getSymbolAtLocation(moduleSpecifier)?.getDeclarations();
16
+ }
17
+ function resolveSymbolDeclarations(nameNode, typeChecker) {
18
+ let symbol = typeChecker.getSymbolAtLocation(nameNode);
19
+ if (!symbol) return;
20
+ if (symbol.flags & ts.SymbolFlags.Alias) symbol = typeChecker.getAliasedSymbol(symbol);
21
+ return symbol.getDeclarations();
22
+ }
23
+ var restrictedImports_default = ruleCreator.createRule(typescriptLanguage, {
24
+ about: {
25
+ description: "Restricts specified modules from being imported.",
26
+ id: "restrictedImports"
27
+ },
28
+ messages: {
29
+ moduleRestricted: {
30
+ primary: "'{{ source }}' import is restricted.",
31
+ secondary: ["This module has been restricted by project configuration."],
32
+ suggestions: ["Remove this import or replace it with an allowed alternative."]
33
+ },
34
+ moduleRestrictedWithMessage: {
35
+ primary: "'{{ source }}' import is restricted. {{ customMessage }}",
36
+ secondary: ["This module has been restricted by project configuration."],
37
+ suggestions: ["Remove this import or replace it with an allowed alternative."]
38
+ },
39
+ namespaceRestricted: {
40
+ primary: "* import is invalid because '{{ restrictedNames }}' from '{{ source }}' is restricted.",
41
+ secondary: ["This import uses a namespace or wildcard import, but specific names from this module are restricted."],
42
+ suggestions: ["Replace the namespace import with named imports that are allowed."]
43
+ },
44
+ namespaceRestrictedWithMessage: {
45
+ primary: "* import is invalid because '{{ restrictedNames }}' from '{{ source }}' is restricted. {{ customMessage }}",
46
+ secondary: ["This import uses a namespace or wildcard import, but specific names from this module are restricted."],
47
+ suggestions: ["Replace the namespace import with named imports that are allowed."]
48
+ },
49
+ restricted: {
50
+ primary: "'{{ importName }}' import from '{{ source }}' is restricted.",
51
+ secondary: ["This import name has been restricted by project configuration."],
52
+ suggestions: ["Remove this import or replace it with an allowed alternative."]
53
+ },
54
+ restrictedWithMessage: {
55
+ primary: "'{{ importName }}' import from '{{ source }}' is restricted. {{ customMessage }}",
56
+ secondary: ["This import name has been restricted by project configuration."],
57
+ suggestions: ["Remove this import or replace it with an allowed alternative."]
58
+ }
59
+ },
60
+ options: { restrictions: z.array(restrictionSchema).default([]).describe("Restrictions on imports, using TypeOrValueSpecifier to identify targets.") },
61
+ setup(context) {
62
+ function checkNamedRestrictions(restrictions, declarations, importedName, isTypeOnly, source, range, program) {
63
+ for (const restriction of restrictions) {
64
+ if (restriction.allowTypeImports && isTypeOnly) continue;
65
+ if (matchesSpecifier(importedName, declarations, restriction.specifier, program)) context.report({
66
+ data: {
67
+ customMessage: restriction.message ?? "",
68
+ importName: importedName,
69
+ source
70
+ },
71
+ message: restriction.message ? "restrictedWithMessage" : "restricted",
72
+ range
73
+ });
74
+ }
75
+ }
76
+ function checkWildcardRestrictions(restrictions, moduleDeclarations, source, topLevelTypeOnly, range, program) {
77
+ for (const restriction of restrictions) {
78
+ if (restriction.allowTypeImports && topLevelTypeOnly) continue;
79
+ const names = getSpecifierNames(restriction.specifier);
80
+ if (names !== void 0) {
81
+ if (matchesSpecifier(void 0, moduleDeclarations, {
82
+ ...restriction.specifier,
83
+ name: void 0
84
+ }, program)) context.report({
85
+ data: {
86
+ customMessage: restriction.message ?? "",
87
+ restrictedNames: names.join("', '"),
88
+ source
89
+ },
90
+ message: restriction.message ? "namespaceRestrictedWithMessage" : "namespaceRestricted",
91
+ range
92
+ });
93
+ } else if (matchesSpecifier(void 0, moduleDeclarations, restriction.specifier, program)) context.report({
94
+ data: {
95
+ customMessage: restriction.message ?? "",
96
+ source
97
+ },
98
+ message: restriction.message ? "moduleRestrictedWithMessage" : "moduleRestricted",
99
+ range
100
+ });
101
+ }
102
+ }
103
+ return { visitors: {
104
+ ExportDeclaration: (node, { options, program, sourceFile, typeChecker }) => {
105
+ if (!node.moduleSpecifier || !ts.isStringLiteral(node.moduleSpecifier)) return;
106
+ const source = node.moduleSpecifier.text;
107
+ const topLevelTypeOnly = node.isTypeOnly;
108
+ const range = getTSNodeRange(node, sourceFile);
109
+ if (node.exportClause && ts.isNamedExports(node.exportClause)) for (const element of node.exportClause.elements) {
110
+ const isTypeOnly = topLevelTypeOnly || element.isTypeOnly;
111
+ const importedName = element.propertyName ? element.propertyName.text : element.name.text;
112
+ const declarations = resolveSymbolDeclarations(element.name, typeChecker);
113
+ if (!declarations?.length) continue;
114
+ checkNamedRestrictions(options.restrictions, declarations, importedName, isTypeOnly, source, range, program);
115
+ }
116
+ else {
117
+ const moduleDeclarations = resolveModuleDeclarations(node.moduleSpecifier, typeChecker);
118
+ if (!moduleDeclarations?.length) return;
119
+ checkWildcardRestrictions(options.restrictions, moduleDeclarations, source, topLevelTypeOnly, range, program);
120
+ }
121
+ },
122
+ ImportDeclaration: (node, { options, program, sourceFile, typeChecker }) => {
123
+ if (!ts.isStringLiteral(node.moduleSpecifier)) return;
124
+ const source = node.moduleSpecifier.text;
125
+ const range = getTSNodeRange(node, sourceFile);
126
+ if (!node.importClause) {
127
+ const moduleDeclarations = resolveModuleDeclarations(node.moduleSpecifier, typeChecker);
128
+ if (!moduleDeclarations?.length) return;
129
+ for (const restriction of options.restrictions) if (matchesSpecifier(void 0, moduleDeclarations, restriction.specifier, program)) context.report({
130
+ data: {
131
+ customMessage: restriction.message ?? "",
132
+ source
133
+ },
134
+ message: restriction.message ? "moduleRestrictedWithMessage" : "moduleRestricted",
135
+ range
136
+ });
137
+ return;
138
+ }
139
+ const topLevelTypeOnly = node.importClause.phaseModifier === SyntaxKind.TypeKeyword;
140
+ if (node.importClause.name) {
141
+ const declarations = resolveSymbolDeclarations(node.importClause.name, typeChecker);
142
+ if (declarations?.length) checkNamedRestrictions(options.restrictions, declarations, "default", topLevelTypeOnly, source, range, program);
143
+ }
144
+ const bindings = node.importClause.namedBindings;
145
+ if (!bindings) return;
146
+ if (ts.isNamedImports(bindings)) {
147
+ for (const element of bindings.elements) {
148
+ const isTypeOnly = topLevelTypeOnly || element.isTypeOnly;
149
+ const importedName = element.propertyName ? element.propertyName.text : element.name.text;
150
+ const declarations = resolveSymbolDeclarations(element.name, typeChecker);
151
+ if (!declarations?.length) continue;
152
+ checkNamedRestrictions(options.restrictions, declarations, importedName, isTypeOnly, source, range, program);
153
+ }
154
+ return;
155
+ }
156
+ const moduleDeclarations = resolveModuleDeclarations(node.moduleSpecifier, typeChecker);
157
+ if (!moduleDeclarations?.length) return;
158
+ checkWildcardRestrictions(options.restrictions, moduleDeclarations, source, topLevelTypeOnly, range, program);
159
+ }
160
+ } };
161
+ }
162
+ });
163
+ //#endregion
164
+ export { restrictedImports_default as default };
165
+
166
+ //# sourceMappingURL=restrictedImports.js.map
@@ -1,6 +1,5 @@
1
1
  import { ruleCreator } from "./ruleCreator.js";
2
- import { forEachChild } from "./utils/forEachChild.js";
3
- import { getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
2
+ import { forEachChild, getTSNodeRange, typescriptLanguage } from "@flint.fyi/typescript-language";
4
3
  import ts, { SyntaxKind } from "typescript";
5
4
  import { z } from "zod/v4";
6
5
  //#region src/rules/typeImports.ts
@@ -14,13 +14,10 @@ var unassignedVariables_default = ruleCreator.createRule(typescriptLanguage, {
14
14
  suggestions: ["Assign a value to the variable, either at declaration or before it is used.", "If the variable is meant to be `undefined`, consider making that explicit for clarity."]
15
15
  } },
16
16
  setup(context) {
17
- function hasAssignments(identifier, sourceFile, typeChecker) {
18
- return !!getModifyingReferences(identifier, sourceFile, typeChecker).length;
19
- }
20
- return { visitors: { VariableDeclaration: (node, { sourceFile, typeChecker }) => {
17
+ return { visitors: { VariableDeclaration: (node, { sourceFile }) => {
21
18
  if (node.initializer || node.name.kind !== SyntaxKind.Identifier) return;
22
19
  if (node.parent.kind === SyntaxKind.VariableDeclarationList && !!(node.parent.flags & ts.NodeFlags.Const)) return;
23
- if (!hasAssignments(node.name, sourceFile, typeChecker)) context.report({
20
+ if (!getModifyingReferences(node.name, sourceFile).length) context.report({
24
21
  data: { name: node.name.text },
25
22
  message: "noUnassigned",
26
23
  range: {
@@ -1,5 +1,5 @@
1
- import type { AST } from "@flint.fyi/typescript-language";
2
1
  import ts from "typescript";
2
+ import type { AST } from "@flint.fyi/typescript-language";
3
3
  export interface AccessorInfo {
4
4
  index: number;
5
5
  node: AST.GetAccessorDeclaration | AST.SetAccessorDeclaration;
@@ -1,5 +1,5 @@
1
- import type { Checker } from "@flint.fyi/typescript-language";
2
1
  import ts from "typescript";
2
+ import type { Checker } from "@flint.fyi/typescript-language";
3
3
  export declare const AnyType: {
4
4
  readonly Any: "any";
5
5
  readonly AnyArray: "any[]";
@@ -1,5 +1,5 @@
1
- import type { AST } from "@flint.fyi/typescript-language";
2
1
  import { SyntaxKind } from "typescript";
2
+ import { type AST } from "@flint.fyi/typescript-language";
3
3
  export type EqualityOperator = "!=" | "!==" | "==" | "===";
4
4
  export declare function isNullishLiteral(node: AST.Expression): boolean;
5
5
  export declare function isUndefinedIdentifier(node: AST.Expression): boolean;
@@ -1,4 +1,4 @@
1
- import type { Checker } from "@flint.fyi/typescript-language";
2
1
  import type ts from "typescript";
2
+ import type { Checker } from "@flint.fyi/typescript-language";
3
3
  export declare function formatReportedType(type: ts.Type, typeChecker: Checker): string;
4
4
  //# sourceMappingURL=formatReportedType.d.ts.map
@@ -1,5 +1,5 @@
1
- import { type AST, type TypeScriptFileServices } from "@flint.fyi/typescript-language";
2
1
  import ts from "typescript";
2
+ import { type AST, type TypeScriptFileServices } from "@flint.fyi/typescript-language";
3
3
  export declare function getRegExpConstruction(node: AST.CallExpression | AST.NewExpression, { sourceFile, typeChecker }: TypeScriptFileServices): {
4
4
  args: ts.NodeArray<AST.Expression>;
5
5
  flags: string;
@@ -1,4 +1,4 @@
1
- import type { AST } from "@flint.fyi/typescript-language";
2
1
  import * as ts from "typescript";
2
+ import type { AST } from "@flint.fyi/typescript-language";
3
3
  export declare function isDirectEqualityCheck(node: AST.ArrowFunction | AST.FunctionExpression, operators: ts.SyntaxKind[], parameterName: string): AST.Expression | undefined;
4
4
  //# sourceMappingURL=isDirectEqualityCheck.d.ts.map
@@ -1,5 +1,5 @@
1
- import type { AST } from "@flint.fyi/typescript-language";
2
1
  import ts from "typescript";
2
+ import type { AST } from "@flint.fyi/typescript-language";
3
3
  /**
4
4
  * Does a simple check to see if there is an any being assigned to a non-any type.
5
5
  *
@@ -0,0 +1,3 @@
1
+ import type { TypeOrValueSpecifier } from "./schemas.ts";
2
+ export declare function getSpecifierNames(specifier: TypeOrValueSpecifier): string[] | undefined;
3
+ //# sourceMappingURL=getSpecifierNames.d.ts.map
@@ -0,0 +1,9 @@
1
+ //#region src/type-utils/getSpecifierNames.ts
2
+ function getSpecifierNames(specifier) {
3
+ if (specifier.name === void 0) return;
4
+ return Array.isArray(specifier.name) ? specifier.name : [specifier.name];
5
+ }
6
+ //#endregion
7
+ export { getSpecifierNames };
8
+
9
+ //# sourceMappingURL=getSpecifierNames.js.map
@@ -0,0 +1,3 @@
1
+ import ts from "typescript";
2
+ export declare function isDeclaredInModuleBlock(declaration: ts.Declaration, packageName: string): boolean;
3
+ //# sourceMappingURL=isDeclaredInModuleBlock.d.ts.map
@@ -0,0 +1,14 @@
1
+ import ts from "typescript";
2
+ //#region src/type-utils/isDeclaredInModuleBlock.ts
3
+ function isDeclaredInModuleBlock(declaration, packageName) {
4
+ let current = declaration;
5
+ while (!ts.isSourceFile(current)) {
6
+ if (ts.isModuleDeclaration(current) && !(current.flags & ts.NodeFlags.Namespace) && ts.isStringLiteral(current.name) && current.name.text === packageName) return true;
7
+ current = current.parent;
8
+ }
9
+ return false;
10
+ }
11
+ //#endregion
12
+ export { isDeclaredInModuleBlock };
13
+
14
+ //# sourceMappingURL=isDeclaredInModuleBlock.js.map
@@ -0,0 +1,3 @@
1
+ import ts from "typescript";
2
+ export declare function isFromFile(sourceFile: ts.SourceFile, specifiedPath: string | undefined, program: ts.Program): boolean;
3
+ //# sourceMappingURL=isFromFile.d.ts.map
@@ -0,0 +1,13 @@
1
+ import ts from "typescript";
2
+ import { pathKey } from "@flint.fyi/utils";
3
+ import path from "node:path";
4
+ //#region src/type-utils/isFromFile.ts
5
+ function isFromFile(sourceFile, specifiedPath, program) {
6
+ if (specifiedPath === void 0) return !sourceFile.fileName.includes("/node_modules/") && !program.isSourceFileDefaultLibrary(sourceFile);
7
+ const caseSensitive = ts.sys.useCaseSensitiveFileNames;
8
+ return pathKey(sourceFile.fileName, caseSensitive) === pathKey(path.resolve(program.getCurrentDirectory(), specifiedPath), caseSensitive);
9
+ }
10
+ //#endregion
11
+ export { isFromFile };
12
+
13
+ //# sourceMappingURL=isFromFile.js.map
@@ -0,0 +1,3 @@
1
+ import ts from "typescript";
2
+ export declare function isFromPackage(declaration: ts.Declaration, packageName: string, program: ts.Program): boolean;
3
+ //# sourceMappingURL=isFromPackage.d.ts.map
@@ -0,0 +1,16 @@
1
+ import { isDeclaredInModuleBlock } from "./isDeclaredInModuleBlock.js";
2
+ import "typescript";
3
+ //#region src/type-utils/isFromPackage.ts
4
+ function isFromPackage(declaration, packageName, program) {
5
+ if (isDeclaredInModuleBlock(declaration, packageName)) return true;
6
+ const sourceFile = declaration.getSourceFile();
7
+ if (!program.isSourceFileFromExternalLibrary(sourceFile)) return false;
8
+ const resolvedName = program.sourceFileToPackageName.get(sourceFile.path);
9
+ if (resolvedName === packageName || sourceFile.fileName.includes(`/node_modules/${packageName}/`)) return true;
10
+ const typesPackageName = packageName.replace(/^@([^/]+)\//, "$1__");
11
+ return resolvedName === typesPackageName || sourceFile.fileName.includes(`/node_modules/@types/${typesPackageName}/`);
12
+ }
13
+ //#endregion
14
+ export { isFromPackage };
15
+
16
+ //# sourceMappingURL=isFromPackage.js.map
@@ -0,0 +1,4 @@
1
+ import type ts from "typescript";
2
+ import type { TypeOrValueSpecifier } from "./schemas.ts";
3
+ export declare function matchesSpecifier(importedName: string | undefined, declarations: ts.Declaration[], specifier: TypeOrValueSpecifier, program: ts.Program): boolean;
4
+ //# sourceMappingURL=matchesSpecifier.d.ts.map
@@ -0,0 +1,20 @@
1
+ import { getSpecifierNames } from "./getSpecifierNames.js";
2
+ import { isFromFile } from "./isFromFile.js";
3
+ import { isFromPackage } from "./isFromPackage.js";
4
+ import { declarationIncludesGlobal } from "@flint.fyi/typescript-language";
5
+ //#region src/type-utils/matchesSpecifier.ts
6
+ function matchesSpecifier(importedName, declarations, specifier, program) {
7
+ const names = getSpecifierNames(specifier);
8
+ if (names !== void 0 && (importedName === void 0 || !names.includes(importedName))) return false;
9
+ return declarations.some((declaration) => {
10
+ switch (specifier.from) {
11
+ case "file": return isFromFile(declaration.getSourceFile(), specifier.path, program);
12
+ case "lib": return declarationIncludesGlobal(declaration);
13
+ case "package": return isFromPackage(declaration, specifier.package, program);
14
+ }
15
+ });
16
+ }
17
+ //#endregion
18
+ export { matchesSpecifier };
19
+
20
+ //# sourceMappingURL=matchesSpecifier.js.map
@@ -0,0 +1,15 @@
1
+ import { z } from "zod/v4";
2
+ export declare const typeOrValueSpecifierSchema: z.ZodUnion<readonly [z.ZodObject<{
3
+ from: z.ZodLiteral<"file">;
4
+ name: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
5
+ path: z.ZodOptional<z.ZodString>;
6
+ }, z.core.$strip>, z.ZodObject<{
7
+ from: z.ZodLiteral<"lib">;
8
+ name: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
9
+ }, z.core.$strip>, z.ZodObject<{
10
+ from: z.ZodLiteral<"package">;
11
+ name: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
12
+ package: z.ZodString;
13
+ }, z.core.$strip>]>;
14
+ export type TypeOrValueSpecifier = z.infer<typeof typeOrValueSpecifierSchema>;
15
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1,25 @@
1
+ import { z } from "zod/v4";
2
+ //#region src/type-utils/schemas.ts
3
+ const fileSpecifierSchema = z.object({
4
+ from: z.literal("file"),
5
+ name: z.union([z.string(), z.array(z.string())]).optional(),
6
+ path: z.string().optional()
7
+ });
8
+ const libSpecifierSchema = z.object({
9
+ from: z.literal("lib"),
10
+ name: z.union([z.string(), z.array(z.string())]).optional()
11
+ });
12
+ const packageSpecifierSchema = z.object({
13
+ from: z.literal("package"),
14
+ name: z.union([z.string(), z.array(z.string())]).optional(),
15
+ package: z.string()
16
+ });
17
+ const typeOrValueSpecifierSchema = z.union([
18
+ fileSpecifierSchema,
19
+ libSpecifierSchema,
20
+ packageSpecifierSchema
21
+ ]);
22
+ //#endregion
23
+ export { typeOrValueSpecifierSchema };
24
+
25
+ //# sourceMappingURL=schemas.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flint.fyi/ts",
3
- "version": "0.19.1",
3
+ "version": "0.20.0",
4
4
  "description": "[Experimental] TypeScript language plugin for Flint.",
5
5
  "homepage": "https://flint.fyi/rules/ts",
6
6
  "repository": {
@@ -10,8 +10,8 @@
10
10
  },
11
11
  "license": "MIT",
12
12
  "author": {
13
- "name": "JoshuaKGoldberg",
14
- "email": "npm@joshuakgoldberg.com"
13
+ "name": "Flint Team",
14
+ "url": "https://flint.fyi/team"
15
15
  },
16
16
  "sideEffects": false,
17
17
  "type": "module",
@@ -29,14 +29,14 @@
29
29
  "ts-api-utils": "^2.4.0",
30
30
  "typescript": "^5.9.3 || ^6.0.0",
31
31
  "zod": "^4.3.6",
32
- "@flint.fyi/core": "^0.22.0",
32
+ "@flint.fyi/core": "^0.23.0",
33
33
  "@flint.fyi/utils": "^0.14.1",
34
- "@flint.fyi/typescript-language": "^0.18.2"
34
+ "@flint.fyi/typescript-language": "^0.18.3"
35
35
  },
36
36
  "devDependencies": {
37
37
  "tsdown": "0.21.0",
38
38
  "vitest": "4.1.0",
39
- "@flint.fyi/rule-tester": "^0.16.3"
39
+ "@flint.fyi/rule-tester": "^0.16.4"
40
40
  },
41
41
  "engines": {
42
42
  "node": ">=24.0.0"
@@ -1,4 +0,0 @@
1
- import type { AST } from "@flint.fyi/typescript-language";
2
- import ts from "typescript";
3
- export declare const forEachChild: <T>(node: AST.AnyNode, cbNode: (node: AST.AnyNode) => T | undefined, cbNodes?: (nodes: ts.NodeArray<AST.AnyNode>) => T | undefined) => T | undefined;
4
- //# sourceMappingURL=forEachChild.d.ts.map
@@ -1,7 +0,0 @@
1
- import ts from "typescript";
2
- //#region src/rules/utils/forEachChild.ts
3
- const forEachChild = ts.forEachChild;
4
- //#endregion
5
- export { forEachChild };
6
-
7
- //# sourceMappingURL=forEachChild.js.map