Invoke "prettier . --write" to prettify all source files

This commit is contained in:
Pete Gonzalez 2020-11-24 20:12:30 -08:00
Родитель 8fec2c3cab
Коммит 5e8673388d
108 изменённых файлов: 2633 добавлений и 2427 удалений

Просмотреть файл

@ -1,10 +1,7 @@
// This is a workaround for https://github.com/eslint/eslint/issues/3458 // This is a workaround for https://github.com/eslint/eslint/issues/3458
require("@rushstack/eslint-config/patch/modern-module-resolution"); require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = { module.exports = {
extends: [ extends: ['@rushstack/eslint-config/profile/node', '@rushstack/eslint-config/mixins/friendly-locals'],
"@rushstack/eslint-config/profile/node", parserOptions: { tsconfigRootDir: __dirname }
"@rushstack/eslint-config/mixins/friendly-locals",
],
parserOptions: { tsconfigRootDir: __dirname },
}; };

Просмотреть файл

@ -1,17 +1,16 @@
/**
/** * Returns the average of two numbers.
* Returns the average of two numbers. *
* * @remarks
* @remarks * This method is part of the {@link core-library#Statistics | Statistics subsystem}.
* This method is part of the {@link core-library#Statistics | Statistics subsystem}. * This incomplete HTML tag should be reported as an error: <tag
* This incomplete HTML tag should be reported as an error: <tag *
* * @privateRemarks
* @privateRemarks * This content should not show up on the web site.
* This content should not show up on the web site. *
* * @param x - The first input number
* @param x - The first input number * @param y - The second input number
* @param y - The second input number * @returns The average of `x` and `y`
* @returns The average of `x` and `y` *
* * @beta
* @beta */
*/

Просмотреть файл

