@almadar/eslint-plugin 2.4.1 → 2.4.2
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/index.js +22 -0
- package/package.json +4 -1
- package/rules/behavior-no-organisms.js +166 -0
package/index.js
CHANGED
|
@@ -28,6 +28,9 @@ const organismRequireEntityRow = require("./rules/organism-require-entity-row");
|
|
|
28
28
|
// Atom/molecule rules
|
|
29
29
|
const atomMoleculeNoEntityProp = require("./rules/atom-molecule-no-entity-prop");
|
|
30
30
|
|
|
31
|
+
// Behavior rules
|
|
32
|
+
const behaviorNoOrganisms = require("./rules/behavior-no-organisms");
|
|
33
|
+
|
|
31
34
|
// Service rules - Phase 1-3 (Implemented)
|
|
32
35
|
const serviceLazySingleton = require("./rules/service-lazy-singleton");
|
|
33
36
|
const serviceSingletonResettable = require("./rules/service-singleton-resettable");
|
|
@@ -82,6 +85,9 @@ const plugin = {
|
|
|
82
85
|
// Atom/molecule rules
|
|
83
86
|
"atom-molecule-no-entity-prop": atomMoleculeNoEntityProp,
|
|
84
87
|
|
|
88
|
+
// Behavior rules
|
|
89
|
+
"behavior-no-organisms": behaviorNoOrganisms,
|
|
90
|
+
|
|
85
91
|
// Service rules - Phase 1-3
|
|
86
92
|
"service-lazy-singleton": serviceLazySingleton,
|
|
87
93
|
"service-singleton-resettable": serviceSingletonResettable,
|
|
@@ -134,6 +140,9 @@ plugin.configs.recommended = {
|
|
|
134
140
|
"almadar/template-extends-base": "warn",
|
|
135
141
|
"almadar/require-translate": "warn",
|
|
136
142
|
|
|
143
|
+
// Behavior rules — restrict to atoms/molecules only
|
|
144
|
+
"almadar/behavior-no-organisms": "error",
|
|
145
|
+
|
|
137
146
|
// Organism rules — enforce dumb organism pattern
|
|
138
147
|
"almadar/organism-no-data-state": "error",
|
|
139
148
|
"almadar/organism-no-auto-fetch": "error",
|
|
@@ -230,4 +239,17 @@ plugin.configs["services-strict"] = {
|
|
|
230
239
|
},
|
|
231
240
|
};
|
|
232
241
|
|
|
242
|
+
// Behaviors config — lint .orb behavior schemas for pattern tier compliance
|
|
243
|
+
// Requires: jsonc-eslint-parser (pnpm add -D jsonc-eslint-parser)
|
|
244
|
+
// Usage in eslint.config.js: { files: ["**/*.orb"], ...almadar.configs.behaviors }
|
|
245
|
+
plugin.configs.behaviors = {
|
|
246
|
+
plugins: { almadar: plugin },
|
|
247
|
+
languageOptions: {
|
|
248
|
+
parser: require("jsonc-eslint-parser"),
|
|
249
|
+
},
|
|
250
|
+
rules: {
|
|
251
|
+
"almadar/behavior-no-organisms": "error",
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
|
|
233
255
|
module.exports = plugin;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@almadar/eslint-plugin",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Custom ESLint rules for Almadar component architecture",
|
|
6
6
|
"main": "index.js",
|
|
@@ -22,6 +22,9 @@
|
|
|
22
22
|
"publishConfig": {
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"jsonc-eslint-parser": "3.1.0"
|
|
27
|
+
},
|
|
25
28
|
"scripts": {
|
|
26
29
|
"lint": "eslint index.js rules/ utils/"
|
|
27
30
|
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* behavior-no-organisms
|
|
5
|
+
*
|
|
6
|
+
* Behaviors (.orb files in behaviors/) must only use atom and molecule patterns.
|
|
7
|
+
* Organisms and templates are forbidden, with exceptions for:
|
|
8
|
+
* - Game-related patterns (category "game" in component-mapping.json)
|
|
9
|
+
* - form / form-section (explicitly allowed by convention)
|
|
10
|
+
*
|
|
11
|
+
* Requires: jsonc-eslint-parser configured for *.orb files.
|
|
12
|
+
*
|
|
13
|
+
* To regenerate BLOCKED_PATTERNS from component-mapping.json:
|
|
14
|
+
* node -e "
|
|
15
|
+
* const m = require('../almadar-patterns/src/component-mapping.json').mappings;
|
|
16
|
+
* const blocked = Object.entries(m)
|
|
17
|
+
* .filter(([k, v]) =>
|
|
18
|
+
* (v.importPath.includes('/organisms/') || v.importPath.includes('/templates/'))
|
|
19
|
+
* && v.category !== 'game'
|
|
20
|
+
* && !['form', 'form-section'].includes(k))
|
|
21
|
+
* .map(([k]) => k);
|
|
22
|
+
* console.log(JSON.stringify(blocked, null, 2));
|
|
23
|
+
* "
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// Organism patterns: importPath contains /organisms/, category !== "game"
|
|
27
|
+
// Template patterns: importPath contains /templates/, not game-related
|
|
28
|
+
// Excludes: form, form-section (allowed by convention)
|
|
29
|
+
const BLOCKED_PATTERNS = new Set([
|
|
30
|
+
// ── Organisms (non-game, non-form) ────────────────────────────────
|
|
31
|
+
"page-header",
|
|
32
|
+
"entity-list",
|
|
33
|
+
"entity-table",
|
|
34
|
+
"entity-cards",
|
|
35
|
+
"stats",
|
|
36
|
+
"detail-panel",
|
|
37
|
+
"wizard-container",
|
|
38
|
+
"confirm-dialog",
|
|
39
|
+
"master-detail",
|
|
40
|
+
"dashboard-grid",
|
|
41
|
+
"runtime-debugger",
|
|
42
|
+
"button-pattern",
|
|
43
|
+
"custom-pattern",
|
|
44
|
+
"drawer-slot",
|
|
45
|
+
"header",
|
|
46
|
+
"layout-pattern",
|
|
47
|
+
"modal-slot",
|
|
48
|
+
"navigation",
|
|
49
|
+
"orbital-visualization",
|
|
50
|
+
"section",
|
|
51
|
+
"sidebar",
|
|
52
|
+
"split",
|
|
53
|
+
"table",
|
|
54
|
+
"toast-slot",
|
|
55
|
+
"u-i-slot-renderer",
|
|
56
|
+
"split-pane",
|
|
57
|
+
"tabbed-container",
|
|
58
|
+
"collapsible-section",
|
|
59
|
+
"camera3-d",
|
|
60
|
+
"lighting3-d",
|
|
61
|
+
"scene3-d",
|
|
62
|
+
"canvas3-d-error-boundary",
|
|
63
|
+
"canvas3-d-loading-state",
|
|
64
|
+
"model-loader",
|
|
65
|
+
"physics-object3-d",
|
|
66
|
+
"feature-renderer",
|
|
67
|
+
"feature-renderer3-d",
|
|
68
|
+
"unit-renderer",
|
|
69
|
+
"trait-slot",
|
|
70
|
+
"trait-state-viewer",
|
|
71
|
+
"content-renderer",
|
|
72
|
+
"jazari-state-machine",
|
|
73
|
+
"state-machine-view",
|
|
74
|
+
"book-chapter-view",
|
|
75
|
+
"book-cover-page",
|
|
76
|
+
"book-nav-bar",
|
|
77
|
+
"book-table-of-contents",
|
|
78
|
+
"book-viewer",
|
|
79
|
+
"simulation-controls",
|
|
80
|
+
"simulation-graph",
|
|
81
|
+
"chart",
|
|
82
|
+
"timeline",
|
|
83
|
+
"media-gallery",
|
|
84
|
+
"signature-pad",
|
|
85
|
+
"document-viewer",
|
|
86
|
+
"graph-canvas",
|
|
87
|
+
"code-viewer",
|
|
88
|
+
"event-log",
|
|
89
|
+
"object-rule-panel",
|
|
90
|
+
"rule-editor",
|
|
91
|
+
"action-palette",
|
|
92
|
+
"sequence-bar",
|
|
93
|
+
"code-view",
|
|
94
|
+
"state-architect-board",
|
|
95
|
+
"state-node",
|
|
96
|
+
"transition-arrow",
|
|
97
|
+
"variable-panel",
|
|
98
|
+
|
|
99
|
+
// ── Templates (non-game) ──────────────────────────────────────────
|
|
100
|
+
"auth-layout",
|
|
101
|
+
"counter-template",
|
|
102
|
+
"dashboard-layout",
|
|
103
|
+
"generic-app-template",
|
|
104
|
+
]);
|
|
105
|
+
|
|
106
|
+
// Alternative names that map to the same organism
|
|
107
|
+
const ALIASES = {
|
|
108
|
+
form: null, // explicitly allowed
|
|
109
|
+
"form-section": null, // explicitly allowed
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
module.exports = {
|
|
113
|
+
meta: {
|
|
114
|
+
type: "problem",
|
|
115
|
+
docs: {
|
|
116
|
+
description:
|
|
117
|
+
"Behavior schemas must only use atom and molecule patterns. " +
|
|
118
|
+
"Organisms and templates (except games and form-section) are forbidden.",
|
|
119
|
+
category: "Almadar Architecture",
|
|
120
|
+
},
|
|
121
|
+
messages: {
|
|
122
|
+
blockedPattern:
|
|
123
|
+
'Pattern "{{pattern}}" is an organism/template. ' +
|
|
124
|
+
"Behaviors must only use atoms and molecules. " +
|
|
125
|
+
"Replace with a molecule-level equivalent or restructure the schema.",
|
|
126
|
+
},
|
|
127
|
+
schema: [],
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
create(context) {
|
|
131
|
+
const filename = context.filename || context.getFilename();
|
|
132
|
+
|
|
133
|
+
// Only apply to .orb files inside behaviors/ directories
|
|
134
|
+
if (!filename.endsWith(".orb") || !filename.includes("behaviors")) {
|
|
135
|
+
return {};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
// jsonc-eslint-parser provides JSONProperty AST nodes
|
|
140
|
+
JSONProperty(node) {
|
|
141
|
+
// Look for "type": "<pattern-name>" properties
|
|
142
|
+
if (
|
|
143
|
+
node.key &&
|
|
144
|
+
node.key.type === "JSONLiteral" &&
|
|
145
|
+
node.key.value === "type" &&
|
|
146
|
+
node.value &&
|
|
147
|
+
node.value.type === "JSONLiteral" &&
|
|
148
|
+
typeof node.value.value === "string"
|
|
149
|
+
) {
|
|
150
|
+
const pattern = node.value.value;
|
|
151
|
+
|
|
152
|
+
// Skip explicitly allowed aliases
|
|
153
|
+
if (pattern in ALIASES) return;
|
|
154
|
+
|
|
155
|
+
if (BLOCKED_PATTERNS.has(pattern)) {
|
|
156
|
+
context.report({
|
|
157
|
+
node: node.value,
|
|
158
|
+
messageId: "blockedPattern",
|
|
159
|
+
data: { pattern },
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
},
|
|
166
|
+
};
|