From f440bd1793218ea64b4f9754e913f971d8f3d8c0 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 6 Jun 2019 09:45:07 +0100 Subject: [PATCH] build(docs-infra): fix CLI command github links (#30889) The "view" links were broken because the version used to compute the git tag for the GitHub URL included a build SHA. Now we clean that off before using it in the URL. The links are to the JSON schema that defines the documentation for the command. This is accurate but confusing because the content for the long description is stored in a separate markdown file referenced from this schema file. This commit adds a second set of links for the long description, if it exists, which links directly to the markdown file. Closes #30700 PR Close #30889 --- .../transforms/cli-docs-package/index.js | 94 ++++++++++--------- .../cli-docs-package/readers/cli-command.js | 53 +++++++++-- .../readers/cli-command.spec.js | 30 ++++-- .../readers/mocks/commands/add.json | 3 + .../templates/cli/cli-command.template.html | 5 +- 5 files changed, 123 insertions(+), 62 deletions(-) create mode 100644 aio/tools/transforms/cli-docs-package/readers/mocks/commands/add.json diff --git a/aio/tools/transforms/cli-docs-package/index.js b/aio/tools/transforms/cli-docs-package/index.js index 3d4fa95da7..ecefe7d809 100644 --- a/aio/tools/transforms/cli-docs-package/index.js +++ b/aio/tools/transforms/cli-docs-package/index.js @@ -1,4 +1,5 @@ const {resolve} = require('canonical-path'); +const semver = require('semver'); const Package = require('dgeni').Package; const basePackage = require('../angular-base-package'); const contentPackage = require('../content-package'); @@ -8,59 +9,60 @@ const CLI_SOURCE_PATH = resolve(CLI_SOURCE_ROOT, 'node_modules/@angular/cli'); const CLI_SOURCE_HELP_PATH = resolve(CLI_SOURCE_PATH, 'help'); // Define the dgeni package for generating the docs -module.exports = new Package('cli-docs', [basePackage, contentPackage]) +module.exports = + new Package('cli-docs', [basePackage, contentPackage]) -// Register the services and file readers -.factory(require('./readers/cli-command')) + // Register the services and file readers + .factory(require('./readers/cli-command')) -// Register the processors -.processor(require('./processors/processCliContainerDoc')) -.processor(require('./processors/processCliCommands')) -.processor(require('./processors/filterHiddenCommands')) + // Register the processors + .processor(require('./processors/processCliContainerDoc')) + .processor(require('./processors/processCliCommands')) + .processor(require('./processors/filterHiddenCommands')) -// Configure file reading -.config(function(readFilesProcessor, cliCommandFileReader) { - readFilesProcessor.fileReaders.push(cliCommandFileReader); - readFilesProcessor.sourceFiles = readFilesProcessor.sourceFiles.concat([ - { - basePath: CLI_SOURCE_HELP_PATH, - include: resolve(CLI_SOURCE_HELP_PATH, '*.json'), - fileReader: 'cliCommandFileReader' - }, - { - basePath: CONTENTS_PATH, - include: resolve(CONTENTS_PATH, 'cli/**'), - fileReader: 'contentFileReader' - }, - ]); -}) + // Configure file reading + .config(function(readFilesProcessor, cliCommandFileReader) { + readFilesProcessor.fileReaders.push(cliCommandFileReader); + readFilesProcessor.sourceFiles = readFilesProcessor.sourceFiles.concat([ + { + basePath: CLI_SOURCE_HELP_PATH, + include: resolve(CLI_SOURCE_HELP_PATH, '*.json'), + fileReader: 'cliCommandFileReader' + }, + { + basePath: CONTENTS_PATH, + include: resolve(CONTENTS_PATH, 'cli/**'), + fileReader: 'contentFileReader' + }, + ]); + }) -.config(function(templateFinder, templateEngine, getInjectables) { - // Where to find the templates for the CLI doc rendering - templateFinder.templateFolders.unshift(resolve(TEMPLATES_PATH, 'cli')); - // Add in templating filters and tags - templateEngine.filters = templateEngine.filters.concat(getInjectables(requireFolder(__dirname, './rendering'))); -}) + .config(function(templateFinder, templateEngine, getInjectables) { + // Where to find the templates for the CLI doc rendering + templateFinder.templateFolders.unshift(resolve(TEMPLATES_PATH, 'cli')); + // Add in templating filters and tags + templateEngine.filters = templateEngine.filters.concat( + getInjectables(requireFolder(__dirname, './rendering'))); + }) -.config(function(renderDocsProcessor) { + .config(function(renderDocsProcessor) { - const cliPackage = require(resolve(CLI_SOURCE_PATH, 'package.json')); - const repoUrlParts = cliPackage.repository.url.replace(/\.git$/, '').split('/'); - const version = `v${cliPackage.version}`; - const repo = repoUrlParts.pop(); - const owner = repoUrlParts.pop(); - const cliVersionInfo = { - gitRepoInfo: { owner, repo }, - currentVersion: { raw: version } - }; + const cliPackage = require(resolve(CLI_SOURCE_PATH, 'package.json')); + const repoUrlParts = cliPackage.repository.url.replace(/\.git$/, '').split('/'); + const version = `v${semver.clean(cliPackage.version)}`; + const repo = repoUrlParts.pop(); + const owner = repoUrlParts.pop(); + const cliVersionInfo = {gitRepoInfo: {owner, repo}, currentVersion: {raw: version}}; - // Add the cli version data to the renderer, for use in things like github links - renderDocsProcessor.extraData.cliVersionInfo = cliVersionInfo; -}) + // Add the cli version data to the renderer, for use in things like github links + renderDocsProcessor.extraData.cliVersionInfo = cliVersionInfo; + }) -.config(function(convertToJsonProcessor, postProcessHtml) { - convertToJsonProcessor.docTypes = convertToJsonProcessor.docTypes.concat(['cli-command', 'cli-overview']); - postProcessHtml.docTypes = postProcessHtml.docTypes.concat(['cli-command', 'cli-overview']); -}); + .config(function(convertToJsonProcessor, postProcessHtml) { + convertToJsonProcessor.docTypes = + convertToJsonProcessor.docTypes.concat(['cli-command', 'cli-overview']); + postProcessHtml.docTypes = + postProcessHtml.docTypes.concat(['cli-command', 'cli-overview']); + }); diff --git a/aio/tools/transforms/cli-docs-package/readers/cli-command.js b/aio/tools/transforms/cli-docs-package/readers/cli-command.js index bca735a8ee..d1203bff48 100644 --- a/aio/tools/transforms/cli-docs-package/readers/cli-command.js +++ b/aio/tools/transforms/cli-docs-package/readers/cli-command.js @@ -26,22 +26,59 @@ module.exports = function cliCommandFileReader(log) { docType: 'cli-command', id: `cli-${doc.name}`, commandAliases: doc.aliases || [], - aliases: computeAliases(doc), - path, + aliases: computeAliases(doc), path, outputPath: `${path}.json`, breadCrumbs: [ - { text: 'CLI', path: 'cli' }, - { text: name, path }, + {text: 'CLI', path: 'cli'}, + {text: name, path}, ] }); + if (doc.longDescription) { + doc.longDescriptionDoc = createLongDescriptionDoc(fileInfo); + } return [result]; } catch (e) { log.warn(`Failed to read cli command file: "${fileInfo.relativePath}" - ${e.message}`); } } }; -}; + function computeAliases(doc) { + return [doc.name].concat(doc.aliases || []).map(alias => `cli-${alias}`); + } -function computeAliases(doc) { - return [doc.name].concat(doc.aliases || []).map(alias => `cli-${alias}`); -} \ No newline at end of file + /** + * Synthesize a doc for the CLI command long description, which is used to generate links + * for viewing and editing the long description in GitHub. + * + * The long description is stored in a markdown file that is referenced from the original + * schema file for the command, via the `$longDescription` field. The field is a relative path + * to the markdown file from the schema file. + * + * This function tries to retrieve that original schema based on the file path of the help JSON + * file, which was passed to the `cliCommandFileReader.getDocs()` method. + */ + function createLongDescriptionDoc(fileInfo) { + try { + const path = require('canonical-path'); + const fs = require('fs'); + const json5 = require('json5'); + + const schemaJsonPath = path.resolve(fileInfo.basePath, '../commands', fileInfo.relativePath); + const schemaJson = fs.readFileSync(schemaJsonPath); + const schema = json5.parse(schemaJson); + if (schema.$longDescription) { + return { + docType: 'content', + startingLine: 0, + fileInfo: { + realProjectRelativePath: + path.join(path.dirname(fileInfo.realProjectRelativePath), schema.$longDescription) + } + }; + } + } catch (e) { + log.warn('Unable to read CLI long description file info', e, fileInfo); + return undefined; + } + } +}; diff --git a/aio/tools/transforms/cli-docs-package/readers/cli-command.spec.js b/aio/tools/transforms/cli-docs-package/readers/cli-command.spec.js index 341c2758af..9c7cfdb3c4 100644 --- a/aio/tools/transforms/cli-docs-package/readers/cli-command.spec.js +++ b/aio/tools/transforms/cli-docs-package/readers/cli-command.spec.js @@ -40,7 +40,12 @@ const content = ` } `; -const fileInfo = {content, baseName: 'add'}; +const fileInfo = { + content, + baseName: 'add', + relativePath: 'add.json', + basePath: __dirname + '/mocks/help', +}; describe('cli-command reader', () => { describe('getDocs', () => { @@ -77,8 +82,8 @@ describe('cli-command reader', () => { it('should compute the bread crumbs', () => { const docs = reader.getDocs(fileInfo); expect(docs[0].breadCrumbs).toEqual([ - { text: 'CLI', path: 'cli' }, - { text: 'add', path: 'cli/add' }, + {text: 'CLI', path: 'cli'}, + {text: 'add', path: 'cli/add'}, ]); }); @@ -89,7 +94,9 @@ describe('cli-command reader', () => { it('should extract the long description', () => { const docs = reader.getDocs(fileInfo); - expect(docs[0].longDescription).toEqual('Add support for a library in your project, for example adding `@angular/pwa` which would configure\nyour project for PWA support.\n'); + expect(docs[0].longDescription) + .toEqual( + 'Add support for a library in your project, for example adding `@angular/pwa` which would configure\nyour project for PWA support.\n'); }); it('should extract the command type', () => { @@ -110,10 +117,19 @@ describe('cli-command reader', () => { it('should extract the options', () => { const docs = reader.getDocs(fileInfo); expect(docs[0].options).toEqual([ - jasmine.objectContaining({ name: 'collection' }), - jasmine.objectContaining({ name: 'help' }), - jasmine.objectContaining({ name: 'helpJson' }), + jasmine.objectContaining({name: 'collection'}), + jasmine.objectContaining({name: 'help'}), + jasmine.objectContaining({name: 'helpJson'}), ]); }); + + it('should extract file info for the long description', () => { + const [doc] = reader.getDocs(fileInfo); + expect(doc.longDescriptionDoc).toEqual({ + docType: 'content', + startingLine: 0, + fileInfo: {realProjectRelativePath: 'packages/angular/cli/commands/add-long.md'} + }); + }); }); }); diff --git a/aio/tools/transforms/cli-docs-package/readers/mocks/commands/add.json b/aio/tools/transforms/cli-docs-package/readers/mocks/commands/add.json new file mode 100644 index 0000000000..84423ec268 --- /dev/null +++ b/aio/tools/transforms/cli-docs-package/readers/mocks/commands/add.json @@ -0,0 +1,3 @@ +{ + "$longDescription": "./add-long.md" +} \ No newline at end of file diff --git a/aio/tools/transforms/templates/cli/cli-command.template.html b/aio/tools/transforms/templates/cli/cli-command.template.html index 0b775a4b7d..5de2178ff9 100644 --- a/aio/tools/transforms/templates/cli/cli-command.template.html +++ b/aio/tools/transforms/templates/cli/cli-command.template.html @@ -14,7 +14,10 @@ {$ cli.renderSyntax(doc) $} {% if doc.longDescription.length %} -

Description

+

+ {$ github.githubLinks(doc.longDescriptionDoc, cliVersionInfo) $} + Description +

{$ doc.longDescription | marked $} {% endif%}