Merge pull request #2061 from niktek/bugfix/jsdocs-not-being-inserted-for-all-components

Multi script block handling for jsdocs
This commit is contained in:
Nik
2023-09-26 21:06:19 +10:00
committed by GitHub

View File

@@ -1,5 +1,6 @@
#!/usr/bin/env node #!/usr/bin/env node
import { readFileSync, writeFileSync } from 'fs'; import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import fs from 'fs'; import fs from 'fs';
import ts from 'typescript'; import ts from 'typescript';
@@ -14,37 +15,59 @@ function extractScriptsFromComponents(dir) {
const leadingCharsToStrip = 8; //strip the src/lib/ from the filenames when adding it to the filesToProps mapping const leadingCharsToStrip = 8; //strip the src/lib/ from the filenames when adding it to the filesToProps mapping
const list = fs.readdirSync(dir); const list = fs.readdirSync(dir);
list.forEach((file) => { list.forEach((file) => {
file = dir + '/' + file; file = join(dir, file);
const stat = fs.statSync(file); const stat = fs.statSync(file);
if (stat && stat.isDirectory()) { if (stat && stat.isDirectory()) {
extractScriptsFromComponents(file); extractScriptsFromComponents(file);
} else { } else {
if (file.endsWith('svelte')) { if (file.endsWith('svelte')) {
const src = readFileSync(file).toString(); const src = readFileSync(file).toString();
// Split out the script block with lang='ts' section so that we can pass it to the TS compiler const scripts = extractScriptBlocks(src);
// there can be multiple script elements e.g. context='module'
const begin = src.indexOf('"ts">\n') + 1;
const script = src.substring(begin, src.indexOf('</script>', begin));
// the first param is the filename, it is not for reading from the file, but rather for when ts reports issues // the first param is the filename, it is not for reading from the file, but rather for when ts reports issues
// it is also not creating an actual source file, but rather an AST. // it is also not creating an actual source file, but rather an AST.
const node = ts.createSourceFile(file.slice(leadingCharsToStrip), script, ts.ScriptTarget.Latest); const name = file.slice(leadingCharsToStrip);
filesToProps[file.slice(leadingCharsToStrip)] = { node: node }; filesToProps[name] = { scriptNodes: [], props: {} };
for (const script of scripts) {
const node = ts.createSourceFile(name, script, ts.ScriptTarget.Latest);
filesToProps[name].scriptNodes.push(node);
}
} }
} }
}); });
} }
function extractScriptBlocks(content) {
let scriptBlocks = [];
let startString = '<script';
let endString = '</script>';
let curPos = 0;
while (curPos != -1) {
curPos = content.indexOf(startString, curPos);
if (curPos == -1) {
break;
}
// ignore attributes in script tag
curPos = content.indexOf('>', curPos) + 1;
let endPos = content.indexOf(endString, curPos);
if (endPos == -1) {
console.log('Error: missing closing tag for script block');
}
scriptBlocks.push(content.substring(curPos, endPos));
curPos = endPos;
}
return scriptBlocks;
}
function extractJSDocBlocks() { function extractJSDocBlocks() {
for (const file in filesToProps) { for (const file in filesToProps) {
let propsObj = {}; for (const node of filesToProps[file].scriptNodes) {
_extractJSDocBlocks(filesToProps[file].node, propsObj); _extractJSDocBlocks(file, node);
filesToProps[file].props = propsObj; }
} }
} }
// Recursive function for traversing node hierarchy to get JSDocs blocks, different node types have the information we want in different places // Recursive function for traversing node hierarchy to get JSDocs blocks, different node types have the information we want in different places
function _extractJSDocBlocks(srcFile, propsObj) { function _extractJSDocBlocks(file, tsNode) {
ts.forEachChild(srcFile, (node) => { ts.forEachChild(tsNode, (node) => {
if (node?.jsDoc) { if (node?.jsDoc) {
// console.log(srcFile); // console.log(srcFile);
const jsDoc = node.jsDoc[node.jsDoc.length - 1]; const jsDoc = node.jsDoc[node.jsDoc.length - 1];
@@ -52,23 +75,26 @@ function _extractJSDocBlocks(srcFile, propsObj) {
switch (node.kind) { switch (node.kind) {
case ts.SyntaxKind.FirstStatement: case ts.SyntaxKind.FirstStatement:
if (declaration.type?.typeName?.escapedText == 'CssClasses') { if (declaration.type?.typeName?.escapedText == 'CssClasses') {
propsObj[declaration.name.escapedText] = { comment: jsDoc.comment, type: 'css' }; filesToProps[file].props[declaration.name.escapedText] = { comment: jsDoc.comment, type: 'css' };
} else { } else {
propsObj[declaration.name.escapedText] = { comment: jsDoc.comment, type: 'prop' }; filesToProps[file].props[declaration.name.escapedText] = { comment: jsDoc.comment, type: 'prop' };
} }
break; break;
case ts.SyntaxKind.ExpressionStatement: case ts.SyntaxKind.ExpressionStatement:
propsObj[node.expression.arguments[0].text] = { comment: jsDoc.tags[jsDoc.tags.length - 1].comment ?? '', type: 'event' }; filesToProps[file].props[node.expression.arguments[0].text] = {
comment: jsDoc.tags[jsDoc.tags.length - 1].comment ?? '',
type: 'event'
};
break; break;
} }
} }
_extractJSDocBlocks(node, propsObj); _extractJSDocBlocks(file, node);
}); });
} }
function writeJSDocsToDefinitionFiles() { function writeJSDocsToDefinitionFiles() {
// these two will probably bite us in the ass later on.. but the maximum damage will be no descriptions in intellisense, can live with that. // these two will probably bite us in the ass later on.. but the maximum damage will be no descriptions in intellisense, can live with that.
const propsBegin = 'props: {'; const propsBegin = '[x: string]: any;';
const eventsBegin = 'events: {'; const eventsBegin = 'events: {';
const blockEnd = '}'; const blockEnd = '}';
// we only insert JSDocs for properties that had a JSDoc block declared for them in the component file. Some props that might be defined // we only insert JSDocs for properties that had a JSDoc block declared for them in the component file. Some props that might be defined
@@ -131,6 +157,5 @@ function generateKeyWordsFromProps() {
extractScriptsFromComponents('src/lib/components'); extractScriptsFromComponents('src/lib/components');
extractScriptsFromComponents('src/lib/utilities'); extractScriptsFromComponents('src/lib/utilities');
extractJSDocBlocks(); extractJSDocBlocks();
extractJSDocBlocks();
writeJSDocsToDefinitionFiles(); writeJSDocsToDefinitionFiles();
generateKeyWordsFromProps(); generateKeyWordsFromProps();