Skip to content

Commit 6b9ac66

Browse files
committed
Make it easier to write HBS in Markdown
1 parent a50cadb commit 6b9ac66

File tree

11 files changed

+159
-73
lines changed

11 files changed

+159
-73
lines changed

.eslintrc.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ module.exports = {
3030
'ember-cli-build.js',
3131
'config/**/*.js',
3232
'lib/**/*.js',
33-
'tests/dummy/config/**/*.js'
33+
'tests/dummy/config/**/*.js',
34+
'tests-node/**/*.js'
3435
],
3536
excludedFiles: [
3637
'app/**',
@@ -58,6 +59,14 @@ module.exports = {
5859
env: {
5960
embertest: true
6061
}
62+
},
63+
64+
// node test files
65+
{
66+
files: ['tests-node/**/*.js'],
67+
rules: {
68+
'node/no-unpublished-require': 'off'
69+
}
6170
}
6271
]
6372
};

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ env:
1717
# See https://git.io/vdao3 for details.
1818
- JOBS=1
1919
matrix:
20+
- EMBER_TRY_SCENARIO=node
2021
- EMBER_TRY_SCENARIO=ember-lts-2.12
2122
- EMBER_TRY_SCENARIO=ember-lts-2.16
2223
- EMBER_TRY_SCENARIO=ember-release

addon/components/docs-snippet/template.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@
1212
{{/copy-button}}
1313
{{/if}}
1414

15-
{{code-snippet name=(if hasBlock (concat name '.hbs') name) language=language}}
15+
{{code-snippet name=name language=language}}
1616
</div>

