UNPKG

@microsoft/api-extractor

Version:

Validatation, documentation, and auditing for the exported API of a TypeScript package

260 lines (258 loc) 11.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var ApiDefinitionReference_1 = require("./ApiDefinitionReference"); var ApiItem_1 = require("./definitions/ApiItem"); var Token_1 = require("./Token"); var DocElementParser = (function () { function DocElementParser() { } DocElementParser.getAsText = function (collection, reportError) { var text = ''; collection.forEach(function (docElement) { switch (docElement.kind) { case 'textDocElement': text += docElement.value + " "; break; case 'linkDocElement': // links don't count towards the summary break; case 'seeDocElement': // see doesn't count towards the summary break; default: reportError('Unexpected item in IDocElement collection'); break; } }); return text.trim(); }; DocElementParser.makeTextElement = function (text) { if (!text) { return; } return { kind: 'textDocElement', value: text }; }; DocElementParser.parse = function (documentation, tokenizer) { var docElements = []; var parsing = true; var token; while (parsing) { token = tokenizer.peekToken(); if (!token) { parsing = false; // end of stream break; } if (token.type === Token_1.TokenType.Tag) { switch (token.tag) { case '@see': tokenizer.getToken(); docElements.push({ kind: 'seeDocElement', seeElements: this.parse(documentation, tokenizer) }); break; default: parsing = false; // end of summary tokens break; } } else if (token.type === Token_1.TokenType.Inline) { switch (token.tag) { case '@inheritdoc': tokenizer.getToken(); if (docElements.length > 0 || documentation.summary.length > 0) { documentation.reportError('Cannot provide summary in JsDoc if @inheritdoc tag is given'); } documentation.incompleteInheritdocs.push(token); documentation.isDocInherited = true; break; case '@link': var linkDocElement = this.parseLinkTag(documentation, token); if (linkDocElement) { // Push to docElements to retain position in the documentation docElements.push(linkDocElement); if (linkDocElement.referenceType === 'code') { documentation.incompleteLinks.push(linkDocElement); } } tokenizer.getToken(); // get the link token break; default: parsing = false; break; } } else if (token.type === Token_1.TokenType.Text) { docElements.push({ kind: 'textDocElement', value: token.text }); tokenizer.getToken(); } else { documentation.reportError("Unidentifiable Token " + token.type + " " + token.tag + " " + token.text); } } return docElements; }; /** * This method parses the semantic information in an \@link JSDoc tag, creates and returns a * linkDocElement with the corresponding information. If the corresponding inline tag \@link is * not formatted correctly an error will be reported. * * The format for the \@link tag is {\@link url or API defintion reference | display text}, where * the '|' is only needed if the optional display text is given. * * Examples: * \{@link http://microsoft.com | microsoft home \} * \{@link http://microsoft.com \} * \{@link @microsoft/sp-core-library:Guid.newGuid | new Guid Object \} * \{@link @microsoft/sp-core-library:Guid.newGuid \} */ DocElementParser.parseLinkTag = function (documentation, tokenItem) { if (!tokenItem.text) { documentation.reportError('Invalid @link inline token, a url or API definition reference must be given'); return; } // Make sure there are no extra pipes var pipeSplitContent = tokenItem.text.split('|'); pipeSplitContent = pipeSplitContent.map(function (value) { if (value) { return value.trim(); } }); if (pipeSplitContent.length > 2) { documentation.reportError('Invalid @link parameters, at most one pipe character allowed.'); return; } // Try to guess if the tokenContent is a link or API definition reference var linkDocElement; if (tokenItem.text.match(this._hrefRegEx)) { var urlContent = pipeSplitContent[0].split(' '); // Make sure only a single url is given if (urlContent.length > 1 && urlContent[1] !== '') { documentation.reportError('Invalid @link parameter, url must be a single string.'); return; } linkDocElement = { kind: 'linkDocElement', referenceType: 'href', targetUrl: urlContent[0], value: '' }; } else { // we are processing an API definition reference var apiDefitionRef = ApiDefinitionReference_1.default.createFromString(pipeSplitContent[0], documentation.reportError); // Once we can locate local API definitions, an error should be reported here if not found. if (apiDefitionRef) { linkDocElement = { kind: 'linkDocElement', referenceType: 'code', scopeName: apiDefitionRef.scopeName, packageName: apiDefitionRef.packageName, exportName: apiDefitionRef.exportName, memberName: apiDefitionRef.memberName }; } } // If a display name is given, ensure it only contains characters for words. if (linkDocElement && pipeSplitContent.length > 1) { var displayTextParts = pipeSplitContent[1].match(this._wordRegEx); if (displayTextParts && displayTextParts[0].length !== pipeSplitContent[1].length) { documentation.reportError('Display name in @link token may only contain alphabetic characters.'); return; } // Full match is valid text linkDocElement.value = displayTextParts[0].trim(); } return linkDocElement; }; /** * This method parses the semantic information in an \@inheritdoc JSDoc tag and sets * all the relevant documenation properties from the inherited doc onto the documenation * of the current api item. * * The format for the \@inheritdoc tag is {\@inheritdoc scopeName/packageName:exportName.memberName}. * For more information on the format see IInheritdocRef. */ DocElementParser.parseInheritDoc = function (documentation, token, warnings) { // Check to make sure the API definition reference is at most one string var tokenChunks = token.text.split(' '); if (tokenChunks.length > 1) { documentation.reportError('Too many parameters for @inheritdoc inline tag.' + 'The format should be {@inheritdoc scopeName/packageName:exportName}. Extra parameters are ignored'); return; } // Create the IApiDefinitionReference object // Deconstruct the API reference expression 'scopeName/packageName:exportName.memberName' var apiDefinitionRef = ApiDefinitionReference_1.default.createFromString(token.text, documentation.reportError); // if API reference expression is formatted incorrectly then apiDefinitionRef will be undefined if (!apiDefinitionRef) { documentation.reportError('Incorrecty formatted API definition reference'); return; } // Atempt to locate the apiDefinitionRef var resolvedApiItem = documentation.referenceResolver.resolve(apiDefinitionRef, documentation.extractor.package, warnings); // If no resolvedApiItem found then nothing to inherit // But for the time being set the summary to a text object if (!resolvedApiItem) { var textDocItem = { kind: 'textDocElement', value: "See documentation for " + tokenChunks[0] }; documentation.summary = [textDocItem]; return; } // We are going to copy the resolvedApiItem's documentation // We must make sure it's documentation can be completed, // if we cannot, an error will be reported viathe documentation error handler. // This will only be the case our resolvedApiItem was created from a local // ApiItem. Resolutions from JSON will have an undefined 'apiItem' property. // Example: a circular reference will report an error. if (resolvedApiItem.apiItem) { resolvedApiItem.apiItem.completeInitialization(); } // inheritdoc found, copy over IDocBase properties documentation.summary = resolvedApiItem.summary; documentation.remarks = resolvedApiItem.remarks; // Copy over detailed properties if neccessary // Add additional cases if needed switch (resolvedApiItem.kind) { case ApiItem_1.ApiItemKind.Function: documentation.parameters = resolvedApiItem.params; documentation.returnsMessage = resolvedApiItem.returnsMessage; break; case ApiItem_1.ApiItemKind.Method: documentation.parameters = resolvedApiItem.params; documentation.returnsMessage = resolvedApiItem.returnsMessage; break; } // Check if inheritdoc is depreacted // We need to check if this documentation has a deprecated message // but it may not appear until after this token. if (resolvedApiItem.deprecatedMessage.length > 0) { documentation.isDocInheritedDeprecated = true; } }; return DocElementParser; }()); /** * Matches only strings that contain characters for words. * Any non word characters or spaces, will be present in the third entry in the match results * if they exist. */ DocElementParser._wordRegEx = /^([\w\s]*)/; /** * Matches a href reference. This is used to get an idea whether a given reference is for an href * or an API definition reference. * * For example, the following would be matched: * 'http://' * 'https://' * * The following would not be matched: * '@microsoft/sp-core-library:Guid.newGuid' * 'Guid.newGuid' * 'Guid' */ DocElementParser._hrefRegEx = /^[a-z]+:\/\//; exports.default = DocElementParser; //# sourceMappingURL=DocElementParser.js.map