Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changeset/rude-nails-jog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
---

`audit-docs`: honour style props a component omits from its props interface. A component that repurposes a style prop drops it via `Omit<SomeStyleProps, 'x'>` and destructures it as an ordinary prop, so it never reaches `extractStyles` — `Board`'s `margin` is the grid gap, not a CSS margin. The audit read the style list named in the `extractStyles` call and knew nothing about the `Omit`, so it demanded the docs list a prop the component does not accept. Tooling only; nothing about the published package changes.
42 changes: 38 additions & 4 deletions scripts/audit-docs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,31 @@ async function readTsxFile(filePath) {
return null;
}

/**
* Style props the component's own props interface drops via
* `Omit<SomeStyleProps, 'a' | 'b'>`.
*
* A component that repurposes a style prop for something else omits it from the
* style-prop interface and destructures it as an ordinary prop, so it never
* reaches `extractStyles` — `Board`'s `margin` is the grid gap, not a CSS
* margin. The `extractStyles` call still names the full style list, so without
* this the audit would demand the docs list a prop the component does not
* accept.
*/
function extractOmittedStyleProps(fileContent) {
const omitted = new Set();
const omitRe = /Omit<\s*(?:\w+\s*\|\s*)*?(\w*(?:StyleProps|BaseProps))\s*,\s*([^>]+?)>/g;
let m;
while ((m = omitRe.exec(fileContent)) !== null) {
const nameRe = /'([^']+)'|"([^"]+)"/g;
let n;
while ((n = nameRe.exec(m[2])) !== null) {
omitted.add(n[1] ?? n[2]);
}
}
return omitted;
}

async function detectStyleProps(componentDir, componentName, verbose) {
const mainFile = path.join(componentDir, `${componentName}.tsx`);
let content;
Expand All @@ -826,8 +851,17 @@ async function detectStyleProps(componentDir, componentName, verbose) {
return null;
}

const omitted = extractOmittedStyleProps(content);
const finish = (props) => {
const unique = [...new Set(props)].filter((prop) => !omitted.has(prop));
if (verbose && omitted.size > 0) {
console.log(` [${componentName}] Omitted style props: ${[...omitted].join(', ')}`);
}
return unique;
};

let result = extractStylePropsFromFile(content);
if (result) return [...new Set(result)];
if (result) return finish(result);

const entries = await fs.readdir(componentDir, { withFileTypes: true });
for (const entry of entries) {
Expand All @@ -841,7 +875,7 @@ async function detectStyleProps(componentDir, componentName, verbose) {
result = extractStylePropsFromFile(fileContent);
if (result) {
if (verbose) console.log(` [${componentName}] Found extractStyles in ${entry.name}`);
return [...new Set(result)];
return finish(result);
}
}

Expand All @@ -853,7 +887,7 @@ async function detectStyleProps(componentDir, componentName, verbose) {
result = extractStylePropsFromFile(file.content);
if (result) {
if (verbose) console.log(` [${componentName}] Found extractStyles via import ${path.relative(ROOT, file.path)}`);
return [...new Set(result)];
return finish(result);
}

const reExportRe = /export\s+\*\s+from\s+'([^']+)'/g;
Expand All @@ -865,7 +899,7 @@ async function detectStyleProps(componentDir, componentName, verbose) {
result = extractStylePropsFromFile(reFile.content);
if (result) {
if (verbose) console.log(` [${componentName}] Found extractStyles via re-export ${path.relative(ROOT, reFile.path)}`);
return [...new Set(result)];
return finish(result);
}
}
}
Expand Down
Loading