config/ember-try.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
module.exports = {
22
useYarn: true,
33
scenarios: [
4+
{
5+
name: 'node',
6+
command: 'yarn test:node'
7+
},
48
{
59
name: 'ember-lts-2.12',
610
npm: {

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ module.exports = {
7171

7272
this._super.included.apply(this, arguments);
7373

74+
includer.options.includeFileExtensionInSnippetNames = includer.options.includeFileExtensionInSnippetNames || false;
7475
includer.options.snippetSearchPaths = includer.options.snippetSearchPaths || ['tests/dummy/app'];
7576
includer.options.snippetRegexes = Object.assign({}, {
7677
begin: /{{#(?:docs-snippet|demo.example|demo.live-example)\sname=(?:"|')(\S+)(?:"|')/,

lib/utils/compile-markdown.js

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@
33
const marked = require('marked');
44
const highlightjs = require('highlightjs');
55

6-
module.exports = function compileMarkdown(string, options) {
6+
module.exports = function compileMarkdown(source, options) {
7+
let tokens;
78
let config = { highlight };
89

910
if (options && options.targetHandlebars) {
1011
config.renderer = new HBSRenderer();
12+
tokens = compactParagraphs(source);
13+
} else {
14+
tokens = marked.lexer(source);
1115
}
1216

13-
return `<div class="docs-md">${marked(string, config)}</div>`;
17+
return `<div class="docs-md">${marked.parser(tokens, config).trim()}</div>`;
1418
};
1519

1620
function highlight(code, lang) {
@@ -21,6 +25,36 @@ function highlight(code, lang) {
2125
}
2226
}
2327

28+
function compactParagraphs(source) {
29+
let tokens = marked.lexer(source);
30+
let compacted = [];
31+
32+
compacted.links = tokens.links;
33+
34+
let diff = 0;
35+
for (let token of tokens) {
36+
let wasBalanced = diff === 0;
37+
38+
diff += count(/\{\{#/g, token.text);
39+
diff -= count(/\{\{\//g, token.text);
40+
41+
if (wasBalanced) {
42+
compacted.push(token);
43+
} else {
44+
let last = compacted[compacted.length - 1];
45+
last.text = `${last.text} ${token.text}`;
46+
}
47+
}
48+
49+
return compacted;
50+
}
51+
52+
function count(regex, string) {
53+
let total = 0;
54+
while (regex.exec(string)) total++;
55+
return total;
56+
}
57+
2458
class HBSRenderer extends marked.Renderer {
2559
// Escape curlies in code spans/blocks to avoid treating them as Handlebars
2660
codespan() {

package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
"build": "ember build",
1818
"lint:js": "eslint .",
1919
"start": "ember serve",
20-
"test": "ember try:each"
20+
"test": "ember try:each",
21+
"test:node": "qunit tests-node"
2122
},
2223
"dependencies": {
2324
"@glimmer/syntax": "^0.30.5",
@@ -33,7 +34,7 @@
3334
"ember-cli-clipboard": "^0.8.1",
3435
"ember-cli-htmlbars": "^2.0.3",
3536
"ember-cli-sass": "7.1.3",
36-
"ember-code-snippet": "^2.0.0",
37+
"ember-code-snippet": "dfreeman/ember-code-snippet#exclude-file-extension",
3738
"ember-component-css": "^0.3.5",
3839
"ember-data": "^2.18.0",
3940
"ember-href-to": "^1.15.1",
@@ -64,6 +65,7 @@
6465
},
6566
"devDependencies": {
6667
"broccoli-asset-rev": "^2.6.0",
68+
"common-tags": "^1.7.2",
6769
"ember-ajax": "^3.0.0",
6870
"ember-cli": "~2.18.1",
6971
"ember-cli-dependency-checker": "^2.1.0",
@@ -85,7 +87,8 @@
8587
"ember-source": "~2.18.0",
8688
"eslint-plugin-ember": "^5.0.3",
8789
"eslint-plugin-node": "^5.2.1",
88-
"loader.js": "^4.6.0"
90+
"loader.js": "^4.6.0",
91+
"qunit": "^2.5.0"
8992
},
9093
"engines": {
9194
"node": "^4.5 || 6.* || >= 7.*"
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
'use strict';
2+
3+
const QUnit = require('qunit'), test = QUnit.test;
4+
const stripIndent = require('common-tags').stripIndent;
5+
const compileMarkdown = require('../../../lib/utils/compile-markdown');
6+
7+
QUnit.module('Unit | compile-markdown', function(hooks) {
8+
test('compacting paragraphs', function(assert) {
9+
let input = stripIndent`
10+
{{#foo-bar}}
11+
12+
{{/foo-bar}}
13+
`;
14+
15+
let result = compileMarkdown(input, { targetHandlebars: true });
16+
let expected = stripIndent`
17+
<div class="docs-md"><p>{{#foo-bar}} {{/foo-bar}}</p></div>
18+
`;
19+
20+
assert.equal(result, expected);
21+
});
22+
23+
test('compacting implicit code blocks', function(assert) {
24+
// Surrounding whitespace + 4-space indent = code block in MD
25+
let input = stripIndent`
26+
{{#foo-bar}}
27+
28+
hello
29+
30+
{{/foo-bar}}
31+
`;
32+
33+
let result = compileMarkdown(input, { targetHandlebars: true });
34+
let expected = stripIndent`
35+
<div class="docs-md"><p>{{#foo-bar}} hello {{/foo-bar}}</p></div>
36+
`;
37+
38+
assert.equal(result, expected);
39+
});
40+
});

tests/dummy/app/pods/application/template.hbs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
{{! BEGIN-SNIPPET docs-demo-application-template }}
2-
{{! // tests/dummy/app/templates/application.hbs }}
32
{{outlet}}
43

54
{{docs-keyboard-shortcuts}}

tests/dummy/app/pods/docs/quickstart/template.md

Lines changed: 49 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -10,91 +10,82 @@ This quickstart guide will get you going with a docs site for your brand new add
1010

1111
2. **Add the docs route.** Open `tests/dummy/app/router.js` and add a route named `docs`. Go ahead and make it nested, since this is where you'll be defining additional documentation pages.
1212

13-
```js
14-
// tests/dummy/app/router.js
15-
this.route('docs', function() {
16-
this.route('api', function() {
17-
this.route('class', { path: '/:class_id' });
13+
{{#docs-snippet name='quickstart-router' title='tests/dummy/app/router.js'}}
14+
this.route('docs', function() {
15+
this.route('api', function() {
16+
this.route('class', { path: '/:class_id' });
17+
});
1818
});
19-
});
20-
```
19+
{{/docs-snippet}}
2120

2221
3. **Create your app skeleton.** Open your application template and add the DocsNavbar component, customizing its properties for your project. You can also add the global keyboard shortcuts component (use `?` to show the help window). Here's our application template:
2322

24-
{{docs-snippet name="docs-demo-application-template.hbs"}}
23+
{{docs-snippet name="docs-demo-application-template" title="tests/dummy/app/templates/application.hbs"}}
2524

2625
4. **Create your docs skeleton.** Create a template for the `docs` route and add the DocsViewer component.
2726

28-
```hbs
29-
{{! tests/dummy/app/pods/docs/template.hbs }}
30-
{{#docs-viewer as |viewer|}}
31-
32-
{{#viewer.nav as |nav|}}
33-
{{nav.item 'Overview' 'docs.index'}}
34-
{{/viewer.nav}}
35-
36-
{{#viewer.main}}
37-
<div class="docs-container docs__center">
38-
<div class="docs-section">
39-
{{outlet}}
27+
{{#docs-snippet name='quickstart-skeleton' title='tests/dummy/app/pods/docs/template.hbs'}}
28+
{{#docs-viewer as |viewer|}}
29+
{{#viewer.nav as |nav|}}
30+
{{nav.item 'Overview' 'docs.index'}}
31+
{{/viewer.nav}}
32+
33+
{{#viewer.main}}
34+
<div class="docs-container docs__center">
35+
<div class="docs-section">
36+
{{outlet}}
37+
</div>
4038
</div>
41-
</div>
42-
{{/viewer.main}}
43-
44-
{{/docs-viewer}}
45-
```
39+
{{/viewer.main}}
40+
{{/docs-viewer}}
41+
{{/docs-snippet}}
4642

4743
5. **Fill out the index of your docs page.** We called it Overview but you can call it whatever you want. Since Addon Docs supports markdown templates out of the box, we can make this a Markdown file.
4844

49-
```md
50-
<!-- tests/dummy/app/templates/docs/index.md -->
51-
# Overview
45+
{{#docs-snippet name='quickstart-markdown' title='tests/dummy/app/templates/docs/index.md' language='markdown'}}
46+
# Overview
5247

53-
This is my new addon, and it rocks!
54-
```
48+
This is my new addon, and it rocks!
49+
{{/docs-snippet}}
5550

5651
6. **Add a 404 route.** Add a route to the end of your router and create an associated template.
5752

58-
```js
59-
// tests/dummy/app/router.js
60-
this.route('not-found', { path: '/*path' });
61-
```
53+
{{#docs-snippet name='quickstart-404-route' title='tests/dummy/app/router.js'}}
54+
this.route('not-found', { path: '/*path' });
55+
{{/docs-snippet}}
6256

6357
<br />
6458

65-
```hbs
66-
{{! tests/dummy/app/templates/not-found.hbs }}
67-
<div class="docs-container">
68-
<h1>Not found</h1>
69-
<p>This page doesn't exist. {{#link-to 'index'}}Head home?{{/link-to}}</p>
70-
</div>
71-
```
59+
{{#docs-snippet name='quickstart-404-template' title='tests/dummy/app/templates/not-found.hbs'}}
60+
<div class="docs-container">
61+
<h1>Not found</h1>
62+
<p>This page doesn't exist. {{#link-to 'index'}}Head home?{{/link-to}}</p>
63+
</div>
64+
{{/docs-snippet}}
65+
7266
8. **Add more docs pages.** It's up to you how to structure your docs - use the Snippet, Viewer and Demo components to help you write your documentation. Let's add another page to ours: we'll add the route, link to our docs viewer, and the actual template.
7367

74-
```js
75-
// tests/dummy/app/router.js
76-
this.route('docs', function() {
77-
this.route('installation');
78-
});
79-
```
68+
{{#docs-snippet name='quickstart-more-docs' title='tests/dummy/app/router.js'}}
69+
this.route('docs', function() {
70+
this.route('installation');
71+
});
72+
{{/docs-snippet}}
8073

8174
<br />
8275

83-
```hbs
84-
{{! tests/dummy/app/templates/docs.hbs }}
85-
{{#viewer.nav as |nav|}}
86-
{{nav.item 'Installation' 'docs.installation'}}
87-
{{/viewer.nav}}
88-
```
76+
{{#docs-snippet name='quickstart-more-nav' title='tests/dummy/app/templates/docs.hbs'}}
77+
{{#viewer.nav as |nav|}}
78+
{{nav.item 'Installation' 'docs.installation'}}
79+
{{/viewer.nav}}
80+
{{/docs-snippet}}
8981

9082
<br />
9183

92-
```md
93-
<!-- tests/dummy/app/templates/docs/installation.md -->
94-
# Installation
84+
{{#docs-snippet name='quickstart-installation-readme' title='tests/dummy/app/templates/docs/installation.md'}}
85+
# Installation
9586

96-
To install My Addon, run...
97-
```
87+
To install My Addon, run...
88+
{{/docs-snippet}}
9889

9990
8. **Round out your site.**
10091
- **Add a favicon.** We recommend using [Ember CLI Favicon]( https://github.com/davewasmer/ember-cli-favicon).

0 commit comments

Comments
 (0)