@useavalon/avalon 0.1.10 → 0.1.12
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.
- package/README.md +54 -54
- package/dist/mod.js +1 -0
- package/dist/src/build/integration-bundler-plugin.js +1 -0
- package/dist/src/build/integration-config.js +1 -0
- package/dist/src/build/integration-detection-plugin.js +1 -0
- package/dist/src/build/integration-resolver-plugin.js +1 -0
- package/dist/src/build/island-manifest.js +1 -0
- package/dist/src/build/island-types-generator.js +5 -0
- package/dist/src/build/mdx-island-transform.js +2 -0
- package/dist/src/build/mdx-plugin.js +1 -0
- package/dist/src/build/page-island-transform.js +3 -0
- package/dist/src/build/prop-extractors/index.js +1 -0
- package/dist/src/build/prop-extractors/lit.js +1 -0
- package/dist/src/build/prop-extractors/qwik.js +1 -0
- package/dist/src/build/prop-extractors/solid.js +1 -0
- package/dist/src/build/prop-extractors/svelte.js +1 -0
- package/dist/src/build/prop-extractors/vue.js +1 -0
- package/dist/src/build/sidecar-file-manager.js +1 -0
- package/dist/src/build/sidecar-renderer.js +6 -0
- package/dist/src/client/adapters/index.js +1 -0
- package/dist/src/client/components.js +1 -0
- package/dist/src/client/css-hmr-handler.js +1 -0
- package/dist/src/client/framework-adapter.js +13 -0
- package/dist/src/client/hmr-coordinator.js +1 -0
- package/dist/src/client/hmr-error-overlay.js +214 -0
- package/dist/src/client/main.js +39 -0
- package/{src → dist/src}/client/types/framework-runtime.d.ts +68 -68
- package/{src → dist/src}/client/types/vite-hmr.d.ts +46 -46
- package/dist/src/client/types/vite-virtual-modules.d.ts +70 -0
- package/dist/src/components/Image.js +1 -0
- package/dist/src/components/IslandErrorBoundary.js +1 -0
- package/dist/src/components/LayoutDataErrorBoundary.js +1 -0
- package/dist/src/components/LayoutErrorBoundary.js +1 -0
- package/dist/src/components/PersistentIsland.js +1 -0
- package/dist/src/components/StreamingErrorBoundary.js +1 -0
- package/dist/src/components/StreamingLayout.js +29 -0
- package/dist/src/core/components/component-analyzer.js +1 -0
- package/dist/src/core/components/component-detection.js +5 -0
- package/dist/src/core/components/enhanced-framework-detector.js +1 -0
- package/dist/src/core/components/framework-registry.js +1 -0
- package/dist/src/core/content/mdx-processor.js +1 -0
- package/dist/src/core/integrations/index.js +1 -0
- package/dist/src/core/integrations/loader.js +1 -0
- package/dist/src/core/integrations/registry.js +1 -0
- package/dist/src/core/islands/island-persistence.js +1 -0
- package/dist/src/core/islands/island-state-serializer.js +1 -0
- package/dist/src/core/islands/persistent-island-context.js +1 -0
- package/dist/src/core/islands/use-persistent-state.js +1 -0
- package/dist/src/core/layout/enhanced-layout-resolver.js +1 -0
- package/dist/src/core/layout/layout-cache-manager.js +1 -0
- package/dist/src/core/layout/layout-composer.js +1 -0
- package/dist/src/core/layout/layout-data-loader.js +1 -0
- package/dist/src/core/layout/layout-discovery.js +1 -0
- package/dist/src/core/layout/layout-matcher.js +1 -0
- package/dist/src/core/layout/layout-types.js +1 -0
- package/dist/src/core/modules/framework-module-resolver.js +1 -0
- package/dist/src/islands/component-analysis.js +1 -0
- package/dist/src/islands/css-utils.js +17 -0
- package/dist/src/islands/discovery/index.js +1 -0
- package/dist/src/islands/discovery/registry.js +1 -0
- package/dist/src/islands/discovery/resolver.js +2 -0
- package/dist/src/islands/discovery/scanner.js +1 -0
- package/dist/src/islands/discovery/types.js +1 -0
- package/dist/src/islands/discovery/validator.js +18 -0
- package/dist/src/islands/discovery/watcher.js +1 -0
- package/dist/src/islands/framework-detection.js +1 -0
- package/dist/src/islands/integration-loader.js +1 -0
- package/dist/src/islands/island.js +1 -0
- package/dist/src/islands/render-cache.js +1 -0
- package/dist/src/islands/types.js +1 -0
- package/dist/src/islands/universal-css-collector.js +5 -0
- package/dist/src/islands/universal-head-collector.js +2 -0
- package/{src → dist/src}/layout-system.d.ts +592 -592
- package/dist/src/layout-system.js +1 -0
- package/dist/src/middleware/discovery.js +1 -0
- package/dist/src/middleware/executor.js +1 -0
- package/dist/src/middleware/index.js +1 -0
- package/dist/src/middleware/types.js +1 -0
- package/dist/src/nitro/build-config.js +1 -0
- package/dist/src/nitro/config.js +1 -0
- package/dist/src/nitro/error-handler.js +198 -0
- package/dist/src/nitro/index.js +1 -0
- package/dist/src/nitro/island-manifest.js +2 -0
- package/dist/src/nitro/middleware-adapter.js +1 -0
- package/dist/src/nitro/renderer.js +183 -0
- package/dist/src/nitro/route-discovery.js +1 -0
- package/dist/src/nitro/types.js +1 -0
- package/dist/src/render/collect-css.js +3 -0
- package/{src/render/error-pages.ts → dist/src/render/error-pages.js} +7 -38
- package/dist/src/render/isolated-ssr-renderer.js +1 -0
- package/dist/src/render/ssr.js +90 -0
- package/dist/src/schemas/api.js +1 -0
- package/dist/src/schemas/core.js +1 -0
- package/dist/src/schemas/index.js +1 -0
- package/dist/src/schemas/layout.js +1 -0
- package/dist/src/schemas/routing/index.js +1 -0
- package/dist/src/schemas/routing.js +1 -0
- package/dist/src/types/as-island.js +1 -0
- package/{src → dist/src}/types/image.d.ts +106 -106
- package/{src → dist/src}/types/index.d.ts +22 -22
- package/{src → dist/src}/types/island-jsx.d.ts +33 -33
- package/{src → dist/src}/types/island-prop.d.ts +20 -20
- package/dist/src/types/layout.js +1 -0
- package/{src → dist/src}/types/mdx.d.ts +6 -6
- package/dist/src/types/routing.js +1 -0
- package/dist/src/types/types.js +1 -0
- package/{src → dist/src}/types/urlpattern.d.ts +49 -49
- package/{src → dist/src}/types/vite-env.d.ts +11 -11
- package/dist/src/utils/dev-logger.js +12 -0
- package/dist/src/utils/fs.js +1 -0
- package/dist/src/vite-plugin/auto-discover.js +1 -0
- package/dist/src/vite-plugin/config.js +1 -0
- package/dist/src/vite-plugin/errors.js +1 -0
- package/dist/src/vite-plugin/image-optimization.js +45 -0
- package/dist/src/vite-plugin/integration-activator.js +1 -0
- package/dist/src/vite-plugin/island-sidecar-plugin.js +1 -0
- package/dist/src/vite-plugin/module-discovery.js +1 -0
- package/dist/src/vite-plugin/nitro-integration.js +42 -0
- package/dist/src/vite-plugin/plugin.js +1 -0
- package/dist/src/vite-plugin/types.js +1 -0
- package/dist/src/vite-plugin/validation.js +2 -0
- package/package.json +57 -26
- package/mod.ts +0 -302
- package/src/build/integration-bundler-plugin.ts +0 -116
- package/src/build/integration-config.ts +0 -168
- package/src/build/integration-detection-plugin.ts +0 -117
- package/src/build/integration-resolver-plugin.ts +0 -90
- package/src/build/island-manifest.ts +0 -269
- package/src/build/island-types-generator.ts +0 -476
- package/src/build/mdx-island-transform.ts +0 -464
- package/src/build/mdx-plugin.ts +0 -98
- package/src/build/page-island-transform.ts +0 -598
- package/src/build/prop-extractors/index.ts +0 -21
- package/src/build/prop-extractors/lit.ts +0 -140
- package/src/build/prop-extractors/qwik.ts +0 -16
- package/src/build/prop-extractors/solid.ts +0 -125
- package/src/build/prop-extractors/svelte.ts +0 -194
- package/src/build/prop-extractors/vue.ts +0 -111
- package/src/build/sidecar-file-manager.ts +0 -104
- package/src/build/sidecar-renderer.ts +0 -30
- package/src/client/adapters/index.js +0 -12
- package/src/client/adapters/index.ts +0 -13
- package/src/client/adapters/lit-adapter.js +0 -467
- package/src/client/adapters/lit-adapter.ts +0 -654
- package/src/client/adapters/preact-adapter.js +0 -223
- package/src/client/adapters/preact-adapter.ts +0 -331
- package/src/client/adapters/qwik-adapter.js +0 -259
- package/src/client/adapters/qwik-adapter.ts +0 -345
- package/src/client/adapters/react-adapter.js +0 -220
- package/src/client/adapters/react-adapter.ts +0 -353
- package/src/client/adapters/solid-adapter.js +0 -295
- package/src/client/adapters/solid-adapter.ts +0 -451
- package/src/client/adapters/svelte-adapter.js +0 -368
- package/src/client/adapters/svelte-adapter.ts +0 -524
- package/src/client/adapters/vue-adapter.js +0 -278
- package/src/client/adapters/vue-adapter.ts +0 -467
- package/src/client/components.js +0 -23
- package/src/client/components.ts +0 -35
- package/src/client/css-hmr-handler.js +0 -263
- package/src/client/css-hmr-handler.ts +0 -344
- package/src/client/framework-adapter.js +0 -283
- package/src/client/framework-adapter.ts +0 -462
- package/src/client/hmr-coordinator.js +0 -274
- package/src/client/hmr-coordinator.ts +0 -396
- package/src/client/hmr-error-overlay.js +0 -533
- package/src/client/main.js +0 -816
- package/src/client/types/vite-virtual-modules.d.ts +0 -60
- package/src/components/Image.tsx +0 -123
- package/src/components/IslandErrorBoundary.tsx +0 -145
- package/src/components/LayoutDataErrorBoundary.tsx +0 -141
- package/src/components/LayoutErrorBoundary.tsx +0 -127
- package/src/components/PersistentIsland.tsx +0 -52
- package/src/components/StreamingErrorBoundary.tsx +0 -233
- package/src/components/StreamingLayout.tsx +0 -538
- package/src/core/components/component-analyzer.ts +0 -192
- package/src/core/components/component-detection.ts +0 -508
- package/src/core/components/enhanced-framework-detector.ts +0 -500
- package/src/core/components/framework-registry.ts +0 -563
- package/src/core/content/mdx-processor.ts +0 -46
- package/src/core/integrations/index.ts +0 -19
- package/src/core/integrations/loader.ts +0 -125
- package/src/core/integrations/registry.ts +0 -175
- package/src/core/islands/island-persistence.ts +0 -325
- package/src/core/islands/island-state-serializer.ts +0 -258
- package/src/core/islands/persistent-island-context.tsx +0 -80
- package/src/core/islands/use-persistent-state.ts +0 -68
- package/src/core/layout/enhanced-layout-resolver.ts +0 -322
- package/src/core/layout/layout-cache-manager.ts +0 -485
- package/src/core/layout/layout-composer.ts +0 -357
- package/src/core/layout/layout-data-loader.ts +0 -516
- package/src/core/layout/layout-discovery.ts +0 -243
- package/src/core/layout/layout-matcher.ts +0 -299
- package/src/core/layout/layout-types.ts +0 -110
- package/src/core/modules/framework-module-resolver.ts +0 -273
- package/src/islands/component-analysis.ts +0 -213
- package/src/islands/css-utils.ts +0 -565
- package/src/islands/discovery/index.ts +0 -80
- package/src/islands/discovery/registry.ts +0 -340
- package/src/islands/discovery/resolver.ts +0 -477
- package/src/islands/discovery/scanner.ts +0 -386
- package/src/islands/discovery/types.ts +0 -117
- package/src/islands/discovery/validator.ts +0 -544
- package/src/islands/discovery/watcher.ts +0 -368
- package/src/islands/framework-detection.ts +0 -428
- package/src/islands/integration-loader.ts +0 -490
- package/src/islands/island.tsx +0 -565
- package/src/islands/render-cache.ts +0 -550
- package/src/islands/types.ts +0 -80
- package/src/islands/universal-css-collector.ts +0 -157
- package/src/islands/universal-head-collector.ts +0 -137
- package/src/layout-system.ts +0 -218
- package/src/middleware/discovery.ts +0 -268
- package/src/middleware/executor.ts +0 -315
- package/src/middleware/index.ts +0 -76
- package/src/middleware/types.ts +0 -99
- package/src/nitro/build-config.ts +0 -576
- package/src/nitro/config.ts +0 -483
- package/src/nitro/error-handler.ts +0 -636
- package/src/nitro/index.ts +0 -173
- package/src/nitro/island-manifest.ts +0 -584
- package/src/nitro/middleware-adapter.ts +0 -260
- package/src/nitro/renderer.ts +0 -1471
- package/src/nitro/route-discovery.ts +0 -439
- package/src/nitro/types.ts +0 -321
- package/src/render/collect-css.ts +0 -198
- package/src/render/isolated-ssr-renderer.ts +0 -654
- package/src/render/ssr.ts +0 -1030
- package/src/schemas/api.ts +0 -30
- package/src/schemas/core.ts +0 -64
- package/src/schemas/index.ts +0 -212
- package/src/schemas/layout.ts +0 -279
- package/src/schemas/routing/index.ts +0 -38
- package/src/schemas/routing.ts +0 -376
- package/src/types/as-island.ts +0 -20
- package/src/types/layout.ts +0 -285
- package/src/types/routing.ts +0 -555
- package/src/types/types.ts +0 -5
- package/src/utils/dev-logger.ts +0 -299
- package/src/utils/fs.ts +0 -151
- package/src/vite-plugin/auto-discover.ts +0 -551
- package/src/vite-plugin/config.ts +0 -266
- package/src/vite-plugin/errors.ts +0 -127
- package/src/vite-plugin/image-optimization.ts +0 -156
- package/src/vite-plugin/integration-activator.ts +0 -126
- package/src/vite-plugin/island-sidecar-plugin.ts +0 -176
- package/src/vite-plugin/module-discovery.ts +0 -189
- package/src/vite-plugin/nitro-integration.ts +0 -1354
- package/src/vite-plugin/plugin.ts +0 -401
- package/src/vite-plugin/types.ts +0 -327
- package/src/vite-plugin/validation.ts +0 -228
|
@@ -1,464 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MDX Island Transform Plugin
|
|
3
|
-
*
|
|
4
|
-
* Transforms island component usage in MDX files into renderIsland() calls.
|
|
5
|
-
*
|
|
6
|
-
* Supports two patterns:
|
|
7
|
-
* 1. Imports from islands/ directories (legacy pattern)
|
|
8
|
-
* 2. Components used with the `island` prop (preferred pattern)
|
|
9
|
-
*
|
|
10
|
-
* Problem: MDX files import island components directly and render them as raw JSX.
|
|
11
|
-
* After MDX compilation, these become jsxDEV(ComponentName, ...) calls. Preact's
|
|
12
|
-
* renderToString doesn't support async components, so we can't use async wrappers.
|
|
13
|
-
*
|
|
14
|
-
* Solution: Replaces the compiled JSX calls (_jsxDEV(Component, { island: ... }))
|
|
15
|
-
* with await expressions that call renderIsland() directly. This allows proper
|
|
16
|
-
* async SSR rendering of islands in MDX files.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import type { Plugin } from 'vite';
|
|
20
|
-
import { dirname } from 'node:path';
|
|
21
|
-
|
|
22
|
-
export interface MDXIslandTransformOptions {
|
|
23
|
-
islandPathPatterns?: RegExp[];
|
|
24
|
-
verbose?: boolean;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const DEFAULT_ISLAND_PATTERNS = [
|
|
28
|
-
/['"]\.\.\/islands\//,
|
|
29
|
-
/['"]\.\/islands\//,
|
|
30
|
-
/['"]\.\.\/\.\.\/islands\//,
|
|
31
|
-
/['"]\$islands\//,
|
|
32
|
-
/['"]@\/islands\//,
|
|
33
|
-
/['"]\/src\/islands\//,
|
|
34
|
-
];
|
|
35
|
-
|
|
36
|
-
interface IslandImport {
|
|
37
|
-
localName: string;
|
|
38
|
-
importPath: string;
|
|
39
|
-
islandPropUsage: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Find all default imports in the code
|
|
44
|
-
*/
|
|
45
|
-
function findAllDefaultImports(code: string): Map<string, string> {
|
|
46
|
-
const imports = new Map<string, string>();
|
|
47
|
-
const re = /import\s+([A-Z]\w*)\s+from\s+(['"][^'"]+['"])/g;
|
|
48
|
-
let m;
|
|
49
|
-
while ((m = re.exec(code)) !== null) {
|
|
50
|
-
const localName = m[1];
|
|
51
|
-
const importPath = m[2].slice(1, -1);
|
|
52
|
-
imports.set(localName, importPath);
|
|
53
|
-
}
|
|
54
|
-
return imports;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Find components used with the island prop in the code
|
|
59
|
-
* Handles both raw JSX (<Component island={...}) and compiled JSX (_jsxDEV(Component, { island:)
|
|
60
|
-
*/
|
|
61
|
-
function findIslandPropUsage(code: string): Set<string> {
|
|
62
|
-
const components = new Set<string>();
|
|
63
|
-
|
|
64
|
-
// Match raw JSX: <ComponentName ... island={...} or <ComponentName ... island ...
|
|
65
|
-
const rawJsxRe = /<([A-Z]\w*)\s+[^>]*\bisland\s*[={]/g;
|
|
66
|
-
let m;
|
|
67
|
-
while ((m = rawJsxRe.exec(code)) !== null) {
|
|
68
|
-
components.add(m[1]);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Match compiled JSX: _jsxDEV(ComponentName, { island: or jsxDEV(ComponentName, { island:
|
|
72
|
-
// Also handles jsx() and jsxs() variants
|
|
73
|
-
const compiledJsxRe = /(?:_?jsxs?(?:DEV)?)\s*\(\s*([A-Z]\w*)\s*,\s*\{[^}]*\bisland\s*:/g;
|
|
74
|
-
while ((m = compiledJsxRe.exec(code)) !== null) {
|
|
75
|
-
components.add(m[1]);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return components;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function findIslandImports(code: string, patterns: RegExp[]): IslandImport[] {
|
|
82
|
-
const imports: IslandImport[] = [];
|
|
83
|
-
const allImports = findAllDefaultImports(code);
|
|
84
|
-
const islandPropComponents = findIslandPropUsage(code);
|
|
85
|
-
|
|
86
|
-
for (const [localName, importPath] of allImports) {
|
|
87
|
-
const quotedPath = `"${importPath}"`;
|
|
88
|
-
const isFromIslandsDir = patterns.some(p => p.test(quotedPath));
|
|
89
|
-
const hasIslandProp = islandPropComponents.has(localName);
|
|
90
|
-
|
|
91
|
-
if (isFromIslandsDir || hasIslandProp) {
|
|
92
|
-
imports.push({ localName, importPath, islandPropUsage: hasIslandProp });
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return imports;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Resolve an import path to an absolute src path for renderIsland
|
|
101
|
-
*/
|
|
102
|
-
function resolveIslandSrc(importPath: string, fileId: string): string {
|
|
103
|
-
// Already absolute
|
|
104
|
-
if (importPath.startsWith('/src/')) return importPath;
|
|
105
|
-
if (importPath.startsWith('/app/')) return importPath;
|
|
106
|
-
if (importPath.startsWith('/')) return importPath;
|
|
107
|
-
|
|
108
|
-
// Handle aliases - convert to absolute paths
|
|
109
|
-
if (importPath.startsWith('@/')) {
|
|
110
|
-
return '/app/' + importPath.slice(2);
|
|
111
|
-
}
|
|
112
|
-
if (importPath.startsWith('@shared/')) {
|
|
113
|
-
return '/app/shared/' + importPath.slice(8);
|
|
114
|
-
}
|
|
115
|
-
if (importPath.startsWith('@modules/')) {
|
|
116
|
-
return '/app/modules/' + importPath.slice(9);
|
|
117
|
-
}
|
|
118
|
-
if (importPath.startsWith('$components/')) {
|
|
119
|
-
return '/src/components/' + importPath.slice(12);
|
|
120
|
-
}
|
|
121
|
-
if (importPath.startsWith('$islands/')) {
|
|
122
|
-
return '/src/islands/' + importPath.slice(9);
|
|
123
|
-
}
|
|
124
|
-
if (importPath.startsWith('~/')) {
|
|
125
|
-
return '/src/' + importPath.slice(2);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Relative import - resolve relative to the file
|
|
129
|
-
if (importPath.startsWith('.')) {
|
|
130
|
-
const normalized = fileId.replaceAll('\\', '/');
|
|
131
|
-
|
|
132
|
-
// Try to find /app/ or /src/ in the path
|
|
133
|
-
let baseIndex = normalized.indexOf('/app/');
|
|
134
|
-
if (baseIndex === -1) baseIndex = normalized.indexOf('/src/');
|
|
135
|
-
|
|
136
|
-
if (baseIndex !== -1) {
|
|
137
|
-
const fileDir = dirname(normalized.slice(baseIndex));
|
|
138
|
-
// Simple path resolution
|
|
139
|
-
const parts = fileDir.split('/');
|
|
140
|
-
const importParts = importPath.split('/');
|
|
141
|
-
|
|
142
|
-
for (const part of importParts) {
|
|
143
|
-
if (part === '..') {
|
|
144
|
-
parts.pop();
|
|
145
|
-
} else if (part !== '.') {
|
|
146
|
-
parts.push(part);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return parts.join('/');
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Fallback for islands directory pattern
|
|
155
|
-
if (importPath.includes('/islands/')) {
|
|
156
|
-
const parts = importPath.split('/');
|
|
157
|
-
return '/src/islands/' + parts.at(-1);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Fallback: return as-is with /src/ prefix
|
|
161
|
-
return '/src/' + importPath.split('/').pop();
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function detectFramework(src: string): string | undefined {
|
|
165
|
-
if (src.endsWith('.vue')) return 'vue';
|
|
166
|
-
if (src.endsWith('.svelte')) return 'svelte';
|
|
167
|
-
if (src.includes('.solid.')) return 'solid';
|
|
168
|
-
if (src.includes('.lit.')) return 'lit';
|
|
169
|
-
if (src.includes('.qwik.')) return 'qwik';
|
|
170
|
-
if (src.endsWith('.tsx') || src.endsWith('.jsx')) return 'preact';
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Skip a brace-delimited expression `{...}`, handling nested braces and strings.
|
|
176
|
-
* Returns the index after the closing brace.
|
|
177
|
-
*/
|
|
178
|
-
function skipBracedExpression(code: string, openBraceIdx: number): number {
|
|
179
|
-
let pos = openBraceIdx + 1;
|
|
180
|
-
let depth = 1;
|
|
181
|
-
while (pos < code.length && depth > 0) {
|
|
182
|
-
const ch = code[pos];
|
|
183
|
-
if (ch === '{') {
|
|
184
|
-
depth++;
|
|
185
|
-
pos++;
|
|
186
|
-
} else if (ch === '}') {
|
|
187
|
-
depth--;
|
|
188
|
-
if (depth > 0) pos++;
|
|
189
|
-
} else if (ch === "'" || ch === '"' || ch === '`') {
|
|
190
|
-
pos = skipStringLiteral(code, pos);
|
|
191
|
-
} else {
|
|
192
|
-
pos++;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
return pos < code.length ? pos + 1 : pos;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Skip a string literal (single, double, or backtick).
|
|
200
|
-
* Returns index after closing quote.
|
|
201
|
-
*/
|
|
202
|
-
function skipStringLiteral(code: string, pos: number): number {
|
|
203
|
-
const quote = code[pos];
|
|
204
|
-
pos++;
|
|
205
|
-
while (pos < code.length && code[pos] !== quote) {
|
|
206
|
-
if (code[pos] === '\\') pos++; // skip escaped char
|
|
207
|
-
pos++;
|
|
208
|
-
}
|
|
209
|
-
return pos < code.length ? pos + 1 : pos;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Find the end of a JSX function call: _jsxDEV(Component, {...}, ...)
|
|
214
|
-
* Returns the index after the closing parenthesis.
|
|
215
|
-
*/
|
|
216
|
-
function findJsxCallEnd(code: string, startIdx: number): number {
|
|
217
|
-
let pos = startIdx;
|
|
218
|
-
let depth = 0;
|
|
219
|
-
|
|
220
|
-
// Find the opening parenthesis
|
|
221
|
-
while (pos < code.length && code[pos] !== '(') pos++;
|
|
222
|
-
if (pos >= code.length) return startIdx;
|
|
223
|
-
|
|
224
|
-
// Now track parentheses depth
|
|
225
|
-
while (pos < code.length) {
|
|
226
|
-
const ch = code[pos];
|
|
227
|
-
if (ch === '(') {
|
|
228
|
-
depth++;
|
|
229
|
-
pos++;
|
|
230
|
-
} else if (ch === ')') {
|
|
231
|
-
depth--;
|
|
232
|
-
pos++;
|
|
233
|
-
if (depth === 0) return pos;
|
|
234
|
-
} else if (ch === '{') {
|
|
235
|
-
pos = skipBracedExpression(code, pos);
|
|
236
|
-
} else if (ch === "'" || ch === '"' || ch === '`') {
|
|
237
|
-
pos = skipStringLiteral(code, pos);
|
|
238
|
-
} else {
|
|
239
|
-
pos++;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return pos;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* Extract the island prop value from a JSX props object.
|
|
247
|
-
* Given `{ island: { condition: 'on:interaction' }, other: 1 }`, returns `{ condition: 'on:interaction' }`
|
|
248
|
-
*/
|
|
249
|
-
function extractIslandProp(propsStr: string): { islandValue: string; otherProps: string } | null {
|
|
250
|
-
// Find `island:` or `island :` in the props
|
|
251
|
-
const islandMatch = propsStr.match(/\bisland\s*:\s*/);
|
|
252
|
-
if (!islandMatch) return null;
|
|
253
|
-
|
|
254
|
-
const islandStart = islandMatch.index! + islandMatch[0].length;
|
|
255
|
-
|
|
256
|
-
// The island value could be:
|
|
257
|
-
// 1. An object literal: { condition: 'on:interaction' }
|
|
258
|
-
// 2. A variable reference: islandOpts
|
|
259
|
-
// 3. A more complex expression
|
|
260
|
-
|
|
261
|
-
let islandEnd: number;
|
|
262
|
-
if (propsStr[islandStart] === '{') {
|
|
263
|
-
// Object literal - find matching closing brace
|
|
264
|
-
islandEnd = skipBracedExpression(propsStr, islandStart);
|
|
265
|
-
} else {
|
|
266
|
-
// Find the next comma or closing brace
|
|
267
|
-
let pos = islandStart;
|
|
268
|
-
let depth = 0;
|
|
269
|
-
while (pos < propsStr.length) {
|
|
270
|
-
const ch = propsStr[pos];
|
|
271
|
-
if (ch === '{' || ch === '[' || ch === '(') {
|
|
272
|
-
depth++;
|
|
273
|
-
pos++;
|
|
274
|
-
} else if (ch === '}' || ch === ']' || ch === ')') {
|
|
275
|
-
if (depth === 0) break;
|
|
276
|
-
depth--;
|
|
277
|
-
pos++;
|
|
278
|
-
} else if (ch === ',' && depth === 0) {
|
|
279
|
-
break;
|
|
280
|
-
} else {
|
|
281
|
-
pos++;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
islandEnd = pos;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
const islandValue = propsStr.slice(islandStart, islandEnd).trim();
|
|
288
|
-
|
|
289
|
-
// Build other props by removing the island prop
|
|
290
|
-
const beforeIsland = propsStr.slice(0, islandMatch.index!).trim();
|
|
291
|
-
const afterIsland = propsStr.slice(islandEnd).trim();
|
|
292
|
-
|
|
293
|
-
// Clean up: remove trailing/leading commas
|
|
294
|
-
let otherProps = beforeIsland;
|
|
295
|
-
if (afterIsland.startsWith(',')) {
|
|
296
|
-
otherProps += afterIsland.slice(1);
|
|
297
|
-
} else {
|
|
298
|
-
otherProps += afterIsland;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// Remove trailing comma before closing brace
|
|
302
|
-
otherProps = otherProps.replace(/,\s*}$/, '}').replace(/{\s*,/, '{');
|
|
303
|
-
|
|
304
|
-
return { islandValue, otherProps };
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Replace JSX calls for a component with renderIsland await expressions.
|
|
309
|
-
* Transforms: _jsxDEV(Component, { island: {...}, prop: 1 }, ...)
|
|
310
|
-
* Into: (await __AvalonRenderIsland({ src: "...", ...island, props: { prop: 1 } }))
|
|
311
|
-
*/
|
|
312
|
-
function replaceJsxCalls(
|
|
313
|
-
code: string,
|
|
314
|
-
componentName: string,
|
|
315
|
-
srcPath: string,
|
|
316
|
-
framework: string | undefined,
|
|
317
|
-
): string {
|
|
318
|
-
// Match patterns like: _jsxDEV(ComponentName, or jsxDEV(ComponentName, or jsx(ComponentName,
|
|
319
|
-
const jsxCallPattern = new RegExp(
|
|
320
|
-
'(_?jsxs?(?:DEV)?)\\s*\\(\\s*' + componentName + '\\s*,',
|
|
321
|
-
'g'
|
|
322
|
-
);
|
|
323
|
-
|
|
324
|
-
let result = '';
|
|
325
|
-
let lastIndex = 0;
|
|
326
|
-
let match;
|
|
327
|
-
|
|
328
|
-
while ((match = jsxCallPattern.exec(code)) !== null) {
|
|
329
|
-
const matchStart = match.index;
|
|
330
|
-
const jsxFn = match[1];
|
|
331
|
-
|
|
332
|
-
// Find the end of this JSX call
|
|
333
|
-
const callEnd = findJsxCallEnd(code, matchStart);
|
|
334
|
-
const fullCall = code.slice(matchStart, callEnd);
|
|
335
|
-
|
|
336
|
-
// Check if this call has an island prop
|
|
337
|
-
if (!fullCall.includes('island')) {
|
|
338
|
-
// No island prop, keep as-is
|
|
339
|
-
result += code.slice(lastIndex, callEnd);
|
|
340
|
-
lastIndex = callEnd;
|
|
341
|
-
continue;
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// Extract the props object - it's the second argument
|
|
345
|
-
// Pattern: jsxFn(Component, { props }, key, isStatic, source, self)
|
|
346
|
-
const propsStart = fullCall.indexOf('{');
|
|
347
|
-
if (propsStart === -1) {
|
|
348
|
-
result += code.slice(lastIndex, callEnd);
|
|
349
|
-
lastIndex = callEnd;
|
|
350
|
-
continue;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const propsEnd = skipBracedExpression(fullCall, propsStart);
|
|
354
|
-
const propsStr = fullCall.slice(propsStart, propsEnd);
|
|
355
|
-
|
|
356
|
-
const extracted = extractIslandProp(propsStr);
|
|
357
|
-
if (!extracted) {
|
|
358
|
-
result += code.slice(lastIndex, callEnd);
|
|
359
|
-
lastIndex = callEnd;
|
|
360
|
-
continue;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const { islandValue, otherProps } = extracted;
|
|
364
|
-
const fwArg = framework ? `framework: "${framework}",` : '';
|
|
365
|
-
|
|
366
|
-
// Check if otherProps is empty (just `{}`)
|
|
367
|
-
const hasOtherProps = otherProps.trim() !== '{}' && otherProps.trim() !== '';
|
|
368
|
-
const propsArg = hasOtherProps ? `props: ${otherProps},` : '';
|
|
369
|
-
|
|
370
|
-
// Build the renderIsland call
|
|
371
|
-
// We spread the island value to get condition, ssr, etc.
|
|
372
|
-
const renderCall = `(await __AvalonRenderIsland({ src: "${srcPath}", ${fwArg} ...(${islandValue}), ${propsArg} ssr: (${islandValue}).ssr !== undefined ? (${islandValue}).ssr : true }))`;
|
|
373
|
-
|
|
374
|
-
result += code.slice(lastIndex, matchStart) + renderCall;
|
|
375
|
-
lastIndex = callEnd;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
result += code.slice(lastIndex);
|
|
379
|
-
return result;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
export function mdxIslandTransform(options: MDXIslandTransformOptions = {}): Plugin {
|
|
383
|
-
const { islandPathPatterns = DEFAULT_ISLAND_PATTERNS, verbose = false } = options;
|
|
384
|
-
|
|
385
|
-
return {
|
|
386
|
-
name: 'avalon:mdx-island-transform',
|
|
387
|
-
enforce: 'post',
|
|
388
|
-
|
|
389
|
-
transform(code: string, id: string) {
|
|
390
|
-
if (!id.endsWith('.mdx') && !id.includes('.mdx?')) {
|
|
391
|
-
return null;
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
const islandImports = findIslandImports(code, islandPathPatterns);
|
|
395
|
-
if (islandImports.length === 0) {
|
|
396
|
-
return null;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
if (verbose) {
|
|
400
|
-
console.log('[mdx-island-transform] Found ' + islandImports.length + ' island import(s) in ' + id);
|
|
401
|
-
for (const imp of islandImports) {
|
|
402
|
-
console.log(' - ' + imp.localName + ' from ' + imp.importPath + (imp.islandPropUsage ? ' (island prop)' : ' (islands dir)'));
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
let transformed = code;
|
|
407
|
-
|
|
408
|
-
// Add the renderIsland import for async SSR
|
|
409
|
-
const hasAvalonImport =
|
|
410
|
-
transformed.includes('from "@useavalon/avalon"') || transformed.includes("from '@useavalon/avalon'");
|
|
411
|
-
|
|
412
|
-
if (!hasAvalonImport) {
|
|
413
|
-
const firstImport = /^(import\s.+?from\s+.+?\n)/m.exec(transformed);
|
|
414
|
-
if (firstImport) {
|
|
415
|
-
const pos = transformed.indexOf(firstImport[0]) + firstImport[0].length;
|
|
416
|
-
const line = 'import { renderIsland as __AvalonRenderIsland } from "@useavalon/avalon";\n';
|
|
417
|
-
transformed = transformed.slice(0, pos) + line + transformed.slice(pos);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
// Replace JSX calls with renderIsland await expressions
|
|
422
|
-
for (const island of islandImports) {
|
|
423
|
-
const srcPath = resolveIslandSrc(island.importPath, id);
|
|
424
|
-
const fw = detectFramework(srcPath);
|
|
425
|
-
|
|
426
|
-
transformed = replaceJsxCalls(transformed, island.localName, srcPath, fw);
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// Comment out the original imports (keep for CSS graph but don't use the binding)
|
|
430
|
-
for (const island of islandImports) {
|
|
431
|
-
const importRe = new RegExp(
|
|
432
|
-
`import\\s+${island.localName}\\s+from\\s+(['"][^'"]+['"])`,
|
|
433
|
-
'g'
|
|
434
|
-
);
|
|
435
|
-
transformed = transformed.replace(
|
|
436
|
-
importRe,
|
|
437
|
-
`import $1; // [mdx-island-transform] kept for CSS: ${island.localName}`
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
// Make the MDX content function async so we can use await
|
|
442
|
-
// Transform: function _createMdxContent(props) {
|
|
443
|
-
// Into: async function _createMdxContent(props) {
|
|
444
|
-
transformed = transformed.replace(
|
|
445
|
-
/function\s+_createMdxContent\s*\(/g,
|
|
446
|
-
'async function _createMdxContent('
|
|
447
|
-
);
|
|
448
|
-
|
|
449
|
-
// Also make the default export async if it wraps _createMdxContent
|
|
450
|
-
// Transform: export default function MDXContent(props = {}) {
|
|
451
|
-
// Into: export default async function MDXContent(props = {}) {
|
|
452
|
-
transformed = transformed.replace(
|
|
453
|
-
/export\s+default\s+function\s+MDXContent\s*\(/g,
|
|
454
|
-
'export default async function MDXContent('
|
|
455
|
-
);
|
|
456
|
-
|
|
457
|
-
if (verbose) {
|
|
458
|
-
console.log('[mdx-island-transform] Transformed ' + id);
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
return { code: transformed, map: null };
|
|
462
|
-
},
|
|
463
|
-
};
|
|
464
|
-
}
|
package/src/build/mdx-plugin.ts
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import type { Plugin } from 'vite';
|
|
2
|
-
import type { Pluggable } from 'unified';
|
|
3
|
-
|
|
4
|
-
export interface MDXPluginOptions {
|
|
5
|
-
remarkPlugins?: Pluggable[];
|
|
6
|
-
rehypePlugins?: Pluggable[];
|
|
7
|
-
development?: boolean;
|
|
8
|
-
jsxImportSource?: string;
|
|
9
|
-
/** Enable syntax highlighting via rehype-highlight (default: true) */
|
|
10
|
-
syntaxHighlighting?: boolean;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Creates and configures the MDX Vite plugin
|
|
15
|
-
*
|
|
16
|
-
* This function sets up the MDX plugin with:
|
|
17
|
-
* - Frontmatter processing (remark-frontmatter, remark-mdx-frontmatter)
|
|
18
|
-
* - GitHub Flavored Markdown support (remark-gfm)
|
|
19
|
-
* - Syntax highlighting (rehype-highlight) - enabled by default
|
|
20
|
-
* - Custom remark/rehype plugins
|
|
21
|
-
* - JSX runtime configuration
|
|
22
|
-
*
|
|
23
|
-
* @param options - MDX plugin configuration options
|
|
24
|
-
* @returns Array of Vite plugins (empty if MDX dependencies are not available)
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* ```ts
|
|
28
|
-
* // Basic usage with defaults
|
|
29
|
-
* const plugins = await createMDXPlugin();
|
|
30
|
-
*
|
|
31
|
-
* // With React JSX runtime
|
|
32
|
-
* const plugins = await createMDXPlugin({ jsxImportSource: 'react' });
|
|
33
|
-
*
|
|
34
|
-
* // Disable syntax highlighting
|
|
35
|
-
* const plugins = await createMDXPlugin({ syntaxHighlighting: false });
|
|
36
|
-
* ```
|
|
37
|
-
*/
|
|
38
|
-
export async function createMDXPlugin(options: MDXPluginOptions = {}): Promise<Plugin[]> {
|
|
39
|
-
const {
|
|
40
|
-
remarkPlugins = [],
|
|
41
|
-
rehypePlugins = [],
|
|
42
|
-
development = false,
|
|
43
|
-
jsxImportSource = 'preact',
|
|
44
|
-
syntaxHighlighting = true,
|
|
45
|
-
} = options;
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
// Load the core MDX plugin
|
|
49
|
-
const { default: mdx } = await import('@mdx-js/rollup');
|
|
50
|
-
|
|
51
|
-
// Load remark plugins for frontmatter processing and GFM support
|
|
52
|
-
const { default: remarkFrontmatter } = await import('remark-frontmatter');
|
|
53
|
-
const { default: remarkMdxFrontmatter } = await import('remark-mdx-frontmatter');
|
|
54
|
-
const { default: remarkGfm } = await import('remark-gfm');
|
|
55
|
-
|
|
56
|
-
// Build rehype plugins array based on options
|
|
57
|
-
const finalRehypePlugins: Pluggable[] = [];
|
|
58
|
-
|
|
59
|
-
// Add syntax highlighting if enabled
|
|
60
|
-
if (syntaxHighlighting) {
|
|
61
|
-
try {
|
|
62
|
-
const { default: rehypeHighlight } = await import('rehype-highlight');
|
|
63
|
-
finalRehypePlugins.push(rehypeHighlight);
|
|
64
|
-
} catch {
|
|
65
|
-
console.warn('[avalon:mdx] rehype-highlight not available, syntax highlighting disabled');
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Add user-provided rehype plugins
|
|
70
|
-
finalRehypePlugins.push(...rehypePlugins);
|
|
71
|
-
|
|
72
|
-
// Configure MDX plugin with frontmatter processing, GFM support, and optional syntax highlighting
|
|
73
|
-
const mdxPlugin = mdx({
|
|
74
|
-
// Plugin chains - frontmatter must come first, then export as named exports
|
|
75
|
-
remarkPlugins: [remarkFrontmatter, remarkMdxFrontmatter, remarkGfm, ...remarkPlugins],
|
|
76
|
-
rehypePlugins: finalRehypePlugins,
|
|
77
|
-
|
|
78
|
-
// JSX configuration for the specified runtime
|
|
79
|
-
jsxImportSource: jsxImportSource,
|
|
80
|
-
|
|
81
|
-
// Development vs production optimizations
|
|
82
|
-
development,
|
|
83
|
-
|
|
84
|
-
// Ensure proper module format
|
|
85
|
-
format: 'mdx',
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
return [mdxPlugin];
|
|
89
|
-
} catch (error) {
|
|
90
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
91
|
-
console.error('❌ Failed to configure MDX plugin:', errorMessage);
|
|
92
|
-
console.warn('💡 Install missing dependencies or check import map');
|
|
93
|
-
|
|
94
|
-
// Always return empty array to allow server to start without MDX
|
|
95
|
-
console.warn('⚠️ MDX plugin disabled - .mdx files will not be processed');
|
|
96
|
-
return [];
|
|
97
|
-
}
|
|
98
|
-
}
|