@ -4,7 +4,6 @@ import { DocNode, DocExcerpt } from '@microsoft/tsdoc';
* This is a simplistic solution until we implement proper DocNode rendering APIs. * This is a simplistic solution until we implement proper DocNode rendering APIs.
*/ */
export class Formatter { export class Formatter {
public static renderDocNode(docNode: DocNode): string { public static renderDocNode(docNode: DocNode): string {
let result: string = ''; let result: string = '';
if (docNode) { if (docNode) {

Просмотреть файл

@ -11,39 +11,41 @@ import * as tsdoc from '@microsoft/tsdoc';
* https://github.com/Microsoft/TypeScript/blob/v3.0.3/src/compiler/utilities.ts#L6382 * https://github.com/Microsoft/TypeScript/blob/v3.0.3/src/compiler/utilities.ts#L6382
*/ */
function isDeclarationKind(kind: ts.SyntaxKind): boolean { function isDeclarationKind(kind: ts.SyntaxKind): boolean {
return kind === ts.SyntaxKind.ArrowFunction return (
|| kind === ts.SyntaxKind.BindingElement kind === ts.SyntaxKind.ArrowFunction ||
|| kind === ts.SyntaxKind.ClassDeclaration kind === ts.SyntaxKind.BindingElement ||
|| kind === ts.SyntaxKind.ClassExpression kind === ts.SyntaxKind.ClassDeclaration ||
|| kind === ts.SyntaxKind.Constructor kind === ts.SyntaxKind.ClassExpression ||
|| kind === ts.SyntaxKind.EnumDeclaration kind === ts.SyntaxKind.Constructor ||
|| kind === ts.SyntaxKind.EnumMember kind === ts.SyntaxKind.EnumDeclaration ||
|| kind === ts.SyntaxKind.ExportSpecifier kind === ts.SyntaxKind.EnumMember ||
|| kind === ts.SyntaxKind.FunctionDeclaration kind === ts.SyntaxKind.ExportSpecifier ||
|| kind === ts.SyntaxKind.FunctionExpression kind === ts.SyntaxKind.FunctionDeclaration ||
|| kind === ts.SyntaxKind.GetAccessor kind === ts.SyntaxKind.FunctionExpression ||
|| kind === ts.SyntaxKind.ImportClause kind === ts.SyntaxKind.GetAccessor ||
|| kind === ts.SyntaxKind.ImportEqualsDeclaration kind === ts.SyntaxKind.ImportClause ||
|| kind === ts.SyntaxKind.ImportSpecifier kind === ts.SyntaxKind.ImportEqualsDeclaration ||
|| kind === ts.SyntaxKind.InterfaceDeclaration kind === ts.SyntaxKind.ImportSpecifier ||
|| kind === ts.SyntaxKind.JsxAttribute kind === ts.SyntaxKind.InterfaceDeclaration ||
|| kind === ts.SyntaxKind.MethodDeclaration kind === ts.SyntaxKind.JsxAttribute ||
|| kind === ts.SyntaxKind.MethodSignature kind === ts.SyntaxKind.MethodDeclaration ||
|| kind === ts.SyntaxKind.ModuleDeclaration kind === ts.SyntaxKind.MethodSignature ||
|| kind === ts.SyntaxKind.NamespaceExportDeclaration kind === ts.SyntaxKind.ModuleDeclaration ||
|| kind === ts.SyntaxKind.NamespaceImport kind === ts.SyntaxKind.NamespaceExportDeclaration ||
|| kind === ts.SyntaxKind.Parameter kind === ts.SyntaxKind.NamespaceImport ||
|| kind === ts.SyntaxKind.PropertyAssignment kind === ts.SyntaxKind.Parameter ||
|| kind === ts.SyntaxKind.PropertyDeclaration kind === ts.SyntaxKind.PropertyAssignment ||
|| kind === ts.SyntaxKind.PropertySignature kind === ts.SyntaxKind.PropertyDeclaration ||
|| kind === ts.SyntaxKind.SetAccessor kind === ts.SyntaxKind.PropertySignature ||
|| kind === ts.SyntaxKind.ShorthandPropertyAssignment kind === ts.SyntaxKind.SetAccessor ||
|| kind === ts.SyntaxKind.TypeAliasDeclaration kind === ts.SyntaxKind.ShorthandPropertyAssignment ||
|| kind === ts.SyntaxKind.TypeParameter kind === ts.SyntaxKind.TypeAliasDeclaration ||
|| kind === ts.SyntaxKind.VariableDeclaration kind === ts.SyntaxKind.TypeParameter ||
|| kind === ts.SyntaxKind.JSDocTypedefTag kind === ts.SyntaxKind.VariableDeclaration ||
|| kind === ts.SyntaxKind.JSDocCallbackTag kind === ts.SyntaxKind.JSDocTypedefTag ||
|| kind === ts.SyntaxKind.JSDocPropertyTag; kind === ts.SyntaxKind.JSDocCallbackTag ||
kind === ts.SyntaxKind.JSDocPropertyTag
);
} }
/** /**
@ -61,16 +63,18 @@ function getJSDocCommentRanges(node: ts.Node, text: string): ts.CommentRange[] {
case ts.SyntaxKind.FunctionExpression: case ts.SyntaxKind.FunctionExpression:
case ts.SyntaxKind.ArrowFunction: case ts.SyntaxKind.ArrowFunction:
case ts.SyntaxKind.ParenthesizedExpression: case ts.SyntaxKind.ParenthesizedExpression:
commentRanges.push(...ts.getTrailingCommentRanges(text, node.pos) || []); commentRanges.push(...(ts.getTrailingCommentRanges(text, node.pos) || []));
break; break;
} }
commentRanges.push(...ts.getLeadingCommentRanges(text, node.pos) || []); commentRanges.push(...(ts.getLeadingCommentRanges(text, node.pos) || []));
// True if the comment starts with '/**' but not if it is '/**/' // True if the comment starts with '/**' but not if it is '/**/'
return commentRanges.filter((comment) => return commentRanges.filter(
text.charCodeAt(comment.pos + 1) === 0x2A /* ts.CharacterCodes.asterisk */ && (comment) =>
text.charCodeAt(comment.pos + 2) === 0x2A /* ts.CharacterCodes.asterisk */ && text.charCodeAt(comment.pos + 1) === 0x2a /* ts.CharacterCodes.asterisk */ &&
text.charCodeAt(comment.pos + 3) !== 0x2F /* ts.CharacterCodes.slash */); text.charCodeAt(comment.pos + 2) === 0x2a /* ts.CharacterCodes.asterisk */ &&
text.charCodeAt(comment.pos + 3) !== 0x2f /* ts.CharacterCodes.slash */
);
} }
interface IFoundComment { interface IFoundComment {
@ -114,7 +118,7 @@ function walkCompilerAstAndFindComments(node: ts.Node, indent: string, foundComm
console.log(`${indent}- ${ts.SyntaxKind[node.kind]}${foundCommentsSuffix}`); console.log(`${indent}- ${ts.SyntaxKind[node.kind]}${foundCommentsSuffix}`);
return node.forEachChild(child => walkCompilerAstAndFindComments(child, indent + ' ', foundComments)); return node.forEachChild((child) => walkCompilerAstAndFindComments(child, indent + ' ', foundComments));
} }
function dumpTSDocTree(docNode: tsdoc.DocNode, indent: string): void { function dumpTSDocTree(docNode: tsdoc.DocNode, indent: string): void {
@ -181,8 +185,8 @@ function parseTSDoc(foundComment: IFoundComment): void {
// Since we have the compiler's analysis, use it to calculate the line/column information, // Since we have the compiler's analysis, use it to calculate the line/column information,
// since this is currently faster than TSDoc's TextRange.getLocation() lookup. // since this is currently faster than TSDoc's TextRange.getLocation() lookup.
const location: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(message.textRange.pos); const location: ts.LineAndCharacter = sourceFile.getLineAndCharacterOfPosition(message.textRange.pos);
const formattedMessage: string = `${sourceFile.fileName}(${location.line + 1},${location.character + 1}):` const formattedMessage: string =
+ ` [TSDoc] ${message}`; `${sourceFile.fileName}(${location.line + 1},${location.character + 1}):` + ` [TSDoc] ${message}`;
console.log(formattedMessage); console.log(formattedMessage);
} }
} }
@ -193,7 +197,7 @@ function parseTSDoc(foundComment: IFoundComment): void {
console.log(os.EOL + colors.cyan(`The ${customModifierDefinition.tagName} modifier was NOT FOUND.`)); console.log(os.EOL + colors.cyan(`The ${customModifierDefinition.tagName} modifier was NOT FOUND.`));
} }
console.log(os.EOL + colors.green('Visiting TSDoc\'s DocNode tree') + os.EOL); console.log(os.EOL + colors.green("Visiting TSDoc's DocNode tree") + os.EOL);
dumpTSDocTree(docComment, ''); dumpTSDocTree(docComment, '');
} }
@ -206,13 +210,13 @@ export function advancedDemo(): void {
const inputFilename: string = path.resolve(path.join(__dirname, '..', 'assets', 'advanced-input.ts')); const inputFilename: string = path.resolve(path.join(__dirname, '..', 'assets', 'advanced-input.ts'));
const compilerOptions: ts.CompilerOptions = { const compilerOptions: ts.CompilerOptions = {
'target': ts.ScriptTarget.ES5 target: ts.ScriptTarget.ES5
}; };
// Compile the input // Compile the input
console.log('Invoking the TypeScript compiler to analyze assets/advanced-input.ts...'); console.log('Invoking the TypeScript compiler to analyze assets/advanced-input.ts...');
const program: ts.Program = ts.createProgram([ inputFilename ], compilerOptions); const program: ts.Program = ts.createProgram([inputFilename], compilerOptions);
// Report any compiler errors // Report any compiler errors
const compilerDiagnostics: ReadonlyArray<ts.Diagnostic> = program.getSemanticDiagnostics(); const compilerDiagnostics: ReadonlyArray<ts.Diagnostic> = program.getSemanticDiagnostics();
@ -220,9 +224,12 @@ export function advancedDemo(): void {
for (const diagnostic of compilerDiagnostics) { for (const diagnostic of compilerDiagnostics) {
const message: string = ts.flattenDiagnosticMessageText(diagnostic.messageText, os.EOL); const message: string = ts.flattenDiagnosticMessageText(diagnostic.messageText, os.EOL);
if (diagnostic.file) { if (diagnostic.file) {
const location: ts.LineAndCharacter = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!); const location: ts.LineAndCharacter = diagnostic.file.getLineAndCharacterOfPosition(
const formattedMessage: string = `${diagnostic.file.fileName}(${location.line + 1},${location.character + 1}):` diagnostic.start!
+ ` [TypeScript] ${message}`; );
const formattedMessage: string =
`${diagnostic.file.fileName}(${location.line + 1},${location.character + 1}):` +
` [TypeScript] ${message}`;
console.log(colors.red(formattedMessage)); console.log(colors.red(formattedMessage));
} else { } else {
console.log(colors.red(message)); console.log(colors.red(message));

Просмотреть файл

@ -27,7 +27,13 @@ export function simpleDemo(): void {
console.log(colors.gray('>>>>>>')); console.log(colors.gray('>>>>>>'));
console.log(os.EOL + colors.green('Extracted Lines:') + os.EOL); console.log(os.EOL + colors.green('Extracted Lines:') + os.EOL);
console.log(JSON.stringify(parserContext.lines.map(x => x.toString()), undefined, ' ')); console.log(
JSON.stringify(
parserContext.lines.map((x) => x.toString()),
undefined,
' '
)
);
console.log(os.EOL + colors.green('Parser Log Messages:') + os.EOL); console.log(os.EOL + colors.green('Parser Log Messages:') + os.EOL);
@ -43,24 +49,26 @@ export function simpleDemo(): void {
const docComment: DocComment = parserContext.docComment; const docComment: DocComment = parserContext.docComment;
console.log(colors.cyan('Summary: ') console.log(colors.cyan('Summary: ') + JSON.stringify(Formatter.renderDocNode(docComment.summarySection)));
+ JSON.stringify(Formatter.renderDocNode(docComment.summarySection)));
if (docComment.remarksBlock) { if (docComment.remarksBlock) {
console.log(colors.cyan('Remarks: ') console.log(
+ JSON.stringify(Formatter.renderDocNode(docComment.remarksBlock.content))); colors.cyan('Remarks: ') + JSON.stringify(Formatter.renderDocNode(docComment.remarksBlock.content))
);
} }
for (const paramBlock of docComment.params.blocks) { for (const paramBlock of docComment.params.blocks) {
console.log(colors.cyan(`Parameter "${paramBlock.parameterName}": `) console.log(
+ JSON.stringify(Formatter.renderDocNode(paramBlock.content))); colors.cyan(`Parameter "${paramBlock.parameterName}": `) +
JSON.stringify(Formatter.renderDocNode(paramBlock.content))
);
} }
if (docComment.returnsBlock) { if (docComment.returnsBlock) {
console.log(colors.cyan('Returns: ') console.log(
+ JSON.stringify(Formatter.renderDocNode(docComment.returnsBlock.content))); colors.cyan('Returns: ') + JSON.stringify(Formatter.renderDocNode(docComment.returnsBlock.content))
);
} }
console.log(colors.cyan('Modifiers: ') console.log(colors.cyan('Modifiers: ') + docComment.modifierTagSet.nodes.map((x) => x.tagName).join(', '));
+ docComment.modifierTagSet.nodes.map(x => x.tagName).join(', '));
} }

Просмотреть файл

@ -1,15 +1,15 @@
variables: variables:
FORCE_COLOR: 1 FORCE_COLOR: 1
jobs: jobs:
- job: PRBuild - job: PRBuild
condition: succeeded() condition: succeeded()
strategy: strategy:
maxParallel: 2 maxParallel: 2
matrix: matrix:
'NodeJs 10': 'NodeJs 10':
NodeVersion: 10 NodeVersion: 10
'NodeJs 12': 'NodeJs 12':
NodeVersion: 12 NodeVersion: 12
steps: steps:
- checkout: self - checkout: self
- template: templates/build.yaml - template: templates/build.yaml

Просмотреть файл

@ -4,10 +4,10 @@ variables:
NodeVersion: 12 NodeVersion: 12
FORCE_COLOR: 1 FORCE_COLOR: 1
steps: steps:
- checkout: self - checkout: self
persistCredentials: true persistCredentials: true
- template: templates/build.yaml - template: templates/build.yaml
- script: 'node common/scripts/install-run-rush.js version --bump --version-policy BOGUS --target-branch $(Build.SourceBranchName)' - script: 'node common/scripts/install-run-rush.js version --bump --version-policy BOGUS --target-branch $(Build.SourceBranchName)'
displayName: 'Rush Version' displayName: 'Rush Version'
- script: 'node common/scripts/install-run-rush.js publish --apply --publish --include-all --npm-auth-token $(npmToken)' - script: 'node common/scripts/install-run-rush.js publish --apply --publish --include-all --npm-auth-token $(npmToken)'
displayName: 'Rush Publish' displayName: 'Rush Publish'

Просмотреть файл

@ -4,8 +4,8 @@ variables:
NodeVersion: 12 NodeVersion: 12
FORCE_COLOR: 1 FORCE_COLOR: 1
steps: steps:
- checkout: self - checkout: self
persistCredentials: true persistCredentials: true
- template: templates/build.yaml - template: templates/build.yaml
- script: 'node common/scripts/install-run-rush.js publish --apply --publish --include-all --npm-auth-token $(npmToken)' - script: 'node common/scripts/install-run-rush.js publish --apply --publish --include-all --npm-auth-token $(npmToken)'
displayName: 'Rush Publish' displayName: 'Rush Publish'

Просмотреть файл

@ -1,13 +1,13 @@
resources: resources:
- repo: self - repo: self
variables: variables:
NodeVersion: 12 NodeVersion: 12
FORCE_COLOR: 1 FORCE_COLOR: 1
steps: steps:
- checkout: self - checkout: self
- template: templates/build.yaml - template: templates/build.yaml
- task: PublishBuildArtifacts@1 - task: PublishBuildArtifacts@1
displayName: 'Publish Playground Artifacts' displayName: 'Publish Playground Artifacts'
inputs: inputs:
PathtoPublish: playground/dist PathtoPublish: playground/dist
ArtifactName: playground ArtifactName: playground

Просмотреть файл

@ -1,18 +1,18 @@
steps: steps:
- task: NodeTool@0 - task: NodeTool@0
displayName: 'Use Node $(NodeVersion).x' displayName: 'Use Node $(NodeVersion).x'
inputs: inputs:
versionSpec: '$(NodeVersion).x' versionSpec: '$(NodeVersion).x'
checkLatest: true checkLatest: true
- script: 'git config --local user.email rushbot@users.noreply.github.com' - script: 'git config --local user.email rushbot@users.noreply.github.com'
displayName: 'git config email' displayName: 'git config email'
- script: 'git config --local user.name Rushbot' - script: 'git config --local user.name Rushbot'
displayName: 'git config name' displayName: 'git config name'
- script: 'node common/scripts/install-run-rush.js change --verify' - script: 'node common/scripts/install-run-rush.js change --verify'
displayName: 'Verify Change Logs' displayName: 'Verify Change Logs'
- script: 'node common/scripts/install-run-rush.js check' - script: 'node common/scripts/install-run-rush.js check'
displayName: 'Rush Check' displayName: 'Rush Check'
- script: 'node common/scripts/install-run-rush.js install' - script: 'node common/scripts/install-run-rush.js install'
displayName: 'Rush Install' displayName: 'Rush Install'
- script: 'node common/scripts/install-run-rush.js rebuild --verbose --production' - script: 'node common/scripts/install-run-rush.js rebuild --verbose --production'
displayName: 'Rush Rebuild' displayName: 'Rush Rebuild'

Просмотреть файл

@ -3,7 +3,7 @@
* Rush features. For full documentation, please see https://rushjs.io * Rush features. For full documentation, please see https://rushjs.io
*/ */
{ {
"$schema": "https://developer.microsoft.com/json-schemas/rush/v5/experiments.schema.json", "$schema": "https://developer.microsoft.com/json-schemas/rush/v5/experiments.schema.json"
/** /**
* Rush 5.14.0 improved incremental builds to ignore spurious changes in the pnpm-lock.json file. * Rush 5.14.0 improved incremental builds to ignore spurious changes in the pnpm-lock.json file.

Просмотреть файл

@ -27,7 +27,6 @@ module.exports = {
* The return value is the updated object. * The return value is the updated object.
*/ */
function readPackage(packageJson, context) { function readPackage(packageJson, context) {
// // The karma types have a missing dependency on typings from the log4js package. // // The karma types have a missing dependency on typings from the log4js package.
// if (packageJson.name === '@types/karma') { // if (packageJson.name === '@types/karma') {
// context.log('Fixed up dependencies for @types/karma'); // context.log('Fixed up dependencies for @types/karma');

Просмотреть файл

@ -20,21 +20,21 @@
// * SemVer range is usually restricted to a single version. // * SemVer range is usually restricted to a single version.
// */ // */
// "definitionName": "lockStepVersion", // "definitionName": "lockStepVersion",
// //
// /** // /**
// * (Required) The name that will be used for the "versionPolicyName" field in rush.json. // * (Required) The name that will be used for the "versionPolicyName" field in rush.json.
// * This name is also used command-line parameters such as "--version-policy" // * This name is also used command-line parameters such as "--version-policy"
// * and "--to-version-policy". // * and "--to-version-policy".
// */ // */
// "policyName": "MyBigFramework", // "policyName": "MyBigFramework",
// //
// /** // /**
// * (Required) The current version. All packages belonging to the set should have this version // * (Required) The current version. All packages belonging to the set should have this version
// * in the current branch. When bumping versions, Rush uses this to determine the next version. // * in the current branch. When bumping versions, Rush uses this to determine the next version.
// * (The "version" field in package.json is NOT considered.) // * (The "version" field in package.json is NOT considered.)
// */ // */
// "version": "1.0.0", // "version": "1.0.0",
// //
// /** // /**
// * (Required) The type of bump that will be performed when publishing the next release. // * (Required) The type of bump that will be performed when publishing the next release.
// * When creating a release branch in Git, this field should be updated according to the // * When creating a release branch in Git, this field should be updated according to the
@ -43,7 +43,7 @@
// * Valid values are: "prerelease", "release", "minor", "patch", "major" // * Valid values are: "prerelease", "release", "minor", "patch", "major"
// */ // */
// "nextBump": "prerelease", // "nextBump": "prerelease",
// //
// /** // /**
// * (Optional) If specified, all packages in the set share a common CHANGELOG.md file. // * (Optional) If specified, all packages in the set share a common CHANGELOG.md file.
// * This file is stored with the specified "main" project, which must be a member of the set. // * This file is stored with the specified "main" project, which must be a member of the set.
@ -53,7 +53,7 @@
// */ // */
// "mainProject": "my-app" // "mainProject": "my-app"
// }, // },
// //
// { // {
// /** // /**
// * (Required) Indicates the kind of version policy being defined ("lockStepVersion" or "individualVersion"). // * (Required) Indicates the kind of version policy being defined ("lockStepVersion" or "individualVersion").
@ -66,9 +66,9 @@
// * is changed. // * is changed.
// */ // */
// "definitionName": "individualVersion", // "definitionName": "individualVersion",
// //
// "policyName": "MyRandomLibraries", // "policyName": "MyRandomLibraries",
// //
// /** // /**
// * (Optional) This can be used to enforce that all packages in the set must share a common // * (Optional) This can be used to enforce that all packages in the set must share a common
// * major version number, e.g. because they are from the same major release branch. // * major version number, e.g. because they are from the same major release branch.
@ -77,7 +77,7 @@
// * to the types of changes made to each project, according to the "rush change" command. // * to the types of changes made to each project, according to the "rush change" command.
// */ // */
// "lockedMajor": 3, // "lockedMajor": 3,
// //
// /** // /**
// * (Optional) When publishing is managed by Rush, by default the "rush change" command will // * (Optional) When publishing is managed by Rush, by default the "rush change" command will
// * request changes for any projects that are modified by a pull request. These change entries // * request changes for any projects that are modified by a pull request. These change entries

Просмотреть файл

@ -1,10 +1,7 @@
// This is a workaround for https://github.com/eslint/eslint/issues/3458 // This is a workaround for https://github.com/eslint/eslint/issues/3458
require("@rushstack/eslint-config/patch/modern-module-resolution"); require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = { module.exports = {
extends: [ extends: ['@rushstack/eslint-config/profile/node', '@rushstack/eslint-config/mixins/friendly-locals'],
"@rushstack/eslint-config/profile/node", parserOptions: { tsconfigRootDir: __dirname }
"@rushstack/eslint-config/mixins/friendly-locals",
],
parserOptions: { tsconfigRootDir: __dirname },
}; };

Просмотреть файл

@ -40,7 +40,7 @@ export class ConfigCache {
// If configFilePath is an empty string, then we'll use the folder of sourceFilePath as our cache key // If configFilePath is an empty string, then we'll use the folder of sourceFilePath as our cache key
// (instead of an empty string) // (instead of an empty string)
const cacheKey: string = configFilePath || (sourceFileFolder + '/'); const cacheKey: string = configFilePath || sourceFileFolder + '/';
Debug.log(`Cache key: "${cacheKey}"`); Debug.log(`Cache key: "${cacheKey}"`);
const nowMs: number = ConfigCache._getTimeInMs(); const nowMs: number = ConfigCache._getTimeInMs();

Просмотреть файл

@ -1,4 +1,3 @@
export class Debug { export class Debug {
// To debug the plugin, temporarily uncomment the body of this function // To debug the plugin, temporarily uncomment the body of this function
public static log(message: string): void { public static log(message: string): void {

Просмотреть файл

@ -1,17 +1,12 @@
import * as eslint from "eslint"; import * as eslint from 'eslint';
import * as ESTree from "estree"; import * as ESTree from 'estree';
import { import { TSDocParser, TextRange, TSDocConfiguration, ParserContext } from '@microsoft/tsdoc';
TSDocParser,
TextRange,
TSDocConfiguration,
ParserContext
} from "@microsoft/tsdoc";
import { TSDocConfigFile } from '@microsoft/tsdoc-config'; import { TSDocConfigFile } from '@microsoft/tsdoc-config';
import { Debug } from './Debug'; import { Debug } from './Debug';
import { ConfigCache } from './ConfigCache'; import { ConfigCache } from './ConfigCache';
const tsdocMessageIds: {[x: string]: string} = {}; const tsdocMessageIds: { [x: string]: string } = {};
const defaultTSDocConfiguration: TSDocConfiguration = new TSDocConfiguration(); const defaultTSDocConfiguration: TSDocConfiguration = new TSDocConfiguration();
defaultTSDocConfiguration.allTsdocMessageIds.forEach((messageId: string) => { defaultTSDocConfiguration.allTsdocMessageIds.forEach((messageId: string) => {
@ -19,34 +14,34 @@ defaultTSDocConfiguration.allTsdocMessageIds.forEach((messageId: string) => {
}); });
interface IPlugin { interface IPlugin {
rules: {[x: string]: eslint.Rule.RuleModule}; rules: { [x: string]: eslint.Rule.RuleModule };
} }
const plugin: IPlugin = { const plugin: IPlugin = {
rules: { rules: {
// NOTE: The actual ESLint rule name will be "tsdoc/syntax". It is calculated by deleting "eslint-plugin-" // NOTE: The actual ESLint rule name will be "tsdoc/syntax". It is calculated by deleting "eslint-plugin-"
// from the NPM package name, and then appending this string. // from the NPM package name, and then appending this string.
"syntax": { syntax: {
meta: { meta: {
messages: { messages: {
"error-loading-config-file": "Error loading TSDoc config file:\n{{details}}", 'error-loading-config-file': 'Error loading TSDoc config file:\n{{details}}',
"error-applying-config": "Error applying TSDoc configuration: {{details}}", 'error-applying-config': 'Error applying TSDoc configuration: {{details}}',
...tsdocMessageIds ...tsdocMessageIds
}, },
type: "problem", type: 'problem',
docs: { docs: {
description: "Validates that TypeScript documentation comments conform to the TSDoc standard", description: 'Validates that TypeScript documentation comments conform to the TSDoc standard',
category: "Stylistic Issues", category: 'Stylistic Issues',
// This package is experimental // This package is experimental
recommended: false, recommended: false,
url: "https://github.com/microsoft/tsdoc/blob/master/eslint-plugin/README.md" url: 'https://github.com/microsoft/tsdoc/blob/master/eslint-plugin/README.md'
} }
}, },
create: (context: eslint.Rule.RuleContext) => { create: (context: eslint.Rule.RuleContext) => {
const sourceFilePath: string = context.getFilename(); const sourceFilePath: string = context.getFilename();
Debug.log(`Linting: "${sourceFilePath}"`); Debug.log(`Linting: "${sourceFilePath}"`);
const tsdocConfiguration: TSDocConfiguration = new TSDocConfiguration() const tsdocConfiguration: TSDocConfiguration = new TSDocConfiguration();
try { try {
const tsdocConfigFile: TSDocConfigFile = ConfigCache.getForSourceFile(sourceFilePath); const tsdocConfigFile: TSDocConfigFile = ConfigCache.getForSourceFile(sourceFilePath);
@ -54,7 +49,7 @@ const plugin: IPlugin = {
if (tsdocConfigFile.hasErrors) { if (tsdocConfigFile.hasErrors) {
context.report({ context.report({
loc: { line: 1, column: 1 }, loc: { line: 1, column: 1 },
messageId: "error-loading-config-file", messageId: 'error-loading-config-file',
data: { data: {
details: tsdocConfigFile.getErrorSummary() details: tsdocConfigFile.getErrorSummary()
} }
@ -66,7 +61,7 @@ const plugin: IPlugin = {
} catch (e) { } catch (e) {
context.report({ context.report({
loc: { line: 1, column: 1 }, loc: { line: 1, column: 1 },
messageId: "error-applying-config", messageId: 'error-applying-config',
data: { data: {
details: e.message details: e.message
} }
@ -76,7 +71,7 @@ const plugin: IPlugin = {
} catch (e) { } catch (e) {
context.report({ context.report({
loc: { line: 1, column: 1 }, loc: { line: 1, column: 1 },
messageId: "error-loading-config-file", messageId: 'error-loading-config-file',
data: { data: {
details: `Unexpected exception: ${e.message}` details: `Unexpected exception: ${e.message}`
} }
@ -88,14 +83,18 @@ const plugin: IPlugin = {
const sourceCode: eslint.SourceCode = context.getSourceCode(); const sourceCode: eslint.SourceCode = context.getSourceCode();
const checkCommentBlocks: (node: ESTree.Node) => void = function (node: ESTree.Node) { const checkCommentBlocks: (node: ESTree.Node) => void = function (node: ESTree.Node) {
for (const comment of sourceCode.getAllComments()) { for (const comment of sourceCode.getAllComments()) {
if (comment.type !== "Block") { if (comment.type !== 'Block') {
continue; continue;
} }
if (!comment.range) { if (!comment.range) {
continue; continue;
} }
const textRange: TextRange = TextRange.fromStringRange(sourceCode.text, comment.range[0], comment.range[1]); const textRange: TextRange = TextRange.fromStringRange(
sourceCode.text,
comment.range[0],
comment.range[1]
);
// Smallest comment is "/***/" // Smallest comment is "/***/"
if (textRange.length < 5) { if (textRange.length < 5) {
@ -120,7 +119,7 @@ const plugin: IPlugin = {
}); });
} }
} }
} };
return { return {
Program: checkCommentBlocks Program: checkCommentBlocks
@ -128,6 +127,6 @@ const plugin: IPlugin = {
} }
} }
} }
} };
export = plugin; export = plugin;

Просмотреть файл

@ -1,32 +1,32 @@
import { RuleTester } from "eslint"; import { RuleTester } from 'eslint';
import * as plugin from "../index"; import * as plugin from '../index';
const ruleTester: RuleTester = new RuleTester({ const ruleTester: RuleTester = new RuleTester({
env: { env: {
es6: true, es6: true
}, }
}); });
ruleTester.run('"tsdoc/syntax" rule', plugin.rules.syntax, { ruleTester.run('"tsdoc/syntax" rule', plugin.rules.syntax, {
valid: [ valid: [
"/**\nA great function!\n */\nfunction foobar() {}\n", '/**\nA great function!\n */\nfunction foobar() {}\n',
"/**\nA great class!\n */\nclass FooBar {}\n", '/**\nA great class!\n */\nclass FooBar {}\n'
], ],
invalid: [ invalid: [
{ {
code: "/**\n * This `is wrong\n */\nfunction foobar() {}\n", code: '/**\n * This `is wrong\n */\nfunction foobar() {}\n',
errors: [ errors: [
{ {
messageId: "tsdoc-code-span-missing-delimiter", messageId: 'tsdoc-code-span-missing-delimiter'
}, }
], ]
}, },
{ {
code: "/**\n * This `is wrong\n */\nclass FooBar {}\n", code: '/**\n * This `is wrong\n */\nclass FooBar {}\n',
errors: [ errors: [
{ {
messageId: "tsdoc-code-span-missing-delimiter", messageId: 'tsdoc-code-span-missing-delimiter'
}, }
], ]
}, }
], ]
}); });

Просмотреть файл

@ -1,18 +1,18 @@
// This is a workaround for https://github.com/eslint/eslint/issues/3458 // This is a workaround for https://github.com/eslint/eslint/issues/3458
require("@rushstack/eslint-config/patch/modern-module-resolution"); require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = { module.exports = {
extends: [ extends: [
"@rushstack/eslint-config/profile/web-app", '@rushstack/eslint-config/profile/web-app',
"@rushstack/eslint-config/mixins/friendly-locals", '@rushstack/eslint-config/mixins/friendly-locals',
"@rushstack/eslint-config/mixins/react", '@rushstack/eslint-config/mixins/react'
], ],
settings: { settings: {
react: { react: {
version: "16.9", version: '16.9'
}, }
}, },
parserOptions: { tsconfigRootDir: __dirname }, parserOptions: { tsconfigRootDir: __dirname }
}; };

Просмотреть файл

@ -8,6 +8,6 @@
"staticAssetsToCopy": { "staticAssetsToCopy": {
"fileExtensions": [".json", ".css"], "fileExtensions": [".json", ".css"],
"includeGlobs": [ "samples/*.ts" ] "includeGlobs": ["samples/*.ts"]
} }
} }

Просмотреть файл

@ -3,7 +3,6 @@ import { PlaygroundView } from './PlaygroundView';
class App extends React.Component { class App extends React.Component {
public render(): React.ReactNode { public render(): React.ReactNode {
return ( return (
<> <>
<PlaygroundView /> <PlaygroundView />

Просмотреть файл

@ -51,7 +51,7 @@ export interface ICodeEditorState {
interface IMonacoWindow extends Window { interface IMonacoWindow extends Window {
require: { require: {
(paths: string[], callback: (monaco: typeof monacoEditor) => void): void; (paths: string[], callback: (monaco: typeof monacoEditor) => void): void;
config: (options: { paths: { [name: string]: string } }) => void config: (options: { paths: { [name: string]: string } }) => void;
}; };
MonacoEnvironment: { MonacoEnvironment: {
getWorkerUrl: (workerId: string, label: string) => void; getWorkerUrl: (workerId: string, label: string) => void;
@ -68,11 +68,11 @@ export class CodeEditor extends React.Component<ICodeEditorProps, ICodeEditorSta
private _existingSyntaxStyles: { [hash: string]: string } = {}; private _existingSyntaxStyles: { [hash: string]: string } = {};
private _editorId: string; private _editorId: string;
private _isMounted: boolean=false; private _isMounted: boolean = false;
private _editor: monacoEditor.editor.IStandaloneCodeEditor | undefined; private _editor: monacoEditor.editor.IStandaloneCodeEditor | undefined;
private _placeholderDivRef: HTMLDivElement | undefined; private _placeholderDivRef: HTMLDivElement | undefined;
private _hostDivRef: HTMLDivElement | undefined; private _hostDivRef: HTMLDivElement | undefined;
public constructor(props: ICodeEditorProps) { public constructor(props: ICodeEditorProps) {
super(props); super(props);
@ -105,17 +105,17 @@ export class CodeEditor extends React.Component<ICodeEditorProps, ICodeEditorSta
private static _initializeMonaco(): Promise<typeof monacoEditor> { private static _initializeMonaco(): Promise<typeof monacoEditor> {
if (!CodeEditor._initializePromise) { if (!CodeEditor._initializePromise) {
CodeEditor._initializePromise = new Promise( CodeEditor._initializePromise = new Promise(
(resolve: (monaco: typeof monacoEditor) => void, reject: (error: Error) => void ) => { (resolve: (monaco: typeof monacoEditor) => void, reject: (error: Error) => void) => {
const monacoWindow: IMonacoWindow = window as unknown as IMonacoWindow; const monacoWindow: IMonacoWindow = (window as unknown) as IMonacoWindow;
monacoWindow.require.config({ paths: { 'vs': `${MONACO_BASE_URL}vs/` }}); monacoWindow.require.config({ paths: { vs: `${MONACO_BASE_URL}vs/` } });
monacoWindow.MonacoEnvironment = { monacoWindow.MonacoEnvironment = {
getWorkerUrl: (workerId, label) => { getWorkerUrl: (workerId, label) => {
return `data:text/javascript;charset=utf-8,${encodeURIComponent( return `data:text/javascript;charset=utf-8,${encodeURIComponent(
'self.MonacoEnvironment = {' + 'self.MonacoEnvironment = {' +
`baseUrl: '${MONACO_BASE_URL}'` + `baseUrl: '${MONACO_BASE_URL}'` +
'};' + '};' +
`importScripts('${MONACO_BASE_URL}vs/base/worker/workerMain.js');` `importScripts('${MONACO_BASE_URL}vs/base/worker/workerMain.js');`
)}`; )}`;
} }
}; };
@ -139,14 +139,16 @@ export class CodeEditor extends React.Component<ICodeEditorProps, ICodeEditorSta
public componentDidMount(): void { public componentDidMount(): void {
this._isMounted = true; this._isMounted = true;
CodeEditor._initializeMonaco().then((monaco) => { CodeEditor._initializeMonaco()
this.setState({ monaco }); .then((monaco) => {
if (this._isMounted) { this.setState({ monaco });
window.addEventListener('resize', this._onWindowResize); if (this._isMounted) {
} window.addEventListener('resize', this._onWindowResize);
}).catch((error) => { }
this.setState({ monacoErrorMessage: `Error loading Monaco editor: ${error}` }); })
}); .catch((error) => {
this.setState({ monacoErrorMessage: `Error loading Monaco editor: ${error}` });
});
} }
public componentWillUnmount(): void { public componentWillUnmount(): void {
@ -201,26 +203,26 @@ export class CodeEditor extends React.Component<ICodeEditorProps, ICodeEditorSta
public render(): React.ReactNode { public render(): React.ReactNode {
if (this.state.monacoErrorMessage) { if (this.state.monacoErrorMessage) {
return ( return (
<FlexColDiv <FlexColDiv className={this.props.className} style={this.props.style}>
className={ this.props.className } {this.state.monacoErrorMessage}
style={ this.props.style } >
{ this.state.monacoErrorMessage }
</FlexColDiv> </FlexColDiv>
); );
} else { } else {
// The Monaco application is very complex and its div does not resize reliably. // The Monaco application is very complex and its div does not resize reliably.
// To work around this, we render a blank placeholder div (that is well-behaved), // To work around this, we render a blank placeholder div (that is well-behaved),
// and then the Monaco host div floats above that using absolute positioning // and then the Monaco host div floats above that using absolute positioning
// and manual resizing. // and manual resizing.
return ( return (
<div className='playground-monaco-placeholder' <div
ref={ this._onRefPlaceholder } className="playground-monaco-placeholder"
style={ { display: 'flex', flexDirection: 'column', flex: 1, ...this.props.style } }> ref={this._onRefPlaceholder}
style={{ display: 'flex', flexDirection: 'column', flex: 1, ...this.props.style }}
<div className='playground-monaco-host' >
ref={ this._onRefHost } <div
style={ { display: 'block', position: 'absolute', backgroundColor: '#00FF00' } } /> className="playground-monaco-host"
ref={this._onRefHost}
style={{ display: 'block', position: 'absolute', backgroundColor: '#00FF00' }}
/>
</div> </div>
); );
} }
@ -230,8 +232,9 @@ export class CodeEditor extends React.Component<ICodeEditorProps, ICodeEditorSta
this._placeholderDivRef = element; this._placeholderDivRef = element;
} }
private _onRefHost(element: HTMLDivElement): void{ private _onRefHost(element: HTMLDivElement): void {
this._hostDivRef = element; this._createEditor(); this._hostDivRef = element;
this._createEditor();
} }
private _applySyntaxStyling(newSyntaxStyles: IStyledRange[]): void { private _applySyntaxStyling(newSyntaxStyles: IStyledRange[]): void {
@ -262,8 +265,9 @@ export class CodeEditor extends React.Component<ICodeEditorProps, ICodeEditorSta
} }
this._getEditorModel().deltaDecorations(decorationsToRemove, []); this._getEditorModel().deltaDecorations(decorationsToRemove, []);
const decorationIds: string[] = this._getEditorModel().deltaDecorations([], decorationsToAdd.map( const decorationIds: string[] = this._getEditorModel().deltaDecorations(
(decoration) => { [],
decorationsToAdd.map((decoration) => {
const startPos: monacoEditor.Position = this._getEditorModel().getPositionAt(decoration.pos); const startPos: monacoEditor.Position = this._getEditorModel().getPositionAt(decoration.pos);
const endPos: monacoEditor.Position = this._getEditorModel().getPositionAt(decoration.end); const endPos: monacoEditor.Position = this._getEditorModel().getPositionAt(decoration.end);
@ -280,8 +284,8 @@ export class CodeEditor extends React.Component<ICodeEditorProps, ICodeEditorSta
inlineClassName: decoration.className inlineClassName: decoration.className
} }
}; };
} })
)); );
for (let i: number = 0; i < decorationsToAdd.length; i++) { for (let i: number = 0; i < decorationsToAdd.length; i++) {
newExistingSyntaxStyles[hashesOfDecorationsToAdd[i]] = decorationIds[i]; newExistingSyntaxStyles[hashesOfDecorationsToAdd[i]] = decorationIds[i];
@ -302,11 +306,10 @@ export class CodeEditor extends React.Component<ICodeEditorProps, ICodeEditorSta
} }
private _createEditor(): void { private _createEditor(): void {
CodeEditor._initializeMonaco().then((monaco) => { CodeEditor._initializeMonaco()
if (!this._editor && this._hostDivRef) { .then((monaco) => {
this._editor = monaco.editor.create( if (!this._editor && this._hostDivRef) {
this._hostDivRef, this._editor = monaco.editor.create(this._hostDivRef, {
{
value: this.props.value || '', value: this.props.value || '',
language: this.props.language, language: this.props.language,
readOnly: this.props.readOnly, readOnly: this.props.readOnly,
@ -316,20 +319,20 @@ export class CodeEditor extends React.Component<ICodeEditorProps, ICodeEditorSta
lineNumbers: this.props.disableLineNumbers ? 'off' : 'on', lineNumbers: this.props.disableLineNumbers ? 'off' : 'on',
theme: this.props.theme, theme: this.props.theme,
wordWrap: this.props.wordWrap ? 'on' : 'off' wordWrap: this.props.wordWrap ? 'on' : 'off'
} });
);
this._getEditorModel().onDidChangeContent((e) => { this._getEditorModel().onDidChangeContent((e) => {
if (this._editor) { if (this._editor) {
this._safeOnChange(this._editor.getValue()); this._safeOnChange(this._editor.getValue());
} }
}); });
this._onWindowResize(); this._onWindowResize();
} }
}).catch((e) => { })
console.error('CodeEditor._createEditor() failed: ' + e.toString()); .catch((e) => {
}); console.error('CodeEditor._createEditor() failed: ' + e.toString());
});
} }
private _onWindowResize(): void { private _onWindowResize(): void {

Просмотреть файл

@ -19,11 +19,11 @@ export class DocAstView extends React.Component<IDocAstViewProps> {
return ( return (
<CodeEditor <CodeEditor
className='playground-ast-text-editor' className="playground-ast-text-editor"
readOnly={ true } readOnly={true}
value={ outputLines.join('\n') } value={outputLines.join('\n')}
disableLineNumbers={ true } disableLineNumbers={true}
theme={ this.props.theme } theme={this.props.theme}
/> />
); );
} }

Просмотреть файл

@ -23,19 +23,19 @@ export class DocDomView extends React.Component<IDocDomViewProps> {
if (parserContext && parserContext.docComment) { if (parserContext && parserContext.docComment) {
const unindentedCode: string = ReactDomServer.renderToStaticMarkup( const unindentedCode: string = ReactDomServer.renderToStaticMarkup(
<DocHtmlView docComment={ parserContext.docComment } /> <DocHtmlView docComment={parserContext.docComment} />
); );
code = this._indentHtml(unindentedCode); code = this._indentHtml(unindentedCode);
} }
return ( return (
<CodeEditor <CodeEditor
className='playground-dom-text-editor' className="playground-dom-text-editor"
readOnly={ true } readOnly={true}
value={ code } value={code}
language='html' language="html"
disableLineNumbers={ true } disableLineNumbers={true}
theme={ this.props.theme } theme={this.props.theme}
/> />
); );
} }

Просмотреть файл

@ -15,9 +15,9 @@ export class DocHtmlView extends React.Component<IDocHtmlViewProps> {
// Summary // Summary
if (docComment.summarySection) { if (docComment.summarySection) {
outputElements.push( outputElements.push(
<React.Fragment key='summary'> <React.Fragment key="summary">
<h2 className='doc-heading'>Summary</h2> <h2 className="doc-heading">Summary</h2>
{ this._renderContainer(docComment.summarySection) } {this._renderContainer(docComment.summarySection)}
</React.Fragment> </React.Fragment>
); );
} }
@ -29,25 +29,23 @@ export class DocHtmlView extends React.Component<IDocHtmlViewProps> {
for (const paramBlock of docComment.params.blocks) { for (const paramBlock of docComment.params.blocks) {
rows.push( rows.push(
<tr key={`param_${rows.length}`}> <tr key={`param_${rows.length}`}>
<td>{ paramBlock.parameterName }</td> <td>{paramBlock.parameterName}</td>
<td>{ this._renderContainer(paramBlock.content) }</td> <td>{this._renderContainer(paramBlock.content)}</td>
</tr> </tr>
); );
} }
outputElements.push( outputElements.push(
<React.Fragment key='parameters'> <React.Fragment key="parameters">
<h2 className='doc-heading'>Parameters</h2> <h2 className="doc-heading">Parameters</h2>
<table className='doc-table'> <table className="doc-table">
<thead> <thead>
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Description</th> <th>Description</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>{rows}</tbody>
{rows}
</tbody>
</table> </table>
</React.Fragment> </React.Fragment>
); );
@ -56,33 +54,34 @@ export class DocHtmlView extends React.Component<IDocHtmlViewProps> {
// Returns // Returns
if (docComment.returnsBlock) { if (docComment.returnsBlock) {
outputElements.push( outputElements.push(
<React.Fragment key='returns'> <React.Fragment key="returns">
<h2 className='doc-heading'>Return Value</h2> <h2 className="doc-heading">Return Value</h2>
{ this._renderContainer(docComment.returnsBlock.content) } {this._renderContainer(docComment.returnsBlock.content)}
</React.Fragment> </React.Fragment>
); );
} }
if (docComment.remarksBlock) { if (docComment.remarksBlock) {
outputElements.push( outputElements.push(
<React.Fragment key='remarks'> <React.Fragment key="remarks">
<h2 className='doc-heading'>Remarks</h2> <h2 className="doc-heading">Remarks</h2>
{ this._renderContainer(docComment.remarksBlock.content) } {this._renderContainer(docComment.remarksBlock.content)}
</React.Fragment> </React.Fragment>
); );
} }
const exampleBlocks: tsdoc.DocBlock[] = docComment.customBlocks.filter(x => x.blockTag.tagNameWithUpperCase const exampleBlocks: tsdoc.DocBlock[] = docComment.customBlocks.filter(
=== tsdoc.StandardTags.example.tagNameWithUpperCase); (x) => x.blockTag.tagNameWithUpperCase === tsdoc.StandardTags.example.tagNameWithUpperCase
);
let exampleNumber: number = 1; let exampleNumber: number = 1;
for (const exampleBlock of exampleBlocks) { for (const exampleBlock of exampleBlocks) {
const heading: string = exampleBlocks.length > 1 ? `Example ${exampleNumber}` : 'Example'; const heading: string = exampleBlocks.length > 1 ? `Example ${exampleNumber}` : 'Example';
outputElements.push( outputElements.push(
<React.Fragment key='seeAlso'> <React.Fragment key="seeAlso">
<h2 className='doc-heading'>{heading}</h2> <h2 className="doc-heading">{heading}</h2>
{ this._renderContainer(exampleBlock.content) } {this._renderContainer(exampleBlock.content)}
</React.Fragment> </React.Fragment>
); );
@ -92,19 +91,13 @@ export class DocHtmlView extends React.Component<IDocHtmlViewProps> {
if (docComment.seeBlocks.length > 0) { if (docComment.seeBlocks.length > 0) {
const listItems: React.ReactNode[] = []; const listItems: React.ReactNode[] = [];
for (const seeBlock of docComment.seeBlocks) { for (const seeBlock of docComment.seeBlocks) {
listItems.push( listItems.push(<li key={`item_${listItems.length}`}>{this._renderContainer(seeBlock.content)}</li>);
<li key={`item_${listItems.length}`}>
{ this._renderContainer(seeBlock.content) }
</li>
);
} }
outputElements.push( outputElements.push(
<React.Fragment key='seeAlso'> <React.Fragment key="seeAlso">
<h2 className='doc-heading'>See Also</h2> <h2 className="doc-heading">See Also</h2>
<ul> <ul>{listItems}</ul>
{listItems}
</ul>
</React.Fragment> </React.Fragment>
); );
} }
@ -118,21 +111,21 @@ export class DocHtmlView extends React.Component<IDocHtmlViewProps> {
const key: string = `modifier_${modifierElements.length}`; const key: string = `modifier_${modifierElements.length}`;
modifierElements.push( modifierElements.push(
<React.Fragment key={key}> <React.Fragment key={key}>
{ ' ' } {' '}
<code className='doc-code-span'>{ modifierTag.tagName }</code> <code className="doc-code-span">{modifierTag.tagName}</code>
</React.Fragment> </React.Fragment>
); );
} }
outputElements.push( outputElements.push(
<React.Fragment key='modifiers'> <React.Fragment key="modifiers">
<h2 className='doc-heading'>Modifiers</h2> <h2 className="doc-heading">Modifiers</h2>
{ modifierElements } {modifierElements}
</React.Fragment> </React.Fragment>
); );
} }
return <div style={ this.props.style }> {outputElements} </div>; return <div style={this.props.style}> {outputElements} </div>;
} }
private _renderContainer(section: tsdoc.DocNodeContainer): React.ReactNode { private _renderContainer(section: tsdoc.DocNodeContainer): React.ReactNode {
@ -141,13 +134,17 @@ export class DocHtmlView extends React.Component<IDocHtmlViewProps> {
const key: string = `key_${elements.length}`; const key: string = `key_${elements.length}`;
elements.push(this._renderDocNode(node, key)); elements.push(this._renderDocNode(node, key));
} }
return (<React.Fragment>{elements}</React.Fragment> ); return <React.Fragment>{elements}</React.Fragment>;
} }
private _renderDocNode(node: tsdoc.DocNode, key: string): React.ReactNode | undefined { private _renderDocNode(node: tsdoc.DocNode, key: string): React.ReactNode | undefined {
switch (node.kind) { switch (node.kind) {
case 'CodeSpan': case 'CodeSpan':
return <code key={key} className='doc-code-span'>{(node as tsdoc.DocCodeSpan).code}</code>; return (
<code key={key} className="doc-code-span">
{(node as tsdoc.DocCodeSpan).code}
</code>
);
case 'ErrorText': case 'ErrorText':
return <React.Fragment key={key}>{(node as tsdoc.DocErrorText).text}</React.Fragment>; return <React.Fragment key={key}>{(node as tsdoc.DocErrorText).text}</React.Fragment>;
case 'EscapedText': case 'EscapedText':
@ -155,10 +152,8 @@ export class DocHtmlView extends React.Component<IDocHtmlViewProps> {
case 'FencedCode': case 'FencedCode':
const docFencedCode: tsdoc.DocFencedCode = node as tsdoc.DocFencedCode; const docFencedCode: tsdoc.DocFencedCode = node as tsdoc.DocFencedCode;
return ( return (
<pre key={key} className='doc-fenced-code'> <pre key={key} className="doc-fenced-code">
<code key={key}> <code key={key}>{docFencedCode.code}</code>
{ docFencedCode.code }
</code>
</pre> </pre>
); );
break; break;
@ -166,35 +161,43 @@ export class DocHtmlView extends React.Component<IDocHtmlViewProps> {
const linkTag: tsdoc.DocLinkTag = node as tsdoc.DocLinkTag; const linkTag: tsdoc.DocLinkTag = node as tsdoc.DocLinkTag;
if (linkTag.urlDestination) { if (linkTag.urlDestination) {
const linkText: string = linkTag.linkText || linkTag.urlDestination; const linkText: string = linkTag.linkText || linkTag.urlDestination;
return <a key={key} href='#'>{linkText}</a>; return (
<a key={key} href="#">
{linkText}
</a>
);
} else { } else {
let identifier: string = ''; let identifier: string = '';
if (linkTag.codeDestination) { if (linkTag.codeDestination) {
// TODO: The library should provide a default rendering for this // TODO: The library should provide a default rendering for this
const memberReferences: ReadonlyArray<tsdoc.DocMemberReference> = linkTag.codeDestination.memberReferences; const memberReferences: ReadonlyArray<tsdoc.DocMemberReference> =
linkTag.codeDestination.memberReferences;
if (memberReferences.length > 0) { if (memberReferences.length > 0) {
const memberIdentifier: tsdoc.DocMemberIdentifier | undefined const memberIdentifier: tsdoc.DocMemberIdentifier | undefined =
= memberReferences[memberReferences.length - 1].memberIdentifier; memberReferences[memberReferences.length - 1].memberIdentifier;
if (memberIdentifier) { if (memberIdentifier) {
identifier = memberIdentifier.identifier; identifier = memberIdentifier.identifier;
} }
} }
} }
const linkText: string = linkTag.linkText || identifier || '???'; const linkText: string = linkTag.linkText || identifier || '???';
return <a key={key} href='#'>{linkText}</a>; return (
<a key={key} href="#">
{linkText}
</a>
);
} }
case 'Paragraph': case 'Paragraph':
// Collapse spaces in the paragraph // Collapse spaces in the paragraph
const transformedParagraph: tsdoc.DocParagraph = tsdoc.DocNodeTransforms.trimSpacesInParagraph( const transformedParagraph: tsdoc.DocParagraph = tsdoc.DocNodeTransforms.trimSpacesInParagraph(
node as tsdoc.DocParagraph); node as tsdoc.DocParagraph
return (
<p key={key}>{ this._renderContainer(transformedParagraph) }</p>
); );
return <p key={key}>{this._renderContainer(transformedParagraph)}</p>;
case 'PlainText': case 'PlainText':
return <React.Fragment key={key}>{(node as tsdoc.DocPlainText).text}</React.Fragment>; return <React.Fragment key={key}>{(node as tsdoc.DocPlainText).text}</React.Fragment>;
case 'SoftBreak': case 'SoftBreak':
return <React.Fragment key={key}>{' '}</React.Fragment>; return <React.Fragment key={key}> </React.Fragment>;
} }
return undefined; return undefined;
} }

Просмотреть файл

@ -1,18 +1,16 @@
import * as React from 'react'; import * as React from 'react';
export interface IFlexDivProps extends React.DetailedHTMLProps< export interface IFlexDivProps
React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> { extends React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement> {}
}
export class FlexRowDiv extends React.Component<IFlexDivProps> {
export class FlexRowDiv extends React.Component<IFlexDivProps> {
public render(): React.ReactNode { public render(): React.ReactNode {
const mergedProps: IFlexDivProps = { const mergedProps: IFlexDivProps = {
...this.props ...this.props
}; };
if (mergedProps.style === undefined) { if (mergedProps.style === undefined) {
mergedProps.style = { }; mergedProps.style = {};
} }
if (mergedProps.style.display === undefined) { if (mergedProps.style.display === undefined) {
mergedProps.style.display = 'flex'; mergedProps.style.display = 'flex';
@ -23,18 +21,16 @@ export class FlexRowDiv extends React.Component<IFlexDivProps> {
return React.createElement('div', mergedProps); return React.createElement('div', mergedProps);
} }
} }
export class FlexColDiv extends React.Component<IFlexDivProps> { export class FlexColDiv extends React.Component<IFlexDivProps> {
public render(): React.ReactNode { public render(): React.ReactNode {
const mergedProps: IFlexDivProps = { const mergedProps: IFlexDivProps = {
...this.props ...this.props
}; };
if (mergedProps.style === undefined) { if (mergedProps.style === undefined) {
mergedProps.style = { }; mergedProps.style = {};
} }
if (mergedProps.style.display === undefined) { if (mergedProps.style.display === undefined) {
mergedProps.style.display = 'flex'; mergedProps.style.display = 'flex';
@ -45,5 +41,4 @@ export class FlexColDiv extends React.Component<IFlexDivProps> {
return React.createElement('div', mergedProps); return React.createElement('div', mergedProps);
} }
} }

Просмотреть файл

@ -6,21 +6,15 @@ import { FlexRowDiv, FlexColDiv } from './FlexDivs';
import { DocHtmlView } from './DocHtmlView'; import { DocHtmlView } from './DocHtmlView';
import { DocDomView } from './DocDomView'; import { DocDomView } from './DocDomView';
import { DocAstView } from './DocAstView'; import { DocAstView } from './DocAstView';
import { import { CodeEditor, ISyntaxMarker, IStyledRange } from './CodeEditor';
CodeEditor,
ISyntaxMarker,
IStyledRange
} from './CodeEditor';
import { DocNodeSyntaxStyler } from './SyntaxStyler/DocNodeSyntaxStyler'; import { DocNodeSyntaxStyler } from './SyntaxStyler/DocNodeSyntaxStyler';
import { SampleInputs } from './samples/SampleInputs'; import { SampleInputs } from './samples/SampleInputs';
export const enum Theme { export const enum Theme {
vs = 'vs' vs = 'vs'
} }
export interface IPlaygroundViewProps { export interface IPlaygroundViewProps {}
}
export interface IPlaygroundViewState { export interface IPlaygroundViewState {
inputText: string; inputText: string;
@ -30,7 +24,7 @@ export interface IPlaygroundViewState {
selectedTheme: string; selectedTheme: string;
} }
export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlaygroundViewState> { export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlaygroundViewState> {
private readonly _textAreaStyle: React.CSSProperties = { private readonly _textAreaStyle: React.CSSProperties = {
width: '100%', width: '100%',
height: '100%', height: '100%',
@ -58,7 +52,7 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
this._inputTextArea_onChange = this._inputTextArea_onChange.bind(this); this._inputTextArea_onChange = this._inputTextArea_onChange.bind(this);
this._selectSample_onChange = this._selectSample_onChange.bind(this); this._selectSample_onChange = this._selectSample_onChange.bind(this);
this._selectTheme_onChange = this._selectTheme_onChange.bind(this) this._selectTheme_onChange = this._selectTheme_onChange.bind(this);
} }
public componentDidMount(): void { public componentDidMount(): void {
@ -109,38 +103,43 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
}; };
return ( return (
<FlexColDiv className='playground-frame' style={ { flex: 1 } }> <FlexColDiv className="playground-frame" style={{ flex: 1 }}>
<FlexRowDiv className='playground-header' style={ headerStyle }> <FlexRowDiv className="playground-header" style={headerStyle}>
<FlexColDiv style={{ fontWeight: 400, fontSize: '26px' }}>TSDoc Playground</FlexColDiv> <FlexColDiv style={{ fontWeight: 400, fontSize: '26px' }}>TSDoc Playground</FlexColDiv>
<FlexColDiv style={{ fontWeight: 400, fontSize: '20px' }}> <FlexColDiv style={{ fontWeight: 400, fontSize: '20px' }}>
<a style={navAnchorStyle} href='https://github.com/Microsoft/tsdoc' target='_blank' <a
rel='noopener noreferrer'> style={navAnchorStyle}
What is TSDoc?</a> href="https://github.com/Microsoft/tsdoc"
target="_blank"
rel="noopener noreferrer"
>
What is TSDoc?
</a>
</FlexColDiv> </FlexColDiv>
</FlexRowDiv> </FlexRowDiv>
<FlexColDiv className='playground-content-area' style={ { margin: '4px', flex: 1 } }> <FlexColDiv className="playground-content-area" style={{ margin: '4px', flex: 1 }}>
<FlexRowDiv className='playground-main-row' style={ mainRowStyle }> <FlexRowDiv className="playground-main-row" style={mainRowStyle}>
{ this._renderInputBox() } {this._renderInputBox()}
<TabPane <TabPane
style={ { flex: 1, marginLeft: '4px' } } style={{ flex: 1, marginLeft: '4px' }}
buttonRowStyle={ { height: '40px', boxSizing: 'border-box' } } buttonRowStyle={{ height: '40px', boxSizing: 'border-box' }}
tabs={ [ tabs={[
{ title: 'HTML', render: this._renderHtml.bind(this) }, { title: 'HTML', render: this._renderHtml.bind(this) },
{ title: 'DOM', render: this._renderDom.bind(this) }, { title: 'DOM', render: this._renderDom.bind(this) },
{ title: 'Lines', render: this._renderLines.bind(this) }, { title: 'Lines', render: this._renderLines.bind(this) },
{ title: 'AST', render: this._renderAst.bind(this) }, { title: 'AST', render: this._renderAst.bind(this) },
{ title: 'Emitter', render: this._renderEmitter.bind(this) } { title: 'Emitter', render: this._renderEmitter.bind(this) }
] } ]}
/> />
</FlexRowDiv> </FlexRowDiv>
<FlexColDiv className='playground-errors-pane' style={ errorsPaneStyle }> <FlexColDiv className="playground-errors-pane" style={errorsPaneStyle}>
{ this._renderErrorList() } {this._renderErrorList()}
</FlexColDiv> </FlexColDiv>
</FlexColDiv> </FlexColDiv>
<FlexRowDiv className='playground-footer' style={ footerStyle }> <FlexRowDiv className="playground-footer" style={footerStyle}>
&copy; 2019 Microsoft &copy; 2019 Microsoft
</FlexRowDiv> </FlexRowDiv>
</FlexColDiv> </FlexColDiv>
@ -172,14 +171,11 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
} }
} }
DocNodeSyntaxStyler.getStylesForDocComment( DocNodeSyntaxStyler.getStylesForDocComment(syntaxStyles, {
syntaxStyles, docNode: this.state.parserContext.docComment,
{ parserContext: this.state.parserContext,
docNode: this.state.parserContext.docComment, themeName: this.state.selectedTheme
parserContext: this.state.parserContext, });
themeName: this.state.selectedTheme
}
);
} }
const editorStyle: React.CSSProperties = { const editorStyle: React.CSSProperties = {
@ -189,21 +185,21 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
}; };
return ( return (
<FlexColDiv className='playground-input-box' style={ { flex: 1 } }> <FlexColDiv className="playground-input-box" style={{ flex: 1 }}>
<div className='playground-button-bar' style={ { height: '40px', boxSizing: 'border-box' } }> <div className="playground-button-bar" style={{ height: '40px', boxSizing: 'border-box' }}>
{ this._renderSelectSample() } {this._renderSelectSample()}
{ this._renderThemeSelector() } {this._renderThemeSelector()}
</div> </div>
<CodeEditor <CodeEditor
className='playground-input-text-editor' className="playground-input-text-editor"
style={ editorStyle } style={editorStyle}
value={ this.state.inputText } value={this.state.inputText}
onChange={ this._inputTextArea_onChange } onChange={this._inputTextArea_onChange}
language='typescript' language="typescript"
markers={ markers } markers={markers}
syntaxStyles={ syntaxStyles } syntaxStyles={syntaxStyles}
theme={ this.state.selectedTheme } theme={this.state.selectedTheme}
/> />
</FlexColDiv> </FlexColDiv>
); );
} }
@ -211,15 +207,15 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
private _renderSelectSample(): React.ReactNode { private _renderSelectSample(): React.ReactNode {
return ( return (
<select <select
className='playground-select-sample' className="playground-select-sample"
value={this.state.selectSampleValue} value={this.state.selectSampleValue}
aria-label='Select a code sample' aria-label="Select a code sample"
onChange={this._selectSample_onChange}> onChange={this._selectSample_onChange}
>
<option value='none'>Choose a sample...</option> <option value="none">Choose a sample...</option>
<option value='basic'>A basic example</option> <option value="basic">A basic example</option>
<option value='advanced'>Some advanced features</option> <option value="advanced">Some advanced features</option>
<option value='hyperlink'>Creating hyperlinks</option> <option value="hyperlink">Creating hyperlinks</option>
</select> </select>
); );
} }
@ -227,13 +223,13 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
private _renderThemeSelector(): React.ReactNode { private _renderThemeSelector(): React.ReactNode {
return ( return (
<select <select
className='playground-select-theme' className="playground-select-theme"
value={this.state.selectedTheme} value={this.state.selectedTheme}
aria-label='Select an editor theme' aria-label="Select an editor theme"
onChange={this._selectTheme_onChange}> onChange={this._selectTheme_onChange}
>
<option value='vs'>Light Theme</option> <option value="vs">Light Theme</option>
<option value='vs-dark'>Dark Theme</option> <option value="vs-dark">Dark Theme</option>
</select> </select>
); );
} }
@ -266,8 +262,8 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
const parserContext: tsdoc.ParserContext | undefined = this.state.parserContext; const parserContext: tsdoc.ParserContext | undefined = this.state.parserContext;
if (parserContext && parserContext.docComment) { if (parserContext && parserContext.docComment) {
return ( return (
<div style={ { overflow: 'auto', paddingLeft: '8px', paddingRight: '8px', flex: 1 } }> <div style={{ overflow: 'auto', paddingLeft: '8px', paddingRight: '8px', flex: 1 }}>
<DocHtmlView docComment={ parserContext.docComment } /> <DocHtmlView docComment={parserContext.docComment} />
</div> </div>
); );
} else { } else {
@ -276,12 +272,7 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
} }
private _renderDom(): React.ReactNode { private _renderDom(): React.ReactNode {
return ( return <DocDomView parserContext={this.state.parserContext} theme={this.state.selectedTheme} />;
<DocDomView
parserContext={ this.state.parserContext }
theme={ this.state.selectedTheme }
/>
);
} }
private _renderLines(): React.ReactNode { private _renderLines(): React.ReactNode {
@ -293,21 +284,16 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
return ( return (
<textarea <textarea
className='playground-lines-text-editor' className="playground-lines-text-editor"
style={ { ...this._textAreaStyle, border: 'none' } } style={{ ...this._textAreaStyle, border: 'none' }}
readOnly={ true } readOnly={true}
value={ outputText } value={outputText}
/> />
); );
} }
private _renderAst(): React.ReactNode { private _renderAst(): React.ReactNode {
return ( return <DocAstView parserContext={this.state.parserContext} theme={this.state.selectedTheme} />;
<DocAstView
parserContext={this.state.parserContext}
theme={ this.state.selectedTheme }
/>
);
} }
private _renderEmitter(): React.ReactNode { private _renderEmitter(): React.ReactNode {
@ -319,10 +305,10 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
return ( return (
<CodeEditor <CodeEditor
className='playground-emitter-text-editor' className="playground-emitter-text-editor"
readOnly={ true } readOnly={true}
value={ outputText } value={outputText}
theme={ this.state.selectedTheme } theme={this.state.selectedTheme}
wordWrap={true} wordWrap={true}
/> />
); );
@ -333,7 +319,7 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
if (this.state.parserFailureText) { if (this.state.parserFailureText) {
errorsText = this.state.parserFailureText; errorsText = this.state.parserFailureText;
} else if (this.state.parserContext) { } else if (this.state.parserContext) {
errorsText = this.state.parserContext.log.messages.map(x => x.toString()).join('\n'); errorsText = this.state.parserContext.log.messages.map((x) => x.toString()).join('\n');
} }
const boxStyle: React.CSSProperties = { const boxStyle: React.CSSProperties = {
@ -345,14 +331,14 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
return ( return (
<> <>
<label htmlFor='errors'>Errors:</label> <label htmlFor="errors">Errors:</label>
<FlexColDiv style={boxStyle}> <FlexColDiv style={boxStyle}>
<textarea <textarea
id='errors' id="errors"
className='playground-errors-textarea' className="playground-errors-textarea"
readOnly={ true } readOnly={true}
value={ errorsText } value={errorsText}
style={ this._textAreaStyle } style={this._textAreaStyle}
/> />
</FlexColDiv> </FlexColDiv>
</> </>
@ -374,10 +360,12 @@ export class PlaygroundView extends React.Component<IPlaygroundViewProps, IPlayg
try { try {
const inputText: string = this.state.inputText; const inputText: string = this.state.inputText;
const configuration: tsdoc.TSDocConfiguration = new tsdoc.TSDocConfiguration(); const configuration: tsdoc.TSDocConfiguration = new tsdoc.TSDocConfiguration();
configuration.addTagDefinition(new tsdoc.TSDocTagDefinition({ configuration.addTagDefinition(
tagName: '@sampleCustomBlockTag', new tsdoc.TSDocTagDefinition({
syntaxKind: tsdoc.TSDocTagSyntaxKind.BlockTag tagName: '@sampleCustomBlockTag',
})); syntaxKind: tsdoc.TSDocTagSyntaxKind.BlockTag
})
);
const tsdocParser: tsdoc.TSDocParser = new tsdoc.TSDocParser(configuration); const tsdocParser: tsdoc.TSDocParser = new tsdoc.TSDocParser(configuration);
const parserContext: tsdoc.ParserContext = tsdocParser.parseString(inputText); const parserContext: tsdoc.ParserContext = tsdocParser.parseString(inputText);

Просмотреть файл

@ -1,10 +1,6 @@
import * as tsdoc from '@microsoft/tsdoc'; import * as tsdoc from '@microsoft/tsdoc';
import { import { MonacoTSDocTheme, IDocNodeSyntaxStylerTheme, IThemeRule } from './DocNodeSyntaxStylerTheme';
MonacoTSDocTheme,
IDocNodeSyntaxStylerTheme,
IThemeRule
} from './DocNodeSyntaxStylerTheme';
import { IStyledRange } from './../CodeEditor'; import { IStyledRange } from './../CodeEditor';
import './syntaxStyles.css'; import './syntaxStyles.css';
@ -20,7 +16,9 @@ interface IAddTokenStylesOptions {
theme: IDocNodeSyntaxStylerTheme; theme: IDocNodeSyntaxStylerTheme;
} }
interface IGetStylesForDocCommentInternalOptions extends IGetStylesForDocCommentOptions, IAddTokenStylesOptions { interface IGetStylesForDocCommentInternalOptions
extends IGetStylesForDocCommentOptions,
IAddTokenStylesOptions {
parentNode?: tsdoc.DocNode; parentNode?: tsdoc.DocNode;
} }
@ -32,7 +30,10 @@ export class DocNodeSyntaxStyler {
private static _classNameId: number = 0; private static _classNameId: number = 0;
private static _themeCache: { [hash: string]: IThemeClassNameMapping } = {}; private static _themeCache: { [hash: string]: IThemeClassNameMapping } = {};
public static getStylesForDocComment(styles: IStyledRange[], options: IGetStylesForDocCommentOptions): void { public static getStylesForDocComment(
styles: IStyledRange[],
options: IGetStylesForDocCommentOptions
): void {
let theme: IDocNodeSyntaxStylerTheme; let theme: IDocNodeSyntaxStylerTheme;
switch (options.themeName) { switch (options.themeName) {
default: default:
@ -47,26 +48,18 @@ export class DocNodeSyntaxStyler {
} }
} }
DocNodeSyntaxStyler._getStylesForDocCommentInternal( DocNodeSyntaxStyler._getStylesForDocCommentInternal(styles, {
styles, ...options,
{ theme,
...options, styleTokens: ['tsdoc']
theme, });
styleTokens: ['tsdoc']
}
);
} }
public static _getStylesForDocCommentInternal( public static _getStylesForDocCommentInternal(
styles: IStyledRange[], styles: IStyledRange[],
options: IGetStylesForDocCommentInternalOptions options: IGetStylesForDocCommentInternalOptions
): void { ): void {
const { const { docNode, parserContext, styleTokens, theme }: IGetStylesForDocCommentInternalOptions = options;
docNode,
parserContext,
styleTokens,
theme
}: IGetStylesForDocCommentInternalOptions = options;
if (docNode instanceof tsdoc.DocExcerpt) { if (docNode instanceof tsdoc.DocExcerpt) {
// Match the context against a color (i.e. tsdoc.link.url) // Match the context against a color (i.e. tsdoc.link.url)
@ -94,149 +87,131 @@ export class DocNodeSyntaxStyler {
case 'MemberReference_LeftParenthesis': case 'MemberReference_LeftParenthesis':
case 'MemberReference_RightParenthesis': case 'MemberReference_RightParenthesis':
case 'ParamBlock_Hyphen': { case 'ParamBlock_Hyphen': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'delimiter']
{ theme, styleTokens: [...styleTokens, 'delimiter'] } });
);
break; break;
} }
case 'InlineTag_TagName': case 'InlineTag_TagName':
case 'BlockTag': { case 'BlockTag': {
const tagDefinition: tsdoc.TSDocTagDefinition | undefined = parserContext.configuration.tryGetTagDefinition( const tagDefinition:
docNode.content.toString() | tsdoc.TSDocTagDefinition
); | undefined = parserContext.configuration.tryGetTagDefinition(docNode.content.toString());
DocNodeSyntaxStyler._addStylesForTag( DocNodeSyntaxStyler._addStylesForTag(styles, docNode.content, tagDefinition, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'tag']
tagDefinition, });
{ theme, styleTokens: [...styleTokens, 'tag'] }
);
break; break;
} }
case 'MemberIdentifier_Identifier': { case 'MemberIdentifier_Identifier': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'member', 'identifier']
{ theme, styleTokens: [...styleTokens, 'member', 'identifier'] } });
);
break; break;
} }
case 'DeclarationReference_PackageName': { case 'DeclarationReference_PackageName': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'packageName']
{ theme, styleTokens: [...styleTokens, 'packageName'] } });
);
break; break;
} }
case 'DeclarationReference_ImportPath': { case 'DeclarationReference_ImportPath': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'importPath']
{ theme, styleTokens: [...styleTokens, 'importPath'] } });
);
break; break;
} }
case 'LinkTag_UrlDestination': { case 'LinkTag_UrlDestination': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'url']
{ theme, styleTokens: [...styleTokens, 'url'] } });
);
break; break;
} }
case 'CodeSpan_Code': case 'CodeSpan_Code':
case 'FencedCode_Code': { case 'FencedCode_Code': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'code']
{ theme, styleTokens: [...styleTokens, 'code'] } });
);
break; break;
} }
case 'FencedCode_Language': { case 'FencedCode_Language': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'language']
{ theme, styleTokens: [...styleTokens, 'language'] } });
);
break; break;
} }
case 'HtmlEndTag_Name': case 'HtmlEndTag_Name':
case 'HtmlStartTag_Name': { case 'HtmlStartTag_Name': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'element', 'name']
{ theme, styleTokens: [...styleTokens, 'element', 'name'] } });
);
break; break;
} }
case 'HtmlAttribute_Name': { case 'HtmlAttribute_Name': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'element', 'attribute', 'name']
{ theme, styleTokens: [...styleTokens, 'element', 'attribute', 'name'] } });
);
break; break;
} }
case 'HtmlAttribute_Value': { case 'HtmlAttribute_Value': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'element', 'attribute', 'value']
{ theme, styleTokens: [...styleTokens, 'element', 'attribute', 'value'] } });
);
break; break;
} }
case 'ErrorText': { case 'ErrorText': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'error']
{ theme, styleTokens: [...styleTokens, 'error'] } });
);
break; break;
} }
case 'EscapedText': { case 'EscapedText': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'escaped']
{ theme, styleTokens: [...styleTokens, 'escaped'] } });
);
break; break;
} }
case 'MemberSelector': { case 'MemberSelector': {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, docNode.content, {
styles, theme,
docNode.content, styleTokens: [...styleTokens, 'member', 'selector']
{ theme, styleTokens: [...styleTokens, 'member', 'selector'] } });
);
break; break;
} }
} }
} }
for (const child of docNode.getChildNodes()) { for (const child of docNode.getChildNodes()) {
DocNodeSyntaxStyler._getStylesForDocCommentInternal( DocNodeSyntaxStyler._getStylesForDocCommentInternal(styles, {
styles, ...options,
{ parentNode: docNode,
...options, docNode: child
parentNode: docNode, });
docNode: child
}
);
} }
} }
@ -246,46 +221,39 @@ export class DocNodeSyntaxStyler {
tagDefinition: tsdoc.TSDocTagDefinition | undefined, tagDefinition: tsdoc.TSDocTagDefinition | undefined,
options: IAddTokenStylesOptions options: IAddTokenStylesOptions
): void { ): void {
const { const { theme, styleTokens }: IAddTokenStylesOptions = options;
theme,
styleTokens
}: IAddTokenStylesOptions = options;
if (tagDefinition) { if (tagDefinition) {
switch (tagDefinition.syntaxKind) { switch (tagDefinition.syntaxKind) {
case tsdoc.TSDocTagSyntaxKind.BlockTag: { case tsdoc.TSDocTagSyntaxKind.BlockTag: {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, excerpt, {
styles, theme,
excerpt, styleTokens: [...styleTokens, 'block']
{ theme, styleTokens: [...styleTokens, 'block'] } });
);
break; break;
} }
case tsdoc.TSDocTagSyntaxKind.InlineTag: { case tsdoc.TSDocTagSyntaxKind.InlineTag: {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, excerpt, {
styles, theme,
excerpt, styleTokens: [...styleTokens, 'inline']
{ theme, styleTokens: [...styleTokens, 'inline'] } });
);
break; break;
} }
case tsdoc.TSDocTagSyntaxKind.ModifierTag: { case tsdoc.TSDocTagSyntaxKind.ModifierTag: {
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, excerpt, {
styles, theme,
excerpt, styleTokens: [...styleTokens, 'modifier']
{ theme, styleTokens: [...styleTokens, 'modifier'] } });
);
break; break;
} }
} }
} else { } else {
// Undefined tag // Undefined tag
DocNodeSyntaxStyler._addTokenStyles( DocNodeSyntaxStyler._addTokenStyles(styles, excerpt, {
styles, theme,
excerpt, styleTokens: [...styleTokens, 'undefined']
{ theme, styleTokens: [...styleTokens, 'undefined'] } });
);
} }
} }

