Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Composable helpers #62

Merged
merged 11 commits into from Jun 24, 2019
4 changes: 4 additions & 0 deletions addon/-private/extension.js
@@ -0,0 +1,4 @@
export default function getExtension(name) {
let m = /\.(\w+)$/i.exec(name);
return m ? m[1].toLowerCase() : undefined;
}
24 changes: 24 additions & 0 deletions addon/-private/get-snippet.js
@@ -0,0 +1,24 @@
import snippets from 'ember-code-snippet/snippets';
import getLanguage from './language';
import getExtension from './extension';
import { assert } from '@ember/debug';

export default function getSnippet(name) {
let source = name
.split('/')
.reduce((dir, name) => dir && dir[name], snippets);
assert(`Code snippet with name "${name}" not found.`, source);

source = source
.replace(/^(\s*\n)*/, '')
.replace(/\s*$/, '');

let language = getLanguage(name);
let extension = getExtension(name);

return {
source,
language,
extension
};
}
27 changes: 27 additions & 0 deletions addon/-private/language.js
@@ -0,0 +1,27 @@
import getExtension from './extension';

export default function getLanguage(name) {
let ext = getExtension(name);
if (ext) {
switch (ext) {
case 'js':
return 'javascript';
case 'coffee':
return 'coffeescript';
case 'hbs':
return 'htmlbars';
case 'css':
return 'css';
case 'scss':
return 'scss';
case 'less':
return 'less';
case 'emblem':
return 'emblem';
case 'ts':
return 'typescript';
default:
return ext;
}
}
}
13 changes: 13 additions & 0 deletions addon/-private/unindent.js
@@ -0,0 +1,13 @@
export default function unindent(src) {
let match, min, lines = src.split("\n").filter(l => l !== '');
for (let i = 0; i < lines.length; i++) {
match = /^[ \t]*/.exec(lines[i]);
if (match && (typeof min === 'undefined' || min > match[0].length)) {
min = match[0].length;
}
}
if (typeof min !== 'undefined' && min > 0) {
src = src.replace(new RegExp("^[ \t]{" + min + "}", 'gm'), "");
}
return src;
}
65 changes: 2 additions & 63 deletions addon/components/code-snippet.js
@@ -1,69 +1,8 @@
import layout from '../templates/components/code-snippet';
import Component from '@ember/component';
import { computed } from '@ember/object';

const Highlight = self.require('highlight.js');

export default Component.extend({
layout,
tagName: 'pre',
classNameBindings: ['language'],
unindent: true,

_unindent(src) {
if (!this.get('unindent')) {
return src;
}
let match, min, lines = src.split("\n").filter(l => l !== '');
for (let i = 0; i < lines.length; i++) {
match = /^[ \t]*/.exec(lines[i]);
if (match && (typeof min === 'undefined' || min > match[0].length)) {
min = match[0].length;
}
}
if (typeof min !== 'undefined' && min > 0) {
src = src.replace(new RegExp("^[ \t]{" + min + "}", 'gm'), "");
}
return src;
},

source: computed('name', function(){
let snippet = this.get('name')
.split('/')
.reduce((dir, name) => dir && dir[name], this.snippets);

return this._unindent(
(snippet || "")
.replace(/^(\s*\n)*/, '')
.replace(/\s*$/, '')
);
}),

didInsertElement(){
Highlight.highlightBlock(this.get('element'));
},

language: computed('name', function(){
let m = /\.(\w+)$/i.exec(this.get('name'));
if (m) {
switch (m[1].toLowerCase()) {
case 'js':
return 'javascript';
case 'coffee':
return 'coffeescript';
case 'hbs':
return 'htmlbars';
case 'css':
return 'css';
case 'scss':
return 'scss';
case 'less':
return 'less';
case 'emblem':
return 'emblem';
case 'ts':
return 'typescript';
}
}
})
tagName: '',
unindent: true
});
27 changes: 27 additions & 0 deletions addon/helpers/dynamic-code-snippet.js
@@ -0,0 +1,27 @@
import { helper } from '@ember/component/helper';
import { get } from '@ember/object';

export default helper(function([source], { language, dynamic, quote = '"'}) {
Object.keys(dynamic).forEach((property) => {
let propertyValue = get(dynamic, property);
let type = typeof propertyValue;

let quotedValue = type === 'string' ? `${quote}${propertyValue}${quote}` : propertyValue;

switch (language) {
case 'handlebars':
case 'htmlbars':
source = source.replace(
new RegExp(`(<\\w*[^>]*\\s@[^=]+=){{${property}}}([^>]*>)`, 'g'),
type === 'string' ? `$1${quotedValue}$2` : `$1{{${propertyValue}}}$2`
);
source = source.replace(new RegExp(`{{${property}}}`, 'g'), propertyValue);
source = source.replace(new RegExp(`=${property}`, 'g'), `=${quotedValue}`);
break;
default:
source = source.replace(new RegExp(`{{${property}}}`, 'g'), quotedValue);
}
});

return source;
});
10 changes: 10 additions & 0 deletions addon/helpers/get-code-snippet.js
@@ -0,0 +1,10 @@
import { helper } from '@ember/component/helper';
import getSnippet from '../-private/get-snippet';
import unindentSource from '../-private/unindent';

