build(docs-infra): implement the 'package' API template (#24631)

PR Close #24631
This commit is contained in:
Pete Bacon Darwin 2018-06-22 16:58:29 +01:00 committed by Matias Niemelä
parent 97277bc9fb
commit d8c828c9b1
23 changed files with 399 additions and 59 deletions

View File

@ -21,7 +21,7 @@
<article class="api-list-container l-content-small docs-content"> <article class="api-list-container l-content-small docs-content">
<div *ngFor="let section of filteredSections | async" > <div *ngFor="let section of filteredSections | async" >
<h2>{{section.title}}</h2> <h2><a [href]="section.path">{{section.title}}</a></h2>
<ul class="api-list"> <ul class="api-list">
<ng-container *ngFor="let item of section.items"> <ng-container *ngFor="let item of section.items">
<li *ngIf="item.show" class="api-item"> <li *ngIf="item.show" class="api-item">

View File

@ -218,6 +218,7 @@ const apiSections: ApiSection[] = [
{ {
"name": "common", "name": "common",
"title": "common", "title": "common",
"path": "api/common",
"items": [ "items": [
{ {
"name": "class_1", "name": "class_1",
@ -256,6 +257,7 @@ const apiSections: ApiSection[] = [
{ {
"name": "core", "name": "core",
"title": "core", "title": "core",
"path": "api/core",
"items": [ "items": [
{ {
"name": "class_3", "name": "class_3",

View File

@ -50,7 +50,7 @@ describe('ApiService', () => {
describe('#sections', () => { describe('#sections', () => {
it('first subscriber should fetch sections', done => { it('first subscriber should fetch sections', done => {
const data = [{name: 'a', title: 'A', items: []}, {name: 'b', title: 'B', items: []}]; const data = [{name: 'a', title: 'A', path: '', items: []}, {name: 'b', title: 'B', path: '', items: []}];
service.sections.subscribe(sections => { service.sections.subscribe(sections => {
expect(sections).toEqual(data); expect(sections).toEqual(data);
@ -61,7 +61,7 @@ describe('ApiService', () => {
}); });
it('second subscriber should get previous sections and NOT trigger refetch', done => { it('second subscriber should get previous sections and NOT trigger refetch', done => {
const data = [{name: 'a', title: 'A', items: []}, {name: 'b', title: 'B', items: []}]; const data = [{name: 'a', title: 'A', path: '', items: []}, {name: 'b', title: 'B', path: '', items: []}];
let subscriptions = 0; let subscriptions = 0;
service.sections.subscribe(sections => { service.sections.subscribe(sections => {
@ -91,7 +91,7 @@ describe('ApiService', () => {
let call = 0; let call = 0;
let data = [{name: 'a', title: 'A', items: []}, {name: 'b', title: 'B', items: []}]; let data = [{name: 'a', title: 'A', path: '', items: []}, {name: 'b', title: 'B', path: '', items: []}];
service.sections.subscribe(sections => { service.sections.subscribe(sections => {
// called twice during this test // called twice during this test
@ -103,7 +103,7 @@ describe('ApiService', () => {
httpMock.expectOne({}).flush(data); httpMock.expectOne({}).flush(data);
// refresh/refetch // refresh/refetch
data = [{name: 'c', title: 'C', items: []}]; data = [{name: 'c', title: 'C', path: '', items: []}];
service.fetchSections(); service.fetchSections();
httpMock.expectOne({}).flush(data); httpMock.expectOne({}).flush(data);

View File

@ -19,6 +19,7 @@ export interface ApiItem {
} }
export interface ApiSection { export interface ApiSection {
path: string;
name: string; name: string;
title: string; title: string;
items: ApiItem[]; items: ApiItem[];

View File

@ -28,7 +28,7 @@ h2 {
h3 { h3 {
font-size: 20px; font-size: 20px;
font-weight: 400; font-weight: 400;
margin: 24px 0px; margin: 24px 0px 12px;
clear: both; clear: both;
} }
@ -55,6 +55,10 @@ h6 {
} }
h2, h3, h4, h5, h6 { h2, h3, h4, h5, h6 {
a {
font-size: inherit;
}
@media screen and (max-width: 600px) { @media screen and (max-width: 600px) {
margin: 8px 0; margin: 8px 0;
} }
@ -105,7 +109,7 @@ table {
border-collapse: collapse; border-collapse: collapse;
border-radius: 2px; border-radius: 2px;
border-spacing: 0; border-spacing: 0;
margin: 0 0 32px 0; margin: 12px 0 32px;
} }
table tbody th { table tbody th {

View File

@ -3,6 +3,7 @@
max-width: 1200px; max-width: 1200px;
table { table {
margin: 12px 0 24px;
th { th {
text-transform: none; text-transform: none;

View File

@ -30,7 +30,7 @@
} }
} }
.method-table, .option-table { .method-table, .option-table, .list-table {
th { th {
display: flex; display: flex;
align-items: center; align-items: center;

View File

@ -105,7 +105,7 @@ $api-symbols: (
content: 'T', content: 'T',
background: $light-green-600 background: $light-green-600
), ),
module: ( package: (
content: 'Pk', content: 'Pk',
background: $purple-600 background: $purple-600
) )

View File

@ -33,6 +33,7 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
.processor(require('./processors/simplifyMemberAnchors')) .processor(require('./processors/simplifyMemberAnchors'))
.processor(require('./processors/computeStability')) .processor(require('./processors/computeStability'))
.processor(require('./processors/removeInjectableConstructors')) .processor(require('./processors/removeInjectableConstructors'))
.processor(require('./processors/processPackages'))
/** /**
* These are the API doc types that will be rendered to actual files. * These are the API doc types that will be rendered to actual files.
@ -40,7 +41,7 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
* more Angular specific API types, such as decorators and directives. * more Angular specific API types, such as decorators and directives.
*/ */
.factory(function API_DOC_TYPES_TO_RENDER(EXPORT_DOC_TYPES) { .factory(function API_DOC_TYPES_TO_RENDER(EXPORT_DOC_TYPES) {
return EXPORT_DOC_TYPES.concat(['decorator', 'directive', 'pipe', 'module']); return EXPORT_DOC_TYPES.concat(['decorator', 'directive', 'pipe', 'package']);
}) })
/** /**
@ -58,8 +59,10 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
return API_DOC_TYPES_TO_RENDER.concat(API_CONTAINED_DOC_TYPES); return API_DOC_TYPES_TO_RENDER.concat(API_CONTAINED_DOC_TYPES);
}) })
.factory(require('./readers/package-content'))
// Where do we get the source files? // Where do we get the source files?
.config(function(readTypeScriptModules, readFilesProcessor, collectExamples, tsParser) { .config(function(readTypeScriptModules, readFilesProcessor, collectExamples, tsParser, packageContentFileReader) {
// Tell TypeScript how to load modules that start with with `@angular` // Tell TypeScript how to load modules that start with with `@angular`
tsParser.options.paths = { '@angular/*': [API_SOURCE_PATH + '/*'] }; tsParser.options.paths = { '@angular/*': [API_SOURCE_PATH + '/*'] };
@ -102,12 +105,19 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
'upgrade/static/index.ts', 'upgrade/static/index.ts',
]; ];
readFilesProcessor.fileReaders.push(packageContentFileReader);
// API Examples // API Examples
readFilesProcessor.sourceFiles = [ readFilesProcessor.sourceFiles = [
{ {
basePath: API_SOURCE_PATH, basePath: API_SOURCE_PATH,
include: API_SOURCE_PATH + '/examples/**/*', include: API_SOURCE_PATH + '/examples/**/*',
fileReader: 'exampleFileReader' fileReader: 'exampleFileReader'
},
{
basePath: API_SOURCE_PATH,
include: API_SOURCE_PATH + '/**/PACKAGE.md',
fileReader: 'packageContentFileReader'
} }
]; ];
collectExamples.exampleFolders.push('examples'); collectExamples.exampleFolders.push('examples');
@ -123,7 +133,7 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
.config(function(computeStability, splitDescription, addNotYetDocumentedProperty, API_DOC_TYPES_TO_RENDER, API_DOC_TYPES) { .config(function(computeStability, splitDescription, addNotYetDocumentedProperty, API_DOC_TYPES_TO_RENDER, API_DOC_TYPES) {
computeStability.docTypes = API_DOC_TYPES_TO_RENDER; computeStability.docTypes = API_DOC_TYPES_TO_RENDER;
// Only split the description on the API docs // Only split the description on the API docs
splitDescription.docTypes = API_DOC_TYPES; splitDescription.docTypes = API_DOC_TYPES.concat(['package-content']);
addNotYetDocumentedProperty.docTypes = API_DOC_TYPES; addNotYetDocumentedProperty.docTypes = API_DOC_TYPES;
}) })
@ -180,7 +190,7 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
generateApiListDoc.outputFolder = API_SEGMENT; generateApiListDoc.outputFolder = API_SEGMENT;
computePathsProcessor.pathTemplates.push({ computePathsProcessor.pathTemplates.push({
docTypes: ['module'], docTypes: ['package'],
getPath: function computeModulePath(doc) { getPath: function computeModulePath(doc) {
doc.moduleFolder = `${API_SEGMENT}/${doc.id.replace(/\/index$/, '')}`; doc.moduleFolder = `${API_SEGMENT}/${doc.id.replace(/\/index$/, '')}`;
return doc.moduleFolder; return doc.moduleFolder;

View File

@ -33,7 +33,7 @@ describe('addSelectorsAsAliases processor', () => {
{ docType: 'directive', name: 'NgModel', aliases: ['NgModel'], directiveOptions: { selector: '\'[ngModel]:not([formControlName]):not([formControl])\'' } }, { docType: 'directive', name: 'NgModel', aliases: ['NgModel'], directiveOptions: { selector: '\'[ngModel]:not([formControlName]):not([formControl])\'' } },
{ docType: 'component', name: 'MyComponent', aliases: ['MyComponent'], componentOptions: { selector: '\'my-component\'' } }, { docType: 'component', name: 'MyComponent', aliases: ['MyComponent'], componentOptions: { selector: '\'my-component\'' } },
{ docType: 'decorator', name: 'MyDecorator', aliases: ['MyDecorator'] }, { docType: 'decorator', name: 'MyDecorator', aliases: ['MyDecorator'] },
{ docType: 'module', name: 'myModule', aliases: ['myModule'], id: 'some/myModule' }, { docType: 'package', name: 'myPackage', aliases: ['myPackage'], id: 'some/myPackage' },
{ docType: 'var', name: 'myVar', aliases: ['myVar'] }, { docType: 'var', name: 'myVar', aliases: ['myVar'] },
{ docType: 'let', name: 'myLet', aliases: ['myLet'] }, { docType: 'let', name: 'myLet', aliases: ['myLet'] },
{ docType: 'const', name: 'myConst', aliases: ['myConst'] }, { docType: 'const', name: 'myConst', aliases: ['myConst'] },

View File

@ -14,14 +14,14 @@ describe('angular-api-package: computeApiBreadCrumbs processor', () => {
}); });
it('should attach a breadCrumbs property to each of the API_DOC_TYPES_TO_RENDER docs', () => { it('should attach a breadCrumbs property to each of the API_DOC_TYPES_TO_RENDER docs', () => {
const API_DOC_TYPES_TO_RENDER = ['class', 'interface', 'module']; const API_DOC_TYPES_TO_RENDER = ['class', 'interface', 'package'];
const processor = processorFactory(API_DOC_TYPES_TO_RENDER); const processor = processorFactory(API_DOC_TYPES_TO_RENDER);
const docs = [ const docs = [
{ docType: 'class', name: 'ClassA', path: 'module-1/class-a', moduleDoc: { id: 'moduleOne', path: 'module-1' } }, { docType: 'class', name: 'ClassA', path: 'module-1/class-a', moduleDoc: { id: 'moduleOne', path: 'module-1' } },
{ docType: 'interface', name: 'InterfaceB', path: 'module-2/interface-b', moduleDoc: { id: 'moduleTwo', path: 'module-2' } }, { docType: 'interface', name: 'InterfaceB', path: 'module-2/interface-b', moduleDoc: { id: 'moduleTwo', path: 'module-2' } },
{ docType: 'guide', name: 'Guide One', path: 'guide/guide-1' }, { docType: 'guide', name: 'Guide One', path: 'guide/guide-1' },
{ docType: 'module', name: 'testing', id: 'http/testing', path: 'http/testing' }, { docType: 'package', name: 'testing', id: 'http/testing', path: 'http/testing' },
]; ];
processor.$process(docs); processor.$process(docs);

View File

@ -8,7 +8,7 @@ module.exports = function computeSearchTitleProcessor() {
case 'function': case 'function':
doc.searchTitle = `${doc.name}()`; doc.searchTitle = `${doc.name}()`;
break; break;
case 'module': case 'package':
doc.searchTitle = `${doc.id} package`; doc.searchTitle = `${doc.id} package`;
break; break;
} }

View File

@ -31,7 +31,7 @@ describe('computeSearchTitle processor', () => {
{ docType: 'pipe', name: 'MyPipe', pipeOptions: { name: 'myPipe' } }, { docType: 'pipe', name: 'MyPipe', pipeOptions: { name: 'myPipe' } },
{ docType: 'directive', name: 'MyDirective', directiveOptions: {} }, { docType: 'directive', name: 'MyDirective', directiveOptions: {} },
{ docType: 'decorator', name: 'MyDecorator' }, { docType: 'decorator', name: 'MyDecorator' },
{ docType: 'module', name: 'myModule', id: 'some/myModule' }, { docType: 'package', name: 'myPackage', id: 'some/myPackage' },
{ docType: 'var', name: 'myVar' }, { docType: 'var', name: 'myVar' },
{ docType: 'let', name: 'myLet' }, { docType: 'let', name: 'myLet' },
{ docType: 'const', name: 'myConst' }, { docType: 'const', name: 'myConst' },
@ -45,7 +45,7 @@ describe('computeSearchTitle processor', () => {
expect(docs[4].searchTitle).toBeUndefined(); expect(docs[4].searchTitle).toBeUndefined();
expect(docs[5].searchTitle).toBeUndefined(); expect(docs[5].searchTitle).toBeUndefined();
expect(docs[6].searchTitle).toBeUndefined(); expect(docs[6].searchTitle).toBeUndefined();
expect(docs[7].searchTitle).toEqual('some/myModule package'); expect(docs[7].searchTitle).toEqual('some/myPackage package');
expect(docs[8].searchTitle).toBeUndefined(); expect(docs[8].searchTitle).toBeUndefined();
expect(docs[9].searchTitle).toBeUndefined(); expect(docs[9].searchTitle).toBeUndefined();
expect(docs[10].searchTitle).toBeUndefined(); expect(docs[10].searchTitle).toBeUndefined();

View File

@ -12,19 +12,20 @@ module.exports = function generateApiListDoc() {
path: this.outputFolder + '/api-list.json', path: this.outputFolder + '/api-list.json',
outputPath: this.outputFolder + '/api-list.json', outputPath: this.outputFolder + '/api-list.json',
data: docs data: docs
.filter(doc => doc.docType === 'module') .filter(doc => doc.docType === 'package')
.map(getModuleInfo) .map(getPackageInfo)
}); });
} }
}; };
}; };
function getModuleInfo(moduleDoc) { function getPackageInfo(packageDoc) {
const moduleName = moduleDoc.id.replace(/\/index$/, ''); const packageName = packageDoc.id.replace(/\/index$/, '');
return { return {
name: moduleName.toLowerCase(), name: packageName.toLowerCase(),
title: moduleName, title: packageName,
items: moduleDoc.exports path: packageDoc.path,
items: packageDoc.exports
// Ignore internals and private exports (indicated by the ɵ prefix) // Ignore internals and private exports (indicated by the ɵ prefix)
.filter(doc => !doc.internal && !doc.privateExport) .filter(doc => !doc.internal && !doc.privateExport)
.map(getExportInfo) .map(getExportInfo)

View File

@ -38,28 +38,28 @@ describe('generateApiListDoc processor', () => {
it('should add an info object to the doc for each module doc', () => { it('should add an info object to the doc for each module doc', () => {
const processor = processorFactory(); const processor = processorFactory();
const docs = [ const docs = [
{ docType: 'module', id: '@angular/common/index', exports: [] }, { docType: 'package', id: '@angular/common/index', exports: [], path: 'common' },
{ docType: 'module', id: '@angular/core/index', exports: [] }, { docType: 'package', id: '@angular/core/index', exports: [], path: 'core' },
{ docType: 'module', id: '@angular/http/index', exports: [] }, { docType: 'package', id: '@angular/http/index', exports: [], path: 'http' },
]; ];
processor.$process(docs); processor.$process(docs);
expect(docs[3].data).toEqual([ expect(docs[3].data).toEqual([
{ name: '@angular/common', title: '@angular/common', items: [] }, { name: '@angular/common', title: '@angular/common', items: [], path: 'common' },
{ name: '@angular/core', title: '@angular/core', items: [] }, { name: '@angular/core', title: '@angular/core', items: [], path: 'core' },
{ name: '@angular/http', title: '@angular/http', items: [] }, { name: '@angular/http', title: '@angular/http', items: [], path: 'http' },
]); ]);
}); });
it('should add info about each export on each module', () => { it('should add info about each export on each module', () => {
const processor = processorFactory(); const processor = processorFactory();
const docs = [ const docs = [
{ docType: 'module', id: '@angular/common/index', exports: [ { docType: 'package', id: '@angular/common/index', exports: [
{ docType: 'directive', name: 'AaaAaa', path: 'aaa' }, { docType: 'directive', name: 'AaaAaa', path: 'aaa' },
{ docType: 'pipe', name: 'BbbBbb', path: 'bbb' }, { docType: 'pipe', name: 'BbbBbb', path: 'bbb' },
{ docType: 'decorator', name: 'CccCcc', path: 'ccc' }, { docType: 'decorator', name: 'CccCcc', path: 'ccc' },
{ docType: 'class', name: 'DddDdd', path: 'ddd' } { docType: 'class', name: 'DddDdd', path: 'ddd' }
] }, ] },
{ docType: 'module', id: '@angular/core/index', exports: [ { docType: 'package', id: '@angular/core/index', exports: [
{ docType: 'interface', name: 'EeeEee', path: 'eee' }, { docType: 'interface', name: 'EeeEee', path: 'eee' },
{ docType: 'function', name: 'FffFff', path: 'fff' }, { docType: 'function', name: 'FffFff', path: 'fff' },
{ docType: 'enum', name: 'GggGgg', path: 'ggg' }, { docType: 'enum', name: 'GggGgg', path: 'ggg' },
@ -86,7 +86,7 @@ describe('generateApiListDoc processor', () => {
it('should ignore internal and private exports', () => { it('should ignore internal and private exports', () => {
const processor = processorFactory(); const processor = processorFactory();
const docs = [ const docs = [
{ docType: 'module', id: '@angular/common/index', exports: [ { docType: 'package', id: '@angular/common/index', exports: [
{ docType: 'directive', name: 'AaaAaa', path: 'aaa', internal: true }, { docType: 'directive', name: 'AaaAaa', path: 'aaa', internal: true },
{ docType: 'class', name: 'XxxXxx', path: 'xxx', privateExport: true }, { docType: 'class', name: 'XxxXxx', path: 'xxx', privateExport: true },
{ docType: 'pipe', name: 'BbbBbb', path: 'bbb' } { docType: 'pipe', name: 'BbbBbb', path: 'bbb' }
@ -101,7 +101,7 @@ describe('generateApiListDoc processor', () => {
it('should convert `let` and `var` docTypes to `const`', () => { it('should convert `let` and `var` docTypes to `const`', () => {
const processor = processorFactory(); const processor = processorFactory();
const docs = [ const docs = [
{ docType: 'module', id: '@angular/common/index', exports: [ { docType: 'package', id: '@angular/common/index', exports: [
{ docType: 'var', name: 'AaaAaa', path: 'aaa' }, { docType: 'var', name: 'AaaAaa', path: 'aaa' },
{ docType: 'let', name: 'BbbBbb', path: 'bbb' }, { docType: 'let', name: 'BbbBbb', path: 'bbb' },
]} ]}
@ -116,7 +116,7 @@ describe('generateApiListDoc processor', () => {
it('should convert security to a boolean securityRisk', () => { it('should convert security to a boolean securityRisk', () => {
const processor = processorFactory(); const processor = processorFactory();
const docs = [ const docs = [
{ docType: 'module', id: '@angular/common/index', exports: [ { docType: 'package', id: '@angular/common/index', exports: [
{ docType: 'class', name: 'AaaAaa', path: 'aaa', security: 'This is a security risk' }, { docType: 'class', name: 'AaaAaa', path: 'aaa', security: 'This is a security risk' },
{ docType: 'class', name: 'BbbBbb', path: 'bbb', security: '' }, { docType: 'class', name: 'BbbBbb', path: 'bbb', security: '' },
]} ]}
@ -131,7 +131,7 @@ describe('generateApiListDoc processor', () => {
it('should convert stability tags to the stable string property', () => { it('should convert stability tags to the stable string property', () => {
const processor = processorFactory(); const processor = processorFactory();
const docs = [ const docs = [
{ docType: 'module', id: '@angular/common/index', exports: [ { docType: 'package', id: '@angular/common/index', exports: [
{ docType: 'class', name: 'AaaAaa', path: 'aaa', stable: undefined }, { docType: 'class', name: 'AaaAaa', path: 'aaa', stable: undefined },
{ docType: 'class', name: 'BbbBbb', path: 'bbb', experimental: 'Some message' }, { docType: 'class', name: 'BbbBbb', path: 'bbb', experimental: 'Some message' },
{ docType: 'class', name: 'CccCcc', path: 'ccc', deprecated: null }, { docType: 'class', name: 'CccCcc', path: 'ccc', deprecated: null },
@ -150,7 +150,7 @@ describe('generateApiListDoc processor', () => {
it('should sort items in each group alphabetically', () => { it('should sort items in each group alphabetically', () => {
const processor = processorFactory(); const processor = processorFactory();
const docs = [ const docs = [
{ docType: 'module', id: '@angular/common/index', exports: [ { docType: 'package', id: '@angular/common/index', exports: [
{ docType: 'class', name: 'DddDdd', path: 'uuu' }, { docType: 'class', name: 'DddDdd', path: 'uuu' },
{ docType: 'class', name: 'BbbBbb', path: 'vvv' }, { docType: 'class', name: 'BbbBbb', path: 'vvv' },
{ docType: 'class', name: 'AaaAaa', path: 'xxx' }, { docType: 'class', name: 'AaaAaa', path: 'xxx' },

View File

@ -0,0 +1,63 @@
const { dirname } = require('canonical-path');
module.exports = function processPackages() {
return {
$runAfter: ['extractDecoratedClassesProcessor'],
$runBefore: ['computing-ids'],
$process(docs) {
const packageContentFiles = {};
const packageMap = {};
docs = docs.filter(doc => {
if (doc.docType === 'package-content') {
packageContentFiles[dirname(doc.fileInfo.filePath)] = doc;
return false;
} else {
return true;
}
});
docs.forEach(doc => {
if (doc.docType === 'module') {
// Convert the doc type from "module" to "package"
doc.docType = 'package';
// The name is actually the full id
doc.name = `@angular/${doc.id}`;
// Partition the exports into groups by type
if (doc.exports) {
doc.classes = doc.exports.filter(doc => doc.docType === 'class');
doc.decorators = doc.exports.filter(doc => doc.docType === 'decorator');
doc.functions = doc.exports.filter(doc => doc.docType === 'function');
doc.structures = doc.exports.filter(doc => doc.docType === 'enum' || doc.docType === 'interface');
doc.directives = doc.exports.filter(doc => doc.docType === 'directive');
doc.pipes = doc.exports.filter(doc => doc.docType === 'pipe');
doc.types = doc.exports.filter(doc => doc.docType === 'type-alias' || doc.docType === 'const');
}
// Copy over docs from the PACKAGE.md file that is used to document packages
const readmeDoc = packageContentFiles[dirname(doc.fileInfo.filePath)];
if (readmeDoc) {
doc.shortDescription = readmeDoc.shortDescription;
doc.description = readmeDoc.description;
doc.see = readmeDoc.see;
doc.fileInfo = readmeDoc.fileInfo;
}
// Compute the primary/secondary entry point relationships
const packageParts = doc.id.split('/');
const primaryPackageName = packageParts[0];
doc.isPrimaryPackage = packageParts.length === 1;
doc.packageInfo = packageMap[primaryPackageName] = packageMap[primaryPackageName] || { primary: undefined, secondary: [] };
if (doc.isPrimaryPackage) {
doc.packageInfo.primary = doc;
} else {
doc.packageInfo.secondary.push(doc);
}
}
});
return docs;
}
};
};

View File

@ -0,0 +1,180 @@
const testPackage = require('../../helpers/test-package');
const processorFactory = require('./processPackages');
const Dgeni = require('dgeni');
describe('processPackages processor', () => {
it('should be available on the injector', () => {
const dgeni = new Dgeni([testPackage('angular-api-package')]);
const injector = dgeni.configureInjector();
const processor = injector.get('processPackages');
expect(processor.$process).toBeDefined();
expect(processor.$runAfter).toEqual(['extractDecoratedClassesProcessor']);
expect(processor.$runBefore).toEqual(['computing-ids']);
});
it('should filter out any `package-content` docs from the collection', () => {
const docs = [
{ fileInfo: { filePath: 'some/a' }, docType: 'a', id: 'a' },
{ fileInfo: { filePath: 'some/x' }, docType: 'package-content', id: 'x' },
{ fileInfo: { filePath: 'some/b' }, docType: 'b', id: 'b' },
{ fileInfo: { filePath: 'some/y' }, docType: 'package-content', id: 'y' },
{ fileInfo: { filePath: 'some/z' }, docType: 'package-content', id: 'z' },
];
const processor = processorFactory();
const newDocs = processor.$process(docs);
expect(newDocs).toEqual([
{ fileInfo: { filePath: 'some/a' }, docType: 'a', id: 'a' },
{ fileInfo: { filePath: 'some/b' }, docType: 'b', id: 'b' },
]);
});
it('should change `module` docs to `package` docs', () => {
const processor = processorFactory();
const docs = [
{ fileInfo: { filePath: 'some/a' }, docType: 'module', id: 'a' },
{ fileInfo: { filePath: 'some/b' }, docType: 'module', id: 'b' },
{ docType: 'other', id: 'c' },
];
const newDocs = processor.$process(docs);
expect(newDocs).toEqual([
jasmine.objectContaining({ docType: 'package', id: 'a' }),
jasmine.objectContaining({ docType: 'package', id: 'b' }),
jasmine.objectContaining({ docType: 'other', id: 'c' }),
]);
});
it('should attach the relevant package contents to the package doc', () => {
const docs = [
{
fileInfo: { filePath: 'some/package-1/index' },
docType: 'module',
id: 'package-1',
someProp: 'foo',
},
{
fileInfo: { filePath: 'some/package-1/PACKAGE.md' },
docType: 'package-content',
id: 'package-1/PACKAGE.md',
shortDescription: 'some short description',
description: 'some description',
see: [ 'a', 'b' ],
},
{
fileInfo: { filePath: 'some/package-2/index' },
docType: 'module',
id: 'package-2',
},
];
const processor = processorFactory();
const newDocs = processor.$process(docs);
const package1 = jasmine.objectContaining({
fileInfo: { filePath: 'some/package-1/PACKAGE.md' },
docType: 'package',
name: '@angular/package-1',
id: 'package-1',
someProp: 'foo',
shortDescription: 'some short description',
description: 'some description',
see: [ 'a', 'b' ],
isPrimaryPackage: true,
});
const package2 = jasmine.objectContaining({
fileInfo: { filePath: 'some/package-2/index' },
docType: 'package',
name: '@angular/package-2',
id: 'package-2',
isPrimaryPackage: true,
});
expect(newDocs).toEqual([package1, package2]);
});
it('should compute primary and second package info', () => {
const docs = [
{
fileInfo: { filePath: 'some/package-1/index' },
docType: 'module',
id: 'package-1',
},
{
fileInfo: { filePath: 'some/package-1/sub-1index' },
docType: 'module',
id: 'package-1/sub-1',
},
{
fileInfo: { filePath: 'some/package-1/sub-2index' },
docType: 'module',
id: 'package-1/sub-2',
},
];
const processor = processorFactory();
const newDocs = processor.$process(docs);
expect(newDocs[0].isPrimaryPackage).toBe(true);
expect(newDocs[1].isPrimaryPackage).toBe(false);
expect(newDocs[2].isPrimaryPackage).toBe(false);
expect(newDocs[0].packageInfo.primary).toBe(newDocs[0]);
expect(newDocs[1].packageInfo.primary).toBe(newDocs[0]);
expect(newDocs[2].packageInfo.primary).toBe(newDocs[0]);
expect(newDocs[0].packageInfo.secondary).toEqual([newDocs[1], newDocs[2]]);
expect(newDocs[1].packageInfo.secondary).toEqual([newDocs[1], newDocs[2]]);
expect(newDocs[2].packageInfo.secondary).toEqual([newDocs[1], newDocs[2]]);
});
it('should partition the exports of packages into groups', () => {
const docs = [
{
fileInfo: { filePath: 'some/x' },
docType: 'module',
id: 'x',
exports: [
{ docType: 'directive', id: 'directive-1' },
{ docType: 'function', id: 'function-1' },
{ docType: 'directive', id: 'directive-2' },
{ docType: 'decorator', id: 'decorator-1' },
{ docType: 'class', id: 'class-1' },
{ docType: 'type-alias', id: 'type-alias-1' },
{ docType: 'class', id: 'class-2' },
{ docType: 'pipe', id: 'pipe-1' },
{ docType: 'const', id: 'const-1' },
{ docType: 'const', id: 'const-2' },
{ docType: 'enum', id: 'enum-1' },
{ docType: 'interface', id: 'interface-1' },
{ docType: 'interface', id: 'interface-2' },
]
},
];
const processor = processorFactory();
const newDocs = processor.$process(docs);
expect(newDocs[0].decorators).toEqual([
{ docType: 'decorator', id: 'decorator-1' },
]);
expect(newDocs[0].functions).toEqual([
{ docType: 'function', id: 'function-1' },
]);
expect(newDocs[0].structures).toEqual([
{ docType: 'enum', id: 'enum-1' },
{ docType: 'interface', id: 'interface-1' },
{ docType: 'interface', id: 'interface-2' },
]);
expect(newDocs[0].directives).toEqual([
{ docType: 'directive', id: 'directive-1' },
{ docType: 'directive', id: 'directive-2' },
]);
expect(newDocs[0].pipes).toEqual([
{ docType: 'pipe', id: 'pipe-1' },
]);
expect(newDocs[0].types).toEqual([
{ docType: 'type-alias', id: 'type-alias-1' },
{ docType: 'const', id: 'const-1' },
{ docType: 'const', id: 'const-2' },
]);
});
});

View File

@ -0,0 +1,25 @@
/**
* @dgService
* @description
* This file reader will pull the contents from a text file that will be used
* as the description of a package.
*
* The doc will initially have the form:
* ```
* {
* content: 'the content of the file',
* startingLine: 1
* }
* ```
*/
module.exports = function packageContentFileReader() {
return {
name: 'packageContentFileReader',
defaultPattern: /PACKAGE\.md$/,
getDocs: function(fileInfo) {
// We return a single element array because content files only contain one document
return [{docType: 'package-content', content: fileInfo.content, startingLine: 1}];
}
};
};

View File

@ -14,7 +14,7 @@ module.exports = function createOverviewDump() {
modules: [] modules: []
}; };
_.forEach(docs, function(doc) { _.forEach(docs, function(doc) {
if (doc.docType === 'module') { if (doc.docType === 'package') {
overviewDoc.modules.push(doc); overviewDoc.modules.push(doc);
} }
}); });

View File

@ -1,12 +1,12 @@
{% macro githubViewHref(doc, versionInfo) -%} {% macro githubViewHref(doc, versionInfo) -%}
https://github.com/{$ versionInfo.gitRepoInfo.owner $}/{$ versionInfo.gitRepoInfo.repo $}/tree/{$ versionInfo.currentVersion.isSnapshot and versionInfo.currentVersion.SHA or versionInfo.currentVersion.raw $}/packages/{$ doc.fileInfo.realProjectRelativePath $}#L{$ doc.startingLine + 1 $}-L{$ doc.endingLine + 1 $} https://github.com/{$ versionInfo.gitRepoInfo.owner $}/{$ versionInfo.gitRepoInfo.repo $}/tree/{$ versionInfo.currentVersion.isSnapshot and versionInfo.currentVersion.SHA or versionInfo.currentVersion.raw $}/packages/{$ doc.fileInfo.realProjectRelativePath or doc.fileInfo.relativePath $}#L{$ doc.startingLine + 1 $}-L{$ doc.endingLine + 1 $}
{%- endmacro -%} {%- endmacro -%}
{% macro githubEditHref(doc, versionInfo) -%} {% macro githubEditHref(doc, versionInfo) -%}
https://github.com/{$ versionInfo.gitRepoInfo.owner $}/{$ versionInfo.gitRepoInfo.repo $}/edit/master/packages/{$ doc.fileInfo.realProjectRelativePath $}?message=docs( https://github.com/{$ versionInfo.gitRepoInfo.owner $}/{$ versionInfo.gitRepoInfo.repo $}/edit/master/packages/{$ doc.fileInfo.realProjectRelativePath or doc.fileInfo.relativePath $}?message=docs(
{%- if doc.moduleDoc %}{$ doc.moduleDoc.id.split('/')[0] $} {%- if doc.moduleDoc %}{$ doc.moduleDoc.id.split('/')[0] $}
{%- elseif doc.docType === 'module' %}{$ doc.id.split('/')[0] $} {%- elseif doc.docType === 'package' %}{$ doc.id.split('/')[0] $}
{%- else %}...{%- endif -%} {%- else %}...{%- endif -%}
)%3A%20describe%20your%20change...#L{$ doc.startingLine + 1 $}-L{$ doc.endingLine + 1 $} )%3A%20describe%20your%20change...#L{$ doc.startingLine + 1 $}-L{$ doc.endingLine + 1 $}
{%- endmacro -%} {%- endmacro -%}

View File

@ -1,16 +0,0 @@
{% extends 'base.template.html' -%}
{% block body -%}
{% include "includes/deprecation.html" %}
{% include "includes/description.html" %}
<section class="export-list">
<ul>
{% for export in doc.exports -%}
<li><a href="{$ export.path $}">{$ export.name $}</a></li>
{%- endfor %}
</ul>
</section>
{%- endblock %}

View File

@ -0,0 +1,40 @@
{% extends 'base.template.html' -%}
{% macro listItems(items, title) %}
{% if items.length %}
<section class="export-list">
<h3>{$ title $}</h3>
<table class="is-full-width list-table">
{% for item in items %}
<tr>
<td><code class="code-anchor">
<a href="{$ item.path $}">{$ item.name $}</a></code></td>
<td>{% if item.shortDescription %}{$ item.shortDescription | marked $}{% endif %}</td>
</tr>
{% endfor %}
</table>
</section>
{% endif %}
{% endmacro %}
{% block body -%}
{% include "includes/deprecation.html" %}
{$ doc.shortDescription | marked $}
{% if doc.description %}{$ doc.description | marked $}{% endif %}
{% include "includes/see-also.html" %}
<h2>Entry points</h2>
{$ listItems([doc.packageInfo.primary], 'Primary') $}
{$ listItems(doc.packageInfo.secondary, 'Secondary') $}
<h2>Exports</h2>
{$ listItems(doc.classes, 'Classes') $}
{$ listItems(doc.decorators, 'Decorators') $}
{$ listItems(doc.functions, 'Functions') $}
{$ listItems(doc.structures, 'Structures') $}
{$ listItems(doc.directives, 'Directives') $}
{$ listItems(doc.pipes, 'Pipes') $}
{$ listItems(doc.types, 'Types') $}
{%- endblock %}

View File

@ -0,0 +1,29 @@
Implements a domain-specific language (DSL) for defining web animation sequences for HTML elements as
multiple transformations over time.
Use this API to define how an HTML element can move, change color, grow or shrink, fade, or slide off
the page. These changes can occur simultaneously or sequentially. You can control the timing of each
of these transformations. The function calls generate the data structures and metadata that enable Angular
to integrate animations into templates and run them based on application states.
Animation definitions are linked to components through the `{@link Component.animations animations}`
property in the `@Component` metadata, typically in the component file of the HTML element to be animated.
The `trigger()` function encapsulates a named animation, with all other function calls nested within. Use
the trigger name to bind the named animation to a specific triggering element in the HTML template.
Angular animations are based on CSS web transition functionality, so anything that can be styled or
transformed in CSS can be animated the same way in Angular. Angular animations allow you to:
* Set animation timings, styles, keyframes, and transitions.
* Animate HTML elements in complex sequences and choreographies.
* Animate HTML elements as they are inserted and removed from the DOM, including responsive real-time
filtering.
* Create reusable animations.
* Animate parent and child elements.
Additional animation functionality is provided in other Angular modules for animation testing, for
route-based animations, and for programmatic animation controls that allow an end user to fast forward
and reverse an animation sequence.
@see Find out more in the [animations guide](guide/animations).
@see See what polyfills you might need in the [browser support guide](guide/browser-support).