@lwrjs/client-modules
Advanced tools
Comparing version 0.0.1 to 0.0.2-alpha.9
@@ -1,35 +0,51 @@ | ||
// @ts-nocheck | ||
import { registerTemplateSwap, registerStyleSwap, registerComponentSwap } from 'lwc'; | ||
import { updateStaleModule } from './util/swap'; | ||
export function initHMR() { | ||
// eslint-disable-next-line no-undef | ||
const isDevMode = globalThis.process.env.NODE_ENV === 'dev'; | ||
// This is a workaround until we don't change the way HMR works | ||
// The server will always return the same canonical "latest URL" | ||
// So we need to track the last new URI instead | ||
const URI_MAPPING = new Map(); | ||
if (isDevMode) { | ||
const socket = new WebSocket(`ws://${location.host}`); | ||
async function moduleUpdate(payload) { | ||
const { | ||
oldUri, | ||
newUri, | ||
module: { specifier }, | ||
} = payload; | ||
socket.addEventListener('message', async ({ data }) => { | ||
const event = JSON.parse(data); | ||
const lastEvalutedUri = URI_MAPPING.get(oldUri) || oldUri; | ||
const oldModule = await import(lastEvalutedUri); | ||
const newModule = await import(newUri); | ||
URI_MAPPING.set(oldUri, newUri); | ||
if (event.eventType === 'moduleUpdate') { | ||
const { | ||
oldUri, | ||
newUri, | ||
module: { namespace, name }, | ||
} = event.payload; | ||
updateStaleModule({ | ||
oldModule, | ||
newModule, | ||
specifier, | ||
}); | ||
} | ||
const oldModule = await import(oldUri); | ||
const newModule = await import(newUri); | ||
async function viewUpdate(payload) { | ||
const pathName = window.location.pathname; | ||
const requestPath = payload.viewId.requestPath; | ||
if (name.endsWith('html') && newModule.default) { | ||
console.log(`Swapping html template for module "${namespace}/${name}"`); | ||
registerTemplateSwap(oldModule.default, newModule.default); | ||
} else if (name.endsWith('css') && newModule.default) { | ||
registerStyleSwap(oldModule.default[0], newModule.default[0]); | ||
} else { | ||
registerComponentSwap(oldModule.default, newModule.default); | ||
} | ||
} | ||
}); | ||
if (requestPath === pathName) { | ||
window.location.reload(); | ||
} | ||
} | ||
export function initHMR(serverURI = '') { | ||
// {apiVersion}/hmr/{format}/{compat}?debug | ||
const socket = new WebSocket(`ws://${location.host}/${serverURI}`); | ||
socket.addEventListener('message', async ({ data }) => { | ||
const { eventType, payload } = JSON.parse(data); | ||
switch (eventType) { | ||
case 'moduleUpdate': | ||
return moduleUpdate(payload); | ||
case 'viewUpdate': | ||
return viewUpdate(payload); | ||
default: | ||
return; | ||
} | ||
}); | ||
} |
@@ -0,1 +1,11 @@ | ||
// TODO: This is a temporal workaround until https://github.com/salesforce/lwc/pull/2083 is sorted | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore | ||
import { createElement } from 'lwc'; | ||
function initializeWebComponent(elementName, Ctor) { | ||
return createElement(elementName, { | ||
is: Ctor | ||
}); | ||
} | ||
/** | ||
@@ -11,2 +21,4 @@ * Convert a module specifier into a valid CustomElement registry name: | ||
*/ | ||
function toKebabCase(specifier) { | ||
@@ -16,14 +28,70 @@ return specifier.replace(/\/v\/[a-zA-Z0-9-_.]+$/, '').replace('/', '-').replace(/([A-Z])/g, c => `-${c.toLowerCase()}`); | ||
/** | ||
* Import any requested static application dependencies and | ||
* define the root application component into the CustomElement registry. | ||
* @param rootAppSpecifier - The bare specifier for the component, eg: 'x/appRoot' | ||
* @param rootApp - A reference to the Constructor (extended from LightningElement) | ||
* This method maps between attribute names | ||
* and the corresponding props name. | ||
*/ | ||
export function init(rootAppSpecifier, rootApp) { | ||
if (typeof customElements !== 'undefined') { | ||
const elementName = toKebabCase(rootAppSpecifier); | ||
customElements.define(elementName, rootApp.CustomElementConstructor); | ||
const CAMEL_REGEX = /-([a-z])/g; | ||
export function getPropFromAttrName(propName) { | ||
return propName.replace(CAMEL_REGEX, g => g[1].toUpperCase()); | ||
} | ||
/** | ||
* Import any requested static application dependencies, define the root | ||
* application component(s) into the CustomElement registry, and inject them. | ||
* @param rootModules - An array of arrays, each one holding a pair of | ||
* bare specifier and corresponding LightningElement constructor | ||
* @example - [['x/appRoot', appCtor], ['x/nav', navCtor]] | ||
*/ | ||
export function init(rootModules) { | ||
if (typeof customElements !== 'undefined' && typeof document !== 'undefined') { | ||
const container = document.querySelector('[lwr-root]'); | ||
rootModules.forEach(([moduleSpecifier, ctor]) => { | ||
// Kebab-case the specifier | ||
const elementName = toKebabCase(moduleSpecifier); // Append the root element to the DOM, if it does not exist | ||
// this is for SPA like routes (one component at the root level) utilizing the lwr-root directive | ||
let el = document.body.querySelector(elementName); | ||
if (!el) { | ||
el = initializeWebComponent(elementName, ctor); | ||
if (container) { | ||
// Append to a node with the "lwr-root" attribute | ||
container.appendChild(el); | ||
} else { | ||
// Otherwise, add the root to the <body> | ||
document.body.appendChild(el); | ||
} | ||
} else { | ||
// We have rendered/ssred an HTML page and we need to reify the components | ||
// Due to the bug described on the header, for each custom element | ||
// we collect the attributes and we replace the element with the new synthetic contructor | ||
const customElements = document.querySelectorAll(elementName); | ||
customElements.forEach(customElement => { | ||
const newElement = initializeWebComponent(elementName, ctor); | ||
for (const { | ||
name, | ||
value | ||
} of customElement.attributes) { | ||
newElement.setAttribute(name, value); | ||
const prop = getPropFromAttrName(name); | ||
if (prop in newElement) { | ||
// Set attributes as properties too for reactivity | ||
newElement[prop] = value; | ||
} | ||
} // Move the children | ||
while (customElement.childNodes.length > 0) { | ||
newElement.appendChild(customElement.childNodes[0]); | ||
} | ||
customElement.parentElement.replaceChild(newElement, customElement); | ||
}); | ||
} | ||
}); | ||
} | ||
} |
@@ -5,6 +5,6 @@ # Lightning Web Runtime :: Application Initialization | ||
- importing static application dependencies | ||
- defining the _root application component_ into the [`CustomElementRegistry`](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry) | ||
- importing static dependencies | ||
- defining the _root component(s)_ into the [`CustomElementRegistry`](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry) | ||
This results in the _root application component_ being renderable by the DOM on first page load. | ||
This results in the _root component(s)_ being renderable by the DOM on first page load. | ||
@@ -15,3 +15,3 @@ ## Definition | ||
interface InitAPI { | ||
init(rootAppSpecifier: string, rootApp: LightningElement): void; | ||
init(rootModules: [string, LightningElement][]): void; | ||
} | ||
@@ -25,4 +25,8 @@ ``` | ||
import xAppRoot from 'x/appRoot'; | ||
import xNav from 'x/nav'; | ||
init('x/appRoot', xAppRoot); | ||
init([ | ||
['x/appRoot', xAppRoot], | ||
['x/nav', xNav], | ||
]); | ||
``` | ||
@@ -32,3 +36,3 @@ | ||
The default `lwr/init` module used by an LWR application can be overridden with these steps: | ||
The default `lwr/init` module used by an LWR route can be overridden with these steps: | ||
@@ -39,18 +43,21 @@ 1. Create a module exporting an `init` function which implements the `InitAPI` interface: | ||
// src/modules/example/init/init.js | ||
export function init(rootAppSpecifier, rootApp) { | ||
const elementName = rootAppSpecifier.replace(/\//, '-'); | ||
customElements.define(elementName, rootApp.CustomElementConstructor); | ||
export function init(rootModules) { | ||
rootModules.forEach(([moduleSpecifier, ctor]) => { | ||
const elementName = toKebabCase(moduleSpecifier); | ||
customElements.define(elementName, ctor.CustomElementConstructor); | ||
}); | ||
} | ||
``` | ||
2. Configure the module specifier in _lwr.config.json_: | ||
2. Configure the `init` module specifier in _lwr.config.json_: | ||
```json | ||
{ | ||
"modules": [], | ||
"application": { | ||
"root": "example/app", | ||
"init": "example/init" | ||
} | ||
"routes": [ | ||
{ | ||
"id": "example", | ||
"init": "example/init" | ||
} | ||
] | ||
} | ||
``` |
@@ -7,32 +7,49 @@ { | ||
}, | ||
"version": "0.0.1", | ||
"types": "build/index.d.ts", | ||
"main": "build/index.js", | ||
"version": "0.0.2-alpha.9+8e3fccca", | ||
"homepage": "https://lwr.dev/", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/salesforce/lwr.git", | ||
"directory": "packages/@lwrjs/client-modules" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/salesforce/lwr/issues" | ||
}, | ||
"type": "module", | ||
"types": "build/es/index.d.ts", | ||
"main": "build/es/index.js", | ||
"module": "build/es/index.js", | ||
"exports": { | ||
".": "./build/es/index.js" | ||
}, | ||
"files": [ | ||
"build/**/*.js", | ||
"build/**/*.d.ts", | ||
"modules", | ||
"assets" | ||
"build/**/*.d.ts" | ||
], | ||
"scripts": { | ||
"build": "node ../../../bin/pack-lwc --dir modules build/modules" | ||
"build": "node ../../../bin/pack-lwc --dir modules build/modules && yarn build:locker", | ||
"build:locker": "rollup --config ./scripts/rollup.config.locker.js" | ||
}, | ||
"dependencies": { | ||
"@locker/sandbox": "^0.12.4", | ||
"@lwrjs/shared-utils": "0.0.2-alpha.9+8e3fccca" | ||
}, | ||
"lwc": { | ||
"modules": [ | ||
{ | ||
"name": "lwc", | ||
"path": "assets/lwc/engine.js", | ||
"description": "Temporal Hack: We need to fix the lwc package to make npm: 'lwc' work" | ||
}, | ||
{ | ||
"dir": "modules" | ||
"dir": "build/modules" | ||
} | ||
], | ||
"expose": [ | ||
"lwc", | ||
"lwr/hmr", | ||
"lwr/init" | ||
"lwr/init", | ||
"lwr/servicesESM", | ||
"lwr/lockerDefine", | ||
"lwr/lockerSandbox" | ||
] | ||
}, | ||
"gitHead": "638109b1e3e85483bc63c87ed2a6c3d582f28e04" | ||
"engines": { | ||
"node": ">=14.15.1 <15" | ||
}, | ||
"gitHead": "8e3fcccaba5138a3c6fac4c75d4506987b6460b7" | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
402578
9139
1
59
0
Yes
2
10
2
+ Added@locker/sandbox@^0.12.4
+ Added@locker/distortion@0.12.14(transitive)
+ Added@locker/html-sanitizer@0.12.14(transitive)
+ Added@locker/near-membrane@0.4.0(transitive)
+ Added@locker/sandbox@0.12.14(transitive)
+ Added@locker/shared@0.12.14(transitive)
+ Added@locker/shared-dom@0.12.14(transitive)
+ Added@locker/shared-url@0.12.14(transitive)
+ Added@types/dompurify@2.1.0(transitive)
+ Added@types/trusted-types@2.0.7(transitive)
+ Addeddompurify@2.2.2(transitive)