Просмотреть файл

@ -14,7 +14,7 @@
margin-left: -1px; margin-left: -1px;
} }
.tsdoc-blocktag+.tsdoc-blocktag { .tsdoc-blocktag + .tsdoc-blocktag {
border-left-style: none; border-left-style: none;
border-top-left-radius: 0px; border-top-left-radius: 0px;
} }

Просмотреть файл

@ -17,7 +17,7 @@ export interface ITabPaneState {
selectedTabIndex: number; selectedTabIndex: number;
} }
export class TabPane extends React.Component<ITabPaneProps, ITabPaneState> { export class TabPane extends React.Component<ITabPaneProps, ITabPaneState> {
// Saved bindings of _onClickTab() with a tabIndex parameter, to avoid the react/jsx-no-bind issue // Saved bindings of _onClickTab() with a tabIndex parameter, to avoid the react/jsx-no-bind issue
private _onClickTabBindings: React.MouseEventHandler<HTMLAnchorElement>[] = []; private _onClickTabBindings: React.MouseEventHandler<HTMLAnchorElement>[] = [];
@ -36,7 +36,7 @@ export class TabPane extends React.Component<ITabPaneProps, ITabPaneState> {
let selectedTabDefinition: ITabDefinition | undefined = undefined; let selectedTabDefinition: ITabDefinition | undefined = undefined;
for (let i: number = 0; i < this.props.tabs.length; ++i) { for (let i: number = 0; i < this.props.tabs.length; ++i) {
const tabDefinition: ITabDefinition = this.props.tabs[i]; const tabDefinition: ITabDefinition = this.props.tabs[i];
const style: React.CSSProperties = { const style: React.CSSProperties = {
padding: '8px', padding: '8px',
@ -58,28 +58,27 @@ export class TabPane extends React.Component<ITabPaneProps, ITabPaneState> {
}; };
buttons.push( buttons.push(
<div key={`tab_${i}`} className='playground-tab-pane-active-tab' style={ activeTabStyle }> <div key={`tab_${i}`} className="playground-tab-pane-active-tab" style={activeTabStyle}>
{tabDefinition.title} {tabDefinition.title}
</div> </div>
); );
} else { } else {
if (!this._onClickTabBindings[i]) { if (!this._onClickTabBindings[i]) {
// Bind _onClickTab() with i as the tabIndex parameter // Bind _onClickTab() with i as the tabIndex parameter
this._onClickTabBindings[i] = this._onClickTab.bind(this, i); this._onClickTabBindings[i] = this._onClickTab.bind(this, i);
} }
buttons.push( buttons.push(
<div key={`tab_${i}`} className='playground-tab-pane-inactive-tab' style={ style }> <div key={`tab_${i}`} className="playground-tab-pane-inactive-tab" style={style}>
<a href='#' <a
style={ { textDecorationLine: 'none', color: '#000000' } } href="#"
onClick={ this._onClickTabBindings[i] }> style={{ textDecorationLine: 'none', color: '#000000' }}
onClick={this._onClickTabBindings[i]}
>
{tabDefinition.title} {tabDefinition.title}
</a> </a>
</div> </div>
); );
} }
} }
@ -97,12 +96,12 @@ export class TabPane extends React.Component<ITabPaneProps, ITabPaneState> {
}; };
return ( return (
<FlexColDiv className='playground-tab-pane' style={ tabPaneStyle }> <FlexColDiv className="playground-tab-pane" style={tabPaneStyle}>
<FlexRowDiv className='playground-tab-pane-buttons' style={ this.props.buttonRowStyle }> <FlexRowDiv className="playground-tab-pane-buttons" style={this.props.buttonRowStyle}>
{ buttons } {buttons}
</FlexRowDiv> </FlexRowDiv>
<FlexColDiv className='playground-tab-pane-content' style={ contentDivStyle }> <FlexColDiv className="playground-tab-pane-content" style={contentDivStyle}>
{ selectedTabDefinition !== undefined ? selectedTabDefinition.render() : '' } {selectedTabDefinition !== undefined ? selectedTabDefinition.render() : ''}
</FlexColDiv> </FlexColDiv>
</FlexColDiv> </FlexColDiv>
); );
@ -111,5 +110,4 @@ export class TabPane extends React.Component<ITabPaneProps, ITabPaneState> {
private _onClickTab(tabIndex: number, event: React.MouseEvent<HTMLAnchorElement>): void { private _onClickTab(tabIndex: number, event: React.MouseEvent<HTMLAnchorElement>): void {
this.setState({ selectedTabIndex: tabIndex }); this.setState({ selectedTabIndex: tabIndex });
} }
} }

Просмотреть файл

