Skip to content

Instantly share code, notes, and snippets.

@andflett
Created June 7, 2024 11:12
Show Gist options
  • Save andflett/f31eb10be457cbd87f79822ea34a7315 to your computer and use it in GitHub Desktop.
Save andflett/f31eb10be457cbd87f79822ea34a7315 to your computer and use it in GitHub Desktop.
docgen.js
// 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