@ecopages/image-processor 0.2.0-alpha.5 → 0.2.0-alpha.50

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 CHANGED
@@ -1,35 +1,38 @@
1
1
  # @ecopages/image-processor
2
2
 
3
- A powerful and flexible image processing library designed to optimize and manage responsive images in ecopages applications.
3
+ Image processing pipeline for responsive, optimized images in Ecopages.
4
+
5
+ It provides automatic image processing (e.g. converting and compressing to WebP) and virtual module integration, allowing you to import your optimized images safely and directly via `ecopages:images`.
4
6
 
5
7
  ## Features
6
8
 
7
- - **Automatic Image Optimization**: Converts and compresses images to modern formats
8
- - **Responsive Image Generation**: Creates multiple image variants for different screen sizes
9
- - **Virtual Module Integration**: Direct import of optimized images through `ecopages:images`
10
- - **TypeScript Support**: Full type definitions and auto-generated types
11
- - **Multiple Layout Options**: Supports fixed, constrained, and full-width layouts
12
- - **Ecopages Integration**: Seamlessly integrated with the ecopages framework
9
+ - **Automatic Image Optimization**: Converts and compresses images to modern formats at build time.
10
+ - **Responsive Image Generation**: Creates multiple variants for different screen sizes.
11
+ - **Virtual Module Integration**: Type-safe imports through `ecopages:images`.
12
+ - **Ecopages Components**: Ready-to-use HTML (`EcoImage`) and React (`EcoImage`) components.
13
+ - **Multiple Layout Options**: Fixed, constrained, and full-width layouts built-in.
13
14
 
14
15
  ## Installation
15
16
 
16
17
  ```bash
17
- npm install @ecopages/image-processor
18
+ bun add @ecopages/image-processor
18
19
  ```
19
20
 
20
21
  ## Configuration
21
22
 
23
+ Import and register the processor in your `eco.config.ts`:
24
+
22
25
  ```typescript
23
26
  import path from 'node:path';
24
- import { ConfigBuilder } from '@ecopages/core';
27
+ import { ConfigBuilder } from '@ecopages/core/config-builder';
25
28
  import { ImageProcessorPlugin } from '@ecopages/image-processor';
26
29
 
27
30
  const imageProcessor = new ImageProcessorPlugin({
28
31
  name: 'ecopages-image-processor',
29
32
  type: 'image',
30
33
  options: {
31
- sourceDir: path.resolve(import.meta.dir, 'src/images'),
32
- outputDir: path.resolve(import.meta.dir, '.eco/images'),
34
+ sourceDir: path.resolve(import.meta.dirname, 'src/images'),
35
+ outputDir: path.resolve(import.meta.dirname, 'dist/images'),
33
36
  publicPath: '/images',
34
37
  acceptedFormats: ['jpg', 'jpeg', 'png', 'webp'],
35
38
  quality: 80,
@@ -44,175 +47,62 @@ const imageProcessor = new ImageProcessorPlugin({
44
47
  });
45
48
 
46
49
  export default await new ConfigBuilder()
47
- .setRootDir(import.meta.dir)
50
+ .setRootDir(import.meta.dirname)
48
51
  .setBaseUrl(import.meta.env.ECOPAGES_BASE_URL)
49
52
  .setProcessors([imageProcessor])
50
53
  .build();
51
54
  ```
52
55
 
53
- ### Configuration Options
54
-
55
- #### ImageProcessorConfig
56
-
57
- | Option | Type | Default | Description |
58
- | ----------------- | --------------------------------------- | ----------------------------- | ------------------------------------- |
59
- | `sourceDir` | `string` | `'/src/public/assets/images'` | Source directory for images |
60
- | `outputDir` | `string` | `'/dist/assets/optimized'` | Output directory for processed images |
61
- | `publicPath` | `string` | `'/assets/optimized'` | Public URL path for images |
62
- | `sizes` | `Array<{width: number, label: string}>` | `[]` | Image variants configuration |
63
- | `quality` | `number` | `80` | Output image quality (0-100) |
64
- | `format` | `'webp' \| 'jpeg' \| 'png' \| 'avif'` | `'webp'` | Output image format |
65
- | `acceptedFormats` | `string[]` | `['jpg','jpeg','png','webp']` | Accepted input formats |
66
-
67
56
  ## Usage
68
57
 
69
58
  ### Virtual Module System
70
59
 
