@logtape/lint 2.2.0-dev.676 → 2.2.0-dev.679
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/core/ast.cjs +6 -7
- package/dist/core/ast.js +6 -7
- package/dist/core/ast.js.map +1 -1
- package/package.json +1 -1
package/dist/core/ast.cjs
CHANGED
|
@@ -449,10 +449,11 @@ function isStringLiteral(node, value) {
|
|
|
449
449
|
return false;
|
|
450
450
|
}
|
|
451
451
|
/**
|
|
452
|
-
* Whether an AST node is the
|
|
452
|
+
* Whether an AST node is the category literal `"logtape"`, `["logtape"]`, or
|
|
453
453
|
* `["logtape", "meta"]`.
|
|
454
454
|
*/
|
|
455
|
-
function
|
|
455
|
+
function isLogtapeMetaCategory(node) {
|
|
456
|
+
if (isStringLiteral(node, "logtape")) return true;
|
|
456
457
|
if (node?.type !== "ArrayExpression") return false;
|
|
457
458
|
const elems = node.elements;
|
|
458
459
|
if (!isStringLiteral(elems[0], "logtape")) return false;
|
|
@@ -461,10 +462,8 @@ function isLogtapeMetaArray(node) {
|
|
|
461
462
|
}
|
|
462
463
|
/**
|
|
463
464
|
* Whether a logger entry covers the meta category with at least one non-empty
|
|
464
|
-
* sinks list.
|
|
465
|
-
*
|
|
466
|
-
* the category as an array, so a bare string `category: "logtape"` leaves the
|
|
467
|
-
* meta logger unconfigured and must not satisfy the rule.
|
|
465
|
+
* sinks list. The string form (`"logtape"`) is equivalent to `["logtape"]`,
|
|
466
|
+
* and both configure the meta logger.
|
|
468
467
|
*/
|
|
469
468
|
function isMetaLoggerEntry(entry) {
|
|
470
469
|
if (!entry || entry.type !== "ObjectExpression") return false;
|
|
@@ -478,7 +477,7 @@ function isMetaLoggerEntry(entry) {
|
|
|
478
477
|
if (keyName === "sinks") sinksNode = unwrapTypeAssertion(prop.value);
|
|
479
478
|
}
|
|
480
479
|
if (!categoryNode) return false;
|
|
481
|
-
if (!
|
|
480
|
+
if (!isLogtapeMetaCategory(categoryNode)) return false;
|
|
482
481
|
if (!sinksNode) return false;
|
|
483
482
|
if (sinksNode.type !== "ArrayExpression") return true;
|
|
484
483
|
return sinksNode.elements.length > 0;
|
package/dist/core/ast.js
CHANGED
|
@@ -448,10 +448,11 @@ function isStringLiteral(node, value) {
|
|
|
448
448
|
return false;
|
|
449
449
|
}
|
|
450
450
|
/**
|
|
451
|
-
* Whether an AST node is the
|
|
451
|
+
* Whether an AST node is the category literal `"logtape"`, `["logtape"]`, or
|
|
452
452
|
* `["logtape", "meta"]`.
|
|
453
453
|
*/
|
|
454
|
-
function
|
|
454
|
+
function isLogtapeMetaCategory(node) {
|
|
455
|
+
if (isStringLiteral(node, "logtape")) return true;
|
|
455
456
|
if (node?.type !== "ArrayExpression") return false;
|
|
456
457
|
const elems = node.elements;
|
|
457
458
|
if (!isStringLiteral(elems[0], "logtape")) return false;
|
|
@@ -460,10 +461,8 @@ function isLogtapeMetaArray(node) {
|
|
|
460
461
|
}
|
|
461
462
|
/**
|
|
462
463
|
* Whether a logger entry covers the meta category with at least one non-empty
|
|
463
|
-
* sinks list.
|
|
464
|
-
*
|
|
465
|
-
* the category as an array, so a bare string `category: "logtape"` leaves the
|
|
466
|
-
* meta logger unconfigured and must not satisfy the rule.
|
|
464
|
+
* sinks list. The string form (`"logtape"`) is equivalent to `["logtape"]`,
|
|
465
|
+
* and both configure the meta logger.
|
|
467
466
|
*/
|
|
468
467
|
function isMetaLoggerEntry(entry) {
|
|
469
468
|
if (!entry || entry.type !== "ObjectExpression") return false;
|
|
@@ -477,7 +476,7 @@ function isMetaLoggerEntry(entry) {
|
|
|
477
476
|
if (keyName === "sinks") sinksNode = unwrapTypeAssertion(prop.value);
|
|
478
477
|
}
|
|
479
478
|
if (!categoryNode) return false;
|
|
480
|
-
if (!
|
|
479
|
+
if (!isLogtapeMetaCategory(categoryNode)) return false;
|
|
481
480
|
if (!sinksNode) return false;
|
|
482
481
|
if (sinksNode.type !== "ArrayExpression") return true;
|
|
483
482
|
return sinksNode.elements.length > 0;
|
package/dist/core/ast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ast.js","names":["LOG_METHODS: Set<string>","ASYNC_FUNCTION_TYPES: Set<string>","source: unknown","node: any","callee: any","lazyNames: Set<string>","args: any","propsObject: any","prop: any","fn: any","DISCARDING_ARRAY_METHODS: Set<string>","FIRE_AND_FORGET_GLOBALS: Set<string>","ELEMENT_PRESERVING_ARRAY_METHODS: Set<string>","arrayNode: any","current: any","ancestor: any","grandAncestor: any","value: string","entry: any","p: any","categoryNode: any","sinksNode: any","configArg: any","el: any"],"sources":["../../src/core/ast.ts"],"sourcesContent":["/**\n * Shared, host-agnostic AST analysis used by both the ESLint rules\n * (`../rules/`) and the Deno Lint plugin (`../deno/plugin.ts`).\n *\n * Every function here works on plain ESTree-shaped AST nodes and depends only\n * on `node.type` / `node.parent` style traversal, so the same logic runs under\n * ESLint, Oxlint, and Deno Lint. Divergences between the host ASTs (e.g. Deno\n * exposing `TemplateElement.cooked` directly where ESTree nests it under\n * `value.cooked`) are absorbed here with `??` fallbacks, so an edge case is\n * fixed once rather than in two drifting copies.\n *\n * This module must not import from `eslint`, not even types: the Deno plugin\n * imports it and must stay loadable without the ESLint package present.\n *\n * @module\n */\n\n// The lint AST is untyped and differs across hosts, so these helpers operate\n// on `any` nodes by design.\n// deno-lint-ignore-file no-explicit-any\n\n/**\n * Log method names that the LogTape lint rules check.\n */\nexport const LOG_METHODS: Set<string> = new Set([\n \"trace\",\n \"debug\",\n \"info\",\n \"warn\",\n \"warning\",\n \"error\",\n \"fatal\",\n]);\n\n/**\n * AST node types that introduce their own function scope.\n */\nexport const ASYNC_FUNCTION_TYPES: Set<string> = new Set([\n \"ArrowFunctionExpression\",\n \"FunctionExpression\",\n \"FunctionDeclaration\",\n]);\n\n/**\n * Maximum depth for the recursive AST scans. The AST is finite, so this is a\n * safety net against pathological nesting rather than an expected limit; it is\n * generous enough to cover deeply nested log property objects (complex API\n * payloads, ORM entities) without false negatives.\n */\nconst MAX_RECURSION_DEPTH = 100;\n\n/**\n * Whether an import source refers to the LogTape core package. Accepts the\n * bare specifier (`@logtape/logtape`) as well as direct Deno-style `jsr:` and\n * `npm:` specifiers with an optional version suffix (e.g.\n * `jsr:@logtape/logtape` or `npm:@logtape/logtape@^1.0.0`).\n */\nexport function isLogtapeImportSource(source: unknown): boolean {\n return typeof source === \"string\" &&\n /^(?:(?:jsr|npm):)?@logtape\\/logtape(?:@[^/]+)?$/.test(source);\n}\n\n/**\n * Unwrap TypeScript type-assertion wrappers around an expression (`x as T`,\n * `<T>x`, `x satisfies T`, `x!`) so the rules analyze the underlying node.\n * Each wrapper exposes the inner node as `.expression`. Returns the node\n * unchanged when it is not such a wrapper.\n */\nexport function unwrapTypeAssertion(node: any): any {\n while (\n node &&\n (node.type === \"TSAsExpression\" ||\n node.type === \"TSTypeAssertion\" ||\n node.type === \"TSSatisfiesExpression\" ||\n node.type === \"TSNonNullExpression\")\n ) {\n node = node.expression;\n }\n return node;\n}\n\n/**\n * Resolve the log method name from a member-expression callee\n * (`logger.debug` -> `\"debug\"`), supporting computed string-literal access\n * (`logger[\"debug\"]`). Returns `null` for a computed non-literal property or a\n * non-member callee.\n */\nexport function logMethodName(callee: any): string | null {\n if (!callee || callee.type !== \"MemberExpression\") return null;\n if (!callee.computed) return callee.property?.name ?? null;\n // A computed key must be a string literal; a numeric literal (logger[0]) is\n // not a method name and must not be returned as one.\n return callee.property?.type === \"Literal\" &&\n typeof callee.property.value === \"string\"\n ? callee.property.value\n : null;\n}\n\n/**\n * Recursively check whether an AST node contains an eagerly evaluated call\n * anywhere in its subtree. Only inspects own-enumerable child nodes, skipping\n * metadata fields. A LogTape `lazy(...)` call (whose local names are in\n * `lazyNames`) is already deferred, so it is not counted as eager; its\n * arguments are still inspected, so `lazy(expensive())` is caught while\n * `lazy(() => expensive())` is not.\n */\nexport function containsCallExpression(\n node: any,\n lazyNames: Set<string>,\n depth = 0,\n): boolean {\n if (depth > MAX_RECURSION_DEPTH || !node || typeof node !== \"object\") {\n return false;\n }\n const isLazyCall = node.type === \"CallExpression\" &&\n node.callee?.type === \"Identifier\" && lazyNames.has(node.callee.name);\n if (\n !isLazyCall && (\n node.type === \"CallExpression\" ||\n node.type === \"OptionalCallExpression\" ||\n node.type === \"NewExpression\" ||\n node.type === \"TaggedTemplateExpression\" ||\n node.type === \"ImportExpression\"\n )\n ) {\n return true;\n }\n // Don't recurse into function bodies — calls inside them are deferred,\n // not eagerly evaluated at the call site.\n if (\n node.type === \"ArrowFunctionExpression\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"FunctionDeclaration\"\n ) {\n return false;\n }\n // Enumerate with for-in, not Object.keys: Deno's lint AST exposes child\n // nodes as enumerable getters on the prototype (Object.keys returns []),\n // while ESTree exposes them as own properties; for-in covers both. `parent`\n // is non-enumerable in Deno and skipped below for ESTree.\n for (const key in node) {\n if (\n key === \"type\" || key === \"start\" || key === \"end\" ||\n key === \"loc\" || key === \"parent\" || key === \"range\"\n ) {\n continue;\n }\n const child = node[key];\n if (Array.isArray(child)) {\n for (const item of child) {\n if (\n typeof item === \"object\" && item !== null &&\n containsCallExpression(item, lazyNames, depth + 1)\n ) return true;\n }\n } else if (typeof child === \"object\" && child !== null) {\n if (containsCallExpression(child, lazyNames, depth + 1)) return true;\n }\n }\n return false;\n}\n\n/**\n * Recursively check whether a node contains an `AwaitExpression` or\n * `YieldExpression` in the same function scope. Does not descend into nested\n * function bodies.\n */\nexport function containsAwaitOrYield(node: any, depth = 0): boolean {\n if (depth > MAX_RECURSION_DEPTH || !node || typeof node !== \"object\") {\n return false;\n }\n if (node.type === \"AwaitExpression\" || node.type === \"YieldExpression\") {\n return true;\n }\n if (\n node.type === \"ArrowFunctionExpression\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"FunctionDeclaration\"\n ) {\n return false;\n }\n // Enumerate with for-in, not Object.keys: Deno's lint AST exposes child\n // nodes as enumerable getters on the prototype (Object.keys returns []),\n // while ESTree exposes them as own properties; for-in covers both. `parent`\n // is non-enumerable in Deno and skipped below for ESTree.\n for (const key in node) {\n if (\n key === \"type\" || key === \"start\" || key === \"end\" ||\n key === \"loc\" || key === \"parent\" || key === \"range\"\n ) {\n continue;\n }\n const child = node[key];\n if (Array.isArray(child)) {\n for (const item of child) {\n if (\n typeof item === \"object\" && item !== null &&\n containsAwaitOrYield(item, depth + 1)\n ) return true;\n }\n } else if (typeof child === \"object\" && child !== null) {\n if (containsAwaitOrYield(child, depth + 1)) return true;\n }\n }\n return false;\n}\n\n/**\n * From a log call's argument list, select the eager properties object and note\n * whether it came from the properties-only overload. The properties object is\n * the second argument in the message+properties form\n * (`logger.debug(\"msg\", { ... })`) or the first argument in the\n * properties-only form (`logger.debug({ ... })`). TypeScript type assertions\n * (e.g. `{ ... } as const`) are unwrapped first. Returns `null` when neither\n * argument is an object literal.\n *\n * `propsObject` is the unwrapped object (for detection and the report\n * location); `fixTarget` is the original, still-wrapped argument node, so the\n * autofix replaces the whole `{ ... } as const` rather than leaving the\n * assertion dangling on the new callback. When the argument is not wrapped the\n * two are the same node.\n */\nexport function selectLazyPropsObject(\n args: any,\n): { propsObject: any; fixTarget: any; propertiesOnly: boolean } | null {\n const firstRaw = args?.[0];\n const secondRaw = args?.[1];\n const firstArg = unwrapTypeAssertion(firstRaw);\n const secondArg = unwrapTypeAssertion(secondRaw);\n if (firstArg && firstArg.type === \"ObjectExpression\") {\n return { propsObject: firstArg, fixTarget: firstRaw, propertiesOnly: true };\n }\n if (secondArg && secondArg.type === \"ObjectExpression\") {\n return {\n propsObject: secondArg,\n fixTarget: secondRaw,\n propertiesOnly: false,\n };\n }\n return null;\n}\n\n/**\n * Whether any property (or spread) of a properties object contains an eager\n * call that would benefit from lazy evaluation.\n */\nexport function propsHaveEagerCall(\n propsObject: any,\n lazyNames: Set<string>,\n): boolean {\n return propsObject.properties?.some(\n (prop: any) =>\n (prop.type === \"Property\" &&\n (containsCallExpression(prop.value, lazyNames) ||\n (prop.computed &&\n containsCallExpression(prop.key, lazyNames)))) ||\n (prop.type === \"SpreadElement\" &&\n containsCallExpression(prop.argument, lazyNames)),\n ) ?? false;\n}\n\n/**\n * Whether `node` is an async function literal (arrow or function expression).\n */\nexport function isAsyncFunctionExpr(node: any): boolean {\n return (node?.type === \"ArrowFunctionExpression\" ||\n node?.type === \"FunctionExpression\") && node.async === true;\n}\n\n/**\n * Walk up the parent chain to find the nearest enclosing function. Returns\n * `null` if the top of the tree is reached without finding one.\n */\nexport function findEnclosingFunction(node: any): any {\n let current = node.parent;\n while (current) {\n if (ASYNC_FUNCTION_TYPES.has(current.type)) return current;\n current = current.parent;\n }\n return null;\n}\n\n/**\n * Whether `fn` is a function passed directly as a call argument (e.g. an array\n * `.map()`/`.forEach()` callback). Such a function's return value is decided\n * by the receiving call, so a promise it returns may be awaited or discarded.\n */\nexport function isCallArgumentFunction(fn: any): boolean {\n const parent = fn?.parent;\n return parent?.type === \"CallExpression\" &&\n parent.callee !== fn &&\n (parent.arguments?.includes(fn) ?? false);\n}\n\n/**\n * Array iteration methods that ignore, coerce, or merely thread the callback's\n * return value, so a promise returned from the callback is not awaited per\n * call. `reduce`/`reduceRight` pass it on as the next accumulator (only the\n * final accumulator is the result), so earlier per-item promises are dropped.\n */\nconst DISCARDING_ARRAY_METHODS: Set<string> = new Set([\n \"forEach\",\n \"filter\",\n \"find\",\n \"findLast\",\n \"findIndex\",\n \"findLastIndex\",\n \"some\",\n \"every\",\n \"reduce\",\n \"reduceRight\",\n]);\n\n/**\n * Global \"fire and forget\" functions that ignore their callback's return value\n * entirely, so a promise returned from the callback is never awaited.\n */\nconst FIRE_AND_FORGET_GLOBALS: Set<string> = new Set([\n \"setTimeout\",\n \"setInterval\",\n \"setImmediate\",\n \"queueMicrotask\",\n \"requestAnimationFrame\",\n \"requestIdleCallback\",\n]);\n\n/**\n * Whether `fn` is a callback argument to a call that ignores or coerces its\n * callback's return value: an array method like `forEach`/`filter`/`some`, or a\n * fire-and-forget global like `setTimeout`/`queueMicrotask`. Such a call does\n * not propagate the callback's promise, so an async log returned from it is\n * dropped and the walk must stop there rather than continue to an outer\n * await/return.\n */\nexport function isDiscardedCallbackArgument(fn: any): boolean {\n const parent = fn?.parent;\n if (!parent || parent.type !== \"CallExpression\") return false;\n if (parent.callee === fn) return false;\n if (!(parent.arguments?.includes(fn) ?? false)) return false;\n const callee = parent.callee;\n // setTimeout(() => ...), queueMicrotask(() => ...), etc.\n if (\n callee?.type === \"Identifier\" && FIRE_AND_FORGET_GLOBALS.has(callee.name)\n ) {\n return true;\n }\n // items.forEach(() => ...) or items[\"forEach\"](() => ...), etc.\n // logMethodName resolves both the plain and computed string-literal forms.\n const methodName = logMethodName(callee);\n return methodName !== null && DISCARDING_ARRAY_METHODS.has(methodName);\n}\n\n/**\n * Whether `node` is the result of a `map()`/`flatMap()` call. Such a call\n * returns an array of the callback's return values, so awaiting or returning it\n * does not await the element promises; only a Promise combinator\n * (`Promise.all`/`allSettled`) awaits those.\n */\nexport function isMapResult(node: any): boolean {\n return node?.type === \"CallExpression\" &&\n node.callee?.type === \"MemberExpression\" &&\n !node.callee.computed &&\n node.callee.property?.type === \"Identifier\" &&\n (node.callee.property.name === \"map\" ||\n node.callee.property.name === \"flatMap\");\n}\n\n/**\n * Array methods that return a new array holding the same elements, so a\n * `map()`/`flatMap()` result chained through them is still an array of the\n * original promises (e.g. `arr.map(cb).filter(Boolean)`). Methods that unwrap\n * to a single element (`find`, `at`) or transform the elements (`map`,\n * `reduce`) are deliberately excluded.\n */\nconst ELEMENT_PRESERVING_ARRAY_METHODS: Set<string> = new Set([\n \"filter\",\n \"slice\",\n \"concat\",\n \"flat\",\n \"reverse\",\n \"sort\",\n \"toSorted\",\n \"toReversed\",\n \"with\",\n]);\n\n/**\n * Whether `node` evaluates to an array of the promises produced by a\n * `map()`/`flatMap()`, either directly or chained through element-preserving\n * array methods (`arr.map(cb).filter(...).slice(...)`). Awaiting or returning\n * such an array awaits the array, not the element promises.\n */\nexport function isMapResultChain(node: any, depth = 0): boolean {\n if (depth > MAX_RECURSION_DEPTH || !node) return false;\n if (isMapResult(node)) return true;\n if (\n node.type === \"CallExpression\" &&\n node.callee?.type === \"MemberExpression\" &&\n !node.callee.computed &&\n node.callee.property?.type === \"Identifier\" &&\n ELEMENT_PRESERVING_ARRAY_METHODS.has(node.callee.property.name)\n ) {\n return isMapResultChain(node.callee.object, depth + 1);\n }\n return false;\n}\n\n/**\n * Whether `node` is an expression that is syntactically a promise. Recognizes\n * `x.then(...)` / `.catch(...)` / `.finally(...)`, `new Promise(...)`, and\n * `Promise.resolve/reject/all/allSettled/race/any(...)`. This cannot see a\n * promise returned by an opaque call (e.g. `fetchData()` whose return type is a\n * promise), which a syntactic lint rule has no type information for.\n */\nexport function isPromiseReturningExpr(node: any): boolean {\n if (!node) return false;\n if (\n node.type === \"CallExpression\" &&\n node.callee?.type === \"MemberExpression\" && !node.callee.computed &&\n node.callee.property?.type === \"Identifier\"\n ) {\n const name = node.callee.property.name;\n if (name === \"then\" || name === \"catch\" || name === \"finally\") return true;\n if (\n node.callee.object?.type === \"Identifier\" &&\n node.callee.object.name === \"Promise\"\n ) return true;\n }\n if (\n node.type === \"NewExpression\" && node.callee?.type === \"Identifier\" &&\n node.callee.name === \"Promise\"\n ) return true;\n return false;\n}\n\n/**\n * Whether a block contains a `return <promise>` statement, without descending\n * into nested functions (whose returns belong to them, not to the block's\n * function).\n */\nexport function blockReturnsPromise(node: any, depth = 0): boolean {\n if (depth > MAX_RECURSION_DEPTH || !node || typeof node !== \"object\") {\n return false;\n }\n if (node.type === \"ReturnStatement\") {\n return isPromiseReturningExpr(node.argument);\n }\n if (\n node.type === \"ArrowFunctionExpression\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"FunctionDeclaration\"\n ) {\n return false;\n }\n // Enumerate with for-in, not Object.keys: Deno's lint AST exposes child\n // nodes as enumerable getters on the prototype (Object.keys returns []),\n // while ESTree exposes them as own properties; for-in covers both. `parent`\n // is non-enumerable in Deno and skipped below for ESTree.\n for (const key in node) {\n if (\n key === \"type\" || key === \"start\" || key === \"end\" ||\n key === \"loc\" || key === \"parent\" || key === \"range\"\n ) continue;\n const child = node[key];\n if (Array.isArray(child)) {\n for (const item of child) {\n if (\n typeof item === \"object\" && item !== null &&\n blockReturnsPromise(item, depth + 1)\n ) return true;\n }\n } else if (typeof child === \"object\" && child !== null) {\n if (blockReturnsPromise(child, depth + 1)) return true;\n }\n }\n return false;\n}\n\n/**\n * Whether `fn` is a function (arrow, function expression, or function\n * declaration) whose body returns a syntactically promise-typed value, even\n * though it is not declared `async`. LogTape awaits any promise the lazy\n * callback returns, e.g. `() => fetchData().then((data) => ({ data }))`, so a\n * non-async helper resolved by reference needs the same handling as an inline\n * one.\n */\nexport function isPromiseReturningCallback(fn: any): boolean {\n if (\n fn?.type !== \"ArrowFunctionExpression\" &&\n fn?.type !== \"FunctionExpression\" &&\n fn?.type !== \"FunctionDeclaration\"\n ) {\n return false;\n }\n const body = fn.body;\n // Concise arrow body: `() => promise`.\n if (body && body.type !== \"BlockStatement\") {\n return isPromiseReturningExpr(body);\n }\n return blockReturnsPromise(body);\n}\n\n/**\n * Whether `arrayNode` is the argument array of a Promise combinator that awaits\n * every element, i.e. `Promise.all([...])` or `Promise.allSettled([...])`.\n * Those consume all element promises, so a log promise inside the array is\n * still awaited when the combinator is awaited. `Promise.race`/`Promise.any`\n * are excluded: they can settle on another promise before the log promise\n * resolves, so the log write is not guaranteed to flush. A bare array literal,\n * by contrast, does not preserve promise semantics at all.\n */\nexport function isPromiseCombinatorArrayArg(arrayNode: any): boolean {\n const parent = arrayNode.parent;\n if (!parent || parent.type !== \"CallExpression\") return false;\n if (!(parent.arguments?.includes(arrayNode) ?? false)) return false;\n const callee = parent.callee;\n return callee?.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.object?.type === \"Identifier\" &&\n callee.object.name === \"Promise\" &&\n callee.property?.type === \"Identifier\" &&\n [\"all\", \"allSettled\"].includes(callee.property.name);\n}\n\n/**\n * Walk the ancestor chain of a log call to decide whether the promise it\n * returns is awaited, returned, or otherwise propagated to a caller that can\n * await it. Returns `true` when the promise is handled and the call therefore\n * needs no `await`.\n *\n * Handled: the call is awaited, returned from a non-discarding function, the\n * concise body of a non-discarding arrow, or chained with\n * `.then`/`.catch`/`.finally`, or it sits inside `Promise.all`/`allSettled`.\n * Not handled (the walk stops and returns `false`): the promise is wrapped in\n * an object/array literal, awaiting a `map()`/`flatMap()` array (which awaits\n * the array, not its element promises), dropped by a discarding callback\n * (`forEach` etc.), or it reaches a statement boundary unconsumed.\n */\nexport function isLogPromiseHandled(node: any): boolean {\n let current: any = node;\n while (current) {\n const ancestor: any = current.parent;\n if (!ancestor) break;\n if (ancestor.type === \"AwaitExpression\") {\n // Awaiting a map()/flatMap() result (or such a result chained through\n // array methods) awaits the array, not the element promises inside it;\n // only a Promise combinator does.\n if (isMapResultChain(current)) break;\n return true;\n }\n // A return makes the promise the enclosing function's return value. If\n // that function is a discarded call argument (e.g. a forEach()/map()\n // callback), keep walking from it so the outer call decides whether the\n // promise is awaited or discarded; if it is a normal or stored function,\n // trust its caller and treat it as propagated.\n if (ancestor.type === \"ReturnStatement\") {\n // Returning a map()/flatMap() result (or one chained through array\n // methods) propagates the array, not the element promises.\n if (isMapResultChain(current)) break;\n const fn = findEnclosingFunction(ancestor);\n if (fn && isCallArgumentFunction(fn)) {\n // A discarder (forEach etc.) drops the returned promise, so the walk\n // must stop rather than reach an outer await/return.\n if (isDiscardedCallbackArgument(fn)) break;\n current = fn;\n continue;\n }\n return true;\n }\n // Concise arrow body: `() => logger.debug(...)` makes the promise the\n // arrow's return value, the same as a return statement. Apply the same\n // rule: keep walking when the arrow is a non-discarding callback, otherwise\n // treat it as propagated to an unknown caller (handled).\n if (\n ancestor.type === \"ArrowFunctionExpression\" &&\n ancestor.body === current\n ) {\n if (!isCallArgumentFunction(ancestor)) return true;\n // A discarder (forEach etc.) drops the promise; stop the walk.\n if (isDiscardedCallbackArgument(ancestor)) break;\n // Otherwise fall through and keep walking from the arrow itself.\n }\n // .then()/.catch()/.finally() must be actual non-computed calls. Computed\n // accesses like obj[then]() use a variable, not the method.\n if (ancestor.type === \"MemberExpression\") {\n const prop = ancestor.property;\n const isPromiseMethod = !ancestor.computed\n ? prop?.type === \"Identifier\" &&\n [\"then\", \"catch\", \"finally\"].includes(prop.name)\n : prop?.type === \"Literal\" &&\n [\"then\", \"catch\", \"finally\"].includes(prop.value);\n if (isPromiseMethod) {\n const grandAncestor: any = ancestor.parent;\n if (\n grandAncestor?.type === \"CallExpression\" &&\n grandAncestor.callee === ancestor\n ) {\n return true;\n }\n }\n }\n // An array or object literal wraps the promise; awaiting or returning the\n // container does not await or propagate the promise itself. The one\n // exception is an array passed to a Promise combinator (Promise.all etc.),\n // which consumes its elements.\n if (ancestor.type === \"ObjectExpression\") break;\n if (ancestor.type === \"ArrayExpression\") {\n // Break for a bare array. For a Promise.all/allSettled array, continue\n // only when the element we came through is itself a promise: if it is a\n // map()/flatMap() result (an array of promises), the combinator awaits\n // that array, not its inner promises, so the log promise stays unhandled.\n if (!isPromiseCombinatorArrayArg(ancestor) || isMapResultChain(current)) {\n break;\n }\n }\n // A sequence (comma) operator yields only its last operand; a log call in\n // an earlier position is evaluated for its side effect and its promise is\n // dropped.\n if (\n ancestor.type === \"SequenceExpression\" &&\n ancestor.expressions?.[ancestor.expressions.length - 1] !== current\n ) {\n break;\n }\n // A unary or binary operator consumes the promise as a plain value\n // (coercion, negation, etc.), so the awaited result is never the log\n // promise itself.\n if (\n ancestor.type === \"UnaryExpression\" ||\n ancestor.type === \"BinaryExpression\"\n ) {\n break;\n }\n // `a && b` yields its right operand when the left is truthy, and a promise\n // is always truthy, so a log promise in the left of && is discarded. (||\n // and ?? in the left position propagate it, so they are left handled.)\n if (\n ancestor.type === \"LogicalExpression\" &&\n ancestor.operator === \"&&\" &&\n ancestor.left === current\n ) {\n break;\n }\n // A conditional's test is coerced to a boolean and discarded; only the\n // taken branch propagates its value.\n if (\n ancestor.type === \"ConditionalExpression\" &&\n ancestor.test === current\n ) {\n break;\n }\n // Stop at statement boundaries.\n if (\n ancestor.type === \"ExpressionStatement\" ||\n ancestor.type === \"VariableDeclarator\" ||\n ancestor.type === \"AssignmentExpression\"\n ) {\n break;\n }\n current = ancestor;\n }\n return false;\n}\n\n/**\n * Whether `await ` can be safely inserted before a log call as an autofix.\n * Only a standalone statement inside an async function qualifies: inserting\n * `await` where the call's value is used (assigned, passed as an argument,\n * returned) would change a `Promise<void>` into `void` and can break code that\n * uses that promise.\n */\nexport function canInsertAwait(node: any): boolean {\n const enclosingFn = findEnclosingFunction(node);\n return enclosingFn != null && enclosingFn.async === true &&\n node.parent?.type === \"ExpressionStatement\";\n}\n\n/**\n * Resolve the static name of an object property key, or `null` when the key is\n * computed from a non-literal expression (e.g. `[someVar]`). A plain key\n * (`loggers`), a string-literal key (`\"loggers\"`), and a computed\n * string-literal key (`[\"loggers\"]`) all resolve to their name, but a computed\n * identifier key like `[loggers]` (a variable) does not.\n */\nexport function staticKeyName(prop: any): string | null {\n if (!prop.computed) {\n if (typeof prop.key?.name === \"string\") return prop.key.name;\n if (typeof prop.key?.value === \"string\") return prop.key.value;\n return null;\n }\n // A computed key must be a string literal or a no-interpolation template\n // literal ([`category`]); a numeric literal ({ [0]: ... }) is not a static\n // name.\n if (prop.key?.type === \"Literal\" && typeof prop.key.value === \"string\") {\n return prop.key.value;\n }\n if (\n prop.key?.type === \"TemplateLiteral\" &&\n prop.key.expressions?.length === 0 &&\n prop.key.quasis?.length === 1\n ) {\n const cooked = prop.key.quasis[0]?.value?.cooked ??\n prop.key.quasis[0]?.cooked;\n return typeof cooked === \"string\" ? cooked : null;\n }\n return null;\n}\n\n/**\n * Whether an AST node is the string `value`, written either as a plain string\n * literal or as a template literal with no interpolations (a backtick constant\n * such as `` `logtape` ``). Deno's `TemplateElement` exposes `cooked`\n * directly; ESTree nests it under `value.cooked`, so accept either shape.\n */\nexport function isStringLiteral(node: any, value: string): boolean {\n if (node?.type === \"Literal\") return node.value === value;\n if (node?.type === \"TemplateLiteral\") {\n const cooked = node.quasis?.[0]?.value?.cooked ?? node.quasis?.[0]?.cooked;\n return node.expressions?.length === 0 &&\n node.quasis?.length === 1 &&\n cooked === value;\n }\n return false;\n}\n\n/**\n * Whether an AST node is the array literal `[\"logtape\"]` or\n * `[\"logtape\", \"meta\"]`.\n */\nexport function isLogtapeMetaArray(node: any): boolean {\n if (node?.type !== \"ArrayExpression\") return false;\n const elems = node.elements;\n if (!isStringLiteral(elems[0], \"logtape\")) return false;\n if (elems.length === 1) return true;\n return elems.length === 2 && isStringLiteral(elems[1], \"meta\");\n}\n\n/**\n * Whether a logger entry covers the meta category with at least one non-empty\n * sinks list. Only the array form (`[\"logtape\"]` or `[\"logtape\", \"meta\"]`)\n * configures the meta logger: core's `configureInternal()` meta check inspects\n * the category as an array, so a bare string `category: \"logtape\"` leaves the\n * meta logger unconfigured and must not satisfy the rule.\n */\nexport function isMetaLoggerEntry(entry: any): boolean {\n if (!entry || entry.type !== \"ObjectExpression\") return false;\n\n // A spread element (e.g. { ...metaLogger }) makes the entry impossible to\n // analyze statically; assume it may configure the meta logger so the rule\n // does not warn on a config it cannot see into.\n if (entry.properties?.some((p: any) => p.type === \"SpreadElement\")) {\n return true;\n }\n\n let categoryNode: any = null;\n let sinksNode: any = null;\n\n for (const prop of entry.properties) {\n if (prop.type !== \"Property\") continue;\n const keyName = staticKeyName(prop);\n // Unwrap a type assertion on the value (e.g. `[\"logtape\", \"meta\"] as\n // const`) so an asserted category or sinks array is still recognized.\n if (keyName === \"category\") categoryNode = unwrapTypeAssertion(prop.value);\n if (keyName === \"sinks\") sinksNode = unwrapTypeAssertion(prop.value);\n }\n\n if (!categoryNode) return false;\n if (!isLogtapeMetaArray(categoryNode)) return false;\n\n if (!sinksNode) return false;\n // Non-literal (e.g. variable reference): assume non-empty to avoid false\n // positives.\n if (sinksNode.type !== \"ArrayExpression\") return true;\n return sinksNode.elements.length > 0;\n}\n\n/**\n * Whether a `configure()`/`configureSync()` argument lacks a dedicated meta\n * logger sink and so should be reported. Returns `false` (no report) when the\n * argument is not an object literal, uses spread elements that may supply the\n * meta logger, or already has a logger entry for the meta category.\n */\nexport function configNeedsMetaSink(configArg: any): boolean {\n if (!configArg || configArg.type !== \"ObjectExpression\") return false;\n\n const properties = configArg.properties ?? [];\n const loggersProperty = properties.find(\n (p: any) => p.type === \"Property\" && staticKeyName(p) === \"loggers\",\n );\n const hasSpread = properties.some((p: any) => p.type === \"SpreadElement\");\n if (!loggersProperty) return !hasSpread;\n\n const loggersValue = unwrapTypeAssertion(loggersProperty.value);\n if (loggersValue.type !== \"ArrayExpression\") return false;\n\n const hasLoggerSpread = loggersValue.elements?.some(\n (el: any) => el?.type === \"SpreadElement\",\n );\n if (hasLoggerSpread) return false;\n\n return !loggersValue.elements?.some(isMetaLoggerEntry);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ast.js","names":["LOG_METHODS: Set<string>","ASYNC_FUNCTION_TYPES: Set<string>","source: unknown","node: any","callee: any","lazyNames: Set<string>","args: any","propsObject: any","prop: any","fn: any","DISCARDING_ARRAY_METHODS: Set<string>","FIRE_AND_FORGET_GLOBALS: Set<string>","ELEMENT_PRESERVING_ARRAY_METHODS: Set<string>","arrayNode: any","current: any","ancestor: any","grandAncestor: any","value: string","entry: any","p: any","categoryNode: any","sinksNode: any","configArg: any","el: any"],"sources":["../../src/core/ast.ts"],"sourcesContent":["/**\n * Shared, host-agnostic AST analysis used by both the ESLint rules\n * (`../rules/`) and the Deno Lint plugin (`../deno/plugin.ts`).\n *\n * Every function here works on plain ESTree-shaped AST nodes and depends only\n * on `node.type` / `node.parent` style traversal, so the same logic runs under\n * ESLint, Oxlint, and Deno Lint. Divergences between the host ASTs (e.g. Deno\n * exposing `TemplateElement.cooked` directly where ESTree nests it under\n * `value.cooked`) are absorbed here with `??` fallbacks, so an edge case is\n * fixed once rather than in two drifting copies.\n *\n * This module must not import from `eslint`, not even types: the Deno plugin\n * imports it and must stay loadable without the ESLint package present.\n *\n * @module\n */\n\n// The lint AST is untyped and differs across hosts, so these helpers operate\n// on `any` nodes by design.\n// deno-lint-ignore-file no-explicit-any\n\n/**\n * Log method names that the LogTape lint rules check.\n */\nexport const LOG_METHODS: Set<string> = new Set([\n \"trace\",\n \"debug\",\n \"info\",\n \"warn\",\n \"warning\",\n \"error\",\n \"fatal\",\n]);\n\n/**\n * AST node types that introduce their own function scope.\n */\nexport const ASYNC_FUNCTION_TYPES: Set<string> = new Set([\n \"ArrowFunctionExpression\",\n \"FunctionExpression\",\n \"FunctionDeclaration\",\n]);\n\n/**\n * Maximum depth for the recursive AST scans. The AST is finite, so this is a\n * safety net against pathological nesting rather than an expected limit; it is\n * generous enough to cover deeply nested log property objects (complex API\n * payloads, ORM entities) without false negatives.\n */\nconst MAX_RECURSION_DEPTH = 100;\n\n/**\n * Whether an import source refers to the LogTape core package. Accepts the\n * bare specifier (`@logtape/logtape`) as well as direct Deno-style `jsr:` and\n * `npm:` specifiers with an optional version suffix (e.g.\n * `jsr:@logtape/logtape` or `npm:@logtape/logtape@^1.0.0`).\n */\nexport function isLogtapeImportSource(source: unknown): boolean {\n return typeof source === \"string\" &&\n /^(?:(?:jsr|npm):)?@logtape\\/logtape(?:@[^/]+)?$/.test(source);\n}\n\n/**\n * Unwrap TypeScript type-assertion wrappers around an expression (`x as T`,\n * `<T>x`, `x satisfies T`, `x!`) so the rules analyze the underlying node.\n * Each wrapper exposes the inner node as `.expression`. Returns the node\n * unchanged when it is not such a wrapper.\n */\nexport function unwrapTypeAssertion(node: any): any {\n while (\n node &&\n (node.type === \"TSAsExpression\" ||\n node.type === \"TSTypeAssertion\" ||\n node.type === \"TSSatisfiesExpression\" ||\n node.type === \"TSNonNullExpression\")\n ) {\n node = node.expression;\n }\n return node;\n}\n\n/**\n * Resolve the log method name from a member-expression callee\n * (`logger.debug` -> `\"debug\"`), supporting computed string-literal access\n * (`logger[\"debug\"]`). Returns `null` for a computed non-literal property or a\n * non-member callee.\n */\nexport function logMethodName(callee: any): string | null {\n if (!callee || callee.type !== \"MemberExpression\") return null;\n if (!callee.computed) return callee.property?.name ?? null;\n // A computed key must be a string literal; a numeric literal (logger[0]) is\n // not a method name and must not be returned as one.\n return callee.property?.type === \"Literal\" &&\n typeof callee.property.value === \"string\"\n ? callee.property.value\n : null;\n}\n\n/**\n * Recursively check whether an AST node contains an eagerly evaluated call\n * anywhere in its subtree. Only inspects own-enumerable child nodes, skipping\n * metadata fields. A LogTape `lazy(...)` call (whose local names are in\n * `lazyNames`) is already deferred, so it is not counted as eager; its\n * arguments are still inspected, so `lazy(expensive())` is caught while\n * `lazy(() => expensive())` is not.\n */\nexport function containsCallExpression(\n node: any,\n lazyNames: Set<string>,\n depth = 0,\n): boolean {\n if (depth > MAX_RECURSION_DEPTH || !node || typeof node !== \"object\") {\n return false;\n }\n const isLazyCall = node.type === \"CallExpression\" &&\n node.callee?.type === \"Identifier\" && lazyNames.has(node.callee.name);\n if (\n !isLazyCall && (\n node.type === \"CallExpression\" ||\n node.type === \"OptionalCallExpression\" ||\n node.type === \"NewExpression\" ||\n node.type === \"TaggedTemplateExpression\" ||\n node.type === \"ImportExpression\"\n )\n ) {\n return true;\n }\n // Don't recurse into function bodies — calls inside them are deferred,\n // not eagerly evaluated at the call site.\n if (\n node.type === \"ArrowFunctionExpression\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"FunctionDeclaration\"\n ) {\n return false;\n }\n // Enumerate with for-in, not Object.keys: Deno's lint AST exposes child\n // nodes as enumerable getters on the prototype (Object.keys returns []),\n // while ESTree exposes them as own properties; for-in covers both. `parent`\n // is non-enumerable in Deno and skipped below for ESTree.\n for (const key in node) {\n if (\n key === \"type\" || key === \"start\" || key === \"end\" ||\n key === \"loc\" || key === \"parent\" || key === \"range\"\n ) {\n continue;\n }\n const child = node[key];\n if (Array.isArray(child)) {\n for (const item of child) {\n if (\n typeof item === \"object\" && item !== null &&\n containsCallExpression(item, lazyNames, depth + 1)\n ) return true;\n }\n } else if (typeof child === \"object\" && child !== null) {\n if (containsCallExpression(child, lazyNames, depth + 1)) return true;\n }\n }\n return false;\n}\n\n/**\n * Recursively check whether a node contains an `AwaitExpression` or\n * `YieldExpression` in the same function scope. Does not descend into nested\n * function bodies.\n */\nexport function containsAwaitOrYield(node: any, depth = 0): boolean {\n if (depth > MAX_RECURSION_DEPTH || !node || typeof node !== \"object\") {\n return false;\n }\n if (node.type === \"AwaitExpression\" || node.type === \"YieldExpression\") {\n return true;\n }\n if (\n node.type === \"ArrowFunctionExpression\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"FunctionDeclaration\"\n ) {\n return false;\n }\n // Enumerate with for-in, not Object.keys: Deno's lint AST exposes child\n // nodes as enumerable getters on the prototype (Object.keys returns []),\n // while ESTree exposes them as own properties; for-in covers both. `parent`\n // is non-enumerable in Deno and skipped below for ESTree.\n for (const key in node) {\n if (\n key === \"type\" || key === \"start\" || key === \"end\" ||\n key === \"loc\" || key === \"parent\" || key === \"range\"\n ) {\n continue;\n }\n const child = node[key];\n if (Array.isArray(child)) {\n for (const item of child) {\n if (\n typeof item === \"object\" && item !== null &&\n containsAwaitOrYield(item, depth + 1)\n ) return true;\n }\n } else if (typeof child === \"object\" && child !== null) {\n if (containsAwaitOrYield(child, depth + 1)) return true;\n }\n }\n return false;\n}\n\n/**\n * From a log call's argument list, select the eager properties object and note\n * whether it came from the properties-only overload. The properties object is\n * the second argument in the message+properties form\n * (`logger.debug(\"msg\", { ... })`) or the first argument in the\n * properties-only form (`logger.debug({ ... })`). TypeScript type assertions\n * (e.g. `{ ... } as const`) are unwrapped first. Returns `null` when neither\n * argument is an object literal.\n *\n * `propsObject` is the unwrapped object (for detection and the report\n * location); `fixTarget` is the original, still-wrapped argument node, so the\n * autofix replaces the whole `{ ... } as const` rather than leaving the\n * assertion dangling on the new callback. When the argument is not wrapped the\n * two are the same node.\n */\nexport function selectLazyPropsObject(\n args: any,\n): { propsObject: any; fixTarget: any; propertiesOnly: boolean } | null {\n const firstRaw = args?.[0];\n const secondRaw = args?.[1];\n const firstArg = unwrapTypeAssertion(firstRaw);\n const secondArg = unwrapTypeAssertion(secondRaw);\n if (firstArg && firstArg.type === \"ObjectExpression\") {\n return { propsObject: firstArg, fixTarget: firstRaw, propertiesOnly: true };\n }\n if (secondArg && secondArg.type === \"ObjectExpression\") {\n return {\n propsObject: secondArg,\n fixTarget: secondRaw,\n propertiesOnly: false,\n };\n }\n return null;\n}\n\n/**\n * Whether any property (or spread) of a properties object contains an eager\n * call that would benefit from lazy evaluation.\n */\nexport function propsHaveEagerCall(\n propsObject: any,\n lazyNames: Set<string>,\n): boolean {\n return propsObject.properties?.some(\n (prop: any) =>\n (prop.type === \"Property\" &&\n (containsCallExpression(prop.value, lazyNames) ||\n (prop.computed &&\n containsCallExpression(prop.key, lazyNames)))) ||\n (prop.type === \"SpreadElement\" &&\n containsCallExpression(prop.argument, lazyNames)),\n ) ?? false;\n}\n\n/**\n * Whether `node` is an async function literal (arrow or function expression).\n */\nexport function isAsyncFunctionExpr(node: any): boolean {\n return (node?.type === \"ArrowFunctionExpression\" ||\n node?.type === \"FunctionExpression\") && node.async === true;\n}\n\n/**\n * Walk up the parent chain to find the nearest enclosing function. Returns\n * `null` if the top of the tree is reached without finding one.\n */\nexport function findEnclosingFunction(node: any): any {\n let current = node.parent;\n while (current) {\n if (ASYNC_FUNCTION_TYPES.has(current.type)) return current;\n current = current.parent;\n }\n return null;\n}\n\n/**\n * Whether `fn` is a function passed directly as a call argument (e.g. an array\n * `.map()`/`.forEach()` callback). Such a function's return value is decided\n * by the receiving call, so a promise it returns may be awaited or discarded.\n */\nexport function isCallArgumentFunction(fn: any): boolean {\n const parent = fn?.parent;\n return parent?.type === \"CallExpression\" &&\n parent.callee !== fn &&\n (parent.arguments?.includes(fn) ?? false);\n}\n\n/**\n * Array iteration methods that ignore, coerce, or merely thread the callback's\n * return value, so a promise returned from the callback is not awaited per\n * call. `reduce`/`reduceRight` pass it on as the next accumulator (only the\n * final accumulator is the result), so earlier per-item promises are dropped.\n */\nconst DISCARDING_ARRAY_METHODS: Set<string> = new Set([\n \"forEach\",\n \"filter\",\n \"find\",\n \"findLast\",\n \"findIndex\",\n \"findLastIndex\",\n \"some\",\n \"every\",\n \"reduce\",\n \"reduceRight\",\n]);\n\n/**\n * Global \"fire and forget\" functions that ignore their callback's return value\n * entirely, so a promise returned from the callback is never awaited.\n */\nconst FIRE_AND_FORGET_GLOBALS: Set<string> = new Set([\n \"setTimeout\",\n \"setInterval\",\n \"setImmediate\",\n \"queueMicrotask\",\n \"requestAnimationFrame\",\n \"requestIdleCallback\",\n]);\n\n/**\n * Whether `fn` is a callback argument to a call that ignores or coerces its\n * callback's return value: an array method like `forEach`/`filter`/`some`, or a\n * fire-and-forget global like `setTimeout`/`queueMicrotask`. Such a call does\n * not propagate the callback's promise, so an async log returned from it is\n * dropped and the walk must stop there rather than continue to an outer\n * await/return.\n */\nexport function isDiscardedCallbackArgument(fn: any): boolean {\n const parent = fn?.parent;\n if (!parent || parent.type !== \"CallExpression\") return false;\n if (parent.callee === fn) return false;\n if (!(parent.arguments?.includes(fn) ?? false)) return false;\n const callee = parent.callee;\n // setTimeout(() => ...), queueMicrotask(() => ...), etc.\n if (\n callee?.type === \"Identifier\" && FIRE_AND_FORGET_GLOBALS.has(callee.name)\n ) {\n return true;\n }\n // items.forEach(() => ...) or items[\"forEach\"](() => ...), etc.\n // logMethodName resolves both the plain and computed string-literal forms.\n const methodName = logMethodName(callee);\n return methodName !== null && DISCARDING_ARRAY_METHODS.has(methodName);\n}\n\n/**\n * Whether `node` is the result of a `map()`/`flatMap()` call. Such a call\n * returns an array of the callback's return values, so awaiting or returning it\n * does not await the element promises; only a Promise combinator\n * (`Promise.all`/`allSettled`) awaits those.\n */\nexport function isMapResult(node: any): boolean {\n return node?.type === \"CallExpression\" &&\n node.callee?.type === \"MemberExpression\" &&\n !node.callee.computed &&\n node.callee.property?.type === \"Identifier\" &&\n (node.callee.property.name === \"map\" ||\n node.callee.property.name === \"flatMap\");\n}\n\n/**\n * Array methods that return a new array holding the same elements, so a\n * `map()`/`flatMap()` result chained through them is still an array of the\n * original promises (e.g. `arr.map(cb).filter(Boolean)`). Methods that unwrap\n * to a single element (`find`, `at`) or transform the elements (`map`,\n * `reduce`) are deliberately excluded.\n */\nconst ELEMENT_PRESERVING_ARRAY_METHODS: Set<string> = new Set([\n \"filter\",\n \"slice\",\n \"concat\",\n \"flat\",\n \"reverse\",\n \"sort\",\n \"toSorted\",\n \"toReversed\",\n \"with\",\n]);\n\n/**\n * Whether `node` evaluates to an array of the promises produced by a\n * `map()`/`flatMap()`, either directly or chained through element-preserving\n * array methods (`arr.map(cb).filter(...).slice(...)`). Awaiting or returning\n * such an array awaits the array, not the element promises.\n */\nexport function isMapResultChain(node: any, depth = 0): boolean {\n if (depth > MAX_RECURSION_DEPTH || !node) return false;\n if (isMapResult(node)) return true;\n if (\n node.type === \"CallExpression\" &&\n node.callee?.type === \"MemberExpression\" &&\n !node.callee.computed &&\n node.callee.property?.type === \"Identifier\" &&\n ELEMENT_PRESERVING_ARRAY_METHODS.has(node.callee.property.name)\n ) {\n return isMapResultChain(node.callee.object, depth + 1);\n }\n return false;\n}\n\n/**\n * Whether `node` is an expression that is syntactically a promise. Recognizes\n * `x.then(...)` / `.catch(...)` / `.finally(...)`, `new Promise(...)`, and\n * `Promise.resolve/reject/all/allSettled/race/any(...)`. This cannot see a\n * promise returned by an opaque call (e.g. `fetchData()` whose return type is a\n * promise), which a syntactic lint rule has no type information for.\n */\nexport function isPromiseReturningExpr(node: any): boolean {\n if (!node) return false;\n if (\n node.type === \"CallExpression\" &&\n node.callee?.type === \"MemberExpression\" && !node.callee.computed &&\n node.callee.property?.type === \"Identifier\"\n ) {\n const name = node.callee.property.name;\n if (name === \"then\" || name === \"catch\" || name === \"finally\") return true;\n if (\n node.callee.object?.type === \"Identifier\" &&\n node.callee.object.name === \"Promise\"\n ) return true;\n }\n if (\n node.type === \"NewExpression\" && node.callee?.type === \"Identifier\" &&\n node.callee.name === \"Promise\"\n ) return true;\n return false;\n}\n\n/**\n * Whether a block contains a `return <promise>` statement, without descending\n * into nested functions (whose returns belong to them, not to the block's\n * function).\n */\nexport function blockReturnsPromise(node: any, depth = 0): boolean {\n if (depth > MAX_RECURSION_DEPTH || !node || typeof node !== \"object\") {\n return false;\n }\n if (node.type === \"ReturnStatement\") {\n return isPromiseReturningExpr(node.argument);\n }\n if (\n node.type === \"ArrowFunctionExpression\" ||\n node.type === \"FunctionExpression\" ||\n node.type === \"FunctionDeclaration\"\n ) {\n return false;\n }\n // Enumerate with for-in, not Object.keys: Deno's lint AST exposes child\n // nodes as enumerable getters on the prototype (Object.keys returns []),\n // while ESTree exposes them as own properties; for-in covers both. `parent`\n // is non-enumerable in Deno and skipped below for ESTree.\n for (const key in node) {\n if (\n key === \"type\" || key === \"start\" || key === \"end\" ||\n key === \"loc\" || key === \"parent\" || key === \"range\"\n ) continue;\n const child = node[key];\n if (Array.isArray(child)) {\n for (const item of child) {\n if (\n typeof item === \"object\" && item !== null &&\n blockReturnsPromise(item, depth + 1)\n ) return true;\n }\n } else if (typeof child === \"object\" && child !== null) {\n if (blockReturnsPromise(child, depth + 1)) return true;\n }\n }\n return false;\n}\n\n/**\n * Whether `fn` is a function (arrow, function expression, or function\n * declaration) whose body returns a syntactically promise-typed value, even\n * though it is not declared `async`. LogTape awaits any promise the lazy\n * callback returns, e.g. `() => fetchData().then((data) => ({ data }))`, so a\n * non-async helper resolved by reference needs the same handling as an inline\n * one.\n */\nexport function isPromiseReturningCallback(fn: any): boolean {\n if (\n fn?.type !== \"ArrowFunctionExpression\" &&\n fn?.type !== \"FunctionExpression\" &&\n fn?.type !== \"FunctionDeclaration\"\n ) {\n return false;\n }\n const body = fn.body;\n // Concise arrow body: `() => promise`.\n if (body && body.type !== \"BlockStatement\") {\n return isPromiseReturningExpr(body);\n }\n return blockReturnsPromise(body);\n}\n\n/**\n * Whether `arrayNode` is the argument array of a Promise combinator that awaits\n * every element, i.e. `Promise.all([...])` or `Promise.allSettled([...])`.\n * Those consume all element promises, so a log promise inside the array is\n * still awaited when the combinator is awaited. `Promise.race`/`Promise.any`\n * are excluded: they can settle on another promise before the log promise\n * resolves, so the log write is not guaranteed to flush. A bare array literal,\n * by contrast, does not preserve promise semantics at all.\n */\nexport function isPromiseCombinatorArrayArg(arrayNode: any): boolean {\n const parent = arrayNode.parent;\n if (!parent || parent.type !== \"CallExpression\") return false;\n if (!(parent.arguments?.includes(arrayNode) ?? false)) return false;\n const callee = parent.callee;\n return callee?.type === \"MemberExpression\" &&\n !callee.computed &&\n callee.object?.type === \"Identifier\" &&\n callee.object.name === \"Promise\" &&\n callee.property?.type === \"Identifier\" &&\n [\"all\", \"allSettled\"].includes(callee.property.name);\n}\n\n/**\n * Walk the ancestor chain of a log call to decide whether the promise it\n * returns is awaited, returned, or otherwise propagated to a caller that can\n * await it. Returns `true` when the promise is handled and the call therefore\n * needs no `await`.\n *\n * Handled: the call is awaited, returned from a non-discarding function, the\n * concise body of a non-discarding arrow, or chained with\n * `.then`/`.catch`/`.finally`, or it sits inside `Promise.all`/`allSettled`.\n * Not handled (the walk stops and returns `false`): the promise is wrapped in\n * an object/array literal, awaiting a `map()`/`flatMap()` array (which awaits\n * the array, not its element promises), dropped by a discarding callback\n * (`forEach` etc.), or it reaches a statement boundary unconsumed.\n */\nexport function isLogPromiseHandled(node: any): boolean {\n let current: any = node;\n while (current) {\n const ancestor: any = current.parent;\n if (!ancestor) break;\n if (ancestor.type === \"AwaitExpression\") {\n // Awaiting a map()/flatMap() result (or such a result chained through\n // array methods) awaits the array, not the element promises inside it;\n // only a Promise combinator does.\n if (isMapResultChain(current)) break;\n return true;\n }\n // A return makes the promise the enclosing function's return value. If\n // that function is a discarded call argument (e.g. a forEach()/map()\n // callback), keep walking from it so the outer call decides whether the\n // promise is awaited or discarded; if it is a normal or stored function,\n // trust its caller and treat it as propagated.\n if (ancestor.type === \"ReturnStatement\") {\n // Returning a map()/flatMap() result (or one chained through array\n // methods) propagates the array, not the element promises.\n if (isMapResultChain(current)) break;\n const fn = findEnclosingFunction(ancestor);\n if (fn && isCallArgumentFunction(fn)) {\n // A discarder (forEach etc.) drops the returned promise, so the walk\n // must stop rather than reach an outer await/return.\n if (isDiscardedCallbackArgument(fn)) break;\n current = fn;\n continue;\n }\n return true;\n }\n // Concise arrow body: `() => logger.debug(...)` makes the promise the\n // arrow's return value, the same as a return statement. Apply the same\n // rule: keep walking when the arrow is a non-discarding callback, otherwise\n // treat it as propagated to an unknown caller (handled).\n if (\n ancestor.type === \"ArrowFunctionExpression\" &&\n ancestor.body === current\n ) {\n if (!isCallArgumentFunction(ancestor)) return true;\n // A discarder (forEach etc.) drops the promise; stop the walk.\n if (isDiscardedCallbackArgument(ancestor)) break;\n // Otherwise fall through and keep walking from the arrow itself.\n }\n // .then()/.catch()/.finally() must be actual non-computed calls. Computed\n // accesses like obj[then]() use a variable, not the method.\n if (ancestor.type === \"MemberExpression\") {\n const prop = ancestor.property;\n const isPromiseMethod = !ancestor.computed\n ? prop?.type === \"Identifier\" &&\n [\"then\", \"catch\", \"finally\"].includes(prop.name)\n : prop?.type === \"Literal\" &&\n [\"then\", \"catch\", \"finally\"].includes(prop.value);\n if (isPromiseMethod) {\n const grandAncestor: any = ancestor.parent;\n if (\n grandAncestor?.type === \"CallExpression\" &&\n grandAncestor.callee === ancestor\n ) {\n return true;\n }\n }\n }\n // An array or object literal wraps the promise; awaiting or returning the\n // container does not await or propagate the promise itself. The one\n // exception is an array passed to a Promise combinator (Promise.all etc.),\n // which consumes its elements.\n if (ancestor.type === \"ObjectExpression\") break;\n if (ancestor.type === \"ArrayExpression\") {\n // Break for a bare array. For a Promise.all/allSettled array, continue\n // only when the element we came through is itself a promise: if it is a\n // map()/flatMap() result (an array of promises), the combinator awaits\n // that array, not its inner promises, so the log promise stays unhandled.\n if (!isPromiseCombinatorArrayArg(ancestor) || isMapResultChain(current)) {\n break;\n }\n }\n // A sequence (comma) operator yields only its last operand; a log call in\n // an earlier position is evaluated for its side effect and its promise is\n // dropped.\n if (\n ancestor.type === \"SequenceExpression\" &&\n ancestor.expressions?.[ancestor.expressions.length - 1] !== current\n ) {\n break;\n }\n // A unary or binary operator consumes the promise as a plain value\n // (coercion, negation, etc.), so the awaited result is never the log\n // promise itself.\n if (\n ancestor.type === \"UnaryExpression\" ||\n ancestor.type === \"BinaryExpression\"\n ) {\n break;\n }\n // `a && b` yields its right operand when the left is truthy, and a promise\n // is always truthy, so a log promise in the left of && is discarded. (||\n // and ?? in the left position propagate it, so they are left handled.)\n if (\n ancestor.type === \"LogicalExpression\" &&\n ancestor.operator === \"&&\" &&\n ancestor.left === current\n ) {\n break;\n }\n // A conditional's test is coerced to a boolean and discarded; only the\n // taken branch propagates its value.\n if (\n ancestor.type === \"ConditionalExpression\" &&\n ancestor.test === current\n ) {\n break;\n }\n // Stop at statement boundaries.\n if (\n ancestor.type === \"ExpressionStatement\" ||\n ancestor.type === \"VariableDeclarator\" ||\n ancestor.type === \"AssignmentExpression\"\n ) {\n break;\n }\n current = ancestor;\n }\n return false;\n}\n\n/**\n * Whether `await ` can be safely inserted before a log call as an autofix.\n * Only a standalone statement inside an async function qualifies: inserting\n * `await` where the call's value is used (assigned, passed as an argument,\n * returned) would change a `Promise<void>` into `void` and can break code that\n * uses that promise.\n */\nexport function canInsertAwait(node: any): boolean {\n const enclosingFn = findEnclosingFunction(node);\n return enclosingFn != null && enclosingFn.async === true &&\n node.parent?.type === \"ExpressionStatement\";\n}\n\n/**\n * Resolve the static name of an object property key, or `null` when the key is\n * computed from a non-literal expression (e.g. `[someVar]`). A plain key\n * (`loggers`), a string-literal key (`\"loggers\"`), and a computed\n * string-literal key (`[\"loggers\"]`) all resolve to their name, but a computed\n * identifier key like `[loggers]` (a variable) does not.\n */\nexport function staticKeyName(prop: any): string | null {\n if (!prop.computed) {\n if (typeof prop.key?.name === \"string\") return prop.key.name;\n if (typeof prop.key?.value === \"string\") return prop.key.value;\n return null;\n }\n // A computed key must be a string literal or a no-interpolation template\n // literal ([`category`]); a numeric literal ({ [0]: ... }) is not a static\n // name.\n if (prop.key?.type === \"Literal\" && typeof prop.key.value === \"string\") {\n return prop.key.value;\n }\n if (\n prop.key?.type === \"TemplateLiteral\" &&\n prop.key.expressions?.length === 0 &&\n prop.key.quasis?.length === 1\n ) {\n const cooked = prop.key.quasis[0]?.value?.cooked ??\n prop.key.quasis[0]?.cooked;\n return typeof cooked === \"string\" ? cooked : null;\n }\n return null;\n}\n\n/**\n * Whether an AST node is the string `value`, written either as a plain string\n * literal or as a template literal with no interpolations (a backtick constant\n * such as `` `logtape` ``). Deno's `TemplateElement` exposes `cooked`\n * directly; ESTree nests it under `value.cooked`, so accept either shape.\n */\nexport function isStringLiteral(node: any, value: string): boolean {\n if (node?.type === \"Literal\") return node.value === value;\n if (node?.type === \"TemplateLiteral\") {\n const cooked = node.quasis?.[0]?.value?.cooked ?? node.quasis?.[0]?.cooked;\n return node.expressions?.length === 0 &&\n node.quasis?.length === 1 &&\n cooked === value;\n }\n return false;\n}\n\n/**\n * Whether an AST node is the category literal `\"logtape\"`, `[\"logtape\"]`, or\n * `[\"logtape\", \"meta\"]`.\n */\nexport function isLogtapeMetaCategory(node: any): boolean {\n if (isStringLiteral(node, \"logtape\")) return true;\n if (node?.type !== \"ArrayExpression\") return false;\n const elems = node.elements;\n if (!isStringLiteral(elems[0], \"logtape\")) return false;\n if (elems.length === 1) return true;\n return elems.length === 2 && isStringLiteral(elems[1], \"meta\");\n}\n\n/**\n * Whether a logger entry covers the meta category with at least one non-empty\n * sinks list. The string form (`\"logtape\"`) is equivalent to `[\"logtape\"]`,\n * and both configure the meta logger.\n */\nexport function isMetaLoggerEntry(entry: any): boolean {\n if (!entry || entry.type !== \"ObjectExpression\") return false;\n\n // A spread element (e.g. { ...metaLogger }) makes the entry impossible to\n // analyze statically; assume it may configure the meta logger so the rule\n // does not warn on a config it cannot see into.\n if (entry.properties?.some((p: any) => p.type === \"SpreadElement\")) {\n return true;\n }\n\n let categoryNode: any = null;\n let sinksNode: any = null;\n\n for (const prop of entry.properties) {\n if (prop.type !== \"Property\") continue;\n const keyName = staticKeyName(prop);\n // Unwrap a type assertion on the value (e.g. `[\"logtape\", \"meta\"] as\n // const`) so an asserted category or sinks array is still recognized.\n if (keyName === \"category\") categoryNode = unwrapTypeAssertion(prop.value);\n if (keyName === \"sinks\") sinksNode = unwrapTypeAssertion(prop.value);\n }\n\n if (!categoryNode) return false;\n if (!isLogtapeMetaCategory(categoryNode)) return false;\n\n if (!sinksNode) return false;\n // Non-literal (e.g. variable reference): assume non-empty to avoid false\n // positives.\n if (sinksNode.type !== \"ArrayExpression\") return true;\n return sinksNode.elements.length > 0;\n}\n\n/**\n * Whether a `configure()`/`configureSync()` argument lacks a dedicated meta\n * logger sink and so should be reported. Returns `false` (no report) when the\n * argument is not an object literal, uses spread elements that may supply the\n * meta logger, or already has a logger entry for the meta category.\n */\nexport function configNeedsMetaSink(configArg: any): boolean {\n if (!configArg || configArg.type !== \"ObjectExpression\") return false;\n\n const properties = configArg.properties ?? [];\n const loggersProperty = properties.find(\n (p: any) => p.type === \"Property\" && staticKeyName(p) === \"loggers\",\n );\n const hasSpread = properties.some((p: any) => p.type === \"SpreadElement\");\n if (!loggersProperty) return !hasSpread;\n\n const loggersValue = unwrapTypeAssertion(loggersProperty.value);\n if (loggersValue.type !== \"ArrayExpression\") return false;\n\n const hasLoggerSpread = loggersValue.elements?.some(\n (el: any) => el?.type === \"SpreadElement\",\n );\n if (hasLoggerSpread) return false;\n\n return !loggersValue.elements?.some(isMetaLoggerEntry);\n}\n"],"mappings":"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"}
|