@ethisyscore/eslint-plugin-coreconnect 1.70.0 → 1.71.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ethisyscore/eslint-plugin-coreconnect",
3
- "version": "1.70.0",
3
+ "version": "1.71.1",
4
4
  "description": "ESLint rules enforcing EthisysCore plugin frontend conventions. Published so a new rule reaches every plugin on a version bump, rather than being copied into each scaffold and drifting.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -24,6 +24,26 @@ export function isHookName(name) {
24
24
  return /^use[A-Z]/.test(name);
25
25
  }
26
26
 
27
+ /**
28
+ * A component, by name.
29
+ *
30
+ * These rules match on a name SUFFIX, so anything ending in the suffix is a candidate - including
31
+ * plain conversion helpers that are not components at all. Measured on origin/develop: `finance`
32
+ * and `projects` and `timesheets` all declare `isoToDateInput` and `isoUtcToLocalDateTimeInput` in
33
+ * `utils/dateUtils.ts` (six), and `finance` a local `toDateInput` (one). Seven reports across three
34
+ * repos, none of them a component, each telling the author to replace an ISO-string converter with
35
+ * a React date field.
36
+ *
37
+ * React's own convention is what makes this safe to detect by name: a component MUST start with an
38
+ * uppercase letter or JSX treats it as an HTML tag, so `<toDateInput />` cannot be the thing these
39
+ * rules exist to catch. That makes the guard free - it cannot mask a real duplicate.
40
+ *
41
+ * @param {string} name
42
+ */
43
+ export function isComponentName(name) {
44
+ return /^[A-Z]/.test(name);
45
+ }
46
+
27
47
  /**
28
48
  * Is this initializer a component definition rather than a string, config object or hook result?
29
49
  *
@@ -80,8 +100,7 @@ export function createNoLocalComponentRule({ pattern, messageId, description, me
80
100
  if (!idNode || idNode.type !== "Identifier") {
81
101
  return;
82
102
  }
83
-
84
- if (isHookName(idNode.name) || !pattern.test(idNode.name)) {
103
+ if (isHookName(idNode.name) || !isComponentName(idNode.name) || !pattern.test(idNode.name)) {
85
104
  return;
86
105
  }
87
106
 
@@ -8,7 +8,12 @@
8
8
  * copies is six places for either to be dropped, and nothing would have noticed.
9
9
  *
10
10
  * Fires on a DECLARATION, never a use, so importing `DateInput` from
11
- * `@ethisyscore/plugin-ui/components/ui` and rendering it is what the rule wants.
11
+ * `@ethisyscore/plugin-ui/components/date` and rendering it is what the rule wants.
12
+ *
13
+ * The sub-path is `/date`, not `/components/ui`: the component needs `@mui/x-date-pickers` and
14
+ * `dayjs`, which are optional peers, so it has a barrel of its own that a plugin without date
15
+ * fields never imports. That also means the `@/components/ui/<name>` file-existence alias does not
16
+ * reach it - a plugin deleting its local `date-input.tsx` changes the import to the package.
12
17
  *
13
18
  * @type {import("eslint").Rule.RuleModule}
14
19
  */
@@ -20,7 +25,7 @@ export default createNoLocalComponentRule({
20
25
  pattern: /Date(Time)?(Input|Picker|Field)$/,
21
26
  messageId: "noLocalDateInput",
22
27
  description:
23
- "Disallow a locally-declared date input - import DateInput from @ethisyscore/plugin-ui",
28
+ "Disallow a locally-declared date input - import DateInput from @ethisyscore/plugin-ui/components/date",
24
29
  message:
25
- "'{{name}}' looks like a local date input. Date fields must come from one implementation or the popper and bounds fixes drift: import { DateInput } from '@ethisyscore/plugin-ui/components/ui' instead. If this is genuinely something else, rename it so it does not end in Input/Picker/Field.",
30
+ "'{{name}}' looks like a local date input. Date fields must come from one implementation or the popper and bounds fixes drift: import { DateInput } from '@ethisyscore/plugin-ui/components/date' instead. If this is genuinely something else, rename it so it does not end in Input/Picker/Field.",
26
31
  });
@@ -7,7 +7,7 @@
7
7
  * can, and this stops a fifth.
8
8
  *
9
9
  * Fires on a DECLARATION, never a use, so importing `EmptyState` from
10
- * `@ethisyscore/plugin-ui/shared` and rendering it is what the rule wants.
10
+ * `@ethisyscore/plugin-ui/components/shared` and rendering it is what the rule wants.
11
11
  *
12
12
  * @type {import("eslint").Rule.RuleModule}
13
13
  */
@@ -19,7 +19,7 @@ export default createNoLocalComponentRule({
19
19
  pattern: /(EmptyState|NoResults|NoData|BlankSlate)$/,
20
20
  messageId: "noLocalEmptyState",
21
21
  description:
22
- "Disallow a locally-declared empty state - import EmptyState from @ethisyscore/plugin-ui/shared",
22
+ "Disallow a locally-declared empty state - import EmptyState from @ethisyscore/plugin-ui/components/shared",
23
23
  message:
24
- "'{{name}}' looks like a local empty state. Import { EmptyState } from '@ethisyscore/plugin-ui/shared' instead, so every app's empty state matches. It takes icon, title, description and fill; pass description=\"\" for a heading only.",
24
+ "'{{name}}' looks like a local empty state. Import { EmptyState } from '@ethisyscore/plugin-ui/components/shared' instead, so every app's empty state matches. It takes icon, title, description and fill; pass description=\"\" for a heading only.",
25
25
  });