71
- The `ecopages:images` virtual module provides a unified, type-safe way to handle images across your project:
60
+ The `ecopages:images` virtual module provides a type-safe way to import processed images:
72
61
 
73
62
  ```typescript
74
- // All images from your source directory are available as named exports
75
- import { heroImage, profilePicture, blogThumbnail } from 'ecopages:images';
76
-
77
- // Names are automatically converted to camelCase
78
- // example:
79
- // src/images/hero-image.jpg -> heroImage
80
- // src/images/profile_picture.png -> profilePicture
63
+ // Imports from your source directory are resolved automatically and camelCased
64
+ import { heroImage, profilePicture } from 'ecopages:images';
81
65
  ```
82
66
 
83
- No manual `dependencies.modules` declaration is required for `ecopages:images`; imports are automatically detected and included in the client bundle.
84
-
85
- #### Benefits:
86
-
87
- - **TypeScript Integration**: Full autocompletion support for image names
88
- - **Automatic Processing**: Images are processed at build time
89
- - **Tree Shaking**: Only imported images and their required metadata are included in the final bundle
90
- - **Type Safety**: Prevents imports of non-existent images
91
- - **Unified API**: Consistent way to handle images across your project
67
+ > [!TIP]
68
+ > **No manual dependencies required.**
69
+ > Ecopages automatically detects these virtual module imports and processes them during the build, enabling effective tree-shaking for only the required images.
92
70
 
93
- ### Importing Images
71
+ ### Components
94
72
 
95
- Images are available through the virtual module `ecopages:images`:
96
-
97
- ```typescript
98
- import { myImage } from 'ecopages:images';
99
-
100
- // myImage contains:
101
- // {
102
- // attributes: {
103
- // src: string,
104
- // width: number, // original image width
105
- // height: number, // original image height
106
- // sizes: string,
107
- // srcset: string
108
- // },
109
- // variants: Array<{ width, height, src, label }>
110
- // }
111
- ```
73
+ The plugin provides ready-to-use components for HTML (`@kitajs/html`) and React:
112
74
 
113
- ### HTML Component
75
+ **HTML Component:**
114
76
 
115
77
  ```typescript
116
78
  import { EcoImage } from '@ecopages/image-processor/component/html';
117
79
 
118
- // Basic usage
119
80
  EcoImage({
120
- ...myImage,
121
- width: 800,
122
- height: 600,
123
- alt: 'My image',
124
- });
125
-
126
- // Advanced usage
127
- EcoImage({
128
- ...myImage,
129
- alt: 'My image',
81
+ ...heroImage,
130
82
  layout: 'constrained',
83
+ alt: 'Hero banner',
131
84
  priority: true,
132
- aspectRatio: '16/9',
133
- staticVariant: 'xl',
134
85
  });
135
86
  ```
136
87
 
137
- ### React Component
88
+ **React Component:**
138
89
 
139
90
  ```jsx
140
- import { EcoImage } from "@ecopages/image-processor/component/react";
141
-
142
- // Basic usage
143
- <EcoImage
144
- {...myImage}
145
- alt="My image"
146
- />
147
-
148
- // Advanced usage
149
- <EcoImage
150
- {...myImage}
151
- alt="My image"
152
- layout="constrained"
153
- priority
154
- aspectRatio="16/9"
155
- staticVariant="xl"
156
- />
157
- ```
158
-
159
- ### Component Props
160
-
161
- The component accepts all standard HTML/React image attributes (`src`, `alt`, `data-*`, `crossOrigin`, etc.) in addition to the following specific props:
162
-
163
- | Prop | Type | Default | Description |
164
- | --------------- | ------------------------------------------ | ------------------- | -------------------------------------------- |
165
- | `width` | `number` | From image metadata | Original width, can be overridden if needed |
166
- | `height` | `number` | From image metadata | Original height, can be overridden if needed |
167
- | `priority` | `boolean` | `false` | Prioritize loading |
168
- | `layout` | `'fixed' \| 'constrained' \| 'full-width'` | `'constrained'` | Layout behavior |
169
- | `staticVariant` | `string` | - | Force specific size variant |
170
- | `aspectRatio` | `string` | From width/height | Override the natural aspect ratio |
171
- | `unstyled` | `boolean` | `false` | Disable default styling |
172
-
173
- Note: Images imported through `ecopages:images` automatically include their width and height metadata, preventing layout shifts by default. These values can be overridden when needed, for example when using a different aspect ratio or specific layout requirements.
91
+ import { EcoImage } from '@ecopages/image-processor/component/react';
174
92
 
