@decantr/verifier 3.5.5 → 3.6.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/README.md CHANGED
@@ -37,7 +37,7 @@ npm install @decantr/verifier
37
37
  - Next.js static/document outputs are treated as framework-rendered documents instead of requiring a Vite-style `id="root"` mount element
38
38
  - project source audits ignore test, spec, story, fixture, and mock files for production drift warnings such as localhost endpoints and unsafe rendering patterns
39
39
  - project audits emit `COMP001` / `import-existing-component` findings when a production source file locally redeclares a primitive such as `Button`, `Card`, or `Dialog` while an exported reusable primitive already exists under common component paths
40
- - project audits emit `COMP010` / `replace-raw-control-with-local-component` findings when production JSX renders raw controls such as `<button>` or `<input>` while a project-owned primitive already exists
40
+ - project audits emit `COMP010` / `replace-raw-control-with-local-component` findings when production JSX renders generic raw controls such as `<button>` or text-like `<input>` while a project-owned primitive already exists; specialized inputs such as file, hidden, checkbox, radio, color, range, and Dropzone `getInputProps()` controls are not treated as generic `Input` drift
41
41
  - project audits emit behavior-obligation findings when accepted `.decantr/local-patterns.json` patterns declare `behavior_obligations` and production source strongly violates statically checkable dialog/form obligations:
42
42
  - `A11Y010` / `restore-dialog-accessible-name`
43
43
  - `A11Y011` / `restore-label-association`
package/dist/index.js CHANGED
@@ -69,6 +69,18 @@ var RAW_CONTROL_COMPONENT_BY_ELEMENT = {
69
69
  table: "Table",
70
70
  textarea: "Textarea"
71
71
  };
72
+ var NON_GENERIC_INPUT_TYPES = /* @__PURE__ */ new Set([
73
+ "button",
74
+ "checkbox",
75
+ "color",
76
+ "file",
77
+ "hidden",
78
+ "image",
79
+ "radio",
80
+ "range",
81
+ "reset",
82
+ "submit"
83
+ ]);
72
84
  function normalizePath(path) {
73
85
  return path.replace(/\\/g, "/");
74
86
  }
@@ -120,10 +132,14 @@ function jsxAttributeStringValue(node, attributeName) {
120
132
  }
121
133
  return null;
122
134
  }
123
- function isHiddenInput(node) {
135
+ function isSpecializedInput(node) {
124
136
  const tagName = jsxIdentifierName(node.tagName);
125
137
  if (tagName !== "input") return false;
126
- return jsxAttributeStringValue(node, "type")?.toLowerCase() === "hidden";
138
+ const type = jsxAttributeStringValue(node, "type")?.toLowerCase();
139
+ if (type && NON_GENERIC_INPUT_TYPES.has(type)) return true;
140
+ return node.attributes.properties.some(
141
+ (property) => ts.isJsxSpreadAttribute(property) && /\bgetInputProps\s*\(/.test(property.expression.getText())
142
+ );
127
143
  }
128
144
  function isReusableComponentPath(file, name) {
129
145
  const normalized = normalizePath(file);
@@ -222,7 +238,7 @@ function collectRawControls(sourceFile, file) {
222
238
  if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
223
239
  const tagName = jsxIdentifierName(node.tagName);
224
240
  const component = tagName && Object.hasOwn(RAW_CONTROL_COMPONENT_BY_ELEMENT, tagName) ? RAW_CONTROL_COMPONENT_BY_ELEMENT[tagName] : null;
225
- if (component && !isHiddenInput(node)) {
241
+ if (component && !isSpecializedInput(node)) {
226
242
  rawControls.push({
227
243
  element: tagName,
228
244
  component,