| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  | const blockC = require('./region-matchers/block-c'); | 
					
						
							|  |  |  | const html = require('./region-matchers/html'); | 
					
						
							|  |  |  | const inlineC = require('./region-matchers/inline-c'); | 
					
						
							|  |  |  | const inlineCOnly = require('./region-matchers/inline-c-only'); | 
					
						
							|  |  |  | const inlineHash = require('./region-matchers/inline-hash'); | 
					
						
							|  |  |  | const NO_NAME_REGION = ''; | 
					
						
							|  |  |  | const DEFAULT_PLASTER = '. . .'; | 
					
						
							|  |  |  | const {mapObject} = require('../utils'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = function regionParser() { | 
					
						
							|  |  |  |   return regionParserImpl; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | regionParserImpl.regionMatchers = { | 
					
						
							|  |  |  |   ts: inlineC, | 
					
						
							|  |  |  |   js: inlineC, | 
					
						
							|  |  |  |   es6: inlineC, | 
					
						
							|  |  |  |   dart: inlineC, | 
					
						
							|  |  |  |   html: html, | 
					
						
							|  |  |  |   css: blockC, | 
					
						
							|  |  |  |   yaml: inlineHash, | 
					
						
							| 
									
										
										
											
												feat(aio): support annotating JSON files with doc-regions
This change allows the example writer to add doc-region annotations to
files that do not allow comments. This is done by creating a clone of the
file and adding `.annotated` to the file name. This new file can contain
inline `// ...` comments that can be used to annotate the doc regions.
Example:
**package.json**
```
{
  "name": "angular.io",
  "version": "0.0.0",
  "main": "index.js",
  "repository": "git@github.com:angular/angular.git",
  "author": "Angular",
  "license": "MIT",
  "private": true,
}
````
**package.json.annotated**
```
{
  "name": "angular.io",
// #docregion version
  "version": "0.0.0",
// #enddocregion
  "main": "index.js",
  "repository": "git@github.com:angular/angular.git",
  "author": "Angular",
  "license": "MIT",
  "private": true,
}
````
This region can then be referenced in examples just like any other doc region:
```
{@example 'package.json' region="version"}
```
											
										 
											2017-02-21 13:09:57 +00:00
										 |  |  |   jade: inlineCOnly, | 
					
						
							| 
									
										
										
										
											2017-02-22 18:12:34 +00:00
										 |  |  |   json: inlineC, | 
					
						
							| 
									
										
										
											
												feat(aio): support annotating JSON files with doc-regions
This change allows the example writer to add doc-region annotations to
files that do not allow comments. This is done by creating a clone of the
file and adding `.annotated` to the file name. This new file can contain
inline `// ...` comments that can be used to annotate the doc regions.
Example:
**package.json**
```
{
  "name": "angular.io",
  "version": "0.0.0",
  "main": "index.js",
  "repository": "git@github.com:angular/angular.git",
  "author": "Angular",
  "license": "MIT",
  "private": true,
}
````
**package.json.annotated**
```
{
  "name": "angular.io",
// #docregion version
  "version": "0.0.0",
// #enddocregion
  "main": "index.js",
  "repository": "git@github.com:angular/angular.git",
  "author": "Angular",
  "license": "MIT",
  "private": true,
}
````
This region can then be referenced in examples just like any other doc region:
```
{@example 'package.json' region="version"}
```
											
										 
											2017-02-21 13:09:57 +00:00
										 |  |  |   'json.annotated': inlineC | 
					
						
							| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param contents string | 
					
						
							|  |  |  |  * @param fileType string | 
					
						
							|  |  |  |  * @returns {contents: string, regions: {[regionName: string]: string}} | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | function regionParserImpl(contents, fileType) { | 
					
						
							|  |  |  |   const regionMatcher = regionParserImpl.regionMatchers[fileType]; | 
					
						
							|  |  |  |   const openRegions = []; | 
					
						
							|  |  |  |   const regionMap = {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (regionMatcher) { | 
					
						
							|  |  |  |     let plaster = regionMatcher.createPlasterComment(DEFAULT_PLASTER); | 
					
						
							|  |  |  |     const lines = contents.split(/\r?\n/).filter((line, index) => { | 
					
						
							|  |  |  |       const startRegion = line.match(regionMatcher.regionStartMatcher); | 
					
						
							|  |  |  |       const endRegion = line.match(regionMatcher.regionEndMatcher); | 
					
						
							|  |  |  |       const updatePlaster = line.match(regionMatcher.plasterMatcher); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // start region processing
 | 
					
						
							|  |  |  |       if (startRegion) { | 
					
						
							|  |  |  |         // open up the specified region
 | 
					
						
							| 
									
										
										
										
											2017-02-02 08:19:20 +00:00
										 |  |  |         const regionNames = getRegionNames(startRegion[1]); | 
					
						
							|  |  |  |         if (regionNames.length === 0) { | 
					
						
							|  |  |  |           regionNames.push(''); | 
					
						
							| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-02-02 08:19:20 +00:00
										 |  |  |         regionNames.forEach(regionName => { | 
					
						
							|  |  |  |           const region = regionMap[regionName]; | 
					
						
							|  |  |  |           if (region) { | 
					
						
							|  |  |  |             if (region.open) { | 
					
						
							|  |  |  |               throw new RegionParserError( | 
					
						
							|  |  |  |                   `Tried to open a region, named "${regionName}", that is already open`, index); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             region.open = true; | 
					
						
							|  |  |  |             region.lines.push(plaster); | 
					
						
							|  |  |  |           } else { | 
					
						
							|  |  |  |             regionMap[regionName] = {lines: [], open: true}; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           openRegions.push(regionName); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // end region processing
 | 
					
						
							|  |  |  |       } else if (endRegion) { | 
					
						
							|  |  |  |         if (openRegions.length === 0) { | 
					
						
							|  |  |  |           throw new RegionParserError('Tried to close a region when none are open', index); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // close down the specified region (or most recent if no name is given)
 | 
					
						
							| 
									
										
										
										
											2017-02-02 08:19:20 +00:00
										 |  |  |         const regionNames = getRegionNames(endRegion[1]); | 
					
						
							|  |  |  |         if (regionNames.length === 0) { | 
					
						
							|  |  |  |           regionNames.push(openRegions[openRegions.length - 1]); | 
					
						
							| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-02-02 08:19:20 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         regionNames.forEach(regionName => { | 
					
						
							|  |  |  |           const region = regionMap[regionName]; | 
					
						
							|  |  |  |           if (!region || !region.open) { | 
					
						
							|  |  |  |             throw new RegionParserError( | 
					
						
							|  |  |  |                 `Tried to close a region, named "${regionName}", that is not open`, index); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           region.open = false; | 
					
						
							|  |  |  |           removeLast(openRegions, regionName); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // doc plaster processing
 | 
					
						
							|  |  |  |       } else if (updatePlaster) { | 
					
						
							|  |  |  |         plaster = regionMatcher.createPlasterComment(updatePlaster[1].trim()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // simple line of content processing
 | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         openRegions.forEach(regionName => regionMap[regionName].lines.push(line)); | 
					
						
							|  |  |  |         // do not filter out this line from the content
 | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // this line contained an annotation so let's filter it out
 | 
					
						
							|  |  |  |       return false; | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2017-02-22 18:12:34 +00:00
										 |  |  |     if (!regionMap['']) { | 
					
						
							|  |  |  |       regionMap[''] = {lines}; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  |     return { | 
					
						
							|  |  |  |       contents: lines.join('\n'), | 
					
						
							| 
									
										
										
										
											2017-03-27 08:57:23 +01:00
										 |  |  |       regions: mapObject(regionMap, (regionName, region) => leftAlign(region.lines).join('\n')) | 
					
						
							| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } else { | 
					
						
							|  |  |  |     return {contents, regions: {}}; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-02 08:19:20 +00:00
										 |  |  | function getRegionNames(input) { | 
					
						
							| 
									
										
										
										
											2017-02-21 17:54:57 +00:00
										 |  |  |   return (input.trim() === '') ? [] : input.split(',').map(name => name.trim()); | 
					
						
							| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function removeLast(array, item) { | 
					
						
							|  |  |  |   const index = array.lastIndexOf(item); | 
					
						
							|  |  |  |   array.splice(index, 1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-03-27 08:57:23 +01:00
										 |  |  | function leftAlign(lines) { | 
					
						
							|  |  |  |   let indent = Number.MAX_VALUE; | 
					
						
							|  |  |  |   lines.forEach(line => { | 
					
						
							|  |  |  |     const lineIndent = line.search(/\S/); | 
					
						
							|  |  |  |     if (lineIndent !== -1) { | 
					
						
							|  |  |  |       indent = Math.min(lineIndent, indent); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return lines.map(line => line.substr(indent)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-21 07:25:27 +00:00
										 |  |  | function RegionParserError(message, index) { | 
					
						
							|  |  |  |   const lineNum = index + 1; | 
					
						
							| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  |   this.message = `regionParser: ${message} (at line ${lineNum}).`; | 
					
						
							|  |  |  |   this.lineNum = lineNum; | 
					
						
							|  |  |  |   this.stack = (new Error()).stack; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | RegionParserError.prototype = Object.create(Error.prototype); | 
					
						
							|  |  |  | RegionParserError.prototype.constructor = RegionParserError; |