@nfq/eslint-config 4.0.0-beta.14 → 4.0.0-beta.16
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/esm/rules/custom/component-file-structure.js +9 -1
- package/dist/esm/rules/custom/component-file-structure.js.map +1 -1
- package/dist/esm/rules/custom/hexagonal-dependency-direction.js +5 -2
- package/dist/esm/rules/custom/hexagonal-dependency-direction.js.map +1 -1
- package/dist/esm/rules/custom/no-unbound-method.js.map +1 -1
- package/dist/esm/rules/custom/utils/component-file-structure-utils.js +1 -1
- package/dist/esm/rules/custom/utils/component-file-structure-utils.js.map +1 -1
- package/dist/index.js +13 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/types/rules/custom/utils/component-file-structure-utils.d.ts +17 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ESLintUtils } from '@typescript-eslint/utils';
|
|
2
|
-
import { getComponentInfoFromStatement, getTypeDeclarationInfo, isDefaultComponentExport, collectTypeReferences, isDisplayNameAssignment, isComponentExport, isStyledDeclaration, isStyledFactoryDeclaration } from './utils/component-file-structure-utils.js';
|
|
2
|
+
import { containsJsx, getComponentInfoFromStatement, getTypeDeclarationInfo, isDefaultComponentExport, collectTypeReferences, isDisplayNameAssignment, isComponentExport, isStyledDeclaration, isStyledFactoryDeclaration } from './utils/component-file-structure-utils.js';
|
|
3
3
|
|
|
4
4
|
const createRule$b = ESLintUtils.RuleCreator(name => `https://github.com/nfqde/eslint-config-nfq/blob/master/docs/rules/${name}.md`);
|
|
5
5
|
const ORDER_LABELS = ['Imports', 'Optional declarations', 'Props types', 'Component', 'Component helpers and displayName', 'Exports', 'Styled components and local helper types'];
|
|
@@ -22,10 +22,18 @@ const componentFileStructure = createRule$b({
|
|
|
22
22
|
},
|
|
23
23
|
name: 'component-file-structure',
|
|
24
24
|
create(context) {
|
|
25
|
+
const filename = context.getFilename();
|
|
26
|
+
const filenameLower = filename.toLowerCase();
|
|
27
|
+
if (!filenameLower.endsWith('.tsx') && !filenameLower.endsWith('.jsx')) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
25
30
|
const {
|
|
26
31
|
sourceCode
|
|
27
32
|
} = context;
|
|
28
33
|
const programBody = sourceCode.ast.body;
|
|
34
|
+
if (!containsJsx(sourceCode.ast)) {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
29
37
|
const componentCandidates = programBody.filter(statement => statement.type !== 'TSModuleDeclaration').map(statement => getComponentInfoFromStatement(statement)).filter(info => Boolean(info));
|
|
30
38
|
if (componentCandidates.length === 0) {
|
|
31
39
|
return {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-file-structure.js","sources":["../../../../src/rules/custom/component-file-structure.ts"],"sourcesContent":["import {ESLintUtils} from '@typescript-eslint/utils';\n\nimport {\n collectTypeReferences,\n getComponentInfoFromStatement,\n getTypeDeclarationInfo,\n isComponentExport,\n isDefaultComponentExport,\n isDisplayNameAssignment,\n isStyledDeclaration,\n isStyledFactoryDeclaration\n} from './utils/component-file-structure-utils';\n\nimport type {ComponentInfo} from './utils/component-file-structure-utils';\nimport type {TSESLint, TSESTree} from '@typescript-eslint/utils';\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/nfqde/eslint-config-nfq/blob/master/docs/rules/${name}.md`\n);\n\ntype MessageIds = 'invalidOrder' | 'missingDisplayName' | 'missingNamedExport';\n\nconst ORDER_LABELS = [\n 'Imports',\n 'Optional declarations',\n 'Props types',\n 'Component',\n 'Component helpers and displayName',\n 'Exports',\n 'Styled components and local helper types'\n];\nconst ORDER_MESSAGE = ORDER_LABELS.join(' -> ');\n// eslint-disable-next-line jsdoc/require-jsdoc\nconst getSectionLabel = (section: number) => ORDER_LABELS[section - 1] ?? 'Unknown';\nconst PAGE_TYPE_NAMES = new Set([\n 'NextPage',\n 'NextPageWithLayout',\n 'NextSSRPage',\n 'NextSSRPageWithLayout',\n 'NextSSGPageWithLayout'\n]);\n\nexport const componentFileStructure = createRule<[], MessageIds>({\n defaultOptions: [],\n meta: {\n docs: {description: 'Enforce file structure order for React component files.'},\n messages: {\n // eslint-disable-next-line @stylistic/max-len\n invalidOrder: 'This file structure block is out of order. {{currentName}} is a {{current}} and should be before {{previousName}}. Expected order: {{order}}.',\n missingDisplayName: 'Component {{name}} must set displayName.',\n missingNamedExport: 'Component {{name}} must be exported by name.'\n },\n schema: [],\n type: 'suggestion'\n },\n name: 'component-file-structure',\n /**\n * Creates the rule listener for enforcing component file structure ordering. It analyzes the program body to\n * identify the primary component, its props types, and structural sections. It then reports ordering issues and\n * missing requirements like displayName or named exports.\n *\n * @param context The ESLint rule context used to access source code and report diagnostics.\n * @returns A rule listener that validates component file structure and reports issues.\n *\n * @example\n * ```tsx\n * // eslint-disable-next-line @nfq/component-file-structure\n * const listener = componentFileStructure.create(context);\n * ```\n */\n // eslint-disable-next-line max-lines-per-function\n create(context) {\n const {sourceCode} = context;\n const programBody = sourceCode.ast.body;\n const componentCandidates = programBody\n .filter((statement): statement is TSESTree.Statement => statement.type !== 'TSModuleDeclaration')\n .map(statement => getComponentInfoFromStatement(statement))\n .filter((info): info is ComponentInfo => Boolean(info));\n\n if (componentCandidates.length === 0) {\n return {};\n }\n\n const componentInfo = componentCandidates.sort(\n (left, right) => left.statement.range[0] - right.statement.range[0]\n )[0];\n const componentName = componentInfo.name;\n const typeDeclarations = new Map<string, TSESTree.Node>();\n\n for (const statement of programBody) {\n const info = getTypeDeclarationInfo(statement as TSESTree.Statement);\n\n if (info) {\n typeDeclarations.set(info.name, info.statement);\n }\n }\n\n const propsTypeNames = new Set<string>();\n const propsTypeRefs = new Set<string>();\n const propsTypeNode = componentInfo.propsTypeNode ?? null;\n /**\n * Determines whether the component uses a page-specific type alias. It inspects the component type node\n * and collects all referenced type names. It then checks if any of those names match known page types to\n * decide if the component is a page component.\n *\n * @returns True when the component type includes a known page type; otherwise false.\n *\n * @example\n * ```tsx\n * const isPage = isPageType();\n * ```\n */\n const isPageType = () => {\n const typeNode = componentInfo.componentTypeNode ?? null;\n\n if (!typeNode) {\n return false;\n }\n\n const typeRefs = new Set<string>();\n\n collectTypeReferences(typeNode, typeRefs);\n\n return Array.from(typeRefs).some(name => PAGE_TYPE_NAMES.has(name));\n };\n\n /**\n * Checks whether a statement assigns a page layout helper to the component. It ensures the statement is\n * an assignment expression targeting a member of the component identifier. It then verifies the member name\n * matches supported layout assignment properties.\n *\n * @param statement The statement to inspect for a layout assignment.\n * @returns True when the statement assigns a layout helper on the component; otherwise false.\n *\n * @example\n * ```tsx\n * const hasLayout = isPageLayoutAssignment(statement);\n * ```\n */\n const isPageLayoutAssignment = (statement: TSESTree.Statement) => {\n if (statement.type !== 'ExpressionStatement') {\n return false;\n }\n\n const {expression} = statement;\n\n if (expression.type !== 'AssignmentExpression') {\n return false;\n }\n\n const {left} = expression;\n\n if (left.type !== 'MemberExpression' || left.object.type !== 'Identifier') {\n return false;\n }\n\n if (left.object.name !== componentName || left.property.type !== 'Identifier') {\n return false;\n }\n\n return left.property.name === 'getLayout' || left.property.name === 'getLayoutKey';\n };\n\n const hasDefaultExport = programBody\n .some(statement => isDefaultComponentExport(statement as TSESTree.Statement, componentName));\n const hasLayoutAssignment = programBody\n .some(statement => isPageLayoutAssignment(statement as TSESTree.Statement));\n const isPageComponent = hasDefaultExport && (hasLayoutAssignment || isPageType());\n\n /**\n * Determines whether a class element represents a static displayName member. It checks that the element is a supported\n * property definition shape and that it is declared as static. It then verifies the identifier name matches the expected\n * displayName property, which signals a static display name assignment on a class.\n *\n * @returns True when the element is a static displayName property; otherwise false.\n *\n * @example\n * ```tsx\n * const isDisplayName = hasDisplayNameMember(member);\n * ```\n */\n const hasStaticDisplayName = () => {\n /**\n * Determines whether a class element represents a static displayName member. It checks that the element is a supported\n * property definition shape and that it is declared as static. It then verifies the identifier name matches the expected\n * displayName property, which signals a static display name assignment on a class.\n *\n * @param member The class element to inspect for a static displayName property.\n * @returns True when the element is a static displayName property; otherwise false.\n *\n * @example\n * ```tsx\n * const isDisplayName = hasDisplayNameMember(member);\n * ```\n */\n const hasDisplayNameMember = (member: TSESTree.ClassElement) => {\n // @ts-expect-error\n if (member.type !== 'PropertyDefinition' && member.type !== 'ClassProperty') {\n return false;\n }\n\n if (!member.static || member.key.type !== 'Identifier') {\n return false;\n }\n\n return member.key.name === 'displayName';\n };\n\n if (componentInfo.statement.type === 'ClassDeclaration') {\n return componentInfo.statement.id?.name === componentName\n && componentInfo.statement.body.body.some(hasDisplayNameMember);\n }\n\n if (componentInfo.statement.type === 'VariableDeclaration') {\n for (const declarator of componentInfo.statement.declarations) {\n if (declarator.id.type !== 'Identifier' || declarator.id.name !== componentName) {\n continue;\n }\n\n const {init} = declarator;\n\n if (init?.type === 'ClassExpression') {\n return init.body.body.some(hasDisplayNameMember);\n }\n }\n }\n\n return false;\n };\n\n if (propsTypeNode) {\n collectTypeReferences(propsTypeNode, propsTypeRefs);\n\n for (const name of propsTypeRefs) {\n if (typeDeclarations.has(name)) {\n propsTypeNames.add(name);\n }\n }\n }\n\n /**\n * Determines whether a statement declares a type that is used as the component props. It inspects the statement\n * for a type declaration and compares the declared name against the collected props type names. It returns a\n * boolean so callers can classify statements into the props type section.\n *\n * @param statement The statement to inspect for a props type declaration.\n * @returns True when the statement declares a props type; otherwise false.\n *\n * @example\n * ```tsx\n * const isProps = isPropsTypeDeclaration(statement);\n * ```\n */\n const isPropsTypeDeclaration = (statement: TSESTree.Statement) => {\n const info = getTypeDeclarationInfo(statement);\n\n return Boolean(info && propsTypeNames.has(info.name));\n };\n\n /**\n * Determines whether a statement declares a type that is not used as the component props. It inspects the\n * statement for a type declaration and ensures the declared name is not among the collected props type names.\n * It returns a boolean so callers can classify statements into non-props type sections.\n *\n * @param statement The statement to inspect for a non-props type declaration.\n * @returns True when the statement declares a non-props type; otherwise false.\n *\n * @example\n * ```tsx\n * const isNonProps = isNonPropsTypeDeclaration(statement);\n * ```\n */\n const isNonPropsTypeDeclaration = (statement: TSESTree.Statement) => {\n const info = getTypeDeclarationInfo(statement);\n\n return Boolean(info && !propsTypeNames.has(info.name));\n };\n\n /**\n * Determines whether a statement is an optional variable-like declaration in the component file structure. It\n * filters out the main component statement and styled declarations to avoid misclassification. It considers\n * variable declarations, non-component function declarations, and enum declarations as optional declarations.\n *\n * @param statement The statement to evaluate for optional declaration status.\n * @returns True when the statement should be treated as an optional declaration; otherwise false.\n *\n * @example\n * ```tsx\n * const isOptional = isOptionalVarDeclaration(statement);\n * ```\n */\n const isOptionalVarDeclaration = (statement: TSESTree.Statement) => {\n if (statement.type === 'VariableDeclaration') {\n if (\n statement === componentInfo.statement\n || isStyledDeclaration(statement)\n || isStyledFactoryDeclaration(statement)\n ) {\n return false;\n }\n\n return true;\n }\n\n if (statement.type === 'FunctionDeclaration') {\n return statement !== componentInfo.statement;\n }\n\n return statement.type === 'TSEnumDeclaration';\n };\n\n /**\n * Determines the structural section number for a given statement in a component file. It checks the statement\n * against known component structure elements like imports, the main component, displayName assignment, exports,\n * styled declarations, optional declarations, and props types. It returns a numeric section marker to help\n * enforce ordering rules or null when the statement does not belong to a tracked section.\n *\n * @param statement The statement to classify within the component file structure.\n * @returns The numeric section identifier for the statement, or null when it does not match a section.\n *\n * @example\n * ```tsx\n * const section = sectionForStatement(statement);\n * ```\n */\n const sectionForStatement = (statement: TSESTree.Statement) => {\n if (statement.type === 'ImportDeclaration') {\n return 1;\n }\n\n if (statement === componentInfo.statement) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 4;\n }\n\n if (isDisplayNameAssignment(statement, componentName)) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 5;\n }\n\n if (\n isComponentExport(statement, componentName)\n || isDefaultComponentExport(statement, componentName)\n ) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 6;\n }\n\n if (\n isNonPropsTypeDeclaration(statement)\n && statement.range[0] < componentInfo.statement.range[0]\n ) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 2;\n }\n\n if (\n isStyledDeclaration(statement)\n || isStyledFactoryDeclaration(statement)\n || isNonPropsTypeDeclaration(statement)\n ) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 7;\n }\n\n if (isOptionalVarDeclaration(statement)) {\n if (statement.range[0] > componentInfo.statement.range[0]) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 5;\n }\n\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 2;\n }\n\n if (isPropsTypeDeclaration(statement)) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 3;\n }\n\n return null;\n };\n\n /**\n * Determines a human-readable label for a statement within the component file structure. It inspects the statement\n * type and section to produce a meaningful name for reporting order violations. It also handles special cases like\n * displayName assignments and exports so diagnostics are clear and actionable.\n *\n * @param statement The statement to derive a label for.\n * @param section The section number associated with the statement.\n * @returns A descriptive name for the statement within its section.\n *\n * @example\n * ```tsx\n * const name = getStatementName(statement, 4);\n * ```\n */\n const getStatementName = (statement: TSESTree.Statement, section: number) => {\n // eslint-disable-next-line @nfq/no-magic-numbers\n if (section === 4) {\n return componentName;\n }\n\n // eslint-disable-next-line @nfq/no-magic-numbers\n if (section === 5 && isDisplayNameAssignment(statement, componentName)) {\n return `${componentName}.displayName`;\n }\n\n if (statement.type === 'VariableDeclaration') {\n const first = statement.declarations[0];\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (first?.id.type === 'Identifier') {\n return first.id.name;\n }\n }\n\n if (statement.type === 'FunctionDeclaration' || statement.type === 'ClassDeclaration') {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return statement.id?.name ?? getSectionLabel(section);\n }\n\n if (statement.type === 'ExportDefaultDeclaration') {\n const {declaration} = statement;\n\n if (declaration.type === 'Identifier') {\n return `export default ${declaration.name}`;\n }\n\n if (declaration.type === 'ClassDeclaration' || declaration.type === 'FunctionDeclaration') {\n if (declaration.id?.name) {\n return `export default ${declaration.id.name}`;\n }\n }\n\n return 'export default';\n }\n\n if (statement.type === 'ExportNamedDeclaration') {\n const names = statement.specifiers\n .filter(specifier => specifier.local.type === 'Identifier')\n // @ts-expect-error\n .map(specifier => specifier.local.name as string);\n\n if (names.length === 1) {\n return `export {${names[0]}}`;\n }\n\n if (names.length > 1) {\n return 'export {...}';\n }\n }\n\n return getSectionLabel(section);\n };\n\n return {\n /**\n * Iterates over the program body statements to validate their order based on the defined component file structure. It\n * tracks the last seen section and reports any statement that appears in an earlier section than the last one. It also checks\n * for the presence of a displayName assignment and a named export for the component, reporting if either is missing.\n * This ensures that the component file adheres to the expected structure and includes necessary metadata for better maintainability and clarity.\n *\n * @example\n * ```tsx\n * Program();\n * ```\n */\n Program() {\n let lastSection = 0;\n let lastSectionName = '';\n let hasDisplayName = hasStaticDisplayName();\n let hasNamedExport = false;\n\n for (const statement of programBody) {\n const section = sectionForStatement(statement as TSESTree.Statement);\n\n // eslint-disable-next-line @nfq/no-magic-numbers\n if (section === 5) {\n hasDisplayName = true;\n }\n\n // eslint-disable-next-line @nfq/no-magic-numbers\n if (section === 6) {\n hasNamedExport = true;\n }\n\n if (!section) {\n continue;\n }\n\n if (section < lastSection) {\n context.report({\n data: {\n current: getSectionLabel(section),\n currentName: getStatementName(statement as TSESTree.Statement, section),\n order: ORDER_MESSAGE,\n previous: getSectionLabel(lastSection),\n previousName: lastSectionName || getSectionLabel(lastSection)\n },\n messageId: 'invalidOrder',\n node: statement\n });\n } else {\n lastSection = section;\n lastSectionName = getStatementName(statement as TSESTree.Statement, section);\n }\n }\n\n if (!hasDisplayName && !isPageComponent) {\n context.report({\n data: {name: componentName},\n messageId: 'missingDisplayName',\n node: componentInfo.statement\n });\n }\n\n if (!hasNamedExport && !isPageComponent) {\n context.report({\n data: {name: componentName},\n messageId: 'missingNamedExport',\n node: componentInfo.statement\n });\n }\n }\n } as TSESLint.RuleListener;\n }\n});"],"names":["createRule","ESLintUtils","RuleCreator","name","ORDER_LABELS","ORDER_MESSAGE","join","getSectionLabel","section","PAGE_TYPE_NAMES","Set","componentFileStructure","defaultOptions","meta","docs","description","messages","invalidOrder","missingDisplayName","missingNamedExport","schema","type","create","context","sourceCode","programBody","ast","body","componentCandidates","filter","statement","map","getComponentInfoFromStatement","info","Boolean","length","componentInfo","sort","left","right","range","componentName","typeDeclarations","Map","getTypeDeclarationInfo","set","propsTypeNames","propsTypeRefs","propsTypeNode","isPageType","typeNode","componentTypeNode","typeRefs","collectTypeReferences","Array","from","some","has","isPageLayoutAssignment","expression","object","property","hasDefaultExport","isDefaultComponentExport","hasLayoutAssignment","isPageComponent","hasStaticDisplayName","hasDisplayNameMember","member","static","key","id","declarator","declarations","init","add","isPropsTypeDeclaration","isNonPropsTypeDeclaration","isOptionalVarDeclaration","isStyledDeclaration","isStyledFactoryDeclaration","sectionForStatement","isDisplayNameAssignment","isComponentExport","getStatementName","first","declaration","names","specifiers","specifier","local","Program","lastSection","lastSectionName","hasDisplayName","hasNamedExport","report","data","current","currentName","order","previous","previousName","messageId","node"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"component-file-structure.js","sources":["../../../../src/rules/custom/component-file-structure.ts"],"sourcesContent":["import {ESLintUtils} from '@typescript-eslint/utils';\n\nimport {\n collectTypeReferences,\n containsJsx,\n getComponentInfoFromStatement,\n getTypeDeclarationInfo,\n isComponentExport,\n isDefaultComponentExport,\n isDisplayNameAssignment,\n isStyledDeclaration,\n isStyledFactoryDeclaration\n} from './utils/component-file-structure-utils';\n\nimport type {ComponentInfo} from './utils/component-file-structure-utils';\nimport type {TSESLint, TSESTree} from '@typescript-eslint/utils';\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/nfqde/eslint-config-nfq/blob/master/docs/rules/${name}.md`\n);\n\ntype MessageIds = 'invalidOrder' | 'missingDisplayName' | 'missingNamedExport';\n\nconst ORDER_LABELS = [\n 'Imports',\n 'Optional declarations',\n 'Props types',\n 'Component',\n 'Component helpers and displayName',\n 'Exports',\n 'Styled components and local helper types'\n];\nconst ORDER_MESSAGE = ORDER_LABELS.join(' -> ');\n// eslint-disable-next-line jsdoc/require-jsdoc\nconst getSectionLabel = (section: number) => ORDER_LABELS[section - 1] ?? 'Unknown';\nconst PAGE_TYPE_NAMES = new Set([\n 'NextPage',\n 'NextPageWithLayout',\n 'NextSSRPage',\n 'NextSSRPageWithLayout',\n 'NextSSGPageWithLayout'\n]);\n\nexport const componentFileStructure = createRule<[], MessageIds>({\n defaultOptions: [],\n meta: {\n docs: {description: 'Enforce file structure order for React component files.'},\n messages: {\n // eslint-disable-next-line @stylistic/max-len\n invalidOrder: 'This file structure block is out of order. {{currentName}} is a {{current}} and should be before {{previousName}}. Expected order: {{order}}.',\n missingDisplayName: 'Component {{name}} must set displayName.',\n missingNamedExport: 'Component {{name}} must be exported by name.'\n },\n schema: [],\n type: 'suggestion'\n },\n name: 'component-file-structure',\n /**\n * Creates the rule listener for enforcing component file structure ordering. It analyzes the program body to\n * identify the primary component, its props types, and structural sections. It then reports ordering issues and\n * missing requirements like displayName or named exports.\n *\n * @param context The ESLint rule context used to access source code and report diagnostics.\n * @returns A rule listener that validates component file structure and reports issues.\n *\n * @example\n * ```tsx\n * // eslint-disable-next-line @nfq/component-file-structure\n * const listener = componentFileStructure.create(context);\n * ```\n */\n // eslint-disable-next-line max-lines-per-function\n create(context) {\n const filename = context.getFilename();\n const filenameLower = filename.toLowerCase();\n\n if (!filenameLower.endsWith('.tsx') && !filenameLower.endsWith('.jsx')) {\n return {};\n }\n\n const {sourceCode} = context;\n const programBody = sourceCode.ast.body;\n\n if (!containsJsx(sourceCode.ast)) {\n return {};\n }\n const componentCandidates = programBody\n .filter((statement): statement is TSESTree.Statement => statement.type !== 'TSModuleDeclaration')\n .map(statement => getComponentInfoFromStatement(statement))\n .filter((info): info is ComponentInfo => Boolean(info));\n\n if (componentCandidates.length === 0) {\n return {};\n }\n\n const componentInfo = componentCandidates.sort(\n (left, right) => left.statement.range[0] - right.statement.range[0]\n )[0];\n const componentName = componentInfo.name;\n const typeDeclarations = new Map<string, TSESTree.Node>();\n\n for (const statement of programBody) {\n const info = getTypeDeclarationInfo(statement as TSESTree.Statement);\n\n if (info) {\n typeDeclarations.set(info.name, info.statement);\n }\n }\n\n const propsTypeNames = new Set<string>();\n const propsTypeRefs = new Set<string>();\n const propsTypeNode = componentInfo.propsTypeNode ?? null;\n /**\n * Determines whether the component uses a page-specific type alias. It inspects the component type node\n * and collects all referenced type names. It then checks if any of those names match known page types to\n * decide if the component is a page component.\n *\n * @returns True when the component type includes a known page type; otherwise false.\n *\n * @example\n * ```tsx\n * const isPage = isPageType();\n * ```\n */\n const isPageType = () => {\n const typeNode = componentInfo.componentTypeNode ?? null;\n\n if (!typeNode) {\n return false;\n }\n\n const typeRefs = new Set<string>();\n\n collectTypeReferences(typeNode, typeRefs);\n\n return Array.from(typeRefs).some(name => PAGE_TYPE_NAMES.has(name));\n };\n\n /**\n * Checks whether a statement assigns a page layout helper to the component. It ensures the statement is\n * an assignment expression targeting a member of the component identifier. It then verifies the member name\n * matches supported layout assignment properties.\n *\n * @param statement The statement to inspect for a layout assignment.\n * @returns True when the statement assigns a layout helper on the component; otherwise false.\n *\n * @example\n * ```tsx\n * const hasLayout = isPageLayoutAssignment(statement);\n * ```\n */\n const isPageLayoutAssignment = (statement: TSESTree.Statement) => {\n if (statement.type !== 'ExpressionStatement') {\n return false;\n }\n\n const {expression} = statement;\n\n if (expression.type !== 'AssignmentExpression') {\n return false;\n }\n\n const {left} = expression;\n\n if (left.type !== 'MemberExpression' || left.object.type !== 'Identifier') {\n return false;\n }\n\n if (left.object.name !== componentName || left.property.type !== 'Identifier') {\n return false;\n }\n\n return left.property.name === 'getLayout' || left.property.name === 'getLayoutKey';\n };\n\n const hasDefaultExport = programBody\n .some(statement => isDefaultComponentExport(statement as TSESTree.Statement, componentName));\n const hasLayoutAssignment = programBody\n .some(statement => isPageLayoutAssignment(statement as TSESTree.Statement));\n const isPageComponent = hasDefaultExport && (hasLayoutAssignment || isPageType());\n\n /**\n * Determines whether a class element represents a static displayName member. It checks that the element is a supported\n * property definition shape and that it is declared as static. It then verifies the identifier name matches the expected\n * displayName property, which signals a static display name assignment on a class.\n *\n * @returns True when the element is a static displayName property; otherwise false.\n *\n * @example\n * ```tsx\n * const isDisplayName = hasDisplayNameMember(member);\n * ```\n */\n const hasStaticDisplayName = () => {\n /**\n * Determines whether a class element represents a static displayName member. It checks that the element is a supported\n * property definition shape and that it is declared as static. It then verifies the identifier name matches the expected\n * displayName property, which signals a static display name assignment on a class.\n *\n * @param member The class element to inspect for a static displayName property.\n * @returns True when the element is a static displayName property; otherwise false.\n *\n * @example\n * ```tsx\n * const isDisplayName = hasDisplayNameMember(member);\n * ```\n */\n const hasDisplayNameMember = (member: TSESTree.ClassElement) => {\n // @ts-expect-error\n if (member.type !== 'PropertyDefinition' && member.type !== 'ClassProperty') {\n return false;\n }\n\n if (!member.static || member.key.type !== 'Identifier') {\n return false;\n }\n\n return member.key.name === 'displayName';\n };\n\n if (componentInfo.statement.type === 'ClassDeclaration') {\n return componentInfo.statement.id?.name === componentName\n && componentInfo.statement.body.body.some(hasDisplayNameMember);\n }\n\n if (componentInfo.statement.type === 'VariableDeclaration') {\n for (const declarator of componentInfo.statement.declarations) {\n if (declarator.id.type !== 'Identifier' || declarator.id.name !== componentName) {\n continue;\n }\n\n const {init} = declarator;\n\n if (init?.type === 'ClassExpression') {\n return init.body.body.some(hasDisplayNameMember);\n }\n }\n }\n\n return false;\n };\n\n if (propsTypeNode) {\n collectTypeReferences(propsTypeNode, propsTypeRefs);\n\n for (const name of propsTypeRefs) {\n if (typeDeclarations.has(name)) {\n propsTypeNames.add(name);\n }\n }\n }\n\n /**\n * Determines whether a statement declares a type that is used as the component props. It inspects the statement\n * for a type declaration and compares the declared name against the collected props type names. It returns a\n * boolean so callers can classify statements into the props type section.\n *\n * @param statement The statement to inspect for a props type declaration.\n * @returns True when the statement declares a props type; otherwise false.\n *\n * @example\n * ```tsx\n * const isProps = isPropsTypeDeclaration(statement);\n * ```\n */\n const isPropsTypeDeclaration = (statement: TSESTree.Statement) => {\n const info = getTypeDeclarationInfo(statement);\n\n return Boolean(info && propsTypeNames.has(info.name));\n };\n\n /**\n * Determines whether a statement declares a type that is not used as the component props. It inspects the\n * statement for a type declaration and ensures the declared name is not among the collected props type names.\n * It returns a boolean so callers can classify statements into non-props type sections.\n *\n * @param statement The statement to inspect for a non-props type declaration.\n * @returns True when the statement declares a non-props type; otherwise false.\n *\n * @example\n * ```tsx\n * const isNonProps = isNonPropsTypeDeclaration(statement);\n * ```\n */\n const isNonPropsTypeDeclaration = (statement: TSESTree.Statement) => {\n const info = getTypeDeclarationInfo(statement);\n\n return Boolean(info && !propsTypeNames.has(info.name));\n };\n\n /**\n * Determines whether a statement is an optional variable-like declaration in the component file structure. It\n * filters out the main component statement and styled declarations to avoid misclassification. It considers\n * variable declarations, non-component function declarations, and enum declarations as optional declarations.\n *\n * @param statement The statement to evaluate for optional declaration status.\n * @returns True when the statement should be treated as an optional declaration; otherwise false.\n *\n * @example\n * ```tsx\n * const isOptional = isOptionalVarDeclaration(statement);\n * ```\n */\n const isOptionalVarDeclaration = (statement: TSESTree.Statement) => {\n if (statement.type === 'VariableDeclaration') {\n if (\n statement === componentInfo.statement\n || isStyledDeclaration(statement)\n || isStyledFactoryDeclaration(statement)\n ) {\n return false;\n }\n\n return true;\n }\n\n if (statement.type === 'FunctionDeclaration') {\n return statement !== componentInfo.statement;\n }\n\n return statement.type === 'TSEnumDeclaration';\n };\n\n /**\n * Determines the structural section number for a given statement in a component file. It checks the statement\n * against known component structure elements like imports, the main component, displayName assignment, exports,\n * styled declarations, optional declarations, and props types. It returns a numeric section marker to help\n * enforce ordering rules or null when the statement does not belong to a tracked section.\n *\n * @param statement The statement to classify within the component file structure.\n * @returns The numeric section identifier for the statement, or null when it does not match a section.\n *\n * @example\n * ```tsx\n * const section = sectionForStatement(statement);\n * ```\n */\n const sectionForStatement = (statement: TSESTree.Statement) => {\n if (statement.type === 'ImportDeclaration') {\n return 1;\n }\n\n if (statement === componentInfo.statement) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 4;\n }\n\n if (isDisplayNameAssignment(statement, componentName)) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 5;\n }\n\n if (\n isComponentExport(statement, componentName)\n || isDefaultComponentExport(statement, componentName)\n ) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 6;\n }\n\n if (\n isNonPropsTypeDeclaration(statement)\n && statement.range[0] < componentInfo.statement.range[0]\n ) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 2;\n }\n\n if (\n isStyledDeclaration(statement)\n || isStyledFactoryDeclaration(statement)\n || isNonPropsTypeDeclaration(statement)\n ) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 7;\n }\n\n if (isOptionalVarDeclaration(statement)) {\n if (statement.range[0] > componentInfo.statement.range[0]) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 5;\n }\n\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 2;\n }\n\n if (isPropsTypeDeclaration(statement)) {\n // eslint-disable-next-line @nfq/no-magic-numbers\n return 3;\n }\n\n return null;\n };\n\n /**\n * Determines a human-readable label for a statement within the component file structure. It inspects the statement\n * type and section to produce a meaningful name for reporting order violations. It also handles special cases like\n * displayName assignments and exports so diagnostics are clear and actionable.\n *\n * @param statement The statement to derive a label for.\n * @param section The section number associated with the statement.\n * @returns A descriptive name for the statement within its section.\n *\n * @example\n * ```tsx\n * const name = getStatementName(statement, 4);\n * ```\n */\n const getStatementName = (statement: TSESTree.Statement, section: number) => {\n // eslint-disable-next-line @nfq/no-magic-numbers\n if (section === 4) {\n return componentName;\n }\n\n // eslint-disable-next-line @nfq/no-magic-numbers\n if (section === 5 && isDisplayNameAssignment(statement, componentName)) {\n return `${componentName}.displayName`;\n }\n\n if (statement.type === 'VariableDeclaration') {\n const first = statement.declarations[0];\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (first?.id.type === 'Identifier') {\n return first.id.name;\n }\n }\n\n if (statement.type === 'FunctionDeclaration' || statement.type === 'ClassDeclaration') {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return statement.id?.name ?? getSectionLabel(section);\n }\n\n if (statement.type === 'ExportDefaultDeclaration') {\n const {declaration} = statement;\n\n if (declaration.type === 'Identifier') {\n return `export default ${declaration.name}`;\n }\n\n if (declaration.type === 'ClassDeclaration' || declaration.type === 'FunctionDeclaration') {\n if (declaration.id?.name) {\n return `export default ${declaration.id.name}`;\n }\n }\n\n return 'export default';\n }\n\n if (statement.type === 'ExportNamedDeclaration') {\n const names = statement.specifiers\n .filter(specifier => specifier.local.type === 'Identifier')\n // @ts-expect-error\n .map(specifier => specifier.local.name as string);\n\n if (names.length === 1) {\n return `export {${names[0]}}`;\n }\n\n if (names.length > 1) {\n return 'export {...}';\n }\n }\n\n return getSectionLabel(section);\n };\n\n return {\n /**\n * Iterates over the program body statements to validate their order based on the defined component file structure. It\n * tracks the last seen section and reports any statement that appears in an earlier section than the last one. It also checks\n * for the presence of a displayName assignment and a named export for the component, reporting if either is missing.\n * This ensures that the component file adheres to the expected structure and includes necessary metadata for better maintainability and clarity.\n *\n * @example\n * ```tsx\n * Program();\n * ```\n */\n Program() {\n let lastSection = 0;\n let lastSectionName = '';\n let hasDisplayName = hasStaticDisplayName();\n let hasNamedExport = false;\n\n for (const statement of programBody) {\n const section = sectionForStatement(statement as TSESTree.Statement);\n\n // eslint-disable-next-line @nfq/no-magic-numbers\n if (section === 5) {\n hasDisplayName = true;\n }\n\n // eslint-disable-next-line @nfq/no-magic-numbers\n if (section === 6) {\n hasNamedExport = true;\n }\n\n if (!section) {\n continue;\n }\n\n if (section < lastSection) {\n context.report({\n data: {\n current: getSectionLabel(section),\n currentName: getStatementName(statement as TSESTree.Statement, section),\n order: ORDER_MESSAGE,\n previous: getSectionLabel(lastSection),\n previousName: lastSectionName || getSectionLabel(lastSection)\n },\n messageId: 'invalidOrder',\n node: statement\n });\n } else {\n lastSection = section;\n lastSectionName = getStatementName(statement as TSESTree.Statement, section);\n }\n }\n\n if (!hasDisplayName && !isPageComponent) {\n context.report({\n data: {name: componentName},\n messageId: 'missingDisplayName',\n node: componentInfo.statement\n });\n }\n\n if (!hasNamedExport && !isPageComponent) {\n context.report({\n data: {name: componentName},\n messageId: 'missingNamedExport',\n node: componentInfo.statement\n });\n }\n }\n } as TSESLint.RuleListener;\n }\n});"],"names":["createRule","ESLintUtils","RuleCreator","name","ORDER_LABELS","ORDER_MESSAGE","join","getSectionLabel","section","PAGE_TYPE_NAMES","Set","componentFileStructure","defaultOptions","meta","docs","description","messages","invalidOrder","missingDisplayName","missingNamedExport","schema","type","create","context","filename","getFilename","filenameLower","toLowerCase","endsWith","sourceCode","programBody","ast","body","containsJsx","componentCandidates","filter","statement","map","getComponentInfoFromStatement","info","Boolean","length","componentInfo","sort","left","right","range","componentName","typeDeclarations","Map","getTypeDeclarationInfo","set","propsTypeNames","propsTypeRefs","propsTypeNode","isPageType","typeNode","componentTypeNode","typeRefs","collectTypeReferences","Array","from","some","has","isPageLayoutAssignment","expression","object","property","hasDefaultExport","isDefaultComponentExport","hasLayoutAssignment","isPageComponent","hasStaticDisplayName","hasDisplayNameMember","member","static","key","id","declarator","declarations","init","add","isPropsTypeDeclaration","isNonPropsTypeDeclaration","isOptionalVarDeclaration","isStyledDeclaration","isStyledFactoryDeclaration","sectionForStatement","isDisplayNameAssignment","isComponentExport","getStatementName","first","declaration","names","specifiers","specifier","local","Program","lastSection","lastSectionName","hasDisplayName","hasNamedExport","report","data","current","currentName","order","previous","previousName","messageId","node"],"mappings":"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"}
|
|
@@ -18,7 +18,7 @@ const hexagonalDependencyDirection = createRule$8({
|
|
|
18
18
|
missingServiceClass: 'Client services must export a class implementing an adapter interface.',
|
|
19
19
|
missingServiceImplements: 'Client services classes must implement an adapter interface.',
|
|
20
20
|
serverDirection: 'Server dependency direction is domain → errors → services → controllers.',
|
|
21
|
-
uiApplicationImport: 'UI may only import from
|
|
21
|
+
uiApplicationImport: 'UI may only import from UI, shared, domain, or application useCases/configs/utils.'
|
|
22
22
|
},
|
|
23
23
|
schema: [],
|
|
24
24
|
type: 'problem'
|
|
@@ -153,6 +153,9 @@ const hexagonalDependencyDirection = createRule$8({
|
|
|
153
153
|
return;
|
|
154
154
|
}
|
|
155
155
|
if (currentLayer === 'client-application' || currentLayer === 'client-application-configs' || currentLayer === 'client-application-services' || currentLayer === 'client-application-usecases' || currentLayer === 'client-application-utils') {
|
|
156
|
+
if (targetLayer === 'client-shared') {
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
156
159
|
if (targetLayer === 'client-ui') {
|
|
157
160
|
context.report({
|
|
158
161
|
messageId: 'crossBoundary',
|
|
@@ -162,7 +165,7 @@ const hexagonalDependencyDirection = createRule$8({
|
|
|
162
165
|
return;
|
|
163
166
|
}
|
|
164
167
|
if (currentLayer === 'client-ui') {
|
|
165
|
-
const allowed = targetLayer === 'client-ui' || targetLayer === 'client-shared' || targetLayer === 'client-application-configs' || targetLayer === 'client-application-usecases' || targetLayer === 'client-application-utils';
|
|
168
|
+
const allowed = targetLayer === 'client-ui' || targetLayer === 'client-shared' || targetLayer === 'client-domain' || targetLayer === 'client-application-configs' || targetLayer === 'client-application-usecases' || targetLayer === 'client-application-utils';
|
|
166
169
|
if (!allowed) {
|
|
167
170
|
context.report({
|
|
168
171
|
messageId: 'uiApplicationImport',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hexagonal-dependency-direction.js","sources":["../../../../src/rules/custom/hexagonal-dependency-direction.ts"],"sourcesContent":["import {ESLintUtils} from '@typescript-eslint/utils';\n\nimport {\n buildAliasMap,\n getLayer,\n isClientLayer,\n isClientUtils,\n isConfigLayer,\n isDomainLayer,\n isMobxStoreFile,\n isServerLayer,\n isServerUtils,\n normalizePath,\n readAliasesFromSettings,\n resolveAliasImport,\n resolveRelativeImport\n} from './utils/hexagonal-dependency-direction-utils';\n\nimport type {TSESLint, TSESTree} from '@typescript-eslint/utils';\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/nfqde/eslint-config-nfq/blob/master/docs/rules/${name}.md`\n);\n\ntype RuleOptions = [];\n\ntype MessageIds = 'apiImport' | 'clientApiImport' | 'crossBoundary' | 'domainImport' | 'domainUtilsImport'\n | 'missingServiceClass' | 'missingServiceImplements' | 'serverDirection' | 'uiApplicationImport';\n\nconst requiredAliases = [\n 'Domain',\n 'Application',\n 'UI',\n 'ApiRoutes',\n 'Controllers',\n 'ServerDomain',\n 'Services'\n];\n\nexport const hexagonalDependencyDirection = createRule<RuleOptions, MessageIds>({\n defaultOptions: [],\n meta: {\n docs: {description: 'Enforce hexagonal dependency direction and boundary imports.'},\n messages: {\n apiImport: 'API routes may only import controllers, middleware, or server utils.',\n clientApiImport: 'Only client application services may import API routes.',\n crossBoundary: 'Client and server layers must not import from each other.',\n domainImport: 'Domain layers must not import from other layers.',\n domainUtilsImport: 'Domain layers must not import from utils.',\n missingServiceClass: 'Client services must export a class implementing an adapter interface.',\n missingServiceImplements: 'Client services classes must implement an adapter interface.',\n serverDirection: 'Server dependency direction is domain → errors → services → controllers.',\n // eslint-disable-next-line @stylistic/max-len\n uiApplicationImport: 'UI may only import from application/useCases, application/configs, or application/utils.'\n },\n schema: [],\n type: 'problem'\n },\n name: 'hexagonal-dependency-direction',\n /**\n * This function creates the ESLint rule listeners for validating hexagonal dependency direction.\n * It initializes alias resolution, determines the current file layer, and wires up import checks.\n * It also validates service class exports and adapter implementations for client services.\n *\n * @param context The rule context used for reporting and settings.\n * @returns A rule listener map for import declarations and program validation.\n *\n * @example\n * ```tsx\n * const rule = hexagonalDependencyDirection.create(context);\n * ```\n */\n // eslint-disable-next-line max-lines-per-function\n create(context) {\n const aliases = readAliasesFromSettings(context.settings);\n const aliasMap = buildAliasMap(aliases);\n\n const missing = requiredAliases.filter(alias => !aliasMap.has(alias));\n\n if (missing.length > 0) {\n return {};\n }\n\n const {filename} = context;\n const normalizedFile = normalizePath(filename);\n const currentLayer = getLayer(normalizedFile);\n\n if (currentLayer === 'unknown') {\n return {};\n }\n\n const isClient = isClientLayer(currentLayer);\n const isServer = isServerLayer(currentLayer);\n\n /**\n * This function validates that client application services export at least one class and that the class\n * implements an adapter interface. It scans the program body for class declarations and named exports\n * containing classes. It reports rule violations when no class is found or when a class lacks implemented\n * interfaces.\n *\n * @param program The program node to analyze for service class exports.\n *\n * @example\n * ```tsx\n * reportInvalidService(program);\n * ```\n */\n const reportInvalidService = (program: TSESTree.Program) => {\n if (currentLayer !== 'client-application-services') {\n return;\n }\n\n if (isMobxStoreFile(program)) {\n return;\n }\n\n const classes: (TSESTree.ClassDeclaration | TSESTree.ClassExpression)[] = [];\n\n for (const node of program.body) {\n // @ts-expect-error\n if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {\n classes.push(node);\n continue;\n }\n\n if (node.type === 'ExportNamedDeclaration' && node.declaration) {\n const {declaration} = node;\n\n // @ts-expect-error\n if (declaration.type === 'ClassDeclaration' || declaration.type === 'ClassExpression') {\n classes.push(declaration);\n }\n }\n }\n\n if (classes.length === 0) {\n context.report({\n messageId: 'missingServiceClass',\n node: program\n });\n\n return;\n }\n\n for (const classNode of classes) {\n if (classNode.implements.length === 0) {\n context.report({\n messageId: 'missingServiceImplements',\n node: classNode\n });\n }\n }\n };\n\n /**\n * Resolves an import source to a normalized path using aliases and relative paths. It first attempts to match\n * the source against configured aliases to ensure project-wide consistency. If no alias applies, it falls back\n * to resolving a relative import against the current file for local references.\n *\n * @param source The raw import source string from the import declaration.\n * @returns The resolved path string when resolution succeeds, or undefined when it cannot be resolved.\n *\n * @example\n * ```tsx\n * const resolved = resolveImport('@app/domain');\n * ```\n */\n const resolveImport = (source: string) => {\n const aliasResolved = resolveAliasImport(source, aliasMap);\n\n if (aliasResolved) {\n return aliasResolved;\n }\n\n return resolveRelativeImport(source, filename);\n };\n\n /**\n * Validates an import declaration against the hexagonal dependency rules for the current file.\n * It resolves the import source into a target layer and checks for forbidden cross-boundary or layer-specific dependencies.\n * It reports violations using the configured ESLint messages when a rule is broken.\n *\n * @param node The import declaration node to validate.\n *\n * @example\n * ```tsx\n * checkImport(node);\n * ```\n */\n // eslint-disable-next-line complexity\n const checkImport = (node: TSESTree.ImportDeclaration) => {\n const source = node.source.value;\n\n if (typeof source !== 'string') {\n return;\n }\n\n const resolved = resolveImport(source);\n\n if (!resolved) {\n return;\n }\n\n const targetLayer = getLayer(resolved);\n\n if (targetLayer === 'unknown') {\n return;\n }\n\n if (isClient && isServerLayer(targetLayer)) {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n\n return;\n }\n\n if (isServer && isClientLayer(targetLayer)) {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n\n return;\n }\n\n if (currentLayer === 'api') {\n const allowed = targetLayer === 'server-controllers'\n || targetLayer === 'server-middleware'\n || targetLayer === 'server-utils';\n\n if (!allowed) {\n context.report({\n messageId: 'apiImport',\n node\n });\n }\n\n return;\n }\n\n if (targetLayer === 'api' && currentLayer !== 'client-application-services') {\n context.report({\n messageId: 'clientApiImport',\n node\n });\n\n return;\n }\n\n if (isDomainLayer(currentLayer)) {\n if (targetLayer === 'client-shared' || targetLayer === 'server-configs') {\n return;\n }\n\n if (isConfigLayer(targetLayer)) {\n return;\n }\n\n if (isClientUtils(targetLayer) || isServerUtils(targetLayer)) {\n context.report({\n messageId: 'domainUtilsImport',\n node\n });\n\n return;\n }\n\n if (targetLayer !== currentLayer) {\n context.report({\n messageId: 'domainImport',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'client-application' || currentLayer === 'client-application-configs'\n || currentLayer === 'client-application-services'\n || currentLayer === 'client-application-usecases'\n || currentLayer === 'client-application-utils') {\n if (targetLayer === 'client-ui') {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'client-ui') {\n const allowed = targetLayer === 'client-ui'\n || targetLayer === 'client-shared'\n || targetLayer === 'client-application-configs'\n || targetLayer === 'client-application-usecases'\n || targetLayer === 'client-application-utils';\n\n if (!allowed) {\n context.report({\n messageId: 'uiApplicationImport',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-errors') {\n const allowed = targetLayer === 'server-errors'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-services') {\n const allowed = targetLayer === 'server-services'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-errors'\n || targetLayer === 'server-utils'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-controllers') {\n const allowed = targetLayer === 'server-controllers'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-errors'\n || targetLayer === 'server-services'\n || targetLayer === 'server-utils'\n || targetLayer === 'server-middleware'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n }\n };\n\n return {\n ImportDeclaration: checkImport,\n /**\n * Inspects constructor method definitions to detect assignments where a class method\n * is bound to `this` via `.bind(this)`, and reports such bindings for further handling.\n * This listener is triggered for the entire program to validate service class exports and adapter implementations\n * for client application services. It ensures that the hexagonal architecture rules are enforced at the program level.\n *\n * @param program The program node to analyze for service class export and implementation validation.\n */\n Program(program) {\n reportInvalidService(program);\n }\n } satisfies TSESLint.RuleListener;\n }\n});"],"names":["createRule","ESLintUtils","RuleCreator","name","requiredAliases","hexagonalDependencyDirection","defaultOptions","meta","docs","description","messages","apiImport","clientApiImport","crossBoundary","domainImport","domainUtilsImport","missingServiceClass","missingServiceImplements","serverDirection","uiApplicationImport","schema","type","create","context","aliases","readAliasesFromSettings","settings","aliasMap","buildAliasMap","missing","filter","alias","has","length","filename","normalizedFile","normalizePath","currentLayer","getLayer","isClient","isClientLayer","isServer","isServerLayer","reportInvalidService","program","isMobxStoreFile","classes","node","body","push","declaration","report","messageId","classNode","implements","resolveImport","source","aliasResolved","resolveAliasImport","resolveRelativeImport","checkImport","value","resolved","targetLayer","allowed","isDomainLayer","isConfigLayer","isClientUtils","isServerUtils","ImportDeclaration","Program"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"hexagonal-dependency-direction.js","sources":["../../../../src/rules/custom/hexagonal-dependency-direction.ts"],"sourcesContent":["import {ESLintUtils} from '@typescript-eslint/utils';\n\nimport {\n buildAliasMap,\n getLayer,\n isClientLayer,\n isClientUtils,\n isConfigLayer,\n isDomainLayer,\n isMobxStoreFile,\n isServerLayer,\n isServerUtils,\n normalizePath,\n readAliasesFromSettings,\n resolveAliasImport,\n resolveRelativeImport\n} from './utils/hexagonal-dependency-direction-utils';\n\nimport type {TSESLint, TSESTree} from '@typescript-eslint/utils';\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/nfqde/eslint-config-nfq/blob/master/docs/rules/${name}.md`\n);\n\ntype RuleOptions = [];\n\ntype MessageIds = 'apiImport' | 'clientApiImport' | 'crossBoundary' | 'domainImport' | 'domainUtilsImport'\n | 'missingServiceClass' | 'missingServiceImplements' | 'serverDirection' | 'uiApplicationImport';\n\nconst requiredAliases = [\n 'Domain',\n 'Application',\n 'UI',\n 'ApiRoutes',\n 'Controllers',\n 'ServerDomain',\n 'Services'\n];\n\nexport const hexagonalDependencyDirection = createRule<RuleOptions, MessageIds>({\n defaultOptions: [],\n meta: {\n docs: {description: 'Enforce hexagonal dependency direction and boundary imports.'},\n messages: {\n apiImport: 'API routes may only import controllers, middleware, or server utils.',\n clientApiImport: 'Only client application services may import API routes.',\n crossBoundary: 'Client and server layers must not import from each other.',\n domainImport: 'Domain layers must not import from other layers.',\n domainUtilsImport: 'Domain layers must not import from utils.',\n missingServiceClass: 'Client services must export a class implementing an adapter interface.',\n missingServiceImplements: 'Client services classes must implement an adapter interface.',\n serverDirection: 'Server dependency direction is domain → errors → services → controllers.',\n uiApplicationImport: 'UI may only import from UI, shared, domain, or application useCases/configs/utils.'\n },\n schema: [],\n type: 'problem'\n },\n name: 'hexagonal-dependency-direction',\n /**\n * This function creates the ESLint rule listeners for validating hexagonal dependency direction.\n * It initializes alias resolution, determines the current file layer, and wires up import checks.\n * It also validates service class exports and adapter implementations for client services.\n *\n * @param context The rule context used for reporting and settings.\n * @returns A rule listener map for import declarations and program validation.\n *\n * @example\n * ```tsx\n * const rule = hexagonalDependencyDirection.create(context);\n * ```\n */\n // eslint-disable-next-line max-lines-per-function\n create(context) {\n const aliases = readAliasesFromSettings(context.settings);\n const aliasMap = buildAliasMap(aliases);\n\n const missing = requiredAliases.filter(alias => !aliasMap.has(alias));\n\n if (missing.length > 0) {\n return {};\n }\n\n const {filename} = context;\n const normalizedFile = normalizePath(filename);\n const currentLayer = getLayer(normalizedFile);\n\n if (currentLayer === 'unknown') {\n return {};\n }\n\n const isClient = isClientLayer(currentLayer);\n const isServer = isServerLayer(currentLayer);\n\n /**\n * This function validates that client application services export at least one class and that the class\n * implements an adapter interface. It scans the program body for class declarations and named exports\n * containing classes. It reports rule violations when no class is found or when a class lacks implemented\n * interfaces.\n *\n * @param program The program node to analyze for service class exports.\n *\n * @example\n * ```tsx\n * reportInvalidService(program);\n * ```\n */\n const reportInvalidService = (program: TSESTree.Program) => {\n if (currentLayer !== 'client-application-services') {\n return;\n }\n\n if (isMobxStoreFile(program)) {\n return;\n }\n\n const classes: (TSESTree.ClassDeclaration | TSESTree.ClassExpression)[] = [];\n\n for (const node of program.body) {\n // @ts-expect-error\n if (node.type === 'ClassDeclaration' || node.type === 'ClassExpression') {\n classes.push(node);\n continue;\n }\n\n if (node.type === 'ExportNamedDeclaration' && node.declaration) {\n const {declaration} = node;\n\n // @ts-expect-error\n if (declaration.type === 'ClassDeclaration' || declaration.type === 'ClassExpression') {\n classes.push(declaration);\n }\n }\n }\n\n if (classes.length === 0) {\n context.report({\n messageId: 'missingServiceClass',\n node: program\n });\n\n return;\n }\n\n for (const classNode of classes) {\n if (classNode.implements.length === 0) {\n context.report({\n messageId: 'missingServiceImplements',\n node: classNode\n });\n }\n }\n };\n\n /**\n * Resolves an import source to a normalized path using aliases and relative paths. It first attempts to match\n * the source against configured aliases to ensure project-wide consistency. If no alias applies, it falls back\n * to resolving a relative import against the current file for local references.\n *\n * @param source The raw import source string from the import declaration.\n * @returns The resolved path string when resolution succeeds, or undefined when it cannot be resolved.\n *\n * @example\n * ```tsx\n * const resolved = resolveImport('@app/domain');\n * ```\n */\n const resolveImport = (source: string) => {\n const aliasResolved = resolveAliasImport(source, aliasMap);\n\n if (aliasResolved) {\n return aliasResolved;\n }\n\n return resolveRelativeImport(source, filename);\n };\n\n /**\n * Validates an import declaration against the hexagonal dependency rules for the current file.\n * It resolves the import source into a target layer and checks for forbidden cross-boundary or layer-specific dependencies.\n * It reports violations using the configured ESLint messages when a rule is broken.\n *\n * @param node The import declaration node to validate.\n *\n * @example\n * ```tsx\n * checkImport(node);\n * ```\n */\n // eslint-disable-next-line complexity\n const checkImport = (node: TSESTree.ImportDeclaration) => {\n const source = node.source.value;\n\n if (typeof source !== 'string') {\n return;\n }\n\n const resolved = resolveImport(source);\n\n if (!resolved) {\n return;\n }\n\n const targetLayer = getLayer(resolved);\n\n if (targetLayer === 'unknown') {\n return;\n }\n\n if (isClient && isServerLayer(targetLayer)) {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n\n return;\n }\n\n if (isServer && isClientLayer(targetLayer)) {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n\n return;\n }\n\n if (currentLayer === 'api') {\n const allowed = targetLayer === 'server-controllers'\n || targetLayer === 'server-middleware'\n || targetLayer === 'server-utils';\n\n if (!allowed) {\n context.report({\n messageId: 'apiImport',\n node\n });\n }\n\n return;\n }\n\n if (targetLayer === 'api' && currentLayer !== 'client-application-services') {\n context.report({\n messageId: 'clientApiImport',\n node\n });\n\n return;\n }\n\n if (isDomainLayer(currentLayer)) {\n if (targetLayer === 'client-shared' || targetLayer === 'server-configs') {\n return;\n }\n\n if (isConfigLayer(targetLayer)) {\n return;\n }\n\n if (isClientUtils(targetLayer) || isServerUtils(targetLayer)) {\n context.report({\n messageId: 'domainUtilsImport',\n node\n });\n\n return;\n }\n\n if (targetLayer !== currentLayer) {\n context.report({\n messageId: 'domainImport',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'client-application' || currentLayer === 'client-application-configs'\n || currentLayer === 'client-application-services'\n || currentLayer === 'client-application-usecases'\n || currentLayer === 'client-application-utils') {\n if (targetLayer === 'client-shared') {\n return;\n }\n\n if (targetLayer === 'client-ui') {\n context.report({\n messageId: 'crossBoundary',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'client-ui') {\n const allowed = targetLayer === 'client-ui'\n || targetLayer === 'client-shared'\n || targetLayer === 'client-domain'\n || targetLayer === 'client-application-configs'\n || targetLayer === 'client-application-usecases'\n || targetLayer === 'client-application-utils';\n\n if (!allowed) {\n context.report({\n messageId: 'uiApplicationImport',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-errors') {\n const allowed = targetLayer === 'server-errors'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-services') {\n const allowed = targetLayer === 'server-services'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-errors'\n || targetLayer === 'server-utils'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n\n return;\n }\n\n if (currentLayer === 'server-controllers') {\n const allowed = targetLayer === 'server-controllers'\n || targetLayer === 'server-domain'\n || targetLayer === 'server-errors'\n || targetLayer === 'server-services'\n || targetLayer === 'server-utils'\n || targetLayer === 'server-middleware'\n || targetLayer === 'server-configs';\n\n if (!allowed) {\n context.report({\n messageId: 'serverDirection',\n node\n });\n }\n }\n };\n\n return {\n ImportDeclaration: checkImport,\n /**\n * Inspects constructor method definitions to detect assignments where a class method\n * is bound to `this` via `.bind(this)`, and reports such bindings for further handling.\n * This listener is triggered for the entire program to validate service class exports and adapter implementations\n * for client application services. It ensures that the hexagonal architecture rules are enforced at the program level.\n *\n * @param program The program node to analyze for service class export and implementation validation.\n */\n Program(program) {\n reportInvalidService(program);\n }\n } satisfies TSESLint.RuleListener;\n }\n});"],"names":["createRule","ESLintUtils","RuleCreator","name","requiredAliases","hexagonalDependencyDirection","defaultOptions","meta","docs","description","messages","apiImport","clientApiImport","crossBoundary","domainImport","domainUtilsImport","missingServiceClass","missingServiceImplements","serverDirection","uiApplicationImport","schema","type","create","context","aliases","readAliasesFromSettings","settings","aliasMap","buildAliasMap","missing","filter","alias","has","length","filename","normalizedFile","normalizePath","currentLayer","getLayer","isClient","isClientLayer","isServer","isServerLayer","reportInvalidService","program","isMobxStoreFile","classes","node","body","push","declaration","report","messageId","classNode","implements","resolveImport","source","aliasResolved","resolveAliasImport","resolveRelativeImport","checkImport","value","resolved","targetLayer","allowed","isDomainLayer","isConfigLayer","isClientUtils","isServerUtils","ImportDeclaration","Program"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-unbound-method.js","sources":["../../../../src/rules/custom/no-unbound-method.ts"],"sourcesContent":["import {AST_NODE_TYPES, ESLintUtils} from '@typescript-eslint/utils';\nimport ts from 'typescript';\n\nimport {\n DEFAULT_DECORATORS,\n getDecoratorName,\n isCallbackUsage,\n isThisBindCall,\n methodUsesThis,\n normalizeDecoratorNames,\n unwrapExpression\n} from './utils/no-unbound-method-utils';\n\nimport type {TSESLint, TSESTree} from '@typescript-eslint/utils';\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/nfqde/eslint-config-nfq/blob/master/docs/rules/${name}.md`\n);\n\ntype RuleOptions = [\n {decoratorNames?: string[]}?\n];\n\ntype MessageIds = 'constructorBind' | 'unboundMethod';\n\ntype MethodInfo = {\n autoBind: boolean;\n hasDecorator: boolean;\n isStatic: boolean;\n usesThis: boolean;\n};\n\nexport const noUnboundMethod = createRule<RuleOptions, MessageIds>({\n defaultOptions: [{decoratorNames: DEFAULT_DECORATORS}],\n meta: {\n docs: {description: 'Warn when unbound class methods that use this are passed as callbacks.'},\n messages: {\n constructorBind: 'Do not bind in the constructor; prefer {{decoratorName}}.',\n unboundMethod: 'Method {{methodName}} uses this and is passed unbound. Add {{decoratorName}} or bind it.'\n },\n schema: [\n {\n additionalProperties: false,\n properties: {\n decoratorNames: {\n items: {type: 'string'},\n type: 'array',\n uniqueItems: true\n }\n },\n type: 'object'\n }\n ],\n type: 'problem'\n },\n name: 'no-unbound-method',\n /**\n * Creates the rule listeners and initializes caches for symbol analysis.\n * It wires the parser services to the TypeScript type checker and configures decorator handling.\n * It returns ESLint listeners that report unbound method usage in callbacks and constructor binding.\n *\n * @param context Rule context used to access parser services and report issues.\n * @returns The rule listener map for this rule.\n *\n * @example\n * ```tsx\n * const listeners = rule.create(context);\n * ```\n */\n // eslint-disable-next-line max-lines-per-function\n create(context) {\n const services = ESLintUtils.getParserServices(context);\n const checker = services.program.getTypeChecker();\n const decoratorNames = normalizeDecoratorNames(context.options[0]?.decoratorNames);\n const decoratorLabel = `@${Array.from(decoratorNames)[0] ?? 'autobind'}`;\n const methodCache = new Map<ts.Symbol, MethodInfo>();\n const reportCache = new Map<ts.Symbol, boolean>();\n const aliasCache = new Map<ts.Symbol, ts.Symbol>();\n const memberSymbolCache = new Map<TSESTree.MemberExpression, ts.Symbol | null>();\n const classAutoBindCache = new WeakMap<ts.ClassLikeDeclaration, boolean>();\n\n const isAutoBindOption = (node: ts.Expression): boolean => {\n if (!ts.isObjectLiteralExpression(node)) {\n return false;\n }\n\n return node.properties.some(property => {\n if (!ts.isPropertyAssignment(property)) {\n return false;\n }\n\n // eslint-disable-next-line no-nested-ternary\n const name = ts.isIdentifier(property.name)\n ? property.name.text\n : ts.isStringLiteral(property.name)\n ? property.name.text\n : null;\n\n if (name !== 'autoBind') {\n return false;\n }\n\n return property.initializer.kind === ts.SyntaxKind.TrueKeyword;\n });\n };\n\n const isMakeAutoObservableCallee = (node: ts.Expression): boolean => {\n if (ts.isIdentifier(node)) {\n return node.text === 'makeAutoObservable';\n }\n\n return ts.isPropertyAccessExpression(node) && node.name.text === 'makeAutoObservable';\n };\n\n const hasAutoBindInConstructor = (classDecl: ts.ClassLikeDeclaration): boolean => {\n const cached = classAutoBindCache.get(classDecl);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const ctor = classDecl.members.find(member => ts.isConstructorDeclaration(member));\n\n if (!ctor?.body) {\n classAutoBindCache.set(classDecl, false);\n\n return false;\n }\n\n for (const statement of ctor.body.statements) {\n if (!ts.isExpressionStatement(statement)) {\n continue;\n }\n\n const expr = statement.expression;\n\n if (!ts.isCallExpression(expr)) {\n continue;\n }\n\n if (!isMakeAutoObservableCallee(expr.expression)) {\n continue;\n }\n\n if (expr.arguments.length < 2) {\n continue;\n }\n\n if (expr.arguments[0].kind !== ts.SyntaxKind.ThisKeyword) {\n continue;\n }\n\n const hasAutoBind = expr.arguments.slice(1).some(arg => isAutoBindOption(arg));\n\n if (hasAutoBind) {\n classAutoBindCache.set(classDecl, true);\n\n return true;\n }\n }\n\n classAutoBindCache.set(classDecl, false);\n\n return false;\n };\n\n /**\n * Resolves an alias symbol to its original symbol and caches the result for reuse.\n * This function avoids repeated TypeScript checker work by memoizing alias resolutions.\n * It ensures downstream analysis consistently uses the same canonical symbol instance.\n *\n * @param symbol The symbol that may be an alias and needs resolution.\n * @returns The resolved canonical symbol for further analysis.\n *\n * @example\n * ```tsx\n * const resolved = resolveSymbol(methodSymbol);\n * ```\n */\n const resolveSymbol = (symbol: ts.Symbol): ts.Symbol => {\n const cached = aliasCache.get(symbol);\n\n if (cached) {\n return cached;\n }\n\n // eslint-disable-next-line no-bitwise\n const resolved = symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;\n\n aliasCache.set(symbol, resolved);\n\n return resolved;\n };\n\n /**\n * Computes metadata about a method-like symbol and caches the result for later use.\n * This function filters out non-method symbols and constructor declarations before gathering details.\n * It also determines decorator presence, static status, and whether the method uses `this`.\n *\n * @param symbol The symbol to inspect for method information.\n * @returns The resolved method info or null when the symbol is not a supported method.\n *\n * @example\n * ```tsx\n * const info = getMethodInfo(methodSymbol);\n * ```\n */\n const getMethodInfo = (symbol: ts.Symbol): MethodInfo | null => {\n const aliased = resolveSymbol(symbol);\n const cached = methodCache.get(aliased);\n\n if (cached) {\n return cached;\n }\n\n if (!(\n // eslint-disable-next-line no-bitwise\n aliased.flags\n & (\n // eslint-disable-next-line no-bitwise\n ts.SymbolFlags.Method\n | ts.SymbolFlags.Property\n | ts.SymbolFlags.Function\n | ts.SymbolFlags.GetAccessor\n | ts.SymbolFlags.SetAccessor\n )\n )) {\n return null;\n }\n\n const declarations = aliased.getDeclarations() ?? [];\n const methodDecl = declarations\n .find(decl => ts.isMethodDeclaration(decl));\n\n if (!methodDecl?.parent || !ts.isClassLike(methodDecl.parent)) {\n return null;\n }\n\n if (ts.isIdentifier(methodDecl.name) && methodDecl.name.text === 'constructor') {\n return null;\n }\n\n const flags = ts.getCombinedModifierFlags(methodDecl);\n // eslint-disable-next-line no-bitwise\n const isStatic = Boolean(flags & ts.ModifierFlags.Static);\n const decorators = ts.canHaveDecorators(methodDecl) ? ts.getDecorators(methodDecl) : undefined;\n const hasDecorator = Boolean(decorators?.some(decorator => {\n const name = getDecoratorName(decorator);\n\n return Boolean(name && decoratorNames.has(name));\n }));\n\n const info: MethodInfo = {\n autoBind: hasAutoBindInConstructor(methodDecl.parent),\n hasDecorator,\n isStatic,\n usesThis: methodUsesThis(methodDecl)\n };\n\n methodCache.set(aliased, info);\n\n return info;\n };\n\n /**\n * Determines whether a symbol should be reported as an unbound method.\n * It resolves aliases and consults cached results to avoid repeated analysis.\n * It evaluates method metadata to ensure only non-static methods that use `this` and lack decorators are flagged.\n *\n * @param symbol The symbol to evaluate for reporting eligibility.\n * @returns True when the symbol represents an unbound method that should be reported.\n *\n * @example\n * ```tsx\n * const shouldReport = shouldReportSymbol(methodSymbol);\n * ```\n */\n const shouldReportSymbol = (symbol: ts.Symbol): boolean => {\n const aliased = resolveSymbol(symbol);\n const cached = reportCache.get(aliased);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const info = getMethodInfo(aliased);\n\n if (!info) {\n reportCache.set(aliased, false);\n\n return false;\n }\n\n const shouldReport = !info.autoBind && !info.isStatic && info.usesThis && !info.hasDecorator;\n\n reportCache.set(aliased, shouldReport);\n\n return shouldReport;\n };\n\n /**\n * Resolves the TypeScript symbol associated with a member expression node.\n * It caches the resolved symbol to minimize repeated checker lookups.\n * It supports both property access and string-literal element access expressions.\n *\n * @param node The member expression node to resolve into a symbol.\n * @returns The resolved symbol, or null when no symbol can be determined.\n *\n * @example\n * ```tsx\n * const symbol = getSymbolForMember(memberExpression);\n * ```\n */\n const getSymbolForMember = (node: TSESTree.MemberExpression): ts.Symbol | null => {\n const cached = memberSymbolCache.get(node);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const tsNode = services.esTreeNodeToTSNodeMap.get(node);\n\n if (ts.isPropertyAccessExpression(tsNode)) {\n const symbol = checker.getSymbolAtLocation(tsNode.name) ?? null;\n\n memberSymbolCache.set(node, symbol);\n\n return symbol;\n }\n\n if (ts.isElementAccessExpression(tsNode)) {\n const arg = tsNode.argumentExpression;\n\n if (ts.isStringLiteralLike(arg)) {\n const symbol = checker.getSymbolAtLocation(arg) ?? null;\n\n memberSymbolCache.set(node, symbol);\n\n return symbol;\n }\n }\n\n memberSymbolCache.set(node, null);\n\n return null;\n };\n\n /**\n * Reports a constructor binding assignment for a method name.\n * It formats the report data with the decorator label and method name.\n * It delegates to the ESLint context to surface the diagnostic at the provided node.\n *\n * @param methodName The name of the method that was bound in the constructor.\n * @param node The AST node that represents the binding expression.\n *\n * @example\n * ```tsx\n * reportConstructorBinding('handleClick', expressionNode);\n * ```\n */\n const reportConstructorBinding = (methodName: string, node: TSESTree.Node) => {\n context.report({\n data: {\n decoratorName: decoratorLabel,\n methodName\n },\n messageId: 'constructorBind',\n node\n });\n };\n\n const listeners: TSESLint.RuleListener = {\n /**\n * Processes a member expression node to determine whether it represents an unbound method usage that\n * should be reported. It performs multiple early returns based on node shape, parent usage, callback context,\n * binding checks, and symbol eligibility. It ultimately reports a linting issue when all conditions\n * indicate a problematic unbound method reference.\n *\n * @param node The member expression node to analyze for unbound method usage.\n *\n * @example\n * ```tsx\n * MemberExpression(node);\n * ```\n */\n MemberExpression(node: TSESTree.MemberExpression) {\n if (node.property.type !== AST_NODE_TYPES.Identifier) {\n return;\n }\n\n const {parent} = node;\n\n if (\n (parent.type === AST_NODE_TYPES.CallExpression || parent.type === AST_NODE_TYPES.NewExpression)\n && parent.callee === node\n ) {\n return;\n }\n\n if (!isCallbackUsage(node)) {\n return;\n }\n\n if (isThisBindCall(node)) {\n return;\n }\n\n const symbol = getSymbolForMember(node);\n\n if (!symbol) {\n return;\n }\n\n if (!shouldReportSymbol(symbol)) {\n return;\n }\n\n context.report({\n data: {\n decoratorName: decoratorLabel,\n methodName: node.property.name\n },\n messageId: 'unboundMethod',\n node\n });\n },\n /**\n * Inspects constructor method definitions to detect assignments where a class method\n * is bound to `this` via `.bind(this)`, and reports such bindings for further handling.\n *\n * @param node The method definition node to analyze.\n */\n MethodDefinition(node: TSESTree.MethodDefinition) {\n if (node.kind !== 'constructor' || !node.value.body) {\n return;\n }\n\n const constructorBody = node.value.body;\n\n for (const statement of constructorBody.body) {\n if (statement.type !== AST_NODE_TYPES.ExpressionStatement) {\n continue;\n }\n\n const {expression} = statement;\n\n if (expression.type !== AST_NODE_TYPES.AssignmentExpression || expression.operator !== '=') {\n continue;\n }\n\n const {left, right} = expression;\n\n if (\n left.type !== AST_NODE_TYPES.MemberExpression\n || left.property.type !== AST_NODE_TYPES.Identifier\n ) {\n continue;\n }\n\n if (left.object.type !== AST_NODE_TYPES.ThisExpression) {\n continue;\n }\n\n if (right.type !== AST_NODE_TYPES.CallExpression) {\n continue;\n }\n\n const {callee} = right;\n\n if (callee.type !== AST_NODE_TYPES.MemberExpression) {\n continue;\n }\n\n if (\n callee.object.type !== AST_NODE_TYPES.MemberExpression\n || callee.object.object.type !== AST_NODE_TYPES.ThisExpression\n || callee.object.property.type !== AST_NODE_TYPES.Identifier\n || callee.object.property.name !== left.property.name\n ) {\n continue;\n }\n\n if (\n callee.property.type !== AST_NODE_TYPES.Identifier\n || callee.property.name !== 'bind'\n ) {\n continue;\n }\n\n if (right.arguments.length === 0) {\n continue;\n }\n\n const firstArg = unwrapExpression(right.arguments[0] as TSESTree.Expression);\n\n if (firstArg.type !== AST_NODE_TYPES.ThisExpression) {\n continue;\n }\n\n reportConstructorBinding(left.property.name, expression);\n }\n }\n };\n\n return listeners;\n }\n});"],"names":["createRule","ESLintUtils","RuleCreator","name","noUnboundMethod","defaultOptions","decoratorNames","DEFAULT_DECORATORS","meta","docs","description","messages","constructorBind","unboundMethod","schema","additionalProperties","properties","items","type","uniqueItems","create","context","services","getParserServices","checker","program","getTypeChecker","normalizeDecoratorNames","options","decoratorLabel","Array","from","methodCache","Map","reportCache","aliasCache","memberSymbolCache","classAutoBindCache","WeakMap","isAutoBindOption","node","ts","isObjectLiteralExpression","some","property","isPropertyAssignment","isIdentifier","text","isStringLiteral","initializer","kind","SyntaxKind","TrueKeyword","isMakeAutoObservableCallee","isPropertyAccessExpression","hasAutoBindInConstructor","classDecl","cached","get","undefined","ctor","members","find","member","isConstructorDeclaration","body","set","statement","statements","isExpressionStatement","expr","expression","isCallExpression","arguments","length","ThisKeyword","hasAutoBind","slice","arg","resolveSymbol","symbol","resolved","flags","SymbolFlags","Alias","getAliasedSymbol","getMethodInfo","aliased","Method","Property","Function","GetAccessor","SetAccessor","declarations","getDeclarations","methodDecl","decl","isMethodDeclaration","parent","isClassLike","getCombinedModifierFlags","isStatic","Boolean","ModifierFlags","Static","decorators","canHaveDecorators","getDecorators","hasDecorator","decorator","getDecoratorName","has","info","autoBind","usesThis","methodUsesThis","shouldReportSymbol","shouldReport","getSymbolForMember","tsNode","esTreeNodeToTSNodeMap","getSymbolAtLocation","isElementAccessExpression","argumentExpression","isStringLiteralLike","reportConstructorBinding","methodName","report","data","decoratorName","messageId","listeners","MemberExpression","AST_NODE_TYPES","Identifier","CallExpression","NewExpression","callee","isCallbackUsage","isThisBindCall","MethodDefinition","value","constructorBody","ExpressionStatement","AssignmentExpression","operator","left","right","object","ThisExpression","firstArg","unwrapExpression"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"no-unbound-method.js","sources":["../../../../src/rules/custom/no-unbound-method.ts"],"sourcesContent":["import {AST_NODE_TYPES, ESLintUtils} from '@typescript-eslint/utils';\nimport ts from 'typescript';\n\nimport {\n DEFAULT_DECORATORS,\n getDecoratorName,\n isCallbackUsage,\n isThisBindCall,\n methodUsesThis,\n normalizeDecoratorNames,\n unwrapExpression\n} from './utils/no-unbound-method-utils';\n\nimport type {TSESLint, TSESTree} from '@typescript-eslint/utils';\n\nconst createRule = ESLintUtils.RuleCreator(\n name => `https://github.com/nfqde/eslint-config-nfq/blob/master/docs/rules/${name}.md`\n);\n\ntype RuleOptions = [\n {decoratorNames?: string[]}?\n];\n\ntype MessageIds = 'constructorBind' | 'unboundMethod';\n\ntype MethodInfo = {\n autoBind: boolean;\n hasDecorator: boolean;\n isStatic: boolean;\n usesThis: boolean;\n};\n\nexport const noUnboundMethod = createRule<RuleOptions, MessageIds>({\n defaultOptions: [{decoratorNames: DEFAULT_DECORATORS}],\n meta: {\n docs: {description: 'Warn when unbound class methods that use this are passed as callbacks.'},\n messages: {\n constructorBind: 'Do not bind in the constructor; prefer {{decoratorName}}.',\n unboundMethod: 'Method {{methodName}} uses this and is passed unbound. Add {{decoratorName}} or bind it.'\n },\n schema: [\n {\n additionalProperties: false,\n properties: {\n decoratorNames: {\n items: {type: 'string'},\n type: 'array',\n uniqueItems: true\n }\n },\n type: 'object'\n }\n ],\n type: 'problem'\n },\n name: 'no-unbound-method',\n /**\n * Creates the rule listeners and initializes caches for symbol analysis.\n * It wires the parser services to the TypeScript type checker and configures decorator handling.\n * It returns ESLint listeners that report unbound method usage in callbacks and constructor binding.\n *\n * @param context Rule context used to access parser services and report issues.\n * @returns The rule listener map for this rule.\n *\n * @example\n * ```tsx\n * const listeners = rule.create(context);\n * ```\n */\n // eslint-disable-next-line max-lines-per-function\n create(context) {\n const services = ESLintUtils.getParserServices(context);\n const checker = services.program.getTypeChecker();\n const decoratorNames = normalizeDecoratorNames(context.options[0]?.decoratorNames);\n const decoratorLabel = `@${Array.from(decoratorNames)[0] ?? 'autobind'}`;\n const methodCache = new Map<ts.Symbol, MethodInfo>();\n const reportCache = new Map<ts.Symbol, boolean>();\n const aliasCache = new Map<ts.Symbol, ts.Symbol>();\n const memberSymbolCache = new Map<TSESTree.MemberExpression, ts.Symbol | null>();\n const classAutoBindCache = new WeakMap<ts.ClassLikeDeclaration, boolean>();\n\n /**\n * Determines whether a TypeScript expression represents an options object with autoBind enabled. It validates\n * that the node is an object literal and inspects its properties for an autoBind flag. It returns true only\n * when the property exists and its initializer is the boolean literal true.\n *\n * @param node The TypeScript expression to inspect for an autoBind option.\n * @returns True when the expression is an object literal with autoBind set to true.\n *\n * @example\n * ```tsx\n * const enabled = isAutoBindOption(optionsExpression);\n * ```\n */\n const isAutoBindOption = (node: ts.Expression): boolean => {\n if (!ts.isObjectLiteralExpression(node)) {\n return false;\n }\n\n return node.properties.some(property => {\n if (!ts.isPropertyAssignment(property)) {\n return false;\n }\n\n // eslint-disable-next-line no-nested-ternary\n const name = ts.isIdentifier(property.name)\n ? property.name.text\n : ts.isStringLiteral(property.name)\n ? property.name.text\n : null;\n\n if (name !== 'autoBind') {\n return false;\n }\n\n return property.initializer.kind === ts.SyntaxKind.TrueKeyword;\n });\n };\n\n /**\n * Checks whether a TypeScript expression refers to a makeAutoObservable call target. It accepts both a direct\n * identifier reference and a property access reference on another expression. It returns true only when the\n * identifier text matches the expected callee name.\n *\n * @param node The TypeScript expression to test as a potential callee.\n * @returns True when the expression resolves to a makeAutoObservable identifier or property access.\n *\n * @example\n * ```tsx\n * const matches = isMakeAutoObservableCallee(callExpression.expression);\n * ```\n */\n const isMakeAutoObservableCallee = (node: ts.Expression): boolean => {\n if (ts.isIdentifier(node)) {\n return node.text === 'makeAutoObservable';\n }\n\n return ts.isPropertyAccessExpression(node) && node.name.text === 'makeAutoObservable';\n };\n\n /**\n * Determines whether a class constructor calls `makeAutoObservable` with autoBind enabled. It inspects the\n * constructor body for a qualifying call expression and caches the result to avoid repeated scans. It returns\n * true only when the call targets `this` and includes an options object that sets autoBind to true.\n *\n * @param classDecl The class declaration whose constructor should be inspected.\n * @returns True when the constructor configures autoBind via makeAutoObservable.\n *\n *\n * @example\n * ```tsx\n * const enabled = hasAutoBindInConstructor(classDeclaration);\n * ```\n */\n const hasAutoBindInConstructor = (classDecl: ts.ClassLikeDeclaration): boolean => {\n const cached = classAutoBindCache.get(classDecl);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const ctor = classDecl.members.find(member => ts.isConstructorDeclaration(member));\n\n if (!ctor?.body) {\n classAutoBindCache.set(classDecl, false);\n\n return false;\n }\n\n for (const statement of ctor.body.statements) {\n if (!ts.isExpressionStatement(statement)) {\n continue;\n }\n\n const expr = statement.expression;\n\n if (!ts.isCallExpression(expr)) {\n continue;\n }\n\n if (!isMakeAutoObservableCallee(expr.expression)) {\n continue;\n }\n\n // eslint-disable-next-line @nfq/no-magic-numbers\n if (expr.arguments.length < 2) {\n continue;\n }\n\n if (expr.arguments[0].kind !== ts.SyntaxKind.ThisKeyword) {\n continue;\n }\n\n const hasAutoBind = expr.arguments.slice(1).some(arg => isAutoBindOption(arg));\n\n if (hasAutoBind) {\n classAutoBindCache.set(classDecl, true);\n\n return true;\n }\n }\n\n classAutoBindCache.set(classDecl, false);\n\n return false;\n };\n\n /**\n * Resolves an alias symbol to its original symbol and caches the result for reuse.\n * This function avoids repeated TypeScript checker work by memoizing alias resolutions.\n * It ensures downstream analysis consistently uses the same canonical symbol instance.\n *\n * @param symbol The symbol that may be an alias and needs resolution.\n * @returns The resolved canonical symbol for further analysis.\n *\n * @example\n * ```tsx\n * const resolved = resolveSymbol(methodSymbol);\n * ```\n */\n const resolveSymbol = (symbol: ts.Symbol): ts.Symbol => {\n const cached = aliasCache.get(symbol);\n\n if (cached) {\n return cached;\n }\n\n // eslint-disable-next-line no-bitwise\n const resolved = symbol.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;\n\n aliasCache.set(symbol, resolved);\n\n return resolved;\n };\n\n /**\n * Computes metadata about a method-like symbol and caches the result for later use.\n * This function filters out non-method symbols and constructor declarations before gathering details.\n * It also determines decorator presence, static status, and whether the method uses `this`.\n *\n * @param symbol The symbol to inspect for method information.\n * @returns The resolved method info or null when the symbol is not a supported method.\n *\n * @example\n * ```tsx\n * const info = getMethodInfo(methodSymbol);\n * ```\n */\n const getMethodInfo = (symbol: ts.Symbol): MethodInfo | null => {\n const aliased = resolveSymbol(symbol);\n const cached = methodCache.get(aliased);\n\n if (cached) {\n return cached;\n }\n\n if (!(\n // eslint-disable-next-line no-bitwise\n aliased.flags\n & (\n // eslint-disable-next-line no-bitwise\n ts.SymbolFlags.Method\n | ts.SymbolFlags.Property\n | ts.SymbolFlags.Function\n | ts.SymbolFlags.GetAccessor\n | ts.SymbolFlags.SetAccessor\n )\n )) {\n return null;\n }\n\n const declarations = aliased.getDeclarations() ?? [];\n const methodDecl = declarations\n .find(decl => ts.isMethodDeclaration(decl));\n\n if (!methodDecl?.parent || !ts.isClassLike(methodDecl.parent)) {\n return null;\n }\n\n if (ts.isIdentifier(methodDecl.name) && methodDecl.name.text === 'constructor') {\n return null;\n }\n\n const flags = ts.getCombinedModifierFlags(methodDecl);\n // eslint-disable-next-line no-bitwise\n const isStatic = Boolean(flags & ts.ModifierFlags.Static);\n const decorators = ts.canHaveDecorators(methodDecl) ? ts.getDecorators(methodDecl) : undefined;\n const hasDecorator = Boolean(decorators?.some(decorator => {\n const name = getDecoratorName(decorator);\n\n return Boolean(name && decoratorNames.has(name));\n }));\n\n const info: MethodInfo = {\n autoBind: hasAutoBindInConstructor(methodDecl.parent),\n hasDecorator,\n isStatic,\n usesThis: methodUsesThis(methodDecl)\n };\n\n methodCache.set(aliased, info);\n\n return info;\n };\n\n /**\n * Determines whether a symbol should be reported as an unbound method.\n * It resolves aliases and consults cached results to avoid repeated analysis.\n * It evaluates method metadata to ensure only non-static methods that use `this` and lack decorators are flagged.\n *\n * @param symbol The symbol to evaluate for reporting eligibility.\n * @returns True when the symbol represents an unbound method that should be reported.\n *\n * @example\n * ```tsx\n * const shouldReport = shouldReportSymbol(methodSymbol);\n * ```\n */\n const shouldReportSymbol = (symbol: ts.Symbol): boolean => {\n const aliased = resolveSymbol(symbol);\n const cached = reportCache.get(aliased);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const info = getMethodInfo(aliased);\n\n if (!info) {\n reportCache.set(aliased, false);\n\n return false;\n }\n\n const shouldReport = !info.autoBind && !info.isStatic && info.usesThis && !info.hasDecorator;\n\n reportCache.set(aliased, shouldReport);\n\n return shouldReport;\n };\n\n /**\n * Resolves the TypeScript symbol associated with a member expression node.\n * It caches the resolved symbol to minimize repeated checker lookups.\n * It supports both property access and string-literal element access expressions.\n *\n * @param node The member expression node to resolve into a symbol.\n * @returns The resolved symbol, or null when no symbol can be determined.\n *\n * @example\n * ```tsx\n * const symbol = getSymbolForMember(memberExpression);\n * ```\n */\n const getSymbolForMember = (node: TSESTree.MemberExpression): ts.Symbol | null => {\n const cached = memberSymbolCache.get(node);\n\n if (cached !== undefined) {\n return cached;\n }\n\n const tsNode = services.esTreeNodeToTSNodeMap.get(node);\n\n if (ts.isPropertyAccessExpression(tsNode)) {\n const symbol = checker.getSymbolAtLocation(tsNode.name) ?? null;\n\n memberSymbolCache.set(node, symbol);\n\n return symbol;\n }\n\n if (ts.isElementAccessExpression(tsNode)) {\n const arg = tsNode.argumentExpression;\n\n if (ts.isStringLiteralLike(arg)) {\n const symbol = checker.getSymbolAtLocation(arg) ?? null;\n\n memberSymbolCache.set(node, symbol);\n\n return symbol;\n }\n }\n\n memberSymbolCache.set(node, null);\n\n return null;\n };\n\n /**\n * Reports a constructor binding assignment for a method name.\n * It formats the report data with the decorator label and method name.\n * It delegates to the ESLint context to surface the diagnostic at the provided node.\n *\n * @param methodName The name of the method that was bound in the constructor.\n * @param node The AST node that represents the binding expression.\n *\n * @example\n * ```tsx\n * reportConstructorBinding('handleClick', expressionNode);\n * ```\n */\n const reportConstructorBinding = (methodName: string, node: TSESTree.Node) => {\n context.report({\n data: {\n decoratorName: decoratorLabel,\n methodName\n },\n messageId: 'constructorBind',\n node\n });\n };\n\n const listeners: TSESLint.RuleListener = {\n /**\n * Processes a member expression node to determine whether it represents an unbound method usage that\n * should be reported. It performs multiple early returns based on node shape, parent usage, callback context,\n * binding checks, and symbol eligibility. It ultimately reports a linting issue when all conditions\n * indicate a problematic unbound method reference.\n *\n * @param node The member expression node to analyze for unbound method usage.\n *\n * @example\n * ```tsx\n * MemberExpression(node);\n * ```\n */\n MemberExpression(node: TSESTree.MemberExpression) {\n if (node.property.type !== AST_NODE_TYPES.Identifier) {\n return;\n }\n\n const {parent} = node;\n\n if (\n (parent.type === AST_NODE_TYPES.CallExpression || parent.type === AST_NODE_TYPES.NewExpression)\n && parent.callee === node\n ) {\n return;\n }\n\n if (!isCallbackUsage(node)) {\n return;\n }\n\n if (isThisBindCall(node)) {\n return;\n }\n\n const symbol = getSymbolForMember(node);\n\n if (!symbol) {\n return;\n }\n\n if (!shouldReportSymbol(symbol)) {\n return;\n }\n\n context.report({\n data: {\n decoratorName: decoratorLabel,\n methodName: node.property.name\n },\n messageId: 'unboundMethod',\n node\n });\n },\n /**\n * Inspects constructor method definitions to detect assignments where a class method\n * is bound to `this` via `.bind(this)`, and reports such bindings for further handling.\n *\n * @param node The method definition node to analyze.\n */\n MethodDefinition(node: TSESTree.MethodDefinition) {\n if (node.kind !== 'constructor' || !node.value.body) {\n return;\n }\n\n const constructorBody = node.value.body;\n\n for (const statement of constructorBody.body) {\n if (statement.type !== AST_NODE_TYPES.ExpressionStatement) {\n continue;\n }\n\n const {expression} = statement;\n\n if (expression.type !== AST_NODE_TYPES.AssignmentExpression || expression.operator !== '=') {\n continue;\n }\n\n const {left, right} = expression;\n\n if (\n left.type !== AST_NODE_TYPES.MemberExpression\n || left.property.type !== AST_NODE_TYPES.Identifier\n ) {\n continue;\n }\n\n if (left.object.type !== AST_NODE_TYPES.ThisExpression) {\n continue;\n }\n\n if (right.type !== AST_NODE_TYPES.CallExpression) {\n continue;\n }\n\n const {callee} = right;\n\n if (callee.type !== AST_NODE_TYPES.MemberExpression) {\n continue;\n }\n\n if (\n callee.object.type !== AST_NODE_TYPES.MemberExpression\n || callee.object.object.type !== AST_NODE_TYPES.ThisExpression\n || callee.object.property.type !== AST_NODE_TYPES.Identifier\n || callee.object.property.name !== left.property.name\n ) {\n continue;\n }\n\n if (\n callee.property.type !== AST_NODE_TYPES.Identifier\n || callee.property.name !== 'bind'\n ) {\n continue;\n }\n\n if (right.arguments.length === 0) {\n continue;\n }\n\n const firstArg = unwrapExpression(right.arguments[0] as TSESTree.Expression);\n\n if (firstArg.type !== AST_NODE_TYPES.ThisExpression) {\n continue;\n }\n\n reportConstructorBinding(left.property.name, expression);\n }\n }\n };\n\n return listeners;\n }\n});"],"names":["createRule","ESLintUtils","RuleCreator","name","noUnboundMethod","defaultOptions","decoratorNames","DEFAULT_DECORATORS","meta","docs","description","messages","constructorBind","unboundMethod","schema","additionalProperties","properties","items","type","uniqueItems","create","context","services","getParserServices","checker","program","getTypeChecker","normalizeDecoratorNames","options","decoratorLabel","Array","from","methodCache","Map","reportCache","aliasCache","memberSymbolCache","classAutoBindCache","WeakMap","isAutoBindOption","node","ts","isObjectLiteralExpression","some","property","isPropertyAssignment","isIdentifier","text","isStringLiteral","initializer","kind","SyntaxKind","TrueKeyword","isMakeAutoObservableCallee","isPropertyAccessExpression","hasAutoBindInConstructor","classDecl","cached","get","undefined","ctor","members","find","member","isConstructorDeclaration","body","set","statement","statements","isExpressionStatement","expr","expression","isCallExpression","arguments","length","ThisKeyword","hasAutoBind","slice","arg","resolveSymbol","symbol","resolved","flags","SymbolFlags","Alias","getAliasedSymbol","getMethodInfo","aliased","Method","Property","Function","GetAccessor","SetAccessor","declarations","getDeclarations","methodDecl","decl","isMethodDeclaration","parent","isClassLike","getCombinedModifierFlags","isStatic","Boolean","ModifierFlags","Static","decorators","canHaveDecorators","getDecorators","hasDecorator","decorator","getDecoratorName","has","info","autoBind","usesThis","methodUsesThis","shouldReportSymbol","shouldReport","getSymbolForMember","tsNode","esTreeNodeToTSNodeMap","getSymbolAtLocation","isElementAccessExpression","argumentExpression","isStringLiteralLike","reportConstructorBinding","methodName","report","data","decoratorName","messageId","listeners","MemberExpression","AST_NODE_TYPES","Identifier","CallExpression","NewExpression","callee","isCallbackUsage","isThisBindCall","MethodDefinition","value","constructorBody","ExpressionStatement","AssignmentExpression","operator","left","right","object","ThisExpression","firstArg","unwrapExpression"],"mappings":"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"}
|
|
@@ -295,5 +295,5 @@ const collectTypeReferences = (typeNode, names) => {
|
|
|
295
295
|
}
|
|
296
296
|
};
|
|
297
297
|
|
|
298
|
-
export { collectTypeReferences, getComponentInfoFromStatement, getTypeDeclarationInfo, isComponentExport, isDefaultComponentExport, isDisplayNameAssignment, isStyledDeclaration, isStyledFactoryDeclaration };
|
|
298
|
+
export { collectTypeReferences, containsJsx, getComponentInfoFromStatement, getTypeDeclarationInfo, isComponentExport, isDefaultComponentExport, isDisplayNameAssignment, isStyledDeclaration, isStyledFactoryDeclaration };
|
|
299
299
|
//# sourceMappingURL=component-file-structure-utils.js.map
|