@useavalon/avalon 0.1.11 → 0.1.13
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/mod.ts +302 -302
- package/package.json +49 -26
- package/src/build/integration-bundler-plugin.ts +116 -116
- package/src/build/integration-config.ts +168 -168
- package/src/build/integration-detection-plugin.ts +117 -117
- package/src/build/integration-resolver-plugin.ts +90 -90
- package/src/build/island-manifest.ts +269 -269
- package/src/build/island-types-generator.ts +476 -476
- package/src/build/mdx-island-transform.ts +464 -464
- package/src/build/mdx-plugin.ts +98 -98
- package/src/build/page-island-transform.ts +598 -598
- package/src/build/prop-extractors/index.ts +21 -21
- package/src/build/prop-extractors/lit.ts +140 -140
- package/src/build/prop-extractors/qwik.ts +16 -16
- package/src/build/prop-extractors/solid.ts +125 -125
- package/src/build/prop-extractors/svelte.ts +194 -194
- package/src/build/prop-extractors/vue.ts +111 -111
- package/src/build/sidecar-file-manager.ts +104 -104
- package/src/build/sidecar-renderer.ts +30 -30
- package/src/client/adapters/index.ts +21 -13
- package/src/client/components.ts +35 -35
- package/src/client/css-hmr-handler.ts +344 -344
- package/src/client/framework-adapter.ts +462 -462
- package/src/client/hmr-coordinator.ts +396 -396
- package/src/client/hmr-error-overlay.js +533 -533
- package/src/client/main.js +824 -816
- package/src/client/types/framework-runtime.d.ts +68 -68
- package/src/client/types/vite-hmr.d.ts +46 -46
- package/src/client/types/vite-virtual-modules.d.ts +70 -60
- package/src/components/Image.tsx +123 -123
- package/src/components/IslandErrorBoundary.tsx +145 -145
- package/src/components/LayoutDataErrorBoundary.tsx +141 -141
- package/src/components/LayoutErrorBoundary.tsx +127 -127
- package/src/components/PersistentIsland.tsx +52 -52
- package/src/components/StreamingErrorBoundary.tsx +233 -233
- package/src/components/StreamingLayout.tsx +538 -538
- package/src/core/components/component-analyzer.ts +192 -192
- package/src/core/components/component-detection.ts +508 -508
- package/src/core/components/enhanced-framework-detector.ts +500 -500
- package/src/core/components/framework-registry.ts +563 -563
- package/src/core/content/mdx-processor.ts +46 -46
- package/src/core/integrations/index.ts +19 -19
- package/src/core/integrations/loader.ts +125 -125
- package/src/core/integrations/registry.ts +175 -175
- package/src/core/islands/island-persistence.ts +325 -325
- package/src/core/islands/island-state-serializer.ts +258 -258
- package/src/core/islands/persistent-island-context.tsx +80 -80
- package/src/core/islands/use-persistent-state.ts +68 -68
- package/src/core/layout/enhanced-layout-resolver.ts +322 -322
- package/src/core/layout/layout-cache-manager.ts +485 -485
- package/src/core/layout/layout-composer.ts +357 -357
- package/src/core/layout/layout-data-loader.ts +516 -516
- package/src/core/layout/layout-discovery.ts +243 -243
- package/src/core/layout/layout-matcher.ts +299 -299
- package/src/core/layout/layout-types.ts +110 -110
- package/src/core/modules/framework-module-resolver.ts +273 -273
- package/src/islands/component-analysis.ts +213 -213
- package/src/islands/css-utils.ts +565 -565
- package/src/islands/discovery/index.ts +80 -80
- package/src/islands/discovery/registry.ts +340 -340
- package/src/islands/discovery/resolver.ts +477 -477
- package/src/islands/discovery/scanner.ts +386 -386
- package/src/islands/discovery/types.ts +117 -117
- package/src/islands/discovery/validator.ts +544 -544
- package/src/islands/discovery/watcher.ts +368 -368
- package/src/islands/framework-detection.ts +428 -428
- package/src/islands/integration-loader.ts +490 -490
- package/src/islands/island.tsx +565 -565
- package/src/islands/render-cache.ts +550 -550
- package/src/islands/types.ts +80 -80
- package/src/islands/universal-css-collector.ts +157 -157
- package/src/islands/universal-head-collector.ts +137 -137
- package/src/layout-system.d.ts +592 -592
- package/src/layout-system.ts +218 -218
- package/src/middleware/discovery.ts +268 -268
- package/src/middleware/executor.ts +315 -315
- package/src/middleware/index.ts +76 -76
- package/src/middleware/types.ts +99 -99
- package/src/nitro/build-config.ts +575 -575
- package/src/nitro/config.ts +483 -483
- package/src/nitro/error-handler.ts +636 -636
- package/src/nitro/index.ts +173 -173
- package/src/nitro/island-manifest.ts +584 -584
- package/src/nitro/middleware-adapter.ts +260 -260
- package/src/nitro/renderer.ts +1471 -1471
- package/src/nitro/route-discovery.ts +439 -439
- package/src/nitro/types.ts +321 -321
- package/src/render/collect-css.ts +198 -198
- package/src/render/error-pages.ts +79 -79
- package/src/render/isolated-ssr-renderer.ts +654 -654
- package/src/render/ssr.ts +1030 -1030
- package/src/schemas/api.ts +30 -30
- package/src/schemas/core.ts +64 -64
- package/src/schemas/index.ts +212 -212
- package/src/schemas/layout.ts +279 -279
- package/src/schemas/routing/index.ts +38 -38
- package/src/schemas/routing.ts +376 -376
- package/src/types/as-island.ts +20 -20
- package/src/types/image.d.ts +106 -106
- package/src/types/index.d.ts +22 -22
- package/src/types/island-jsx.d.ts +33 -33
- package/src/types/island-prop.d.ts +20 -20
- package/src/types/layout.ts +285 -285
- package/src/types/mdx.d.ts +6 -6
- package/src/types/routing.ts +555 -555
- package/src/types/types.ts +5 -5
- package/src/types/urlpattern.d.ts +49 -49
- package/src/types/vite-env.d.ts +11 -11
- package/src/utils/dev-logger.ts +299 -299
- package/src/utils/fs.ts +151 -151
- package/src/vite-plugin/auto-discover.ts +551 -551
- package/src/vite-plugin/config.ts +266 -266
- package/src/vite-plugin/errors.ts +127 -127
- package/src/vite-plugin/image-optimization.ts +156 -156
- package/src/vite-plugin/integration-activator.ts +126 -126
- package/src/vite-plugin/island-sidecar-plugin.ts +176 -176
- package/src/vite-plugin/module-discovery.ts +189 -189
- package/src/vite-plugin/nitro-integration.ts +1354 -1354
- package/src/vite-plugin/plugin.ts +403 -409
- package/src/vite-plugin/types.ts +327 -327
- package/src/vite-plugin/validation.ts +228 -228
- package/src/client/adapters/index.js +0 -12
- 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/css-hmr-handler.js +0 -263
- package/src/client/framework-adapter.js +0 -283
- package/src/client/hmr-coordinator.js +0 -274
|
@@ -1,127 +1,127 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Error Classes for Avalon Vite Plugin
|
|
3
|
-
*
|
|
4
|
-
* This module provides custom error classes for configuration and integration errors.
|
|
5
|
-
* These errors include contextual information to help developers quickly identify
|
|
6
|
-
* and fix issues.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Thrown when configuration is invalid
|
|
11
|
-
*
|
|
12
|
-
* This error includes the specific field name and value that caused the error,
|
|
13
|
-
* making it easier to identify and fix configuration issues.
|
|
14
|
-
*
|
|
15
|
-
* @example
|
|
16
|
-
* ```ts
|
|
17
|
-
* throw new AvalonConfigError(
|
|
18
|
-
* "must be a valid directory path",
|
|
19
|
-
* "islandsDir",
|
|
20
|
-
* 123 // invalid value - should be a string
|
|
21
|
-
* );
|
|
22
|
-
* // Error: Avalon configuration error in 'islandsDir': must be a valid directory path
|
|
23
|
-
* // Received value: 123
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
export class AvalonConfigError extends Error {
|
|
27
|
-
/**
|
|
28
|
-
* The name of the configuration field that caused the error
|
|
29
|
-
*/
|
|
30
|
-
public readonly field: string;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* The invalid value that was provided
|
|
34
|
-
*/
|
|
35
|
-
public readonly value: unknown;
|
|
36
|
-
|
|
37
|
-
constructor(message: string, field: string, value: unknown) {
|
|
38
|
-
const valueStr = formatValue(value);
|
|
39
|
-
super(`Avalon configuration error in '${field}': ${message}\nReceived value: ${valueStr}`);
|
|
40
|
-
this.name = "AvalonConfigError";
|
|
41
|
-
this.field = field;
|
|
42
|
-
this.value = value;
|
|
43
|
-
|
|
44
|
-
// Maintains proper stack trace for where error was thrown (V8 engines)
|
|
45
|
-
if (Error.captureStackTrace) {
|
|
46
|
-
Error.captureStackTrace(this, AvalonConfigError);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Thrown when an integration fails to load or has an invalid name
|
|
53
|
-
*
|
|
54
|
-
* This error includes the integration name and optionally the underlying cause,
|
|
55
|
-
* making it easier to diagnose integration loading issues.
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* ```ts
|
|
59
|
-
* throw new IntegrationError(
|
|
60
|
-
* "Failed to activate integration. Is @useavalon/react installed?",
|
|
61
|
-
* "react",
|
|
62
|
-
* originalError
|
|
63
|
-
* );
|
|
64
|
-
* // Error: Integration 'react': Failed to activate integration. Is @useavalon/react installed?
|
|
65
|
-
* ```
|
|
66
|
-
*/
|
|
67
|
-
export class IntegrationError extends Error {
|
|
68
|
-
/**
|
|
69
|
-
* The name of the integration that caused the error
|
|
70
|
-
*/
|
|
71
|
-
public readonly integrationName: string;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* The original error that caused this error, if any
|
|
75
|
-
*/
|
|
76
|
-
public readonly originalCause?: Error;
|
|
77
|
-
|
|
78
|
-
constructor(message: string, integrationName: string, originalCause?: Error) {
|
|
79
|
-
super(`Integration '${integrationName}': ${message}`, { cause: originalCause });
|
|
80
|
-
this.name = "IntegrationError";
|
|
81
|
-
this.integrationName = integrationName;
|
|
82
|
-
this.originalCause = originalCause;
|
|
83
|
-
|
|
84
|
-
// Maintains proper stack trace for where error was thrown (V8 engines)
|
|
85
|
-
if (Error.captureStackTrace) {
|
|
86
|
-
Error.captureStackTrace(this, IntegrationError);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Format a value for display in error messages
|
|
93
|
-
*
|
|
94
|
-
* @param value - The value to format
|
|
95
|
-
* @returns A string representation of the value
|
|
96
|
-
*/
|
|
97
|
-
function formatValue(value: unknown): string {
|
|
98
|
-
if (value === undefined) {
|
|
99
|
-
return "undefined";
|
|
100
|
-
}
|
|
101
|
-
if (value === null) {
|
|
102
|
-
return "null";
|
|
103
|
-
}
|
|
104
|
-
if (typeof value === "string") {
|
|
105
|
-
return `"${value}"`;
|
|
106
|
-
}
|
|
107
|
-
if (typeof value === "function") {
|
|
108
|
-
return "[Function]";
|
|
109
|
-
}
|
|
110
|
-
if (Array.isArray(value)) {
|
|
111
|
-
if (value.length === 0) {
|
|
112
|
-
return "[]";
|
|
113
|
-
}
|
|
114
|
-
if (value.length <= 3) {
|
|
115
|
-
return `[${value.map(formatValue).join(", ")}]`;
|
|
116
|
-
}
|
|
117
|
-
return `[${value.slice(0, 3).map(formatValue).join(", ")}, ... (${value.length} items)]`;
|
|
118
|
-
}
|
|
119
|
-
if (typeof value === "object") {
|
|
120
|
-
try {
|
|
121
|
-
return JSON.stringify(value);
|
|
122
|
-
} catch {
|
|
123
|
-
return "[Circular]";
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
return String(value as string | number | boolean | bigint | symbol);
|
|
127
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Error Classes for Avalon Vite Plugin
|
|
3
|
+
*
|
|
4
|
+
* This module provides custom error classes for configuration and integration errors.
|
|
5
|
+
* These errors include contextual information to help developers quickly identify
|
|
6
|
+
* and fix issues.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Thrown when configuration is invalid
|
|
11
|
+
*
|
|
12
|
+
* This error includes the specific field name and value that caused the error,
|
|
13
|
+
* making it easier to identify and fix configuration issues.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* throw new AvalonConfigError(
|
|
18
|
+
* "must be a valid directory path",
|
|
19
|
+
* "islandsDir",
|
|
20
|
+
* 123 // invalid value - should be a string
|
|
21
|
+
* );
|
|
22
|
+
* // Error: Avalon configuration error in 'islandsDir': must be a valid directory path
|
|
23
|
+
* // Received value: 123
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class AvalonConfigError extends Error {
|
|
27
|
+
/**
|
|
28
|
+
* The name of the configuration field that caused the error
|
|
29
|
+
*/
|
|
30
|
+
public readonly field: string;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The invalid value that was provided
|
|
34
|
+
*/
|
|
35
|
+
public readonly value: unknown;
|
|
36
|
+
|
|
37
|
+
constructor(message: string, field: string, value: unknown) {
|
|
38
|
+
const valueStr = formatValue(value);
|
|
39
|
+
super(`Avalon configuration error in '${field}': ${message}\nReceived value: ${valueStr}`);
|
|
40
|
+
this.name = "AvalonConfigError";
|
|
41
|
+
this.field = field;
|
|
42
|
+
this.value = value;
|
|
43
|
+
|
|
44
|
+
// Maintains proper stack trace for where error was thrown (V8 engines)
|
|
45
|
+
if (Error.captureStackTrace) {
|
|
46
|
+
Error.captureStackTrace(this, AvalonConfigError);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Thrown when an integration fails to load or has an invalid name
|
|
53
|
+
*
|
|
54
|
+
* This error includes the integration name and optionally the underlying cause,
|
|
55
|
+
* making it easier to diagnose integration loading issues.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* throw new IntegrationError(
|
|
60
|
+
* "Failed to activate integration. Is @useavalon/react installed?",
|
|
61
|
+
* "react",
|
|
62
|
+
* originalError
|
|
63
|
+
* );
|
|
64
|
+
* // Error: Integration 'react': Failed to activate integration. Is @useavalon/react installed?
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
export class IntegrationError extends Error {
|
|
68
|
+
/**
|
|
69
|
+
* The name of the integration that caused the error
|
|
70
|
+
*/
|
|
71
|
+
public readonly integrationName: string;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The original error that caused this error, if any
|
|
75
|
+
*/
|
|
76
|
+
public readonly originalCause?: Error;
|
|
77
|
+
|
|
78
|
+
constructor(message: string, integrationName: string, originalCause?: Error) {
|
|
79
|
+
super(`Integration '${integrationName}': ${message}`, { cause: originalCause });
|
|
80
|
+
this.name = "IntegrationError";
|
|
81
|
+
this.integrationName = integrationName;
|
|
82
|
+
this.originalCause = originalCause;
|
|
83
|
+
|
|
84
|
+
// Maintains proper stack trace for where error was thrown (V8 engines)
|
|
85
|
+
if (Error.captureStackTrace) {
|
|
86
|
+
Error.captureStackTrace(this, IntegrationError);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Format a value for display in error messages
|
|
93
|
+
*
|
|
94
|
+
* @param value - The value to format
|
|
95
|
+
* @returns A string representation of the value
|
|
96
|
+
*/
|
|
97
|
+
function formatValue(value: unknown): string {
|
|
98
|
+
if (value === undefined) {
|
|
99
|
+
return "undefined";
|
|
100
|
+
}
|
|
101
|
+
if (value === null) {
|
|
102
|
+
return "null";
|
|
103
|
+
}
|
|
104
|
+
if (typeof value === "string") {
|
|
105
|
+
return `"${value}"`;
|
|
106
|
+
}
|
|
107
|
+
if (typeof value === "function") {
|
|
108
|
+
return "[Function]";
|
|
109
|
+
}
|
|
110
|
+
if (Array.isArray(value)) {
|
|
111
|
+
if (value.length === 0) {
|
|
112
|
+
return "[]";
|
|
113
|
+
}
|
|
114
|
+
if (value.length <= 3) {
|
|
115
|
+
return `[${value.map(formatValue).join(", ")}]`;
|
|
116
|
+
}
|
|
117
|
+
return `[${value.slice(0, 3).map(formatValue).join(", ")}, ... (${value.length} items)]`;
|
|
118
|
+
}
|
|
119
|
+
if (typeof value === "object") {
|
|
120
|
+
try {
|
|
121
|
+
return JSON.stringify(value);
|
|
122
|
+
} catch {
|
|
123
|
+
return "[Circular]";
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return String(value as string | number | boolean | bigint | symbol);
|
|
127
|
+
}
|
|
@@ -1,156 +1,156 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Image Optimization Plugin for Avalon
|
|
3
|
-
*
|
|
4
|
-
* Wraps vite-imagetools to provide automatic image optimization with
|
|
5
|
-
* sensible defaults for responsive images, modern formats, and srcset generation.
|
|
6
|
-
*
|
|
7
|
-
* Features:
|
|
8
|
-
* - Automatic WebP/AVIF conversion
|
|
9
|
-
* - Responsive srcset generation at multiple breakpoints
|
|
10
|
-
* - Metadata stripping for privacy
|
|
11
|
-
* - JSX component output for easy usage in islands
|
|
12
|
-
*
|
|
13
|
-
* Usage in components:
|
|
14
|
-
* ```tsx
|
|
15
|
-
* // Basic - returns optimized URL
|
|
16
|
-
* import heroImg from './hero.jpg?w=800&format=webp';
|
|
17
|
-
*
|
|
18
|
-
* // With srcset for responsive images
|
|
19
|
-
* import heroImg from './hero.jpg?w=400;800;1200&format=webp&as=srcset';
|
|
20
|
-
*
|
|
21
|
-
* // As JSX component (like Qwik's ?jsx)
|
|
22
|
-
* import HeroImage from './hero.jpg?w=1200&jsx';
|
|
23
|
-
* <HeroImage alt="Hero" />
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import type { Plugin } from "vite";
|
|
28
|
-
import type { ResolvedImageConfig } from "./types.ts";
|
|
29
|
-
import { createRequire } from "node:module";
|
|
30
|
-
import { join } from "node:path";
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Creates the vite-imagetools plugin with Avalon's configuration
|
|
34
|
-
*/
|
|
35
|
-
export async function createImagePlugin(
|
|
36
|
-
config: ResolvedImageConfig,
|
|
37
|
-
verbose: boolean
|
|
38
|
-
): Promise<Plugin[]> {
|
|
39
|
-
if (!config.enabled) {
|
|
40
|
-
if (verbose) {
|
|
41
|
-
console.log(" ⏭️ Image optimization disabled");
|
|
42
|
-
}
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
try {
|
|
47
|
-
// Dynamic import to avoid hard dependency if user disables images.
|
|
48
|
-
// Use createRequire from the project root so we resolve the package
|
|
49
|
-
// from the consuming project's node_modules, not avalon's own context.
|
|
50
|
-
const require = createRequire(join(process.cwd(), 'package.json'));
|
|
51
|
-
const { imagetools } = require("vite-imagetools");
|
|
52
|
-
|
|
53
|
-
if (verbose) {
|
|
54
|
-
console.log(" 🖼️ Image optimization enabled");
|
|
55
|
-
console.log(` Format: ${config.defaultFormat}`);
|
|
56
|
-
console.log(` Quality: ${config.quality}`);
|
|
57
|
-
console.log(` Widths: ${config.widths.join(", ")}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const plugin = imagetools({
|
|
61
|
-
include: config.include,
|
|
62
|
-
exclude: config.exclude,
|
|
63
|
-
removeMetadata: config.removeMetadata,
|
|
64
|
-
|
|
65
|
-
// Default directives applied to all images unless overridden
|
|
66
|
-
defaultDirectives: (url) => {
|
|
67
|
-
const params = new URLSearchParams();
|
|
68
|
-
|
|
69
|
-
// Only apply defaults if no format specified
|
|
70
|
-
if (!url.searchParams.has("format")) {
|
|
71
|
-
params.set("format", config.defaultFormat);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Only apply quality if not specified
|
|
75
|
-
if (!url.searchParams.has("quality")) {
|
|
76
|
-
params.set("quality", String(config.quality));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// If ?jsx is used, generate responsive srcset like Qwik
|
|
80
|
-
if (url.searchParams.has("jsx")) {
|
|
81
|
-
// Generate widths for srcset if not specified
|
|
82
|
-
if (!url.searchParams.has("w") && !url.searchParams.has("width")) {
|
|
83
|
-
params.set("w", config.widths.join(";"));
|
|
84
|
-
}
|
|
85
|
-
params.set("as", "picture");
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return params;
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
return [plugin as Plugin];
|
|
93
|
-
} catch (error) {
|
|
94
|
-
// vite-imagetools not installed
|
|
95
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
96
|
-
|
|
97
|
-
if (message.includes("Cannot find package") || message.includes("MODULE_NOT_FOUND")) {
|
|
98
|
-
console.warn(
|
|
99
|
-
"⚠️ Avalon: Image optimization is enabled but vite-imagetools is not installed.\n" +
|
|
100
|
-
" Install it with: bun add -d vite-imagetools\n" +
|
|
101
|
-
" Or disable image optimization: image: false"
|
|
102
|
-
);
|
|
103
|
-
return [];
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
throw error;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Type declarations for image imports
|
|
112
|
-
* Users can reference these in their tsconfig.json
|
|
113
|
-
*/
|
|
114
|
-
export const IMAGE_TYPES_DECLARATION = `
|
|
115
|
-
declare module '*.jpg' {
|
|
116
|
-
const src: string;
|
|
117
|
-
export default src;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
declare module '*.jpeg' {
|
|
121
|
-
const src: string;
|
|
122
|
-
export default src;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
declare module '*.png' {
|
|
126
|
-
const src: string;
|
|
127
|
-
export default src;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
declare module '*.webp' {
|
|
131
|
-
const src: string;
|
|
132
|
-
export default src;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
declare module '*.avif' {
|
|
136
|
-
const src: string;
|
|
137
|
-
export default src;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
declare module '*.gif' {
|
|
141
|
-
const src: string;
|
|
142
|
-
export default src;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
declare module '*?jsx' {
|
|
146
|
-
import type { ComponentType } from 'preact';
|
|
147
|
-
const Component: ComponentType<{ alt: string; class?: string; style?: Record<string, string> }>;
|
|
148
|
-
export default Component;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
declare module '*&jsx' {
|
|
152
|
-
import type { ComponentType } from 'preact';
|
|
153
|
-
const Component: ComponentType<{ alt: string; class?: string; style?: Record<string, string> }>;
|
|
154
|
-
export default Component;
|
|
155
|
-
}
|
|
156
|
-
`;
|
|
1
|
+
/**
|
|
2
|
+
* Image Optimization Plugin for Avalon
|
|
3
|
+
*
|
|
4
|
+
* Wraps vite-imagetools to provide automatic image optimization with
|
|
5
|
+
* sensible defaults for responsive images, modern formats, and srcset generation.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Automatic WebP/AVIF conversion
|
|
9
|
+
* - Responsive srcset generation at multiple breakpoints
|
|
10
|
+
* - Metadata stripping for privacy
|
|
11
|
+
* - JSX component output for easy usage in islands
|
|
12
|
+
*
|
|
13
|
+
* Usage in components:
|
|
14
|
+
* ```tsx
|
|
15
|
+
* // Basic - returns optimized URL
|
|
16
|
+
* import heroImg from './hero.jpg?w=800&format=webp';
|
|
17
|
+
*
|
|
18
|
+
* // With srcset for responsive images
|
|
19
|
+
* import heroImg from './hero.jpg?w=400;800;1200&format=webp&as=srcset';
|
|
20
|
+
*
|
|
21
|
+
* // As JSX component (like Qwik's ?jsx)
|
|
22
|
+
* import HeroImage from './hero.jpg?w=1200&jsx';
|
|
23
|
+
* <HeroImage alt="Hero" />
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import type { Plugin } from "vite";
|
|
28
|
+
import type { ResolvedImageConfig } from "./types.ts";
|
|
29
|
+
import { createRequire } from "node:module";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Creates the vite-imagetools plugin with Avalon's configuration
|
|
34
|
+
*/
|
|
35
|
+
export async function createImagePlugin(
|
|
36
|
+
config: ResolvedImageConfig,
|
|
37
|
+
verbose: boolean
|
|
38
|
+
): Promise<Plugin[]> {
|
|
39
|
+
if (!config.enabled) {
|
|
40
|
+
if (verbose) {
|
|
41
|
+
console.log(" ⏭️ Image optimization disabled");
|
|
42
|
+
}
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
// Dynamic import to avoid hard dependency if user disables images.
|
|
48
|
+
// Use createRequire from the project root so we resolve the package
|
|
49
|
+
// from the consuming project's node_modules, not avalon's own context.
|
|
50
|
+
const require = createRequire(join(process.cwd(), 'package.json'));
|
|
51
|
+
const { imagetools } = require("vite-imagetools");
|
|
52
|
+
|
|
53
|
+
if (verbose) {
|
|
54
|
+
console.log(" 🖼️ Image optimization enabled");
|
|
55
|
+
console.log(` Format: ${config.defaultFormat}`);
|
|
56
|
+
console.log(` Quality: ${config.quality}`);
|
|
57
|
+
console.log(` Widths: ${config.widths.join(", ")}`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const plugin = imagetools({
|
|
61
|
+
include: config.include,
|
|
62
|
+
exclude: config.exclude,
|
|
63
|
+
removeMetadata: config.removeMetadata,
|
|
64
|
+
|
|
65
|
+
// Default directives applied to all images unless overridden
|
|
66
|
+
defaultDirectives: (url) => {
|
|
67
|
+
const params = new URLSearchParams();
|
|
68
|
+
|
|
69
|
+
// Only apply defaults if no format specified
|
|
70
|
+
if (!url.searchParams.has("format")) {
|
|
71
|
+
params.set("format", config.defaultFormat);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Only apply quality if not specified
|
|
75
|
+
if (!url.searchParams.has("quality")) {
|
|
76
|
+
params.set("quality", String(config.quality));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// If ?jsx is used, generate responsive srcset like Qwik
|
|
80
|
+
if (url.searchParams.has("jsx")) {
|
|
81
|
+
// Generate widths for srcset if not specified
|
|
82
|
+
if (!url.searchParams.has("w") && !url.searchParams.has("width")) {
|
|
83
|
+
params.set("w", config.widths.join(";"));
|
|
84
|
+
}
|
|
85
|
+
params.set("as", "picture");
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return params;
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return [plugin as Plugin];
|
|
93
|
+
} catch (error) {
|
|
94
|
+
// vite-imagetools not installed
|
|
95
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
96
|
+
|
|
97
|
+
if (message.includes("Cannot find package") || message.includes("MODULE_NOT_FOUND")) {
|
|
98
|
+
console.warn(
|
|
99
|
+
"⚠️ Avalon: Image optimization is enabled but vite-imagetools is not installed.\n" +
|
|
100
|
+
" Install it with: bun add -d vite-imagetools\n" +
|
|
101
|
+
" Or disable image optimization: image: false"
|
|
102
|
+
);
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
throw error;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Type declarations for image imports
|
|
112
|
+
* Users can reference these in their tsconfig.json
|
|
113
|
+
*/
|
|
114
|
+
export const IMAGE_TYPES_DECLARATION = `
|
|
115
|
+
declare module '*.jpg' {
|
|
116
|
+
const src: string;
|
|
117
|
+
export default src;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
declare module '*.jpeg' {
|
|
121
|
+
const src: string;
|
|
122
|
+
export default src;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
declare module '*.png' {
|
|
126
|
+
const src: string;
|
|
127
|
+
export default src;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
declare module '*.webp' {
|
|
131
|
+
const src: string;
|
|
132
|
+
export default src;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
declare module '*.avif' {
|
|
136
|
+
const src: string;
|
|
137
|
+
export default src;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
declare module '*.gif' {
|
|
141
|
+
const src: string;
|
|
142
|
+
export default src;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
declare module '*?jsx' {
|
|
146
|
+
import type { ComponentType } from 'preact';
|
|
147
|
+
const Component: ComponentType<{ alt: string; class?: string; style?: Record<string, string> }>;
|
|
148
|
+
export default Component;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
declare module '*&jsx' {
|
|
152
|
+
import type { ComponentType } from 'preact';
|
|
153
|
+
const Component: ComponentType<{ alt: string; class?: string; style?: Record<string, string> }>;
|
|
154
|
+
export default Component;
|
|
155
|
+
}
|
|
156
|
+
`;
|