shred and watch build tooling ( dgeni, gulp, jade)

This commit is contained in:
Jay Traband
2015-08-03 17:45:58 -07:00
committed by YuCheng Hu
parent 2ef750d8cf
commit 1d4c56d7f8
4521 changed files with 584119 additions and 0 deletions
+87
View File
@@ -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;
@@ -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';
}
@@ -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));
});
});
@@ -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');
}
};
};
@@ -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']);
});
});
@@ -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}];
}
};
};
@@ -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'}
]);
});
});
});
@@ -0,0 +1,3 @@
module.exports = function() {
return {name: 'category'};
};
@@ -0,0 +1,3 @@
module.exports = function() {
return {name: 'debugging'};
};
@@ -0,0 +1,3 @@
module.exports = function() {
return {name: 'shortDescription'};
};
@@ -0,0 +1,3 @@
module.exports = function() {
return {name: 'videoUrl'};
};