@akanjs/cli 2.3.11-rc.8 → 2.3.11-rc.9

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.
@@ -15706,6 +15706,7 @@ class AkanQualityScanner {
15706
15706
  const warnings = [
15707
15707
  ...this.#scanGlobalQuality(sourceFiles),
15708
15708
  ...sourceFiles.flatMap((sourceFile2) => this.#scanSingleFileQuality(sourceFile2)),
15709
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanComponentQuality(sourceFile2)),
15709
15710
  ...sourceFiles.flatMap((sourceFile2) => this.#scanConventionQuality(sourceFile2)),
15710
15711
  ...sourceFiles.flatMap((sourceFile2) => this.#scanLayoutQuality(sourceFile2))
15711
15712
  ];
@@ -15834,6 +15835,28 @@ class AkanQualityScanner {
15834
15835
  }
15835
15836
  return warnings;
15836
15837
  }
15838
+ #scanComponentQuality(sourceFile2) {
15839
+ if (!isComponentDeclarationFile(sourceFile2.file))
15840
+ return [];
15841
+ const exportedNames = getExportedDeclarationNames(sourceFile2.sourceFile);
15842
+ const allowedInternalInterfaces = new Set([...exportedNames].map((name) => `${name}Props`));
15843
+ const warnings = [];
15844
+ for (const declaration of getInternalTypeOrFunctionDeclarations(sourceFile2.sourceFile)) {
15845
+ if (exportedNames.has(declaration.name))
15846
+ continue;
15847
+ if (declaration.kind === "interface" && allowedInternalInterfaces.has(declaration.name))
15848
+ continue;
15849
+ warnings.push({
15850
+ rule: "akan.file.component-internal-declaration",
15851
+ scope: "file",
15852
+ severity: "warning",
15853
+ file: sourceFile2.file,
15854
+ line: declaration.line,
15855
+ message: `Component file should not declare non-exported ${declaration.kind} "${declaration.name}". Only a "<Component>Props" interface may stay internal; move other types or helpers to a separate file in ui/, webkit/, or common/ by purpose.`
15856
+ });
15857
+ }
15858
+ return warnings;
15859
+ }
15837
15860
  #scanConventionQuality(sourceFile2) {
15838
15861
  const suffix = CONVENTION_SUFFIXES.find((candidate) => sourceFile2.file.endsWith(candidate));
15839
15862
  if (!suffix)
@@ -15917,7 +15940,7 @@ function formatQualityLocation(file, line) {
15917
15940
  }
15918
15941
  function getExportedFunctionLikes(sourceFile2) {
15919
15942
  const declarations = [];
15920
- const pageExempt = isPageRouteFile(sourceFile2.file);
15943
+ const nameExempt = isPageRouteFile(sourceFile2.file) || isUiComponentFile(sourceFile2.file);
15921
15944
  for (const statement of sourceFile2.sourceFile.statements) {
15922
15945
  if (ts11.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
15923
15946
  declarations.push({
@@ -15926,7 +15949,7 @@ function getExportedFunctionLikes(sourceFile2) {
15926
15949
  file: sourceFile2.file,
15927
15950
  line: getLine(sourceFile2.sourceFile, statement),
15928
15951
  bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement.body),
15929
- duplicateNameExempt: pageExempt || isConventionDuplicateNameExempt(sourceFile2.file, false)
15952
+ duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile2.file, false)
15930
15953
  });
15931
15954
  }
15932
15955
  if (ts11.isClassDeclaration(statement) && statement.name && isExported(statement)) {
@@ -15936,7 +15959,7 @@ function getExportedFunctionLikes(sourceFile2) {
15936
15959
  file: sourceFile2.file,
15937
15960
  line: getLine(sourceFile2.sourceFile, statement),
15938
15961
  bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement),
15939
- duplicateNameExempt: pageExempt || isConventionDuplicateNameExempt(sourceFile2.file, isEnumClassStatement(sourceFile2.sourceFile, statement))
15962
+ duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile2.file, isEnumClassStatement(sourceFile2.sourceFile, statement))
15940
15963
  });
