@microsoft/api-extractor
Advanced tools
Comparing version 2.0.2 to 2.0.3
@@ -5,2 +5,14 @@ { | ||
{ | ||
"version": "2.0.3", | ||
"tag": "@microsoft/api-extractor_v2.0.3", | ||
"date": "Fri, 14 Apr 2017 17:44:08 GMT", | ||
"comments": { | ||
"patch": [ | ||
{ | ||
"comment": "Added collect references ability to detect determine type information of return types and parameter types." | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"version": "2.0.2", | ||
@@ -7,0 +19,0 @@ "tag": "@microsoft/api-extractor_v2.0.2", |
# Change Log - @microsoft/api-extractor | ||
This log was last generated on Fri, 07 Apr 2017 21:43:16 GMT and should not be manually modified. | ||
This log was last generated on Fri, 14 Apr 2017 17:44:08 GMT and should not be manually modified. | ||
## 2.0.3 | ||
Fri, 14 Apr 2017 17:44:08 GMT | ||
### Patches | ||
- Added collect references ability to detect determine type information of return types and parameter types. | ||
## 2.0.2 | ||
@@ -6,0 +13,0 @@ Fri, 07 Apr 2017 21:43:16 GMT |
@@ -90,3 +90,3 @@ "use strict"; | ||
else if (this.packageName) { | ||
result += "$this.packageName"; | ||
result += this.packageName; | ||
} | ||
@@ -93,0 +93,0 @@ return result; |
@@ -28,3 +28,3 @@ // NOTE: THIS SOURCE FILE IS FOR DEBUGGING PURPOSES ONLY. | ||
extractor.loadExternalPackages('./testInputs/external-api-json'); | ||
extractor.analyze({ entryPointFile: './testInputs/example2/index.ts', | ||
extractor.analyze({ entryPointFile: './testInputs/example2/src/index.ts', | ||
otherFiles: [] }); | ||
@@ -37,4 +37,6 @@ var externalPackageApiRef = { | ||
}; | ||
// Normally warnings are kept by the ApiItem data structure, | ||
// and written to the '*.api.ts' file. | ||
var warnings = []; | ||
var apiDefinitionRef = ApiDefinitionReference_1.default.createFromParts(externalPackageApiRef); | ||
console.log(extractor.docItemLoader.getPackage(apiDefinitionRef, console.log)); | ||
var apiFileGenerator = new ApiFileGenerator_1.default(); | ||
@@ -41,0 +43,0 @@ apiFileGenerator.writeApiFile('./lib/DebugRun.api.ts', extractor); |
@@ -54,3 +54,3 @@ /// <reference types="es6-collections" /> | ||
export interface IReferenceResolver { | ||
resolve(apiDefinitionRef: ApiDefinitionReference, apiPackage: ApiPackage, reportError: (message: string) => void): ResolvedApiItem; | ||
resolve(apiDefinitionRef: ApiDefinitionReference, apiPackage: ApiPackage, warnings: string[]): ResolvedApiItem; | ||
} | ||
@@ -130,2 +130,3 @@ export default class ApiDocumentation { | ||
hasReadOnlyTag?: boolean; | ||
warnings: string[]; | ||
/** | ||
@@ -147,3 +148,3 @@ * A function type interface that abstracts away resolving | ||
reportError: (message: string) => void; | ||
constructor(docComment: string, referenceResolver: IReferenceResolver, extractor: Extractor, errorLogger: (message: string) => void); | ||
constructor(docComment: string, referenceResolver: IReferenceResolver, extractor: Extractor, errorLogger: (message: string) => void, warnings: string[]); | ||
/** | ||
@@ -153,3 +154,3 @@ * Executes the implementation details involved in completing the documentation initialization. | ||
*/ | ||
completeInitialization(): void; | ||
completeInitialization(warnings: string[]): void; | ||
protected _parseDocs(): void; | ||
@@ -166,5 +167,5 @@ protected _parseParam(tokenizer: Tokenizer): IParam; | ||
*/ | ||
private _completeInheritdocs(); | ||
private _completeInheritdocs(warnings); | ||
private _reportBadJSDocTag(token); | ||
private _checkInheritDocStatus(); | ||
} |
@@ -43,3 +43,3 @@ /* tslint:disable:no-bitwise */ | ||
var ApiDocumentation = (function () { | ||
function ApiDocumentation(docComment, referenceResolver, extractor, errorLogger) { | ||
function ApiDocumentation(docComment, referenceResolver, extractor, errorLogger, warnings) { | ||
this.originalJsDoc = docComment; | ||
@@ -50,2 +50,3 @@ this.referenceResolver = referenceResolver; | ||
this.parameters = {}; | ||
this.warnings = warnings; | ||
this._parseDocs(); | ||
@@ -57,7 +58,7 @@ } | ||
*/ | ||
ApiDocumentation.prototype.completeInitialization = function () { | ||
ApiDocumentation.prototype.completeInitialization = function (warnings) { | ||
// Ensure links are valid | ||
this._completeLinks(); | ||
// Ensure inheritdocs are valid | ||
this._completeInheritdocs(); | ||
this._completeInheritdocs(warnings); | ||
}; | ||
@@ -240,3 +241,3 @@ ApiDocumentation.prototype._parseDocs = function () { | ||
var apiDefinitionRef = ApiDefinitionReference_1.default.createFromParts(parts); | ||
var resolvedApiItem = this.referenceResolver.resolve(apiDefinitionRef, this.extractor.package, this.reportError); | ||
var resolvedApiItem = this.referenceResolver.resolve(apiDefinitionRef, this.extractor.package, this.warnings); | ||
// If the apiDefinitionRef can not be found the resolcedApiItem will be | ||
@@ -253,6 +254,6 @@ // undefined and an error will have been reported via this.reportError | ||
*/ | ||
ApiDocumentation.prototype._completeInheritdocs = function () { | ||
ApiDocumentation.prototype._completeInheritdocs = function (warnings) { | ||
while (this.incompleteInheritdocs.length) { | ||
var token = this.incompleteInheritdocs.pop(); | ||
DocElementParser_1.default.parseInheritDoc(this, token); | ||
DocElementParser_1.default.parseInheritDoc(this, token, warnings); | ||
} | ||
@@ -259,0 +260,0 @@ }; |
@@ -178,2 +178,6 @@ import * as ts from 'typescript'; | ||
/** | ||
* Called after the constructor to finish the analysis. | ||
*/ | ||
visitTypeReferencesForApiItem(): void; | ||
/** | ||
* Return the compiler's underlying Declaration object | ||
@@ -246,3 +250,14 @@ * @todo Generally ApiItem classes don't expose ts API objects; we should add | ||
hasAnyIncompleteTypes(): boolean; | ||
/** | ||
* This is called by ApiItems to visit the types that appear in an expression. For example, | ||
* if a Public API function returns a class that is defined in this package, but not exported, | ||
* this is a problem. visitTypeReferencesForNode() finds all TypeReference child nodes under the | ||
* specified node and analyzes each one. | ||
*/ | ||
protected visitTypeReferencesForNode(node: ts.Node): void; | ||
/** | ||
* This is a helper for visitTypeReferencesForNode(). It analyzes a single TypeReferenceNode. | ||
*/ | ||
private _analyzeTypeReference(typeReferenceNode); | ||
} | ||
export default ApiItem; |
@@ -9,2 +9,4 @@ /* tslint:disable:no-bitwise */ | ||
var DocElementParser_1 = require("../DocElementParser"); | ||
var PackageJsonHelpers_1 = require("../PackageJsonHelpers"); | ||
var ApiDefinitionReference_1 = require("../ApiDefinitionReference"); | ||
/** | ||
@@ -81,2 +83,11 @@ * Indicates the type of definition represented by a ApiItem object. | ||
})(InitializationState || (InitializationState = {})); | ||
// Names of NPM scopes that contain packages that provide typings for the real package. | ||
// The TypeScript compiler's typings design doesn't seem to handle scoped NPM packages, | ||
// so the transformation will always be simple, like this: | ||
// "@types/example" --> "example" | ||
// NOT like this: | ||
// "@types/@contoso/example" --> "@contoso/example" | ||
// "@contosotypes/example" --> "@contoso/example" | ||
// Eventually this constant should be provided by the gulp task that invokes the compiler. | ||
var typingsScopeNames = ['@types']; | ||
/** | ||
@@ -127,5 +138,11 @@ * ApiItem is an abstract base that represents TypeScript API definitions such as classes, | ||
} | ||
this.documentation = new ApiDocumentation_1.default(originalJsDoc, this.extractor.docItemLoader, this.extractor, this.reportError); | ||
this.documentation = new ApiDocumentation_1.default(originalJsDoc, this.extractor.docItemLoader, this.extractor, this.reportError, this.warnings); | ||
} | ||
/** | ||
* Called after the constructor to finish the analysis. | ||
*/ | ||
ApiItem.prototype.visitTypeReferencesForApiItem = function () { | ||
// (virtual) | ||
}; | ||
/** | ||
* Return the compiler's underlying Declaration object | ||
@@ -197,4 +214,4 @@ * @todo Generally ApiItem classes don't expose ts API objects; we should add | ||
ApiItem.prototype.onCompleteInitialization = function () { | ||
this.documentation.completeInitialization(); | ||
// TODO: this.collectTypeReferences(this); | ||
this.documentation.completeInitialization(this.warnings); | ||
// TODO: this.visitTypeReferencesForNode(this); | ||
var summaryTextCondensed = DocElementParser_1.default.getAsText(this.documentation.summary, this.reportError).replace(/\s\s/g, ' '); | ||
@@ -214,2 +231,6 @@ this.needsDocumentation = this.shouldHaveDocumentation() && summaryTextCondensed.length <= 10; | ||
} | ||
if (this.documentation.isDocInheritedDeprecated && this.documentation.deprecatedMessage.length === 0) { | ||
this.reportError('inheritdoc source item is deprecated. ' + | ||
'Must provide @deprecated message or remove @inheritdoc inline tag.'); | ||
} | ||
}; | ||
@@ -270,2 +291,124 @@ /** | ||
}; | ||
/** | ||
* This is called by ApiItems to visit the types that appear in an expression. For example, | ||
* if a Public API function returns a class that is defined in this package, but not exported, | ||
* this is a problem. visitTypeReferencesForNode() finds all TypeReference child nodes under the | ||
* specified node and analyzes each one. | ||
*/ | ||
ApiItem.prototype.visitTypeReferencesForNode = function (node) { | ||
if (node.kind === ts.SyntaxKind.Block || | ||
(node.kind >= ts.SyntaxKind.JSDocTypeExpression && node.kind <= ts.SyntaxKind.JSDocNeverKeyword)) { | ||
// Don't traverse into code blocks or JSDoc items; we only care about the function signature | ||
return; | ||
} | ||
if (node.kind === ts.SyntaxKind.TypeReference) { | ||
var typeReference = node; | ||
this._analyzeTypeReference(typeReference); | ||
} | ||
// Recurse the tree | ||
for (var _i = 0, _a = node.getChildren(); _i < _a.length; _i++) { | ||
var childNode = _a[_i]; | ||
this.visitTypeReferencesForNode(childNode); | ||
} | ||
}; | ||
/** | ||
* This is a helper for visitTypeReferencesForNode(). It analyzes a single TypeReferenceNode. | ||
*/ | ||
ApiItem.prototype._analyzeTypeReference = function (typeReferenceNode) { | ||
var symbol = this.extractor.typeChecker.getSymbolAtLocation(typeReferenceNode.typeName); | ||
if (!symbol) { | ||
// Is this bad? | ||
return; | ||
} | ||
if (symbol.flags & ts.SymbolFlags.TypeParameter) { | ||
// Don't analyze e.g. "T" in "Set<T>" | ||
return; | ||
} | ||
// Follow the aliases all the way to the ending SourceFile | ||
var currentSymbol = this.followAliases(symbol); | ||
if (!currentSymbol.declarations || !currentSymbol.declarations.length) { | ||
// This is a degenerate case that happens sometimes | ||
return; | ||
} | ||
var sourceFile = currentSymbol.declarations[0].getSourceFile(); | ||
// Walk upwards from that directory until you find a directory containing package.json, | ||
// this is where the referenced type is located. | ||
// Example: "c:\users\<username>\sp-client\spfx-core\sp-core-library" | ||
var typeReferencePackagePath = PackageJsonHelpers_1.default.tryFindPackagePathUpwards(sourceFile.path); | ||
// Example: "@microsoft/sp-core-library" | ||
var typeReferencePackageName = ''; | ||
// If we can not find a package path, we consider the type to be part of the current project's package. | ||
// One case where this happens is when looking for a type that is a symlink | ||
if (!typeReferencePackagePath) { | ||
typeReferencePackageName = this.extractor.package.name; | ||
} | ||
else { | ||
typeReferencePackageName = PackageJsonHelpers_1.default.readPackageName(typeReferencePackagePath); | ||
typingsScopeNames.every(function (typingScopeName) { | ||
if (typeReferencePackageName.indexOf(typingScopeName) > -1) { | ||
typeReferencePackageName = typeReferencePackageName.replace(typingScopeName + '/', ''); | ||
// returning true breaks the every loop | ||
return true; | ||
} | ||
}); | ||
} | ||
// Read the name/version from package.json -- that tells you what package the symbol | ||
// belongs to. If it is your own ApiPackage.name/version, then you know it's a local symbol. | ||
var currentPackageName = this.extractor.package.name; | ||
var typeName = typeReferenceNode.typeName.getText(); | ||
if (!typeReferencePackagePath || typeReferencePackageName === currentPackageName) { | ||
// The type is defined in this project. Did the person remember to export it? | ||
var exportedLocalName = this.extractor.package.tryGetExportedSymbolName(currentSymbol); | ||
if (exportedLocalName) { | ||
// [CASE 1] Local/Exported | ||
// Yes; the type is properly exported. | ||
// TODO: In the future, here we can check for issues such as a @public type | ||
// referencing an @internal type. | ||
return; | ||
} | ||
else { | ||
// [CASE 2] Local/Unexported | ||
// No; issue a warning | ||
this.reportWarning("The type \"" + typeName + "\" needs to be exported by the package" | ||
+ " (e.g. added to index.ts)"); | ||
return; | ||
} | ||
} | ||
// External | ||
// Attempt to load from docItemLoader | ||
var scopedPackageName = ApiDefinitionReference_1.default.parseScopedPackageName(typeReferencePackageName); | ||
var apiDefinitionRefParts = { | ||
scopeName: scopedPackageName.scope, | ||
packageName: scopedPackageName.package, | ||
exportName: '', | ||
memberName: '' | ||
}; | ||
// the currentSymbol.name is the name of an export, if it contains a '.' then the substring | ||
// after the period is the member name | ||
if (currentSymbol.name.indexOf('.') > -1) { | ||
var exportMemberName = currentSymbol.name.split('.'); | ||
apiDefinitionRefParts.exportName = exportMemberName.pop(); | ||
apiDefinitionRefParts.memberName = exportMemberName.pop(); | ||
} | ||
else { | ||
apiDefinitionRefParts.exportName = currentSymbol.name; | ||
} | ||
var apiDefinitionRef = ApiDefinitionReference_1.default.createFromParts(apiDefinitionRefParts); | ||
// Attempt to resolve the type by checking the node modules | ||
var referenceResolutionWarnings = []; | ||
var resolvedApiItem = this.extractor.docItemLoader.resolveJsonReferences(apiDefinitionRef, referenceResolutionWarnings); | ||
if (resolvedApiItem) { | ||
// [CASE 3] External/Resolved | ||
// This is a reference to a type from an external package, and it was resolved. | ||
return; | ||
} | ||
else { | ||
// [CASE 4] External/Unresolved | ||
// For cases when we can't find the external package, we are going to write a report | ||
// at the bottom of the *api.ts file. We do this because we do not yet support references | ||
// to items like react:Component. | ||
// For now we are going to silently ignore these errors. | ||
return; | ||
} | ||
}; | ||
return ApiItem; | ||
@@ -272,0 +415,0 @@ }()); |
@@ -18,3 +18,7 @@ /// <reference types="es6-collections" /> | ||
protected addMemberItem(apiItem: ApiItem): void; | ||
/** | ||
* @virtual | ||
*/ | ||
visitTypeReferencesForApiItem(): void; | ||
} | ||
export default ApiItemContainer; |
@@ -48,2 +48,11 @@ "use strict"; | ||
}; | ||
/** | ||
* @virtual | ||
*/ | ||
ApiItemContainer.prototype.visitTypeReferencesForApiItem = function () { | ||
_super.prototype.visitTypeReferencesForApiItem.call(this); | ||
this.memberItems.forEach(function (apiItem) { | ||
apiItem.visitTypeReferencesForApiItem(); | ||
}); | ||
}; | ||
return ApiItemContainer; | ||
@@ -50,0 +59,0 @@ }(ApiItem_1.default)); |
@@ -33,2 +33,6 @@ import ApiItem, { IApiItemOptions } from './ApiItem'; | ||
/** | ||
* @virtual | ||
*/ | ||
visitTypeReferencesForApiItem(): void; | ||
/** | ||
* Returns a text string such as "someName?: SomeTypeName;", or in the case of a type | ||
@@ -35,0 +39,0 @@ * literal expression, returns a text string such as "someName?:". |
@@ -75,2 +75,11 @@ "use strict"; | ||
/** | ||
* @virtual | ||
*/ | ||
ApiMember.prototype.visitTypeReferencesForApiItem = function () { | ||
_super.prototype.visitTypeReferencesForApiItem.call(this); | ||
if (this.declaration.kind !== ts.SyntaxKind.PropertySignature) { | ||
this.visitTypeReferencesForNode(this.declaration); | ||
} | ||
}; | ||
/** | ||
* Returns a text string such as "someName?: SomeTypeName;", or in the case of a type | ||
@@ -77,0 +86,0 @@ * literal expression, returns a text string such as "someName?:". |
@@ -10,5 +10,18 @@ import * as ts from 'typescript'; | ||
export default class ApiPackage extends ApiItemContainer { | ||
private _exportedNormalizedSymbols; | ||
private static _getOptions(extractor, rootFile); | ||
constructor(extractor: Extractor, rootFile: ts.SourceFile); | ||
/** | ||
* Finds and returns the original symbol name. | ||
* | ||
* For example, suppose a class is defined as "export default class MyClass { }" | ||
* but exported from the package's index.ts like this: | ||
* | ||
* export { default as _MyClass } from './MyClass'; | ||
* | ||
* In this example, given the symbol for _MyClass, getExportedSymbolName() will return | ||
* the string "MyClass". | ||
*/ | ||
tryGetExportedSymbolName(symbol: ts.Symbol): string; | ||
/** | ||
* Find a member in this package by name and return it if found. | ||
@@ -15,0 +28,0 @@ * |
@@ -21,2 +21,3 @@ /* tslint:disable:no-bitwise */ | ||
var TypeScriptHelpers_1 = require("../TypeScriptHelpers"); | ||
var PackageJsonHelpers_1 = require("../PackageJsonHelpers"); | ||
/** | ||
@@ -30,3 +31,6 @@ * This class is part of the ApiItem abstract syntax tree. It represents the top-level | ||
var _this = _super.call(this, ApiPackage._getOptions(extractor, rootFile)) || this; | ||
_this._exportedNormalizedSymbols = []; | ||
_this.kind = ApiItem_1.ApiItemKind.Package; | ||
// The scoped package name. (E.g. "@microsoft/api-extractor") | ||
_this.name = PackageJsonHelpers_1.default.readPackageName(_this.extractor.packageFolder); | ||
var exportSymbols = _this.typeChecker.getExportsOfModule(_this.declarationSymbol); | ||
@@ -63,2 +67,6 @@ if (exportSymbols) { | ||
} | ||
_this._exportedNormalizedSymbols.push({ | ||
exportedName: exportSymbol.name, | ||
followedSymbol: followedSymbol | ||
}); | ||
} | ||
@@ -92,2 +100,23 @@ } | ||
/** | ||
* Finds and returns the original symbol name. | ||
* | ||
* For example, suppose a class is defined as "export default class MyClass { }" | ||
* but exported from the package's index.ts like this: | ||
* | ||
* export { default as _MyClass } from './MyClass'; | ||
* | ||
* In this example, given the symbol for _MyClass, getExportedSymbolName() will return | ||
* the string "MyClass". | ||
*/ | ||
ApiPackage.prototype.tryGetExportedSymbolName = function (symbol) { | ||
var followedSymbol = this.followAliases(symbol); | ||
for (var _i = 0, _a = this._exportedNormalizedSymbols; _i < _a.length; _i++) { | ||
var exportedSymbol = _a[_i]; | ||
if (exportedSymbol.followedSymbol === followedSymbol) { | ||
return exportedSymbol.exportedName; | ||
} | ||
} | ||
return undefined; | ||
}; | ||
/** | ||
* Find a member in this package by name and return it if found. | ||
@@ -94,0 +123,0 @@ * |
@@ -26,2 +26,6 @@ import * as ts from 'typescript'; | ||
/** | ||
* @virtual | ||
*/ | ||
visitTypeReferencesForApiItem(): void; | ||
/** | ||
* Returns a line of text such as "class MyClass extends MyBaseClass", excluding the | ||
@@ -28,0 +32,0 @@ * curly braces and body. The name "MyClass" will be the public name seend by external |
@@ -100,2 +100,15 @@ /* tslint:disable:no-bitwise */ | ||
/** | ||
* @virtual | ||
*/ | ||
ApiStructuredType.prototype.visitTypeReferencesForApiItem = function () { | ||
_super.prototype.visitTypeReferencesForApiItem.call(this); | ||
// Collect type references from the base classes | ||
if (this._classLikeDeclaration && this._classLikeDeclaration.heritageClauses) { | ||
for (var _i = 0, _a = this._classLikeDeclaration.heritageClauses; _i < _a.length; _i++) { | ||
var clause = _a[_i]; | ||
this.visitTypeReferencesForNode(clause); | ||
} | ||
} | ||
}; | ||
/** | ||
* Returns a line of text such as "class MyClass extends MyBaseClass", excluding the | ||
@@ -102,0 +115,0 @@ * curly braces and body. The name "MyClass" will be the public name seend by external |
@@ -29,4 +29,7 @@ /// <reference types="mocha" /> | ||
extractor.analyze({ | ||
entryPointFile: path.join(inputFolder, 'index.ts') | ||
entryPointFile: path.join(inputFolder, 'src/index.ts') | ||
}); | ||
// These warnings would normally be printed at the bottom | ||
// of the source package's '*.api.ts' file. | ||
var warnings = []; | ||
var myDocumentedClass = extractor.package.getSortedMemberItems() | ||
@@ -37,3 +40,3 @@ .filter(function (apiItem) { return apiItem.name === 'MyDocumentedClass'; })[0]; | ||
describe('ApiDocumentation internal methods', function () { | ||
var apiDoc = new ApiDocumentation_1.default('Some summary\n@remarks and some remarks\n@public', extractor.docItemLoader, extractor, console.log); | ||
var apiDoc = new ApiDocumentation_1.default('Some summary\n@remarks and some remarks\n@public', extractor.docItemLoader, extractor, console.log, warnings); | ||
}); | ||
@@ -44,6 +47,16 @@ describe('Documentation Parser Tests', function () { | ||
* To view the expected errors see: | ||
* - testInputs/example2/folder/MyDocumentedClass (9 errors) | ||
* - the test in this file '_parseApiReferenceExpression() without scope name and invalid memberName' (1 error) | ||
* - testInputs/example2/folder/MyDocumentedClass (10 errors) | ||
*/ | ||
chai_1.assert.equal(capturedErrors.length, 10); | ||
chai_1.assert.equal(capturedErrors[0].message, 'Cannot provide summary in JsDoc if @inheritdoc tag is given'); | ||
chai_1.assert.equal(capturedErrors[1].message, 'The JSDoc tag "@summary" is not supported in this context'); | ||
chai_1.assert.equal(capturedErrors[2].message, 'Unexpected text in JSDoc comment: "Mock class for testing JsDoc parser"'); | ||
chai_1.assert.equal(capturedErrors[3].message, 'Unknown JSDoc tag "@badJsDocTag"'); | ||
chai_1.assert.equal(capturedErrors[4].message, 'Unknown tag name for inline tag.'); | ||
chai_1.assert.equal(capturedErrors[5].message, 'Too few parameters for @link inline tag.'); | ||
chai_1.assert.equal(capturedErrors[6].message, 'Unexpected text in JSDoc comment: "can not contain a tag"'); | ||
chai_1.assert.equal(capturedErrors[7].message, 'More than one API Tag was specified'); | ||
chai_1.assert.equal(capturedErrors[8].message, 'API reference expression must be of the form: \'scopeName/packageName:exportName.memberName ' + | ||
'| display text\'where the \'|\' is required if a display text is provided'); | ||
chai_1.assert.equal(capturedErrors[9].message, 'inheritdoc source item is deprecated. Must provide @deprecated message or remove @inheritdoc inline tag.'); | ||
}); | ||
@@ -50,0 +63,0 @@ it('Should parse API tag', function () { |
@@ -52,3 +52,3 @@ import { IDocElement, IHrefLinkElement, ICodeLinkElement } from './IDocElement'; | ||
*/ | ||
static parseInheritDoc(documentation: ApiDocumentation, token: Token): void; | ||
static parseInheritDoc(documentation: ApiDocumentation, token: Token, warnings: string[]): void; | ||
} |
@@ -176,3 +176,3 @@ "use strict"; | ||
*/ | ||
DocElementParser.parseInheritDoc = function (documentation, token) { | ||
DocElementParser.parseInheritDoc = function (documentation, token, warnings) { | ||
// Check to make sure the API definition reference is at most one string | ||
@@ -194,3 +194,3 @@ var tokenChunks = token.text.split(' '); | ||
// Atempt to locate the apiDefinitionRef | ||
var resolvedApiItem = documentation.referenceResolver.resolve(apiDefinitionRef, documentation.extractor.package, documentation.reportError); | ||
var resolvedApiItem = documentation.referenceResolver.resolve(apiDefinitionRef, documentation.extractor.package, warnings); | ||
// If no resolvedApiItem found then nothing to inherit | ||
@@ -233,3 +233,3 @@ // But for the time being set the summary to a text object | ||
// but it may not appear until after this token. | ||
if (resolvedApiItem.deprecatedMessage) { | ||
if (resolvedApiItem.deprecatedMessage.length > 0) { | ||
documentation.isDocInheritedDeprecated = true; | ||
@@ -236,0 +236,0 @@ } |
@@ -39,3 +39,3 @@ import { IDocPackage } from './IDocItem'; | ||
*/ | ||
resolve(apiDefinitionRef: ApiDefinitionReference, apiPackage: ApiPackage, reportError: (message: string) => void): ResolvedApiItem; | ||
resolve(apiDefinitionRef: ApiDefinitionReference, apiPackage: ApiPackage, warnings: string[]): ResolvedApiItem; | ||
/** | ||
@@ -47,3 +47,3 @@ * Resolution of API definition references in the scenario that the reference given indicates | ||
*/ | ||
resolveLocalReferences(apiDefinitionRef: ApiDefinitionReference, apiPackage: ApiPackage, reportError: (message: string) => void): ResolvedApiItem; | ||
resolveLocalReferences(apiDefinitionRef: ApiDefinitionReference, apiPackage: ApiPackage, warnings: string[]): ResolvedApiItem; | ||
/** | ||
@@ -54,3 +54,3 @@ * Resolution of API definition references in the scenario that the reference given indicates | ||
*/ | ||
resolveJsonReferences(apiDefinitionRef: ApiDefinitionReference, reportError: (message: string) => void): ResolvedApiItem; | ||
resolveJsonReferences(apiDefinitionRef: ApiDefinitionReference, warnings: string[]): ResolvedApiItem; | ||
/** | ||
@@ -62,3 +62,3 @@ * Attempts to locate and load the IDocPackage object from the project folder's | ||
*/ | ||
getPackage(apiDefinitionRef: ApiDefinitionReference, reportError: (message: string) => void): IDocPackage; | ||
getPackage(apiDefinitionRef: ApiDefinitionReference): IDocPackage; | ||
/** | ||
@@ -68,3 +68,3 @@ * Loads the API documentation json file and validates that it conforms to our schema. If it does, | ||
*/ | ||
loadPackageIntoCache(packageJsonFilePath: string): IDocPackage; | ||
loadPackageIntoCache(apiJsonFilePath: string, cachePackageName: string): IDocPackage; | ||
} |
@@ -33,13 +33,12 @@ "use strict"; | ||
*/ | ||
DocItemLoader.prototype.resolve = function (apiDefinitionRef, apiPackage, reportError) { | ||
// If there is a packageName then there must be a scopeName, and they | ||
// both must match the current scope and package we are in. | ||
// We can take advantage of '&&' being evaluated left to right. | ||
if (!apiDefinitionRef.packageName && !apiDefinitionRef.scopeName) { | ||
DocItemLoader.prototype.resolve = function (apiDefinitionRef, apiPackage, warnings) { | ||
// We determine if an 'apiDfefinitionRef' is local if it has no package name or if the scoped | ||
// package name is equal to the current package's scoped package name. | ||
if (!apiDefinitionRef.packageName || apiDefinitionRef.toScopePackageString() === apiPackage.name) { | ||
// Resolution for local references | ||
return this.resolveLocalReferences(apiDefinitionRef, apiPackage, reportError); | ||
return this.resolveLocalReferences(apiDefinitionRef, apiPackage, warnings); | ||
} | ||
else { | ||
// Resolution for references in JSON files | ||
return this.resolveJsonReferences(apiDefinitionRef, reportError); | ||
// If there was no resolved apiItem then try loading from JSON | ||
return this.resolveJsonReferences(apiDefinitionRef, warnings); | ||
} | ||
@@ -53,7 +52,7 @@ }; | ||
*/ | ||
DocItemLoader.prototype.resolveLocalReferences = function (apiDefinitionRef, apiPackage, reportError) { | ||
DocItemLoader.prototype.resolveLocalReferences = function (apiDefinitionRef, apiPackage, warnings) { | ||
var apiItem = apiPackage.getMemberItem(apiDefinitionRef.exportName); | ||
// Check if export name was not found | ||
if (!apiItem) { | ||
reportError("Unable to find referenced export \"" + apiDefinitionRef.toExportString() + "\""); | ||
warnings.push("Unable to find referenced export \"" + apiDefinitionRef.toExportString() + "\""); | ||
return undefined; | ||
@@ -77,3 +76,3 @@ } | ||
// memberName was not found, apiDefinitionRef is invalid | ||
reportError("Unable to find referenced member \"" + apiDefinitionRef.toMemberString() + "\""); | ||
warnings.push("Unable to find referenced member \"" + apiDefinitionRef.toMemberString() + "\""); | ||
return undefined; | ||
@@ -88,7 +87,9 @@ } | ||
*/ | ||
DocItemLoader.prototype.resolveJsonReferences = function (apiDefinitionRef, reportError) { | ||
DocItemLoader.prototype.resolveJsonReferences = function (apiDefinitionRef, warnings) { | ||
// Check if package can be not found | ||
var docPackage = this.getPackage(apiDefinitionRef, reportError); | ||
var docPackage = this.getPackage(apiDefinitionRef); | ||
if (!docPackage) { | ||
// Error is reported in this.getPackage() | ||
// package not found in node_modules | ||
warnings.push("Unable to find a documentation file (\"" + apiDefinitionRef.packageName + ".api.json\")" + | ||
' for the referenced package'); | ||
return undefined; | ||
@@ -99,3 +100,3 @@ } | ||
if (!(apiDefinitionRef.exportName in docPackage.exports)) { | ||
reportError("Unable to find referenced export \"" + apiDefinitionRef.toExportString() + "\"\""); | ||
warnings.push("Unable to find referenced export \"" + apiDefinitionRef.toExportString() + "\"\""); | ||
return undefined; | ||
@@ -132,3 +133,3 @@ } | ||
// member name was not found, apiDefinitionRef is invalid | ||
reportError("Unable to find referenced member \"" + apiDefinitionRef.toMemberString() + "\""); | ||
warnings.push("Unable to find referenced member \"" + apiDefinitionRef.toMemberString() + "\""); | ||
return undefined; | ||
@@ -145,3 +146,3 @@ } | ||
*/ | ||
DocItemLoader.prototype.getPackage = function (apiDefinitionRef, reportError) { | ||
DocItemLoader.prototype.getPackage = function (apiDefinitionRef) { | ||
var cachePackageName = ''; | ||
@@ -160,9 +161,8 @@ // We concatenate the scopeName and packageName in case there are packageName conflicts | ||
// Doesn't exist in cache, attempt to load the json file | ||
var packageJsonFilePath = path.join(this._projectFolder, 'node_modules', apiDefinitionRef.scopeName, apiDefinitionRef.packageName, "dist/" + apiDefinitionRef.packageName + ".api.json"); | ||
if (!fsx.existsSync(path.join(packageJsonFilePath))) { | ||
// package not found in node_modules | ||
reportError("Unable to find referenced package \"" + apiDefinitionRef.toScopePackageString() + "\""); | ||
return; | ||
var apiJsonFilePath = path.join(this._projectFolder, 'node_modules', apiDefinitionRef.scopeName, apiDefinitionRef.packageName, "dist/" + apiDefinitionRef.packageName + ".api.json"); | ||
if (!fsx.existsSync(path.join(apiJsonFilePath))) { | ||
// Error should be handled by the caller | ||
return undefined; | ||
} | ||
return this.loadPackageIntoCache(packageJsonFilePath); | ||
return this.loadPackageIntoCache(apiJsonFilePath, cachePackageName); | ||
}; | ||
@@ -173,4 +173,4 @@ /** | ||
*/ | ||
DocItemLoader.prototype.loadPackageIntoCache = function (packageJsonFilePath) { | ||
var apiPackage = JsonFile_1.default.loadJsonFile(packageJsonFilePath); | ||
DocItemLoader.prototype.loadPackageIntoCache = function (apiJsonFilePath, cachePackageName) { | ||
var apiPackage = JsonFile_1.default.loadJsonFile(apiJsonFilePath); | ||
// Validate that the output conforms to our JSON schema | ||
@@ -184,4 +184,3 @@ var apiJsonSchema = JsonFile_1.default.loadJsonFile(path.join(__dirname, './schemas/api-json-schema.json')); | ||
}); | ||
var packageName = path.basename(packageJsonFilePath).split('.').shift(); | ||
this._cache.set(packageName, apiPackage); | ||
this._cache.set(cachePackageName, apiPackage); | ||
return apiPackage; | ||
@@ -188,0 +187,0 @@ }; |
@@ -16,3 +16,3 @@ { | ||
"exports": { | ||
"IExternalPackageLookup": { | ||
"IExternalPackageLookupInheritDoc": { | ||
"kind": "interface", | ||
@@ -47,15 +47,2 @@ "extends": "", | ||
}, | ||
"inheritCorrectlyButNotFound": { | ||
"kind": "enum", | ||
"values": {}, | ||
"deprecatedMessage": [], | ||
"summary": [ | ||
{ | ||
"kind": "textDocElement", | ||
"value": "See documentation for @microsoft/sp-core-library:Display" | ||
} | ||
], | ||
"remarks": [], | ||
"isBeta": false | ||
}, | ||
"inheritDisplayMode": { | ||
@@ -181,3 +168,3 @@ "kind": "enum", | ||
"kind": "textDocElement", | ||
"value": "This doc has an invalid tag that should throw an error (Error #5)" | ||
"value": "This doc has an invalid tag that should throw an error" | ||
} | ||
@@ -204,6 +191,2 @@ ], | ||
"value": "This doc has an unknown inline tag" | ||
}, | ||
{ | ||
"kind": "textDocElement", | ||
"value": "(Error #6)" | ||
} | ||
@@ -228,3 +211,3 @@ ], | ||
"kind": "textDocElement", | ||
"value": "(Error #9). The displayName is not allowed to have non word characters." | ||
"value": "The displayName is not allowed to have non word characters." | ||
} | ||
@@ -246,6 +229,2 @@ ], | ||
"value": "This doc has too few params for link tag" | ||
}, | ||
{ | ||
"kind": "textDocElement", | ||
"value": "(Error #7)" | ||
} | ||
@@ -403,2 +382,15 @@ ], | ||
}, | ||
"packageLocatedButExportNotFound": { | ||
"kind": "enum", | ||
"values": {}, | ||
"deprecatedMessage": [], | ||
"summary": [ | ||
{ | ||
"kind": "textDocElement", | ||
"value": "See documentation for @microsoft/sp-core-library:Display" | ||
} | ||
], | ||
"remarks": [], | ||
"isBeta": false | ||
}, | ||
"TestMissingCommentStar": { | ||
@@ -405,0 +397,0 @@ "kind": "enum", |
@@ -53,2 +53,3 @@ import * as ts from 'typescript'; | ||
private _compilerOptions; | ||
private _packageFolder; | ||
/** | ||
@@ -60,2 +61,6 @@ * The default implementation of ApiErrorHandler, which merely writes to console.log(). | ||
/** | ||
* Getter for the package folder that Extractor is analyzing. | ||
*/ | ||
readonly packageFolder: string; | ||
/** | ||
* Analyzes the specified project. | ||
@@ -62,0 +67,0 @@ */ |
@@ -8,2 +8,3 @@ "use strict"; | ||
var DocItemLoader_1 = require("./DocItemLoader"); | ||
var PackageJsonHelpers_1 = require("./PackageJsonHelpers"); | ||
/** | ||
@@ -26,2 +27,12 @@ * The main entry point for the "api-extractor" utility. The Analyzer object invokes the | ||
}; | ||
Object.defineProperty(Extractor.prototype, "packageFolder", { | ||
/** | ||
* Getter for the package folder that Extractor is analyzing. | ||
*/ | ||
get: function () { | ||
return this._packageFolder; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
/** | ||
@@ -45,4 +56,9 @@ * Analyzes the specified project. | ||
} | ||
// Assign _packageFolder by probing upwards from entryPointFile until we find a package.json | ||
var currentPath = path.resolve(options.entryPointFile); | ||
// This is guaranteed to succeed since we do check prior to this point | ||
this._packageFolder = PackageJsonHelpers_1.default.tryFindPackagePathUpwards(currentPath); | ||
this.package = new ApiPackage_1.default(this, rootFile); // construct members | ||
this.package.completeInitialization(); // creates ApiDocumentation | ||
this.package.visitTypeReferencesForApiItem(); | ||
}; | ||
@@ -74,3 +90,3 @@ /** | ||
var externalJsonFilePath = path.join(externalJsonCollectionPath, file); | ||
_this.docItemLoader.loadPackageIntoCache(externalJsonFilePath); | ||
_this.docItemLoader.loadPackageIntoCache(externalJsonFilePath, path.parse(file).name.split('.').shift()); | ||
} | ||
@@ -77,0 +93,0 @@ }); |
@@ -56,4 +56,4 @@ /// <reference types="mocha" /> | ||
var inputFolder = './testInputs/example2'; | ||
var outputFile = './lib/example2-output.ts'; | ||
var expectedFile = path.join(inputFolder, 'example2-output.ts'); | ||
var outputFile = './lib/example2-output.api.ts'; | ||
var expectedFile = path.join(inputFolder, 'example2-output.api.ts'); | ||
var compilerOptions = { | ||
@@ -71,3 +71,3 @@ target: ts.ScriptTarget.ES5, | ||
extractor.analyze({ | ||
entryPointFile: path.join(inputFolder, 'index.ts') | ||
entryPointFile: path.join(inputFolder, 'src/index.ts') | ||
}); | ||
@@ -78,2 +78,26 @@ var apiFileGenerator = new ApiFileGenerator_1.default(); | ||
}); | ||
it('Example 4', function () { | ||
capturedErrors = []; | ||
var inputFolder = './testInputs/example4'; | ||
var outputFile = './lib/example4-output.api.ts'; | ||
var expectedFile = path.join(inputFolder, 'example4-output.api.ts'); | ||
var compilerOptions = { | ||
target: ts.ScriptTarget.ES5, | ||
module: ts.ModuleKind.CommonJS, | ||
moduleResolution: ts.ModuleResolutionKind.NodeJs, | ||
rootDir: inputFolder, | ||
typeRoots: ['./'] // We need to ignore @types in these tests | ||
}; | ||
var extractor = new Extractor_1.default({ | ||
compilerOptions: compilerOptions, | ||
errorHandler: testErrorHandler | ||
}); | ||
extractor.analyze({ | ||
entryPointFile: path.join(inputFolder, 'src/index.ts') | ||
}); | ||
var apiFileGenerator = new ApiFileGenerator_1.default(); | ||
apiFileGenerator.writeApiFile(outputFile, extractor); | ||
assertFileMatchesExpected(outputFile, expectedFile); | ||
chai_1.assert.equal(capturedErrors.length, 0); | ||
}); | ||
}); | ||
@@ -80,0 +104,0 @@ }); |
@@ -34,3 +34,3 @@ /// <reference types="mocha" /> | ||
extractor.analyze({ | ||
entryPointFile: path.join(inputFolder, 'index.ts') | ||
entryPointFile: path.join(inputFolder, 'src/index.ts') | ||
}); | ||
@@ -37,0 +37,0 @@ var apiJsonGenerator = new ApiJsonGenerator_1.default(); |
@@ -14,2 +14,3 @@ "use strict"; | ||
this.remarks = remarks; | ||
this.deprecatedMessage = deprecatedMessage; | ||
this.isBeta = isBeta; | ||
@@ -16,0 +17,0 @@ this.params = params; |
@@ -41,3 +41,4 @@ /// <reference types="mocha" /> | ||
it('_parseApiReferenceExpression() without scope name and invalid memberName', function () { | ||
// (Error #6) | ||
// This won't raise an error (based on our current decision to only show warnings in the *.api.ts | ||
// files if we can't find a reference) | ||
apiReferenceExpr = 'sp-core-library:Guid:equals'; | ||
@@ -44,0 +45,0 @@ var caughtError = false; |
@@ -39,2 +39,5 @@ /// <reference types="mocha" /> | ||
}); | ||
// These warnings would normally be printed at the bottom | ||
// of the source package's '*.api.ts' file. | ||
var warnings = []; | ||
/** | ||
@@ -46,3 +49,3 @@ * Dummy class wrapping ApiDocumentation to test its protected methods | ||
function TestApiDocumentation() { | ||
return _super.call(this, 'Some summary\n@remarks and some remarks\n@public', extractor.docItemLoader, extractor, console.log) || this; | ||
return _super.call(this, 'Some summary\n@remarks and some remarks\n@public', extractor.docItemLoader, extractor, console.log, warnings) || this; | ||
} | ||
@@ -57,3 +60,3 @@ TestApiDocumentation.prototype.parseParam = function (tokenizer) { | ||
extractor.analyze({ | ||
entryPointFile: path.join(inputFolder, 'index.ts') | ||
entryPointFile: path.join(inputFolder, 'src/index.ts') | ||
}); | ||
@@ -60,0 +63,0 @@ myDocumentedClass = extractor.package.getSortedMemberItems() |
@@ -10,2 +10,3 @@ /// <reference types="mocha" /> | ||
var ApiJsonGenerator_1 = require("../generators/ApiJsonGenerator"); | ||
var ApiFileGenerator_1 = require("../generators/ApiFileGenerator"); | ||
/* tslint:disable:no-function-expression - Mocha uses a poorly scoped "this" pointer */ | ||
@@ -16,2 +17,5 @@ var capturedErrors = []; | ||
} | ||
// These warnings would normally be printed at the bottom | ||
// of the source package's '*.api.ts' file. | ||
var warnings = []; | ||
describe('DocItemLoader tests', function () { | ||
@@ -22,4 +26,6 @@ this.timeout(10000); | ||
var inputFolder = './testInputs/example3'; | ||
var outputFile = './lib/example3-output.json'; | ||
var expectedFile = path.join(inputFolder, 'example3-output.json'); | ||
var outputJsonFile = './lib/example3-output.json'; | ||
var outputApiFile = './lib/example3-output.api.ts'; | ||
var expectedJsonFile = path.join(inputFolder, 'example3-output.json'); | ||
var expectedApiFile = path.join(inputFolder, 'example3-output.api.ts'); | ||
var compilerOptions = { | ||
@@ -38,11 +44,17 @@ target: ts.ScriptTarget.ES5, | ||
extractor.analyze({ | ||
entryPointFile: path.join(inputFolder, 'index.ts') | ||
entryPointFile: './testInputs/example3/src/index.ts' | ||
}); | ||
var apiJsonGenerator = new ApiJsonGenerator_1.default(); | ||
apiJsonGenerator.writeJsonFile(outputFile, extractor); | ||
chai_1.assert.equal(capturedErrors.length, 3); | ||
chai_1.assert.equal(capturedErrors[0].message, 'Unable to find referenced member \"MyClass.methodWithTwoParams\"'); | ||
chai_1.assert.equal(capturedErrors[1].message, 'circular reference'); | ||
chai_1.assert.equal(capturedErrors[2].message, 'Unable to link to "Internal" API item'); | ||
TestFileComparer_1.default.assertFileMatchesExpected(outputFile, expectedFile); | ||
apiJsonGenerator.writeJsonFile(outputJsonFile, extractor); | ||
// This is one error whose output is only visible in the form | ||
// of a 'warning' message in the 'example3-output.api.ts' file. | ||
// 'Unable to find referenced member \"MyClass.methodWithTwoParams\"' is the message | ||
// that should appear. | ||
var apiFileGenerator = new ApiFileGenerator_1.default(); | ||
apiFileGenerator.writeApiFile(outputApiFile, extractor); | ||
chai_1.assert.equal(capturedErrors.length, 2); | ||
chai_1.assert.equal(capturedErrors[0].message, 'circular reference'); | ||
chai_1.assert.equal(capturedErrors[1].message, 'Unable to link to "Internal" API item'); | ||
TestFileComparer_1.default.assertFileMatchesExpected(outputJsonFile, expectedJsonFile); | ||
TestFileComparer_1.default.assertFileMatchesExpected(outputApiFile, expectedApiFile); | ||
}); | ||
@@ -49,0 +61,0 @@ }); |
{ | ||
"name": "@microsoft/api-extractor", | ||
"version": "2.0.2", | ||
"version": "2.0.3", | ||
"description": "Validatation, documentation, and auditing for the exported API of a TypeScript package", | ||
@@ -5,0 +5,0 @@ "main": "lib/index.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
674384
158
8275
9