@useavalon/avalon 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (159) hide show
  1. package/README.md +54 -0
  2. package/mod.ts +301 -0
  3. package/package.json +85 -0
  4. package/src/build/README.md +310 -0
  5. package/src/build/integration-bundler-plugin.ts +116 -0
  6. package/src/build/integration-config.ts +168 -0
  7. package/src/build/integration-detection-plugin.ts +117 -0
  8. package/src/build/integration-resolver-plugin.ts +90 -0
  9. package/src/build/island-manifest.ts +269 -0
  10. package/src/build/island-types-generator.ts +476 -0
  11. package/src/build/mdx-island-transform.ts +464 -0
  12. package/src/build/mdx-plugin.ts +98 -0
  13. package/src/build/page-island-transform.ts +598 -0
  14. package/src/build/prop-extractors/index.ts +21 -0
  15. package/src/build/prop-extractors/lit.ts +140 -0
  16. package/src/build/prop-extractors/qwik.ts +16 -0
  17. package/src/build/prop-extractors/solid.ts +125 -0
  18. package/src/build/prop-extractors/svelte.ts +194 -0
  19. package/src/build/prop-extractors/vue.ts +111 -0
  20. package/src/build/sidecar-file-manager.ts +104 -0
  21. package/src/build/sidecar-renderer.ts +30 -0
  22. package/src/client/adapters/index.ts +13 -0
  23. package/src/client/adapters/lit-adapter.ts +654 -0
  24. package/src/client/adapters/preact-adapter.ts +331 -0
  25. package/src/client/adapters/qwik-adapter.ts +345 -0
  26. package/src/client/adapters/react-adapter.ts +353 -0
  27. package/src/client/adapters/solid-adapter.ts +451 -0
  28. package/src/client/adapters/svelte-adapter.ts +524 -0
  29. package/src/client/adapters/vue-adapter.ts +467 -0
  30. package/src/client/components.ts +35 -0
  31. package/src/client/css-hmr-handler.ts +344 -0
  32. package/src/client/framework-adapter.ts +462 -0
  33. package/src/client/hmr-coordinator.ts +396 -0
  34. package/src/client/hmr-error-overlay.js +533 -0
  35. package/src/client/main.js +816 -0
  36. package/src/client/tests/css-hmr-handler.test.ts +360 -0
  37. package/src/client/tests/framework-adapter.test.ts +519 -0
  38. package/src/client/tests/hmr-coordinator.test.ts +176 -0
  39. package/src/client/tests/hydration-option-parsing.test.ts +107 -0
  40. package/src/client/tests/lit-adapter.test.ts +427 -0
  41. package/src/client/tests/preact-adapter.test.ts +353 -0
  42. package/src/client/tests/qwik-adapter.test.ts +343 -0
  43. package/src/client/tests/react-adapter.test.ts +317 -0
  44. package/src/client/tests/solid-adapter.test.ts +396 -0
  45. package/src/client/tests/svelte-adapter.test.ts +387 -0
  46. package/src/client/tests/vue-adapter.test.ts +407 -0
  47. package/src/client/types/framework-runtime.d.ts +68 -0
  48. package/src/client/types/vite-hmr.d.ts +46 -0
  49. package/src/client/types/vite-virtual-modules.d.ts +60 -0
  50. package/src/components/Image.tsx +123 -0
  51. package/src/components/IslandErrorBoundary.tsx +145 -0
  52. package/src/components/LayoutDataErrorBoundary.tsx +141 -0
  53. package/src/components/LayoutErrorBoundary.tsx +127 -0
  54. package/src/components/PersistentIsland.tsx +52 -0
  55. package/src/components/StreamingErrorBoundary.tsx +233 -0
  56. package/src/components/StreamingLayout.tsx +538 -0
  57. package/src/components/tests/component-analyzer.test.ts +96 -0
  58. package/src/components/tests/component-detection.test.ts +347 -0
  59. package/src/components/tests/persistent-islands.test.ts +398 -0
  60. package/src/core/components/component-analyzer.ts +192 -0
  61. package/src/core/components/component-detection.ts +508 -0
  62. package/src/core/components/enhanced-framework-detector.ts +500 -0
  63. package/src/core/components/framework-registry.ts +563 -0
  64. package/src/core/components/tests/enhanced-framework-detector.test.ts +577 -0
  65. package/src/core/components/tests/framework-registry.test.ts +465 -0
  66. package/src/core/content/mdx-processor.ts +46 -0
  67. package/src/core/integrations/README.md +282 -0
  68. package/src/core/integrations/index.ts +19 -0
  69. package/src/core/integrations/loader.ts +125 -0
  70. package/src/core/integrations/registry.ts +195 -0
  71. package/src/core/islands/island-persistence.ts +325 -0
  72. package/src/core/islands/island-state-serializer.ts +258 -0
  73. package/src/core/islands/persistent-island-context.tsx +80 -0
  74. package/src/core/islands/use-persistent-state.ts +68 -0
  75. package/src/core/layout/enhanced-layout-resolver.ts +322 -0
  76. package/src/core/layout/layout-cache-manager.ts +485 -0
  77. package/src/core/layout/layout-composer.ts +357 -0
  78. package/src/core/layout/layout-data-loader.ts +516 -0
  79. package/src/core/layout/layout-discovery.ts +243 -0
  80. package/src/core/layout/layout-matcher.ts +299 -0
  81. package/src/core/layout/layout-types.ts +110 -0
  82. package/src/core/layout/tests/enhanced-layout-resolver.test.ts +477 -0
  83. package/src/core/layout/tests/layout-cache-optimization.test.ts +149 -0
  84. package/src/core/layout/tests/layout-composer.test.ts +486 -0
  85. package/src/core/layout/tests/layout-data-loader.test.ts +443 -0
  86. package/src/core/layout/tests/layout-discovery.test.ts +253 -0
  87. package/src/core/layout/tests/layout-matcher.test.ts +480 -0
  88. package/src/core/modules/framework-module-resolver.ts +273 -0
  89. package/src/core/modules/tests/framework-module-resolver.test.ts +263 -0
  90. package/src/core/modules/tests/module-resolution-integration.test.ts +117 -0
  91. package/src/islands/component-analysis.ts +213 -0
  92. package/src/islands/css-utils.ts +565 -0
  93. package/src/islands/discovery/index.ts +80 -0
  94. package/src/islands/discovery/registry.ts +340 -0
  95. package/src/islands/discovery/resolver.ts +477 -0
  96. package/src/islands/discovery/scanner.ts +386 -0
  97. package/src/islands/discovery/tests/island-discovery.test.ts +881 -0
  98. package/src/islands/discovery/types.ts +117 -0
  99. package/src/islands/discovery/validator.ts +544 -0
  100. package/src/islands/discovery/watcher.ts +368 -0
  101. package/src/islands/framework-detection.ts +428 -0
  102. package/src/islands/integration-loader.ts +490 -0
  103. package/src/islands/island.tsx +565 -0
  104. package/src/islands/render-cache.ts +550 -0
  105. package/src/islands/types.ts +80 -0
  106. package/src/islands/universal-css-collector.ts +157 -0
  107. package/src/islands/universal-head-collector.ts +137 -0
  108. package/src/layout-system.d.ts +592 -0
  109. package/src/layout-system.ts +218 -0
  110. package/src/middleware/__tests__/discovery.test.ts +107 -0
  111. package/src/middleware/discovery.ts +268 -0
  112. package/src/middleware/executor.ts +315 -0
  113. package/src/middleware/index.ts +76 -0
  114. package/src/middleware/types.ts +99 -0
  115. package/src/nitro/build-config.ts +576 -0
  116. package/src/nitro/config.ts +483 -0
  117. package/src/nitro/error-handler.ts +636 -0
  118. package/src/nitro/index.ts +173 -0
  119. package/src/nitro/island-manifest.ts +584 -0
  120. package/src/nitro/middleware-adapter.ts +260 -0
  121. package/src/nitro/renderer.ts +1458 -0
  122. package/src/nitro/route-discovery.ts +439 -0
  123. package/src/nitro/types.ts +321 -0
  124. package/src/render/collect-css.ts +198 -0
  125. package/src/render/error-pages.ts +79 -0
  126. package/src/render/isolated-ssr-renderer.ts +654 -0
  127. package/src/render/ssr.ts +1030 -0
  128. package/src/schemas/api.ts +30 -0
  129. package/src/schemas/core.ts +64 -0
  130. package/src/schemas/index.ts +212 -0
  131. package/src/schemas/layout.ts +279 -0
  132. package/src/schemas/routing/index.ts +38 -0
  133. package/src/schemas/routing.ts +376 -0
  134. package/src/types/as-island.ts +20 -0
  135. package/src/types/image.d.ts +106 -0
  136. package/src/types/index.d.ts +22 -0
  137. package/src/types/island-jsx.d.ts +33 -0
  138. package/src/types/island-prop.d.ts +20 -0
  139. package/src/types/layout.ts +285 -0
  140. package/src/types/mdx.d.ts +6 -0
  141. package/src/types/routing.ts +555 -0
  142. package/src/types/tests/layout-types.test.ts +197 -0
  143. package/src/types/types.ts +5 -0
  144. package/src/types/urlpattern.d.ts +49 -0
  145. package/src/types/vite-env.d.ts +11 -0
  146. package/src/utils/dev-logger.ts +299 -0
  147. package/src/utils/fs.ts +151 -0
  148. package/src/vite-plugin/auto-discover.ts +551 -0
  149. package/src/vite-plugin/config.ts +266 -0
  150. package/src/vite-plugin/errors.ts +127 -0
  151. package/src/vite-plugin/image-optimization.ts +151 -0
  152. package/src/vite-plugin/integration-activator.ts +126 -0
  153. package/src/vite-plugin/island-sidecar-plugin.ts +176 -0
  154. package/src/vite-plugin/module-discovery.ts +189 -0
  155. package/src/vite-plugin/nitro-integration.ts +1334 -0
  156. package/src/vite-plugin/plugin.ts +329 -0
  157. package/src/vite-plugin/tests/image-optimization.test.ts +54 -0
  158. package/src/vite-plugin/types.ts +327 -0
  159. package/src/vite-plugin/validation.ts +228 -0