175
- ## Layout Modes
176
-
177
- ### Fixed Layout
178
-
179
- ```typescript
180
- EcoImage({
181
- ...myImage,
182
- layout: 'fixed',
183
- width: 400,
184
- height: 300,
185
- alt: 'Fixed image',
186
- });
93
+ <EcoImage {...heroImage} alt="Hero banner" layout="constrained" priority />;
187
94
  ```
188
95
 
189
- ### Constrained Layout
190
-
191
- ```typescript
192
- EcoImage({
193
- ...myImage,
194
- layout: 'constrained',
195
- width: 800,
196
- alt: 'Constrained image',
197
- });
198
- ```
199
-
200
- ### Full-Width Layout
201
-
202
- ```typescript
203
- EcoImage({
204
- ...myImage,
205
- layout: 'full-width',
206
- alt: 'Full-width image',
207
- });
208
- ```
209
-
210
- ## Best Practices
96
+ ### Component Props
211
97
 
212
- 1. Always provide `alt` text for accessibility
213
- 2. Use `priority` for above-the-fold images
214
- 3. Always specify both `width` and `height` to prevent layout shifts
215
- 4. Use `aspectRatio` only when you need to force a different aspect ratio than width/height
216
- 5. Choose appropriate `layout` modes based on your needs
217
- 6. Utilize the virtual module for type-safe image imports
218
- 7. Configure size variants that match your breakpoints
98
+ The components accept standard HTML/React attributes plus these specifics:
99
+
100
+ | Prop | Type | Default |
101
+ | :-------------- | :----------------------------------------- | :-------------- |
102
+ | `layout` | `'fixed' \| 'constrained' \| 'full-width'` | `'constrained'` |
103
+ | `priority` | `boolean` | `false` |
104
+ | `width` | `number` | From metadata |
105
+ | `height` | `number` | From metadata |
106
+ | `aspectRatio` | `string` | Natural ratio |
107
+ | `staticVariant` | `string` | - |
108
+ | `unstyled` | `boolean` | `false` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecopages/image-processor",
3
- "version": "0.2.0-alpha.5",
3
+ "version": "0.2.0-alpha.50",
4
4
  "description": "Image processor, transform and optimize images for web",
5
5
  "keywords": [
6
6
  "image",
@@ -15,14 +15,14 @@
15
15
  "directory": "packages/processors/image-processor"
16
16
  },
17
17
  "peerDependencies": {
18
- "@ecopages/core": "0.2.0-alpha.5"
18
+ "@ecopages/core": "0.2.0-alpha.50"
19
19
  },
20
20
  "dependencies": {
21
- "@ecopages/file-system": "0.2.0-alpha.5",
22
- "@ecopages/logger": "latest",
23
- "react": "^19",
24
- "react-dom": "^19",
25
- "sharp": "^0.33.5"
21
+ "@ecopages/file-system": "0.2.0-alpha.50",
22
+ "@ecopages/logger": "^0.2.3",
23
+ "react": "^19.2.6",
24
+ "react-dom": "^19.2.6",
25
+ "sharp": "^0.34.5"
26
26
  },