@ -1,11 +1,12 @@
html, body { html,
body {
margin: 0; margin: 0;
height: 100%; height: 100%;
font-family: Tahoma, sans-serif; font-family: Tahoma, sans-serif;
} }
.doc-heading { .doc-heading {
color: #2F5496; color: #2f5496;
font-size: 24px; font-size: 24px;
font-weight: 400; font-weight: 400;
} }
@ -14,10 +15,11 @@ html, body {
border-collapse: collapse; border-collapse: collapse;
} }
.doc-table th, td { .doc-table th,
td {
text-align: left; text-align: left;
padding: 5px 10px; padding: 5px 10px;
border-bottom: 2px solid #e0e0e0 border-bottom: 2px solid #e0e0e0;
} }
.doc-fenced-code { .doc-fenced-code {

Просмотреть файл

@ -1,4 +1,3 @@
export namespace SampleInputs { export namespace SampleInputs {
export const advanced: string = require('raw-loader!./advancedSample.ts'); export const advanced: string = require('raw-loader!./advancedSample.ts');
export const basic: string = require('raw-loader!./basicSample.ts'); export const basic: string = require('raw-loader!./basicSample.ts');

Просмотреть файл

@ -13,9 +13,9 @@ if (!webpackConfiguration.devServer.headers) {
} }
webpackConfiguration.devServer.headers = { webpackConfiguration.devServer.headers = {
"Access-Control-Allow-Origin": "*", 'Access-Control-Allow-Origin': '*',
"Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS", 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
...webpackConfiguration.devServer.headers ...webpackConfiguration.devServer.headers
} };
module.exports = webpackConfiguration; module.exports = webpackConfiguration;

Просмотреть файл

@ -16,16 +16,17 @@ const REACT_DOM_URL = {
}; };
const REACT_DOM_SERVER_URL = { const REACT_DOM_SERVER_URL = {
dev: 'https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.1/umd/react-dom-server.browser.development.js', dev: 'https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.1/umd/react-dom-server.browser.development.js',
production: 'https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.1/umd/react-dom-server.browser.production.min.js' production:
'https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.1/umd/react-dom-server.browser.production.min.js'
}; };
const MONACO_URL = { const MONACO_URL = {
dev: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.14.3/min/', dev: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.14.3/min/',
production: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.14.3/min/' production: 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.14.3/min/'
}; };
module.exports.generateBuildWebpackConfiguration = function(env) { module.exports.generateBuildWebpackConfiguration = function (env) {
return _generateBaseWebpackConfiguration((env || {}).production); return _generateBaseWebpackConfiguration((env || {}).production);
} };
module.exports.generateServeWebpackConfiguration = function () { module.exports.generateServeWebpackConfiguration = function () {
const result = _generateBaseWebpackConfiguration(false); const result = _generateBaseWebpackConfiguration(false);
@ -37,7 +38,7 @@ module.exports.generateServeWebpackConfiguration = function () {
}; };
return result; return result;
} };
function _generateBaseWebpackConfiguration(isProduction) { function _generateBaseWebpackConfiguration(isProduction) {
const distDirectory = path.join(__dirname, 'dist'); const distDirectory = path.join(__dirname, 'dist');
@ -49,10 +50,7 @@ function _generateBaseWebpackConfiguration(isProduction) {
rules: [ rules: [
{ {
test: /\.css$/, test: /\.css$/,
use: [ use: [require.resolve('style-loader'), require.resolve('css-loader')]
require.resolve('style-loader'),
require.resolve('css-loader')
]
}, },
{ {
test: /\.(scss|sass)$/, test: /\.(scss|sass)$/,
@ -74,15 +72,15 @@ function _generateBaseWebpackConfiguration(isProduction) {
resolve: { resolve: {
extensions: ['.js', '.jsx', '.json'], extensions: ['.js', '.jsx', '.json'],
alias: { alias: {
'tslib': 'tslib/tslib.es6' tslib: 'tslib/tslib.es6'
} }
}, },
devtool: (isProduction) ? undefined : 'source-map', devtool: isProduction ? undefined : 'source-map',
entry: { entry: {
'tsdoc-playground': path.join(__dirname, 'lib', 'index.js') 'tsdoc-playground': path.join(__dirname, 'lib', 'index.js')
}, },
externals: { externals: {
'react': 'React', react: 'React',
'react-dom': 'ReactDOM', 'react-dom': 'ReactDOM',
'react-dom/server': 'ReactDOMServer' 'react-dom/server': 'ReactDOMServer'
}, },
@ -112,7 +110,7 @@ function _generateBaseWebpackConfiguration(isProduction) {
new SetPublicPathPlugin({ new SetPublicPathPlugin({
scriptName: { scriptName: {
isTokenized: true, isTokenized: true,
name: '[name]_?[a-zA-Z0-9-_]*\.js' name: '[name]_?[a-zA-Z0-9-_]*.js'
} }
}), }),
new BundleAnalyzerPlugin({ new BundleAnalyzerPlugin({

Просмотреть файл

@ -215,7 +215,7 @@
// "tools", // non-shipping projects that are part of the developer toolchain // "tools", // non-shipping projects that are part of the developer toolchain
// "prototypes" // experiments that should mostly be ignored by the review process // "prototypes" // experiments that should mostly be ignored by the review process
// ], // ],
// //
// /** // /**
// * A list of NPM package scopes that will be excluded from review. // * A list of NPM package scopes that will be excluded from review.
// * We recommend to exclude TypeScript typings (the "@types" scope), because // * We recommend to exclude TypeScript typings (the "@types" scope), because
@ -341,7 +341,7 @@
// * The folder name for this variant. // * The folder name for this variant.
// */ // */
// "variantName": "old-sdk", // "variantName": "old-sdk",
// //
// /** // /**
// * An informative description // * An informative description
// */ // */
@ -380,19 +380,19 @@
// * The NPM package name of the project (must match package.json) // * The NPM package name of the project (must match package.json)
// */ // */
// "packageName": "my-app", // "packageName": "my-app",
// //
// /** // /**
// * The path to the project folder, relative to the rush.json config file. // * The path to the project folder, relative to the rush.json config file.
// */ // */
// "projectFolder": "apps/my-app", // "projectFolder": "apps/my-app",
// //
// /** // /**
// * An optional category for usage in the "browser-approved-packages.json" // * An optional category for usage in the "browser-approved-packages.json"
// * and "nonbrowser-approved-packages.json" files. The value must be one of the // * and "nonbrowser-approved-packages.json" files. The value must be one of the
// * strings from the "reviewCategories" defined above. // * strings from the "reviewCategories" defined above.
// */ // */
// "reviewCategory": "production", // "reviewCategory": "production",
// //
// /** // /**
// * A list of local projects that appear as devDependencies for this project, but cannot be // * A list of local projects that appear as devDependencies for this project, but cannot be
// * locally linked because it would create a cyclic dependency; instead, the last published // * locally linked because it would create a cyclic dependency; instead, the last published
@ -401,20 +401,20 @@
// "cyclicDependencyProjects": [ // "cyclicDependencyProjects": [
// // "my-toolchain" // // "my-toolchain"
// ], // ],
// //
// /** // /**
// * If true, then this project will be ignored by the "rush check" command. // * If true, then this project will be ignored by the "rush check" command.
// * The default value is false. // * The default value is false.
// */ // */
// // "skipRushCheck": false, // // "skipRushCheck": false,
// //
// /** // /**
// * A flag indicating that changes to this project will be published to npm, which affects // * A flag indicating that changes to this project will be published to npm, which affects
// * the Rush change and publish workflows. The default value is false. // * the Rush change and publish workflows. The default value is false.
// * NOTE: "versionPolicyName" and "shouldPublish" are alternatives; you cannot specify them both. // * NOTE: "versionPolicyName" and "shouldPublish" are alternatives; you cannot specify them both.
// */ // */
// // "shouldPublish": false, // // "shouldPublish": false,
// //
// /** // /**
// * An optional version policy associated with the project. Version policies are defined // * An optional version policy associated with the project. Version policies are defined
// * in "version-policies.json" file. See the "rush publish" documentation for more info. // * in "version-policies.json" file. See the "rush publish" documentation for more info.
@ -422,13 +422,13 @@
// */ // */
// // "versionPolicyName": "" // // "versionPolicyName": ""
// }, // },
// //
// { // {
// "packageName": "my-controls", // "packageName": "my-controls",
// "projectFolder": "libraries/my-controls", // "projectFolder": "libraries/my-controls",
// "reviewCategory": "production" // "reviewCategory": "production"
// }, // },
// //
// { // {
// "packageName": "my-toolchain", // "packageName": "my-toolchain",
// "projectFolder": "tools/my-toolchain", // "projectFolder": "tools/my-toolchain",

Просмотреть файл

@ -59,7 +59,6 @@
// - Label selectors refer to declarations indicated using the {@label LABEL} tag. The label // - Label selectors refer to declarations indicated using the {@label LABEL} tag. The label
// must be all capitals (e.g. "WITH_NUMBERS") to avoid conflicts with system selectors. // must be all capitals (e.g. "WITH_NUMBERS") to avoid conflicts with system selectors.
//--------------------------------------------------------- //---------------------------------------------------------
// Static vs instance members // Static vs instance members
@ -72,8 +71,7 @@ export class ClassA1 {
* Shortest name: {@link ClassA1.memberA2} * Shortest name: {@link ClassA1.memberA2}
* Full name: {@link (ClassA1:class).(memberA2:instance)} * Full name: {@link (ClassA1:class).(memberA2:instance)}
*/ */
public memberA2(): void { public memberA2(): void {}
}
/** /**
* Shortest name: {@link ClassA1.(memberA3:instance)} * Shortest name: {@link ClassA1.(memberA3:instance)}
@ -82,15 +80,13 @@ export class ClassA1 {
* NOTE: The shortest name cannot omit "instance" because there is a static member * NOTE: The shortest name cannot omit "instance" because there is a static member
* with the same name. * with the same name.
*/ */
public memberA3(): void { public memberA3(): void {}
}
/** /**
* Shortest name: {@link ClassA1.(memberA3:static)} * Shortest name: {@link ClassA1.(memberA3:static)}
* Full name: {@link (ClassA1:class).(memberA3:static)} * Full name: {@link (ClassA1:class).(memberA3:static)}
*/ */
public static memberA3(): void { public static memberA3(): void {}
}
/** /**
* Shortest name: {@link (ClassA1:constructor)} * Shortest name: {@link (ClassA1:constructor)}
@ -128,8 +124,7 @@ export namespace B1.B2.B3 {
* Shortest name: {@link B1.B2.B3.functionB4} * Shortest name: {@link B1.B2.B3.functionB4}
* Full name: {@link (B1:namespace).(B2:namespace).(B3:namespace).(functionB4:function)} * Full name: {@link (B1:namespace).(B2:namespace).(B3:namespace).(functionB4:function)}
*/ */
export function functionB4(): void { export function functionB4(): void {}
}
} }
//--------------------------------------------------------- //---------------------------------------------------------
@ -196,7 +191,6 @@ export function functionD1(xy?: string | number): string | number {
* Full name: {@link (MergedE1:class)} * Full name: {@link (MergedE1:class)}
*/ */
export class MergedE1 { export class MergedE1 {
/** /**
* Shortest name: {@link (MergedE1:constructor)} * Shortest name: {@link (MergedE1:constructor)}
* Full name: {@link (MergedE1:constructor)} * Full name: {@link (MergedE1:constructor)}
@ -223,8 +217,7 @@ export class MergedE1 {
* the TSDoc standard discourages that, because resolving it might require * the TSDoc standard discourages that, because resolving it might require
* unbounded backtracking. * unbounded backtracking.
*/ */
public memberE2(): void { public memberE2(): void {}
}
} }
/** /**
@ -236,8 +229,7 @@ export namespace MergedE1 {
* Shortest name: {@link (MergedE1:namespace).memberE2} * Shortest name: {@link (MergedE1:namespace).memberE2}
* Full name: {@link (MergedE1:namespace).(memberE2:function)} * Full name: {@link (MergedE1:namespace).(memberE2:function)}
*/ */
export function memberE2(): void { export function memberE2(): void {}
}
} }
//--------------------------------------------------------- //---------------------------------------------------------
@ -266,8 +258,7 @@ export function MergedF1(xy: string | number): string | number {
* Shortest name: {@link (MergedF1:namespace)} * Shortest name: {@link (MergedF1:namespace)}
* Full name: {@link (MergedF1:namespace)} * Full name: {@link (MergedF1:namespace)}
*/ */
export namespace MergedF1 { export namespace MergedF1 {}
}
//--------------------------------------------------------- //---------------------------------------------------------
// Merged declarations with extension of the same thing // Merged declarations with extension of the same thing
@ -320,7 +311,6 @@ export namespace MergedG1 {
export let mergedG3: string = ''; export let mergedG3: string = '';
} }
//--------------------------------------------------------- //---------------------------------------------------------
// Enum members // Enum members
@ -371,7 +361,6 @@ export class ClassI1 {
} }
} }
//--------------------------------------------------------- //---------------------------------------------------------
// Malformed names // Malformed names
// //
@ -420,7 +409,6 @@ export interface InterfaceJ1 {
*/ */
'&copy;': string; '&copy;': string;
/** /**
* Shortest name: {@link InterfaceJ1."1.5"} * Shortest name: {@link InterfaceJ1."1.5"}
* Full name: {@link (InterfaceJ1:interface)."1.5"} * Full name: {@link (InterfaceJ1:interface)."1.5"}

Просмотреть файл

@ -1,10 +1,7 @@
// This is a workaround for https://github.com/eslint/eslint/issues/3458 // This is a workaround for https://github.com/eslint/eslint/issues/3458
require("@rushstack/eslint-config/patch/modern-module-resolution"); require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = { module.exports = {
extends: [ extends: ['@rushstack/eslint-config/profile/node', '@rushstack/eslint-config/mixins/friendly-locals'],
"@rushstack/eslint-config/profile/node",
"@rushstack/eslint-config/mixins/friendly-locals",
],
parserOptions: { tsconfigRootDir: __dirname }, parserOptions: { tsconfigRootDir: __dirname },
}; };

Просмотреть файл

@ -6,7 +6,7 @@ import {
TSDocMessageId, TSDocMessageId,
ParserMessage, ParserMessage,
TextRange, TextRange,
IParserMessageParameters IParserMessageParameters,
} from '@microsoft/tsdoc'; } from '@microsoft/tsdoc';
import * as fs from 'fs'; import * as fs from 'fs';
import * as resolve from 'resolve'; import * as resolve from 'resolve';
@ -48,8 +48,8 @@ interface IConfigJson {
*/ */
export class TSDocConfigFile { export class TSDocConfigFile {
public static readonly FILENAME: string = 'tsdoc.json'; public static readonly FILENAME: string = 'tsdoc.json';
public static readonly CURRENT_SCHEMA_URL: string public static readonly CURRENT_SCHEMA_URL: string =
= 'https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json'; 'https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json';
/** /**
* A queryable log that reports warnings and error messages that occurred during parsing. * A queryable log that reports warnings and error messages that occurred during parsing.
@ -75,7 +75,7 @@ export class TSDocConfigFile {
this._fileMTime = 0; this._fileMTime = 0;
this._tsdocSchema = ''; this._tsdocSchema = '';
this._extendsPaths = []; this._extendsPaths = [];
this._tagDefinitions= []; this._tagDefinitions = [];
} }
/** /**
@ -191,7 +191,7 @@ export class TSDocConfigFile {
this._reportError({ this._reportError({
messageId: TSDocMessageId.ConfigFileUnsupportedSchema, messageId: TSDocMessageId.ConfigFileUnsupportedSchema,
messageText: `Unsupported JSON "$schema" value; expecting "${TSDocConfigFile.CURRENT_SCHEMA_URL}"`, messageText: `Unsupported JSON "$schema" value; expecting "${TSDocConfigFile.CURRENT_SCHEMA_URL}"`,
textRange: TextRange.empty textRange: TextRange.empty,
}); });
return; return;
} }
@ -204,7 +204,7 @@ export class TSDocConfigFile {
this._reportError({ this._reportError({
messageId: TSDocMessageId.ConfigFileSchemaError, messageId: TSDocMessageId.ConfigFileSchemaError,
messageText: 'Error loading config file: ' + description, messageText: 'Error loading config file: ' + description,
textRange: TextRange.empty textRange: TextRange.empty,
}); });
return; return;
} }
@ -217,29 +217,39 @@ export class TSDocConfigFile {
for (const jsonTagDefinition of configJson.tagDefinitions || []) { for (const jsonTagDefinition of configJson.tagDefinitions || []) {
let syntaxKind: TSDocTagSyntaxKind; let syntaxKind: TSDocTagSyntaxKind;
switch (jsonTagDefinition.syntaxKind) { switch (jsonTagDefinition.syntaxKind) {
case 'inline': syntaxKind = TSDocTagSyntaxKind.InlineTag; break; case 'inline':
case 'block': syntaxKind = TSDocTagSyntaxKind.BlockTag; break; syntaxKind = TSDocTagSyntaxKind.InlineTag;
case 'modifier': syntaxKind = TSDocTagSyntaxKind.ModifierTag; break; break;
case 'block':
syntaxKind = TSDocTagSyntaxKind.BlockTag;
break;
case 'modifier':
syntaxKind = TSDocTagSyntaxKind.ModifierTag;
break;
default: default:
// The JSON schema should have caught this error // The JSON schema should have caught this error
throw new Error('Unexpected tag kind'); throw new Error('Unexpected tag kind');
} }
this._tagDefinitions.push(new TSDocTagDefinition({ this._tagDefinitions.push(
tagName: jsonTagDefinition.tagName, new TSDocTagDefinition({
syntaxKind: syntaxKind, tagName: jsonTagDefinition.tagName,
allowMultiple: jsonTagDefinition.allowMultiple syntaxKind: syntaxKind,
})); allowMultiple: jsonTagDefinition.allowMultiple,
})
);
} }
} }
private _loadWithExtends(configFilePath: string, referencingConfigFile: TSDocConfigFile | undefined, private _loadWithExtends(
alreadyVisitedPaths: Set<string>): void { configFilePath: string,
referencingConfigFile: TSDocConfigFile | undefined,
alreadyVisitedPaths: Set<string>
): void {
if (!configFilePath) { if (!configFilePath) {
this._reportError({ this._reportError({
messageId: TSDocMessageId.ConfigFileNotFound, messageId: TSDocMessageId.ConfigFileNotFound,
messageText: 'File not found', messageText: 'File not found',
textRange: TextRange.empty textRange: TextRange.empty,
}); });
return; return;
} }
@ -250,7 +260,7 @@ export class TSDocConfigFile {
this._reportError({ this._reportError({
messageId: TSDocMessageId.ConfigFileNotFound, messageId: TSDocMessageId.ConfigFileNotFound,
messageText: 'File not found', messageText: 'File not found',
textRange: TextRange.empty textRange: TextRange.empty,
}); });
return; return;
} }
@ -260,7 +270,7 @@ export class TSDocConfigFile {
this._reportError({ this._reportError({
messageId: TSDocMessageId.ConfigFileCyclicExtends, messageId: TSDocMessageId.ConfigFileCyclicExtends,
messageText: `Circular reference encountered for "extends" field of "${referencingConfigFile.filePath}"`, messageText: `Circular reference encountered for "extends" field of "${referencingConfigFile.filePath}"`,
textRange: TextRange.empty textRange: TextRange.empty,
}); });
return; return;
} }
@ -281,7 +291,7 @@ export class TSDocConfigFile {
this._reportError({ this._reportError({
messageId: TSDocMessageId.ConfigFileUnresolvedExtends, messageId: TSDocMessageId.ConfigFileUnresolvedExtends,
messageText: `Unable to resolve "extends" reference to "${extendsField}"`, messageText: `Unable to resolve "extends" reference to "${extendsField}"`,
textRange: TextRange.empty textRange: TextRange.empty,
}); });
} }

Просмотреть файл

@ -3,10 +3,7 @@ import * as path from 'path';
import { TSDocConfigFile } from '../TSDocConfigFile'; import { TSDocConfigFile } from '../TSDocConfigFile';
function getRelativePath(testPath: string): string { function getRelativePath(testPath: string): string {
return path return path.relative(__dirname, testPath).split('\\').join('/');
.relative(__dirname, testPath)
.split('\\')
.join('/');
} }
expect.addSnapshotSerializer({ expect.addSnapshotSerializer({

Просмотреть файл

@ -1,2 +1 @@
{ {}
}

Просмотреть файл

@ -1,2 +1 @@
{ {}
}

Просмотреть файл

@ -1,2 +1 @@
{ {}
}

Просмотреть файл

@ -1,9 +1,6 @@
{ {
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
"extends": [ "extends": ["./base1/tsdoc-base1.json", "./base2/tsdoc-base2.json"],
"./base1/tsdoc-base1.json",
"./base2/tsdoc-base2.json"
],
"tagDefinitions": [ "tagDefinitions": [
{ {
"tagName": "@root", "tagName": "@root",

Просмотреть файл

@ -1,2 +1 @@
{ {}
}

Просмотреть файл

@ -1,8 +1,6 @@
{ {
"$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json", "$schema": "https://developer.microsoft.com/json-schemas/tsdoc/v0/tsdoc.schema.json",
"extends": [ "extends": ["example-lib/dist/tsdoc-example.json"],
"example-lib/dist/tsdoc-example.json"
],
"tagDefinitions": [ "tagDefinitions": [
{ {
"tagName": "@root", "tagName": "@root",

Просмотреть файл

@ -1,10 +1,7 @@
// This is a workaround for https://github.com/eslint/eslint/issues/3458 // This is a workaround for https://github.com/eslint/eslint/issues/3458
require("@rushstack/eslint-config/patch/modern-module-resolution"); require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = { module.exports = {
extends: [ extends: ['@rushstack/eslint-config/profile/web-app', '@rushstack/eslint-config/mixins/friendly-locals'],
"@rushstack/eslint-config/profile/web-app", parserOptions: { tsconfigRootDir: __dirname }
"@rushstack/eslint-config/mixins/friendly-locals",
],
parserOptions: { tsconfigRootDir: __dirname },
}; };

Просмотреть файл

@ -24,7 +24,7 @@
} }
} }
}, },
"required": [ "$schema" ], "required": ["$schema"],
"additionalProperties": false, "additionalProperties": false,
"definitions": { "definitions": {

Просмотреть файл

@ -20,7 +20,5 @@ test('01 trimSpacesInParagraphNodes()', () => {
expect(firstNode.kind).toEqual(DocNodeKind.Paragraph); expect(firstNode.kind).toEqual(DocNodeKind.Paragraph);
const paragraph: DocParagraph = firstNode as DocParagraph; const paragraph: DocParagraph = firstNode as DocParagraph;
const transformedParagraph: DocParagraph = DocNodeTransforms.trimSpacesInParagraph(paragraph); const transformedParagraph: DocParagraph = DocNodeTransforms.trimSpacesInParagraph(paragraph);
expect( expect(TestHelpers.getDocNodeSnapshot(transformedParagraph)).toMatchSnapshot();
TestHelpers.getDocNodeSnapshot(transformedParagraph)
).toMatchSnapshot();
}); });

Просмотреть файл

@ -4,69 +4,57 @@ import { DocSection, DocPlainText, DocSoftBreak, DocParagraph, DocBlockTag } fro
import { TSDocConfiguration } from '../configuration/TSDocConfiguration'; import { TSDocConfiguration } from '../configuration/TSDocConfiguration';
test('01 Basic paragraph splitting', () => { test('01 Basic paragraph splitting', () => {
TestHelpers.parseAndMatchDocCommentSnapshot([ TestHelpers.parseAndMatchDocCommentSnapshot(
'/**', [
' * ', '/**',
' * This is the', ' * ',
' * first paragraph.', ' * This is the',
' * \t ', ' * first paragraph.',
' * ', ' * \t ',
' * \t ', ' * ',
' * This is the second paragraph.', ' * \t ',
' *', ' * This is the second paragraph.',
' * This is the third paragraph.', ' *',
' *', ' * This is the third paragraph.',
' * ', ' *',
' */' ' * ',
].join('\n')); ' */'
].join('\n')
);
}); });
test('02 Basic paragraph splitting in blocks', () => { test('02 Basic paragraph splitting in blocks', () => {
TestHelpers.parseAndMatchDocCommentSnapshot([ TestHelpers.parseAndMatchDocCommentSnapshot(
'/**', ['/**', ' * P1', ' * @remarks P2', ' *', ' * P3 @deprecated P4', ' *', ' * P5', ' */'].join('\n')
' * P1', );
' * @remarks P2',
' *',
' * P3 @deprecated P4',
' *',
' * P5',
' */'
].join('\n'));
}); });
test('03 Degenerate comment framing', () => { test('03 Degenerate comment framing', () => {
TestHelpers.parseAndMatchDocCommentSnapshot([ TestHelpers.parseAndMatchDocCommentSnapshot(
'/** line 1', ['/** line 1', ' * line 2', '', ' * @public line 3*/'].join('\n')
' * line 2', );
'',
' * @public line 3*/'
].join('\n'));
}); });
test('04 Degenerate manually constructed nodes', () => { test('04 Degenerate manually constructed nodes', () => {
const configuration: TSDocConfiguration = new TSDocConfiguration(); const configuration: TSDocConfiguration = new TSDocConfiguration();
const docSection: DocSection = new DocSection({ configuration }, const docSection: DocSection = new DocSection({ configuration }, [
[ new DocParagraph({ configuration }, [
new DocParagraph({ configuration }, new DocPlainText({ configuration, text: ' para 1 ' }),
[ new DocSoftBreak({ configuration }),
new DocPlainText({ configuration, text: ' para 1 ' }), new DocPlainText({ configuration, text: ' ' }),
new DocSoftBreak({ configuration }), new DocSoftBreak({ configuration }),
new DocPlainText({ configuration, text: ' ' }), new DocPlainText({ configuration, text: ' \t ' }),
new DocSoftBreak({ configuration }), new DocPlainText({ configuration, text: ' ' }),
new DocPlainText({ configuration, text: ' \t ' }), new DocBlockTag({ configuration, tagName: '@public' }),
new DocPlainText({ configuration, text: ' ' }), new DocPlainText({ configuration, text: ' para 2 ' }),
new DocBlockTag( { configuration, tagName: '@public' }), new DocSoftBreak({ configuration }),
new DocPlainText({ configuration, text: ' para 2 ' }), new DocSoftBreak({ configuration }),
new DocSoftBreak({ configuration }), new DocPlainText({ configuration, text: ' para 3 ' })
new DocSoftBreak({ configuration }), ]),
new DocPlainText({ configuration, text: ' para 3 ' }) // Currently we do not discard empty paragraphs
] new DocParagraph({ configuration })
), ]);
// Currently we do not discard empty paragraphs
new DocParagraph({ configuration })
]
);
ParagraphSplitter.splitParagraphsForSection(docSection); ParagraphSplitter.splitParagraphsForSection(docSection);
expect(TestHelpers.getDocNodeSnapshot(docSection)).toMatchSnapshot(); expect(TestHelpers.getDocNodeSnapshot(docSection)).toMatchSnapshot();

Просмотреть файл

@ -9,13 +9,9 @@ import {
import { TestHelpers } from '../parser/__tests__/TestHelpers'; import { TestHelpers } from '../parser/__tests__/TestHelpers';
test('01 Simple @beta and @internal extraction', () => { test('01 Simple @beta and @internal extraction', () => {
const parserContext: ParserContext = TestHelpers.parseAndMatchDocCommentSnapshot([ const parserContext: ParserContext = TestHelpers.parseAndMatchDocCommentSnapshot(
'/**', ['/**', ' * START @beta', ' * @unknownTag', ' * @internal @internal END', ' */'].join('\n')
' * START @beta', );
' * @unknownTag',
' * @internal @internal END',
' */'
].join('\n'));
const docComment: DocComment = parserContext.docComment; const docComment: DocComment = parserContext.docComment;
const modifierTagSet: StandardModifierTagSet = docComment.modifierTagSet; const modifierTagSet: StandardModifierTagSet = docComment.modifierTagSet;
@ -38,23 +34,26 @@ test('02 A basic TSDoc comment with common components', () => {
}) })
]); ]);
const parserContext: ParserContext = TestHelpers.parseAndMatchDocCommentSnapshot([ const parserContext: ParserContext = TestHelpers.parseAndMatchDocCommentSnapshot(
'/**', [
' * Returns the average of two numbers.', '/**',
' *', ' * Returns the average of two numbers.',
' * @remarks', ' *',
' * This method is part of the {@link core-library#Statistics | Statistics subsystem}.', ' * @remarks',
' *', ' * This method is part of the {@link core-library#Statistics | Statistics subsystem}.',
' * @customBlock', ' *',
' * This is a custom block containing an @undefinedBlockTag', ' * @customBlock',
' *', ' * This is a custom block containing an @undefinedBlockTag',
' * @param x - The first input number', ' *',
' * @param y$_ - The second input number', ' * @param x - The first input number',
' * @returns The arithmetic mean of `x` and `y$_`', ' * @param y$_ - The second input number',
' *', ' * @returns The arithmetic mean of `x` and `y$_`',
' * @beta @customModifier', ' *',
' */' ' * @beta @customModifier',
].join('\n'), configuration); ' */'
].join('\n'),
configuration
);
const docComment: DocComment = parserContext.docComment; const docComment: DocComment = parserContext.docComment;
expect(docComment.modifierTagSet.hasTagName('@customModifier')).toEqual(true); expect(docComment.modifierTagSet.hasTagName('@customModifier')).toEqual(true);
@ -73,88 +72,93 @@ test('03 Jumbled order', () => {
}) })
]); ]);
const parserContext: ParserContext = TestHelpers.parseAndMatchDocCommentSnapshot([ const parserContext: ParserContext = TestHelpers.parseAndMatchDocCommentSnapshot(
'/**', [
' * Returns the average of two numbers. @remarks This method is part of the', '/**',
' * {@link core-library#Statistics | Statistics subsystem}.', ' * Returns the average of two numbers. @remarks This method is part of the',
' * @beta @customModifier', ' * {@link core-library#Statistics | Statistics subsystem}.',
' * @returns The arithmetic mean of `x` and `y`', ' * @beta @customModifier',
' * @param x - The first input number @param y - The second input number', ' * @returns The arithmetic mean of `x` and `y`',
' * @customBlock', ' * @param x - The first input number @param y - The second input number',
' * This is a custom block containing an @undefinedBlockTag', ' * @customBlock',
' */' ' * This is a custom block containing an @undefinedBlockTag',
].join('\n'), configuration); ' */'
].join('\n'),
configuration
);
const docComment: DocComment = parserContext.docComment; const docComment: DocComment = parserContext.docComment;
expect(docComment.modifierTagSet.hasTagName('@customModifier')).toEqual(true); expect(docComment.modifierTagSet.hasTagName('@customModifier')).toEqual(true);
}); });
test('03 Incomplete @param blocks', () => { test('03 Incomplete @param blocks', () => {
TestHelpers.parseAndMatchDocCommentSnapshot([ TestHelpers.parseAndMatchDocCommentSnapshot(
'/**', [
' * @param - The first input number', '/**',
' * @param y The second input number', ' * @param - The first input number',
' * @returns The arithmetic mean of `x` and `y`', ' * @param y The second input number',
' */' ' * @returns The arithmetic mean of `x` and `y`',
].join('\n')); ' */'
].join('\n')
);
}); });
test('04 typeParam blocks', () => { test('04 typeParam blocks', () => {
TestHelpers.parseAndMatchDocCommentSnapshot([ TestHelpers.parseAndMatchDocCommentSnapshot(
'/**', [
' * Constructs a map from a JavaScript object', '/**',
' *', ' * Constructs a map from a JavaScript object',
' * @typeParam K - The generic type parameter indicating the key type', ' *',
' * @param jsonObject - The input object', ' * @typeParam K - The generic type parameter indicating the key type',
' * @typeParam V - The generic type parameter indicating the value type', ' * @param jsonObject - The input object',
' * @returns The map', ' * @typeParam V - The generic type parameter indicating the value type',
' */' ' * @returns The map',
].join('\n')); ' */'
].join('\n')
);
}); });
test('05 Invalid JSDoc syntax in @param blocks', () => { test('05 Invalid JSDoc syntax in @param blocks', () => {
TestHelpers.parseAndMatchDocCommentSnapshot([ TestHelpers.parseAndMatchDocCommentSnapshot(
'/**', [
' * @param {type} a - description', '/**',
' * @param {{}} b - description', ' * @param {type} a - description',
' * @param {"{"} c - description', ' * @param {{}} b - description',
' * @param {"\\""} d - description', ' * @param {"{"} c - description',
' * @param e {type} - description', ' * @param {"\\""} d - description',
' * @param f {{}} - description', ' * @param e {type} - description',
' * @param g {"{"} - description', ' * @param f {{}} - description',
' * @param h - {type} description', ' * @param g {"{"} - description',
' * @param i - {{}} description', ' * @param h - {type} description',
' * @param j - {"{"} description', ' * @param i - {{}} description',
' * @param [k] - description', ' * @param j - {"{"} description',
' * @param [l=] - description', ' * @param [k] - description',
' * @param [m=[]] - description', ' * @param [l=] - description',
' * @param [n="["] - description', ' * @param [m=[]] - description',
' * @param [o="\\""] - description', ' * @param [n="["] - description',
' */' ' * @param [o="\\""] - description',
].join('\n')); ' */'
].join('\n')
);
}); });
test('06 Invalid JSDoc optional name', () => { test('06 Invalid JSDoc optional name', () => {
TestHelpers.parseAndMatchDocCommentSnapshot([ TestHelpers.parseAndMatchDocCommentSnapshot(
'/**', [
' * Example 1', '/**',
' *', ' * Example 1',
' * @param [n - this is', ' *',
' * the description', ' * @param [n - this is',
' *', ' * the description',
' * @public', ' *',
' */' ' * @public',
].join('\n')); ' */'
].join('\n')
);
}); });
test('07 Invalid JSDoc type', () => { test('07 Invalid JSDoc type', () => {
TestHelpers.parseAndMatchDocCommentSnapshot([ TestHelpers.parseAndMatchDocCommentSnapshot(
'/**', ['/**', ' * Example 1', ' *', ' * @param { test', ' *', ' * @public', ' */'].join('\n')
' * Example 1', );
' *', });
' * @param { test',
' *',
' * @public',
' */'
].join('\n'));
});

Просмотреть файл

@ -19,8 +19,11 @@ export class DeclarationReference {
private _navigation: Navigation.Locals | Navigation.Exports | undefined; private _navigation: Navigation.Locals | Navigation.Exports | undefined;
private _symbol: SymbolReference | undefined; private _symbol: SymbolReference | undefined;
public constructor(source?: ModuleSource | GlobalSource, navigation?: Navigation.Locals | Navigation.Exports, public constructor(
symbol?: SymbolReference) { source?: ModuleSource | GlobalSource,
navigation?: Navigation.Locals | Navigation.Exports,
symbol?: SymbolReference
) {
this._source = source; this._source = source;
this._navigation = navigation; this._navigation = navigation;
this._symbol = symbol; this._symbol = symbol;
@ -48,8 +51,7 @@ export class DeclarationReference {
} }
public get isEmpty(): boolean { public get isEmpty(): boolean {
return this.source === undefined return this.source === undefined && this.symbol === undefined;
&& this.symbol === undefined;
} }
public static parse(text: string): DeclarationReference { public static parse(text: string): DeclarationReference {
@ -77,9 +79,11 @@ export class DeclarationReference {
*/ */
public static isWellFormedComponentString(text: string): boolean { public static isWellFormedComponentString(text: string): boolean {
const scanner: Scanner = new Scanner(text); const scanner: Scanner = new Scanner(text);
return scanner.scan() === Token.String ? scanner.scan() === Token.EofToken : return scanner.scan() === Token.String
scanner.token() === Token.Text ? scanner.scan() === Token.EofToken : ? scanner.scan() === Token.EofToken
scanner.token() === Token.EofToken; : scanner.token() === Token.Text
? scanner.scan() === Token.EofToken
: scanner.token() === Token.EofToken;
} }
/** /**
@ -120,10 +124,12 @@ export class DeclarationReference {
*/ */
public static isWellFormedModuleSourceString(text: string): boolean { public static isWellFormedModuleSourceString(text: string): boolean {
const scanner: Scanner = new Scanner(text + '!'); const scanner: Scanner = new Scanner(text + '!');
return scanner.rescanModuleSource() === Token.ModuleSource return (
&& !scanner.stringIsUnterminated scanner.rescanModuleSource() === Token.ModuleSource &&
&& scanner.scan() === Token.ExclamationToken !scanner.stringIsUnterminated &&
&& scanner.scan() === Token.EofToken; scanner.scan() === Token.ExclamationToken &&
scanner.scan() === Token.EofToken
);
} }
/** /**
@ -181,8 +187,12 @@ export class DeclarationReference {
return this._source === source ? this : new DeclarationReference(source, this._navigation, this._symbol); return this._source === source ? this : new DeclarationReference(source, this._navigation, this._symbol);
} }
public withNavigation(navigation: Navigation.Locals | Navigation.Exports | undefined): DeclarationReference { public withNavigation(
return this._navigation === navigation ? this : new DeclarationReference(this._source, navigation, this._symbol); navigation: Navigation.Locals | Navigation.Exports | undefined
): DeclarationReference {
return this._navigation === navigation
? this
: new DeclarationReference(this._source, navigation, this._symbol);
} }
public withSymbol(symbol: SymbolReference | undefined): DeclarationReference { public withSymbol(symbol: SymbolReference | undefined): DeclarationReference {
@ -190,8 +200,9 @@ export class DeclarationReference {
} }
public withComponentPath(componentPath: ComponentPath): DeclarationReference { public withComponentPath(componentPath: ComponentPath): DeclarationReference {
return this.withSymbol(this.symbol ? this.symbol.withComponentPath(componentPath) : return this.withSymbol(
new SymbolReference(componentPath)); this.symbol ? this.symbol.withComponentPath(componentPath) : new SymbolReference(componentPath)
);
} }
public withMeaning(meaning: Meaning | undefined): DeclarationReference { public withMeaning(meaning: Meaning | undefined): DeclarationReference {
@ -226,9 +237,10 @@ export class DeclarationReference {
} }
public toString(): string { public toString(): string {
const navigation: string = this._source instanceof ModuleSource const navigation: string =
&& this._symbol this._source instanceof ModuleSource && this._symbol && this.navigation === Navigation.Locals
&& this.navigation === Navigation.Locals ? '~' : ''; ? '~'
: '';
return `${this.source || ''}${navigation}${this.symbol || ''}`; return `${this.source || ''}${navigation}${this.symbol || ''}`;
} }
} }
@ -254,7 +266,8 @@ export class ModuleSource {
private _pathComponents: IParsedPackage | undefined; private _pathComponents: IParsedPackage | undefined;
public constructor(path: string, userEscaped: boolean = true) { public constructor(path: string, userEscaped: boolean = true) {
this.escapedPath = this instanceof ParsedModuleSource ? path : escapeModuleSourceIfNeeded(path, userEscaped); this.escapedPath =
this instanceof ParsedModuleSource ? path : escapeModuleSourceIfNeeded(path, userEscaped);
} }
public get path(): string { public get path(): string {
@ -278,9 +291,11 @@ export class ModuleSource {
return this._getOrParsePathComponents().importPath || ''; return this._getOrParsePathComponents().importPath || '';
} }
public static fromScopedPackage(scopeName: string | undefined, unscopedPackageName: string, importPath?: string): public static fromScopedPackage(
ModuleSource { scopeName: string | undefined,
unscopedPackageName: string,
importPath?: string
): ModuleSource {
let packageName: string = unscopedPackageName; let packageName: string = unscopedPackageName;
if (scopeName) { if (scopeName) {
if (scopeName.charAt(0) === '@') { if (scopeName.charAt(0) === '@') {
@ -302,7 +317,6 @@ export class ModuleSource {
packageName: string, packageName: string,
importPath?: string importPath?: string
): ModuleSource { ): ModuleSource {
if (!parsed) { if (!parsed) {
throw new Error('Parsed package must be provided.'); throw new Error('Parsed package must be provided.');
} }
@ -349,8 +363,7 @@ export class ModuleSource {
} }
} }
class ParsedModuleSource extends ModuleSource { class ParsedModuleSource extends ModuleSource {}
}
// matches the following: // matches the following:
// 'foo' -> ["foo", "foo", undefined, "foo", undefined] // 'foo' -> ["foo", "foo", undefined, "foo", undefined]
@ -398,8 +411,7 @@ function parsePackageName(text: string): IParsedPackage | null {
export class GlobalSource { export class GlobalSource {
public static readonly instance: GlobalSource = new GlobalSource(); public static readonly instance: GlobalSource = new GlobalSource();
private constructor() { private constructor() {}
}
public toString(): string { public toString(): string {
return '!'; return '!';
@ -409,10 +421,7 @@ export class GlobalSource {
/** /**
* @beta * @beta
*/ */
export type Component = export type Component = ComponentString | ComponentReference;
| ComponentString
| ComponentReference
;
/** /**
* @beta * @beta
@ -433,11 +442,7 @@ export namespace Component {
/** /**
* @beta * @beta
*/ */
export type ComponentLike = export type ComponentLike = Component | DeclarationReference | string;
| Component
| DeclarationReference
| string
;
/** /**
* @beta * @beta
@ -454,8 +459,7 @@ export class ComponentString {
} }
} }
class ParsedComponentString extends ComponentString { class ParsedComponentString extends ComponentString {}
}
/** /**
* @beta * @beta
@ -486,10 +490,7 @@ export class ComponentReference {
/** /**
* @beta * @beta
*/ */
export type ComponentPath = export type ComponentPath = ComponentRoot | ComponentNavigation;
| ComponentRoot
| ComponentNavigation
;
/** /**
* @beta * @beta
@ -501,7 +502,11 @@ export abstract class ComponentPathBase {
this.component = component; this.component = component;
} }
public addNavigationStep(this: ComponentPath, navigation: Navigation, component: ComponentLike): ComponentPath { public addNavigationStep(
this: ComponentPath,
navigation: Navigation,
component: ComponentLike
): ComponentPath {
// tslint:disable-next-line:no-use-before-declare // tslint:disable-next-line:no-use-before-declare
return new ComponentNavigation(this, navigation, Component.from(component)); return new ComponentNavigation(this, navigation, Component.from(component));
} }
@ -540,12 +545,15 @@ export class ComponentNavigation extends ComponentPathBase {
} }
public withNavigation(navigation: Navigation): ComponentNavigation { public withNavigation(navigation: Navigation): ComponentNavigation {
return this.navigation === navigation ? this : new ComponentNavigation(this.parent, navigation, this.component); return this.navigation === navigation
? this
: new ComponentNavigation(this.parent, navigation, this.component);
} }
public withComponent(component: ComponentLike): ComponentNavigation { public withComponent(component: ComponentLike): ComponentNavigation {
return this.component === component ? this : return this.component === component
new ComponentNavigation(this.parent, this.navigation, Component.from(component)); ? this
: new ComponentNavigation(this.parent, this.navigation, Component.from(component));
} }
public toString(): string { public toString(): string {
@ -557,20 +565,20 @@ export class ComponentNavigation extends ComponentPathBase {
* @beta * @beta
*/ */
export const enum Meaning { export const enum Meaning {
Class = 'class', // SymbolFlags.Class Class = 'class', // SymbolFlags.Class
Interface = 'interface', // SymbolFlags.Interface Interface = 'interface', // SymbolFlags.Interface
TypeAlias = 'type', // SymbolFlags.TypeAlias TypeAlias = 'type', // SymbolFlags.TypeAlias
Enum = 'enum', // SymbolFlags.Enum Enum = 'enum', // SymbolFlags.Enum
Namespace = 'namespace', // SymbolFlags.Module Namespace = 'namespace', // SymbolFlags.Module
Function = 'function', // SymbolFlags.Function Function = 'function', // SymbolFlags.Function
Variable = 'var', // SymbolFlags.Variable Variable = 'var', // SymbolFlags.Variable
Constructor = 'constructor', // SymbolFlags.Constructor Constructor = 'constructor', // SymbolFlags.Constructor
Member = 'member', // SymbolFlags.ClassMember | SymbolFlags.EnumMember Member = 'member', // SymbolFlags.ClassMember | SymbolFlags.EnumMember
Event = 'event', // Event = 'event', //
CallSignature = 'call', // SymbolFlags.Signature (for __call) CallSignature = 'call', // SymbolFlags.Signature (for __call)
ConstructSignature = 'new', // SymbolFlags.Signature (for __new) ConstructSignature = 'new', // SymbolFlags.Signature (for __new)
IndexSignature = 'index', // SymbolFlags.Signature (for __index) IndexSignature = 'index', // SymbolFlags.Signature (for __index)
ComplexType = 'complex' // Any complex type ComplexType = 'complex' // Any complex type
} }
/** /**
@ -590,7 +598,10 @@ export class SymbolReference {
public readonly meaning: Meaning | undefined; public readonly meaning: Meaning | undefined;
public readonly overloadIndex: number | undefined; public readonly overloadIndex: number | undefined;
public constructor(component: ComponentPath | undefined, { meaning, overloadIndex }: ISymbolReferenceOptions = {}) { public constructor(
component: ComponentPath | undefined,
{ meaning, overloadIndex }: ISymbolReferenceOptions = {}
) {
this.componentPath = component; this.componentPath = component;
this.overloadIndex = overloadIndex; this.overloadIndex = overloadIndex;
this.meaning = meaning; this.meaning = meaning;
@ -601,29 +612,35 @@ export class SymbolReference {
} }
public withComponentPath(componentPath: ComponentPath | undefined): SymbolReference { public withComponentPath(componentPath: ComponentPath | undefined): SymbolReference {
return this.componentPath === componentPath ? this : new SymbolReference(componentPath, { return this.componentPath === componentPath
meaning: this.meaning, ? this
overloadIndex: this.overloadIndex : new SymbolReference(componentPath, {
}); meaning: this.meaning,
overloadIndex: this.overloadIndex
});
} }
public withMeaning(meaning: Meaning | undefined): SymbolReference { public withMeaning(meaning: Meaning | undefined): SymbolReference {
return this.meaning === meaning ? this : new SymbolReference(this.componentPath, { return this.meaning === meaning
meaning, ? this
overloadIndex: this.overloadIndex : new SymbolReference(this.componentPath, {
}); meaning,
overloadIndex: this.overloadIndex
});
} }
public withOverloadIndex(overloadIndex: number | undefined): SymbolReference { public withOverloadIndex(overloadIndex: number | undefined): SymbolReference {
return this.overloadIndex === overloadIndex ? this : new SymbolReference(this.componentPath, { return this.overloadIndex === overloadIndex
meaning: this.meaning, ? this
overloadIndex : new SymbolReference(this.componentPath, {
}); meaning: this.meaning,
overloadIndex
});
} }
public addNavigationStep(navigation: Navigation, component: ComponentLike): SymbolReference { public addNavigationStep(navigation: Navigation, component: ComponentLike): SymbolReference {
if (!this.componentPath) { if (!this.componentPath) {
throw new Error('Cannot add a navigation step to an empty symbol reference.'); throw new Error('Cannot add a navigation step to an empty symbol reference.');
} }
return new SymbolReference(this.componentPath.addNavigationStep(navigation, component)); return new SymbolReference(this.componentPath.addNavigationStep(navigation, component));
} }
@ -645,75 +662,108 @@ const enum Token {
None, None,
EofToken, EofToken,
// Punctuator // Punctuator
OpenBraceToken, // '{' OpenBraceToken, // '{'
CloseBraceToken, // '}' CloseBraceToken, // '}'
OpenParenToken, // '(' OpenParenToken, // '('
CloseParenToken, // ')' CloseParenToken, // ')'
OpenBracketToken, // '[' OpenBracketToken, // '['
CloseBracketToken, // ']' CloseBracketToken, // ']'
ExclamationToken, // '!' ExclamationToken, // '!'
DotToken, // '.' DotToken, // '.'
HashToken, // '#' HashToken, // '#'
TildeToken, // '~' TildeToken, // '~'
ColonToken, // ':' ColonToken, // ':'
CommaToken, // ',' CommaToken, // ','
AtToken, // '@' AtToken, // '@'
DecimalDigits, // '12345' DecimalDigits, // '12345'
String, // '"abc"' String, // '"abc"'
Text, // 'abc' Text, // 'abc'
ModuleSource, // 'abc/def!' (excludes '!') ModuleSource, // 'abc/def!' (excludes '!')
// Keywords // Keywords
ClassKeyword, // 'class' ClassKeyword, // 'class'
InterfaceKeyword, // 'interface' InterfaceKeyword, // 'interface'
TypeKeyword, // 'type' TypeKeyword, // 'type'
EnumKeyword, // 'enum' EnumKeyword, // 'enum'
NamespaceKeyword, // 'namespace' NamespaceKeyword, // 'namespace'
FunctionKeyword, // 'function' FunctionKeyword, // 'function'
VarKeyword, // 'var' VarKeyword, // 'var'
ConstructorKeyword, // 'constructor' ConstructorKeyword, // 'constructor'
MemberKeyword, // 'member' MemberKeyword, // 'member'
EventKeyword, // 'event' EventKeyword, // 'event'
CallKeyword, // 'call' CallKeyword, // 'call'
NewKeyword, // 'new' NewKeyword, // 'new'
IndexKeyword, // 'index' IndexKeyword, // 'index'
ComplexKeyword // 'complex' ComplexKeyword // 'complex'
} }
function tokenToString(token: Token): string { function tokenToString(token: Token): string {
switch (token) { switch (token) {
case Token.OpenBraceToken: return '{'; case Token.OpenBraceToken:
case Token.CloseBraceToken: return '}'; return '{';
case Token.OpenParenToken: return '('; case Token.CloseBraceToken:
case Token.CloseParenToken: return ')'; return '}';
case Token.OpenBracketToken: return '['; case Token.OpenParenToken:
case Token.CloseBracketToken: return ']'; return '(';
case Token.ExclamationToken: return '!'; case Token.CloseParenToken:
case Token.DotToken: return '.'; return ')';
case Token.HashToken: return '#'; case Token.OpenBracketToken:
case Token.TildeToken: return '~'; return '[';
case Token.ColonToken: return ':'; case Token.CloseBracketToken:
case Token.CommaToken: return ','; return ']';
case Token.AtToken: return '@'; case Token.ExclamationToken:
case Token.ClassKeyword: return 'class'; return '!';
case Token.InterfaceKeyword: return 'interface'; case Token.DotToken:
case Token.TypeKeyword: return 'type'; return '.';
case Token.EnumKeyword: return 'enum'; case Token.HashToken:
case Token.NamespaceKeyword: return 'namespace'; return '#';
case Token.FunctionKeyword: return 'function'; case Token.TildeToken:
case Token.VarKeyword: return 'var'; return '~';
case Token.ConstructorKeyword: return 'constructor'; case Token.ColonToken:
case Token.MemberKeyword: return 'member'; return ':';
case Token.EventKeyword: return 'event'; case Token.CommaToken:
case Token.CallKeyword: return 'call'; return ',';
case Token.NewKeyword: return 'new'; case Token.AtToken:
case Token.IndexKeyword: return 'index'; return '@';
case Token.ComplexKeyword: return 'complex'; case Token.ClassKeyword:
case Token.None: return '<none>'; return 'class';
case Token.EofToken: return '<eof>'; case Token.InterfaceKeyword:
case Token.DecimalDigits: return '<decimal digits>'; return 'interface';
case Token.String: return '<string>'; case Token.TypeKeyword:
case Token.Text: return '<text>'; return 'type';
case Token.ModuleSource: return '<module source>'; case Token.EnumKeyword:
return 'enum';
case Token.NamespaceKeyword:
return 'namespace';
case Token.FunctionKeyword:
return 'function';
case Token.VarKeyword:
return 'var';
case Token.ConstructorKeyword:
return 'constructor';
case Token.MemberKeyword:
return 'member';
case Token.EventKeyword:
return 'event';
case Token.CallKeyword:
return 'call';
case Token.NewKeyword:
return 'new';
case Token.IndexKeyword:
return 'index';
case Token.ComplexKeyword:
return 'complex';
case Token.None:
return '<none>';
case Token.EofToken:
return '<eof>';
case Token.DecimalDigits:
return '<decimal digits>';
case Token.String:
return '<string>';
case Token.Text:
return '<text>';
case Token.ModuleSource:
return '<module source>';
} }
} }
@ -760,7 +810,9 @@ class Scanner {
const stringIsUnterminated: boolean = this._stringIsUnterminated; const stringIsUnterminated: boolean = this._stringIsUnterminated;
let accepted: boolean = false; let accepted: boolean = false;
try { try {
const accept: () => void = () => { accepted = true; }; const accept: () => void = () => {
accepted = true;
};
return cb(accept); return cb(accept);
} finally { } finally {
if (!accepted) { if (!accepted) {
@ -780,29 +832,42 @@ class Scanner {
while (!this.eof) { while (!this.eof) {
const ch: string = this._text.charAt(this._pos++); const ch: string = this._text.charAt(this._pos++);
switch (ch) { switch (ch) {
case '{': return this._token = Token.OpenBraceToken; case '{':
case '}': return this._token = Token.CloseBraceToken; return (this._token = Token.OpenBraceToken);
case '(': return this._token = Token.OpenParenToken; case '}':
case ')': return this._token = Token.CloseParenToken; return (this._token = Token.CloseBraceToken);
case '[': return this._token = Token.OpenBracketToken; case '(':
case ']': return this._token = Token.CloseBracketToken; return (this._token = Token.OpenParenToken);
case '!': return this._token = Token.ExclamationToken; case ')':
case '.': return this._token = Token.DotToken; return (this._token = Token.CloseParenToken);
case '#': return this._token = Token.HashToken; case '[':
case '~': return this._token = Token.TildeToken; return (this._token = Token.OpenBracketToken);
case ':': return this._token = Token.ColonToken; case ']':
case ',': return this._token = Token.CommaToken; return (this._token = Token.CloseBracketToken);
case '@': return this._token = Token.AtToken; case '!':
return (this._token = Token.ExclamationToken);
case '.':
return (this._token = Token.DotToken);
case '#':
return (this._token = Token.HashToken);
case '~':
return (this._token = Token.TildeToken);
case ':':
return (this._token = Token.ColonToken);
case ',':
return (this._token = Token.CommaToken);
case '@':
return (this._token = Token.AtToken);
case '"': case '"':
this.scanString(); this.scanString();
return this._token = Token.String; return (this._token = Token.String);
default: default:
this.scanText(); this.scanText();
return this._token = Token.Text; return (this._token = Token.Text);
} }
} }
} }
return this._token = Token.EofToken; return (this._token = Token.EofToken);
} }
public rescanModuleSource(): Token { public rescanModuleSource(): Token {
@ -812,7 +877,7 @@ class Scanner {
case Token.EofToken: case Token.EofToken:
return this._token; return this._token;
} }
return this.speculate(accept => { return this.speculate((accept) => {
if (!this.eof) { if (!this.eof) {
this._pos = this._tokenPos; this._pos = this._tokenPos;
this._stringIsUnterminated = false; this._stringIsUnterminated = false;
@ -824,7 +889,7 @@ class Scanner {
return this._token; return this._token;
} }
accept(); accept();
return this._token = Token.ModuleSource; return (this._token = Token.ModuleSource);
} }
this._pos++; this._pos++;
if (ch === '"') { if (ch === '"') {
@ -854,20 +919,34 @@ class Scanner {
if (this._token === Token.Text) { if (this._token === Token.Text) {
const tokenText: string = this.tokenText; const tokenText: string = this.tokenText;
switch (tokenText) { switch (tokenText) {
case 'class': return this._token = Token.ClassKeyword; case 'class':
case 'interface': return this._token = Token.InterfaceKeyword; return (this._token = Token.ClassKeyword);
case 'type': return this._token = Token.TypeKeyword; case 'interface':
case 'enum': return this._token = Token.EnumKeyword; return (this._token = Token.InterfaceKeyword);
case 'namespace': return this._token = Token.NamespaceKeyword; case 'type':
case 'function': return this._token = Token.FunctionKeyword; return (this._token = Token.TypeKeyword);
case 'var': return this._token = Token.VarKeyword; case 'enum':
case 'constructor': return this._token = Token.ConstructorKeyword; return (this._token = Token.EnumKeyword);
case 'member': return this._token = Token.MemberKeyword; case 'namespace':
case 'event': return this._token = Token.EventKeyword; return (this._token = Token.NamespaceKeyword);
case 'call': return this._token = Token.CallKeyword; case 'function':
case 'new': return this._token = Token.NewKeyword; return (this._token = Token.FunctionKeyword);
case 'index': return this._token = Token.IndexKeyword; case 'var':
case 'complex': return this._token = Token.ComplexKeyword; return (this._token = Token.VarKeyword);
case 'constructor':
return (this._token = Token.ConstructorKeyword);
case 'member':
return (this._token = Token.MemberKeyword);
case 'event':
return (this._token = Token.EventKeyword);
case 'call':
return (this._token = Token.CallKeyword);
case 'new':
return (this._token = Token.NewKeyword);
case 'index':
return (this._token = Token.IndexKeyword);
case 'complex':
return (this._token = Token.ComplexKeyword);
} }
} }
return this._token; return this._token;
@ -877,7 +956,7 @@ class Scanner {
if (this._token === Token.Text) { if (this._token === Token.Text) {
const tokenText: string = this.tokenText; const tokenText: string = this.tokenText;
if (/^\d+$/.test(tokenText)) { if (/^\d+$/.test(tokenText)) {
return this._token = Token.DecimalDigits; return (this._token = Token.DecimalDigits);
} }
} }
return this._token; return this._token;
@ -887,7 +966,8 @@ class Scanner {
while (!this.eof) { while (!this.eof) {
const ch: string = this._text.charAt(this._pos++); const ch: string = this._text.charAt(this._pos++);
switch (ch) { switch (ch) {
case '"': return; case '"':
return;
case '\\': case '\\':
this.scanEscapeSequence(); this.scanEscapeSequence();
break; break;
@ -916,39 +996,42 @@ class Scanner {
} }
// EscapeSequence:: `0` [lookahead != DecimalDigit] // EscapeSequence:: `0` [lookahead != DecimalDigit]
if (ch === '0' if (
&& (this._pos + 1 === this._text.length ch === '0' &&
|| !isDecimalDigit(this._text.charAt(this._pos + 1)))) { (this._pos + 1 === this._text.length || !isDecimalDigit(this._text.charAt(this._pos + 1)))
) {
this._pos++; this._pos++;
return; return;
} }
// EscapeSequence:: HexEscapeSequence // EscapeSequence:: HexEscapeSequence
if (ch === 'x' if (
&& this._pos + 3 <= this._text.length ch === 'x' &&
&& isHexDigit(this._text.charAt(this._pos + 1)) this._pos + 3 <= this._text.length &&
&& isHexDigit(this._text.charAt(this._pos + 2))) { isHexDigit(this._text.charAt(this._pos + 1)) &&
isHexDigit(this._text.charAt(this._pos + 2))
) {
this._pos += 3; this._pos += 3;
return; return;
} }
// EscapeSequence:: UnicodeEscapeSequence // EscapeSequence:: UnicodeEscapeSequence
// UnicodeEscapeSequence:: `u` Hex4Digits // UnicodeEscapeSequence:: `u` Hex4Digits
if (ch === 'u' if (
&& this._pos + 5 <= this._text.length ch === 'u' &&
&& isHexDigit(this._text.charAt(this._pos + 1)) this._pos + 5 <= this._text.length &&
&& isHexDigit(this._text.charAt(this._pos + 2)) isHexDigit(this._text.charAt(this._pos + 1)) &&
&& isHexDigit(this._text.charAt(this._pos + 3)) isHexDigit(this._text.charAt(this._pos + 2)) &&
&& isHexDigit(this._text.charAt(this._pos + 4))) { isHexDigit(this._text.charAt(this._pos + 3)) &&
isHexDigit(this._text.charAt(this._pos + 4))
) {
this._pos += 5; this._pos += 5;
return; return;
} }
// EscapeSequence:: UnicodeEscapeSequence // EscapeSequence:: UnicodeEscapeSequence
// UnicodeEscapeSequence:: `u` `{` CodePoint `}` // UnicodeEscapeSequence:: `u` `{` CodePoint `}`
if (ch === 'u' if (ch === 'u' && this._pos + 4 <= this._text.length && this._text.charAt(this._pos + 1) === '{') {
&& this._pos + 4 <= this._text.length
&& this._text.charAt(this._pos + 1) === '{') {
let hexDigits: string = this._text.charAt(this._pos + 2); let hexDigits: string = this._text.charAt(this._pos + 2);
if (isHexDigit(hexDigits)) { if (isHexDigit(hexDigits)) {
for (let i: number = this._pos + 3; i < this._text.length; i++) { for (let i: number = this._pos + 3; i < this._text.length; i++) {
@ -1064,7 +1147,10 @@ class Parser {
private parseRootComponent(): ComponentPath { private parseRootComponent(): ComponentPath {
if (!this.isStartOfComponent()) { if (!this.isStartOfComponent()) {
return this.fail('Component expected', new ComponentRoot(new ComponentString('', /*userEscaped*/ true))); return this.fail(
'Component expected',
new ComponentRoot(new ComponentString('', /*userEscaped*/ true))
);
} }
const component: Component = this.parseComponent(); const component: Component = this.parseComponent();
@ -1072,7 +1158,7 @@ class Parser {
} }
private parseComponentRest(component: ComponentPath): ComponentPath { private parseComponentRest(component: ComponentPath): ComponentPath {
for (; ;) { for (;;) {
switch (this.token()) { switch (this.token()) {
case Token.DotToken: case Token.DotToken:
case Token.HashToken: case Token.HashToken:
@ -1089,30 +1175,49 @@ class Parser {
private parseNavigation(): Navigation { private parseNavigation(): Navigation {
switch (this._scanner.token()) { switch (this._scanner.token()) {
case Token.DotToken: return this._scanner.scan(), Navigation.Exports; case Token.DotToken:
case Token.HashToken: return this._scanner.scan(), Navigation.Members; return this._scanner.scan(), Navigation.Exports;
case Token.TildeToken: return this._scanner.scan(), Navigation.Locals; case Token.HashToken:
default: return this.fail('Expected \'.\', \'#\', or \'~\'', Navigation.Exports); return this._scanner.scan(), Navigation.Members;
case Token.TildeToken:
return this._scanner.scan(), Navigation.Locals;
default:
return this.fail("Expected '.', '#', or '~'", Navigation.Exports);
} }
} }
private tryParseMeaning(): Meaning | undefined { private tryParseMeaning(): Meaning | undefined {
switch (this._scanner.rescanMeaning()) { switch (this._scanner.rescanMeaning()) {
case Token.ClassKeyword: return this._scanner.scan(), Meaning.Class; case Token.ClassKeyword:
case Token.InterfaceKeyword: return this._scanner.scan(), Meaning.Interface; return this._scanner.scan(), Meaning.Class;
case Token.TypeKeyword: return this._scanner.scan(), Meaning.TypeAlias; case Token.InterfaceKeyword:
case Token.EnumKeyword: return this._scanner.scan(), Meaning.Enum; return this._scanner.scan(), Meaning.Interface;
case Token.NamespaceKeyword: return this._scanner.scan(), Meaning.Namespace; case Token.TypeKeyword:
case Token.FunctionKeyword: return this._scanner.scan(), Meaning.Function; return this._scanner.scan(), Meaning.TypeAlias;
case Token.VarKeyword: return this._scanner.scan(), Meaning.Variable; case Token.EnumKeyword:
case Token.ConstructorKeyword: return this._scanner.scan(), Meaning.Constructor; return this._scanner.scan(), Meaning.Enum;
case Token.MemberKeyword: return this._scanner.scan(), Meaning.Member; case Token.NamespaceKeyword:
case Token.EventKeyword: return this._scanner.scan(), Meaning.Event; return this._scanner.scan(), Meaning.Namespace;
case Token.CallKeyword: return this._scanner.scan(), Meaning.CallSignature; case Token.FunctionKeyword:
case Token.NewKeyword: return this._scanner.scan(), Meaning.ConstructSignature; return this._scanner.scan(), Meaning.Function;
case Token.IndexKeyword: return this._scanner.scan(), Meaning.IndexSignature; case Token.VarKeyword:
case Token.ComplexKeyword: return this._scanner.scan(), Meaning.ComplexType; return this._scanner.scan(), Meaning.Variable;
default: return undefined; case Token.ConstructorKeyword:
return this._scanner.scan(), Meaning.Constructor;
case Token.MemberKeyword:
return this._scanner.scan(), Meaning.Member;
case Token.EventKeyword:
return this._scanner.scan(), Meaning.Event;
case Token.CallKeyword:
return this._scanner.scan(), Meaning.CallSignature;
case Token.NewKeyword:
return this._scanner.scan(), Meaning.ConstructSignature;
case Token.IndexKeyword:
return this._scanner.scan(), Meaning.IndexSignature;
case Token.ComplexKeyword:
return this._scanner.scan(), Meaning.ComplexType;
default:
return undefined;
} }
} }
@ -1151,7 +1256,7 @@ class Parser {
private parseComponentCharacters(): string { private parseComponentCharacters(): string {
let text: string = ''; let text: string = '';
for (; ;) { for (;;) {
switch (this._scanner.token()) { switch (this._scanner.token()) {
case Token.Text: case Token.Text:
text += this.parseText(); text += this.parseText();
@ -1224,21 +1329,24 @@ class Parser {
function formatNavigation(navigation: Navigation | undefined): string { function formatNavigation(navigation: Navigation | undefined): string {
switch (navigation) { switch (navigation) {
case Navigation.Exports: return '.'; case Navigation.Exports:
case Navigation.Members: return '#'; return '.';
case Navigation.Locals: return '~'; case Navigation.Members:
default: return ''; return '#';
case Navigation.Locals:
return '~';
default:
return '';
} }
} }
function isCharacterEscapeSequence(ch: string): boolean { function isCharacterEscapeSequence(ch: string): boolean {
return isSingleEscapeCharacter(ch) return isSingleEscapeCharacter(ch) || isNonEscapeCharacter(ch);
|| isNonEscapeCharacter(ch);
} }
function isSingleEscapeCharacter(ch: string): boolean { function isSingleEscapeCharacter(ch: string): boolean {
switch (ch) { switch (ch) {
case '\'': case "'":
case '"': case '"':
case '\\': case '\\':
case 'b': case 'b':
@ -1254,8 +1362,7 @@ function isSingleEscapeCharacter(ch: string): boolean {
} }
function isNonEscapeCharacter(ch: string): boolean { function isNonEscapeCharacter(ch: string): boolean {
return !isEscapeCharacter(ch) return !isEscapeCharacter(ch) && !isLineTerminator(ch);
&& !isLineTerminator(ch);
} }
function isEscapeCharacter(ch: string): boolean { function isEscapeCharacter(ch: string): boolean {
@ -1264,8 +1371,7 @@ function isEscapeCharacter(ch: string): boolean {
case 'u': case 'u':
return true; return true;
default: default:
return isSingleEscapeCharacter(ch) return isSingleEscapeCharacter(ch) || isDecimalDigit(ch);
|| isDecimalDigit(ch);
} }
} }
@ -1357,4 +1463,4 @@ function escapeModuleSourceIfNeeded(text: string, userEscaped?: boolean): string
return text; return text;
} }
return DeclarationReference.escapeModuleSourceString(text); return DeclarationReference.escapeModuleSourceString(text);
} }

Просмотреть файл

@ -33,12 +33,12 @@ describe('parser', () => {
expect(ref.symbol!.componentPath!.component.toString()).toBe('[abc.[def]]'); expect(ref.symbol!.componentPath!.component.toString()).toBe('[abc.[def]]');
}); });
it.each` it.each`
text | path | navigation | symbol text | path | navigation | symbol
${'abc!'} | ${'abc'} | ${undefined} | ${undefined} ${'abc!'} | ${'abc'} | ${undefined} | ${undefined}
${'"abc"!'} | ${'"abc"'} | ${undefined} | ${undefined} ${'"abc"!'} | ${'"abc"'} | ${undefined} | ${undefined}
${'@microsoft/rush-stack-compiler-3.5!'} | ${'@microsoft/rush-stack-compiler-3.5'} | ${undefined} | ${undefined} ${'@microsoft/rush-stack-compiler-3.5!'} | ${'@microsoft/rush-stack-compiler-3.5'} | ${undefined} | ${undefined}
${'abc!def'} | ${'abc'} | ${'.'} | ${'def'} ${'abc!def'} | ${'abc'} | ${'.'} | ${'def'}
${'abc!~def'} | ${'abc'} | ${'~'} | ${'def'} ${'abc!~def'} | ${'abc'} | ${'~'} | ${'def'}
`('parse module source $text', ({ text, path, navigation, symbol }) => { `('parse module source $text', ({ text, path, navigation, symbol }) => {
const ref: DeclarationReference = DeclarationReference.parse(text); const ref: DeclarationReference = DeclarationReference.parse(text);
expect(ref.source).toBeInstanceOf(ModuleSource); expect(ref.source).toBeInstanceOf(ModuleSource);
@ -63,21 +63,21 @@ describe('parser', () => {
expect(ref.symbol!.componentPath!.component.toString()).toBe(symbol); expect(ref.symbol!.componentPath!.component.toString()).toBe(symbol);
}); });
it.each` it.each`
text | meaning text | meaning
${'a:class'} | ${Meaning.Class} ${'a:class'} | ${Meaning.Class}
${'a:interface'} | ${Meaning.Interface} ${'a:interface'} | ${Meaning.Interface}
${'a:type'} | ${Meaning.TypeAlias} ${'a:type'} | ${Meaning.TypeAlias}
${'a:enum'} | ${Meaning.Enum} ${'a:enum'} | ${Meaning.Enum}
${'a:namespace'} | ${Meaning.Namespace} ${'a:namespace'} | ${Meaning.Namespace}
${'a:function'} | ${Meaning.Function} ${'a:function'} | ${Meaning.Function}
${'a:var'} | ${Meaning.Variable} ${'a:var'} | ${Meaning.Variable}
${'a:constructor'} | ${Meaning.Constructor} ${'a:constructor'} | ${Meaning.Constructor}
${'a:member'} | ${Meaning.Member} ${'a:member'} | ${Meaning.Member}
${'a:event'} | ${Meaning.Event} ${'a:event'} | ${Meaning.Event}
${'a:call'} | ${Meaning.CallSignature} ${'a:call'} | ${Meaning.CallSignature}
${'a:new'} | ${Meaning.ConstructSignature} ${'a:new'} | ${Meaning.ConstructSignature}
${'a:index'} | ${Meaning.IndexSignature} ${'a:index'} | ${Meaning.IndexSignature}
${'a:complex'} | ${Meaning.ComplexType} ${'a:complex'} | ${Meaning.ComplexType}
`('parse meaning $meaning', ({ text, meaning }) => { `('parse meaning $meaning', ({ text, meaning }) => {
const ref: DeclarationReference = DeclarationReference.parse(text); const ref: DeclarationReference = DeclarationReference.parse(text);
expect(ref.symbol!.meaning).toBe(meaning); expect(ref.symbol!.meaning).toBe(meaning);
@ -119,8 +119,10 @@ describe('parser', () => {
}); });
}); });
it('add navigation step', () => { it('add navigation step', () => {
const ref: DeclarationReference = DeclarationReference.empty() const ref: DeclarationReference = DeclarationReference.empty().addNavigationStep(
.addNavigationStep(Navigation.Members, ComponentReference.parse('[Symbol.iterator]')); Navigation.Members,
ComponentReference.parse('[Symbol.iterator]')
);
const symbol: SymbolReference = ref.symbol!; const symbol: SymbolReference = ref.symbol!;
expect(symbol).toBeInstanceOf(SymbolReference); expect(symbol).toBeInstanceOf(SymbolReference);
expect(symbol.componentPath).toBeDefined(); expect(symbol.componentPath).toBeDefined();
@ -128,77 +130,77 @@ it('add navigation step', () => {
}); });
describe('DeclarationReference', () => { describe('DeclarationReference', () => {
it.each` it.each`
text | expected text | expected
${''} | ${true} ${''} | ${true}
${'a'} | ${true} ${'a'} | ${true}
${'a.b'} | ${false} ${'a.b'} | ${false}
${'a~b'} | ${false} ${'a~b'} | ${false}
${'a#b'} | ${false} ${'a#b'} | ${false}
${'a:class'} | ${false} ${'a:class'} | ${false}
${'a!'} | ${false} ${'a!'} | ${false}
${'@a'} | ${false} ${'@a'} | ${false}
${'a@'} | ${false} ${'a@'} | ${false}
${'['} | ${false} ${'['} | ${false}
${']'} | ${false} ${']'} | ${false}
${'{'} | ${false} ${'{'} | ${false}
${'}'} | ${false} ${'}'} | ${false}
${'('} | ${false} ${'('} | ${false}
${')'} | ${false} ${')'} | ${false}
${'[a]'} | ${false} ${'[a]'} | ${false}
${'[a.b]'} | ${false} ${'[a.b]'} | ${false}
${'[a!b]'} | ${false} ${'[a!b]'} | ${false}
${'""'} | ${true} ${'""'} | ${true}
${'"a"'} | ${true} ${'"a"'} | ${true}
${'"a.b"'} | ${true} ${'"a.b"'} | ${true}
${'"a~b"'} | ${true} ${'"a~b"'} | ${true}
${'"a#b"'} | ${true} ${'"a#b"'} | ${true}
${'"a:class"'} | ${true} ${'"a:class"'} | ${true}
${'"a!"'} | ${true} ${'"a!"'} | ${true}
${'"@a"'} | ${true} ${'"@a"'} | ${true}
${'"a@"'} | ${true} ${'"a@"'} | ${true}
${'"["'} | ${true} ${'"["'} | ${true}
${'"]"'} | ${true} ${'"]"'} | ${true}
${'"{"'} | ${true} ${'"{"'} | ${true}
${'"}"'} | ${true} ${'"}"'} | ${true}
${'"("'} | ${true} ${'"("'} | ${true}
${'")"'} | ${true} ${'")"'} | ${true}
`('isWellFormedComponentString($text)', ({ text, expected }) => { `('isWellFormedComponentString($text)', ({ text, expected }) => {
expect(DeclarationReference.isWellFormedComponentString(text)).toBe(expected); expect(DeclarationReference.isWellFormedComponentString(text)).toBe(expected);
}); });
it.each` it.each`
text | expected text | expected
${''} | ${'""'} ${''} | ${'""'}
${'a'} | ${'a'} ${'a'} | ${'a'}
${'a.b'} | ${'"a.b"'} ${'a.b'} | ${'"a.b"'}
${'a~b'} | ${'"a~b"'} ${'a~b'} | ${'"a~b"'}
${'a#b'} | ${'"a#b"'} ${'a#b'} | ${'"a#b"'}
${'a:class'} | ${'"a:class"'} ${'a:class'} | ${'"a:class"'}
${'a!'} | ${'"a!"'} ${'a!'} | ${'"a!"'}
${'@a'} | ${'"@a"'} ${'@a'} | ${'"@a"'}
${'a@'} | ${'"a@"'} ${'a@'} | ${'"a@"'}
${'['} | ${'"["'} ${'['} | ${'"["'}
${']'} | ${'"]"'} ${']'} | ${'"]"'}
${'{'} | ${'"{"'} ${'{'} | ${'"{"'}
${'}'} | ${'"}"'} ${'}'} | ${'"}"'}
${'('} | ${'"("'} ${'('} | ${'"("'}
${')'} | ${'")"'} ${')'} | ${'")"'}
${'[a]'} | ${'"[a]"'} ${'[a]'} | ${'"[a]"'}
${'[a.b]'} | ${'"[a.b]"'} ${'[a.b]'} | ${'"[a.b]"'}
${'[a!b]'} | ${'"[a!b]"'} ${'[a!b]'} | ${'"[a!b]"'}
${'""'} | ${'"\\\"\\\""'} ${'""'} | ${'"\\"\\""'}
${'"a"'} | ${'"\\\"a\\\""'} ${'"a"'} | ${'"\\"a\\""'}
`('escapeComponentString($text)', ({ text, expected }) => { `('escapeComponentString($text)', ({ text, expected }) => {
expect(DeclarationReference.escapeComponentString(text)).toBe(expected); expect(DeclarationReference.escapeComponentString(text)).toBe(expected);
}); });
it.each` it.each`
text | expected text | expected
${''} | ${''} ${''} | ${''}
${'""'} | ${''} ${'""'} | ${''}
${'a'} | ${'a'} ${'a'} | ${'a'}
${'"a"'} | ${'a'} ${'"a"'} | ${'a'}
${'"a.b"'} | ${'a.b'} ${'"a.b"'} | ${'a.b'}
${'"\\"\\""'} | ${'""'} ${'"\\"\\""'} | ${'""'}
${'"\\"a\\""'} | ${'"a"'} ${'"\\"a\\""'} | ${'"a"'}
`('unescapeComponentString($text)', ({ text, expected }) => { `('unescapeComponentString($text)', ({ text, expected }) => {
if (expected === undefined) { if (expected === undefined) {
expect(() => DeclarationReference.unescapeComponentString(text)).toThrow(); expect(() => DeclarationReference.unescapeComponentString(text)).toThrow();
@ -207,79 +209,79 @@ describe('DeclarationReference', () => {
} }
}); });
it.each` it.each`
text | expected text | expected
${''} | ${false} ${''} | ${false}
${'a'} | ${true} ${'a'} | ${true}
${'a.b'} | ${true} ${'a.b'} | ${true}
${'a~b'} | ${true} ${'a~b'} | ${true}
${'a#b'} | ${true} ${'a#b'} | ${true}
${'a:class'} | ${true} ${'a:class'} | ${true}
${'a!'} | ${false} ${'a!'} | ${false}
${'@a'} | ${true} ${'@a'} | ${true}
${'a@'} | ${true} ${'a@'} | ${true}
${'['} | ${true} ${'['} | ${true}
${']'} | ${true} ${']'} | ${true}
${'{'} | ${true} ${'{'} | ${true}
${'}'} | ${true} ${'}'} | ${true}
${'('} | ${true} ${'('} | ${true}
${')'} | ${true} ${')'} | ${true}
${'[a]'} | ${true} ${'[a]'} | ${true}
${'[a.b]'} | ${true} ${'[a.b]'} | ${true}
${'[a!b]'} | ${false} ${'[a!b]'} | ${false}
${'""'} | ${true} ${'""'} | ${true}
${'"a"'} | ${true} ${'"a"'} | ${true}
${'"a.b"'} | ${true} ${'"a.b"'} | ${true}
${'"a~b"'} | ${true} ${'"a~b"'} | ${true}
${'"a#b"'} | ${true} ${'"a#b"'} | ${true}
${'"a:class"'} | ${true} ${'"a:class"'} | ${true}
${'"a!"'} | ${true} ${'"a!"'} | ${true}
${'"@a"'} | ${true} ${'"@a"'} | ${true}
${'"a@"'} | ${true} ${'"a@"'} | ${true}
${'"["'} | ${true} ${'"["'} | ${true}
${'"]"'} | ${true} ${'"]"'} | ${true}
${'"{"'} | ${true} ${'"{"'} | ${true}
${'"}"'} | ${true} ${'"}"'} | ${true}
${'"("'} | ${true} ${'"("'} | ${true}
${'")"'} | ${true} ${'")"'} | ${true}
${'"[a!b]"'} | ${true} ${'"[a!b]"'} | ${true}
`('isWellFormedModuleSourceString($text)', ({ text, expected }) => { `('isWellFormedModuleSourceString($text)', ({ text, expected }) => {
expect(DeclarationReference.isWellFormedModuleSourceString(text)).toBe(expected); expect(DeclarationReference.isWellFormedModuleSourceString(text)).toBe(expected);
}); });
it.each` it.each`
text | expected text | expected
${''} | ${'""'} ${''} | ${'""'}
${'a'} | ${'a'} ${'a'} | ${'a'}
${'a.b'} | ${'a.b'} ${'a.b'} | ${'a.b'}
${'a~b'} | ${'a~b'} ${'a~b'} | ${'a~b'}
${'a#b'} | ${'a#b'} ${'a#b'} | ${'a#b'}
${'a:class'} | ${'a:class'} ${'a:class'} | ${'a:class'}
${'a!'} | ${'"a!"'} ${'a!'} | ${'"a!"'}
${'@a'} | ${'@a'} ${'@a'} | ${'@a'}
${'a@'} | ${'a@'} ${'a@'} | ${'a@'}
${'['} | ${'['} ${'['} | ${'['}
${']'} | ${']'} ${']'} | ${']'}
${'{'} | ${'{'} ${'{'} | ${'{'}
${'}'} | ${'}'} ${'}'} | ${'}'}
${'('} | ${'('} ${'('} | ${'('}
${')'} | ${')'} ${')'} | ${')'}
${'[a]'} | ${'[a]'} ${'[a]'} | ${'[a]'}
${'[a.b]'} | ${'[a.b]'} ${'[a.b]'} | ${'[a.b]'}
${'[a!b]'} | ${'"[a!b]"'} ${'[a!b]'} | ${'"[a!b]"'}
${'""'} | ${'"\\\"\\\""'} ${'""'} | ${'"\\"\\""'}
${'"a"'} | ${'"\\\"a\\\""'} ${'"a"'} | ${'"\\"a\\""'}
`('escapeModuleSourceString($text)', ({ text, expected }) => { `('escapeModuleSourceString($text)', ({ text, expected }) => {
expect(DeclarationReference.escapeModuleSourceString(text)).toBe(expected); expect(DeclarationReference.escapeModuleSourceString(text)).toBe(expected);
}); });
it.each` it.each`
text | expected text | expected
${''} | ${undefined} ${''} | ${undefined}
${'""'} | ${''} ${'""'} | ${''}
${'a'} | ${'a'} ${'a'} | ${'a'}
${'"a"'} | ${'a'} ${'"a"'} | ${'a'}
${'"a!"'} | ${'a!'} ${'"a!"'} | ${'a!'}
${'"a.b"'} | ${'a.b'} ${'"a.b"'} | ${'a.b'}
${'"\\"\\""'} | ${'""'} ${'"\\"\\""'} | ${'""'}
${'"\\"a\\""'} | ${'"a"'} ${'"\\"a\\""'} | ${'"a"'}
`('unescapeModuleSourceString($text)', ({ text, expected }) => { `('unescapeModuleSourceString($text)', ({ text, expected }) => {
if (expected === undefined) { if (expected === undefined) {
expect(() => DeclarationReference.unescapeModuleSourceString(text)).toThrow(); expect(() => DeclarationReference.unescapeModuleSourceString(text)).toThrow();
@ -303,26 +305,28 @@ describe('ModuleSource', () => {
expect(source.importPath).toBe(importPath); expect(source.importPath).toBe(importPath);
}); });
it.each` it.each`
packageName | importPath | text packageName | importPath | text
${'a'} | ${undefined} | ${'a'} ${'a'} | ${undefined} | ${'a'}
${'a'} | ${'b'} | ${'a/b'} ${'a'} | ${'b'} | ${'a/b'}
${'@a/b'} | ${undefined} | ${'@a/b'} ${'@a/b'} | ${undefined} | ${'@a/b'}
${'@a/b'} | ${'c'} | ${'@a/b/c'} ${'@a/b'} | ${'c'} | ${'@a/b/c'}
`('fromPackage($packageName, $importPath)', ({ packageName, importPath, text }) => { `('fromPackage($packageName, $importPath)', ({ packageName, importPath, text }) => {
const source: ModuleSource = ModuleSource.fromPackage(packageName, importPath); const source: ModuleSource = ModuleSource.fromPackage(packageName, importPath);
expect(source.path).toBe(text); expect(source.path).toBe(text);
}); });
it.each` it.each`
scopeName | unscopedPackageName | importPath | text scopeName | unscopedPackageName | importPath | text
${''} | ${'a'} | ${undefined} | ${'a'} ${''} | ${'a'} | ${undefined} | ${'a'}
${''} | ${'a'} | ${'b'} | ${'a/b'} ${''} | ${'a'} | ${'b'} | ${'a/b'}
${'a'} | ${'b'} | ${undefined} | ${'@a/b'} ${'a'} | ${'b'} | ${undefined} | ${'@a/b'}
${'@a'} | ${'b'} | ${undefined} | ${'@a/b'} ${'@a'} | ${'b'} | ${undefined} | ${'@a/b'}
${'a'} | ${'b'} | ${'c'} | ${'@a/b/c'} ${'a'} | ${'b'} | ${'c'} | ${'@a/b/c'}
${'@a'} | ${'b'} | ${'c'} | ${'@a/b/c'} ${'@a'} | ${'b'} | ${'c'} | ${'@a/b/c'}
`('fromScopedPackage($scopeName, $unscopedPackageName, $importPath)', `(
'fromScopedPackage($scopeName, $unscopedPackageName, $importPath)',
({ scopeName, unscopedPackageName, importPath, text }) => { ({ scopeName, unscopedPackageName, importPath, text }) => {
const source: ModuleSource = ModuleSource.fromScopedPackage(scopeName, unscopedPackageName, importPath); const source: ModuleSource = ModuleSource.fromScopedPackage(scopeName, unscopedPackageName, importPath);
expect(source.path).toBe(text); expect(source.path).toBe(text);
}); }
}); );
});

