@lwrjs/client-modules
Advanced tools
Comparing version
@@ -1,2 +0,2 @@ | ||
import { ProviderContext, ResourceDefinition, ResourceIdentifier, ResourceProvider, RuntimeContext } from '@lwrjs/types'; | ||
import { ProviderContext, ResourceDefinition, ResourceIdentifier, ResourceProvider, RuntimeEnvironment } from '@lwrjs/types'; | ||
export default class ClientModuleService implements ResourceProvider { | ||
@@ -8,4 +8,4 @@ name: string; | ||
constructor(context: ProviderContext); | ||
getResource<T extends ResourceIdentifier, R extends RuntimeContext>(resource: T, context: R): Promise<ResourceDefinition | undefined>; | ||
getResource<T extends ResourceIdentifier, R extends RuntimeEnvironment>(resource: T, environment: R): Promise<ResourceDefinition | undefined>; | ||
} | ||
//# sourceMappingURL=index.d.ts.map |
@@ -18,3 +18,3 @@ "use strict"; | ||
} | ||
async getResource(resource, context) { | ||
async getResource(resource, environment) { | ||
const { specifier, version = this.version } = resource; | ||
@@ -30,5 +30,5 @@ // Check that the resource is the correct version | ||
specifier, | ||
path: absFilepath, | ||
type: 'text/javascript', | ||
src: this.context.resourceRegistry.resolveResourceUri(resource, context), | ||
type: 'application/javascript', | ||
stream: fs_1.default.createReadStream(absFilepath), | ||
src: this.context.resourceRegistry.resolveResourceUri(resource, environment), | ||
}; | ||
@@ -35,0 +35,0 @@ } |
@@ -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; | ||
} | ||
}); | ||
} |
@@ -16,29 +16,34 @@ /** | ||
* Import any requested static application dependencies, define the root | ||
* application component into the CustomElement registry, and inject it. | ||
* @param rootAppSpecifier - The bare specifier for the component, eg: 'x/appRoot' | ||
* @param rootApp - A reference to the Constructor (extended from LightningElement) | ||
* 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]] | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export function init(rootAppSpecifier, rootApp) { | ||
export function init(rootModules) { | ||
if (typeof customElements !== 'undefined' && typeof document !== 'undefined') { | ||
const elementName = toKebabCase(rootAppSpecifier); // Add the root to the CustomElementRegistry | ||
const container = document.querySelector('[lwr-root]'); | ||
rootModules.forEach(([moduleSpecifier, ctor]) => { | ||
// Kebab-case the specifier | ||
const elementName = toKebabCase(moduleSpecifier); // Add the root to the CustomElementRegistry | ||
customElements.define(elementName, rootApp.CustomElementConstructor); // Append the root element to the DOM, if it does not exist | ||
customElements.define(elementName, ctor.CustomElementConstructor); // Append the root element to the DOM, if it does not exist | ||
const rootEl = document.body.querySelector(elementName); | ||
let el = document.body.querySelector(elementName); | ||
if (!rootEl) { | ||
const el = document.createElement(elementName); | ||
const container = document.querySelector('[lwr-root]'); | ||
if (!el) { | ||
el = document.createElement(elementName); | ||
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); | ||
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); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} |
@@ -6,5 +6,5 @@ # 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) | ||
- defining the _root application 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 application 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], | ||
]); | ||
``` | ||
@@ -38,9 +42,11 @@ | ||
// 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_: | ||
@@ -47,0 +53,0 @@ ```json |
@@ -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; | ||
} | ||
}); | ||
} |
@@ -12,7 +12,14 @@ import { init } from '../init'; | ||
}; | ||
const TestCmp4 = { | ||
CustomElementConstructor: class Fake extends HTMLElement {}, | ||
}; | ||
const TestCmp5 = { | ||
CustomElementConstructor: class Fake extends HTMLElement {}, | ||
}; | ||
describe('LWR init', () => { | ||
it('initializes and append root element to <body>', () => { | ||
init('generated/lauraHomePage', TestCmp); | ||
it('initializes and appends root element to <body>', () => { | ||
init([['generated/lauraHomePage', TestCmp]]); | ||
expect(customElements.get('generated-laura-home-page')).toBeDefined(); | ||
expect(document.body.querySelector('generated-laura-home-page')).not.toBeNull(); | ||
}); | ||
@@ -23,14 +30,25 @@ it('initializes and append root element to lwr-root node', () => { | ||
document.body.appendChild(container); | ||
init('c/darrell', TestCmp2); | ||
init([['c/darrell', TestCmp2]]); | ||
expect(customElements.get('c-darrell')).toBeDefined(); | ||
expect(document.body.querySelector('[lwr-root] c-darrell')).not.toBeNull(); | ||
}); | ||
it('initializes and skips appending the root element', () => { | ||
document.body.appendChild(document.createElement('c-diego')); | ||
init('c/diego', TestCmp3); | ||
init([['c/diego', TestCmp3]]); | ||
expect(customElements.get('c-diego')).toBeDefined(); | ||
}); | ||
it('initializes and appends multiple root elements to <body>', () => { | ||
init([ | ||
['c/hunter', TestCmp4], | ||
['c/brian', TestCmp5], | ||
]); | ||
expect(customElements.get('c-hunter')).toBeDefined(); | ||
expect(customElements.get('c-brian')).toBeDefined(); | ||
expect(document.body.querySelector('c-hunter')).not.toBeNull(); | ||
expect(document.body.querySelector('c-brian')).not.toBeNull(); | ||
}); | ||
it('initializes in a headless environment', () => { | ||
const oldCustomElements = customElements; | ||
delete globalThis.customElements; | ||
init('c/khang', TestCmp); | ||
init([['c/khang', TestCmp]]); | ||
globalThis.customElements = oldCustomElements; | ||
@@ -37,0 +55,0 @@ expect(customElements.get('c-khang')).toBeUndefined(); |
@@ -0,1 +1,3 @@ | ||
import type { LightningElement } from 'lwc/types'; | ||
/** | ||
@@ -20,27 +22,33 @@ * Convert a module specifier into a valid CustomElement registry name: | ||
* Import any requested static application dependencies, define the root | ||
* application component into the CustomElement registry, and inject it. | ||
* @param rootAppSpecifier - The bare specifier for the component, eg: 'x/appRoot' | ||
* @param rootApp - A reference to the Constructor (extended from LightningElement) | ||
* 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(rootAppSpecifier: string, rootApp): void { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
export function init(rootModules: [string, LightningElement][]): void { | ||
if (typeof customElements !== 'undefined' && typeof document !== 'undefined') { | ||
const elementName = toKebabCase(rootAppSpecifier); | ||
const container = document.querySelector('[lwr-root]'); | ||
// Add the root to the CustomElementRegistry | ||
customElements.define(elementName, rootApp.CustomElementConstructor); | ||
rootModules.forEach(([moduleSpecifier, ctor]) => { | ||
// Kebab-case the specifier | ||
const elementName = toKebabCase(moduleSpecifier); | ||
// Append the root element to the DOM, if it does not exist | ||
const rootEl = document.body.querySelector(elementName); | ||
if (!rootEl) { | ||
const el = document.createElement(elementName); | ||
const container = document.querySelector('[lwr-root]'); | ||
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); | ||
// Add the root to the CustomElementRegistry | ||
customElements.define(elementName, ctor.CustomElementConstructor); | ||
// Append the root element to the DOM, if it does not exist | ||
let el = document.body.querySelector(elementName); | ||
if (!el) { | ||
el = document.createElement(elementName); | ||
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); | ||
} | ||
} | ||
} | ||
}); | ||
} | ||
} |
@@ -6,5 +6,5 @@ # 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) | ||
- defining the _root application 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 application 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], | ||
]); | ||
``` | ||
@@ -38,9 +42,11 @@ | ||
// 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_: | ||
@@ -47,0 +53,0 @@ ```json |
@@ -7,3 +7,3 @@ { | ||
}, | ||
"version": "0.0.2-alpha3", | ||
"version": "0.0.2-alpha30", | ||
"homepage": "https://lwr.dev/", | ||
@@ -23,16 +23,15 @@ "repository": { | ||
"build/**/*.d.ts", | ||
"modules", | ||
"assets" | ||
"modules" | ||
], | ||
"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.11.11", | ||
"@lwrjs/shared-utils": "0.0.2-alpha30" | ||
}, | ||
"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" | ||
@@ -42,8 +41,9 @@ } | ||
"expose": [ | ||
"lwc", | ||
"lwr/hmr", | ||
"lwr/init" | ||
"lwr/init", | ||
"lwr/servicesESM", | ||
"lwr/lockerDefine" | ||
] | ||
}, | ||
"gitHead": "0a3ec38ad674e502f5dae4d232de8975fb0e4d76" | ||
"gitHead": "ec31eb8e6e809d9afc95f63c0428e04a5830451d" | ||
} |
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
21
75%58
11.54%219302
-11.33%2
Infinity%4920
-21.51%1
Infinity%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added