27
27
  "exports": {
28
28
  ".": {
@@ -2,8 +2,8 @@
2
2
  * This file contains the plugins for bundling the image specifications.
3
3
  * @module @ecopages/image-processor/bun-plugins
4
4
  */
5
- import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
6
- import type { ImageMap } from './plugin';
5
+ import type { EcoBuildPlugin } from '@ecopages/core/plugins/processor';
6
+ import type { ImageMap } from './plugin.js';
7
7
  /**
8
8
  * This function creates a plugin for bundling the image specifications.
9
9
  * https://bun.sh/docs/runtime/plugins#virtual-modules
@@ -1,4 +1,4 @@
1
- import { anyCaseToCamelCase } from "./utils";
1
+ import { anyCaseToCamelCase } from "./utils.js";
2
2
  function createPluginResult(exports) {
3
3
  return {
4
4
  contents: `${Object.entries(exports).map(([key, value]) => `export const ${anyCaseToCamelCase(key)} = ${JSON.stringify(value)};`).join("\n")}`,
@@ -2,7 +2,7 @@
2
2
  * Image component that renders the image as a string.
3
3
  * @module @ecopages/image-processor/component/html
4
4
  */
5
- import { type EcoImageProps } from '../image-renderer';
5
+ import { type EcoImageProps } from '../image-renderer.js';
6
6
  /**
7
7
  * EcoImage
8
8
  * This component generates the image element based on the provided props as a string
@@ -1,4 +1,4 @@
1
- import { renderer } from "../image-renderer";
1
+ import { renderer } from "../image-renderer.js";
2
2
  const EcoImage = (props) => {
3
3
  return renderer.renderToString(props);
4
4
  };
@@ -3,7 +3,7 @@
3
3
  * @module @ecopages/image-processor/component/react
4
4
  */
5
5
  import { type JSX } from 'react';
6
- import { type EcoImageProps } from '../image-renderer';
6
+ import { type EcoImageProps } from '../image-renderer.js';
7
7
  /**
8
8
  * EcoImage
9
9
  * This component generates the image element based on the provided props as JSX
@@ -1,5 +1,5 @@
1
1
  import { createElement } from "react";
2
- import { renderer } from "../image-renderer";
2
+ import { renderer } from "../image-renderer.js";
3
3
  const EcoImage = (props) => {
4
4
  return createElement("img", {
5
5
  ...renderer.generateAttributesJsx(props),
@@ -1,4 +1,4 @@
1
- import type { ImageLayout } from './image-renderer';
1
+ import type { ImageLayout } from './image-renderer.js';
2
2
  /**
3
3
  * Default image layout
4
4
  * @constant "constrained"
@@ -2,8 +2,8 @@
2
2
  * This file contains the plugins for bundling the image specifications.
3
3
  * @module @ecopages/image-processor/image-plugins
4
4
  */
5
- import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
6
- import type { ImageMap } from './plugin';
5
+ import type { EcoBuildPlugin } from '@ecopages/core/plugins/processor';
6
+ import type { ImageMap } from './plugin.js';
7
7
  /**
8
8
  * This function creates a plugin for bundling the image specifications.
9
9
  * https://bun.sh/docs/runtime/plugins#virtual-modules
@@ -1,4 +1,4 @@
1
- import { anyCaseToCamelCase } from "./utils";
1
+ import { anyCaseToCamelCase } from "./utils.js";
2
2
  function createPluginResult(exports) {
3
3
  return {
4
4
  contents: `${Object.entries(exports).map(([key, value]) => `export const ${anyCaseToCamelCase(key)} = ${JSON.stringify(value)};`).join("\n")}`,
@@ -1,5 +1,5 @@
1
- import type { ImageMap, ImageProcessorConfig } from './plugin';
2
- import type { ImageSpecifications } from './types';
1
+ import type { ImageMap, ImageProcessorConfig } from './plugin.js';
2
+ import type { ImageSpecifications } from './types.js';
3
3
  /**
4
4
  * ImageProcessor
5
5
  * This is the core class for processing images.
@@ -1,9 +1,9 @@
1
1
  import path from "node:path";
2
- import { deepMerge } from "@ecopages/core/utils/deep-merge";
2
+ import { mergeProcessorOptions } from "@ecopages/core/plugins/processor";
3
3
  import { fileSystem } from "@ecopages/file-system";
4
4
  import { Logger } from "@ecopages/logger";
5
5
  import sharp from "sharp";
6
- import { ImageUtils } from "./image-utils";
6
+ import { ImageUtils } from "./image-utils.js";
7
7
  const appLogger = new Logger("[@ecopages/image-processor]", {
8
8
  debug: process.env.ECOPAGES_LOGGER_DEBUG === "true"
9
9
  });
@@ -11,7 +11,7 @@ class ImageProcessor {
11
11
  config;
12
12
  cacheManager;
13
13
  constructor(config, cacheManager) {
14
- this.config = deepMerge({ cacheEnabled: true }, config);
14
+ this.config = mergeProcessorOptions({ cacheEnabled: true }, config);
15
15
  this.cacheManager = cacheManager;
16
16
  fileSystem.ensureDir(this.config.outputDir);
17
17
  }
@@ -2,7 +2,7 @@
2
2
  * ImageRenderer
3
3
  * @module
4
4
  */
5
- import type { ImageSpecifications } from './types';
5
+ import type { ImageSpecifications } from './types.js';
6
6
  /**
7
7
  * Image layout options
8
8
  */
@@ -1,5 +1,5 @@
1
- import { DEFAULT_LAYOUT } from "./constants";
2
- import { ImageUtils } from "./image-utils";
1
+ import { DEFAULT_LAYOUT } from "./constants.js";
2
+ import { ImageUtils } from "./image-utils.js";
3
3
  class LayoutAttributesManager {
4
4
  static shouldIncludeWidthHeight(layout) {
5
5
  return layout === "fixed";
@@ -1,4 +1,4 @@
1
- import type { EcoImageProps } from './image-renderer';
1
+ import type { EcoImageProps } from './image-renderer.js';
2
2
  /**
3
3
  * ImageUtils
4
4
  * This class contains utility methods for working with images
@@ -7,7 +7,7 @@ import type { EcoImageProps } from './image-renderer';
7
7
  export declare class ImageUtils {
8
8
  private static readonly BREAKPOINTS;
9
9
  private static readonly VIEWPORT_SIZES;
10
- static readonly DEFAULT_LAYOUT: import("./image-renderer").ImageLayout;
10
+ static readonly DEFAULT_LAYOUT: import("./image-renderer.js").ImageLayout;
11
11
  /**
12
12
  * Generates a srcset string from processed image variants using relative paths
13
13
  * @param {ImageVariant[]} variants - Array of processed image variants
@@ -1,4 +1,4 @@
1
- import { DEFAULT_LAYOUT } from "./constants";
1
+ import { DEFAULT_LAYOUT } from "./constants.js";
2
2
  class ImageUtils {
3
3
  static BREAKPOINTS = {
4
4
  desktop: 1024,
package/src/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export * from './image-processor';
2
- export * from './plugin';
3
- export * from './types';
1
+ export * from './image-processor.js';
2
+ export * from './plugin.js';
3
+ export * from './types.js';
package/src/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export * from "./image-processor";
2
- export * from "./plugin";
3
- export * from "./types";
1
+ export * from "./image-processor.js";
2
+ export * from "./plugin.js";
3
+ export * from "./types.js";
package/src/plugin.d.ts CHANGED
@@ -2,10 +2,9 @@
2
2
  * ImageProcessorPlugin
3
3
  * @module @ecopages/image-processor
4
4
  */
5
- import { Processor, type ProcessorConfig } from '@ecopages/core/plugins/processor';
6
- import type { EcoBuildPlugin } from '@ecopages/core/build/build-types';
7
- import { ImageProcessor } from './image-processor';
8
- import type { ImageSize, ImageSpecifications } from './types';
5
+ import { Processor, type EcoBuildPlugin, type ProcessorConfig } from '@ecopages/core/plugins/processor';
6
+ import { ImageProcessor } from './image-processor.js';
7
+ import type { ImageSize, ImageSpecifications } from './types.js';
9
8
  /**
10
9
  * Configuration for the image processor
11
10
  */
@@ -40,10 +39,29 @@ export type ImageMap = Record<string, ImageSpecifications>;
40
39
  */
41
40
  export declare class ImageProcessorPlugin extends Processor<ImageProcessorConfig> {
42
41
  private processor;
42
+ private buildContributionsPrepared;
43
+ private resolvedConfig?;
43
44
  processedImages: Record<string, ImageSpecifications>;
44
45
  constructor(config: Omit<ProcessorConfig<ImageProcessorConfig>, 'name' | 'description'>);
45
46
  get buildPlugins(): EcoBuildPlugin[];
46
47
  get plugins(): EcoBuildPlugin[];
48
+ /**
49
+ * Replaces image-map contents without swapping the backing object.
50
+ *
51
+ * @remarks
52
+ * The build/runtime virtual-module plugins close over `processedImages`, so
53
+ * mutating the existing object keeps those plugins live after preparation.
54
+ */
55
+ private replaceProcessedImages;
56
+ /**
57
+ * Prepares the image virtual-module state before config build seals the app
58
+ * manifest.
59
+ */
60
+ prepareBuildContributions(): Promise<void>;
61
+ private getRuntimeVirtualModulePath;
62
+ private getGeneratedOutputPath;
63
+ private hasGeneratedOutputs;
64
+ private rehydrateGeneratedOutputs;
47
65
  /**
48
66
  * Generate dependencies for processor.
49
67
  * It is ossible to define which one should be included in the final bundle based on the environment.
@@ -51,7 +69,7 @@ export declare class ImageProcessorPlugin extends Processor<ImageProcessorConfig
51
69
  */
52
70
  private generateDependencies;
53
71
  /**
54
- * Setup the image processor and create the virtual module.
72
+ * Prepares build contributions if not already done and rehydrates previously generated image outputs.
55
73
  */
56
74
  setup(): Promise<void>;
57
75
  /**