Просмотреть файл

@ -30,10 +30,14 @@ export class DocNodeManager {
// Example: "_myIdentifier99" // Example: "_myIdentifier99"
private static readonly _nodeKindRegExp: RegExp = /^[_a-z][_a-z0-9]*$/i; private static readonly _nodeKindRegExp: RegExp = /^[_a-z][_a-z0-9]*$/i;
private readonly _docNodeDefinitionsByKind: Map<string, IRegisteredDocNodeDefinition> private readonly _docNodeDefinitionsByKind: Map<string, IRegisteredDocNodeDefinition> = new Map<
= new Map<string, IRegisteredDocNodeDefinition>(); string,
private readonly _docNodeDefinitionsByConstructor: Map<DocNodeConstructor, IRegisteredDocNodeDefinition> IRegisteredDocNodeDefinition
= new Map<DocNodeConstructor, IRegisteredDocNodeDefinition>(); >();
private readonly _docNodeDefinitionsByConstructor: Map<
DocNodeConstructor,
IRegisteredDocNodeDefinition
> = new Map<DocNodeConstructor, IRegisteredDocNodeDefinition>();
/** /**
* Registers a list of {@link IDocNodeDefinition} objects to be used with the associated * Registers a list of {@link IDocNodeDefinition} objects to be used with the associated
@ -47,22 +51,29 @@ export class DocNodeManager {
for (const definition of definitions) { for (const definition of definitions) {
if (!DocNodeManager._nodeKindRegExp.test(definition.docNodeKind)) { if (!DocNodeManager._nodeKindRegExp.test(definition.docNodeKind)) {
throw new Error(`The DocNode kind ${JSON.stringify(definition.docNodeKind)} is not a valid identifier.` throw new Error(
+ ` It must start with an underscore or letter, and be comprised of letters, numbers, and underscores`); `The DocNode kind ${JSON.stringify(definition.docNodeKind)} is not a valid identifier.` +
` It must start with an underscore or letter, and be comprised of letters, numbers, and underscores`
);
} }
let existingDefinition: IRegisteredDocNodeDefinition | undefined let existingDefinition: IRegisteredDocNodeDefinition | undefined = this._docNodeDefinitionsByKind.get(
= this._docNodeDefinitionsByKind.get(definition.docNodeKind); definition.docNodeKind
);
if (existingDefinition !== undefined) { if (existingDefinition !== undefined) {
throw new Error(`The DocNode kind "${definition.docNodeKind}" was already registered` throw new Error(
+ ` by ${existingDefinition.packageName}`); `The DocNode kind "${definition.docNodeKind}" was already registered` +
` by ${existingDefinition.packageName}`
);
} }
existingDefinition = this._docNodeDefinitionsByConstructor.get(definition.constructor); existingDefinition = this._docNodeDefinitionsByConstructor.get(definition.constructor);
if (existingDefinition !== undefined) { if (existingDefinition !== undefined) {
throw new Error(`This DocNode constructor was already registered by ${existingDefinition.packageName}` throw new Error(
+ ` as ${existingDefinition.docNodeKind}`); `This DocNode constructor was already registered by ${existingDefinition.packageName}` +
` as ${existingDefinition.docNodeKind}`
);
} }
const newDefinition: IRegisteredDocNodeDefinition = { const newDefinition: IRegisteredDocNodeDefinition = {
@ -113,7 +124,9 @@ export class DocNodeManager {
} }
private _getDefinition(docNodeKind: string): IRegisteredDocNodeDefinition { private _getDefinition(docNodeKind: string): IRegisteredDocNodeDefinition {
const definition: IRegisteredDocNodeDefinition | undefined = this._docNodeDefinitionsByKind.get(docNodeKind); const definition: IRegisteredDocNodeDefinition | undefined = this._docNodeDefinitionsByKind.get(
docNodeKind
);
if (definition === undefined) { if (definition === undefined) {
throw new Error(`The DocNode kind "${docNodeKind}" was not registered with this TSDocConfiguration`); throw new Error(`The DocNode kind "${docNodeKind}" was not registered with this TSDocConfiguration`);
} }

Просмотреть файл

@ -48,7 +48,7 @@ export class TSDocConfiguration {
* {@link TSDocValidationConfiguration.reportUnsupportedTags} is enabled. * {@link TSDocValidationConfiguration.reportUnsupportedTags} is enabled.
*/ */
public get supportedTagDefinitions(): ReadonlyArray<TSDocTagDefinition> { public get supportedTagDefinitions(): ReadonlyArray<TSDocTagDefinition> {
return this.tagDefinitions.filter(x => this.isTagSupported(x)); return this.tagDefinitions.filter((x) => this.isTagSupported(x));
} }
/** /**
@ -90,8 +90,9 @@ export class TSDocConfiguration {
* Whereas if a tag is "supported", this means it is defined AND the application implements the tag. * Whereas if a tag is "supported", this means it is defined AND the application implements the tag.
*/ */
public addTagDefinition(tagDefinition: TSDocTagDefinition): void { public addTagDefinition(tagDefinition: TSDocTagDefinition): void {
const existingDefinition: TSDocTagDefinition | undefined const existingDefinition: TSDocTagDefinition | undefined = this._tagDefinitionsByName.get(
= this._tagDefinitionsByName.get(tagDefinition.tagNameWithUpperCase); tagDefinition.tagNameWithUpperCase
);
if (existingDefinition === tagDefinition) { if (existingDefinition === tagDefinition) {
return; return;
@ -112,9 +113,10 @@ export class TSDocConfiguration {
* @param supported - if specified, calls the {@link TSDocConfiguration.setSupportForTag} * @param supported - if specified, calls the {@link TSDocConfiguration.setSupportForTag}
* method to mark the definitions as supported or unsupported * method to mark the definitions as supported or unsupported
*/ */
public addTagDefinitions(tagDefinitions: ReadonlyArray<TSDocTagDefinition>, public addTagDefinitions(
supported?: boolean | undefined): void { tagDefinitions: ReadonlyArray<TSDocTagDefinition>,
supported?: boolean | undefined
): void {
for (const tagDefinition of tagDefinitions) { for (const tagDefinition of tagDefinitions) {
this.addTagDefinition(tagDefinition); this.addTagDefinition(tagDefinition);
@ -189,8 +191,9 @@ export class TSDocConfiguration {
} }
private _requireTagToBeDefined(tagDefinition: TSDocTagDefinition): void { private _requireTagToBeDefined(tagDefinition: TSDocTagDefinition): void {
const matching: TSDocTagDefinition | undefined const matching: TSDocTagDefinition | undefined = this._tagDefinitionsByName.get(
= this._tagDefinitionsByName.get(tagDefinition.tagNameWithUpperCase); tagDefinition.tagNameWithUpperCase
);
if (matching) { if (matching) {
if (matching === tagDefinition) { if (matching === tagDefinition) {
return; return;

Просмотреть файл

@ -77,8 +77,8 @@ export class TSDocTagDefinition {
this.tagName = parameters.tagName; this.tagName = parameters.tagName;
this.tagNameWithUpperCase = parameters.tagName.toUpperCase(); this.tagNameWithUpperCase = parameters.tagName.toUpperCase();
this.syntaxKind = parameters.syntaxKind; this.syntaxKind = parameters.syntaxKind;
this.standardization = (parameters as ITSDocTagDefinitionInternalParameters).standardization this.standardization =
|| Standardization.None; (parameters as ITSDocTagDefinitionInternalParameters).standardization || Standardization.None;
this.allowMultiple = !!parameters.allowMultiple; this.allowMultiple = !!parameters.allowMultiple;
} }
} }

Просмотреть файл

@ -1,10 +1,17 @@
import { DocNode, DocNodeKind, DocPlainText, DocFencedCode, DocCodeSpan, DocLinkTag, DocEscapedText } from '../nodes'; import {
DocNode,
DocNodeKind,
DocPlainText,
DocFencedCode,
DocCodeSpan,
DocLinkTag,
DocEscapedText
} from '../nodes';
/** /**
* Renders a DocNode tree as plain text, without any rich text formatting or markup. * Renders a DocNode tree as plain text, without any rich text formatting or markup.
*/ */
export class PlainTextEmitter { export class PlainTextEmitter {
/** /**
* Returns true if the specified node contains any text content. * Returns true if the specified node contains any text content.
* *
@ -30,14 +37,17 @@ export class PlainTextEmitter {
* The default value is 1. * The default value is 1.
*/ */
public static hasAnyTextContent(nodes: ReadonlyArray<DocNode>, requiredCharacters?: number): boolean; public static hasAnyTextContent(nodes: ReadonlyArray<DocNode>, requiredCharacters?: number): boolean;
public static hasAnyTextContent(nodeOrNodes: DocNode | ReadonlyArray<DocNode>, requiredCharacters?: number): boolean { public static hasAnyTextContent(
nodeOrNodes: DocNode | ReadonlyArray<DocNode>,
requiredCharacters?: number
): boolean {
if (requiredCharacters === undefined || requiredCharacters < 1) { if (requiredCharacters === undefined || requiredCharacters < 1) {
requiredCharacters = 1; // default requiredCharacters = 1; // default
} }
let nodes: ReadonlyArray<DocNode>; let nodes: ReadonlyArray<DocNode>;
if (nodeOrNodes instanceof DocNode) { if (nodeOrNodes instanceof DocNode) {
nodes = [ nodeOrNodes ]; nodes = [nodeOrNodes];
} else { } else {
nodes = nodeOrNodes; nodes = nodeOrNodes;
} }
@ -47,9 +57,11 @@ export class PlainTextEmitter {
return foundCharacters >= requiredCharacters; return foundCharacters >= requiredCharacters;
} }
private static _scanTextContent(nodes: ReadonlyArray<DocNode>, requiredCharacters: number, private static _scanTextContent(
foundCharacters: number): number { nodes: ReadonlyArray<DocNode>,
requiredCharacters: number,
foundCharacters: number
): number {
for (const node of nodes) { for (const node of nodes) {
switch (node.kind) { switch (node.kind) {
case DocNodeKind.FencedCode: case DocNodeKind.FencedCode:
@ -81,7 +93,11 @@ export class PlainTextEmitter {
break; break;
} }
foundCharacters += PlainTextEmitter._scanTextContent(node.getChildNodes(), requiredCharacters, foundCharacters); foundCharacters += PlainTextEmitter._scanTextContent(
node.getChildNodes(),
requiredCharacters,
foundCharacters
);
if (foundCharacters >= requiredCharacters) { if (foundCharacters >= requiredCharacters) {
break; break;
@ -97,10 +113,10 @@ export class PlainTextEmitter {
let i: number = 0; let i: number = 0;
while (i < length) { while (i < length) {
switch (s.charCodeAt(i)) { switch (s.charCodeAt(i)) {
case 32: // space case 32: // space
case 9: // tab case 9: // tab
case 13: // CR case 13: // CR
case 10: // LF case 10: // LF
break; break;
default: default:
++count; ++count;

Просмотреть файл

@ -68,7 +68,10 @@ export class TSDocEmitter {
this._renderCompleteObject(output, htmlTag); this._renderCompleteObject(output, htmlTag);
} }
public renderDeclarationReference(output: IStringBuilder, declarationReference: DocDeclarationReference): void { public renderDeclarationReference(
output: IStringBuilder,
declarationReference: DocDeclarationReference
): void {
this._emitCommentFraming = false; this._emitCommentFraming = false;
this._renderCompleteObject(output, declarationReference); this._renderCompleteObject(output, declarationReference);
} }
@ -143,7 +146,10 @@ export class TSDocEmitter {
const docDeclarationReference: DocDeclarationReference = docNode as DocDeclarationReference; const docDeclarationReference: DocDeclarationReference = docNode as DocDeclarationReference;
this._writeContent(docDeclarationReference.packageName); this._writeContent(docDeclarationReference.packageName);
this._writeContent(docDeclarationReference.importPath); this._writeContent(docDeclarationReference.importPath);
if (docDeclarationReference.packageName !== undefined || docDeclarationReference.importPath !== undefined) { if (
docDeclarationReference.packageName !== undefined ||
docDeclarationReference.importPath !== undefined
) {
this._writeContent('#'); this._writeContent('#');
} }
this._renderNodes(docDeclarationReference.memberReferences); this._renderNodes(docDeclarationReference.memberReferences);
@ -196,8 +202,8 @@ export class TSDocEmitter {
this._writeContent(docHtmlStartTag.name); this._writeContent(docHtmlStartTag.name);
this._writeContent(docHtmlStartTag.spacingAfterName); this._writeContent(docHtmlStartTag.spacingAfterName);
let needsSpace: boolean = docHtmlStartTag.spacingAfterName === undefined let needsSpace: boolean =
|| docHtmlStartTag.spacingAfterName.length === 0; docHtmlStartTag.spacingAfterName === undefined || docHtmlStartTag.spacingAfterName.length === 0;
for (const attribute of docHtmlStartTag.htmlAttributes) { for (const attribute of docHtmlStartTag.htmlAttributes) {
if (needsSpace) { if (needsSpace) {
@ -302,7 +308,9 @@ export class TSDocEmitter {
break; break;
case DocNodeKind.Paragraph: case DocNodeKind.Paragraph:
const trimmedParagraph: DocParagraph = DocNodeTransforms.trimSpacesInParagraph(docNode as DocParagraph); const trimmedParagraph: DocParagraph = DocNodeTransforms.trimSpacesInParagraph(
docNode as DocParagraph
);
if (trimmedParagraph.nodes.length > 0) { if (trimmedParagraph.nodes.length > 0) {
if (this._hangingParagraph) { if (this._hangingParagraph) {
// If it's a hanging paragraph, then don't skip a line // If it's a hanging paragraph, then don't skip a line
@ -340,9 +348,7 @@ export class TSDocEmitter {
} }
} }
private _renderInlineTag(docInlineTagBase: DocInlineTagBase, private _renderInlineTag(docInlineTagBase: DocInlineTagBase, writeInlineTagContent: () => void): void {
writeInlineTagContent: () => void): void {
this._writeContent('{'); this._writeContent('{');
this._writeContent(docInlineTagBase.tagName); this._writeContent(docInlineTagBase.tagName);
writeInlineTagContent(); writeInlineTagContent();
@ -393,8 +399,7 @@ export class TSDocEmitter {
if (this._lineState === LineState.Closed) { if (this._lineState === LineState.Closed) {
if (this._emitCommentFraming) { if (this._emitCommentFraming) {
this._output!.append('/**' + this.eol this._output!.append('/**' + this.eol + ' *');
+ ' *');
} }
this._lineState = LineState.StartOfLine; this._lineState = LineState.StartOfLine;
} }
@ -414,8 +419,7 @@ export class TSDocEmitter {
private _writeNewline(): void { private _writeNewline(): void {
if (this._lineState === LineState.Closed) { if (this._lineState === LineState.Closed) {
if (this._emitCommentFraming) { if (this._emitCommentFraming) {
this._output!.append('/**' + this.eol this._output!.append('/**' + this.eol + ' *');
+ ' *');
} }
this._lineState = LineState.StartOfLine; this._lineState = LineState.StartOfLine;
} }

Просмотреть файл

@ -15,7 +15,7 @@ function createSnapshot(input: string): {} {
const parserContext: ParserContext = tsdocParser.parseString(input); const parserContext: ParserContext = tsdocParser.parseString(input);
const output: string = parserContext.docComment.emitAsTsdoc(); const output: string = parserContext.docComment.emitAsTsdoc();
return { return {
errors: parserContext.log.messages.map(x => x.toString()), errors: parserContext.log.messages.map((x) => x.toString()),
output: '\n' + output output: '\n' + output
}; };
} }

Просмотреть файл

@ -1,9 +1,4 @@
export { DocNodeManager, IDocNodeDefinition, DocNodeConstructor } from './configuration/DocNodeManager';
export {
DocNodeManager,
IDocNodeDefinition,
DocNodeConstructor
} from './configuration/DocNodeManager';
export { TSDocConfiguration } from './configuration/TSDocConfiguration'; export { TSDocConfiguration } from './configuration/TSDocConfiguration';
export { export {
ITSDocTagDefinitionParameters, ITSDocTagDefinitionParameters,

Просмотреть файл

@ -8,31 +8,31 @@ export class BuiltInDocNodes {
const docNodeManager: DocNodeManager = configuration.docNodeManager; const docNodeManager: DocNodeManager = configuration.docNodeManager;
docNodeManager.registerDocNodes('@microsoft/tsdoc', [ docNodeManager.registerDocNodes('@microsoft/tsdoc', [
{ docNodeKind: DocNodeKind.Block, constructor: nodes.DocBlock }, { docNodeKind: DocNodeKind.Block, constructor: nodes.DocBlock },
{ docNodeKind: DocNodeKind.BlockTag, constructor: nodes.DocBlockTag }, { docNodeKind: DocNodeKind.BlockTag, constructor: nodes.DocBlockTag },
{ docNodeKind: DocNodeKind.CodeSpan, constructor: nodes.DocCodeSpan }, { docNodeKind: DocNodeKind.CodeSpan, constructor: nodes.DocCodeSpan },
{ docNodeKind: DocNodeKind.Comment, constructor: nodes.DocComment}, { docNodeKind: DocNodeKind.Comment, constructor: nodes.DocComment },
{ docNodeKind: DocNodeKind.DeclarationReference, constructor: nodes.DocDeclarationReference }, { docNodeKind: DocNodeKind.DeclarationReference, constructor: nodes.DocDeclarationReference },
{ docNodeKind: DocNodeKind.ErrorText, constructor: nodes.DocErrorText }, { docNodeKind: DocNodeKind.ErrorText, constructor: nodes.DocErrorText },
{ docNodeKind: DocNodeKind.EscapedText, constructor: nodes.DocEscapedText }, { docNodeKind: DocNodeKind.EscapedText, constructor: nodes.DocEscapedText },
{ docNodeKind: DocNodeKind.Excerpt, constructor: nodes.DocExcerpt }, { docNodeKind: DocNodeKind.Excerpt, constructor: nodes.DocExcerpt },
{ docNodeKind: DocNodeKind.FencedCode, constructor: nodes.DocFencedCode }, { docNodeKind: DocNodeKind.FencedCode, constructor: nodes.DocFencedCode },
{ docNodeKind: DocNodeKind.HtmlAttribute, constructor: nodes.DocHtmlAttribute }, { docNodeKind: DocNodeKind.HtmlAttribute, constructor: nodes.DocHtmlAttribute },
{ docNodeKind: DocNodeKind.HtmlEndTag, constructor: nodes.DocHtmlEndTag }, { docNodeKind: DocNodeKind.HtmlEndTag, constructor: nodes.DocHtmlEndTag },
{ docNodeKind: DocNodeKind.HtmlStartTag, constructor: nodes.DocHtmlStartTag }, { docNodeKind: DocNodeKind.HtmlStartTag, constructor: nodes.DocHtmlStartTag },
{ docNodeKind: DocNodeKind.InheritDocTag, constructor: nodes.DocInheritDocTag }, { docNodeKind: DocNodeKind.InheritDocTag, constructor: nodes.DocInheritDocTag },
{ docNodeKind: DocNodeKind.InlineTag, constructor: nodes.DocInlineTag }, { docNodeKind: DocNodeKind.InlineTag, constructor: nodes.DocInlineTag },
{ docNodeKind: DocNodeKind.LinkTag, constructor: nodes.DocLinkTag }, { docNodeKind: DocNodeKind.LinkTag, constructor: nodes.DocLinkTag },
{ docNodeKind: DocNodeKind.MemberIdentifier, constructor: nodes.DocMemberIdentifier }, { docNodeKind: DocNodeKind.MemberIdentifier, constructor: nodes.DocMemberIdentifier },
{ docNodeKind: DocNodeKind.MemberReference, constructor: nodes.DocMemberReference }, { docNodeKind: DocNodeKind.MemberReference, constructor: nodes.DocMemberReference },
{ docNodeKind: DocNodeKind.MemberSelector, constructor: nodes.DocMemberSelector }, { docNodeKind: DocNodeKind.MemberSelector, constructor: nodes.DocMemberSelector },
{ docNodeKind: DocNodeKind.MemberSymbol, constructor: nodes.DocMemberSymbol }, { docNodeKind: DocNodeKind.MemberSymbol, constructor: nodes.DocMemberSymbol },
{ docNodeKind: DocNodeKind.Paragraph, constructor: nodes.DocParagraph }, { docNodeKind: DocNodeKind.Paragraph, constructor: nodes.DocParagraph },
{ docNodeKind: DocNodeKind.ParamBlock, constructor: nodes.DocParamBlock}, { docNodeKind: DocNodeKind.ParamBlock, constructor: nodes.DocParamBlock },
{ docNodeKind: DocNodeKind.ParamCollection, constructor: nodes.DocParamCollection }, { docNodeKind: DocNodeKind.ParamCollection, constructor: nodes.DocParamCollection },
{ docNodeKind: DocNodeKind.PlainText, constructor: nodes.DocPlainText }, { docNodeKind: DocNodeKind.PlainText, constructor: nodes.DocPlainText },
{ docNodeKind: DocNodeKind.Section, constructor: nodes.DocSection }, { docNodeKind: DocNodeKind.Section, constructor: nodes.DocSection },
{ docNodeKind: DocNodeKind.SoftBreak, constructor: nodes.DocSoftBreak } { docNodeKind: DocNodeKind.SoftBreak, constructor: nodes.DocSoftBreak }
]); ]);
docNodeManager.registerAllowableChildren(DocNodeKind.Section, [ docNodeManager.registerAllowableChildren(DocNodeKind.Section, [

Просмотреть файл

@ -69,15 +69,14 @@ export class DocBlockTag extends DocNode {
/** @override */ /** @override */
protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> { protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> {
return [ return [this._tagNameExcerpt];
this._tagNameExcerpt
];
} }
public getTokenSequence(): TokenSequence { public getTokenSequence(): TokenSequence {
if (!this._tagNameExcerpt) { if (!this._tagNameExcerpt) {
throw new Error('DocBlockTag.getTokenSequence() failed because this object did not' throw new Error(
+ ' originate from a parsed input'); 'DocBlockTag.getTokenSequence() failed because this object did not' + ' originate from a parsed input'
);
} }
return this._tagNameExcerpt.content; return this._tagNameExcerpt.content;
} }

Просмотреть файл

@ -80,10 +80,6 @@ export class DocCodeSpan extends DocNode {
/** @override */ /** @override */
protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> { protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> {
return [ return [this._openingDelimiterExcerpt, this._codeExcerpt, this._closingDelimiterExcerpt];
this._openingDelimiterExcerpt,
this._codeExcerpt,
this._closingDelimiterExcerpt
];
} }
} }

Просмотреть файл

@ -9,8 +9,7 @@ import { DocParamCollection } from './DocParamCollection';
/** /**
* Constructor parameters for {@link DocComment}. * Constructor parameters for {@link DocComment}.
*/ */
export interface IDocCommentParameters extends IDocNodeParameters { export interface IDocCommentParameters extends IDocNodeParameters {}
}
/** /**
* Represents an entire documentation comment conforming to the TSDoc structure. * Represents an entire documentation comment conforming to the TSDoc structure.
@ -107,7 +106,7 @@ export class DocComment extends DocNode {
this.modifierTagSet = new StandardModifierTagSet(); this.modifierTagSet = new StandardModifierTagSet();
this._seeBlocks = [] this._seeBlocks = [];
this._customBlocks = []; this._customBlocks = [];
} }

Просмотреть файл

@ -47,7 +47,9 @@ export class DocDeclarationReference extends DocNode {
* Don't call this directly. Instead use {@link TSDocParser} * Don't call this directly. Instead use {@link TSDocParser}
* @internal * @internal
*/ */
public constructor(parameters: IDocDeclarationReferenceParameters | IDocDeclarationReferenceParsedParameters) { public constructor(
parameters: IDocDeclarationReferenceParameters | IDocDeclarationReferenceParsedParameters
) {
super(parameters); super(parameters);
if (DocNode.isParsedParameters(parameters)) { if (DocNode.isParsedParameters(parameters)) {
@ -65,14 +67,14 @@ export class DocDeclarationReference extends DocNode {
content: parameters.importPathExcerpt content: parameters.importPathExcerpt
}); });
} }
if (parameters.importHashExcerpt ) { if (parameters.importHashExcerpt) {
this._importHashExcerpt = new DocExcerpt({ this._importHashExcerpt = new DocExcerpt({
configuration: this.configuration, configuration: this.configuration,
excerptKind: ExcerptKind.DeclarationReference_ImportHash, excerptKind: ExcerptKind.DeclarationReference_ImportHash,
content: parameters.importHashExcerpt content: parameters.importHashExcerpt
}); });
} }
if (parameters.spacingAfterImportHashExcerpt ) { if (parameters.spacingAfterImportHashExcerpt) {
this._spacingAfterImportHashExcerpt = new DocExcerpt({ this._spacingAfterImportHashExcerpt = new DocExcerpt({
configuration: this.configuration, configuration: this.configuration,
excerptKind: ExcerptKind.Spacing, excerptKind: ExcerptKind.Spacing,

Просмотреть файл

@ -57,7 +57,7 @@ export class DocErrorText extends DocNode {
if (this._text === undefined) { if (this._text === undefined) {
this._text = this._textExcerpt.content.toString(); this._text = this._textExcerpt.content.toString();
} }
return this._text; return this._text;
} }
public get textExcerpt(): TokenSequence | undefined { public get textExcerpt(): TokenSequence | undefined {
@ -97,8 +97,6 @@ export class DocErrorText extends DocNode {
/** @override */ /** @override */
protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> { protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> {
return [ return [this._textExcerpt];
this._textExcerpt
];
} }
} }

Просмотреть файл

@ -85,8 +85,6 @@ export class DocEscapedText extends DocNode {
/** @override */ /** @override */
protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> { protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> {
return [ return [this._encodedTextExcerpt];
this._encodedTextExcerpt
];
} }
} }

Просмотреть файл

@ -85,7 +85,6 @@ export const enum ExcerptKind {
/* eslint-enable @typescript-eslint/naming-convention */ /* eslint-enable @typescript-eslint/naming-convention */
/** /**
* Constructor parameters for {@link DocExcerpt}. * Constructor parameters for {@link DocExcerpt}.
*/ */

Просмотреть файл

@ -99,7 +99,6 @@ export class DocHtmlAttribute extends DocNode {
content: parameters.spacingAfterValueExcerpt content: parameters.spacingAfterValueExcerpt
}); });
} }
} else { } else {
this._name = parameters.name; this._name = parameters.name;
this._spacingAfterName = parameters.spacingAfterName; this._spacingAfterName = parameters.spacingAfterName;

Просмотреть файл

@ -163,7 +163,6 @@ export class DocHtmlStartTag extends DocNode {
this._closingDelimiterExcerpt this._closingDelimiterExcerpt
]; ];
} }
} }
// Circular reference // Circular reference

Просмотреть файл

@ -24,7 +24,6 @@ export interface IDocInheritDocTagParsedParameters extends IDocInlineTagBasePars
* Represents an `{@inheritDoc}` tag. * Represents an `{@inheritDoc}` tag.
*/ */
export class DocInheritDocTag extends DocInlineTagBase { export class DocInheritDocTag extends DocInlineTagBase {
private readonly _declarationReference: DocDeclarationReference | undefined; private readonly _declarationReference: DocDeclarationReference | undefined;
/** /**
@ -55,9 +54,8 @@ export class DocInheritDocTag extends DocInlineTagBase {
} }
/** @override */ /** @override */
protected getChildNodesForContent(): ReadonlyArray<DocNode | undefined> { // abstract protected getChildNodesForContent(): ReadonlyArray<DocNode | undefined> {
return [ // abstract
this._declarationReference return [this._declarationReference];
];
} }
} }

Просмотреть файл

@ -44,7 +44,7 @@ export class DocInlineTag extends DocInlineTagBase {
if (DocNode.isParsedParameters(parameters)) { if (DocNode.isParsedParameters(parameters)) {
if (parameters.tagContentExcerpt) { if (parameters.tagContentExcerpt) {
this._tagContentExcerpt = new DocExcerpt({ this._tagContentExcerpt = new DocExcerpt({
configuration: this.configuration, configuration: this.configuration,
excerptKind: ExcerptKind.InlineTag_TagContent, excerptKind: ExcerptKind.InlineTag_TagContent,
content: parameters.tagContentExcerpt content: parameters.tagContentExcerpt
@ -78,9 +78,8 @@ export class DocInlineTag extends DocInlineTagBase {
} }
/** @override */ /** @override */
protected getChildNodesForContent(): ReadonlyArray<DocNode | undefined> { // abstract protected getChildNodesForContent(): ReadonlyArray<DocNode | undefined> {
return [ // abstract
this._tagContentExcerpt return [this._tagContentExcerpt];
];
} }
} }

Просмотреть файл

@ -125,7 +125,6 @@ export class DocLinkTag extends DocInlineTagBase {
this._urlDestination = parameters.urlDestination; this._urlDestination = parameters.urlDestination;
this._linkText = parameters.linkText; this._linkText = parameters.linkText;
} }
} }
/** @override */ /** @override */
@ -190,7 +189,8 @@ export class DocLinkTag extends DocInlineTagBase {
} }
/** @override */ /** @override */
protected getChildNodesForContent(): ReadonlyArray<DocNode | undefined> { // abstract protected getChildNodesForContent(): ReadonlyArray<DocNode | undefined> {
// abstract
return [ return [
this._codeDestination, this._codeDestination,
this._urlDestinationExcerpt, this._urlDestinationExcerpt,

Просмотреть файл

@ -113,10 +113,6 @@ export class DocMemberIdentifier extends DocNode {
/** @override */ /** @override */
protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> { protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> {
return [ return [this._leftQuoteExcerpt, this._identifierExcerpt, this._rightQuoteExcerpt];
this._leftQuoteExcerpt,
this._identifierExcerpt,
this._rightQuoteExcerpt
];
} }
} }

Просмотреть файл

@ -112,16 +112,18 @@ export class DocMemberSelector extends DocNode {
if (DocMemberSelector._labelSelectorRegExp.test(this._selector)) { if (DocMemberSelector._labelSelectorRegExp.test(this._selector)) {
this._selectorKind = SelectorKind.Label; this._selectorKind = SelectorKind.Label;
} else { } else {
this._errorMessage = 'A label selector must be comprised of upper case letters, numbers,' this._errorMessage =
+ ' and underscores and must not start with a number'; 'A label selector must be comprised of upper case letters, numbers,' +
' and underscores and must not start with a number';
} }
} else { } else {
if (StringChecks.isSystemSelector(this._selector)) { if (StringChecks.isSystemSelector(this._selector)) {
this._selectorKind = SelectorKind.System; this._selectorKind = SelectorKind.System;
} else if (DocMemberSelector._likeSystemSelectorRegExp.test(this._selector)) { } else if (DocMemberSelector._likeSystemSelectorRegExp.test(this._selector)) {
// It looks like a system selector, but is not // It looks like a system selector, but is not
this._errorMessage = `The selector ${JSON.stringify(this._selector)}` this._errorMessage =
+ ` is not a recognized TSDoc system selector name`; `The selector ${JSON.stringify(this._selector)}` +
` is not a recognized TSDoc system selector name`;
} else { } else {
// It doesn't look like anything we recognize // It doesn't look like anything we recognize
this._errorMessage = 'Invalid syntax for selector'; this._errorMessage = 'Invalid syntax for selector';
@ -163,8 +165,6 @@ export class DocMemberSelector extends DocNode {
/** @override */ /** @override */
protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> { protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> {
return [ return [this._selectorExcerpt];
this._selectorExcerpt
];
} }
} }

Просмотреть файл

@ -9,31 +9,31 @@ import { TSDocConfiguration } from '../configuration/TSDocConfiguration';
* NPM package name, followed by a "#" symbol, followed by the class name (without the "Doc" prefix). * NPM package name, followed by a "#" symbol, followed by the class name (without the "Doc" prefix).
*/ */
export const enum DocNodeKind { export const enum DocNodeKind {
Block = 'Block', Block = 'Block',
BlockTag = 'BlockTag', BlockTag = 'BlockTag',
Excerpt = 'Excerpt', Excerpt = 'Excerpt',
FencedCode = 'FencedCode', FencedCode = 'FencedCode',
CodeSpan = 'CodeSpan', CodeSpan = 'CodeSpan',
Comment = 'Comment', Comment = 'Comment',
DeclarationReference = 'DeclarationReference', DeclarationReference = 'DeclarationReference',
ErrorText = 'ErrorText', ErrorText = 'ErrorText',
EscapedText = 'EscapedText', EscapedText = 'EscapedText',
HtmlAttribute = 'HtmlAttribute', HtmlAttribute = 'HtmlAttribute',
HtmlEndTag = 'HtmlEndTag', HtmlEndTag = 'HtmlEndTag',
HtmlStartTag = 'HtmlStartTag', HtmlStartTag = 'HtmlStartTag',
InheritDocTag = 'InheritDocTag', InheritDocTag = 'InheritDocTag',
InlineTag = 'InlineTag', InlineTag = 'InlineTag',
LinkTag = 'LinkTag', LinkTag = 'LinkTag',
MemberIdentifier = 'MemberIdentifier', MemberIdentifier = 'MemberIdentifier',
MemberReference = 'MemberReference', MemberReference = 'MemberReference',
MemberSelector = 'MemberSelector', MemberSelector = 'MemberSelector',
MemberSymbol = 'MemberSymbol', MemberSymbol = 'MemberSymbol',
Paragraph = 'Paragraph', Paragraph = 'Paragraph',
ParamBlock = 'ParamBlock', ParamBlock = 'ParamBlock',
ParamCollection = 'ParamCollection', ParamCollection = 'ParamCollection',
PlainText = 'PlainText', PlainText = 'PlainText',
Section = 'Section', Section = 'Section',
SoftBreak = 'SoftBreak' SoftBreak = 'SoftBreak'
} }
/** /**
@ -93,7 +93,7 @@ export abstract class DocNode {
// Do this sanity check here, since the constructor cannot access abstract members // Do this sanity check here, since the constructor cannot access abstract members
this.configuration.docNodeManager.throwIfNotRegisteredKind(this.kind); this.configuration.docNodeManager.throwIfNotRegisteredKind(this.kind);
return this.onGetChildNodes().filter(x => x !== undefined) as ReadonlyArray<DocNode>; return this.onGetChildNodes().filter((x) => x !== undefined) as ReadonlyArray<DocNode>;
} }
/** /**
@ -113,9 +113,9 @@ export abstract class DocNode {
* hierarchy for its constructor parameters. The "parser scenario" constructs the object by parsing a TypeScript * hierarchy for its constructor parameters. The "parser scenario" constructs the object by parsing a TypeScript
* source file, does create DocExcerpt child nodes, and generally uses the {@link IDocNodeParsedParameters} hierarchy. * source file, does create DocExcerpt child nodes, and generally uses the {@link IDocNodeParsedParameters} hierarchy.
*/ */
public static isParsedParameters(parameters: IDocNodeParameters | IDocNodeParsedParameters): public static isParsedParameters(
parameters is IDocNodeParsedParameters { parameters: IDocNodeParameters | IDocNodeParsedParameters
): parameters is IDocNodeParsedParameters {
return (parameters as IDocNodeParsedParameters).parsed === true; return (parameters as IDocNodeParsedParameters).parsed === true;
} }
} }

Просмотреть файл

@ -3,16 +3,12 @@ import { DocNode, IDocNodeParameters, IDocNodeParsedParameters } from './DocNode
/** /**
* Constructor parameters for {@link DocNodeContainer}. * Constructor parameters for {@link DocNodeContainer}.
*/ */
export interface IDocNodeContainerParameters extends IDocNodeParameters { export interface IDocNodeContainerParameters extends IDocNodeParameters {}
}
/** /**
* Constructor parameters for {@link DocNodeContainer}. * Constructor parameters for {@link DocNodeContainer}.
*/ */
export interface IDocNodeContainerParsedParameters extends IDocNodeParsedParameters { export interface IDocNodeContainerParsedParameters extends IDocNodeParsedParameters {}
}
/** /**
* DocNodeContainer is the base class for DocNode classes that allow arbitrary child nodes to be added by the consumer. * DocNodeContainer is the base class for DocNode classes that allow arbitrary child nodes to be added by the consumer.
@ -25,9 +21,10 @@ export abstract class DocNodeContainer extends DocNode {
* Don't call this directly. Instead use {@link TSDocParser} * Don't call this directly. Instead use {@link TSDocParser}
* @internal * @internal
*/ */
public constructor(parameters: IDocNodeContainerParameters | IDocNodeContainerParsedParameters, public constructor(
childNodes?: ReadonlyArray<DocNode>) { parameters: IDocNodeContainerParameters | IDocNodeContainerParsedParameters,
childNodes?: ReadonlyArray<DocNode>
) {
super(parameters); super(parameters);
if (childNodes !== undefined && childNodes.length > 0) { if (childNodes !== undefined && childNodes.length > 0) {
@ -47,8 +44,10 @@ export abstract class DocNodeContainer extends DocNode {
*/ */
public appendNode(docNode: DocNode): void { public appendNode(docNode: DocNode): void {
if (!this.configuration.docNodeManager.isAllowedChild(this.kind, docNode.kind)) { if (!this.configuration.docNodeManager.isAllowedChild(this.kind, docNode.kind)) {
throw new Error(`The TSDocConfiguration does not allow a ${this.kind} node to` throw new Error(
+ ` contain a node of type ${docNode.kind}`); `The TSDocConfiguration does not allow a ${this.kind} node to` +
` contain a node of type ${docNode.kind}`
);
} }
this._nodes!.push(docNode); this._nodes!.push(docNode);

Просмотреть файл

@ -4,8 +4,7 @@ import { DocNodeContainer, IDocNodeContainerParameters } from './DocNodeContaine
/** /**
* Constructor parameters for {@link DocParagraph}. * Constructor parameters for {@link DocParagraph}.
*/ */
export interface IDocParagraphParameters extends IDocNodeContainerParameters { export interface IDocParagraphParameters extends IDocNodeContainerParameters {}
}
/** /**
* Represents a paragraph of text, similar to a `<p>` element in HTML. * Represents a paragraph of text, similar to a `<p>` element in HTML.

Просмотреть файл

@ -146,7 +146,6 @@ export class DocParamBlock extends DocBlock {
content: parameters.unsupportedJsdocTypeAfterHyphenExcerpt content: parameters.unsupportedJsdocTypeAfterHyphenExcerpt
}); });
} }
} }
} }

Просмотреть файл

@ -4,9 +4,7 @@ import { DocParamBlock } from './DocParamBlock';
/** /**
* Constructor parameters for {@link DocParamCollection}. * Constructor parameters for {@link DocParamCollection}.
*/ */
export interface IDocParamCollectionParameters extends IDocNodeParameters { export interface IDocParamCollectionParameters extends IDocNodeParameters {}
}
/** /**
* Represents a collection of DocParamBlock objects and provides efficient operations for looking up the * Represents a collection of DocParamBlock objects and provides efficient operations for looking up the

Просмотреть файл

@ -80,8 +80,6 @@ export class DocPlainText extends DocNode {
/** @override */ /** @override */
protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> { protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> {
return [ return [this._textExcerpt];
this._textExcerpt
];
} }
} }

Просмотреть файл

@ -9,16 +9,12 @@ import {
/** /**
* Constructor parameters for {@link DocSection}. * Constructor parameters for {@link DocSection}.
*/ */
export interface IDocSectionParameters extends IDocNodeContainerParameters { export interface IDocSectionParameters extends IDocNodeContainerParameters {}
}
/** /**
* Constructor parameters for {@link DocSection}. * Constructor parameters for {@link DocSection}.
*/ */
export interface IDocSectionParsedParameters extends IDocNodeContainerParsedParameters { export interface IDocSectionParsedParameters extends IDocNodeContainerParsedParameters {}
}
/** /**
* Represents a general block of rich text. * Represents a general block of rich text.
@ -28,9 +24,10 @@ export class DocSection extends DocNodeContainer {
* Don't call this directly. Instead use {@link TSDocParser} * Don't call this directly. Instead use {@link TSDocParser}
* @internal * @internal
*/ */
public constructor(parameters: IDocSectionParameters | IDocSectionParsedParameters, public constructor(
childNodes?: ReadonlyArray<DocNode>) { parameters: IDocSectionParameters | IDocSectionParsedParameters,
childNodes?: ReadonlyArray<DocNode>
) {
super(parameters, childNodes); super(parameters, childNodes);
} }
@ -65,5 +62,4 @@ export class DocSection extends DocNodeContainer {
this.appendNodeInParagraph(docNode); this.appendNodeInParagraph(docNode);
} }
} }
} }

Просмотреть файл

@ -5,8 +5,7 @@ import { DocExcerpt, ExcerptKind } from './DocExcerpt';
/** /**
* Constructor parameters for {@link DocSoftBreak}. * Constructor parameters for {@link DocSoftBreak}.
*/ */
export interface IDocSoftBreakParameters extends IDocNodeParameters { export interface IDocSoftBreakParameters extends IDocNodeParameters {}
}
/** /**
* Constructor parameters for {@link DocSoftBreak}. * Constructor parameters for {@link DocSoftBreak}.
@ -54,8 +53,6 @@ export class DocSoftBreak extends DocNode {
/** @override */ /** @override */
protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> { protected onGetChildNodes(): ReadonlyArray<DocNode | undefined> {
return [ return [this._softBreakExcerpt];
this._softBreakExcerpt
];
} }
} }

Просмотреть файл

@ -1,4 +1,3 @@
export * from './DocBlock'; export * from './DocBlock';
export * from './DocBlockTag'; export * from './DocBlockTag';
export * from './DocCodeSpan'; export * from './DocCodeSpan';

Просмотреть файл

@ -53,12 +53,16 @@ export class LineExtractor {
case State.BeginComment2: case State.BeginComment2:
parserContext.log.addMessageForTextRange( parserContext.log.addMessageForTextRange(
TSDocMessageId.CommentNotFound, TSDocMessageId.CommentNotFound,
'Expecting a "/**" comment', range); 'Expecting a "/**" comment',
range
);
return false; return false;
default: default:
parserContext.log.addMessageForTextRange( parserContext.log.addMessageForTextRange(
TSDocMessageId.CommentMissingClosingDelimiter, TSDocMessageId.CommentMissingClosingDelimiter,
'Unexpected end of input', range); 'Unexpected end of input',
range
);
return false; return false;
} }
} }
@ -78,7 +82,8 @@ export class LineExtractor {
parserContext.log.addMessageForTextRange( parserContext.log.addMessageForTextRange(
TSDocMessageId.CommentOpeningDelimiterSyntax, TSDocMessageId.CommentOpeningDelimiterSyntax,
'Expecting a leading "/**"', 'Expecting a leading "/**"',
range.getNewRange(currentIndex, currentIndex + 1)); range.getNewRange(currentIndex, currentIndex + 1)
);
return false; return false;
} }
break; break;
@ -94,7 +99,8 @@ export class LineExtractor {
parserContext.log.addMessageForTextRange( parserContext.log.addMessageForTextRange(
TSDocMessageId.CommentOpeningDelimiterSyntax, TSDocMessageId.CommentOpeningDelimiterSyntax,
'Expecting a leading "/**"', 'Expecting a leading "/**"',
range.getNewRange(currentIndex, currentIndex + 1)); range.getNewRange(currentIndex, currentIndex + 1)
);
return false; return false;
} }
break; break;

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,10 +1,4 @@
import { import { DocSection, DocNode, DocNodeKind, DocParagraph, DocPlainText } from '../nodes';
DocSection,
DocNode,
DocNodeKind,
DocParagraph,
DocPlainText
} from '../nodes';
/** /**
* The ParagraphSplitter is a secondary stage that runs after the NodeParser has constructed * The ParagraphSplitter is a secondary stage that runs after the NodeParser has constructed
@ -68,7 +62,6 @@ export class ParagraphSplitter {
let currentIndex: number = 0; let currentIndex: number = 0;
while (currentIndex < inputParagraphNodes.length) { while (currentIndex < inputParagraphNodes.length) {
// Scan forwards to the end of the line // Scan forwards to the end of the line
let isBlankLine: boolean = true; let isBlankLine: boolean = true;
let lineEndIndex: number = currentIndex; // non-inclusive let lineEndIndex: number = currentIndex; // non-inclusive

Просмотреть файл

@ -29,25 +29,33 @@ export class ParserMessageLog {
* Append a message associated with a TextRange. * Append a message associated with a TextRange.
*/ */
public addMessageForTextRange(messageId: TSDocMessageId, messageText: string, textRange: TextRange): void { public addMessageForTextRange(messageId: TSDocMessageId, messageText: string, textRange: TextRange): void {
this.addMessage(new ParserMessage({ this.addMessage(
messageId, new ParserMessage({
messageText, messageId,
textRange messageText,
})); textRange
})
);
} }
/** /**
* Append a message associated with a TokenSequence. * Append a message associated with a TokenSequence.
*/ */
public addMessageForTokenSequence(messageId: TSDocMessageId, messageText: string, tokenSequence: TokenSequence, public addMessageForTokenSequence(
docNode?: DocNode): void { messageId: TSDocMessageId,
this.addMessage(new ParserMessage({ messageText: string,
messageId, tokenSequence: TokenSequence,
messageText, docNode?: DocNode
textRange: tokenSequence.getContainingTextRange(), ): void {
tokenSequence, this.addMessage(
docNode new ParserMessage({
})); messageId,
messageText,
textRange: tokenSequence.getContainingTextRange(),
tokenSequence,
docNode
})
);
} }
/** /**
@ -65,12 +73,14 @@ export class ParserMessageLog {
tokenSequence = docErrorText.errorLocation; tokenSequence = docErrorText.errorLocation;
} }
this.addMessage(new ParserMessage({ this.addMessage(
messageId: docErrorText.messageId, new ParserMessage({
messageText: docErrorText.errorMessage, messageId: docErrorText.messageId,
textRange: tokenSequence.getContainingTextRange(), messageText: docErrorText.errorMessage,
tokenSequence: tokenSequence, textRange: tokenSequence.getContainingTextRange(),
docNode: docErrorText tokenSequence: tokenSequence,
})); docNode: docErrorText
})
);
} }
} }

Просмотреть файл

@ -32,10 +32,18 @@ export class StringChecks {
private static readonly _systemSelectors: Set<string> = new Set<string>([ private static readonly _systemSelectors: Set<string> = new Set<string>([
// For classes: // For classes:
'instance', 'static', 'constructor', 'instance',
'static',
'constructor',
// For merged declarations: // For merged declarations:
'class', 'enum', 'function', 'interface', 'namespace', 'type', 'variable' 'class',
'enum',
'function',
'interface',
'namespace',
'type',
'variable'
]); ]);
/** /**
@ -76,8 +84,10 @@ export class StringChecks {
return 'The URL cannot be empty'; return 'The URL cannot be empty';
} }
if (!StringChecks._urlSchemeRegExp.test(url)) { if (!StringChecks._urlSchemeRegExp.test(url)) {
return 'An @link URL must begin with a scheme comprised only of letters and numbers followed by "://".' return (
+ ' (For general URLs, use an HTML "<a>" tag instead.)'; 'An @link URL must begin with a scheme comprised only of letters and numbers followed by "://".' +
' (For general URLs, use an HTML "<a>" tag instead.)'
);
} }
if (!StringChecks._urlSchemeAfterRegExp.test(url)) { if (!StringChecks._urlSchemeAfterRegExp.test(url)) {
return 'An @link URL must have at least one character after "://"'; return 'An @link URL must have at least one character after "://"';
@ -125,7 +135,10 @@ export class StringChecks {
/** /**
* Tests whether the input string is a valid declaration reference import path. * Tests whether the input string is a valid declaration reference import path.
*/ */
public static explainIfInvalidImportPath(importPath: string, prefixedByPackageName: boolean): string | undefined { public static explainIfInvalidImportPath(
importPath: string,
prefixedByPackageName: boolean
): string | undefined {
if (importPath.length > 0) { if (importPath.length > 0) {
if (importPath.indexOf('//') >= 0) { if (importPath.indexOf('//') >= 0) {
return 'An import path must not contain "//"'; return 'An import path must not contain "//"';

Просмотреть файл

@ -1,4 +1,3 @@
/** /**
* Unique identifiers for messages reported by the TSDoc parser. * Unique identifiers for messages reported by the TSDoc parser.
* *

Просмотреть файл

@ -100,11 +100,13 @@ export class TextRange {
* then the output would be "12[345]67". * then the output would be "12[345]67".
*/ */
public getDebugDump(posDelimiter: string, endDelimiter: string): string { public getDebugDump(posDelimiter: string, endDelimiter: string): string {
return this.buffer.substring(0, this.pos) return (
+ posDelimiter this.buffer.substring(0, this.pos) +
+ this.buffer.substring(this.pos, this.end) posDelimiter +
+ endDelimiter this.buffer.substring(this.pos, this.end) +
+ this.buffer.substring(this.end); endDelimiter +
this.buffer.substring(this.end)
);
} }
/** /**
@ -132,12 +134,14 @@ export class TextRange {
const current: string = this.buffer[currentIndex]; const current: string = this.buffer[currentIndex];
++currentIndex; ++currentIndex;
if (current === '\r') { // CR if (current === '\r') {
// CR
// Ignore '\r' and assume it will always have an accompanying '\n' // Ignore '\r' and assume it will always have an accompanying '\n'
continue; continue;
} }
if (current === '\n') { // LF if (current === '\n') {
// LF
++line; ++line;
column = 1; column = 1;
} else { } else {

Просмотреть файл

@ -54,8 +54,10 @@ export class TokenReader {
public extractAccumulatedSequence(): TokenSequence { public extractAccumulatedSequence(): TokenSequence {
if (this._accumulatedStartIndex === this._currentIndex) { if (this._accumulatedStartIndex === this._currentIndex) {
// If this happens, it indicates a parser bug: // If this happens, it indicates a parser bug:
throw new Error('Parser assertion failed: The queue should not be empty when' throw new Error(
+ ' extractAccumulatedSequence() is called'); 'Parser assertion failed: The queue should not be empty when' +
' extractAccumulatedSequence() is called'
);
} }
const sequence: TokenSequence = new TokenSequence({ const sequence: TokenSequence = new TokenSequence({
@ -102,9 +104,11 @@ export class TokenReader {
startIndex: this._accumulatedStartIndex, startIndex: this._accumulatedStartIndex,
endIndex: this._currentIndex endIndex: this._currentIndex
}); });
const tokenStrings: string[] = sequence.tokens.map(x => x.toString()); const tokenStrings: string[] = sequence.tokens.map((x) => x.toString());
throw new Error('Parser assertion failed: The queue should be empty, but it contains:\n' throw new Error(
+ JSON.stringify(tokenStrings)); 'Parser assertion failed: The queue should be empty, but it contains:\n' +
JSON.stringify(tokenStrings)
);
} }
} }

Просмотреть файл

@ -91,7 +91,7 @@ export class TokenSequence {
* Returns the concatenated text of all the tokens. * Returns the concatenated text of all the tokens.
*/ */
public toString(): string { public toString(): string {
return this.tokens.map(x => x.toString()).join(''); return this.tokens.map((x) => x.toString()).join('');
} }
private _validateBounds(): void { private _validateBounds(): void {

Просмотреть файл

@ -2,10 +2,9 @@ import { TextRange } from './TextRange';
import { Token, TokenKind } from './Token'; import { Token, TokenKind } from './Token';
export class Tokenizer { export class Tokenizer {
private static readonly _commonMarkPunctuationCharacters: string private static readonly _commonMarkPunctuationCharacters: string = '!"#$%&\'()*+,-./:;<=>?@[\\]^`{|}~';
= '!"#$%&\'()*+,\-.\/:;<=>?@[\\]^`{|}~'; private static readonly _wordCharacters: string =
private static readonly _wordCharacters: string 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_';
= 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_';
private static _charCodeMap: { [charCode: number]: TokenKind | undefined }; private static _charCodeMap: { [charCode: number]: TokenKind | undefined };
private static _punctuationTokens: { [tokenKind: number]: boolean }; private static _punctuationTokens: { [tokenKind: number]: boolean };
@ -27,13 +26,11 @@ export class Tokenizer {
} }
if (lastLine) { if (lastLine) {
tokens.push(new Token(TokenKind.EndOfInput, tokens.push(
lastLine.getNewRange(lastLine.end, lastLine.end), new Token(TokenKind.EndOfInput, lastLine.getNewRange(lastLine.end, lastLine.end), lastLine)
lastLine)); );
} else { } else {
tokens.push(new Token(TokenKind.EndOfInput, tokens.push(new Token(TokenKind.EndOfInput, TextRange.empty, TextRange.empty));
TextRange.empty,
TextRange.empty));
} }
return tokens; return tokens;
@ -68,9 +65,11 @@ export class Tokenizer {
// 1. There is an existing token, AND // 1. There is an existing token, AND
// 2. It is the same kind of token, AND // 2. It is the same kind of token, AND
// 3. It's not punctuation (which is always one character) // 3. It's not punctuation (which is always one character)
if (tokenKind !== undefined if (
&& characterKind === tokenKind tokenKind !== undefined &&
&& Tokenizer._isMultiCharacterToken(tokenKind)) { characterKind === tokenKind &&
Tokenizer._isMultiCharacterToken(tokenKind)
) {
// yes, append // yes, append
} else { } else {
// Is there a previous completed token to push? // Is there a previous completed token to push?
@ -125,29 +124,29 @@ export class Tokenizer {
// !"#$%&\'()*+,\-.\/:;<=>?@[\\]^_`{|}~ // !"#$%&\'()*+,\-.\/:;<=>?@[\\]^_`{|}~
const specialMap: { [character: string]: TokenKind } = { const specialMap: { [character: string]: TokenKind } = {
'\\' : TokenKind.Backslash, '\\': TokenKind.Backslash,
'<' : TokenKind.LessThan, '<': TokenKind.LessThan,
'>' : TokenKind.GreaterThan, '>': TokenKind.GreaterThan,
'=' : TokenKind.Equals, '=': TokenKind.Equals,
'\'' : TokenKind.SingleQuote, "'": TokenKind.SingleQuote,
'"' : TokenKind.DoubleQuote, '"': TokenKind.DoubleQuote,
'/' : TokenKind.Slash, '/': TokenKind.Slash,
'-' : TokenKind.Hyphen, '-': TokenKind.Hyphen,
'@' : TokenKind.AtSign, '@': TokenKind.AtSign,
'{' : TokenKind.LeftCurlyBracket, '{': TokenKind.LeftCurlyBracket,
'}' : TokenKind.RightCurlyBracket, '}': TokenKind.RightCurlyBracket,
'`' : TokenKind.Backtick, '`': TokenKind.Backtick,
'.' : TokenKind.Period, '.': TokenKind.Period,
':' : TokenKind.Colon, ':': TokenKind.Colon,
',' : TokenKind.Comma, ',': TokenKind.Comma,
'[' : TokenKind.LeftSquareBracket, '[': TokenKind.LeftSquareBracket,
']' : TokenKind.RightSquareBracket, ']': TokenKind.RightSquareBracket,
'|' : TokenKind.Pipe, '|': TokenKind.Pipe,
'(' : TokenKind.LeftParenthesis, '(': TokenKind.LeftParenthesis,
')' : TokenKind.RightParenthesis, ')': TokenKind.RightParenthesis,
'#' : TokenKind.PoundSymbol, '#': TokenKind.PoundSymbol,
'+' : TokenKind.Plus, '+': TokenKind.Plus,
'$' : TokenKind.DollarSign $: TokenKind.DollarSign
}; };
for (const key of Object.getOwnPropertyNames(specialMap)) { for (const key of Object.getOwnPropertyNames(specialMap)) {
Tokenizer._charCodeMap[key.charCodeAt(0)] = specialMap[key]; Tokenizer._charCodeMap[key.charCodeAt(0)] = specialMap[key];

Просмотреть файл

@ -8,36 +8,39 @@ function parseAndMatchSnapshot(buffer: string): void {
expect({ expect({
buffer: TestHelpers.getEscaped(buffer), buffer: TestHelpers.getEscaped(buffer),
comment: TestHelpers.getEscaped(parserContext.commentRange.toString()), comment: TestHelpers.getEscaped(parserContext.commentRange.toString()),
lines: parserContext.lines.map(line => TestHelpers.getEscaped(line.toString())), lines: parserContext.lines.map((line) => TestHelpers.getEscaped(line.toString())),
logMessages: parserContext.log.messages.map(message => message.text) logMessages: parserContext.log.messages.map((message) => message.text)
}).toMatchSnapshot(); }).toMatchSnapshot();
} }
test('A. Whitespace variations', () => { test('A. Whitespace variations', () => {
parseAndMatchSnapshot(`/***/`); // 1 parseAndMatchSnapshot(`/***/`); // 1
parseAndMatchSnapshot(` /***/ `); // 2 parseAndMatchSnapshot(` /***/ `); // 2
parseAndMatchSnapshot(` /** */ `); // 3 parseAndMatchSnapshot(` /** */ `); // 3
parseAndMatchSnapshot(` /**\n\n*/ `); // 4 parseAndMatchSnapshot(` /**\n\n*/ `); // 4
parseAndMatchSnapshot(` /**L1*/ `); // 5 parseAndMatchSnapshot(` /**L1*/ `); // 5
parseAndMatchSnapshot(` /** L1 */ `); // 6 parseAndMatchSnapshot(` /** L1 */ `); // 6
parseAndMatchSnapshot(` /**L1\n*/ `); // 7 parseAndMatchSnapshot(` /**L1\n*/ `); // 7
parseAndMatchSnapshot(` /**L1*\n*/ `); // 8 parseAndMatchSnapshot(` /**L1*\n*/ `); // 8
parseAndMatchSnapshot(` /**\nL1*/ `); // 9 parseAndMatchSnapshot(` /**\nL1*/ `); // 9
parseAndMatchSnapshot(` /**\n L1 */ `); // 10 parseAndMatchSnapshot(` /**\n L1 */ `); // 10
parseAndMatchSnapshot(` /**\nL1\n*/ `); // 11 parseAndMatchSnapshot(` /**\nL1\n*/ `); // 11
parseAndMatchSnapshot(` /**\nL1\n\nL2*/ `); // 12 parseAndMatchSnapshot(` /**\nL1\n\nL2*/ `); // 12
parseAndMatchSnapshot(` /**\n*L1\n*/ `); // 13 parseAndMatchSnapshot(` /**\n*L1\n*/ `); // 13
parseAndMatchSnapshot(` /**\n * L1\n*/ `); // 14 parseAndMatchSnapshot(` /**\n * L1\n*/ `); // 14
parseAndMatchSnapshot(` /**\n * L1\n */ `); // 15 parseAndMatchSnapshot(` /**\n * L1\n */ `); // 15
parseAndMatchSnapshot(` /**L1\n *L2\nL3*/ `); // 16 parseAndMatchSnapshot(` /**L1\n *L2\nL3*/ `); // 16
parseAndMatchSnapshot(` /** L1\n * L2\n L3*/ `); // 17 parseAndMatchSnapshot(` /** L1\n * L2\n L3*/ `); // 17
parseAndMatchSnapshot(` /** L1 \n * L2 \n L3 */ `); // 18 parseAndMatchSnapshot(` /** L1 \n * L2 \n L3 */ `); // 18
parseAndMatchSnapshot([ // 19 parseAndMatchSnapshot(
'/** L1 ', [
' * L2 ', // 19
' L3 ', '/** L1 ',
' L4 */' ' * L2 ',
].join('\r\n')); ' L3 ',
' L4 */'
].join('\r\n')
);
}); });
// TODO: Special handling for these somewhat common ornamentations // TODO: Special handling for these somewhat common ornamentations
@ -52,43 +55,14 @@ test('B. Extra stars', () => {
}); });
test('C. Missing stars', () => { test('C. Missing stars', () => {
parseAndMatchSnapshot([ parseAndMatchSnapshot(['/**', '```', 'a', ' b', ' c ', ' d', '```', ' */'].join('\n'));
'/**',
'```',
'a',
' b',
' c ',
' d',
'```',
' */'
].join('\n'));
parseAndMatchSnapshot([ parseAndMatchSnapshot(['/**', '```', 'ee', ' ff', ' gg ', ' hh', '```', ' */'].join('\n'));
'/**',
'```',
'ee',
' ff',
' gg ',
' hh',
'```',
' */'
].join('\n'));
}); });
test('D. Newline styles', () => { test('D. Newline styles', () => {
parseAndMatchSnapshot([ parseAndMatchSnapshot(['', '/**', ' * L1', ' */', ''].join('\r\n'));
'', parseAndMatchSnapshot(['/**', 'L1', 'L2', '*/'].join('\r\n'));
'/**',
' * L1',
' */',
''
].join('\r\n'));
parseAndMatchSnapshot([
'/**',
'L1',
'L2',
'*/'
].join('\r\n'));
// We currently don't support CR or LFCR, so a single "\r" is treated // We currently don't support CR or LFCR, so a single "\r" is treated
// as part of the line. // as part of the line.

Просмотреть файл

@ -1,44 +1,30 @@
import { TestHelpers } from './TestHelpers'; import { TestHelpers } from './TestHelpers';
test('00 Tokenizer simple case', () => { test('00 Tokenizer simple case', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * line 1 ', // extra space at end of line '/**',
' * line 2', ' * line 1 ', // extra space at end of line
' */' ' * line 2',
].join('\n')); ' */'
].join('\n')
);
}); });
test('01 Tokenizer degenerate cases', () => { test('01 Tokenizer degenerate cases', () => {
TestHelpers.parseAndMatchNodeParserSnapshot('/***/'); TestHelpers.parseAndMatchNodeParserSnapshot('/***/');
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' *', ' */'].join('\n'));
'/**',
' *',
' */'
].join('\n'));
TestHelpers.parseAndMatchNodeParserSnapshot([
'/**',
' ',
' ',
' */'
].join('\n'));
TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' ', ' ', ' */'].join('\n'));
}); });
test('02 Backslash escapes: positive examples', () => { test('02 Backslash escapes: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * \\$\\@param', ' */'].join('\n'));
'/**',
' * \\$\\@param',
' */'
].join('\n'));
}); });
test('03 Backslash escapes: negative examples', () => { test('03 Backslash escapes: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * letter: \\A space: \\ end of line: \\', ' */'].join('\n')
' * letter: \\A space: \\ end of line: \\', );
' */'
].join('\n'));
}); });

Просмотреть файл

@ -1,95 +1,64 @@
import { TestHelpers } from './TestHelpers'; import { TestHelpers } from './TestHelpers';
test('00 Code span basic, positive', () => { test('00 Code span basic, positive', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * line `1`', ' * line ` 2` sdf', ' */'].join('\n'));
'/**', TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * M`&`M', ' */'].join('\n'));
' * line `1`',
' * line ` 2` sdf',
' */'
].join('\n'));
TestHelpers.parseAndMatchNodeParserSnapshot([
'/**',
' * M`&`M',
' */'
].join('\n'));
}); });
test('01 Code span basic, negative', () => { test('01 Code span basic, negative', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * `multi', ' * line`', ' */'].join('\n'));
'/**', TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * ``', ' */'].join('\n'));
' * `multi',
' * line`',
' */'
].join('\n'));
TestHelpers.parseAndMatchNodeParserSnapshot([
'/**',
' * ``',
' */'
].join('\n'));
}); });
test('03 Code fence, positive', () => { test('03 Code fence, positive', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * This is a code fence with all parts:', '/**',
' * ```a language! ', ' * This is a code fence with all parts:',
' * some `code` here', ' * ```a language! ',
' * ``` ', ' * some `code` here',
' */' ' * ``` ',
].join('\n')); ' */'
TestHelpers.parseAndMatchNodeParserSnapshot([ ].join('\n')
'/**', );
' * This is a code fence with no language or trailing whitespace:', TestHelpers.parseAndMatchNodeParserSnapshot(
' * ```', [
' * some `code` here', '/**',
' * ```*/' ' * This is a code fence with no language or trailing whitespace:',
].join('\n')); ' * ```',
' * some `code` here',
' * ```*/'
].join('\n')
);
}); });
test('04 Code fence, negative', () => { test('04 Code fence, negative', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * Code fence incorrectly indented:', ' * ```', ' */'].join('\n')
' * Code fence incorrectly indented:', );
' * ```', TestHelpers.parseAndMatchNodeParserSnapshot(
' */' ['/**', ' * Code fence not starting the line:', ' *a```', ' */'].join('\n')
].join('\n')); );
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * Code fence not being terminated 1:', ' * ```*/'].join('\n')
' * Code fence not starting the line:', );
' *a```', TestHelpers.parseAndMatchNodeParserSnapshot(
' */' ['/**', ' * Code fence not being terminated 2:', ' * ``` some stuff', ' */'].join('\n')
].join('\n')); );
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * Language having backticks:', ' * ``` some stuff ```', ' */'].join('\n')
' * Code fence not being terminated 1:', );
' * ```*/' TestHelpers.parseAndMatchNodeParserSnapshot(
].join('\n')); ['/**', ' * Closing delimiter being indented:', ' * ```', ' * code', ' * ```', ' */'].join('\n')
TestHelpers.parseAndMatchNodeParserSnapshot([ );
'/**', TestHelpers.parseAndMatchNodeParserSnapshot(
' * Code fence not being terminated 2:', [
' * ``` some stuff', '/**',
' */' ' * Closing delimiter not being on a line by itself:',
].join('\n')); ' * ```',
TestHelpers.parseAndMatchNodeParserSnapshot([ ' * code',
'/**', ' * ``` a',
' * Language having backticks:', ' */'
' * ``` some stuff ```', ].join('\n')
' */' );
].join('\n'));
TestHelpers.parseAndMatchNodeParserSnapshot([
'/**',
' * Closing delimiter being indented:',
' * ```',
' * code',
' * ```',
' */'
].join('\n'));
TestHelpers.parseAndMatchNodeParserSnapshot([
'/**',
' * Closing delimiter not being on a line by itself:',
' * ```',
' * code',
' * ``` a',
' */'
].join('\n'));
}); });

Просмотреть файл

@ -1,129 +1,101 @@
import { TestHelpers } from './TestHelpers'; import { TestHelpers } from './TestHelpers';
test('01 HTML start tags: simple, positive', () => { test('01 HTML start tags: simple, positive', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * <tag/>', '/**',
' * <tag-a />', ' * <tag/>',
' * <tag-b ><tag-c />', ' * <tag-a />',
' * <tag-d', ' * <tag-b ><tag-c />',
' * >', ' * <tag-d',
' * <tag-e', ' * >',
' * /> ', ' * <tag-e',
' */' ' * /> ',
].join('\n')); ' */'
].join('\n')
);
}); });
test('02 HTML start tags: simple, negative', () => { test('02 HTML start tags: simple, negative', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * < tag/>', ' * <tag -a />', ' * <tag-b /<tag-c / >', ' * <tag-d', ' */'].join('\n')
' * < tag/>', );
' * <tag -a />',
' * <tag-b /<tag-c / >',
' * <tag-d',
' */'
].join('\n'));
}); });
test('03 HTML start tags: with attributes, positive', () => { test('03 HTML start tags: with attributes, positive', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * <tag-a attr-one="one" >', ' * <tag-b', ' * attr-two', ' * = "2"', ' * />', ' */'].join(
' * <tag-a attr-one="one" >', '\n'
' * <tag-b', )
' * attr-two', );
' * = "2"', TestHelpers.parseAndMatchNodeParserSnapshot(
' * />', [
' */' '/**',
].join('\n')); ' * <tag-c attr-three="3" four=\'4\'/>',
TestHelpers.parseAndMatchNodeParserSnapshot([ ' * <tag-d',
'/**', ' * attr-five',
' * <tag-c attr-three="3" four=\'4\'/>', ' * = "5"',
' * <tag-d', ' * six',
' * attr-five', " * = '6'",
' * = "5"', ' * />',
' * six', ' */'
' * = \'6\'', ].join('\n')
' * />', );
' */' TestHelpers.parseAndMatchNodeParserSnapshot(
].join('\n')); [
TestHelpers.parseAndMatchNodeParserSnapshot([ '/**',
'/**', ' * <tag-e attr-one="one" two=\'two\'/>',
' * <tag-e attr-one="one" two=\'two\'/>', ' * <tag-f',
' * <tag-f', ' * attr-one',
' * attr-one', ' * = "one"',
' * = "one"', ' * two',
' * two', " * = 'two'",
' * = \'two\'', ' * />',
' * />', ' */'
' */' ].join('\n')
].join('\n')); );
}); });
test('04 HTML start tags: with attributes, negative', () => { test('04 HTML start tags: with attributes, negative', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * <tag-a attr -one="one" />', '/**',
' * <tag-b attr- two="two" />', ' * <tag-a attr -one="one" />',
' * <tag-c attr-three=\'three" />', ' * <tag-b attr- two="two" />',
' * <tag-d attr-four=@"four" />', ' * <tag-c attr-three=\'three" />',
' * <tag-e attr-five@="five" />', ' * <tag-d attr-four=@"four" />',
' * <tag-f attr-six="six"seven="seven" />', ' * <tag-e attr-five@="five" />',
' */' ' * <tag-f attr-six="six"seven="seven" />',
].join('\n')); ' */'
TestHelpers.parseAndMatchNodeParserSnapshot([ ].join('\n')
'/**', );
' * <tag-g attr="multi', TestHelpers.parseAndMatchNodeParserSnapshot(
' * line" />', ['/**', ' * <tag-g attr="multi', ' * line" />', ' */'].join('\n')
' */' );
].join('\n'));
}); });
test('05 Eclipsed TSDoc', () => { test('05 Eclipsed TSDoc', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * <tag attr-one="@tag" />', ' */'].join('\n'));
'/**',
' * <tag attr-one="@tag" />',
' */'
].join('\n'));
}); });
test('06 Closing tags, positive', () => { test('06 Closing tags, positive', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * </tag-a>', ' * </tag-b >', ' * </tag-c', ' * >', ' */'].join('\n')
' * </tag-a>', );
' * </tag-b >',
' * </tag-c',
' * >',
' */'
].join('\n'));
}); });
test('07 Closing tags, negative', () => { test('07 Closing tags, negative', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * </tag-a/>', ' * </ tag-b>', ' * </tag-c', ' */'].join('\n')
' * </tag-a/>', );
' * </ tag-b>',
' * </tag-c',
' */'
].join('\n'));
}); });
test('08 Unusual HTML names, positive', () => { test('08 Unusual HTML names, positive', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * <a1/>', ' * <a-a>', ' * <a--9->', ' */'].join('\n')
' * <a1/>', );
' * <a-a>',
' * <a--9->',
' */'
].join('\n'));
}); });
test('09 Unusual HTML names, negative', () => { test('09 Unusual HTML names, negative', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * <1a/>', ' * <a.a>', ' * <_a>', ' */'].join('\n'));
'/**',
' * <1a/>',
' * <a.a>',
' * <_a>',
' */'
].join('\n'));
}); });

Просмотреть файл

@ -1,52 +1,25 @@
import { TestHelpers } from './TestHelpers'; import { TestHelpers } from './TestHelpers';
test('00 InheritDoc tag: positive examples', () => { test('00 InheritDoc tag: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * {@inheritDoc}', ' */'].join('\n'));
'/**', TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * {@inheritDoc Class.member}', ' */'].join('\n'));
' * {@inheritDoc}', TestHelpers.parseAndMatchNodeParserSnapshot(
' */' ['/**', ' * {@inheritDoc package# Class . member}', ' */'].join('\n')
].join('\n')); );
TestHelpers.parseAndMatchNodeParserSnapshot([
'/**',
' * {@inheritDoc Class.member}',
' */'
].join('\n'));
TestHelpers.parseAndMatchNodeParserSnapshot([
'/**',
' * {@inheritDoc package# Class . member}',
' */'
].join('\n'));
}); });
test('01 InheritDoc tag: negative examples', () => { test('01 InheritDoc tag: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * {@inheritDoc | link text}', ' */'].join('\n'));
'/**', TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * {@inheritDoc Class % junk}', ' */'].join('\n'));
' * {@inheritDoc | link text}', TestHelpers.parseAndMatchNodeParserSnapshot(
' */' ['/**', ' * {@inheritDoc}', ' * {@inheritDoc}', ' */'].join('\n')
].join('\n')); );
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * summary text', ' * @remarks', ' * {@inheritDoc}', ' */'].join('\n')
' * {@inheritDoc Class % junk}', );
' */'
].join('\n'));
TestHelpers.parseAndMatchNodeParserSnapshot([
'/**',
' * {@inheritDoc}',
' * {@inheritDoc}',
' */'
].join('\n'));
TestHelpers.parseAndMatchNodeParserSnapshot([
'/**',
' * summary text',
' * @remarks',
' * {@inheritDoc}',
' */'
].join('\n'));
// Old API Extractor syntax // Old API Extractor syntax
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * {@inheritdoc @scope/library:IDisposable.isDisposed}', ' */'].join('\n')
' * {@inheritdoc @scope/library:IDisposable.isDisposed}', );
' */'
].join('\n'));
}); });

