@mionjs/devtools 0.8.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/build/eslint/cjs/index.cjs +41 -0
- package/build/eslint/cjs/index.cjs.map +1 -0
- package/build/eslint/cjs/package.json +1 -0
- package/build/eslint/cjs/src/eslint/index.cjs +13 -0
- package/build/eslint/cjs/src/eslint/index.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/index.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.cjs +121 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/enforce-type-imports.d.ts +7 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.cjs +71 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/formatTypeNames.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.cjs +220 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-mixed-union-properties.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.cjs +251 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-type-imports.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.cjs +72 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-typeof-runtype.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.cjs +220 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-unreachable-union-types.d.ts +3 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.cjs +71 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/no-vite-client.d.ts +4 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.cjs +346 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/pure-functions.d.ts +4 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.cjs +328 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/strong-typed-routes.d.ts +4 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.cjs +52 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.cjs.map +1 -0
- package/build/eslint/cjs/src/eslint/rules/type-formats-imports.d.ts +3 -0
- package/build/eslint/cjs/src/pureFns/purityRules.cjs +67 -0
- package/build/eslint/cjs/src/pureFns/purityRules.cjs.map +1 -0
- package/build/eslint/cjs/src/pureFns/purityRules.d.ts +4 -0
- package/build/eslint/esm/index.js +42 -0
- package/build/eslint/esm/index.js.map +1 -0
- package/build/eslint/esm/src/eslint/index.d.ts +3 -0
- package/build/eslint/esm/src/eslint/index.js +14 -0
- package/build/eslint/esm/src/eslint/index.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.d.ts +7 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.js +122 -0
- package/build/eslint/esm/src/eslint/rules/enforce-type-imports.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.js +71 -0
- package/build/eslint/esm/src/eslint/rules/formatTypeNames.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.js +221 -0
- package/build/eslint/esm/src/eslint/rules/no-mixed-union-properties.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.js +252 -0
- package/build/eslint/esm/src/eslint/rules/no-type-imports.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.js +73 -0
- package/build/eslint/esm/src/eslint/rules/no-typeof-runtype.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.js +221 -0
- package/build/eslint/esm/src/eslint/rules/no-unreachable-union-types.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.d.ts +4 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.js +72 -0
- package/build/eslint/esm/src/eslint/rules/no-vite-client.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.d.ts +4 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.js +347 -0
- package/build/eslint/esm/src/eslint/rules/pure-functions.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.d.ts +4 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.js +329 -0
- package/build/eslint/esm/src/eslint/rules/strong-typed-routes.js.map +1 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.d.ts +3 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.js +53 -0
- package/build/eslint/esm/src/eslint/rules/type-formats-imports.js.map +1 -0
- package/build/eslint/esm/src/pureFns/purityRules.d.ts +4 -0
- package/build/eslint/esm/src/pureFns/purityRules.js +67 -0
- package/build/eslint/esm/src/pureFns/purityRules.js.map +1 -0
- package/build/vite-plugin/cjs/index.cjs +14 -0
- package/build/vite-plugin/cjs/index.cjs.map +1 -0
- package/build/vite-plugin/cjs/package.json +1 -0
- package/build/vite-plugin/cjs/src/pureFns/purityRules.cjs +65 -0
- package/build/vite-plugin/cjs/src/pureFns/purityRules.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/pureFns/purityRules.d.ts +4 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.cjs +235 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotCacheGenerator.d.ts +23 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.cjs +134 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/aotDiskCache.d.ts +6 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.cjs +15 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/cjsPackageJsonPlugin.d.ts +2 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.cjs +25 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/constants.d.ts +10 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.cjs +628 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/extractPureFn.d.ts +13 -0
- package/build/vite-plugin/cjs/src/vite-plugin/index.cjs +14 -0
- package/build/vite-plugin/cjs/src/vite-plugin/index.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/index.d.ts +5 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.cjs +404 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/mionVitePlugin.d.ts +36 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.cjs +55 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/resolveModule.d.ts +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.cjs +165 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/transformers.d.ts +10 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.cjs +2 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/types.d.ts +57 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtual-modules.d.cjs +2 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtual-modules.d.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.cjs +68 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.cjs.map +1 -0
- package/build/vite-plugin/cjs/src/vite-plugin/virtualModule.d.ts +2 -0
- package/build/vite-plugin/esm/index.js +14 -0
- package/build/vite-plugin/esm/index.js.map +1 -0
- package/build/vite-plugin/esm/src/pureFns/purityRules.d.ts +4 -0
- package/build/vite-plugin/esm/src/pureFns/purityRules.js +65 -0
- package/build/vite-plugin/esm/src/pureFns/purityRules.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.d.ts +23 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.js +235 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotCacheGenerator.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.d.ts +6 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.js +133 -0
- package/build/vite-plugin/esm/src/vite-plugin/aotDiskCache.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.d.ts +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.js +15 -0
- package/build/vite-plugin/esm/src/vite-plugin/cjsPackageJsonPlugin.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.d.ts +10 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.js +25 -0
- package/build/vite-plugin/esm/src/vite-plugin/constants.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.d.ts +13 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.js +611 -0
- package/build/vite-plugin/esm/src/vite-plugin/extractPureFn.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.d.ts +5 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.js +14 -0
- package/build/vite-plugin/esm/src/vite-plugin/index.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.d.ts +36 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.js +387 -0
- package/build/vite-plugin/esm/src/vite-plugin/mionVitePlugin.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.d.ts +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.js +33 -0
- package/build/vite-plugin/esm/src/vite-plugin/resolveModule.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.d.ts +10 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.js +148 -0
- package/build/vite-plugin/esm/src/vite-plugin/transformers.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.d.ts +57 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.js +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/types.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtual-modules.d.js +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtual-modules.d.js.map +1 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.d.ts +2 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.js +68 -0
- package/build/vite-plugin/esm/src/vite-plugin/virtualModule.js.map +1 -0
- package/package.json +94 -0
- package/src/vite-plugin/virtual-modules.d.ts +48 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-unreachable-union-types.cjs","sources":["../../../../../../src/eslint/rules/no-unreachable-union-types.ts"],"sourcesContent":["/* ########\n * 2024 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {TSESTree, TSESLint, AST_NODE_TYPES} from '@typescript-eslint/utils';\n\ntype PropertyInfo = {\n name: string;\n isOptional: boolean;\n};\n\n/**\n * Extracts property names from an object type (interface body, type literal)\n */\nfunction getObjectTypeProperties(node: TSESTree.TypeNode): PropertyInfo[] | null {\n if (node.type === AST_NODE_TYPES.TSTypeLiteral) {\n const props: PropertyInfo[] = [];\n for (const member of node.members) {\n if (member.type === AST_NODE_TYPES.TSPropertySignature && member.key.type === AST_NODE_TYPES.Identifier) {\n props.push({name: member.key.name, isOptional: !!member.optional});\n }\n }\n return props;\n }\n return null;\n}\n\n/**\n * Checks if typeB (earlier in union) blocks typeA (later in union), making typeA unreachable.\n *\n * TypeB blocks TypeA when:\n * - All properties of TypeB (required + optional) exist in TypeA\n * - TypeA has at least as many required properties as TypeB\n * - TypeA is more specific than TypeB (more properties OR same properties but more required)\n *\n * Examples:\n * - {a: string} blocks {a: string; b: number} - TypeB has 'a' (req), TypeA has 'a' (req) + 'b' (req)\n * - {a?: string} does NOT block {b: number; c: string} - TypeB has 'a', TypeA doesn't have 'a'\n * - {a: string; b?: number} blocks {a: string; b: number} - same props, but TypeA has more required\n *\n * @param typeAProps - Properties of the later type (potentially unreachable)\n * @param typeBProps - Properties of the earlier type (potentially blocking)\n * @returns true if typeB blocks typeA\n */\nfunction isSupersetOf(typeAProps: PropertyInfo[], typeBProps: PropertyInfo[]): boolean {\n const typeARequired = typeAProps.filter((p) => !p.isOptional);\n const typeBRequired = typeBProps.filter((p) => !p.isOptional);\n\n // TypeA must have at least as many required properties as TypeB\n if (typeARequired.length < typeBRequired.length) return false;\n\n // TypeA must be more specific: either more properties OR same properties but more required\n const isMoreSpecific =\n typeAProps.length > typeBProps.length ||\n (typeAProps.length === typeBProps.length && typeARequired.length > typeBRequired.length);\n\n if (!isMoreSpecific) return false;\n\n // Check if ALL properties of TypeB (required and optional) exist in TypeA\n for (const propB of typeBProps) {\n const propA = typeAProps.find((p) => p.name === propB.name);\n // If TypeB has a prop that TypeA doesn't have, B doesn't block A\n if (!propA) return false;\n }\n\n // TypeA has all properties of TypeB and is more specific, so TypeB blocks TypeA\n return true;\n}\n\n/**\n * Checks if a union type has interfaces where one is a superset of another\n * and the subset comes before the superset (making the superset unreachable)\n */\nfunction findUnreachableTypes(unionNode: TSESTree.TSUnionType): {unreachable: TSESTree.TypeNode; blocker: TSESTree.TypeNode}[] {\n const issues: {unreachable: TSESTree.TypeNode; blocker: TSESTree.TypeNode}[] = [];\n const typesWithProps: {node: TSESTree.TypeNode; props: PropertyInfo[]}[] = [];\n\n // Collect all object types with their properties\n for (const typeNode of unionNode.types) {\n const props = getObjectTypeProperties(typeNode);\n if (props && props.length > 0) {\n typesWithProps.push({node: typeNode, props});\n }\n }\n\n // For each pair of types, check if one is a superset of another\n for (let i = 0; i < typesWithProps.length; i++) {\n for (let j = i + 1; j < typesWithProps.length; j++) {\n const typeA = typesWithProps[i];\n const typeB = typesWithProps[j];\n\n // If typeA (earlier) is a subset of typeB (later), typeB is unreachable\n if (isSupersetOf(typeB.props, typeA.props)) {\n issues.push({unreachable: typeB.node, blocker: typeA.node});\n }\n }\n }\n\n return issues;\n}\n\n/**\n * Gets a readable representation of an object type for error messages\n */\nfunction getTypeDescription(node: TSESTree.TypeNode): string {\n if (node.type === AST_NODE_TYPES.TSTypeLiteral) {\n const props = getObjectTypeProperties(node);\n if (props) {\n return `{${props.map((p) => (p.isOptional ? `${p.name}?` : p.name)).join(', ')}}`;\n }\n }\n return 'object type';\n}\n\n/**\n * Gets the router function name if the function is a handler for route/middleFn/headersFn\n */\nfunction getRouterFunctionName(\n func: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration,\n context: TSESLint.RuleContext<any, any>\n): string | null {\n const parent = func.parent;\n if (parent?.type === AST_NODE_TYPES.CallExpression) {\n if (parent.callee.type === AST_NODE_TYPES.Identifier) {\n const functionName = parent.callee.name;\n if (['route', 'middleFn', 'headersFn'].includes(functionName) && isImportedFromMionRouter(functionName, context)) {\n return functionName;\n }\n }\n }\n return null;\n}\n\n/**\n * Checks if the union type or type reference is in a parameter that should be checked\n */\nfunction isInCheckableParameter(\n node: TSESTree.TSUnionType | TSESTree.TSTypeReference,\n func: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression | TSESTree.FunctionDeclaration,\n routerFunctionName: string\n): boolean {\n // Find which parameter contains this union type\n let current: TSESTree.Node | undefined = node.parent;\n while (current && current !== func) {\n // Check if we're in a parameter\n if (\n current.type === AST_NODE_TYPES.Identifier ||\n current.type === AST_NODE_TYPES.ArrayPattern ||\n current.type === AST_NODE_TYPES.ObjectPattern\n ) {\n const paramIndex = func.params.indexOf(current as TSESTree.Parameter);\n if (paramIndex !== -1) {\n // For route and middleFn: skip first parameter (context)\n if ((routerFunctionName === 'route' || routerFunctionName === 'middleFn') && paramIndex >= 1) {\n return true;\n }\n // For headersFn: skip first two parameters (context and headers)\n if (routerFunctionName === 'headersFn' && paramIndex >= 2) {\n return true;\n }\n return false;\n }\n }\n current = current.parent;\n }\n return false;\n}\n\n/**\n * Checks if the union type or type reference is a return type or parameter type of route/middleFn/headersFn\n */\nfunction isRouterUnionType(\n node: TSESTree.TSUnionType | TSESTree.TSTypeReference,\n context: TSESLint.RuleContext<any, any>\n): boolean {\n // Check if we're in a return type annotation or parameter type annotation\n let current: TSESTree.Node | undefined = node.parent;\n while (current) {\n // Check if we're in a function that's used in route/middleFn/headersFn\n if (\n current.type === AST_NODE_TYPES.ArrowFunctionExpression ||\n current.type === AST_NODE_TYPES.FunctionExpression ||\n current.type === AST_NODE_TYPES.FunctionDeclaration\n ) {\n const routerFunctionName = getRouterFunctionName(current, context);\n if (routerFunctionName) {\n // Check if we're in the return type\n if (current.returnType?.typeAnnotation === node || isDescendantOf(node, current.returnType?.typeAnnotation)) {\n return true;\n }\n // Check if we're in a parameter type (excluding context and headers params)\n if (isInCheckableParameter(node, current, routerFunctionName)) {\n return true;\n }\n }\n }\n // Check if we're in a type annotation for Handler/HeaderHandler\n if (current.type === AST_NODE_TYPES.TSTypeAnnotation) {\n const typeAnnotationParent = current.parent;\n if (typeAnnotationParent?.type === AST_NODE_TYPES.Identifier) {\n const declarator = typeAnnotationParent.parent;\n if (declarator?.type === AST_NODE_TYPES.VariableDeclarator) {\n // Check if the type annotation references Handler/HeaderHandler\n if (current.typeAnnotation.type === AST_NODE_TYPES.TSTypeReference) {\n const typeName = current.typeAnnotation.typeName;\n if (typeName.type === AST_NODE_TYPES.Identifier) {\n if (\n (typeName.name === 'Handler' || typeName.name === 'HeaderHandler') &&\n isImportedFromMionRouter(typeName.name, context)\n ) {\n return true;\n }\n }\n }\n }\n }\n }\n current = current.parent;\n }\n return false;\n}\n\n/**\n * Checks if a node is a descendant of another node\n */\nfunction isDescendantOf(node: TSESTree.Node | undefined, ancestor: TSESTree.Node | undefined): boolean {\n if (!node || !ancestor) return false;\n let current: TSESTree.Node | undefined = node;\n while (current) {\n if (current === ancestor) return true;\n current = current.parent;\n }\n return false;\n}\n\n/**\n * Checks if a name is imported from @mionjs/router\n */\nfunction isImportedFromMionRouter(name: string, context: TSESLint.RuleContext<any, any>): boolean {\n const sourceCode = context.sourceCode;\n const program = sourceCode.ast;\n\n for (const statement of program.body) {\n if (statement.type === AST_NODE_TYPES.ImportDeclaration) {\n const source = statement.source.value;\n if (source === '@mionjs/router' || source === '@mionjs/router/') {\n for (const specifier of statement.specifiers) {\n if (\n specifier.type === AST_NODE_TYPES.ImportSpecifier &&\n specifier.imported.type === AST_NODE_TYPES.Identifier &&\n specifier.imported.name === name\n ) {\n return true;\n }\n }\n }\n }\n }\n return false;\n}\n\n/**\n * Resolves a type reference to its actual union type definition\n */\nfunction resolveTypeReference(node: TSESTree.TypeNode, context: TSESLint.RuleContext<any, any>): TSESTree.TSUnionType | null {\n if (node.type !== AST_NODE_TYPES.TSTypeReference) {\n return null;\n }\n\n // Get the type name\n if (node.typeName.type !== AST_NODE_TYPES.Identifier) {\n return null;\n }\n\n const typeName = node.typeName.name;\n const sourceCode = context.sourceCode;\n const program = sourceCode.ast;\n\n // Find the type alias declaration\n for (const statement of program.body) {\n if (statement.type === AST_NODE_TYPES.TSTypeAliasDeclaration) {\n if (statement.id.name === typeName && statement.typeAnnotation.type === AST_NODE_TYPES.TSUnionType) {\n return statement.typeAnnotation;\n }\n }\n }\n\n return null;\n}\n\nconst rule: TSESLint.RuleModule<'unreachableUnionType', []> = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Detect union types where one interface is unreachable at runtime when using isType function because a subset type comes before it',\n },\n messages: {\n unreachableUnionType:\n 'Union type {{unreachableType}} is unreachable at runtime when doing type checking because {{blockerType}} will always match first. ' +\n 'To fix this move the more specific type {{unreachableType}} first within the union, ie: {{unreachableType}} | {{blockerType}} ',\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n return {\n TSUnionType(node: TSESTree.TSUnionType) {\n // Only check unions in router context\n if (!isRouterUnionType(node, context)) {\n return;\n }\n\n const issues = findUnreachableTypes(node);\n for (const issue of issues) {\n context.report({\n node: node,\n messageId: 'unreachableUnionType',\n data: {\n unreachableType: getTypeDescription(issue.unreachable),\n blockerType: getTypeDescription(issue.blocker),\n },\n });\n }\n },\n TSTypeReference(node: TSESTree.TSTypeReference) {\n // Try to resolve the type reference to a union type\n const unionType = resolveTypeReference(node, context);\n if (!unionType) {\n return;\n }\n\n // Only check if this type reference is used in a router context\n if (!isRouterUnionType(node, context)) {\n return;\n }\n\n const issues = findUnreachableTypes(unionType);\n for (const issue of issues) {\n context.report({\n node: node,\n messageId: 'unreachableUnionType',\n data: {\n unreachableType: getTypeDescription(issue.unreachable),\n blockerType: getTypeDescription(issue.blocker),\n },\n });\n }\n },\n };\n },\n};\n\nexport default rule;\n"],"names":["AST_NODE_TYPES"],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const utils = require("@typescript-eslint/utils");
|
|
3
|
+
const purityRules = require("../../pureFns/purityRules.cjs");
|
|
4
|
+
const rule = {
|
|
5
|
+
meta: {
|
|
6
|
+
type: "problem",
|
|
7
|
+
docs: {
|
|
8
|
+
description: "Enforce non-Vite client constraints: require name/bodyHash string literal for pureServerFn() and mapFrom(), and disallow registerPureFnFactory() which requires Vite transforms."
|
|
9
|
+
},
|
|
10
|
+
messages: {
|
|
11
|
+
missingPureFnName: "pureServerFn() requires a name as the second argument (string literal) for non-Vite environments.",
|
|
12
|
+
missingMapFromName: "mapFrom() requires a name as the third argument (string literal) for non-Vite environments.",
|
|
13
|
+
nameNotStringLiteral: "{{callee}}() name argument must be a string literal, not a variable or expression.",
|
|
14
|
+
registerPureFnFactoryNotAllowed: "registerPureFnFactory() is not supported in non-Vite environments. It requires Vite build-time transforms."
|
|
15
|
+
},
|
|
16
|
+
schema: []
|
|
17
|
+
},
|
|
18
|
+
defaultOptions: [],
|
|
19
|
+
create(context) {
|
|
20
|
+
let pureFnNames = null;
|
|
21
|
+
return {
|
|
22
|
+
Program(node) {
|
|
23
|
+
pureFnNames = /* @__PURE__ */ new Map();
|
|
24
|
+
for (const statement of node.body) {
|
|
25
|
+
if (statement.type !== utils.AST_NODE_TYPES.ImportDeclaration) continue;
|
|
26
|
+
const source = statement.source.value;
|
|
27
|
+
if (!purityRules.PURE_FN_SOURCE_PACKAGES.includes(source)) continue;
|
|
28
|
+
for (const specifier of statement.specifiers) {
|
|
29
|
+
if (specifier.type === utils.AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === utils.AST_NODE_TYPES.Identifier) {
|
|
30
|
+
const importedName = specifier.imported.name;
|
|
31
|
+
if (importedName === "pureServerFn" || importedName === "mapFrom" || importedName === "registerPureFnFactory") {
|
|
32
|
+
pureFnNames.set(specifier.local.name, importedName);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
CallExpression(node) {
|
|
39
|
+
if (!pureFnNames || pureFnNames.size === 0) return;
|
|
40
|
+
if (node.callee.type !== utils.AST_NODE_TYPES.Identifier) return;
|
|
41
|
+
const importedName = pureFnNames.get(node.callee.name);
|
|
42
|
+
if (!importedName) return;
|
|
43
|
+
if (importedName === "pureServerFn") {
|
|
44
|
+
if (node.arguments.length < 2) {
|
|
45
|
+
context.report({ node, messageId: "missingPureFnName" });
|
|
46
|
+
} else if (node.arguments[1].type !== utils.AST_NODE_TYPES.Literal || typeof node.arguments[1].value !== "string") {
|
|
47
|
+
context.report({
|
|
48
|
+
node: node.arguments[1],
|
|
49
|
+
messageId: "nameNotStringLiteral",
|
|
50
|
+
data: { callee: "pureServerFn" }
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
} else if (importedName === "mapFrom") {
|
|
54
|
+
if (node.arguments.length < 3) {
|
|
55
|
+
context.report({ node, messageId: "missingMapFromName" });
|
|
56
|
+
} else if (node.arguments[2].type !== utils.AST_NODE_TYPES.Literal || typeof node.arguments[2].value !== "string") {
|
|
57
|
+
context.report({
|
|
58
|
+
node: node.arguments[2],
|
|
59
|
+
messageId: "nameNotStringLiteral",
|
|
60
|
+
data: { callee: "mapFrom" }
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
} else if (importedName === "registerPureFnFactory") {
|
|
64
|
+
context.report({ node, messageId: "registerPureFnFactoryNotAllowed" });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
module.exports = rule;
|
|
71
|
+
//# sourceMappingURL=no-vite-client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-vite-client.cjs","sources":["../../../../../../src/eslint/rules/no-vite-client.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {TSESTree, TSESLint, AST_NODE_TYPES} from '@typescript-eslint/utils';\nimport {PURE_FN_SOURCE_PACKAGES} from '../../pureFns/purityRules.ts';\n\ntype MessageIds = 'missingPureFnName' | 'missingMapFromName' | 'nameNotStringLiteral' | 'registerPureFnFactoryNotAllowed';\n\n/** Enforces non-Vite client constraints: requires names for pureServerFn/mapFrom and disallows registerPureFnFactory */\nconst rule: TSESLint.RuleModule<MessageIds, []> = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Enforce non-Vite client constraints: require name/bodyHash string literal for pureServerFn() and mapFrom(), and disallow registerPureFnFactory() which requires Vite transforms.',\n },\n messages: {\n missingPureFnName:\n 'pureServerFn() requires a name as the second argument (string literal) for non-Vite environments.',\n missingMapFromName: 'mapFrom() requires a name as the third argument (string literal) for non-Vite environments.',\n nameNotStringLiteral: '{{callee}}() name argument must be a string literal, not a variable or expression.',\n registerPureFnFactoryNotAllowed:\n 'registerPureFnFactory() is not supported in non-Vite environments. It requires Vite build-time transforms.',\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n let pureFnNames: Map<string, string> | null = null;\n\n return {\n Program(node: TSESTree.Program) {\n pureFnNames = new Map();\n for (const statement of node.body) {\n if (statement.type !== AST_NODE_TYPES.ImportDeclaration) continue;\n const source = statement.source.value;\n if (!PURE_FN_SOURCE_PACKAGES.includes(source as (typeof PURE_FN_SOURCE_PACKAGES)[number])) continue;\n\n for (const specifier of statement.specifiers) {\n if (\n specifier.type === AST_NODE_TYPES.ImportSpecifier &&\n specifier.imported.type === AST_NODE_TYPES.Identifier\n ) {\n const importedName = specifier.imported.name;\n if (\n importedName === 'pureServerFn' ||\n importedName === 'mapFrom' ||\n importedName === 'registerPureFnFactory'\n ) {\n pureFnNames.set(specifier.local.name, importedName);\n }\n }\n }\n }\n },\n\n CallExpression(node: TSESTree.CallExpression) {\n if (!pureFnNames || pureFnNames.size === 0) return;\n if (node.callee.type !== AST_NODE_TYPES.Identifier) return;\n const importedName = pureFnNames.get(node.callee.name);\n if (!importedName) return;\n\n if (importedName === 'pureServerFn') {\n if (node.arguments.length < 2) {\n context.report({node, messageId: 'missingPureFnName'});\n } else if (\n node.arguments[1].type !== AST_NODE_TYPES.Literal ||\n typeof (node.arguments[1] as TSESTree.Literal).value !== 'string'\n ) {\n context.report({\n node: node.arguments[1],\n messageId: 'nameNotStringLiteral',\n data: {callee: 'pureServerFn'},\n });\n }\n } else if (importedName === 'mapFrom') {\n if (node.arguments.length < 3) {\n context.report({node, messageId: 'missingMapFromName'});\n } else if (\n node.arguments[2].type !== AST_NODE_TYPES.Literal ||\n typeof (node.arguments[2] as TSESTree.Literal).value !== 'string'\n ) {\n context.report({\n node: node.arguments[2],\n messageId: 'nameNotStringLiteral',\n data: {callee: 'mapFrom'},\n });\n }\n } else if (importedName === 'registerPureFnFactory') {\n context.report({node, messageId: 'registerPureFnFactoryNotAllowed'});\n }\n },\n };\n },\n};\n\nexport default rule;\n"],"names":["AST_NODE_TYPES","PURE_FN_SOURCE_PACKAGES"],"mappings":";;;AAaA,MAAM,OAA4C;AAAA,EAC9C,MAAM;AAAA,IACF,MAAM;AAAA,IACN,MAAM;AAAA,MACF,aACI;AAAA,IAAA;AAAA,IAER,UAAU;AAAA,MACN,mBACI;AAAA,MACJ,oBAAoB;AAAA,MACpB,sBAAsB;AAAA,MACtB,iCACI;AAAA,IAAA;AAAA,IAER,QAAQ,CAAA;AAAA,EAAC;AAAA,EAEb,gBAAgB,CAAA;AAAA,EAChB,OAAO,SAAS;AACZ,QAAI,cAA0C;AAE9C,WAAO;AAAA,MACH,QAAQ,MAAwB;AAC5B,0CAAkB,IAAA;AAClB,mBAAW,aAAa,KAAK,MAAM;AAC/B,cAAI,UAAU,SAASA,MAAAA,eAAe,kBAAmB;AACzD,gBAAM,SAAS,UAAU,OAAO;AAChC,cAAI,CAACC,YAAAA,wBAAwB,SAAS,MAAkD,EAAG;AAE3F,qBAAW,aAAa,UAAU,YAAY;AAC1C,gBACI,UAAU,SAASD,MAAAA,eAAe,mBAClC,UAAU,SAAS,SAASA,MAAAA,eAAe,YAC7C;AACE,oBAAM,eAAe,UAAU,SAAS;AACxC,kBACI,iBAAiB,kBACjB,iBAAiB,aACjB,iBAAiB,yBACnB;AACE,4BAAY,IAAI,UAAU,MAAM,MAAM,YAAY;AAAA,cACtD;AAAA,YACJ;AAAA,UACJ;AAAA,QACJ;AAAA,MACJ;AAAA,MAEA,eAAe,MAA+B;AAC1C,YAAI,CAAC,eAAe,YAAY,SAAS,EAAG;AAC5C,YAAI,KAAK,OAAO,SAASA,MAAAA,eAAe,WAAY;AACpD,cAAM,eAAe,YAAY,IAAI,KAAK,OAAO,IAAI;AACrD,YAAI,CAAC,aAAc;AAEnB,YAAI,iBAAiB,gBAAgB;AACjC,cAAI,KAAK,UAAU,SAAS,GAAG;AAC3B,oBAAQ,OAAO,EAAC,MAAM,WAAW,qBAAoB;AAAA,UACzD,WACI,KAAK,UAAU,CAAC,EAAE,SAASA,MAAAA,eAAe,WAC1C,OAAQ,KAAK,UAAU,CAAC,EAAuB,UAAU,UAC3D;AACE,oBAAQ,OAAO;AAAA,cACX,MAAM,KAAK,UAAU,CAAC;AAAA,cACtB,WAAW;AAAA,cACX,MAAM,EAAC,QAAQ,eAAA;AAAA,YAAc,CAChC;AAAA,UACL;AAAA,QACJ,WAAW,iBAAiB,WAAW;AACnC,cAAI,KAAK,UAAU,SAAS,GAAG;AAC3B,oBAAQ,OAAO,EAAC,MAAM,WAAW,sBAAqB;AAAA,UAC1D,WACI,KAAK,UAAU,CAAC,EAAE,SAASA,MAAAA,eAAe,WAC1C,OAAQ,KAAK,UAAU,CAAC,EAAuB,UAAU,UAC3D;AACE,oBAAQ,OAAO;AAAA,cACX,MAAM,KAAK,UAAU,CAAC;AAAA,cACtB,WAAW;AAAA,cACX,MAAM,EAAC,QAAQ,UAAA;AAAA,YAAS,CAC3B;AAAA,UACL;AAAA,QACJ,WAAW,iBAAiB,yBAAyB;AACjD,kBAAQ,OAAO,EAAC,MAAM,WAAW,mCAAkC;AAAA,QACvE;AAAA,MACJ;AAAA,IAAA;AAAA,EAER;AACJ;;"}
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const utils = require("@typescript-eslint/utils");
|
|
3
|
+
const purityRules = require("../../pureFns/purityRules.cjs");
|
|
4
|
+
function buildPureFnImportCache(program) {
|
|
5
|
+
const pureFnNames = /* @__PURE__ */ new Map();
|
|
6
|
+
for (const statement of program.body) {
|
|
7
|
+
if (statement.type !== utils.AST_NODE_TYPES.ImportDeclaration) continue;
|
|
8
|
+
const source = statement.source.value;
|
|
9
|
+
if (!purityRules.PURE_FN_SOURCE_PACKAGES.includes(source)) continue;
|
|
10
|
+
for (const specifier of statement.specifiers) {
|
|
11
|
+
if (specifier.type === utils.AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === utils.AST_NODE_TYPES.Identifier) {
|
|
12
|
+
const importedName = specifier.imported.name;
|
|
13
|
+
if (importedName === "pureServerFn" || importedName === "registerPureFnFactory" || importedName === "mapFrom") {
|
|
14
|
+
pureFnNames.set(specifier.local.name, importedName);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return { pureFnNames };
|
|
20
|
+
}
|
|
21
|
+
function collectBindingNames(node, scope) {
|
|
22
|
+
switch (node.type) {
|
|
23
|
+
case utils.AST_NODE_TYPES.Identifier:
|
|
24
|
+
scope.add(node.name);
|
|
25
|
+
break;
|
|
26
|
+
case utils.AST_NODE_TYPES.ObjectPattern:
|
|
27
|
+
for (const prop of node.properties) {
|
|
28
|
+
if (prop.type === utils.AST_NODE_TYPES.Property) {
|
|
29
|
+
collectBindingNames(prop.value, scope);
|
|
30
|
+
} else if (prop.type === utils.AST_NODE_TYPES.RestElement) {
|
|
31
|
+
collectBindingNames(prop.argument, scope);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
case utils.AST_NODE_TYPES.ArrayPattern:
|
|
36
|
+
for (const element of node.elements) {
|
|
37
|
+
if (element) collectBindingNames(element, scope);
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
case utils.AST_NODE_TYPES.RestElement:
|
|
41
|
+
collectBindingNames(node.argument, scope);
|
|
42
|
+
break;
|
|
43
|
+
case utils.AST_NODE_TYPES.AssignmentPattern:
|
|
44
|
+
collectBindingNames(node.left, scope);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function collectLocalDeclarations(node, scope) {
|
|
49
|
+
if (node.type === utils.AST_NODE_TYPES.VariableDeclarator) {
|
|
50
|
+
collectBindingNames(node.id, scope);
|
|
51
|
+
}
|
|
52
|
+
if (node.type === utils.AST_NODE_TYPES.FunctionDeclaration) {
|
|
53
|
+
if (node.id) scope.add(node.id.name);
|
|
54
|
+
for (const param of node.params) collectBindingNames(param, scope);
|
|
55
|
+
}
|
|
56
|
+
if (node.type === utils.AST_NODE_TYPES.FunctionExpression) {
|
|
57
|
+
if (node.id) scope.add(node.id.name);
|
|
58
|
+
for (const param of node.params) collectBindingNames(param, scope);
|
|
59
|
+
}
|
|
60
|
+
if (node.type === utils.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
61
|
+
for (const param of node.params) collectBindingNames(param, scope);
|
|
62
|
+
}
|
|
63
|
+
if ((node.type === utils.AST_NODE_TYPES.ForOfStatement || node.type === utils.AST_NODE_TYPES.ForInStatement) && node.left.type === utils.AST_NODE_TYPES.VariableDeclaration) {
|
|
64
|
+
for (const decl of node.left.declarations) {
|
|
65
|
+
collectBindingNames(decl.id, scope);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (node.type === utils.AST_NODE_TYPES.CatchClause && node.param) {
|
|
69
|
+
collectBindingNames(node.param, scope);
|
|
70
|
+
}
|
|
71
|
+
for (const key of Object.keys(node)) {
|
|
72
|
+
if (key === "parent") continue;
|
|
73
|
+
const child = node[key];
|
|
74
|
+
if (child && typeof child === "object") {
|
|
75
|
+
if (Array.isArray(child)) {
|
|
76
|
+
for (const item of child) {
|
|
77
|
+
if (item && typeof item === "object" && "type" in item) {
|
|
78
|
+
collectLocalDeclarations(item, scope);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} else if ("type" in child) {
|
|
82
|
+
collectLocalDeclarations(child, scope);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function collectLocalScope(fnNode) {
|
|
88
|
+
const scope = /* @__PURE__ */ new Set();
|
|
89
|
+
for (const param of fnNode.params) {
|
|
90
|
+
collectBindingNames(param, scope);
|
|
91
|
+
}
|
|
92
|
+
if (fnNode.type === utils.AST_NODE_TYPES.FunctionExpression && fnNode.id) {
|
|
93
|
+
scope.add(fnNode.id.name);
|
|
94
|
+
}
|
|
95
|
+
collectLocalDeclarations(fnNode.body, scope);
|
|
96
|
+
return scope;
|
|
97
|
+
}
|
|
98
|
+
function checkPurityViolations(body, localScope, fnTypeLabel, context) {
|
|
99
|
+
const forbiddenSet = purityRules.FORBIDDEN_IDENTIFIERS;
|
|
100
|
+
function visit(node) {
|
|
101
|
+
if (node.type === utils.AST_NODE_TYPES.ThisExpression) {
|
|
102
|
+
context.report({ node, messageId: "purityThis", data: { fnType: fnTypeLabel } });
|
|
103
|
+
}
|
|
104
|
+
if (node.type === utils.AST_NODE_TYPES.AwaitExpression) {
|
|
105
|
+
context.report({ node, messageId: "purityAwait", data: { fnType: fnTypeLabel } });
|
|
106
|
+
}
|
|
107
|
+
if (node.type === utils.AST_NODE_TYPES.YieldExpression) {
|
|
108
|
+
context.report({ node, messageId: "purityYield", data: { fnType: fnTypeLabel } });
|
|
109
|
+
}
|
|
110
|
+
if (node.type === utils.AST_NODE_TYPES.ImportExpression) {
|
|
111
|
+
context.report({ node, messageId: "purityDynamicImport", data: { fnType: fnTypeLabel } });
|
|
112
|
+
}
|
|
113
|
+
if (node.type === utils.AST_NODE_TYPES.Identifier) {
|
|
114
|
+
const name = node.name;
|
|
115
|
+
if (node.parent?.type === utils.AST_NODE_TYPES.MemberExpression && node.parent.property === node && !node.parent.computed) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (node.parent?.type === utils.AST_NODE_TYPES.Property && node.parent.key === node && !node.parent.computed && !node.parent.shorthand) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (forbiddenSet.has(name)) {
|
|
122
|
+
context.report({
|
|
123
|
+
node,
|
|
124
|
+
messageId: "purityForbiddenIdentifier",
|
|
125
|
+
data: { name, fnType: fnTypeLabel }
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
if (!localScope.has(name) && !purityRules.ALLOWED_GLOBALS.has(name)) {
|
|
130
|
+
context.report({
|
|
131
|
+
node,
|
|
132
|
+
messageId: "purityClosureVariable",
|
|
133
|
+
data: { name, fnType: fnTypeLabel }
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
for (const key of Object.keys(node)) {
|
|
138
|
+
if (key === "parent" || key === "typeAnnotation" || key === "returnType" || key === "typeParameters" || key === "typeArguments")
|
|
139
|
+
continue;
|
|
140
|
+
const child = node[key];
|
|
141
|
+
if (child && typeof child === "object") {
|
|
142
|
+
if (Array.isArray(child)) {
|
|
143
|
+
for (const item of child) {
|
|
144
|
+
if (item && typeof item === "object" && "type" in item) {
|
|
145
|
+
visit(item);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} else if ("type" in child) {
|
|
149
|
+
visit(child);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
visit(body);
|
|
155
|
+
}
|
|
156
|
+
function resolveVariableInitializer(name, program) {
|
|
157
|
+
function search(node) {
|
|
158
|
+
if (node.type === utils.AST_NODE_TYPES.VariableDeclarator) {
|
|
159
|
+
if (node.id.type === utils.AST_NODE_TYPES.Identifier && node.id.name === name && node.init) {
|
|
160
|
+
return node.init;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
for (const key of Object.keys(node)) {
|
|
164
|
+
if (key === "parent") continue;
|
|
165
|
+
const child = node[key];
|
|
166
|
+
if (child && typeof child === "object") {
|
|
167
|
+
if (Array.isArray(child)) {
|
|
168
|
+
for (const item of child) {
|
|
169
|
+
if (item && typeof item === "object" && "type" in item) {
|
|
170
|
+
const result = search(item);
|
|
171
|
+
if (result) return result;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
} else if ("type" in child) {
|
|
175
|
+
const result = search(child);
|
|
176
|
+
if (result) return result;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
return search(program);
|
|
183
|
+
}
|
|
184
|
+
function resolveToExpression(node, program) {
|
|
185
|
+
if (node.type === utils.AST_NODE_TYPES.Identifier) {
|
|
186
|
+
return resolveVariableInitializer(node.name, program);
|
|
187
|
+
}
|
|
188
|
+
return node;
|
|
189
|
+
}
|
|
190
|
+
function isImportedIdentifier(name, program) {
|
|
191
|
+
for (const statement of program.body) {
|
|
192
|
+
if (statement.type !== utils.AST_NODE_TYPES.ImportDeclaration) continue;
|
|
193
|
+
for (const specifier of statement.specifiers) {
|
|
194
|
+
if (specifier.local.name === name) return true;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
function findUnresolvedIdentifier(arg, program) {
|
|
200
|
+
if (arg.type === utils.AST_NODE_TYPES.Identifier) {
|
|
201
|
+
const resolved = resolveVariableInitializer(arg.name, program);
|
|
202
|
+
if (!resolved) return { name: arg.name, node: arg };
|
|
203
|
+
if (resolved.type === utils.AST_NODE_TYPES.ObjectExpression) {
|
|
204
|
+
return findUnresolvedPureFnInObject(resolved, program);
|
|
205
|
+
}
|
|
206
|
+
return { name: arg.name, node: arg };
|
|
207
|
+
}
|
|
208
|
+
if (arg.type === utils.AST_NODE_TYPES.ObjectExpression) {
|
|
209
|
+
return findUnresolvedPureFnInObject(arg, program);
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
function findUnresolvedPureFnInObject(obj, program) {
|
|
214
|
+
for (const prop of obj.properties) {
|
|
215
|
+
if (prop.type !== utils.AST_NODE_TYPES.Property) continue;
|
|
216
|
+
if (prop.key.type !== utils.AST_NODE_TYPES.Identifier || prop.key.name !== "pureFn") continue;
|
|
217
|
+
if (prop.value.type === utils.AST_NODE_TYPES.Identifier) {
|
|
218
|
+
const resolved = resolveToExpression(prop.value, program);
|
|
219
|
+
if (!resolved || resolved.type !== utils.AST_NODE_TYPES.FunctionExpression && resolved.type !== utils.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
220
|
+
return { name: prop.value.name, node: prop.value };
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
function reportUnresolvedArgument(node, callee, program, context) {
|
|
227
|
+
const argIndex = callee === "registerPureFnFactory" ? 2 : callee === "mapFrom" ? 1 : 0;
|
|
228
|
+
const arg = node.arguments[argIndex];
|
|
229
|
+
if (!arg) return;
|
|
230
|
+
const identifierToCheck = findUnresolvedIdentifier(arg, program);
|
|
231
|
+
if (!identifierToCheck) return;
|
|
232
|
+
const name = identifierToCheck.name;
|
|
233
|
+
const reportNode = identifierToCheck.node;
|
|
234
|
+
if (isImportedIdentifier(name, program)) {
|
|
235
|
+
context.report({ node: reportNode, messageId: "importedArgument", data: { callee, name } });
|
|
236
|
+
} else {
|
|
237
|
+
context.report({ node: reportNode, messageId: "unresolvedArgument", data: { callee, name } });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
function extractFromObjectExpression(obj, program) {
|
|
241
|
+
let fnNode = null;
|
|
242
|
+
let isFactory = false;
|
|
243
|
+
for (const prop of obj.properties) {
|
|
244
|
+
if (prop.type !== utils.AST_NODE_TYPES.Property) continue;
|
|
245
|
+
if (prop.key.type !== utils.AST_NODE_TYPES.Identifier) continue;
|
|
246
|
+
if (prop.key.name === "pureFn") {
|
|
247
|
+
const resolved = resolveToExpression(prop.value, program);
|
|
248
|
+
if (resolved && (resolved.type === utils.AST_NODE_TYPES.FunctionExpression || resolved.type === utils.AST_NODE_TYPES.ArrowFunctionExpression)) {
|
|
249
|
+
fnNode = resolved;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (prop.key.name === "isFactory") {
|
|
253
|
+
if (prop.value.type === utils.AST_NODE_TYPES.Literal && prop.value.value === true) {
|
|
254
|
+
isFactory = true;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (fnNode) return { fnNode, isFactory };
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
function extractPureServerFnTarget(node, program) {
|
|
262
|
+
const arg = node.arguments[0];
|
|
263
|
+
if (!arg) return null;
|
|
264
|
+
const resolved = resolveToExpression(arg, program);
|
|
265
|
+
if (!resolved) return null;
|
|
266
|
+
if (resolved.type === utils.AST_NODE_TYPES.FunctionExpression || resolved.type === utils.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
267
|
+
return { fnNode: resolved, isFactory: false };
|
|
268
|
+
}
|
|
269
|
+
if (resolved.type === utils.AST_NODE_TYPES.ObjectExpression) {
|
|
270
|
+
return extractFromObjectExpression(resolved, program);
|
|
271
|
+
}
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
function extractFactoryFnTarget(node, program) {
|
|
275
|
+
const fnArg = node.arguments[2];
|
|
276
|
+
if (!fnArg) return null;
|
|
277
|
+
const resolved = resolveToExpression(fnArg, program);
|
|
278
|
+
if (!resolved) return null;
|
|
279
|
+
if (resolved.type === utils.AST_NODE_TYPES.FunctionExpression || resolved.type === utils.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
280
|
+
return { fnNode: resolved, isFactory: true };
|
|
281
|
+
}
|
|
282
|
+
return null;
|
|
283
|
+
}
|
|
284
|
+
function extractMapFromMapperTarget(node, program) {
|
|
285
|
+
const mapperArg = node.arguments[1];
|
|
286
|
+
if (!mapperArg) return null;
|
|
287
|
+
const resolved = resolveToExpression(mapperArg, program);
|
|
288
|
+
if (!resolved) return null;
|
|
289
|
+
if (resolved.type === utils.AST_NODE_TYPES.FunctionExpression || resolved.type === utils.AST_NODE_TYPES.ArrowFunctionExpression) {
|
|
290
|
+
return { fnNode: resolved, isFactory: false };
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
const rule = {
|
|
295
|
+
meta: {
|
|
296
|
+
type: "problem",
|
|
297
|
+
docs: {
|
|
298
|
+
description: "Validate that functions passed to pureServerFn(), registerPureFnFactory(), and mapFrom() are pure and do not use forbidden identifiers, closures, or side effects."
|
|
299
|
+
},
|
|
300
|
+
messages: {
|
|
301
|
+
purityThis: "'this' is not allowed in {{fnType}}",
|
|
302
|
+
purityAwait: "async/await is not allowed in {{fnType}}",
|
|
303
|
+
purityYield: "generators are not allowed in {{fnType}}",
|
|
304
|
+
purityDynamicImport: "Dynamic import() is not allowed in {{fnType}}",
|
|
305
|
+
purityForbiddenIdentifier: '"{{name}}" is not allowed in {{fnType}}',
|
|
306
|
+
purityClosureVariable: 'Closure variable "{{name}}" is not allowed in {{fnType}}. Pure functions cannot access outer scope variables.',
|
|
307
|
+
importedArgument: '{{callee}}() argument "{{name}}" is imported from another module. Pure functions must be defined inline or as a variable in the same file.',
|
|
308
|
+
unresolvedArgument: '{{callee}}() argument "{{name}}" could not be resolved to a variable declaration in this file. Pure functions must be defined inline or as a variable in the same file.'
|
|
309
|
+
},
|
|
310
|
+
schema: []
|
|
311
|
+
},
|
|
312
|
+
defaultOptions: [],
|
|
313
|
+
create(context) {
|
|
314
|
+
let importCache = null;
|
|
315
|
+
let programNode = null;
|
|
316
|
+
return {
|
|
317
|
+
Program(node) {
|
|
318
|
+
programNode = node;
|
|
319
|
+
importCache = buildPureFnImportCache(node);
|
|
320
|
+
},
|
|
321
|
+
CallExpression(node) {
|
|
322
|
+
if (!importCache || !programNode || importCache.pureFnNames.size === 0) return;
|
|
323
|
+
if (node.callee.type !== utils.AST_NODE_TYPES.Identifier) return;
|
|
324
|
+
const importedName = importCache.pureFnNames.get(node.callee.name);
|
|
325
|
+
if (!importedName) return;
|
|
326
|
+
let target = null;
|
|
327
|
+
if (importedName === "pureServerFn") {
|
|
328
|
+
target = extractPureServerFnTarget(node, programNode);
|
|
329
|
+
} else if (importedName === "registerPureFnFactory") {
|
|
330
|
+
target = extractFactoryFnTarget(node, programNode);
|
|
331
|
+
} else if (importedName === "mapFrom") {
|
|
332
|
+
target = extractMapFromMapperTarget(node, programNode);
|
|
333
|
+
}
|
|
334
|
+
if (!target) {
|
|
335
|
+
reportUnresolvedArgument(node, importedName, programNode, context);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
const fnTypeLabel = target.isFactory ? "factory functions" : "pure functions";
|
|
339
|
+
const localScope = collectLocalScope(target.fnNode);
|
|
340
|
+
checkPurityViolations(target.fnNode.body, localScope, fnTypeLabel, context);
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
module.exports = rule;
|
|
346
|
+
//# sourceMappingURL=pure-functions.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pure-functions.cjs","sources":["../../../../../../src/eslint/rules/pure-functions.ts"],"sourcesContent":["/* ########\n * 2026 mion\n * Author: Ma-jerez\n * License: MIT\n * The software is provided \"as is\", without warranty of any kind.\n * ######## */\n\nimport {TSESTree, TSESLint, AST_NODE_TYPES} from '@typescript-eslint/utils';\nimport {ALLOWED_GLOBALS, FORBIDDEN_IDENTIFIERS, PURE_FN_SOURCE_PACKAGES} from '../../pureFns/purityRules.ts';\n\ntype MessageIds =\n | 'purityThis'\n | 'purityAwait'\n | 'purityYield'\n | 'purityDynamicImport'\n | 'purityForbiddenIdentifier'\n | 'purityClosureVariable'\n | 'importedArgument'\n | 'unresolvedArgument';\n\n/** Cache of pure function imports from @mionjs/core and @mionjs/client */\ninterface PureFnImports {\n /** Maps local name -> imported name for pureServerFn/registerPureFnFactory/mapFrom */\n pureFnNames: Map<string, string>;\n}\n\n/** Builds a cache of pure function imports from @mionjs/core and @mionjs/client */\nfunction buildPureFnImportCache(program: TSESTree.Program): PureFnImports {\n const pureFnNames = new Map<string, string>();\n\n for (const statement of program.body) {\n if (statement.type !== AST_NODE_TYPES.ImportDeclaration) continue;\n const source = statement.source.value;\n if (!PURE_FN_SOURCE_PACKAGES.includes(source as (typeof PURE_FN_SOURCE_PACKAGES)[number])) continue;\n\n for (const specifier of statement.specifiers) {\n if (specifier.type === AST_NODE_TYPES.ImportSpecifier && specifier.imported.type === AST_NODE_TYPES.Identifier) {\n const importedName = specifier.imported.name;\n if (importedName === 'pureServerFn' || importedName === 'registerPureFnFactory' || importedName === 'mapFrom') {\n pureFnNames.set(specifier.local.name, importedName);\n }\n }\n }\n }\n\n return {pureFnNames};\n}\n\n/** Collects all binding names from a pattern (handles destructuring) */\nfunction collectBindingNames(node: TSESTree.Node, scope: Set<string>): void {\n switch (node.type) {\n case AST_NODE_TYPES.Identifier:\n scope.add(node.name);\n break;\n case AST_NODE_TYPES.ObjectPattern:\n for (const prop of node.properties) {\n if (prop.type === AST_NODE_TYPES.Property) {\n collectBindingNames(prop.value, scope);\n } else if (prop.type === AST_NODE_TYPES.RestElement) {\n collectBindingNames(prop.argument, scope);\n }\n }\n break;\n case AST_NODE_TYPES.ArrayPattern:\n for (const element of node.elements) {\n if (element) collectBindingNames(element, scope);\n }\n break;\n case AST_NODE_TYPES.RestElement:\n collectBindingNames(node.argument, scope);\n break;\n case AST_NODE_TYPES.AssignmentPattern:\n collectBindingNames(node.left, scope);\n break;\n }\n}\n\n/** Collects all locally declared variable names from a node tree */\nfunction collectLocalDeclarations(node: TSESTree.Node, scope: Set<string>): void {\n if (node.type === AST_NODE_TYPES.VariableDeclarator) {\n collectBindingNames(node.id, scope);\n }\n\n if (node.type === AST_NODE_TYPES.FunctionDeclaration) {\n if (node.id) scope.add(node.id.name);\n for (const param of node.params) collectBindingNames(param, scope);\n }\n\n if (node.type === AST_NODE_TYPES.FunctionExpression) {\n if (node.id) scope.add(node.id.name);\n for (const param of node.params) collectBindingNames(param, scope);\n }\n\n if (node.type === AST_NODE_TYPES.ArrowFunctionExpression) {\n for (const param of node.params) collectBindingNames(param, scope);\n }\n\n if (\n (node.type === AST_NODE_TYPES.ForOfStatement || node.type === AST_NODE_TYPES.ForInStatement) &&\n node.left.type === AST_NODE_TYPES.VariableDeclaration\n ) {\n for (const decl of node.left.declarations) {\n collectBindingNames(decl.id, scope);\n }\n }\n\n if (node.type === AST_NODE_TYPES.CatchClause && node.param) {\n collectBindingNames(node.param, scope);\n }\n\n // Recurse into children\n for (const key of Object.keys(node)) {\n if (key === 'parent') continue;\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === 'object') {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === 'object' && 'type' in item) {\n collectLocalDeclarations(item as TSESTree.Node, scope);\n }\n }\n } else if ('type' in child) {\n collectLocalDeclarations(child as TSESTree.Node, scope);\n }\n }\n }\n}\n\n/** Collects the full local scope for a function node */\nfunction collectLocalScope(fnNode: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression): Set<string> {\n const scope = new Set<string>();\n\n // Add parameter names\n for (const param of fnNode.params) {\n collectBindingNames(param, scope);\n }\n\n // Add function name (for recursion)\n if (fnNode.type === AST_NODE_TYPES.FunctionExpression && fnNode.id) {\n scope.add(fnNode.id.name);\n }\n\n // Walk body collecting declarations\n collectLocalDeclarations(fnNode.body, scope);\n\n return scope;\n}\n\n/** Checks purity violations by traversing the function body AST */\nfunction checkPurityViolations(\n body: TSESTree.Node,\n localScope: Set<string>,\n fnTypeLabel: string,\n context: TSESLint.RuleContext<MessageIds, []>\n): void {\n const forbiddenSet = FORBIDDEN_IDENTIFIERS;\n\n function visit(node: TSESTree.Node): void {\n if (node.type === AST_NODE_TYPES.ThisExpression) {\n context.report({node, messageId: 'purityThis', data: {fnType: fnTypeLabel}});\n }\n\n if (node.type === AST_NODE_TYPES.AwaitExpression) {\n context.report({node, messageId: 'purityAwait', data: {fnType: fnTypeLabel}});\n }\n\n if (node.type === AST_NODE_TYPES.YieldExpression) {\n context.report({node, messageId: 'purityYield', data: {fnType: fnTypeLabel}});\n }\n\n if (node.type === AST_NODE_TYPES.ImportExpression) {\n context.report({node, messageId: 'purityDynamicImport', data: {fnType: fnTypeLabel}});\n }\n\n if (node.type === AST_NODE_TYPES.Identifier) {\n const name = node.name;\n\n // Skip non-computed property access (obj.prop — only check 'obj')\n if (node.parent?.type === AST_NODE_TYPES.MemberExpression && node.parent.property === node && !node.parent.computed) {\n return;\n }\n\n // Skip non-computed, non-shorthand property keys in object literals\n if (\n node.parent?.type === AST_NODE_TYPES.Property &&\n node.parent.key === node &&\n !node.parent.computed &&\n !node.parent.shorthand\n ) {\n return;\n }\n\n // Check forbidden identifiers\n if (forbiddenSet.has(name)) {\n context.report({\n node,\n messageId: 'purityForbiddenIdentifier',\n data: {name, fnType: fnTypeLabel},\n });\n return;\n }\n\n // Check for closure variables (not local, not allowed global)\n if (!localScope.has(name) && !ALLOWED_GLOBALS.has(name)) {\n context.report({\n node,\n messageId: 'purityClosureVariable',\n data: {name, fnType: fnTypeLabel},\n });\n }\n }\n\n // Recurse into children (skip type-only AST subtrees)\n for (const key of Object.keys(node)) {\n if (\n key === 'parent' ||\n key === 'typeAnnotation' ||\n key === 'returnType' ||\n key === 'typeParameters' ||\n key === 'typeArguments'\n )\n continue;\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === 'object') {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === 'object' && 'type' in item) {\n visit(item as TSESTree.Node);\n }\n }\n } else if ('type' in child) {\n visit(child as TSESTree.Node);\n }\n }\n }\n }\n\n visit(body);\n}\n\n/** Resolves a variable identifier to its initializer expression within the same module (searches all scopes) */\nfunction resolveVariableInitializer(name: string, program: TSESTree.Program): TSESTree.Expression | null {\n function search(node: TSESTree.Node): TSESTree.Expression | null {\n if (node.type === AST_NODE_TYPES.VariableDeclarator) {\n if (node.id.type === AST_NODE_TYPES.Identifier && node.id.name === name && node.init) {\n return node.init;\n }\n }\n for (const key of Object.keys(node)) {\n if (key === 'parent') continue;\n const child = (node as unknown as Record<string, unknown>)[key];\n if (child && typeof child === 'object') {\n if (Array.isArray(child)) {\n for (const item of child) {\n if (item && typeof item === 'object' && 'type' in item) {\n const result = search(item as TSESTree.Node);\n if (result) return result;\n }\n }\n } else if ('type' in child) {\n const result = search(child as TSESTree.Node);\n if (result) return result;\n }\n }\n }\n return null;\n }\n return search(program);\n}\n\n/** Extracts a function or object expression from a node, resolving variable references */\nfunction resolveToExpression(node: TSESTree.Node, program: TSESTree.Program): TSESTree.Expression | null {\n if (node.type === AST_NODE_TYPES.Identifier) {\n return resolveVariableInitializer(node.name, program);\n }\n return node as TSESTree.Expression;\n}\n\n/** Checks if an identifier name is an import from any module */\nfunction isImportedIdentifier(name: string, program: TSESTree.Program): boolean {\n for (const statement of program.body) {\n if (statement.type !== AST_NODE_TYPES.ImportDeclaration) continue;\n for (const specifier of statement.specifiers) {\n if (specifier.local.name === name) return true;\n }\n }\n return false;\n}\n\n/** Finds the unresolved identifier in a pure function argument */\nfunction findUnresolvedIdentifier(arg: TSESTree.Node, program: TSESTree.Program): {name: string; node: TSESTree.Node} | null {\n // Direct identifier: pureServerFn(myFn) or registerPureFnFactory('ns', 'id', myFn)\n if (arg.type === AST_NODE_TYPES.Identifier) {\n const resolved = resolveVariableInitializer(arg.name, program);\n if (!resolved) return {name: arg.name, node: arg};\n\n // Resolved to something but extraction still returned null — check object form\n if (resolved.type === AST_NODE_TYPES.ObjectExpression) {\n return findUnresolvedPureFnInObject(resolved, program);\n }\n\n // Resolved to something unexpected (call expression, etc.)\n return {name: arg.name, node: arg};\n }\n\n // Inline object: pureServerFn({ pureFn: importedFn })\n if (arg.type === AST_NODE_TYPES.ObjectExpression) {\n return findUnresolvedPureFnInObject(arg, program);\n }\n\n return null;\n}\n\n/** Finds unresolved pureFn identifier inside an object expression */\nfunction findUnresolvedPureFnInObject(\n obj: TSESTree.ObjectExpression,\n program: TSESTree.Program\n): {name: string; node: TSESTree.Node} | null {\n for (const prop of obj.properties) {\n if (prop.type !== AST_NODE_TYPES.Property) continue;\n if (prop.key.type !== AST_NODE_TYPES.Identifier || prop.key.name !== 'pureFn') continue;\n if (prop.value.type === AST_NODE_TYPES.Identifier) {\n const resolved = resolveToExpression(prop.value, program);\n if (\n !resolved ||\n (resolved.type !== AST_NODE_TYPES.FunctionExpression && resolved.type !== AST_NODE_TYPES.ArrowFunctionExpression)\n ) {\n return {name: (prop.value as TSESTree.Identifier).name, node: prop.value};\n }\n }\n }\n return null;\n}\n\n/** Reports an error when a pure function argument cannot be resolved */\nfunction reportUnresolvedArgument(\n node: TSESTree.CallExpression,\n callee: string,\n program: TSESTree.Program,\n context: TSESLint.RuleContext<MessageIds, []>\n): void {\n const argIndex = callee === 'registerPureFnFactory' ? 2 : callee === 'mapFrom' ? 1 : 0;\n const arg = node.arguments[argIndex];\n if (!arg) return;\n\n const identifierToCheck = findUnresolvedIdentifier(arg, program);\n if (!identifierToCheck) return;\n\n const name = identifierToCheck.name;\n const reportNode = identifierToCheck.node;\n\n if (isImportedIdentifier(name, program)) {\n context.report({node: reportNode, messageId: 'importedArgument', data: {callee, name}});\n } else {\n context.report({node: reportNode, messageId: 'unresolvedArgument', data: {callee, name}});\n }\n}\n\n/** Extracts function + isFactory from an object expression (PureFnDef) */\nfunction extractFromObjectExpression(\n obj: TSESTree.ObjectExpression,\n program: TSESTree.Program\n): {fnNode: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression; isFactory: boolean} | null {\n let fnNode: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression | null = null;\n let isFactory = false;\n\n for (const prop of obj.properties) {\n if (prop.type !== AST_NODE_TYPES.Property) continue;\n if (prop.key.type !== AST_NODE_TYPES.Identifier) continue;\n\n if (prop.key.name === 'pureFn') {\n // pureFn value could be inline or a variable reference\n const resolved = resolveToExpression(prop.value, program);\n if (\n resolved &&\n (resolved.type === AST_NODE_TYPES.FunctionExpression || resolved.type === AST_NODE_TYPES.ArrowFunctionExpression)\n ) {\n fnNode = resolved;\n }\n }\n\n if (prop.key.name === 'isFactory') {\n if (prop.value.type === AST_NODE_TYPES.Literal && prop.value.value === true) {\n isFactory = true;\n }\n }\n }\n\n if (fnNode) return {fnNode, isFactory};\n return null;\n}\n\n/** Extracts the function node and isFactory flag from a pureServerFn() call */\nfunction extractPureServerFnTarget(\n node: TSESTree.CallExpression,\n program: TSESTree.Program\n): {fnNode: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression; isFactory: boolean} | null {\n const arg = node.arguments[0];\n if (!arg) return null;\n\n // Resolve variable reference if needed\n const resolved = resolveToExpression(arg, program);\n if (!resolved) return null;\n\n // Direct function: pureServerFn(function() {}) or pureServerFn(() => {})\n if (resolved.type === AST_NODE_TYPES.FunctionExpression || resolved.type === AST_NODE_TYPES.ArrowFunctionExpression) {\n return {fnNode: resolved, isFactory: false};\n }\n\n // Object form: pureServerFn({ pureFn: fn, isFactory: bool })\n if (resolved.type === AST_NODE_TYPES.ObjectExpression) {\n return extractFromObjectExpression(resolved, program);\n }\n\n return null;\n}\n\n/** Extracts the factory function from a registerPureFnFactory() call */\nfunction extractFactoryFnTarget(\n node: TSESTree.CallExpression,\n program: TSESTree.Program\n): {fnNode: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression; isFactory: true} | null {\n // registerPureFnFactory(namespace, functionID, factoryFn)\n const fnArg = node.arguments[2];\n if (!fnArg) return null;\n\n // Resolve variable reference if needed\n const resolved = resolveToExpression(fnArg, program);\n if (!resolved) return null;\n\n if (resolved.type === AST_NODE_TYPES.FunctionExpression || resolved.type === AST_NODE_TYPES.ArrowFunctionExpression) {\n return {fnNode: resolved, isFactory: true};\n }\n\n return null;\n}\n\n/** Extracts the mapper function from a mapFrom(source, mapper) call */\nfunction extractMapFromMapperTarget(\n node: TSESTree.CallExpression,\n program: TSESTree.Program\n): {fnNode: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression; isFactory: boolean} | null {\n // mapFrom(source, mapper) - mapper is the 2nd argument\n const mapperArg = node.arguments[1];\n if (!mapperArg) return null;\n\n const resolved = resolveToExpression(mapperArg, program);\n if (!resolved) return null;\n\n if (resolved.type === AST_NODE_TYPES.FunctionExpression || resolved.type === AST_NODE_TYPES.ArrowFunctionExpression) {\n return {fnNode: resolved, isFactory: false};\n }\n\n return null;\n}\n\nconst rule: TSESLint.RuleModule<MessageIds, []> = {\n meta: {\n type: 'problem',\n docs: {\n description:\n 'Validate that functions passed to pureServerFn(), registerPureFnFactory(), and mapFrom() are pure and do not use forbidden identifiers, closures, or side effects.',\n },\n messages: {\n purityThis: \"'this' is not allowed in {{fnType}}\",\n purityAwait: 'async/await is not allowed in {{fnType}}',\n purityYield: 'generators are not allowed in {{fnType}}',\n purityDynamicImport: 'Dynamic import() is not allowed in {{fnType}}',\n purityForbiddenIdentifier: '\"{{name}}\" is not allowed in {{fnType}}',\n purityClosureVariable:\n 'Closure variable \"{{name}}\" is not allowed in {{fnType}}. Pure functions cannot access outer scope variables.',\n importedArgument:\n '{{callee}}() argument \"{{name}}\" is imported from another module. Pure functions must be defined inline or as a variable in the same file.',\n unresolvedArgument:\n '{{callee}}() argument \"{{name}}\" could not be resolved to a variable declaration in this file. Pure functions must be defined inline or as a variable in the same file.',\n },\n schema: [],\n },\n defaultOptions: [],\n create(context) {\n let importCache: PureFnImports | null = null;\n let programNode: TSESTree.Program | null = null;\n\n return {\n Program(node) {\n programNode = node;\n importCache = buildPureFnImportCache(node);\n },\n\n CallExpression(node: TSESTree.CallExpression) {\n if (!importCache || !programNode || importCache.pureFnNames.size === 0) return;\n if (node.callee.type !== AST_NODE_TYPES.Identifier) return;\n\n const importedName = importCache.pureFnNames.get(node.callee.name);\n if (!importedName) return;\n\n let target: {\n fnNode: TSESTree.FunctionExpression | TSESTree.ArrowFunctionExpression;\n isFactory: boolean;\n } | null = null;\n\n if (importedName === 'pureServerFn') {\n target = extractPureServerFnTarget(node, programNode);\n } else if (importedName === 'registerPureFnFactory') {\n target = extractFactoryFnTarget(node, programNode);\n } else if (importedName === 'mapFrom') {\n target = extractMapFromMapperTarget(node, programNode);\n }\n\n if (!target) {\n reportUnresolvedArgument(node, importedName, programNode, context);\n return;\n }\n\n const fnTypeLabel = target.isFactory ? 'factory functions' : 'pure functions';\n const localScope = collectLocalScope(target.fnNode);\n checkPurityViolations(target.fnNode.body, localScope, fnTypeLabel, context);\n },\n };\n },\n};\n\nexport default rule;\n"],"names":["AST_NODE_TYPES","PURE_FN_SOURCE_PACKAGES","FORBIDDEN_IDENTIFIERS","ALLOWED_GLOBALS"],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { TSESLint } from '@typescript-eslint/utils';
|
|
2
|
+
type MessageIds = 'purityThis' | 'purityAwait' | 'purityYield' | 'purityDynamicImport' | 'purityForbiddenIdentifier' | 'purityClosureVariable' | 'importedArgument' | 'unresolvedArgument';
|
|
3
|
+
declare const rule: TSESLint.RuleModule<MessageIds, []>;
|
|
4
|
+
export default rule;
|