export default helper(function([name], { unindent = true }) {
let snippet = getSnippet(name);
return unindent
? { ...snippet, source: unindentSource(snippet.source) }
: snippet;
});
7 changes: 7 additions & 0 deletions addon/index.js
@@ -0,0 +1,7 @@
import getSnippet from './-private/get-snippet';
import unindent from './-private/unindent';

export {
getSnippet,
unindent
};
6 changes: 5 additions & 1 deletion addon/templates/components/code-snippet.hbs
@@ -1 +1,5 @@
{{source}}
{{#let (get-code-snippet name unindent=unindent) as |snippet|}}
<pre class={{snippet.language}}>
{{snippet.source}}
</pre>
{{/let}}
7 changes: 1 addition & 6 deletions app/components/code-snippet.js
@@ -1,6 +1 @@
import CodeSnippet from 'ember-code-snippet/components/code-snippet';
import snippets from "../snippets";

export default CodeSnippet.extend({
snippets: snippets
});
export { default } from 'ember-code-snippet/components/code-snippet';
1 change: 1 addition & 0 deletions app/helpers/dynamic-code-snippet.js
@@ -0,0 +1 @@
export { default } from 'ember-code-snippet/helpers/dynamic-code-snippet';
1 change: 1 addition & 0 deletions app/helpers/get-code-snippet.js
@@ -0,0 +1 @@
export { default } from 'ember-code-snippet/helpers/get-code-snippet';
6 changes: 5 additions & 1 deletion ember-cli-build.js
Expand Up @@ -4,7 +4,11 @@ const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');

module.exports = function(defaults) {
let app = new EmberAddon(defaults, {
snippetSearchPaths: ['tests/integration']
snippetSearchPaths: ['tests/integration', 'tests/dummy'],
'ember-prism': {
'components': ['markup-templating', 'handlebars', 'javascript'],
'plugins': ['line-highlight']
}
});

/*
Expand Down
33 changes: 2 additions & 31 deletions index.js
Expand Up @@ -37,25 +37,7 @@ module.exports = {
return app.options.includeFileExtensionInSnippetNames !== false;
},

includeHighlightJS() {
let app = findHost(this);
if (typeof app.options.includeHighlightJS === 'boolean') {
return app.options.includeHighlightJS;
} else {
return true;
}
},

includeHighlightStyle() {
let app = findHost(this);
if (typeof app.options.includeHighlightStyle === 'boolean') {
return app.options.includeHighlightStyle;
} else {
return true;
}
},

treeForApp(tree) {
treeForAddon(tree) {
let snippets = mergeTrees(this.snippetPaths().filter(function(path){
return fs.existsSync(path);
}));
Expand All @@ -74,17 +56,6 @@ module.exports = {
outputFile: 'snippets.js'
});

return mergeTrees([tree, snippets]);
},

included(app) {
if (this.includeHighlightJS()) {
app.import('vendor/highlight.pack.js', { using: [
{ transformation: 'amd', as: 'highlight.js' }
] } );
}
if (this.includeHighlightStyle()) {
app.import('vendor/highlight-style.css');
}
return this._super.treeForAddon.call(this, mergeTrees([tree, snippets]));
}
};
1 change: 1 addition & 0 deletions package.json
Expand Up @@ -45,6 +45,7 @@
"ember-export-application-global": "^2.0.0",
"ember-load-initializers": "^2.0.0",
"ember-maybe-import-regenerator": "^0.1.6",
"ember-prism": "^0.4.0",
"ember-qunit": "^4.4.1",
"ember-resolver": "^5.0.1",
"ember-source": "~3.10.0",
Expand Down
5 changes: 5 additions & 0 deletions tests/dummy/app/controllers/application.js
@@ -0,0 +1,5 @@
import Controller from '@ember/controller';

export default Controller.extend({
inputType: 'text'
});
48 changes: 46 additions & 2 deletions tests/dummy/app/templates/application.hbs
@@ -1,3 +1,47 @@
<h2 id="title">Welcome to Ember</h2>
<h2 id="title">Welcome to ember-code-snippet</h2>

{{outlet}}
<h3>Static snippet</h3>

<!-- BEGIN-SNIPPET static -->
<p>I am a <strong>handlebars</strong> template!</p>
<p>The value is: {{val}}</p>
<div>
{{input value=val}}
</div>
<!-- END-SNIPPET -->

<h4>Plain:</h4>

{{code-snippet name="static.hbs"}}

<h4>Prism.js</h4>

{{#code-block language="handlebars"}}{{code-snippet name="dynamic.hbs"}}{{/code-block}}

<h3>Dynamic snippet</h3>

<div>
<label for="type">Input type:</label>
{{input id="type" value=inputType}}
</div>

<hr>

<!-- BEGIN-SNIPPET dynamic -->
<p>I am a <strong>handlebars</strong> template!</p>
<p>The value is: {{val}}</p>
<div>
{{input value=val type=inputType}}
</div>
<!-- END-SNIPPET -->

{{#let (get-code-snippet "dynamic.hbs") as |snippet|}}
<pre class={{snippet.language}}>
{{dynamic-code-snippet snippet.source
language=snippet.language
dynamic=(hash
inputType=inputType
)
}}
</pre>
{{/let}}