Просмотреть файл

@ -1,100 +1,96 @@
import { TestHelpers } from './TestHelpers'; import { TestHelpers } from './TestHelpers';
test('00 Link text: positive examples', () => { test('00 Link text: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * {@link http://example1.com}', '/**',
' * {@link http://example2.com|}', ' * {@link http://example1.com}',
' * {@link http://example3.com| }', ' * {@link http://example2.com|}',
' * {@link http://example4.com|link text}', ' * {@link http://example3.com| }',
' * 1{@link http://example5.com| link', ' * {@link http://example4.com|link text}',
' * text }2', ' * 1{@link http://example5.com| link',
' * 3{@link http://example5.com| ', ' * text }2',
' * link text ', ' * 3{@link http://example5.com| ',
' * }4', ' * link text ',
' */' ' * }4',
].join('\n')); ' */'
].join('\n')
);
}); });
test('01 Link text: negative examples', () => { test('01 Link text: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * {@link}', ' * {@link http://example1.com| link | text}', ' */'].join('\n')
' * {@link}', );
' * {@link http://example1.com| link | text}',
' */'
].join('\n'));
}); });
test('02 URL destination: positive examples', () => { test('02 URL destination: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * {@link http://example1.com}', '/**',
' * {@link https://example2.com#hash|link text}', ' * {@link http://example1.com}',
' * {@link customscheme://data}', ' * {@link https://example2.com#hash|link text}',
' */' ' * {@link customscheme://data}',
].join('\n')); ' */'
].join('\n')
);
}); });
test('03 URL destination: negative examples', () => { test('03 URL destination: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * {@link http://example1.com spaces}', '/**',
' * {@link http://example2.com spaces|link text}', ' * {@link http://example1.com spaces}',
' * {@link ftp+ssh://example3.com}', ' * {@link http://example2.com spaces|link text}',
' * {@link mailto:bob@example4.com}', ' * {@link ftp+ssh://example3.com}',
' * {@link //example5.com}', ' * {@link mailto:bob@example4.com}',
' * {@link http://}', ' * {@link //example5.com}',
' */' ' * {@link http://}',
].join('\n')); ' */'
].join('\n')
);
}); });
test('04 Declaration reference with package name: positive examples', () => { test('04 Declaration reference with package name: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * {@link my-example1#}', '/**',
' * {@link my-example2/path3#}', ' * {@link my-example1#}',
' * {@link my-example4/path5/path6#}', ' * {@link my-example2/path3#}',
' * {@link @scope/my-example7/path8/path9#}', ' * {@link my-example4/path5/path6#}',
' * {@link @scope/my-example7#}', ' * {@link @scope/my-example7/path8/path9#}',
' */' ' * {@link @scope/my-example7#}',
].join('\n')); ' */'
].join('\n')
);
}); });
test('05 Declaration reference with package name: negative examples', () => { test('05 Declaration reference with package name: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * {@link example1/#}', '/**',
' * {@link example2/a//b#}', ' * {@link example1/#}',
' * {@link @scope/ex@mple3#}', ' * {@link example2/a//b#}',
' * {@link @/example4#}', ' * {@link @scope/ex@mple3#}',
' * {@link @scope//my-example5#}', ' * {@link @/example4#}',
' * {@link @scope#}', ' * {@link @scope//my-example5#}',
' */' ' * {@link @scope#}',
].join('\n')); ' */'
TestHelpers.parseAndMatchNodeParserSnapshot([ ].join('\n')
'/**', );
' * {@link @#}', TestHelpers.parseAndMatchNodeParserSnapshot(
' * {@link #}', ['/**', ' * {@link @#}', ' * {@link #}', ' * {@link #Button}', ' */'].join('\n')
' * {@link #Button}', );
' */'
].join('\n'));
}); });
test('06 Declaration reference with import path only: positive examples', () => { test('06 Declaration reference with import path only: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * {@link ../path1#}', ' * {@link ./path2#}', ' * {@link ./path3/../path4#}', ' */'].join('\n')
' * {@link ../path1#}', );
' * {@link ./path2#}',
' * {@link ./path3/../path4#}',
' */'
].join('\n'));
}); });
test('07 Declaration reference with import path only: negative examples', () => { test('07 Declaration reference with import path only: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * {@link /path1#}', ' * {@link /path1 path2#}', ' */'].join('\n')
' * {@link /path1#}', );
' * {@link /path1 path2#}',
' */'
].join('\n'));
}); });

Просмотреть файл

@ -1,118 +1,102 @@
import { TestHelpers } from './TestHelpers'; import { TestHelpers } from './TestHelpers';
test('00 Simple member references: positive examples', () => { test('00 Simple member references: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * {@link Class1}', '/**',
' * {@link Class2.member2}', ' * {@link Class1}',
' * {@link namespace3 . namespace4 ', ' * {@link Class2.member2}',
' * . namespace5 | link text}', ' * {@link namespace3 . namespace4 ',
' */' ' * . namespace5 | link text}',
].join('\n')); ' */'
].join('\n')
);
}); });
test('01 Simple member references: negative examples', () => { test('01 Simple member references: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * {@link Class1..member2}', ' * {@link .member3}', ' * {@link member4.}', ' */'].join('\n')
' * {@link Class1..member2}', );
' * {@link .member3}',
' * {@link member4.}',
' */'
].join('\n'));
}); });
test('02 System selectors: positive examples', () => { test('02 System selectors: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * {@link (Class1:class)}', '/**',
' * {@link (Class2:class).(member3:static)}', ' * {@link (Class1:class)}',
' * {@link Class4.(member5:static)}', ' * {@link (Class2:class).(member3:static)}',
' * {@link (Class6:class ) . ( member7:static)}', ' * {@link Class4.(member5:static)}',
' */' ' * {@link (Class6:class ) . ( member7:static)}',
].join('\n')); ' */'
].join('\n')
);
}); });
test('03 System selectors: negative examples', () => { test('03 System selectors: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * {@link (Class1:class}', '/**',
' * {@link (Class2:class))}', ' * {@link (Class1:class}',
' * {@link (Class3::class)}', ' * {@link (Class2:class))}',
' * {@link (Class4 class)}', ' * {@link (Class3::class)}',
' * {@link (member5:badname)}', ' * {@link (Class4 class)}',
' * {@link (Class6:class)(member:static)}', ' * {@link (member5:badname)}',
' * {@link Class7:class}', ' * {@link (Class6:class)(member:static)}',
' */' ' * {@link Class7:class}',
].join('\n')); ' */'
].join('\n')
);
}); });
test('04 Label selectors: positive examples', () => { test('04 Label selectors: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * {@link (Class1:LABEL1)}', ' * {@link ( function2 : MY_LABEL2 ) }', ' */'].join('\n')
' * {@link (Class1:LABEL1)}', );
' * {@link ( function2 : MY_LABEL2 ) }',
' */'
].join('\n'));
}); });
test('05 Label selectors: negative examples', () => { test('05 Label selectors: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', ['/**', ' * {@link (Class1:Label)}', ' * {@link (Class2:SPAß)}', ' */'].join('\n')
' * {@link (Class1:Label)}', );
' * {@link (Class2:SPAß)}',
' */'
].join('\n'));
}); });
test('06 Index selectors: positive examples', () => { test('06 Index selectors: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * {@link (function2 : 3 )}', ' */'].join('\n'));
'/**',
' * {@link (function2 : 3 )}',
' */'
].join('\n'));
}); });
test('07 Index selectors: negative examples', () => { test('07 Index selectors: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * {@link (function2:03)}', ' */'].join('\n'));
'/**',
' * {@link (function2:03)}',
' */'
].join('\n'));
}); });
test('08 Unusual identifiers: positive examples', () => { test('08 Unusual identifiers: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * {@link Class$_1 . $_1member}', ' */'].join('\n'));
'/**',
' * {@link Class$_1 . $_1member}',
' */'
].join('\n'));
}); });
test('09 Unusual identifiers: negative examples', () => { test('09 Unusual identifiers: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(['/**', ' * {@link Class-1}', ' */'].join('\n'));
'/**',
' * {@link Class-1}',
' */'
].join('\n'));
}); });
test('10 Quoted identifiers: positive examples', () => { test('10 Quoted identifiers: positive examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * {@link "static"}', '/**',
' * {@link Class1 . "member"}', ' * {@link "static"}',
' * {@link Class2."|" | link text}', ' * {@link Class1 . "member"}',
' */' ' * {@link Class2."|" | link text}',
].join('\n')); ' */'
].join('\n')
);
}); });
test('11 Quoted identifiers: negative examples', () => { test('11 Quoted identifiers: negative examples', () => {
TestHelpers.parseAndMatchNodeParserSnapshot([ TestHelpers.parseAndMatchNodeParserSnapshot(
'/**', [
' * {@link "static}', '/**',
' * {@link Class1.""}', ' * {@link "static}',
' * {@link Class2.interface}', ' * {@link Class1.""}',
' * {@link Class3.1}', ' * {@link Class2.interface}',
' */' ' * {@link Class3.1}',
].join('\n')); ' */'
].join('\n')
);
}); });

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше