diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..140d005 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# https://editorconfig.org/ + +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml new file mode 100644 index 0000000..c0cc8b8 --- /dev/null +++ b/.github/workflows/publish.yaml @@ -0,0 +1,36 @@ +name: Publish Package + +on: + push: + tags: + - 'v**' + +jobs: + publish: + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + id-token: write + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + registry-url: 'https://registry.npmjs.org' + + - name: Use dependencies cache + uses: actions/cache@v4 + with: + path: ~/.npm + key: npm-${{ hashFiles('package-lock.json') }} + restore-keys: npm- + + - name: Install dependencies + run: npm ci --ignore-scripts --no-audit --no-fund + + - name: Publish package + run: npm publish --provenance + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f06235c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules +dist diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..c42da84 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +engine-strict = true diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..9e0e2bb --- /dev/null +++ b/changelog.md @@ -0,0 +1,6 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..372eacf --- /dev/null +++ b/contributing.md @@ -0,0 +1,19 @@ +# Contributing +To setup, follow these steps: + +```sh +git clone https://github.com/voorhoede/Image.git +cd Image +npm ci +``` + +## General Prerequisites +Node.js, [latest LTS is recommended](https://nodejs.org/en/about/releases/). + +### Tips + +## Publishing +1. Switch to the main branch and ensure it is up-to-date with remote. +1. Update `changelog.md` with relevant changes and stage with `git add`. +1. Run `npm version --force` with the appropiate version bump to include the changelog changes in the same commit. +1. This should automatically push the commit and new version tag to trigger publishing from CI. diff --git a/core/generate-src-set.mts b/core/generate-src-set.mts new file mode 100644 index 0000000..295d8f5 --- /dev/null +++ b/core/generate-src-set.mts @@ -0,0 +1,52 @@ +import type { ImageLoader } from './types.mts'; + +const deviceSizes = [640, 750, 828, 1080, 1200, 1920, 2048, 3840]; +const imageSizes = [16, 32, 48, 64, 96, 128, 256, 384]; +const allSizes = [...deviceSizes, ...imageSizes].sort((a, b) => a - b); + +export function generateSrcSet({ + loader, + src, + width, + quality, + sizes, +}: { + loader: ImageLoader; + src: string; + width: number; + quality: number; + sizes?: string; +}) { + const { widths, kind } = getWidths(width, sizes); + + return widths + .map( + (width, index) => + `${loader({ src, width, quality })} ${ + kind === 'w' ? width : index + 1 + }${kind}` + ) + .join(', '); +} + +function getWidths(width: number, sizes: string | undefined) { + if (sizes) { + // Find all the viewport percentage lengths + const percentSizes = [...sizes.matchAll(/(^|\s)(1?\d?\d)vw/g)].map( + (captureGroups) => + parseInt(captureGroups[2]) + ); + if (percentSizes.length) { + const smallestRatio = Math.min(...percentSizes) * 0.01; + return { + widths: allSizes.filter( + (size) => size >= deviceSizes[0] * smallestRatio + ), + kind: 'w', + }; + } + return { widths: allSizes, kind: 'w' }; + } + + return { widths: [width, width * 2], kind: 'x' }; +} diff --git a/core/types.mts b/core/types.mts new file mode 100644 index 0000000..5c2f8d3 --- /dev/null +++ b/core/types.mts @@ -0,0 +1,5 @@ +export type ImageLoader = (resolverProps: { + src: string; + width: number; + quality?: number; +}) => string; diff --git a/license.md b/license.md new file mode 100644 index 0000000..2e15f78 --- /dev/null +++ b/license.md @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/package.json b/package.json new file mode 100644 index 0000000..8100f85 --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "name": "Image", + "workspaces": [ + ], + "engines": { + "node": ">=20" + } +} diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..3dc9f55 --- /dev/null +++ b/readme.md @@ -0,0 +1,6 @@ +# Image component +Image component to fetch optimized images from a CDN. + +## Niceness +- No client-side JavaScript +- Generate `srcset` from native `sizes` attribute