├── vendor ├── .gitkeep └── embed.js ├── tests ├── helpers │ └── .gitkeep ├── unit │ └── .gitkeep ├── integration │ └── .gitkeep ├── dummy │ ├── app │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── models │ │ │ └── .gitkeep │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── components │ │ │ └── .gitkeep │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── application.js │ │ ├── styles │ │ │ └── app.css │ │ ├── router.js │ │ ├── templates │ │ │ └── application.hbs │ │ ├── app.js │ │ └── index.html │ ├── public │ │ ├── assets │ │ │ ├── ignore.css │ │ │ └── ignore.js │ │ └── robots.txt │ └── config │ │ ├── optional-features.json │ │ ├── ember-cli-update.json │ │ ├── targets.js │ │ └── environment.js ├── test-helper.js ├── index.html └── acceptance │ └── embed-test.js ├── .watchmanconfig ├── .prettierrc.js ├── app └── services │ └── embedded.js ├── .template-lintrc.js ├── renovate.json ├── config ├── environment.js └── ember-try.js ├── .ember-cli ├── .prettierignore ├── .eslintignore ├── .dependabot └── config.yml ├── .editorconfig ├── .gitignore ├── .npmignore ├── addon └── services │ └── embedded.js ├── testem.js ├── CONTRIBUTING.md ├── ember-cli-build.js ├── LICENSE.md ├── .eslintrc.js ├── index.js ├── .github └── workflows │ └── ci.yml ├── RELEASE.md ├── package.json ├── README.md ├── lib └── process-html.js └── CHANGELOG.md /vendor/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/integration/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/helpers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/models/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/routes/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/public/assets/ignore.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/dummy/public/assets/ignore.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["tmp", "dist"] 3 | } 4 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | }; 6 | -------------------------------------------------------------------------------- /tests/dummy/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /app/services/embedded.js: -------------------------------------------------------------------------------- 1 | export { default } from 'ember-embedded-snippet/services/embedded'; 2 | -------------------------------------------------------------------------------- /.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | }; 6 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>kaliber5/renovate-config:ember-addon" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment /*, appConfig*/) { 4 | return { 5 | locationType: environment === 'test' ? 'none' : 'hash', 6 | }; 7 | }; 8 | -------------------------------------------------------------------------------- /tests/dummy/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true 6 | } 7 | -------------------------------------------------------------------------------- /tests/dummy/app/styles/app.css: -------------------------------------------------------------------------------- 1 | body, :host { 2 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | } 4 | 5 | .title { 6 | color: #00f; 7 | } 8 | 9 | .something { 10 | color: #00f !important; 11 | } 12 | -------------------------------------------------------------------------------- /tests/dummy/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'dummy/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () {}); 10 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | /tmp/ 7 | 8 | # dependencies 9 | /bower_components/ 10 | /node_modules/ 11 | 12 | # misc 13 | /coverage/ 14 | !.* 15 | .eslintcache 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /bower.json.ember-try 20 | /package.json.ember-try 21 | -------------------------------------------------------------------------------- /tests/dummy/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 |

Welcome to Ember

