├── .editorconfig ├── .ember-cli ├── .eslintignore ├── .eslintrc.js ├── .gitignore ├── .npmignore ├── .template-lintrc.js ├── .travis.yml ├── .watchmanconfig ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── addon ├── -private │ ├── extension.js │ ├── get-snippet.js │ ├── language.js │ └── unindent.js ├── helpers │ └── get-code-snippet.js └── index.js ├── app └── helpers │ └── get-code-snippet.js ├── config ├── ember-try.js └── environment.js ├── ember-cli-build.js ├── index.js ├── package.json ├── snippet-finder.js ├── testem.js ├── tests ├── dummy │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── application.js │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── resolver.js │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ └── components │ │ │ └── .gitkeep │ ├── config │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ └── public │ │ └── robots.txt ├── helpers │ └── .gitkeep ├── index.html ├── integration │ ├── .gitkeep │ └── helpers │ │ └── get-code-snippet-test.js ├── test-helper.js └── unit │ ├── .gitkeep │ └── get-code-snippet-test.js ├── utils └── findHost.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | indent_style = space 14 | indent_size = 2 15 | 16 | [*.hbs] 17 | insert_final_newline = false 18 | 19 | [*.{diff,md}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Ember CLI sends analytics information by default. The data is completely 4 | anonymous, but there are times when you might want to disable this behavior. 5 | 6 | Setting `disableAnalytics` to true will prevent any data from being sent. 7 | */ 8 | "disableAnalytics": false 9 | } 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | /vendor/ 4 | 5 | # compiled output 6 | /dist/ 7 | /tmp/ 8 | 9 | # dependencies 10 | /bower_components/ 11 | /node_modules/ 12 | 13 | # misc 14 | /coverage/ 15 | !.* 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /bower.json.ember-try 20 | /package.json.ember-try 21 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 2018, 5 | sourceType: 'module' 6 | }, 7 | plugins: [ 8 | 'ember' 9 | ], 10 | extends: [ 11 | 'eslint:recommended', 12 | 'plugin:ember/recommended' 13 | ], 14 | env: { 15 | browser: true 16 | }, 17 | rules: { 18 | }, 19 | overrides: [ 20 | // node files 21 | { 22 | files: [ 23 | '.eslintrc.js', 24 | '.template-lintrc.js', 25 | 'ember-cli-build.js', 26 | 'index.js', 27 | 'testem.js', 28 | 'blueprints/*/index.js', 29 | 'config/**/*.js', 30 | 'tests/dummy/config/**/*.js', 31 | 'snippet-finder.js', 32 | 'utils/**/*.js' 33 | ], 34 | excludedFiles: [ 35 | 'addon/**', 36 | 'addon-test-support/**', 37 | 'app/**', 38 | 'tests/dummy/app/**' 39 | ], 40 | parserOptions: { 41 | sourceType: 'script', 42 | ecmaVersion: 2015 43 | }, 44 | env: { 45 | browser: false, 46 | node: true 47 | }, 48 | plugins: ['node'], 49 | rules: Object.assign({}, require('eslint-plugin-node').configs.recommended.rules, { 50 | // add your custom rules and overrides for node files here 51 | }) 52 | } 53 | ] 54 | }; 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist/ 5 | /tmp/ 6 | 7 | # dependencies 8 | /bower_components/ 9 | /node_modules/ 10 | 11 | # misc 12 | /.env* 13 | /.pnp* 14 | /.sass-cache 15 | /connect.lock 16 | /coverage/ 17 | /libpeerconnection.log 18 | /npm-debug.log* 19 | /testem.log 20 | /yarn-error.log 21 | 22 | # ember-try 23 | /.node_modules.ember-try/ 24 | /bower.json.ember-try 25 | /package.json.ember-try 26 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /bower_components/ 7 | 8 | # misc 9 | /.bowerrc 10 | /.editorconfig 11 | /.ember-cli 12 | /.env* 13 | /.eslintignore 14 | /.eslintrc.js 15 | /.gitignore 16 | /.template-lintrc.js 17 | /.travis.yml 18 | /.watchmanconfig 19 | /bower.json 20 | /config/ember-try.js 21 | /CONTRIBUTING.md 22 | /ember-cli-build.js 23 | /testem.js 24 | /tests/ 25 | /yarn.lock 26 | .gitkeep 27 | 28 | # ember-try 29 | /.node_modules.ember-try/ 30 | /bower.json.ember-try 31 | /package.json.ember-try 32 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | rules: { 6 | 'block-indentation': false, 7 | 'no-html-comments': false 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: node_js 3 | node_js: 4 | # we recommend testing addons with the same minimum supported node version as Ember CLI 5 | # so that your addon works for all apps 6 | - "8" 7 | 8 | sudo: false 9 | dist: trusty 10 | 11 | addons: 12 | chrome: stable 13 | 14 | cache: 15 | yarn: true 16 | 17 | env: 18 | global: 19 | # See https://git.io/vdao3 for details. 20 | - JOBS=1 21 | 22 | branches: 23 | only: 24 | - master 25 | # npm version tags 26 | - /^v\d+\.\d+\.\d+/ 27 | 28 | jobs: 29 | fail_fast: true 30 | allow_failures: 31 | - env: EMBER_TRY_SCENARIO=ember-canary 32 | 33 | include: 34 | # runs linting and tests with current locked deps 35 | 36 | - stage: "Tests" 37 | name: "Tests" 38 | install: 39 | - yarn install --non-interactive 40 | script: 41 | - yarn lint:hbs 42 | - yarn lint:js 43 | - yarn test 44 | 45 | - name: "Floating Dependencies" 46 | script: 47 | - yarn test 48 | 49 | # we recommend new addons test the current and previous LTS 50 | # as well as latest stable release (bonus points to beta/canary) 51 | - stage: "Additional Tests" 52 | env: EMBER_TRY_SCENARIO=ember-lts-2.18 53 | - env: EMBER_TRY_SCENARIO=ember-lts-3.4 54 | - env: EMBER_TRY_SCENARIO=ember-release 55 | - env: EMBER_TRY_SCENARIO=ember-beta 56 | - env: EMBER_TRY_SCENARIO=ember-canary 57 | - env: EMBER_TRY_SCENARIO=ember-default-with-jquery 58 | 59 | before_install: 60 | - curl -o- -L https://yarnpkg.com/install.sh | bash 61 | - export PATH=$HOME/.yarn/bin:$PATH 62 | 63 | install: 64 | - yarn install --no-lockfile --non-interactive 65 | 66 | script: 67 | - node_modules/.bin/ember try:one $EMBER_TRY_SCENARIO 68 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 3.0.0 2 | 3 | - BREAKING: ember-code-snippet doesn't provide a built-in syntax highlighting component anymore. Instead, you can combine it with the syntax highlighting component of your choice. A recommended choice is [ember-prism](https://github.com/shipshapecode/ember-prism). 4 | 5 | To upgrade from ember-code-snippet 2.x: 6 | 1. `ember install ember-prism` 7 | 2. Create your own `code-snippet` component like: 8 | 9 | ```hbs 10 | {{!- get-code-sniippet is provided 11 | by ember-code-snippet }} 12 | {{#let (get-code-snippet @name) as |snippet|}} 13 | {{!- CodeBlock is provided by ember-prism -}} 14 | 15 | {{snippet.source}} 16 | 17 | {{/let}} 18 | ``` 19 | 20 | # 2.4.0 21 | - ENHANCEMENT: allow configuring snippet file extensions, by @shimizust 22 | 23 | # 2.3.1 24 | 25 | - HOUSEKEEPING: bumping a deprecated glob dep. Thanks @AndreJoaquim. 26 | 27 | # 2.3.0 28 | 29 | - ENHANCEMENT: add TypeScript to the default list of file types we will examine for snippets. Thanks @aaxelb. 30 | 31 | # 2.2.2 32 | 33 | - HOUSEKEEPING: fix broccoli 2 deprecations, thanks @dcyriller 34 | 35 | # 2.2.1 36 | 37 | - BUGFIX: make index.js name match NPM package name by @esbanarango 38 | 39 | # 2.2.0 40 | 41 | - ENHANCEMENT: Add support for snippets in YAML files by @acdn-sglanzer 42 | 43 | # 2.1.0 44 | 45 | - ENHANCEMENT: Allow excluding the file extension from the snippet name by @dfreeman 46 | 47 | # 2.0.1 48 | - BUGFIX: fix loading snippets from a subdirectory by @dwickern 49 | - HOUSEkEEPING: update deps by @BnitoBzh 50 | 51 | # 2.0.0 52 | 53 | - BREAKING CHANGE: we used to include syntax highlighting support for *every* language supported by highlight.js. This is hundreds of languages and it results in a lot of unused code. Starting in 2.0.0, we only include a small set of defaults that are likely relevant to Ember users, and provide the option to substitute your own custom build of highlight.js if you need a different set. See "Syntax Highlighting Language Support" in the README. 54 | 55 | - ENHANCEMENT: you can now disable the automatic inclusion of our default theme and provide one of the other highlight.js themes. See "Theming Support" in the README. 56 | 57 | - ENHANCEMENT: detect nested code snippets (PR #42 by @defreeman) 58 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | * `git clone ` 6 | * `cd ember-code-snippet` 7 | * `yarn install` 8 | 9 | ## Linting 10 | 11 | * `yarn lint:hbs` 12 | * `yarn lint:js` 13 | * `yarn lint:js --fix` 14 | 15 | ## Running tests 16 | 17 | * `ember test` – Runs the test suite on the current Ember version 18 | * `ember test --server` – Runs the test suite in "watch mode" 19 | * `ember try:each` – Runs the test suite against multiple Ember versions 20 | 21 | ## Running the dummy application 22 | 23 | * `ember serve` 24 | * Visit the dummy application at [http://localhost:4200](http://localhost:4200). 25 | 26 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Edward Faulkner 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Code Snippet Ember Helper 2 | ============================ 3 | 4 | This is an Ember helper (and ember-cli addon) that lets you render 5 | code snippets within your app. The code snippets can live in their own 6 | dedicated files or you can extract blocks of code from your 7 | application itself. 8 | 9 | Compatibility 10 | ------------------------------------------------------------------------------ 11 | 12 | * Ember.js v2.18 or above 13 | * Ember CLI v2.13 or above 14 | * Node.js v8 or above 15 | 16 | Installation 17 | ------------------------------------------------------------------------------ 18 | 19 | ``` 20 | ember install ember-code-snippet 21 | ``` 22 | 23 | Usage 24 | ------------------------------------------------------------------------------ 25 | 26 | ### Defining snippets 27 | 28 | There are two ways to store your code snippets. You can use either or 29 | both together. 30 | 31 | #### With separate snippet files 32 | 33 | Create a new "snippets" directory at the top level of your ember-cli 34 | application or addon, and place the code snippets you'd like to render in their 35 | own files inside it. They will be identified by filename. So if you 36 | create the file `snippets/sample-template.hbs`, you can refer to it when using 37 | the `get-code-snippet` helper as `sample-template.hbs`. 38 | 39 | You can choose to load snippet files from different paths by passing 40 | an option to `new EmberApp` in your `ember-cli-build.js`: 41 | 42 | ```js 43 | var app = new EmberApp({ 44 | snippetPaths: ['snippets'] 45 | }); 46 | ``` 47 | 48 | If you want to use snippets located in an addon's dummy application, 49 | add the dummy app path to `snippetPaths`: 50 | 51 | ```js 52 | var app = new EmberAddon({ 53 | snippetPaths: ['tests/dummy/app'] 54 | }); 55 | ``` 56 | 57 | #### From within your application source 58 | 59 | In any file under your `app` tree, annotate the start and end of a 60 | code snippet block by placing comments like this: 61 | 62 | ```js 63 | // BEGIN-SNIPPET my-nice-example 64 | function sample(){ 65 | return 42; 66 | }; 67 | // END-SNIPPET 68 | ``` 69 | 70 | The above is a Javascript example, but you can use any language's 71 | comment format. We're just looking for lines that match 72 | `/\bBEGIN-SNIPPET\s+(\S+)\b/` and `/\bEND-SNIPPET\b/`. 73 | 74 | The opening comment must include a name. The helper will identify 75 | these snippets using the names you specified plus the file extension 76 | of the file in which they appeared. 77 | 78 | You can also define your own regex to find snippets. Just use the `snippetRegexes` option: 79 | 80 | ```js 81 | var app = new EmberAddon({ 82 | snippetRegexes: { 83 | begin: /{{#element-example\sname=\"(\S+)\"/, 84 | end: /{{\/element-example}}/, 85 | } 86 | }); 87 | ``` 88 | 89 | In the regex above everything in the `element-example` component block will be a snippet! Just make sure the first regex capture group is the name of the snippet. 90 | 91 | You can choose which paths will be searched for inline snippets by 92 | settings the snippetSearchPaths option when creating your application 93 | in ember-cli-build.js: 94 | 95 | ```js 96 | var app = new EmberApp({ 97 | snippetSearchPaths: ['app', 'other'] 98 | }); 99 | ``` 100 | 101 | By default, the file extension from the containing file will automatically be included in the snippet name. For instance, the example above has `BEGIN-SNIPPET my-nice-example` in the JS source, and is subsequently referenced as `"my-nice-example.js"`. To disable this behavior, use the `includeFileExtensionInSnippetNames` option: 102 | 103 | ```js 104 | var app = new EmberApp({ 105 | includeFileExtensionInSnippetNames: false 106 | }); 107 | ``` 108 | 109 | ### Helper usage 110 | 111 | After you have defined your snippets, you can use the `get-code-snippet` helper to get the snippet data 112 | for rendering: `{{get-code-snippet "my-nice-example.js"}}`. The returned value will be a JavaScript object with the 113 | following properties: 114 | 115 | * `source`: the source code extracted from the given snippet 116 | * `language`: the snippets language, following the naming conventions of the popular `prism.js` library, e.g. `handlebars` for Ember templates 117 | * `extension`: the file extension of the file containing the given snippet 118 | 119 | By default, the helper will try to unindent the code block by 120 | removing whitespace characters from the start of each line until the 121 | code bumps up against the edge. You can disable this with: 122 | 123 | ```hbs 124 | {{get-code-snippet "my-nice-example.js" unindent=false}} 125 | ``` 126 | 127 | The following example will render a `
` tag with a given code snippet:
128 | 
129 | ```hbs
130 | {{#let (get-code-snippet "static.hbs") as |snippet|}}
131 |   
{{snippet.source}}
132 | {{/let}} 133 | ``` 134 | 135 | ### Syntax Highlighting 136 | 137 | This addon does not provide any syntax highlighting capabilities itself, but instead it is designed with composability 138 | in mind, so you can add highlighting capabilities with any highlighting library on top of the snippet extraction 139 | primitives of this addon. The following is an example of rendering a code snippet using code highlighting provided by the 140 | [ember-prism](https://github.com/shipshapecode/ember-prism) addon: 141 | 142 | ```hbs 143 | {{#let (get-code-snippet "demo.hbs") as |snippet|}} 144 | 145 | {{snippet.source}} 146 | 147 | {{/let}} 148 | ``` 149 | 150 | If you want to show multiple snippets, it makes sense to extract that template code into a reusable component. In fact 151 | previous versions of `ember-code-snippet` shipped a `code-snippet` component, that you can replace now with the new 152 | helper and your highlighting library of choice. The following template-only component could replace the previously 153 | available component ``, again using `ember-prism` in this case: 154 | 155 | ```hbs 156 | {{!-- templates/components/code-snippet.hbs --}} 157 | {{#let (get-code-snippet @name) as |snippet|}} 158 | 159 | {{snippet.source}} 160 | 161 | {{/let}} 162 | ``` 163 | 164 | ### JavaScript usage 165 | 166 | When you want to use the code snippet functionality from JavaScript, you can import the `getCodeSnippet` function like 167 | this: 168 | 169 | ```js 170 | import { getCodeSnippet } from 'ember-code-snippet'; 171 | ``` 172 | 173 | Its call signature is similar to the helper invocation: `getCodeSnippet(name, unindent = true)`, and it returns the same 174 | POJO as described above. 175 | 176 | 177 | Contributing 178 | ------------------------------------------------------------------------------ 179 | 180 | See the [Contributing](CONTRIBUTING.md) guide for details. 181 | 182 | 183 | License 184 | ------------------------------------------------------------------------------ 185 | 186 | This project is licensed under the [MIT License](LICENSE.md). 187 | -------------------------------------------------------------------------------- /addon/-private/extension.js: -------------------------------------------------------------------------------- 1 | export default function getExtension(name) { 2 | let m = /\.(\w+)$/i.exec(name); 3 | return m ? m[1].toLowerCase() : undefined; 4 | } 5 | -------------------------------------------------------------------------------- /addon/-private/get-snippet.js: -------------------------------------------------------------------------------- 1 | import snippets from 'ember-code-snippet/snippets'; 2 | import getLanguage from './language'; 3 | import getExtension from './extension'; 4 | import unindentSource from '../-private/unindent'; 5 | import { assert } from '@ember/debug'; 6 | 7 | export default function getSnippet(name, unindent = true) { 8 | let source = name 9 | .split('/') 10 | .reduce((dir, name) => dir && dir[name], snippets); 11 | assert(`Code snippet with name "${name}" not found.`, source); 12 | 13 | source = source 14 | .replace(/^(\s*\n)*/, '') 15 | .replace(/\s*$/, ''); 16 | 17 | if (unindent) { 18 | source = unindentSource(source); 19 | } 20 | 21 | let language = getLanguage(name); 22 | let extension = getExtension(name); 23 | 24 | return { 25 | source, 26 | language, 27 | extension 28 | }; 29 | } -------------------------------------------------------------------------------- /addon/-private/language.js: -------------------------------------------------------------------------------- 1 | import getExtension from './extension'; 2 | 3 | export default function getLanguage(name) { 4 | let ext = getExtension(name); 5 | if (ext) { 6 | switch (ext) { 7 | case 'js': 8 | return 'javascript'; 9 | case 'coffee': 10 | return 'coffeescript'; 11 | case 'hbs': 12 | return 'handlebars'; 13 | case 'css': 14 | return 'css'; 15 | case 'scss': 16 | return 'scss'; 17 | case 'less': 18 | return 'less'; 19 | case 'emblem': 20 | return 'emblem'; 21 | case 'ts': 22 | return 'typescript'; 23 | default: 24 | return ext; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /addon/-private/unindent.js: -------------------------------------------------------------------------------- 1 | export default function unindent(src) { 2 | let match, min, lines = src.split("\n").filter(l => l !== ''); 3 | for (let i = 0; i < lines.length; i++) { 4 | match = /^[ \t]*/.exec(lines[i]); 5 | if (match && (typeof min === 'undefined' || min > match[0].length)) { 6 | min = match[0].length; 7 | } 8 | } 9 | if (typeof min !== 'undefined' && min > 0) { 10 | src = src.replace(new RegExp("^[ \t]{" + min + "}", 'gm'), ""); 11 | } 12 | return src; 13 | } 14 | -------------------------------------------------------------------------------- /addon/helpers/get-code-snippet.js: -------------------------------------------------------------------------------- 1 | import { helper } from '@ember/component/helper'; 2 | import { getCodeSnippet } from 'ember-code-snippet'; 3 | 4 | export default helper(function([name], { unindent = true }) { 5 | return getCodeSnippet(name, unindent); 6 | }); 7 | -------------------------------------------------------------------------------- /addon/index.js: -------------------------------------------------------------------------------- 1 | import getCodeSnippet from './-private/get-snippet'; 2 | 3 | export { 4 | getCodeSnippet 5 | }; 6 | -------------------------------------------------------------------------------- /app/helpers/get-code-snippet.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-code-snippet/helpers/get-code-snippet'; 2 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | 5 | module.exports = function() { 6 | return Promise.all([ 7 | getChannelURL('release'), 8 | getChannelURL('beta'), 9 | getChannelURL('canary') 10 | ]).then((urls) => { 11 | return { 12 | useYarn: true, 13 | scenarios: [ 14 | { 15 | name: 'ember-lts-2.18', 16 | env: { 17 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 'jquery-integration': true }) 18 | }, 19 | npm: { 20 | devDependencies: { 21 | '@ember/jquery': '^0.5.1', 22 | 'ember-source': '~2.18.0' 23 | } 24 | } 25 | }, 26 | { 27 | name: 'ember-lts-3.4', 28 | npm: { 29 | devDependencies: { 30 | 'ember-source': '~3.4.0' 31 | } 32 | } 33 | }, 34 | { 35 | name: 'ember-release', 36 | npm: { 37 | devDependencies: { 38 | 'ember-source': urls[0] 39 | } 40 | } 41 | }, 42 | { 43 | name: 'ember-beta', 44 | npm: { 45 | devDependencies: { 46 | 'ember-source': urls[1] 47 | } 48 | } 49 | }, 50 | { 51 | name: 'ember-canary', 52 | npm: { 53 | devDependencies: { 54 | 'ember-source': urls[2] 55 | } 56 | } 57 | }, 58 | // The default `.travis.yml` runs this scenario via `yarn test`, 59 | // not via `ember try`. It's still included here so that running 60 | // `ember try:each` manually or from a customized CI config will run it 61 | // along with all the other scenarios. 62 | { 63 | name: 'ember-default', 64 | npm: { 65 | devDependencies: {} 66 | } 67 | }, 68 | { 69 | name: 'ember-default-with-jquery', 70 | env: { 71 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 72 | 'jquery-integration': true 73 | }) 74 | }, 75 | npm: { 76 | devDependencies: { 77 | '@ember/jquery': '^0.5.1' 78 | } 79 | } 80 | } 81 | ] 82 | }; 83 | }); 84 | }; 85 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(/* environment, appConfig */) { 4 | return { }; 5 | }; 6 | -------------------------------------------------------------------------------- /ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); 4 | 5 | module.exports = function(defaults) { 6 | let app = new EmberAddon(defaults, { 7 | snippetSearchPaths: ['tests/integration', 'tests/dummy'], 8 | 'ember-prism': { 9 | 'components': ['markup-templating', 'handlebars', 'javascript'], 10 | 'plugins': ['line-highlight'] 11 | } 12 | }); 13 | 14 | /* 15 | This build file specifies the options for the dummy test app of this 16 | addon, located in `/tests/dummy` 17 | This build file does *not* influence how the addon or the app using it 18 | behave. You most likely want to be modifying `./index.js` or app's build file 19 | */ 20 | 21 | return app.toTree(); 22 | }; 23 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const mergeTrees = require('broccoli-merge-trees'); 5 | const flatiron = require('broccoli-flatiron'); 6 | const snippetFinder = require('./snippet-finder'); 7 | const findHost = require('./utils/findHost'); 8 | 9 | module.exports = { 10 | name: require('./package').name, 11 | 12 | snippetPaths() { 13 | let app = findHost(this); 14 | return app.options.snippetPaths || ['snippets']; 15 | }, 16 | 17 | snippetSearchPaths(){ 18 | let app = findHost(this); 19 | return app.options.snippetSearchPaths || ['app']; 20 | }, 21 | 22 | snippetRegexes() { 23 | let app = findHost(this); 24 | return [{ 25 | begin: /\bBEGIN-SNIPPET\s+(\S+)\b/, 26 | end: /\bEND-SNIPPET\b/ 27 | }].concat(app.options.snippetRegexes || []); 28 | }, 29 | 30 | snippetExtensions() { 31 | let app = findHost(this); 32 | return app.options.snippetExtensions || ['js','ts','gjs','gts','coffee','html','hbs','md','css','sass','scss','less','emblem','yaml']; 33 | }, 34 | 35 | includeExtensions() { 36 | let app = findHost(this); 37 | return app.options.includeFileExtensionInSnippetNames !== false; 38 | }, 39 | 40 | treeForAddon(tree) { 41 | let snippets = mergeTrees(this.snippetPaths().filter(function(path){ 42 | return fs.existsSync(path); 43 | })); 44 | 45 | let snippetOptions = { 46 | snippetRegexes: this.snippetRegexes(), 47 | includeExtensions: this.includeExtensions(), 48 | snippetExtensions: this.snippetExtensions() 49 | }; 50 | 51 | snippets = mergeTrees(this.snippetSearchPaths().map(function(path){ 52 | return snippetFinder(path, snippetOptions); 53 | }).concat(snippets)); 54 | 55 | snippets = flatiron(snippets, { 56 | outputFile: 'snippets.js' 57 | }); 58 | 59 | return this._super.treeForAddon.call(this, mergeTrees([tree, snippets])); 60 | } 61 | }; 62 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-code-snippet", 3 | "version": "3.0.0", 4 | "description": "Ember component for embedding syntax-highlighted code samples.", 5 | "main": "index.js", 6 | "author": "Edward Faulkner ", 7 | "license": "MIT", 8 | "repository": "https://github.com/ef4/ember-code-snippet", 9 | "keywords": [ 10 | "ember-addon" 11 | ], 12 | "directories": { 13 | "doc": "doc", 14 | "test": "tests" 15 | }, 16 | "scripts": { 17 | "build": "ember build", 18 | "lint:hbs": "ember-template-lint .", 19 | "lint:js": "eslint .", 20 | "start": "ember serve", 21 | "test": "ember test", 22 | "test:all": "ember try:each" 23 | }, 24 | "dependencies": { 25 | "broccoli-flatiron": "^0.1.3", 26 | "broccoli-merge-trees": "^1.0.0", 27 | "broccoli-plugin": "^1.3.1", 28 | "ember-cli-babel": "^7.7.3", 29 | "ember-cli-htmlbars": "^3.0.1", 30 | "es6-promise": "^1.0.0", 31 | "glob": "^7.1.3" 32 | }, 33 | "devDependencies": { 34 | "@ember/optional-features": "^0.7.0", 35 | "broccoli-asset-rev": "^3.0.0", 36 | "ember-cli": "~3.10.0", 37 | "ember-cli-dependency-checker": "^3.1.0", 38 | "ember-cli-eslint": "^5.1.0", 39 | "ember-cli-htmlbars-inline-precompile": "^2.1.0", 40 | "ember-cli-inject-live-reload": "^1.8.2", 41 | "ember-cli-sri": "^2.1.1", 42 | "ember-cli-template-lint": "^1.0.0-beta.1", 43 | "ember-cli-uglify": "^2.1.0", 44 | "ember-disable-prototype-extensions": "^1.1.3", 45 | "ember-export-application-global": "^2.0.0", 46 | "ember-load-initializers": "^2.0.0", 47 | "ember-maybe-import-regenerator": "^0.1.6", 48 | "ember-prism": "^0.4.0", 49 | "ember-qunit": "^4.4.1", 50 | "ember-resolver": "^5.0.1", 51 | "ember-source": "~3.10.0", 52 | "ember-source-channel-url": "^1.1.0", 53 | "ember-try": "^1.0.0", 54 | "eslint-plugin-ember": "^6.2.0", 55 | "eslint-plugin-node": "^8.0.1", 56 | "loader.js": "^4.7.0", 57 | "qunit-dom": "^0.8.4" 58 | }, 59 | "engines": { 60 | "node": "8.* || >= 10.*" 61 | }, 62 | "ember-addon": { 63 | "configPath": "tests/dummy/config" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /snippet-finder.js: -------------------------------------------------------------------------------- 1 | /* use strict */ 2 | 3 | const Plugin = require('broccoli-plugin'); 4 | const glob = require('glob'); 5 | const _Promise = require('es6-promise').Promise; 6 | const fs = require('fs'); 7 | const path = require('path'); 8 | 9 | 10 | function findFiles(srcDir, options) { 11 | let fileNamePattern = `**/*.+(${options.snippetExtensions.join('|')})`; 12 | return new _Promise(function(resolve, reject) { 13 | glob(path.join(srcDir, fileNamePattern), function (err, files) { 14 | if (err) { 15 | reject(err); 16 | } else { 17 | resolve(files); 18 | } 19 | }); 20 | }); 21 | } 22 | 23 | function extractSnippets(fileContent, regexes) { 24 | let stack = []; 25 | let output = {}; 26 | fileContent.split("\n").forEach(function(line){ 27 | let top = stack[stack.length - 1]; 28 | if (top && top.regex.end.test(line)) { 29 | output[top.name] = top.content.join("\n"); 30 | stack.pop(); 31 | } 32 | 33 | stack.forEach(function(snippet) { 34 | snippet.content.push(line); 35 | }); 36 | 37 | let match; 38 | let regex = regexes.find(function(regex) { 39 | return match = regex.begin.exec(line); 40 | }); 41 | 42 | if (match) { 43 | stack.push({ 44 | regex: regex, 45 | name: match[1], 46 | content: [] 47 | }); 48 | } 49 | }); 50 | return output; 51 | } 52 | 53 | function writeSnippets(files, outputPath, options) { 54 | files.forEach((filename) => { 55 | let regexes = options.snippetRegexes; 56 | let snippets = extractSnippets(fs.readFileSync(filename, 'utf-8'), regexes); 57 | for (let name in snippets) { 58 | let destFile = path.join(outputPath, name); 59 | let includeExtensions = options.includeExtensions; 60 | if (includeExtensions) { 61 | destFile += path.extname(filename); 62 | } 63 | fs.writeFileSync(destFile, snippets[name]); 64 | } 65 | }); 66 | } 67 | 68 | function SnippetFinder(inputNode, options) { 69 | if (!(this instanceof SnippetFinder)) { 70 | return new SnippetFinder(inputNode, options); 71 | } 72 | 73 | Plugin.call(this, [inputNode], { 74 | name: 'SnippetFinder', 75 | annotation: `SnippetFinder output: ${options.outputFile}`, 76 | persistentOutput: options.persistentOutput, 77 | needCache: options.needCache, 78 | }); 79 | 80 | this.options = options; 81 | } 82 | 83 | SnippetFinder.prototype = Object.create(Plugin.prototype); 84 | SnippetFinder.prototype.constructor = SnippetFinder; 85 | 86 | SnippetFinder.prototype.build = function() { 87 | return findFiles(this.inputPaths[0], this.options).then((files) => { 88 | writeSnippets(files, this.outputPath, this.options); 89 | }); 90 | }; 91 | 92 | module.exports = SnippetFinder; 93 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | test_page: 'tests/index.html?hidepassed', 3 | disable_watching: true, 4 | launch_in_ci: [ 5 | 'Chrome' 6 | ], 7 | launch_in_dev: [ 8 | 'Chrome' 9 | ], 10 | browser_args: { 11 | Chrome: { 12 | ci: [ 13 | // --no-sandbox is needed when running Chrome inside a container 14 | process.env.CI ? '--no-sandbox' : null, 15 | '--headless', 16 | '--disable-gpu', 17 | '--disable-dev-shm-usage', 18 | '--disable-software-rasterizer', 19 | '--mute-audio', 20 | '--remote-debugging-port=0', 21 | '--window-size=1440,900' 22 | ].filter(Boolean) 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from './resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from './config/environment'; 5 | 6 | const App = Application.extend({ 7 | modulePrefix: config.modulePrefix, 8 | podModulePrefix: config.podModulePrefix, 9 | Resolver 10 | }); 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef4/ember-code-snippet/f4a52132d91563bb6e85d659ad0bfba384733535/tests/dummy/app/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef4/ember-code-snippet/f4a52132d91563bb6e85d659ad0bfba384733535/tests/dummy/app/controllers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/controllers/application.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | 3 | export default Controller.extend({ 4 | inputType: 'text' 5 | }); 6 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef4/ember-code-snippet/f4a52132d91563bb6e85d659ad0bfba384733535/tests/dummy/app/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} 16 | 17 | 18 | {{content-for "body"}} 19 | 20 | 21 | 22 | 23 | {{content-for "body-footer"}} 24 | 25 | 26 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef4/ember-code-snippet/f4a52132d91563bb6e85d659ad0bfba384733535/tests/dummy/app/models/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/resolver.js: -------------------------------------------------------------------------------- 1 | import Resolver from 'ember-resolver'; 2 | 3 | export default Resolver; 4 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from './config/environment'; 3 | 4 | const Router = EmberRouter.extend({ 5 | location: config.locationType, 6 | rootURL: config.rootURL 7 | }); 8 | 9 | Router.map(function() { 10 | }); 11 | 12 | export default Router; 13 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef4/ember-code-snippet/f4a52132d91563bb6e85d659ad0bfba384733535/tests/dummy/app/routes/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef4/ember-code-snippet/f4a52132d91563bb6e85d659ad0bfba384733535/tests/dummy/app/styles/app.css -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

Welcome to ember-code-snippet

2 | 3 |

Static snippet

4 | 5 | 6 |

I am a handlebars template!

7 |

The value is: {{val}}

8 |
9 | {{input value=val}} 10 |
11 | 12 | 13 |

Plain:

14 | 15 | {{#let (get-code-snippet "static.hbs") as |snippet|}} 16 |
{{snippet.source}}
17 | {{/let}} 18 | 19 |

Prism.js

20 | 21 | {{#code-block language="handlebars"}}{{get (get-code-snippet "static.hbs") "source"}}{{/code-block}} 22 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef4/ember-code-snippet/f4a52132d91563bb6e85d659ad0bfba384733535/tests/dummy/app/templates/components/.gitkeep -------------------------------------------------------------------------------- /tests/dummy/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(environment) { 4 | let ENV = { 5 | modulePrefix: 'dummy', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'auto', 9 | EmberENV: { 10 | FEATURES: { 11 | // Here you can enable experimental features on an ember canary build 12 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 13 | }, 14 | EXTEND_PROTOTYPES: { 15 | // Prevent Ember Data from overriding Date.parse. 16 | Date: false 17 | } 18 | }, 19 | 20 | APP: { 21 | // Here you can pass flags/options to your application instance 22 | // when it is created 23 | } 24 | }; 25 | 26 | if (environment === 'development') { 27 | // ENV.APP.LOG_RESOLVER = true; 28 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 29 | // ENV.APP.LOG_TRANSITIONS = true; 30 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 31 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 32 | } 33 | 34 | if (environment === 'test') { 35 | // Testem prefers this... 36 | ENV.locationType = 'none'; 37 | 38 | // keep test console output quieter 39 | ENV.APP.LOG_ACTIVE_GENERATION = false; 40 | ENV.APP.LOG_VIEW_LOOKUPS = false; 41 | 42 | ENV.APP.rootElement = '#ember-testing'; 43 | ENV.APP.autoboot = false; 44 | } 45 | 46 | if (environment === 'production') { 47 | // here you can enable a production-specific feature 48 | } 49 | 50 | return ENV; 51 | }; 52 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "jquery-integration": false 3 | } 4 | -------------------------------------------------------------------------------- /tests/dummy/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions' 7 | ]; 8 | 9 | const isCI = !!process.env.CI; 10 | const isProduction = process.env.EMBER_ENV === 'production'; 11 | 12 | if (isCI || isProduction) { 13 | browsers.push('ie 11'); 14 | } 15 | 16 | module.exports = { 17 | browsers 18 | }; 19 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /tests/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef4/ember-code-snippet/f4a52132d91563bb6e85d659ad0bfba384733535/tests/helpers/.gitkeep -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy Tests 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | {{content-for "test-head"}} 12 | 13 | 14 | 15 | 16 | 17 | {{content-for "head-footer"}} 18 | {{content-for "test-head-footer"}} 19 | 20 | 21 | {{content-for "body"}} 22 | {{content-for "test-body"}} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {{content-for "body-footer"}} 31 | {{content-for "test-body-footer"}} 32 | 33 | 34 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef4/ember-code-snippet/f4a52132d91563bb6e85d659ad0bfba384733535/tests/integration/.gitkeep -------------------------------------------------------------------------------- /tests/integration/helpers/get-code-snippet-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import hbs from 'htmlbars-inline-precompile'; 5 | 6 | module('Integration | Helper | get-code-snippet', function(hooks) { 7 | setupRenderingTest(hooks); 8 | 9 | test('it returns snippet', async function(assert) { 10 | await render(hbs` 11 | // BEGIN-SNIPPET render-test 12 | function sample(){ 13 | return 42; 14 | }; 15 | // END-SNIPPET 16 | 17 | {{#with (get-code-snippet "render-test.js") as |snippet|}} 18 |
{{snippet.source}}
19 |
{{snippet.language}}
20 |
{{snippet.extension}}
21 | {{/with}} 22 | `); 23 | 24 | assert.strictEqual( 25 | this.element.querySelector('#source').textContent, 26 | 'function sample(){\n return 42;\n};' 27 | ); 28 | assert.dom('#language').hasText('javascript'); // language is determined by file extension, so JS in this case 29 | assert.dom('#extension').hasText('js'); 30 | }); 31 | 32 | test('it returns snippet w/ indented source', async function(assert) { 33 | await render(hbs` 34 | // BEGIN-SNIPPET render-test 35 | function sample(){ 36 | return 42; 37 | }; 38 | // END-SNIPPET 39 | 40 | {{#with (get-code-snippet "render-test.js" unindent=false) as |snippet|}} 41 |
{{snippet.source}}
42 |
{{snippet.language}}
43 |
{{snippet.extension}}
44 | {{/with}} 45 | `); 46 | 47 | assert.strictEqual( 48 | this.element.querySelector('#source').textContent, 49 | ' function sample(){\n return 42;\n };' 50 | ); 51 | assert.dom('#language').hasText('javascript'); // language is determined by file extension, so JS in this case 52 | assert.dom('#extension').hasText('js'); 53 | }); 54 | }); 55 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from '../app'; 2 | import config from '../config/environment'; 3 | import { setApplication } from '@ember/test-helpers'; 4 | import { start } from 'ember-qunit'; 5 | 6 | setApplication(Application.create(config.APP)); 7 | 8 | start(); 9 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ef4/ember-code-snippet/f4a52132d91563bb6e85d659ad0bfba384733535/tests/unit/.gitkeep -------------------------------------------------------------------------------- /tests/unit/get-code-snippet-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { getCodeSnippet } from 'ember-code-snippet'; 3 | 4 | module('Unit | getCodeSnippet', function() { 5 | 6 | test('it returns snippet', async function(assert) { 7 | let snippet = getCodeSnippet('render-test.js'); 8 | 9 | assert.strictEqual(snippet.source, 'function sample(){\n return 42;\n};'); 10 | assert.strictEqual(snippet.language, 'javascript'); 11 | assert.strictEqual(snippet.extension, 'js'); 12 | }); 13 | 14 | test('it returns snippet w/ indented source', async function(assert) { 15 | let snippet = getCodeSnippet('render-test.js', false); 16 | 17 | assert.strictEqual(snippet.source, ' function sample(){\n return 42;\n };'); 18 | assert.strictEqual(snippet.language, 'javascript'); 19 | assert.strictEqual(snippet.extension, 'js'); 20 | }); 21 | 22 | test('it returns handlebars snippet', async function(assert) { 23 | let snippet = getCodeSnippet('static.hbs'); 24 | 25 | assert.ok(snippet.source.includes('

I am a handlebars template!

')); 26 | assert.strictEqual(snippet.language, 'handlebars'); 27 | assert.strictEqual(snippet.extension, 'hbs'); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /utils/findHost.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Finds the host given an addon's context by climbing 3 | * up the parent/app hierarchy. 4 | * See: https://github.com/ember-engines/ember-asset-loader/blob/master/lib/utils/find-host.js 5 | * 6 | * @param {EmberAddon|EmberApp} context 7 | * @return {EmberApp} app 8 | */ 9 | module.exports = function findHost(context) { 10 | var current = context; 11 | var app; 12 | 13 | // Keep iterating upward until we don't have a grandparent. 14 | // Has to do this grandparent check because at some point we hit the project. 15 | do { 16 | app = current.app || app; 17 | } while (current.parent && current.parent.parent && (current = current.parent)); 18 | 19 | return app; 20 | }; --------------------------------------------------------------------------------