Skip to content

Commit 3347f86

Browse files
authored
Merge pull request #49 from ember-learn/easier-hbs-in-markdown
Easier hbs in markdown
2 parents 7e1dbb6 + 4ca5341 commit 3347f86

File tree

33 files changed

+231
-130
lines changed

33 files changed

+231
-130
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-demo/x-live-example/component.js

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ export default Component.extend({
1111

1212
// Set initial template from snippet
1313
let name = this.get('name');
14-
15-
// We support either .hbs snippets or .md
16-
let rawTemplate = snippets[`${name}.hbs`] || snippets[`${name}.md`];
14+
let rawTemplate = snippets[name] || '';
1715

1816
this.set('rawTemplate', this._unindent(rawTemplate));
1917

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: 66 additions & 11 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) {
7-
let config = { highlight };
6+
module.exports = function compileMarkdown(source, config) {
7+
let tokens = marked.lexer(source);
8+
let markedOptions = {
9+
highlight,
10+
renderer: new HBSRenderer(config)
11+
};
812

9-
if (options && options.targetHandlebars) {
10-
config.renderer = new HBSRenderer();
13+
if (config && config.targetHandlebars) {
14+
tokens = compactParagraphs(tokens);
1115
}
1216

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

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

28+
// Whitespace can imply paragraphs in Markdown, which can result
29+
// in interleaving between <p> tags and block component invocations,
30+
// so this scans the Marked tokens to turn things like this:
31+
// <p>{{#my-component}}<p>
32+
// <p>{{/my-component}}</p>
33+
// Into this:
34+
// <p>{{#my-component}} {{/my-component}}</p>
35+
function compactParagraphs(tokens) {
36+
let compacted = [];
37+
38+
compacted.links = tokens.links;
39+
40+
let balance = 0;
41+
for (let token of tokens) {
42+
if (balance === 0) {
43+
compacted.push(token);
44+
} else {
45+
let last = compacted[compacted.length - 1];
46+
last.text = `${last.text} ${token.text}`;
47+
}
48+
49+
balance += count(/\{\{#/g, token.text);
50+
balance -= count(/\{\{\//g, token.text);
51+
}
52+
53+
return compacted;
54+
}
55+
56+
function count(regex, string) {
57+
let total = 0;
58+
while (regex.exec(string)) total++;
59+
return total;
60+
}
61+
2462
class HBSRenderer extends marked.Renderer {
25-
// Escape curlies in code spans/blocks to avoid treating them as Handlebars
63+
constructor(config) {
64+
super();
65+
this.config = config || {};
66+
}
67+
2668
codespan() {
27-
return this._escapeCurlies(super.codespan.apply(this, arguments));
69+
return this._processCode(super.codespan.apply(this, arguments));
2870
}
2971

3072
code() {
31-
let code = this._escapeCurlies(super.code.apply(this, arguments));
73+
let code = this._processCode(super.code.apply(this, arguments));
3274
return code.replace(/^<pre>/, '<pre class="docs-md__code">');
3375
}
3476

3577
// Unescape quotes in text, as they may be part of a Handlebars statement
3678
text() {
37-
return super.text.apply(this, arguments)
38-
.replace(/&quot;|&#34;/g, `"`)
39-
.replace(/&apos;|&#39;/g, `'`);
79+
let text = super.text.apply(this, arguments);
80+
if (this.config.targetHandlebars) {
81+
text = text
82+
.replace(/&quot;|&#34;/g, `"`)
83+
.replace(/&apos;|&#39;/g, `'`);
84+
}
85+
return text;
86+
}
87+
88+
// Escape curlies in code spans/blocks to avoid treating them as Handlebars
89+
_processCode(string) {
90+
if (this.config.targetHandlebars) {
91+
string = this._escapeCurlies(string);
92+
}
93+
94+
return string;
4095
}
4196

4297
_escapeCurlies(string) {

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/app.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Resolver from './resolver';
33
import loadInitializers from 'ember-load-initializers';
44
import config from './config/environment';
55

6-
// BEGIN-SNIPPET sample-snippet
6+
// BEGIN-SNIPPET sample-snippet.js
77
const App = Application.extend({
88
modulePrefix: config.modulePrefix,
99
podModulePrefix: config.podModulePrefix,

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

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

54
{{docs-keyboard-shortcuts}}

tests/dummy/app/pods/docs/components/docs-demo/controller.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ import Controller from '@ember/controller';
33
export default Controller.extend({
44

55
actions: {
6-
// BEGIN-SNIPPET docs-demo-multiple
76
toggleIsShowing() {
87
this.toggleProperty('isShowing');
98
}
10-
// END-SNIPPET
119
}
1210

1311
});

tests/dummy/app/pods/docs/components/docs-demo/demo1/template.hbs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
{{! BEGIN-SNIPPET docs-demo-basic-src }}
1+
{{! BEGIN-SNIPPET docs-demo-basic-src.hbs }}
22
{{#docs-demo as |demo|}}
3-
{{#demo.example name='docs-demo-basic'}}
4-
<p>I am a <strong>handlebars</strong> template!</p>
5-
<p>The value is: {{val}}</p>
6-
<div>
7-
{{input value=val}}
8-
</div>
3+
{{#demo.example name='docs-demo-basic.hbs'}}
4+
<p>I am a <strong>handlebars</strong> template!</p>
5+
<p>The value is: {{val}}</p>
6+
<div>
7+
{{input value=val}}
8+
</div>
99
{{/demo.example}}
1010

1111
{{demo.snippet 'docs-demo-basic.hbs'}}

tests/dummy/app/pods/docs/components/docs-demo/demo2/component.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import Component from '@ember/component';
33
export default Component.extend({
44

55
actions: {
6-
// BEGIN-SNIPPET docs-demo-multiple
6+
// BEGIN-SNIPPET docs-demo-multiple.js
77
toggleIsShowing() {
88
this.toggleProperty('isShowing');
99
}
1010
// END-SNIPPET
1111
}
12-
12+
1313
});

tests/dummy/app/pods/docs/components/docs-demo/demo2/template.hbs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{{#docs-demo as |demo|}}
2-
{{#demo.example data-test-id='docs-demo-multiple'}}
3-
{{!-- BEGIN-SNIPPET docs-demo-multiple --}}
2+
{{#demo.example data-test-id='docs-demo-multiple.hbs'}}
3+
{{!-- BEGIN-SNIPPET docs-demo-multiple.hbs --}}
44
<button onclick={{action 'toggleIsShowing'}}>
55
Press me!
66
</button>
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
{{!-- BEGIN-SNIPPET docs-demo-custom-src --}}
1+
{{!-- BEGIN-SNIPPET docs-demo-custom-src.hbs --}}
22
{{#docs-demo as |demo|}}
3-
{{#demo.example name='docs-demo-custom'}}
3+
{{#demo.example name='docs-demo-custom.md'}}
44
<pre>
55
# Markdown
66
- Has syntax highlighting, too
77
</pre>
88
{{/demo.example}}
99

1010
{{demo.snippet 'docs-demo-custom-src.hbs' label='Source'}}
11-
{{demo.snippet 'docs-demo-custom.hbs' label='My Custom Label' language='markdown'}}
11+
{{demo.snippet 'docs-demo-custom.md' label='My Custom Label' language='markdown'}}
1212
{{/docs-demo}}
1313
{{!-- END-SNIPPET --}}

tests/dummy/app/pods/docs/components/docs-demo/demo4/template.hbs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
{{! BEGIN-SNIPPET live-example-src }}
1+
{{! BEGIN-SNIPPET live-example-src.hbs }}
22
{{#docs-demo as |demo|}}
3-
{{#demo.live-example name='live-example'}}
3+
{{#demo.live-example name='live-example.hbs'}}
44
<p>The air up here is {{foo}}</p>
55

66
{{input value=foo}}

tests/dummy/app/pods/docs/components/docs-hero/demo/template.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{{#docs-demo as |demo|}}
2-
{{#demo.example name='docs-hero'}}
2+
{{#demo.example name='docs-hero.hbs'}}
33
{{docs-hero
44
logo='ember'
55
slim-heading='Super'

tests/dummy/app/pods/docs/components/docs-logo/demo1/styles.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// BEGIN-SNIPPET docs-logo-ember-styles
1+
// BEGIN-SNIPPET docs-logo-ember-styles.scss
22
.my-ember-addon-logo {
33
background: #333;
44
color: white;

tests/dummy/app/pods/docs/components/docs-logo/demo1/template.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{{#docs-demo as |demo|}}
2-
{{#demo.example name='docs-logo-ember'}}
2+
{{#demo.example name='docs-logo-ember.hbs'}}
33
<div class='my-ember-addon-logo'>
44
{{docs-logo}}
55
</div>

tests/dummy/app/pods/docs/components/docs-logo/demo2/styles.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// BEGIN-SNIPPET docs-logo-ember-cli-styles
1+
// BEGIN-SNIPPET docs-logo-ember-cli-styles.scss
22
.my-ember-cli-addon-logo {
33
background: #F44336;
44
color: white;

tests/dummy/app/pods/docs/components/docs-logo/demo2/template.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{{#docs-demo as |demo|}}
2-
{{#demo.example name='docs-logo-ember-cli'}}
2+
{{#demo.example name='docs-logo-ember-cli.hbs'}}
33
<div class='my-ember-cli-addon-logo'>
44
{{docs-logo logo='ember-cli'}}
55
</div>

tests/dummy/app/pods/docs/components/docs-logo/demo3/styles.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// BEGIN-SNIPPET docs-logo-ember-data-styles
1+
// BEGIN-SNIPPET docs-logo-ember-data-styles.scss
22
.my-ember-data-addon-logo {
33
background: #2196F3;
44
color: white;

tests/dummy/app/pods/docs/components/docs-logo/demo3/template.hbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{{#docs-demo as |demo|}}
2-
{{#demo.example name='docs-logo-ember-data'}}
2+
{{#demo.example name='docs-logo-ember-data.hbs'}}
33
<div class='my-ember-data-addon-logo'>
44
{{docs-logo logo='ember-data'}}
55
</div>

0 commit comments

Comments
 (0)