feat(compiler): initial skeleton for API doc extraction (#51733)

This commit adds a barebones skeleton for extracting information to be used for extracting info that can be used for API reference generation. Subsequent PRs will expand on this with increasingly real extraction. I started with @alxhub's #51615 and very slightly polished to get to this minimal commit.

PR Close #51733
This commit is contained in:
Jeremy Elbourn 2023-09-06 12:09:31 -07:00 committed by Pawel Kozlowski
parent 9bd1551c3f
commit 7e82df45c5
11 changed files with 169 additions and 0 deletions

View file

@ -75,6 +75,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/core",
"//packages/compiler-cli/src/ngtsc/core:api",
"//packages/compiler-cli/src/ngtsc/diagnostics",
"//packages/compiler-cli/src/ngtsc/docs",
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/incremental",
"//packages/compiler-cli/src/ngtsc/indexer",

View file

@ -16,6 +16,7 @@ ts_library(
"//packages/compiler-cli/src/ngtsc/annotations/common",
"//packages/compiler-cli/src/ngtsc/cycles",
"//packages/compiler-cli/src/ngtsc/diagnostics",
"//packages/compiler-cli/src/ngtsc/docs",
"//packages/compiler-cli/src/ngtsc/entry_point",
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/imports",

View file

@ -12,6 +12,7 @@ import {ComponentDecoratorHandler, DirectiveDecoratorHandler, InjectableDecorato
import {InjectableClassRegistry} from '../../annotations/common';
import {CycleAnalyzer, CycleHandlingStrategy, ImportGraph} from '../../cycles';
import {COMPILER_ERRORS_WITH_GUIDES, ERROR_DETAILS_PAGE_BASE_URL, ErrorCode, FatalDiagnosticError, ngErrorCode} from '../../diagnostics';
import {DocEntry, DocsExtractor} from '../../docs';
import {checkForPrivateExports, ReferenceGraph} from '../../entry_point';
import {absoluteFromSourceFile, AbsoluteFsPath, LogicalFileSystem, resolve} from '../../file_system';
import {AbsoluteModuleStrategy, AliasingHost, AliasStrategy, DefaultImportTracker, DeferredSymbolTracker, ImportRewriter, LocalIdentifierStrategy, LogicalProjectStrategy, ModuleResolver, NoopImportRewriter, PrivateExportAliasingHost, R3SymbolsImportRewriter, Reference, ReferenceEmitStrategy, ReferenceEmitter, RelativePathStrategy, UnifiedModulesAliasingHost, UnifiedModulesStrategy} from '../../imports';
@ -656,6 +657,26 @@ export class NgCompiler {
return generateAnalysis(context);
}
/**
* Gets information for the current program that may be used to generate API
* reference documentation. This includes Angular-specific information, such
* as component inputs and outputs.
*/
getApiDocumentation(): DocEntry[] {
const compilation = this.ensureAnalyzed();
const checker = this.inputProgram.getTypeChecker();
const docsExtractor = new DocsExtractor(checker, compilation.metaReader);
let entries: DocEntry[] = [];
for (const sourceFile of this.inputProgram.getSourceFiles()) {
// We don't want to generate docs for `.d.ts` files.
if (sourceFile.isDeclarationFile) continue;
entries.push(...docsExtractor.extract(sourceFile));
}
return entries;
}
/**
* Collect i18n messages into the `Xi18nContext`.
*/

View file

@ -0,0 +1,18 @@
load("//tools:defaults.bzl", "ts_library")
package(default_visibility = ["//visibility:public"])
# Compiler code pertaining to extracting data for generating API reference documentation.
ts_library(
name = "docs",
srcs = ["index.ts"] + glob([
"src/**/*.ts",
]),
module_name = "@angular/compiler-cli/src/ngtsc/docs",
deps = [
"//packages/compiler-cli/src/ngtsc/metadata",
"//packages/compiler-cli/src/ngtsc/util",
"@npm//@types/node",
"@npm//typescript",
],
)

View file

@ -0,0 +1,10 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
export {DocEntry} from './src/entities';
export {DocsExtractor} from './src/extractor';

View file

@ -0,0 +1,12 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
/** Base type for all documentation entities. */
export interface DocEntry {
name: string;
}

View file

@ -0,0 +1,48 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import ts from 'typescript';
import {MetadataReader} from '../../metadata';
import {DocEntry} from './entities';
/**
* Extracts all information from a source file that may be relevant for generating
* public API documentation.
*/
export class DocsExtractor {
constructor(private checker: ts.TypeChecker, private reader: MetadataReader) {}
/**
* Gets the set of all documentable entries from a source file.
* @param sourceFile The file from which to extract documentable entries.
*/
extract(sourceFile: ts.SourceFile): DocEntry[] {
let entries: DocEntry[] = [];
for (const statement of sourceFile.statements) {
// TODO(jelbourn): get all of rest of the docs
if (ts.isClassDeclaration(statement)) {
// Assume that anonymous classes should not be part of public documentation.
if (!statement.name) continue;
entries = entries.concat(this.extractClassDocs(statement));
}
}
return entries;
}
/** Extract docs info specific to classes. */
private extractClassDocs(statement: ts.ClassDeclaration): DocEntry {
// TODO(jelbourn): get all of the rest of the docs
return {name: statement.name!.text};
}
}

View file

@ -15,6 +15,7 @@ import {verifySupportedTypeScriptVersion} from '../typescript_support';
import {CompilationTicket, freshCompilationTicket, incrementalFromCompilerTicket, NgCompiler, NgCompilerHost} from './core';
import {NgCompilerOptions} from './core/api';
import {DocEntry} from './docs';
import {absoluteFrom, AbsoluteFsPath, getFileSystem, resolve} from './file_system';
import {TrackedIncrementalBuildStrategy} from './incremental';
import {IndexedComponent} from './indexer';
@ -347,6 +348,15 @@ export class NgtscProgram implements api.Program {
return this.compiler.getIndexedComponents();
}
/**
* Gets information for the current program that may be used to generate API
* reference documentation. This includes Angular-specific information, such
* as component inputs and outputs.
*/
getApiDocumentation(): DocEntry[] {
return this.compiler.getApiDocumentation();
}
getEmittedSourceFiles(): Map<string, ts.SourceFile> {
throw new Error('Method not implemented.');
}

View file

@ -9,6 +9,7 @@ ts_library(
"//packages/compiler-cli",
"//packages/compiler-cli/src/ngtsc/core:api",
"//packages/compiler-cli/src/ngtsc/diagnostics",
"//packages/compiler-cli/src/ngtsc/docs",
"//packages/compiler-cli/src/ngtsc/file_system",
"//packages/compiler-cli/src/ngtsc/file_system/testing",
"//packages/compiler-cli/src/ngtsc/indexer",

View file

@ -0,0 +1,39 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {DocEntry} from '@angular/compiler-cli/src/ngtsc/docs';
import {runInEachFileSystem} from '@angular/compiler-cli/src/ngtsc/file_system/testing';
import {loadStandardTestFiles} from '@angular/compiler-cli/src/ngtsc/testing';
import {NgtscTestEnvironment} from './env';
const testFiles = loadStandardTestFiles({fakeCore: true, fakeCommon: true});
runInEachFileSystem(os => {
let env!: NgtscTestEnvironment;
describe('ngtsc docs extraction', () => {
beforeEach(() => {
env = NgtscTestEnvironment.setup(testFiles);
env.tsconfig();
});
it('should extract classes', () => {
env.write('test.ts', `
class UserProfile {}
class CustomSlider {}
`);
const docs: DocEntry[] = env.driveDocsExtraction();
expect(docs.length).toBe(2);
expect(docs[0].name).toBe('UserProfile');
expect(docs[1].name).toBe('CustomSlider');
});
});
});

View file

@ -7,6 +7,7 @@
*/
import {CustomTransformers, defaultGatherDiagnostics, Program} from '@angular/compiler-cli';
import {DocEntry} from '@angular/compiler-cli/src/ngtsc/docs';
import * as api from '@angular/compiler-cli/src/transformers/api';
import ts from 'typescript';
@ -286,6 +287,13 @@ export class NgtscTestEnvironment {
return (program as NgtscProgram).getIndexedComponents();
}
driveDocsExtraction(): DocEntry[] {
const {rootNames, options} = readNgcCommandLineAndConfiguration(this.commandLineArgs);
const host = createCompilerHost({options});
const program = createProgram({rootNames, host, options});
return (program as NgtscProgram).getApiDocumentation();
}
driveXi18n(format: string, outputFileName: string, locale: string|null = null): void {
const errorSpy = jasmine.createSpy('consoleError').and.callFake(console.error);
const args = [