@lariocpt/flawless-react 0.0.0-stage → 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.
Files changed (143) hide show
  1. package/.agents/skills/flawless-a11y/SKILL.md +113 -0
  2. package/.agents/skills/flawless-api/SKILL.md +159 -0
  3. package/.agents/skills/flawless-api-fixtures/SKILL.md +144 -0
  4. package/.agents/skills/flawless-e2e/SKILL.md +155 -0
  5. package/.agents/skills/flawless-expo/SKILL.md +283 -0
  6. package/.agents/skills/flawless-findings/SKILL.md +116 -0
  7. package/.agents/skills/flawless-load/SKILL.md +112 -0
  8. package/.agents/skills/flawless-nextjs/SKILL.md +198 -0
  9. package/.agents/skills/flawless-perf-harness/SKILL.md +158 -0
  10. package/.agents/skills/flawless-perf-static/SKILL.md +131 -0
  11. package/.agents/skills/flawless-react/SKILL.md +403 -0
  12. package/.agents/skills/flawless-react-rules/SKILL.md +96 -0
  13. package/.agents/skills/flawless-regen/SKILL.md +135 -0
  14. package/.agents/skills/flawless-unit-browser/SKILL.md +178 -0
  15. package/LICENSE +21 -0
  16. package/README.md +608 -2
  17. package/bin/flawless-react +4 -0
  18. package/package.json +67 -4
  19. package/scripts/postinstall.d.mts +46 -0
  20. package/scripts/postinstall.mjs +127 -0
  21. package/src/analyse/authShims.ts +220 -0
  22. package/src/analyse/bootability.ts +257 -0
  23. package/src/analyse/childBridge.ts +302 -0
  24. package/src/analyse/childrenIntoSlot.ts +66 -0
  25. package/src/analyse/compoundFamily.ts +100 -0
  26. package/src/analyse/dataExports.ts +200 -0
  27. package/src/analyse/extractChildAttrs.ts +112 -0
  28. package/src/analyse/extractDataDeps.ts +857 -0
  29. package/src/analyse/extractDisclosures.ts +186 -0
  30. package/src/analyse/extractFetchCalls.ts +81 -0
  31. package/src/analyse/extractFlows.ts +239 -0
  32. package/src/analyse/extractGuards.ts +270 -0
  33. package/src/analyse/extractHooks.ts +237 -0
  34. package/src/analyse/extractInteractions.ts +1421 -0
  35. package/src/analyse/extractLogging.ts +45 -0
  36. package/src/analyse/extractNativeA11y.ts +138 -0
  37. package/src/analyse/extractPredicates.ts +784 -0
  38. package/src/analyse/extractPropUsage.ts +162 -0
  39. package/src/analyse/extractProps.ts +1212 -0
  40. package/src/analyse/extractRenderMode.ts +165 -0
  41. package/src/analyse/extractRootTag.ts +117 -0
  42. package/src/analyse/extractState.ts +361 -0
  43. package/src/analyse/extractStaticText.ts +212 -0
  44. package/src/analyse/extractVariants.ts +186 -0
  45. package/src/analyse/harvestApiEndpoints.ts +284 -0
  46. package/src/analyse/hostProps.ts +185 -0
  47. package/src/analyse/hostTag.ts +72 -0
  48. package/src/analyse/htmlContentModel.ts +94 -0
  49. package/src/analyse/index.ts +744 -0
  50. package/src/analyse/mountPathCalls.ts +442 -0
  51. package/src/analyse/nativeContentModel.ts +129 -0
  52. package/src/analyse/nativeDialect.ts +83 -0
  53. package/src/analyse/nativeProps.ts +339 -0
  54. package/src/analyse/ownReturns.ts +31 -0
  55. package/src/analyse/parseFile.ts +919 -0
  56. package/src/analyse/pos.ts +8 -0
  57. package/src/analyse/providerWraps.ts +1977 -0
  58. package/src/analyse/pureModule.ts +88 -0
  59. package/src/analyse/reexportTable.ts +148 -0
  60. package/src/analyse/refs.ts +195 -0
  61. package/src/analyse/renderPath.ts +441 -0
  62. package/src/analyse/resolveDepReturns.ts +370 -0
  63. package/src/analyse/resolveIndexKeys.ts +98 -0
  64. package/src/analyse/resolveModule.ts +431 -0
  65. package/src/analyse/resolveTagRootTags.ts +103 -0
  66. package/src/analyse/serverActionReturns.ts +213 -0
  67. package/src/analyse/sourceCache.ts +263 -0
  68. package/src/analyse/spreadSiblings.ts +98 -0
  69. package/src/analyse/walkChildren.ts +98 -0
  70. package/src/analyse/zodSchema.ts +424 -0
  71. package/src/bin.ts +23 -0
  72. package/src/changed.ts +221 -0
  73. package/src/cli.ts +560 -0
  74. package/src/cliFlags.ts +281 -0
  75. package/src/core.ts +1229 -0
  76. package/src/docLine.ts +122 -0
  77. package/src/emitPaths.ts +51 -0
  78. package/src/expo/detect.ts +184 -0
  79. package/src/expo/refusal.ts +62 -0
  80. package/src/expo/routes.ts +145 -0
  81. package/src/index.ts +95 -0
  82. package/src/mcp/analysisTools.ts +720 -0
  83. package/src/mcp/infoTools.ts +135 -0
  84. package/src/mcp/protocol.ts +130 -0
  85. package/src/mcp/run.ts +269 -0
  86. package/src/mcp/runTools.ts +362 -0
  87. package/src/mcp/server.ts +279 -0
  88. package/src/mcp/spec.ts +86 -0
  89. package/src/mcp/support.ts +268 -0
  90. package/src/mcp/tools.ts +75 -0
  91. package/src/nextjs/apiRoutes.ts +116 -0
  92. package/src/nextjs/detect.ts +397 -0
  93. package/src/nextjs/emit.ts +106 -0
  94. package/src/nextjs/liveSpec.ts +79 -0
  95. package/src/nextjs/mswHandlers.ts +55 -0
  96. package/src/nextjs/providerWrapper.ts +603 -0
  97. package/src/nextjs/routes.ts +86 -0
  98. package/src/nextjs/swagger.ts +32 -0
  99. package/src/packageJson.ts +414 -0
  100. package/src/platform.ts +18 -0
  101. package/src/prompt.ts +132 -0
  102. package/src/reactCompiler.ts +165 -0
  103. package/src/reporters/findings.ts +156 -0
  104. package/src/reporters/report.ts +463 -0
  105. package/src/runner/runTests.ts +402 -0
  106. package/src/scaffold/adoptStyle.ts +181 -0
  107. package/src/scaffold/index.ts +253 -0
  108. package/src/scaffold/openapi.ts +183 -0
  109. package/src/scaffold/templates.ts +2379 -0
  110. package/src/scaffold-tests/index.ts +463 -0
  111. package/src/scaffold-tests/mockValues.ts +208 -0
  112. package/src/scaffold-tests/mocks.ts +794 -0
  113. package/src/scaffold-tests/nativeIndex.ts +52 -0
  114. package/src/scaffold-tests/nativeTemplates.ts +1528 -0
  115. package/src/scaffold-tests/postDispatch.ts +152 -0
  116. package/src/scaffold-tests/registry.ts +443 -0
  117. package/src/scaffold-tests/selectors.ts +493 -0
  118. package/src/scaffold-tests/selectorsNative.ts +273 -0
  119. package/src/scaffold-tests/stamp.ts +79 -0
  120. package/src/scaffold-tests/templates.ts +3578 -0
  121. package/src/skills/cli.ts +248 -0
  122. package/src/skills/compat.ts +166 -0
  123. package/src/skills/data.ts +192 -0
  124. package/src/skills/md.d.ts +7 -0
  125. package/src/skills/menu.ts +55 -0
  126. package/src/skills/spec.ts +227 -0
  127. package/src/suiteScripts.ts +110 -0
  128. package/src/tasks/a11yNative.ts +281 -0
  129. package/src/tasks/a11yScan.ts +67 -0
  130. package/src/tasks/biomeRunner.ts +301 -0
  131. package/src/tasks/biomeSweep.ts +166 -0
  132. package/src/tasks/e2eExpo.ts +436 -0
  133. package/src/tasks/e2eRoutes.ts +756 -0
  134. package/src/tasks/eslintReactRules.ts +67 -0
  135. package/src/tasks/index.ts +24 -0
  136. package/src/tasks/loadExpo.ts +273 -0
  137. package/src/tasks/loadPipes.ts +750 -0
  138. package/src/tasks/perfHarness.ts +145 -0
  139. package/src/tasks/staticPerfAnalysis.ts +510 -0
  140. package/src/tasks/types.ts +1 -0
  141. package/src/tasks/unitAndBrowser.ts +462 -0
  142. package/src/types.ts +1419 -0
  143. package/src/version.ts +12 -0
@@ -0,0 +1,45 @@
1
+ import ts from "typescript";
2
+
3
+ /**
4
+ * Whether the component reports errors itself, and through what.
5
+ *
6
+ * `app/(auth)/error.tsx` is an error boundary: it exists to catch a render
7
+ * failure and log it, which it does from a mount effect. The generated browser
8
+ * spec then asserted "mounts without console errors" and failed it — the
9
+ * harness marking a component wrong for meeting its contract. Three of the
10
+ * dogfood app's error boundaries failed that way.
11
+ *
12
+ * Two shapes count, and the qualification on the second is what keeps this from
13
+ * becoming a name guess:
14
+ *
15
+ * - `console.error(...)` / `console.warn(...)` — unambiguous.
16
+ * - `x.error(...)` / `x.warn(...)` where `x` is an IMPORTED binding. That is a
17
+ * module the component pulled in for the purpose (`logger`, `log`, `pino`),
18
+ * not an arbitrary local whose method happens to share the name — a form
19
+ * object's `.error()`, say, which reaches no console at all.
20
+ *
21
+ * Returns the call as written, so the spec can cite it instead of asserting
22
+ * something it cannot justify.
23
+ */
24
+ export function detectSelfLogging(
25
+ bodyNode: ts.Node,
26
+ importedNames: Set<string>,
27
+ ): string | undefined {
28
+ let found: string | undefined;
29
+ const visit = (n: ts.Node): void => {
30
+ if (found) return;
31
+ if (ts.isCallExpression(n) && ts.isPropertyAccessExpression(n.expression)) {
32
+ const method = n.expression.name.text;
33
+ const target = n.expression.expression;
34
+ if ((method === "error" || method === "warn") && ts.isIdentifier(target)) {
35
+ if (target.text === "console" || importedNames.has(target.text)) {
36
+ found = `${target.text}.${method}()`;
37
+ return;
38
+ }
39
+ }
40
+ }
41
+ ts.forEachChild(n, visit);
42
+ };
43
+ visit(bodyNode);
44
+ return found;
45
+ }
@@ -0,0 +1,138 @@
1
+ import ts from "typescript";
2
+
3
+ /**
4
+ * Attribute-level readers for the native a11y census — the peer of
5
+ * nativeProps.ts on the attribute axis. The census WALK lives in
6
+ * extractInteractions.ts, which owns the tag reader, the hint collector and the
7
+ * label harvest; what lives here is everything that reads a single attributes
8
+ * bag, so the readers stay import-cycle-free and testable on a bare parse.
9
+ *
10
+ * One posture throughout, inherited from nativeProps.ts: a miss costs coverage,
11
+ * a guess costs a false finding. Every reader that meets a value it cannot
12
+ * decide says "unknown" (or reports dynamic presence), and every rule downstream
13
+ * stands down on that answer rather than flagging it.
14
+ */
15
+
16
+ /** The elements whose whole purpose is to be pressed. RN's own <Button> is
17
+ * absent on purpose: it carries an unconditional accessibilityRole in RN's own
18
+ * source (NATIVE_TAG_TO_ROLE's one row) and names itself via `title`. */
19
+ export const PRESS_FAMILY: ReadonlySet<string> = new Set([
20
+ "Pressable",
21
+ "TouchableOpacity",
22
+ "TouchableHighlight",
23
+ "TouchableWithoutFeedback",
24
+ ]);
25
+
26
+ const LABEL_ATTRS = new Set(["accessibilityLabel", "aria-label"]);
27
+ const ROLE_ATTRS = new Set(["accessibilityRole", "role"]);
28
+
29
+ function attrName(a: ts.JsxAttributeLike): string | undefined {
30
+ if (!ts.isJsxAttribute(a)) return undefined;
31
+ return ts.isIdentifier(a.name) ? a.name.text : undefined;
32
+ }
33
+
34
+ /** The string a literal initializer holds — `x="v"` or `x={"v"}` — else undefined. */
35
+ function literalString(init: ts.JsxAttribute["initializer"]): string | undefined {
36
+ if (!init) return undefined;
37
+ if (ts.isStringLiteral(init)) return init.text;
38
+ if (ts.isJsxExpression(init) && init.expression && ts.isStringLiteral(init.expression)) {
39
+ return init.expression.text;
40
+ }
41
+ return undefined;
42
+ }
43
+
44
+ /** Literal true: a bare attribute, `{true}`, or the string "true". */
45
+ function literalTrue(init: ts.JsxAttribute["initializer"]): boolean {
46
+ if (!init) return true;
47
+ if (ts.isJsxExpression(init) && init.expression) {
48
+ return init.expression.kind === ts.SyntaxKind.TrueKeyword;
49
+ }
50
+ return literalString(init) === "true";
51
+ }
52
+
53
+ /** Literal false: `{false}` or the string "false" — i.e. provably NOT hidden. */
54
+ function literalFalse(init: ts.JsxAttribute["initializer"]): boolean {
55
+ if (!init) return false;
56
+ if (ts.isJsxExpression(init) && init.expression) {
57
+ return init.expression.kind === ts.SyntaxKind.FalseKeyword;
58
+ }
59
+ return literalString(init) === "false";
60
+ }
61
+
62
+ /**
63
+ * Whether this element hides itself from the accessibility tree.
64
+ *
65
+ * "yes" needs a literal: `aria-hidden` / `accessibilityElementsHidden` true, or
66
+ * `importantForAccessibility` set to "no-hide-descendants" or "no". The "no"
67
+ * value is Android-only and does not hide descendants — counting it as hidden
68
+ * is deliberate forgiveness: the author plainly addressed the a11y tree here,
69
+ * and a finding against a deliberate (if imperfect) attempt reads as noise.
70
+ *
71
+ * "unknown" means one of the three attributes is present but not decidable
72
+ * (a spread value, a variable). The icon rule stands down on it.
73
+ */
74
+ export function readHiddenFromA11y(attrs: ts.JsxAttributes): "yes" | "unknown" | undefined {
75
+ let unknown = false;
76
+ for (const a of attrs.properties) {
77
+ const name = attrName(a);
78
+ if (!name || !ts.isJsxAttribute(a)) continue;
79
+ if (name === "aria-hidden" || name === "accessibilityElementsHidden") {
80
+ if (literalTrue(a.initializer)) return "yes";
81
+ if (!literalFalse(a.initializer)) unknown = true;
82
+ } else if (name === "importantForAccessibility") {
83
+ const value = literalString(a.initializer);
84
+ if (value === "no-hide-descendants" || value === "no") return "yes";
85
+ if (value === undefined) unknown = true;
86
+ }
87
+ }
88
+ return unknown ? "unknown" : undefined;
89
+ }
90
+
91
+ /** An accessibilityLabel / aria-label is present but not a string literal. */
92
+ export function hasDynamicLabel(attrs: ts.JsxAttributes): boolean {
93
+ return hasDynamicIn(attrs, LABEL_ATTRS);
94
+ }
95
+
96
+ /** An accessibilityRole / role is present but not a string literal. */
97
+ export function hasDynamicRole(attrs: ts.JsxAttributes): boolean {
98
+ return hasDynamicIn(attrs, ROLE_ATTRS);
99
+ }
100
+
101
+ function hasDynamicIn(attrs: ts.JsxAttributes, names: ReadonlySet<string>): boolean {
102
+ for (const a of attrs.properties) {
103
+ const name = attrName(a);
104
+ if (!name || !ts.isJsxAttribute(a)) continue;
105
+ if (names.has(name) && literalString(a.initializer) === undefined) return true;
106
+ }
107
+ return false;
108
+ }
109
+
110
+ /**
111
+ * Every literal role attribute as written, kept per attribute name because the
112
+ * two props validate against DIFFERENT unions — "header" is an
113
+ * accessibilityRole, "heading" is a role, and each is a typo in the other's
114
+ * column.
115
+ */
116
+ export function literalRoleAttrs(
117
+ attrs: ts.JsxAttributes,
118
+ ): Array<{ attr: "accessibilityRole" | "role"; value: string }> {
119
+ const out: Array<{ attr: "accessibilityRole" | "role"; value: string }> = [];
120
+ for (const a of attrs.properties) {
121
+ const name = attrName(a);
122
+ if (!ts.isJsxAttribute(a)) continue;
123
+ if (name !== "accessibilityRole" && name !== "role") continue;
124
+ const value = literalString(a.initializer);
125
+ if (value !== undefined) out.push({ attr: name, value });
126
+ }
127
+ return out;
128
+ }
129
+
130
+ /** A literal accessibilityLabel / aria-label on this attributes bag. */
131
+ export function hasLiteralLabel(attrs: ts.JsxAttributes): boolean {
132
+ for (const a of attrs.properties) {
133
+ const name = attrName(a);
134
+ if (!name || !ts.isJsxAttribute(a)) continue;
135
+ if (LABEL_ATTRS.has(name) && literalString(a.initializer) !== undefined) return true;
136
+ }
137
+ return false;
138
+ }