Created
June 7, 2024 11:12
-
-
Save andflett/f31eb10be457cbd87f79822ea34a7315 to your computer and use it in GitHub Desktop.
docgen.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// We generate a JSON file containing all components' metadata from comments in | |
// JSDoc format within the source code itself, and publish this (versioned) | |
// along side the design system for use in our docs site. | |
var fs = require("fs"); | |
// eslint-disable-next-line no-unused-vars | |
const colors = require("colors"); | |
var read = require("fs-readdir-recursive"); | |
const docgen = require("react-docgen-typescript"); | |
const options = { | |
savePropValueAsString: true, | |
shouldExtractLiteralValuesFromEnum: true, | |
propFilter: (prop, component) => { | |
if (prop.declarations !== undefined && prop.declarations.length > 0) { | |
const hasPropAdditionalDescription = prop.declarations.find( | |
declaration => { | |
return ( | |
// Only show props from our own code, not from HTML, DOM, aria, or | |
// other libraries (with the exception of our radix primitives) | |
!declaration.fileName.includes("node_modules") || | |
declaration.fileName.includes("styled-system") || | |
(declaration.fileName.includes("@radix-ui") && | |
!declaration.fileName.includes("@radix-ui/react-primitive/")) | |
); | |
} | |
); | |
return Boolean(hasPropAdditionalDescription); | |
} | |
return true; | |
}, | |
}; | |
// Find all .ts(x) files in the src directories | |
// Check if the directory exists | |
if (!fs.existsSync("./docs")) { | |
fs.mkdirSync("./docs"); | |
fs.mkdirSync("./docs/internal"); | |
} | |
let components = []; | |
const sourceFolders = [ | |
"./src/internal/components/", | |
"./src/default/components/", | |
]; | |
// Recursively fetch a list of all .tsx files in the source folders for the docgen | |
sourceFolders.forEach(sourceFolder => { | |
let files = read(sourceFolder, function (name, index, dir) { | |
return name.split(".")[1] === "tsx" || name.indexOf(".") === -1; | |
}); | |
files.forEach(file => { | |
components.push({ | |
folder: sourceFolder, | |
fileName: file, | |
scope: sourceFolder.split("/")[2], | |
name: file.split(".")[0].split("/").pop(), | |
}); | |
}); | |
}); | |
// Scope is used to ensure we don't have duplicate names, this will mostly be if | |
// we ever have variants of a component for different themes, e.g. a patient and | |
// internal facing form field | |
console.log("Found " + components.length + " components. Generating docs..."); | |
// We're also generating a navigable index of components for the docs site | |
let index = { | |
Uncategorised: [], | |
}; | |
// Generate docs for each component file | |
components.forEach(component => { | |
console.log( | |
`${"π".blue} Generating docs for ` + | |
component.name + | |
" with scope " + | |
component.scope | |
); | |
// Parse a file for docgen info | |
let docs = docgen.parse(component.folder + component.fileName, options)[0]; | |
if (docs) { | |
let nodocgen = docs.tags?.nodocgen; | |
if (nodocgen !== undefined) { | |
console.log( | |
`${"π«".red} Ignoring ` + docs.displayName + " due to @nodocgen tag" | |
); | |
return; | |
} | |
// Add the component to the index using the category tag (if present in JSDoc | |
// format from source code, e.g. @category Typography) | |
let category = docs.tags?.category; | |
// We still want docs for deprecated components, but we don't want to add | |
// them to the navigation | |
let deprecated = docs.tags?.deprecated; | |
if (!deprecated) { | |
let item = { | |
name: docs.displayName, | |
scope: component.scope, | |
}; | |
// We have a category tag in our JSdocs source, so add it here | |
if (category) { | |
// Category doesn't exist yet | |
if (!index[category]) { | |
index[category] = [item]; | |
} else { | |
index[category].push(item); | |
} | |
} else { | |
// We don't have a category tag in our source code JSdocs, so we'll put it at the top level | |
index.Uncategorised.push(item); | |
} | |
} | |
let fullPath = | |
component.scope && component.scope !== "default" | |
? `${component.scope}/${docs.displayName}` | |
: `${docs.displayName}`; | |
// Pop the generated docs into our package (this will be distributed with the Design System itself) | |
try { | |
fs.writeFileSync( | |
`./docs/${fullPath}.ts`, | |
`const ${docs.displayName} = ${JSON.stringify(docs)}; export default ${ | |
docs.displayName | |
};` | |
); | |
} catch (e) { | |
console.log(`${"x".red} Could not save: ` + e); | |
} | |
console.log(`${"β ".green} ` + docs.displayName); | |
} else { | |
console.log( | |
`${"β".red} No docs could be generated for ` + component.fileName | |
); | |
} | |
}); | |
// Save the index | |
fs.writeFileSync( | |
`./docs/componentIndex.ts`, | |
`export const componentIndex = ${JSON.stringify(index)}` | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment