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": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/store/properties/alternatives/items"
|
||||
},
|
||||
"default": []
|
||||
"type": "string"
|
||||
},
|
||||
"tests": {
|
||||
"type": "array",
|
||||
@ -413,6 +409,27 @@
|
||||
},
|
||||
"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": [
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
{
|
||||
"_variables": {
|
||||
"lastUpdateCheck": 1740681917455
|
||||
"lastUpdateCheck": 1741718719942
|
||||
}
|
||||
}
|
||||
255
README.md
255
README.md
@ -1,111 +1,144 @@
|
||||
# Williamsburg
|
||||
|
||||
## Template Integrations
|
||||
- Tailwind CSS v4
|
||||
- Astro SEO - Powered by [@astrolib/seo](https://github.com/onwidget/astrolib/tree/main/packages/seo)
|
||||
- Astro Sitemap - https://docs.astro.build/en/guides/integrations-guide/sitemap/
|
||||
|
||||
## Template Structure
|
||||
|
||||
The template follows a typical Astro project structure. You'll find the following key directories and files:
|
||||
|
||||
|
||||
```
|
||||
/
|
||||
├── public/
|
||||
├── src/
|
||||
│ └── pages/
|
||||
│ └── index.astro
|
||||
└── package.json
|
||||
```
|
||||
|
||||
- `src/pages/`: Contains `.astro` and `.md` files. Each file becomes a route in your project based on its name.
|
||||
- `src/components/`: Ideal for placing your Astro/React/Vue/Svelte/Preact components.
|
||||
- `public/`: For static assets such as images that you want to serve directly.
|
||||
|
||||
## Commands
|
||||
|
||||
All commands are run from the root of the project, from a terminal:
|
||||
|
||||
| Command | Action |
|
||||
| :--------------------- | :----------------------------------------------- |
|
||||
| `npm install` | Installs dependencies |
|
||||
| `npm run dev` | Starts local dev server at `localhost:3000` |
|
||||
| `npm run build` | Build your production site to `./dist/` |
|
||||
| `npm run preview` | Preview your build locally, before deploying |
|
||||
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
|
||||
| `npm run astro --help` | Get help using the Astro CLI |
|
||||
|
||||
Learn more - Explore more through Astro's official [documentation](https://docs.astro.build).
|
||||
|
||||
------
|
||||
Updated on 30th December 2024
|
||||
|
||||
## This update includes:
|
||||
- Add Tailwind CSS v4 Beta
|
||||
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.
|
||||
- Astro V5
|
||||
This update includes Astro V5, which is a major update that includes several new features and improvements.
|
||||
|
||||
|
||||
- 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
|
||||
|
||||
## On the next update
|
||||
|
||||
- Add Image component from Astro
|
||||
The Astro Image component is coming back to the themes
|
||||
|
||||
- Reusable components
|
||||
This template now includes reusable components, such as the `Text` component:
|
||||
|
||||
- Text Component
|
||||
A versatile and reusable component for handling text across your project, ensuring consistency and easy customization.
|
||||
|
||||
- **HTML Tags:** Easily change the HTML element (like `p`, `h1`, `span`, `a`) using the `tag` prop, with `p` being the default.
|
||||
- **Variants:** Pick from preset text styles (such as `displayXL` or `textBase`) for a consistent look.
|
||||
- **Custom Classes:** Add or adjust styles with the `class` prop.
|
||||
- **Accessibility:** Customize with additional props like `id`, `href`, `title`, and `style`.
|
||||
- **Content Slot:** Add any content inside the component, including optional left and right icons.
|
||||
Example usage:
|
||||
```astro
|
||||
<Text tag="h1" variant="displayXL" class="text-center">
|
||||
Welcome to the new version!
|
||||
</Text>
|
||||
```
|
||||
|
||||
- Button Component
|
||||
A customizable button component with options to fit your design needs:
|
||||
|
||||
- **Variants:** Choose from predefined styles like `primary` (dark background) and `secondary` (lighter background), with support for dark mode.
|
||||
- **Sizes:** Select `small` or `medium` for different button heights and padding.
|
||||
- **Gaps:** Control the spacing between content with the `gapSize` prop (either `small` or `medium`).
|
||||
- **Custom Classes:** Apply additional styles using the `class` prop.
|
||||
- **Slots:** Include icons or extra content with optional `left-icon` and `right-icon` slots.
|
||||
Example usage:
|
||||
```astro
|
||||
<Button size="small" variant="primary">Primary small</Button>
|
||||
```
|
||||
|
||||
- Wrapper Component
|
||||
A flexible layout component that helps with consistent spacing and alignment.
|
||||
|
||||
- **Variants:** The default `standard` variant includes responsive widths, centered content, and padding.
|
||||
- **Custom Classes:** Add or change styles with the `class` prop.
|
||||
- **Content Slot:** Easily add any child components or content inside.
|
||||
|
||||
```astro
|
||||
<Wrapper variant="standard">
|
||||
Your content goes here
|
||||
</Wrapper>
|
||||
```
|
||||
-----
|
||||
|
||||
### [Support](https://lexingtonthemes.com/legal/support/)
|
||||
### [Documentation](https://lexingtonthemes.com/documentation/)
|
||||
### [Get your bundle](https://lexingtonthemes.com)
|
||||
|
||||
|
||||
### References
|
||||
|
||||
-[PWA](https://vite-pwa-org.netlify.app/)
|
||||
# Astro Site Documentation
|
||||
|
||||
This README provides comprehensive documentation for this Astro-based website project.
|
||||
|
||||
## 🚀 Project Overview
|
||||
|
||||
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.
|
||||
|
||||
## 📁 Project Structure
|
||||
|
||||
```
|
||||
/
|
||||
├── public/ # Static assets
|
||||
├── src/
|
||||
│ ├── components/ # UI components
|
||||
│ ├── content/ # Content collections
|
||||
│ ├── layouts/ # Page layouts
|
||||
│ ├── pages/ # Page routes
|
||||
│ │ ├── en/ # English pages
|
||||
│ │ └── [lang]/ # Other language pages
|
||||
│ ├── i18n/ # Internationalization files
|
||||
│ └── content.config.ts # Content collection configuration
|
||||
├── astro.config.mjs # Astro configuration
|
||||
├── tailwind.config.cjs # Tailwind CSS configuration
|
||||
└── package.json # Project dependencies
|
||||
```
|
||||
|
||||
## 📚 Content Collections
|
||||
|
||||
The site utilizes Astro's content collections to manage structured content. Collections are defined in `src/content.config.ts` and include:
|
||||
|
||||
- **Blog Posts**: Articles with frontmatter for metadata like title, date, author, etc.
|
||||
- **Authors**: Information about content authors.
|
||||
|
||||
Collections are stored in the `src/content/` directory, organized by language:
|
||||
- `src/content/blog/en/` - English blog posts
|
||||
- `src/content/blog/[lang]/` - Blog posts in other languages
|
||||
|
||||
## 🧩 Components
|
||||
|
||||
The site includes reusable UI components located in `src/components/`:
|
||||
|
||||
- **Core Components**: Basic UI elements like buttons, cards, and navigation
|
||||
- **Blog Components**: Specialized components for the blog section
|
||||
- **SEO Components**: Components for SEO optimization
|
||||
- **UI Components**: Interface elements like headers, footers, and menus
|
||||
|
||||
These components help maintain consistent design across the site while making development more efficient.
|
||||
|
||||
## 📏 Layouts
|
||||
|
||||
Layouts in `src/layouts/` provide consistent page structures:
|
||||
|
||||
- **BaseLayout.astro**: The main layout with common elements (header, footer)
|
||||
- **BlogLayout.astro**: Specialized layout for blog posts
|
||||
- **PostLayout.astro**: Layout for individual blog posts
|
||||
- **AboutLayout.astro**: Layout for about pages
|
||||
|
||||
Layouts wrap content with consistent HTML structure, styling, and components.
|
||||
|
||||
## 🌐 Internationalization (i18n)
|
||||
|
||||
The site supports multiple languages through a built-in internationalization system:
|
||||
|
||||
- Default language is English (`en`)
|
||||
- Content is organized by language in directory structures
|
||||
- URL format follows `/[lang]/[route]` pattern
|
||||
- Translation files are stored in the `src/i18n/` directory
|
||||
|
||||
Language switching is available to visitors, with localized content and UI elements.
|
||||
|
||||
## 📦 Dependencies
|
||||
|
||||
The project relies on the following main dependencies:
|
||||
|
||||
- **Astro**: Core framework for building the site
|
||||
- **Tailwind CSS**: Utility-first CSS framework for styling
|
||||
- **@astrojs/tailwind**: Tailwind CSS integration for Astro
|
||||
- **@astrojs/mdx**: MDX integration for enhanced markdown
|
||||
- **@astrojs/sitemap**: Automatic sitemap generation
|
||||
- **@astrojs/image**: Image optimization tools
|
||||
|
||||
Additional dependencies may include utility libraries and development tools.
|
||||
|
||||
## 🛠️ Development Instructions
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js (v16 or later)
|
||||
- npm or yarn
|
||||
|
||||
### Getting Started
|
||||
|
||||
1. Clone the repository:
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
cd <project-directory>
|
||||
```
|
||||
|
||||
2. Install dependencies:
|
||||
```bash
|
||||
npm install
|
||||
# or
|
||||
yarn install
|
||||
```
|
||||
|
||||
3. Start the development server:
|
||||
```bash
|
||||
npm run dev
|
||||
# or
|
||||
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"
|
||||
// https://astro-imagetools-docs.vercel.app/en/global-config-options/
|
||||
export default defineConfig({
|
||||
placeholder: "blurred",
|
||||
format: ["webp", "avif", "jpg"],
|
||||
fallbackFormat: "jpg",
|
||||
delay:250,
|
||||
includeSourceFormat: false,
|
||||
formatOptions: {
|
||||
jpg: {
|
||||
quality: 80,
|
||||
},
|
||||
png: {
|
||||
quality: 80,
|
||||
},
|
||||
webp: {
|
||||
quality: 50,
|
||||
}
|
||||
}
|
||||
import { defineConfig } from "imagetools/config"
|
||||
// https://astro-imagetools-docs.vercel.app/en/global-config-options/
|
||||
export default defineConfig({
|
||||
placeholder: "blurred",
|
||||
format: ["webp", "avif", "jpg"],
|
||||
fallbackFormat: "jpg",
|
||||
delay:250,
|
||||
includeSourceFormat: false,
|
||||
formatOptions: {
|
||||
jpg: {
|
||||
quality: 80,
|
||||
},
|
||||
png: {
|
||||
quality: 80,
|
||||
},
|
||||
webp: {
|
||||
quality: 50,
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -3,24 +3,37 @@ import tailwindcss from '@tailwindcss/vite'
|
||||
import { imagetools } from "imagetools"
|
||||
import react from "@astrojs/react"
|
||||
import mdx from "@astrojs/mdx";
|
||||
|
||||
import { defineConfig } from 'astro/config';
|
||||
import compress from 'vite-plugin-compression';
|
||||
import sitemap from "@astrojs/sitemap";
|
||||
export default defineConfig({
|
||||
site: 'https://polymech.io',
|
||||
devToolbar: {
|
||||
enabled: true,
|
||||
enabled: false,
|
||||
},
|
||||
i18n: {
|
||||
locales: ["es", "en", "de", "fr", "it", "ar", "ja", "zh"],
|
||||
locales: ["es", "en", "de", "fr", "it", "ar", "ja", "zh", "nl", "it", "pt"],
|
||||
defaultLocale: "en",
|
||||
},
|
||||
vite: {
|
||||
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: {
|
||||
target: 'esnext',
|
||||
assetsDir: './assets',
|
||||
modulePreload: { polyfill: false },
|
||||
commonjsOptions: { esmExternals: true }
|
||||
commonjsOptions: { esmExternals: true },
|
||||
assetsInlineLimit: 1024,
|
||||
sourcemap: false,
|
||||
},
|
||||
ssr: {
|
||||
external: ['cacache', 'glob', 'xlsx', 'sharp', '@polymech/kbot-d']
|
||||
@ -46,10 +59,11 @@ export default defineConfig({
|
||||
skipInline: false,
|
||||
drafts: true
|
||||
},
|
||||
site: 'https://polymech.io',
|
||||
integrations: [
|
||||
//starlight(),
|
||||
//sitemap(),
|
||||
sitemap({
|
||||
|
||||
}),
|
||||
mdx(),
|
||||
//AstroPWA({}),
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
| **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. |
|
||||
| **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. |
|
||||
| **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. |
|
||||
|
||||
---
|
||||
|
||||
### Additional Notes
|
||||
|
||||
- **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.).
|
||||
- React can be mixed in for **partial hydration**, letting you deliver mostly static pages but with interactive “islands” of React.
|
||||
|
||||
- **Vite Configuration**:
|
||||
- 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`.
|
||||
- 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**:
|
||||
- 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.
|
||||
|
||||
- **Hydration Directives**:
|
||||
- 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.
|
||||
|
||||
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.
|
||||
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** |
|
||||
|-----------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| **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). |
|
||||
| **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. |
|
||||
| **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
|
||||
|
||||
- **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.).
|
||||
- React can be mixed in for **partial hydration**, letting you deliver mostly static pages but with interactive “islands” of React.
|
||||
|
||||
- **Vite Configuration**:
|
||||
- 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`.
|
||||
- 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**:
|
||||
- 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.
|
||||
|
||||
- **Hydration Directives**:
|
||||
- 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.
|
||||
|
||||
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
|
||||
|
||||
- all documents in Markdown, insert new line after headings, and before code sections
|
||||
- standard chapters: brief, references (with links)
|
||||
# Preferences
|
||||
|
||||
- all documents in Markdown, insert new line after headings, and before code sections
|
||||
- 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
|
||||
|
||||
This document outlines how to implement canonical tags and hreflang attributes in an Astro project to support multilingual SEO.
|
||||
|
||||
## 1. SEO Component (`SEO.astro`)
|
||||
|
||||
Create a reusable component that accepts a canonical URL and an array of hreflang entries.
|
||||
|
||||
```astro
|
||||
---
|
||||
// src/components/SEO.astro
|
||||
const { canonical, hreflangs } = Astro.props;
|
||||
---
|
||||
{canonical && <link rel="canonical" href={canonical} />}
|
||||
{hreflangs &&
|
||||
hreflangs.map((entry) => (
|
||||
<link key={entry.lang} rel="alternate" hreflang={entry.lang} href={entry.url} />
|
||||
))}
|
||||
# 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.
|
||||
|
||||
## 1. SEO Component (`SEO.astro`)
|
||||
|
||||
Create a reusable component that accepts a canonical URL and an array of hreflang entries.
|
||||
|
||||
```astro
|
||||
---
|
||||
// src/components/SEO.astro
|
||||
const { canonical, hreflangs } = Astro.props;
|
||||
---
|
||||
{canonical && <link rel="canonical" href={canonical} />}
|
||||
{hreflangs &&
|
||||
hreflangs.map((entry) => (
|
||||
<link key={entry.lang} rel="alternate" hreflang={entry.lang} href={entry.url} />
|
||||
))}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
## Image to text
|
||||
|
||||
- [ ] options, free, images to text (not OCR)
|
||||
- [ ] as markdown table with links, prices, specs/features, language
|
||||
- [ ] check commercial and opensource models (huggingface)
|
||||
## Image to text
|
||||
|
||||
- [ ] options, free, images to text (not OCR)
|
||||
- [ ] as markdown table with links, prices, specs/features, language
|
||||
- [ ] check commercial and opensource models (huggingface)
|
||||
|
||||
@ -1,26 +1,26 @@
|
||||
## Map
|
||||
|
||||
- skip checked todos
|
||||
- dont comment
|
||||
- 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
|
||||
- [ ] document all in ../src/components/polymech/map.md, including API key setup
|
||||
- [ ] save all TS types in ../src/components/polymech/map-types.ts
|
||||
- [ ] center the map (center of all locations)
|
||||
- [ ] add options: satalite, terrain (default)
|
||||
|
||||
export interface GeoPos {
|
||||
lon: number
|
||||
lat: number
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
geo:GeoPos
|
||||
title:string
|
||||
}
|
||||
|
||||
export interface Options{
|
||||
zoom?:number
|
||||
api_key?:string
|
||||
}
|
||||
## Map
|
||||
|
||||
- skip checked todos
|
||||
- dont comment
|
||||
- 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
|
||||
- [ ] document all in ../src/components/polymech/map.md, including API key setup
|
||||
- [ ] save all TS types in ../src/components/polymech/map-types.ts
|
||||
- [ ] center the map (center of all locations)
|
||||
- [ ] add options: satalite, terrain (default)
|
||||
|
||||
export interface GeoPos {
|
||||
lon: number
|
||||
lat: number
|
||||
}
|
||||
|
||||
export interface Location {
|
||||
geo:GeoPos
|
||||
title:string
|
||||
}
|
||||
|
||||
export interface Options{
|
||||
zoom?:number
|
||||
api_key?:string
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
## i18n
|
||||
|
||||
skip checked todos
|
||||
|
||||
- [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
|
||||
## i18n
|
||||
|
||||
skip checked todos
|
||||
|
||||
- [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
|
||||
|
||||
142
package-lock.json
generated
142
package-lock.json
generated
@ -30,6 +30,7 @@
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.9",
|
||||
"cacache": "^19.0.1",
|
||||
"env-var": "^7.5.0",
|
||||
"exifreader": "^4.26.1",
|
||||
"file-type": "^20.0.0",
|
||||
"find-cache-dir": "^5.0.0",
|
||||
@ -64,6 +65,8 @@
|
||||
"tailwindcss": "^4.0.7",
|
||||
"type-fest": "^4.34.1",
|
||||
"vite": "^6.1.1",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"write-file-atomic": "^6.0.0",
|
||||
"xlsx": "^0.18.5",
|
||||
"yargs": "^17.7.2",
|
||||
"zod": "^3.24.2"
|
||||
@ -5132,6 +5135,24 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "1.0.1",
|
||||
"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==",
|
||||
"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": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
|
||||
@ -7382,7 +7426,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@ -8445,6 +8488,18 @@
|
||||
"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": {
|
||||
"version": "4.5.4",
|
||||
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
|
||||
@ -15325,6 +15380,15 @@
|
||||
"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": {
|
||||
"version": "1.0.0",
|
||||
"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": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
@ -16195,23 +16316,18 @@
|
||||
"license": "ISC"
|
||||
},
|
||||
"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==",
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz",
|
||||
"integrity": "sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"imurmurhash": "^0.1.4",
|
||||
"is-typedarray": "^1.0.0",
|
||||
"signal-exit": "^3.0.2",
|
||||
"typedarray-to-buffer": "^3.1.5"
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"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": {
|
||||
"version": "7.5.10",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz",
|
||||
|
||||
@ -40,6 +40,7 @@
|
||||
"autoprefixer": "^10.4.20",
|
||||
"axios": "^1.7.9",
|
||||
"cacache": "^19.0.1",
|
||||
"env-var": "^7.5.0",
|
||||
"exifreader": "^4.26.1",
|
||||
"file-type": "^20.0.0",
|
||||
"find-cache-dir": "^5.0.0",
|
||||
@ -74,6 +75,8 @@
|
||||
"tailwindcss": "^4.0.7",
|
||||
"type-fest": "^4.34.1",
|
||||
"vite": "^6.1.1",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"write-file-atomic": "^6.0.0",
|
||||
"xlsx": "^0.18.5",
|
||||
"yargs": "^17.7.2",
|
||||
"zod": "^3.24.2"
|
||||
|
||||
108
packages/api.md
108
packages/api.md
@ -1,55 +1,55 @@
|
||||
|
||||
|
||||
components->Img -> renderImg -> getImage(sizes, format ...) -> element
|
||||
|
||||
|
||||
getImage -> getProcessedImage(args[0] : src:local path)
|
||||
args[0]
|
||||
{
|
||||
src: "https://assets.osr-plastic.org//products/sheetpress/cassandra-edczmax-rc2/media/gallery/perspective.jpg",
|
||||
type: "Img",
|
||||
sizes: "(min-width: 800px) 800px, 800vw",
|
||||
format: "avif",
|
||||
breakpoints: undefined,
|
||||
placeholder: "blurred",
|
||||
artDirectives: [
|
||||
],
|
||||
fallbackFormat: "avif",
|
||||
includeSourceFormat: false,
|
||||
formatOptions: {
|
||||
jpg: {
|
||||
quality: 80,
|
||||
},
|
||||
png: {
|
||||
quality: 80,
|
||||
},
|
||||
webp: {
|
||||
quality: 50,
|
||||
},
|
||||
},
|
||||
transformConfigs: {
|
||||
},
|
||||
}
|
||||
|
||||
->
|
||||
|
||||
{
|
||||
uuid: "B16DBD24",
|
||||
images: [
|
||||
{
|
||||
sources: [
|
||||
{
|
||||
src: "/_astro/perspective@1320w.e183f84c.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",
|
||||
},
|
||||
],
|
||||
sizes: {
|
||||
width: 1320,
|
||||
height: 1980,
|
||||
},
|
||||
fallback: "data:image/avif;base64,
|
||||
imagesizes: "(min-width: 800px) 800px, 800vw",
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
components->Img -> renderImg -> getImage(sizes, format ...) -> element
|
||||
|
||||
|
||||
getImage -> getProcessedImage(args[0] : src:local path)
|
||||
args[0]
|
||||
{
|
||||
src: "https://assets.osr-plastic.org//products/sheetpress/cassandra-edczmax-rc2/media/gallery/perspective.jpg",
|
||||
type: "Img",
|
||||
sizes: "(min-width: 800px) 800px, 800vw",
|
||||
format: "avif",
|
||||
breakpoints: undefined,
|
||||
placeholder: "blurred",
|
||||
artDirectives: [
|
||||
],
|
||||
fallbackFormat: "avif",
|
||||
includeSourceFormat: false,
|
||||
formatOptions: {
|
||||
jpg: {
|
||||
quality: 80,
|
||||
},
|
||||
png: {
|
||||
quality: 80,
|
||||
},
|
||||
webp: {
|
||||
quality: 50,
|
||||
},
|
||||
},
|
||||
transformConfigs: {
|
||||
},
|
||||
}
|
||||
|
||||
->
|
||||
|
||||
{
|
||||
uuid: "B16DBD24",
|
||||
images: [
|
||||
{
|
||||
sources: [
|
||||
{
|
||||
src: "/_astro/perspective@1320w.e183f84c.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",
|
||||
},
|
||||
],
|
||||
sizes: {
|
||||
width: 1320,
|
||||
height: 1980,
|
||||
},
|
||||
fallback: "data:image/avif;base64,
|
||||
imagesizes: "(min-width: 800px) 800px, 800vw",
|
||||
},
|
||||
],
|
||||
}
|
||||
@ -1,17 +1,17 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
browser: true,
|
||||
es2020: true,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: "module",
|
||||
},
|
||||
plugins: ["unicorn"],
|
||||
extends: ["eslint:recommended"],
|
||||
rules: {
|
||||
"unicorn/prefer-node-protocol": "error",
|
||||
},
|
||||
};
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
browser: true,
|
||||
es2020: true,
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2022,
|
||||
sourceType: "module",
|
||||
},
|
||||
plugins: ["unicorn"],
|
||||
extends: ["eslint:recommended"],
|
||||
rules: {
|
||||
"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
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
lint:
|
||||
env:
|
||||
ASTRO_TELEMETRY_DISABLED: true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v2.2.1
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Prettier
|
||||
run: pnpm run format:check
|
||||
|
||||
- name: ESLint
|
||||
run: pnpm run lint
|
||||
|
||||
test:
|
||||
name: "Test: ${{ matrix.os }} (node@${{ matrix.node_version }})"
|
||||
env:
|
||||
ASTRO_TELEMETRY_DISABLED: true
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node_version: [14, 16]
|
||||
include:
|
||||
- os: windows-latest
|
||||
node_version: 16
|
||||
- os: macos-latest
|
||||
node_version: 16
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v2.2.1
|
||||
|
||||
- name: Setup node@${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter astro-imagetools run test
|
||||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
lint:
|
||||
env:
|
||||
ASTRO_TELEMETRY_DISABLED: true
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v2.2.1
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: "pnpm"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Prettier
|
||||
run: pnpm run format:check
|
||||
|
||||
- name: ESLint
|
||||
run: pnpm run lint
|
||||
|
||||
test:
|
||||
name: "Test: ${{ matrix.os }} (node@${{ matrix.node_version }})"
|
||||
env:
|
||||
ASTRO_TELEMETRY_DISABLED: true
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
node_version: [14, 16]
|
||||
include:
|
||||
- os: windows-latest
|
||||
node_version: 16
|
||||
- os: macos-latest
|
||||
node_version: 16
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup PNPM
|
||||
uses: pnpm/action-setup@v2.2.1
|
||||
|
||||
- name: Setup node@${{ matrix.node_version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node_version }}
|
||||
cache: "pnpm"
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
|
||||
- name: Test
|
||||
run: pnpm --filter astro-imagetools run test
|
||||
|
||||
40
packages/imagetools/.gitignore
vendored
40
packages/imagetools/.gitignore
vendored
@ -1,20 +1,20 @@
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# build output
|
||||
dist/
|
||||
|
||||
# logs
|
||||
*.log
|
||||
|
||||
# npm
|
||||
package-lock.json
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# env
|
||||
*.env
|
||||
|
||||
# astro-imagetools
|
||||
packages/astro-imagetools/astroViteConfigs.js
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
# build output
|
||||
dist/
|
||||
|
||||
# logs
|
||||
*.log
|
||||
|
||||
# npm
|
||||
package-lock.json
|
||||
|
||||
# macOS-specific files
|
||||
.DS_Store
|
||||
|
||||
# env
|
||||
*.env
|
||||
|
||||
# astro-imagetools
|
||||
packages/astro-imagetools/astroViteConfigs.js
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
*.test.ts
|
||||
test-fixtures
|
||||
astroViteConfigs.js
|
||||
vitest.config.ts
|
||||
*.test.ts
|
||||
test-fixtures
|
||||
astroViteConfigs.js
|
||||
vitest.config.ts
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
## force pnpm to hoist
|
||||
## force pnpm to hoist
|
||||
shamefully-hoist = true
|
||||
@ -1,2 +1,2 @@
|
||||
pnpm-lock.yaml
|
||||
demo/dist
|
||||
pnpm-lock.yaml
|
||||
demo/dist
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"files": "**/*.astro",
|
||||
"options": { "parser": "astro" }
|
||||
}
|
||||
],
|
||||
"plugins": ["prettier-plugin-astro"]
|
||||
}
|
||||
{
|
||||
"overrides": [
|
||||
{
|
||||
"files": "**/*.astro",
|
||||
"options": { "parser": "astro" }
|
||||
}
|
||||
],
|
||||
"plugins": ["prettier-plugin-astro"]
|
||||
}
|
||||
|
||||
@ -1,21 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Rafid Muhymin Wafi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Rafid Muhymin Wafi
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
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
|
||||
SOFTWARE.
|
||||
@ -1,39 +1,39 @@
|
||||
# **Astro ImageTools**
|
||||
|
||||
**Astro ImageTools** is a collection of tools for optimizing images, background images, and generating responsive images for the **Astro JS** framework.
|
||||
|
||||
## Features
|
||||
|
||||
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>`)
|
||||
- ✅ **Background Image Optimization**
|
||||
- ✅ **Responsive Images**
|
||||
- ✅ **Simple and intuitive Art Direction API**
|
||||
- ✅ **Lazy Loading**
|
||||
- ✅ **Programmatic APIs**
|
||||
- ✅ **Asynchronous Decoding**
|
||||
- ✅ **Unique Breakpoints Calculation**
|
||||
- ✅ **Preloading for urgent images**
|
||||
- ✅ **SVG Tracing and Posterization**
|
||||
- ✅ **100% Scoped CSS**
|
||||
- ✅ **Four kind of Layouts: `constrained`, `fixed`, `fullWidth` & `fill`**
|
||||
- ✅ **Three kind of Placeholder Images: `blurred`, `dominantColor` & `tracedSVG`**
|
||||
- ✅ **Long list of supported Image Formats**
|
||||
- ✅ **Long List of supported Configuration Options**
|
||||
- ✅ **Supports Remote Images and Data URIs too**
|
||||
- ✅ **Support for _`sharp`less_ Environments**
|
||||
- ✅ **Both Memory-based and FS-based Caching for better Performance**
|
||||
- ✅ **Respects to _Semantics of HTML_ as much as possible**
|
||||
|
||||
## 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.
|
||||
|
||||
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 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. 🧑🚀
|
||||
# **Astro ImageTools**
|
||||
|
||||
**Astro ImageTools** is a collection of tools for optimizing images, background images, and generating responsive images for the **Astro JS** framework.
|
||||
|
||||
## Features
|
||||
|
||||
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>`)
|
||||
- ✅ **Background Image Optimization**
|
||||
- ✅ **Responsive Images**
|
||||
- ✅ **Simple and intuitive Art Direction API**
|
||||
- ✅ **Lazy Loading**
|
||||
- ✅ **Programmatic APIs**
|
||||
- ✅ **Asynchronous Decoding**
|
||||
- ✅ **Unique Breakpoints Calculation**
|
||||
- ✅ **Preloading for urgent images**
|
||||
- ✅ **SVG Tracing and Posterization**
|
||||
- ✅ **100% Scoped CSS**
|
||||
- ✅ **Four kind of Layouts: `constrained`, `fixed`, `fullWidth` & `fill`**
|
||||
- ✅ **Three kind of Placeholder Images: `blurred`, `dominantColor` & `tracedSVG`**
|
||||
- ✅ **Long list of supported Image Formats**
|
||||
- ✅ **Long List of supported Configuration Options**
|
||||
- ✅ **Supports Remote Images and Data URIs too**
|
||||
- ✅ **Support for _`sharp`less_ Environments**
|
||||
- ✅ **Both Memory-based and FS-based Caching for better Performance**
|
||||
- ✅ **Respects to _Semantics of HTML_ as much as possible**
|
||||
|
||||
## 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.
|
||||
|
||||
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 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. 🧑🚀
|
||||
|
||||
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 { getSrcPath } from "./utils/getSrcPath.js";
|
||||
import getResolvedSrc from "./utils/getResolvedSrc.js";
|
||||
|
||||
export default async function importImage(path) {
|
||||
try {
|
||||
const { search, protocol, pathname } = new URL(path);
|
||||
|
||||
const { src: id, base } = await getResolvedSrc(
|
||||
protocol === "data:" ? protocol + pathname : path
|
||||
);
|
||||
|
||||
const src = (await load(id + search, base)).slice(16, -1);
|
||||
|
||||
return src;
|
||||
} catch (error) {
|
||||
const id = await getSrcPath(path);
|
||||
|
||||
const src = (await load(id)).slice(16, -1);
|
||||
|
||||
return src;
|
||||
}
|
||||
}
|
||||
import load from "../plugin/hooks/load.js";
|
||||
import { getSrcPath } from "./utils/getSrcPath.js";
|
||||
import getResolvedSrc from "./utils/getResolvedSrc.js";
|
||||
|
||||
export default async function importImage(path) {
|
||||
try {
|
||||
const { search, protocol, pathname } = new URL(path);
|
||||
|
||||
const { src: id, base } = await getResolvedSrc(
|
||||
protocol === "data:" ? protocol + pathname : path
|
||||
);
|
||||
|
||||
const src = (await load(id + search, base)).slice(16, -1);
|
||||
|
||||
return src;
|
||||
} catch (error) {
|
||||
const id = await getSrcPath(path);
|
||||
|
||||
const src = (await load(id)).slice(16, -1);
|
||||
|
||||
return src;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
export { default as renderImg } from "./renderImg.js";
|
||||
export { default as renderPicture } from "./renderPicture.js";
|
||||
export { default as renderBackgroundImage } from "./renderBackgroundImage.js";
|
||||
export { default as renderBackgroundPicture } from "./renderBackgroundPicture.js";
|
||||
export { default as importImage } from "./importImage.js";
|
||||
export { default as renderImg } from "./renderImg.js";
|
||||
export { default as renderPicture } from "./renderPicture.js";
|
||||
export { default as renderBackgroundImage } from "./renderBackgroundImage.js";
|
||||
export { default as renderBackgroundPicture } from "./renderBackgroundPicture.js";
|
||||
export { default as importImage } from "./importImage.js";
|
||||
export { getImageDetails, loadImage } from "./utils/imagetools.js"
|
||||
@ -1,8 +1,8 @@
|
||||
import type {
|
||||
BackgroundImageConfigOptions,
|
||||
BackgroundImageHTMLData,
|
||||
} from "../types";
|
||||
|
||||
export default function renderBackgroundImage(
|
||||
config: BackgroundImageConfigOptions
|
||||
): Promise<BackgroundImageHTMLData>;
|
||||
import type {
|
||||
BackgroundImageConfigOptions,
|
||||
BackgroundImageHTMLData,
|
||||
} from "../types";
|
||||
|
||||
export default function renderBackgroundImage(
|
||||
config: BackgroundImageConfigOptions
|
||||
): Promise<BackgroundImageHTMLData>;
|
||||
|
||||
@ -1,159 +1,159 @@
|
||||
// @ts-check
|
||||
import crypto from "node:crypto";
|
||||
import getImage from "./utils/getImage.js";
|
||||
import getLinkElement from "./utils/getLinkElement.js";
|
||||
import getStyleElement from "./utils/getStyleElement.js";
|
||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||
import getContainerElement from "./utils/getContainerElement.js";
|
||||
|
||||
export default async function renderBackgroundImage(props) {
|
||||
const type = "BackgroundImage";
|
||||
|
||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||
|
||||
const {
|
||||
src,
|
||||
tag,
|
||||
content,
|
||||
preload,
|
||||
attributes,
|
||||
placeholder,
|
||||
breakpoints,
|
||||
backgroundSize,
|
||||
backgroundPosition,
|
||||
format,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
artDirectives,
|
||||
} = filteredProps;
|
||||
|
||||
const {
|
||||
link: linkAttributes = {},
|
||||
style: styleAttributes = {},
|
||||
container: containerAttributes = {},
|
||||
} = attributes;
|
||||
|
||||
const sizes = "";
|
||||
|
||||
const { uuid, images } = await getImage({
|
||||
src,
|
||||
type,
|
||||
sizes,
|
||||
format,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
artDirectives,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
transformConfigs,
|
||||
});
|
||||
|
||||
const className = `astro-imagetools-background-image-${uuid}`;
|
||||
|
||||
const { imagesizes } = images[images.length - 1];
|
||||
|
||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
||||
|
||||
const backgroundImageStylesArray = images.map(({ media, sources }) => {
|
||||
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
||||
|
||||
const fallbackUrlCustomVariable = `--astro-imagetools-background-image-fallback-url${uuid}`;
|
||||
|
||||
const newSources = {};
|
||||
|
||||
sources.forEach(({ src, format, srcset }) => {
|
||||
const sources = srcset
|
||||
.split(", ")
|
||||
.map((source) => [
|
||||
source.slice(0, source.lastIndexOf(" ")),
|
||||
source.slice(source.lastIndexOf(" ") + 1, -1),
|
||||
]);
|
||||
|
||||
sources.forEach(([path, width]) => {
|
||||
if (!newSources[width]) {
|
||||
newSources[width] = [];
|
||||
}
|
||||
|
||||
newSources[width].push({ src, format, path });
|
||||
});
|
||||
});
|
||||
|
||||
const widths = Object.keys(newSources)
|
||||
.map((width) => parseInt(width))
|
||||
.reverse();
|
||||
|
||||
const maxWidth = Math.max(...widths);
|
||||
|
||||
const styles = widths
|
||||
.map((width) => {
|
||||
const sources = newSources[width];
|
||||
|
||||
const styles = sources
|
||||
.map(
|
||||
({ format, path }, i) =>
|
||||
`
|
||||
${i !== sources.length - 1 ? `.${format} ` : ""}.${className} {
|
||||
background-repeat: no-repeat;
|
||||
background-image: url(${path}),
|
||||
var(${fallbackUrlCustomVariable});
|
||||
background-size: ${backgroundSize};
|
||||
background-position: ${backgroundPosition};
|
||||
}
|
||||
`
|
||||
)
|
||||
.reverse()
|
||||
.join("");
|
||||
|
||||
return width === maxWidth
|
||||
? styles
|
||||
: `
|
||||
@media screen and (max-width: ${width}px) {
|
||||
${styles}
|
||||
}
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
return {
|
||||
fallbackUrlCustomVariable,
|
||||
styles: media
|
||||
? `
|
||||
@media ${media} {
|
||||
${styles}
|
||||
}
|
||||
`
|
||||
: styles,
|
||||
};
|
||||
});
|
||||
|
||||
const containerStyles = `
|
||||
.${className} {
|
||||
position: relative;
|
||||
${images
|
||||
.map(({ fallback }, i) => {
|
||||
const fallbackUrlCustomVariable =
|
||||
backgroundImageStylesArray[i].fallbackUrlCustomVariable;
|
||||
|
||||
return `${fallbackUrlCustomVariable}: url("${encodeURI(fallback)}");`;
|
||||
})
|
||||
.join("\n")}
|
||||
}
|
||||
`;
|
||||
|
||||
const backgroundStyles =
|
||||
backgroundImageStylesArray.map(({ styles }) => styles).join("\n") +
|
||||
containerStyles;
|
||||
|
||||
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
||||
|
||||
const htmlElement = getContainerElement({
|
||||
tag,
|
||||
content,
|
||||
className,
|
||||
containerAttributes,
|
||||
});
|
||||
|
||||
return { link, style, htmlElement };
|
||||
}
|
||||
// @ts-check
|
||||
import crypto from "node:crypto";
|
||||
import getImage from "./utils/getImage.js";
|
||||
import getLinkElement from "./utils/getLinkElement.js";
|
||||
import getStyleElement from "./utils/getStyleElement.js";
|
||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||
import getContainerElement from "./utils/getContainerElement.js";
|
||||
|
||||
export default async function renderBackgroundImage(props) {
|
||||
const type = "BackgroundImage";
|
||||
|
||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||
|
||||
const {
|
||||
src,
|
||||
tag,
|
||||
content,
|
||||
preload,
|
||||
attributes,
|
||||
placeholder,
|
||||
breakpoints,
|
||||
backgroundSize,
|
||||
backgroundPosition,
|
||||
format,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
artDirectives,
|
||||
} = filteredProps;
|
||||
|
||||
const {
|
||||
link: linkAttributes = {},
|
||||
style: styleAttributes = {},
|
||||
container: containerAttributes = {},
|
||||
} = attributes;
|
||||
|
||||
const sizes = "";
|
||||
|
||||
const { uuid, images } = await getImage({
|
||||
src,
|
||||
type,
|
||||
sizes,
|
||||
format,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
artDirectives,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
transformConfigs,
|
||||
});
|
||||
|
||||
const className = `astro-imagetools-background-image-${uuid}`;
|
||||
|
||||
const { imagesizes } = images[images.length - 1];
|
||||
|
||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
||||
|
||||
const backgroundImageStylesArray = images.map(({ media, sources }) => {
|
||||
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
||||
|
||||
const fallbackUrlCustomVariable = `--astro-imagetools-background-image-fallback-url${uuid}`;
|
||||
|
||||
const newSources = {};
|
||||
|
||||
sources.forEach(({ src, format, srcset }) => {
|
||||
const sources = srcset
|
||||
.split(", ")
|
||||
.map((source) => [
|
||||
source.slice(0, source.lastIndexOf(" ")),
|
||||
source.slice(source.lastIndexOf(" ") + 1, -1),
|
||||
]);
|
||||
|
||||
sources.forEach(([path, width]) => {
|
||||
if (!newSources[width]) {
|
||||
newSources[width] = [];
|
||||
}
|
||||
|
||||
newSources[width].push({ src, format, path });
|
||||
});
|
||||
});
|
||||
|
||||
const widths = Object.keys(newSources)
|
||||
.map((width) => parseInt(width))
|
||||
.reverse();
|
||||
|
||||
const maxWidth = Math.max(...widths);
|
||||
|
||||
const styles = widths
|
||||
.map((width) => {
|
||||
const sources = newSources[width];
|
||||
|
||||
const styles = sources
|
||||
.map(
|
||||
({ format, path }, i) =>
|
||||
`
|
||||
${i !== sources.length - 1 ? `.${format} ` : ""}.${className} {
|
||||
background-repeat: no-repeat;
|
||||
background-image: url(${path}),
|
||||
var(${fallbackUrlCustomVariable});
|
||||
background-size: ${backgroundSize};
|
||||
background-position: ${backgroundPosition};
|
||||
}
|
||||
`
|
||||
)
|
||||
.reverse()
|
||||
.join("");
|
||||
|
||||
return width === maxWidth
|
||||
? styles
|
||||
: `
|
||||
@media screen and (max-width: ${width}px) {
|
||||
${styles}
|
||||
}
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
return {
|
||||
fallbackUrlCustomVariable,
|
||||
styles: media
|
||||
? `
|
||||
@media ${media} {
|
||||
${styles}
|
||||
}
|
||||
`
|
||||
: styles,
|
||||
};
|
||||
});
|
||||
|
||||
const containerStyles = `
|
||||
.${className} {
|
||||
position: relative;
|
||||
${images
|
||||
.map(({ fallback }, i) => {
|
||||
const fallbackUrlCustomVariable =
|
||||
backgroundImageStylesArray[i].fallbackUrlCustomVariable;
|
||||
|
||||
return `${fallbackUrlCustomVariable}: url("${encodeURI(fallback)}");`;
|
||||
})
|
||||
.join("\n")}
|
||||
}
|
||||
`;
|
||||
|
||||
const backgroundStyles =
|
||||
backgroundImageStylesArray.map(({ styles }) => styles).join("\n") +
|
||||
containerStyles;
|
||||
|
||||
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
||||
|
||||
const htmlElement = getContainerElement({
|
||||
tag,
|
||||
content,
|
||||
className,
|
||||
containerAttributes,
|
||||
});
|
||||
|
||||
return { link, style, htmlElement };
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import type {
|
||||
BackgroundPictureConfigOptions,
|
||||
BackgroundPictureHTMLData,
|
||||
} from "../types";
|
||||
|
||||
export default function renderBackgroundPicture(
|
||||
config: BackgroundPictureConfigOptions
|
||||
): Promise<BackgroundPictureHTMLData>;
|
||||
import type {
|
||||
BackgroundPictureConfigOptions,
|
||||
BackgroundPictureHTMLData,
|
||||
} from "../types";
|
||||
|
||||
export default function renderBackgroundPicture(
|
||||
config: BackgroundPictureConfigOptions
|
||||
): Promise<BackgroundPictureHTMLData>;
|
||||
|
||||
@ -1,127 +1,127 @@
|
||||
// @ts-check
|
||||
import getImage from "./utils/getImage.js";
|
||||
import getImgElement from "./utils/getImgElement.js";
|
||||
import getLinkElement from "./utils/getLinkElement.js";
|
||||
import getStyleElement from "./utils/getStyleElement.js";
|
||||
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||
import getPictureElement from "./utils/getPictureElement.js";
|
||||
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
||||
import getContainerElement from "./utils/getContainerElement.js";
|
||||
|
||||
export default async function renderBackgroundPicture(props) {
|
||||
const type = "BackgroundPicture";
|
||||
|
||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||
|
||||
const {
|
||||
src,
|
||||
tag,
|
||||
content,
|
||||
sizes,
|
||||
preload,
|
||||
loading,
|
||||
decoding,
|
||||
attributes,
|
||||
placeholder,
|
||||
breakpoints,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
format,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
fadeInTransition,
|
||||
artDirectives,
|
||||
} = filteredProps;
|
||||
|
||||
const {
|
||||
img: imgAttributes = {},
|
||||
link: linkAttributes = {},
|
||||
style: styleAttributes = {},
|
||||
picture: pictureAttributes = {},
|
||||
container: containerAttributes = {},
|
||||
} = attributes;
|
||||
|
||||
const { uuid, images } = await getImage({
|
||||
src,
|
||||
type,
|
||||
sizes,
|
||||
format,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
artDirectives,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
transformConfigs,
|
||||
});
|
||||
|
||||
const className = `astro-imagetools-picture-${uuid}`,
|
||||
containerClassName = `astro-imagetools-background-picture-${uuid}`;
|
||||
|
||||
const { imagesizes } = images[images.length - 1];
|
||||
|
||||
const backgroundStyles = getBackgroundStyles(
|
||||
images,
|
||||
className,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
fadeInTransition,
|
||||
{ isBackgroundPicture: true, containerClassName }
|
||||
);
|
||||
|
||||
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
||||
|
||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
||||
|
||||
const layoutStyles = getLayoutStyles({ isBackgroundImage: true });
|
||||
|
||||
// Background Images shouldn't convey important information
|
||||
const alt = "";
|
||||
|
||||
const sources = images.flatMap(({ media, sources, sizes, imagesizes }) =>
|
||||
sources.map(({ format, src, srcset }) =>
|
||||
src
|
||||
? getImgElement({
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
style,
|
||||
srcset,
|
||||
loading,
|
||||
decoding,
|
||||
imagesizes,
|
||||
fadeInTransition,
|
||||
layoutStyles,
|
||||
imgAttributes,
|
||||
})
|
||||
: `<source
|
||||
srcset="${srcset}"
|
||||
sizes="${imagesizes}"
|
||||
width="${sizes.width}"
|
||||
height="${sizes.height}"
|
||||
type="${`image/${format}`}"
|
||||
${media ? `media="${media}"` : ""}
|
||||
/>`
|
||||
)
|
||||
);
|
||||
|
||||
const picture = getPictureElement({
|
||||
sources,
|
||||
className,
|
||||
layoutStyles,
|
||||
pictureAttributes,
|
||||
isBackgroundPicture: true,
|
||||
});
|
||||
|
||||
const htmlElement = getContainerElement({
|
||||
tag,
|
||||
content: picture + content,
|
||||
containerAttributes,
|
||||
isBackgroundPicture: true,
|
||||
containerClassName,
|
||||
});
|
||||
|
||||
return { link, style, htmlElement };
|
||||
}
|
||||
// @ts-check
|
||||
import getImage from "./utils/getImage.js";
|
||||
import getImgElement from "./utils/getImgElement.js";
|
||||
import getLinkElement from "./utils/getLinkElement.js";
|
||||
import getStyleElement from "./utils/getStyleElement.js";
|
||||
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||
import getPictureElement from "./utils/getPictureElement.js";
|
||||
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
||||
import getContainerElement from "./utils/getContainerElement.js";
|
||||
|
||||
export default async function renderBackgroundPicture(props) {
|
||||
const type = "BackgroundPicture";
|
||||
|
||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||
|
||||
const {
|
||||
src,
|
||||
tag,
|
||||
content,
|
||||
sizes,
|
||||
preload,
|
||||
loading,
|
||||
decoding,
|
||||
attributes,
|
||||
placeholder,
|
||||
breakpoints,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
format,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
fadeInTransition,
|
||||
artDirectives,
|
||||
} = filteredProps;
|
||||
|
||||
const {
|
||||
img: imgAttributes = {},
|
||||
link: linkAttributes = {},
|
||||
style: styleAttributes = {},
|
||||
picture: pictureAttributes = {},
|
||||
container: containerAttributes = {},
|
||||
} = attributes;
|
||||
|
||||
const { uuid, images } = await getImage({
|
||||
src,
|
||||
type,
|
||||
sizes,
|
||||
format,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
artDirectives,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
transformConfigs,
|
||||
});
|
||||
|
||||
const className = `astro-imagetools-picture-${uuid}`,
|
||||
containerClassName = `astro-imagetools-background-picture-${uuid}`;
|
||||
|
||||
const { imagesizes } = images[images.length - 1];
|
||||
|
||||
const backgroundStyles = getBackgroundStyles(
|
||||
images,
|
||||
className,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
fadeInTransition,
|
||||
{ isBackgroundPicture: true, containerClassName }
|
||||
);
|
||||
|
||||
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
||||
|
||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
||||
|
||||
const layoutStyles = getLayoutStyles({ isBackgroundImage: true });
|
||||
|
||||
// Background Images shouldn't convey important information
|
||||
const alt = "";
|
||||
|
||||
const sources = images.flatMap(({ media, sources, sizes, imagesizes }) =>
|
||||
sources.map(({ format, src, srcset }) =>
|
||||
src
|
||||
? getImgElement({
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
style,
|
||||
srcset,
|
||||
loading,
|
||||
decoding,
|
||||
imagesizes,
|
||||
fadeInTransition,
|
||||
layoutStyles,
|
||||
imgAttributes,
|
||||
})
|
||||
: `<source
|
||||
srcset="${srcset}"
|
||||
sizes="${imagesizes}"
|
||||
width="${sizes.width}"
|
||||
height="${sizes.height}"
|
||||
type="${`image/${format}`}"
|
||||
${media ? `media="${media}"` : ""}
|
||||
/>`
|
||||
)
|
||||
);
|
||||
|
||||
const picture = getPictureElement({
|
||||
sources,
|
||||
className,
|
||||
layoutStyles,
|
||||
pictureAttributes,
|
||||
isBackgroundPicture: true,
|
||||
});
|
||||
|
||||
const htmlElement = getContainerElement({
|
||||
tag,
|
||||
content: picture + content,
|
||||
containerAttributes,
|
||||
isBackgroundPicture: true,
|
||||
containerClassName,
|
||||
});
|
||||
|
||||
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";
|
||||
|
||||
export default function renderImg(
|
||||
config: ImgConfigOptions
|
||||
): Promise<ImgHTMLData>;
|
||||
import type { ImgConfigOptions, ImgHTMLData } from "../types";
|
||||
|
||||
export default function renderImg(
|
||||
config: ImgConfigOptions
|
||||
): Promise<ImgHTMLData>;
|
||||
|
||||
@ -1,93 +1,93 @@
|
||||
// @ts-check
|
||||
import getImage from "./utils/getImage.js";
|
||||
import getImgElement from "./utils/getImgElement.js";
|
||||
import getLinkElement from "./utils/getLinkElement.js";
|
||||
import getStyleElement from "./utils/getStyleElement.js";
|
||||
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
||||
|
||||
export default async function renderImg(props) {
|
||||
const type = "Img";
|
||||
|
||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||
|
||||
const {
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
preload,
|
||||
loading,
|
||||
decoding,
|
||||
attributes,
|
||||
layout,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
format,
|
||||
formatOptions,
|
||||
} = filteredProps;
|
||||
|
||||
const artDirectives = [],
|
||||
fallbackFormat = format,
|
||||
fadeInTransition = false,
|
||||
includeSourceFormat = false;
|
||||
|
||||
const {
|
||||
img: imgAttributes = {},
|
||||
link: linkAttributes = {},
|
||||
style: styleAttributes = {},
|
||||
} = attributes;
|
||||
|
||||
const { uuid, images } = await getImage({
|
||||
src,
|
||||
type,
|
||||
sizes,
|
||||
format,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
artDirectives,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
transformConfigs,
|
||||
});
|
||||
|
||||
const className = `astro-imagetools-img-${uuid}`;
|
||||
|
||||
const { imagesizes } = images[images.length - 1];
|
||||
const backgroundStyles = getBackgroundStyles(
|
||||
images,
|
||||
className,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
fadeInTransition,
|
||||
{ isImg: true }
|
||||
);
|
||||
const style = getStyleElement({ styleAttributes, backgroundStyles })
|
||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes })
|
||||
const layoutStyles = getLayoutStyles({ layout })
|
||||
|
||||
const sources = images.flatMap(({ sources, sizes, imagesizes }) =>
|
||||
sources.map(({ src, srcset }) =>
|
||||
getImgElement({
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
style,
|
||||
srcset,
|
||||
loading,
|
||||
decoding,
|
||||
imagesizes,
|
||||
fadeInTransition,
|
||||
layoutStyles,
|
||||
imgAttributes,
|
||||
imgClassName: className,
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
const [img] = sources
|
||||
return { link, style, img }
|
||||
}
|
||||
// @ts-check
|
||||
import getImage from "./utils/getImage.js";
|
||||
import getImgElement from "./utils/getImgElement.js";
|
||||
import getLinkElement from "./utils/getLinkElement.js";
|
||||
import getStyleElement from "./utils/getStyleElement.js";
|
||||
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
||||
|
||||
export default async function renderImg(props) {
|
||||
const type = "Img";
|
||||
|
||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||
|
||||
const {
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
preload,
|
||||
loading,
|
||||
decoding,
|
||||
attributes,
|
||||
layout,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
format,
|
||||
formatOptions,
|
||||
} = filteredProps;
|
||||
|
||||
const artDirectives = [],
|
||||
fallbackFormat = format,
|
||||
fadeInTransition = false,
|
||||
includeSourceFormat = false;
|
||||
|
||||
const {
|
||||
img: imgAttributes = {},
|
||||
link: linkAttributes = {},
|
||||
style: styleAttributes = {},
|
||||
} = attributes;
|
||||
|
||||
const { uuid, images } = await getImage({
|
||||
src,
|
||||
type,
|
||||
sizes,
|
||||
format,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
artDirectives,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
transformConfigs,
|
||||
});
|
||||
|
||||
const className = `astro-imagetools-img-${uuid}`;
|
||||
|
||||
const { imagesizes } = images[images.length - 1];
|
||||
const backgroundStyles = getBackgroundStyles(
|
||||
images,
|
||||
className,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
fadeInTransition,
|
||||
{ isImg: true }
|
||||
);
|
||||
const style = getStyleElement({ styleAttributes })
|
||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes })
|
||||
const layoutStyles = getLayoutStyles({ layout })
|
||||
|
||||
const sources = images.flatMap(({ sources, sizes, imagesizes }) =>
|
||||
sources.map(({ src, srcset }) =>
|
||||
getImgElement({
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
style,
|
||||
srcset,
|
||||
loading,
|
||||
decoding,
|
||||
imagesizes,
|
||||
fadeInTransition,
|
||||
layoutStyles,
|
||||
imgAttributes,
|
||||
imgClassName: className,
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
const [img] = sources
|
||||
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";
|
||||
|
||||
export default function renderPicture(
|
||||
config: PictureConfigOptions
|
||||
): Promise<PictureHTMLData>;
|
||||
import type { PictureConfigOptions, PictureHTMLData } from "../types";
|
||||
|
||||
export default function renderPicture(
|
||||
config: PictureConfigOptions
|
||||
): Promise<PictureHTMLData>;
|
||||
|
||||
@ -1,111 +1,111 @@
|
||||
// @ts-check
|
||||
import getImage from "./utils/getImage.js";
|
||||
import getImgElement from "./utils/getImgElement.js";
|
||||
import getLinkElement from "./utils/getLinkElement.js";
|
||||
import getStyleElement from "./utils/getStyleElement.js";
|
||||
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||
import getPictureElement from "./utils/getPictureElement.js";
|
||||
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
||||
|
||||
export default async function renderPicture(props) {
|
||||
const type = "Picture";
|
||||
|
||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||
|
||||
const {
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
preload,
|
||||
loading,
|
||||
decoding,
|
||||
attributes,
|
||||
layout,
|
||||
placeholder,
|
||||
breakpoints,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
format,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
fadeInTransition,
|
||||
artDirectives,
|
||||
} = filteredProps;
|
||||
|
||||
const {
|
||||
img: imgAttributes = {},
|
||||
link: linkAttributes = {},
|
||||
style: styleAttributes = {},
|
||||
picture: pictureAttributes = {},
|
||||
} = attributes;
|
||||
|
||||
const { uuid, images } = await getImage({
|
||||
src,
|
||||
type,
|
||||
sizes,
|
||||
format,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
artDirectives,
|
||||
transformConfigs,
|
||||
});
|
||||
|
||||
const className = `astro-imagetools-picture-${uuid}`;
|
||||
|
||||
const { imagesizes } = images[images.length - 1];
|
||||
|
||||
const backgroundStyles = getBackgroundStyles(
|
||||
images,
|
||||
className,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
fadeInTransition
|
||||
);
|
||||
|
||||
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
||||
|
||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
||||
|
||||
const layoutStyles = getLayoutStyles({ layout });
|
||||
|
||||
const sources = images.flatMap(({ media, sources, sizes, imagesizes }) =>
|
||||
sources.map(({ format, src, srcset }) =>
|
||||
src
|
||||
? getImgElement({
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
style,
|
||||
srcset,
|
||||
loading,
|
||||
decoding,
|
||||
imagesizes,
|
||||
fadeInTransition,
|
||||
layoutStyles,
|
||||
imgAttributes,
|
||||
})
|
||||
: `<source
|
||||
srcset="${srcset}"
|
||||
sizes="${imagesizes}"
|
||||
width="${sizes.width}"
|
||||
height="${sizes.height}"
|
||||
type="${`image/${format}`}"
|
||||
${media ? `media="${media}"` : ""}
|
||||
/>`
|
||||
)
|
||||
);
|
||||
|
||||
const picture = getPictureElement({
|
||||
sources,
|
||||
className,
|
||||
layoutStyles,
|
||||
pictureAttributes,
|
||||
});
|
||||
|
||||
return { link, style, picture };
|
||||
}
|
||||
// @ts-check
|
||||
import getImage from "./utils/getImage.js";
|
||||
import getImgElement from "./utils/getImgElement.js";
|
||||
import getLinkElement from "./utils/getLinkElement.js";
|
||||
import getStyleElement from "./utils/getStyleElement.js";
|
||||
import getLayoutStyles from "./utils/getLayoutStyles.js";
|
||||
import getFilteredProps from "./utils/getFilteredProps.js";
|
||||
import getPictureElement from "./utils/getPictureElement.js";
|
||||
import getBackgroundStyles from "./utils/getBackgroundStyles.js";
|
||||
|
||||
export default async function renderPicture(props) {
|
||||
const type = "Picture";
|
||||
|
||||
const { filteredProps, transformConfigs } = getFilteredProps(type, props);
|
||||
|
||||
const {
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
preload,
|
||||
loading,
|
||||
decoding,
|
||||
attributes,
|
||||
layout,
|
||||
placeholder,
|
||||
breakpoints,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
format,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
fadeInTransition,
|
||||
artDirectives,
|
||||
} = filteredProps;
|
||||
|
||||
const {
|
||||
img: imgAttributes = {},
|
||||
link: linkAttributes = {},
|
||||
style: styleAttributes = {},
|
||||
picture: pictureAttributes = {},
|
||||
} = attributes;
|
||||
|
||||
const { uuid, images } = await getImage({
|
||||
src,
|
||||
type,
|
||||
sizes,
|
||||
format,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
artDirectives,
|
||||
transformConfigs,
|
||||
});
|
||||
|
||||
const className = `astro-imagetools-picture-${uuid}`;
|
||||
|
||||
const { imagesizes } = images[images.length - 1];
|
||||
|
||||
const backgroundStyles = getBackgroundStyles(
|
||||
images,
|
||||
className,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
fadeInTransition
|
||||
);
|
||||
|
||||
const style = getStyleElement({ styleAttributes, backgroundStyles });
|
||||
|
||||
const link = getLinkElement({ images, preload, imagesizes, linkAttributes });
|
||||
|
||||
const layoutStyles = getLayoutStyles({ layout });
|
||||
|
||||
const sources = images.flatMap(({ media, sources, sizes, imagesizes }) =>
|
||||
sources.map(({ format, src, srcset }) =>
|
||||
src
|
||||
? getImgElement({
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
style,
|
||||
srcset,
|
||||
loading,
|
||||
decoding,
|
||||
imagesizes,
|
||||
fadeInTransition,
|
||||
layoutStyles,
|
||||
imgAttributes,
|
||||
})
|
||||
: `<source
|
||||
srcset="${srcset}"
|
||||
sizes="${imagesizes}"
|
||||
width="${sizes.width}"
|
||||
height="${sizes.height}"
|
||||
type="${`image/${format}`}"
|
||||
${media ? `media="${media}"` : ""}
|
||||
/>`
|
||||
)
|
||||
);
|
||||
|
||||
const picture = getPictureElement({
|
||||
sources,
|
||||
className,
|
||||
layoutStyles,
|
||||
pictureAttributes,
|
||||
});
|
||||
|
||||
return { link, style, picture };
|
||||
}
|
||||
|
||||
@ -1,38 +1,38 @@
|
||||
// @ts-check
|
||||
import fs from "node:fs";
|
||||
import { extname } from "node:path";
|
||||
import * as codecs from "@astropub/codecs";
|
||||
|
||||
export async function getImageDetails(path, width, height, aspect) {
|
||||
const extension = extname(path).slice(1);
|
||||
|
||||
const imageFormat = extension === "jpeg" ? "jpg" : extension;
|
||||
|
||||
const buffer = fs.readFileSync(path);
|
||||
const decodedImage = await codecs.jpg.decode(buffer);
|
||||
|
||||
if (aspect && !width && !height) {
|
||||
if (!width && !height) {
|
||||
({ width } = decodedImage);
|
||||
}
|
||||
|
||||
if (width) {
|
||||
height = width / aspect;
|
||||
}
|
||||
|
||||
if (height) {
|
||||
width = height * aspect;
|
||||
}
|
||||
}
|
||||
|
||||
const image = await decodedImage.resize({ width, height });
|
||||
|
||||
const { width: imageWidth, height: imageHeight } = image;
|
||||
|
||||
return {
|
||||
image,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
imageFormat,
|
||||
};
|
||||
}
|
||||
// @ts-check
|
||||
import fs from "node:fs";
|
||||
import { extname } from "node:path";
|
||||
import * as codecs from "@astropub/codecs";
|
||||
|
||||
export async function getImageDetails(path, width, height, aspect) {
|
||||
const extension = extname(path).slice(1);
|
||||
|
||||
const imageFormat = extension === "jpeg" ? "jpg" : extension;
|
||||
|
||||
const buffer = fs.readFileSync(path);
|
||||
const decodedImage = await codecs.jpg.decode(buffer);
|
||||
|
||||
if (aspect && !width && !height) {
|
||||
if (!width && !height) {
|
||||
({ width } = decodedImage);
|
||||
}
|
||||
|
||||
if (width) {
|
||||
height = width / aspect;
|
||||
}
|
||||
|
||||
if (height) {
|
||||
width = height * aspect;
|
||||
}
|
||||
}
|
||||
|
||||
const image = await decodedImage.resize({ width, height });
|
||||
|
||||
const { width: imageWidth, height: imageHeight } = image;
|
||||
|
||||
return {
|
||||
image,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
imageFormat,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,137 +1,137 @@
|
||||
// @ts-check
|
||||
import getSrcset from "./getSrcset.js";
|
||||
import getConfigOptions from "./getConfigOptions.js";
|
||||
import getFallbackImage from "./getFallbackImage.js";
|
||||
import getProcessedImage from "./getProcessedImage.js";
|
||||
|
||||
export default async function getArtDirectedImages(
|
||||
artDirectives = [],
|
||||
placeholder,
|
||||
format,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
rest
|
||||
) {
|
||||
const images = await Promise.all(
|
||||
artDirectives.map(
|
||||
async ({
|
||||
src,
|
||||
media,
|
||||
sizes: directiveImagesizes,
|
||||
placeholder: directivePlaceholder,
|
||||
breakpoints: directiveBreakpoints,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
backgroundSize,
|
||||
backgroundPosition,
|
||||
format: directiveFormat,
|
||||
fallbackFormat: directiveFallbackFormat,
|
||||
includeSourceFormat: directiveIncludeSourceFormat,
|
||||
formatOptions: directiveFormatOptions = {},
|
||||
...configOptions
|
||||
}) => {
|
||||
const {
|
||||
path,
|
||||
base,
|
||||
rest: rest2,
|
||||
image,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
imageFormat,
|
||||
} = await getProcessedImage(src, configOptions);
|
||||
|
||||
rest2.aspect = `${imageWidth / imageHeight}`;
|
||||
|
||||
const calculatedConfigs = getConfigOptions(
|
||||
imageWidth,
|
||||
directiveImagesizes || imagesizes,
|
||||
directiveBreakpoints || breakpoints,
|
||||
directiveFormat || format,
|
||||
imageFormat,
|
||||
directiveFallbackFormat || fallbackFormat,
|
||||
directiveIncludeSourceFormat || includeSourceFormat
|
||||
);
|
||||
|
||||
const { formats, requiredBreakpoints } = calculatedConfigs;
|
||||
|
||||
imagesizes = calculatedConfigs.imagesizes;
|
||||
|
||||
const maxWidth = requiredBreakpoints[requiredBreakpoints.length - 1];
|
||||
|
||||
const sources = await Promise.all(
|
||||
formats.map(async (format) => {
|
||||
const srcset = await getSrcset(
|
||||
path,
|
||||
base,
|
||||
requiredBreakpoints,
|
||||
format,
|
||||
{
|
||||
...rest,
|
||||
...rest2,
|
||||
...formatOptions[format],
|
||||
...directiveFormatOptions[format],
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
format,
|
||||
srcset,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const sizes = {
|
||||
width: maxWidth,
|
||||
height: Math.round(maxWidth / rest2.aspect),
|
||||
};
|
||||
|
||||
const object = {
|
||||
fit: objectFit,
|
||||
position: objectPosition,
|
||||
};
|
||||
|
||||
const background = {
|
||||
size: backgroundSize,
|
||||
position: backgroundPosition,
|
||||
};
|
||||
|
||||
const fallback = await getFallbackImage(
|
||||
path,
|
||||
directivePlaceholder || placeholder,
|
||||
image,
|
||||
imageFormat,
|
||||
{ ...formatOptions, ...directiveFormatOptions },
|
||||
{ ...rest, ...rest2 }
|
||||
);
|
||||
|
||||
const returnValue = {
|
||||
media,
|
||||
sources,
|
||||
sizes,
|
||||
fallback,
|
||||
imagesizes,
|
||||
};
|
||||
|
||||
const isBackgroundImage = !!backgroundSize || !!backgroundPosition;
|
||||
|
||||
isBackgroundImage
|
||||
? (returnValue.background = background)
|
||||
: (returnValue.object = object);
|
||||
|
||||
return {
|
||||
media,
|
||||
sources,
|
||||
sizes,
|
||||
object,
|
||||
fallback,
|
||||
imagesizes,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return images;
|
||||
}
|
||||
// @ts-check
|
||||
import getSrcset from "./getSrcset.js";
|
||||
import getConfigOptions from "./getConfigOptions.js";
|
||||
import getFallbackImage from "./getFallbackImage.js";
|
||||
import getProcessedImage from "./getProcessedImage.js";
|
||||
|
||||
export default async function getArtDirectedImages(
|
||||
artDirectives = [],
|
||||
placeholder,
|
||||
format,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
rest
|
||||
) {
|
||||
const images = await Promise.all(
|
||||
artDirectives.map(
|
||||
async ({
|
||||
src,
|
||||
media,
|
||||
sizes: directiveImagesizes,
|
||||
placeholder: directivePlaceholder,
|
||||
breakpoints: directiveBreakpoints,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
backgroundSize,
|
||||
backgroundPosition,
|
||||
format: directiveFormat,
|
||||
fallbackFormat: directiveFallbackFormat,
|
||||
includeSourceFormat: directiveIncludeSourceFormat,
|
||||
formatOptions: directiveFormatOptions = {},
|
||||
...configOptions
|
||||
}) => {
|
||||
const {
|
||||
path,
|
||||
base,
|
||||
rest: rest2,
|
||||
image,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
imageFormat,
|
||||
} = await getProcessedImage(src, configOptions);
|
||||
|
||||
rest2.aspect = `${imageWidth / imageHeight}`;
|
||||
|
||||
const calculatedConfigs = getConfigOptions(
|
||||
imageWidth,
|
||||
directiveImagesizes || imagesizes,
|
||||
directiveBreakpoints || breakpoints,
|
||||
directiveFormat || format,
|
||||
imageFormat,
|
||||
directiveFallbackFormat || fallbackFormat,
|
||||
directiveIncludeSourceFormat || includeSourceFormat
|
||||
);
|
||||
|
||||
const { formats, requiredBreakpoints } = calculatedConfigs;
|
||||
|
||||
imagesizes = calculatedConfigs.imagesizes;
|
||||
|
||||
const maxWidth = requiredBreakpoints[requiredBreakpoints.length - 1];
|
||||
|
||||
const sources = await Promise.all(
|
||||
formats.map(async (format) => {
|
||||
const srcset = await getSrcset(
|
||||
path,
|
||||
base,
|
||||
requiredBreakpoints,
|
||||
format,
|
||||
{
|
||||
...rest,
|
||||
...rest2,
|
||||
...formatOptions[format],
|
||||
...directiveFormatOptions[format],
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
format,
|
||||
srcset,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
const sizes = {
|
||||
width: maxWidth,
|
||||
height: Math.round(maxWidth / rest2.aspect),
|
||||
};
|
||||
|
||||
const object = {
|
||||
fit: objectFit,
|
||||
position: objectPosition,
|
||||
};
|
||||
|
||||
const background = {
|
||||
size: backgroundSize,
|
||||
position: backgroundPosition,
|
||||
};
|
||||
|
||||
const fallback = await getFallbackImage(
|
||||
path,
|
||||
directivePlaceholder || placeholder,
|
||||
image,
|
||||
imageFormat,
|
||||
{ ...formatOptions, ...directiveFormatOptions },
|
||||
{ ...rest, ...rest2 }
|
||||
);
|
||||
|
||||
const returnValue = {
|
||||
media,
|
||||
sources,
|
||||
sizes,
|
||||
fallback,
|
||||
imagesizes,
|
||||
};
|
||||
|
||||
const isBackgroundImage = !!backgroundSize || !!backgroundPosition;
|
||||
|
||||
isBackgroundImage
|
||||
? (returnValue.background = background)
|
||||
: (returnValue.object = object);
|
||||
|
||||
return {
|
||||
media,
|
||||
sources,
|
||||
sizes,
|
||||
object,
|
||||
fallback,
|
||||
imagesizes,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
return images;
|
||||
}
|
||||
|
||||
@ -1,27 +1,27 @@
|
||||
// @ts-check
|
||||
|
||||
import printWarning from "../../utils/printWarning.js";
|
||||
|
||||
export default function getAttributesString({
|
||||
attributes,
|
||||
element = "",
|
||||
excludeArray = [],
|
||||
}) {
|
||||
const attributesString = Object.keys(attributes)
|
||||
.filter((key) => {
|
||||
if (excludeArray.includes(key)) {
|
||||
printWarning({
|
||||
key,
|
||||
element,
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((key) => `${key}="${attributes[key]}"`)
|
||||
.join(" ");
|
||||
|
||||
return attributesString;
|
||||
}
|
||||
// @ts-check
|
||||
|
||||
import printWarning from "../../utils/printWarning.js";
|
||||
|
||||
export default function getAttributesString({
|
||||
attributes,
|
||||
element = "",
|
||||
excludeArray = [],
|
||||
}) {
|
||||
const attributesString = Object.keys(attributes)
|
||||
.filter((key) => {
|
||||
if (excludeArray.includes(key)) {
|
||||
printWarning({
|
||||
key,
|
||||
element,
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
.map((key) => `${key}="${attributes[key]}"`)
|
||||
.join(" ");
|
||||
|
||||
return attributesString;
|
||||
}
|
||||
|
||||
@ -1,97 +1,97 @@
|
||||
// @ts-check
|
||||
|
||||
export default function getBackgroundStyles(
|
||||
images,
|
||||
className,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
fadeInTransition,
|
||||
{ isImg = false, isBackgroundPicture = false, containerClassName = "" } = {}
|
||||
) {
|
||||
const sourcesWithFallback = images.filter(({ fallback }) => fallback);
|
||||
|
||||
if (sourcesWithFallback.length === 0) return "";
|
||||
|
||||
const staticStyles = !fadeInTransition
|
||||
? ""
|
||||
: `
|
||||
${
|
||||
isBackgroundPicture
|
||||
? `
|
||||
.${containerClassName} * {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
.${className} {
|
||||
--opacity: 1;
|
||||
--z-index: 0;
|
||||
}
|
||||
|
||||
${
|
||||
!isBackgroundPicture
|
||||
? `
|
||||
.${className} img {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
.${className}::after {
|
||||
inset: 0;
|
||||
content: "";
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
transition: opacity ${
|
||||
typeof fadeInTransition !== "object"
|
||||
? "1s"
|
||||
: (() => {
|
||||
const {
|
||||
delay = "0s",
|
||||
duration = "1s",
|
||||
timingFunction = "ease",
|
||||
} = fadeInTransition;
|
||||
|
||||
return `${duration} ${timingFunction} ${delay}`;
|
||||
})()
|
||||
};
|
||||
opacity: var(--opacity);
|
||||
z-index: var(--z-index);
|
||||
}
|
||||
`;
|
||||
|
||||
const dynamicStyles = images
|
||||
.map(({ media, fallback, object }) => {
|
||||
const elementSelector = className + (!isImg ? " img" : ""),
|
||||
backgroundElementSelector =
|
||||
className + (fadeInTransition ? "::after" : "");
|
||||
|
||||
const style = `
|
||||
.${elementSelector} {
|
||||
object-fit: ${object?.fit || objectFit};
|
||||
object-position: ${object?.position || objectPosition};
|
||||
}
|
||||
|
||||
.${backgroundElementSelector} {
|
||||
background-size: ${object?.fit || objectFit};
|
||||
background-image: url("${encodeURI(fallback)}");
|
||||
background-position: ${object?.position || objectPosition};
|
||||
}
|
||||
`;
|
||||
|
||||
return media ? `@media ${media} { ${style} }` : style;
|
||||
})
|
||||
.reverse();
|
||||
|
||||
const backgroundStyles = [staticStyles, ...dynamicStyles].join("");
|
||||
|
||||
return backgroundStyles;
|
||||
}
|
||||
// @ts-check
|
||||
|
||||
export default function getBackgroundStyles(
|
||||
images,
|
||||
className,
|
||||
objectFit,
|
||||
objectPosition,
|
||||
fadeInTransition,
|
||||
{ isImg = false, isBackgroundPicture = false, containerClassName = "" } = {}
|
||||
) {
|
||||
const sourcesWithFallback = images.filter(({ fallback }) => fallback);
|
||||
|
||||
if (sourcesWithFallback.length === 0) return "";
|
||||
|
||||
const staticStyles = !fadeInTransition
|
||||
? ""
|
||||
: `
|
||||
${
|
||||
isBackgroundPicture
|
||||
? `
|
||||
.${containerClassName} * {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
.${className} {
|
||||
--opacity: 1;
|
||||
--z-index: 0;
|
||||
}
|
||||
|
||||
${
|
||||
!isBackgroundPicture
|
||||
? `
|
||||
.${className} img {
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
}
|
||||
`
|
||||
: ""
|
||||
}
|
||||
|
||||
.${className}::after {
|
||||
inset: 0;
|
||||
content: "";
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
transition: opacity ${
|
||||
typeof fadeInTransition !== "object"
|
||||
? "1s"
|
||||
: (() => {
|
||||
const {
|
||||
delay = "0s",
|
||||
duration = "1s",
|
||||
timingFunction = "ease",
|
||||
} = fadeInTransition;
|
||||
|
||||
return `${duration} ${timingFunction} ${delay}`;
|
||||
})()
|
||||
};
|
||||
opacity: var(--opacity);
|
||||
z-index: var(--z-index);
|
||||
}
|
||||
`;
|
||||
|
||||
const dynamicStyles = images
|
||||
.map(({ media, fallback, object }) => {
|
||||
const elementSelector = className + (!isImg ? " img" : ""),
|
||||
backgroundElementSelector =
|
||||
className + (fadeInTransition ? "::after" : "");
|
||||
|
||||
const style = `
|
||||
.${elementSelector} {
|
||||
object-fit: ${object?.fit || objectFit};
|
||||
object-position: ${object?.position || objectPosition};
|
||||
}
|
||||
|
||||
.${backgroundElementSelector} {
|
||||
background-size: ${object?.fit || objectFit};
|
||||
background-image: url("${encodeURI(fallback)}");
|
||||
background-position: ${object?.position || objectPosition};
|
||||
}
|
||||
`;
|
||||
|
||||
return media ? `@media ${media} { ${style} }` : style;
|
||||
})
|
||||
.reverse();
|
||||
|
||||
const backgroundStyles = [staticStyles, ...dynamicStyles].join("");
|
||||
|
||||
return backgroundStyles;
|
||||
}
|
||||
|
||||
@ -1,77 +1,77 @@
|
||||
// @ts-check
|
||||
import printWarning from "../../utils/printWarning.js";
|
||||
|
||||
export default function getBreakpoints(breakpoints, imageWidth) {
|
||||
if (Array.isArray(breakpoints)) {
|
||||
return breakpoints.sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
const { count, minWidth = 320 } = breakpoints || {};
|
||||
|
||||
const maxWidth = (() => {
|
||||
if (breakpoints?.maxWidth) return breakpoints.maxWidth;
|
||||
|
||||
if (imageWidth > 3840) {
|
||||
printWarning({
|
||||
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.",
|
||||
});
|
||||
|
||||
return 3840;
|
||||
}
|
||||
|
||||
return imageWidth;
|
||||
})();
|
||||
|
||||
const breakPoints = [];
|
||||
|
||||
const diff = maxWidth - minWidth;
|
||||
|
||||
const n =
|
||||
count ||
|
||||
(maxWidth <= 400
|
||||
? 1
|
||||
: maxWidth <= 640
|
||||
? 2
|
||||
: maxWidth <= 800
|
||||
? 3
|
||||
: maxWidth <= 1024
|
||||
? 4
|
||||
: maxWidth <= 1280
|
||||
? 5
|
||||
: maxWidth <= 1440
|
||||
? 6
|
||||
: maxWidth <= 1920
|
||||
? 7
|
||||
: maxWidth <= 2560
|
||||
? 8
|
||||
: maxWidth <= 2880
|
||||
? 9
|
||||
: maxWidth <= 3840
|
||||
? 10
|
||||
: 11);
|
||||
|
||||
let currentWidth = minWidth;
|
||||
|
||||
n > 1 && breakPoints.push(currentWidth);
|
||||
|
||||
let steps = 0;
|
||||
|
||||
for (let i = 1; i < n; i++) {
|
||||
steps += i;
|
||||
}
|
||||
|
||||
const pixelsPerStep = diff / steps;
|
||||
|
||||
for (let i = 1; i < n - 1; i++) {
|
||||
const next = pixelsPerStep * (n - i) + currentWidth;
|
||||
|
||||
breakPoints.push(Math.round(next));
|
||||
|
||||
currentWidth = next;
|
||||
}
|
||||
|
||||
breakPoints.push(maxWidth);
|
||||
|
||||
return [...new Set(breakPoints)];
|
||||
}
|
||||
// @ts-check
|
||||
import printWarning from "../../utils/printWarning.js";
|
||||
|
||||
export default function getBreakpoints(breakpoints, imageWidth) {
|
||||
if (Array.isArray(breakpoints)) {
|
||||
return breakpoints.sort((a, b) => a - b);
|
||||
}
|
||||
|
||||
const { count, minWidth = 320 } = breakpoints || {};
|
||||
|
||||
const maxWidth = (() => {
|
||||
if (breakpoints?.maxWidth) return breakpoints.maxWidth;
|
||||
|
||||
if (imageWidth > 3840) {
|
||||
printWarning({
|
||||
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.",
|
||||
});
|
||||
|
||||
return 3840;
|
||||
}
|
||||
|
||||
return imageWidth;
|
||||
})();
|
||||
|
||||
const breakPoints = [];
|
||||
|
||||
const diff = maxWidth - minWidth;
|
||||
|
||||
const n =
|
||||
count ||
|
||||
(maxWidth <= 400
|
||||
? 1
|
||||
: maxWidth <= 640
|
||||
? 2
|
||||
: maxWidth <= 800
|
||||
? 3
|
||||
: maxWidth <= 1024
|
||||
? 4
|
||||
: maxWidth <= 1280
|
||||
? 5
|
||||
: maxWidth <= 1440
|
||||
? 6
|
||||
: maxWidth <= 1920
|
||||
? 7
|
||||
: maxWidth <= 2560
|
||||
? 8
|
||||
: maxWidth <= 2880
|
||||
? 9
|
||||
: maxWidth <= 3840
|
||||
? 10
|
||||
: 11);
|
||||
|
||||
let currentWidth = minWidth;
|
||||
|
||||
n > 1 && breakPoints.push(currentWidth);
|
||||
|
||||
let steps = 0;
|
||||
|
||||
for (let i = 1; i < n; i++) {
|
||||
steps += i;
|
||||
}
|
||||
|
||||
const pixelsPerStep = diff / steps;
|
||||
|
||||
for (let i = 1; i < n - 1; i++) {
|
||||
const next = pixelsPerStep * (n - i) + currentWidth;
|
||||
|
||||
breakPoints.push(Math.round(next));
|
||||
|
||||
currentWidth = next;
|
||||
}
|
||||
|
||||
breakPoints.push(maxWidth);
|
||||
|
||||
return [...new Set(breakPoints)];
|
||||
}
|
||||
|
||||
@ -1,34 +1,34 @@
|
||||
// @ts-check
|
||||
import getBreakpoints from "./getBreakpoints.js";
|
||||
|
||||
export default function getConfigOptions(
|
||||
imageWidth,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
format,
|
||||
imageFormat,
|
||||
fallbackFormat,
|
||||
includeSourceFormat
|
||||
) {
|
||||
const formats = [
|
||||
...new Set(
|
||||
[format, includeSourceFormat && imageFormat]
|
||||
.flat()
|
||||
.filter((f) => f && f !== fallbackFormat)
|
||||
),
|
||||
fallbackFormat,
|
||||
];
|
||||
|
||||
const requiredBreakpoints = getBreakpoints(breakpoints, imageWidth);
|
||||
|
||||
imagesizes =
|
||||
typeof imagesizes === "string"
|
||||
? imagesizes
|
||||
: imagesizes(requiredBreakpoints);
|
||||
|
||||
return {
|
||||
formats,
|
||||
imagesizes,
|
||||
requiredBreakpoints,
|
||||
};
|
||||
}
|
||||
// @ts-check
|
||||
import getBreakpoints from "./getBreakpoints.js";
|
||||
|
||||
export default function getConfigOptions(
|
||||
imageWidth,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
format,
|
||||
imageFormat,
|
||||
fallbackFormat,
|
||||
includeSourceFormat
|
||||
) {
|
||||
const formats = [
|
||||
...new Set(
|
||||
[format, includeSourceFormat && imageFormat]
|
||||
.flat()
|
||||
.filter((f) => f && f !== fallbackFormat)
|
||||
),
|
||||
fallbackFormat,
|
||||
];
|
||||
|
||||
const requiredBreakpoints = getBreakpoints(breakpoints, imageWidth);
|
||||
|
||||
imagesizes =
|
||||
typeof imagesizes === "string"
|
||||
? imagesizes
|
||||
: imagesizes(requiredBreakpoints);
|
||||
|
||||
return {
|
||||
formats,
|
||||
imagesizes,
|
||||
requiredBreakpoints,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,48 +1,48 @@
|
||||
// @ts-check
|
||||
import getAttributesString from "./getAttributesString.js";
|
||||
|
||||
export default function getContainerElement({
|
||||
tag,
|
||||
content,
|
||||
className = "",
|
||||
containerAttributes,
|
||||
isBackgroundPicture = false,
|
||||
containerClassName = "",
|
||||
}) {
|
||||
const {
|
||||
class: customClasses = "",
|
||||
style: customInlineStyles = "",
|
||||
...restContainerAttributes
|
||||
} = containerAttributes;
|
||||
|
||||
const attributesString = getAttributesString({
|
||||
attributes: restContainerAttributes,
|
||||
});
|
||||
|
||||
const classAttribute = [
|
||||
isBackgroundPicture
|
||||
? "astro-imagetools-background-picture"
|
||||
: "astro-imagetools-background-image",
|
||||
isBackgroundPicture ? containerClassName : className,
|
||||
customClasses,
|
||||
]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const styleAttribute = [
|
||||
isBackgroundPicture ? "position: relative;" : "",
|
||||
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
||||
]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const containerElement = `<${tag}
|
||||
${attributesString}
|
||||
class="${classAttribute}"
|
||||
style="${styleAttribute}"
|
||||
>
|
||||
${content}
|
||||
</${tag}>`;
|
||||
|
||||
return containerElement;
|
||||
}
|
||||
// @ts-check
|
||||
import getAttributesString from "./getAttributesString.js";
|
||||
|
||||
export default function getContainerElement({
|
||||
tag,
|
||||
content,
|
||||
className = "",
|
||||
containerAttributes,
|
||||
isBackgroundPicture = false,
|
||||
containerClassName = "",
|
||||
}) {
|
||||
const {
|
||||
class: customClasses = "",
|
||||
style: customInlineStyles = "",
|
||||
...restContainerAttributes
|
||||
} = containerAttributes;
|
||||
|
||||
const attributesString = getAttributesString({
|
||||
attributes: restContainerAttributes,
|
||||
});
|
||||
|
||||
const classAttribute = [
|
||||
isBackgroundPicture
|
||||
? "astro-imagetools-background-picture"
|
||||
: "astro-imagetools-background-image",
|
||||
isBackgroundPicture ? containerClassName : className,
|
||||
customClasses,
|
||||
]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const styleAttribute = [
|
||||
isBackgroundPicture ? "position: relative;" : "",
|
||||
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
||||
]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const containerElement = `<${tag}
|
||||
${attributesString}
|
||||
class="${classAttribute}"
|
||||
style="${styleAttribute}"
|
||||
>
|
||||
${content}
|
||||
</${tag}>`;
|
||||
|
||||
return containerElement;
|
||||
}
|
||||
|
||||
@ -1,58 +1,58 @@
|
||||
// @ts-check
|
||||
|
||||
import util from "node:util";
|
||||
import potrace from "potrace";
|
||||
import getSrcset from "./getSrcset.js";
|
||||
import { sharp } from "../../utils/runtimeChecks.js";
|
||||
|
||||
export default async function getFallbackImage(
|
||||
src,
|
||||
placeholder,
|
||||
image,
|
||||
format,
|
||||
formatOptions,
|
||||
rest
|
||||
) {
|
||||
const base = null;
|
||||
|
||||
switch (placeholder) {
|
||||
case "blurred": {
|
||||
const dataUri = await getSrcset(src, base, [20], format, {
|
||||
inline: true,
|
||||
...rest,
|
||||
...formatOptions[format],
|
||||
});
|
||||
|
||||
return dataUri;
|
||||
}
|
||||
case "tracedSVG": {
|
||||
const { function: fn, options } = formatOptions.tracedSVG;
|
||||
|
||||
const traceSVG = util.promisify(potrace[fn]);
|
||||
|
||||
const imageBuffer = sharp
|
||||
? await image.toBuffer()
|
||||
: Buffer.from(
|
||||
(await image.encode(`image/${format === "jpg" ? "jpeg" : format}`))
|
||||
.data
|
||||
);
|
||||
|
||||
const tracedSVG = await traceSVG(imageBuffer, options);
|
||||
|
||||
return `data:image/svg+xml;utf8,${tracedSVG}`;
|
||||
}
|
||||
case "dominantColor": {
|
||||
if (sharp) {
|
||||
var { r, g, b } = (await image.stats()).dominant;
|
||||
} else {
|
||||
[r, g, b] = image.color;
|
||||
}
|
||||
|
||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" style="background: rgb(${r},${g},${b})"></svg>`;
|
||||
|
||||
return `data:image/svg+xml;utf8,${svg}`;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// @ts-check
|
||||
|
||||
import util from "node:util";
|
||||
import potrace from "potrace";
|
||||
import getSrcset from "./getSrcset.js";
|
||||
import { sharp } from "../../utils/runtimeChecks.js";
|
||||
|
||||
export default async function getFallbackImage(
|
||||
src,
|
||||
placeholder,
|
||||
image,
|
||||
format,
|
||||
formatOptions,
|
||||
rest
|
||||
) {
|
||||
const base = null;
|
||||
|
||||
switch (placeholder) {
|
||||
case "blurred": {
|
||||
const dataUri = await getSrcset(src, base, [20], format, {
|
||||
inline: true,
|
||||
...rest,
|
||||
...formatOptions[format],
|
||||
});
|
||||
|
||||
return dataUri;
|
||||
}
|
||||
case "tracedSVG": {
|
||||
const { function: fn, options } = formatOptions.tracedSVG;
|
||||
|
||||
const traceSVG = util.promisify(potrace[fn]);
|
||||
|
||||
const imageBuffer = sharp
|
||||
? await image.toBuffer()
|
||||
: Buffer.from(
|
||||
(await image.encode(`image/${format === "jpg" ? "jpeg" : format}`))
|
||||
.data
|
||||
);
|
||||
|
||||
const tracedSVG = await traceSVG(imageBuffer, options);
|
||||
|
||||
return `data:image/svg+xml;utf8,${tracedSVG}`;
|
||||
}
|
||||
case "dominantColor": {
|
||||
if (sharp) {
|
||||
var { r, g, b } = (await image.stats()).dominant;
|
||||
} else {
|
||||
[r, g, b] = image.color;
|
||||
}
|
||||
|
||||
const svg = `<svg xmlns="http://www.w3.org/2000/svg" style="background: rgb(${r},${g},${b})"></svg>`;
|
||||
|
||||
return `data:image/svg+xml;utf8,${svg}`;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,138 +1,138 @@
|
||||
// @ts-check
|
||||
import filterConfigs from "../../utils/filterConfigs.js";
|
||||
import {
|
||||
supportedConfigs,
|
||||
GlobalConfigOptions,
|
||||
} from "../../utils/runtimeChecks.js";
|
||||
|
||||
const GlobalOnlyProperties = ["cacheDir", "assetFileNames"];
|
||||
|
||||
const NonGlobalSupportedConfigs = supportedConfigs.filter(
|
||||
(key) => !GlobalOnlyProperties.includes(key)
|
||||
);
|
||||
|
||||
const NonProperties = {
|
||||
Img: [
|
||||
"tag",
|
||||
"content",
|
||||
"backgroundSize",
|
||||
"backgroundPosition",
|
||||
"fallbackFormat",
|
||||
"includeSourceFormat",
|
||||
"fadeInTransition",
|
||||
"artDirectives",
|
||||
],
|
||||
Picture: ["tag", "content", "backgroundSize", "backgroundPosition"],
|
||||
BackgroundImage: [
|
||||
"alt",
|
||||
"loading",
|
||||
"decoding",
|
||||
"layout",
|
||||
"objectFit",
|
||||
"objectPosition",
|
||||
"fadeInTransition",
|
||||
],
|
||||
BackgroundPicture: ["alt", "backgroundSize", "backgroundPosition"],
|
||||
};
|
||||
|
||||
const ImgProperties = NonGlobalSupportedConfigs.filter(
|
||||
(key) => !NonProperties.Img.includes(key)
|
||||
),
|
||||
PictureProperties = NonGlobalSupportedConfigs.filter(
|
||||
(key) => !NonProperties.Picture.includes(key)
|
||||
),
|
||||
BackgroundImageProperties = NonGlobalSupportedConfigs.filter(
|
||||
(key) => !NonProperties.BackgroundImage.includes(key)
|
||||
),
|
||||
BackgroundPictureProperties = NonGlobalSupportedConfigs.filter(
|
||||
(key) => !NonProperties.BackgroundPicture.includes(key)
|
||||
);
|
||||
|
||||
const SupportedProperties = {
|
||||
Img: ImgProperties,
|
||||
Picture: PictureProperties,
|
||||
BackgroundImage: BackgroundImageProperties,
|
||||
BackgroundPicture: BackgroundPictureProperties,
|
||||
};
|
||||
|
||||
export default function getFilteredProps(type, props) {
|
||||
const filteredGlobalConfigs = filterConfigs(
|
||||
"Global",
|
||||
GlobalConfigOptions,
|
||||
SupportedProperties[type],
|
||||
{ warn: false }
|
||||
);
|
||||
|
||||
const { search, searchParams } = new URL(props.src, "file://");
|
||||
|
||||
props.src = props.src.replace(search, "");
|
||||
|
||||
const paramOptions = Object.fromEntries(searchParams);
|
||||
|
||||
const filteredLocalProps = filterConfigs(
|
||||
type,
|
||||
{
|
||||
...paramOptions,
|
||||
...props,
|
||||
},
|
||||
SupportedProperties[type]
|
||||
);
|
||||
|
||||
const resolvedProps = {
|
||||
...filteredGlobalConfigs,
|
||||
...filteredLocalProps,
|
||||
};
|
||||
|
||||
const {
|
||||
src,
|
||||
alt,
|
||||
tag = "section",
|
||||
content = "",
|
||||
sizes = function (breakpoints) {
|
||||
const maxWidth = breakpoints[breakpoints.length - 1];
|
||||
return `(min-width: ${maxWidth}px) ${maxWidth}px, 100vw`;
|
||||
},
|
||||
preload,
|
||||
loading = preload ? "eager" : "lazy",
|
||||
decoding = "async",
|
||||
attributes = {},
|
||||
layout = "constrained",
|
||||
placeholder = "blurred",
|
||||
breakpoints,
|
||||
objectFit = "cover",
|
||||
objectPosition = "50% 50%",
|
||||
backgroundSize = "cover",
|
||||
backgroundPosition = "50% 50%",
|
||||
format = type === "Img" ? undefined : ["avif", "webp"],
|
||||
fallbackFormat,
|
||||
includeSourceFormat = true,
|
||||
formatOptions = {
|
||||
tracedSVG: {
|
||||
function: "trace",
|
||||
},
|
||||
},
|
||||
fadeInTransition = true,
|
||||
artDirectives,
|
||||
...transformConfigs
|
||||
} = resolvedProps;
|
||||
|
||||
// prettier-ignore
|
||||
const allProps = {
|
||||
src, alt, tag, content, sizes, preload, loading, decoding, attributes, layout, placeholder,
|
||||
breakpoints, objectFit, objectPosition, backgroundSize, backgroundPosition, format,
|
||||
fallbackFormat, includeSourceFormat, formatOptions, fadeInTransition, artDirectives,
|
||||
...transformConfigs,
|
||||
};
|
||||
|
||||
const filteredProps = filterConfigs(
|
||||
type,
|
||||
allProps,
|
||||
SupportedProperties[type],
|
||||
{ warn: false }
|
||||
);
|
||||
|
||||
return {
|
||||
filteredProps,
|
||||
transformConfigs,
|
||||
};
|
||||
}
|
||||
// @ts-check
|
||||
import filterConfigs from "../../utils/filterConfigs.js";
|
||||
import {
|
||||
supportedConfigs,
|
||||
GlobalConfigOptions,
|
||||
} from "../../utils/runtimeChecks.js";
|
||||
|
||||
const GlobalOnlyProperties = ["cacheDir", "assetFileNames"];
|
||||
|
||||
const NonGlobalSupportedConfigs = supportedConfigs.filter(
|
||||
(key) => !GlobalOnlyProperties.includes(key)
|
||||
);
|
||||
|
||||
const NonProperties = {
|
||||
Img: [
|
||||
"tag",
|
||||
"content",
|
||||
"backgroundSize",
|
||||
"backgroundPosition",
|
||||
"fallbackFormat",
|
||||
"includeSourceFormat",
|
||||
"fadeInTransition",
|
||||
"artDirectives",
|
||||
],
|
||||
Picture: ["tag", "content", "backgroundSize", "backgroundPosition"],
|
||||
BackgroundImage: [
|
||||
"alt",
|
||||
"loading",
|
||||
"decoding",
|
||||
"layout",
|
||||
"objectFit",
|
||||
"objectPosition",
|
||||
"fadeInTransition",
|
||||
],
|
||||
BackgroundPicture: ["alt", "backgroundSize", "backgroundPosition"],
|
||||
};
|
||||
|
||||
const ImgProperties = NonGlobalSupportedConfigs.filter(
|
||||
(key) => !NonProperties.Img.includes(key)
|
||||
),
|
||||
PictureProperties = NonGlobalSupportedConfigs.filter(
|
||||
(key) => !NonProperties.Picture.includes(key)
|
||||
),
|
||||
BackgroundImageProperties = NonGlobalSupportedConfigs.filter(
|
||||
(key) => !NonProperties.BackgroundImage.includes(key)
|
||||
),
|
||||
BackgroundPictureProperties = NonGlobalSupportedConfigs.filter(
|
||||
(key) => !NonProperties.BackgroundPicture.includes(key)
|
||||
);
|
||||
|
||||
const SupportedProperties = {
|
||||
Img: ImgProperties,
|
||||
Picture: PictureProperties,
|
||||
BackgroundImage: BackgroundImageProperties,
|
||||
BackgroundPicture: BackgroundPictureProperties,
|
||||
};
|
||||
|
||||
export default function getFilteredProps(type, props) {
|
||||
const filteredGlobalConfigs = filterConfigs(
|
||||
"Global",
|
||||
GlobalConfigOptions,
|
||||
SupportedProperties[type],
|
||||
{ warn: false }
|
||||
);
|
||||
|
||||
const { search, searchParams } = new URL(props.src, "file://");
|
||||
|
||||
props.src = props.src.replace(search, "");
|
||||
|
||||
const paramOptions = Object.fromEntries(searchParams);
|
||||
|
||||
const filteredLocalProps = filterConfigs(
|
||||
type,
|
||||
{
|
||||
...paramOptions,
|
||||
...props,
|
||||
},
|
||||
SupportedProperties[type]
|
||||
);
|
||||
|
||||
const resolvedProps = {
|
||||
...filteredGlobalConfigs,
|
||||
...filteredLocalProps,
|
||||
};
|
||||
|
||||
const {
|
||||
src,
|
||||
alt,
|
||||
tag = "section",
|
||||
content = "",
|
||||
sizes = function (breakpoints) {
|
||||
const maxWidth = breakpoints[breakpoints.length - 1];
|
||||
return `(min-width: ${maxWidth}px) ${maxWidth}px, 100vw`;
|
||||
},
|
||||
preload,
|
||||
loading = preload ? "eager" : "lazy",
|
||||
decoding = "async",
|
||||
attributes = {},
|
||||
layout = "constrained",
|
||||
placeholder = "blurred",
|
||||
breakpoints,
|
||||
objectFit = "cover",
|
||||
objectPosition = "50% 50%",
|
||||
backgroundSize = "cover",
|
||||
backgroundPosition = "50% 50%",
|
||||
format = type === "Img" ? undefined : ["avif", "webp"],
|
||||
fallbackFormat,
|
||||
includeSourceFormat = true,
|
||||
formatOptions = {
|
||||
tracedSVG: {
|
||||
function: "trace",
|
||||
},
|
||||
},
|
||||
fadeInTransition = true,
|
||||
artDirectives,
|
||||
...transformConfigs
|
||||
} = resolvedProps;
|
||||
|
||||
// prettier-ignore
|
||||
const allProps = {
|
||||
src, alt, tag, content, sizes, preload, loading, decoding, attributes, layout, placeholder,
|
||||
breakpoints, objectFit, objectPosition, backgroundSize, backgroundPosition, format,
|
||||
fallbackFormat, includeSourceFormat, formatOptions, fadeInTransition, artDirectives,
|
||||
...transformConfigs,
|
||||
};
|
||||
|
||||
const filteredProps = filterConfigs(
|
||||
type,
|
||||
allProps,
|
||||
SupportedProperties[type],
|
||||
{ warn: false }
|
||||
);
|
||||
|
||||
return {
|
||||
filteredProps,
|
||||
transformConfigs,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,49 +1,49 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import getFilteredProps from "./getFilteredProps";
|
||||
|
||||
describe("getFilteredProps", () => {
|
||||
it("should should merge in default props", () => {
|
||||
const result = getFilteredProps("Img", { src: "/img.jpeg", alt: "alt" });
|
||||
expect(result).toEqual({
|
||||
filteredProps: {
|
||||
alt: "alt",
|
||||
attributes: {},
|
||||
breakpoints: undefined,
|
||||
decoding: "async",
|
||||
format: undefined,
|
||||
formatOptions: {
|
||||
tracedSVG: {
|
||||
function: "trace",
|
||||
},
|
||||
},
|
||||
layout: "constrained",
|
||||
loading: "lazy",
|
||||
objectFit: "cover",
|
||||
objectPosition: "50% 50%",
|
||||
placeholder: "blurred",
|
||||
preload: undefined,
|
||||
sizes: expect.any(Function),
|
||||
src: "/img.jpeg",
|
||||
},
|
||||
transformConfigs: {},
|
||||
});
|
||||
});
|
||||
|
||||
it("should accept empty string for `alt` prop on Img", () => {
|
||||
const result = getFilteredProps("Img", { src: "/img.jpeg", alt: "" });
|
||||
expect(result).toMatchObject({
|
||||
filteredProps: {
|
||||
alt: "",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should accept empty string for `alt` prop on Picture", () => {
|
||||
const result = getFilteredProps("Picture", { src: "/img.jpeg", alt: "" });
|
||||
expect(result).toMatchObject({
|
||||
filteredProps: {
|
||||
alt: "",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
import { describe, expect, it } from "vitest";
|
||||
import getFilteredProps from "./getFilteredProps";
|
||||
|
||||
describe("getFilteredProps", () => {
|
||||
it("should should merge in default props", () => {
|
||||
const result = getFilteredProps("Img", { src: "/img.jpeg", alt: "alt" });
|
||||
expect(result).toEqual({
|
||||
filteredProps: {
|
||||
alt: "alt",
|
||||
attributes: {},
|
||||
breakpoints: undefined,
|
||||
decoding: "async",
|
||||
format: undefined,
|
||||
formatOptions: {
|
||||
tracedSVG: {
|
||||
function: "trace",
|
||||
},
|
||||
},
|
||||
layout: "constrained",
|
||||
loading: "lazy",
|
||||
objectFit: "cover",
|
||||
objectPosition: "50% 50%",
|
||||
placeholder: "blurred",
|
||||
preload: undefined,
|
||||
sizes: expect.any(Function),
|
||||
src: "/img.jpeg",
|
||||
},
|
||||
transformConfigs: {},
|
||||
});
|
||||
});
|
||||
|
||||
it("should accept empty string for `alt` prop on Img", () => {
|
||||
const result = getFilteredProps("Img", { src: "/img.jpeg", alt: "" });
|
||||
expect(result).toMatchObject({
|
||||
filteredProps: {
|
||||
alt: "",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should accept empty string for `alt` prop on Picture", () => {
|
||||
const result = getFilteredProps("Picture", { src: "/img.jpeg", alt: "" });
|
||||
expect(result).toMatchObject({
|
||||
filteredProps: {
|
||||
alt: "",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,107 +1,107 @@
|
||||
// @ts-check
|
||||
import crypto from "node:crypto";
|
||||
import objectHash from "object-hash";
|
||||
import getImageSources from "./getImageSources.js";
|
||||
import getProcessedImage from "./getProcessedImage.js";
|
||||
import getArtDirectedImages from "./getArtDirectedImages.js";
|
||||
import pMap from "p-map";
|
||||
|
||||
const imagesData = new Map();
|
||||
|
||||
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
export default async function ({
|
||||
src,
|
||||
type,
|
||||
sizes: imagesizes,
|
||||
format,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
artDirectives,
|
||||
transformConfigs,
|
||||
}) {
|
||||
try {
|
||||
const args = Array.from(arguments);
|
||||
const hash = objectHash(args);
|
||||
if (imagesData.has(hash)) {
|
||||
return imagesData.get(hash);
|
||||
}
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
const { path, base, rest, image, imageWidth, imageHeight, imageFormat } =
|
||||
await getProcessedImage(src, transformConfigs);
|
||||
|
||||
await delay(250);
|
||||
src = path;
|
||||
|
||||
rest.aspect = `${imageWidth / imageHeight}`;
|
||||
if (!fallbackFormat) {
|
||||
fallbackFormat = imageFormat;
|
||||
}
|
||||
|
||||
// Fetch both image sources and art-directed images
|
||||
const [mainImage, artDirectedImages] = await pMap(
|
||||
[
|
||||
async () =>
|
||||
await getImageSources(
|
||||
src,
|
||||
base,
|
||||
image,
|
||||
format,
|
||||
imageWidth,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
imageFormat,
|
||||
formatOptions,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
rest
|
||||
),
|
||||
async () => {
|
||||
await delay(250);
|
||||
return await getArtDirectedImages(
|
||||
artDirectives,
|
||||
placeholder,
|
||||
format,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
rest
|
||||
);
|
||||
},
|
||||
],
|
||||
async (task) => await task(),
|
||||
{ concurrency: 1 }
|
||||
);
|
||||
|
||||
// Ensure artDirectedImages is an array
|
||||
const images = Array.isArray(artDirectedImages) ? [...artDirectedImages, mainImage] : [mainImage];
|
||||
|
||||
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
||||
|
||||
const returnObject = {
|
||||
uuid,
|
||||
images,
|
||||
};
|
||||
|
||||
imagesData.set(hash, returnObject);
|
||||
|
||||
const end = performance.now();
|
||||
|
||||
console.log(
|
||||
`Responsive Image sets generated for ${type} at ${args[0].src} in ${end - start}ms`
|
||||
);
|
||||
|
||||
return returnObject;
|
||||
} catch (error) {
|
||||
console.error("Error processing images:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
// @ts-check
|
||||
import crypto from "node:crypto";
|
||||
import objectHash from "object-hash";
|
||||
import getImageSources from "./getImageSources.js";
|
||||
import getProcessedImage from "./getProcessedImage.js";
|
||||
import getArtDirectedImages from "./getArtDirectedImages.js";
|
||||
import pMap from "p-map";
|
||||
|
||||
const imagesData = new Map();
|
||||
|
||||
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
export default async function ({
|
||||
src,
|
||||
type,
|
||||
sizes: imagesizes,
|
||||
format,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
artDirectives,
|
||||
transformConfigs,
|
||||
}) {
|
||||
try {
|
||||
const args = Array.from(arguments);
|
||||
const hash = objectHash(args);
|
||||
if (imagesData.has(hash)) {
|
||||
return imagesData.get(hash);
|
||||
}
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
const { path, base, rest, image, imageWidth, imageHeight, imageFormat } =
|
||||
await getProcessedImage(src, transformConfigs);
|
||||
|
||||
await delay(100);
|
||||
src = path;
|
||||
|
||||
rest.aspect = `${imageWidth / imageHeight}`;
|
||||
if (!fallbackFormat) {
|
||||
fallbackFormat = imageFormat;
|
||||
}
|
||||
|
||||
// Fetch both image sources and art-directed images
|
||||
const [mainImage, artDirectedImages] = await pMap(
|
||||
[
|
||||
async () =>
|
||||
await getImageSources(
|
||||
src,
|
||||
base,
|
||||
image,
|
||||
format,
|
||||
imageWidth,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
imageFormat,
|
||||
formatOptions,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
rest
|
||||
),
|
||||
async () => {
|
||||
await delay(100);
|
||||
return await getArtDirectedImages(
|
||||
artDirectives,
|
||||
placeholder,
|
||||
format,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
formatOptions,
|
||||
rest
|
||||
);
|
||||
},
|
||||
],
|
||||
async (task) => await task(),
|
||||
{ concurrency: 1 }
|
||||
);
|
||||
|
||||
// Ensure artDirectedImages is an array
|
||||
const images = Array.isArray(artDirectedImages) ? [...artDirectedImages, mainImage] : [mainImage];
|
||||
|
||||
const uuid = crypto.randomBytes(4).toString("hex").toUpperCase();
|
||||
|
||||
const returnObject = {
|
||||
uuid,
|
||||
images,
|
||||
};
|
||||
|
||||
imagesData.set(hash, returnObject);
|
||||
|
||||
const end = performance.now();
|
||||
|
||||
console.log(
|
||||
`Responsive Image sets generated for ${type} at ${args[0].src} in ${end - start}ms`
|
||||
);
|
||||
|
||||
return returnObject;
|
||||
} catch (error) {
|
||||
console.error("Error processing images:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,91 +1,91 @@
|
||||
// @ts-check
|
||||
import getSrcset from "./getSrcset.js";
|
||||
import getConfigOptions from "./getConfigOptions.js";
|
||||
import getFallbackImage from "./getFallbackImage.js";
|
||||
import pMap from "p-map";
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export default async function getImageSources(
|
||||
src,
|
||||
base,
|
||||
image,
|
||||
format,
|
||||
imageWidth,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
imageFormat,
|
||||
formatOptions,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
rest
|
||||
) {
|
||||
try {
|
||||
const calculatedConfigs = getConfigOptions(
|
||||
imageWidth,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
format,
|
||||
imageFormat,
|
||||
fallbackFormat,
|
||||
includeSourceFormat
|
||||
);
|
||||
|
||||
const { formats, requiredBreakpoints } = calculatedConfigs;
|
||||
imagesizes = calculatedConfigs.imagesizes;
|
||||
const maxWidth = requiredBreakpoints[requiredBreakpoints.length - 1];
|
||||
const sliceLength = -(maxWidth.toString().length + 2);
|
||||
|
||||
const sources = await pMap(
|
||||
formats,
|
||||
async (format) => {
|
||||
try {
|
||||
await delay(250);
|
||||
const srcset = await getSrcset(src, base, requiredBreakpoints, format, {
|
||||
...rest,
|
||||
...formatOptions[format],
|
||||
});
|
||||
|
||||
const srcsets = srcset.split(", ");
|
||||
const srcObject =
|
||||
format === fallbackFormat
|
||||
? { src: srcsets[srcsets.length - 1].slice(0, sliceLength) }
|
||||
: {};
|
||||
|
||||
return {
|
||||
...srcObject,
|
||||
format,
|
||||
srcset,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Error processing format ${format}:`, error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
{ concurrency: 1 }
|
||||
);
|
||||
|
||||
const filteredSources = sources.filter(Boolean);
|
||||
|
||||
const sizes = {
|
||||
width: maxWidth,
|
||||
height: Math.round(maxWidth / rest.aspect),
|
||||
};
|
||||
|
||||
const fallback = await getFallbackImage(
|
||||
src,
|
||||
placeholder,
|
||||
image,
|
||||
fallbackFormat,
|
||||
formatOptions,
|
||||
rest
|
||||
)
|
||||
return { sources: filteredSources, sizes, fallback, imagesizes };
|
||||
} catch (error) {
|
||||
console.error("Error in getImageSources:", error);
|
||||
return { sources: [], sizes: {}, fallback: null, imagesizes: null };
|
||||
}
|
||||
}
|
||||
// @ts-check
|
||||
import getSrcset from "./getSrcset.js";
|
||||
import getConfigOptions from "./getConfigOptions.js";
|
||||
import getFallbackImage from "./getFallbackImage.js";
|
||||
import pMap from "p-map";
|
||||
|
||||
function delay(ms) {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
export default async function getImageSources(
|
||||
src,
|
||||
base,
|
||||
image,
|
||||
format,
|
||||
imageWidth,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
placeholder,
|
||||
imageFormat,
|
||||
formatOptions,
|
||||
fallbackFormat,
|
||||
includeSourceFormat,
|
||||
rest
|
||||
) {
|
||||
try {
|
||||
const calculatedConfigs = getConfigOptions(
|
||||
imageWidth,
|
||||
imagesizes,
|
||||
breakpoints,
|
||||
format,
|
||||
imageFormat,
|
||||
fallbackFormat,
|
||||
includeSourceFormat
|
||||
);
|
||||
|
||||
const { formats, requiredBreakpoints } = calculatedConfigs;
|
||||
imagesizes = calculatedConfigs.imagesizes;
|
||||
const maxWidth = requiredBreakpoints[requiredBreakpoints.length - 1];
|
||||
const sliceLength = -(maxWidth.toString().length + 2);
|
||||
|
||||
const sources = await pMap(
|
||||
formats,
|
||||
async (format) => {
|
||||
try {
|
||||
await delay(100);
|
||||
const srcset = await getSrcset(src, base, requiredBreakpoints, format, {
|
||||
...rest,
|
||||
...formatOptions[format],
|
||||
});
|
||||
|
||||
const srcsets = srcset.split(", ");
|
||||
const srcObject =
|
||||
format === fallbackFormat
|
||||
? { src: srcsets[srcsets.length - 1].slice(0, sliceLength) }
|
||||
: {};
|
||||
|
||||
return {
|
||||
...srcObject,
|
||||
format,
|
||||
srcset,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error(`Error processing format ${format}:`, error);
|
||||
return null;
|
||||
}
|
||||
},
|
||||
{ concurrency: 1 }
|
||||
);
|
||||
|
||||
const filteredSources = sources.filter(Boolean);
|
||||
|
||||
const sizes = {
|
||||
width: maxWidth,
|
||||
height: Math.round(maxWidth / rest.aspect),
|
||||
};
|
||||
|
||||
const fallback = await getFallbackImage(
|
||||
src,
|
||||
placeholder,
|
||||
image,
|
||||
fallbackFormat,
|
||||
formatOptions,
|
||||
rest
|
||||
)
|
||||
return { sources: filteredSources, sizes, fallback, imagesizes };
|
||||
} catch (error) {
|
||||
console.error("Error in getImageSources:", error);
|
||||
return { sources: [], sizes: {}, fallback: null, imagesizes: null };
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,80 +1,80 @@
|
||||
// @ts-check
|
||||
|
||||
import getAttributesString from "./getAttributesString.js";
|
||||
|
||||
export default function getImgElement({
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
style,
|
||||
srcset,
|
||||
loading,
|
||||
decoding,
|
||||
imagesizes,
|
||||
fadeInTransition,
|
||||
layoutStyles,
|
||||
imgAttributes,
|
||||
imgClassName = "",
|
||||
}) {
|
||||
const {
|
||||
class: customClasses = "",
|
||||
style: customInlineStyles = "",
|
||||
onload: customOnload = "",
|
||||
...restImgAttributes
|
||||
} = imgAttributes;
|
||||
|
||||
const attributesString = getAttributesString({
|
||||
attributes: restImgAttributes,
|
||||
element: "img",
|
||||
excludeArray: [
|
||||
"src",
|
||||
"alt",
|
||||
"srcset",
|
||||
"sizes",
|
||||
"width",
|
||||
"height",
|
||||
"loading",
|
||||
"decoding",
|
||||
],
|
||||
});
|
||||
|
||||
const classAttribute = ["astro-imagetools-img", imgClassName, customClasses]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const styleAttribute = [
|
||||
"display: inline-block; overflow: hidden; vertical-align: middle;",
|
||||
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
||||
layoutStyles,
|
||||
]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const onloadAttribute = [
|
||||
!imgClassName && style
|
||||
? fadeInTransition
|
||||
? `parentElement.style.setProperty('--z-index', 1); parentElement.style.setProperty('--opacity', 0);`
|
||||
: `parentElement.style.backgroundImage = 'unset';`
|
||||
: "",
|
||||
customOnload,
|
||||
]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const imgElement = `<img
|
||||
${attributesString}
|
||||
src="${src}"
|
||||
${typeof alt === "string" ? `alt="${alt}"` : ""}
|
||||
srcset="${srcset}"
|
||||
sizes="${imagesizes}"
|
||||
width="${sizes.width}"
|
||||
height="${sizes.height}"
|
||||
${loading ? `loading="${loading}"` : ""}
|
||||
${decoding ? `decoding="${decoding}"` : ""}
|
||||
class="${classAttribute}"
|
||||
style="${styleAttribute}"
|
||||
onload="${onloadAttribute}"
|
||||
/>`;
|
||||
|
||||
return imgElement;
|
||||
}
|
||||
// @ts-check
|
||||
|
||||
import getAttributesString from "./getAttributesString.js";
|
||||
|
||||
export default function getImgElement({
|
||||
src,
|
||||
alt,
|
||||
sizes,
|
||||
style,
|
||||
srcset,
|
||||
loading,
|
||||
decoding,
|
||||
imagesizes,
|
||||
fadeInTransition,
|
||||
layoutStyles,
|
||||
imgAttributes,
|
||||
imgClassName = "",
|
||||
}) {
|
||||
const {
|
||||
class: customClasses = "",
|
||||
style: customInlineStyles = "",
|
||||
onload: customOnload = "",
|
||||
...restImgAttributes
|
||||
} = imgAttributes;
|
||||
|
||||
const attributesString = getAttributesString({
|
||||
attributes: restImgAttributes,
|
||||
element: "img",
|
||||
excludeArray: [
|
||||
"src",
|
||||
"alt",
|
||||
"srcset",
|
||||
"sizes",
|
||||
"width",
|
||||
"height",
|
||||
"loading",
|
||||
"decoding",
|
||||
],
|
||||
});
|
||||
|
||||
const classAttribute = ["astro-imagetools-img", imgClassName, customClasses]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const styleAttribute = [
|
||||
"display: inline-block; overflow: hidden; vertical-align: middle;",
|
||||
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
||||
layoutStyles,
|
||||
]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const onloadAttribute = [
|
||||
!imgClassName && style
|
||||
? fadeInTransition
|
||||
? `parentElement.style.setProperty('--z-index', 1); parentElement.style.setProperty('--opacity', 0);`
|
||||
: `parentElement.style.backgroundImage = 'unset';`
|
||||
: "",
|
||||
customOnload,
|
||||
]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const imgElement = `<img
|
||||
${attributesString}
|
||||
src="${src}"
|
||||
${typeof alt === "string" ? `alt="${alt}"` : ""}
|
||||
srcset="${srcset}"
|
||||
sizes="${imagesizes}"
|
||||
width="${sizes.width}"
|
||||
height="${sizes.height}"
|
||||
${loading ? `loading="${loading}"` : ""}
|
||||
${decoding ? `decoding="${decoding}"` : ""}
|
||||
class="${classAttribute}"
|
||||
style="${styleAttribute}"
|
||||
onload="${onloadAttribute}"
|
||||
/>`;
|
||||
|
||||
return imgElement;
|
||||
}
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
// @ts-check
|
||||
|
||||
export default function getLayoutStyles({
|
||||
layout = null,
|
||||
isBackgroundImage = false,
|
||||
}) {
|
||||
return isBackgroundImage
|
||||
? "width: 100%; height: 100%;"
|
||||
: layout === "fill"
|
||||
? `width: 100%; height: 100%;`
|
||||
: layout === "fullWidth"
|
||||
? `width: 100%; height: auto;`
|
||||
: layout === "fixed"
|
||||
? ""
|
||||
: "max-width: 100%; height: auto;";
|
||||
}
|
||||
// @ts-check
|
||||
|
||||
export default function getLayoutStyles({
|
||||
layout = null,
|
||||
isBackgroundImage = false,
|
||||
}) {
|
||||
return isBackgroundImage
|
||||
? "width: 100%; height: 100%;"
|
||||
: layout === "fill"
|
||||
? `width: 100%; height: 100%;`
|
||||
: layout === "fullWidth"
|
||||
? `width: 100%; height: auto;`
|
||||
: layout === "fixed"
|
||||
? ""
|
||||
: "max-width: 100%; height: auto;";
|
||||
}
|
||||
|
||||
@ -1,34 +1,34 @@
|
||||
// @ts-check
|
||||
import getAttributesString from "./getAttributesString.js";
|
||||
|
||||
export default function getLinkElement({
|
||||
images = [],
|
||||
preload = "",
|
||||
imagesizes = "",
|
||||
linkAttributes,
|
||||
}) {
|
||||
const imagesrcset =
|
||||
preload &&
|
||||
images[images.length - 1]?.sources.find(
|
||||
({ format: fmt }) => fmt === preload
|
||||
)?.srcset;
|
||||
|
||||
const attributesString = getAttributesString({
|
||||
element: "link",
|
||||
attributes: linkAttributes,
|
||||
excludeArray: ["as", "rel", "imagesizes", "imagesrcset"],
|
||||
});
|
||||
|
||||
const linkElement =
|
||||
preload && images.length
|
||||
? `<link
|
||||
${attributesString}
|
||||
as="image"
|
||||
rel="preload"
|
||||
imagesizes="${imagesizes}"
|
||||
imagesrcset="${imagesrcset}"
|
||||
/>`
|
||||
: "";
|
||||
|
||||
return linkElement;
|
||||
}
|
||||
// @ts-check
|
||||
import getAttributesString from "./getAttributesString.js";
|
||||
|
||||
export default function getLinkElement({
|
||||
images = [],
|
||||
preload = "",
|
||||
imagesizes = "",
|
||||
linkAttributes,
|
||||
}) {
|
||||
const imagesrcset =
|
||||
preload &&
|
||||
images[images.length - 1]?.sources.find(
|
||||
({ format: fmt }) => fmt === preload
|
||||
)?.srcset;
|
||||
|
||||
const attributesString = getAttributesString({
|
||||
element: "link",
|
||||
attributes: linkAttributes,
|
||||
excludeArray: ["as", "rel", "imagesizes", "imagesrcset"],
|
||||
});
|
||||
|
||||
const linkElement =
|
||||
preload && images.length
|
||||
? `<link
|
||||
${attributesString}
|
||||
as="image"
|
||||
rel="preload"
|
||||
imagesizes="${imagesizes}"
|
||||
imagesrcset="${imagesrcset}"
|
||||
/>`
|
||||
: "";
|
||||
|
||||
return linkElement;
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import getLinkElement from "./getLinkElement";
|
||||
|
||||
describe("getLinkElement", () => {
|
||||
it("returns an empty string if preload is not set", () => {
|
||||
const result = getLinkElement({ linkAttributes: {} });
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
it("returns an empty string if no images are provided", () => {
|
||||
const result = getLinkElement({ linkAttributes: {}, preload: "webp" });
|
||||
expect(result).toBe("");
|
||||
});
|
||||
});
|
||||
import { describe, expect, it } from "vitest";
|
||||
import getLinkElement from "./getLinkElement";
|
||||
|
||||
describe("getLinkElement", () => {
|
||||
it("returns an empty string if preload is not set", () => {
|
||||
const result = getLinkElement({ linkAttributes: {} });
|
||||
expect(result).toBe("");
|
||||
});
|
||||
|
||||
it("returns an empty string if no images are provided", () => {
|
||||
const result = getLinkElement({ linkAttributes: {}, preload: "webp" });
|
||||
expect(result).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,43 +1,43 @@
|
||||
// @ts-check
|
||||
import getAttributesString from "./getAttributesString.js";
|
||||
|
||||
export default function getPictureElement({
|
||||
sources,
|
||||
className,
|
||||
layoutStyles,
|
||||
pictureAttributes,
|
||||
isBackgroundPicture = false,
|
||||
}) {
|
||||
const {
|
||||
class: customClasses = "",
|
||||
style: customInlineStyles = "",
|
||||
...restPictureAttributes
|
||||
} = pictureAttributes;
|
||||
|
||||
const attributesString = getAttributesString({
|
||||
attributes: restPictureAttributes,
|
||||
});
|
||||
|
||||
const classAttribute = ["astro-imagetools-picture", className, customClasses]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const styleAttribute = [
|
||||
isBackgroundPicture
|
||||
? `position: absolute; z-index: 0; width: 100%; height: 100%; display: inline-block;`
|
||||
: `position: relative; display: inline-block;`,
|
||||
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
||||
layoutStyles,
|
||||
]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const pictureElement = `<picture
|
||||
${attributesString}
|
||||
class="${classAttribute}"
|
||||
style="${styleAttribute}"
|
||||
>${sources.join("\n")}
|
||||
</picture>`;
|
||||
|
||||
return pictureElement;
|
||||
}
|
||||
// @ts-check
|
||||
import getAttributesString from "./getAttributesString.js";
|
||||
|
||||
export default function getPictureElement({
|
||||
sources,
|
||||
className,
|
||||
layoutStyles,
|
||||
pictureAttributes,
|
||||
isBackgroundPicture = false,
|
||||
}) {
|
||||
const {
|
||||
class: customClasses = "",
|
||||
style: customInlineStyles = "",
|
||||
...restPictureAttributes
|
||||
} = pictureAttributes;
|
||||
|
||||
const attributesString = getAttributesString({
|
||||
attributes: restPictureAttributes,
|
||||
});
|
||||
|
||||
const classAttribute = ["astro-imagetools-picture", className, customClasses]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const styleAttribute = [
|
||||
isBackgroundPicture
|
||||
? `position: absolute; z-index: 0; width: 100%; height: 100%; display: inline-block;`
|
||||
: `position: relative; display: inline-block;`,
|
||||
customInlineStyles + (customInlineStyles.endsWith(";") ? "" : ";"),
|
||||
layoutStyles,
|
||||
]
|
||||
.join(" ")
|
||||
.trim();
|
||||
|
||||
const pictureElement = `<picture
|
||||
${attributesString}
|
||||
class="${classAttribute}"
|
||||
style="${styleAttribute}"
|
||||
>${sources.join("\n")}
|
||||
</picture>`;
|
||||
|
||||
return pictureElement;
|
||||
}
|
||||
|
||||
@ -1,59 +1,59 @@
|
||||
// @ts-check
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { extname, relative, resolve } from "node:path";
|
||||
|
||||
import { getSrcPath } from "./getSrcPath.js";
|
||||
import getResolvedSrc from "./getResolvedSrc.js";
|
||||
import { cwd, sharp } from "../../utils/runtimeChecks.js";
|
||||
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
|
||||
|
||||
const { getImageDetails } = await (sharp
|
||||
? import("./imagetools.js")
|
||||
: import("./codecs.js"));
|
||||
|
||||
export default async function getProcessedImage(src, transformConfigs) {
|
||||
throwErrorIfUnsupported(src, extname(src).slice(1));
|
||||
|
||||
let base;
|
||||
if (src.match("(http://|https://|data:image/).*")) {
|
||||
({ src, base } = await getResolvedSrc(src))
|
||||
} else {
|
||||
const {
|
||||
default: { isSsrBuild },
|
||||
} = await import("../../astroViteConfigs.js")
|
||||
|
||||
if (isSsrBuild) {
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const assetPath = resolve(filename, "../../client") + src
|
||||
src = "/" + relative(cwd, assetPath);
|
||||
}
|
||||
}
|
||||
const {
|
||||
w,
|
||||
h,
|
||||
ar,
|
||||
width = w,
|
||||
height = h,
|
||||
aspect = ar,
|
||||
...rest
|
||||
} = transformConfigs;
|
||||
|
||||
const path = src.replace(/\\/g, `/`);
|
||||
|
||||
const { image, imageWidth, imageHeight, imageFormat } = await getImageDetails(
|
||||
await getSrcPath(src),
|
||||
width,
|
||||
height,
|
||||
aspect
|
||||
);
|
||||
|
||||
return {
|
||||
path,
|
||||
base,
|
||||
rest,
|
||||
image,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
imageFormat,
|
||||
};
|
||||
}
|
||||
// @ts-check
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { extname, relative, resolve } from "node:path";
|
||||
|
||||
import { getSrcPath } from "./getSrcPath.js";
|
||||
import getResolvedSrc from "./getResolvedSrc.js";
|
||||
import { cwd, sharp } from "../../utils/runtimeChecks.js";
|
||||
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
|
||||
|
||||
const { getImageDetails } = await (sharp
|
||||
? import("./imagetools.js")
|
||||
: import("./codecs.js"));
|
||||
|
||||
export default async function getProcessedImage(src, transformConfigs) {
|
||||
throwErrorIfUnsupported(src, extname(src).slice(1));
|
||||
|
||||
let base;
|
||||
if (src.match("(http://|https://|data:image/).*")) {
|
||||
({ src, base } = await getResolvedSrc(src))
|
||||
} else {
|
||||
const {
|
||||
default: { isSsrBuild },
|
||||
} = await import("../../astroViteConfigs.js")
|
||||
|
||||
if (isSsrBuild) {
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const assetPath = resolve(filename, "../../client") + src
|
||||
src = "/" + relative(cwd, assetPath);
|
||||
}
|
||||
}
|
||||
const {
|
||||
w,
|
||||
h,
|
||||
ar,
|
||||
width = w,
|
||||
height = h,
|
||||
aspect = ar,
|
||||
...rest
|
||||
} = transformConfigs;
|
||||
|
||||
const path = src.replace(/\\/g, `/`);
|
||||
|
||||
const { image, imageWidth, imageHeight, imageFormat } = await getImageDetails(
|
||||
await getSrcPath(src),
|
||||
width,
|
||||
height,
|
||||
aspect
|
||||
);
|
||||
|
||||
return {
|
||||
path,
|
||||
base,
|
||||
rest,
|
||||
image,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
imageFormat,
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,50 +1,50 @@
|
||||
// @ts-check
|
||||
import fs from "node:fs";
|
||||
import crypto from "node:crypto";
|
||||
import { join, parse, relative } from "node:path";
|
||||
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
|
||||
import {
|
||||
cwd,
|
||||
fsCachePath,
|
||||
supportedImageTypes,
|
||||
} from "../../utils/runtimeChecks.js";
|
||||
|
||||
const { fileTypeFromBuffer } = await import("file-type");
|
||||
|
||||
export default async function getResolvedSrc(src) {
|
||||
const token = crypto.createHash("md5").update(src).digest("hex");
|
||||
|
||||
let filepath = fsCachePath + token;
|
||||
|
||||
const fileExists = (() => {
|
||||
for (const type of supportedImageTypes) {
|
||||
const fileExists = fs.existsSync(filepath + `.${type}`);
|
||||
|
||||
if (fileExists) {
|
||||
filepath += `.${type}`;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
if (!fileExists) {
|
||||
const buffer = Buffer.from(await (await fetch(src)).arrayBuffer());
|
||||
|
||||
const { ext } = (await fileTypeFromBuffer(buffer)) || {};
|
||||
|
||||
throwErrorIfUnsupported(src, ext);
|
||||
|
||||
filepath += `.${ext}`;
|
||||
|
||||
fs.writeFileSync(filepath, buffer);
|
||||
}
|
||||
|
||||
const base = /^https?:/.test(src)
|
||||
? parse(new URL(src).pathname).name
|
||||
: undefined;
|
||||
|
||||
src = join("/", relative(cwd, filepath));
|
||||
|
||||
return { src, base };
|
||||
}
|
||||
// @ts-check
|
||||
import fs from "node:fs";
|
||||
import crypto from "node:crypto";
|
||||
import { join, parse, relative } from "node:path";
|
||||
import throwErrorIfUnsupported from "./throwErrorIfUnsupported.js";
|
||||
import {
|
||||
cwd,
|
||||
fsCachePath,
|
||||
supportedImageTypes,
|
||||
} from "../../utils/runtimeChecks.js";
|
||||
|
||||
const { fileTypeFromBuffer } = await import("file-type");
|
||||
|
||||
export default async function getResolvedSrc(src) {
|
||||
const token = crypto.createHash("md5").update(src).digest("hex");
|
||||
|
||||
let filepath = fsCachePath + token;
|
||||
|
||||
const fileExists = (() => {
|
||||
for (const type of supportedImageTypes) {
|
||||
const fileExists = fs.existsSync(filepath + `.${type}`);
|
||||
|
||||
if (fileExists) {
|
||||
filepath += `.${type}`;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
if (!fileExists) {
|
||||
const buffer = Buffer.from(await (await fetch(src)).arrayBuffer());
|
||||
|
||||
const { ext } = (await fileTypeFromBuffer(buffer)) || {};
|
||||
|
||||
throwErrorIfUnsupported(src, ext);
|
||||
|
||||
filepath += `.${ext}`;
|
||||
|
||||
fs.writeFileSync(filepath, buffer);
|
||||
}
|
||||
|
||||
const base = /^https?:/.test(src)
|
||||
? parse(new URL(src).pathname).name
|
||||
: undefined;
|
||||
|
||||
src = join("/", relative(cwd, filepath));
|
||||
|
||||
return { src, base };
|
||||
}
|
||||
|
||||
@ -1,32 +1,32 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
// To strip off params when checking for file on disk.
|
||||
const paramPattern = /\?.*/;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
export async function getSrcPath(src) {
|
||||
const { default: astroViteConfigs } = await import(
|
||||
"../../astroViteConfigs.js"
|
||||
);
|
||||
|
||||
// If this is already resolved to a file, return it.
|
||||
if (fs.existsSync(src.replace(paramPattern, ""))) return src;
|
||||
|
||||
const rootPath = path.join(astroViteConfigs.rootDir, src);
|
||||
const rootTest = rootPath.replace(paramPattern, "");
|
||||
if (fs.existsSync(rootTest)) return rootPath;
|
||||
|
||||
const publicPath = path.join(astroViteConfigs.publicDir, src);
|
||||
const publicTest = publicPath.replace(paramPattern, "");
|
||||
if (fs.existsSync(publicTest)) return publicPath;
|
||||
|
||||
// Fallback
|
||||
return src;
|
||||
}
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
// To strip off params when checking for file on disk.
|
||||
const paramPattern = /\?.*/;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
export async function getSrcPath(src) {
|
||||
const { default: astroViteConfigs } = await import(
|
||||
"../../astroViteConfigs.js"
|
||||
);
|
||||
|
||||
// If this is already resolved to a file, return it.
|
||||
if (fs.existsSync(src.replace(paramPattern, ""))) return src;
|
||||
|
||||
const rootPath = path.join(astroViteConfigs.rootDir, src);
|
||||
const rootTest = rootPath.replace(paramPattern, "");
|
||||
if (fs.existsSync(rootTest)) return rootPath;
|
||||
|
||||
const publicPath = path.join(astroViteConfigs.publicDir, src);
|
||||
const publicTest = publicPath.replace(paramPattern, "");
|
||||
if (fs.existsSync(publicTest)) return publicPath;
|
||||
|
||||
// Fallback
|
||||
return src;
|
||||
}
|
||||
|
||||
@ -1,67 +1,67 @@
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, afterAll, vi } from "vitest";
|
||||
import { getSrcPath } from "./getSrcPath";
|
||||
|
||||
vi.mock("../../astroViteConfigs.js", () => {
|
||||
return {
|
||||
default: {
|
||||
rootDir: buildPath(),
|
||||
// Custom publicDir
|
||||
publicDir: buildPath("out"),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Build an absolute path to the target in the fixture directory
|
||||
*/
|
||||
function buildPath(target = "") {
|
||||
return path.resolve(__dirname, "../../test-fixtures/getSrcPath", target);
|
||||
}
|
||||
|
||||
describe("getLinkElement", () => {
|
||||
afterAll(() => {
|
||||
vi.unmock("../../astroViteConfigs.js");
|
||||
});
|
||||
|
||||
it("finds a file in the root of the project", async () => {
|
||||
const result = await getSrcPath("root.jpeg");
|
||||
expect(result).toBe(buildPath("root.jpeg"));
|
||||
});
|
||||
|
||||
it("finds a file in the public folder", async () => {
|
||||
const result = await getSrcPath("out.jpeg");
|
||||
expect(result).toBe(buildPath("out/out.jpeg"));
|
||||
});
|
||||
|
||||
it("returns an absolute path unchanged, if it exists", async () => {
|
||||
const result = await getSrcPath(buildPath("out/out.jpeg"));
|
||||
expect(result).toBe(buildPath("out/out.jpeg"));
|
||||
});
|
||||
|
||||
it("handles query parameters", async () => {
|
||||
const result = await getSrcPath("root.jpeg?w=200");
|
||||
expect(result).toBe(buildPath("root.jpeg?w=200"));
|
||||
});
|
||||
|
||||
it("handles query parameters for public-resolved files", async () => {
|
||||
const result = await getSrcPath("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 () => {
|
||||
const result = await getSrcPath(
|
||||
"https://cdn.nedis.com/images/products_high_res/TVRC2080BK_P30.JPG"
|
||||
);
|
||||
expect(result).toBe(
|
||||
"https://cdn.nedis.com/images/products_high_res/TVRC2080BK_P30.JPG"
|
||||
);
|
||||
});
|
||||
|
||||
it("finds relative paths correctly", async () => {
|
||||
const outResult = await getSrcPath("./out/out.jpeg");
|
||||
const rootResult = await getSrcPath("./root.jpeg");
|
||||
expect(outResult).toBe(buildPath("out/out.jpeg"));
|
||||
expect(rootResult).toBe(buildPath("root.jpeg"));
|
||||
});
|
||||
});
|
||||
import path from "node:path";
|
||||
import { describe, expect, it, afterAll, vi } from "vitest";
|
||||
import { getSrcPath } from "./getSrcPath";
|
||||
|
||||
vi.mock("../../astroViteConfigs.js", () => {
|
||||
return {
|
||||
default: {
|
||||
rootDir: buildPath(),
|
||||
// Custom publicDir
|
||||
publicDir: buildPath("out"),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Build an absolute path to the target in the fixture directory
|
||||
*/
|
||||
function buildPath(target = "") {
|
||||
return path.resolve(__dirname, "../../test-fixtures/getSrcPath", target);
|
||||
}
|
||||
|
||||
describe("getLinkElement", () => {
|
||||
afterAll(() => {
|
||||
vi.unmock("../../astroViteConfigs.js");
|
||||
});
|
||||
|
||||
it("finds a file in the root of the project", async () => {
|
||||
const result = await getSrcPath("root.jpeg");
|
||||
expect(result).toBe(buildPath("root.jpeg"));
|
||||
});
|
||||
|
||||
it("finds a file in the public folder", async () => {
|
||||
const result = await getSrcPath("out.jpeg");
|
||||
expect(result).toBe(buildPath("out/out.jpeg"));
|
||||
});
|
||||
|
||||
it("returns an absolute path unchanged, if it exists", async () => {
|
||||
const result = await getSrcPath(buildPath("out/out.jpeg"));
|
||||
expect(result).toBe(buildPath("out/out.jpeg"));
|
||||
});
|
||||
|
||||
it("handles query parameters", async () => {
|
||||
const result = await getSrcPath("root.jpeg?w=200");
|
||||
expect(result).toBe(buildPath("root.jpeg?w=200"));
|
||||
});
|
||||
|
||||
it("handles query parameters for public-resolved files", async () => {
|
||||
const result = await getSrcPath("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 () => {
|
||||
const result = await getSrcPath(
|
||||
"https://cdn.nedis.com/images/products_high_res/TVRC2080BK_P30.JPG"
|
||||
);
|
||||
expect(result).toBe(
|
||||
"https://cdn.nedis.com/images/products_high_res/TVRC2080BK_P30.JPG"
|
||||
);
|
||||
});
|
||||
|
||||
it("finds relative paths correctly", async () => {
|
||||
const outResult = await getSrcPath("./out/out.jpeg");
|
||||
const rootResult = await getSrcPath("./root.jpeg");
|
||||
expect(outResult).toBe(buildPath("out/out.jpeg"));
|
||||
expect(rootResult).toBe(buildPath("root.jpeg"));
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,39 +1,39 @@
|
||||
// @ts-check
|
||||
import { getSrcPath } from "./getSrcPath.js";
|
||||
|
||||
export default async function getSrcset(
|
||||
src,
|
||||
base,
|
||||
breakpoints,
|
||||
format,
|
||||
options
|
||||
) {
|
||||
options = {
|
||||
format,
|
||||
w: breakpoints,
|
||||
...options,
|
||||
};
|
||||
|
||||
const keys = Object.keys(options);
|
||||
|
||||
const params = keys.length
|
||||
? keys
|
||||
.map((key) =>
|
||||
Array.isArray(options[key])
|
||||
? `&${key}=${options[key].join(";")}`
|
||||
: `&${key}=${options[key]}`
|
||||
)
|
||||
.join("")
|
||||
: "";
|
||||
|
||||
const id = `${src}?${params.slice(1)}`;
|
||||
|
||||
const fullPath = await getSrcPath(id);
|
||||
|
||||
const { default: load } = await import("../../plugin/hooks/load.js");
|
||||
|
||||
// @ts-ignore
|
||||
const srcset = (await load(fullPath, base)).slice(16, -1);
|
||||
|
||||
return srcset;
|
||||
}
|
||||
// @ts-check
|
||||
import { getSrcPath } from "./getSrcPath.js";
|
||||
|
||||
export default async function getSrcset(
|
||||
src,
|
||||
base,
|
||||
breakpoints,
|
||||
format,
|
||||
options
|
||||
) {
|
||||
options = {
|
||||
format,
|
||||
w: breakpoints,
|
||||
...options,
|
||||
};
|
||||
|
||||
const keys = Object.keys(options);
|
||||
|
||||
const params = keys.length
|
||||
? keys
|
||||
.map((key) =>
|
||||
Array.isArray(options[key])
|
||||
? `&${key}=${options[key].join(";")}`
|
||||
: `&${key}=${options[key]}`
|
||||
)
|
||||
.join("")
|
||||
: "";
|
||||
|
||||
const id = `${src}?${params.slice(1)}`;
|
||||
|
||||
const fullPath = await getSrcPath(id);
|
||||
|
||||
const { default: load } = await import("../../plugin/hooks/load.js");
|
||||
|
||||
// @ts-ignore
|
||||
const srcset = (await load(fullPath, base)).slice(16, -1);
|
||||
|
||||
return srcset;
|
||||
}
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
// @ts-check
|
||||
import getAttributesString from "./getAttributesString.js";
|
||||
|
||||
export default function getStyleElement({
|
||||
styleAttributes,
|
||||
backgroundStyles = "",
|
||||
}) {
|
||||
const attributesString = getAttributesString({
|
||||
attributes: styleAttributes,
|
||||
});
|
||||
|
||||
const styleElement = `<style ${attributesString}>${backgroundStyles}</style>`;
|
||||
|
||||
return styleElement;
|
||||
}
|
||||
// @ts-check
|
||||
import getAttributesString from "./getAttributesString.js";
|
||||
|
||||
export default function getStyleElement({
|
||||
styleAttributes,
|
||||
backgroundStyles = "",
|
||||
}) {
|
||||
const attributesString = getAttributesString({
|
||||
attributes: styleAttributes,
|
||||
});
|
||||
|
||||
const styleElement = `<style ${attributesString}>${backgroundStyles}</style>`;
|
||||
|
||||
return styleElement;
|
||||
}
|
||||
|
||||
@ -1,40 +1,40 @@
|
||||
// @ts-check
|
||||
import {
|
||||
builtins,
|
||||
loadImage,
|
||||
applyTransforms,
|
||||
generateTransforms,
|
||||
} from "imagetools-core";
|
||||
export {
|
||||
loadImage
|
||||
} from "imagetools-core";
|
||||
export async function getImageDetails(path, width, height, aspect) {
|
||||
const loadedImage = loadImage(path);
|
||||
|
||||
if (aspect && !width && !height) {
|
||||
if (!width && !height) {
|
||||
({ width } = await loadedImage.metadata());
|
||||
}
|
||||
|
||||
if (width) {
|
||||
height = width / aspect;
|
||||
}
|
||||
|
||||
if (height) {
|
||||
width = height * aspect;
|
||||
}
|
||||
}
|
||||
|
||||
const { image, metadata } = await applyTransforms(
|
||||
generateTransforms({ width, height }, builtins).transforms,
|
||||
loadedImage
|
||||
);
|
||||
|
||||
const {
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
format: imageFormat,
|
||||
} = metadata;
|
||||
|
||||
return { image, imageWidth, imageHeight, imageFormat };
|
||||
}
|
||||
// @ts-check
|
||||
import {
|
||||
builtins,
|
||||
loadImage,
|
||||
applyTransforms,
|
||||
generateTransforms,
|
||||
} from "imagetools-core";
|
||||
export {
|
||||
loadImage
|
||||
} from "imagetools-core";
|
||||
export async function getImageDetails(path, width, height, aspect) {
|
||||
const loadedImage = loadImage(path);
|
||||
|
||||
if (aspect && !width && !height) {
|
||||
if (!width && !height) {
|
||||
({ width } = await loadedImage.metadata());
|
||||
}
|
||||
|
||||
if (width) {
|
||||
height = width / aspect;
|
||||
}
|
||||
|
||||
if (height) {
|
||||
width = height * aspect;
|
||||
}
|
||||
}
|
||||
|
||||
const { image, metadata } = await applyTransforms(
|
||||
generateTransforms({ width, height }, builtins).transforms,
|
||||
loadedImage
|
||||
);
|
||||
|
||||
const {
|
||||
width: imageWidth,
|
||||
height: imageHeight,
|
||||
format: imageFormat,
|
||||
} = metadata;
|
||||
|
||||
return { image, imageWidth, imageHeight, imageFormat };
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
// @ts-check
|
||||
import { supportedImageTypes } from "../../utils/runtimeChecks.js";
|
||||
|
||||
export default function throwErrorIfUnsupported(src, ext) {
|
||||
if (!ext && typeof ext !== "string") {
|
||||
throw new Error(`Failed to load ${src}; Invalid image format`);
|
||||
}
|
||||
|
||||
if (ext && !supportedImageTypes.includes(ext.toLowerCase())) {
|
||||
throw new Error(
|
||||
`Failed to load ${src}; Invalid image format ${ext} or the format is not supported by astro-imagetools`
|
||||
);
|
||||
}
|
||||
}
|
||||
// @ts-check
|
||||
import { supportedImageTypes } from "../../utils/runtimeChecks.js";
|
||||
|
||||
export default function throwErrorIfUnsupported(src, ext) {
|
||||
if (!ext && typeof ext !== "string") {
|
||||
throw new Error(`Failed to load ${src}; Invalid image format`);
|
||||
}
|
||||
|
||||
if (ext && !supportedImageTypes.includes(ext.toLowerCase())) {
|
||||
throw new Error(
|
||||
`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\\",
|
||||
"rootDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\",
|
||||
"mode": "production",
|
||||
"outDir": "dist",
|
||||
"assetsDir": "/_astro",
|
||||
"outDir": "C:\\Users\\zx\\Desktop\\polymech\\polymech-site\\dist\\",
|
||||
"assetsDir": "_astro",
|
||||
"sourcemap": false,
|
||||
"assetFileNames": "/_astro/[name]@[width].[hash][extname]"
|
||||
}
|
||||
@ -1,46 +1,46 @@
|
||||
---
|
||||
import renderBackgroundImage from "../api/renderBackgroundImage.js";
|
||||
import type { BackgroundImageConfigOptions } from "../types.d";
|
||||
|
||||
const content = await Astro.slots.render("default");
|
||||
|
||||
declare interface Props
|
||||
extends Pick<
|
||||
BackgroundImageConfigOptions,
|
||||
Exclude<keyof BackgroundImageConfigOptions, "content">
|
||||
> {}
|
||||
|
||||
const { link, style, htmlElement } = await renderBackgroundImage({
|
||||
content,
|
||||
...(Astro.props as Props),
|
||||
});
|
||||
---
|
||||
|
||||
<Fragment set:html={link + style + htmlElement} />
|
||||
|
||||
<script>
|
||||
const { classList } = document.documentElement;
|
||||
|
||||
const addClass = classList.add.bind(classList);
|
||||
|
||||
addClass("jpeg");
|
||||
addClass("png");
|
||||
|
||||
const isFormatSupported = (format, dataUri) => {
|
||||
const image = new Image();
|
||||
|
||||
image.src = `data:image/${format};base64,${dataUri}`;
|
||||
|
||||
image.onload = addClass(format);
|
||||
};
|
||||
|
||||
// TODO: Check support for JXL images
|
||||
// isFormatSupported("jxl", "/woAEBAJCAQBACwASxLFgoUJEP3D/wA=");
|
||||
|
||||
isFormatSupported("webp", "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==");
|
||||
|
||||
isFormatSupported(
|
||||
"avif",
|
||||
"AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A="
|
||||
);
|
||||
</script>
|
||||
---
|
||||
import renderBackgroundImage from "../api/renderBackgroundImage.js";
|
||||
import type { BackgroundImageConfigOptions } from "../types.d";
|
||||
|
||||
const content = await Astro.slots.render("default");
|
||||
|
||||
declare interface Props
|
||||
extends Pick<
|
||||
BackgroundImageConfigOptions,
|
||||
Exclude<keyof BackgroundImageConfigOptions, "content">
|
||||
> {}
|
||||
|
||||
const { link, style, htmlElement } = await renderBackgroundImage({
|
||||
content,
|
||||
...(Astro.props as Props),
|
||||
});
|
||||
---
|
||||
|
||||
<Fragment set:html={link + style + htmlElement} />
|
||||
|
||||
<script>
|
||||
const { classList } = document.documentElement;
|
||||
|
||||
const addClass = classList.add.bind(classList);
|
||||
|
||||
addClass("jpeg");
|
||||
addClass("png");
|
||||
|
||||
const isFormatSupported = (format, dataUri) => {
|
||||
const image = new Image();
|
||||
|
||||
image.src = `data:image/${format};base64,${dataUri}`;
|
||||
|
||||
image.onload = addClass(format);
|
||||
};
|
||||
|
||||
// TODO: Check support for JXL images
|
||||
// isFormatSupported("jxl", "/woAEBAJCAQBACwASxLFgoUJEP3D/wA=");
|
||||
|
||||
isFormatSupported("webp", "UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==");
|
||||
|
||||
isFormatSupported(
|
||||
"avif",
|
||||
"AAAAIGZ0eXBhdmlmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljdAAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEAAAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAAAamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAAxhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAACVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A="
|
||||
);
|
||||
</script>
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
---
|
||||
import renderBackgroundPicture from "../api/renderBackgroundPicture.js";
|
||||
import { BackgroundPictureConfigOptions } from "../types.d";
|
||||
|
||||
declare interface Props
|
||||
extends Pick<
|
||||
BackgroundPictureConfigOptions,
|
||||
Exclude<keyof BackgroundPictureConfigOptions, "content">
|
||||
> {}
|
||||
|
||||
const content = await Astro.slots.render("default");
|
||||
|
||||
const { link, style, htmlElement } = await renderBackgroundPicture({
|
||||
content,
|
||||
...(Astro.props as Props),
|
||||
});
|
||||
---
|
||||
|
||||
<Fragment set:html={link + style + htmlElement} />
|
||||
---
|
||||
import renderBackgroundPicture from "../api/renderBackgroundPicture.js";
|
||||
import { BackgroundPictureConfigOptions } from "../types.d";
|
||||
|
||||
declare interface Props
|
||||
extends Pick<
|
||||
BackgroundPictureConfigOptions,
|
||||
Exclude<keyof BackgroundPictureConfigOptions, "content">
|
||||
> {}
|
||||
|
||||
const content = await Astro.slots.render("default");
|
||||
|
||||
const { link, style, htmlElement } = await renderBackgroundPicture({
|
||||
content,
|
||||
...(Astro.props as Props),
|
||||
});
|
||||
---
|
||||
|
||||
<Fragment set:html={link + style + htmlElement} />
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
---
|
||||
import renderImage from "../api/renderImage.js";
|
||||
import type { PictureConfigOptions as ImageConfigOptions } from "../types.d";
|
||||
|
||||
const { link, style, image } = await renderImage(
|
||||
Astro.props as ImageConfigOptions
|
||||
);
|
||||
---
|
||||
|
||||
<Fragment set:html={link + style + image} />
|
||||
---
|
||||
import renderImage from "../api/renderImage.js";
|
||||
import type { PictureConfigOptions as ImageConfigOptions } from "../types.d";
|
||||
|
||||
const { link, style, image } = await renderImage(
|
||||
Astro.props as ImageConfigOptions
|
||||
);
|
||||
---
|
||||
|
||||
<Fragment set:html={link + style + image} />
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<!-- prettier-ignore -->
|
||||
<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=");
|
||||
</script>
|
||||
<!-- prettier-ignore -->
|
||||
<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=");
|
||||
</script>
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
---
|
||||
import renderImg from "../api/renderImg.js";
|
||||
import type { ImgConfigOptions } from "../types.d";
|
||||
|
||||
declare interface Props extends ImgConfigOptions {}
|
||||
|
||||
const { link, style, img } = await renderImg(Astro.props as Props);
|
||||
---
|
||||
|
||||
<Fragment set:html={link + style + img} />
|
||||
---
|
||||
import renderImg from "../api/renderImg.js";
|
||||
import type { ImgConfigOptions } from "../types.d";
|
||||
|
||||
declare interface Props extends ImgConfigOptions {}
|
||||
|
||||
const { link, style, img } = await renderImg(Astro.props as Props);
|
||||
---
|
||||
|
||||
<Fragment set:html={link + style + img} />
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
---
|
||||
import renderPicture from "../api/renderPicture.js";
|
||||
import type { PictureConfigOptions } from "../types.d";
|
||||
|
||||
declare interface Props extends PictureConfigOptions {}
|
||||
|
||||
const { link, style, picture } = await renderPicture(Astro.props as Props);
|
||||
---
|
||||
|
||||
<Fragment set:html={link + style + picture} />
|
||||
---
|
||||
import renderPicture from "../api/renderPicture.js";
|
||||
import type { PictureConfigOptions } from "../types.d";
|
||||
|
||||
declare interface Props extends PictureConfigOptions {}
|
||||
|
||||
const { link, style, picture } = await renderPicture(Astro.props as Props);
|
||||
---
|
||||
|
||||
<Fragment set:html={link + style + picture} />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
export { default as Img } from "./Img.astro";
|
||||
export { default as Picture } from "./Picture.astro";
|
||||
export { default as BackgroundImage } from "./BackgroundImage.astro";
|
||||
export { default as BackgroundPicture } from "./BackgroundPicture.astro";
|
||||
export { default as ImageSupportDetection } from "./ImageSupportDetection.astro";
|
||||
export { default as Img } from "./Img.astro";
|
||||
export { default as Picture } from "./Picture.astro";
|
||||
export { default as BackgroundImage } from "./BackgroundImage.astro";
|
||||
export { default as BackgroundPicture } from "./BackgroundPicture.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";
|
||||
|
||||
export function defineConfig(config: GlobalConfigOptions): GlobalConfigOptions;
|
||||
import type { GlobalConfigOptions } from "./types";
|
||||
|
||||
export function defineConfig(config: GlobalConfigOptions): GlobalConfigOptions;
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
export function defineConfig(config) {
|
||||
return config;
|
||||
}
|
||||
export function defineConfig(config) {
|
||||
return config;
|
||||
}
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
## force pnpm to hoist
|
||||
## force pnpm to hoist
|
||||
shamefully-hoist = true
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"startCommand": "npm start",
|
||||
"env": {
|
||||
"ENABLE_CJS_IMPORTS": true
|
||||
}
|
||||
}
|
||||
{
|
||||
"startCommand": "npm start",
|
||||
"env": {
|
||||
"ENABLE_CJS_IMPORTS": true
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
# Astro ImageTools Live Examples
|
||||
|
||||
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.
|
||||
# Astro ImageTools Live Examples
|
||||
|
||||
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.
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
import { defineConfig } from "astro-imagetools/config";
|
||||
|
||||
export default defineConfig({});
|
||||
import { defineConfig } from "astro-imagetools/config";
|
||||
|
||||
export default defineConfig({});
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
import { defineConfig } from "astro/config";
|
||||
import { astroImageTools } from "astro-imagetools";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
vite: {
|
||||
plugins: [
|
||||
{
|
||||
name: "import.meta.url-transformer",
|
||||
transform: (code, id) => {
|
||||
if (id.endsWith(".astro"))
|
||||
return code.replace(/import.meta.url/g, `"${id}"`);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
experimental: {
|
||||
integrations: true,
|
||||
},
|
||||
|
||||
integrations: [astroImageTools],
|
||||
});
|
||||
import { defineConfig } from "astro/config";
|
||||
import { astroImageTools } from "astro-imagetools";
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
vite: {
|
||||
plugins: [
|
||||
{
|
||||
name: "import.meta.url-transformer",
|
||||
transform: (code, id) => {
|
||||
if (id.endsWith(".astro"))
|
||||
return code.replace(/import.meta.url/g, `"${id}"`);
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
experimental: {
|
||||
integrations: true,
|
||||
},
|
||||
|
||||
integrations: [astroImageTools],
|
||||
});
|
||||
|
||||
@ -1,23 +1,23 @@
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro-spa": "^1.3.9",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/lit": "^1.1.2",
|
||||
"@astrojs/preact": "^2.0.2",
|
||||
"@astrojs/react": "^2.0.2",
|
||||
"@astrojs/solid-js": "^2.0.2",
|
||||
"@astrojs/svelte": "^2.0.1",
|
||||
"@astrojs/vue": "^2.0.1",
|
||||
"astro": "^2.0.6",
|
||||
"astro-imagetools": "workspace:^0.9.0"
|
||||
}
|
||||
}
|
||||
{
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"start": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"astro-spa": "^1.3.9",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@astrojs/lit": "^1.1.2",
|
||||
"@astrojs/preact": "^2.0.2",
|
||||
"@astrojs/react": "^2.0.2",
|
||||
"@astrojs/solid-js": "^2.0.2",
|
||||
"@astrojs/svelte": "^2.0.1",
|
||||
"@astrojs/vue": "^2.0.1",
|
||||
"astro": "^2.0.6",
|
||||
"astro-imagetools": "workspace:^0.9.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
{
|
||||
"infiniteLoopProtection": true,
|
||||
"hardReloadOnChange": false,
|
||||
"view": "browser",
|
||||
"template": "node",
|
||||
"container": {
|
||||
"port": 3000,
|
||||
"startScript": "start",
|
||||
"node": "16"
|
||||
}
|
||||
}
|
||||
{
|
||||
"infiniteLoopProtection": true,
|
||||
"hardReloadOnChange": false,
|
||||
"view": "browser",
|
||||
"template": "node",
|
||||
"container": {
|
||||
"port": 3000,
|
||||
"startScript": "start",
|
||||
"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 MainLayout from "./MainLayout.astro";
|
||||
|
||||
const { layout, importMetaUrl } = Astro.props;
|
||||
---
|
||||
|
||||
<MainLayout {importMetaUrl}>
|
||||
<h1><code>{layout}</code> Layout Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Picture src="https://picsum.photos/1024/768/" alt="A random image" {layout}
|
||||
/>
|
||||
</MainLayout>
|
||||
---
|
||||
import { Picture } from "astro-imagetools/components";
|
||||
import MainLayout from "./MainLayout.astro";
|
||||
|
||||
const { layout, importMetaUrl } = Astro.props;
|
||||
---
|
||||
|
||||
<MainLayout {importMetaUrl}>
|
||||
<h1><code>{layout}</code> Layout Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Picture src="https://picsum.photos/1024/768/" alt="A random image" {layout}
|
||||
/>
|
||||
</MainLayout>
|
||||
|
||||
@ -1,57 +1,57 @@
|
||||
---
|
||||
import { Spa } from "astro-spa";
|
||||
import "../styles/index.css";
|
||||
|
||||
const { importMetaUrl } = Astro.props as { importMetaUrl: string };
|
||||
|
||||
const path =
|
||||
Astro.props.content?.path ||
|
||||
importMetaUrl?.slice(importMetaUrl.indexOf("/pages/") + 7);
|
||||
|
||||
const GitHubURL = `https://github.com/RafidMuhymin/astro-imagetools/tree/main/demo/src/pages/${path}`;
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap-reboot.min.css"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
|
||||
<!-- Unset bootstrap image resets -->
|
||||
<style is:global>
|
||||
img, svg {
|
||||
vertical-align: unset;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100"
|
||||
height="100"
|
||||
viewBox="0 0 250 250"
|
||||
fill="#151513"
|
||||
class="view-source"
|
||||
>
|
||||
<a title="View Source" href={GitHubURL}>
|
||||
<path d="M0 0l115 115h15l12 27 108 108V0z" fill="#fff"></path>
|
||||
<path
|
||||
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"
|
||||
></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"
|
||||
></path>
|
||||
</a>
|
||||
</svg>
|
||||
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<Spa />
|
||||
</body>
|
||||
</html>
|
||||
---
|
||||
import { Spa } from "astro-spa";
|
||||
import "../styles/index.css";
|
||||
|
||||
const { importMetaUrl } = Astro.props as { importMetaUrl: string };
|
||||
|
||||
const path =
|
||||
Astro.props.content?.path ||
|
||||
importMetaUrl?.slice(importMetaUrl.indexOf("/pages/") + 7);
|
||||
|
||||
const GitHubURL = `https://github.com/RafidMuhymin/astro-imagetools/tree/main/demo/src/pages/${path}`;
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap-reboot.min.css"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
|
||||
<!-- Unset bootstrap image resets -->
|
||||
<style is:global>
|
||||
img, svg {
|
||||
vertical-align: unset;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100"
|
||||
height="100"
|
||||
viewBox="0 0 250 250"
|
||||
fill="#151513"
|
||||
class="view-source"
|
||||
>
|
||||
<a title="View Source" href={GitHubURL}>
|
||||
<path d="M0 0l115 115h15l12 27 108 108V0z" fill="#fff"></path>
|
||||
<path
|
||||
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"
|
||||
></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"
|
||||
></path>
|
||||
</a>
|
||||
</svg>
|
||||
|
||||
<slot />
|
||||
</main>
|
||||
|
||||
<Spa />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
---
|
||||
import { Picture } from "astro-imagetools/components";
|
||||
import MainLayout from "./MainLayout.astro";
|
||||
|
||||
const { placeholder, importMetaUrl } = Astro.props;
|
||||
---
|
||||
|
||||
<MainLayout {importMetaUrl}>
|
||||
<h1><code>{placeholder}</code> Placeholder Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Picture
|
||||
src="https://picsum.photos/1024/768/"
|
||||
alt="A random image"
|
||||
{placeholder}
|
||||
/>
|
||||
</MainLayout>
|
||||
---
|
||||
import { Picture } from "astro-imagetools/components";
|
||||
import MainLayout from "./MainLayout.astro";
|
||||
|
||||
const { placeholder, importMetaUrl } = Astro.props;
|
||||
---
|
||||
|
||||
<MainLayout {importMetaUrl}>
|
||||
<h1><code>{placeholder}</code> Placeholder Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Picture
|
||||
src="https://picsum.photos/1024/768/"
|
||||
alt="A random image"
|
||||
{placeholder}
|
||||
/>
|
||||
</MainLayout>
|
||||
|
||||
@ -1,48 +1,48 @@
|
||||
---
|
||||
import { ImageSupportDetection } from "astro-imagetools/components";
|
||||
import { renderBackgroundImage } from "astro-imagetools/api";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
|
||||
const { link, style, htmlElement } = await renderBackgroundImage({
|
||||
src: "https://picsum.photos/1024/768/",
|
||||
content: `<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||
aliquam at quidem dolorum?
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||
reiciendis dicta quaerat maiores omnis.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||
suscipit sequi. At, deleniti nihil.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||
Nesciunt, repellat mollitia. Rem, labore?
|
||||
</p>`,
|
||||
});
|
||||
---
|
||||
|
||||
<ImageSupportDetection />
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"renderBackgroundImage"}</code> API Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Fragment set:html={link + style + htmlElement} />
|
||||
</MainLayout>
|
||||
---
|
||||
import { ImageSupportDetection } from "astro-imagetools/components";
|
||||
import { renderBackgroundImage } from "astro-imagetools/api";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
|
||||
const { link, style, htmlElement } = await renderBackgroundImage({
|
||||
src: "https://picsum.photos/1024/768/",
|
||||
content: `<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||
aliquam at quidem dolorum?
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||
reiciendis dicta quaerat maiores omnis.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||
suscipit sequi. At, deleniti nihil.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||
Nesciunt, repellat mollitia. Rem, labore?
|
||||
</p>`,
|
||||
});
|
||||
---
|
||||
|
||||
<ImageSupportDetection />
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"renderBackgroundImage"}</code> API Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Fragment set:html={link + style + htmlElement} />
|
||||
</MainLayout>
|
||||
|
||||
@ -1,45 +1,45 @@
|
||||
---
|
||||
import { renderBackgroundPicture } from "astro-imagetools/api";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
|
||||
const { link, style, htmlElement } = await renderBackgroundPicture({
|
||||
src: "https://picsum.photos/1024/768/",
|
||||
content: `<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||
aliquam at quidem dolorum?
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||
reiciendis dicta quaerat maiores omnis.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||
suscipit sequi. At, deleniti nihil.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||
Nesciunt, repellat mollitia. Rem, labore?
|
||||
</p>`,
|
||||
});
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"renderBackgroundPicture"}</code> API Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Fragment set:html={link + style + htmlElement} />
|
||||
</MainLayout>
|
||||
---
|
||||
import { renderBackgroundPicture } from "astro-imagetools/api";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
|
||||
const { link, style, htmlElement } = await renderBackgroundPicture({
|
||||
src: "https://picsum.photos/1024/768/",
|
||||
content: `<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||
aliquam at quidem dolorum?
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||
reiciendis dicta quaerat maiores omnis.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||
suscipit sequi. At, deleniti nihil.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||
Nesciunt, repellat mollitia. Rem, labore?
|
||||
</p>`,
|
||||
});
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"renderBackgroundPicture"}</code> API Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Fragment set:html={link + style + htmlElement} />
|
||||
</MainLayout>
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
---
|
||||
import { renderImg } from "astro-imagetools/api";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
|
||||
const { link, style, img } = await renderImg({
|
||||
src: "https://picsum.photos/1024/768/",
|
||||
alt: "A random image",
|
||||
});
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"renderImg"}</code> API Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Fragment set:html={link + style + img} />
|
||||
</MainLayout>
|
||||
---
|
||||
import { renderImg } from "astro-imagetools/api";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
|
||||
const { link, style, img } = await renderImg({
|
||||
src: "https://picsum.photos/1024/768/",
|
||||
alt: "A random image",
|
||||
});
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"renderImg"}</code> API Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Fragment set:html={link + style + img} />
|
||||
</MainLayout>
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
---
|
||||
import { renderPicture } from "astro-imagetools/api";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
|
||||
const { link, style, picture } = await renderPicture({
|
||||
src: "https://picsum.photos/1024/768/",
|
||||
alt: "A random image",
|
||||
});
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"renderPicture"}</code> API Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Fragment set:html={link + style + picture} />
|
||||
</MainLayout>
|
||||
---
|
||||
import { renderPicture } from "astro-imagetools/api";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
|
||||
const { link, style, picture } = await renderPicture({
|
||||
src: "https://picsum.photos/1024/768/",
|
||||
alt: "A random image",
|
||||
});
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"renderPicture"}</code> API Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Fragment set:html={link + style + picture} />
|
||||
</MainLayout>
|
||||
|
||||
@ -1,45 +1,45 @@
|
||||
---
|
||||
import {
|
||||
BackgroundImage,
|
||||
ImageSupportDetection,
|
||||
} from "astro-imagetools/components";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
---
|
||||
|
||||
<ImageSupportDetection />
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>
|
||||
Astro ImageTools <code>{"<BackgroundImage />"}</code> Component Example
|
||||
</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<BackgroundImage src="https://picsum.photos/1024/768/">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||
aliquam at quidem dolorum?
|
||||
</p>
|
||||
<p>
|
||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||
reiciendis dicta quaerat maiores omnis.
|
||||
</p>
|
||||
<p>
|
||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||
suscipit sequi. At, deleniti nihil.
|
||||
</p>
|
||||
<p>
|
||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||
</p>
|
||||
<p>
|
||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||
Nesciunt, repellat mollitia. Rem, labore?
|
||||
</p>
|
||||
</BackgroundImage>
|
||||
</MainLayout>
|
||||
---
|
||||
import {
|
||||
BackgroundImage,
|
||||
ImageSupportDetection,
|
||||
} from "astro-imagetools/components";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
---
|
||||
|
||||
<ImageSupportDetection />
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>
|
||||
Astro ImageTools <code>{"<BackgroundImage />"}</code> Component Example
|
||||
</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<BackgroundImage src="https://picsum.photos/1024/768/">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||
aliquam at quidem dolorum?
|
||||
</p>
|
||||
<p>
|
||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||
reiciendis dicta quaerat maiores omnis.
|
||||
</p>
|
||||
<p>
|
||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||
suscipit sequi. At, deleniti nihil.
|
||||
</p>
|
||||
<p>
|
||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||
</p>
|
||||
<p>
|
||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||
Nesciunt, repellat mollitia. Rem, labore?
|
||||
</p>
|
||||
</BackgroundImage>
|
||||
</MainLayout>
|
||||
|
||||
@ -1,40 +1,40 @@
|
||||
---
|
||||
import { BackgroundPicture } from "astro-imagetools/components";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>
|
||||
Astro ImageTools <code>{"<BackgroundPicture />"}</code> Component Example
|
||||
</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<BackgroundPicture src="https://picsum.photos/1024/768/">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||
aliquam at quidem dolorum?
|
||||
</p>
|
||||
<p>
|
||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||
reiciendis dicta quaerat maiores omnis.
|
||||
</p>
|
||||
<p>
|
||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||
suscipit sequi. At, deleniti nihil.
|
||||
</p>
|
||||
<p>
|
||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||
</p>
|
||||
<p>
|
||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||
Nesciunt, repellat mollitia. Rem, labore?
|
||||
</p>
|
||||
</BackgroundPicture>
|
||||
</MainLayout>
|
||||
---
|
||||
import { BackgroundPicture } from "astro-imagetools/components";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>
|
||||
Astro ImageTools <code>{"<BackgroundPicture />"}</code> Component Example
|
||||
</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<BackgroundPicture src="https://picsum.photos/1024/768/">
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eveniet, nisi!
|
||||
Commodi, dolor hic omnis debitis natus eaque nisi magni corrupti earum
|
||||
aliquam at quidem dolorum?
|
||||
</p>
|
||||
<p>
|
||||
Eos facere amet nesciunt! Vero, culpa eaque quasi ullam atque alias
|
||||
repudiandae facere placeat ipsam recusandae quam minus nemo officia
|
||||
reiciendis dicta quaerat maiores omnis.
|
||||
</p>
|
||||
<p>
|
||||
Pariatur doloribus, facilis enim accusamus velit, amet ducimus dolorum
|
||||
unde numquam doloremque eveniet eum et error, quod quas fugit commodi
|
||||
suscipit sequi. At, deleniti nihil.
|
||||
</p>
|
||||
<p>
|
||||
Laborum voluptate maxime dicta alias minus nam, doloribus accusantium
|
||||
veritatis perferendis, expedita eaque. Deleniti deserunt, iure dolorum
|
||||
itaque fugiat assumenda ullam amet asperiores soluta ipsam!
|
||||
</p>
|
||||
<p>
|
||||
Veniam qui ad, illo fuga autem voluptatibus iusto cum reprehenderit error.
|
||||
Nemo nisi laborum blanditiis, accusamus dolores harum labore perspiciatis.
|
||||
Nesciunt, repellat mollitia. Rem, labore?
|
||||
</p>
|
||||
</BackgroundPicture>
|
||||
</MainLayout>
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
---
|
||||
import { Img } from "astro-imagetools/components";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"<Img />"}</code> Component Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Img src="https://picsum.photos/1024/768/" alt="A random image" />
|
||||
</MainLayout>
|
||||
---
|
||||
import { Img } from "astro-imagetools/components";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"<Img />"}</code> Component Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Img src="https://picsum.photos/1024/768/" alt="A random image" />
|
||||
</MainLayout>
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
---
|
||||
import { Picture } from "astro-imagetools/components";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"<Picture />"}</code> Component Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Picture src="https://picsum.photos/1024/768/" alt="A random image" />
|
||||
</MainLayout>
|
||||
---
|
||||
import { Picture } from "astro-imagetools/components";
|
||||
import MainLayout from "../../layouts/MainLayout.astro";
|
||||
---
|
||||
|
||||
<MainLayout importMetaUrl={import.meta.url}>
|
||||
<h1>Astro ImageTools <code>{"<Picture />"}</code> Component Example</h1>
|
||||
|
||||
<br />
|
||||
|
||||
<Picture src="https://picsum.photos/1024/768/" alt="A random image" />
|
||||
</MainLayout>
|
||||
|
||||
@ -1,78 +1,78 @@
|
||||
---
|
||||
path: index.md
|
||||
layout: ../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
# 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.
|
||||
|
||||
<hr />
|
||||
|
||||
## Components
|
||||
|
||||
- [`<Img />` Component](/components/Img)
|
||||
- [`<Picture />` Component](/components/Picture)
|
||||
- [`<BackgroundImage />` Component](/components/BackgroundImage)
|
||||
- [`<BackgroundPicture />` Component](/components/BackgroundPicture)
|
||||
|
||||
## APIs
|
||||
|
||||
- [`renderImg` API](/api/renderImg)
|
||||
- [`renderPicture` API](/api/renderPicture)
|
||||
- [`renderBackgroundImage` API](/api/renderBackgroundImage)
|
||||
- [`renderBackgroundPicture` API](/api/renderBackgroundPicture)
|
||||
|
||||
## Layout
|
||||
|
||||
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.
|
||||
|
||||
- [Constrained Layout](/layout/constrained)
|
||||
- [Fixed Layout](/layout/fixed)
|
||||
- [Full Width Layout](/layout/fullWidth)
|
||||
- [Fill Layout](/layout/fill)
|
||||
|
||||
<hr />
|
||||
|
||||
## Placeholder
|
||||
|
||||
The `placeholder` property tells the image what to show while loading.
|
||||
|
||||
- [Blurred Placeholder](/placeholder/blurred)
|
||||
- [Dominant Color Placeholder](/placeholder/dominantColor)
|
||||
- [Traced SVG Placeholder](/placeholder/tracedSVG)
|
||||
- [No Placeholder](/placeholder/none)
|
||||
|
||||
<hr />
|
||||
|
||||
## Internal Image
|
||||
|
||||
The following is an example of a reference to an internal image in the `src` directory (`../images/elva-800w.jpg`).
|
||||
|
||||

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

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

|
||||
|
||||
<hr />
|
||||
|
||||
## Learn More
|
||||
|
||||
You can optionally configure many more things!
|
||||
|
||||
Checkout the [Astro ImageTools](https://astro-imagetools-docs.vercel.app/) documentation to learn more.
|
||||
---
|
||||
path: index.md
|
||||
layout: ../layouts/MainLayout.astro
|
||||
---
|
||||
|
||||
# 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.
|
||||
|
||||
<hr />
|
||||
|
||||
## Components
|
||||
|
||||
- [`<Img />` Component](/components/Img)
|
||||
- [`<Picture />` Component](/components/Picture)
|
||||
- [`<BackgroundImage />` Component](/components/BackgroundImage)
|
||||
- [`<BackgroundPicture />` Component](/components/BackgroundPicture)
|
||||
|
||||
## APIs
|
||||
|
||||
- [`renderImg` API](/api/renderImg)
|
||||
- [`renderPicture` API](/api/renderPicture)
|
||||
- [`renderBackgroundImage` API](/api/renderBackgroundImage)
|
||||
- [`renderBackgroundPicture` API](/api/renderBackgroundPicture)
|
||||
|
||||
## Layout
|
||||
|
||||
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.
|
||||
|
||||
- [Constrained Layout](/layout/constrained)
|
||||
- [Fixed Layout](/layout/fixed)
|
||||
- [Full Width Layout](/layout/fullWidth)
|
||||
- [Fill Layout](/layout/fill)
|
||||
|
||||
<hr />
|
||||
|
||||
## Placeholder
|
||||
|
||||
The `placeholder` property tells the image what to show while loading.
|
||||
|
||||
- [Blurred Placeholder](/placeholder/blurred)
|
||||
- [Dominant Color Placeholder](/placeholder/dominantColor)
|
||||
- [Traced SVG Placeholder](/placeholder/tracedSVG)
|
||||
- [No Placeholder](/placeholder/none)
|
||||
|
||||
<hr />
|
||||
|
||||
## Internal Image
|
||||
|
||||
The following is an example of a reference to an internal image in the `src` directory (`../images/elva-800w.jpg`).
|
||||
|
||||

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

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

|
||||
|
||||
<hr />
|
||||
|
||||
## Learn More
|
||||
|
||||
You can optionally configure many more things!
|
||||
|
||||
Checkout the [Astro ImageTools](https://astro-imagetools-docs.vercel.app/) documentation to learn more.
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||
---
|
||||
|
||||
<LayoutsLayout layout="constrained" importMetaUrl={import.meta.url} />
|
||||
---
|
||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||
---
|
||||
|
||||
<LayoutsLayout layout="constrained" importMetaUrl={import.meta.url} />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||
---
|
||||
|
||||
<LayoutsLayout layout="fill" importMetaUrl={import.meta.url} />
|
||||
---
|
||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||
---
|
||||
|
||||
<LayoutsLayout layout="fill" importMetaUrl={import.meta.url} />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||
---
|
||||
|
||||
<LayoutsLayout layout="fixed" importMetaUrl={import.meta.url} />
|
||||
---
|
||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||
---
|
||||
|
||||
<LayoutsLayout layout="fixed" importMetaUrl={import.meta.url} />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||
---
|
||||
|
||||
<LayoutsLayout layout="fullWidth" importMetaUrl={import.meta.url} />
|
||||
---
|
||||
import LayoutsLayout from "../../layouts/LayoutsLayout.astro";
|
||||
---
|
||||
|
||||
<LayoutsLayout layout="fullWidth" importMetaUrl={import.meta.url} />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||
---
|
||||
|
||||
<PlaceholderLayout placeholder="blurred" importMetaUrl={import.meta.url} />
|
||||
---
|
||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||
---
|
||||
|
||||
<PlaceholderLayout placeholder="blurred" importMetaUrl={import.meta.url} />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||
---
|
||||
|
||||
<PlaceholderLayout placeholder="dominantColor" importMetaUrl={import.meta.url} />
|
||||
---
|
||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||
---
|
||||
|
||||
<PlaceholderLayout placeholder="dominantColor" importMetaUrl={import.meta.url} />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||
---
|
||||
|
||||
<PlaceholderLayout placeholder="none" importMetaUrl={import.meta.url} />
|
||||
---
|
||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||
---
|
||||
|
||||
<PlaceholderLayout placeholder="none" importMetaUrl={import.meta.url} />
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
---
|
||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||
---
|
||||
|
||||
<PlaceholderLayout placeholder="tracedSVG" importMetaUrl={import.meta.url} />
|
||||
---
|
||||
import PlaceholderLayout from "../../layouts/PlaceholderLayout.astro";
|
||||
---
|
||||
|
||||
<PlaceholderLayout placeholder="tracedSVG" importMetaUrl={import.meta.url} />
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
main {
|
||||
padding: 1.5rem;
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
code::before {
|
||||
content: "`";
|
||||
}
|
||||
|
||||
code::after {
|
||||
content: "`";
|
||||
}
|
||||
|
||||
.view-source {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
main {
|
||||
padding: 1.5rem;
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
code::before {
|
||||
content: "`";
|
||||
}
|
||||
|
||||
code::after {
|
||||
content: "`";
|
||||
}
|
||||
|
||||
.view-source {
|
||||
position: fixed;
|
||||
top: 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