Created
January 20, 2023 21:38
-
-
Save coreyward/e70642a41e2181641a817a632a82cd2d to your computer and use it in GitHub Desktop.
Inject JSDoc comments into generated TypeScript Type definition files
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
/** | |
* This script injects JSDoc comments from the JS source files into the type | |
* definition files. This is necessary because the type definition files | |
* generated by TypeScript do not include JSDoc comments. | |
* | |
* @see https://github.com/microsoft/TypeScript/issues/14619 | |
* | |
* The strategy is a bit hacky, but straightforward: | |
* | |
* 1. Recursively walk the output folder looking for .d.ts files | |
* 2. For each .d.ts file, find the corresponding .js file | |
* 3. Read the type definition file and identify function declarations that do | |
* not have JSDoc comments | |
* 4. Read the .js file and find the corresponding function declarations that | |
* have JSDoc comments | |
* 5. Extract matched comments and strip redundant information about types | |
* 6. Inject the comments into the type definition file | |
* | |
* This has some shortcomings. There is no actual parsing or static analysis | |
* going on, so it's possible that some functions or comments will be missed. | |
* It's also plausible that something matches unexpectedly and breaks the type | |
* file. Since the output folder is generated anyways, these are hopefully easy | |
* to remedy issues. | |
* | |
* NOTES: | |
* - You may need to alter the source-file identification. For my purposes, | |
* substituting `.js` in place of `.d.ts` was all that was needed. If you | |
* have `.jsx` source files, you will need to change that. | |
* - This will NOT work with TypeScript source files in a majority of cases. | |
* | |
*/ | |
const fs = require("fs") | |
const path = require("path") | |
const srcFolder = path.join(__dirname, "src") | |
const outputFolder = path.join(__dirname, "dist") | |
const getFiles = (dir, done) => { | |
let results = [] | |
fs.readdirSync(dir).forEach((file) => { | |
file = path.join(dir, file) | |
const stat = fs.statSync(file) | |
if (stat && stat.isDirectory()) { | |
results = results.concat(getFiles(file, done)) | |
} else { | |
results.push(file) | |
} | |
}) | |
return results | |
} | |
getFiles(outputFolder).forEach((file) => { | |
if (!file.endsWith(".d.ts")) return | |
const relativePath = path.relative(outputFolder, file) | |
const srcFile = path.join(srcFolder, relativePath).replace(".d.ts", ".js") | |
if (fs.existsSync(srcFile)) { | |
injectComments(srcFile, file) | |
} | |
}) | |
console.log("Done.") | |
function injectComments(srcFile, typeFile) { | |
const fileData = fs.readFileSync(srcFile, "utf8") | |
const typeData = fs.readFileSync(typeFile, "utf8") | |
const functionDeclarationMatches = Array.from( | |
typeData.matchAll( | |
/(?<!\*\/\n)(?:export|declare) function ([a-zA-Z0-9]+)\(/g | |
) | |
) | |
const output = functionDeclarationMatches.reduce( | |
(output, { 0: matchedText, 1: functionName }) => { | |
const functionDefinitionLinePattern = `(?:export (?:default )?)?(?:const|let|var|function) ${functionName}[^a-zA-Z0-9.]` | |
const pattern = new RegExp( | |
`(\\/\\*\\*\\s*\n([^*]|(\\*(?!\\/)))*\\*\\/)\n${functionDefinitionLinePattern}` | |
) | |
const match = fileData.match(pattern) | |
const jsDocComment = match?.[1] | |
if (jsDocComment) { | |
output = output.replace( | |
matchedText, | |
[stripTypeComments(jsDocComment), matchedText].join("\n") | |
) | |
} | |
return output | |
}, | |
typeData | |
) | |
if (output !== typeData) { | |
console.log(`Updating ${typeFile}`) | |
fs.writeFileSync(typeFile, output) | |
} | |
} | |
function stripTypeComments(comment) { | |
return comment | |
.replaceAll(/^.*?@(param|property|typedef|returns).*$\n/gm, "") | |
.replaceAll(/^[ *]+$\n/gm, "") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment