build(aio): support guide authoring
This commit implements various tags, inline tags and changes the markdown renderer to use Rho. This enables us to generate guide type documents.
This commit is contained in:
parent
470997ebb9
commit
12f03b90fd
|
@ -88,12 +88,20 @@ The result looks like this:
|
|||
and download the source code from there
|
||||
or simply read the pertinent source here.
|
||||
|
||||
{@tabs}
|
||||
{@example 'cb-component-relative-paths/ts/app/some.component.ts' -title='app/some.component.ts'}
|
||||
{@example 'cb-component-relative-paths/ts/app/some.component.html' -title='app/some.component.html'}
|
||||
{@example 'cb-component-relative-paths/ts/app/some.component.css' -title='app/some.component.css'}
|
||||
{@example 'cb-component-relative-paths/ts/app/app.component.ts' -title='app/app.component.ts'}
|
||||
{@endtabs}
|
||||
<md-tab-group>
|
||||
<md-tab label="app/some.component.ts">
|
||||
{@example 'cb-component-relative-paths/ts/app/some.component.ts'}
|
||||
</md-tab>
|
||||
<md-tab label="app/some.component.html">
|
||||
{@example 'cb-component-relative-paths/ts/app/some.component.html'}
|
||||
</md-tab>
|
||||
<md-tab label="app/some.component.css">
|
||||
{@example 'cb-component-relative-paths/ts/app/some.component.css'}
|
||||
</md-tab>
|
||||
<md-tab label="app/app.component.ts">
|
||||
{@example 'cb-component-relative-paths/ts/app/app.component.ts'}
|
||||
</md-tab>
|
||||
<md-tab-group>
|
||||
|
||||
|
||||
{@a why-default}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
<h1>{$ doc.title $}</h1>
|
||||
{$ doc.description | marked $}
|
|
@ -3351,6 +3351,10 @@
|
|||
"version": "0.1.7",
|
||||
"dev": true
|
||||
},
|
||||
"has-color": {
|
||||
"version": "0.1.7",
|
||||
"dev": true
|
||||
},
|
||||
"has-cors": {
|
||||
"version": "1.1.0",
|
||||
"dev": true
|
||||
|
@ -3387,6 +3391,10 @@
|
|||
"version": "2.1.4",
|
||||
"dev": true
|
||||
},
|
||||
"html": {
|
||||
"version": "1.0.0",
|
||||
"dev": true
|
||||
},
|
||||
"htmlparser2": {
|
||||
"version": "3.9.2",
|
||||
"dev": true,
|
||||
|
@ -4439,6 +4447,28 @@
|
|||
"version": "0.3.4",
|
||||
"dev": true
|
||||
},
|
||||
"nomnom": {
|
||||
"version": "1.8.1",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "1.0.0",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "0.4.0",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "0.1.1",
|
||||
"dev": true
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.6.0",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"nopt": {
|
||||
"version": "3.0.6",
|
||||
"dev": true
|
||||
|
@ -5042,6 +5072,10 @@
|
|||
"version": "2.5.1",
|
||||
"dev": true
|
||||
},
|
||||
"rho": {
|
||||
"version": "0.3.0",
|
||||
"dev": true
|
||||
},
|
||||
"right-align": {
|
||||
"version": "0.1.3",
|
||||
"dev": true
|
||||
|
|
|
@ -2735,7 +2735,7 @@
|
|||
},
|
||||
"canonical-path": {
|
||||
"version": "0.0.2",
|
||||
"from": "canonical-path@0.0.2",
|
||||
"from": "canonical-path@>=0.0.2 <0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/canonical-path/-/canonical-path-0.0.2.tgz",
|
||||
"dev": true
|
||||
},
|
||||
|
@ -3476,7 +3476,7 @@
|
|||
},
|
||||
"dgeni-packages": {
|
||||
"version": "0.16.5",
|
||||
"from": "dgeni-packages@>=0.16.5 <0.17.0",
|
||||
"from": "dgeni-packages@>=0.16.0 <0.17.0",
|
||||
"resolved": "https://registry.npmjs.org/dgeni-packages/-/dgeni-packages-0.16.5.tgz",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
|
@ -4853,6 +4853,12 @@
|
|||
"resolved": "https://registry.npmjs.org/has-binary/-/has-binary-0.1.7.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"has-color": {
|
||||
"version": "0.1.7",
|
||||
"from": "has-color@>=0.1.0 <0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/has-color/-/has-color-0.1.7.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"has-cors": {
|
||||
"version": "1.1.0",
|
||||
"from": "has-cors@1.1.0",
|
||||
|
@ -4907,6 +4913,12 @@
|
|||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.1.4.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"html": {
|
||||
"version": "1.0.0",
|
||||
"from": "html@*",
|
||||
"resolved": "https://registry.npmjs.org/html/-/html-1.0.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"htmlparser2": {
|
||||
"version": "3.9.2",
|
||||
"from": "htmlparser2@>=3.7.3 <4.0.0",
|
||||
|
@ -6451,6 +6463,38 @@
|
|||
"resolved": "https://registry.npmjs.org/node-watch/-/node-watch-0.3.4.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"nomnom": {
|
||||
"version": "1.8.1",
|
||||
"from": "nomnom@*",
|
||||
"resolved": "https://registry.npmjs.org/nomnom/-/nomnom-1.8.1.tgz",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": {
|
||||
"version": "1.0.0",
|
||||
"from": "ansi-styles@>=1.0.0 <1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-1.0.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "0.4.0",
|
||||
"from": "chalk@>=0.4.0 <0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-0.4.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "0.1.1",
|
||||
"from": "strip-ansi@>=0.1.0 <0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-0.1.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"underscore": {
|
||||
"version": "1.6.0",
|
||||
"from": "underscore@>=1.6.0 <1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"nopt": {
|
||||
"version": "3.0.6",
|
||||
"from": "nopt@>=2.0.0 <3.0.0||>=3.0.0 <4.0.0",
|
||||
|
@ -7344,6 +7388,12 @@
|
|||
"resolved": "https://registry.npmjs.org/rewire/-/rewire-2.5.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"rho": {
|
||||
"version": "0.3.0",
|
||||
"from": "rho@>=0.3.0 <0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/rho/-/rho-0.3.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"right-align": {
|
||||
"version": "0.1.3",
|
||||
"from": "right-align@>=0.1.1 <0.2.0",
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"zone.js": "^0.7.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "^1.0.14"
|
||||
"fsevents": "^1.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/angularjs": "^1.5.13-alpha",
|
||||
|
@ -77,6 +77,7 @@
|
|||
"protractor": "^4.0.11",
|
||||
"react": "^0.14.0",
|
||||
"rewire": "^2.3.3",
|
||||
"rho": "^0.3.0",
|
||||
"rollup": "^0.26.3",
|
||||
"rollup-plugin-commonjs": "^5.0.5",
|
||||
"selenium-webdriver": "^2.53.3",
|
||||
|
@ -95,4 +96,4 @@
|
|||
"yargs": "^3.31.0",
|
||||
"yarn": "^0.19.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ const linksPackage = require('../links-package');
|
|||
const examplesPackage = require('../examples-package');
|
||||
const targetPackage = require('../target-package');
|
||||
const cheatsheetPackage = require('../cheatsheet-package');
|
||||
const rhoPackage = require('../rho-package');
|
||||
|
||||
const PROJECT_ROOT = path.resolve(__dirname, '../../..');
|
||||
const API_SOURCE_PATH = path.resolve(PROJECT_ROOT, 'modules');
|
||||
|
@ -28,7 +29,7 @@ module.exports =
|
|||
'angular.io',
|
||||
[
|
||||
jsdocPackage, nunjucksPackage, typescriptPackage, linksPackage, examplesPackage,
|
||||
gitPackage, targetPackage, cheatsheetPackage
|
||||
gitPackage, targetPackage, cheatsheetPackage, rhoPackage
|
||||
])
|
||||
|
||||
// Register the processors
|
||||
|
@ -81,14 +82,25 @@ module.exports =
|
|||
|
||||
readFilesProcessor.basePath = PROJECT_ROOT;
|
||||
readFilesProcessor.sourceFiles = [
|
||||
{basePath: CONTENTS_PATH, include: CONTENTS_PATH + '/cheatsheet/*.md'}, {
|
||||
{
|
||||
basePath: CONTENTS_PATH,
|
||||
include: CONTENTS_PATH + '/cookbook/**/*.md',
|
||||
fileReader: 'contentFileReader'
|
||||
},
|
||||
{basePath: CONTENTS_PATH, include: CONTENTS_PATH + '/cheatsheet/*.md'},
|
||||
{
|
||||
basePath: API_SOURCE_PATH,
|
||||
include: API_SOURCE_PATH + '/@angular/examples/**/*',
|
||||
fileReader: 'exampleFileReader'
|
||||
}
|
||||
},
|
||||
{
|
||||
basePath: CONTENTS_PATH,
|
||||
include: CONTENTS_PATH + '/examples/**/*',
|
||||
fileReader: 'exampleFileReader'
|
||||
},
|
||||
];
|
||||
|
||||
collectExamples.exampleFolders = ['@angular/examples'];
|
||||
collectExamples.exampleFolders = ['@angular/examples', 'examples'];
|
||||
|
||||
generateKeywordsProcessor.ignoreWordsFile = 'tools/docs/angular.io-package/ignore.words';
|
||||
generateKeywordsProcessor.docTypesToIgnore = ['example-region'];
|
||||
|
@ -113,7 +125,7 @@ module.exports =
|
|||
|
||||
|
||||
// Configure jsdoc-style tag parsing
|
||||
.config(function(parseTagsProcessor, getInjectables) {
|
||||
.config(function(parseTagsProcessor, getInjectables, inlineTagProcessor) {
|
||||
// Load up all the tag definitions in the tag-defs folder
|
||||
parseTagsProcessor.tagDefinitions =
|
||||
parseTagsProcessor.tagDefinitions.concat(getInjectables(requireFolder('./tag-defs')));
|
||||
|
@ -127,6 +139,8 @@ module.exports =
|
|||
tagDef.transforms = [];
|
||||
}
|
||||
});
|
||||
|
||||
inlineTagProcessor.inlineTagDefinitions.push(require('./inline-tag-defs/anchor'));
|
||||
})
|
||||
|
||||
|
||||
|
@ -212,7 +226,8 @@ module.exports =
|
|||
pathTemplate: GUIDE_SEGMENT + '/cheatsheet.json',
|
||||
outputPathTemplate: '${path}'
|
||||
},
|
||||
{docTypes: ['example-region'], getOutputPath: function() {}}
|
||||
{docTypes: ['example-region'], getOutputPath: function() {}},
|
||||
{docTypes: ['content'], pathTemplate: '${id}', outputPathTemplate: '${path}.html'}
|
||||
];
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
module.exports = {
|
||||
name: 'a',
|
||||
description: 'A shorthand for creating heading anchors. Usage: `{@a some-id}`',
|
||||
handler: function(doc, tagName, tagDescription, docs) {
|
||||
return '<a id="' + tagDescription.trim() + '"></a>';
|
||||
}
|
||||
};
|
|
@ -15,6 +15,11 @@ module.exports = new Package('content', [jsdocPackage, linksPackage])
|
|||
readFilesProcessor.fileReaders.push(contentFileReader);
|
||||
})
|
||||
|
||||
.config(function(parseTagsProcessor, getInjectables) {
|
||||
parseTagsProcessor.tagDefinitions = parseTagsProcessor.tagDefinitions.concat(
|
||||
getInjectables(requireFolder('./tag-defs')));
|
||||
})
|
||||
|
||||
// Configure ids and paths
|
||||
.config(function(computeIdsProcessor, computePathsProcessor) {
|
||||
|
||||
|
@ -33,3 +38,10 @@ module.exports = new Package('content', [jsdocPackage, linksPackage])
|
|||
getAliases: function(doc) { return [doc.id]; }
|
||||
});
|
||||
});
|
||||
|
||||
function requireFolder(folderPath) {
|
||||
const absolutePath = path.resolve(__dirname, folderPath);
|
||||
return fs.readdirSync(absolutePath)
|
||||
.filter(p => !/[._]spec\.js$/.test(p)) // ignore spec files
|
||||
.map(p => require(path.resolve(absolutePath, p)));
|
||||
}
|
|
@ -20,7 +20,7 @@ module.exports = function contentFileReader() {
|
|||
getDocs: function(fileInfo) {
|
||||
|
||||
// We return a single element array because content files only contain one document
|
||||
return [{docType: 'guide', content: fileInfo.content, startingLine: 1}];
|
||||
return [{docType: 'content', content: fileInfo.content, startingLine: 1}];
|
||||
}
|
||||
};
|
||||
};
|
|
@ -37,7 +37,7 @@ describe('contentFileReader', function() {
|
|||
'project/path/modules/someModule/foo/docs/subfolder/bar.ngdoc', 'A load of content',
|
||||
'project/path');
|
||||
expect(fileReader.getDocs(fileInfo)).toEqual([
|
||||
{docType: 'guide', content: 'A load of content', startingLine: 1}
|
||||
{docType: 'content', content: 'A load of content', startingLine: 1}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = function() {
|
||||
return {name: 'intro'};
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = function() {
|
||||
return {name: 'title'};
|
||||
};
|
|
@ -22,22 +22,27 @@ module.exports = function exampleInlineTagDef(
|
|||
|
||||
|
||||
handler: function(doc, tagName, tagDescription) {
|
||||
const EXAMPLES_FOLDER = collectExamples.exampleFolders[0];
|
||||
const EXAMPLES_FOLDERS = collectExamples.exampleFolders;
|
||||
|
||||
var tagArgs = parseArgString(entities.decodeHTML(tagDescription));
|
||||
|
||||
var unnamedArgs = tagArgs._;
|
||||
var relativePath = unnamedArgs[0];
|
||||
var regionName = tagArgs.region || (unnamedArgs.length > 1 ? unnamedArgs[1] : null);
|
||||
var regionName = tagArgs.region || (unnamedArgs.length > 1 ? unnamedArgs[1] : '');
|
||||
var title = tagArgs.title || (unnamedArgs.length > 2 ? unnamedArgs[2] : null);
|
||||
var stylePattern = tagArgs.stylePattern; // TODO: not yet implemented here
|
||||
|
||||
var exampleFile = exampleMap[EXAMPLES_FOLDER][relativePath];
|
||||
// Find the example in the folders
|
||||
var exampleFile;
|
||||
EXAMPLES_FOLDERS.some(
|
||||
EXAMPLES_FOLDER => { return exampleFile = exampleMap[EXAMPLES_FOLDER][relativePath]; });
|
||||
|
||||
if (!exampleFile) {
|
||||
log.error(
|
||||
createDocMessage('Missing example file... relativePath: "' + relativePath + '".', doc));
|
||||
log.error(
|
||||
'Example files available are:', Object.keys(exampleMap[EXAMPLES_FOLDER]).join('\n'));
|
||||
'Example files available are:',
|
||||
EXAMPLES_FOLDERS.map(
|
||||
EXAMPLES_FOLDER => Object.keys(exampleMap[EXAMPLES_FOLDER]).join('\n')));
|
||||
return '';
|
||||
}
|
||||
|
||||
|
|
|
@ -42,19 +42,24 @@ function regionParserImpl(contents, fileType) {
|
|||
// start region processing
|
||||
if (startRegion) {
|
||||
// open up the specified region
|
||||
const regionName = getRegionName(startRegion[1]);
|
||||
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};
|
||||
const regionNames = getRegionNames(startRegion[1]);
|
||||
if (regionNames.length === 0) {
|
||||
regionNames.push('');
|
||||
}
|
||||
openRegions.push(regionName);
|
||||
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);
|
||||
});
|
||||
|
||||
// end region processing
|
||||
} else if (endRegion) {
|
||||
|
@ -62,14 +67,20 @@ function regionParserImpl(contents, fileType) {
|
|||
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)
|
||||
const regionName = getRegionName(endRegion[1]) || openRegions[openRegions.length - 1];
|
||||
const region = regionMap[regionName];
|
||||
if (!region || !region.open) {
|
||||
throw new RegionParserError(
|
||||
`Tried to close a region, named "${regionName}", that is not open`, index);
|
||||
const regionNames = getRegionNames(endRegion[1]);
|
||||
if (regionNames.length === 0) {
|
||||
regionNames.push(openRegions[openRegions.length - 1]);
|
||||
}
|
||||
region.open = false;
|
||||
removeLast(openRegions, regionName);
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
// doc plaster processing
|
||||
} else if (updatePlaster) {
|
||||
|
@ -94,8 +105,8 @@ function regionParserImpl(contents, fileType) {
|
|||
}
|
||||
}
|
||||
|
||||
function getRegionName(input) {
|
||||
return input.trim();
|
||||
function getRegionNames(input) {
|
||||
return input.split(',').map(name => name.trim()).filter(name => name.length > 0);
|
||||
}
|
||||
|
||||
function removeLast(array, item) {
|
||||
|
|
|
@ -70,6 +70,11 @@ describe('regionParser service', () => {
|
|||
expect(output.regions['Y']).toEqual(t('ghi'));
|
||||
});
|
||||
|
||||
it('should open a region with a null name if there is no region name', () => {
|
||||
const output = regionParser(t('/* #docregion */', 'abc', '/* #enddocregion */'), 'test-type');
|
||||
expect(output.regions['']).toEqual('abc');
|
||||
});
|
||||
|
||||
it('should close the most recently opened region if there is no region name', () => {
|
||||
const output = regionParser(
|
||||
t('/* #docregion X*/', 'abc', '/* #docregion Y */', 'def', '/* #enddocregion */', 'ghi',
|
||||
|
@ -136,6 +141,16 @@ describe('regionParser service', () => {
|
|||
expect(output.regions['']).toEqual(t('abc', '/* . . . */', 'ghi'));
|
||||
expect(output.regions['A']).toEqual(t('jkl', '/* ... elided ... */', 'pqr'));
|
||||
});
|
||||
|
||||
it('should parse multiple region names separated by commas', () => {
|
||||
const output = regionParser(
|
||||
t('/* #docregion , A, B */', 'abc', '/* #enddocregion B */', '/* #docregion C */', 'xyz',
|
||||
'/* #enddocregion A, C, */'),
|
||||
'test-type');
|
||||
expect(output.regions['A']).toEqual(t('abc', 'xyz'));
|
||||
expect(output.regions['B']).toEqual(t('abc'));
|
||||
expect(output.regions['C']).toEqual(t('xyz'));
|
||||
})
|
||||
});
|
||||
|
||||
function t() {
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
var Package = require('dgeni').Package;
|
||||
|
||||
/**
|
||||
* @dgPackage rho
|
||||
* @description Overrides the renderMarkdown service with an implementation based on Rho
|
||||
*/
|
||||
module.exports = new Package('rho', ['nunjucks'])
|
||||
|
||||
.factory(require('./services/renderMarkdown'));
|
|
@ -0,0 +1,55 @@
|
|||
var rho = require('rho');
|
||||
|
||||
/**
|
||||
* @dgService renderMarkdown
|
||||
* @description
|
||||
* Render the markdown in the given string as HTML.
|
||||
*/
|
||||
module.exports = function renderMarkdown() {
|
||||
|
||||
|
||||
// TODO(petebd): We might want to remove the leading whitespace from the code
|
||||
// block before it gets to the markdown code render function
|
||||
|
||||
// We need to teach Rho about inline tags so that it doesn't try to process
|
||||
// the inside of the tag
|
||||
const emitNormal = rho.InlineCompiler.prototype.emitNormal;
|
||||
rho.InlineCompiler.prototype.emitNormal = function(walk) {
|
||||
if (this.emitText(walk)) return;
|
||||
if (tryDgeniInlineTag(this, walk)) return;
|
||||
emitNormal.call(this, walk);
|
||||
};
|
||||
|
||||
rho.BlockCompiler.prototype.emitBlock = function(walk) {
|
||||
walk.skipBlankLines();
|
||||
this.countBlockIndent(walk);
|
||||
if (this.tryUnorderedList(walk)) return;
|
||||
if (this.tryOrderedList(walk)) return;
|
||||
if (this.tryDefinitionList(walk)) return;
|
||||
if (this.tryHeading(walk)) return;
|
||||
if (this.tryCodeBlock(walk)) return;
|
||||
if (this.tryDiv(walk)) return;
|
||||
if (this.tryHtml(walk)) return;
|
||||
if (tryDgeniInlineTag(this, walk, true)) return;
|
||||
if (this.tryHrTable(walk)) return;
|
||||
this.emitParagraph(walk);
|
||||
};
|
||||
|
||||
function tryDgeniInlineTag(compiler, walk, isBlock) {
|
||||
if (!walk.at('{@')) return false;
|
||||
|
||||
const startIdx = walk.position;
|
||||
var endIdx = walk.indexOf('}');
|
||||
|
||||
if (endIdx === null) return false;
|
||||
|
||||
if (isBlock) compiler.out.push('<div>');
|
||||
compiler.out.push(walk.substring(startIdx, endIdx + 1));
|
||||
if (isBlock) compiler.out.push('</div>');
|
||||
|
||||
walk.startFrom(endIdx + 2);
|
||||
return true;
|
||||
};
|
||||
|
||||
return function renderMarkdownImpl(content) { return rho.toHtml(content, true); };
|
||||
};
|
|
@ -0,0 +1,41 @@
|
|||
const renderMarkdownFactory = require('./renderMarkdown');
|
||||
const renderMarkdown = renderMarkdownFactory();
|
||||
|
||||
describe('rho: renderMarkdown service', () => {
|
||||
it('should convert markdown to HTML', () => {
|
||||
const content = '# heading 1\n' +
|
||||
'\n' +
|
||||
'A paragraph with *bold* and _italic_.\n' +
|
||||
'\n' +
|
||||
'* List item 1\n' +
|
||||
'* List item 2';
|
||||
const output = renderMarkdown(content);
|
||||
|
||||
expect(output).toEqual(
|
||||
'<h1>heading 1</h1>\n' +
|
||||
'<p>A paragraph with <strong>bold</strong> and <em>italic</em>.</p>\n' +
|
||||
'<ul>\n' +
|
||||
' <li>List item 1</li>\n' +
|
||||
' <li>List item 2</li>\n' +
|
||||
'</ul>\n');
|
||||
});
|
||||
|
||||
it('should not process markdown inside inline tags', () => {
|
||||
const content = '# heading {@link some_url_path}';
|
||||
const output = renderMarkdown(content);
|
||||
expect(output).toEqual('<h1>heading {@link some_url_path}</h1>\n');
|
||||
});
|
||||
|
||||
it('should not put block level inline tags inside paragraphs', () => {
|
||||
const content = 'A paragraph.\n' +
|
||||
'\n' +
|
||||
'{@example blah **blah** blah }\n' +
|
||||
'\n' +
|
||||
'Another paragraph';
|
||||
const output = renderMarkdown(content);
|
||||
expect(output).toEqual(
|
||||
'<p>A paragraph.</p>\n' +
|
||||
'<div>{@example blah **blah** blah }</div>\n' +
|
||||
'<p>Another paragraph</p>\n');
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue