experiments in a post-browser web
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};