@geonosis/oxlint-plugin-biological-architecture 3.1.0 → 3.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/CHANGELOG.md +57 -0
- package/README.md +75 -6
- package/RULES.md +202 -101
- package/corpus/.oxlintrc.json +5 -0
- package/corpus/data/bad-dynamic-orm.ts +5 -0
- package/corpus/data/bad-type-only-orm.ts +3 -0
- package/corpus/e2e/bad-list-response.spec.ts +5 -0
- package/corpus/e2e/bad-timeout.spec.ts +5 -0
- package/corpus/e2e/good-detail-response.spec.ts +5 -0
- package/corpus/e2e/good-state.spec.ts +5 -0
- package/corpus/manifest.json +2 -0
- package/corpus/medusa/queries/bad-silent-cap.ts +11 -0
- package/corpus/medusa/queries/good-paged-through.ts +19 -0
- package/corpus/medusa/queries/integration-tests/any-row.ts +10 -0
- package/corpus/tissues/bad-awaits-data.tsx +8 -0
- package/corpus/tissues/bad-fetches-data.tsx +7 -0
- package/corpus/tissues/good-takes-products.tsx +5 -0
- package/dist/discover-atoms.d.ts +2 -37
- package/dist/discover-atoms.js +1 -104
- package/dist/index.d.ts +3 -12
- package/dist/index.js +21 -176
- package/dist/presets.d.ts +11 -19
- package/dist/presets.js +94 -4
- package/dist/registry.d.ts +2 -0
- package/dist/registry.js +172 -0
- package/dist/rules/atom-no-deps.d.ts +1 -0
- package/dist/rules/atom-no-deps.js +10 -11
- package/dist/rules/boundary-time-is-primitive.d.ts +0 -7
- package/dist/rules/cell-must-be-stateful.d.ts +2 -2
- package/dist/rules/cell-must-be-stateful.js +24 -23
- package/dist/rules/cell-must-not-compose-cell.d.ts +1 -0
- package/dist/rules/cell-must-not-compose-cell.js +10 -3
- package/dist/rules/cell-no-cross-feature-organelles.d.ts +1 -0
- package/dist/rules/cell-no-cross-feature-organelles.js +7 -0
- package/dist/rules/cell-no-tissues.d.ts +1 -0
- package/dist/rules/cell-no-tissues.js +11 -7
- package/dist/rules/cells-folder-index-is-barrel.d.ts +12 -10
- package/dist/rules/cells-folder-index-is-barrel.js +66 -34
- package/dist/rules/compound-must-be-stateless.d.ts +2 -0
- package/dist/rules/compound-must-be-stateless.js +24 -26
- package/dist/rules/compound-no-stores.d.ts +1 -0
- package/dist/rules/compound-no-stores.js +12 -10
- package/dist/rules/constants-in-constants-file.d.ts +2 -8
- package/dist/rules/constants-in-constants-file.js +5 -6
- package/dist/rules/dialect-through-the-seam.d.ts +0 -13
- package/dist/rules/durable-body-reads-through-steps.d.ts +0 -14
- package/dist/rules/durable-body-reads-through-steps.js +8 -3
- package/dist/rules/effect-hook-naming.d.ts +9 -11
- package/dist/rules/effect-hook-naming.js +33 -31
- package/dist/rules/emit-declares-attempts.d.ts +0 -17
- package/dist/rules/emit-declares-attempts.js +5 -10
- package/dist/rules/font-roles-only.d.ts +0 -18
- package/dist/rules/font-roles-only.js +19 -22
- package/dist/rules/hermetic-e2e-no-wall-clock-waits.d.ts +53 -0
- package/dist/rules/hermetic-e2e-no-wall-clock-waits.js +100 -0
- package/dist/rules/icon-control-has-name.js +7 -1
- package/dist/rules/layer-walls.d.ts +0 -25
- package/dist/rules/layer-walls.js +7 -13
- package/dist/rules/lib/atom-map.d.ts +24 -0
- package/dist/rules/lib/atom-map.js +16 -0
- package/dist/rules/lib/cells-barrel.d.ts +0 -1
- package/dist/rules/lib/colors.d.ts +0 -19
- package/dist/rules/lib/colors.js +10 -243
- package/dist/rules/lib/comments.d.ts +19 -0
- package/dist/rules/lib/comments.js +5 -0
- package/dist/rules/lib/context.d.ts +0 -10
- package/dist/rules/lib/direction-rule.d.ts +4 -21
- package/dist/rules/lib/direction-rule.js +25 -4
- package/dist/rules/lib/doors.d.ts +2 -18
- package/dist/rules/lib/doors.js +81 -41
- package/dist/rules/lib/fresh.d.ts +5 -0
- package/dist/rules/lib/fresh.js +15 -0
- package/dist/rules/lib/hooks.d.ts +2 -17
- package/dist/rules/lib/hooks.js +14 -9
- package/dist/rules/lib/imports.d.ts +2 -23
- package/dist/rules/lib/imports.js +7 -2
- package/dist/rules/lib/jsx.d.ts +31 -0
- package/dist/rules/lib/jsx.js +11 -0
- package/dist/rules/lib/landing.d.ts +0 -40
- package/dist/rules/lib/literal-call.d.ts +1 -0
- package/dist/rules/lib/literal-call.js +2 -0
- package/dist/rules/lib/module-text.d.ts +3 -27
- package/dist/rules/lib/module-text.js +19 -4
- package/dist/rules/lib/mutations.d.ts +0 -14
- package/dist/rules/lib/nodes.d.ts +1 -18
- package/dist/rules/lib/nodes.js +1 -0
- package/dist/rules/lib/objects.d.ts +2 -11
- package/dist/rules/lib/objects.js +21 -0
- package/dist/rules/lib/options.d.ts +3 -52
- package/dist/rules/lib/options.js +49 -18
- package/dist/rules/lib/package-imports.d.ts +0 -5
- package/dist/rules/lib/package-imports.js +4 -10
- package/dist/rules/lib/paths.d.ts +15 -41
- package/dist/rules/lib/paths.js +34 -1
- package/dist/rules/lib/pattern-match.d.ts +2 -12
- package/dist/rules/lib/pattern-match.js +5 -1
- package/dist/rules/lib/probe.d.ts +0 -43
- package/dist/rules/lib/reexports.d.ts +0 -18
- package/dist/rules/lib/render-tracking.d.ts +0 -8
- package/dist/rules/lib/run-files.d.ts +18 -0
- package/dist/rules/lib/run-files.js +144 -0
- package/dist/rules/lib/shared-features.d.ts +0 -8
- package/dist/rules/lib/shared-features.js +2 -9
- package/dist/rules/lib/state-sources.d.ts +8 -0
- package/dist/rules/lib/state-sources.js +55 -0
- package/dist/rules/lib/steps.d.ts +3 -5
- package/dist/rules/lib/steps.js +6 -1
- package/dist/rules/lib/styling.d.ts +9 -42
- package/dist/rules/lib/styling.js +36 -10
- package/dist/rules/lib/tiers.d.ts +11 -24
- package/dist/rules/lib/tiers.js +41 -1
- package/dist/rules/lib/tissue-entrypoint.d.ts +0 -1
- package/dist/rules/lib/tissue-entrypoint.js +4 -5
- package/dist/rules/lib/token-layer.d.ts +19 -0
- package/dist/rules/lib/token-layer.js +170 -0
- package/dist/rules/lib/tsconfig-paths.d.ts +0 -4
- package/dist/rules/lib/tsconfig-paths.js +47 -15
- package/dist/rules/lib/type-members.d.ts +6 -0
- package/dist/rules/lib/type-members.js +165 -0
- package/dist/rules/lib/visitors.d.ts +0 -22
- package/dist/rules/lib/workspace-members.d.ts +2 -0
- package/dist/rules/lib/workspace-members.js +113 -0
- package/dist/rules/list-take-without-order.d.ts +24 -0
- package/dist/rules/list-take-without-order.js +293 -16
- package/dist/rules/max-comment-density.d.ts +3 -18
- package/dist/rules/max-comment-density.js +3 -3
- package/dist/rules/molecule-atoms-only.d.ts +1 -0
- package/dist/rules/molecule-atoms-only.js +11 -7
- package/dist/rules/molecule-must-compose.d.ts +2 -0
- package/dist/rules/molecule-must-compose.js +17 -21
- package/dist/rules/mutating-step-declares-retry.d.ts +2 -7
- package/dist/rules/mutating-step-declares-retry.js +29 -11
- package/dist/rules/named-when-block.d.ts +0 -2
- package/dist/rules/named-when-block.js +2 -8
- package/dist/rules/next-route-segment-is-thin-delegate.d.ts +4 -19
- package/dist/rules/next-route-segment-is-thin-delegate.js +47 -71
- package/dist/rules/no-brand-names.d.ts +0 -9
- package/dist/rules/no-brand-names.js +1 -1
- package/dist/rules/no-card-shaped-div.d.ts +4 -42
- package/dist/rules/no-card-shaped-div.js +12 -4
- package/dist/rules/no-cross-feature-stores.d.ts +1 -0
- package/dist/rules/no-cross-feature-stores.js +9 -5
- package/dist/rules/no-d1-transaction.d.ts +2 -5
- package/dist/rules/no-d1-transaction.js +7 -0
- package/dist/rules/no-date-string-compare.d.ts +2 -0
- package/dist/rules/no-date-string-compare.js +14 -8
- package/dist/rules/no-duplicate-jsx-patterns.d.ts +3 -1
- package/dist/rules/no-duplicate-jsx-patterns.js +16 -1
- package/dist/rules/no-generic-utility-package.d.ts +48 -2
- package/dist/rules/no-generic-utility-package.js +48 -9
- package/dist/rules/no-hook-in-component-disguise.d.ts +27 -13
- package/dist/rules/no-hook-in-component-disguise.js +38 -30
- package/dist/rules/no-index-count-as-exact.d.ts +0 -3
- package/dist/rules/no-inert-hidden-jsx.d.ts +4 -21
- package/dist/rules/no-inert-hidden-jsx.js +13 -12
- package/dist/rules/no-inline-data-in-jsx.d.ts +2 -0
- package/dist/rules/no-inline-data-in-jsx.js +17 -2
- package/dist/rules/no-invalid-feature-folders.d.ts +2 -0
- package/dist/rules/no-invalid-feature-folders.js +12 -4
- package/dist/rules/no-loader-side-effect-in-index-file.d.ts +2 -9
- package/dist/rules/no-logic-in-component-files.d.ts +6 -5
- package/dist/rules/no-logic-in-component-files.js +52 -31
- package/dist/rules/no-mock-db-in-integration.d.ts +0 -15
- package/dist/rules/no-mock-db-in-integration.js +3 -4
- package/dist/rules/no-orm-outside-db.d.ts +1 -0
- package/dist/rules/no-orm-outside-db.js +10 -7
- package/dist/rules/no-raw-html-atoms.d.ts +3 -22
- package/dist/rules/no-raw-html-atoms.js +5 -17
- package/dist/rules/no-raw-sql-outside-allowed.d.ts +2 -0
- package/dist/rules/no-raw-sql-outside-allowed.js +10 -15
- package/dist/rules/no-react-namespace.d.ts +2 -0
- package/dist/rules/no-react-namespace.js +13 -0
- package/dist/rules/no-renamed-html-props.d.ts +2 -0
- package/dist/rules/no-renamed-html-props.js +11 -2
- package/dist/rules/no-render-prop-reader.d.ts +4 -20
- package/dist/rules/no-render-prop-reader.js +13 -5
- package/dist/rules/no-runtime-dead-affordance.d.ts +3 -26
- package/dist/rules/no-runtime-dead-affordance.js +7 -20
- package/dist/rules/no-swallowed-catch.js +2 -8
- package/dist/rules/no-trivial-wrapper-component.d.ts +4 -19
- package/dist/rules/no-trivial-wrapper-component.js +24 -33
- package/dist/rules/no-ts-in-bio-folders.d.ts +2 -13
- package/dist/rules/no-ts-in-bio-folders.js +9 -7
- package/dist/rules/no-type-definitions-in-components.d.ts +2 -0
- package/dist/rules/no-type-definitions-in-components.js +12 -1
- package/dist/rules/no-unregistered-plugin-dir.d.ts +0 -41
- package/dist/rules/no-unregistered-plugin-dir.js +1 -9
- package/dist/rules/no-void-port.d.ts +2 -4
- package/dist/rules/no-void-port.js +7 -0
- package/dist/rules/organelle-dependency.d.ts +1 -0
- package/dist/rules/organelle-dependency.js +10 -8
- package/dist/rules/organelle-single-source.d.ts +2 -0
- package/dist/rules/organelle-single-source.js +58 -9
- package/dist/rules/plugin-route-namespaced.d.ts +0 -11
- package/dist/rules/plugin-route-namespaced.js +2 -2
- package/dist/rules/queries-require-org-scope.d.ts +0 -3
- package/dist/rules/queries-require-org-scope.js +3 -10
- package/dist/rules/queue-loop-is-the-library.js +3 -2
- package/dist/rules/route-no-inline-mutation.d.ts +1 -25
- package/dist/rules/route-no-inline-mutation.js +2 -7
- package/dist/rules/router-schema-parity.d.ts +0 -9
- package/dist/rules/router-schema-parity.js +5 -9
- package/dist/rules/service-name-not-reserved.d.ts +0 -3
- package/dist/rules/service-name-not-reserved.js +1 -34
- package/dist/rules/ssot-no-inline-facts.js +2 -5
- package/dist/rules/ssot-no-process-env.js +4 -12
- package/dist/rules/step-declares-compensation-or-none.d.ts +2 -1
- package/dist/rules/step-declares-compensation-or-none.js +17 -1
- package/dist/rules/step-opens-its-own-cell.d.ts +3 -26
- package/dist/rules/step-opens-its-own-cell.js +8 -13
- package/dist/rules/store-route-scopes-tenant-data.js +8 -26
- package/dist/rules/subpath-over-barrel.d.ts +3 -10
- package/dist/rules/subpath-over-barrel.js +55 -10
- package/dist/rules/subscriber-declares-id.d.ts +0 -2
- package/dist/rules/subscriber-declares-id.js +2 -8
- package/dist/rules/tables-declare-their-plane.d.ts +0 -2
- package/dist/rules/tables-declare-their-plane.js +4 -10
- package/dist/rules/tenant-tables-carry-org-id.d.ts +1 -4
- package/dist/rules/tenant-tables-carry-org-id.js +5 -12
- package/dist/rules/test-no-clock-polling-for-promise-flips.d.ts +7 -1
- package/dist/rules/test-no-clock-polling-for-promise-flips.js +21 -1
- package/dist/rules/time-through-the-door.d.ts +0 -24
- package/dist/rules/time-through-the-door.js +5 -5
- package/dist/rules/tissue-must-compose.d.ts +4 -2
- package/dist/rules/tissue-must-compose.js +25 -20
- package/dist/rules/tissue-no-data-props.d.ts +57 -1
- package/dist/rules/tissue-no-data-props.js +114 -32
- package/dist/rules/tissue-no-fetch.d.ts +26 -0
- package/dist/rules/tissue-no-fetch.js +57 -0
- package/dist/rules/tissue-no-hooks.d.ts +21 -23
- package/dist/rules/tissue-no-hooks.js +22 -4
- package/dist/rules/tissue-no-organelles.d.ts +1 -0
- package/dist/rules/tissue-no-organelles.js +9 -1
- package/dist/rules/tissue-no-stores.d.ts +1 -0
- package/dist/rules/tissue-no-stores.js +9 -1
- package/dist/rules/todo-names-its-plan.d.ts +1 -28
- package/dist/rules/todo-names-its-plan.js +2 -2
- package/dist/rules/tokens-only-colors.d.ts +8 -46
- package/dist/rules/tokens-only-colors.js +64 -20
- package/dist/rules/unique-query-step-name.d.ts +0 -7
- package/dist/rules/unique-query-step-name.js +6 -10
- package/dist/rules/worker-handles-are-scoped.d.ts +11 -8
- package/dist/rules/worker-handles-are-scoped.js +12 -5
- package/dist/rules/zustand-v5-best-practices.d.ts +8 -18
- package/dist/rules/zustand-v5-best-practices.js +32 -56
- package/dist/timing.d.ts +18 -0
- package/dist/timing.js +75 -0
- package/package.json +2 -2
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// src/rules/lib/styling.ts
|
|
2
|
-
|
|
2
|
+
import { attributeNamed } from "./jsx.js";
|
|
3
|
+
var isContainer = (node) => node.type === "JSXExpressionContainer";
|
|
4
|
+
var isTemplate = (node) => node?.type === "TemplateLiteral";
|
|
3
5
|
var classNameOf = (node) => {
|
|
4
6
|
const value = attributeNamed(node, "className")?.value;
|
|
5
7
|
if (value === undefined || value === null)
|
|
@@ -8,11 +10,19 @@ var classNameOf = (node) => {
|
|
|
8
10
|
const literal = value.value;
|
|
9
11
|
return typeof literal === "string" ? literal : undefined;
|
|
10
12
|
}
|
|
11
|
-
if (value
|
|
13
|
+
if (isContainer(value))
|
|
12
14
|
return expressionText(unwrap(value.expression));
|
|
13
|
-
}
|
|
14
15
|
return;
|
|
15
16
|
};
|
|
17
|
+
var staticClassNameOf = (node) => {
|
|
18
|
+
const value = attributeNamed(node, "className")?.value;
|
|
19
|
+
if (value !== undefined && value !== null && isContainer(value)) {
|
|
20
|
+
const expression = unwrap(value.expression);
|
|
21
|
+
if (isTemplate(expression) && (expression.expressions ?? []).length > 0)
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
return classNameOf(node);
|
|
25
|
+
};
|
|
16
26
|
var unwrap = (node) => {
|
|
17
27
|
let current = node ?? null;
|
|
18
28
|
while (current !== null && (current.type === "TSAsExpression" || current.type === "TSSatisfiesExpression" || current.type === "TSNonNullExpression" || current.type === "ParenthesizedExpression")) {
|
|
@@ -55,16 +65,29 @@ var styleEntriesOf = (node) => {
|
|
|
55
65
|
return text === undefined ? [] : [{ property: name, value: text }];
|
|
56
66
|
});
|
|
57
67
|
};
|
|
58
|
-
var
|
|
68
|
+
var styleObjectOf = (node) => {
|
|
59
69
|
const value = attributeNamed(node, "style")?.value;
|
|
60
|
-
if (value
|
|
61
|
-
return
|
|
70
|
+
if (value === undefined || value === null || !isContainer(value))
|
|
71
|
+
return null;
|
|
62
72
|
const expression = unwrap(value.expression);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
return expression?.type === "ObjectExpression" ? expression : null;
|
|
74
|
+
};
|
|
75
|
+
var styleProperties = (node) => styleObjectOf(node)?.properties ?? [];
|
|
76
|
+
var withoutVariants = (token) => {
|
|
77
|
+
let depth = 0;
|
|
78
|
+
let from = 0;
|
|
79
|
+
for (const [at, char] of [...token].entries()) {
|
|
80
|
+
if (char === "[")
|
|
81
|
+
depth += 1;
|
|
82
|
+
else if (char === "]")
|
|
83
|
+
depth -= 1;
|
|
84
|
+
else if (char === ":" && depth === 0)
|
|
85
|
+
from = at + 1;
|
|
86
|
+
}
|
|
87
|
+
return token.slice(from);
|
|
66
88
|
};
|
|
67
|
-
var arbitraryValuesOf = (className) => className.split(/\s+/).flatMap((
|
|
89
|
+
var arbitraryValuesOf = (className) => className.split(/\s+/).flatMap((classToken) => {
|
|
90
|
+
const token = withoutVariants(classToken).replace(/^!|!$/g, "");
|
|
68
91
|
const open = token.indexOf("[");
|
|
69
92
|
const close = token.lastIndexOf("]");
|
|
70
93
|
if (open <= 0 || close <= open)
|
|
@@ -84,7 +107,10 @@ var variableOf = (value) => /^var\(\s*(--[A-Za-z0-9_-]+)/.exec(value.trim())?.[1
|
|
|
84
107
|
export {
|
|
85
108
|
arbitraryValuesOf,
|
|
86
109
|
classNameOf,
|
|
110
|
+
keyOf,
|
|
87
111
|
localVariablesOf,
|
|
112
|
+
staticClassNameOf,
|
|
88
113
|
styleEntriesOf,
|
|
114
|
+
styleObjectOf,
|
|
89
115
|
variableOf
|
|
90
116
|
};
|
|
@@ -1,31 +1,18 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The tiers, and the one path pattern every rule here tests a filename against.
|
|
3
|
-
*
|
|
4
|
-
* `(?:features/[^/]+/)?<tier>/` is a Medusa storefront's broader form, kept when the two forks were
|
|
5
|
-
* merged: it reaches a flat kit (`packages/ui/src/tissues/…`) as well as a feature-sliced app, and
|
|
6
|
-
* a rule that reaches more files is the stricter rule. The `(?:^|/)` anchor is this file's
|
|
7
|
-
* addition, so `subtissues/` is not read as `tissues/`.
|
|
8
|
-
*/
|
|
9
1
|
export declare const TIERS: readonly ['atoms', 'molecules', 'compounds', 'organelles', 'cells', 'tissues'];
|
|
10
2
|
export type Tier = (typeof TIERS)[number];
|
|
3
|
+
export declare const APP_FOLDER: RegExp;
|
|
4
|
+
export declare const LAYOUTS_FOLDER: RegExp;
|
|
5
|
+
export declare const STORES_FOLDER: RegExp;
|
|
6
|
+
export declare const ABOVE_ATOMS_FOLDER: RegExp;
|
|
7
|
+
export declare const ORGANS_FOLDER: RegExp;
|
|
8
|
+
export declare const COMPONENT_FILE_PATTERN: RegExp;
|
|
9
|
+
export declare const PRIVATE_SIBLING: RegExp;
|
|
10
|
+
export declare const CELL_PRIVATE_SIBLING: RegExp;
|
|
11
11
|
export declare const inTier: (filename: string, tier: Tier) => boolean;
|
|
12
|
-
|
|
12
|
+
export declare const inOrgans: (filename: string) => boolean;
|
|
13
|
+
export declare const folderOf: <Name extends string>(source: string, names: readonly Name[]) => Name | undefined;
|
|
14
|
+
export declare const inTiersAbove: (filename: string, tier: Tier) => boolean;
|
|
13
15
|
export declare const tierOf: (filename: string) => Tier | undefined;
|
|
14
|
-
/**
|
|
15
|
-
* The tier a file sits DIRECTLY in — the folder that is its own parent — or none.
|
|
16
|
-
*
|
|
17
|
-
* `tierOf` answers for anywhere in the path, which is what a rule about IMPORTS wants. A rule about
|
|
18
|
-
* the file's own folder wants the parent and nothing else: `cells/parts/helper.ts` is in `parts/`,
|
|
19
|
-
* and `subtissues/` is not `tissues/` however a regex written without an anchor read it (#5).
|
|
20
|
-
*/
|
|
21
16
|
export declare const tierFolderOf: (filename: string) => Tier | undefined;
|
|
22
|
-
/**
|
|
23
|
-
* The feature a path belongs to, or none — a flat kit (`packages/ui/src/atoms/`) has tiers and no
|
|
24
|
-
* features, and a rule that assumed otherwise simply never fired there.
|
|
25
|
-
*/
|
|
26
17
|
export declare const featureOf: (filename: string) => string | undefined;
|
|
27
|
-
/**
|
|
28
|
-
* A `scope` naming something that is not a tier matches no file, which is the inert rule the
|
|
29
|
-
* option refusal exists to stop — one option along. So it is a config error, and it stops the run.
|
|
30
|
-
*/
|
|
31
18
|
export declare const requireTiers: (rule: string, names: readonly string[]) => Tier[];
|
package/dist/rules/lib/tiers.js
CHANGED
|
@@ -9,7 +9,35 @@ var PATTERNS = {
|
|
|
9
9
|
organelles: /(?:^|\/)(?:features\/[^/]+\/)?organelles\//,
|
|
10
10
|
tissues: /(?:^|\/)(?:features\/[^/]+\/)?tissues\//
|
|
11
11
|
};
|
|
12
|
+
var APP_FOLDER = /(?:^|\/)app\//;
|
|
13
|
+
var LAYOUTS_FOLDER = /(?:^|\/)layouts\//;
|
|
14
|
+
var STORES_FOLDER = /(?:^|\/)stores\//;
|
|
15
|
+
var ABOVE_ATOMS_FOLDER = /(?:^|\/)(molecules|compounds|organelles|cells|layouts)\//;
|
|
16
|
+
var ORGANS_FOLDER = /(?:^|\/)(?:features\/[^/]+\/)?organs\//;
|
|
17
|
+
var COMPONENT_FILE_PATTERN = /(?:^|\/)(?:features\/[^/]+\/)?(compounds|organelles|cells|tissues|organs)\/.+\.tsx$/;
|
|
18
|
+
var SHARED_PRIVATE_SUFFIXES = [
|
|
19
|
+
"parts",
|
|
20
|
+
"sections",
|
|
21
|
+
"summary",
|
|
22
|
+
"layouts",
|
|
23
|
+
"icons",
|
|
24
|
+
"types",
|
|
25
|
+
"sidebar",
|
|
26
|
+
"data",
|
|
27
|
+
"stories",
|
|
28
|
+
"spec",
|
|
29
|
+
"test"
|
|
30
|
+
];
|
|
31
|
+
var privateSiblingOf = (...own) => new RegExp(`\\.(?:${[...SHARED_PRIVATE_SUFFIXES, ...own].join("|")})\\.tsx$`);
|
|
32
|
+
var PRIVATE_SIBLING = privateSiblingOf("hooks", "utils", "context", "store", "config");
|
|
33
|
+
var CELL_PRIVATE_SIBLING = privateSiblingOf("form", "steps");
|
|
12
34
|
var inTier = (filename, tier) => PATTERNS[tier].test(normalise(filename));
|
|
35
|
+
var inOrgans = (filename) => ORGANS_FOLDER.test(normalise(filename));
|
|
36
|
+
var folderOf = (source, names) => {
|
|
37
|
+
const listed = new Set(names);
|
|
38
|
+
return normalise(source).split("/").find((segment) => listed.has(segment));
|
|
39
|
+
};
|
|
40
|
+
var inTiersAbove = (filename, tier) => TIERS.slice(TIERS.indexOf(tier) + 1).some((above) => inTier(filename, above));
|
|
13
41
|
var tierOf = (filename) => TIERS.find((tier) => inTier(filename, tier));
|
|
14
42
|
var tierFolderOf = (filename) => {
|
|
15
43
|
const parent = normalise(filename).split("/").at(-2);
|
|
@@ -17,15 +45,27 @@ var tierFolderOf = (filename) => {
|
|
|
17
45
|
};
|
|
18
46
|
var FEATURE = /(?:^|\/)features\/([^/]+)\//;
|
|
19
47
|
var featureOf = (filename) => FEATURE.exec(normalise(filename))?.[1];
|
|
48
|
+
var isTier = (name) => TIERS.some((tier) => tier === name);
|
|
20
49
|
var requireTiers = (rule, names) => names.map((name) => {
|
|
21
|
-
if (
|
|
50
|
+
if (isTier(name))
|
|
22
51
|
return name;
|
|
23
52
|
throw new Error(`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(", ")}.`);
|
|
24
53
|
});
|
|
25
54
|
export {
|
|
55
|
+
ABOVE_ATOMS_FOLDER,
|
|
56
|
+
APP_FOLDER,
|
|
57
|
+
CELL_PRIVATE_SIBLING,
|
|
58
|
+
COMPONENT_FILE_PATTERN,
|
|
59
|
+
LAYOUTS_FOLDER,
|
|
60
|
+
ORGANS_FOLDER,
|
|
61
|
+
PRIVATE_SIBLING,
|
|
62
|
+
STORES_FOLDER,
|
|
26
63
|
TIERS,
|
|
27
64
|
featureOf,
|
|
65
|
+
folderOf,
|
|
66
|
+
inOrgans,
|
|
28
67
|
inTier,
|
|
68
|
+
inTiersAbove,
|
|
29
69
|
requireTiers,
|
|
30
70
|
tierFolderOf,
|
|
31
71
|
tierOf
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
// src/rules/lib/tissue-entrypoint.ts
|
|
2
|
-
import { normalise } from "./paths.js";
|
|
3
|
-
|
|
4
|
-
var PRIVATE_TISSUE_SIBLING = /\.(parts|sections|summary|layouts|icons|types|hooks|utils|context|store|sidebar|data|config|stories|spec|test)\.tsx$/;
|
|
2
|
+
import { fileOf, normalise } from "./paths.js";
|
|
3
|
+
import { inTier, PRIVATE_SIBLING } from "./tiers.js";
|
|
5
4
|
var tissueEntrypointOf = (filename) => {
|
|
6
5
|
const normalized = normalise(filename);
|
|
7
|
-
const basename = normalized
|
|
6
|
+
const basename = fileOf(normalized);
|
|
8
7
|
return {
|
|
9
8
|
basename,
|
|
10
|
-
isEntrypoint:
|
|
9
|
+
isEntrypoint: inTier(normalized, "tissues") && basename.endsWith(".tsx") && basename !== "index.tsx" && !PRIVATE_SIBLING.test(basename)
|
|
11
10
|
};
|
|
12
11
|
};
|
|
13
12
|
export {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ModuleNode } from './imports.js';
|
|
2
|
+
import type { AnyNode } from './nodes.js';
|
|
3
|
+
export declare const THEMEKIT = "@geonosis/themekit";
|
|
4
|
+
export declare const PALETTE_TAKERS: readonly string[];
|
|
5
|
+
export declare const PALETTE_TYPES: readonly string[];
|
|
6
|
+
export declare const tokenLayerOf: () => {
|
|
7
|
+
covers: (chain: readonly AnyNode[]) => boolean;
|
|
8
|
+
enclosing: () => AnyNode[];
|
|
9
|
+
resolve: () => void;
|
|
10
|
+
visitor: {
|
|
11
|
+
CallExpression(node: AnyNode): void;
|
|
12
|
+
ImportDeclaration(node: ModuleNode): void;
|
|
13
|
+
TSAsExpression(node: AnyNode): void;
|
|
14
|
+
TSSatisfiesExpression(node: AnyNode): void;
|
|
15
|
+
ObjectExpression(node: AnyNode): void;
|
|
16
|
+
'ObjectExpression:exit'(): void;
|
|
17
|
+
VariableDeclaration(node: AnyNode): void;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
// src/rules/lib/token-layer.ts
|
|
2
|
+
import { isTypeOnly, sourceOf } from "./imports.js";
|
|
3
|
+
import { propertyNameOf } from "./nodes.js";
|
|
4
|
+
import { withoutTypes } from "./objects.js";
|
|
5
|
+
var THEMEKIT = "@geonosis/themekit";
|
|
6
|
+
var PALETTE_TAKERS = [
|
|
7
|
+
"compile",
|
|
8
|
+
"defineTheme",
|
|
9
|
+
"emitVariables",
|
|
10
|
+
"toDtcg",
|
|
11
|
+
"valueAt"
|
|
12
|
+
];
|
|
13
|
+
var PALETTE_TYPES = [
|
|
14
|
+
"StatusPair",
|
|
15
|
+
"Theme",
|
|
16
|
+
"ThemeColors",
|
|
17
|
+
"ThemeFonts",
|
|
18
|
+
"ThemeMode",
|
|
19
|
+
"ThemeModes",
|
|
20
|
+
"ThemeRadii",
|
|
21
|
+
"ThemeShadows",
|
|
22
|
+
"ThemeStatusColors",
|
|
23
|
+
"TokenValue"
|
|
24
|
+
];
|
|
25
|
+
var isNode = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26
|
+
var childOf = (node, key) => {
|
|
27
|
+
const child = node?.[key];
|
|
28
|
+
return isNode(child) ? child : undefined;
|
|
29
|
+
};
|
|
30
|
+
var childrenOf = (node, key) => {
|
|
31
|
+
const children = node[key];
|
|
32
|
+
return Array.isArray(children) ? children.filter(isNode) : [];
|
|
33
|
+
};
|
|
34
|
+
var nameOf = (node) => typeof node?.name === "string" ? node.name : undefined;
|
|
35
|
+
var tokenLayerOf = () => {
|
|
36
|
+
const takers = new Set;
|
|
37
|
+
const namespaces = new Set;
|
|
38
|
+
const typeLocals = new Set;
|
|
39
|
+
const typeNamespaces = new Set;
|
|
40
|
+
const typed = [];
|
|
41
|
+
const objects = new Set;
|
|
42
|
+
const passed = new Set;
|
|
43
|
+
const bindings = new Map;
|
|
44
|
+
const open = [];
|
|
45
|
+
const single = (name) => {
|
|
46
|
+
const binding = bindings.get(name);
|
|
47
|
+
return binding?.declarations === 1 ? binding.object : undefined;
|
|
48
|
+
};
|
|
49
|
+
const isPaletteType = (reference) => {
|
|
50
|
+
if (reference?.type !== "TSTypeReference")
|
|
51
|
+
return false;
|
|
52
|
+
const name = childOf(reference, "typeName");
|
|
53
|
+
if (name?.type === "Identifier")
|
|
54
|
+
return typeLocals.has(nameOf(name) ?? "");
|
|
55
|
+
if (name?.type !== "TSQualifiedName")
|
|
56
|
+
return false;
|
|
57
|
+
const namespace = nameOf(childOf(name, "left"));
|
|
58
|
+
const member = nameOf(childOf(name, "right"));
|
|
59
|
+
return namespace !== undefined && member !== undefined && typeNamespaces.has(namespace) && PALETTE_TYPES.includes(member);
|
|
60
|
+
};
|
|
61
|
+
const typedObject = (expression, reference) => {
|
|
62
|
+
const object = withoutTypes(expression);
|
|
63
|
+
if (object?.type === "ObjectExpression")
|
|
64
|
+
typed.push({ object, reference });
|
|
65
|
+
};
|
|
66
|
+
const takesPalette = (callee) => {
|
|
67
|
+
if (callee?.type === "Identifier")
|
|
68
|
+
return takers.has(nameOf(callee) ?? "");
|
|
69
|
+
if (callee?.type !== "MemberExpression")
|
|
70
|
+
return false;
|
|
71
|
+
const owner = nameOf(childOf(callee, "object"));
|
|
72
|
+
const name = propertyNameOf(callee);
|
|
73
|
+
return owner !== undefined && namespaces.has(owner) && name !== null && PALETTE_TAKERS.includes(name);
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
covers: (chain) => chain.some((one) => objects.has(one)),
|
|
77
|
+
enclosing: () => [...open],
|
|
78
|
+
resolve: () => {
|
|
79
|
+
for (const one of typed) {
|
|
80
|
+
if (isPaletteType(one.reference))
|
|
81
|
+
objects.add(one.object);
|
|
82
|
+
}
|
|
83
|
+
const queue = [...objects];
|
|
84
|
+
for (const name of passed) {
|
|
85
|
+
const held = single(name);
|
|
86
|
+
if (held !== undefined && !objects.has(held)) {
|
|
87
|
+
objects.add(held);
|
|
88
|
+
queue.push(held);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
for (const object of queue) {
|
|
92
|
+
for (const member of childrenOf(object, "properties")) {
|
|
93
|
+
const value = withoutTypes(member.type === "SpreadElement" ? member.argument : member.value);
|
|
94
|
+
const held = value?.type === "Identifier" ? single(nameOf(value) ?? "") : undefined;
|
|
95
|
+
if (held !== undefined && !objects.has(held)) {
|
|
96
|
+
objects.add(held);
|
|
97
|
+
queue.push(held);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
visitor: {
|
|
103
|
+
CallExpression(node) {
|
|
104
|
+
if (!takesPalette(childOf(node, "callee")))
|
|
105
|
+
return;
|
|
106
|
+
const argument = withoutTypes(childrenOf(node, "arguments")[0]);
|
|
107
|
+
if (argument?.type === "ObjectExpression")
|
|
108
|
+
objects.add(argument);
|
|
109
|
+
else if (argument?.type === "Identifier")
|
|
110
|
+
passed.add(nameOf(argument) ?? "");
|
|
111
|
+
},
|
|
112
|
+
ImportDeclaration(node) {
|
|
113
|
+
if (sourceOf(node) !== THEMEKIT)
|
|
114
|
+
return;
|
|
115
|
+
for (const specifier of node.specifiers ?? []) {
|
|
116
|
+
const local = specifier.local?.name;
|
|
117
|
+
if (local === undefined)
|
|
118
|
+
continue;
|
|
119
|
+
const typeOnly = isTypeOnly(node) || specifier.importKind === "type";
|
|
120
|
+
if (specifier.type === "ImportNamespaceSpecifier") {
|
|
121
|
+
typeNamespaces.add(local);
|
|
122
|
+
if (!typeOnly)
|
|
123
|
+
namespaces.add(local);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const imported = specifier.imported?.name;
|
|
127
|
+
if (imported === undefined)
|
|
128
|
+
continue;
|
|
129
|
+
if (PALETTE_TYPES.includes(imported))
|
|
130
|
+
typeLocals.add(local);
|
|
131
|
+
if (!typeOnly && PALETTE_TAKERS.includes(imported))
|
|
132
|
+
takers.add(local);
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
TSAsExpression(node) {
|
|
136
|
+
typedObject(node.expression, childOf(node, "typeAnnotation"));
|
|
137
|
+
},
|
|
138
|
+
TSSatisfiesExpression(node) {
|
|
139
|
+
typedObject(node.expression, childOf(node, "typeAnnotation"));
|
|
140
|
+
},
|
|
141
|
+
ObjectExpression(node) {
|
|
142
|
+
open.push(node);
|
|
143
|
+
},
|
|
144
|
+
"ObjectExpression:exit"() {
|
|
145
|
+
open.pop();
|
|
146
|
+
},
|
|
147
|
+
VariableDeclaration(node) {
|
|
148
|
+
for (const declarator of childrenOf(node, "declarations")) {
|
|
149
|
+
const id = childOf(declarator, "id");
|
|
150
|
+
const name = nameOf(id);
|
|
151
|
+
if (id?.type !== "Identifier" || name === undefined)
|
|
152
|
+
continue;
|
|
153
|
+
typedObject(declarator.init, childOf(childOf(id, "typeAnnotation"), "typeAnnotation"));
|
|
154
|
+
const init = withoutTypes(declarator.init);
|
|
155
|
+
const held = node.kind === "const" && init?.type === "ObjectExpression" ? init : undefined;
|
|
156
|
+
bindings.set(name, {
|
|
157
|
+
declarations: (bindings.get(name)?.declarations ?? 0) + 1,
|
|
158
|
+
...held === undefined ? {} : { object: held }
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
export {
|
|
166
|
+
PALETTE_TAKERS,
|
|
167
|
+
PALETTE_TYPES,
|
|
168
|
+
THEMEKIT,
|
|
169
|
+
tokenLayerOf
|
|
170
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/rules/lib/tsconfig-paths.ts
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join, resolve } from "node:path";
|
|
4
|
-
import { parseLooseJson } from "./module-text.js";
|
|
4
|
+
import { parseLooseJson, readManifest } from "./module-text.js";
|
|
5
5
|
import { longestMatch } from "./pattern-match.js";
|
|
6
6
|
var CHAIN = 5;
|
|
7
7
|
var configs = new Map;
|
|
@@ -26,12 +26,7 @@ var baseFileOf = (dir, base) => {
|
|
|
26
26
|
return;
|
|
27
27
|
if (rest !== "")
|
|
28
28
|
return withJson(join(packageDir, rest));
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
declared = JSON.parse(readFileSync(join(packageDir, "package.json"), "utf8")).tsconfig;
|
|
32
|
-
} catch {
|
|
33
|
-
declared = undefined;
|
|
34
|
-
}
|
|
29
|
+
const declared = readManifest(join(packageDir, "package.json"))?.tsconfig;
|
|
35
30
|
return join(packageDir, typeof declared === "string" ? declared : "tsconfig.json");
|
|
36
31
|
};
|
|
37
32
|
var packageDirAbove = (dir, name) => {
|
|
@@ -77,19 +72,56 @@ var readConfig = (file) => {
|
|
|
77
72
|
}
|
|
78
73
|
return { anchor, paths };
|
|
79
74
|
};
|
|
75
|
+
var GLOB = /[*?{[]/;
|
|
76
|
+
var strings = (value) => Array.isArray(value) ? value.filter((one) => typeof one === "string") : [];
|
|
77
|
+
var referencePathsOf = (references) => (Array.isArray(references) ? references : []).flatMap((one) => {
|
|
78
|
+
if (typeof one !== "object" || one === null || !("path" in one))
|
|
79
|
+
return [];
|
|
80
|
+
return typeof one.path === "string" ? [one.path] : [];
|
|
81
|
+
});
|
|
82
|
+
var includedBy = (file) => {
|
|
83
|
+
const dir = dirname(file);
|
|
84
|
+
const entries = strings(rawOf(file)?.include);
|
|
85
|
+
return entries.length === 0 ? [dir] : entries.map((entry) => {
|
|
86
|
+
const segments = entry.split("/");
|
|
87
|
+
const globAt = segments.findIndex((one) => GLOB.test(one));
|
|
88
|
+
const head = globAt === -1 ? segments : segments.slice(0, globAt);
|
|
89
|
+
const last = head.at(-1);
|
|
90
|
+
const literal = last !== undefined && /\.[a-z]+$/.test(last) ? head.slice(0, -1) : head;
|
|
91
|
+
return resolve(dir, ...literal);
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
var isUnder = (dir, parent) => dir === parent || dir.startsWith(`${parent}/`);
|
|
95
|
+
var referencedConfig = (file, dir) => {
|
|
96
|
+
const refs = rawOf(file)?.references;
|
|
97
|
+
for (const named of referencePathsOf(refs)) {
|
|
98
|
+
const target = resolve(dirname(file), named);
|
|
99
|
+
const config = CONFIG_FILES.map((name) => join(target, name)).find((one) => existsSync(one));
|
|
100
|
+
const project = config ?? (target.endsWith(".json") ? target : withJson(target));
|
|
101
|
+
if (!existsSync(project))
|
|
102
|
+
continue;
|
|
103
|
+
if (!includedBy(project).some((included) => isUnder(dir, included)))
|
|
104
|
+
continue;
|
|
105
|
+
const found = readConfig(project);
|
|
106
|
+
if (found !== null)
|
|
107
|
+
return found;
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
};
|
|
80
111
|
var CONFIG_FILES = ["tsconfig.json", "jsconfig.json"];
|
|
112
|
+
var nearestConfigFile = (dir) => {
|
|
113
|
+
const file = CONFIG_FILES.map((name) => join(dir, name)).find((one) => existsSync(one));
|
|
114
|
+
if (file !== undefined)
|
|
115
|
+
return file;
|
|
116
|
+
const parent = dirname(dir);
|
|
117
|
+
return parent === dir ? undefined : nearestConfigFile(parent);
|
|
118
|
+
};
|
|
81
119
|
var configFor = (dir) => {
|
|
82
120
|
const known = configs.get(dir);
|
|
83
121
|
if (known !== undefined)
|
|
84
122
|
return known;
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
if (file !== undefined)
|
|
88
|
-
found = readConfig(file);
|
|
89
|
-
else {
|
|
90
|
-
const parent = dirname(dir);
|
|
91
|
-
found = parent === dir ? null : configFor(parent);
|
|
92
|
-
}
|
|
123
|
+
const file = nearestConfigFile(dir);
|
|
124
|
+
const found = file === undefined ? null : readConfig(file) ?? referencedConfig(file, dir);
|
|
93
125
|
configs.set(dir, found);
|
|
94
126
|
return found;
|
|
95
127
|
};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// src/rules/lib/type-members.ts
|
|
2
|
+
import { readSource } from "./module-text.js";
|
|
3
|
+
var DEPTH = 4;
|
|
4
|
+
var OPENERS = new Set(["(", "[", "{", "<"]);
|
|
5
|
+
var CLOSERS = new Set([")", "]", "}", ">"]);
|
|
6
|
+
var QUOTES = new Set(['"', "'", "`"]);
|
|
7
|
+
var nextOutside = (text, from) => {
|
|
8
|
+
const quote = text[from];
|
|
9
|
+
if (quote === undefined || !QUOTES.has(quote))
|
|
10
|
+
return from;
|
|
11
|
+
let at = from + 1;
|
|
12
|
+
while (at < text.length && text[at] !== quote)
|
|
13
|
+
at += text[at] === "\\" ? 2 : 1;
|
|
14
|
+
return at;
|
|
15
|
+
};
|
|
16
|
+
function* scan(text, from = 0) {
|
|
17
|
+
let depth = 0;
|
|
18
|
+
for (let at = from;at < text.length; at += 1) {
|
|
19
|
+
at = nextOutside(text, at);
|
|
20
|
+
const char = text[at] ?? "";
|
|
21
|
+
const closes = CLOSERS.has(char) && !(char === ">" && text[at - 1] === "=");
|
|
22
|
+
if (OPENERS.has(char))
|
|
23
|
+
depth += 1;
|
|
24
|
+
else if (closes)
|
|
25
|
+
depth -= 1;
|
|
26
|
+
yield { at, char, closes, depth };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
var closesAt = (text, open) => {
|
|
30
|
+
for (const { at, closes, depth } of scan(text, open))
|
|
31
|
+
if (closes && depth === 0)
|
|
32
|
+
return at;
|
|
33
|
+
return -1;
|
|
34
|
+
};
|
|
35
|
+
var splitTop = (text, separators) => {
|
|
36
|
+
const parts = [];
|
|
37
|
+
let from = 0;
|
|
38
|
+
for (const { at, char, depth } of scan(text)) {
|
|
39
|
+
if (depth === 0 && separators.has(char)) {
|
|
40
|
+
parts.push(text.slice(from, at));
|
|
41
|
+
from = at + 1;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
parts.push(text.slice(from));
|
|
45
|
+
return parts.map((one) => one.trim()).filter((one) => one !== "");
|
|
46
|
+
};
|
|
47
|
+
var CONTINUES_BEFORE = /(?:[|&:=,(<]|=>)\s*$/;
|
|
48
|
+
var CONTINUES_AFTER = /^\s*[|&?:.=)]/;
|
|
49
|
+
var membersText = (body) => {
|
|
50
|
+
const members = [];
|
|
51
|
+
let from = 0;
|
|
52
|
+
for (const { at, char, depth } of scan(body)) {
|
|
53
|
+
if (depth !== 0)
|
|
54
|
+
continue;
|
|
55
|
+
if (char === ";" || char === ",") {
|
|
56
|
+
members.push(body.slice(from, at));
|
|
57
|
+
from = at + 1;
|
|
58
|
+
} else if (char === `
|
|
59
|
+
`) {
|
|
60
|
+
const current = body.slice(from, at);
|
|
61
|
+
if (current.trim() !== "" && !CONTINUES_BEFORE.test(current) && !CONTINUES_AFTER.test(body.slice(at + 1))) {
|
|
62
|
+
members.push(current);
|
|
63
|
+
from = at + 1;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
members.push(body.slice(from));
|
|
68
|
+
return members.map((one) => one.trim()).filter((one) => one !== "");
|
|
69
|
+
};
|
|
70
|
+
var KEY = /^(?:readonly\s+)?(?:(["'])(.*?)\1|([A-Za-z_$][\w$]*))\s*\??\s*(?=[:(<])/;
|
|
71
|
+
var memberOf = (text) => {
|
|
72
|
+
const key = KEY.exec(text);
|
|
73
|
+
if (key === null)
|
|
74
|
+
return;
|
|
75
|
+
const name = key[2] ?? key[3] ?? "";
|
|
76
|
+
const rest = text.slice(key[0].length).trim();
|
|
77
|
+
if (rest.startsWith(":"))
|
|
78
|
+
return { name, type: rest.slice(1).trim() };
|
|
79
|
+
const generics = rest.startsWith("<") ? closesAt(rest, 0) + 1 : 0;
|
|
80
|
+
const open = generics > 0 ? generics : 0;
|
|
81
|
+
if (rest[open] !== "(")
|
|
82
|
+
return;
|
|
83
|
+
const close = closesAt(rest, open);
|
|
84
|
+
if (close === -1)
|
|
85
|
+
return;
|
|
86
|
+
const returned = rest.slice(close + 1).replace(/^\s*:\s*/, "");
|
|
87
|
+
return { name, type: `${rest.slice(0, close + 1)} => ${returned === "" ? "void" : returned}` };
|
|
88
|
+
};
|
|
89
|
+
var parseMembers = (body) => membersText(body).flatMap((one) => memberOf(one) ?? []);
|
|
90
|
+
var escaped = (name) => name.replaceAll("$", String.raw`\$`);
|
|
91
|
+
var bodyAfter = (text, from) => {
|
|
92
|
+
const open = text.indexOf("{", from);
|
|
93
|
+
if (open === -1)
|
|
94
|
+
return;
|
|
95
|
+
const close = closesAt(text, open);
|
|
96
|
+
return close === -1 ? undefined : text.slice(open + 1, close);
|
|
97
|
+
};
|
|
98
|
+
var aliasText = (text, from) => {
|
|
99
|
+
let end = from;
|
|
100
|
+
for (const { at, char, depth } of scan(text, from)) {
|
|
101
|
+
if (depth === 0 && (char === ";" || char === `
|
|
102
|
+
`)) {
|
|
103
|
+
const scanned = text.slice(from, at);
|
|
104
|
+
if (char === ";" || scanned.trim() !== "" && !CONTINUES_BEFORE.test(scanned) && !CONTINUES_AFTER.test(text.slice(at + 1))) {
|
|
105
|
+
end = at;
|
|
106
|
+
break;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
end = at + 1;
|
|
110
|
+
}
|
|
111
|
+
return text.slice(from, end);
|
|
112
|
+
};
|
|
113
|
+
var IDENTIFIER_TYPE = /^([A-Za-z_$][\w$]*)(?:<.*>)?$/s;
|
|
114
|
+
var membersIn = (text, name, depth) => {
|
|
115
|
+
if (depth > DEPTH)
|
|
116
|
+
return;
|
|
117
|
+
const interfaced = new RegExp(String.raw`\binterface\s+${escaped(name)}\b`).exec(text);
|
|
118
|
+
if (interfaced !== null) {
|
|
119
|
+
const body = bodyAfter(text, interfaced.index + interfaced[0].length);
|
|
120
|
+
return body === undefined ? undefined : parseMembers(body);
|
|
121
|
+
}
|
|
122
|
+
const aliased = new RegExp(String.raw`\btype\s+${escaped(name)}\b[^=]*=\s*`).exec(text);
|
|
123
|
+
if (aliased === null)
|
|
124
|
+
return;
|
|
125
|
+
const parts = splitTop(aliasText(text, aliased.index + aliased[0].length), new Set(["&"]));
|
|
126
|
+
const found = parts.flatMap((part) => {
|
|
127
|
+
if (part.startsWith("{") && closesAt(part, 0) === part.length - 1) {
|
|
128
|
+
return [parseMembers(part.slice(1, -1))];
|
|
129
|
+
}
|
|
130
|
+
const named = IDENTIFIER_TYPE.exec(part)?.[1];
|
|
131
|
+
const inner = named === undefined ? undefined : membersIn(text, named, depth + 1);
|
|
132
|
+
return inner === undefined ? [] : [inner];
|
|
133
|
+
});
|
|
134
|
+
return found.length === 0 ? undefined : found.flat();
|
|
135
|
+
};
|
|
136
|
+
var membersOfNamedType = (file, name) => {
|
|
137
|
+
const text = readSource(file);
|
|
138
|
+
return text === undefined ? undefined : membersIn(text, name, 0);
|
|
139
|
+
};
|
|
140
|
+
var STATE_REFERENCE = /^(?:React\.)?(?:Dispatch|SetStateAction)\b|^Function$/;
|
|
141
|
+
var UNDECLARED = new Set(["any", "unknown", "object", "{}"]);
|
|
142
|
+
var unwrapped = (type) => {
|
|
143
|
+
let current = type.trim();
|
|
144
|
+
while (current.startsWith("(") && closesAt(current, 0) === current.length - 1) {
|
|
145
|
+
current = current.slice(1, -1).trim();
|
|
146
|
+
}
|
|
147
|
+
return current;
|
|
148
|
+
};
|
|
149
|
+
var isFunctionType = (type) => {
|
|
150
|
+
const from = type.startsWith("<") ? closesAt(type, 0) + 1 : 0;
|
|
151
|
+
if (type[from] !== "(")
|
|
152
|
+
return false;
|
|
153
|
+
const close = closesAt(type, from);
|
|
154
|
+
return close !== -1 && /^\s*=>/.test(type.slice(close + 1));
|
|
155
|
+
};
|
|
156
|
+
var callableOrUndeclared = (type) => {
|
|
157
|
+
const alternatives = splitTop(unwrapped(type), new Set(["|"])).map(unwrapped);
|
|
158
|
+
if (alternatives.some((one) => isFunctionType(one) || STATE_REFERENCE.test(one)))
|
|
159
|
+
return "handler";
|
|
160
|
+
return alternatives.some((one) => UNDECLARED.has(one.replaceAll(/\s+/g, ""))) ? "undeclared" : undefined;
|
|
161
|
+
};
|
|
162
|
+
export {
|
|
163
|
+
callableOrUndeclared,
|
|
164
|
+
membersOfNamedType
|
|
165
|
+
};
|