From 5d706b8e05442e6a7c40e4d5857dca6064c5b933 Mon Sep 17 00:00:00 2001 From: YuCheng Hu Date: Mon, 5 Apr 2021 13:31:48 -0400 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=A2=E5=A4=B1=E7=9A=84?= =?UTF-8?q?=E8=84=9A=E6=9C=AC=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../angular-api-package/tag-defs/default.js | 3 + .../angular-base-package/ignore-words.json | 705 ++++++++++++++++++ .../processors/splitDescription.js | 28 + .../processors/splitDescription.spec.js | 71 ++ .../angular-errors-package/index.js | 87 +++ .../processors/processErrorDocs.js | 55 ++ .../processors/processErrorDocs.spec.js | 125 ++++ .../processors/processErrorsContainerDoc.js | 11 + .../processErrorsContainerDoc.spec.js | 22 + .../angular-errors-package/readers/error.js | 23 + .../readers/error.spec.js | 44 ++ .../tag-defs/category.js | 3 + .../tag-defs/debugging.js | 3 + .../tag-defs/shortDescription.js | 3 + .../tag-defs/videoUrl.js | 3 + .../authors-package/base-authoring-package.js | 36 + aio/tools/transforms/authors-package/utils.js | 11 + .../content-package/tag-defs/reviewed.js | 21 + .../templates/error/error.template.html | 28 + .../error/errors-container.template.html | 18 + 20 files changed, 1300 insertions(+) create mode 100644 aio/tools/transforms/angular-api-package/tag-defs/default.js create mode 100644 aio/tools/transforms/angular-base-package/ignore-words.json create mode 100644 aio/tools/transforms/angular-base-package/processors/splitDescription.js create mode 100644 aio/tools/transforms/angular-base-package/processors/splitDescription.spec.js create mode 100644 aio/tools/transforms/angular-errors-package/index.js create mode 100644 aio/tools/transforms/angular-errors-package/processors/processErrorDocs.js create mode 100644 aio/tools/transforms/angular-errors-package/processors/processErrorDocs.spec.js create mode 100644 aio/tools/transforms/angular-errors-package/processors/processErrorsContainerDoc.js create mode 100644 aio/tools/transforms/angular-errors-package/processors/processErrorsContainerDoc.spec.js create mode 100644 aio/tools/transforms/angular-errors-package/readers/error.js create mode 100644 aio/tools/transforms/angular-errors-package/readers/error.spec.js create mode 100644 aio/tools/transforms/angular-errors-package/tag-defs/category.js create mode 100644 aio/tools/transforms/angular-errors-package/tag-defs/debugging.js create mode 100644 aio/tools/transforms/angular-errors-package/tag-defs/shortDescription.js create mode 100644 aio/tools/transforms/angular-errors-package/tag-defs/videoUrl.js create mode 100644 aio/tools/transforms/authors-package/base-authoring-package.js create mode 100644 aio/tools/transforms/authors-package/utils.js create mode 100644 aio/tools/transforms/content-package/tag-defs/reviewed.js create mode 100644 aio/tools/transforms/templates/error/error.template.html create mode 100644 aio/tools/transforms/templates/error/errors-container.template.html diff --git a/aio/tools/transforms/angular-api-package/tag-defs/default.js b/aio/tools/transforms/angular-api-package/tag-defs/default.js new file mode 100644 index 0000000000..d43674d845 --- /dev/null +++ b/aio/tools/transforms/angular-api-package/tag-defs/default.js @@ -0,0 +1,3 @@ +module.exports = function() { + return {name: 'default'}; +}; diff --git a/aio/tools/transforms/angular-base-package/ignore-words.json b/aio/tools/transforms/angular-base-package/ignore-words.json new file mode 100644 index 0000000000..0f56284ddb --- /dev/null +++ b/aio/tools/transforms/angular-base-package/ignore-words.json @@ -0,0 +1,705 @@ +{ + "en": [ + "a", + "able", + "about", + "above", + "abst", + "accordance", + "according", + "accordingly", + "across", + "act", + "actually", + "added", + "adj", + "adopted", + "affected", + "affecting", + "affects", + "after", + "afterwards", + "again", + "against", + "ah", + "all", + "almost", + "alone", + "along", + "already", + "also", + "although", + "always", + "am", + "among", + "amongst", + "an", + "and", + "announce", + "another", + "any", + "anybody", + "anyhow", + "anymore", + "anyone", + "anything", + "anyway", + "anyways", + "anywhere", + "apparently", + "approximately", + "are", + "aren", + "arent", + "arise", + "around", + "as", + "aside", + "ask", + "asking", + "at", + "auth", + "available", + "away", + "awfully", + "b", + "back", + "be", + "became", + "because", + "become", + "becomes", + "becoming", + "been", + "before", + "beforehand", + "begin", + "beginning", + "beginnings", + "begins", + "behind", + "being", + "believe", + "below", + "beside", + "besides", + "between", + "beyond", + "biol", + "both", + "brief", + "briefly", + "but", + "by", + "c", + "ca", + "came", + "can", + "cannot", + "can't", + "cant", + "cause", + "causes", + "certain", + "certainly", + "co", + "com", + "come", + "comes", + "contain", + "containing", + "contains", + "could", + "couldnt", + "d", + "date", + "did", + "didn't", + "didnt", + "different", + "do", + "does", + "doesn't", + "doesnt", + "doing", + "done", + "don't", + "dont", + "down", + "downwards", + "due", + "during", + "e", + "each", + "ed", + "edu", + "effect", + "eg", + "eight", + "eighty", + "either", + "else", + "elsewhere", + "end", + "ending", + "enough", + "especially", + "et", + "et-al", + "etc", + "even", + "ever", + "every", + "everybody", + "everyone", + "everything", + "everywhere", + "ex", + "except", + "f", + "far", + "few", + "ff", + "fifth", + "first", + "five", + "fix", + "followed", + "following", + "follows", + "for", + "former", + "formerly", + "forth", + "found", + "four", + "from", + "further", + "furthermore", + "g", + "gave", + "get", + "gets", + "getting", + "give", + "given", + "gives", + "giving", + "go", + "goes", + "gone", + "got", + "gotten", + "h", + "had", + "happens", + "hardly", + "has", + "hasn't", + "hasnt", + "have", + "haven't", + "havent", + "having", + "he", + "hed", + "hence", + "her", + "here", + "hereafter", + "hereby", + "herein", + "heres", + "hereupon", + "hers", + "herself", + "hes", + "hi", + "hid", + "him", + "himself", + "his", + "hither", + "home", + "how", + "howbeit", + "however", + "hundred", + "i", + "id", + "ie", + "if", + "i'll", + "ill", + "im", + "immediate", + "immediately", + "importance", + "important", + "in", + "inc", + "indeed", + "index", + "information", + "instead", + "into", + "invention", + "inward", + "is", + "isn't", + "isnt", + "it", + "itd", + "it'll", + "itll", + "its", + "itself", + "i've", + "ive", + "j", + "just", + "k", + "keep", + "keeps", + "kept", + "keys", + "kg", + "km", + "know", + "known", + "knows", + "l", + "largely", + "last", + "lately", + "later", + "latter", + "latterly", + "least", + "less", + "lest", + "let", + "lets", + "like", + "liked", + "likely", + "line", + "little", + "'ll", + "'ll", + "look", + "looking", + "looks", + "ltd", + "m", + "made", + "mainly", + "make", + "makes", + "many", + "may", + "maybe", + "me", + "mean", + "means", + "meantime", + "meanwhile", + "merely", + "mg", + "might", + "million", + "miss", + "ml", + "more", + "moreover", + "most", + "mostly", + "mr", + "mrs", + "much", + "mug", + "must", + "my", + "myself", + "n", + "na", + "name", + "namely", + "nay", + "nd", + "near", + "nearly", + "necessarily", + "necessary", + "need", + "needs", + "neither", + "never", + "nevertheless", + "new", + "next", + "nine", + "ninety", + "no", + "nobody", + "non", + "none", + "nonetheless", + "noone", + "nor", + "normally", + "nos", + "not", + "noted", + "nothing", + "now", + "nowhere", + "o", + "obtain", + "obtained", + "obviously", + "of", + "off", + "often", + "oh", + "ok", + "okay", + "old", + "omitted", + "on", + "once", + "one", + "ones", + "only", + "onto", + "or", + "ord", + "other", + "others", + "otherwise", + "ought", + "our", + "ours", + "ourselves", + "out", + "outside", + "over", + "overall", + "owing", + "own", + "p", + "page", + "pages", + "part", + "particular", + "particularly", + "past", + "per", + "perhaps", + "placed", + "please", + "plus", + "poorly", + "possible", + "possibly", + "potentially", + "pp", + "predominantly", + "present", + "previously", + "primarily", + "probably", + "promptly", + "proud", + "provides", + "put", + "q", + "que", + "quickly", + "quite", + "qv", + "r", + "ran", + "rather", + "rd", + "re", + "readily", + "really", + "recent", + "recently", + "ref", + "refs", + "regarding", + "regardless", + "regards", + "related", + "relatively", + "research", + "respectively", + "resulted", + "resulting", + "results", + "right", + "run", + "s", + "said", + "same", + "saw", + "say", + "saying", + "says", + "sec", + "section", + "see", + "seeing", + "seem", + "seemed", + "seeming", + "seems", + "seen", + "self", + "selves", + "sent", + "seven", + "several", + "shall", + "she", + "shed", + "she'll", + "shell", + "shes", + "should", + "shouldn't", + "shouldnt", + "show", + "showed", + "shown", + "showns", + "shows", + "significant", + "significantly", + "similar", + "similarly", + "since", + "six", + "slightly", + "so", + "some", + "somebody", + "somehow", + "someone", + "somethan", + "something", + "sometime", + "sometimes", + "somewhat", + "somewhere", + "soon", + "sorry", + "specifically", + "specified", + "specify", + "specifying", + "state", + "states", + "still", + "stop", + "strongly", + "sub", + "substantially", + "successfully", + "such", + "sufficiently", + "suggest", + "sup", + "sure", + "t", + "take", + "taken", + "taking", + "tell", + "tends", + "th", + "than", + "thank", + "thanks", + "thanx", + "that", + "that'll", + "thatll", + "thats", + "that've", + "thatve", + "the", + "their", + "theirs", + "them", + "themselves", + "then", + "thence", + "there", + "thereafter", + "thereby", + "thered", + "therefore", + "therein", + "there'll", + "therell", + "thereof", + "therere", + "theres", + "thereto", + "thereupon", + "there've", + "thereve", + "these", + "they", + "theyd", + "they'll", + "theyll", + "theyre", + "they've", + "theyve", + "think", + "this", + "those", + "thou", + "though", + "thoughh", + "thousand", + "throug", + "through", + "throughout", + "thru", + "thus", + "til", + "tip", + "to", + "together", + "too", + "took", + "toward", + "towards", + "tried", + "tries", + "truly", + "try", + "trying", + "ts", + "twice", + "two", + "u", + "un", + "under", + "unfortunately", + "unless", + "unlike", + "unlikely", + "until", + "unto", + "up", + "upon", + "ups", + "us", + "use", + "used", + "useful", + "usefully", + "usefulness", + "uses", + "using", + "usually", + "v", + "value", + "various", + "'ve", + "'ve", + "very", + "via", + "viz", + "vol", + "vols", + "vs", + "w", + "want", + "wants", + "was", + "wasn't", + "wasnt", + "way", + "we", + "wed", + "welcome", + "we'll", + "well", + "went", + "were", + "weren't", + "werent", + "we've", + "weve", + "what", + "whatever", + "what'll", + "whatll", + "whats", + "when", + "whence", + "whenever", + "where", + "whereafter", + "whereas", + "whereby", + "wherein", + "wheres", + "whereupon", + "wherever", + "whether", + "which", + "while", + "whim", + "whither", + "who", + "whod", + "whoever", + "whole", + "who'll", + "wholl", + "whom", + "whomever", + "whos", + "whose", + "why", + "widely", + "will", + "willing", + "wish", + "with", + "within", + "without", + "won't", + "wont", + "words", + "would", + "wouldn't", + "wouldnt", + "www", + "x", + "y", + "yes", + "yet", + "you", + "youd", + "you'll", + "youll", + "your", + "youre", + "yours", + "yourself", + "yourselves", + "you've", + "youve", + "z", + "zero" + ] +} diff --git a/aio/tools/transforms/angular-base-package/processors/splitDescription.js b/aio/tools/transforms/angular-base-package/processors/splitDescription.js new file mode 100644 index 0000000000..dd5f584802 --- /dev/null +++ b/aio/tools/transforms/angular-base-package/processors/splitDescription.js @@ -0,0 +1,28 @@ +/** + * Split the description (of selected docs) into: + * * `shortDescription`: the first paragraph + * * `description`: the rest of the paragraphs + */ +module.exports = function splitDescription() { + return { + $runAfter: ['tags-extracted'], + $runBefore: ['processing-docs'], + docTypes: [], + $process(docs) { + docs.forEach(doc => { + if (this.docTypes.indexOf(doc.docType) !== -1 && doc.description !== undefined) { + const description = doc.description.trim(); + const endOfParagraph = description.search(/\n\s*\n/); + if (endOfParagraph === -1) { + doc.shortDescription = description; + doc.description = ''; + } else { + doc.shortDescription = description.substr(0, endOfParagraph).trim(); + doc.description = description.substr(endOfParagraph).trim(); + } + } + }); + } + }; +}; + diff --git a/aio/tools/transforms/angular-base-package/processors/splitDescription.spec.js b/aio/tools/transforms/angular-base-package/processors/splitDescription.spec.js new file mode 100644 index 0000000000..313aa51cab --- /dev/null +++ b/aio/tools/transforms/angular-base-package/processors/splitDescription.spec.js @@ -0,0 +1,71 @@ +const testPackage = require('../../helpers/test-package'); +const processorFactory = require('./splitDescription'); +const Dgeni = require('dgeni'); + +describe('splitDescription processor', () => { + + it('should be available on the injector', () => { + const dgeni = new Dgeni([testPackage('angular-api-package')]); + const injector = dgeni.configureInjector(); + const processor = injector.get('splitDescription'); + expect(processor.$process).toBeDefined(); + }); + + it('should run before the correct processor', () => { + const processor = processorFactory(); + expect(processor.$runBefore).toEqual(['processing-docs']); + }); + + it('should run after the correct processor', () => { + const processor = processorFactory(); + expect(processor.$runAfter).toEqual(['tags-extracted']); + }); + + it('should split the `description` property into the first paragraph and other paragraphs', () => { + const processor = processorFactory(); + processor.docTypes = ['test']; + const docs = [ + { docType: 'test' }, + { docType: 'test', description: '' }, + { docType: 'test', description: 'abc' }, + { docType: 'test', description: 'abc\n' }, + { docType: 'test', description: 'abc\n\n' }, + { docType: 'test', description: 'abc\ncde' }, + { docType: 'test', description: 'abc\ncde\n' }, + { docType: 'test', description: 'abc\n\ncde' }, + { docType: 'test', description: 'abc\n \ncde' }, + { docType: 'test', description: 'abc\n\n\ncde' }, + { docType: 'test', description: 'abc\n\ncde\nfgh' }, + { docType: 'test', description: 'abc\n\ncde\n\nfgh' }, + ]; + processor.$process(docs); + expect(docs).toEqual([ + { docType: 'test' }, + { docType: 'test', shortDescription: '', description: '' }, + { docType: 'test', shortDescription: 'abc', description: '' }, + { docType: 'test', shortDescription: 'abc', description: '' }, + { docType: 'test', shortDescription: 'abc', description: '' }, + { docType: 'test', shortDescription: 'abc\ncde', description: '' }, + { docType: 'test', shortDescription: 'abc\ncde', description: '' }, + { docType: 'test', shortDescription: 'abc', description: 'cde' }, + { docType: 'test', shortDescription: 'abc', description: 'cde' }, + { docType: 'test', shortDescription: 'abc', description: 'cde' }, + { docType: 'test', shortDescription: 'abc', description: 'cde\nfgh' }, + { docType: 'test', shortDescription: 'abc', description: 'cde\n\nfgh' }, + ]); + }); + + it('should ignore docs that do not match the specified doc types', () => { + const processor = processorFactory(); + processor.docTypes = ['test']; + const docs = [ + { docType: 'test', description: 'abc\n\ncde' }, + { docType: 'other', description: 'abc\n\ncde' } + ]; + processor.$process(docs); + expect(docs).toEqual([ + { docType: 'test', shortDescription: 'abc', description: 'cde' }, + { docType: 'other', description: 'abc\n\ncde' } + ]); + }); +}); \ No newline at end of file diff --git a/aio/tools/transforms/angular-errors-package/index.js b/aio/tools/transforms/angular-errors-package/index.js new file mode 100644 index 0000000000..961b727739 --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/index.js @@ -0,0 +1,87 @@ +/** + * @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 + */ +const path = require('canonical-path'); +const Package = require('dgeni').Package; +const basePackage = require('../angular-base-package'); +const contentPackage = require('../content-package'); +const {CONTENTS_PATH, TEMPLATES_PATH, requireFolder} = require('../config'); + +const errorPackage = new Package('angular-errors', [basePackage, contentPackage]); +errorPackage.factory(require('./readers/error')) + .processor(require('./processors/processErrorDocs')) + .processor(require('./processors/processErrorsContainerDoc')) + + // Where do we find the error documentation files? + .config(function(readFilesProcessor, errorFileReader) { + readFilesProcessor.fileReaders.push(errorFileReader); + readFilesProcessor.sourceFiles = readFilesProcessor.sourceFiles.concat([ + { + basePath: CONTENTS_PATH, + include: CONTENTS_PATH + '/errors/**/*.md', + exclude: CONTENTS_PATH + '/errors/index.md', + fileReader: 'errorFileReader' + }, + { + basePath: CONTENTS_PATH, + include: CONTENTS_PATH + '/errors/index.md', + fileReader: 'contentFileReader' + }, + ]); + }) + + // Here we compute the `id`, `code`, `aliases`, `path` and `outputPath` for the `error` docs. + // * The `id` is the same as the `path` (the source path with the `.md` stripped off). + // * The `code` is the id without any containing paths (currently all errors must be on the top + // level). + // * The `aliases` are used for automatic code linking and search terms. + .config(function(computeIdsProcessor, computePathsProcessor) { + computeIdsProcessor.idTemplates.push({ + docTypes: ['error'], + getId: function(doc) { + return doc.fileInfo + .relativePath + // strip off the extension + .replace(/\.\w*$/, ''); + }, + getAliases: function(doc) { + doc.code = path.basename(doc.id); + return [doc.id, doc.code]; + } + }); + + computePathsProcessor.pathTemplates = computePathsProcessor.pathTemplates.concat([ + { + docTypes: ['error'], + getPath: (doc) => doc.id, + outputPathTemplate: '${path}.json', + }, + ]); + }) + + // Configure jsdoc-style tag parsing + .config(function(parseTagsProcessor, getInjectables) { + // Load up all the tag definitions in the tag-defs folder + parseTagsProcessor.tagDefinitions = parseTagsProcessor.tagDefinitions.concat( + getInjectables(requireFolder(__dirname, './tag-defs'))); + }) + + // The templates that define how the `error` and `error-container` doc-types are rendered are + // found in the `TEMPLATES_PATH/error` directory. + .config(function(templateFinder) { + templateFinder.templateFolders.unshift(path.resolve(TEMPLATES_PATH, 'error')); + }) + + // The AIO application expects content files to be provided as JSON files that it requests via + // HTTP. So here we tell the `convertToJsonProcessor` to include docs of type `error` in those + // that it converts. + .config(function(convertToJsonProcessor, postProcessHtml) { + convertToJsonProcessor.docTypes.push('error'); + postProcessHtml.docTypes.push('error'); + }); + +module.exports = errorPackage; \ No newline at end of file diff --git a/aio/tools/transforms/angular-errors-package/processors/processErrorDocs.js b/aio/tools/transforms/angular-errors-package/processors/processErrorDocs.js new file mode 100644 index 0000000000..ca661fcfd2 --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/processors/processErrorDocs.js @@ -0,0 +1,55 @@ +module.exports = function processErrorDocs(createDocMessage) { + return { + $runAfter: ['extra-docs-added'], + $runBefore: ['rendering-docs'], + $process(docs) { + const navigationDoc = docs.find(doc => doc.docType === 'navigation-json'); + const errorsNode = navigationDoc && findErrorsNode(navigationDoc.data['SideNav']); + + if (!errorsNode) { + throw new Error(createDocMessage( + 'Missing `errors` url - This node is needed as a place to insert the generated errors docs.', + navigationDoc)); + } + + docs.forEach(doc => { + if (doc.docType === 'error') { + // Add to navigation doc + const title = `${doc.code}: ${doc.name}`; + errorsNode.children.push({url: doc.path, title: title, tooltip: doc.name}); + } + }); + }, + }; +}; + +/** + * Look for the `errors` navigation node. It is the node whose first child has `url: 'errors'`. + * (NOTE: Using the URL instead of the title, because it is more robust.) + * + * We will "recursively" check all navigation nodes and their children (in breadth-first order), + * until we find the `errors` node. Keep a list of nodes lists to check. + * (NOTE: Each item in the list is a LIST of nodes.) + */ +function findErrorsNode(nodes) { + const nodesList = [nodes]; + + while (nodesList.length > 0) { + // Get the first item from the list of nodes lists. + const currentNodes = nodesList.shift(); + const errorsNode = currentNodes.find(isErrorsNode); + + // One of the nodes in `currentNodes` was the `errors` node. Return it. + if (errorsNode) return errorsNode; + + // The `errors` node is not in `currentNodes`. Check each node's children (if any). + currentNodes.forEach(node => node.children && nodesList.push(node.children)); + } + + // We checked all navigation nodes and their children and did not find the `errors` node. + return undefined; +} + +function isErrorsNode(node) { + return node.children && node.children.length && node.children[0].url === 'errors'; +} diff --git a/aio/tools/transforms/angular-errors-package/processors/processErrorDocs.spec.js b/aio/tools/transforms/angular-errors-package/processors/processErrorDocs.spec.js new file mode 100644 index 0000000000..908f0003a6 --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/processors/processErrorDocs.spec.js @@ -0,0 +1,125 @@ +const testPackage = require('../../helpers/test-package'); +const Dgeni = require('dgeni'); + +describe('processErrorDocs processor', () => { + let dgeni, injector, processor, createDocMessage; + + beforeEach(() => { + dgeni = new Dgeni([testPackage('angular-errors-package')]); + injector = dgeni.configureInjector(); + processor = injector.get('processErrorDocs'); + createDocMessage = injector.get('createDocMessage'); + }); + + it('should be available on the injector', () => { + expect(processor.$process).toBeDefined(); + }); + + it('should run after the correct processor', () => { + expect(processor.$runAfter).toEqual(['extra-docs-added']); + }); + + it('should run before the correct processor', () => { + expect(processor.$runBefore).toEqual(['rendering-docs']); + }); + + it('should add the error to the `errors` node in the navigation doc if there is a top level node with a `errors` url', + () => { + const errorDoc = { + docType: 'error', + name: 'error1', + code: '888', + path: 'errors/error1', + }; + const navigation = { + docType: 'navigation-json', + data: { + SideNav: [ + {url: 'some/page', title: 'Some Page'}, + { + title: 'Errors', + children: [{'title': 'Overview', 'url': 'errors'}], + }, + {url: 'other/page', title: 'Other Page'}, + ], + }, + }; + processor.$process([errorDoc, navigation]); + expect(navigation.data.SideNav[1].title).toEqual('Errors'); + expect(navigation.data.SideNav[1].children).toEqual([ + {url: 'errors', title: 'Overview'}, + {url: 'errors/error1', title: '888: error1', tooltip: 'error1'}, + ]); + }); + + it('should detect the `errors` node if it is nested in another node', () => { + const errorDoc = { + docType: 'error', + name: 'error1', + code: '888', + path: 'errors/error1', + }; + const navigation = { + docType: 'navigation-json', + data: { + SideNav: [ + {url: 'some/page', title: 'Some Page'}, + { + title: 'Errors Grandparent', + children: [ + {url: 'some/nested/page', title: 'Some Nested Page'}, + { + title: 'Errors Parent', + children: [ + {url: 'some/more/nested/page', title: 'Some More Nested Page'}, + { + title: 'Errors', + children: [{'title': 'Overview', 'url': 'errors'}], + }, + {url: 'other/more/nested/page', title: 'Other More Nested Page'}, + ], + }, + {url: 'other/nested/page', title: 'Other Nested Page'}, + ], + }, + {url: 'other/page', title: 'Other Page'}, + ], + }, + }; + + processor.$process([errorDoc, navigation]); + + const errorsContainerNode = navigation.data.SideNav[1].children[1].children[1]; + expect(errorsContainerNode.title).toEqual('Errors'); + expect(errorsContainerNode.children).toEqual([ + {url: 'errors', title: 'Overview'}, + {url: 'errors/error1', title: '888: error1', tooltip: 'error1'}, + ]); + }); + + it('should complain if there is no child with `errors` url', () => { + const errorDoc = { + docType: 'error', + name: 'error1', + code: '888', + path: 'errors/error1', + }; + const navigation = { + docType: 'navigation-json', + data: { + SideNav: [ + {url: 'some/page', title: 'Some Page'}, { + title: 'Errors', + tooltip: 'Angular Error reference', + children: [{'title': 'Overview', 'url': 'not-errors'}] + }, + {url: 'other/page', title: 'Other Page'} + ] + } + }; + expect(() => processor.$process([errorDoc, navigation])) + .toThrowError(createDocMessage( + 'Missing `errors` url - This node is needed as a place to insert the generated errors docs.', + navigation)); + }); +}); diff --git a/aio/tools/transforms/angular-errors-package/processors/processErrorsContainerDoc.js b/aio/tools/transforms/angular-errors-package/processors/processErrorsContainerDoc.js new file mode 100644 index 0000000000..876674beff --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/processors/processErrorsContainerDoc.js @@ -0,0 +1,11 @@ +module.exports = function processErrorsContainerDoc() { + return { + $runAfter: ['extra-docs-added'], + $runBefore: ['rendering-docs'], + $process(docs) { + const errorsDoc = docs.find(doc => doc.id === 'errors/index'); + errorsDoc.id = 'errors-container'; + errorsDoc.errors = docs.filter(doc => doc.docType === 'error'); + } + }; +}; diff --git a/aio/tools/transforms/angular-errors-package/processors/processErrorsContainerDoc.spec.js b/aio/tools/transforms/angular-errors-package/processors/processErrorsContainerDoc.spec.js new file mode 100644 index 0000000000..ef8cb17276 --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/processors/processErrorsContainerDoc.spec.js @@ -0,0 +1,22 @@ +const testPackage = require('../../helpers/test-package'); +const processorFactory = require('./processErrorsContainerDoc'); +const Dgeni = require('dgeni'); + +describe('processErrorsContainerDoc processor', () => { + it('should be available on the injector', () => { + const dgeni = new Dgeni([testPackage('angular-errors-package')]); + const injector = dgeni.configureInjector(); + const processor = injector.get('processErrorsContainerDoc'); + 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']); + }); +}); diff --git a/aio/tools/transforms/angular-errors-package/readers/error.js b/aio/tools/transforms/angular-errors-package/readers/error.js new file mode 100644 index 0000000000..1a974ba02a --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/readers/error.js @@ -0,0 +1,23 @@ +/** + * @dgService + * @description + * This file reader will pull the contents from a text file (by default .md) + * + * The doc will initially have the form: + * ``` + * { + * docType: 'error', + * content: 'the content of the file', + * } + * ``` + */ +module.exports = function errorFileReader() { + return { + name: 'errorFileReader', + defaultPattern: /\.md$/, + getDocs: function(fileInfo) { + // We return a single element array because content files only contain one document + return [{docType: 'error', content: fileInfo.content}]; + } + }; +}; diff --git a/aio/tools/transforms/angular-errors-package/readers/error.spec.js b/aio/tools/transforms/angular-errors-package/readers/error.spec.js new file mode 100644 index 0000000000..bfc07b2073 --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/readers/error.spec.js @@ -0,0 +1,44 @@ +const Dgeni = require('dgeni'); +const path = require('canonical-path'); +const testPackage = require('../../helpers/test-package'); + +describe('errorFileReader', () => { + let dgeni, injector, fileReader; + + beforeEach(() => { + dgeni = new Dgeni([testPackage('angular-errors-package', false)]); + injector = dgeni.configureInjector(); + fileReader = injector.get('errorFileReader'); + }); + + function createFileInfo(file, content, basePath) { + return { + fileReader: fileReader.name, + filePath: file, + baseName: path.basename(file, path.extname(file)), + extension: path.extname(file).replace(/^\./, ''), + basePath: basePath, + relativePath: path.relative(basePath, file), + content: content + }; + } + + describe('defaultPattern', () => { + it('should match .md files', () => { + expect(fileReader.defaultPattern.test('abc.md')).toBeTruthy(); + expect(fileReader.defaultPattern.test('abc.js')).toBeFalsy(); + }); + }); + + + describe('getDocs', () => { + it('should return an object containing info about the file and its contents', () => { + const fileInfo = createFileInfo( + 'project/path/modules/someModule/foo/docs/subfolder/bar.md', 'A load of content', + 'project/path'); + expect(fileReader.getDocs(fileInfo)).toEqual([ + {docType: 'error', content: 'A load of content'} + ]); + }); + }); +}); diff --git a/aio/tools/transforms/angular-errors-package/tag-defs/category.js b/aio/tools/transforms/angular-errors-package/tag-defs/category.js new file mode 100644 index 0000000000..51619bc782 --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/tag-defs/category.js @@ -0,0 +1,3 @@ +module.exports = function() { + return {name: 'category'}; +}; diff --git a/aio/tools/transforms/angular-errors-package/tag-defs/debugging.js b/aio/tools/transforms/angular-errors-package/tag-defs/debugging.js new file mode 100644 index 0000000000..c6e46e7054 --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/tag-defs/debugging.js @@ -0,0 +1,3 @@ +module.exports = function() { + return {name: 'debugging'}; +}; diff --git a/aio/tools/transforms/angular-errors-package/tag-defs/shortDescription.js b/aio/tools/transforms/angular-errors-package/tag-defs/shortDescription.js new file mode 100644 index 0000000000..60a54b99c9 --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/tag-defs/shortDescription.js @@ -0,0 +1,3 @@ +module.exports = function() { + return {name: 'shortDescription'}; +}; diff --git a/aio/tools/transforms/angular-errors-package/tag-defs/videoUrl.js b/aio/tools/transforms/angular-errors-package/tag-defs/videoUrl.js new file mode 100644 index 0000000000..a99676dba6 --- /dev/null +++ b/aio/tools/transforms/angular-errors-package/tag-defs/videoUrl.js @@ -0,0 +1,3 @@ +module.exports = function() { + return {name: 'videoUrl'}; +}; diff --git a/aio/tools/transforms/authors-package/base-authoring-package.js b/aio/tools/transforms/authors-package/base-authoring-package.js new file mode 100644 index 0000000000..3dbfd6cb46 --- /dev/null +++ b/aio/tools/transforms/authors-package/base-authoring-package.js @@ -0,0 +1,36 @@ +/** + * @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 + */ +const Package = require('dgeni').Package; +const basePackage = require('../angular-base-package'); + +/** + * A base package used by all the authoring packages in this folder. + * + * This package turns off lots of the potentially fatal checks to allow + * doc-gen to complete when authors are using the `docs-watch` or `serve-and-sync` + * jobs. + */ +const baseAuthoringPackage = new Package('base-authoring', [basePackage]); +baseAuthoringPackage + .config(function(checkAnchorLinksProcessor, checkForUnusedExampleRegions) { + // These are disabled here to prevent false negatives for the `docs-watch` task. + checkAnchorLinksProcessor.$enabled = false; + checkForUnusedExampleRegions.$enabled = false; + }) + + .config(function(linkInlineTagDef) { + // Do not fail the processing if there is an invalid link + linkInlineTagDef.failOnBadLink = false; + }) + + .config(function(renderExamples) { + // Do not fail the processing if there is a broken example + renderExamples.ignoreBrokenExamples = true; + }); + +module.exports = baseAuthoringPackage; diff --git a/aio/tools/transforms/authors-package/utils.js b/aio/tools/transforms/authors-package/utils.js new file mode 100644 index 0000000000..d36515d92b --- /dev/null +++ b/aio/tools/transforms/authors-package/utils.js @@ -0,0 +1,11 @@ +/** + * @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 + */ + +module.exports = { + codeExampleMatcher: /]*\bpath="([^"]+)"/g, +}; diff --git a/aio/tools/transforms/content-package/tag-defs/reviewed.js b/aio/tools/transforms/content-package/tag-defs/reviewed.js new file mode 100644 index 0000000000..bcbe6bd18c --- /dev/null +++ b/aio/tools/transforms/content-package/tag-defs/reviewed.js @@ -0,0 +1,21 @@ +/** + * @reviewed + * + * Use this tag-def to indicate the date when this document was last reviewed. + * The date-string will be passed to the `Date()` constructor. + */ +module.exports = function(createDocMessage) { + return { + name: 'reviewed', + transforms(doc, tag, value) { + if (!/^\d{4}-\d{1,2}-\d{1,2}/.test(value.trim())) { + throw new Error(createDocMessage( + `Tag Error: @${tag.tagName} tag is missing the required date in the form "yyyy-mm-dd" but got "${value}"\n`, + doc)); + } + return { + date: new Date(value), + }; + } + }; +}; diff --git a/aio/tools/transforms/templates/error/error.template.html b/aio/tools/transforms/templates/error/error.template.html new file mode 100644 index 0000000000..46093c2503 --- /dev/null +++ b/aio/tools/transforms/templates/error/error.template.html @@ -0,0 +1,28 @@ +{% import "lib/githubLinks.html" as github -%} + +

{$ doc.code $}: {$ doc.shortDescription $}

+ + +{% if doc.videoUrl.length %} +
+ +
+{% endif%} + +{% block content %} +
+

Description

+ {$ doc.description | marked $} +
+
+
+

Debugging the error

+ {$ doc.debugging | marked $} +
+{% endblock %} \ No newline at end of file diff --git a/aio/tools/transforms/templates/error/errors-container.template.html b/aio/tools/transforms/templates/error/errors-container.template.html new file mode 100644 index 0000000000..606a9f6716 --- /dev/null +++ b/aio/tools/transforms/templates/error/errors-container.template.html @@ -0,0 +1,18 @@ +{% extends 'content.template.html' -%} + +{% block content %} +
+ {$ doc.description | marked $} +
+ + +{% endblock %} \ No newline at end of file