2 | 3 | Arguments: 4 |
5 | {{#each-in this.args as |key value|}} 6 |
{{key}}
7 |
{{value}}
8 | {{/each-in}} 9 |
10 | 11 |

12 | This is content styled by inline CSS. 13 |

-------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | /tmp/ 7 | 8 | # dependencies 9 | /bower_components/ 10 | /node_modules/ 11 | 12 | # misc 13 | /coverage/ 14 | !.* 15 | .*/ 16 | .eslintcache 17 | 18 | # ember-try 19 | /.node_modules.ember-try/ 20 | /bower.json.ember-try 21 | /package.json.ember-try 22 | -------------------------------------------------------------------------------- /.dependabot/config.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | update_configs: 3 | - package_manager: "javascript" 4 | directory: "/" 5 | update_schedule: "weekly" 6 | automerged_updates: 7 | - match: 8 | dependency_type: "all" 9 | update_type: "in_range" 10 | version_requirement_updates: "increase_versions" 11 | commit_message: 12 | prefix: "" 13 | -------------------------------------------------------------------------------- /tests/test-helper.js: -------------------------------------------------------------------------------- 1 | import Application from 'dummy/app'; 2 | import config from 'dummy/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { start } from 'ember-qunit'; 7 | 8 | setApplication(Application.create(config.APP)); 9 | 10 | setup(QUnit.assert); 11 | 12 | start(); 13 | -------------------------------------------------------------------------------- /tests/dummy/app/controllers/application.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import { inject as service } from '@ember/service'; 3 | 4 | export default class ApplicationController extends Controller { 5 | @service 6 | embedded; 7 | 8 | // we intentionally do this eagerly, so we can catch in tests that args are available as early as the initial render! 9 | args = this.embedded.args; 10 | } 11 | -------------------------------------------------------------------------------- /tests/dummy/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'dummy/config/environment'; 5 | 6 | export default class App extends Application { 7 | modulePrefix = config.modulePrefix; 8 | podModulePrefix = config.podModulePrefix; 9 | Resolver = Resolver; 10 | } 11 | 12 | loadInitializers(App, config.modulePrefix); 13 | -------------------------------------------------------------------------------- /.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 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.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 | /.eslintcache 16 | /connect.lock 17 | /coverage/ 18 | /libpeerconnection.log 19 | /npm-debug.log* 20 | /testem.log 21 | /yarn-error.log 22 | 23 | # ember-try 24 | /.node_modules.ember-try/ 25 | /bower.json.ember-try 26 | /package.json.ember-try 27 | -------------------------------------------------------------------------------- /tests/dummy/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "3.27.0", 7 | "blueprints": [ 8 | { 9 | "name": "addon", 10 | "outputRepo": "https://github.com/ember-cli/ember-addon-output", 11 | "codemodsSource": "ember-addon-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": [ 14 | "--yarn" 15 | ] 16 | } 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.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 | /.eslintcache 14 | /.eslintignore 15 | /.eslintrc.js 16 | /.git/ 17 | /.gitignore 18 | /.prettierignore 19 | /.prettierrc.js 20 | /.template-lintrc.js 21 | /.travis.yml 22 | /.watchmanconfig 23 | /bower.json 24 | /config/ember-try.js 25 | /CONTRIBUTING.md 26 | /ember-cli-build.js 27 | /testem.js 28 | /tests/ 29 | /yarn-error.log 30 | /yarn.lock 31 | .gitkeep 32 | 33 | # ember-try 34 | /.node_modules.ember-try/ 35 | /bower.json.ember-try 36 | /package.json.ember-try 37 | -------------------------------------------------------------------------------- /addon/services/embedded.js: -------------------------------------------------------------------------------- 1 | import Service from '@ember/service'; 2 | 3 | export default class EmbeddedService extends Service { 4 | /** 5 | * The host name of our origin server, where the embedded app itself is hosted 6 | * 7 | * @property originHost 8 | * @type string 9 | * @public 10 | */ 11 | get originHost() { 12 | let scriptTag = document.querySelector('script[src$="/embed.js"]'); 13 | 14 | return scriptTag 15 | ? scriptTag.src.replace(/(https?:\/\/.*?)\/.*/g, '$1') 16 | : undefined; 17 | } 18 | 19 | args = 20 | typeof window !== undefined 21 | ? window.__ember_embedded_snippet_args ?? {} 22 | : {}; 23 | } 24 | -------------------------------------------------------------------------------- /testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_start_timeout: 120, 9 | browser_args: { 10 | Chrome: { 11 | ci: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.CI ? '--no-sandbox' : null, 14 | '--headless', 15 | '--disable-dev-shm-usage', 16 | '--disable-software-rasterizer', 17 | '--mute-audio', 18 | '--remote-debugging-port=0', 19 | '--window-size=1440,900', 20 | ].filter(Boolean), 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How To Contribute 2 | 3 | ## Installation 4 | 5 | * `git clone ` 6 | * `cd ember-embedded-snippet` 7 | * `yarn install` 8 | 9 | ## Linting 10 | 11 | * `yarn lint` 12 | * `yarn lint:fix` 13 | 14 | ## Running tests 15 | 16 | * `ember test` – Runs the test suite on the current Ember version 17 | * `ember test --server` – Runs the test suite in "watch mode" 18 | * `ember try:each` – Runs the test suite against multiple Ember versions 19 | 20 | ## Running the dummy application 21 | 22 | * `ember serve` 23 | * Visit the dummy application at [http://localhost:4200](http://localhost:4200). 24 | 25 | For more information on using ember-cli, visit [https://ember-cli.com/](https://ember-cli.com/). 26 | -------------------------------------------------------------------------------- /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 | // Add options here 8 | }); 9 | 10 | /* 11 | This build file specifies the options for the dummy test app of this 12 | addon, located in `/tests/dummy` 13 | This build file does *not* influence how the addon or the app using it 14 | behave. You most likely want to be modifying `./index.js` or app's build file 15 | */ 16 | 17 | const { maybeEmbroider } = require('@embroider/test-setup'); 18 | const appTree = maybeEmbroider(app); 19 | 20 | return require('./index').process(app, appTree); 21 | }; 22 | -------------------------------------------------------------------------------- /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 | // Ember's browser support policy is changing, and IE11 support will end in 10 | // v4.0 onwards. 11 | // 12 | // See https://deprecations.emberjs.com/v3.x#toc_3-0-browser-support-policy 13 | // 14 | // If you need IE11 support on a version of Ember that still offers support 15 | // for it, uncomment the code block below. 16 | // 17 | // const isCI = Boolean(process.env.CI); 18 | // const isProduction = process.env.EMBER_ENV === 'production'; 19 | // 20 | // if (isCI || isProduction) { 21 | // browsers.push('ie 11'); 22 | // } 23 | 24 | module.exports = { 25 | browsers, 26 | }; 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | root: true, 5 | parser: 'babel-eslint', 6 | parserOptions: { 7 | ecmaVersion: 2018, 8 | sourceType: 'module', 9 | ecmaFeatures: { 10 | legacyDecorators: true, 11 | }, 12 | }, 13 | plugins: ['ember'], 14 | extends: [ 15 | 'eslint:recommended', 16 | 'plugin:ember/recommended', 17 | 'plugin:prettier/recommended', 18 | ], 19 | env: { 20 | browser: true, 21 | }, 22 | rules: {}, 23 | overrides: [ 24 | // node files 25 | { 26 | files: [ 27 | '.eslintrc.js', 28 | '.prettierrc.js', 29 | '.template-lintrc.js', 30 | 'ember-cli-build.js', 31 | 'index.js', 32 | 'testem.js', 33 | 'blueprints/*/index.js', 34 | 'config/**/*.js', 35 | 'tests/dummy/config/**/*.js', 36 | 'lib/**/*', 37 | ], 38 | excludedFiles: [ 39 | 'addon/**', 40 | 'addon-test-support/**', 41 | 'app/**', 42 | 'tests/dummy/app/**', 43 | ], 44 | parserOptions: { 45 | sourceType: 'script', 46 | }, 47 | env: { 48 | browser: false, 49 | node: true, 50 | }, 51 | plugins: ['node'], 52 | extends: ['plugin:node/recommended'], 53 | }, 54 | { 55 | // Test files: 56 | files: ['tests/**/*-test.{js,ts}'], 57 | extends: ['plugin:qunit/recommended'], 58 | }, 59 | ], 60 | }; 61 | -------------------------------------------------------------------------------- /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: 'hash', 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/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Dummy 7 | 8 | 9 | 10 | {{content-for "head"}} 11 | 12 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | 33 | {{content-for "head-footer"}} 34 | 35 | 36 | {{content-for "body"}} 37 | 38 | 39 | 40 |

Outside of Ember

41 | 42 | 43 | 44 | 45 | 46 | 49 | 50 | 53 | 54 | {{content-for "body-footer"}} 55 | 56 | 57 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | name: require('./package').name, 5 | 6 | included() { 7 | let app = this.app; 8 | 9 | if (app.options.fingerprint === undefined) { 10 | app.options.fingerprint = {}; 11 | } 12 | if (app.options.fingerprint.exclude === undefined) { 13 | app.options.fingerprint.exclude = []; 14 | } 15 | app.options.fingerprint.exclude.push('embed.js'); 16 | 17 | // do not store config in meta tag 18 | app.options.storeConfigInMeta = false; 19 | 20 | // we start the app explicitly 21 | app.options.autoRun = false; 22 | }, 23 | 24 | config(env, baseConfig) { 25 | this._rootURL = baseConfig.rootURL; 26 | }, 27 | 28 | _process(appTree) { 29 | const mergeTrees = require('broccoli-merge-trees'); 30 | const ProcessHtmlPlugin = require('./lib/process-html'); 31 | 32 | const processedTree = new ProcessHtmlPlugin(appTree, { 33 | rootURL: this._rootURL, 34 | ui: this.project.ui, 35 | appName: this.app.name, 36 | }); 37 | 38 | const babelAddon = this.app.project.findAddonByName('ember-cli-babel'); 39 | const compiledTree = babelAddon.transpileTree(processedTree, { 40 | 'ember-cli-babel': { 41 | compileModules: false, 42 | }, 43 | }); 44 | 45 | return mergeTrees([appTree, compiledTree], { overwrite: true }); 46 | }, 47 | 48 | process(app, appTree) { 49 | let ownAddon = app.project.findAddonByName('ember-embedded-snippet'); 50 | 51 | if (!ownAddon) { 52 | throw new Error( 53 | "Could not find initialized ember-embedded-snippet addon. It must be part of your app's dependencies!" 54 | ); 55 | } 56 | 57 | return ownAddon._process(appTree); 58 | }, 59 | }; 60 | -------------------------------------------------------------------------------- /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 | 26 | 27 | 32 | 33 | {{content-for "head-footer"}} 34 | {{content-for "test-head-footer"}} 35 | 36 | 37 | {{content-for "body"}} 38 | {{content-for "test-body"}} 39 | 40 |
41 |
42 |
43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | {{content-for "body-footer"}} 56 | {{content-for "test-body-footer"}} 57 | 58 | 59 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - renovate/* 8 | tags: 9 | - '*' 10 | pull_request: 11 | schedule: 12 | - cron: '0 4 * * 5' # Fridays at 4am 13 | 14 | jobs: 15 | test: 16 | name: Tests 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Checkout code 21 | uses: actions/checkout@v3 22 | - name: Setup node.js 23 | uses: actions/setup-node@v3 24 | with: 25 | node-version: 12 26 | - name: Install dependencies 27 | uses: bahmutov/npm-install@v1 28 | - name: Lint 29 | run: yarn lint 30 | - name: Test 31 | run: yarn test:ember 32 | 33 | test-no-lock: 34 | name: Floating Dependencies 35 | runs-on: ubuntu-latest 36 | needs: 37 | - test 38 | steps: 39 | - name: Checkout code 40 | uses: actions/checkout@v3 41 | - name: Setup node.js 42 | uses: actions/setup-node@v3 43 | with: 44 | node-version: 12 45 | - name: Install dependencies 46 | uses: bahmutov/npm-install@v1 47 | with: 48 | useLockFile: false 49 | - name: Test 50 | run: yarn test:ember 51 | 52 | test-try: 53 | name: Additional Tests 54 | runs-on: ubuntu-latest 55 | needs: 56 | - test 57 | strategy: 58 | matrix: 59 | scenario: 60 | - ember-lts-3.16 61 | - ember-lts-3.20 62 | - ember-lts-3.24 63 | - ember-release 64 | # - ember-beta 65 | - ember-canary 66 | - ember-default-with-jquery 67 | - ember-classic 68 | - embroider-safe 69 | - embroider-optimized 70 | steps: 71 | - name: Checkout code 72 | uses: actions/checkout@v3 73 | - name: Setup node.js 74 | uses: actions/setup-node@v3 75 | with: 76 | node-version: 12 77 | - name: Install dependencies 78 | uses: bahmutov/npm-install@v1 79 | - name: Test 80 | run: yarn ember try:one ${{ matrix.scenario }} 81 | -------------------------------------------------------------------------------- /config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup'); 5 | 6 | module.exports = async function () { 7 | return { 8 | useYarn: true, 9 | scenarios: [ 10 | { 11 | name: 'ember-lts-3.16', 12 | npm: { 13 | devDependencies: { 14 | 'ember-source': '~3.16.0', 15 | }, 16 | }, 17 | }, 18 | { 19 | name: 'ember-lts-3.20', 20 | npm: { 21 | devDependencies: { 22 | 'ember-source': '~3.20.5', 23 | }, 24 | }, 25 | }, 26 | { 27 | name: 'ember-lts-3.24', 28 | npm: { 29 | devDependencies: { 30 | 'ember-source': '~3.24.3', 31 | }, 32 | }, 33 | }, 34 | { 35 | name: 'ember-release', 36 | npm: { 37 | devDependencies: { 38 | 'ember-source': await getChannelURL('release'), 39 | }, 40 | }, 41 | }, 42 | { 43 | name: 'ember-beta', 44 | npm: { 45 | devDependencies: { 46 | 'ember-source': await getChannelURL('beta'), 47 | }, 48 | }, 49 | }, 50 | { 51 | name: 'ember-canary', 52 | npm: { 53 | devDependencies: { 54 | 'ember-source': await getChannelURL('canary'), 55 | }, 56 | }, 57 | }, 58 | { 59 | name: 'ember-default-with-jquery', 60 | env: { 61 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 62 | 'jquery-integration': true, 63 | }), 64 | }, 65 | npm: { 66 | devDependencies: { 67 | '@ember/jquery': '^1.1.0', 68 | }, 69 | }, 70 | }, 71 | { 72 | name: 'ember-classic', 73 | env: { 74 | EMBER_OPTIONAL_FEATURES: JSON.stringify({ 75 | 'application-template-wrapper': true, 76 | 'default-async-observers': false, 77 | 'template-only-glimmer-components': false, 78 | }), 79 | }, 80 | npm: { 81 | ember: { 82 | edition: 'classic', 83 | }, 84 | }, 85 | }, 86 | embroiderSafe(), 87 | embroiderOptimized(), 88 | ], 89 | }; 90 | }; 91 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release Process 2 | 3 | Releases are mostly automated using 4 | [release-it](https://github.com/release-it/release-it/) and 5 | [lerna-changelog](https://github.com/lerna/lerna-changelog/). 6 | 7 | ## Preparation 8 | 9 | Since the majority of the actual release process is automated, the primary 10 | remaining task prior to releasing is confirming that all pull requests that 11 | have been merged since the last release have been labeled with the appropriate 12 | `lerna-changelog` labels and the titles have been updated to ensure they 13 | represent something that would make sense to our users. Some great information 14 | on why this is important can be found at 15 | [keepachangelog.com](https://keepachangelog.com/en/1.0.0/), but the overall 16 | guiding principle here is that changelogs are for humans, not machines. 17 | 18 | When reviewing merged PR's the labels to be used are: 19 | 20 | * breaking - Used when the PR is considered a breaking change. 21 | * enhancement - Used when the PR adds a new feature or enhancement. 22 | * bug - Used when the PR fixes a bug included in a previous release. 23 | * documentation - Used when the PR adds or updates documentation. 24 | * internal - Used for internal changes that still require a mention in the 25 | changelog/release notes. 26 | 27 | ## Release 28 | 29 | Once the prep work is completed, the actual release is straight forward: 30 | 31 | * First, ensure that you have installed your projects dependencies: 32 | 33 | ```sh 34 | yarn install 35 | ``` 36 | 37 | * Second, ensure that you have obtained a 38 | [GitHub personal access token][generate-token] with the `repo` scope (no 39 | other permissions are needed). Make sure the token is available as the 40 | `GITHUB_AUTH` environment variable. 41 | 42 | For instance: 43 | 44 | ```bash 45 | export GITHUB_AUTH=abc123def456 46 | ``` 47 | 48 | [generate-token]: https://github.com/settings/tokens/new?scopes=repo&description=GITHUB_AUTH+env+variable 49 | 50 | * And last (but not least 😁) do your release. 51 | 52 | ```sh 53 | npx release-it 54 | ``` 55 | 56 | [release-it](https://github.com/release-it/release-it/) manages the actual 57 | release process. It will prompt you to to choose the version number after which 58 | you will have the chance to hand tweak the changelog to be used (for the 59 | `CHANGELOG.md` and GitHub release), then `release-it` continues on to tagging, 60 | pushing the tag and commits, etc. 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ember-embedded-snippet", 3 | "version": "0.7.1", 4 | "description": "Embed Ember apps into external pages with a simple snippet", 5 | "keywords": [ 6 | "ember-addon" 7 | ], 8 | "repository": "https://github.com/kaliber5/ember-embedded-snippet", 9 | "license": "MIT", 10 | "author": "Simon Ihmig ", 11 | "directories": { 12 | "doc": "doc", 13 | "test": "tests" 14 | }, 15 | "scripts": { 16 | "build": "ember build --environment=production", 17 | "lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"", 18 | "lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix", 19 | "lint:hbs": "ember-template-lint .", 20 | "lint:hbs:fix": "ember-template-lint . --fix", 21 | "lint:js": "eslint . --cache", 22 | "lint:js:fix": "eslint . --fix", 23 | "start": "ember serve", 24 | "test": "npm-run-all lint test:*", 25 | "test:ember": "ember test", 26 | "test:ember-compatibility": "ember try:each" 27 | }, 28 | "dependencies": { 29 | "broccoli-caching-writer": "^3.0.3", 30 | "broccoli-merge-trees": "^4.2.0", 31 | "ember-cli-babel": "^7.26.6", 32 | "jsdom": "^19.0.0" 33 | }, 34 | "devDependencies": { 35 | "@ember/optional-features": "2.0.0", 36 | "@ember/test-helpers": "2.8.1", 37 | "@embroider/test-setup": "1.6.0", 38 | "@glimmer/component": "1.1.2", 39 | "@glimmer/tracking": "1.1.2", 40 | "babel-eslint": "10.1.0", 41 | "broccoli-asset-rev": "3.0.0", 42 | "ember-auto-import": "2.4.2", 43 | "ember-cli": "3.28.5", 44 | "ember-cli-dependency-checker": "3.3.1", 45 | "ember-cli-inject-live-reload": "2.1.0", 46 | "ember-cli-htmlbars": "6.1.0", 47 | "ember-cli-sri": "2.1.1", 48 | "ember-cli-terser": "4.0.2", 49 | "ember-disable-prototype-extensions": "1.1.3", 50 | "ember-load-initializers": "2.1.2", 51 | "ember-page-title": "7.0.0", 52 | "ember-qunit": "5.1.5", 53 | "ember-resolver": "8.0.3", 54 | "ember-source": "3.28.9", 55 | "ember-source-channel-url": "3.0.0", 56 | "ember-template-lint": "4.12.0", 57 | "ember-try": "2.0.0", 58 | "eslint": "7.32.0", 59 | "eslint-config-prettier": "8.5.0", 60 | "eslint-plugin-ember": "10.6.1", 61 | "eslint-plugin-node": "11.1.0", 62 | "eslint-plugin-prettier": "4.2.1", 63 | "eslint-plugin-qunit": "7.3.1", 64 | "loader.js": "4.7.0", 65 | "npm-run-all": "4.1.5", 66 | "prettier": "2.7.1", 67 | "qunit": "2.19.1", 68 | "qunit-dom": "2.0.0", 69 | "release-it": "14.14.3", 70 | "release-it-lerna-changelog": "4.0.1", 71 | "webpack": "5.74.0" 72 | }, 73 | "engines": { 74 | "node": "12.* || >= 14" 75 | }, 76 | "publishConfig": { 77 | "registry": "https://registry.npmjs.org" 78 | }, 79 | "ember": { 80 | "edition": "octane" 81 | }, 82 | "ember-addon": { 83 | "configPath": "tests/dummy/config", 84 | "after": "ember-export-application-global" 85 | }, 86 | "release-it": { 87 | "plugins": { 88 | "release-it-lerna-changelog": { 89 | "infile": "CHANGELOG.md", 90 | "launchEditor": true 91 | } 92 | }, 93 | "git": { 94 | "tagName": "v${version}" 95 | }, 96 | "github": { 97 | "release": true, 98 | "tokenRef": "GITHUB_AUTH" 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ember-embedded-snippet 2 | ============================================================================== 3 | 4 | ![CI](https://github.com/kaliber5/ember-embedded-snippet/workflows/CI/badge.svg) 5 | 6 | Embed your Ember app as a WebComponent into external pages with a simple JavaScript snippet. 7 | 8 | 9 | Compatibility 10 | ------------------------------------------------------------------------------ 11 | 12 | * Ember.js v3.16 or above 13 | * Ember CLI v3.20 or above 14 | * Node.js v12 or above 15 | 16 | 17 | Installation 18 | ------------------------------------------------------------------------------ 19 | 20 | ``` 21 | ember install ember-embedded-snippet 22 | ``` 23 | 24 | To allow compatibility with `ember-auto-import` v2 and Embroider, since `v0.7.0` the addon requires some manual 25 | setup to postprocess Ember's build. 26 | 27 | In your `ember-cli-build.js` change the last part: 28 | 29 | ```diff 30 | module.exports = function (defaults) { 31 | let app = new EmberAddon(defaults, { 32 | // ... 33 | }); 34 | 35 | - return app.toTree(); 36 | + return require('ember-embedded-snippet').process(app, app.toTree()); 37 | } 38 | ``` 39 | 40 | Then you should also add the custom element invocation to your `app/index.html`, so when running locally using `ember serve` 41 | the app is bootstrapped the same way as when it is embedded in production. See also the "Usage" below! 42 | 43 | ```diff 44 | 45 | {{content-for "body"}} 46 | 47 | + 48 | 49 | 50 | 51 | 52 | {{content-for "body-footer"}} 53 | 54 | ``` 55 | 56 | > Note: doing this also for `tests/index.html` will not work, you should keep that file as-is. That means tests use the default 57 | > bootstrapping process of Ember. 58 | 59 | Usage 60 | ------------------------------------------------------------------------------ 61 | 62 | Add a snippet like this to your static or server rendered HTML page: 63 | 64 | ```html 65 | 66 | 67 | ``` 68 | 69 | `my-app-name` is the name of your app. As custom elements require to contain a dash, if your app name is a single word, append the `-app` suffix. 70 | 71 | ### Shadow DOM 72 | 73 | To prevent styles leaking from the parent page into your app or vice versa, you can enable [shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) support: 74 | 75 | ```html 76 | 77 | ``` 78 | 79 | > Note that even with shadow DOM, inherited styles will also be inherited by your shadow DOM, thus leak into your app. 80 | > So you might want to [reset inheritable styles](https://developers.google.com/web/fundamentals/web-components/shadowdom#reset) 81 | 82 | ### Custom arguments 83 | 84 | Any other attributes added to the custom element are taken as custom arguments, that you can read from the `embedded` service's `args` property: 85 | 86 | ```html 87 | 88 | ``` 89 | 90 | ```js 91 | import Controller from '@ember/controller'; 92 | import { inject as service } from '@ember/service'; 93 | 94 | export default class FooController extends Controller { 95 | @service 96 | embedded; 97 | 98 | get foo() { 99 | return this.embedded.args.hasOwnProperty('enable-foo') ? this.embedded.args.foo : null; 100 | } 101 | } 102 | ``` 103 | 104 | 105 | Contributing 106 | ------------------------------------------------------------------------------ 107 | 108 | See the [Contributing](CONTRIBUTING.md) guide for details. 109 | 110 | 111 | License 112 | ------------------------------------------------------------------------------ 113 | 114 | This project is licensed under the [MIT License](LICENSE.md). 115 | -------------------------------------------------------------------------------- /lib/process-html.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const Plugin = require('broccoli-caching-writer'); 6 | const { JSDOM } = require('jsdom'); 7 | 8 | const INDEX_FILE = 'index.html'; 9 | 10 | // convert https://developer.mozilla.org/en-US/docs/Web/API/NamedNodeMap to simple serializable pojo 11 | function convertAttributesToObject(attributes) { 12 | return [...attributes].reduce( 13 | (hash, { name, value }) => ({ [name]: value, ...hash }), 14 | {} 15 | ); 16 | } 17 | 18 | class ProcessHtmlPlugin extends Plugin { 19 | constructor(input, { appName, rootURL, ui }) { 20 | super([input], { 21 | cacheInclude: [/index\.html/], 22 | }); 23 | this.appName = appName; 24 | this.rootURL = rootURL; 25 | this.ui = ui; 26 | } 27 | 28 | build() { 29 | if (this._processHtml()) { 30 | this._writeEmbedScript(); 31 | } 32 | } 33 | 34 | _processHtml() { 35 | this.references = []; 36 | const html = fs.readFileSync(path.join(this.inputPaths[0], INDEX_FILE), { 37 | encoding: 'utf8', 38 | }); 39 | 40 | const dom = new JSDOM(html); 41 | const document = dom.window.document; 42 | 43 | this.customElementName = this.appName; 44 | if (!this.customElementName.includes('-')) { 45 | this.customElementName += '-app'; 46 | } 47 | 48 | const customElement = document.querySelector(this.customElementName); 49 | if (!customElement) { 50 | this.ui.writeWarnLine( 51 | `ember-embedded-snippet: could not find invocation of custom element <${this.customElementName}>, skipping processing...` 52 | ); 53 | return false; 54 | } 55 | 56 | this.scripts = document.querySelectorAll( 57 | 'script:not([data-embedded-ignore])' 58 | ); 59 | this.links = document.querySelectorAll( 60 | 'link[rel="stylesheet"]:not([data-embedded-ignore])' 61 | ); 62 | this.styles = document.querySelectorAll( 63 | 'style:not([data-embedded-ignore])' 64 | ); 65 | 66 | // Remove all scripts and styles from the final index.html, as those will be loaded form our embed.js script 67 | this.scripts.forEach((ref) => ref.remove()); 68 | this.links.forEach((ref) => ref.remove()); 69 | this.styles.forEach((ref) => ref.remove()); 70 | 71 | // insert embed.js 72 | const embedScript = document.createElement('script'); 73 | embedScript.setAttribute('src', `${this.rootURL}embed.js`); 74 | customElement.insertAdjacentElement('beforebegin', embedScript); 75 | 76 | fs.writeFileSync(path.join(this.outputPath, INDEX_FILE), dom.serialize(), { 77 | encoding: 'utf8', 78 | }); 79 | 80 | return true; 81 | } 82 | 83 | _writeEmbedScript() { 84 | let js = fs.readFileSync(path.join(__dirname, '..', 'vendor', 'embed.js'), { 85 | encoding: 'utf8', 86 | }); 87 | 88 | for (const [key, value] of Object.entries(this._dynamicReplacements)) { 89 | js = js.replace(new RegExp(`/${key}/`, 'g'), value); 90 | js = js.replace(new RegExp(key, 'g'), value); 91 | } 92 | 93 | fs.writeFileSync(path.join(this.outputPath, 'embed.js'), js); 94 | } 95 | 96 | get _dynamicReplacements() { 97 | const links = Array.from(this.links).map((node) => ({ 98 | attributes: convertAttributesToObject(node.attributes), 99 | })); 100 | const scripts = Array.from(this.scripts).map((node) => ({ 101 | attributes: convertAttributesToObject(node.attributes), 102 | content: node.textContent !== '' ? node.textContent : null, 103 | })); 104 | const styles = Array.from(this.styles).map((node) => ({ 105 | attributes: convertAttributesToObject(node.attributes), 106 | content: node.textContent !== '' ? node.textContent : null, 107 | })); 108 | 109 | return { 110 | '###APPNAME###': this.appName, 111 | '###CENAME###': this.customElementName, 112 | '###LINKS###': JSON.stringify(links), 113 | '###SCRIPTS###': JSON.stringify(scripts), 114 | '###STYLES###': JSON.stringify(styles), 115 | }; 116 | } 117 | } 118 | 119 | module.exports = ProcessHtmlPlugin; 120 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## v0.7.1 (2022-05-05) 4 | 5 | #### :rocket: Enhancement 6 | * [#181](https://github.com/kaliber5/ember-embedded-snippet/pull/181) Load scripts in parallel instead of sequentially ([@simonihmig](https://github.com/simonihmig)) 7 | 8 | #### Committers: 1 9 | - Simon Ihmig ([@simonihmig](https://github.com/simonihmig)) 10 | 11 | ## v0.7.0 (2021-10-26) 12 | 13 | #### :boom: Breaking Change 14 | * [#148](https://github.com/kaliber5/ember-embedded-snippet/pull/148) Major refactoring of `embed.js` generation and app integration ([@simonihmig](https://github.com/simonihmig)) 15 | 16 | #### :rocket: Enhancement 17 | * [#165](https://github.com/kaliber5/ember-embedded-snippet/pull/165) Support inline `