Skip to content

Commit

Permalink
Merge pull request #31 from playcanvas/feat-remote-tsdoc
Browse files Browse the repository at this point in the history
Removes @microsoft/tsdoc dependancy
  • Loading branch information
marklundin authored Jan 31, 2025
2 parents aea38f4 + 4e40e54 commit 269e75a
Show file tree
Hide file tree
Showing 6 changed files with 27 additions and 192 deletions.
6 changes: 0 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
},
"version": "1.3.2",
"dependencies": {
"@microsoft/tsdoc": "^0.15.0",
"@playcanvas/eslint-config": "^1.7.4 || ^2.0.0",
"@typescript/vfs": "^1.6.0",
"eslint": "^8.56.0 || ^9.0.0",
Expand Down
79 changes: 4 additions & 75 deletions src/parsers/attribute-parser.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { TSDocConfiguration, TSDocTagDefinition, TSDocTagSyntaxKind, TextRange, TSDocParser } from '@microsoft/tsdoc';
import * as ts from 'typescript';

import { ParsingError } from './parsing-error.js';
import { hasTag } from '../utils/attribute-utils.js';
import { parseTag, validateTag } from '../utils/tag-utils.js';
import { extractTextFromDocNode, getLeadingBlockCommentRanges, getLiteralValue, getType } from '../utils/ts-utils.js';
import { getLiteralValue, getType } from '../utils/ts-utils.js';

/**
* A class to parse JSDoc comments and extract attribute metadata.
Expand All @@ -25,44 +24,18 @@ export class AttributeParser {
this.program = env.languageService.getProgram();
this.typeChecker = this.program.getTypeChecker();

this.attributeTag = new TSDocTagDefinition({
tagName: '@attribute', syntaxKind: TSDocTagSyntaxKind.ModifierTag
});

this.typeTag = new TSDocTagDefinition({
tagName: '@type', syntaxKind: TSDocTagSyntaxKind.BlockTag
});

const conf = new TSDocConfiguration();
conf.validation.reportUnsupportedTags = false;

conf.addTagDefinition(this.attributeTag);

tags.forEach((_, tag) => {
const tagDefinition = new TSDocTagDefinition({
tagName: `@${tag}`, syntaxKind: TSDocTagSyntaxKind.BlockTag, allowMultiple: false
});
conf.addTagDefinition(tagDefinition);
});

this.parser = new TSDocParser(conf);
}

/**
* Parses a JSdoc comment and extracts any associated metadata if the `@attribute`
* modifier is used. Returns null if this is not an attribute comment.
*
* @param {TextRange} comment - The comment to parse
* @param {ts.Node} node - The node to parse
* @param {ParsingError[]} errors - An array to store any parsing errors
* @param {boolean} requiresAttributeTag - Whether the comment must have an attribute tag
* @returns {*} - The extracted metadata or null if no metadata was found
*/
parseAttributeComment(comment, node, errors = [], requiresAttributeTag = true) {

const parserContext = this.parser.parseRange(comment);
const docComment = parserContext.docComment;
const file = node.getSourceFile();
parseAttributeComment(node, errors = [], requiresAttributeTag = true) {

// Check if the comment has an attribute tag
if (!requiresAttributeTag || hasTag('attribute', node)) {
Expand All @@ -73,7 +46,7 @@ export class AttributeParser {
if (!attribute) return;

// Extract the description from the summary section
const description = extractTextFromDocNode(docComment.summarySection);
const description = node.jsDoc[0].comment;
if (description) {
attribute.description = description.split('.')[0];
}
Expand All @@ -96,6 +69,7 @@ export class AttributeParser {
attribute[tag.tagName.text] = value;

} catch (error) {
const file = node.getSourceFile();
const { line, character } = file.getLineAndCharacterOfPosition(tag.getStart());
const parseError = new ParsingError(tag, `Error (${line}, ${character}): Parsing Tag '@${tag.tagName.text} ${commentText}' - ${error.message}`);
errors.push(parseError);
Expand All @@ -107,51 +81,6 @@ export class AttributeParser {
}
}

/**
* Returns the leading block comments for an typescript AST node.
*
* @param {import('typescript').Node} node - The node to parse
* @param {string[]} errors - An array to store any parsing errors
* @returns {import('@microsoft/tsdoc').DocComment} - The extracted doc comment;
*/
getCommentsForNode(node, errors = []) {
const fullText = node.getSourceFile().getFullText();
const comments = getLeadingBlockCommentRanges(node, fullText);

if (comments.length === 0) return null;

const lastComment = comments[0];
const textRange = TextRange.fromStringRange(fullText, lastComment.pos, lastComment.end);

const parserContext = this.parser.parseRange(textRange);
const docComment = parserContext.docComment;

// Check for comment parsing errors and flag them
this.populateErrors(node, parserContext, errors);

return docComment;
}

/**
* Populates the errors array with any parsing errors found in the parser context.
*
* @param {import('typescript').Node} node - The node to parse
* @param {import('@microsoft/tsdoc').ParserContext} parserContext - The parser context to check for errors
* @param {string[]} errors - An array to store any parsing errors
*/
populateErrors(node, parserContext, errors) {
if (parserContext.log.messages.length > 0) {
const sourceFile = node.getSourceFile();

const formattedErrors = parserContext.log.messages.map((message) => {
const location = sourceFile.getLineAndCharacterOfPosition(message.textRange.pos);
return `${sourceFile.fileName}(${location.line + 1},${location.character + 1}): [TSDoc] ${message}`;
});

errors.push(...formattedErrors);
}
}

/**
* Extract the public members and their values from a TypeScript node.
*
Expand Down
20 changes: 8 additions & 12 deletions src/parsers/script-parser.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { TextRange } from '@microsoft/tsdoc';
import * as ts from 'typescript';

import { AttributeParser } from './attribute-parser.js';
import { ParsingError } from './parsing-error.js';
import { hasTag } from '../utils/attribute-utils.js';
import { zipArrays } from '../utils/generic-utils.js';
import { flatMapAnyNodes, getJSDocCommentRanges, getLiteralValue, parseArrayLiteral, parseFloatNode } from '../utils/ts-utils.js';
import { flatMapAnyNodes, getJSDocTags, getLiteralValue, parseArrayLiteral, parseFloatNode } from '../utils/ts-utils.js';

/**
* @typedef {object} Attribute
Expand Down Expand Up @@ -322,17 +321,14 @@ export class ScriptParser {
}

// Find "/** */" style comments associated with this node.
const comments = getJSDocCommentRanges(node, this.typeChecker);
const members = getJSDocTags(node, this.typeChecker);

// Parse the comments for attribute metadata
for (const comment of comments) {

const memberFileText = comment.member.getSourceFile().getFullText();
for (const { member, memberName } of members) {

// Parse the comment for attribute metadata
const attributeMetadata = this.attributeParser.parseAttributeComment(
TextRange.fromStringRange(memberFileText, comment.range.pos, comment.range.end),
comment.member,
member,
errors,
requiresAttributeTag
);
Expand All @@ -341,11 +337,11 @@ export class ScriptParser {
if (attributeMetadata) {

const { type, typeName } = attributeMetadata;
const initializer = comment.member?.initializer;
const initializer = member?.initializer;

if (typeName === 'any') {

const error = new ParsingError(comment.member, `The attribute "${comment.memberName}" is not initialized and does not have a @type tag.`);
const error = new ParsingError(member, `The attribute "${memberName}" is not initialized and does not have a @type tag.`);
errors.push(error);
continue;

Expand All @@ -369,7 +365,7 @@ export class ScriptParser {

// Check if the type is a valid interface
if (!isInitialized && !typeIsInterface && !typeIsJSDocTypeDef && !isJSDocTypeLiteral) {
const error = new ParsingError(comment.member, `Attribute "${comment.memberName}" is an invalid type.`);
const error = new ParsingError(member, `Attribute "${memberName}" is an invalid type.`);
errors.push(error);
continue;
}
Expand Down Expand Up @@ -408,7 +404,7 @@ export class ScriptParser {
}

// Add the attribute to the list of found attributes
attributes[comment.memberName] = mapAttributesToOutput(attributeMetadata);
attributes[memberName] = mapAttributesToOutput(attributeMetadata);

}
}
Expand Down
30 changes: 2 additions & 28 deletions src/utils/attribute-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@


/**
* Returns a tsdoc tag from a JSDoc comment.
* Returns a jsdoc tag from a JSDoc comment.
* @param {string} tag - The tag to search for
* @param {import('typescript').Node} doc - The JSDoc comment node
* @returns {import('@microsoft/tsdoc').DocNode | null} - The tag node
* @returns {import('typescript').Node} | null} - The tag node
*/
export const getTagFromJsdoc = (tag, doc) => {
return doc?.tags?.find(doc => doc.tagName.text === tag);
Expand Down Expand Up @@ -42,29 +42,3 @@ export const hasTag = (tag, node) => {
export const isInterface = (node) => {
return hasTag('interface', node);
};

// /**
// * Parses a type tag in the format "{type}".
// * @param {import('@microsoft/tsdoc').DocSection} content - The content of the tag.
// * @param {object} metadata - An object to store the extracted metadata.
// * @param {{ node: import('typescript').Node, env: tsdoc.Environment, errors: string[] }} opts - Additional parser options
// * @returns {{ type: import('typescript').Type | null, array: boolean | null }} - The extracted type and array flag.
// */
// export function parseTypeTag(content, metadata, { node, env, errors }) {

// // Extract the full source text of the original file
// const originalSourceText = node.getSourceFile().getFullText();
// const typeString = extractTextFromDocNode(content);
// const sourceText = `${originalSourceText}\nlet a: ${typeString};`;

// env.createFile("/virtual.ts", sourceText);

// const updatedProgram = env.languageService.getProgram();
// const newTypeChecker = updatedProgram.getTypeChecker();

// const sourceFile = updatedProgram.getSourceFile('/virtual.ts');

// const variableStatement = sourceFile.statements[sourceFile.statements.length - 1];
// const variableDeclaration = variableStatement.declarationList.declarations[0];
// return getType(variableDeclaration, newTypeChecker);
// }
Loading

0 comments on commit 269e75a

Please sign in to comment.