From a4307ab8b73c87ad3bdb3d3f107d55fac25a60d4 Mon Sep 17 00:00:00 2001 From: andy-haynes <36863574+andy-haynes@users.noreply.github.com> Date: Tue, 2 Apr 2024 11:24:56 -0700 Subject: [PATCH] feat: Preact versioning (#393) * remove preact version parameters * consolidate import logic * jsdoc * optional config * bump Preact version * fix Preact import aliasing --- apps/web/src/components/WebEngineVariants.tsx | 4 -- packages/application/src/hooks/useCompiler.ts | 10 ++-- .../application/src/hooks/useComponents.ts | 23 +------- .../application/src/hooks/useWebEngine.ts | 2 +- .../src/hooks/useWebEngineSandbox.ts | 10 +--- packages/application/src/types.ts | 3 +- packages/compiler/src/compiler.ts | 38 +++--------- packages/compiler/src/import.ts | 59 +++++++++++++++++-- packages/compiler/src/types.ts | 1 - packages/sandbox/src/components/Preview.tsx | 4 -- packages/sandbox/src/constants.ts | 1 - 11 files changed, 71 insertions(+), 84 deletions(-) diff --git a/apps/web/src/components/WebEngineVariants.tsx b/apps/web/src/components/WebEngineVariants.tsx index 4677bc76..616fab81 100644 --- a/apps/web/src/components/WebEngineVariants.tsx +++ b/apps/web/src/components/WebEngineVariants.tsx @@ -10,8 +10,6 @@ import { useEffect, useState } from 'react'; import { useComponentSourcesStore } from '@/stores/component-sources'; import { useContainerMessagesStore } from '@/stores/container-messages'; -const PREACT_VERSION = '10.17.1'; - interface WebEnginePropsVariantProps { account: AccountState | null; rootComponentPath: string; @@ -33,7 +31,6 @@ export function WebEngine({ showContainerBoundaries: flags?.showContainerBoundaries, }, flags, - preactVersion: PREACT_VERSION, hooks: { containerSourceCompiled: ({ componentPath, rawSource }) => addSource(componentPath, rawSource), @@ -95,7 +92,6 @@ export function SandboxWebEngine({ showContainerBoundaries: flags?.showContainerBoundaries, }, flags, - preactVersion: PREACT_VERSION, hooks: { containerSourceCompiled: ({ componentPath, rawSource }) => addSource(componentPath, rawSource), diff --git a/packages/application/src/hooks/useCompiler.ts b/packages/application/src/hooks/useCompiler.ts index 0869bd14..a4894ae9 100644 --- a/packages/application/src/hooks/useCompiler.ts +++ b/packages/application/src/hooks/useCompiler.ts @@ -12,7 +12,7 @@ export function useCompiler({ config, localComponents, }: { - config: WebEngineConfiguration; + config?: WebEngineConfiguration; localComponents?: { [path: string]: BOSModule }; }) { const [compiler, setCompiler] = useState(null); @@ -33,15 +33,13 @@ export function useCompiler({ compiler.postMessage({ action: 'init', localComponents, - preactVersion: config.preactVersion, - enableBlockHeightVersioning: config.flags?.enableBlockHeightVersioning, + enableBlockHeightVersioning: config?.flags?.enableBlockHeightVersioning, }); }, [ compiler, - config.flags?.bosLoaderUrl, - config.preactVersion, localComponents, - config.flags?.enableBlockHeightVersioning, + config?.flags?.bosLoaderUrl, + config?.flags?.enableBlockHeightVersioning, ]); return compiler; diff --git a/packages/application/src/hooks/useComponents.ts b/packages/application/src/hooks/useComponents.ts index d213d011..89d543b7 100644 --- a/packages/application/src/hooks/useComponents.ts +++ b/packages/application/src/hooks/useComponents.ts @@ -42,8 +42,7 @@ export function useComponents({ [components] ); - const hooks = { ...config.hooks } || {}; - const { flags, preactVersion } = config; + const hooks = { ...config?.hooks } || {}; useEffect(() => { setIsValidRootComponentPath( @@ -55,7 +54,7 @@ export function useComponents({ }, [rootComponentPath]); hooks.componentRendered = (componentId: string) => { - config.hooks?.componentRendered?.(componentId); + config?.hooks?.componentRendered?.(componentId); setComponents((currentComponents) => ({ ...currentComponents, [componentId]: { @@ -92,21 +91,6 @@ export function useComponents({ hooks?.containerSourceCompiled?.(data); - // set the Preact import maps - // TODO find a better place for this - const preactImportBasePath = `https://esm.sh/preact@${preactVersion}`; - importedModules.set('preact', preactImportBasePath); - importedModules.set('preact/', `${preactImportBasePath}/`); - importedModules.set('react', `${preactImportBasePath}/compat`); - importedModules.set('react-dom', `${preactImportBasePath}/compat`); - - for (const moduleName of importedModules.keys()) { - const [lib, subpath] = moduleName.split('/'); - if (subpath && ['preact', 'react-dom'].includes(lib)) { - importedModules.delete(moduleName); - } - } - const component = { ...components[componentId], componentId, @@ -131,8 +115,7 @@ export function useComponents({ rootComponentSource, error, isValidRootComponentPath, - flags?.bosLoaderUrl, - preactVersion, + config?.flags?.bosLoaderUrl, ]); return { diff --git a/packages/application/src/hooks/useWebEngine.ts b/packages/application/src/hooks/useWebEngine.ts index 8eb689d6..0190a48a 100644 --- a/packages/application/src/hooks/useWebEngine.ts +++ b/packages/application/src/hooks/useWebEngine.ts @@ -22,7 +22,7 @@ export function useWebEngine({ addComponent, compiler, components, - debug: config.debug, + debug: config?.debug, getComponentRenderCount, hooks, }); diff --git a/packages/application/src/hooks/useWebEngineSandbox.ts b/packages/application/src/hooks/useWebEngineSandbox.ts index 5a1c9023..f24cd64b 100644 --- a/packages/application/src/hooks/useWebEngineSandbox.ts +++ b/packages/application/src/hooks/useWebEngineSandbox.ts @@ -12,7 +12,6 @@ export function useWebEngineSandbox({ rootComponentPath, }: UseWebEngineSandboxParams) { const [nonce, setNonce] = useState(''); - const preactVersion = config.preactVersion; const { appendStylesheet, resetContainerStylesheet } = useCss(); const compiler = useCompiler({ config, localComponents }); @@ -50,14 +49,7 @@ export function useWebEngineSandbox({ action: 'execute', componentId: rootComponentPath, }); - }, [ - compiler, - domRoots, - localComponents, - preactVersion, - rootComponentPath, - setComponents, - ]); + }, [compiler, domRoots, localComponents, rootComponentPath, setComponents]); return { components, diff --git a/packages/application/src/types.ts b/packages/application/src/types.ts index b49e4459..c0c71306 100644 --- a/packages/application/src/types.ts +++ b/packages/application/src/types.ts @@ -114,7 +114,7 @@ export interface CompilerWorker extends Omit { } export interface UseWebEngineParams { - config: WebEngineConfiguration; + config?: WebEngineConfiguration; rootComponentPath?: string; } @@ -141,7 +141,6 @@ export interface WebEngineConfiguration { debug?: WebEngineDebug; flags?: WebEngineFlags; hooks?: WebEngineHooks; - preactVersion: string; } export interface WebEngineFlags { diff --git a/packages/compiler/src/compiler.ts b/packages/compiler/src/compiler.ts index d2561e9f..902ce079 100644 --- a/packages/compiler/src/compiler.ts +++ b/packages/compiler/src/compiler.ts @@ -3,7 +3,10 @@ import { SocialDb } from '@bos-web-engine/social-db'; import { buildComponentSource } from './component'; import { CssParser } from './css'; -import { buildModuleImports, buildModulePackageUrl } from './import'; +import { + buildContainerModuleImports, + buildModuleImportStatements, +} from './import'; import { fetchComponentSources } from './source'; import { transpileSource } from './transpile'; import type { @@ -30,7 +33,6 @@ export class ComponentCompiler { private bosSourceCache: Map>; private compiledSourceCache: Map; private readonly sendWorkerMessage: SendMessageCallback; - private preactVersion?: string; private enableBlockHeightVersioning?: boolean; private social: SocialDb; private readonly cssParser: CssParser; @@ -46,12 +48,7 @@ export class ComponentCompiler { }); } - init({ - localComponents, - preactVersion, - enableBlockHeightVersioning, - }: CompilerInitAction) { - this.preactVersion = preactVersion; + init({ localComponents, enableBlockHeightVersioning }: CompilerInitAction) { this.enableBlockHeightVersioning = enableBlockHeightVersioning; this.bosSourceCache.clear(); @@ -287,31 +284,10 @@ export class ComponentCompiler { .flat(); // build the import map used by the container - const importedModules = containerModuleImports.reduce( - (importMap, { moduleName, modulePath }) => { - const importMapEntries = buildModulePackageUrl( - moduleName, - modulePath, - this.preactVersion! - ); - - if (!importMapEntries) { - return importMap; - } - - const moduleEntry = importMap.get(moduleName); - if (moduleEntry) { - return importMap; - } - - importMap.set(importMapEntries.moduleName, importMapEntries.url); - return importMap; - }, - new Map() - ); + const importedModules = buildContainerModuleImports(containerModuleImports); const componentSource = [ - ...buildModuleImports(containerModuleImports), + ...buildModuleImportStatements(containerModuleImports), ...[...transformedComponents.values()].map( ({ transpiled }) => transpiled ), diff --git a/packages/compiler/src/import.ts b/packages/compiler/src/import.ts index 493ed599..6c8c9414 100644 --- a/packages/compiler/src/import.ts +++ b/packages/compiler/src/import.ts @@ -3,6 +3,7 @@ import initializeWalletSelectorPlugin from '@bos-web-engine/wallet-selector-plug import type { ImportExpression, ModuleImport } from './types'; +const PREACT_VERSION = '10.20.1'; const BWE_MODULE_URL_PREFIX = 'near://'; const PLUGIN_MODULES = new Map([ ['@bos-web-engine/social-db-plugin', initializeSocialDbPlugin.toString()], @@ -78,7 +79,9 @@ const extractModuleName = (modulePath: string) => { * Build container-level imports based on module imports across all Components * @param moduleImports set of module imports across Components within a container */ -export const buildModuleImports = (moduleImports: ModuleImport[]): string[] => { +export const buildModuleImportStatements = ( + moduleImports: ModuleImport[] +): string[] => { if (!moduleImports.length) { return []; } @@ -258,12 +261,11 @@ const aggregateModuleImports = (imports: ImportExpression[]): ImportsByType => { /** * Build the importmap URL based on package name/URL * @param moduleName module name specified in the import statement - * @param preactVersion version of Preact dependency + * @param modulePath module import path */ export const buildModulePackageUrl = ( moduleName: string, - modulePath: string, - preactVersion: string + modulePath: string ) => { if (modulePath.startsWith('https://')) { return { @@ -274,6 +276,53 @@ export const buildModulePackageUrl = ( return { moduleName, - url: `https://esm.sh/${moduleName}?alias=react:preact/compat&deps=preact@${preactVersion}`, + url: `https://esm.sh/${moduleName}?alias=react:preact/compat&external=preact`, }; }; + +/** + * Given a set of module imports, construct the importmap with references to esm.sh modules + * @param containerModuleImports set of module imports across the container + */ +export const buildContainerModuleImports = ( + containerModuleImports: ModuleImport[] +) => { + const importedModules = containerModuleImports.reduce( + (importMap, { moduleName, modulePath }) => { + const importMapEntries = buildModulePackageUrl(moduleName, modulePath); + + if (!importMapEntries) { + return importMap; + } + + const moduleEntry = importMap.get(moduleName); + if (moduleEntry) { + return importMap; + } + + importMap.set(importMapEntries.moduleName, importMapEntries.url); + return importMap; + }, + new Map() + ); + + // set the Preact import maps + const preactImportPath = `https://esm.sh/stable/preact@${PREACT_VERSION}`; + const preactCompatPath = `https://esm.sh/preact@${PREACT_VERSION}/compat`; + importedModules.set('preact', preactImportPath); + importedModules.set('react', preactCompatPath); + importedModules.set('react-dom', preactCompatPath); + + // remove conflicting imports from source + for (const moduleName of importedModules.keys()) { + const [lib, subpath] = moduleName.split('/'); + if (subpath && ['preact', 'react-dom'].includes(lib)) { + importedModules.delete(moduleName); + } + } + + importedModules.set('preact/compat', preactCompatPath); + importedModules.set('preact/compat/', `${preactCompatPath}/`); + + return importedModules; +}; diff --git a/packages/compiler/src/types.ts b/packages/compiler/src/types.ts index 43498e24..479059f3 100644 --- a/packages/compiler/src/types.ts +++ b/packages/compiler/src/types.ts @@ -14,7 +14,6 @@ export type LocalComponentMap = { [path: string]: BOSModule }; export interface CompilerInitAction { action: 'init'; localComponents?: LocalComponentMap; - preactVersion: string; enableBlockHeightVersioning?: boolean; } diff --git a/packages/sandbox/src/components/Preview.tsx b/packages/sandbox/src/components/Preview.tsx index a7a71c84..903dcbdc 100644 --- a/packages/sandbox/src/components/Preview.tsx +++ b/packages/sandbox/src/components/Preview.tsx @@ -11,7 +11,6 @@ import { useEffect, useState } from 'react'; import s from './Preview.module.css'; import { DEFAULT_SANDBOX_ACCOUNT_ID, - PREACT_VERSION, PREVIEW_UPDATE_DEBOUNCE_DELAY, } from '../constants'; import { useDebouncedValue } from '../hooks/useDebounced'; @@ -42,9 +41,6 @@ export function Preview() { const accountId = account?.accountId ?? DEFAULT_SANDBOX_ACCOUNT_ID; const { components, nonce } = useWebEngineSandbox({ - config: { - preactVersion: PREACT_VERSION, - }, localComponents, rootComponentPath, }); diff --git a/packages/sandbox/src/constants.ts b/packages/sandbox/src/constants.ts index 771628a2..f854b169 100644 --- a/packages/sandbox/src/constants.ts +++ b/packages/sandbox/src/constants.ts @@ -6,7 +6,6 @@ export const FILE_EXTENSIONS = ['tsx', 'module.css'] as const; export type FileExtension = (typeof FILE_EXTENSIONS)[number]; export const DEFAULT_SANDBOX_ACCOUNT_ID = 'bwe-web.near'; -export const PREACT_VERSION = '10.17.1'; export const FILE_EXTENSION_REGEX = new RegExp( `\\.(${FILE_EXTENSIONS.join('|')})$` );