generated from polymech/site-template
Synced from site
This commit is contained in:
parent
3e13363c6c
commit
6541d83fba
446
.astro/collections/projects.schema.json
Normal file
446
.astro/collections/projects.schema.json
Normal file
@ -0,0 +1,446 @@
|
|||||||
|
{
|
||||||
|
"$ref": "#/definitions/projects",
|
||||||
|
"definitions": {
|
||||||
|
"projects": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"cart_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"price": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"cscartCats": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cscartId": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"vendorId": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"shipping": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"price": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"transit": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"handling": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"default": {
|
||||||
|
"price": 0,
|
||||||
|
"transit": 12,
|
||||||
|
"handling": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"manufacturing": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"lead_time": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"replaced_by": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"alternatives": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"url",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"used_by": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/projects/properties/alternatives/items"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"thumb": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"responsive": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"meta": {},
|
||||||
|
"keywords": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"alt": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"height": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"width": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"order": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"exif": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": {},
|
||||||
|
"jfif": {},
|
||||||
|
"exif": {},
|
||||||
|
"gps": {}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"flags": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"edrawings": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cad": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"configuration": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"step": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"model": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"html": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"file",
|
||||||
|
"name",
|
||||||
|
"configuration"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"showDimensions": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"showParts": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"slug": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"keywords": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"meta_keywords": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"versions": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"version": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"up": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"down": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"family": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sheet": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"authors": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"url"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assets": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"gallery": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/projects/properties/image"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"renderings": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/projects/properties/image"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/projects/properties/image"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/projects/properties/image"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"showcase": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/projects/properties/image"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"samples": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/projects/properties/image"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false,
|
||||||
|
"default": {
|
||||||
|
"gallery": [],
|
||||||
|
"renderings": [],
|
||||||
|
"components": [],
|
||||||
|
"configurations": [],
|
||||||
|
"showcase": [],
|
||||||
|
"samples": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"tests": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/projects/properties/alternatives/items"
|
||||||
|
},
|
||||||
|
"default": []
|
||||||
|
},
|
||||||
|
"download": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"gallery": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"glob": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"glob"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"default": {
|
||||||
|
"renderings": {
|
||||||
|
"glob": [
|
||||||
|
"*.+(JPG|jpg|png|PNG|gif)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"gallery": {
|
||||||
|
"glob": [
|
||||||
|
"*.+(JPG|jpg|png|PNG|gif)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"glob": [
|
||||||
|
"*.+(JPG|jpg|png|PNG|gif)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"configurations": {
|
||||||
|
"glob": [
|
||||||
|
"*.+(JPG|jpg|png|PNG|gif)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"showcase": {
|
||||||
|
"glob": [
|
||||||
|
"*.+(JPG|jpg|png|PNG|gif)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"samples": {
|
||||||
|
"glob": [
|
||||||
|
"*.+(JPG|jpg|png|PNG|gif)"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Preview3d": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"howto_categories": {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"steps": {},
|
||||||
|
"sourceLanguage": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"category": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"product_dimensions": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"production": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fusion-folder": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"nc-folder": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cam": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/projects/properties/authors/items"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"fusion-folder",
|
||||||
|
"nc-folder",
|
||||||
|
"cam"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"score": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"highlights": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"specs": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"readme": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"shared": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"code",
|
||||||
|
"manufacturing",
|
||||||
|
"name",
|
||||||
|
"slug",
|
||||||
|
"category"
|
||||||
|
],
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#"
|
||||||
|
}
|
||||||
@ -296,11 +296,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resources": {
|
"resources": {
|
||||||
"type": "array",
|
"type": "string"
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/store/properties/alternatives/items"
|
|
||||||
},
|
|
||||||
"default": []
|
|
||||||
},
|
},
|
||||||
"tests": {
|
"tests": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@ -413,6 +409,27 @@
|
|||||||
},
|
},
|
||||||
"score": {
|
"score": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
|
},
|
||||||
|
"body": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"features": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"highlights": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"specs": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"license": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"readme": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"shared": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"_variables": {
|
"_variables": {
|
||||||
"lastUpdateCheck": 1740681917455
|
"lastUpdateCheck": 1741718719942
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
255
README.md
255
README.md
@ -1,111 +1,144 @@
|
|||||||
# Williamsburg
|
# Astro Site Documentation
|
||||||
|
|
||||||
## Template Integrations
|
This README provides comprehensive documentation for this Astro-based website project.
|
||||||
- Tailwind CSS v4
|
|
||||||
- Astro SEO - Powered by [@astrolib/seo](https://github.com/onwidget/astrolib/tree/main/packages/seo)
|
## 🚀 Project Overview
|
||||||
- Astro Sitemap - https://docs.astro.build/en/guides/integrations-guide/sitemap/
|
|
||||||
|
This is a multilingual website built with [Astro](https://astro.build), a modern static site generator. The site features a blog, content collections, and internationalization support. It uses Tailwind CSS for styling and includes various components and layouts for consistent design.
|
||||||
## Template Structure
|
|
||||||
|
## 📁 Project Structure
|
||||||
The template follows a typical Astro project structure. You'll find the following key directories and files:
|
|
||||||
|
```
|
||||||
|
/
|
||||||
```
|
├── public/ # Static assets
|
||||||
/
|
├── src/
|
||||||
├── public/
|
│ ├── components/ # UI components
|
||||||
├── src/
|
│ ├── content/ # Content collections
|
||||||
│ └── pages/
|
│ ├── layouts/ # Page layouts
|
||||||
│ └── index.astro
|
│ ├── pages/ # Page routes
|
||||||
└── package.json
|
│ │ ├── en/ # English pages
|
||||||
```
|
│ │ └── [lang]/ # Other language pages
|
||||||
|
│ ├── i18n/ # Internationalization files
|
||||||
- `src/pages/`: Contains `.astro` and `.md` files. Each file becomes a route in your project based on its name.
|
│ └── content.config.ts # Content collection configuration
|
||||||
- `src/components/`: Ideal for placing your Astro/React/Vue/Svelte/Preact components.
|
├── astro.config.mjs # Astro configuration
|
||||||
- `public/`: For static assets such as images that you want to serve directly.
|
├── tailwind.config.cjs # Tailwind CSS configuration
|
||||||
|
└── package.json # Project dependencies
|
||||||
## Commands
|
```
|
||||||
|
|
||||||
All commands are run from the root of the project, from a terminal:
|
## 📚 Content Collections
|
||||||
|
|
||||||
| Command | Action |
|
The site utilizes Astro's content collections to manage structured content. Collections are defined in `src/content.config.ts` and include:
|
||||||
| :--------------------- | :----------------------------------------------- |
|
|
||||||
| `npm install` | Installs dependencies |
|
- **Blog Posts**: Articles with frontmatter for metadata like title, date, author, etc.
|
||||||
| `npm run dev` | Starts local dev server at `localhost:3000` |
|
- **Authors**: Information about content authors.
|
||||||
| `npm run build` | Build your production site to `./dist/` |
|
|
||||||
| `npm run preview` | Preview your build locally, before deploying |
|
Collections are stored in the `src/content/` directory, organized by language:
|
||||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
- `src/content/blog/en/` - English blog posts
|
||||||
| `npm run astro --help` | Get help using the Astro CLI |
|
- `src/content/blog/[lang]/` - Blog posts in other languages
|
||||||
|
|
||||||
Learn more - Explore more through Astro's official [documentation](https://docs.astro.build).
|
## 🧩 Components
|
||||||
|
|
||||||
------
|
The site includes reusable UI components located in `src/components/`:
|
||||||
Updated on 30th December 2024
|
|
||||||
|
- **Core Components**: Basic UI elements like buttons, cards, and navigation
|
||||||
## This update includes:
|
- **Blog Components**: Specialized components for the blog section
|
||||||
- Add Tailwind CSS v4 Beta
|
- **SEO Components**: Components for SEO optimization
|
||||||
On this version, Tailwind CSS is now beta version from Tailwind CSS V4, this means that there's no `tailwind.config.mjs` file anymore. From now on, all style will be added on the `css` file. You can find the styles on the `src/styles/global.css` file.
|
- **UI Components**: Interface elements like headers, footers, and menus
|
||||||
- Astro V5
|
|
||||||
This update includes Astro V5, which is a major update that includes several new features and improvements.
|
These components help maintain consistent design across the site while making development more efficient.
|
||||||
|
|
||||||
|
## 📏 Layouts
|
||||||
- Astro SEO by @astrolib/seo
|
|
||||||
This update includes the integration of the Astro SEO package by @astrolib/seo, is an integration that makes managing your SEO easier in Astro projects. It is fully based on the excellent Next SEO library
|
Layouts in `src/layouts/` provide consistent page structures:
|
||||||
|
|
||||||
## On the next update
|
- **BaseLayout.astro**: The main layout with common elements (header, footer)
|
||||||
|
- **BlogLayout.astro**: Specialized layout for blog posts
|
||||||
- Add Image component from Astro
|
- **PostLayout.astro**: Layout for individual blog posts
|
||||||
The Astro Image component is coming back to the themes
|
- **AboutLayout.astro**: Layout for about pages
|
||||||
|
|
||||||
- Reusable components
|
Layouts wrap content with consistent HTML structure, styling, and components.
|
||||||
This template now includes reusable components, such as the `Text` component:
|
|
||||||
|
## 🌐 Internationalization (i18n)
|
||||||
- Text Component
|
|
||||||
A versatile and reusable component for handling text across your project, ensuring consistency and easy customization.
|
The site supports multiple languages through a built-in internationalization system:
|
||||||
|
|
||||||
- **HTML Tags:** Easily change the HTML element (like `p`, `h1`, `span`, `a`) using the `tag` prop, with `p` being the default.
|
- Default language is English (`en`)
|
||||||
- **Variants:** Pick from preset text styles (such as `displayXL` or `textBase`) for a consistent look.
|
- Content is organized by language in directory structures
|
||||||
- **Custom Classes:** Add or adjust styles with the `class` prop.
|
- URL format follows `/[lang]/[route]` pattern
|
||||||
- **Accessibility:** Customize with additional props like `id`, `href`, `title`, and `style`.
|
- Translation files are stored in the `src/i18n/` directory
|
||||||
- **Content Slot:** Add any content inside the component, including optional left and right icons.
|
|
||||||
Example usage:
|
Language switching is available to visitors, with localized content and UI elements.
|
||||||
```astro
|
|
||||||
<Text tag="h1" variant="displayXL" class="text-center">
|
## 📦 Dependencies
|
||||||
Welcome to the new version!
|
|
||||||
</Text>
|
The project relies on the following main dependencies:
|
||||||
```
|
|
||||||
|
- **Astro**: Core framework for building the site
|
||||||
- Button Component
|
- **Tailwind CSS**: Utility-first CSS framework for styling
|
||||||
A customizable button component with options to fit your design needs:
|
- **@astrojs/tailwind**: Tailwind CSS integration for Astro
|
||||||
|
- **@astrojs/mdx**: MDX integration for enhanced markdown
|
||||||
- **Variants:** Choose from predefined styles like `primary` (dark background) and `secondary` (lighter background), with support for dark mode.
|
- **@astrojs/sitemap**: Automatic sitemap generation
|
||||||
- **Sizes:** Select `small` or `medium` for different button heights and padding.
|
- **@astrojs/image**: Image optimization tools
|
||||||
- **Gaps:** Control the spacing between content with the `gapSize` prop (either `small` or `medium`).
|
|
||||||
- **Custom Classes:** Apply additional styles using the `class` prop.
|
Additional dependencies may include utility libraries and development tools.
|
||||||
- **Slots:** Include icons or extra content with optional `left-icon` and `right-icon` slots.
|
|
||||||
Example usage:
|
## 🛠️ Development Instructions
|
||||||
```astro
|
|
||||||
<Button size="small" variant="primary">Primary small</Button>
|
### Prerequisites
|
||||||
```
|
|
||||||
|
- Node.js (v16 or later)
|
||||||
- Wrapper Component
|
- npm or yarn
|
||||||
A flexible layout component that helps with consistent spacing and alignment.
|
|
||||||
|
### Getting Started
|
||||||
- **Variants:** The default `standard` variant includes responsive widths, centered content, and padding.
|
|
||||||
- **Custom Classes:** Add or change styles with the `class` prop.
|
1. Clone the repository:
|
||||||
- **Content Slot:** Easily add any child components or content inside.
|
```bash
|
||||||
|
git clone <repository-url>
|
||||||
```astro
|
cd <project-directory>
|
||||||
<Wrapper variant="standard">
|
```
|
||||||
Your content goes here
|
|
||||||
</Wrapper>
|
2. Install dependencies:
|
||||||
```
|
```bash
|
||||||
-----
|
npm install
|
||||||
|
# or
|
||||||
### [Support](https://lexingtonthemes.com/legal/support/)
|
yarn install
|
||||||
### [Documentation](https://lexingtonthemes.com/documentation/)
|
```
|
||||||
### [Get your bundle](https://lexingtonthemes.com)
|
|
||||||
|
3. Start the development server:
|
||||||
|
```bash
|
||||||
### References
|
npm run dev
|
||||||
|
# or
|
||||||
-[PWA](https://vite-pwa-org.netlify.app/)
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Open your browser and visit `http://localhost:3000`
|
||||||
|
|
||||||
|
### Building for Production
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
# or
|
||||||
|
yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
The build output will be in the `dist/` directory.
|
||||||
|
|
||||||
|
### Additional Commands
|
||||||
|
|
||||||
|
- **Preview build**: `npm run preview`
|
||||||
|
- **Check code**: `npm run astro check`
|
||||||
|
- **Lint code**: `npm run lint` (if configured)
|
||||||
|
|
||||||
|
## 📝 Contributing
|
||||||
|
|
||||||
|
When contributing to this project, please follow these guidelines:
|
||||||
|
|
||||||
|
1. Create components in the appropriate directories
|
||||||
|
2. Follow the existing code style and naming conventions
|
||||||
|
3. Add proper documentation for new features
|
||||||
|
4. Test your changes before submitting
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
|
||||||
|
[Add your license information here]
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
import { defineConfig } from "imagetools/config"
|
import { defineConfig } from "imagetools/config"
|
||||||
// https://astro-imagetools-docs.vercel.app/en/global-config-options/
|
// https://astro-imagetools-docs.vercel.app/en/global-config-options/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
placeholder: "blurred",
|
placeholder: "blurred",
|
||||||
format: ["webp", "avif", "jpg"],
|
format: ["webp", "avif", "jpg"],
|
||||||
fallbackFormat: "jpg",
|
fallbackFormat: "jpg",
|
||||||
delay:250,
|
delay:250,
|
||||||
includeSourceFormat: false,
|
includeSourceFormat: false,
|
||||||
formatOptions: {
|
formatOptions: {
|
||||||
jpg: {
|
jpg: {
|
||||||
quality: 80,
|
quality: 80,
|
||||||
},
|
},
|
||||||
png: {
|
png: {
|
||||||
quality: 80,
|
quality: 80,
|
||||||
},
|
},
|
||||||
webp: {
|
webp: {
|
||||||
quality: 50,
|
quality: 50,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -3,24 +3,37 @@ import tailwindcss from '@tailwindcss/vite'
|
|||||||
import { imagetools } from "imagetools"
|
import { imagetools } from "imagetools"
|
||||||
import react from "@astrojs/react"
|
import react from "@astrojs/react"
|
||||||
import mdx from "@astrojs/mdx";
|
import mdx from "@astrojs/mdx";
|
||||||
|
import { defineConfig } from 'astro/config';
|
||||||
|
import compress from 'vite-plugin-compression';
|
||||||
|
import sitemap from "@astrojs/sitemap";
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
site: 'https://polymech.io',
|
||||||
devToolbar: {
|
devToolbar: {
|
||||||
enabled: true,
|
enabled: false,
|
||||||
},
|
},
|
||||||
i18n: {
|
i18n: {
|
||||||
locales: ["es", "en", "de", "fr", "it", "ar", "ja", "zh"],
|
locales: ["es", "en", "de", "fr", "it", "ar", "ja", "zh", "nl", "it", "pt"],
|
||||||
defaultLocale: "en",
|
defaultLocale: "en",
|
||||||
},
|
},
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [
|
plugins: [
|
||||||
tailwindcss()
|
tailwindcss({
|
||||||
|
config: './tailwind.config.cjs',
|
||||||
|
jit: true
|
||||||
|
}),
|
||||||
|
compress({
|
||||||
|
algorithm: 'gzip', // You can also use 'brotliCompress' for Brotli
|
||||||
|
threshold: 1024, // Compress files larger than 1KB
|
||||||
|
deleteOriginFile: false, // Keep original files (optional)
|
||||||
|
})
|
||||||
],
|
],
|
||||||
build: {
|
build: {
|
||||||
target: 'esnext',
|
target: 'esnext',
|
||||||
assetsDir: './assets',
|
assetsDir: './assets',
|
||||||
modulePreload: { polyfill: false },
|
modulePreload: { polyfill: false },
|
||||||
commonjsOptions: { esmExternals: true }
|
commonjsOptions: { esmExternals: true },
|
||||||
|
assetsInlineLimit: 1024,
|
||||||
|
sourcemap: false,
|
||||||
},
|
},
|
||||||
ssr: {
|
ssr: {
|
||||||
external: ['cacache', 'glob', 'xlsx', 'sharp', '@polymech/kbot-d']
|
external: ['cacache', 'glob', 'xlsx', 'sharp', '@polymech/kbot-d']
|
||||||
@ -46,10 +59,11 @@ export default defineConfig({
|
|||||||
skipInline: false,
|
skipInline: false,
|
||||||
drafts: true
|
drafts: true
|
||||||
},
|
},
|
||||||
site: 'https://polymech.io',
|
|
||||||
integrations: [
|
integrations: [
|
||||||
//starlight(),
|
//starlight(),
|
||||||
//sitemap(),
|
sitemap({
|
||||||
|
|
||||||
|
}),
|
||||||
mdx(),
|
mdx(),
|
||||||
//AstroPWA({}),
|
//AstroPWA({}),
|
||||||
react(),
|
react(),
|
||||||
|
|||||||
68
docs/envs.md
68
docs/envs.md
@ -1,34 +1,34 @@
|
|||||||
Below is a conceptual **Markdown table** that contrasts different ways to use React and Astro together—covering client-side, server-side rendering (SSR), and embedding components. It also includes notes about **Vite configurations** and **plugins** you might need.
|
Below is a conceptual **Markdown table** that contrasts different ways to use React and Astro together—covering client-side, server-side rendering (SSR), and embedding components. It also includes notes about **Vite configurations** and **plugins** you might need.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
| **Environment / Approach** | **Description** | **SSR vs. Client** | **Integration Steps** | **Vite / Plugin Considerations** |
|
| **Environment / Approach** | **Description** | **SSR vs. Client** | **Integration Steps** | **Vite / Plugin Considerations** |
|
||||||
|-----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|-----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| **1. Astro + React (Client-side Only)** | Using React components within your Astro project, rendered exclusively on the client. | - *Client:* The browser executes the JavaScript.<br/>- *SSR:* The components are not rendered by the server; only HTML placeholders or minimal stubs are served. | 1. Install `@astrojs/react`. <br/>2. Import and register your React component in your `.astro` file. <br/>3. Use the `client:load`, `client:idle`, or `client:visible` directives for client hydration. | - **Vite config:** Astro automatically configures Vite for React by default when `@astrojs/react` is installed. <br/>- **Plugins:** The official `@astrojs/react` plugin handles bundling, transpilation, and client hydration. |
|
| **1. Astro + React (Client-side Only)** | Using React components within your Astro project, rendered exclusively on the client. | - *Client:* The browser executes the JavaScript.<br/>- *SSR:* The components are not rendered by the server; only HTML placeholders or minimal stubs are served. | 1. Install `@astrojs/react`. <br/>2. Import and register your React component in your `.astro` file. <br/>3. Use the `client:load`, `client:idle`, or `client:visible` directives for client hydration. | - **Vite config:** Astro automatically configures Vite for React by default when `@astrojs/react` is installed. <br/>- **Plugins:** The official `@astrojs/react` plugin handles bundling, transpilation, and client hydration. |
|
||||||
| **2. Astro + React (SSR)** | Using Astro’s server-side rendering (experimental or through a hosting platform that supports SSR) to **render React components on the server** before sending HTML to the client. | - *Client:* The component can then hydrate on the client if needed (otherwise it remains static, but pre-rendered from SSR). <br/>- *SSR:* React components are rendered by Astro’s SSR. | 1. Configure SSR in your `astro.config.mjs` or with your hosting provider’s settings. <br/>2. Make sure `@astrojs/react` is installed and your SSR environment is set up. <br/>3. Use SSR-friendly APIs (avoid browser-only globals in server code). | - **Vite config:** May need additional SSR config if your framework or deployment environment has special needs. <br/>- **Plugins:** Typically the same `@astrojs/react` plugin, but ensure any external SSR plugins are compatible (e.g. for styling or data-fetching). |
|
| **2. Astro + React (SSR)** | Using Astro’s server-side rendering (experimental or through a hosting platform that supports SSR) to **render React components on the server** before sending HTML to the client. | - *Client:* The component can then hydrate on the client if needed (otherwise it remains static, but pre-rendered from SSR). <br/>- *SSR:* React components are rendered by Astro’s SSR. | 1. Configure SSR in your `astro.config.mjs` or with your hosting provider’s settings. <br/>2. Make sure `@astrojs/react` is installed and your SSR environment is set up. <br/>3. Use SSR-friendly APIs (avoid browser-only globals in server code). | - **Vite config:** May need additional SSR config if your framework or deployment environment has special needs. <br/>- **Plugins:** Typically the same `@astrojs/react` plugin, but ensure any external SSR plugins are compatible (e.g. for styling or data-fetching). |
|
||||||
| **3. React Inside an Astro Component** | An `.astro` file that includes smaller React components inline, e.g., embedding `<ReactComponent />` inside Astro’s templating syntax. This is the most common scenario in Astro projects. | - *Client:* If the component is client-hydrated, it becomes interactive in the browser. <br/>- *SSR:* If Astro is set for SSR, the component is pre-rendered on the server. | 1. Create a `.astro` component. <br/>2. Import React components, e.g. `import MyButton from '../components/MyButton.jsx'`. <br/>3. Embed `<MyButton client:load />` or use another hydration directive. | - **Vite config:** Generally works out-of-the-box with `@astrojs/react`. <br/>- **Plugins:** Additional React-friendly Vite or Babel plugins can be added in your `astro.config.mjs` or in a separate config if needed. |
|
| **3. React Inside an Astro Component** | An `.astro` file that includes smaller React components inline, e.g., embedding `<ReactComponent />` inside Astro’s templating syntax. This is the most common scenario in Astro projects. | - *Client:* If the component is client-hydrated, it becomes interactive in the browser. <br/>- *SSR:* If Astro is set for SSR, the component is pre-rendered on the server. | 1. Create a `.astro` component. <br/>2. Import React components, e.g. `import MyButton from '../components/MyButton.jsx'`. <br/>3. Embed `<MyButton client:load />` or use another hydration directive. | - **Vite config:** Generally works out-of-the-box with `@astrojs/react`. <br/>- **Plugins:** Additional React-friendly Vite or Babel plugins can be added in your `astro.config.mjs` or in a separate config if needed. |
|
||||||
| **4. Astro Component inside React** | Less common scenario. You’d have a React application and want to render an Astro component within that React tree. Typically, you’d compile Astro into static output or use the Astro server output as an API (SSR). | - *Client:* If you bundle Astro components as static output, there’s no direct hydration for “Astro markup” inside React unless you embed them as iframes or separate routes. <br/>- *SSR:* Potentially possible, but complicated. | 1. **Static approach**: Build Astro pages/components to static HTML and embed via `<iframe>` or fetch the rendered content. <br/>2. **SSR approach**: Build an Astro SSR server and fetch rendered HTML from it. <br/>3. Integrating “live” Astro code directly in React bundlers is not straightforward. | - **Vite config:** React’s Vite config typically doesn’t parse `.astro` files. You may need custom integration or a multi-app approach. <br/>- **Plugins:** No out-of-the-box solution for Astro inside React. Consider separate builds or direct HTML consumption. |
|
| **4. Astro Component inside React** | Less common scenario. You’d have a React application and want to render an Astro component within that React tree. Typically, you’d compile Astro into static output or use the Astro server output as an API (SSR). | - *Client:* If you bundle Astro components as static output, there’s no direct hydration for “Astro markup” inside React unless you embed them as iframes or separate routes. <br/>- *SSR:* Potentially possible, but complicated. | 1. **Static approach**: Build Astro pages/components to static HTML and embed via `<iframe>` or fetch the rendered content. <br/>2. **SSR approach**: Build an Astro SSR server and fetch rendered HTML from it. <br/>3. Integrating “live” Astro code directly in React bundlers is not straightforward. | - **Vite config:** React’s Vite config typically doesn’t parse `.astro` files. You may need custom integration or a multi-app approach. <br/>- **Plugins:** No out-of-the-box solution for Astro inside React. Consider separate builds or direct HTML consumption. |
|
||||||
| **5. Full React App with some Astro Pages** | A primarily React-based project, but certain pages or partial content is rendered by Astro (e.g., for static site generation or for speed). | - *Client:* React runs as usual. If Astro pages are served as static HTML, no direct hydration from Astro’s side unless specifically set up. <br/>- *SSR:* Varies based on hosting approach. | 1. Create a separate Astro project (or folder) for static pages. <br/>2. Configure your build/deployment to serve Astro pages separately. <br/>3. Link between React app and Astro-generated routes, passing data if necessary. | - **Vite config:** Possibly need separate Vite configs for React and Astro or a shared monorepo approach. <br/>- **Plugins:** Use the standard React Vite plugin for the main app, and `@astrojs/react` (and others) for the Astro portion. |
|
| **5. Full React App with some Astro Pages** | A primarily React-based project, but certain pages or partial content is rendered by Astro (e.g., for static site generation or for speed). | - *Client:* React runs as usual. If Astro pages are served as static HTML, no direct hydration from Astro’s side unless specifically set up. <br/>- *SSR:* Varies based on hosting approach. | 1. Create a separate Astro project (or folder) for static pages. <br/>2. Configure your build/deployment to serve Astro pages separately. <br/>3. Link between React app and Astro-generated routes, passing data if necessary. | - **Vite config:** Possibly need separate Vite configs for React and Astro or a shared monorepo approach. <br/>- **Plugins:** Use the standard React Vite plugin for the main app, and `@astrojs/react` (and others) for the Astro portion. |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Additional Notes
|
### Additional Notes
|
||||||
|
|
||||||
- **SSR (Server-Side Rendering) vs. Client-Side**:
|
- **SSR (Server-Side Rendering) vs. Client-Side**:
|
||||||
- Astro’s default behavior is **static site generation** (SSG). If you need SSR, you must enable Astro’s SSR mode (experimental or through a supported deployment target like Vercel, Netlify, etc.).
|
- Astro’s default behavior is **static site generation** (SSG). If you need SSR, you must enable Astro’s SSR mode (experimental or through a supported deployment target like Vercel, Netlify, etc.).
|
||||||
- React can be mixed in for **partial hydration**, letting you deliver mostly static pages but with interactive “islands” of React.
|
- React can be mixed in for **partial hydration**, letting you deliver mostly static pages but with interactive “islands” of React.
|
||||||
|
|
||||||
- **Vite Configuration**:
|
- **Vite Configuration**:
|
||||||
- When using React inside Astro, the official `@astrojs/react` integration handles most details.
|
- When using React inside Astro, the official `@astrojs/react` integration handles most details.
|
||||||
- If you have advanced needs (e.g., custom Babel transforms, PostCSS, or TypeScript paths), you can tweak Astro’s underlying Vite config in `astro.config.mjs`.
|
- If you have advanced needs (e.g., custom Babel transforms, PostCSS, or TypeScript paths), you can tweak Astro’s underlying Vite config in `astro.config.mjs`.
|
||||||
- For embedding Astro in a larger React project, or vice versa, consider **mono-repo** patterns or separate builds. Typically, each framework is built with its own config or pipeline.
|
- For embedding Astro in a larger React project, or vice versa, consider **mono-repo** patterns or separate builds. Typically, each framework is built with its own config or pipeline.
|
||||||
|
|
||||||
- **Astro Components in a React Context**:
|
- **Astro Components in a React Context**:
|
||||||
- Rendering `.astro` files “directly” inside a React build pipeline is not straightforward because React expects JavaScript/JSX while Astro components require the Astro compiler.
|
- Rendering `.astro` files “directly” inside a React build pipeline is not straightforward because React expects JavaScript/JSX while Astro components require the Astro compiler.
|
||||||
- Typically, it’s more straightforward to create **static exports** (HTML) from Astro and consume them in a React app or route to them.
|
- Typically, it’s more straightforward to create **static exports** (HTML) from Astro and consume them in a React app or route to them.
|
||||||
|
|
||||||
- **Hydration Directives**:
|
- **Hydration Directives**:
|
||||||
- Astro’s special directives like `client:load`, `client:idle`, and `client:visible` determine when/how a React component hydrates on the client.
|
- Astro’s special directives like `client:load`, `client:idle`, and `client:visible` determine when/how a React component hydrates on the client.
|
||||||
- For SSR, if you want a component to remain static, do not add a client directive, and it will render in pure HTML.
|
- For SSR, if you want a component to remain static, do not add a client directive, and it will render in pure HTML.
|
||||||
|
|
||||||
Use the table and notes above to guide how you structure your Astro + React workflow, deciding whether your components render on the client, server, or a bit of both.
|
Use the table and notes above to guide how you structure your Astro + React workflow, deciding whether your components render on the client, server, or a bit of both.
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
# Preferences
|
# Preferences
|
||||||
|
|
||||||
- all documents in Markdown, insert new line after headings, and before code sections
|
- all documents in Markdown, insert new line after headings, and before code sections
|
||||||
- standard chapters: brief, references (with links)
|
- standard chapters: brief, references (with links)
|
||||||
|
|||||||
36
docs/seo.md
36
docs/seo.md
@ -1,18 +1,18 @@
|
|||||||
# SEO Component for Canonical Tags and Hreflang Attributes in Astro
|
# SEO Component for Canonical Tags and Hreflang Attributes in Astro
|
||||||
|
|
||||||
This document outlines how to implement canonical tags and hreflang attributes in an Astro project to support multilingual SEO.
|
This document outlines how to implement canonical tags and hreflang attributes in an Astro project to support multilingual SEO.
|
||||||
|
|
||||||
## 1. SEO Component (`SEO.astro`)
|
## 1. SEO Component (`SEO.astro`)
|
||||||
|
|
||||||
Create a reusable component that accepts a canonical URL and an array of hreflang entries.
|
Create a reusable component that accepts a canonical URL and an array of hreflang entries.
|
||||||
|
|
||||||
```astro
|
```astro
|
||||||
---
|
---
|
||||||
// src/components/SEO.astro
|
// src/components/SEO.astro
|
||||||
const { canonical, hreflangs } = Astro.props;
|
const { canonical, hreflangs } = Astro.props;
|
||||||
---
|
---
|
||||||
{canonical && <link rel="canonical" href={canonical} />}
|
{canonical && <link rel="canonical" href={canonical} />}
|
||||||
{hreflangs &&
|
{hreflangs &&
|
||||||
hreflangs.map((entry) => (
|
hreflangs.map((entry) => (
|
||||||
<link key={entry.lang} rel="alternate" hreflang={entry.lang} href={entry.url} />
|
<link key={entry.lang} rel="alternate" hreflang={entry.lang} href={entry.url} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
## Image to text
|
## Image to text
|
||||||
|
|
||||||
- [ ] options, free, images to text (not OCR)
|
- [ ] options, free, images to text (not OCR)
|
||||||
- [ ] as markdown table with links, prices, specs/features, language
|
- [ ] as markdown table with links, prices, specs/features, language
|
||||||
- [ ] check commercial and opensource models (huggingface)
|
- [ ] check commercial and opensource models (huggingface)
|
||||||
|
|||||||
@ -1,26 +1,26 @@
|
|||||||
## Map
|
## Map
|
||||||
|
|
||||||
- skip checked todos
|
- skip checked todos
|
||||||
- dont comment
|
- dont comment
|
||||||
- simple markup, for errors, use a dedicated error component
|
- simple markup, for errors, use a dedicated error component
|
||||||
|
|
||||||
- [ ] complete & self-containing Astro component, tailwind: Google Maps Static API, pass location array & Options as props, default to 2 locations in Lamu, save in ../src/components/polymech/map.astro
|
- [ ] complete & self-containing Astro component, tailwind: Google Maps Static API, pass location array & Options as props, default to 2 locations in Lamu, save in ../src/components/polymech/map.astro
|
||||||
- [ ] document all in ../src/components/polymech/map.md, including API key setup
|
- [ ] document all in ../src/components/polymech/map.md, including API key setup
|
||||||
- [ ] save all TS types in ../src/components/polymech/map-types.ts
|
- [ ] save all TS types in ../src/components/polymech/map-types.ts
|
||||||
- [ ] center the map (center of all locations)
|
- [ ] center the map (center of all locations)
|
||||||
- [ ] add options: satalite, terrain (default)
|
- [ ] add options: satalite, terrain (default)
|
||||||
|
|
||||||
export interface GeoPos {
|
export interface GeoPos {
|
||||||
lon: number
|
lon: number
|
||||||
lat: number
|
lat: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Location {
|
export interface Location {
|
||||||
geo:GeoPos
|
geo:GeoPos
|
||||||
title:string
|
title:string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Options{
|
export interface Options{
|
||||||
zoom?:number
|
zoom?:number
|
||||||
api_key?:string
|
api_key?:string
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
## i18n
|
## i18n
|
||||||
|
|
||||||
skip checked todos
|
skip checked todos
|
||||||
|
|
||||||
- [x] enumerate all steps and concerns related to RTL languages for Astro
|
- [x] enumerate all steps and concerns related to RTL languages for Astro
|
||||||
- [ ] create an excessive guide on the subject, eg: cultural aspects, perception, standards and expectations in arab countries
|
- [ ] create an excessive guide on the subject, eg: cultural aspects, perception, standards and expectations in arab countries
|
||||||
|
|||||||
142
package-lock.json
generated
142
package-lock.json
generated
@ -30,6 +30,7 @@
|
|||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"cacache": "^19.0.1",
|
"cacache": "^19.0.1",
|
||||||
|
"env-var": "^7.5.0",
|
||||||
"exifreader": "^4.26.1",
|
"exifreader": "^4.26.1",
|
||||||
"file-type": "^20.0.0",
|
"file-type": "^20.0.0",
|
||||||
"find-cache-dir": "^5.0.0",
|
"find-cache-dir": "^5.0.0",
|
||||||
@ -64,6 +65,8 @@
|
|||||||
"tailwindcss": "^4.0.7",
|
"tailwindcss": "^4.0.7",
|
||||||
"type-fest": "^4.34.1",
|
"type-fest": "^4.34.1",
|
||||||
"vite": "^6.1.1",
|
"vite": "^6.1.1",
|
||||||
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
"write-file-atomic": "^6.0.0",
|
||||||
"xlsx": "^0.18.5",
|
"xlsx": "^0.18.5",
|
||||||
"yargs": "^17.7.2",
|
"yargs": "^17.7.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
@ -5132,6 +5135,24 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/configstore/node_modules/signal-exit": {
|
||||||
|
"version": "3.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
||||||
|
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/configstore/node_modules/write-file-atomic": {
|
||||||
|
"version": "3.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
|
||||||
|
"integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"imurmurhash": "^0.1.4",
|
||||||
|
"is-typedarray": "^1.0.0",
|
||||||
|
"signal-exit": "^3.0.2",
|
||||||
|
"typedarray-to-buffer": "^3.1.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/convert-source-map": {
|
"node_modules/convert-source-map": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||||
@ -5701,6 +5722,15 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/env-var": {
|
||||||
|
"version": "7.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/env-var/-/env-var-7.5.0.tgz",
|
||||||
|
"integrity": "sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/es-define-property": {
|
"node_modules/es-define-property": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
@ -6959,6 +6989,20 @@
|
|||||||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/fs-extra": {
|
||||||
|
"version": "10.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||||
|
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"graceful-fs": "^4.2.0",
|
||||||
|
"jsonfile": "^6.0.1",
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fs-minipass": {
|
"node_modules/fs-minipass": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
|
||||||
@ -7382,7 +7426,6 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
@ -8445,6 +8488,18 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsonfile": {
|
||||||
|
"version": "6.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
|
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"universalify": "^2.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"graceful-fs": "^4.1.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/keyv": {
|
"node_modules/keyv": {
|
||||||
"version": "4.5.4",
|
"version": "4.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||||
@ -15325,6 +15380,15 @@
|
|||||||
"url": "https://opencollective.com/unified"
|
"url": "https://opencollective.com/unified"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/universalify": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unset-value": {
|
"node_modules/unset-value": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
|
||||||
@ -15814,6 +15878,63 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vite-plugin-compression": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz",
|
||||||
|
"integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"chalk": "^4.1.2",
|
||||||
|
"debug": "^4.3.3",
|
||||||
|
"fs-extra": "^10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vite": ">=2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite-plugin-compression/node_modules/ansi-styles": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"color-convert": "^2.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite-plugin-compression/node_modules/chalk": {
|
||||||
|
"version": "4.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
|
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vite-plugin-compression/node_modules/supports-color": {
|
||||||
|
"version": "7.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||||
|
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"has-flag": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vite/node_modules/fsevents": {
|
"node_modules/vite/node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
@ -16195,23 +16316,18 @@
|
|||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/write-file-atomic": {
|
"node_modules/write-file-atomic": {
|
||||||
"version": "3.0.3",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz",
|
||||||
"integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
|
"integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"imurmurhash": "^0.1.4",
|
"imurmurhash": "^0.1.4",
|
||||||
"is-typedarray": "^1.0.0",
|
"signal-exit": "^4.0.1"
|
||||||
"signal-exit": "^3.0.2",
|
},
|
||||||
"typedarray-to-buffer": "^3.1.5"
|
"engines": {
|
||||||
|
"node": "^18.17.0 || >=20.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/write-file-atomic/node_modules/signal-exit": {
|
|
||||||
"version": "3.0.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
|
|
||||||
"integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
|
|
||||||
"license": "ISC"
|
|
||||||
},
|
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "7.5.10",
|
"version": "7.5.10",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||||
|
|||||||
@ -40,6 +40,7 @@
|
|||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"cacache": "^19.0.1",
|
"cacache": "^19.0.1",
|
||||||
|
"env-var": "^7.5.0",
|
||||||
"exifreader": "^4.26.1",
|
"exifreader": "^4.26.1",
|
||||||
"file-type": "^20.0.0",
|
"file-type": "^20.0.0",
|
||||||
"find-cache-dir": "^5.0.0",
|
"find-cache-dir": "^5.0.0",
|
||||||
@ -74,6 +75,8 @@
|
|||||||
"tailwindcss": "^4.0.7",
|
"tailwindcss": "^4.0.7",
|
||||||
"type-fest": "^4.34.1",
|
"type-fest": "^4.34.1",
|
||||||
"vite": "^6.1.1",
|
"vite": "^6.1.1",
|
||||||
|
"vite-plugin-compression": "^0.5.1",
|
||||||
|
"write-file-atomic": "^6.0.0",
|
||||||
"xlsx": "^0.18.5",
|
"xlsx": "^0.18.5",
|
||||||
"yargs": "^17.7.2",
|
"yargs": "^17.7.2",
|
||||||
"zod": "^3.24.2"
|
"zod": "^3.24.2"
|
||||||
|
|||||||
108
packages/api.md
108
packages/api.md
@ -1,55 +1,55 @@
|
|||||||
|
|
||||||
|
|
||||||
components->Img -> renderImg -> getImage(sizes, format ...) -> element
|
components->Img -> renderImg -> getImage(sizes, format ...) -> element
|
||||||
|
|
||||||
|
|
||||||
getImage -> getProcessedImage(args[0] : src:local path)
|
getImage -> getProcessedImage(args[0] : src:local path)
|
||||||
args[0]
|
args[0]
|
||||||
{
|
{
|
||||||
src: "https://assets.osr-plastic.org//products/sheetpress/cassandra-edczmax-rc2/media/gallery/perspective.jpg",
|
src: "https://assets.osr-plastic.org//products/sheetpress/cassandra-edczmax-rc2/media/gallery/perspective.jpg",
|
||||||
type: "Img",
|
type: "Img",
|
||||||
sizes: "(min-width: 800px) 800px, 800vw",
|
sizes: "(min-width: 800px) 800px, 800vw",
|
||||||
format: "avif",
|
format: "avif",
|
||||||
breakpoints: undefined,
|
breakpoints: undefined,
|
||||||
placeholder: "blurred",
|
placeholder: "blurred",
|
||||||
artDirectives: [
|
artDirectives: [
|
||||||
],
|
],
|
||||||
fallbackFormat: "avif",
|
fallbackFormat: "avif",
|
||||||
includeSourceFormat: false,
|
includeSourceFormat: false,
|
||||||
formatOptions: {
|
formatOptions: {
|
||||||
jpg: {
|
jpg: {
|
||||||
quality: 80,
|
quality: 80,
|
||||||
},
|
},
|
||||||
png: {
|
png: {
|
||||||
quality: 80,
|
quality: 80,
|
||||||
},
|
},
|
||||||
webp: {
|
webp: {
|
||||||
quality: 50,
|
quality: 50,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
transformConfigs: {
|
transformConfigs: {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
->
|
->
|
||||||
|
|
||||||
{
|
{
|
||||||
uuid: "B16DBD24",
|
uuid: "B16DBD24",
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
sources: [
|
sources: [
|
||||||
{
|
{
|
||||||
src: "/_astro/perspective@1320w.e183f84c.avif",
|
src: "/_astro/perspective@1320w.e183f84c.avif",
|
||||||
format: "avif",
|
format: "avif",
|
||||||
srcset: "/_astro/perspective@320w.3435f7a3.avif 320w, /_astro/perspective@653w.e53b07bb.avif 653w, /_astro/perspective@920w.a7628e27.avif 920w, /_astro/perspective@1120w.90dfde67.avif 1120w, /_astro/perspective@1253w.084f9de3.avif 1253w, /_astro/perspective@1320w.e183f84c.avif 1320w",
|
srcset: "/_astro/perspective@320w.3435f7a3.avif 320w, /_astro/perspective@653w.e53b07bb.avif 653w, /_astro/perspective@920w.a7628e27.avif 920w, /_astro/perspective@1120w.90dfde67.avif 1120w, /_astro/perspective@1253w.084f9de3.avif 1253w, /_astro/perspective@1320w.e183f84c.avif 1320w",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sizes: {
|
sizes: {
|
||||||
width: 1320,
|
width: 1320,
|
||||||
height: 1980,
|
height: 1980,
|
||||||
},
|
},
|
||||||
fallback: "data:image/avif;base64,
|
fallback: "data:image/avif;base64,
|
||||||
imagesizes: "(min-width: 800px) 800px, 800vw",
|
imagesizes: "(min-width: 800px) 800px, 800vw",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
@ -1,17 +1,17 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true,
|
||||||
browser: true,
|
browser: true,
|
||||||
es2020: true,
|
es2020: true,
|
||||||
},
|
},
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
ecmaVersion: 2022,
|
ecmaVersion: 2022,
|
||||||
sourceType: "module",
|
sourceType: "module",
|
||||||
},
|
},
|
||||||
plugins: ["unicorn"],
|
plugins: ["unicorn"],
|
||||||
extends: ["eslint:recommended"],
|
extends: ["eslint:recommended"],
|
||||||
rules: {
|
rules: {
|
||||||
"unicorn/prefer-node-protocol": "error",
|
"unicorn/prefer-node-protocol": "error",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
130
packages/imagetools/.github/workflows/ci.yml
vendored
130
packages/imagetools/.github/workflows/ci.yml
vendored
@ -1,65 +1,65 @@
|
|||||||
name: CI
|
name: CI
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
lint:
|
||||||
env:
|
env:
|
||||||
ASTRO_TELEMETRY_DISABLED: true
|
ASTRO_TELEMETRY_DISABLED: true
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup PNPM
|
- name: Setup PNPM
|
||||||
uses: pnpm/action-setup@v2.2.1
|
uses: pnpm/action-setup@v2.2.1
|
||||||
|
|
||||||
- name: Setup node
|
- name: Setup node
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 16
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Prettier
|
- name: Prettier
|
||||||
run: pnpm run format:check
|
run: pnpm run format:check
|
||||||
|
|
||||||
- name: ESLint
|
- name: ESLint
|
||||||
run: pnpm run lint
|
run: pnpm run lint
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: "Test: ${{ matrix.os }} (node@${{ matrix.node_version }})"
|
name: "Test: ${{ matrix.os }} (node@${{ matrix.node_version }})"
|
||||||
env:
|
env:
|
||||||
ASTRO_TELEMETRY_DISABLED: true
|
ASTRO_TELEMETRY_DISABLED: true
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-latest]
|
||||||
node_version: [14, 16]
|
node_version: [14, 16]
|
||||||
include:
|
include:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
node_version: 16
|
node_version: 16
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
node_version: 16
|
node_version: 16
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup PNPM
|
- name: Setup PNPM
|
||||||
uses: pnpm/action-setup@v2.2.1
|
uses: pnpm/action-setup@v2.2.1
|
||||||
|
|
||||||
- name: Setup node@${{ matrix.node_version }}
|
- name: Setup node@${{ matrix.node_version }}
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node_version }}
|
node-version: ${{ matrix.node_version }}
|
||||||
cache: "pnpm"
|
cache: "pnpm"
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: pnpm --filter astro-imagetools run test
|
run: pnpm --filter astro-imagetools run test
|
||||||
|
|||||||
40
packages/imagetools/.gitignore
vendored
40
packages/imagetools/.gitignore
vendored
@ -1,20 +1,20 @@
|
|||||||
# dependencies
|
# dependencies
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# build output
|
# build output
|
||||||
dist/
|
dist/
|
||||||
|
|
||||||
# logs
|
# logs
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
# npm
|
# npm
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
|
||||||
# macOS-specific files
|
# macOS-specific files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
# env
|
# env
|
||||||
*.env
|
*.env
|
||||||
|
|
||||||
# astro-imagetools
|
# astro-imagetools
|
||||||
packages/astro-imagetools/astroViteConfigs.js
|
packages/astro-imagetools/astroViteConfigs.js
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
*.test.ts
|
*.test.ts
|
||||||
test-fixtures
|
test-fixtures
|
||||||
astroViteConfigs.js
|
astroViteConfigs.js
|
||||||
vitest.config.ts
|
vitest.config.ts
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
## force pnpm to hoist
|
## force pnpm to hoist
|
||||||
shamefully-hoist = true
|
shamefully-hoist = true
|
||||||
@ -1,2 +1,2 @@
|
|||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
demo/dist
|
demo/dist
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
{
|
{
|
||||||
"overrides": [
|
"overrides": [
|
||||||
{
|
{
|
||||||
"files": "**/*.astro",
|
"files": "**/*.astro",
|
||||||
"options": { "parser": "astro" }
|
"options": { "parser": "astro" }
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"plugins": ["prettier-plugin-astro"]
|
"plugins": ["prettier-plugin-astro"]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2022 Rafid Muhymin Wafi
|
Copyright (c) 2022 Rafid Muhymin Wafi
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
in the Software without restriction, including without limitation the rights
|
in the Software without restriction, including without limitation the rights
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
furnished to do so, subject to the following conditions:
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
The above copyright notice and this permission notice shall be included in all
|
||||||
copies or substantial portions of the Software.
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
@ -1,39 +1,39 @@
|
|||||||
# **Astro ImageTools**
|
# **Astro ImageTools**
|
||||||
|
|
||||||
**Astro ImageTools** is a collection of tools for optimizing images, background images, and generating responsive images for the **Astro JS** framework.
|
**Astro ImageTools** is a collection of tools for optimizing images, background images, and generating responsive images for the **Astro JS** framework.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
Below is a short list of features that **Astro ImageTools** offers. For more information, please see component-specific or API-specific documentation.
|
Below is a short list of features that **Astro ImageTools** offers. For more information, please see component-specific or API-specific documentation.
|
||||||
|
|
||||||
- ✅ **Regular Image Optimization** (`<img>` and `<picture>`)
|
- ✅ **Regular Image Optimization** (`<img>` and `<picture>`)
|
||||||
- ✅ **Background Image Optimization**
|
- ✅ **Background Image Optimization**
|
||||||
- ✅ **Responsive Images**
|
- ✅ **Responsive Images**
|
||||||
- ✅ **Simple and intuitive Art Direction API**
|
- ✅ **Simple and intuitive Art Direction API**
|
||||||
- ✅ **Lazy Loading**
|
- ✅ **Lazy Loading**
|
||||||
- ✅ **Programmatic APIs**
|
- ✅ **Programmatic APIs**
|
||||||
- ✅ **Asynchronous Decoding**
|
- ✅ **Asynchronous Decoding**
|
||||||
- ✅ **Unique Breakpoints Calculation**
|
- ✅ **Unique Breakpoints Calculation**
|
||||||
- ✅ **Preloading for urgent images**
|
- ✅ **Preloading for urgent images**
|
||||||
- ✅ **SVG Tracing and Posterization**
|
- ✅ **SVG Tracing and Posterization**
|
||||||
- ✅ **100% Scoped CSS**
|
- ✅ **100% Scoped CSS**
|
||||||
- ✅ **Four kind of Layouts: `constrained`, `fixed`, `fullWidth` & `fill`**
|
- ✅ **Four kind of Layouts: `constrained`, `fixed`, `fullWidth` & `fill`**
|
||||||
- ✅ **Three kind of Placeholder Images: `blurred`, `dominantColor` & `tracedSVG`**
|
- ✅ **Three kind of Placeholder Images: `blurred`, `dominantColor` & `tracedSVG`**
|
||||||
- ✅ **Long list of supported Image Formats**
|
- ✅ **Long list of supported Image Formats**
|
||||||
- ✅ **Long List of supported Configuration Options**
|
- ✅ **Long List of supported Configuration Options**
|
||||||
- ✅ **Supports Remote Images and Data URIs too**
|
- ✅ **Supports Remote Images and Data URIs too**
|
||||||
- ✅ **Support for _`sharp`less_ Environments**
|
- ✅ **Support for _`sharp`less_ Environments**
|
||||||
- ✅ **Both Memory-based and FS-based Caching for better Performance**
|
- ✅ **Both Memory-based and FS-based Caching for better Performance**
|
||||||
- ✅ **Respects to _Semantics of HTML_ as much as possible**
|
- ✅ **Respects to _Semantics of HTML_ as much as possible**
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
To get started with **Astro ImageTools**, first check out the [Installation](https://astro-imagetools-docs.vercel.app/en/installation) documentation for instructions on how to install the `astro-imagetools` package.
|
To get started with **Astro ImageTools**, first check out the [Installation](https://astro-imagetools-docs.vercel.app/en/installation) documentation for instructions on how to install the `astro-imagetools` package.
|
||||||
|
|
||||||
If you are looking for the available components and APIs, please check out the [Components and APIs](https://astro-imagetools-docs.vercel.app/en/components-and-apis) documentation.
|
If you are looking for the available components and APIs, please check out the [Components and APIs](https://astro-imagetools-docs.vercel.app/en/components-and-apis) documentation.
|
||||||
|
|
||||||
If you want to view live examples of the components, APIs, layouts, and placeholder images, check out the [Astro ImageTools Demo](https://astro-imagetools-demo.vercel.app/) website.
|
If you want to view live examples of the components, APIs, layouts, and placeholder images, check out the [Astro ImageTools Demo](https://astro-imagetools-demo.vercel.app/) website.
|
||||||
|
|
||||||
If you want to report any issues or have found a missing feature, please report it on [GitHub](https://github.com/RafidMuhymin/astro-imagetools/)!
|
If you want to report any issues or have found a missing feature, please report it on [GitHub](https://github.com/RafidMuhymin/astro-imagetools/)!
|
||||||
|
|
||||||
Good luck out there, Astronaut. 🧑🚀
|
Good luck out there, Astronaut. 🧑🚀
|
||||||
|
|||||||
2
packages/imagetools/api/importImage.d.ts
vendored
2
packages/imagetools/api/importImage.d.ts
vendored
@ -1 +1 @@
|
|||||||
export default function importImage(url: string): Promise<string>;
|
export default function importImage(url: string): Promise<string>;
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
import load from "../plugin/hooks/load.js";
|
import load from "../plugin/hooks/load.js";
|
||||||
import { getSrcPath } from "./utils/getSrcPath.js";
|
import { getSrcPath } from "./utils/getSrcPath.js";
|
||||||
import getResolvedSrc from "./utils/getResolvedSrc.js";
|
import getResolvedSrc from "./utils/getResolvedSrc.js";
|
||||||
|
|
||||||
export default async function importImage(path) {
|
export default async function importImage(path) {
|
||||||
try {
|
try {
|
||||||
const { search, protocol, pathname } = new URL(path);
|
const { search, protocol, pathname } = new URL(path);
|
||||||
|
|
||||||
const { src: id, base } = await getResolvedSrc(
|
const { src: id, base } = await getResolvedSrc(
|
||||||
protocol === "data:" ? protocol + pathname : path
|
protocol === "data:" ? protocol + pathname : path
|
||||||
);
|
);
|
||||||
|
|
||||||
const src = (await load(id + search, base)).slice(16, -1);
|
const src = (await load(id + search, base)).slice(16, -1);
|
||||||
|
|
||||||
return src;
|
return src;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const id = await getSrcPath(path);
|
const id = await getSrcPath(path);
|
||||||
|
|
||||||
const src = (await load(id)).slice(16, -1);
|
const src = (await load(id)).slice(16, -1);
|
||||||
|
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export { default as renderImg } from "./renderImg.js";
|
export { default as renderImg } from "./renderImg.js";
|
||||||
export { default as renderPicture } from "./renderPicture.js";
|
export { default as renderPicture } from "./renderPicture.js";
|
||||||
export { default as renderBackgroundImage } from "./renderBackgroundImage.js";
|
export { default as renderBackgroundImage } from "./renderBackgroundImage.js";
|
||||||
export { default as renderBackgroundPicture } from "./renderBackgroundPicture.js";
|
export { default as renderBackgroundPicture } from "./renderBackgroundPicture.js";
|
||||||
export { default as importImage } from "./importImage.js";
|
export { default as importImage } from "./importImage.js";
|
||||||
export { getImageDetails, loadImage } from "./utils/imagetools.js"
|
export { getImageDetails, loadImage } from "./utils/imagetools.js"
|
||||||
@ -1,8 +1,8 @@
|
|||||||
import type {
|
import type {
|
||||||
BackgroundImageConfigOptions,
|
BackgroundImageConfigOptions,
|
||||||
BackgroundImageHTMLData,
|
BackgroundImageHTMLData,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|
||||||
export default function renderBackgroundImage(
|
export default function renderBackgroundImage(
|
||||||
config: BackgroundImageConfigOptions
|
config: BackgroundImageConfigOptions
|
||||||
): Promise<BackgroundImageHTMLData>;
|
): Promise<BackgroundImageHTMLData>;
|
||||||
|
|||||||
@ -1,159 +1,159 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
import getImage from "./utils/getImage.js";
|
import getImage from "./utils/getImage.js";
|
||||||
import getLinkElement from "./utils/getLinkElement.js";
|
import getLinkElement from "./utils/getLinkElement.js";
|
||||||
import getStyleElement from "./utils/getStyleElement.js";
|
import getStyleElement from "./utils/getStyleElement.js";
|
||||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||||
import getContainerElement from "./utils/getContainerElement.js";
|
import getContainerElement from "./utils/getContainerElement.js";
|
||||||
|
|
||||||
export default async function renderBackgroundImage(props) {
|
export default async function renderBackgroundImage(props) {
|
||||||
const type = "BackgroundImage";
|
const type = "BackgroundImage";
|
||||||
|
|
||||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
src,
|
src,
|
||||||
tag,
|
tag,
|
||||||
content,
|
content,
|
||||||
preload,
|
preload,
|
||||||
attributes,
|
attributes,
|
||||||
placeholder,
|
placeholder,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
backgroundSize,
|
backgroundSize,
|
||||||
backgroundPosition,
|
backgroundPosition,
|
||||||
format,
|
format,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
artDirectives,
|
artDirectives,
|
||||||
} = filteredProps;
|
} = filteredProps;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
link: linkAttributes = {},
|
link: linkAttributes = {},
|
||||||
style: styleAttributes = {},
|
style: styleAttributes = {},
|
||||||
container: containerAttributes = {},
|
container: containerAttributes = {},
|
||||||
} = attributes;
|
} = attributes;
|
||||||
|
|
||||||
const sizes = "";
|
const sizes = "";
|
||||||
|
|
||||||
const { uuid, images } = await getImage({
|
const { uuid, images } = await getImage({
|
||||||
src,
|
src,
|
||||||
type,
|
type,
|
||||||
sizes,
|
sizes,
|
||||||
format,
|
format,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
placeholder,
|
placeholder,
|
||||||
artDirectives,
|
artDirectives,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
transformConfigs,
|
transformConfigs,
|
||||||
});
|
});
|
||||||
|
|
||||||
const className = `astro-imagetools-background-image-${uuid}`;
|
const className = `astro-imagetools-background-image-${uuid}`;
|
||||||
|
|
||||||
const { imagesizes } = images[images.length - 1];
|
const { imagesizes } = images[images.length - 1];
|
||||||
|
|
||||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
||||||
|
|
||||||
const backgroundImageStylesArray = images.map(({ media, sources }) => {
|
const backgroundImageStylesArray = images.map(({ media, sources }) => {
|
||||||
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
||||||
|
|
||||||
const fallbackUrlCustomVariable = `--astro-imagetools-background-image-fallback-url${uuid}`;
|
const fallbackUrlCustomVariable = `--astro-imagetools-background-image-fallback-url${uuid}`;
|
||||||
|
|
||||||
const newSources = {};
|
const newSources = {};
|
||||||
|
|
||||||
sources.forEach(({ src, format, srcset }) => {
|
sources.forEach(({ src, format, srcset }) => {
|
||||||
const sources = srcset
|
const sources = srcset
|
||||||
.split(", ")
|
.split(", ")
|
||||||
.map((source) => [
|
.map((source) => [
|
||||||
source.slice(0, source.lastIndexOf(" ")),
|
source.slice(0, source.lastIndexOf(" ")),
|
||||||
source.slice(source.lastIndexOf(" ") + 1, -1),
|
source.slice(source.lastIndexOf(" ") + 1, -1),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
sources.forEach(([path, width]) => {
|
sources.forEach(([path, width]) => {
|
||||||
if (!newSources[width]) {
|
if (!newSources[width]) {
|
||||||
newSources[width] = [];
|
newSources[width] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
newSources[width].push({ src, format, path });
|
newSources[width].push({ src, format, path });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const widths = Object.keys(newSources)
|
const widths = Object.keys(newSources)
|
||||||
.map((width) => parseInt(width))
|
.map((width) => parseInt(width))
|
||||||
.reverse();
|
.reverse();
|
||||||
|
|
||||||
const maxWidth = Math.max(...widths);
|
const maxWidth = Math.max(...widths);
|
||||||
|
|
||||||
const styles = widths
|
const styles = widths
|
||||||
.map((width) => {
|
.map((width) => {
|
||||||
const sources = newSources[width];
|
const sources = newSources[width];
|
||||||
|
|
||||||
const styles = sources
|
const styles = sources
|
||||||
.map(
|
.map(
|
||||||
({ format, path }, i) =>
|
({ format, path }, i) =>
|
||||||
`
|
`
|
||||||
${i !== sources.length - 1 ? `.${format} ` : ""}.${className} {
|
${i !== sources.length - 1 ? `.${format} ` : ""}.${className} {
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-image: url(${path}),
|
background-image: url(${path}),
|
||||||
var(${fallbackUrlCustomVariable});
|
var(${fallbackUrlCustomVariable});
|
||||||
background-size: ${backgroundSize};
|
background-size: ${backgroundSize};
|
||||||
background-position: ${backgroundPosition};
|
background-position: ${backgroundPosition};
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
.reverse()
|
.reverse()
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
return width === maxWidth
|
return width === maxWidth
|
||||||
? styles
|
? styles
|
||||||
: `
|
: `
|
||||||
@media screen and (max-width: ${width}px) {
|
@media screen and (max-width: ${width}px) {
|
||||||
${styles}
|
${styles}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
})
|
})
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
return {
|
return {
|
||||||
fallbackUrlCustomVariable,
|
fallbackUrlCustomVariable,
|
||||||
styles: media
|
styles: media
|
||||||
? `
|
? `
|
||||||
@media ${media} {
|
@media ${media} {
|
||||||
${styles}
|
${styles}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
: styles,
|
: styles,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const containerStyles = `
|
const containerStyles = `
|
||||||
.${className} {
|
.${className} {
|
||||||
position: relative;
|
position: relative;
|
||||||
${images
|
${images
|
||||||
.map(({ fallback }, i) => {
|
.map(({ fallback }, i) => {
|
||||||
const fallbackUrlCustomVariable =
|
const fallbackUrlCustomVariable =
|
||||||
backgroundImageStylesArray[i].fallbackUrlCustomVariable;
|
backgroundImageStylesArray[i].fallbackUrlCustomVariable;
|
||||||
|
|
||||||
return `${fallbackUrlCustomVariable}: url("${encodeURI(fallback)}");`;
|
return `${fallbackUrlCustomVariable}: url("${encodeURI(fallback)}");`;
|
||||||
})
|
})
|
||||||
.join("\n")}
|
.join("\n")}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const backgroundStyles =
|
const backgroundStyles =
|
||||||
backgroundImageStylesArray.map(({ styles }) => styles).join("\n") +
|
backgroundImageStylesArray.map(({ styles }) => styles).join("\n") +
|
||||||
containerStyles;
|
containerStyles;
|
||||||
|
|
||||||
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
||||||
|
|
||||||
const htmlElement = getContainerElement({
|
const htmlElement = getContainerElement({
|
||||||
tag,
|
tag,
|
||||||
content,
|
content,
|
||||||
className,
|
className,
|
||||||
containerAttributes,
|
containerAttributes,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { link, style, htmlElement };
|
return { link, style, htmlElement };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import type {
|
import type {
|
||||||
BackgroundPictureConfigOptions,
|
BackgroundPictureConfigOptions,
|
||||||
BackgroundPictureHTMLData,
|
BackgroundPictureHTMLData,
|
||||||
} from "../types";
|
} from "../types";
|
||||||
|
|
||||||
export default function renderBackgroundPicture(
|
export default function renderBackgroundPicture(
|
||||||
config: BackgroundPictureConfigOptions
|
config: BackgroundPictureConfigOptions
|
||||||
): Promise<BackgroundPictureHTMLData>;
|
): Promise<BackgroundPictureHTMLData>;
|
||||||
|
|||||||
@ -1,127 +1,127 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import getImage from "./utils/getImage.js";
|
import getImage from "./utils/getImage.js";
|
||||||
import getImgElement from "./utils/getImgElement.js";
|
import getImgElement from "./utils/getImgElement.js";
|
||||||
import getLinkElement from "./utils/getLinkElement.js";
|
import getLinkElement from "./utils/getLinkElement.js";
|
||||||
import getStyleElement from "./utils/getStyleElement.js";
|
import getStyleElement from "./utils/getStyleElement.js";
|
||||||
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
||||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||||
import getPictureElement from "./utils/getPictureElement.js";
|
import getPictureElement from "./utils/getPictureElement.js";
|
||||||
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
||||||
import getContainerElement from "./utils/getContainerElement.js";
|
import getContainerElement from "./utils/getContainerElement.js";
|
||||||
|
|
||||||
export default async function renderBackgroundPicture(props) {
|
export default async function renderBackgroundPicture(props) {
|
||||||
const type = "BackgroundPicture";
|
const type = "BackgroundPicture";
|
||||||
|
|
||||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
src,
|
src,
|
||||||
tag,
|
tag,
|
||||||
content,
|
content,
|
||||||
sizes,
|
sizes,
|
||||||
preload,
|
preload,
|
||||||
loading,
|
loading,
|
||||||
decoding,
|
decoding,
|
||||||
attributes,
|
attributes,
|
||||||
placeholder,
|
placeholder,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
objectFit,
|
objectFit,
|
||||||
objectPosition,
|
objectPosition,
|
||||||
format,
|
format,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
fadeInTransition,
|
fadeInTransition,
|
||||||
artDirectives,
|
artDirectives,
|
||||||
} = filteredProps;
|
} = filteredProps;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
img: imgAttributes = {},
|
img: imgAttributes = {},
|
||||||
link: linkAttributes = {},
|
link: linkAttributes = {},
|
||||||
style: styleAttributes = {},
|
style: styleAttributes = {},
|
||||||
picture: pictureAttributes = {},
|
picture: pictureAttributes = {},
|
||||||
container: containerAttributes = {},
|
container: containerAttributes = {},
|
||||||
} = attributes;
|
} = attributes;
|
||||||
|
|
||||||
const { uuid, images } = await getImage({
|
const { uuid, images } = await getImage({
|
||||||
src,
|
src,
|
||||||
type,
|
type,
|
||||||
sizes,
|
sizes,
|
||||||
format,
|
format,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
placeholder,
|
placeholder,
|
||||||
artDirectives,
|
artDirectives,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
transformConfigs,
|
transformConfigs,
|
||||||
});
|
});
|
||||||
|
|
||||||
const className = `astro-imagetools-picture-${uuid}`,
|
const className = `astro-imagetools-picture-${uuid}`,
|
||||||
containerClassName = `astro-imagetools-background-picture-${uuid}`;
|
containerClassName = `astro-imagetools-background-picture-${uuid}`;
|
||||||
|
|
||||||
const { imagesizes } = images[images.length - 1];
|
const { imagesizes } = images[images.length - 1];
|
||||||
|
|
||||||
const backgroundStyles = getBackgroundStyles(
|
const backgroundStyles = getBackgroundStyles(
|
||||||
images,
|
images,
|
||||||
className,
|
className,
|
||||||
objectFit,
|
objectFit,
|
||||||
objectPosition,
|
objectPosition,
|
||||||
fadeInTransition,
|
fadeInTransition,
|
||||||
{ isBackgroundPicture: true, containerClassName }
|
{ isBackgroundPicture: true, containerClassName }
|
||||||
);
|
);
|
||||||
|
|
||||||
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
||||||
|
|
||||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
||||||
|
|
||||||
const layoutStyles = getLayoutStyles({ isBackgroundImage: true });
|
const layoutStyles = getLayoutStyles({ isBackgroundImage: true });
|
||||||
|
|
||||||
// Background Images shouldn't convey important information
|
// Background Images shouldn't convey important information
|
||||||
const alt = "";
|
const alt = "";
|
||||||
|
|
||||||
const sources = images.flatMap(({ media, sources, sizes, imagesizes }) =>
|
const sources = images.flatMap(({ media, sources, sizes, imagesizes }) =>
|
||||||
sources.map(({ format, src, srcset }) =>
|
sources.map(({ format, src, srcset }) =>
|
||||||
src
|
src
|
||||||
? getImgElement({
|
? getImgElement({
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
sizes,
|
sizes,
|
||||||
style,
|
style,
|
||||||
srcset,
|
srcset,
|
||||||
loading,
|
loading,
|
||||||
decoding,
|
decoding,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
fadeInTransition,
|
fadeInTransition,
|
||||||
layoutStyles,
|
layoutStyles,
|
||||||
imgAttributes,
|
imgAttributes,
|
||||||
})
|
})
|
||||||
: `<source
|
: `<source
|
||||||
srcset="${srcset}"
|
srcset="${srcset}"
|
||||||
sizes="${imagesizes}"
|
sizes="${imagesizes}"
|
||||||
width="${sizes.width}"
|
width="${sizes.width}"
|
||||||
height="${sizes.height}"
|
height="${sizes.height}"
|
||||||
type="${`image/${format}`}"
|
type="${`image/${format}`}"
|
||||||
${media ? `media="${media}"` : ""}
|
${media ? `media="${media}"` : ""}
|
||||||
/>`
|
/>`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const picture = getPictureElement({
|
const picture = getPictureElement({
|
||||||
sources,
|
sources,
|
||||||
className,
|
className,
|
||||||
layoutStyles,
|
layoutStyles,
|
||||||
pictureAttributes,
|
pictureAttributes,
|
||||||
isBackgroundPicture: true,
|
isBackgroundPicture: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const htmlElement = getContainerElement({
|
const htmlElement = getContainerElement({
|
||||||
tag,
|
tag,
|
||||||
content: picture + content,
|
content: picture + content,
|
||||||
containerAttributes,
|
containerAttributes,
|
||||||
isBackgroundPicture: true,
|
isBackgroundPicture: true,
|
||||||
containerClassName,
|
containerClassName,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { link, style, htmlElement };
|
return { link, style, htmlElement };
|
||||||
}
|
}
|
||||||
|
|||||||
10
packages/imagetools/api/renderImg.d.ts
vendored
10
packages/imagetools/api/renderImg.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
import type { ImgConfigOptions, ImgHTMLData } from "../types";
|
import type { ImgConfigOptions, ImgHTMLData } from "../types";
|
||||||
|
|
||||||
export default function renderImg(
|
export default function renderImg(
|
||||||
config: ImgConfigOptions
|
config: ImgConfigOptions
|
||||||
): Promise<ImgHTMLData>;
|
): Promise<ImgHTMLData>;
|
||||||
|
|||||||
@ -1,93 +1,93 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import getImage from "./utils/getImage.js";
|
import getImage from "./utils/getImage.js";
|
||||||
import getImgElement from "./utils/getImgElement.js";
|
import getImgElement from "./utils/getImgElement.js";
|
||||||
import getLinkElement from "./utils/getLinkElement.js";
|
import getLinkElement from "./utils/getLinkElement.js";
|
||||||
import getStyleElement from "./utils/getStyleElement.js";
|
import getStyleElement from "./utils/getStyleElement.js";
|
||||||
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
||||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||||
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
||||||
|
|
||||||
export default async function renderImg(props) {
|
export default async function renderImg(props) {
|
||||||
const type = "Img";
|
const type = "Img";
|
||||||
|
|
||||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
sizes,
|
sizes,
|
||||||
preload,
|
preload,
|
||||||
loading,
|
loading,
|
||||||
decoding,
|
decoding,
|
||||||
attributes,
|
attributes,
|
||||||
layout,
|
layout,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
placeholder,
|
placeholder,
|
||||||
objectFit,
|
objectFit,
|
||||||
objectPosition,
|
objectPosition,
|
||||||
format,
|
format,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
} = filteredProps;
|
} = filteredProps;
|
||||||
|
|
||||||
const artDirectives = [],
|
const artDirectives = [],
|
||||||
fallbackFormat = format,
|
fallbackFormat = format,
|
||||||
fadeInTransition = false,
|
fadeInTransition = false,
|
||||||
includeSourceFormat = false;
|
includeSourceFormat = false;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
img: imgAttributes = {},
|
img: imgAttributes = {},
|
||||||
link: linkAttributes = {},
|
link: linkAttributes = {},
|
||||||
style: styleAttributes = {},
|
style: styleAttributes = {},
|
||||||
} = attributes;
|
} = attributes;
|
||||||
|
|
||||||
const { uuid, images } = await getImage({
|
const { uuid, images } = await getImage({
|
||||||
src,
|
src,
|
||||||
type,
|
type,
|
||||||
sizes,
|
sizes,
|
||||||
format,
|
format,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
placeholder,
|
placeholder,
|
||||||
artDirectives,
|
artDirectives,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
transformConfigs,
|
transformConfigs,
|
||||||
});
|
});
|
||||||
|
|
||||||
const className = `astro-imagetools-img-${uuid}`;
|
const className = `astro-imagetools-img-${uuid}`;
|
||||||
|
|
||||||
const { imagesizes } = images[images.length - 1];
|
const { imagesizes } = images[images.length - 1];
|
||||||
const backgroundStyles = getBackgroundStyles(
|
const backgroundStyles = getBackgroundStyles(
|
||||||
images,
|
images,
|
||||||
className,
|
className,
|
||||||
objectFit,
|
objectFit,
|
||||||
objectPosition,
|
objectPosition,
|
||||||
fadeInTransition,
|
fadeInTransition,
|
||||||
{ isImg: true }
|
{ isImg: true }
|
||||||
);
|
);
|
||||||
const style = getStyleElement({ styleAttributes, backgroundStyles })
|
const style = getStyleElement({ styleAttributes })
|
||||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes })
|
const link = getLinkElement({ images, preload, imagesizes, linkAttributes })
|
||||||
const layoutStyles = getLayoutStyles({ layout })
|
const layoutStyles = getLayoutStyles({ layout })
|
||||||
|
|
||||||
const sources = images.flatMap(({ sources, sizes, imagesizes }) =>
|
const sources = images.flatMap(({ sources, sizes, imagesizes }) =>
|
||||||
sources.map(({ src, srcset }) =>
|
sources.map(({ src, srcset }) =>
|
||||||
getImgElement({
|
getImgElement({
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
sizes,
|
sizes,
|
||||||
style,
|
style,
|
||||||
srcset,
|
srcset,
|
||||||
loading,
|
loading,
|
||||||
decoding,
|
decoding,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
fadeInTransition,
|
fadeInTransition,
|
||||||
layoutStyles,
|
layoutStyles,
|
||||||
imgAttributes,
|
imgAttributes,
|
||||||
imgClassName: className,
|
imgClassName: className,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
const [img] = sources
|
const [img] = sources
|
||||||
return { link, style, img }
|
return { link, style, img }
|
||||||
}
|
}
|
||||||
|
|||||||
10
packages/imagetools/api/renderPicture.d.ts
vendored
10
packages/imagetools/api/renderPicture.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
import type { PictureConfigOptions, PictureHTMLData } from "../types";
|
import type { PictureConfigOptions, PictureHTMLData } from "../types";
|
||||||
|
|
||||||
export default function renderPicture(
|
export default function renderPicture(
|
||||||
config: PictureConfigOptions
|
config: PictureConfigOptions
|
||||||
): Promise<PictureHTMLData>;
|
): Promise<PictureHTMLData>;
|
||||||
|
|||||||
@ -1,111 +1,111 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import getImage from "./utils/getImage.js";
|
import getImage from "./utils/getImage.js";
|
||||||
import getImgElement from "./utils/getImgElement.js";
|
import getImgElement from "./utils/getImgElement.js";
|
||||||
import getLinkElement from "./utils/getLinkElement.js";
|
import getLinkElement from "./utils/getLinkElement.js";
|
||||||
import getStyleElement from "./utils/getStyleElement.js";
|
import getStyleElement from "./utils/getStyleElement.js";
|
||||||
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
||||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||||
import getPictureElement from "./utils/getPictureElement.js";
|
import getPictureElement from "./utils/getPictureElement.js";
|
||||||
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
||||||
|
|
||||||
export default async function renderPicture(props) {
|
export default async function renderPicture(props) {
|
||||||
const type = "Picture";
|
const type = "Picture";
|
||||||
|
|
||||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
sizes,
|
sizes,
|
||||||
preload,
|
preload,
|
||||||
loading,
|
loading,
|
||||||
decoding,
|
decoding,
|
||||||
attributes,
|
attributes,
|
||||||
layout,
|
layout,
|
||||||
placeholder,
|
placeholder,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
objectFit,
|
objectFit,
|
||||||
objectPosition,
|
objectPosition,
|
||||||
format,
|
format,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
fadeInTransition,
|
fadeInTransition,
|
||||||
artDirectives,
|
artDirectives,
|
||||||
} = filteredProps;
|
} = filteredProps;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
img: imgAttributes = {},
|
img: imgAttributes = {},
|
||||||
link: linkAttributes = {},
|
link: linkAttributes = {},
|
||||||
style: styleAttributes = {},
|
style: styleAttributes = {},
|
||||||
picture: pictureAttributes = {},
|
picture: pictureAttributes = {},
|
||||||
} = attributes;
|
} = attributes;
|
||||||
|
|
||||||
const { uuid, images } = await getImage({
|
const { uuid, images } = await getImage({
|
||||||
src,
|
src,
|
||||||
type,
|
type,
|
||||||
sizes,
|
sizes,
|
||||||
format,
|
format,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
placeholder,
|
placeholder,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
artDirectives,
|
artDirectives,
|
||||||
transformConfigs,
|
transformConfigs,
|
||||||
});
|
});
|
||||||
|
|
||||||
const className = `astro-imagetools-picture-${uuid}`;
|
const className = `astro-imagetools-picture-${uuid}`;
|
||||||
|
|
||||||
const { imagesizes } = images[images.length - 1];
|
const { imagesizes } = images[images.length - 1];
|
||||||
|
|
||||||
const backgroundStyles = getBackgroundStyles(
|
const backgroundStyles = getBackgroundStyles(
|
||||||
images,
|
images,
|
||||||
className,
|
className,
|
||||||
objectFit,
|
objectFit,
|
||||||
objectPosition,
|
objectPosition,
|
||||||
fadeInTransition
|
fadeInTransition
|
||||||
);
|
);
|
||||||
|
|
||||||
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
||||||
|
|
||||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
||||||
|
|
||||||
const layoutStyles = getLayoutStyles({ layout });
|
const layoutStyles = getLayoutStyles({ layout });
|
||||||
|
|
||||||
const sources = images.flatMap(({ media, sources, sizes, imagesizes }) =>
|
const sources = images.flatMap(({ media, sources, sizes, imagesizes }) =>
|
||||||
sources.map(({ format, src, srcset }) =>
|
sources.map(({ format, src, srcset }) =>
|
||||||
src
|
src
|
||||||
? getImgElement({
|
? getImgElement({
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
sizes,
|
sizes,
|
||||||
style,
|
style,
|
||||||
srcset,
|
srcset,
|
||||||
loading,
|
loading,
|
||||||
decoding,
|
decoding,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
fadeInTransition,
|
fadeInTransition,
|
||||||
layoutStyles,
|
layoutStyles,
|
||||||
imgAttributes,
|
imgAttributes,
|
||||||
})
|
})
|
||||||
: `<source
|
: `<source
|
||||||
srcset="${srcset}"
|
srcset="${srcset}"
|
||||||
sizes="${imagesizes}"
|
sizes="${imagesizes}"
|
||||||
width="${sizes.width}"
|
width="${sizes.width}"
|
||||||
height="${sizes.height}"
|
height="${sizes.height}"
|
||||||
type="${`image/${format}`}"
|
type="${`image/${format}`}"
|
||||||
${media ? `media="${media}"` : ""}
|
${media ? `media="${media}"` : ""}
|
||||||
/>`
|
/>`
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const picture = getPictureElement({
|
const picture = getPictureElement({
|
||||||
sources,
|
sources,
|
||||||
className,
|
className,
|
||||||
layoutStyles,
|
layoutStyles,
|
||||||
pictureAttributes,
|
pictureAttributes,
|
||||||
});
|
});
|
||||||
|
|
||||||
return { link, style, picture };
|
return { link, style, picture };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,38 +1,38 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import { extname } from "node:path";
|
import { extname } from "node:path";
|
||||||
import * as codecs from "@astropub/codecs";
|
import * as codecs from "@astropub/codecs";
|
||||||
|
|
||||||
export async function getImageDetails(path, width, height, aspect) {
|
export async function getImageDetails(path, width, height, aspect) {
|
||||||
const extension = extname(path).slice(1);
|
const extension = extname(path).slice(1);
|
||||||
|
|
||||||
const imageFormat = extension === "jpeg" ? "jpg" : extension;
|
const imageFormat = extension === "jpeg" ? "jpg" : extension;
|
||||||
|
|
||||||
const buffer = fs.readFileSync(path);
|
const buffer = fs.readFileSync(path);
|
||||||
const decodedImage = await codecs.jpg.decode(buffer);
|
const decodedImage = await codecs.jpg.decode(buffer);
|
||||||
|
|
||||||
if (aspect && !width && !height) {
|
if (aspect && !width && !height) {
|
||||||
if (!width && !height) {
|
if (!width && !height) {
|
||||||
({ width } = decodedImage);
|
({ width } = decodedImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width) {
|
if (width) {
|
||||||
height = width / aspect;
|
height = width / aspect;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height) {
|
if (height) {
|
||||||
width = height * aspect;
|
width = height * aspect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const image = await decodedImage.resize({ width, height });
|
const image = await decodedImage.resize({ width, height });
|
||||||
|
|
||||||
const { width: imageWidth, height: imageHeight } = image;
|
const { width: imageWidth, height: imageHeight } = image;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
image,
|
image,
|
||||||
imageWidth,
|
imageWidth,
|
||||||
imageHeight,
|
imageHeight,
|
||||||
imageFormat,
|
imageFormat,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,137 +1,137 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import getSrcset from "./getSrcset.js";
|
import getSrcset from "./getSrcset.js";
|
||||||
import getConfigOptions from "./getConfigOptions.js";
|
import getConfigOptions from "./getConfigOptions.js";
|
||||||
import getFallbackImage from "./getFallbackImage.js";
|
import getFallbackImage from "./getFallbackImage.js";
|
||||||
import getProcessedImage from "./getProcessedImage.js";
|
import getProcessedImage from "./getProcessedImage.js";
|
||||||
|
|
||||||
export default async function getArtDirectedImages(
|
export default async function getArtDirectedImages(
|
||||||
artDirectives = [],
|
artDirectives = [],
|
||||||
placeholder,
|
placeholder,
|
||||||
format,
|
format,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
rest
|
rest
|
||||||
) {
|
) {
|
||||||
const images = await Promise.all(
|
const images = await Promise.all(
|
||||||
artDirectives.map(
|
artDirectives.map(
|
||||||
async ({
|
async ({
|
||||||
src,
|
src,
|
||||||
media,
|
media,
|
||||||
sizes: directiveImagesizes,
|
sizes: directiveImagesizes,
|
||||||
placeholder: directivePlaceholder,
|
placeholder: directivePlaceholder,
|
||||||
breakpoints: directiveBreakpoints,
|
breakpoints: directiveBreakpoints,
|
||||||
objectFit,
|
objectFit,
|
||||||
objectPosition,
|
objectPosition,
|
||||||
backgroundSize,
|
backgroundSize,
|
||||||
backgroundPosition,
|
backgroundPosition,
|
||||||
format: directiveFormat,
|
format: directiveFormat,
|
||||||
fallbackFormat: directiveFallbackFormat,
|
fallbackFormat: directiveFallbackFormat,
|
||||||
includeSourceFormat: directiveIncludeSourceFormat,
|
includeSourceFormat: directiveIncludeSourceFormat,
|
||||||
formatOptions: directiveFormatOptions = {},
|
formatOptions: directiveFormatOptions = {},
|
||||||
...configOptions
|
...configOptions
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
path,
|
path,
|
||||||
base,
|
base,
|
||||||
rest: rest2,
|
rest: rest2,
|
||||||
image,
|
image,
|
||||||
imageWidth,
|
imageWidth,
|
||||||
imageHeight,
|
imageHeight,
|
||||||
imageFormat,
|
imageFormat,
|
||||||
} = await getProcessedImage(src, configOptions);
|
} = await getProcessedImage(src, configOptions);
|
||||||
|
|
||||||
rest2.aspect = `${imageWidth / imageHeight}`;
|
rest2.aspect = `${imageWidth / imageHeight}`;
|
||||||
|
|
||||||
const calculatedConfigs = getConfigOptions(
|
const calculatedConfigs = getConfigOptions(
|
||||||
imageWidth,
|
imageWidth,
|
||||||
directiveImagesizes || imagesizes,
|
directiveImagesizes || imagesizes,
|
||||||
directiveBreakpoints || breakpoints,
|
directiveBreakpoints || breakpoints,
|
||||||
directiveFormat || format,
|
directiveFormat || format,
|
||||||
imageFormat,
|
imageFormat,
|
||||||
directiveFallbackFormat || fallbackFormat,
|
directiveFallbackFormat || fallbackFormat,
|
||||||
directiveIncludeSourceFormat || includeSourceFormat
|
directiveIncludeSourceFormat || includeSourceFormat
|
||||||
);
|
);
|
||||||
|
|
||||||
const { formats, requiredBreakpoints } = calculatedConfigs;
|
const { formats, requiredBreakpoints } = calculatedConfigs;
|
||||||
|
|
||||||
imagesizes = calculatedConfigs.imagesizes;
|
imagesizes = calculatedConfigs.imagesizes;
|
||||||
|
|
||||||
const maxWidth = requiredBreakpoints[requiredBreakpoints.length - 1];
|
const maxWidth = requiredBreakpoints[requiredBreakpoints.length - 1];
|
||||||
|
|
||||||
const sources = await Promise.all(
|
const sources = await Promise.all(
|
||||||
formats.map(async (format) => {
|
formats.map(async (format) => {
|
||||||
const srcset = await getSrcset(
|
const srcset = await getSrcset(
|
||||||
path,
|
path,
|
||||||
base,
|
base,
|
||||||
requiredBreakpoints,
|
requiredBreakpoints,
|
||||||
format,
|
format,
|
||||||
{
|
{
|
||||||
...rest,
|
...rest,
|
||||||
...rest2,
|
...rest2,
|
||||||
...formatOptions[format],
|
...formatOptions[format],
|
||||||
...directiveFormatOptions[format],
|
...directiveFormatOptions[format],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
format,
|
format,
|
||||||
srcset,
|
srcset,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const sizes = {
|
const sizes = {
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: Math.round(maxWidth / rest2.aspect),
|
height: Math.round(maxWidth / rest2.aspect),
|
||||||
};
|
};
|
||||||
|
|
||||||
const object = {
|
const object = {
|
||||||
fit: objectFit,
|
fit: objectFit,
|
||||||
position: objectPosition,
|
position: objectPosition,
|
||||||
};
|
};
|
||||||
|
|
||||||
const background = {
|
const background = {
|
||||||
size: backgroundSize,
|
size: backgroundSize,
|
||||||
position: backgroundPosition,
|
position: backgroundPosition,
|
||||||
};
|
};
|
||||||
|
|
||||||
const fallback = await getFallbackImage(
|
const fallback = await getFallbackImage(
|
||||||
path,
|
path,
|
||||||
directivePlaceholder || placeholder,
|
directivePlaceholder || placeholder,
|
||||||
image,
|
image,
|
||||||
imageFormat,
|
imageFormat,
|
||||||
{ ...formatOptions, ...directiveFormatOptions },
|
{ ...formatOptions, ...directiveFormatOptions },
|
||||||
{ ...rest, ...rest2 }
|
{ ...rest, ...rest2 }
|
||||||
);
|
);
|
||||||
|
|
||||||
const returnValue = {
|
const returnValue = {
|
||||||
media,
|
media,
|
||||||
sources,
|
sources,
|
||||||
sizes,
|
sizes,
|
||||||
fallback,
|
fallback,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
};
|
};
|
||||||
|
|
||||||
const isBackgroundImage = !!backgroundSize || !!backgroundPosition;
|
const isBackgroundImage = !!backgroundSize || !!backgroundPosition;
|
||||||
|
|
||||||
isBackgroundImage
|
isBackgroundImage
|
||||||
? (returnValue.background = background)
|
? (returnValue.background = background)
|
||||||
: (returnValue.object = object);
|
: (returnValue.object = object);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
media,
|
media,
|
||||||
sources,
|
sources,
|
||||||
sizes,
|
sizes,
|
||||||
object,
|
object,
|
||||||
fallback,
|
fallback,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
return images;
|
return images;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,27 +1,27 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
import printWarning from "../../utils/printWarning.js";
|
import printWarning from "../../utils/printWarning.js";
|
||||||
|
|
||||||
export default function getAttributesString({
|
export default function getAttributesString({
|
||||||
attributes,
|
attributes,
|
||||||
element = "",
|
element = "",
|
||||||
excludeArray = [],
|
excludeArray = [],
|
||||||
}) {
|
}) {
|
||||||
const attributesString = Object.keys(attributes)
|
const attributesString = Object.keys(attributes)
|
||||||
.filter((key) => {
|
.filter((key) => {
|
||||||
if (excludeArray.includes(key)) {
|
if (excludeArray.includes(key)) {
|
||||||
printWarning({
|
printWarning({
|
||||||
key,
|
key,
|
||||||
element,
|
element,
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
.map((key) => `${key}="${attributes[key]}"`)
|
.map((key) => `${key}="${attributes[key]}"`)
|
||||||
.join(" ");
|
.join(" ");
|
||||||
|
|
||||||
return attributesString;
|
return attributesString;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,97 +1,97 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
export default function getBackgroundStyles(
|
export default function getBackgroundStyles(
|
||||||
images,
|
images,
|
||||||
className,
|
className,
|
||||||
objectFit,
|
objectFit,
|
||||||
objectPosition,
|
objectPosition,
|
||||||
fadeInTransition,
|
fadeInTransition,
|
||||||
{ isImg = false, isBackgroundPicture = false, containerClassName = "" } = {}
|
{ isImg = false, isBackgroundPicture = false, containerClassName = "" } = {}
|
||||||
) {
|
) {
|
||||||
const sourcesWithFallback = images.filter(({ fallback }) => fallback);
|
const sourcesWithFallback = images.filter(({ fallback }) => fallback);
|
||||||
|
|
||||||
if (sourcesWithFallback.length === 0) return "";
|
if (sourcesWithFallback.length === 0) return "";
|
||||||
|
|
||||||
const staticStyles = !fadeInTransition
|
const staticStyles = !fadeInTransition
|
||||||
? ""
|
? ""
|
||||||
: `
|
: `
|
||||||
${
|
${
|
||||||
isBackgroundPicture
|
isBackgroundPicture
|
||||||
? `
|
? `
|
||||||
.${containerClassName} * {
|
.${containerClassName} * {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
.${className} {
|
.${className} {
|
||||||
--opacity: 1;
|
--opacity: 1;
|
||||||
--z-index: 0;
|
--z-index: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
${
|
${
|
||||||
!isBackgroundPicture
|
!isBackgroundPicture
|
||||||
? `
|
? `
|
||||||
.${className} img {
|
.${className} img {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
.${className}::after {
|
.${className}::after {
|
||||||
inset: 0;
|
inset: 0;
|
||||||
content: "";
|
content: "";
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
transition: opacity ${
|
transition: opacity ${
|
||||||
typeof fadeInTransition !== "object"
|
typeof fadeInTransition !== "object"
|
||||||
? "1s"
|
? "1s"
|
||||||
: (() => {
|
: (() => {
|
||||||
const {
|
const {
|
||||||
delay = "0s",
|
delay = "0s",
|
||||||
duration = "1s",
|
duration = "1s",
|
||||||
timingFunction = "ease",
|
timingFunction = "ease",
|
||||||
} = fadeInTransition;
|
} = fadeInTransition;
|
||||||
|
|
||||||
return `${duration} ${timingFunction} ${delay}`;
|
return `${duration} ${timingFunction} ${delay}`;
|
||||||
})()
|
})()
|
||||||
};
|
};
|
||||||
opacity: var(--opacity);
|
opacity: var(--opacity);
|
||||||
z-index: var(--z-index);
|
z-index: var(--z-index);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const dynamicStyles = images
|
const dynamicStyles = images
|
||||||
.map(({ media, fallback, object }) => {
|
.map(({ media, fallback, object }) => {
|
||||||
const elementSelector = className + (!isImg ? " img" : ""),
|
const elementSelector = className + (!isImg ? " img" : ""),
|
||||||
backgroundElementSelector =
|
backgroundElementSelector =
|
||||||
className + (fadeInTransition ? "::after" : "");
|
className + (fadeInTransition ? "::after" : "");
|
||||||
|
|
||||||
const style = `
|
const style = `
|
||||||
.${elementSelector} {
|
.${elementSelector} {
|
||||||
object-fit: ${object?.fit || objectFit};
|
object-fit: ${object?.fit || objectFit};
|
||||||
object-position: ${object?.position || objectPosition};
|
object-position: ${object?.position || objectPosition};
|
||||||
}
|
}
|
||||||
|
|
||||||
.${backgroundElementSelector} {
|
.${backgroundElementSelector} {
|
||||||
background-size: ${object?.fit || objectFit};
|
background-size: ${object?.fit || objectFit};
|
||||||
background-image: url("${encodeURI(fallback)}");
|
background-image: url("${encodeURI(fallback)}");
|
||||||
background-position: ${object?.position || objectPosition};
|
background-position: ${object?.position || objectPosition};
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return media ? `@media ${media} { ${style} }` : style;
|
return media ? `@media ${media} { ${style} }` : style;
|
||||||
})
|
})
|
||||||
.reverse();
|
.reverse();
|
||||||
|
|
||||||
const backgroundStyles = [staticStyles, ...dynamicStyles].join("");
|
const backgroundStyles = [staticStyles, ...dynamicStyles].join("");
|
||||||
|
|
||||||
return backgroundStyles;
|
return backgroundStyles;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,77 +1,77 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import printWarning from "../../utils/printWarning.js";
|
import printWarning from "../../utils/printWarning.js";
|
||||||
|
|
||||||
export default function getBreakpoints(breakpoints, imageWidth) {
|
export default function getBreakpoints(breakpoints, imageWidth) {
|
||||||
if (Array.isArray(breakpoints)) {
|
if (Array.isArray(breakpoints)) {
|
||||||
return breakpoints.sort((a, b) => a - b);
|
return breakpoints.sort((a, b) => a - b);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { count, minWidth = 320 } = breakpoints || {};
|
const { count, minWidth = 320 } = breakpoints || {};
|
||||||
|
|
||||||
const maxWidth = (() => {
|
const maxWidth = (() => {
|
||||||
if (breakpoints?.maxWidth) return breakpoints.maxWidth;
|
if (breakpoints?.maxWidth) return breakpoints.maxWidth;
|
||||||
|
|
||||||
if (imageWidth > 3840) {
|
if (imageWidth > 3840) {
|
||||||
printWarning({
|
printWarning({
|
||||||
message:
|
message:
|
||||||
"The width of the source image is greater than 3840px. The generated breakpoints will be capped at 3840px. If you need breakpoints larger than this, please pass the maxWidth option to the breakpoints property.",
|
"The width of the source image is greater than 3840px. The generated breakpoints will be capped at 3840px. If you need breakpoints larger than this, please pass the maxWidth option to the breakpoints property.",
|
||||||
});
|
});
|
||||||
|
|
||||||
return 3840;
|
return 3840;
|
||||||
}
|
}
|
||||||
|
|
||||||
return imageWidth;
|
return imageWidth;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
const breakPoints = [];
|
const breakPoints = [];
|
||||||
|
|
||||||
const diff = maxWidth - minWidth;
|
const diff = maxWidth - minWidth;
|
||||||
|
|
||||||
const n =
|
const n =
|
||||||
count ||
|
count ||
|
||||||
(maxWidth <= 400
|
(maxWidth <= 400
|
||||||
? 1
|
? 1
|
||||||
: maxWidth <= 640
|
: maxWidth <= 640
|
||||||
? 2
|
? 2
|
||||||
: maxWidth <= 800
|
: maxWidth <= 800
|
||||||
? 3
|
? 3
|
||||||
: maxWidth <= 1024
|
: maxWidth <= 1024
|
||||||
? 4
|
? 4
|
||||||
: maxWidth <= 1280
|
: maxWidth <= 1280
|
||||||
? 5
|
? 5
|
||||||
: maxWidth <= 1440
|
: maxWidth <= 1440
|
||||||
? 6
|
? 6
|
||||||
: maxWidth <= 1920
|
: maxWidth <= 1920
|
||||||
? 7
|
? 7
|
||||||
: maxWidth <= 2560
|
: maxWidth <= 2560
|
||||||
? 8
|
? 8
|
||||||
: maxWidth <= 2880
|
: maxWidth <= 2880
|
||||||
? 9
|
? 9
|
||||||
: maxWidth <= 3840
|
: maxWidth <= 3840
|
||||||
? 10
|
? 10
|
||||||
: 11);
|
: 11);
|
||||||
|
|
||||||
let currentWidth = minWidth;
|
let currentWidth = minWidth;
|
||||||
|
|
||||||
n > 1 && breakPoints.push(currentWidth);
|
n > 1 && breakPoints.push(currentWidth);
|
||||||
|
|
||||||
let steps = 0;
|
let steps = 0;
|
||||||
|
|
||||||
for (let i = 1; i < n; i++) {
|
for (let i = 1; i < n; i++) {
|
||||||
steps += i;
|
steps += i;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pixelsPerStep = diff / steps;
|
const pixelsPerStep = diff / steps;
|
||||||
|
|
||||||
for (let i = 1; i < n - 1; i++) {
|
for (let i = 1; i < n - 1; i++) {
|
||||||
const next = pixelsPerStep * (n - i) + currentWidth;
|
const next = pixelsPerStep * (n - i) + currentWidth;
|
||||||
|
|
||||||
breakPoints.push(Math.round(next));
|
breakPoints.push(Math.round(next));
|
||||||
|
|
||||||
currentWidth = next;
|
currentWidth = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
breakPoints.push(maxWidth);
|
breakPoints.push(maxWidth);
|
||||||
|
|
||||||
return [...new Set(breakPoints)];
|
return [...new Set(breakPoints)];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,34 +1,34 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import getBreakpoints from "./getBreakpoints.js";
|
import getBreakpoints from "./getBreakpoints.js";
|
||||||
|
|
||||||
export default function getConfigOptions(
|
export default function getConfigOptions(
|
||||||
imageWidth,
|
imageWidth,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
format,
|
format,
|
||||||
imageFormat,
|
imageFormat,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat
|
includeSourceFormat
|
||||||
) {
|
) {
|
||||||
const formats = [
|
const formats = [
|
||||||
...new Set(
|
...new Set(
|
||||||
[format, includeSourceFormat && imageFormat]
|
[format, includeSourceFormat && imageFormat]
|
||||||
.flat()
|
.flat()
|
||||||
.filter((f) => f && f !== fallbackFormat)
|
.filter((f) => f && f !== fallbackFormat)
|
||||||
),
|
),
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
];
|
];
|
||||||
|
|
||||||
const requiredBreakpoints = getBreakpoints(breakpoints, imageWidth);
|
const requiredBreakpoints = getBreakpoints(breakpoints, imageWidth);
|
||||||
|
|
||||||
imagesizes =
|
imagesizes =
|
||||||
typeof imagesizes === "string"
|
typeof imagesizes === "string"
|
||||||
? imagesizes
|
? imagesizes
|
||||||
: imagesizes(requiredBreakpoints);
|
: imagesizes(requiredBreakpoints);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
formats,
|
formats,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
requiredBreakpoints,
|
requiredBreakpoints,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,48 +1,48 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import getAttributesString from "./getAttributesString.js";
|
import getAttributesString from "./getAttributesString.js";
|
||||||
|
|
||||||
export default function getContainerElement({
|
export default function getContainerElement({
|
||||||
tag,
|
tag,
|
||||||
content,
|
content,
|
||||||
className = "",
|
className = "",
|
||||||
containerAttributes,
|
containerAttributes,
|
||||||
isBackgroundPicture = false,
|
isBackgroundPicture = false,
|
||||||
containerClassName = "",
|
containerClassName = "",
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
class: customClasses = "",
|
class: customClasses = "",
|
||||||
style: customInlineStyles = "",
|
style: customInlineStyles = "",
|
||||||
...restContainerAttributes
|
...restContainerAttributes
|
||||||
} = containerAttributes;
|
} = containerAttributes;
|
||||||
|
|
||||||
const attributesString = getAttributesString({
|
const attributesString = getAttributesString({
|
||||||
attributes: restContainerAttributes,
|
attributes: restContainerAttributes,
|
||||||
});
|
});
|
||||||
|
|
||||||
const classAttribute = [
|
const classAttribute = [
|
||||||
isBackgroundPicture
|
isBackgroundPicture
|
||||||
? "astro-imagetools-background-picture"
|
? "astro-imagetools-background-picture"
|
||||||
: "astro-imagetools-background-image",
|
: "astro-imagetools-background-image",
|
||||||
isBackgroundPicture ? containerClassName : className,
|
isBackgroundPicture ? containerClassName : className,
|
||||||
customClasses,
|
customClasses,
|
||||||
]
|
]
|
||||||
.join(" ")
|
.join(" ")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
const styleAttribute = [
|
const styleAttribute = [
|
||||||
isBackgroundPicture ? "position: relative;" : "",
|
isBackgroundPicture ? "position: relative;" : "",
|
||||||
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
||||||
]
|
]
|
||||||
.join(" ")
|
.join(" ")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
const containerElement = `<${tag}
|
const containerElement = `<${tag}
|
||||||
${attributesString}
|
${attributesString}
|
||||||
class="${classAttribute}"
|
class="${classAttribute}"
|
||||||
style="${styleAttribute}"
|
style="${styleAttribute}"
|
||||||
>
|
>
|
||||||
${content}
|
${content}
|
||||||
</${tag}>`;
|
</${tag}>`;
|
||||||
|
|
||||||
return containerElement;
|
return containerElement;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,58 +1,58 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
import util from "node:util";
|
import util from "node:util";
|
||||||
import potrace from "potrace";
|
import potrace from "potrace";
|
||||||
import getSrcset from "./getSrcset.js";
|
import getSrcset from "./getSrcset.js";
|
||||||
import { sharp } from "../../utils/runtimeChecks.js";
|
import { sharp } from "../../utils/runtimeChecks.js";
|
||||||
|
|
||||||
export default async function getFallbackImage(
|
export default async function getFallbackImage(
|
||||||
src,
|
src,
|
||||||
placeholder,
|
placeholder,
|
||||||
image,
|
image,
|
||||||
format,
|
format,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
rest
|
rest
|
||||||
) {
|
) {
|
||||||
const base = null;
|
const base = null;
|
||||||
|
|
||||||
switch (placeholder) {
|
switch (placeholder) {
|
||||||
case "blurred": {
|
case "blurred": {
|
||||||
const dataUri = await getSrcset(src, base, [20], format, {
|
const dataUri = await getSrcset(src, base, [20], format, {
|
||||||
inline: true,
|
inline: true,
|
||||||
...rest,
|
...rest,
|
||||||
...formatOptions[format],
|
...formatOptions[format],
|
||||||
});
|
});
|
||||||
|
|
||||||
return dataUri;
|
return dataUri;
|
||||||
}
|
}
|
||||||
case "tracedSVG": {
|
case "tracedSVG": {
|
||||||
const { function: fn, options } = formatOptions.tracedSVG;
|
const { function: fn, options } = formatOptions.tracedSVG;
|
||||||
|
|
||||||
const traceSVG = util.promisify(potrace[fn]);
|
const traceSVG = util.promisify(potrace[fn]);
|
||||||
|
|
||||||
const imageBuffer = sharp
|
const imageBuffer = sharp
|
||||||
? await image.toBuffer()
|
? await image.toBuffer()
|
||||||
: Buffer.from(
|
: Buffer.from(
|
||||||
(await image.encode(`image/${format === "jpg" ? "jpeg" : format}`))
|
(await image.encode(`image/${format === "jpg" ? "jpeg" : format}`))
|
||||||
.data
|
.data
|
||||||
);
|
);
|
||||||
|
|
||||||
const tracedSVG = await traceSVG(imageBuffer, options);
|
const tracedSVG = await traceSVG(imageBuffer, options);
|
||||||
|
|
||||||
return `data:image/svg+xml;utf8,${tracedSVG}`;
|
return `data:image/svg+xml;utf8,${tracedSVG}`;
|
||||||
}
|
}
|
||||||
case "dominantColor": {
|
case "dominantColor": {
|
||||||
if (sharp) {
|
if (sharp) {
|
||||||
var { r, g, b } = (await image.stats()).dominant;
|
var { r, g, b } = (await image.stats()).dominant;
|
||||||
} else {
|
} else {
|
||||||
[r, g, b] = image.color;
|
[r, g, b] = image.color;
|
||||||
}
|
}
|
||||||
|
|
||||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" style="background: rgb(${r},${g},${b})"></svg>`;
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" style="background: rgb(${r},${g},${b})"></svg>`;
|
||||||
|
|
||||||
return `data:image/svg+xml;utf8,${svg}`;
|
return `data:image/svg+xml;utf8,${svg}`;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,138 +1,138 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import filterConfigs from "../../utils/filterConfigs.js";
|
import filterConfigs from "../../utils/filterConfigs.js";
|
||||||
import {
|
import {
|
||||||
supportedConfigs,
|
supportedConfigs,
|
||||||
GlobalConfigOptions,
|
GlobalConfigOptions,
|
||||||
} from "../../utils/runtimeChecks.js";
|
} from "../../utils/runtimeChecks.js";
|
||||||
|
|
||||||
const GlobalOnlyProperties = ["cacheDir", "assetFileNames"];
|
const GlobalOnlyProperties = ["cacheDir", "assetFileNames"];
|
||||||
|
|
||||||
const NonGlobalSupportedConfigs = supportedConfigs.filter(
|
const NonGlobalSupportedConfigs = supportedConfigs.filter(
|
||||||
(key) => !GlobalOnlyProperties.includes(key)
|
(key) => !GlobalOnlyProperties.includes(key)
|
||||||
);
|
);
|
||||||
|
|
||||||
const NonProperties = {
|
const NonProperties = {
|
||||||
Img: [
|
Img: [
|
||||||
"tag",
|
"tag",
|
||||||
"content",
|
"content",
|
||||||
"backgroundSize",
|
"backgroundSize",
|
||||||
"backgroundPosition",
|
"backgroundPosition",
|
||||||
"fallbackFormat",
|
"fallbackFormat",
|
||||||
"includeSourceFormat",
|
"includeSourceFormat",
|
||||||
"fadeInTransition",
|
"fadeInTransition",
|
||||||
"artDirectives",
|
"artDirectives",
|
||||||
],
|
],
|
||||||
Picture: ["tag", "content", "backgroundSize", "backgroundPosition"],
|
Picture: ["tag", "content", "backgroundSize", "backgroundPosition"],
|
||||||
BackgroundImage: [
|
BackgroundImage: [
|
||||||
"alt",
|
"alt",
|
||||||
"loading",
|
"loading",
|
||||||
"decoding",
|
"decoding",
|
||||||
"layout",
|
"layout",
|
||||||
"objectFit",
|
"objectFit",
|
||||||
"objectPosition",
|
"objectPosition",
|
||||||
"fadeInTransition",
|
"fadeInTransition",
|
||||||
],
|
],
|
||||||
BackgroundPicture: ["alt", "backgroundSize", "backgroundPosition"],
|
BackgroundPicture: ["alt", "backgroundSize", "backgroundPosition"],
|
||||||
};
|
};
|
||||||
|
|
||||||
const ImgProperties = NonGlobalSupportedConfigs.filter(
|
const ImgProperties = NonGlobalSupportedConfigs.filter(
|
||||||
(key) => !NonProperties.Img.includes(key)
|
(key) => !NonProperties.Img.includes(key)
|
||||||
),
|
),
|
||||||
PictureProperties = NonGlobalSupportedConfigs.filter(
|
PictureProperties = NonGlobalSupportedConfigs.filter(
|
||||||
(key) => !NonProperties.Picture.includes(key)
|
(key) => !NonProperties.Picture.includes(key)
|
||||||
),
|
),
|
||||||
BackgroundImageProperties = NonGlobalSupportedConfigs.filter(
|
BackgroundImageProperties = NonGlobalSupportedConfigs.filter(
|
||||||
(key) => !NonProperties.BackgroundImage.includes(key)
|
(key) => !NonProperties.BackgroundImage.includes(key)
|
||||||
),
|
),
|
||||||
BackgroundPictureProperties = NonGlobalSupportedConfigs.filter(
|
BackgroundPictureProperties = NonGlobalSupportedConfigs.filter(
|
||||||
(key) => !NonProperties.BackgroundPicture.includes(key)
|
(key) => !NonProperties.BackgroundPicture.includes(key)
|
||||||
);
|
);
|
||||||
|
|
||||||
const SupportedProperties = {
|
const SupportedProperties = {
|
||||||
Img: ImgProperties,
|
Img: ImgProperties,
|
||||||
Picture: PictureProperties,
|
Picture: PictureProperties,
|
||||||
BackgroundImage: BackgroundImageProperties,
|
BackgroundImage: BackgroundImageProperties,
|
||||||
BackgroundPicture: BackgroundPictureProperties,
|
BackgroundPicture: BackgroundPictureProperties,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function getFilteredProps(type, props) {
|
export default function getFilteredProps(type, props) {
|
||||||
const filteredGlobalConfigs = filterConfigs(
|
const filteredGlobalConfigs = filterConfigs(
|
||||||
"Global",
|
"Global",
|
||||||
GlobalConfigOptions,
|
GlobalConfigOptions,
|
||||||
SupportedProperties[type],
|
SupportedProperties[type],
|
||||||
{ warn: false }
|
{ warn: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
const { search, searchParams } = new URL(props.src, "file://");
|
const { search, searchParams } = new URL(props.src, "file://");
|
||||||
|
|
||||||
props.src = props.src.replace(search, "");
|
props.src = props.src.replace(search, "");
|
||||||
|
|
||||||
const paramOptions = Object.fromEntries(searchParams);
|
const paramOptions = Object.fromEntries(searchParams);
|
||||||
|
|
||||||
const filteredLocalProps = filterConfigs(
|
const filteredLocalProps = filterConfigs(
|
||||||
type,
|
type,
|
||||||
{
|
{
|
||||||
...paramOptions,
|
...paramOptions,
|
||||||
...props,
|
...props,
|
||||||
},
|
},
|
||||||
SupportedProperties[type]
|
SupportedProperties[type]
|
||||||
);
|
);
|
||||||
|
|
||||||
const resolvedProps = {
|
const resolvedProps = {
|
||||||
...filteredGlobalConfigs,
|
...filteredGlobalConfigs,
|
||||||
...filteredLocalProps,
|
...filteredLocalProps,
|
||||||
};
|
};
|
||||||
|
|
||||||
const {
|
const {
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
tag = "section",
|
tag = "section",
|
||||||
content = "",
|
content = "",
|
||||||
sizes = function (breakpoints) {
|
sizes = function (breakpoints) {
|
||||||
const maxWidth = breakpoints[breakpoints.length - 1];
|
const maxWidth = breakpoints[breakpoints.length - 1];
|
||||||
return `(min-width: ${maxWidth}px) ${maxWidth}px, 100vw`;
|
return `(min-width: ${maxWidth}px) ${maxWidth}px, 100vw`;
|
||||||
},
|
},
|
||||||
preload,
|
preload,
|
||||||
loading = preload ? "eager" : "lazy",
|
loading = preload ? "eager" : "lazy",
|
||||||
decoding = "async",
|
decoding = "async",
|
||||||
attributes = {},
|
attributes = {},
|
||||||
layout = "constrained",
|
layout = "constrained",
|
||||||
placeholder = "blurred",
|
placeholder = "blurred",
|
||||||
breakpoints,
|
breakpoints,
|
||||||
objectFit = "cover",
|
objectFit = "cover",
|
||||||
objectPosition = "50% 50%",
|
objectPosition = "50% 50%",
|
||||||
backgroundSize = "cover",
|
backgroundSize = "cover",
|
||||||
backgroundPosition = "50% 50%",
|
backgroundPosition = "50% 50%",
|
||||||
format = type === "Img" ? undefined : ["avif", "webp"],
|
format = type === "Img" ? undefined : ["avif", "webp"],
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat = true,
|
includeSourceFormat = true,
|
||||||
formatOptions = {
|
formatOptions = {
|
||||||
tracedSVG: {
|
tracedSVG: {
|
||||||
function: "trace",
|
function: "trace",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
fadeInTransition = true,
|
fadeInTransition = true,
|
||||||
artDirectives,
|
artDirectives,
|
||||||
...transformConfigs
|
...transformConfigs
|
||||||
} = resolvedProps;
|
} = resolvedProps;
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
const allProps = {
|
const allProps = {
|
||||||
src, alt, tag, content, sizes, preload, loading, decoding, attributes, layout, placeholder,
|
src, alt, tag, content, sizes, preload, loading, decoding, attributes, layout, placeholder,
|
||||||
breakpoints, objectFit, objectPosition, backgroundSize, backgroundPosition, format,
|
breakpoints, objectFit, objectPosition, backgroundSize, backgroundPosition, format,
|
||||||
fallbackFormat, includeSourceFormat, formatOptions, fadeInTransition, artDirectives,
|
fallbackFormat, includeSourceFormat, formatOptions, fadeInTransition, artDirectives,
|
||||||
...transformConfigs,
|
...transformConfigs,
|
||||||
};
|
};
|
||||||
|
|
||||||
const filteredProps = filterConfigs(
|
const filteredProps = filterConfigs(
|
||||||
type,
|
type,
|
||||||
allProps,
|
allProps,
|
||||||
SupportedProperties[type],
|
SupportedProperties[type],
|
||||||
{ warn: false }
|
{ warn: false }
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
filteredProps,
|
filteredProps,
|
||||||
transformConfigs,
|
transformConfigs,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,49 +1,49 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import getFilteredProps from "./getFilteredProps";
|
import getFilteredProps from "./getFilteredProps";
|
||||||
|
|
||||||
describe("getFilteredProps", () => {
|
describe("getFilteredProps", () => {
|
||||||
it("should should merge in default props", () => {
|
it("should should merge in default props", () => {
|
||||||
const result = getFilteredProps("Img", { src: "/img.jpeg", alt: "alt" });
|
const result = getFilteredProps("Img", { src: "/img.jpeg", alt: "alt" });
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
filteredProps: {
|
filteredProps: {
|
||||||
alt: "alt",
|
alt: "alt",
|
||||||
attributes: {},
|
attributes: {},
|
||||||
breakpoints: undefined,
|
breakpoints: undefined,
|
||||||
decoding: "async",
|
decoding: "async",
|
||||||
format: undefined,
|
format: undefined,
|
||||||
formatOptions: {
|
formatOptions: {
|
||||||
tracedSVG: {
|
tracedSVG: {
|
||||||
function: "trace",
|
function: "trace",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
layout: "constrained",
|
layout: "constrained",
|
||||||
loading: "lazy",
|
loading: "lazy",
|
||||||
objectFit: "cover",
|
objectFit: "cover",
|
||||||
objectPosition: "50% 50%",
|
objectPosition: "50% 50%",
|
||||||
placeholder: "blurred",
|
placeholder: "blurred",
|
||||||
preload: undefined,
|
preload: undefined,
|
||||||
sizes: expect.any(Function),
|
sizes: expect.any(Function),
|
||||||
src: "/img.jpeg",
|
src: "/img.jpeg",
|
||||||
},
|
},
|
||||||
transformConfigs: {},
|
transformConfigs: {},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should accept empty string for `alt` prop on Img", () => {
|
it("should accept empty string for `alt` prop on Img", () => {
|
||||||
const result = getFilteredProps("Img", { src: "/img.jpeg", alt: "" });
|
const result = getFilteredProps("Img", { src: "/img.jpeg", alt: "" });
|
||||||
expect(result).toMatchObject({
|
expect(result).toMatchObject({
|
||||||
filteredProps: {
|
filteredProps: {
|
||||||
alt: "",
|
alt: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should accept empty string for `alt` prop on Picture", () => {
|
it("should accept empty string for `alt` prop on Picture", () => {
|
||||||
const result = getFilteredProps("Picture", { src: "/img.jpeg", alt: "" });
|
const result = getFilteredProps("Picture", { src: "/img.jpeg", alt: "" });
|
||||||
expect(result).toMatchObject({
|
expect(result).toMatchObject({
|
||||||
filteredProps: {
|
filteredProps: {
|
||||||
alt: "",
|
alt: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,107 +1,107 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
import objectHash from "object-hash";
|
import objectHash from "object-hash";
|
||||||
import getImageSources from "./getImageSources.js";
|
import getImageSources from "./getImageSources.js";
|
||||||
import getProcessedImage from "./getProcessedImage.js";
|
import getProcessedImage from "./getProcessedImage.js";
|
||||||
import getArtDirectedImages from "./getArtDirectedImages.js";
|
import getArtDirectedImages from "./getArtDirectedImages.js";
|
||||||
import pMap from "p-map";
|
import pMap from "p-map";
|
||||||
|
|
||||||
const imagesData = new Map();
|
const imagesData = new Map();
|
||||||
|
|
||||||
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
|
|
||||||
export default async function ({
|
export default async function ({
|
||||||
src,
|
src,
|
||||||
type,
|
type,
|
||||||
sizes: imagesizes,
|
sizes: imagesizes,
|
||||||
format,
|
format,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
placeholder,
|
placeholder,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
artDirectives,
|
artDirectives,
|
||||||
transformConfigs,
|
transformConfigs,
|
||||||
}) {
|
}) {
|
||||||
try {
|
try {
|
||||||
const args = Array.from(arguments);
|
const args = Array.from(arguments);
|
||||||
const hash = objectHash(args);
|
const hash = objectHash(args);
|
||||||
if (imagesData.has(hash)) {
|
if (imagesData.has(hash)) {
|
||||||
return imagesData.get(hash);
|
return imagesData.get(hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
|
|
||||||
const { path, base, rest, image, imageWidth, imageHeight, imageFormat } =
|
const { path, base, rest, image, imageWidth, imageHeight, imageFormat } =
|
||||||
await getProcessedImage(src, transformConfigs);
|
await getProcessedImage(src, transformConfigs);
|
||||||
|
|
||||||
await delay(250);
|
await delay(100);
|
||||||
src = path;
|
src = path;
|
||||||
|
|
||||||
rest.aspect = `${imageWidth / imageHeight}`;
|
rest.aspect = `${imageWidth / imageHeight}`;
|
||||||
if (!fallbackFormat) {
|
if (!fallbackFormat) {
|
||||||
fallbackFormat = imageFormat;
|
fallbackFormat = imageFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch both image sources and art-directed images
|
// Fetch both image sources and art-directed images
|
||||||
const [mainImage, artDirectedImages] = await pMap(
|
const [mainImage, artDirectedImages] = await pMap(
|
||||||
[
|
[
|
||||||
async () =>
|
async () =>
|
||||||
await getImageSources(
|
await getImageSources(
|
||||||
src,
|
src,
|
||||||
base,
|
base,
|
||||||
image,
|
image,
|
||||||
format,
|
format,
|
||||||
imageWidth,
|
imageWidth,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
placeholder,
|
placeholder,
|
||||||
imageFormat,
|
imageFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
rest
|
rest
|
||||||
),
|
),
|
||||||
async () => {
|
async () => {
|
||||||
await delay(250);
|
await delay(100);
|
||||||
return await getArtDirectedImages(
|
return await getArtDirectedImages(
|
||||||
artDirectives,
|
artDirectives,
|
||||||
placeholder,
|
placeholder,
|
||||||
format,
|
format,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
rest
|
rest
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
async (task) => await task(),
|
async (task) => await task(),
|
||||||
{ concurrency: 1 }
|
{ concurrency: 1 }
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ensure artDirectedImages is an array
|
// Ensure artDirectedImages is an array
|
||||||
const images = Array.isArray(artDirectedImages) ? [...artDirectedImages, mainImage] : [mainImage];
|
const images = Array.isArray(artDirectedImages) ? [...artDirectedImages, mainImage] : [mainImage];
|
||||||
|
|
||||||
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
||||||
|
|
||||||
const returnObject = {
|
const returnObject = {
|
||||||
uuid,
|
uuid,
|
||||||
images,
|
images,
|
||||||
};
|
};
|
||||||
|
|
||||||
imagesData.set(hash, returnObject);
|
imagesData.set(hash, returnObject);
|
||||||
|
|
||||||
const end = performance.now();
|
const end = performance.now();
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`Responsive Image sets generated for ${type} at ${args[0].src} in ${end - start}ms`
|
`Responsive Image sets generated for ${type} at ${args[0].src} in ${end - start}ms`
|
||||||
);
|
);
|
||||||
|
|
||||||
return returnObject;
|
return returnObject;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error processing images:", error);
|
console.error("Error processing images:", error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,91 +1,91 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import getSrcset from "./getSrcset.js";
|
import getSrcset from "./getSrcset.js";
|
||||||
import getConfigOptions from "./getConfigOptions.js";
|
import getConfigOptions from "./getConfigOptions.js";
|
||||||
import getFallbackImage from "./getFallbackImage.js";
|
import getFallbackImage from "./getFallbackImage.js";
|
||||||
import pMap from "p-map";
|
import pMap from "p-map";
|
||||||
|
|
||||||
function delay(ms) {
|
function delay(ms) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function getImageSources(
|
export default async function getImageSources(
|
||||||
src,
|
src,
|
||||||
base,
|
base,
|
||||||
image,
|
image,
|
||||||
format,
|
format,
|
||||||
imageWidth,
|
imageWidth,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
placeholder,
|
placeholder,
|
||||||
imageFormat,
|
imageFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat,
|
includeSourceFormat,
|
||||||
rest
|
rest
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const calculatedConfigs = getConfigOptions(
|
const calculatedConfigs = getConfigOptions(
|
||||||
imageWidth,
|
imageWidth,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
format,
|
format,
|
||||||
imageFormat,
|
imageFormat,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
includeSourceFormat
|
includeSourceFormat
|
||||||
);
|
);
|
||||||
|
|
||||||
const { formats, requiredBreakpoints } = calculatedConfigs;
|
const { formats, requiredBreakpoints } = calculatedConfigs;
|
||||||
imagesizes = calculatedConfigs.imagesizes;
|
imagesizes = calculatedConfigs.imagesizes;
|
||||||
const maxWidth = requiredBreakpoints[requiredBreakpoints.length - 1];
|
const maxWidth = requiredBreakpoints[requiredBreakpoints.length - 1];
|
||||||
const sliceLength = -(maxWidth.toString().length + 2);
|
const sliceLength = -(maxWidth.toString().length + 2);
|
||||||
|
|
||||||
const sources = await pMap(
|
const sources = await pMap(
|
||||||
formats,
|
formats,
|
||||||
async (format) => {
|
async (format) => {
|
||||||
try {
|
try {
|
||||||
await delay(250);
|
await delay(100);
|
||||||
const srcset = await getSrcset(src, base, requiredBreakpoints, format, {
|
const srcset = await getSrcset(src, base, requiredBreakpoints, format, {
|
||||||
...rest,
|
...rest,
|
||||||
...formatOptions[format],
|
...formatOptions[format],
|
||||||
});
|
});
|
||||||
|
|
||||||
const srcsets = srcset.split(", ");
|
const srcsets = srcset.split(", ");
|
||||||
const srcObject =
|
const srcObject =
|
||||||
format === fallbackFormat
|
format === fallbackFormat
|
||||||
? { src: srcsets[srcsets.length - 1].slice(0, sliceLength) }
|
? { src: srcsets[srcsets.length - 1].slice(0, sliceLength) }
|
||||||
: {};
|
: {};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...srcObject,
|
...srcObject,
|
||||||
format,
|
format,
|
||||||
srcset,
|
srcset,
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(`Error processing format ${format}:`, error);
|
console.error(`Error processing format ${format}:`, error);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ concurrency: 1 }
|
{ concurrency: 1 }
|
||||||
);
|
);
|
||||||
|
|
||||||
const filteredSources = sources.filter(Boolean);
|
const filteredSources = sources.filter(Boolean);
|
||||||
|
|
||||||
const sizes = {
|
const sizes = {
|
||||||
width: maxWidth,
|
width: maxWidth,
|
||||||
height: Math.round(maxWidth / rest.aspect),
|
height: Math.round(maxWidth / rest.aspect),
|
||||||
};
|
};
|
||||||
|
|
||||||
const fallback = await getFallbackImage(
|
const fallback = await getFallbackImage(
|
||||||
src,
|
src,
|
||||||
placeholder,
|
placeholder,
|
||||||
image,
|
image,
|
||||||
fallbackFormat,
|
fallbackFormat,
|
||||||
formatOptions,
|
formatOptions,
|
||||||
rest
|
rest
|
||||||
)
|
)
|
||||||
return { sources: filteredSources, sizes, fallback, imagesizes };
|
return { sources: filteredSources, sizes, fallback, imagesizes };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error in getImageSources:", error);
|
console.error("Error in getImageSources:", error);
|
||||||
return { sources: [], sizes: {}, fallback: null, imagesizes: null };
|
return { sources: [], sizes: {}, fallback: null, imagesizes: null };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,80 +1,80 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
import getAttributesString from "./getAttributesString.js";
|
import getAttributesString from "./getAttributesString.js";
|
||||||
|
|
||||||
export default function getImgElement({
|
export default function getImgElement({
|
||||||
src,
|
src,
|
||||||
alt,
|
alt,
|
||||||
sizes,
|
sizes,
|
||||||
style,
|
style,
|
||||||
srcset,
|
srcset,
|
||||||
loading,
|
loading,
|
||||||
decoding,
|
decoding,
|
||||||
imagesizes,
|
imagesizes,
|
||||||
fadeInTransition,
|
fadeInTransition,
|
||||||
layoutStyles,
|
layoutStyles,
|
||||||
imgAttributes,
|
imgAttributes,
|
||||||
imgClassName = "",
|
imgClassName = "",
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
class: customClasses = "",
|
class: customClasses = "",
|
||||||
style: customInlineStyles = "",
|
style: customInlineStyles = "",
|
||||||
onload: customOnload = "",
|
onload: customOnload = "",
|
||||||
...restImgAttributes
|
...restImgAttributes
|
||||||
} = imgAttributes;
|
} = imgAttributes;
|
||||||
|
|
||||||
const attributesString = getAttributesString({
|
const attributesString = getAttributesString({
|
||||||
attributes: restImgAttributes,
|
attributes: restImgAttributes,
|
||||||
element: "img",
|
element: "img",
|
||||||
excludeArray: [
|
excludeArray: [
|
||||||
"src",
|
"src",
|
||||||
"alt",
|
"alt",
|
||||||
"srcset",
|
"srcset",
|
||||||
"sizes",
|
"sizes",
|
||||||
"width",
|
"width",
|
||||||
"height",
|
"height",
|
||||||
"loading",
|
"loading",
|
||||||
"decoding",
|
"decoding",
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const classAttribute = ["astro-imagetools-img", imgClassName, customClasses]
|
const classAttribute = ["astro-imagetools-img", imgClassName, customClasses]
|
||||||
.join(" ")
|
.join(" ")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
const styleAttribute = [
|
const styleAttribute = [
|
||||||
"display: inline-block; overflow: hidden; vertical-align: middle;",
|
"display: inline-block; overflow: hidden; vertical-align: middle;",
|
||||||
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
||||||
layoutStyles,
|
layoutStyles,
|
||||||
]
|
]
|
||||||
.join(" ")
|
.join(" ")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
const onloadAttribute = [
|
const onloadAttribute = [
|
||||||
!imgClassName && style
|
!imgClassName && style
|
||||||
? fadeInTransition
|
? fadeInTransition
|
||||||
? `parentElement.style.setProperty('--z-index', 1); parentElement.style.setProperty('--opacity', 0);`
|
? `parentElement.style.setProperty('--z-index', 1); parentElement.style.setProperty('--opacity', 0);`
|
||||||
: `parentElement.style.backgroundImage = 'unset';`
|
: `parentElement.style.backgroundImage = 'unset';`
|
||||||
: "",
|
: "",
|
||||||
customOnload,
|
customOnload,
|
||||||
]
|
]
|
||||||
.join(" ")
|
.join(" ")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
const imgElement = `<img
|
const imgElement = `<img
|
||||||
${attributesString}
|
${attributesString}
|
||||||
src="${src}"
|
src="${src}"
|
||||||
${typeof alt === "string" ? `alt="${alt}"` : ""}
|
${typeof alt === "string" ? `alt="${alt}"` : ""}
|
||||||
srcset="${srcset}"
|
srcset="${srcset}"
|
||||||
sizes="${imagesizes}"
|
sizes="${imagesizes}"
|
||||||
width="${sizes.width}"
|
width="${sizes.width}"
|
||||||
height="${sizes.height}"
|
height="${sizes.height}"
|
||||||
${loading ? `loading="${loading}"` : ""}
|
${loading ? `loading="${loading}"` : ""}
|
||||||
${decoding ? `decoding="${decoding}"` : ""}
|
${decoding ? `decoding="${decoding}"` : ""}
|
||||||
class="${classAttribute}"
|
class="${classAttribute}"
|
||||||
style="${styleAttribute}"
|
style="${styleAttribute}"
|
||||||
onload="${onloadAttribute}"
|
onload="${onloadAttribute}"
|
||||||
/>`;
|
/>`;
|
||||||
|
|
||||||
return imgElement;
|
return imgElement;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
|
|
||||||
export default function getLayoutStyles({
|
export default function getLayoutStyles({
|
||||||
layout = null,
|
layout = null,
|
||||||
isBackgroundImage = false,
|
isBackgroundImage = false,
|
||||||
}) {
|
}) {
|
||||||
return isBackgroundImage
|
return isBackgroundImage
|
||||||
? "width: 100%; height: 100%;"
|
? "width: 100%; height: 100%;"
|
||||||
: layout === "fill"
|
: layout === "fill"
|
||||||
? `width: 100%; height: 100%;`
|
? `width: 100%; height: 100%;`
|
||||||
: layout === "fullWidth"
|
: layout === "fullWidth"
|
||||||
? `width: 100%; height: auto;`
|
? `width: 100%; height: auto;`
|
||||||
: layout === "fixed"
|
: layout === "fixed"
|
||||||
? ""
|
? ""
|
||||||
: "max-width: 100%; height: auto;";
|
: "max-width: 100%; height: auto;";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,34 +1,34 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import getAttributesString from "./getAttributesString.js";
|
import getAttributesString from "./getAttributesString.js";
|
||||||
|
|
||||||
export default function getLinkElement({
|
export default function getLinkElement({
|
||||||
images = [],
|
images = [],
|
||||||
preload = "",
|
preload = "",
|
||||||
imagesizes = "",
|
imagesizes = "",
|
||||||
linkAttributes,
|
linkAttributes,
|
||||||
}) {
|
}) {
|
||||||
const imagesrcset =
|
const imagesrcset =
|
||||||
preload &&
|
preload &&
|
||||||
images[images.length - 1]?.sources.find(
|
images[images.length - 1]?.sources.find(
|
||||||
({ format: fmt }) => fmt === preload
|
({ format: fmt }) => fmt === preload
|
||||||
)?.srcset;
|
)?.srcset;
|
||||||
|
|
||||||
const attributesString = getAttributesString({
|
const attributesString = getAttributesString({
|
||||||
element: "link",
|
element: "link",
|
||||||
attributes: linkAttributes,
|
attributes: linkAttributes,
|
||||||
excludeArray: ["as", "rel", "imagesizes", "imagesrcset"],
|
excludeArray: ["as", "rel", "imagesizes", "imagesrcset"],
|
||||||
});
|
});
|
||||||
|
|
||||||
const linkElement =
|
const linkElement =
|
||||||
preload && images.length
|
preload && images.length
|
||||||
? `<link
|
? `<link
|
||||||
${attributesString}
|
${attributesString}
|
||||||
as="image"
|
as="image"
|
||||||
rel="preload"
|
rel="preload"
|
||||||
imagesizes="${imagesizes}"
|
imagesizes="${imagesizes}"
|
||||||
imagesrcset="${imagesrcset}"
|
imagesrcset="${imagesrcset}"
|
||||||
/>`
|
/>`
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
return linkElement;
|
return linkElement;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import getLinkElement from "./getLinkElement";
|
import getLinkElement from "./getLinkElement";
|
||||||
|
|
||||||
describe("getLinkElement", () => {
|
describe("getLinkElement", () => {
|
||||||
it("returns an empty string if preload is not set", () => {
|
it("returns an empty string if preload is not set", () => {
|
||||||
const result = getLinkElement({ linkAttributes: {} });
|
const result = getLinkElement({ linkAttributes: {} });
|
||||||
expect(result).toBe("");
|
expect(result).toBe("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns an empty string if no images are provided", () => {
|
it("returns an empty string if no images are provided", () => {
|
||||||
const result = getLinkElement({ linkAttributes: {}, preload: "webp" });
|
const result = getLinkElement({ linkAttributes: {}, preload: "webp" });
|
||||||
expect(result).toBe("");
|
expect(result).toBe("");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,43 +1,43 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import getAttributesString from "./getAttributesString.js";
|
import getAttributesString from "./getAttributesString.js";
|
||||||
|
|
||||||
export default function getPictureElement({
|
export default function getPictureElement({
|
||||||
sources,
|
sources,
|
||||||
className,
|
className,
|
||||||
layoutStyles,
|
layoutStyles,
|
||||||
pictureAttributes,
|
pictureAttributes,
|
||||||
isBackgroundPicture = false,
|
isBackgroundPicture = false,
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
class: customClasses = "",
|
class: customClasses = "",
|
||||||
style: customInlineStyles = "",
|
style: customInlineStyles = "",
|
||||||
...restPictureAttributes
|
...restPictureAttributes
|
||||||
} = pictureAttributes;
|
} = pictureAttributes;
|
||||||
|
|
||||||
const attributesString = getAttributesString({
|
const attributesString = getAttributesString({
|
||||||
attributes: restPictureAttributes,
|
attributes: restPictureAttributes,
|
||||||
});
|
});
|
||||||
|
|
||||||
const classAttribute = ["astro-imagetools-picture", className, customClasses]
|
const classAttribute = ["astro-imagetools-picture", className, customClasses]
|
||||||
.join(" ")
|
.join(" ")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
const styleAttribute = [
|
const styleAttribute = [
|
||||||
isBackgroundPicture
|
isBackgroundPicture
|
||||||
? `position: absolute; z-index: 0; width: 100%; height: 100%; display: inline-block;`
|
? `position: absolute; z-index: 0; width: 100%; height: 100%; display: inline-block;`
|
||||||
: `position: relative; display: inline-block;`,
|
: `position: relative; display: inline-block;`,
|
||||||
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
||||||
layoutStyles,
|
layoutStyles,
|
||||||
]
|
]
|
||||||
.join(" ")
|
.join(" ")
|
||||||
.trim();
|
.trim();
|
||||||
|
|
||||||
const pictureElement = `<picture
|
const pictureElement = `<picture
|
||||||
${attributesString}
|
${attributesString}
|
||||||
class="${classAttribute}"
|
class="${classAttribute}"
|
||||||
style="${styleAttribute}"
|
style="${styleAttribute}"
|
||||||
>${sources.join("\n")}
|
>${sources.join("\n")}
|
||||||
</picture>`;
|
</picture>`;
|
||||||
|
|
||||||
return pictureElement;
|
return pictureElement;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,59 +1,59 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import { extname, relative, resolve } from "node:path";
|
import { extname, relative, resolve } from "node:path";
|
||||||
|
|
||||||
import { getSrcPath } from "./getSrcPath.js";
|
import { getSrcPath } from "./getSrcPath.js";
|
||||||
import getResolvedSrc from "./getResolvedSrc.js";
|
import getResolvedSrc from "./getResolvedSrc.js";
|
||||||
import { cwd, sharp } from "../../utils/runtimeChecks.js";
|
import { cwd, sharp } from "../../utils/runtimeChecks.js";
|
||||||
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
|
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
|
||||||
|
|
||||||
const { getImageDetails } = await (sharp
|
const { getImageDetails } = await (sharp
|
||||||
? import("./imagetools.js")
|
? import("./imagetools.js")
|
||||||
: import("./codecs.js"));
|
: import("./codecs.js"));
|
||||||
|
|
||||||
export default async function getProcessedImage(src, transformConfigs) {
|
export default async function getProcessedImage(src, transformConfigs) {
|
||||||
throwErrorIfUnsupported(src, extname(src).slice(1));
|
throwErrorIfUnsupported(src, extname(src).slice(1));
|
||||||
|
|
||||||
let base;
|
let base;
|
||||||
if (src.match("(http://|https://|data:image/).*")) {
|
if (src.match("(http://|https://|data:image/).*")) {
|
||||||
({ src, base } = await getResolvedSrc(src))
|
({ src, base } = await getResolvedSrc(src))
|
||||||
} else {
|
} else {
|
||||||
const {
|
const {
|
||||||
default: { isSsrBuild },
|
default: { isSsrBuild },
|
||||||
} = await import("../../astroViteConfigs.js")
|
} = await import("../../astroViteConfigs.js")
|
||||||
|
|
||||||
if (isSsrBuild) {
|
if (isSsrBuild) {
|
||||||
const filename = fileURLToPath(import.meta.url)
|
const filename = fileURLToPath(import.meta.url)
|
||||||
const assetPath = resolve(filename, "../../client") + src
|
const assetPath = resolve(filename, "../../client") + src
|
||||||
src = "/" + relative(cwd, assetPath);
|
src = "/" + relative(cwd, assetPath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
w,
|
w,
|
||||||
h,
|
h,
|
||||||
ar,
|
ar,
|
||||||
width = w,
|
width = w,
|
||||||
height = h,
|
height = h,
|
||||||
aspect = ar,
|
aspect = ar,
|
||||||
...rest
|
...rest
|
||||||
} = transformConfigs;
|
} = transformConfigs;
|
||||||
|
|
||||||
const path = src.replace(/\\/g, `/`);
|
const path = src.replace(/\\/g, `/`);
|
||||||
|
|
||||||
const { image, imageWidth, imageHeight, imageFormat } = await getImageDetails(
|
const { image, imageWidth, imageHeight, imageFormat } = await getImageDetails(
|
||||||
await getSrcPath(src),
|
await getSrcPath(src),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
aspect
|
aspect
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
path,
|
path,
|
||||||
base,
|
base,
|
||||||
rest,
|
rest,
|
||||||
image,
|
image,
|
||||||
imageWidth,
|
imageWidth,
|
||||||
imageHeight,
|
imageHeight,
|
||||||
imageFormat,
|
imageFormat,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,50 +1,50 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import crypto from "node:crypto";
|
import crypto from "node:crypto";
|
||||||
import { join, parse, relative } from "node:path";
|
import { join, parse, relative } from "node:path";
|
||||||
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
|
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
|
||||||
import {
|
import {
|
||||||
cwd,
|
cwd,
|
||||||
fsCachePath,
|
fsCachePath,
|
||||||
supportedImageTypes,
|
supportedImageTypes,
|
||||||
} from "../../utils/runtimeChecks.js";
|
} from "../../utils/runtimeChecks.js";
|
||||||
|
|
||||||
const { fileTypeFromBuffer } = await import("file-type");
|
const { fileTypeFromBuffer } = await import("file-type");
|
||||||
|
|
||||||
export default async function getResolvedSrc(src) {
|
export default async function getResolvedSrc(src) {
|
||||||
const token = crypto.createHash("md5").update(src).digest("hex");
|
const token = crypto.createHash("md5").update(src).digest("hex");
|
||||||
|
|
||||||
let filepath = fsCachePath + token;
|
let filepath = fsCachePath + token;
|
||||||
|
|
||||||
const fileExists = (() => {
|
const fileExists = (() => {
|
||||||
for (const type of supportedImageTypes) {
|
for (const type of supportedImageTypes) {
|
||||||
const fileExists = fs.existsSync(filepath + `.${type}`);
|
const fileExists = fs.existsSync(filepath + `.${type}`);
|
||||||
|
|
||||||
if (fileExists) {
|
if (fileExists) {
|
||||||
filepath += `.${type}`;
|
filepath += `.${type}`;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (!fileExists) {
|
if (!fileExists) {
|
||||||
const buffer = Buffer.from(await (await fetch(src)).arrayBuffer());
|
const buffer = Buffer.from(await (await fetch(src)).arrayBuffer());
|
||||||
|
|
||||||
const { ext } = (await fileTypeFromBuffer(buffer)) || {};
|
const { ext } = (await fileTypeFromBuffer(buffer)) || {};
|
||||||
|
|
||||||
throwErrorIfUnsupported(src, ext);
|
throwErrorIfUnsupported(src, ext);
|
||||||
|
|
||||||
filepath += `.${ext}`;
|
filepath += `.${ext}`;
|
||||||
|
|
||||||
fs.writeFileSync(filepath, buffer);
|
fs.writeFileSync(filepath, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
const base = /^https?:/.test(src)
|
const base = /^https?:/.test(src)
|
||||||
? parse(new URL(src).pathname).name
|
? parse(new URL(src).pathname).name
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
src = join("/", relative(cwd, filepath));
|
src = join("/", relative(cwd, filepath));
|
||||||
|
|
||||||
return { src, base };
|
return { src, base };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +1,32 @@
|
|||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
// To strip off params when checking for file on disk.
|
// To strip off params when checking for file on disk.
|
||||||
const paramPattern = /\?.*/;
|
const paramPattern = /\?.*/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getSrcPath allows the use of `src` attributes relative to either the public folder or project root.
|
* getSrcPath allows the use of `src` attributes relative to either the public folder or project root.
|
||||||
*
|
*
|
||||||
* It first checks to see if the src is a file relative to the project root.
|
* It first checks to see if the src is a file relative to the project root.
|
||||||
* If the file isn't found, it will look in the public folder.
|
* If the file isn't found, it will look in the public folder.
|
||||||
* Finally, if it still can't be found, the original input will be returned.
|
* Finally, if it still can't be found, the original input will be returned.
|
||||||
*/
|
*/
|
||||||
export async function getSrcPath(src) {
|
export async function getSrcPath(src) {
|
||||||
const { default: astroViteConfigs } = await import(
|
const { default: astroViteConfigs } = await import(
|
||||||
"../../astroViteConfigs.js"
|
"../../astroViteConfigs.js"
|
||||||
);
|
);
|
||||||
|
|
||||||
// If this is already resolved to a file, return it.
|
// If this is already resolved to a file, return it.
|
||||||
if (fs.existsSync(src.replace(paramPattern, ""))) return src;
|
if (fs.existsSync(src.replace(paramPattern, ""))) return src;
|
||||||
|
|
||||||
const rootPath = path.join(astroViteConfigs.rootDir, src);
|
const rootPath = path.join(astroViteConfigs.rootDir, src);
|
||||||
const rootTest = rootPath.replace(paramPattern, "");
|
const rootTest = rootPath.replace(paramPattern, "");
|
||||||
if (fs.existsSync(rootTest)) return rootPath;
|
if (fs.existsSync(rootTest)) return rootPath;
|
||||||
|
|
||||||
const publicPath = path.join(astroViteConfigs.publicDir, src);
|
const publicPath = path.join(astroViteConfigs.publicDir, src);
|
||||||
const publicTest = publicPath.replace(paramPattern, "");
|
const publicTest = publicPath.replace(paramPattern, "");
|
||||||
if (fs.existsSync(publicTest)) return publicPath;
|
if (fs.existsSync(publicTest)) return publicPath;
|
||||||
|
|
||||||
// Fallback
|
// Fallback
|
||||||
return src;
|
return src;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,67 +1,67 @@
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { describe, expect, it, afterAll, vi } from "vitest";
|
import { describe, expect, it, afterAll, vi } from "vitest";
|
||||||
import { getSrcPath } from "./getSrcPath";
|
import { getSrcPath } from "./getSrcPath";
|
||||||
|
|
||||||
vi.mock("../../astroViteConfigs.js", () => {
|
vi.mock("../../astroViteConfigs.js", () => {
|
||||||
return {
|
return {
|
||||||
default: {
|
default: {
|
||||||
rootDir: buildPath(),
|
rootDir: buildPath(),
|
||||||
// Custom publicDir
|
// Custom publicDir
|
||||||
publicDir: buildPath("out"),
|
publicDir: buildPath("out"),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build an absolute path to the target in the fixture directory
|
* Build an absolute path to the target in the fixture directory
|
||||||
*/
|
*/
|
||||||
function buildPath(target = "") {
|
function buildPath(target = "") {
|
||||||
return path.resolve(__dirname, "../../test-fixtures/getSrcPath", target);
|
return path.resolve(__dirname, "../../test-fixtures/getSrcPath", target);
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("getLinkElement", () => {
|
describe("getLinkElement", () => {
|
||||||
afterAll(() => {
|
afterAll(() => {
|
||||||
vi.unmock("../../astroViteConfigs.js");
|
vi.unmock("../../astroViteConfigs.js");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("finds a file in the root of the project", async () => {
|
it("finds a file in the root of the project", async () => {
|
||||||
const result = await getSrcPath("root.jpeg");
|
const result = await getSrcPath("root.jpeg");
|
||||||
expect(result).toBe(buildPath("root.jpeg"));
|
expect(result).toBe(buildPath("root.jpeg"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("finds a file in the public folder", async () => {
|
it("finds a file in the public folder", async () => {
|
||||||
const result = await getSrcPath("out.jpeg");
|
const result = await getSrcPath("out.jpeg");
|
||||||
expect(result).toBe(buildPath("out/out.jpeg"));
|
expect(result).toBe(buildPath("out/out.jpeg"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns an absolute path unchanged, if it exists", async () => {
|
it("returns an absolute path unchanged, if it exists", async () => {
|
||||||
const result = await getSrcPath(buildPath("out/out.jpeg"));
|
const result = await getSrcPath(buildPath("out/out.jpeg"));
|
||||||
expect(result).toBe(buildPath("out/out.jpeg"));
|
expect(result).toBe(buildPath("out/out.jpeg"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles query parameters", async () => {
|
it("handles query parameters", async () => {
|
||||||
const result = await getSrcPath("root.jpeg?w=200");
|
const result = await getSrcPath("root.jpeg?w=200");
|
||||||
expect(result).toBe(buildPath("root.jpeg?w=200"));
|
expect(result).toBe(buildPath("root.jpeg?w=200"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("handles query parameters for public-resolved files", async () => {
|
it("handles query parameters for public-resolved files", async () => {
|
||||||
const result = await getSrcPath("out.jpeg?w=200");
|
const result = await getSrcPath("out.jpeg?w=200");
|
||||||
expect(result).toBe(buildPath("out/out.jpeg?w=200"));
|
expect(result).toBe(buildPath("out/out.jpeg?w=200"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("returns the original input if the file is not found", async () => {
|
it("returns the original input if the file is not found", async () => {
|
||||||
const result = await getSrcPath(
|
const result = await getSrcPath(
|
||||||
"https://cdn.nedis.com/images/products_high_res/TVRC2080BK_P30.JPG"
|
"https://cdn.nedis.com/images/products_high_res/TVRC2080BK_P30.JPG"
|
||||||
);
|
);
|
||||||
expect(result).toBe(
|
expect(result).toBe(
|
||||||
"https://cdn.nedis.com/images/products_high_res/TVRC2080BK_P30.JPG"
|
"https://cdn.nedis.com/images/products_high_res/TVRC2080BK_P30.JPG"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("finds relative paths correctly", async () => {
|
it("finds relative paths correctly", async () => {
|
||||||
const outResult = await getSrcPath("./out/out.jpeg");
|
const outResult = await getSrcPath("./out/out.jpeg");
|
||||||
const rootResult = await getSrcPath("./root.jpeg");
|
const rootResult = await getSrcPath("./root.jpeg");
|
||||||
expect(outResult).toBe(buildPath("out/out.jpeg"));
|
expect(outResult).toBe(buildPath("out/out.jpeg"));
|
||||||
expect(rootResult).toBe(buildPath("root.jpeg"));
|
expect(rootResult).toBe(buildPath("root.jpeg"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,39 +1,39 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { getSrcPath } from "./getSrcPath.js";
|
import { getSrcPath } from "./getSrcPath.js";
|
||||||
|
|
||||||
export default async function getSrcset(
|
export default async function getSrcset(
|
||||||
src,
|
src,
|
||||||
base,
|
base,
|
||||||
breakpoints,
|
breakpoints,
|
||||||
format,
|
format,
|
||||||
options
|
options
|
||||||
) {
|
) {
|
||||||
options = {
|
options = {
|
||||||
format,
|
format,
|
||||||
w: breakpoints,
|
w: breakpoints,
|
||||||
...options,
|
...options,
|
||||||
};
|
};
|
||||||
|
|
||||||
const keys = Object.keys(options);
|
const keys = Object.keys(options);
|
||||||
|
|
||||||
const params = keys.length
|
const params = keys.length
|
||||||
? keys
|
? keys
|
||||||
.map((key) =>
|
.map((key) =>
|
||||||
Array.isArray(options[key])
|
Array.isArray(options[key])
|
||||||
? `&${key}=${options[key].join(";")}`
|
? `&${key}=${options[key].join(";")}`
|
||||||
: `&${key}=${options[key]}`
|
: `&${key}=${options[key]}`
|
||||||
)
|
)
|
||||||
.join("")
|
.join("")
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
const id = `${src}?${params.slice(1)}`;
|
const id = `${src}?${params.slice(1)}`;
|
||||||
|
|
||||||
const fullPath = await getSrcPath(id);
|
const fullPath = await getSrcPath(id);
|
||||||
|
|
||||||
const { default: load } = await import("../../plugin/hooks/load.js");
|
const { default: load } = await import("../../plugin/hooks/load.js");
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const srcset = (await load(fullPath, base)).slice(16, -1);
|
const srcset = (await load(fullPath, base)).slice(16, -1);
|
||||||
|
|
||||||
return srcset;
|
return srcset;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import getAttributesString from "./getAttributesString.js";
|
import getAttributesString from "./getAttributesString.js";
|
||||||
|
|
||||||
export default function getStyleElement({
|
export default function getStyleElement({
|
||||||
styleAttributes,
|
styleAttributes,
|
||||||
backgroundStyles = "",
|
backgroundStyles = "",
|
||||||
}) {
|
}) {
|
||||||
const attributesString = getAttributesString({
|
const attributesString = getAttributesString({
|
||||||
attributes: styleAttributes,
|
attributes: styleAttributes,
|
||||||
});
|
});
|
||||||
|
|
||||||
const styleElement = `<style ${attributesString}>${backgroundStyles}</style>`;
|
const styleElement = `<style ${attributesString}>${backgroundStyles}</style>`;
|
||||||
|
|
||||||
return styleElement;
|
return styleElement;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,40 +1,40 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import {
|
import {
|
||||||
builtins,
|
builtins,
|
||||||
loadImage,
|
loadImage,
|
||||||
applyTransforms,
|
applyTransforms,
|
||||||
generateTransforms,
|
generateTransforms,
|
||||||
} from "imagetools-core";
|
} from "imagetools-core";
|
||||||
export {
|
export {
|
||||||
loadImage
|
loadImage
|
||||||
} from "imagetools-core";
|
} from "imagetools-core";
|
||||||
export async function getImageDetails(path, width, height, aspect) {
|
export async function getImageDetails(path, width, height, aspect) {
|
||||||
const loadedImage = loadImage(path);
|
const loadedImage = loadImage(path);
|
||||||
|
|
||||||
if (aspect && !width && !height) {
|
if (aspect && !width && !height) {
|
||||||
if (!width && !height) {
|
if (!width && !height) {
|
||||||
({ width } = await loadedImage.metadata());
|
({ width } = await loadedImage.metadata());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (width) {
|
if (width) {
|
||||||
height = width / aspect;
|
height = width / aspect;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height) {
|
if (height) {
|
||||||
width = height * aspect;
|
width = height * aspect;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { image, metadata } = await applyTransforms(
|
const { image, metadata } = await applyTransforms(
|
||||||
generateTransforms({ width, height }, builtins).transforms,
|
generateTransforms({ width, height }, builtins).transforms,
|
||||||
loadedImage
|
loadedImage
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
width: imageWidth,
|
width: imageWidth,
|
||||||
height: imageHeight,
|
height: imageHeight,
|
||||||
format: imageFormat,
|
format: imageFormat,
|
||||||
} = metadata;
|
} = metadata;
|
||||||
|
|
||||||
return { image, imageWidth, imageHeight, imageFormat };
|
return { image, imageWidth, imageHeight, imageFormat };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
// @ts-check
|
// @ts-check
|
||||||
import { supportedImageTypes } from "../../utils/runtimeChecks.js";
|
import { supportedImageTypes } from "../../utils/runtimeChecks.js";
|
||||||
|
|
||||||
export default function throwErrorIfUnsupported(src, ext) {
|
export default function throwErrorIfUnsupported(src, ext) {
|
||||||
if (!ext && typeof ext !== "string") {
|
if (!ext && typeof ext !== "string") {
|
||||||
throw new Error(`Failed to load ${src}; Invalid image format`);
|
throw new Error(`Failed to load ${src}; Invalid image format`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ext && !supportedImageTypes.includes(ext.toLowerCase())) {
|
if (ext && !supportedImageTypes.includes(ext.toLowerCase())) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Failed to load ${src}; Invalid image format ${ext} or the format is not supported by astro-imagetools`
|
`Failed to load ${src}; Invalid image format ${ext} or the format is not supported by astro-imagetools`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,8 @@ export default {
|
|||||||
"publicDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\public\\",
|
"publicDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\public\\",
|
||||||
"rootDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\",
|
"rootDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\",
|
||||||
"mode": "production",
|
"mode": "production",
|
||||||
"outDir": "dist",
|
"outDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\dist\\",
|
||||||
"assetsDir": "/_astro",
|
"assetsDir": "_astro",
|
||||||
"sourcemap": false,
|
"sourcemap": false,
|
||||||
"assetFileNames": "/_astro/[name]@[width].[hash][extname]"
|
"assetFileNames": "/_astro/[name]@[width].[hash][extname]"
|
||||||
}
|
}
|
||||||
@ -1,46 +1,46 @@
|
|||||||
---
|
---
|
||||||
import renderBackgroundImage from "../api/renderBackgroundImage.js";
|
import renderBackgroundImage from "../api/renderBackgroundImage.js";
|
||||||
import type { BackgroundImageConfigOptions } from "../types.d";
|
import type { BackgroundImageConfigOptions } from "../types.d";
|
||||||
|
|
||||||
const content = await Astro.slots.render("default");
|
const content = await Astro.slots.render("default");
|
||||||
|
|
||||||
declare interface Props
|
declare interface Props
|
||||||
extends Pick<
|
extends Pick<
|
||||||
BackgroundImageConfigOptions,
|
BackgroundImageConfigOptions,
|
||||||
Exclude<keyof BackgroundImageConfigOptions, "content">
|
Exclude<keyof BackgroundImageConfigOptions, "content">
|
||||||
> {}
|
> {}
|
||||||
|
|
||||||
const { link, style, htmlElement } = await renderBackgroundImage({
|
const { link, style, htmlElement } = await renderBackgroundImage({
|
||||||
content,
|
content,
|
||||||
...(Astro.props as Props),
|
...(Astro.props as Props),
|
||||||
});
|
});
|
||||||
---
|
---
|
||||||
|
|
||||||
<Fragment set:html={link + style + htmlElement} />
|
<Fragment set:html={link + style + htmlElement} />
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
const { classList } = document.documentElement;
|
const { classList } = document.documentElement;
|
||||||
|
|
||||||
const addClass = classList.add.bind(classList);
|
const addClass = classList.add.bind(classList);
|
||||||
|
|
||||||
addClass("jpeg");
|
addClass("jpeg");
|
||||||
addClass("png");
|
addClass("png");
|
||||||
|
|
||||||
const isFormatSupported = (format, dataUri) => {
|
const isFormatSupported = (format, dataUri) => {
|
||||||
const image = new Image();
|
const image = new Image();
|
||||||
|
|
||||||
image.src = `data:image/${format};base64,${dataUri}`;
|
image.src = `data:image/${format};base64,${dataUri}`;
|
||||||
|
|
||||||
image.onload = addClass(format);
|
image.onload = addClass(format);
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Check support for JXL images
|
// TODO: Check support for JXL images
|
||||||
// isFormatSupported("jxl", "/woAEBAJCAQBACwASxLFgoUJEP3D/wA=");
|
// isFormatSupported("jxl", "/woAEBAJCAQBACwASxLFgoUJEP3D/wA=");
|
||||||
|
|
||||||
isFormatSupported("webp", "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==");
|
isFormatSupported("webp", "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==");
|
||||||
|
|
||||||
isFormatSupported(
|
isFormatSupported(
|
||||||
"avif",
|
"avif",
|
||||||
"AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A="
|
"AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A="
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
---
|
---
|
||||||
import renderBackgroundPicture from "../api/renderBackgroundPicture.js";
|
import renderBackgroundPicture from "../api/renderBackgroundPicture.js";
|
||||||
import { BackgroundPictureConfigOptions } from "../types.d";
|
import { BackgroundPictureConfigOptions } from "../types.d";
|
||||||
|
|
||||||
declare interface Props
|
declare interface Props
|
||||||
extends Pick<
|
extends Pick<
|
||||||
BackgroundPictureConfigOptions,
|
BackgroundPictureConfigOptions,
|
||||||
Exclude<keyof BackgroundPictureConfigOptions, "content">
|
Exclude<keyof BackgroundPictureConfigOptions, "content">
|
||||||
> {}
|
> {}
|
||||||
|
|
||||||
const content = await Astro.slots.render("default");
|
const content = await Astro.slots.render("default");
|
||||||
|
|
||||||
const { link, style, htmlElement } = await renderBackgroundPicture({
|
const { link, style, htmlElement } = await renderBackgroundPicture({
|
||||||
content,
|
content,
|
||||||
...(Astro.props as Props),
|
...(Astro.props as Props),
|
||||||
});
|
});
|
||||||
---
|
---
|
||||||
|
|
||||||
<Fragment set:html={link + style + htmlElement} />
|
<Fragment set:html={link + style + htmlElement} />
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
import renderImage from "../api/renderImage.js";
|
import renderImage from "../api/renderImage.js";
|
||||||
import type { PictureConfigOptions as ImageConfigOptions } from "../types.d";
|
import type { PictureConfigOptions as ImageConfigOptions } from "../types.d";
|
||||||
|
|
||||||
const { link, style, image } = await renderImage(
|
const { link, style, image } = await renderImage(
|
||||||
Astro.props as ImageConfigOptions
|
Astro.props as ImageConfigOptions
|
||||||
);
|
);
|
||||||
---
|
---
|
||||||
|
|
||||||
<Fragment set:html={link + style + image} />
|
<Fragment set:html={link + style + image} />
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
<script is:inline>
|
<script is:inline>
|
||||||
const{classList:e}=document.documentElement,A=e.add.bind(e);A("jpeg");A("png");const g=(B,d)=>{const a=new Image;a.src=`data:image/${B};base64,${d}`,a.onload=A(B)};g("webp","UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==");g("avif","AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=");
|
const{classList:e}=document.documentElement,A=e.add.bind(e);A("jpeg");A("png");const g=(B,d)=>{const a=new Image;a.src=`data:image/${B};base64,${d}`,a.onload=A(B)};g("webp","UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==");g("avif","AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=");
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
import renderImg from "../api/renderImg.js";
|
import renderImg from "../api/renderImg.js";
|
||||||
import type { ImgConfigOptions } from "../types.d";
|
import type { ImgConfigOptions } from "../types.d";
|
||||||
|
|
||||||
declare interface Props extends ImgConfigOptions {}
|
declare interface Props extends ImgConfigOptions {}
|
||||||
|
|
||||||
const { link, style, img } = await renderImg(Astro.props as Props);
|
const { link, style, img } = await renderImg(Astro.props as Props);
|
||||||
---
|
---
|
||||||
|
|
||||||
<Fragment set:html={link + style + img} />
|
<Fragment set:html={link + style + img} />
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
import renderPicture from "../api/renderPicture.js";
|
import renderPicture from "../api/renderPicture.js";
|
||||||
import type { PictureConfigOptions } from "../types.d";
|
import type { PictureConfigOptions } from "../types.d";
|
||||||
|
|
||||||
declare interface Props extends PictureConfigOptions {}
|
declare interface Props extends PictureConfigOptions {}
|
||||||
|
|
||||||
const { link, style, picture } = await renderPicture(Astro.props as Props);
|
const { link, style, picture } = await renderPicture(Astro.props as Props);
|
||||||
---
|
---
|
||||||
|
|
||||||
<Fragment set:html={link + style + picture} />
|
<Fragment set:html={link + style + picture} />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export { default as Img } from "./Img.astro";
|
export { default as Img } from "./Img.astro";
|
||||||
export { default as Picture } from "./Picture.astro";
|
export { default as Picture } from "./Picture.astro";
|
||||||
export { default as BackgroundImage } from "./BackgroundImage.astro";
|
export { default as BackgroundImage } from "./BackgroundImage.astro";
|
||||||
export { default as BackgroundPicture } from "./BackgroundPicture.astro";
|
export { default as BackgroundPicture } from "./BackgroundPicture.astro";
|
||||||
export { default as ImageSupportDetection } from "./ImageSupportDetection.astro";
|
export { default as ImageSupportDetection } from "./ImageSupportDetection.astro";
|
||||||
|
|||||||
6
packages/imagetools/config.d.ts
vendored
6
packages/imagetools/config.d.ts
vendored
@ -1,3 +1,3 @@
|
|||||||
import type { GlobalConfigOptions } from "./types";
|
import type { GlobalConfigOptions } from "./types";
|
||||||
|
|
||||||
export function defineConfig(config: GlobalConfigOptions): GlobalConfigOptions;
|
export function defineConfig(config: GlobalConfigOptions): GlobalConfigOptions;
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export function defineConfig(config) {
|
export function defineConfig(config) {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
## force pnpm to hoist
|
## force pnpm to hoist
|
||||||
shamefully-hoist = true
|
shamefully-hoist = true
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"startCommand": "npm start",
|
"startCommand": "npm start",
|
||||||
"env": {
|
"env": {
|
||||||
"ENABLE_CJS_IMPORTS": true
|
"ENABLE_CJS_IMPORTS": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# Astro ImageTools Live Examples
|
# Astro ImageTools Live Examples
|
||||||
|
|
||||||
This repository contains source code for the [**Astro ImageTools Demo**](https://astro-imagetools-demo.vercel.app) website.
|
This repository contains source code for the [**Astro ImageTools Demo**](https://astro-imagetools-demo.vercel.app) website.
|
||||||
|
|
||||||
The demo displays live examples of the components and APIs provided by the [**Astro ImageTools**](https://npmjs.com/package/astro-imagetools) library and the **Layouts** and **Placeholders** supported by the library.
|
The demo displays live examples of the components and APIs provided by the [**Astro ImageTools**](https://npmjs.com/package/astro-imagetools) library and the **Layouts** and **Placeholders** supported by the library.
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
import { defineConfig } from "astro-imagetools/config";
|
import { defineConfig } from "astro-imagetools/config";
|
||||||
|
|
||||||
export default defineConfig({});
|
export default defineConfig({});
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
import { defineConfig } from "astro/config";
|
import { defineConfig } from "astro/config";
|
||||||
import { astroImageTools } from "astro-imagetools";
|
import { astroImageTools } from "astro-imagetools";
|
||||||
|
|
||||||
// https://astro.build/config
|
// https://astro.build/config
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
vite: {
|
vite: {
|
||||||
plugins: [
|
plugins: [
|
||||||
{
|
{
|
||||||
name: "import.meta.url-transformer",
|
name: "import.meta.url-transformer",
|
||||||
transform: (code, id) => {
|
transform: (code, id) => {
|
||||||
if (id.endsWith(".astro"))
|
if (id.endsWith(".astro"))
|
||||||
return code.replace(/import.meta.url/g, `"${id}"`);
|
return code.replace(/import.meta.url/g, `"${id}"`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
experimental: {
|
experimental: {
|
||||||
integrations: true,
|
integrations: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
integrations: [astroImageTools],
|
integrations: [astroImageTools],
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "astro dev",
|
"dev": "astro dev",
|
||||||
"start": "astro dev",
|
"start": "astro dev",
|
||||||
"build": "astro build",
|
"build": "astro build",
|
||||||
"preview": "astro preview"
|
"preview": "astro preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"astro-spa": "^1.3.9",
|
"astro-spa": "^1.3.9",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@astrojs/lit": "^1.1.2",
|
"@astrojs/lit": "^1.1.2",
|
||||||
"@astrojs/preact": "^2.0.2",
|
"@astrojs/preact": "^2.0.2",
|
||||||
"@astrojs/react": "^2.0.2",
|
"@astrojs/react": "^2.0.2",
|
||||||
"@astrojs/solid-js": "^2.0.2",
|
"@astrojs/solid-js": "^2.0.2",
|
||||||
"@astrojs/svelte": "^2.0.1",
|
"@astrojs/svelte": "^2.0.1",
|
||||||
"@astrojs/vue": "^2.0.1",
|
"@astrojs/vue": "^2.0.1",
|
||||||
"astro": "^2.0.6",
|
"astro": "^2.0.6",
|
||||||
"astro-imagetools": "workspace:^0.9.0"
|
"astro-imagetools": "workspace:^0.9.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"infiniteLoopProtection": true,
|
"infiniteLoopProtection": true,
|
||||||
"hardReloadOnChange": false,
|
"hardReloadOnChange": false,
|
||||||
"view": "browser",
|
"view": "browser",
|
||||||
"template": "node",
|
"template": "node",
|
||||||
"container": {
|
"container": {
|
||||||
"port": 3000,
|
"port": 3000,
|
||||||
"startScript": "start",
|
"startScript": "start",
|
||||||
"node": "16"
|
"node": "16"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
packages/imagetools/demo/src/env.d.ts
vendored
2
packages/imagetools/demo/src/env.d.ts
vendored
@ -1 +1 @@
|
|||||||
/// <reference types="astro/client" />
|
/// <reference types="astro/client" />
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
---
|
---
|
||||||
import { Picture } from "astro-imagetools/components";
|
import { Picture } from "astro-imagetools/components";
|
||||||
import MainLayout from "./MainLayout.astro";
|
import MainLayout from "./MainLayout.astro";
|
||||||
|
|
||||||
const { layout, importMetaUrl } = Astro.props;
|
const { layout, importMetaUrl } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout {importMetaUrl}>
|
<MainLayout {importMetaUrl}>
|
||||||
<h1><code>{layout}</code> Layout Example</h1>
|
<h1><code>{layout}</code> Layout Example</h1>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Picture src="https://picsum.photos/1024/768/" alt="A random image" {layout}
|
<Picture src="https://picsum.photos/1024/768/" alt="A random image" {layout}
|
||||||
/>
|
/>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
@ -1,57 +1,57 @@
|
|||||||
---
|
---
|
||||||
import { Spa } from "astro-spa";
|
import { Spa } from "astro-spa";
|
||||||
import "../styles/index.css";
|
import "../styles/index.css";
|
||||||
|
|
||||||
const { importMetaUrl } = Astro.props as { importMetaUrl: string };
|
const { importMetaUrl } = Astro.props as { importMetaUrl: string };
|
||||||
|
|
||||||
const path =
|
const path =
|
||||||
Astro.props.content?.path ||
|
Astro.props.content?.path ||
|
||||||
importMetaUrl?.slice(importMetaUrl.indexOf("/pages/") + 7);
|
importMetaUrl?.slice(importMetaUrl.indexOf("/pages/") + 7);
|
||||||
|
|
||||||
const GitHubURL = `https://github.com/RafidMuhymin/astro-imagetools/tree/main/demo/src/pages/${path}`;
|
const GitHubURL = `https://github.com/RafidMuhymin/astro-imagetools/tree/main/demo/src/pages/${path}`;
|
||||||
---
|
---
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap-reboot.min.css"
|
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap-reboot.min.css"
|
||||||
crossorigin="anonymous"
|
crossorigin="anonymous"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Unset bootstrap image resets -->
|
<!-- Unset bootstrap image resets -->
|
||||||
<style is:global>
|
<style is:global>
|
||||||
img, svg {
|
img, svg {
|
||||||
vertical-align: unset;
|
vertical-align: unset;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="100"
|
width="100"
|
||||||
height="100"
|
height="100"
|
||||||
viewBox="0 0 250 250"
|
viewBox="0 0 250 250"
|
||||||
fill="#151513"
|
fill="#151513"
|
||||||
class="view-source"
|
class="view-source"
|
||||||
>
|
>
|
||||||
<a title="View Source" href={GitHubURL}>
|
<a title="View Source" href={GitHubURL}>
|
||||||
<path d="M0 0l115 115h15l12 27 108 108V0z" fill="#fff"></path>
|
<path d="M0 0l115 115h15l12 27 108 108V0z" fill="#fff"></path>
|
||||||
<path
|
<path
|
||||||
class="view-source_arm___1DMT"
|
class="view-source_arm___1DMT"
|
||||||
d="M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 15 9 16"
|
d="M128 109c-15-9-9-19-9-19 3-7 2-11 2-11-1-7 3-2 3-2 4 5 2 11 2 11-3 10 5 15 9 16"
|
||||||
></path>
|
></path>
|
||||||
<path
|
<path
|
||||||
d="M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z"
|
d="M115 115s4 2 5 0l14-14c3-2 6-3 8-3-8-11-15-24 2-41 5-5 10-7 16-7 1-2 3-7 12-11 0 0 5 3 7 16 4 2 8 5 12 9s7 8 9 12c14 3 17 7 17 7-4 8-9 11-11 11 0 6-2 11-7 16-16 16-30 10-41 2 0 3-1 7-5 11l-12 11c-1 1 1 5 1 5z"
|
||||||
></path>
|
></path>
|
||||||
</a>
|
</a>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<Spa />
|
<Spa />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -1,18 +1,18 @@
|
|||||||
---
|
---
|
||||||
import { Picture } from "astro-imagetools/components";
|
import { Picture } from "astro-imagetools/components";
|
||||||
import MainLayout from "./MainLayout.astro";
|
import MainLayout from "./MainLayout.astro";
|
||||||
|
|
||||||
const { placeholder, importMetaUrl } = Astro.props;
|
const { placeholder, importMetaUrl } = Astro.props;
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout {importMetaUrl}>
|
<MainLayout {importMetaUrl}>
|
||||||
<h1><code>{placeholder}</code> Placeholder Example</h1>
|
<h1><code>{placeholder}</code> Placeholder Example</h1>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Picture
|
<Picture
|
||||||
src="https://picsum.photos/1024/768/"
|
src="https://picsum.photos/1024/768/"
|
||||||
alt="A random image"
|
alt="A random image"
|
||||||
{placeholder}
|
{placeholder}
|
||||||
/>
|
/>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
@ -1,48 +1,48 @@
|
|||||||
---
|
---
|
||||||
import { ImageSupportDetection } from "astro-imagetools/components";
|
import { ImageSupportDetection } from "astro-imagetools/components";
|
||||||
import { renderBackgroundImage } from "astro-imagetools/api";
|
import { renderBackgroundImage } from "astro-imagetools/api";
|
||||||
import MainLayout from "../../layouts/MainLayout.astro";
|
import MainLayout from "../../layouts/MainLayout.astro";
|
||||||
|
|
||||||
const { link, style, htmlElement } = await renderBackgroundImage({
|
const { link, style, htmlElement } = await renderBackgroundImage({
|
||||||
src: "https://picsum.photos/1024/768/",
|
src: "https://picsum.photos/1024/768/",
|
||||||
content: `<p>
|
content: `<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||||
aliquam at quidem dolorum?
|
aliquam at quidem dolorum?
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||||
reiciendis dicta quaerat maiores omnis.
|
reiciendis dicta quaerat maiores omnis.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||||
suscipit sequi. At, deleniti nihil.
|
suscipit sequi. At, deleniti nihil.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||||
Nesciunt, repellat mollitia. Rem, labore?
|
Nesciunt, repellat mollitia. Rem, labore?
|
||||||
</p>`,
|
</p>`,
|
||||||
});
|
});
|
||||||
---
|
---
|
||||||
|
|
||||||
<ImageSupportDetection />
|
<ImageSupportDetection />
|
||||||
|
|
||||||
<MainLayout importMetaUrl={import.meta.url}>
|
<MainLayout importMetaUrl={import.meta.url}>
|
||||||
<h1>Astro ImageTools <code>{"renderBackgroundImage"}</code> API Example</h1>
|
<h1>Astro ImageTools <code>{"renderBackgroundImage"}</code> API Example</h1>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Fragment set:html={link + style + htmlElement} />
|
<Fragment set:html={link + style + htmlElement} />
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
@ -1,45 +1,45 @@
|
|||||||
---
|
---
|
||||||
import { renderBackgroundPicture } from "astro-imagetools/api";
|
import { renderBackgroundPicture } from "astro-imagetools/api";
|
||||||
import MainLayout from "../../layouts/MainLayout.astro";
|
import MainLayout from "../../layouts/MainLayout.astro";
|
||||||
|
|
||||||
const { link, style, htmlElement } = await renderBackgroundPicture({
|
const { link, style, htmlElement } = await renderBackgroundPicture({
|
||||||
src: "https://picsum.photos/1024/768/",
|
src: "https://picsum.photos/1024/768/",
|
||||||
content: `<p>
|
content: `<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||||
aliquam at quidem dolorum?
|
aliquam at quidem dolorum?
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||||
reiciendis dicta quaerat maiores omnis.
|
reiciendis dicta quaerat maiores omnis.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||||
suscipit sequi. At, deleniti nihil.
|
suscipit sequi. At, deleniti nihil.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||||
Nesciunt, repellat mollitia. Rem, labore?
|
Nesciunt, repellat mollitia. Rem, labore?
|
||||||
</p>`,
|
</p>`,
|
||||||
});
|
});
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout importMetaUrl={import.meta.url}>
|
<MainLayout importMetaUrl={import.meta.url}>
|
||||||
<h1>Astro ImageTools <code>{"renderBackgroundPicture"}</code> API Example</h1>
|
<h1>Astro ImageTools <code>{"renderBackgroundPicture"}</code> API Example</h1>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Fragment set:html={link + style + htmlElement} />
|
<Fragment set:html={link + style + htmlElement} />
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
---
|
---
|
||||||
import { renderImg } from "astro-imagetools/api";
|
import { renderImg } from "astro-imagetools/api";
|
||||||
import MainLayout from "../../layouts/MainLayout.astro";
|
import MainLayout from "../../layouts/MainLayout.astro";
|
||||||
|
|
||||||
const { link, style, img } = await renderImg({
|
const { link, style, img } = await renderImg({
|
||||||
src: "https://picsum.photos/1024/768/",
|
src: "https://picsum.photos/1024/768/",
|
||||||
alt: "A random image",
|
alt: "A random image",
|
||||||
});
|
});
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout importMetaUrl={import.meta.url}>
|
<MainLayout importMetaUrl={import.meta.url}>
|
||||||
<h1>Astro ImageTools <code>{"renderImg"}</code> API Example</h1>
|
<h1>Astro ImageTools <code>{"renderImg"}</code> API Example</h1>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Fragment set:html={link + style + img} />
|
<Fragment set:html={link + style + img} />
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
---
|
---
|
||||||
import { renderPicture } from "astro-imagetools/api";
|
import { renderPicture } from "astro-imagetools/api";
|
||||||
import MainLayout from "../../layouts/MainLayout.astro";
|
import MainLayout from "../../layouts/MainLayout.astro";
|
||||||
|
|
||||||
const { link, style, picture } = await renderPicture({
|
const { link, style, picture } = await renderPicture({
|
||||||
src: "https://picsum.photos/1024/768/",
|
src: "https://picsum.photos/1024/768/",
|
||||||
alt: "A random image",
|
alt: "A random image",
|
||||||
});
|
});
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout importMetaUrl={import.meta.url}>
|
<MainLayout importMetaUrl={import.meta.url}>
|
||||||
<h1>Astro ImageTools <code>{"renderPicture"}</code> API Example</h1>
|
<h1>Astro ImageTools <code>{"renderPicture"}</code> API Example</h1>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Fragment set:html={link + style + picture} />
|
<Fragment set:html={link + style + picture} />
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
@ -1,45 +1,45 @@
|
|||||||
---
|
---
|
||||||
import {
|
import {
|
||||||
BackgroundImage,
|
BackgroundImage,
|
||||||
ImageSupportDetection,
|
ImageSupportDetection,
|
||||||
} from "astro-imagetools/components";
|
} from "astro-imagetools/components";
|
||||||
import MainLayout from "../../layouts/MainLayout.astro";
|
import MainLayout from "../../layouts/MainLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<ImageSupportDetection />
|
<ImageSupportDetection />
|
||||||
|
|
||||||
<MainLayout importMetaUrl={import.meta.url}>
|
<MainLayout importMetaUrl={import.meta.url}>
|
||||||
<h1>
|
<h1>
|
||||||
Astro ImageTools <code>{"<BackgroundImage />"}</code> Component Example
|
Astro ImageTools <code>{"<BackgroundImage />"}</code> Component Example
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<BackgroundImage src="https://picsum.photos/1024/768/">
|
<BackgroundImage src="https://picsum.photos/1024/768/">
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||||
aliquam at quidem dolorum?
|
aliquam at quidem dolorum?
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||||
reiciendis dicta quaerat maiores omnis.
|
reiciendis dicta quaerat maiores omnis.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||||
suscipit sequi. At, deleniti nihil.
|
suscipit sequi. At, deleniti nihil.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||||
Nesciunt, repellat mollitia. Rem, labore?
|
Nesciunt, repellat mollitia. Rem, labore?
|
||||||
</p>
|
</p>
|
||||||
</BackgroundImage>
|
</BackgroundImage>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
@ -1,40 +1,40 @@
|
|||||||
---
|
---
|
||||||
import { BackgroundPicture } from "astro-imagetools/components";
|
import { BackgroundPicture } from "astro-imagetools/components";
|
||||||
import MainLayout from "../../layouts/MainLayout.astro";
|
import MainLayout from "../../layouts/MainLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout importMetaUrl={import.meta.url}>
|
<MainLayout importMetaUrl={import.meta.url}>
|
||||||
<h1>
|
<h1>
|
||||||
Astro ImageTools <code>{"<BackgroundPicture />"}</code> Component Example
|
Astro ImageTools <code>{"<BackgroundPicture />"}</code> Component Example
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<BackgroundPicture src="https://picsum.photos/1024/768/">
|
<BackgroundPicture src="https://picsum.photos/1024/768/">
|
||||||
<p>
|
<p>
|
||||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||||
aliquam at quidem dolorum?
|
aliquam at quidem dolorum?
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||||
reiciendis dicta quaerat maiores omnis.
|
reiciendis dicta quaerat maiores omnis.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||||
suscipit sequi. At, deleniti nihil.
|
suscipit sequi. At, deleniti nihil.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||||
Nesciunt, repellat mollitia. Rem, labore?
|
Nesciunt, repellat mollitia. Rem, labore?
|
||||||
</p>
|
</p>
|
||||||
</BackgroundPicture>
|
</BackgroundPicture>
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
import { Img } from "astro-imagetools/components";
|
import { Img } from "astro-imagetools/components";
|
||||||
import MainLayout from "../../layouts/MainLayout.astro";
|
import MainLayout from "../../layouts/MainLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout importMetaUrl={import.meta.url}>
|
<MainLayout importMetaUrl={import.meta.url}>
|
||||||
<h1>Astro ImageTools <code>{"<Img />"}</code> Component Example</h1>
|
<h1>Astro ImageTools <code>{"<Img />"}</code> Component Example</h1>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Img src="https://picsum.photos/1024/768/" alt="A random image" />
|
<Img src="https://picsum.photos/1024/768/" alt="A random image" />
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
import { Picture } from "astro-imagetools/components";
|
import { Picture } from "astro-imagetools/components";
|
||||||
import MainLayout from "../../layouts/MainLayout.astro";
|
import MainLayout from "../../layouts/MainLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<MainLayout importMetaUrl={import.meta.url}>
|
<MainLayout importMetaUrl={import.meta.url}>
|
||||||
<h1>Astro ImageTools <code>{"<Picture />"}</code> Component Example</h1>
|
<h1>Astro ImageTools <code>{"<Picture />"}</code> Component Example</h1>
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<Picture src="https://picsum.photos/1024/768/" alt="A random image" />
|
<Picture src="https://picsum.photos/1024/768/" alt="A random image" />
|
||||||
</MainLayout>
|
</MainLayout>
|
||||||
|
|||||||
@ -1,78 +1,78 @@
|
|||||||
---
|
---
|
||||||
path: index.md
|
path: index.md
|
||||||
layout: ../layouts/MainLayout.astro
|
layout: ../layouts/MainLayout.astro
|
||||||
---
|
---
|
||||||
|
|
||||||
# Image Optimization in Astro with Astro ImageTools
|
# Image Optimization in Astro with Astro ImageTools
|
||||||
|
|
||||||
This page demonstrates the usage of the [astro-imagetools](https://www.npmjs.com/package/astro-imagetools) library with live examples.
|
This page demonstrates the usage of the [astro-imagetools](https://www.npmjs.com/package/astro-imagetools) library with live examples.
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
- [`<Img />` Component](/components/Img)
|
- [`<Img />` Component](/components/Img)
|
||||||
- [`<Picture />` Component](/components/Picture)
|
- [`<Picture />` Component](/components/Picture)
|
||||||
- [`<BackgroundImage />` Component](/components/BackgroundImage)
|
- [`<BackgroundImage />` Component](/components/BackgroundImage)
|
||||||
- [`<BackgroundPicture />` Component](/components/BackgroundPicture)
|
- [`<BackgroundPicture />` Component](/components/BackgroundPicture)
|
||||||
|
|
||||||
## APIs
|
## APIs
|
||||||
|
|
||||||
- [`renderImg` API](/api/renderImg)
|
- [`renderImg` API](/api/renderImg)
|
||||||
- [`renderPicture` API](/api/renderPicture)
|
- [`renderPicture` API](/api/renderPicture)
|
||||||
- [`renderBackgroundImage` API](/api/renderBackgroundImage)
|
- [`renderBackgroundImage` API](/api/renderBackgroundImage)
|
||||||
- [`renderBackgroundPicture` API](/api/renderBackgroundPicture)
|
- [`renderBackgroundPicture` API](/api/renderBackgroundPicture)
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
The `layout` property tells the image to respond differently depending on the device size or the container size.
|
The `layout` property tells the image to respond differently depending on the device size or the container size.
|
||||||
|
|
||||||
Select a layout below and try resizing the window or rotating your device to see how the image reacts.
|
Select a layout below and try resizing the window or rotating your device to see how the image reacts.
|
||||||
|
|
||||||
- [Constrained Layout](/layout/constrained)
|
- [Constrained Layout](/layout/constrained)
|
||||||
- [Fixed Layout](/layout/fixed)
|
- [Fixed Layout](/layout/fixed)
|
||||||
- [Full Width Layout](/layout/fullWidth)
|
- [Full Width Layout](/layout/fullWidth)
|
||||||
- [Fill Layout](/layout/fill)
|
- [Fill Layout](/layout/fill)
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
## Placeholder
|
## Placeholder
|
||||||
|
|
||||||
The `placeholder` property tells the image what to show while loading.
|
The `placeholder` property tells the image what to show while loading.
|
||||||
|
|
||||||
- [Blurred Placeholder](/placeholder/blurred)
|
- [Blurred Placeholder](/placeholder/blurred)
|
||||||
- [Dominant Color Placeholder](/placeholder/dominantColor)
|
- [Dominant Color Placeholder](/placeholder/dominantColor)
|
||||||
- [Traced SVG Placeholder](/placeholder/tracedSVG)
|
- [Traced SVG Placeholder](/placeholder/tracedSVG)
|
||||||
- [No Placeholder](/placeholder/none)
|
- [No Placeholder](/placeholder/none)
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
## Internal Image
|
## Internal Image
|
||||||
|
|
||||||
The following is an example of a reference to an internal image in the `src` directory (`../images/elva-800w.jpg`).
|
The following is an example of a reference to an internal image in the `src` directory (`../images/elva-800w.jpg`).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
## External Image
|
## External Image
|
||||||
|
|
||||||
The following is an example of a reference to an external image (`https://picsum.photos/1024/768`).
|
The following is an example of a reference to an external image (`https://picsum.photos/1024/768`).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
## Image in /public
|
## Image in /public
|
||||||
|
|
||||||
This image is in the public directory (`/images/public.jpeg`).
|
This image is in the public directory (`/images/public.jpeg`).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
## Learn More
|
## Learn More
|
||||||
|
|
||||||
You can optionally configure many more things!
|
You can optionally configure many more things!
|
||||||
|
|
||||||
Checkout the [Astro ImageTools](https://astro-imagetools-docs.vercel.app/) documentation to learn more.
|
Checkout the [Astro ImageTools](https://astro-imagetools-docs.vercel.app/) documentation to learn more.
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<LayoutsLayout layout="constrained" importMetaUrl={import.meta.url} />
|
<LayoutsLayout layout="constrained" importMetaUrl={import.meta.url} />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<LayoutsLayout layout="fill" importMetaUrl={import.meta.url} />
|
<LayoutsLayout layout="fill" importMetaUrl={import.meta.url} />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<LayoutsLayout layout="fixed" importMetaUrl={import.meta.url} />
|
<LayoutsLayout layout="fixed" importMetaUrl={import.meta.url} />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<LayoutsLayout layout="fullWidth" importMetaUrl={import.meta.url} />
|
<LayoutsLayout layout="fullWidth" importMetaUrl={import.meta.url} />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<PlaceholderLayout placeholder="blurred" importMetaUrl={import.meta.url} />
|
<PlaceholderLayout placeholder="blurred" importMetaUrl={import.meta.url} />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<PlaceholderLayout placeholder="dominantColor" importMetaUrl={import.meta.url} />
|
<PlaceholderLayout placeholder="dominantColor" importMetaUrl={import.meta.url} />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<PlaceholderLayout placeholder="none" importMetaUrl={import.meta.url} />
|
<PlaceholderLayout placeholder="none" importMetaUrl={import.meta.url} />
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||||
---
|
---
|
||||||
|
|
||||||
<PlaceholderLayout placeholder="tracedSVG" importMetaUrl={import.meta.url} />
|
<PlaceholderLayout placeholder="tracedSVG" importMetaUrl={import.meta.url} />
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
main {
|
main {
|
||||||
padding: 1.5rem;
|
padding: 1.5rem;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
code::before {
|
code::before {
|
||||||
content: "`";
|
content: "`";
|
||||||
}
|
}
|
||||||
|
|
||||||
code::after {
|
code::after {
|
||||||
content: "`";
|
content: "`";
|
||||||
}
|
}
|
||||||
|
|
||||||
.view-source {
|
.view-source {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user