experiments in a post-browser web
at main 390 lines 9.2 kB view raw
1/** 2 * Peek Component Bundle Configuration 3 * 4 * Defines the component library structure for bundling and distribution. 5 * Can be used with any ESM-compatible bundler (esbuild, rollup, vite, etc.) 6 * 7 * Usage: 8 * import { bundleConfig, generateImportMap, getEntryPoints } from 'peek://app/components/bundle.js'; 9 */ 10 11import { LIBRARY_VERSION } from './version.js'; 12 13/** 14 * Component module definitions 15 */ 16export const MODULES = { 17 // Core utilities 18 base: { 19 path: './base.js', 20 exports: ['PeekElement', 'sharedStyles'], 21 dependencies: ['lit'] 22 }, 23 signals: { 24 path: './signals.js', 25 exports: ['signal', 'computed', 'effect', 'batch', 'watch', 'fromExternal'], 26 dependencies: [] 27 }, 28 schema: { 29 path: './schema.js', 30 exports: ['validate', 'createValidator', 'assertValid', 'isValid', 'Schema'], 31 dependencies: [] 32 }, 33 'data-binding': { 34 path: './data-binding.js', 35 exports: ['DataBoundElement', 'DataBindingMixin', 'createDataComponent'], 36 dependencies: ['./base.js', './signals.js', './schema.js'] 37 }, 38 events: { 39 path: './events.js', 40 exports: ['bus', 'on', 'once', 'emit', 'channel', 'typedEvent', 'waitFor', 'EventBusMixin'], 41 dependencies: [] 42 }, 43 theme: { 44 path: './theme.js', 45 exports: ['registerTheme', 'setTheme', 'getTheme', 'getThemeTokens', 'ThemeMixin'], 46 dependencies: [] 47 }, 48 extension: { 49 path: './extension.js', 50 exports: ['registerExtension', 'ExtensionContext', 'initContentScript', 'initPopup'], 51 dependencies: ['./theme.js'] 52 }, 53 registry: { 54 path: './registry.js', 55 exports: ['registry', 'defineComponent', 'loadComponent', 'createElement'], 56 dependencies: [] 57 }, 58 version: { 59 path: './version.js', 60 exports: ['version', 'LIBRARY_VERSION', 'checkCompatibility'], 61 dependencies: [] 62 }, 63 dev: { 64 path: './dev.js', 65 exports: ['devTools', 'enableDevMode', 'enableHotReload'], 66 dependencies: ['./registry.js', './theme.js', './extension.js', './version.js'], 67 devOnly: true 68 }, 69 70 // Basic components 71 'peek-button': { 72 path: './peek-button.js', 73 exports: ['PeekButton'], 74 dependencies: ['./base.js'], 75 component: true 76 }, 77 'peek-card': { 78 path: './peek-card.js', 79 exports: ['PeekCard'], 80 dependencies: ['./base.js'], 81 component: true 82 }, 83 'peek-list': { 84 path: './peek-list.js', 85 exports: ['PeekList', 'PeekListItem'], 86 dependencies: ['./base.js'], 87 component: true 88 }, 89 90 // Complex components 91 'peek-carousel': { 92 path: './peek-carousel.js', 93 exports: ['PeekCarousel'], 94 dependencies: ['./base.js'], 95 component: true 96 }, 97 'peek-input': { 98 path: './peek-input.js', 99 exports: ['PeekInput'], 100 dependencies: ['./base.js'], 101 component: true 102 }, 103 'peek-grid': { 104 path: './peek-grid.js', 105 exports: ['PeekGrid', 'PeekGridItem'], 106 dependencies: ['./base.js'], 107 component: true 108 }, 109 'peek-dialog': { 110 path: './peek-dialog.js', 111 exports: ['PeekDialog'], 112 dependencies: ['./base.js'], 113 component: true 114 }, 115 116 // Native/Open UI components 117 'peek-popover': { 118 path: './peek-popover.js', 119 exports: ['PeekPopover'], 120 dependencies: ['./base.js'], 121 component: true 122 }, 123 'peek-tabs': { 124 path: './peek-tabs.js', 125 exports: ['PeekTabs', 'PeekTab', 'PeekTabPanel'], 126 dependencies: ['./base.js'], 127 component: true 128 }, 129 'peek-details': { 130 path: './peek-details.js', 131 exports: ['PeekDetails'], 132 dependencies: ['./base.js'], 133 component: true 134 }, 135 136 // Phase 4 components 137 'peek-select': { 138 path: './peek-select.js', 139 exports: ['PeekSelect'], 140 dependencies: ['./base.js'], 141 component: true 142 }, 143 'peek-dropdown': { 144 path: './peek-dropdown.js', 145 exports: ['PeekDropdown', 'PeekDropdownItem', 'PeekDropdownDivider'], 146 dependencies: ['./base.js'], 147 component: true 148 }, 149 'peek-switch': { 150 path: './peek-switch.js', 151 exports: ['PeekSwitch'], 152 dependencies: ['./base.js'], 153 component: true 154 }, 155 'peek-drawer': { 156 path: './peek-drawer.js', 157 exports: ['PeekDrawer'], 158 dependencies: ['./base.js'], 159 component: true 160 }, 161 'peek-tooltip': { 162 path: './peek-tooltip.js', 163 exports: ['PeekTooltip'], 164 dependencies: ['./base.js'], 165 component: true 166 }, 167 'peek-button-group': { 168 path: './peek-button-group.js', 169 exports: ['PeekButtonGroup', 'PeekButtonGroupItem'], 170 dependencies: ['./base.js'], 171 component: true 172 } 173}; 174 175/** 176 * Bundle presets 177 */ 178export const PRESETS = { 179 // Full bundle with everything 180 full: { 181 include: Object.keys(MODULES).filter(m => !MODULES[m].devOnly), 182 description: 'Complete component library' 183 }, 184 185 // Core only - utilities without components 186 core: { 187 include: ['base', 'signals', 'schema', 'data-binding', 'events', 'theme', 'extension', 'registry', 'version'], 188 description: 'Core utilities only, no components' 189 }, 190 191 // Minimal - just base and essential utilities 192 minimal: { 193 include: ['base', 'signals', 'events'], 194 description: 'Minimal utilities for custom components' 195 }, 196 197 // Basic components 198 basic: { 199 include: ['base', 'peek-button', 'peek-card', 'peek-list'], 200 description: 'Basic components only' 201 }, 202 203 // Form components 204 forms: { 205 include: ['base', 'peek-button', 'peek-input', 'peek-select', 'peek-switch', 'peek-button-group'], 206 description: 'Form-related components' 207 }, 208 209 // Layout components 210 layout: { 211 include: ['base', 'peek-card', 'peek-grid', 'peek-tabs', 'peek-dialog', 'peek-drawer', 'peek-details'], 212 description: 'Layout and container components' 213 }, 214 215 // Interactive components 216 interactive: { 217 include: ['base', 'peek-button', 'peek-list', 'peek-dropdown', 'peek-popover', 'peek-tooltip'], 218 description: 'Interactive and menu components' 219 } 220}; 221 222/** 223 * Get entry points for bundler 224 * @param {string} preset - Preset name or 'full' 225 * @returns {Object} Entry point configuration 226 */ 227export function getEntryPoints(preset = 'full') { 228 const modules = PRESETS[preset]?.include || Object.keys(MODULES); 229 230 const entries = {}; 231 modules.forEach(name => { 232 const mod = MODULES[name]; 233 if (mod) { 234 entries[name] = mod.path; 235 } 236 }); 237 238 return entries; 239} 240 241/** 242 * Get dependency graph 243 * @param {string[]} modules - Module names 244 * @returns {Map<string, string[]>} 245 */ 246export function getDependencyGraph(modules = null) { 247 const names = modules || Object.keys(MODULES); 248 const graph = new Map(); 249 250 names.forEach(name => { 251 const mod = MODULES[name]; 252 if (mod) { 253 graph.set(name, mod.dependencies.map(d => d.replace('./', '').replace('.js', ''))); 254 } 255 }); 256 257 return graph; 258} 259 260/** 261 * Generate import map for native ES modules 262 * @param {string} baseUrl - Base URL for components 263 * @param {string} preset - Preset name 264 * @returns {Object} Import map 265 */ 266export function generateImportMap(baseUrl = '/components/', preset = 'full') { 267 const modules = PRESETS[preset]?.include || Object.keys(MODULES); 268 269 const imports = { 270 'peek://app/components/': baseUrl 271 }; 272 273 modules.forEach(name => { 274 const mod = MODULES[name]; 275 if (mod) { 276 imports[`peek://app/components/${name}.js`] = `${baseUrl}${mod.path}`; 277 } 278 }); 279 280 // Add index 281 imports['peek://app/components/index.js'] = `${baseUrl}index.js`; 282 283 return { imports }; 284} 285 286/** 287 * Generate bundle configuration 288 * @param {Object} options - Configuration options 289 * @returns {Object} Bundle config 290 */ 291export function bundleConfig(options = {}) { 292 const { 293 preset = 'full', 294 format = 'esm', 295 minify = true, 296 sourcemap = true, 297 outdir = 'dist', 298 external = ['lit'] 299 } = options; 300 301 const entryPoints = getEntryPoints(preset); 302 303 return { 304 // Common bundler options 305 entryPoints: Object.values(entryPoints), 306 format, 307 minify, 308 sourcemap, 309 outdir, 310 external, 311 312 // Metadata 313 version: LIBRARY_VERSION, 314 preset, 315 modules: Object.keys(entryPoints), 316 317 // ESBuild specific 318 esbuild: { 319 entryPoints, 320 bundle: true, 321 format, 322 minify, 323 sourcemap, 324 outdir, 325 external, 326 target: ['es2022'], 327 platform: 'browser' 328 }, 329 330 // Rollup specific 331 rollup: { 332 input: entryPoints, 333 output: { 334 dir: outdir, 335 format, 336 sourcemap, 337 preserveModules: true 338 }, 339 external 340 }, 341 342 // Vite specific 343 vite: { 344 build: { 345 lib: { 346 entry: entryPoints, 347 formats: [format === 'esm' ? 'es' : format] 348 }, 349 outDir: outdir, 350 sourcemap, 351 minify: minify ? 'esbuild' : false, 352 rollupOptions: { 353 external 354 } 355 } 356 } 357 }; 358} 359 360/** 361 * Get all component tag names 362 * @returns {string[]} 363 */ 364export function getComponentNames() { 365 return Object.entries(MODULES) 366 .filter(([_, mod]) => mod.component) 367 .map(([name]) => name); 368} 369 370/** 371 * Get all utility module names 372 * @returns {string[]} 373 */ 374export function getUtilityNames() { 375 return Object.entries(MODULES) 376 .filter(([_, mod]) => !mod.component) 377 .map(([name]) => name); 378} 379 380export default { 381 MODULES, 382 PRESETS, 383 getEntryPoints, 384 getDependencyGraph, 385 generateImportMap, 386 bundleConfig, 387 getComponentNames, 388 getUtilityNames, 389 version: LIBRARY_VERSION 390};