@geonosis/oxlint-plugin-biological-architecture 0.2.0 → 0.3.0
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/corpus/.oxlintrc.json +1 -0
- package/corpus/admin/order-widget.tsx +13 -0
- package/corpus/atoms-discovery/generic-only.tsx +9 -0
- package/corpus/atoms-discovery/html-attributes-select.tsx +15 -0
- package/corpus/atoms-discovery/icon-check.tsx +7 -0
- package/corpus/atoms-discovery/label.tsx +3 -0
- package/corpus/atoms-discovery/props-button.tsx +27 -0
- package/corpus/atoms-discovery/props-without-ref-textarea.tsx +11 -0
- package/corpus/atoms-discovery/react-namespaced-input.tsx +13 -0
- package/corpus/atoms-discovery/slot-anchor.stories.tsx +9 -0
- package/corpus/atoms-discovery/slot-anchor.tsx +15 -0
- package/corpus/emits/bad-no-options.ts +7 -0
- package/corpus/emits/bad-priority-only.ts +11 -0
- package/corpus/emits/bad-single-attempt.ts +8 -0
- package/corpus/emits/good-retry-budget.ts +9 -0
- package/corpus/emits/good-shared-constant.ts +10 -0
- package/corpus/emits/retry.ts +3 -0
- package/corpus/manifest.json +1 -0
- package/corpus/molecules/raw-button.tsx +10 -0
- package/dist/{chunk-HXBZSOIU.js → chunk-55NK7NJN.js} +1 -1
- package/dist/discover-atoms.d.ts +12 -0
- package/dist/discover-atoms.js +197 -5
- package/dist/index.js +118 -14
- package/dist/presets.js +1 -1
- package/package.json +2 -2
package/corpus/.oxlintrc.json
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// A file in no tier folder at all — a Medusa admin widget, a Next route, a one-off surface. The
|
|
2
|
+
// atom exists there too, so no-raw-html-atoms still has something to say.
|
|
3
|
+
import { Card } from '@kit/ui/atoms/card'
|
|
4
|
+
|
|
5
|
+
export function OrderWidget({ onRefresh }: { onRefresh: () => void }) {
|
|
6
|
+
return (
|
|
7
|
+
<Card>
|
|
8
|
+
<button onClick={onRefresh} type="button">
|
|
9
|
+
Refresh
|
|
10
|
+
</button>
|
|
11
|
+
</Card>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { useMemo } from 'react'
|
|
2
|
+
|
|
3
|
+
const weights: Record<string, number> = { bold: 700, regular: 400 }
|
|
4
|
+
|
|
5
|
+
export function Spacer({ weight = 'regular' }: { weight?: string }) {
|
|
6
|
+
const value = useMemo<number>(() => weights[weight] ?? 400, [weight])
|
|
7
|
+
|
|
8
|
+
return <div style={{ fontWeight: value }} />
|
|
9
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { HTMLAttributes } from 'react'
|
|
2
|
+
|
|
3
|
+
interface NativeSelectProps extends Omit<HTMLAttributes<HTMLSelectElement>, 'onChange'> {
|
|
4
|
+
onPick?: (value: string) => void
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function NativeSelect({ onPick, children, ...props }: NativeSelectProps) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="relative">
|
|
10
|
+
<select onChange={(event) => onPick?.(event.target.value)} {...props}>
|
|
11
|
+
{children}
|
|
12
|
+
</select>
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { cva, type VariantProps } from 'class-variance-authority'
|
|
2
|
+
import { Slot } from 'radix-ui'
|
|
3
|
+
import type { ComponentProps, Ref } from 'react'
|
|
4
|
+
|
|
5
|
+
const buttonVariants = cva('inline-flex', {
|
|
6
|
+
variants: { size: { medium: 'px-7', small: 'px-5' } },
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
type ButtonSize = NonNullable<VariantProps<typeof buttonVariants>['size']>
|
|
10
|
+
|
|
11
|
+
const labels: Record<string, string> = { medium: 'Medium', small: 'Small' }
|
|
12
|
+
|
|
13
|
+
interface ButtonProps extends ComponentProps<'button'>, VariantProps<typeof buttonVariants> {
|
|
14
|
+
asChild?: boolean
|
|
15
|
+
ref?: Ref<HTMLButtonElement>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function Button({ asChild = false, size, children, ...props }: ButtonProps) {
|
|
19
|
+
const Comp = asChild ? Slot.Root : 'button'
|
|
20
|
+
const resolved: ButtonSize = size ?? 'medium'
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<Comp aria-label={labels[resolved]} {...props}>
|
|
24
|
+
{children}
|
|
25
|
+
</Comp>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef } from 'react'
|
|
2
|
+
|
|
3
|
+
type TextAreaProps = ComponentPropsWithoutRef<'textarea'> & { invalid?: boolean }
|
|
4
|
+
|
|
5
|
+
export function TextArea({ invalid = false, ...props }: TextAreaProps) {
|
|
6
|
+
return (
|
|
7
|
+
<div className="relative">
|
|
8
|
+
<textarea data-invalid={invalid} {...props} />
|
|
9
|
+
</div>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
|
|
3
|
+
interface TextInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
|
4
|
+
invalid?: boolean
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function TextInput({ invalid = false, ...props }: TextInputProps) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="relative">
|
|
10
|
+
<input data-invalid={invalid} {...props} />
|
|
11
|
+
</div>
|
|
12
|
+
)
|
|
13
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Slot } from 'radix-ui'
|
|
2
|
+
|
|
3
|
+
import { cn } from '#lib/utils'
|
|
4
|
+
|
|
5
|
+
interface LinkAtomProps {
|
|
6
|
+
asChild?: boolean
|
|
7
|
+
children?: unknown
|
|
8
|
+
className?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function LinkAtom({ asChild = false, children, className }: LinkAtomProps) {
|
|
12
|
+
const Comp = asChild ? Slot.Root : 'a'
|
|
13
|
+
|
|
14
|
+
return <Comp className={cn('underline', className)}>{children}</Comp>
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { EventPriority } from '@medusajs/framework/utils'
|
|
2
|
+
import { emitEventStep } from '@medusajs/medusa/core-flows'
|
|
3
|
+
|
|
4
|
+
// An options bag that names a priority and no attempts: configured to the eye, droppable to the
|
|
5
|
+
// worker, which reads only `attempts > 1` as "a retry was asked for".
|
|
6
|
+
export const emitSampleRequested = (data: { order_id: string }) =>
|
|
7
|
+
emitEventStep({
|
|
8
|
+
data,
|
|
9
|
+
eventName: 'sample.requested',
|
|
10
|
+
options: { priority: EventPriority.CRITICAL },
|
|
11
|
+
}).config({ name: 'emit-sample-requested' })
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EventPriority } from '@medusajs/framework/utils'
|
|
2
|
+
import { emitEventStep } from '@medusajs/medusa/core-flows'
|
|
3
|
+
|
|
4
|
+
export const emitOrderPlaced = (data: { id: string }) =>
|
|
5
|
+
emitEventStep({
|
|
6
|
+
data,
|
|
7
|
+
eventName: 'order.placed',
|
|
8
|
+
options: { attempts: 3, priority: EventPriority.CRITICAL },
|
|
9
|
+
}).config({ name: 'emit-order-placed' })
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { emitEventStep } from '@medusajs/medusa/core-flows'
|
|
2
|
+
|
|
3
|
+
import { RETRY_BACKGROUND } from './retry'
|
|
4
|
+
|
|
5
|
+
export const emitProofReady = (data: { proof_id: string }) =>
|
|
6
|
+
emitEventStep({
|
|
7
|
+
data,
|
|
8
|
+
eventName: 'proof.ready',
|
|
9
|
+
options: { attempts: RETRY_BACKGROUND.attempts },
|
|
10
|
+
})
|
package/corpus/manifest.json
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"biological-architecture/document-sagas-are-generic",
|
|
14
14
|
"biological-architecture/documents-share-one-table",
|
|
15
15
|
"biological-architecture/effect-hook-naming",
|
|
16
|
+
"biological-architecture/emit-declares-attempts",
|
|
16
17
|
"biological-architecture/layer-walls",
|
|
17
18
|
"biological-architecture/max-comment-density",
|
|
18
19
|
"biological-architecture/molecule-atoms-only",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// A molecule hand-rolling the element an atom already wraps: level 2, where the rule bites.
|
|
2
|
+
import { Icon } from '../atoms/icon'
|
|
3
|
+
|
|
4
|
+
export function IconAction({ onPress }: { onPress: () => void }) {
|
|
5
|
+
return (
|
|
6
|
+
<button onClick={onPress} type="button">
|
|
7
|
+
<Icon name="plus" />
|
|
8
|
+
</button>
|
|
9
|
+
)
|
|
10
|
+
}
|
|
@@ -52,7 +52,7 @@ var BACKEND_WORKFLOWS = [
|
|
|
52
52
|
];
|
|
53
53
|
var BACKEND_D1 = ["no-d1-transaction", "worker-handles-are-scoped"];
|
|
54
54
|
var BACKEND_SAGAFLOW_CF = ["step-opens-its-own-cell"];
|
|
55
|
-
var BACKEND_MEDUSA = ["store-route-scopes-tenant-data"];
|
|
55
|
+
var BACKEND_MEDUSA = ["emit-declares-attempts", "store-route-scopes-tenant-data"];
|
|
56
56
|
var TENANT_SCOPING = [
|
|
57
57
|
"queries-require-org-scope",
|
|
58
58
|
"store-route-scopes-tenant-data",
|
package/dist/discover-atoms.d.ts
CHANGED
|
@@ -34,6 +34,18 @@ declare function buildAtomRuleRestrictions(atomsDir: string, importPathPrefix?:
|
|
|
34
34
|
* Variants are kept as alternatives in the error message.
|
|
35
35
|
*/
|
|
36
36
|
declare function scanAtomMappings(atomsDir: string): AtomMapping[];
|
|
37
|
+
/**
|
|
38
|
+
* The native element an atom wraps, read from the one place every atom says so: its props type.
|
|
39
|
+
*
|
|
40
|
+
* `ComponentProps<'button'>`, `ComponentPropsWithoutRef<'textarea'>`,
|
|
41
|
+
* `HTMLAttributes<HTMLSelectElement>` and `React.ButtonHTMLAttributes<HTMLButtonElement>` all name
|
|
42
|
+
* the element outright, and an atom that types its props any other way is an atom nobody can
|
|
43
|
+
* compose. The Radix `asChild ? Slot.Root : 'button'` form comes next, and the JSX-tag scan is
|
|
44
|
+
* only the fallback — it was the whole detector once, and on a real tree it returned `string`,
|
|
45
|
+
* `typeof` and `path` while missing every shadcn button in the repo.
|
|
46
|
+
*
|
|
47
|
+
* The name is kept because consumers import it.
|
|
48
|
+
*/
|
|
37
49
|
declare function detectFirstNativeJsxElement(source: string): null | string;
|
|
38
50
|
|
|
39
51
|
export { buildAtomRestrictions, buildAtomRuleRestrictions, detectFirstNativeJsxElement, scanAtomMappings };
|
package/dist/discover-atoms.js
CHANGED
|
@@ -2,6 +2,164 @@
|
|
|
2
2
|
import { readdirSync, readFileSync } from "fs";
|
|
3
3
|
import { basename, join } from "path";
|
|
4
4
|
var GENERIC_ELEMENTS = /* @__PURE__ */ new Set(["div", "span"]);
|
|
5
|
+
var HTML_ELEMENTS = /* @__PURE__ */ new Set([
|
|
6
|
+
"a",
|
|
7
|
+
"abbr",
|
|
8
|
+
"address",
|
|
9
|
+
"area",
|
|
10
|
+
"article",
|
|
11
|
+
"aside",
|
|
12
|
+
"audio",
|
|
13
|
+
"b",
|
|
14
|
+
"bdi",
|
|
15
|
+
"bdo",
|
|
16
|
+
"blockquote",
|
|
17
|
+
"br",
|
|
18
|
+
"button",
|
|
19
|
+
"canvas",
|
|
20
|
+
"caption",
|
|
21
|
+
"cite",
|
|
22
|
+
"code",
|
|
23
|
+
"col",
|
|
24
|
+
"colgroup",
|
|
25
|
+
"data",
|
|
26
|
+
"datalist",
|
|
27
|
+
"dd",
|
|
28
|
+
"del",
|
|
29
|
+
"details",
|
|
30
|
+
"dfn",
|
|
31
|
+
"dialog",
|
|
32
|
+
"div",
|
|
33
|
+
"dl",
|
|
34
|
+
"dt",
|
|
35
|
+
"em",
|
|
36
|
+
"embed",
|
|
37
|
+
"fieldset",
|
|
38
|
+
"figcaption",
|
|
39
|
+
"figure",
|
|
40
|
+
"footer",
|
|
41
|
+
"form",
|
|
42
|
+
"h1",
|
|
43
|
+
"h2",
|
|
44
|
+
"h3",
|
|
45
|
+
"h4",
|
|
46
|
+
"h5",
|
|
47
|
+
"h6",
|
|
48
|
+
"header",
|
|
49
|
+
"hgroup",
|
|
50
|
+
"hr",
|
|
51
|
+
"i",
|
|
52
|
+
"iframe",
|
|
53
|
+
"img",
|
|
54
|
+
"input",
|
|
55
|
+
"ins",
|
|
56
|
+
"kbd",
|
|
57
|
+
"label",
|
|
58
|
+
"legend",
|
|
59
|
+
"li",
|
|
60
|
+
"main",
|
|
61
|
+
"map",
|
|
62
|
+
"mark",
|
|
63
|
+
"menu",
|
|
64
|
+
"meter",
|
|
65
|
+
"nav",
|
|
66
|
+
"noscript",
|
|
67
|
+
"object",
|
|
68
|
+
"ol",
|
|
69
|
+
"optgroup",
|
|
70
|
+
"option",
|
|
71
|
+
"output",
|
|
72
|
+
"p",
|
|
73
|
+
"picture",
|
|
74
|
+
"pre",
|
|
75
|
+
"progress",
|
|
76
|
+
"q",
|
|
77
|
+
"rp",
|
|
78
|
+
"rt",
|
|
79
|
+
"ruby",
|
|
80
|
+
"s",
|
|
81
|
+
"samp",
|
|
82
|
+
"search",
|
|
83
|
+
"section",
|
|
84
|
+
"select",
|
|
85
|
+
"small",
|
|
86
|
+
"source",
|
|
87
|
+
"span",
|
|
88
|
+
"strong",
|
|
89
|
+
"sub",
|
|
90
|
+
"summary",
|
|
91
|
+
"sup",
|
|
92
|
+
"table",
|
|
93
|
+
"tbody",
|
|
94
|
+
"td",
|
|
95
|
+
"template",
|
|
96
|
+
"textarea",
|
|
97
|
+
"tfoot",
|
|
98
|
+
"th",
|
|
99
|
+
"thead",
|
|
100
|
+
"time",
|
|
101
|
+
"tr",
|
|
102
|
+
"track",
|
|
103
|
+
"u",
|
|
104
|
+
"ul",
|
|
105
|
+
"var",
|
|
106
|
+
"video",
|
|
107
|
+
"wbr"
|
|
108
|
+
]);
|
|
109
|
+
var TAG_OF_DOM_INTERFACE = {
|
|
110
|
+
HTMLAnchorElement: "a",
|
|
111
|
+
HTMLAreaElement: "area",
|
|
112
|
+
HTMLAudioElement: "audio",
|
|
113
|
+
HTMLBRElement: "br",
|
|
114
|
+
HTMLButtonElement: "button",
|
|
115
|
+
HTMLCanvasElement: "canvas",
|
|
116
|
+
HTMLDListElement: "dl",
|
|
117
|
+
HTMLDataElement: "data",
|
|
118
|
+
HTMLDataListElement: "datalist",
|
|
119
|
+
HTMLDetailsElement: "details",
|
|
120
|
+
HTMLDialogElement: "dialog",
|
|
121
|
+
HTMLDivElement: "div",
|
|
122
|
+
HTMLEmbedElement: "embed",
|
|
123
|
+
HTMLFieldSetElement: "fieldset",
|
|
124
|
+
HTMLFormElement: "form",
|
|
125
|
+
HTMLHRElement: "hr",
|
|
126
|
+
HTMLIFrameElement: "iframe",
|
|
127
|
+
HTMLImageElement: "img",
|
|
128
|
+
HTMLInputElement: "input",
|
|
129
|
+
HTMLLIElement: "li",
|
|
130
|
+
HTMLLabelElement: "label",
|
|
131
|
+
HTMLLegendElement: "legend",
|
|
132
|
+
HTMLMapElement: "map",
|
|
133
|
+
HTMLMenuElement: "menu",
|
|
134
|
+
HTMLMeterElement: "meter",
|
|
135
|
+
HTMLOListElement: "ol",
|
|
136
|
+
HTMLObjectElement: "object",
|
|
137
|
+
HTMLOptGroupElement: "optgroup",
|
|
138
|
+
HTMLOptionElement: "option",
|
|
139
|
+
HTMLOutputElement: "output",
|
|
140
|
+
HTMLParagraphElement: "p",
|
|
141
|
+
HTMLPictureElement: "picture",
|
|
142
|
+
HTMLPreElement: "pre",
|
|
143
|
+
HTMLProgressElement: "progress",
|
|
144
|
+
HTMLSelectElement: "select",
|
|
145
|
+
HTMLSourceElement: "source",
|
|
146
|
+
HTMLSpanElement: "span",
|
|
147
|
+
HTMLTableCaptionElement: "caption",
|
|
148
|
+
HTMLTableColElement: "col",
|
|
149
|
+
HTMLTableElement: "table",
|
|
150
|
+
HTMLTableRowElement: "tr",
|
|
151
|
+
HTMLTextAreaElement: "textarea",
|
|
152
|
+
HTMLTimeElement: "time",
|
|
153
|
+
HTMLTrackElement: "track",
|
|
154
|
+
HTMLUListElement: "ul",
|
|
155
|
+
HTMLVideoElement: "video"
|
|
156
|
+
};
|
|
157
|
+
var TAG_OF_ATTRIBUTES_PREFIX = { anchor: "a" };
|
|
158
|
+
var NOT_AN_ATOM = /\.(?:stories|test|spec)\.tsx$/;
|
|
159
|
+
var COMPONENT_PROPS = /(?:React\.)?ComponentProps(?:WithoutRef|WithRef)?\s*<\s*['"]([a-z][a-z0-9]*)['"]\s*>/g;
|
|
160
|
+
var HTML_ATTRIBUTES = /(?:React\.)?([A-Z][A-Za-z]*?)?HTMLAttributes\s*<\s*(HTML[A-Za-z]*Element)\s*>/g;
|
|
161
|
+
var SLOT_FALLBACK = /Slot(?:\.Root)?\s*:\s*['"]([a-z][a-z0-9]*)['"]/;
|
|
162
|
+
var JSX_TAG = /(?<![A-Za-z0-9_$])<([a-z][a-z0-9]*)(?=[\s/>])/g;
|
|
5
163
|
function buildAtomRestrictions(atomsDir, importPathPrefix = "@kit/ui/atoms") {
|
|
6
164
|
const mappings = scanAtomMappings(atomsDir);
|
|
7
165
|
return mappings.map(({ atomName, nativeElement }) => ({
|
|
@@ -23,6 +181,7 @@ function scanAtomMappings(atomsDir) {
|
|
|
23
181
|
for (const entry of entries) {
|
|
24
182
|
if (!entry.isFile()) continue;
|
|
25
183
|
if (!entry.name.endsWith(".tsx")) continue;
|
|
184
|
+
if (NOT_AN_ATOM.test(entry.name)) continue;
|
|
26
185
|
const atomFile = entry.name;
|
|
27
186
|
const atomName = basename(atomFile, ".tsx");
|
|
28
187
|
const content = readFileSync(join(atomsDir, atomFile), "utf8");
|
|
@@ -42,11 +201,44 @@ function scanAtomMappings(atomsDir) {
|
|
|
42
201
|
return mappings;
|
|
43
202
|
}
|
|
44
203
|
function detectFirstNativeJsxElement(source) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
204
|
+
const withoutComments = stripComments(source);
|
|
205
|
+
const declared = elementFromPropsType(withoutComments);
|
|
206
|
+
if (declared) return declared;
|
|
207
|
+
const slot = SLOT_FALLBACK.exec(withoutComments)?.[1];
|
|
208
|
+
if (slot && HTML_ELEMENTS.has(slot)) return slot;
|
|
209
|
+
return elementFromJsx(withoutComments);
|
|
210
|
+
}
|
|
211
|
+
function elementFromPropsType(source) {
|
|
212
|
+
const found = [];
|
|
213
|
+
for (const match of source.matchAll(COMPONENT_PROPS)) {
|
|
214
|
+
const element = match[1];
|
|
215
|
+
if (element && HTML_ELEMENTS.has(element)) found.push({ at: match.index, element });
|
|
216
|
+
}
|
|
217
|
+
for (const match of source.matchAll(HTML_ATTRIBUTES)) {
|
|
218
|
+
const element = elementOfAttributes(match[1], match[2]);
|
|
219
|
+
if (element) found.push({ at: match.index, element });
|
|
220
|
+
}
|
|
221
|
+
return found.toSorted((a, b) => a.at - b.at)[0]?.element ?? null;
|
|
222
|
+
}
|
|
223
|
+
function elementOfAttributes(prefix, domInterface) {
|
|
224
|
+
if (prefix !== void 0) {
|
|
225
|
+
const lower = prefix.toLowerCase();
|
|
226
|
+
const named = TAG_OF_ATTRIBUTES_PREFIX[lower] ?? lower;
|
|
227
|
+
if (HTML_ELEMENTS.has(named)) return named;
|
|
228
|
+
}
|
|
229
|
+
const mapped = domInterface === void 0 ? void 0 : TAG_OF_DOM_INTERFACE[domInterface];
|
|
230
|
+
return mapped !== void 0 && HTML_ELEMENTS.has(mapped) ? mapped : null;
|
|
231
|
+
}
|
|
232
|
+
function elementFromJsx(withoutComments) {
|
|
233
|
+
const cleaned = withoutComments.replaceAll(/(["'`])(?:\\.|(?!\1)[^\\])*\1/g, '""');
|
|
234
|
+
for (const match of cleaned.matchAll(JSX_TAG)) {
|
|
235
|
+
const tag = match[1];
|
|
236
|
+
if (tag && HTML_ELEMENTS.has(tag)) return tag;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
function stripComments(source) {
|
|
241
|
+
return source.replaceAll(/\/\*[\s\S]*?\*\//g, "").replaceAll(/(?<!:)\/\/.*$/gm, "");
|
|
50
242
|
}
|
|
51
243
|
function toPascalCase(kebab) {
|
|
52
244
|
return kebab.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRESETS,
|
|
3
3
|
rulesOfPreset
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-55NK7NJN.js";
|
|
5
5
|
|
|
6
6
|
// src/index.ts
|
|
7
7
|
import { createRequire } from "module";
|
|
@@ -743,6 +743,80 @@ file and the export together \u2014 half the pair still fires.`,
|
|
|
743
743
|
};
|
|
744
744
|
var effect_hook_naming_default = effectHookNaming;
|
|
745
745
|
|
|
746
|
+
// src/rules/emit-declares-attempts.ts
|
|
747
|
+
var RULE = "emit-declares-attempts";
|
|
748
|
+
var DEFAULT_EMITTERS = ["emitEventStep"];
|
|
749
|
+
var OPTIONS = "options";
|
|
750
|
+
var ATTEMPTS = "attempts";
|
|
751
|
+
var EVENT_NAME = "eventName";
|
|
752
|
+
var NO_RETRY = 1;
|
|
753
|
+
var objectOf = (node) => node?.type === "ObjectExpression" ? node : null;
|
|
754
|
+
var propertiesOf = (object) => {
|
|
755
|
+
const properties = object.properties ?? [];
|
|
756
|
+
return properties.every((one) => one.type === "Property") ? properties : null;
|
|
757
|
+
};
|
|
758
|
+
var valueOf = (properties, name) => properties.find((one) => one.key?.type === "Identifier" && one.key.name === name)?.value;
|
|
759
|
+
var emitDeclaresAttempts = {
|
|
760
|
+
create(context) {
|
|
761
|
+
const emitters = new Set(
|
|
762
|
+
requireOption(RULE, context.options?.[0]?.emitters ?? DEFAULT_EMITTERS, "emitters")
|
|
763
|
+
);
|
|
764
|
+
return {
|
|
765
|
+
CallExpression(node) {
|
|
766
|
+
if (node.callee?.type !== "Identifier") return;
|
|
767
|
+
const emitter = node.callee.name ?? "";
|
|
768
|
+
if (!emitters.has(emitter)) return;
|
|
769
|
+
const payload = objectOf(node.arguments?.[0]);
|
|
770
|
+
if (payload === null) return;
|
|
771
|
+
const properties = propertiesOf(payload);
|
|
772
|
+
if (properties === null) return;
|
|
773
|
+
const named = valueOf(properties, EVENT_NAME);
|
|
774
|
+
const site = named?.type === "Literal" && typeof named.value === "string" ? named.value : emitter;
|
|
775
|
+
const options = valueOf(properties, OPTIONS);
|
|
776
|
+
if (options !== void 0 && objectOf(options) === null) return;
|
|
777
|
+
const bag = options === void 0 ? [] : propertiesOf(objectOf(options));
|
|
778
|
+
if (bag === null) return;
|
|
779
|
+
const attempts = valueOf(bag, ATTEMPTS);
|
|
780
|
+
if (attempts === void 0) {
|
|
781
|
+
context.report({ data: { emitter, site }, messageId: "noBudget", node });
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
if (attempts.type !== "Literal" || typeof attempts.value !== "number") return;
|
|
785
|
+
if (attempts.value > NO_RETRY) return;
|
|
786
|
+
context.report({
|
|
787
|
+
data: { attempts: String(attempts.value), emitter, site },
|
|
788
|
+
messageId: "tooFew",
|
|
789
|
+
node
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
},
|
|
794
|
+
fixShape: `Every emit carries a retry budget: \`options.attempts\`, taken from the shared retry constants rather
|
|
795
|
+
than written as a number at the call site. \`attempts\` defaults to 1 and the worker reads only
|
|
796
|
+
\`attempts > 1\` as "a retry was configured", so an emit with \`{ priority }\` and no \`attempts\` is
|
|
797
|
+
droppable while looking deliberate. Pass \`options: { attempts: RETRY_BACKGROUND.attempts, priority }\`.
|
|
798
|
+
A budget the rule cannot evaluate \u2014 a constant, a computed value \u2014 passes: it says nothing rather
|
|
799
|
+
than pushing the number back inline. Configure \`emitters\` if the repo wraps the emit under another name.`,
|
|
800
|
+
meta: {
|
|
801
|
+
docs: {
|
|
802
|
+
description: "An emit declares how many times it may be retried. Medusa's `emitEventStep` defaults `attempts` to 1 and the worker treats only `attempts > 1` as retry-configured, so an options bag without one is a droppable event that reads as a deliberate one. `emitters` is an option; a literal `attempts` is judged, anything else passes."
|
|
803
|
+
},
|
|
804
|
+
messages: {
|
|
805
|
+
noBudget: '`{{emitter}}` emits "{{site}}" with no `options.attempts`. `attempts` defaults to 1 and the worker treats only `attempts > 1` as a configured retry, so this event is dropped on its first failure. Give it a budget from the shared retry constants.',
|
|
806
|
+
tooFew: '`{{emitter}}` emits "{{site}}" with `attempts: {{attempts}}`, which is the default spelled out \u2014 one attempt is no retry. Take the budget from the shared retry constants instead.'
|
|
807
|
+
},
|
|
808
|
+
schema: [
|
|
809
|
+
{
|
|
810
|
+
additionalProperties: false,
|
|
811
|
+
properties: { emitters: { items: { type: "string" }, type: "array" } },
|
|
812
|
+
type: "object"
|
|
813
|
+
}
|
|
814
|
+
],
|
|
815
|
+
type: "problem"
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
var emit_declares_attempts_default = emitDeclaresAttempts;
|
|
819
|
+
|
|
746
820
|
// src/rules/layer-walls.ts
|
|
747
821
|
import * as path2 from "path";
|
|
748
822
|
var REQUIRE = "require";
|
|
@@ -2112,14 +2186,34 @@ below the seam, and a caller that names it has crossed it.`,
|
|
|
2112
2186
|
};
|
|
2113
2187
|
var no_orm_outside_db_default = noOrmOutsideDb;
|
|
2114
2188
|
|
|
2189
|
+
// src/rules/lib/tiers.ts
|
|
2190
|
+
var TIERS = ["atoms", "molecules", "compounds", "organelles", "cells", "tissues"];
|
|
2191
|
+
var PATTERNS = {
|
|
2192
|
+
atoms: /(?:^|\/)(?:features\/[^/]+\/)?atoms\//,
|
|
2193
|
+
cells: /(?:^|\/)(?:features\/[^/]+\/)?cells\//,
|
|
2194
|
+
compounds: /(?:^|\/)(?:features\/[^/]+\/)?compounds\//,
|
|
2195
|
+
molecules: /(?:^|\/)(?:features\/[^/]+\/)?molecules\//,
|
|
2196
|
+
organelles: /(?:^|\/)(?:features\/[^/]+\/)?organelles\//,
|
|
2197
|
+
tissues: /(?:^|\/)(?:features\/[^/]+\/)?tissues\//
|
|
2198
|
+
};
|
|
2199
|
+
var inTier = (filename, tier) => PATTERNS[tier].test(filename.replaceAll("\\", "/"));
|
|
2200
|
+
var requireTiers = (rule, names) => names.map((name) => {
|
|
2201
|
+
if (TIERS.includes(name)) return name;
|
|
2202
|
+
throw new Error(
|
|
2203
|
+
`biological-architecture/${rule} is scoped to "${name}", which is not a tier, so that scope can never match a file. The tiers are ${TIERS.join(", ")}.`
|
|
2204
|
+
);
|
|
2205
|
+
});
|
|
2206
|
+
|
|
2115
2207
|
// src/rules/no-raw-html-atoms.ts
|
|
2208
|
+
var RULE2 = "no-raw-html-atoms";
|
|
2209
|
+
var WHERE_RAW_HTML_IS_THE_POINT = "atoms";
|
|
2116
2210
|
var noRawHtmlAtoms = {
|
|
2117
2211
|
create(context) {
|
|
2118
|
-
const restrictions = requireOption(
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
);
|
|
2212
|
+
const restrictions = requireOption(RULE2, context.options[0], "element\u2192atom map");
|
|
2213
|
+
const scope = context.options[1]?.scope;
|
|
2214
|
+
const filename = context.filename;
|
|
2215
|
+
const inScope = scope === void 0 ? !inTier(filename, WHERE_RAW_HTML_IS_THE_POINT) : requireTiers(RULE2, scope).some((tier) => inTier(filename, tier));
|
|
2216
|
+
if (!inScope) return {};
|
|
2123
2217
|
const elementMap = /* @__PURE__ */ new Map();
|
|
2124
2218
|
for (const r of restrictions) {
|
|
2125
2219
|
elementMap.set(r.element, r);
|
|
@@ -2144,15 +2238,17 @@ var noRawHtmlAtoms = {
|
|
|
2144
2238
|
};
|
|
2145
2239
|
},
|
|
2146
2240
|
fixShape: `A native element that already has an atom wrapper (\`<button>\`, \`<input>\`, \`<td>\`, \`<header>\`, \u2026) is
|
|
2147
|
-
written as the atom
|
|
2148
|
-
|
|
2149
|
-
|
|
2241
|
+
written as the atom everywhere above the atom tier. Import the atom named in the rule's options and use it.
|
|
2242
|
+
Inside \`atoms/\` the raw element is the point of the file, so the rule says nothing there; every other
|
|
2243
|
+
file is in scope, tier folder or not, and \`scope\` narrows it to named tiers when a repo wants that.
|
|
2244
|
+
Configure the element\u2192atom map per repo; enabled without one the rule refuses the run, because a rule
|
|
2245
|
+
with no map to check reads exactly like a tree with no raw elements in it.`,
|
|
2150
2246
|
meta: {
|
|
2151
2247
|
docs: {
|
|
2152
|
-
description: "Forbid raw native HTML elements when an atom wrapper exists. Use the atom the options name instead."
|
|
2248
|
+
description: "Forbid raw native HTML elements when an atom wrapper exists. Use the atom the options name instead. Silent inside the atom tier, where the raw element is the point; `scope` narrows it to named tiers."
|
|
2153
2249
|
},
|
|
2154
2250
|
messages: {
|
|
2155
|
-
forbidden: 'Use <{{atom}}> from "{{importPath}}" instead of raw <{{element}}>. Native HTML elements that have an atom wrapper must not be used directly
|
|
2251
|
+
forbidden: 'Use <{{atom}}> from "{{importPath}}" instead of raw <{{element}}>. Native HTML elements that have an atom wrapper must not be used directly above the atom tier - use the atom to preserve consistent styling and behavior.'
|
|
2156
2252
|
},
|
|
2157
2253
|
schema: [
|
|
2158
2254
|
{
|
|
@@ -2166,6 +2262,13 @@ a tree with no raw elements in it.`,
|
|
|
2166
2262
|
type: "object"
|
|
2167
2263
|
},
|
|
2168
2264
|
type: "array"
|
|
2265
|
+
},
|
|
2266
|
+
{
|
|
2267
|
+
additionalProperties: false,
|
|
2268
|
+
properties: {
|
|
2269
|
+
scope: { items: { enum: [...TIERS], type: "string" }, type: "array" }
|
|
2270
|
+
},
|
|
2271
|
+
type: "object"
|
|
2169
2272
|
}
|
|
2170
2273
|
],
|
|
2171
2274
|
type: "problem"
|
|
@@ -3132,14 +3235,14 @@ can be misspelled and a second default nobody agreed to.`,
|
|
|
3132
3235
|
var ssot_no_process_env_default = ssotNoProcessEnv;
|
|
3133
3236
|
|
|
3134
3237
|
// src/rules/step-opens-its-own-cell.ts
|
|
3135
|
-
var
|
|
3238
|
+
var RULE3 = "step-opens-its-own-cell";
|
|
3136
3239
|
var STEP = "step";
|
|
3137
3240
|
var readable = (pattern) => pattern.replaceAll("\\", "").replace(/\$$/, "");
|
|
3138
3241
|
var stepOpensItsOwnCell = {
|
|
3139
3242
|
create(context) {
|
|
3140
3243
|
const { wrapper } = context.options?.[0] ?? {};
|
|
3141
|
-
const engine = requireOption(
|
|
3142
|
-
const within = requireOption(
|
|
3244
|
+
const engine = requireOption(RULE3, context.options?.[0]?.engine, "engine");
|
|
3245
|
+
const within = requireOption(RULE3, context.options?.[0]?.within, "within");
|
|
3143
3246
|
const filename = context.filename.replaceAll("\\", "/");
|
|
3144
3247
|
if (!new RegExp(within).test(filename)) return {};
|
|
3145
3248
|
if (wrapper !== void 0 && new RegExp(wrapper).test(filename)) return {};
|
|
@@ -4002,6 +4105,7 @@ var rules = {
|
|
|
4002
4105
|
"document-sagas-are-generic": document_sagas_are_generic_default,
|
|
4003
4106
|
"documents-share-one-table": documents_share_one_table_default,
|
|
4004
4107
|
"effect-hook-naming": effect_hook_naming_default,
|
|
4108
|
+
"emit-declares-attempts": emit_declares_attempts_default,
|
|
4005
4109
|
"layer-walls": layer_walls_default,
|
|
4006
4110
|
"max-comment-density": max_comment_density_default,
|
|
4007
4111
|
"molecule-atoms-only": molecule_atoms_only_default,
|
package/dist/presets.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geonosis/oxlint-plugin-biological-architecture",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Biological tier architecture as lint — the union of the dielime and during.day rule sets, shipped as presets.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"oxlint",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"oxlint": ">=1.77"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@geonosis/lint-parity": "0.
|
|
36
|
+
"@geonosis/lint-parity": "0.3.0"
|
|
37
37
|
},
|
|
38
38
|
"engines": {
|
|
39
39
|
"node": ">=22"
|