pasika 0.10.35 → 0.10.37

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.
@@ -1882,11 +1882,21 @@ function resolveComponentPlacement(componentFile, index) {
1882
1882
  return { countedConsumers: counted, expectedFolder: shared, reason: "ccf" };
1883
1883
  }
1884
1884
  var formatFolder = (folder) => `src/${folder.join("/")}/`;
1885
+ function hasExactEntry(directory, entryName) {
1886
+ try {
1887
+ return fs2.readdirSync(directory).includes(entryName);
1888
+ } catch {
1889
+ return false;
1890
+ }
1891
+ }
1892
+ function hasComponentOwner(folderPath, folderName) {
1893
+ return hasExactEntry(folderPath, `${folderName}.tsx`);
1894
+ }
1885
1895
  function isValidComponentFolder(folder, sourceRoot) {
1886
1896
  const name = folder[folder.length - 1];
1887
1897
  if (!name || SUPPORT_FOLDERS2.has(name)) return false;
1888
1898
  const folderPath = path10.join(sourceRoot, ...folder);
1889
- return fs2.existsSync(path10.join(folderPath, `${name}.tsx`)) && fs2.existsSync(path10.join(folderPath, "index.ts"));
1899
+ return hasComponentOwner(folderPath, name) && hasExactEntry(folderPath, "index.ts");
1890
1900
  }
