feat(docs-infra): generate Angular CLI command reference (#25363)

PR Close #25363
This commit is contained in:
Pete Bacon Darwin
2018-09-14 10:05:57 +01:00
committed by Kara Erickson
parent 39a67548ac
commit f29b218060
28 changed files with 965 additions and 15 deletions

View File

@ -0,0 +1,9 @@
module.exports = function filterHiddenCommands() {
return {
$runAfter: ['files-read'],
$runBefore: ['processCliContainerDoc'],
$process(docs) {
return docs.filter(doc => doc.docType !== 'cli-command' || doc.hidden !== true);
}
};
};

View File

@ -0,0 +1,40 @@
const testPackage = require('../../helpers/test-package');
const processorFactory = require('./filterHiddenCommands');
const Dgeni = require('dgeni');
describe('filterHiddenCommands processor', () => {
it('should be available on the injector', () => {
const dgeni = new Dgeni([testPackage('cli-docs-package')]);
const injector = dgeni.configureInjector();
const processor = injector.get('filterHiddenCommands');
expect(processor.$process).toBeDefined();
});
it('should run after the correct processor', () => {
const processor = processorFactory();
expect(processor.$runAfter).toEqual(['files-read']);
});
it('should run before the correct processor', () => {
const processor = processorFactory();
expect(processor.$runBefore).toEqual(['processCliContainerDoc']);
});
it('should remove CLI command docs that are hidden', () => {
const processor = processorFactory();
const filtered = processor.$process([
{ docType: 'cli-command', id: 'one' },
{ docType: 'cli-command', id: 'two', hidden: true },
{ docType: 'cli-command', id: 'three', hidden: false },
{ docType: 'other-doc', id: 'four', hidden: true },
{ docType: 'other-doc', id: 'five', hidden: false },
]);
expect(filtered).toEqual([
{ docType: 'cli-command', id: 'one' },
{ docType: 'cli-command', id: 'three', hidden: false },
{ docType: 'other-doc', id: 'four', hidden: true },
{ docType: 'other-doc', id: 'five', hidden: false },
]);
});
});

View File

@ -0,0 +1,68 @@
module.exports = function processCliCommands() {
return {
$runAfter: ['extra-docs-added'],
$runBefore: ['rendering-docs'],
$process(docs) {
const navigationDoc = docs.find(doc => doc.docType === 'navigation-json');
const navigationNode = navigationDoc && navigationDoc.data['SideNav'].find(node => node.title === 'CLI Commands');
docs.forEach(doc => {
if (doc.docType === 'cli-command') {
doc.names = collectNames(doc.name, doc.commandAliases);
// Recursively process the options
processOptions(doc, doc.options);
// Add to navigation doc
if (navigationNode) {
navigationNode.children.push({ url: doc.path, title: `ng ${doc.name}` });
}
}
});
}
};
};
function processOptions(container, options) {
container.positionalOptions = [];
container.namedOptions = [];
options.forEach(option => {
if (option.type === 'boolean' && option.default === undefined) {
option.default = false;
}
// Ignore any hidden options
if (option.hidden) { return; }
option.types = option.types || [option.type];
option.names = collectNames(option.name, option.aliases);
// Now work out what kind of option it is: positional/named
if (option.positional !== undefined) {
container.positionalOptions[option.positional] = option;
} else {
container.namedOptions.push(option);
}
// Recurse if there are subcommands
if (option.subcommands) {
option.subcommands = getValues(option.subcommands);
option.subcommands.forEach(subcommand => {
subcommand.names = collectNames(subcommand.name, subcommand.aliases);
processOptions(subcommand, subcommand.options);
});
}
});
container.namedOptions.sort((a, b) => a.name > b.name ? 1 : -1);
}
function collectNames(name, aliases) {
return [name].concat(aliases);
}
function getValues(obj) {
return Object.keys(obj).map(key => obj[key]);
}

View File

@ -0,0 +1,264 @@
const testPackage = require('../../helpers/test-package');
const processorFactory = require('./processCliCommands');
const Dgeni = require('dgeni');
describe('processCliCommands processor', () => {
it('should be available on the injector', () => {
const dgeni = new Dgeni([testPackage('cli-docs-package')]);
const injector = dgeni.configureInjector();
const processor = injector.get('processCliCommands');
expect(processor.$process).toBeDefined();
});
it('should run after the correct processor', () => {
const processor = processorFactory();
expect(processor.$runAfter).toEqual(['extra-docs-added']);
});
it('should run before the correct processor', () => {
const processor = processorFactory();
expect(processor.$runBefore).toEqual(['rendering-docs']);
});
it('should collect the names (name + aliases)', () => {
const processor = processorFactory();
const doc = {
docType: 'cli-command',
name: 'name',
commandAliases: ['alias1', 'alias2'],
options: [],
};
processor.$process([doc]);
expect(doc.names).toEqual(['name', 'alias1', 'alias2']);
});
describe('options', () => {
it('should remove the hidden options', () => {
const processor = processorFactory();
const doc = {
docType: 'cli-command',
name: 'name',
commandAliases: [],
options: [
{ name: 'option1' },
{ name: 'option2', hidden: true },
{ name: 'option3' },
{ name: 'option4', hidden: true },
],
};
processor.$process([doc]);
expect(doc.namedOptions).toEqual([
jasmine.objectContaining({ name: 'option1' }),
jasmine.objectContaining({ name: 'option3' }),
]);
});
it('should collect the non-hidden positional and named options', () => {
const processor = processorFactory();
const doc = {
docType: 'cli-command',
name: 'name',
commandAliases: [],
options: [
{ name: 'named1' },
{ name: 'positional1', positional: 0},
{ name: 'named2', hidden: true },
{ name: 'positional2', hidden: true, positional: 1},
],
};
processor.$process([doc]);
expect(doc.positionalOptions).toEqual([
jasmine.objectContaining({ name: 'positional1', positional: 0}),
]);
expect(doc.namedOptions).toEqual([
jasmine.objectContaining({ name: 'named1' }),
]);
});
it('should sort the named options into order by name', () => {
const processor = processorFactory();
const doc = {
docType: 'cli-command',
name: 'name',
commandAliases: [],
options: [
{ name: 'c' },
{ name: 'a' },
{ name: 'b' },
],
};
processor.$process([doc]);
expect(doc.namedOptions).toEqual([
jasmine.objectContaining({ name: 'a' }),
jasmine.objectContaining({ name: 'b' }),
jasmine.objectContaining({ name: 'c' }),
]);
});
});
describe('subcommands', () => {
it('should convert subcommands hash into a collection', () => {
const processor = processorFactory();
const doc = {
docType: 'cli-command',
name: 'name',
commandAliases: [],
options: [{
name: 'supercommand',
subcommands: {
subcommand1: {
name: 'subcommand1',
options: [
{ name: 'subcommand1-option1' },
{ name: 'subcommand1-option2' },
],
},
subcommand2: {
name: 'subcommand2',
options: [
{ name: 'subcommand2-option1' },
{ name: 'subcommand2-option2' },
],
}
},
}],
};
processor.$process([doc]);
expect(doc.options[0].subcommands).toEqual([
jasmine.objectContaining({ name: 'subcommand1' }),
jasmine.objectContaining({ name: 'subcommand2' }),
]);
});
it('should remove the hidden subcommand options', () => {
const processor = processorFactory();
const doc = {
docType: 'cli-command',
name: 'name',
commandAliases: [],
options: [{
name: 'supercommand',
subcommands: {
subcommand1: {
name: 'subcommand1',
options: [
{ name: 'subcommand1-option1' },
{ name: 'subcommand1-option2', hidden: true },
],
},
subcommand2: {
name: 'subcommand2',
options: [
{ name: 'subcommand2-option1', hidden: true },
{ name: 'subcommand2-option2' },
],
}
},
}],
};
processor.$process([doc]);
expect(doc.options[0].subcommands[0].namedOptions).toEqual([
jasmine.objectContaining({ name: 'subcommand1-option1' }),
]);
expect(doc.options[0].subcommands[1].namedOptions).toEqual([
jasmine.objectContaining({ name: 'subcommand2-option2' }),
]);
});
it('should collect the non-hidden positional arguments and named options', () => {
const processor = processorFactory();
const doc = {
docType: 'cli-command',
name: 'name',
commandAliases: [],
options: [{
name: 'supercommand',
subcommands: {
subcommand1: {
name: 'subcommand1',
options: [
{ name: 'subcommand1-option1' },
{ name: 'subcommand1-option2', positional: 0 },
],
},
subcommand2: {
name: 'subcommand2',
options: [
{ name: 'subcommand2-option1', hidden: true },
{ name: 'subcommand2-option2', hidden: true, positional: 1 },
],
}
},
}],
};
processor.$process([doc]);
expect(doc.options[0].subcommands[0].positionalOptions).toEqual([
jasmine.objectContaining({ name: 'subcommand1-option2', positional: 0}),
]);
expect(doc.options[0].subcommands[0].namedOptions).toEqual([
jasmine.objectContaining({ name: 'subcommand1-option1' }),
]);
expect(doc.options[0].subcommands[1].positionalOptions).toEqual([]);
expect(doc.options[0].subcommands[1].namedOptions).toEqual([]);
});
it('should sort the named subcommand options into order by name', () => {
const processor = processorFactory();
const doc = {
docType: 'cli-command',
name: 'name',
commandAliases: [],
options: [{
name: 'supercommand',
subcommands: {
subcommand1: {
name: 'subcommand1',
options: [
{ name: 'c' },
{ name: 'a' },
{ name: 'b' },
]
}
}
}],
};
processor.$process([doc]);
expect(doc.options[0].subcommands[0].namedOptions).toEqual([
jasmine.objectContaining({ name: 'a' }),
jasmine.objectContaining({ name: 'b' }),
jasmine.objectContaining({ name: 'c' }),
]);
});
});
it('should add the command to the CLI node in the navigation doc', () => {
const processor = processorFactory();
const command = {
docType: 'cli-command',
name: 'command1',
commandAliases: ['alias1', 'alias2'],
options: [],
path: 'cli/command1',
};
const navigation = {
docType: 'navigation-json',
data: {
SideNav: [
{ url: 'some/page', title: 'Some Page' },
{ url: 'cli', title: 'CLI Commands', children: [
{ url: 'cli', title: 'Using the CLI' },
]},
{ url: 'other/page', title: 'Other Page' },
]
}
};
processor.$process([command, navigation]);
expect(navigation.data.SideNav[1].title).toEqual('CLI Commands');
expect(navigation.data.SideNav[1].children).toEqual([
{ url: 'cli', title: 'Using the CLI' },
{ url: 'cli/command1', title: 'ng command1' },
]);
});
});

View File

@ -0,0 +1,11 @@
module.exports = function processCliContainerDoc() {
return {
$runAfter: ['extra-docs-added'],
$runBefore: ['rendering-docs'],
$process(docs) {
const cliDoc = docs.find(doc => doc.id === 'cli/index');
cliDoc.id = 'cli-container';
cliDoc.commands = docs.filter(doc => doc.docType === 'cli-command');
}
};
};

View File

@ -0,0 +1,23 @@
const testPackage = require('../../helpers/test-package');
const processorFactory = require('./processCliContainerDoc');
const Dgeni = require('dgeni');
describe('processCliContainerDoc processor', () => {
it('should be available on the injector', () => {
const dgeni = new Dgeni([testPackage('cli-docs-package')]);
const injector = dgeni.configureInjector();
const processor = injector.get('processCliContainerDoc');
expect(processor.$process).toBeDefined();
});
it('should run after the correct processor', () => {
const processor = processorFactory();
expect(processor.$runAfter).toEqual(['extra-docs-added']);
});
it('should run before the correct processor', () => {
const processor = processorFactory();
expect(processor.$runBefore).toEqual(['rendering-docs']);
});
});