@lariocpt/flawless-react 0.0.0-stage → 0.3.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/.agents/skills/flawless-a11y/SKILL.md +113 -0
- package/.agents/skills/flawless-api/SKILL.md +159 -0
- package/.agents/skills/flawless-api-fixtures/SKILL.md +144 -0
- package/.agents/skills/flawless-e2e/SKILL.md +155 -0
- package/.agents/skills/flawless-expo/SKILL.md +283 -0
- package/.agents/skills/flawless-findings/SKILL.md +116 -0
- package/.agents/skills/flawless-load/SKILL.md +112 -0
- package/.agents/skills/flawless-nextjs/SKILL.md +198 -0
- package/.agents/skills/flawless-perf-harness/SKILL.md +158 -0
- package/.agents/skills/flawless-perf-static/SKILL.md +131 -0
- package/.agents/skills/flawless-react/SKILL.md +403 -0
- package/.agents/skills/flawless-react-rules/SKILL.md +96 -0
- package/.agents/skills/flawless-regen/SKILL.md +135 -0
- package/.agents/skills/flawless-unit-browser/SKILL.md +178 -0
- package/LICENSE +21 -0
- package/README.md +611 -2
- package/bin/flawless-react +4 -0
- package/package.json +67 -4
- package/scripts/postinstall.d.mts +46 -0
- package/scripts/postinstall.mjs +127 -0
- package/src/analyse/authShims.ts +220 -0
- package/src/analyse/bootability.ts +257 -0
- package/src/analyse/childBridge.ts +302 -0
- package/src/analyse/childrenIntoSlot.ts +66 -0
- package/src/analyse/compoundFamily.ts +100 -0
- package/src/analyse/dataExports.ts +200 -0
- package/src/analyse/extractChildAttrs.ts +112 -0
- package/src/analyse/extractDataDeps.ts +857 -0
- package/src/analyse/extractDisclosures.ts +186 -0
- package/src/analyse/extractFetchCalls.ts +81 -0
- package/src/analyse/extractFlows.ts +239 -0
- package/src/analyse/extractGuards.ts +270 -0
- package/src/analyse/extractHooks.ts +237 -0
- package/src/analyse/extractInteractions.ts +1421 -0
- package/src/analyse/extractLogging.ts +45 -0
- package/src/analyse/extractNativeA11y.ts +138 -0
- package/src/analyse/extractPredicates.ts +784 -0
- package/src/analyse/extractPropUsage.ts +162 -0
- package/src/analyse/extractProps.ts +1212 -0
- package/src/analyse/extractRenderMode.ts +165 -0
- package/src/analyse/extractRootTag.ts +117 -0
- package/src/analyse/extractState.ts +361 -0
- package/src/analyse/extractStaticText.ts +212 -0
- package/src/analyse/extractVariants.ts +186 -0
- package/src/analyse/harvestApiEndpoints.ts +284 -0
- package/src/analyse/hostProps.ts +185 -0
- package/src/analyse/hostTag.ts +72 -0
- package/src/analyse/htmlContentModel.ts +94 -0
- package/src/analyse/index.ts +744 -0
- package/src/analyse/mountPathCalls.ts +442 -0
- package/src/analyse/nativeContentModel.ts +129 -0
- package/src/analyse/nativeDialect.ts +83 -0
- package/src/analyse/nativeProps.ts +339 -0
- package/src/analyse/ownReturns.ts +31 -0
- package/src/analyse/parseFile.ts +919 -0
- package/src/analyse/pos.ts +8 -0
- package/src/analyse/providerWraps.ts +1977 -0
- package/src/analyse/pureModule.ts +88 -0
- package/src/analyse/reexportTable.ts +148 -0
- package/src/analyse/refs.ts +195 -0
- package/src/analyse/renderPath.ts +441 -0
- package/src/analyse/resolveDepReturns.ts +370 -0
- package/src/analyse/resolveIndexKeys.ts +98 -0
- package/src/analyse/resolveModule.ts +431 -0
- package/src/analyse/resolveTagRootTags.ts +103 -0
- package/src/analyse/serverActionReturns.ts +213 -0
- package/src/analyse/sourceCache.ts +263 -0
- package/src/analyse/spreadSiblings.ts +98 -0
- package/src/analyse/walkChildren.ts +98 -0
- package/src/analyse/zodSchema.ts +424 -0
- package/src/bin.ts +23 -0
- package/src/changed.ts +221 -0
- package/src/cli.ts +560 -0
- package/src/cliFlags.ts +281 -0
- package/src/core.ts +1229 -0
- package/src/docLine.ts +122 -0
- package/src/emitPaths.ts +51 -0
- package/src/expo/detect.ts +184 -0
- package/src/expo/refusal.ts +62 -0
- package/src/expo/routes.ts +145 -0
- package/src/index.ts +95 -0
- package/src/mcp/analysisTools.ts +720 -0
- package/src/mcp/infoTools.ts +135 -0
- package/src/mcp/protocol.ts +130 -0
- package/src/mcp/run.ts +269 -0
- package/src/mcp/runTools.ts +362 -0
- package/src/mcp/server.ts +279 -0
- package/src/mcp/spec.ts +86 -0
- package/src/mcp/support.ts +268 -0
- package/src/mcp/tools.ts +75 -0
- package/src/nextjs/apiRoutes.ts +116 -0
- package/src/nextjs/detect.ts +397 -0
- package/src/nextjs/emit.ts +106 -0
- package/src/nextjs/liveSpec.ts +79 -0
- package/src/nextjs/mswHandlers.ts +55 -0
- package/src/nextjs/providerWrapper.ts +603 -0
- package/src/nextjs/routes.ts +86 -0
- package/src/nextjs/swagger.ts +32 -0
- package/src/packageJson.ts +414 -0
- package/src/platform.ts +18 -0
- package/src/prompt.ts +132 -0
- package/src/reactCompiler.ts +165 -0
- package/src/reporters/findings.ts +156 -0
- package/src/reporters/report.ts +463 -0
- package/src/runner/runTests.ts +402 -0
- package/src/scaffold/adoptStyle.ts +181 -0
- package/src/scaffold/index.ts +253 -0
- package/src/scaffold/openapi.ts +183 -0
- package/src/scaffold/templates.ts +2379 -0
- package/src/scaffold-tests/index.ts +463 -0
- package/src/scaffold-tests/mockValues.ts +208 -0
- package/src/scaffold-tests/mocks.ts +794 -0
- package/src/scaffold-tests/nativeIndex.ts +52 -0
- package/src/scaffold-tests/nativeTemplates.ts +1528 -0
- package/src/scaffold-tests/postDispatch.ts +152 -0
- package/src/scaffold-tests/registry.ts +443 -0
- package/src/scaffold-tests/selectors.ts +493 -0
- package/src/scaffold-tests/selectorsNative.ts +273 -0
- package/src/scaffold-tests/stamp.ts +79 -0
- package/src/scaffold-tests/templates.ts +3578 -0
- package/src/skills/cli.ts +248 -0
- package/src/skills/compat.ts +166 -0
- package/src/skills/data.ts +192 -0
- package/src/skills/md.d.ts +7 -0
- package/src/skills/menu.ts +55 -0
- package/src/skills/spec.ts +227 -0
- package/src/suiteScripts.ts +110 -0
- package/src/tasks/a11yNative.ts +281 -0
- package/src/tasks/a11yScan.ts +67 -0
- package/src/tasks/biomeRunner.ts +301 -0
- package/src/tasks/biomeSweep.ts +166 -0
- package/src/tasks/e2eExpo.ts +436 -0
- package/src/tasks/e2eRoutes.ts +756 -0
- package/src/tasks/eslintReactRules.ts +67 -0
- package/src/tasks/index.ts +24 -0
- package/src/tasks/loadExpo.ts +273 -0
- package/src/tasks/loadPipes.ts +750 -0
- package/src/tasks/perfHarness.ts +145 -0
- package/src/tasks/staticPerfAnalysis.ts +510 -0
- package/src/tasks/types.ts +1 -0
- package/src/tasks/unitAndBrowser.ts +462 -0
- package/src/types.ts +1419 -0
- package/src/version.ts +12 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
import type { PropsShape, PropValue } from "../types.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A curated, closed table of React Native host elements — the peer of
|
|
5
|
+
* hostProps.ts, and deliberately NOT a model of React Native.
|
|
6
|
+
*
|
|
7
|
+
* Keyed by SPECIFIER then by module-side export name, because that is the only
|
|
8
|
+
* evidence there is: `<View>` is a host element because it came from
|
|
9
|
+
* "react-native", and `<View>` imported from "./my-ui" is a component that
|
|
10
|
+
* happens to share the name. Keying on the bare tag would make the second one a
|
|
11
|
+
* host element too.
|
|
12
|
+
*
|
|
13
|
+
* `BASE` is empty, and that is the honest answer rather than an oversight.
|
|
14
|
+
* React Native has no universal prop whose synthesised default is both valid and
|
|
15
|
+
* useful. `className` belongs to NativeWind, not to RN — synthesising it would
|
|
16
|
+
* encode a build-tool assumption into the analysis. `style` mocks to `{}`, which
|
|
17
|
+
* is inert. What this table supplies that hostProps.ts also supplies is
|
|
18
|
+
* `acceptsChildren` and `handlers`; the prop SHAPE is genuinely thinner here,
|
|
19
|
+
* and pretending otherwise is how you get an assertion that cannot fail.
|
|
20
|
+
*
|
|
21
|
+
* Three invariants are baked in, each because breaking it produces a *false test
|
|
22
|
+
* result* rather than missing coverage. tests/nativeProps.test.ts pins all three.
|
|
23
|
+
*
|
|
24
|
+
* 1. Never synthesise `value` (except Switch, which has no `defaultValue`).
|
|
25
|
+
* `<TextInput value={x} />` with no `onChangeText` is React Native's
|
|
26
|
+
* controlled-input trap: the input is frozen and `fireEvent.changeText` is a
|
|
27
|
+
* silent no-op that still passes. Strictly worse than the DOM version, where
|
|
28
|
+
* React at least logs a controlled-input warning. Always `defaultValue`.
|
|
29
|
+
* 2. Never synthesise `placeholder` / `testID` / `accessibilityLabel` /
|
|
30
|
+
* `accessibilityHint` / `nativeID`. Those five ARE the entire locator surface
|
|
31
|
+
* of @testing-library/react-native. An empty default makes
|
|
32
|
+
* `getByPlaceholderText("")` throw; a non-empty invented one makes the spec
|
|
33
|
+
* assert a handle the app does not have.
|
|
34
|
+
* 3. Never synthesise `accessibilityRole` / `role`. RNTL resolves role from that
|
|
35
|
+
* prop ALONE, so an invented role makes `getByRole` pass on a fiction. This is
|
|
36
|
+
* the structural analogue of the DOM table's "`type` is always a real literal":
|
|
37
|
+
* the one prop a query resolves against must never be a placeholder.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
export interface NativeElementProps {
|
|
41
|
+
shape: PropsShape;
|
|
42
|
+
variantOptions?: Record<string, PropValue[]>;
|
|
43
|
+
acceptsChildren: boolean;
|
|
44
|
+
/** Handler props the scaffolder stubs. */
|
|
45
|
+
handlers: string[];
|
|
46
|
+
/** The only element a raw string may legally be rendered inside. */
|
|
47
|
+
rendersText?: true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const NONE: PropsShape = {};
|
|
51
|
+
|
|
52
|
+
function el(
|
|
53
|
+
shape: PropsShape,
|
|
54
|
+
acceptsChildren: boolean,
|
|
55
|
+
handlers: string[],
|
|
56
|
+
extra: Partial<NativeElementProps> = {},
|
|
57
|
+
): NativeElementProps {
|
|
58
|
+
return { shape, acceptsChildren, handlers, ...extra };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const REACT_NATIVE: Record<string, NativeElementProps> = {
|
|
62
|
+
View: el({ ...NONE }, true, []),
|
|
63
|
+
// `onPress` on Text is real and works — RN routes it through the text's own
|
|
64
|
+
// press responder. It is not a Pressable, but it IS drivable.
|
|
65
|
+
Text: el({ ...NONE }, true, ["onPress"], { rendersText: true }),
|
|
66
|
+
Pressable: el({ disabled: false }, true, ["onPress", "onLongPress"]),
|
|
67
|
+
TouchableOpacity: el({ disabled: false }, true, ["onPress", "onLongPress"]),
|
|
68
|
+
TouchableHighlight: el({ disabled: false }, true, ["onPress", "onLongPress"]),
|
|
69
|
+
TouchableWithoutFeedback: el({ ...NONE }, true, ["onPress", "onLongPress"]),
|
|
70
|
+
// defaultValue, never value — invariant 1.
|
|
71
|
+
TextInput: el({ defaultValue: "", editable: true }, false, [
|
|
72
|
+
"onChangeText",
|
|
73
|
+
"onFocus",
|
|
74
|
+
"onBlur",
|
|
75
|
+
"onSubmitEditing",
|
|
76
|
+
]),
|
|
77
|
+
ScrollView: el({ ...NONE }, true, []),
|
|
78
|
+
FlatList: el({ ...NONE }, false, ["onRefresh"]),
|
|
79
|
+
SectionList: el({ ...NONE }, false, ["onRefresh"]),
|
|
80
|
+
KeyboardAvoidingView: el({ ...NONE }, true, []),
|
|
81
|
+
ActivityIndicator: el({ animating: true, size: "small" }, false, [], {
|
|
82
|
+
variantOptions: { size: ["small", "large"] },
|
|
83
|
+
}),
|
|
84
|
+
RefreshControl: el({ refreshing: false }, false, ["onRefresh"]),
|
|
85
|
+
// The one place `value` appears, and it is forced: RN's Switch has no
|
|
86
|
+
// defaultValue, so value + the mandatory onValueChange is the only
|
|
87
|
+
// representable uncontrolled state. tests/nativeProps.test.ts allows it BY NAME
|
|
88
|
+
// with this reason, rather than weakening invariant 1 for everything.
|
|
89
|
+
Switch: el({ value: false }, false, ["onValueChange"]),
|
|
90
|
+
Image: el({ ...NONE }, false, []),
|
|
91
|
+
ImageBackground: el({ ...NONE }, true, []),
|
|
92
|
+
Modal: el({ visible: true }, true, ["onRequestClose"]),
|
|
93
|
+
// RN's own Button is the ONLY element in this table that sets an
|
|
94
|
+
// accessibilityRole unconditionally in its own source. See NATIVE_TAG_TO_ROLE.
|
|
95
|
+
Button: el({ title: "Button", disabled: false }, false, ["onPress"]),
|
|
96
|
+
SafeAreaView: el({ ...NONE }, true, []),
|
|
97
|
+
StatusBar: el({ ...NONE }, false, []),
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const SAFE_AREA_CONTEXT: Record<string, NativeElementProps> = {
|
|
101
|
+
SafeAreaView: el({ ...NONE }, true, []),
|
|
102
|
+
SafeAreaProvider: el({ ...NONE }, true, []),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @expo/vector-icons exports one component per glyph family. They render an
|
|
107
|
+
* icon and no text, which is what makes an icon-only Pressable unaddressable —
|
|
108
|
+
* so they have to be in the table for that to be PROVABLE rather than guessed
|
|
109
|
+
* from the tag name. Exported for the native a11y task, whose icon rule needs
|
|
110
|
+
* "is this a vector icon" answered from the same table the label harvest uses.
|
|
111
|
+
*/
|
|
112
|
+
export const VECTOR_ICON_FAMILIES = [
|
|
113
|
+
"Ionicons",
|
|
114
|
+
"MaterialIcons",
|
|
115
|
+
"MaterialCommunityIcons",
|
|
116
|
+
"FontAwesome",
|
|
117
|
+
"FontAwesome5",
|
|
118
|
+
"FontAwesome6",
|
|
119
|
+
"Feather",
|
|
120
|
+
"AntDesign",
|
|
121
|
+
"Entypo",
|
|
122
|
+
"EvilIcons",
|
|
123
|
+
"Foundation",
|
|
124
|
+
"Octicons",
|
|
125
|
+
"SimpleLineIcons",
|
|
126
|
+
"Zocial",
|
|
127
|
+
] as const;
|
|
128
|
+
|
|
129
|
+
const VECTOR_ICONS: Record<string, NativeElementProps> = Object.fromEntries(
|
|
130
|
+
VECTOR_ICON_FAMILIES.map((name) => [name, el({ name: "help-circle", size: 24 }, false, [])]),
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
const TABLE: Readonly<Record<string, Readonly<Record<string, NativeElementProps>>>> = {
|
|
134
|
+
"react-native": REACT_NATIVE,
|
|
135
|
+
"react-native-safe-area-context": SAFE_AREA_CONTEXT,
|
|
136
|
+
"@expo/vector-icons": VECTOR_ICONS,
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const NATIVE_ELEMENTS = TABLE;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Tag -> implicit accessibilityRole. **One row, and pinned at one row.**
|
|
143
|
+
*
|
|
144
|
+
* @testing-library/react-native's `getRole()` returns the explicit
|
|
145
|
+
* `role`/`accessibilityRole` prop, else "text" for Text elements, else "none".
|
|
146
|
+
* There is no Pressable -> button mapping anywhere in the library. So a fuller
|
|
147
|
+
* table here would not improve queries — it would emit `getByRole("button")`
|
|
148
|
+
* against elements that carry no role, which matches nothing. On the dogfood app
|
|
149
|
+
* that is 82 confidently wrong queries.
|
|
150
|
+
*
|
|
151
|
+
* A row may be added only when the element's own React Native source sets the
|
|
152
|
+
* role UNCONDITIONALLY. RN's `<Button>` does; Pressable, Touchable* and TextInput
|
|
153
|
+
* do not. tests/nativeProps.test.ts asserts the size of this table, because
|
|
154
|
+
* "it is nearly empty" is a claim that decays silently.
|
|
155
|
+
*/
|
|
156
|
+
export const NATIVE_TAG_TO_ROLE: Readonly<Record<string, string>> = { Button: "button" };
|
|
157
|
+
|
|
158
|
+
/** Elements that render no text, so an element containing only these has no accessible name. */
|
|
159
|
+
export const TEXTLESS_ELEMENTS: ReadonlySet<string> = new Set([
|
|
160
|
+
...VECTOR_ICON_FAMILIES,
|
|
161
|
+
"ActivityIndicator",
|
|
162
|
+
"Image",
|
|
163
|
+
"Switch",
|
|
164
|
+
"RefreshControl",
|
|
165
|
+
"StatusBar",
|
|
166
|
+
]);
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* The values React Native accepts for `accessibilityRole`, copied from the
|
|
170
|
+
* `AccessibilityRole` union in react-native 0.81.5's own
|
|
171
|
+
* Libraries/Components/View/ViewAccessibility.js.
|
|
172
|
+
*
|
|
173
|
+
* Copied rather than derived at runtime, for the same reason the element table
|
|
174
|
+
* above is closed: this repo does not depend on react-native, the analysed app
|
|
175
|
+
* may not have it installed when the sweep runs, and a finding set that varies
|
|
176
|
+
* with the target's install state is a nondeterministic report. A closed copy
|
|
177
|
+
* can only go stale, and a membership + size pin in
|
|
178
|
+
* tests/nativeElementModel.test.ts makes staleness a red test at the next RN
|
|
179
|
+
* bump rather than a silent drift.
|
|
180
|
+
*
|
|
181
|
+
* Why the rule exists at all: RN widens the union with `| string` (both the
|
|
182
|
+
* Flow source and the shipped .d.ts), so a typo'd role type-checks clean and
|
|
183
|
+
* then announces NOTHING on either platform. tsc will never catch it; this is
|
|
184
|
+
* the one place that can.
|
|
185
|
+
*/
|
|
186
|
+
export const VALID_ACCESSIBILITY_ROLES: ReadonlySet<string> = new Set([
|
|
187
|
+
"none",
|
|
188
|
+
"button",
|
|
189
|
+
"dropdownlist",
|
|
190
|
+
"togglebutton",
|
|
191
|
+
"link",
|
|
192
|
+
"search",
|
|
193
|
+
"image",
|
|
194
|
+
"keyboardkey",
|
|
195
|
+
"text",
|
|
196
|
+
"adjustable",
|
|
197
|
+
"imagebutton",
|
|
198
|
+
"header",
|
|
199
|
+
"summary",
|
|
200
|
+
"alert",
|
|
201
|
+
"checkbox",
|
|
202
|
+
"combobox",
|
|
203
|
+
"menu",
|
|
204
|
+
"menubar",
|
|
205
|
+
"menuitem",
|
|
206
|
+
"progressbar",
|
|
207
|
+
"radio",
|
|
208
|
+
"radiogroup",
|
|
209
|
+
"scrollbar",
|
|
210
|
+
"spinbutton",
|
|
211
|
+
"switch",
|
|
212
|
+
"tab",
|
|
213
|
+
"tabbar",
|
|
214
|
+
"tablist",
|
|
215
|
+
"timer",
|
|
216
|
+
"list",
|
|
217
|
+
"toolbar",
|
|
218
|
+
"grid",
|
|
219
|
+
"pager",
|
|
220
|
+
"scrollview",
|
|
221
|
+
"horizontalscrollview",
|
|
222
|
+
"viewgroup",
|
|
223
|
+
"webview",
|
|
224
|
+
"drawerlayout",
|
|
225
|
+
"slidingdrawer",
|
|
226
|
+
"iconmenu",
|
|
227
|
+
]);
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* The values React Native accepts for the web-style `role` prop (RN 0.71+) —
|
|
231
|
+
* the `Role` union from the same ViewAccessibility.js, which is a DIFFERENT
|
|
232
|
+
* domain than `accessibilityRole` ("heading" is a Role, "header" is an
|
|
233
|
+
* AccessibilityRole). The two sets are deliberately separate so the invalid-role
|
|
234
|
+
* rule validates each attribute against its own union rather than the blur of
|
|
235
|
+
* both.
|
|
236
|
+
*/
|
|
237
|
+
export const VALID_ROLES: ReadonlySet<string> = new Set([
|
|
238
|
+
"alert",
|
|
239
|
+
"alertdialog",
|
|
240
|
+
"application",
|
|
241
|
+
"article",
|
|
242
|
+
"banner",
|
|
243
|
+
"button",
|
|
244
|
+
"cell",
|
|
245
|
+
"checkbox",
|
|
246
|
+
"columnheader",
|
|
247
|
+
"combobox",
|
|
248
|
+
"complementary",
|
|
249
|
+
"contentinfo",
|
|
250
|
+
"definition",
|
|
251
|
+
"dialog",
|
|
252
|
+
"directory",
|
|
253
|
+
"document",
|
|
254
|
+
"feed",
|
|
255
|
+
"figure",
|
|
256
|
+
"form",
|
|
257
|
+
"grid",
|
|
258
|
+
"group",
|
|
259
|
+
"heading",
|
|
260
|
+
"img",
|
|
261
|
+
"link",
|
|
262
|
+
"list",
|
|
263
|
+
"listitem",
|
|
264
|
+
"log",
|
|
265
|
+
"main",
|
|
266
|
+
"marquee",
|
|
267
|
+
"math",
|
|
268
|
+
"menu",
|
|
269
|
+
"menubar",
|
|
270
|
+
"menuitem",
|
|
271
|
+
"meter",
|
|
272
|
+
"navigation",
|
|
273
|
+
"none",
|
|
274
|
+
"note",
|
|
275
|
+
"option",
|
|
276
|
+
"presentation",
|
|
277
|
+
"progressbar",
|
|
278
|
+
"radio",
|
|
279
|
+
"radiogroup",
|
|
280
|
+
"region",
|
|
281
|
+
"row",
|
|
282
|
+
"rowgroup",
|
|
283
|
+
"rowheader",
|
|
284
|
+
"scrollbar",
|
|
285
|
+
"searchbox",
|
|
286
|
+
"separator",
|
|
287
|
+
"slider",
|
|
288
|
+
"spinbutton",
|
|
289
|
+
"status",
|
|
290
|
+
"summary",
|
|
291
|
+
"switch",
|
|
292
|
+
"tab",
|
|
293
|
+
"table",
|
|
294
|
+
"tablist",
|
|
295
|
+
"tabpanel",
|
|
296
|
+
"term",
|
|
297
|
+
"timer",
|
|
298
|
+
"toolbar",
|
|
299
|
+
"tooltip",
|
|
300
|
+
"tree",
|
|
301
|
+
"treegrid",
|
|
302
|
+
"treeitem",
|
|
303
|
+
]);
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* The facts for a tag, or undefined when the specifier/name pair is not in the
|
|
307
|
+
* table — which means "this is a component", exactly the state every uppercase
|
|
308
|
+
* tag is in today. Never a guess from the name.
|
|
309
|
+
*/
|
|
310
|
+
export function nativeElementFor(
|
|
311
|
+
specifier: string,
|
|
312
|
+
exportedName: string,
|
|
313
|
+
): NativeElementProps | undefined {
|
|
314
|
+
const known = TABLE[specifier]?.[exportedName];
|
|
315
|
+
if (!known) return undefined;
|
|
316
|
+
return { ...known, shape: { ...known.shape } };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Every element name in the table, for the tests and the ambiguity census. */
|
|
320
|
+
export function nativeElementNames(): string[] {
|
|
321
|
+
return [...new Set(Object.values(TABLE).flatMap((m) => Object.keys(m)))].sort();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Whether a native element accepts children, by resolved element NAME.
|
|
326
|
+
*
|
|
327
|
+
* For consumers that hold a `ResolvedHostTag` (name + dialect) but not the
|
|
328
|
+
* specifier it came from. Safe because the table's names are unambiguous across
|
|
329
|
+
* specifiers: `SafeAreaView` appears under both "react-native" and
|
|
330
|
+
* "react-native-safe-area-context" with the same answer. A test pins that.
|
|
331
|
+
*/
|
|
332
|
+
export function nativeAcceptsChildrenByName(name: string): boolean {
|
|
333
|
+
for (const byName of Object.values(TABLE)) {
|
|
334
|
+
const found = byName[name];
|
|
335
|
+
if (found) return found.acceptsChildren;
|
|
336
|
+
}
|
|
337
|
+
// Unknown native-dialect tag: a component, and components take children.
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The block's own return statements — a nested function's returns are its own.
|
|
5
|
+
*
|
|
6
|
+
* Shared by the render-mode detector (parseFile.ts) and the server-action
|
|
7
|
+
* return extractor (serverActionReturns.ts): both ask "what does THIS function
|
|
8
|
+
* yield", and both would be lied to by a closure's returns. Accessors and
|
|
9
|
+
* constructors are function boundaries too — a getter's return is not the
|
|
10
|
+
* enclosing function's.
|
|
11
|
+
*/
|
|
12
|
+
export function ownReturns(body: ts.Block): ts.ReturnStatement[] {
|
|
13
|
+
const out: ts.ReturnStatement[] = [];
|
|
14
|
+
const visit = (n: ts.Node): void => {
|
|
15
|
+
if (
|
|
16
|
+
ts.isArrowFunction(n) ||
|
|
17
|
+
ts.isFunctionExpression(n) ||
|
|
18
|
+
ts.isFunctionDeclaration(n) ||
|
|
19
|
+
ts.isMethodDeclaration(n) ||
|
|
20
|
+
ts.isGetAccessorDeclaration(n) ||
|
|
21
|
+
ts.isSetAccessorDeclaration(n) ||
|
|
22
|
+
ts.isConstructorDeclaration(n)
|
|
23
|
+
) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (ts.isReturnStatement(n)) out.push(n);
|
|
27
|
+
ts.forEachChild(n, visit);
|
|
28
|
+
};
|
|
29
|
+
ts.forEachChild(body, visit);
|
|
30
|
+
return out;
|
|
31
|
+
}
|