@@ -0,0 +1,565 @@
1
+ /**
2
+ * CSS Utilities Module
3
+ *
4
+ * This module contains all CSS-related utilities for the Island system:
5
+ * - Svelte SSR CSS collection and management
6
+ * - CSS scoping for component isolation
7
+ * - CSS optimization and minification
8
+ * - CSS parsing and deduplication
9
+ */
10
+
11
+ // Enhanced global CSS collector for SSR with scoping support
12
+ declare global {
13
+ var __svelteSSRCSS: Map<string, SvelteSSRCSSEntry> | undefined;
14
+ }
15
+
16
+ interface SvelteSSRCSSEntry {
17
+ css: string;
18
+ scopeId: string;
19
+ src: string;
20
+ isGlobal: boolean;
21
+ timestamp: number;
22
+ }
23
+
24
+ // Initialize enhanced global CSS collector
25
+ if (typeof globalThis !== "undefined" && !globalThis.__svelteSSRCSS) {
26
+ globalThis.__svelteSSRCSS = new Map();
27
+ }
28
+
29
+ /**
30
+ * Add CSS to the global Svelte SSR collection with scoping information
31
+ */
32
+ export function addSvelteSSRCSS(
33
+ css: string,
34
+ scopeId: string,
35
+ src: string,
36
+ isGlobal = false,
37
+ ): void {
38
+ if (!globalThis.__svelteSSRCSS) {
39
+ globalThis.__svelteSSRCSS = new Map();
40
+ }
41
+
42
+ const entry: SvelteSSRCSSEntry = {
43
+ css: css.trim(),
44
+ scopeId,
45
+ src,
46
+ isGlobal,
47
+ timestamp: Date.now(),
48
+ };
49
+
50
+ // Use scopeId as key to prevent duplicates from the same component
51
+ globalThis.__svelteSSRCSS.set(scopeId, entry);
52
+ }
53
+
54
+ /**
55
+ * Get collected Svelte SSR CSS with enhanced processing and deduplication
56
+ * Enhanced version with better CSS management and document head injection support
57
+ */
58
+ export function getSvelteSSRCSS(clear = false) {
59
+ if (!globalThis.__svelteSSRCSS || globalThis.__svelteSSRCSS.size === 0) {
60
+ return "";
61
+ }
62
+
63
+ const entries = Array.from(globalThis.__svelteSSRCSS.values());
64
+
65
+ // Sort entries: global styles first, then component styles by timestamp
66
+ entries.sort((a, b) => {
67
+ if (a.isGlobal && !b.isGlobal) return -1;
68
+ if (!a.isGlobal && b.isGlobal) return 1;
69
+ return a.timestamp - b.timestamp;
70
+ });
71
+
72
+ // Separate global and component-scoped CSS for better organization
73
+ const globalEntries = entries.filter((entry) => entry.isGlobal);
74
+ const componentEntries = entries.filter((entry) => !entry.isGlobal);
75
+
76
+ // Process global CSS
77
+ const globalCSS = globalEntries.map((entry) => {
78
+ const comment = `/* Global CSS from: ${entry.src} */`;
79
+ return `${comment}\n${entry.css}`;
80
+ }).join("\n\n");
81
+
82
+ // Process component-scoped CSS
83
+ const componentCSS = componentEntries.map((entry) => {
84
+ const comment = `/* Component CSS: ${entry.src} (${entry.scopeId}) */`;
85
+ return `${comment}\n${entry.css}`;
86
+ }).join("\n\n");
87
+
88
+ // Combine in proper order: global first, then component-specific
89
+ const combinedCSS = [globalCSS, componentCSS].filter((css) => css.trim())
90
+ .join("\n\n");
91
+
92
+ // Apply enhanced CSS optimization and deduplication
93
+ const optimizedCSS = optimizeSvelteSSRCSS(combinedCSS);
94
+
95
+ if (clear) {
96
+ globalThis.__svelteSSRCSS.clear();
97
+ }
98
+
99
+ return optimizedCSS;
100
+ }
101
+
102
+ /**
103
+ * Get CSS formatted for document head injection during SSR
104
+ * Returns CSS wrapped in appropriate style tags with metadata
105
+ */
106
+ export function getSvelteSSRCSSForHead(clear = false) {
107
+ const css = getSvelteSSRCSS(clear);
108
+
109
+ if (!css.trim()) {
110
+ return "";
111
+ }
112
+
113
+ // Wrap in style tag with metadata for identification and debugging
114
+ const timestamp = new Date().toISOString();
115
+ const styleTag =
116
+ `<style data-svelte-ssr="true" data-generated="${timestamp}">\n${css}\n</style>`;
117
+
118
+ return styleTag;
119
+ }
120
+
121
+ /**
122
+ * Get CSS statistics for debugging and monitoring
123
+ */
124
+ export function getSvelteSSRCSSStats() {
125
+ if (!globalThis.__svelteSSRCSS || globalThis.__svelteSSRCSS.size === 0) {
126
+ return {
127
+ totalComponents: 0,
128
+ globalComponents: 0,
129
+ scopedComponents: 0,
130
+ totalCSSSize: 0,
131
+ averageCSSSize: 0,
132
+ oldestTimestamp: 0,
133
+ newestTimestamp: 0,
134
+ };
135
+ }
136
+
137
+ const entries = Array.from(globalThis.__svelteSSRCSS.values());
138
+ const globalEntries = entries.filter((entry) => entry.isGlobal);
139
+ const scopedEntries = entries.filter((entry) => !entry.isGlobal);
140
+ const totalCSSSize = entries.reduce(
141
+ (sum, entry) => sum + entry.css.length,
142
+ 0,
143
+ );
144
+ const timestamps = entries.map((entry) => entry.timestamp);
145
+
146
+ return {
147
+ totalComponents: entries.length,
148
+ globalComponents: globalEntries.length,
149
+ scopedComponents: scopedEntries.length,
150
+ totalCSSSize,
151
+ averageCSSSize: entries.length > 0
152
+ ? Math.round(totalCSSSize / entries.length)
153
+ : 0,
154
+ oldestTimestamp: Math.min(...timestamps),
155
+ newestTimestamp: Math.max(...timestamps),
156
+ };
157
+ }
158
+
159
+ /**
160
+ * Get CSS for specific component scope
161
+ */
162
+ export function getSvelteComponentCSS(scopeId: string) {
163
+ if (!globalThis.__svelteSSRCSS) {
164
+ return null;
165
+ }
166
+
167
+ const entry = globalThis.__svelteSSRCSS.get(scopeId);
168
+ return entry ? entry.css : null;
169
+ }
170
+
171
+ /**
172
+ * Clear CSS for specific component scope
173
+ */
174
+ export function clearSvelteComponentCSS(scopeId: string) {
175
+ if (!globalThis.__svelteSSRCSS) {
176
+ return false;
177
+ }
178
+
179
+ const deleted = globalThis.__svelteSSRCSS.delete(scopeId);
180
+ if (deleted) {
181
+ // CSS cleared for component scope
182
+ }
183
+ return deleted;
184
+ }
185
+
186
+ /**
187
+ * Optimize collected Svelte SSR CSS by removing duplicates and minifying
188
+ * Enhanced version with better deduplication and cross-component optimization
189
+ */
190
+ function optimizeSvelteSSRCSS(css: string) {
191
+ try {
192
+ // Use the enhanced CSS optimization functions
193
+ const optimizedCSS = optimizeComponentCSS(css);
194
+
195
+ // Additional optimizations specific to SSR CSS collection
196
+ const finalCSS = optimizeSSRCSSCollection(optimizedCSS);
197
+
198
+ return finalCSS;
199
+ } catch (error) {
200
+ console.warn(`⚠️ Failed to optimize Svelte SSR CSS:`, error);
201
+ return css;
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Apply SSR-specific CSS optimizations across multiple components
207
+ */
208
+ function optimizeSSRCSSCollection(css: string) {
209
+ try {
210
+ // Remove duplicate comments
211
+ const withoutDuplicateComments = removeDuplicateComments(css);
212
+
213
+ // Optimize cross-component CSS patterns
214
+ const crossOptimized = optimizeCrossComponentCSS(withoutDuplicateComments);
215
+
216
+ // Apply final formatting based on environment
217
+ const isDev = process.env.NODE_ENV !== "production";
218
+ if (!isDev) {
219
+ return minifyCSS(crossOptimized);
220
+ }
221
+
222
+ // Keep readable format in development with proper indentation
223
+ return formatDevelopmentCSS(crossOptimized);
224
+ } catch (error) {
225
+ console.warn(`⚠️ Failed to apply SSR CSS collection optimizations:`, error);
226
+ return css;
227
+ }
228
+ }
229
+
230
+ /**
231
+ * Remove duplicate CSS comments while preserving important ones
232
+ */
233
+ function removeDuplicateComments(css: string) {
234
+ const seenComments = new Set<string>();
235
+
236
+ return css.replace(/\/\*[^*]*\*+(?:[^/*][^*]*\*+)*\//g, (comment) => {
237
+ // Keep important comments (those with specific markers)
238
+ if (
239
+ comment.includes("!important") || comment.includes("@preserve") ||
240
+ comment.includes("license")
241
+ ) {
242
+ return comment;
243
+ }
244
+
245
+ // Remove duplicate comments
246
+ if (seenComments.has(comment)) {
247
+ return "";
248
+ }
249
+
250
+ seenComments.add(comment);
251
+ return comment;
252
+ });
253
+ }
254
+
255
+ /**
256
+ * Optimize CSS patterns that appear across multiple components
257
+ */
258
+ function optimizeCrossComponentCSS(css: string) {
259
+ // This could be extended to merge similar rules across components
260
+ // For now, just clean up whitespace and formatting
261
+ return css
262
+ .replace(/\n\s*\n\s*\n/g, "\n\n") // Collapse multiple empty lines
263
+ .replace(/^\s*\n/gm, "") // Remove empty lines at start of sections
264
+ .trim();
265
+ }
266
+
267
+ /**
268
+ * Format CSS for development with proper indentation and spacing
269
+ */
270
+ function formatDevelopmentCSS(css: string) {
271
+ return css
272
+ .replace(/\{/g, " {\n ")
273
+ .replace(/;/g, ";\n ")
274
+ .replace(/\}/g, "\n}\n")
275
+ .replace(/\n {2}\n/g, "\n")
276
+ .replace(/\n\n+/g, "\n\n")
277
+ .trim();
278
+ }
279
+
280
+ /**
281
+ * Apply CSS scoping to selectors with advanced logic
282
+ * Enhanced version with better selector parsing and scoping rules
283
+ */
284
+ export function applyCSSScoping(cssContent: string, scopeId: string) {
285
+ return cssContent.replace(/([^{}]+){/g, (match, selector) => {
286
+ const trimmedSelector = selector.trim();
287
+
288
+ // Skip at-rules (@media, @keyframes, @import, etc.)
289
+ if (trimmedSelector.startsWith("@")) {
290
+ return match;
291
+ }
292
+
293
+ // Skip selectors that already have scoping
294
+ if (trimmedSelector.includes(`[data-${scopeId}]`)) {
295
+ return match;
296
+ }
297
+
298
+ // Skip :global() selectors (remove the wrapper)
299
+ if (trimmedSelector.includes(":global(")) {
300
+ const globalSelector = trimmedSelector.replace(
301
+ /:global\(([^)]+)\)/g,
302
+ "$1",
303
+ );
304
+ return `${globalSelector} {`;
305
+ }
306
+
307
+ // Apply scoping to each selector in a comma-separated list
308
+ const scopedSelectors = trimmedSelector
309
+ .split(",")
310
+ .map((sel: string) => applySelectorScoping(sel.trim(), scopeId))
311
+ .join(", ");
312
+
313
+ return `${scopedSelectors} {`;
314
+ });
315
+ }
316
+
317
+ /**
318
+ * Apply scoping to a single CSS selector with comprehensive logic
319
+ */
320
+ export function applySelectorScoping(selector: string, scopeId: string) {
321
+ // Handle empty or invalid selectors
322
+ if (!selector || selector.length === 0) {
323
+ return selector;
324
+ }
325
+
326
+ // Skip already scoped selectors
327
+ if (selector.includes(`[data-${scopeId}]`)) {
328
+ return selector;
329
+ }
330
+
331
+ // Handle complex selectors with combinators (>, +, ~, space)
332
+ const combinatorRegex = /(\s*[>+~]\s*|\s+)/;
333
+ const parts = selector.split(combinatorRegex);
334
+
335
+ if (parts.length > 1) {
336
+ // Complex selector with combinators - scope the first part only
337
+ const firstPart = parts[0].trim();
338
+ const rest = parts.slice(1).join("");
339
+ return applySingleSelectorScoping(firstPart, scopeId) + rest;
340
+ }
341
+
342
+ // Simple selector
343
+ return applySingleSelectorScoping(selector, scopeId);
344
+ }
345
+
346
+ /**
347
+ * Apply scoping to a single, simple CSS selector
348
+ */
349
+ function applySingleSelectorScoping(selector: string, scopeId: string) {
350
+ // Handle pseudo-elements (::before, ::after)
351
+ const pseudoElementMatch = selector.match(/^([^:]+)(::.*)?$/);
352
+ if (pseudoElementMatch) {
353
+ const [, baseSelector, pseudoElement] = pseudoElementMatch;
354
+ return `${baseSelector}[data-${scopeId}]${pseudoElement || ""}`;
355
+ }
356
+
357
+ // Handle pseudo-classes (:hover, :focus, :nth-child, etc.)
358
+ const pseudoClassMatch = selector.match(/^([^:]+)(:.*)?$/);
359
+ if (pseudoClassMatch) {
360
+ const [, baseSelector, pseudoClass] = pseudoClassMatch;
361
+ return `${baseSelector}[data-${scopeId}]${pseudoClass || ""}`;
362
+ }
363
+
364
+ // Simple selector without pseudo-classes/elements
365
+ return `${selector}[data-${scopeId}]`;
366
+ }
367
+
368
+ /**
369
+ * Optimize component CSS by removing duplicates and applying minification
370
+ * Enhanced version with better deduplication and optimization strategies
371
+ */
372
+ export function optimizeComponentCSS(css: string) {
373
+ try {
374
+ // Parse CSS into rules with better handling
375
+ const rules = parseCSRules(css);
376
+ const optimizedRules = deduplicateCSRules(rules);
377
+
378
+ // Reconstruct CSS
379
+ const optimizedCSS = optimizedRules.join("\n");
380
+
381
+ // Apply environment-specific optimizations
382
+ const isDev = process.env.NODE_ENV !== "production";
383
+ if (!isDev) {
384
+ return minifyCSS(optimizedCSS);
385
+ }
386
+
387
+ return optimizedCSS;
388
+ } catch (error) {
389
+ console.warn(`⚠️ Failed to optimize component CSS:`, error);
390
+ return css;
391
+ }
392
+ }
393
+
394
+ /**
395
+ * Parse CSS into individual rules with proper handling of nested structures
396
+ */
397
+ function parseCSRules(css: string) {
398
+ const rules: string[] = [];
399
+ let currentRule = "";
400
+ let braceDepth = 0;
401
+ let inString = false;
402
+ let stringChar = "";
403
+
404
+ for (let i = 0; i < css.length; i++) {
405
+ const char = css[i];
406
+ const prevChar = i > 0 ? css[i - 1] : "";
407
+
408
+ // Handle string literals
409
+ if ((char === '"' || char === "'") && prevChar !== "\\") {
410
+ if (!inString) {
411
+ inString = true;
412
+ stringChar = char;
413
+ } else if (char === stringChar) {
414
+ inString = false;
415
+ stringChar = "";
416
+ }
417
+ }
418
+
419
+ if (!inString) {
420
+ if (char === "{") {
421
+ braceDepth++;
422
+ } else if (char === "}") {
423
+ braceDepth--;
424
+
425
+ if (braceDepth === 0) {
426
+ // End of a complete rule
427
+ currentRule += char;
428
+ const trimmedRule = currentRule.trim();
429
+ if (trimmedRule && !isEmptyRule(trimmedRule)) {
430
+ rules.push(trimmedRule);
431
+ }
432
+ currentRule = "";
433
+ continue;
434
+ }
435
+ }
436
+ }
437
+
438
+ currentRule += char;
439
+ }
440
+
441
+ // Handle any remaining content
442
+ if (currentRule.trim()) {
443
+ rules.push(currentRule.trim());
444
+ }
445
+
446
+ return rules;
447
+ }
448
+
449
+ /**
450
+ * Deduplicate CSS rules while preserving order and handling specificity
451
+ */
452
+ function deduplicateCSRules(rules: string[]) {
453
+ const seenRules = new Map<string, { rule: string; index: number }>();
454
+ const result: string[] = [];
455
+
456
+ for (let i = 0; i < rules.length; i++) {
457
+ const rule = rules[i];
458
+ const ruleKey = extractRuleKey(rule);
459
+
460
+ if (ruleKey) {
461
+ const existing = seenRules.get(ruleKey);
462
+ if (existing) {
463
+ // Replace earlier rule with later one (cascade order)
464
+ result[existing.index] = rule;
465
+ seenRules.set(ruleKey, { rule, index: existing.index });
466
+ } else {
467
+ // New rule
468
+ const index = result.length;
469
+ result.push(rule);
470
+ seenRules.set(ruleKey, { rule, index });
471
+ }
472
+ } else {
473
+ // Non-standard rule (at-rules, etc.) - keep as-is
474
+ result.push(rule);
475
+ }
476
+ }
477
+
478
+ return result.filter((rule) => rule !== null);
479
+ }
480
+
481
+ /**
482
+ * Extract a key for rule deduplication (selector + property combination)
483
+ */
484
+ function extractRuleKey(rule: string) {
485
+ const match = rule.match(/^([^{]+)\{([^}]+)\}/);
486
+ if (!match) return null;
487
+
488
+ const selector = match[1].trim();
489
+ const declarations = match[2].trim();
490
+
491
+ // For deduplication, we consider rules with the same selector as duplicates
492
+ // Later rules will override earlier ones (CSS cascade)
493
+ return selector;
494
+ }
495
+
496
+ /**
497
+ * Check if a CSS rule is effectively empty
498
+ */
499
+ function isEmptyRule(rule: string) {
500
+ const match = rule.match(/^[^{]+\{([^}]*)\}/);
501
+ if (!match) return true;
502
+
503
+ const declarations = match[1].trim();
504
+ return declarations.length === 0 || declarations === ";";
505
+ }
506
+
507
+ /**
508
+ * Minify CSS for production builds
509
+ */
510
+ export function minifyCSS(css: string) {
511
+ return css
512
+ // Remove comments
513
+ .replace(/\/\*[\s\S]*?\*\//g, "")
514
+ // Remove unnecessary whitespace
515
+ .replace(/\s+/g, " ")
516
+ // Remove whitespace around braces and semicolons
517
+ .replace(/\s*{\s*/g, "{")
518
+ .replace(/\s*}\s*/g, "}")
519
+ .replace(/\s*;\s*/g, ";")
520
+ .replace(/;\s*}/g, "}")
521
+ // Remove trailing semicolons before closing braces
522
+ .replace(/;}/g, "}")
523
+ // Trim
524
+ .trim();
525
+ }
526
+
527
+ /**
528
+ * Generate consistent scope ID for any component framework
529
+ * Provides unified scoping across Vue, Svelte, and other frameworks
530
+ */
531
+ export function generateComponentScopeId(
532
+ src: string,
533
+ framework: string = "component",
534
+ ) {
535
+ const cleanPath = src
536
+ .replace(/^\/+/, "") // Remove leading slashes
537
+ .replace(/\.(svelte|tsx|jsx|vue|ts|js)$/, "") // Remove file extensions
538
+ .replace(/[^a-zA-Z0-9\/]/g, "-") // Replace special chars with hyphens
539
+ .replace(/\/+/g, "-") // Replace path separators with hyphens
540
+ .replace(/-+/g, "-") // Collapse multiple hyphens
541
+ .replace(/^-|-$/g, "") // Remove leading/trailing hyphens
542
+ .toLowerCase();
543
+
544
+ // Add framework prefix and hash for collision resistance
545
+ const hash = simpleHash(src);
546
+ return `${framework}-${cleanPath}-${hash}`;
547
+ }
548
+
549
+ /**
550
+ * Simple hash function for generating consistent short hashes
551
+ */
552
+ export function simpleHash(str: string) {
553
+ let hash = 0;
554
+ for (let i = 0; i < str.length; i++) {
555
+ const char = str.charCodeAt(i);
556
+ hash = ((hash << 5) - hash) + char;
557
+ hash = hash & hash; // Convert to 32-bit integer
558
+ }
559
+ return Math.abs(hash).toString(36).substring(0, 6);
560
+ }
561
+
562
+ // Note: processSvelteSSRCSS, extractRawCSSFromHead, and applySvelteComponentScoping
563
+ // were removed as they are not used anywhere in the codebase.
564
+ // They were originally intended for Svelte SSR CSS processing but are superseded
565
+ // by the Svelte 5 render() function which handles CSS differently.
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Island Discovery Module
3
+ *
4
+ * Exports all types and functions for discovering island components
5
+ * in nested directory structures.
6
+ */
7
+
8
+ // Types
9
+ export type {
10
+ IslandDirectory,
11
+ DiscoveredIsland,
12
+ IslandCollision,
13
+ IslandChangeEvent,
14
+ IslandFileExtension,
15
+ IslandDiscoveryConfig,
16
+ } from "./types.ts";
17
+
18
+ export {
19
+ ISLAND_FILE_EXTENSIONS,
20
+ DEFAULT_DISCOVERY_CONFIG,
21
+ isSupportedIslandExtension,
22
+ } from "./types.ts";
23
+
24
+ // Scanner functions
25
+ export {
26
+ discoverIslandDirectories,
27
+ discoverIslandsInDirectory,
28
+ discoverAllIslands,
29
+ isIslandsDirectory,
30
+ getDefaultIslandsPath,
31
+ hasDefaultIslandsDirectory,
32
+ getQualifiedIslandName,
33
+ parseQualifiedIslandName,
34
+ } from "./scanner.ts";
35
+
36
+ // Registry
37
+ export {
38
+ IslandRegistry,
39
+ createIslandRegistry,
40
+ } from "./registry.ts";
41
+
42
+ // Resolver
43
+ export type {
44
+ ResolutionResult,
45
+ ImportPathOptions,
46
+ } from "./resolver.ts";
47
+
48
+ export {
49
+ IslandResolver,
50
+ createIslandResolver,
51
+ } from "./resolver.ts";
52
+
53
+ // Validator
54
+ export type {
55
+ ValidationResult,
56
+ ValidationError,
57
+ ValidationWarning,
58
+ CircularDependency,
59
+ } from "./validator.ts";
60
+
61
+ export {
62
+ IslandValidator,
63
+ createIslandValidator,
64
+ validateAllIslands,
65
+ formatValidationError,
66
+ formatValidationWarning,
67
+ formatCircularDependency,
68
+ formatValidationResult,
69
+ } from "./validator.ts";
70
+
71
+ // Watcher
72
+ export type {
73
+ IslandChangeCallback,
74
+ IslandWatcherOptions,
75
+ } from "./watcher.ts";
76
+
77
+ export {
78
+ IslandWatcher,
79
+ createIslandWatcher,
80
+ } from "./watcher.ts";