15941
15964
  }
15942
15965
  if (ts11.isVariableStatement(statement) && isExported(statement)) {
@@ -15949,7 +15972,7 @@ function getExportedFunctionLikes(sourceFile2) {
15949
15972
  file: sourceFile2.file,
15950
15973
  line: getLine(sourceFile2.sourceFile, declaration),
15951
15974
  bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, declaration.initializer),
15952
- duplicateNameExempt: pageExempt || isConventionDuplicateNameExempt(sourceFile2.file, false)
15975
+ duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile2.file, false)
15953
15976
  });
15954
15977
  }
15955
15978
  }
@@ -15960,6 +15983,10 @@ function isPageRouteFile(file) {
15960
15983
  const segments = file.split("/");
15961
15984
  return (segments[0] === "apps" || segments[0] === "libs") && segments[2] === "page";
15962
15985
  }
15986
+ function isUiComponentFile(file) {
15987
+ const segments = file.split("/");
15988
+ return (segments[0] === "apps" || segments[0] === "libs") && segments[2] === "ui";
15989
+ }
15963
15990
  function isConventionDuplicateNameExempt(file, isEnumClass) {
15964
15991
  if (!isInLibModule(file))
15965
15992
  return false;
@@ -15985,6 +16012,72 @@ function isEnumClassStatement(sourceFile2, statement) {
15985
16012
  function getExportedClassNames(sourceFile2) {
15986
16013
  return sourceFile2.statements.filter((statement) => ts11.isClassDeclaration(statement) && !!statement.name).filter((statement) => isExported(statement)).map((statement) => statement.name.text);
15987
16014
  }
16015
+ function isComponentDeclarationFile(file) {
16016
+ if (!file.endsWith(".tsx"))
16017
+ return false;
16018
+ const segments = file.split("/");
16019
+ const [root, , area] = segments;
16020
+ if (root !== "apps" && root !== "libs")
16021
+ return false;
16022
+ if (area === "lib" || area === "ui")
16023
+ return true;
16024
+ return root === "apps" && area === "page";
16025
+ }
16026
+ function getExportedDeclarationNames(sourceFile2) {
16027
+ const names = new Set;
16028
+ for (const statement of sourceFile2.statements) {
16029
+ if (ts11.isExportDeclaration(statement) && statement.exportClause && ts11.isNamedExports(statement.exportClause)) {
16030
+ for (const element of statement.exportClause.elements) {
16031
+ names.add(element.name.text);
16032
+ if (element.propertyName)
16033
+ names.add(element.propertyName.text);
16034
+ }
16035
+ continue;
16036
+ }
16037
+ if (!isExported(statement))
16038
+ continue;
16039
+ for (const name of getStatementDeclarationNames(statement))
16040
+ names.add(name);
16041
+ }
16042
+ return names;
16043
+ }
16044
+ function getStatementDeclarationNames(statement) {
16045
+ if (ts11.isFunctionDeclaration(statement) && statement.name)
16046
+ return [statement.name.text];
16047
+ if (ts11.isClassDeclaration(statement) && statement.name)
16048
+ return [statement.name.text];
16049
+ if (ts11.isInterfaceDeclaration(statement))
16050
+ return [statement.name.text];
16051
+ if (ts11.isTypeAliasDeclaration(statement))
16052
+ return [statement.name.text];
16053
+ if (ts11.isEnumDeclaration(statement))
16054
+ return [statement.name.text];
16055
+ if (ts11.isVariableStatement(statement)) {
16056
+ return statement.declarationList.declarations.filter((declaration) => ts11.isIdentifier(declaration.name)).map((declaration) => declaration.name.text);
16057
+ }
16058
+ return [];
16059
+ }
16060
+ function getInternalTypeOrFunctionDeclarations(sourceFile2) {
16061
+ const declarations = [];
16062
+ for (const statement of sourceFile2.statements) {
16063
+ if (isExported(statement))
16064
+ continue;
16065
+ if (ts11.isInterfaceDeclaration(statement)) {
16066
+ declarations.push({ name: statement.name.text, kind: "interface", line: getLine(sourceFile2, statement) });
16067
+ } else if (ts11.isTypeAliasDeclaration(statement)) {
16068
+ declarations.push({ name: statement.name.text, kind: "type", line: getLine(sourceFile2, statement) });
16069
+ } else if (ts11.isFunctionDeclaration(statement) && statement.name) {
16070
+ declarations.push({ name: statement.name.text, kind: "function", line: getLine(sourceFile2, statement) });
16071
+ } else if (ts11.isVariableStatement(statement)) {
16072
+ for (const declaration of statement.declarationList.declarations) {
16073
+ if (!ts11.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
16074
+ continue;
16075
+ declarations.push({ name: declaration.name.text, kind: "function", line: getLine(sourceFile2, declaration) });
16076
+ }
16077
+ }
16078
+ }
16079
+ return declarations;
16080
+ }
15988
16081
  function getPlaceholderExportWarnings(sourceFile2) {
15989
16082
  if (!sourceFile2.file.endsWith("/index.ts") && !sourceFile2.file.endsWith("/index.tsx"))
15990
16083
  return [];
package/index.js CHANGED
@@ -15704,6 +15704,7 @@ class AkanQualityScanner {
15704
15704
  const warnings = [
15705
15705
  ...this.#scanGlobalQuality(sourceFiles),
15706
15706
  ...sourceFiles.flatMap((sourceFile2) => this.#scanSingleFileQuality(sourceFile2)),
15707
+ ...sourceFiles.flatMap((sourceFile2) => this.#scanComponentQuality(sourceFile2)),
15707
15708
  ...sourceFiles.flatMap((sourceFile2) => this.#scanConventionQuality(sourceFile2)),
15708
15709
  ...sourceFiles.flatMap((sourceFile2) => this.#scanLayoutQuality(sourceFile2))
15709
15710
  ];
@@ -15832,6 +15833,28 @@ class AkanQualityScanner {
15832
15833
  }
15833
15834
  return warnings;
15834
15835
  }
15836
+ #scanComponentQuality(sourceFile2) {
15837
+ if (!isComponentDeclarationFile(sourceFile2.file))
15838
+ return [];
15839
+ const exportedNames = getExportedDeclarationNames(sourceFile2.sourceFile);
15840
+ const allowedInternalInterfaces = new Set([...exportedNames].map((name) => `${name}Props`));
15841
+ const warnings = [];
15842
+ for (const declaration of getInternalTypeOrFunctionDeclarations(sourceFile2.sourceFile)) {
15843
+ if (exportedNames.has(declaration.name))
15844
+ continue;
15845
+ if (declaration.kind === "interface" && allowedInternalInterfaces.has(declaration.name))
15846
+ continue;
15847
+ warnings.push({
15848
+ rule: "akan.file.component-internal-declaration",
15849
+ scope: "file",
15850
+ severity: "warning",
15851
+ file: sourceFile2.file,
15852
+ line: declaration.line,
15853
+ message: `Component file should not declare non-exported ${declaration.kind} "${declaration.name}". Only a "<Component>Props" interface may stay internal; move other types or helpers to a separate file in ui/, webkit/, or common/ by purpose.`
15854
+ });
15855
+ }
15856
+ return warnings;
15857
+ }
15835
15858
  #scanConventionQuality(sourceFile2) {
15836
15859
  const suffix = CONVENTION_SUFFIXES.find((candidate) => sourceFile2.file.endsWith(candidate));
15837
15860
  if (!suffix)
@@ -15915,7 +15938,7 @@ function formatQualityLocation(file, line) {
15915
15938
  }
15916
15939
  function getExportedFunctionLikes(sourceFile2) {
15917
15940
  const declarations = [];
15918
- const pageExempt = isPageRouteFile(sourceFile2.file);
15941
+ const nameExempt = isPageRouteFile(sourceFile2.file) || isUiComponentFile(sourceFile2.file);
15919
15942
  for (const statement of sourceFile2.sourceFile.statements) {
15920
15943
  if (ts11.isFunctionDeclaration(statement) && statement.name && isExported(statement)) {
15921
15944
  declarations.push({
@@ -15924,7 +15947,7 @@ function getExportedFunctionLikes(sourceFile2) {
15924
15947
  file: sourceFile2.file,
15925
15948
  line: getLine(sourceFile2.sourceFile, statement),
15926
15949
  bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement.body),
15927
- duplicateNameExempt: pageExempt || isConventionDuplicateNameExempt(sourceFile2.file, false)
15950
+ duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile2.file, false)
15928
15951
  });
15929
15952
  }
15930
15953
  if (ts11.isClassDeclaration(statement) && statement.name && isExported(statement)) {
@@ -15934,7 +15957,7 @@ function getExportedFunctionLikes(sourceFile2) {
15934
15957
  file: sourceFile2.file,
15935
15958
  line: getLine(sourceFile2.sourceFile, statement),
15936
15959
  bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, statement),
15937
- duplicateNameExempt: pageExempt || isConventionDuplicateNameExempt(sourceFile2.file, isEnumClassStatement(sourceFile2.sourceFile, statement))
15960
+ duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile2.file, isEnumClassStatement(sourceFile2.sourceFile, statement))
15938
15961
  });
15939
15962
  }
15940
15963
  if (ts11.isVariableStatement(statement) && isExported(statement)) {
@@ -15947,7 +15970,7 @@ function getExportedFunctionLikes(sourceFile2) {
15947
15970
  file: sourceFile2.file,
15948
15971
  line: getLine(sourceFile2.sourceFile, declaration),
15949
15972
  bodyFingerprint: getBodyFingerprint(sourceFile2.sourceFile, declaration.initializer),
15950
- duplicateNameExempt: pageExempt || isConventionDuplicateNameExempt(sourceFile2.file, false)
15973
+ duplicateNameExempt: nameExempt || isConventionDuplicateNameExempt(sourceFile2.file, false)
15951
15974
  });
15952
15975
  }
15953
15976
  }
@@ -15958,6 +15981,10 @@ function isPageRouteFile(file) {
15958
15981
  const segments = file.split("/");
15959
15982
  return (segments[0] === "apps" || segments[0] === "libs") && segments[2] === "page";
15960
15983
  }
15984
+ function isUiComponentFile(file) {
15985
+ const segments = file.split("/");
15986
+ return (segments[0] === "apps" || segments[0] === "libs") && segments[2] === "ui";
15987
+ }
15961
15988
  function isConventionDuplicateNameExempt(file, isEnumClass) {
15962
15989
  if (!isInLibModule(file))
15963
15990
  return false;
@@ -15983,6 +16010,72 @@ function isEnumClassStatement(sourceFile2, statement) {
15983
16010
  function getExportedClassNames(sourceFile2) {
15984
16011
  return sourceFile2.statements.filter((statement) => ts11.isClassDeclaration(statement) && !!statement.name).filter((statement) => isExported(statement)).map((statement) => statement.name.text);
15985
16012
  }
16013
+ function isComponentDeclarationFile(file) {
16014
+ if (!file.endsWith(".tsx"))
16015
+ return false;
16016
+ const segments = file.split("/");
16017
+ const [root, , area] = segments;
16018
+ if (root !== "apps" && root !== "libs")
16019
+ return false;
16020
+ if (area === "lib" || area === "ui")
16021
+ return true;
16022
+ return root === "apps" && area === "page";
16023
+ }
16024
+ function getExportedDeclarationNames(sourceFile2) {
16025
+ const names = new Set;
16026
+ for (const statement of sourceFile2.statements) {
16027
+ if (ts11.isExportDeclaration(statement) && statement.exportClause && ts11.isNamedExports(statement.exportClause)) {
16028
+ for (const element of statement.exportClause.elements) {
16029
+ names.add(element.name.text);
16030
+ if (element.propertyName)
16031
+ names.add(element.propertyName.text);
16032
+ }
16033
+ continue;
16034
+ }
16035
+ if (!isExported(statement))
16036
+ continue;
16037
+ for (const name of getStatementDeclarationNames(statement))
16038
+ names.add(name);
16039
+ }
16040
+ return names;
16041
+ }
16042
+ function getStatementDeclarationNames(statement) {
16043
+ if (ts11.isFunctionDeclaration(statement) && statement.name)
16044
+ return [statement.name.text];
16045
+ if (ts11.isClassDeclaration(statement) && statement.name)
16046
+ return [statement.name.text];
16047
+ if (ts11.isInterfaceDeclaration(statement))
16048
+ return [statement.name.text];
16049
+ if (ts11.isTypeAliasDeclaration(statement))
16050
+ return [statement.name.text];
16051
+ if (ts11.isEnumDeclaration(statement))
16052
+ return [statement.name.text];
16053
+ if (ts11.isVariableStatement(statement)) {
16054
+ return statement.declarationList.declarations.filter((declaration) => ts11.isIdentifier(declaration.name)).map((declaration) => declaration.name.text);
16055
+ }
16056
+ return [];
16057
+ }
16058
+ function getInternalTypeOrFunctionDeclarations(sourceFile2) {
16059
+ const declarations = [];
16060
+ for (const statement of sourceFile2.statements) {
16061
+ if (isExported(statement))
16062
+ continue;
16063
+ if (ts11.isInterfaceDeclaration(statement)) {
16064
+ declarations.push({ name: statement.name.text, kind: "interface", line: getLine(sourceFile2, statement) });
16065
+ } else if (ts11.isTypeAliasDeclaration(statement)) {
16066
+ declarations.push({ name: statement.name.text, kind: "type", line: getLine(sourceFile2, statement) });
16067
+ } else if (ts11.isFunctionDeclaration(statement) && statement.name) {
16068
+ declarations.push({ name: statement.name.text, kind: "function", line: getLine(sourceFile2, statement) });
16069
+ } else if (ts11.isVariableStatement(statement)) {
16070
+ for (const declaration of statement.declarationList.declarations) {
16071
+ if (!ts11.isIdentifier(declaration.name) || !isFunctionLikeInitializer(declaration.initializer))
16072
+ continue;
16073
+ declarations.push({ name: declaration.name.text, kind: "function", line: getLine(sourceFile2, declaration) });
16074
+ }
16075
+ }
16076
+ }
16077
+ return declarations;
16078
+ }
15986
16079
  function getPlaceholderExportWarnings(sourceFile2) {
15987
16080
  if (!sourceFile2.file.endsWith("/index.ts") && !sourceFile2.file.endsWith("/index.tsx"))
15988
16081
  return [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akanjs/cli",
3
- "version": "2.3.11-rc.8",
3
+ "version": "2.3.11-rc.9",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -34,7 +34,7 @@
34
34
  "@langchain/openai": "^1.4.6",
35
35
  "@tailwindcss/node": "^4.3.0",
36
36
  "@trapezedev/project": "^7.1.4",
37
- "akanjs": "2.3.11-rc.8",
37
+ "akanjs": "2.3.11-rc.9",
38
38
  "chalk": "^5.6.2",
39
39
  "commander": "^14.0.3",
40
40
  "daisyui": "5.5.23",
@@ -132,6 +132,20 @@
132
132
  }
133
133
  },
134
134
  "overrides": [
135
+ {
136
+ "includes": [
137
+ "apps/**/*.ts",
138
+ "apps/**/*.tsx",
139
+ "libs/**/*.ts",
140
+ "libs/**/*.tsx",
141
+ "!**/*.test.ts",
142
+ "!**/*.test.tsx",
143
+ "!**/*.spec.ts",
144
+ "!**/*.spec.tsx",
145
+ "!**/common/**"
146
+ ],
147
+ "plugins": ["./node_modules/@akanjs/devkit/lint/no-throw-raw-error.grit"]
148
+ },
135
149
  {
136
150
  "includes": ["**/page/**/*.ts", "**/page/**/*.tsx", "**/*.Unit.tsx", "**/*.View.tsx"],
137
151
  "plugins": [