Skip to content

Commit 812074f

Browse files
committed
Uses a loader as the single point of entry
1 parent 9442525 commit 812074f

File tree

5 files changed

+280
-16
lines changed

5 files changed

+280
-16
lines changed

addon/create-web-components.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class ApplicationContainer extends HTMLElement {
2222
constructor() {
2323
super()
2424
this.#shadowRoot = this.attachShadow({mode: 'closed'})
25+
2526
// The 2 divs are a trick in how Ember finds their parent
2627
let rootParent = document.createElement('div')
2728
_appendStyles(rootParent, this.#styles)
@@ -30,8 +31,18 @@ class ApplicationContainer extends HTMLElement {
3031
this.#shadowRoot.appendChild(rootParent)
3132
rootParent.appendChild(rootElement)
3233

34+
// Handle inner config
35+
let configSource = this.querySelector('[data-json-config]').textContent
36+
let appConfig = JSON.parse(configSource)
37+
38+
// Handle attribute-based config
39+
let attributes = [...this.attributes].reduce((acc, a)=> {
40+
return {...acc, [a.nodeName]: a.nodeValue }
41+
}, {})
42+
config.appConfig = { ...appConfig, ...attributes }
3343
}
3444

45+
// Starts the app when an element is connected
3546
connectedCallback() {
3647
if (this.#application || !this.isConnected) {
3748
return
@@ -43,6 +54,7 @@ class ApplicationContainer extends HTMLElement {
4354
this.#application = app
4455
}
4556

57+
// Destroy the app on disconnection of the node
4658
disconnectedCallback() {
4759
if (!this.#application.isDestroyed && !this.#application.isDestroying) {
4860
this.#application.destroy()

index.js

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
'use strict';
22

3+
const writeFile = require('broccoli-file-creator');
4+
const funnel = require('broccoli-funnel')
5+
const merge = require('broccoli-merge-trees')
6+
const path = require('path')
7+
const rollup = require('broccoli-rollup')
8+
const json = require('rollup-plugin-json')
9+
const babel = require('rollup-plugin-babel')
10+
const resolve = require('rollup-plugin-node-resolve')
11+
312
module.exports = {
413
name: require('./package').name,
514
config(env, config) {
@@ -13,7 +22,6 @@ module.exports = {
1322
let assets = this._findAssets(app.options.outputPaths)
1423
return {
1524
webComponentsAssets : assets
16-
1725
}
1826
},
1927
included(addon) {
@@ -31,9 +39,42 @@ module.exports = {
3139
}
3240
} = paths
3341
return {
34-
css: { ...appCss, vendor: vendorCss },
35-
js: { app: appJs, vendor: vendorJs }
42+
css: [ vendorCss, appCss.app ],
43+
js: [ vendorJs, appJs ]
3644
}
3745

46+
},
47+
treeForPublic() {
48+
let componentConfig = {
49+
name: this.app.name,
50+
assets: this.config().webComponentsAssets
51+
}
52+
let loader = funnel(path.resolve(__dirname, './loader'))
53+
const data = writeFile('/loader/component.json', JSON.stringify(componentConfig))
54+
const tree = merge([data, loader])
55+
const loaderRollup = rollup(tree, {
56+
rollup: {
57+
input: 'index.js',
58+
output: {
59+
format: 'umd',
60+
file: 'loader.js'
61+
},
62+
plugins: [
63+
resolve(),
64+
json(),
65+
babel({
66+
plugins: [
67+
'@babel/plugin-proposal-class-properties'
68+
],
69+
presets: [['@babel/env', {
70+
targets: 'last 2 version, ie 11',
71+
useBuiltIns: "entry",
72+
corejs: 3
73+
}]]
74+
})
75+
]
76+
}
77+
});
78+
return loaderRollup
3879
}
3980
};

loader/index.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import "regenerator-runtime/runtime";
2+
import data from './loader/component.json'
3+
4+
let name = data.name;
5+
6+
// fake private method
7+
function _appendCss(dest, css) {
8+
let files = Object.values(css)
9+
files.forEach((f)=> {
10+
let style = document.createElement('link')
11+
style.setAttribute('href', f)
12+
style.setAttribute('rel', 'stylesheet')
13+
dest.appendChild(style)
14+
})
15+
}
16+
17+
// fake private method
18+
async function _appendJs(dest, js) {
19+
let files = Object.values(js)
20+
return files.reduce(async (p, f)=> {
21+
await p
22+
return new Promise(function(resolve) {
23+
let js = document.createElement('script')
24+
js.defer = true
25+
js.setAttribute('src', f)
26+
dest.appendChild(js)
27+
js.onload = ()=> resolve()
28+
})
29+
}, Promise.resolve())
30+
}
31+
32+
let css = (data.assets || {}).css
33+
let js = (data.assets || {}).js
34+
35+
class ApplicationContainer extends HTMLElement {
36+
#css = css
37+
#js = js
38+
#shadowRoot
39+
#application
40+
41+
constructor() {
42+
super()
43+
this.#shadowRoot = this.attachShadow({mode: 'closed'})
44+
45+
// The 2 divs are a trick in how Ember finds their parent
46+
let rootParent = document.createElement('div')
47+
_appendCss(rootParent, this.#css)
48+
this._loading = _appendJs(rootParent, this.#js)
49+
let rootElement = document.createElement('div')
50+
rootElement.setAttribute('data-ember-root-element', '')
51+
this.#shadowRoot.appendChild(rootParent)
52+
rootParent.appendChild(rootElement)
53+
}
54+
55+
// Starts the app when an element is connected
56+
async connectedCallback() {
57+
if (this._application || !this.isConnected) {
58+
return
59+
}
60+
61+
await this._loading
62+
63+
// Handle inner config
64+
let configSource = this.querySelector('[data-json-config]').textContent
65+
let appConfig = JSON.parse(configSource)
66+
67+
// Handle attribute-based config
68+
let attributes = [...this.attributes].reduce((acc, a)=> {
69+
return {...acc, [a.nodeName]: a.nodeValue }
70+
}, {})
71+
let config = { ...appConfig, ...attributes }
72+
73+
// Create the app
74+
let app = require(`${name}/app`).default.create({
75+
rootElement: this.#shadowRoot.querySelector(`[data-ember-root-element]`),
76+
config
77+
})
78+
this._application = app
79+
80+
// Register the attributes
81+
app.register('service:context', config, { instantiate: false })
82+
}
83+
84+
// Destroy the app on disconnection of the node
85+
disconnectedCallback() {
86+
if (!this._application.isDestroyed && !this._application.isDestroying) {
87+
this._application.destroy()
88+
}
89+
}
90+
91+
// That makes the application accessible via:
92+
// document.querySelector('application-name').__EMBER_APPLICATION
93+
get __EMBER_APPLICATION() {
94+
return this._application
95+
}
96+
}
97+
98+
let componentName = name
99+
if (-1 === name.indexOf('-')) {
100+
componentName += '-app'
101+
}
102+
103+
customElements.define(componentName, ApplicationContainer)

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,17 @@
2121
"test:all": "ember try:each"
2222
},
2323
"dependencies": {
24+
"broccoli-rollup": "^4.1.1",
2425
"ember-cli-babel": "^7.7.3",
25-
"ember-get-config": "^0.2.4"
26+
"ember-get-config": "^0.2.4",
27+
"rollup-plugin-babel": "^4.3.3",
28+
"rollup-plugin-json": "^4.0.0"
2629
},
2730
"devDependencies": {
31+
"@babel/preset-env": "^7.5.5",
2832
"@ember/optional-features": "^0.7.0",
2933
"broccoli-asset-rev": "^3.0.0",
34+
"core-js": "3",
3035
"ember-cli": "~3.12.0",
3136
"ember-cli-dependency-checker": "^3.1.0",
3237
"ember-cli-eslint": "^5.1.0",
@@ -47,7 +52,8 @@
4752
"eslint-plugin-ember": "^6.2.0",
4853
"eslint-plugin-node": "^9.0.1",
4954
"loader.js": "^4.7.0",
50-
"qunit-dom": "^0.8.4"
55+
"qunit-dom": "^0.8.4",
56+
"rollup-plugin-node-resolve": "^5.2.0"
5157
},
5258
"engines": {
5359
"node": "8.* || >= 10.*"

0 commit comments

Comments
 (0)