-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore: refactor all so we can publish as npm module
* chore: (clearDOM) replace setTimeout with requestAnimationFrame * chore: refactor all so we can publish module * chore: rename to "custom-elements-hmr-polyfill" * chore: refactor package function * chore: update sample * chore: add more scripts to package.json * chore: update readme * chore: update package.json - author
- Loading branch information
1 parent
34db1e1
commit c0d54ae
Showing
22 changed files
with
222 additions
and
226 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
node_modules | ||
dist | ||
dist | ||
dev | ||
.cache | ||
.iml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
# customElements-hmr | ||
try and get fusebox hmr working with "customElements" API | ||
# custom-elements-hmr-polyfill | ||
Custom Element HMR polyfill | ||
|
||
How to use | ||
How to start sample: | ||
* `npm install` | ||
* `npm start` | ||
* `npm start` | ||
|
||
How to build: | ||
* `npm build` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,26 @@ | ||
{ | ||
"name": "customelements-hmr", | ||
"version": "1.0.0", | ||
"description": "try and get fusebox hmr working with \"customElements\" API", | ||
"name": "custom-elements-hmr-polyfill", | ||
"version": "1.0.0-alpha.1", | ||
"description": "Custom Elements HMR polyfill", | ||
"main": "index.js", | ||
"scripts": { | ||
"start": "node fuse" | ||
"start": "node fuse", | ||
"watch": "node fuse", | ||
"build": "node ./node_modules/typescript/bin/tsc --build ./tsconfig-build.json", | ||
"test": "todo" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/vegarringdal/customElements-hmr.git" | ||
"url": "git+https://github.com/vegarringdal/custom-elements-hmr-polyfill.git" | ||
}, | ||
"author": "", | ||
"author": "Vegar Ringdal<[email protected]> & Aron Homberg<[email protected] >", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/vegarringdal/customElements-hmr/issues" | ||
"url": "https://github.com/vegarringdal/custom-elements-hmr-polyfill/issues" | ||
}, | ||
"homepage": "https://github.com/vegarringdal/customElements-hmr#readme", | ||
"homepage": "https://github.com/vegarringdal/custom-elements-hmr-polyfill#readme", | ||
"devDependencies": { | ||
"fuse-box": "^4.0.0-alpha.80", | ||
"fuse-box": "^4.0.0-next.88", | ||
"fuse-box-typechecker": "^3.0.0-next.13", | ||
"husky": "^3.0.4", | ||
"lint-staged": "^9.2.3", | ||
|
This file was deleted.
Oops, something went wrong.
2 changes: 1 addition & 1 deletion
2
src/decorator/DefineCustomElement.ts → src/package/decorator/DefineCustomElement.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { initCache } from './polyfill/hmrCache'; | ||
import { overrideCustomElementDefine } from './polyfill/overrideCustomElementDefine'; | ||
export { clearDOM } from './utils/clearDOM'; | ||
export { defineCustomElement } from './decorator/defineCustomElement'; | ||
|
||
export function applyPolyfill() { | ||
initCache(); | ||
overrideCustomElementDefine(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { getMostRecentImpl } from './hmrCache'; | ||
|
||
export const BLACKLISTED_PATCH_METHODS = [ | ||
'constructor', | ||
'connectedCallback', | ||
'disconnectedCallback', | ||
'adoptedCallback', | ||
'attributeChangedCallback' | ||
]; | ||
|
||
export function constructInstance(elementName: string, args: any, newTarget: any) { | ||
const mostRecentImpl = getMostRecentImpl(elementName); | ||
|
||
// Constructed instance partly points to outdated impl details. | ||
// This patch loop makes sure that the hook methods aren't overridden, | ||
// the constructor stays intact but methods, getters, setters and fields | ||
// are updated according to the most recent implementation: | ||
const customElementInstance = Reflect.construct(mostRecentImpl, args, newTarget); | ||
const ownPropertyNames = Object.getOwnPropertyNames(mostRecentImpl.prototype); | ||
|
||
const whitelistedPropertyNames = ownPropertyNames.filter((propertyName: string) => { | ||
return BLACKLISTED_PATCH_METHODS.indexOf(propertyName) === -1; | ||
}); | ||
|
||
for (let i = 0; i < whitelistedPropertyNames.length; i++) { | ||
const propertyDescriptor = Object.getOwnPropertyDescriptor( | ||
mostRecentImpl.prototype, | ||
whitelistedPropertyNames[i] | ||
); | ||
|
||
if (propertyDescriptor) { | ||
Object.defineProperty( | ||
customElementInstance, | ||
whitelistedPropertyNames[i], | ||
propertyDescriptor | ||
); | ||
} | ||
} | ||
return customElementInstance; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { getMostRecentImpl } from './hmrCache'; | ||
|
||
export function createHookClass(elementName: string, originalImpl: any) { | ||
return class extends originalImpl { | ||
static get observedAttributes() { | ||
return super.observedAttributes; | ||
} | ||
|
||
connectedCallback() { | ||
const mostRecentImpl = getMostRecentImpl(elementName).prototype; | ||
if (mostRecentImpl.connectedCallback) { | ||
mostRecentImpl.connectedCallback.apply(this, arguments); | ||
} | ||
} | ||
|
||
disconnectedCallback() { | ||
const mostRecentImpl = getMostRecentImpl(elementName).prototype; | ||
if (mostRecentImpl.disconnectedCallback) { | ||
mostRecentImpl.disconnectedCallback.apply(this, arguments); | ||
} | ||
} | ||
|
||
adoptedCallback() { | ||
const mostRecentImpl = getMostRecentImpl(elementName).prototype; | ||
if (mostRecentImpl.adoptedCallback) { | ||
mostRecentImpl.adoptedCallback.apply(this, arguments); | ||
} | ||
} | ||
|
||
attributeChangedCallback() { | ||
const mostRecentImpl = getMostRecentImpl(elementName).prototype; | ||
if (mostRecentImpl.attributeChangedCallback) { | ||
mostRecentImpl.attributeChangedCallback.apply(this, arguments); | ||
} | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
export function initCache() { | ||
if (!(<any>globalThis).hmrCache) { | ||
(<any>globalThis).hmrCache = {}; | ||
} | ||
} | ||
|
||
export function getMostRecentImpl(elementName: string) { | ||
return (<any>globalThis).hmrCache[elementName]; | ||
} | ||
|
||
export function setMostRecentImpl(elementName: string, impl: any) { | ||
(<any>globalThis).hmrCache[elementName] = impl; | ||
} | ||
|
||
export function isCacheInitialized() { | ||
return (<any>globalThis).hmrCache.initialized; | ||
} | ||
|
||
export function setCacheAsInitialized() { | ||
(<any>globalThis).hmrCache.initialized = true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { setMostRecentImpl, isCacheInitialized, setCacheAsInitialized } from './hmrCache'; | ||
import { createHookClass } from './createHookClass'; | ||
import { constructInstance } from './constructInstance'; | ||
|
||
export function overrideCustomElementDefine() { | ||
if (!isCacheInitialized()) { | ||
// make sure the override happens only once | ||
setCacheAsInitialized(); | ||
|
||
const originalDefineFn = CustomElementRegistry.prototype.define; | ||
|
||
CustomElementRegistry.prototype.define = function( | ||
elementName: string, | ||
impl: any, | ||
options: ElementDefinitionOptions | ||
) { | ||
const registeredCustomElement = customElements.get(elementName); | ||
|
||
if (!registeredCustomElement) { | ||
const hookClass = new Proxy(createHookClass(elementName, impl), { | ||
construct: function(element, args, newTarget) { | ||
return constructInstance(elementName, args, newTarget); | ||
} | ||
}); | ||
originalDefineFn.apply(this, [elementName, hookClass, options]); | ||
} | ||
setMostRecentImpl(elementName, impl); | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,14 @@ | ||
const clearDOM = () => { | ||
export function clearDOM() { | ||
if (document.body) { | ||
setTimeout(() => { | ||
requestAnimationFrame(() => { | ||
// simulate a Virtual DOM re-render | ||
// TODO: How to find out (after HMR) which elements actually changed? | ||
// TODO: The whole optimization not to reload the page is dependent of being able to tell the VDOM | ||
// TODO: Only to re-render those elements that changed?! | ||
const oldBodyHtml = document.body.innerHTML; | ||
document.body.innerHTML = ''; | ||
document.body.innerHTML = oldBodyHtml; | ||
}, 100); | ||
}); | ||
} | ||
}; | ||
clearDOM(); | ||
|
Oops, something went wrong.