1891
1901
  function owningFolderOf(consumer, sourceRoot) {
1892
1902
  const result = outOfSupportFolders(folderSegmentsOf(consumer, sourceRoot));
@@ -2156,7 +2166,7 @@ function componentFolderStart(segments) {
2156
2166
  return -1;
2157
2167
  }
2158
2168
  function hasComponentBarrel(folderPath) {
2159
- return fs4.existsSync(path13.join(folderPath, "index.ts"));
2169
+ return hasExactEntry(folderPath, "index.ts");
2160
2170
  }
2161
2171
  function componentFolderViolation(segments, sourceRoot) {
2162
2172
  const start = componentFolderStart(segments);
@@ -2166,7 +2176,7 @@ function componentFolderViolation(segments, sourceRoot) {
2166
2176
  if (!folder || SUPPORT_FOLDERS2.has(folder)) continue;
2167
2177
  const folderPath = path13.join(sourceRoot, ...segments.slice(0, depth + 1));
2168
2178
  const label = `src/${segments.slice(0, depth + 1).join("/")}/`;
2169
- if (!fs4.existsSync(path13.join(folderPath, `${folder}.tsx`))) {
2179
+ if (!hasComponentOwner(folderPath, folder)) {
2170
2180
  return `A folder that is not a support folder must be a component folder; add "${folder}.tsx" to ${label} or move its files into a support folder.`;
2171
2181
  }
2172
2182
  if (!hasComponentBarrel(folderPath)) {
@@ -3875,40 +3885,70 @@ var hookComplexityRule = {
3875
3885
  if (relative.startsWith("..")) return {};
3876
3886
  const segments = relative.split(path28.sep);
3877
3887
  const sourceText = context.sourceCode.text;
3878
- function checkHook(node, name, body, exported) {
3879
- if (!exported) return;
3880
- if (!name || !isHookName3(name)) return;
3881
- if (!body) return;
3882
- const score = computeExtractionScore(body, sourceText);
3888
+ const index = getProjectIndex(sourceRoot);
3889
+ const localHookCalls = /* @__PURE__ */ new Set();
3890
+ const hooks = [];
3891
+ function registerHook(node, name, body, exported) {
3892
+ if (!name || !isHookName3(name) || !body) return;
3893
+ hooks.push({ node, name, body, exported });
3894
+ }
3895
+ function reportHooks() {
3883
3896
  const parentFolder = segments.length >= 2 ? segments[segments.length - 2] : void 0;
3884
3897
  const inSupportFolder = parentFolder === "hooks";
3885
- if (score >= 2 && !inSupportFolder) {
3886
- context.report({
3887
- node,
3888
- message: `Hook "${name}" has an extraction score of ${String(score)} and must be extracted to a hooks/ folder. See docs/next-codebase-guide/rules/hook-extraction-rule.md`
3889
- });
3890
- } else if (score < 2 && inSupportFolder) {
3891
- context.report({
3892
- node,
3893
- message: `Hook "${name}" has an extraction score below two and must stay inline in its consumer file. See docs/next-codebase-guide/rules/hook-extraction-rule.md`
3894
- });
3898
+ for (const { node, name, body, exported } of hooks) {
3899
+ const score = computeExtractionScore(body, sourceText);
3900
+ const hasLocalConsumer = localHookCalls.has(name);
3901
+ const externalConsumerCount = exported ? index?.symbolConsumers.get(symbolKey(filename, name))?.size ?? 0 : 0;
3902
+ if (score >= 2) {
3903
+ if (!inSupportFolder && (exported || hasLocalConsumer)) {
3904
+ context.report({
3905
+ node,
3906
+ message: `Hook "${name}" has an extraction score of ${String(score)} and must be extracted to a hooks/ folder. See docs/next-codebase-guide/rules/hook-extraction-rule.md`
3907
+ });
3908
+ }
3909
+ continue;
3910
+ }
3911
+ const consumerCount = externalConsumerCount + (hasLocalConsumer ? 1 : 0);
3912
+ if (inSupportFolder && exported) {
3913
+ if (consumerCount < 2) {
3914
+ context.report({
3915
+ node,
3916
+ message: `Hook "${name}" has an extraction score below two and must be inlined directly into its sole consumer instead of remaining a custom hook. See docs/next-codebase-guide/rules/hook-extraction-rule.md`
3917
+ });
3918
+ }
3919
+ continue;
3920
+ }
3921
+ const isSingleLocalConsumer = hasLocalConsumer && externalConsumerCount === 0;
3922
+ if (isSingleLocalConsumer) {
3923
+ context.report({
3924
+ node,
3925
+ message: `Hook "${name}" has one local consumer and an extraction score below two; inline its React primitives and handlers directly into the consumer instead of keeping a custom hook. See docs/next-codebase-guide/rules/hook-extraction-rule.md`
3926
+ });
3927
+ }
3895
3928
  }
3896
3929
  }
3897
3930
  return {
3898
3931
  FunctionDeclaration(node) {
3899
3932
  const exported = node.parent?.type === "ExportNamedDeclaration";
3900
- checkHook(node, node.id?.name, node.body, exported);
3933
+ registerHook(node, node.id?.name, node.body, exported);
3901
3934
  },
3902
3935
  VariableDeclarator(node) {
3903
3936
  if (node.id.type !== "Identifier") return;
3904
- const exported = node.parent.parent?.type === "ExportNamedDeclaration";
3905
- if (!exported) return;
3906
3937
  const init = node.init;
3907
3938
  if (!init || init.type !== "ArrowFunctionExpression" && init.type !== "FunctionExpression") {
3908
3939
  return;
3909
3940
  }
3910
3941
  if (init.body.type !== "BlockStatement") return;
3911
- checkHook(node, node.id.name, init.body, true);
3942
+ const exported = node.parent.parent?.type === "ExportNamedDeclaration";
3943
+ registerHook(node, node.id.name, init.body, exported);
3944
+ },
3945
+ CallExpression(node) {
3946
+ if (node.callee.type === "Identifier" && isHookName3(node.callee.name)) {
3947
+ localHookCalls.add(node.callee.name);
3948
+ }
3949
+ },
3950
+ "Program:exit"() {
3951
+ reportHooks();
3912
3952
  }
3913
3953
  };
3914
3954
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pasika",
3
- "version": "0.10.35",
3
+ "version": "0.10.37",
4
4
  "description": "Reusable agent setup package",
5
5
  "repository": {
6
6
  "type": "git",