├── .changeset ├── README.md └── config.json ├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .prettierignore ├── .prettierrc.js ├── LICENSE.md ├── README.md ├── apps ├── docs │ ├── .gitignore │ ├── .vitepress │ │ ├── config.ts │ │ └── theme │ │ │ ├── index.ts │ │ │ └── style.css │ ├── package.json │ └── src │ │ ├── assets │ │ ├── aurora.jpg │ │ ├── formats │ │ │ ├── aurora.avif │ │ │ ├── aurora.jpg │ │ │ └── aurora.webp │ │ └── lqip │ │ │ ├── blurhash.png │ │ │ ├── color.png │ │ │ ├── inline.png │ │ │ ├── original.png │ │ │ └── thumbhash.png │ │ ├── build │ │ ├── index.md │ │ ├── vite.md │ │ └── webpack.md │ │ ├── cdn │ │ ├── cloudinary.md │ │ ├── fastly.md │ │ ├── imgix.md │ │ ├── index.md │ │ └── netlify.md │ │ ├── credits.md │ │ ├── frameworks │ │ ├── ember.md │ │ ├── index.md │ │ ├── react.md │ │ ├── solid.md │ │ ├── svelte.md │ │ └── wc.md │ │ ├── history.md │ │ ├── index.md │ │ ├── intro │ │ ├── getting-started.md │ │ └── what.md │ │ ├── public │ │ ├── aurora-home.webp │ │ ├── aurora-original.jpg │ │ └── robots.txt │ │ ├── team.md │ │ └── usage │ │ ├── component.md │ │ ├── concepts.md │ │ ├── image-formats.md │ │ ├── local-images.md │ │ ├── lqip.md │ │ └── remote-images.md ├── ember-test-app │ ├── .editorconfig │ ├── .ember-cli │ ├── .gitignore │ ├── .prettierrc.js │ ├── .stylelintignore │ ├── .stylelintrc.js │ ├── .template-lintrc.js │ ├── .watchmanconfig │ ├── README.md │ ├── app │ │ ├── app.ts │ │ ├── components │ │ │ └── .gitkeep │ │ ├── config │ │ │ └── environment.d.ts │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── image.ts │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── images │ │ │ └── aurora.jpg │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ ├── cloudinary.hbs │ │ │ ├── fastly.hbs │ │ │ ├── image.hbs │ │ │ ├── imgix.hbs │ │ │ ├── index.hbs │ │ │ └── netlify.hbs │ ├── config │ │ ├── ember-cli-update.json │ │ ├── ember-try.js │ │ ├── environment.js │ │ ├── fastboot-testing.js │ │ ├── fastboot.js │ │ ├── optional-features.json │ │ └── targets.js │ ├── ember-cli-build.js │ ├── eslint.config.mjs │ ├── package.json │ ├── public │ │ ├── robots.txt │ │ └── test-image.jpg │ ├── testem.js │ ├── tests │ │ ├── fastboot │ │ │ ├── cloudinary-test.js │ │ │ ├── fastly-test.js │ │ │ ├── image-test.js │ │ │ ├── imgix-test.js │ │ │ └── netlify-test.js │ │ ├── helpers │ │ │ ├── image.helper.ts │ │ │ └── index.js │ │ ├── index.html │ │ ├── integration │ │ │ ├── .gitkeep │ │ │ ├── components │ │ │ │ └── responsive-image-test.gts │ │ │ └── helpers │ │ │ │ ├── responsive-image-cloudinary-test.gts │ │ │ │ ├── responsive-image-fastly-test.gts │ │ │ │ ├── responsive-image-imgix-test.gts │ │ │ │ ├── responsive-image-netlify-test.gts │ │ │ │ └── responsive-image-resolve-test.gts │ │ ├── test-helper.ts │ │ └── unit │ │ │ └── .gitkeep │ ├── tsconfig.json │ └── types │ │ ├── glint.d.ts │ │ └── global.d.ts ├── ember-vite │ ├── .editorconfig │ ├── .ember-cli │ ├── .github │ │ └── workflows │ │ │ └── ci.yml │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc.cjs │ ├── .stylelintignore │ ├── .stylelintrc.js │ ├── .template-lintrc.js │ ├── .watchmanconfig │ ├── README.md │ ├── app │ │ ├── app.js │ │ ├── components │ │ │ └── .gitkeep │ │ ├── config │ │ │ └── environment.js │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── index.js │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── images │ │ │ └── aurora.jpg │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ └── index.hbs │ ├── babel.config.cjs │ ├── config │ │ ├── ember-cli-update.json │ │ ├── environment.js │ │ ├── optional-features.json │ │ └── targets.js │ ├── ember-cli-build.js │ ├── eslint.config.mjs │ ├── index.html │ ├── package.json │ ├── playwright.config.mts │ ├── public │ │ └── robots.txt │ ├── testem.js │ ├── tests │ │ └── responsive-image.spec.ts │ ├── tsconfig.json │ ├── turbo.json │ └── vite.config.mjs ├── ember-webpack │ ├── .editorconfig │ ├── .ember-cli │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc.js │ ├── .stylelintignore │ ├── .stylelintrc.js │ ├── .template-lintrc.js │ ├── .watchmanconfig │ ├── README.md │ ├── app │ │ ├── app.ts │ │ ├── components │ │ │ └── .gitkeep │ │ ├── config │ │ │ └── environment.d.ts │ │ ├── controllers │ │ │ ├── .gitkeep │ │ │ └── index.ts │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── images │ │ │ └── aurora.jpg │ │ ├── index.html │ │ ├── models │ │ │ └── .gitkeep │ │ ├── router.js │ │ ├── routes │ │ │ └── .gitkeep │ │ ├── styles │ │ │ └── app.css │ │ └── templates │ │ │ ├── application.hbs │ │ │ └── index.hbs │ ├── config │ │ ├── ember-cli-update.json │ │ ├── environment.js │ │ ├── fastboot.js │ │ ├── optional-features.json │ │ └── targets.js │ ├── ember-cli-build.js │ ├── eslint.config.mjs │ ├── package.json │ ├── playwright.config.mts │ ├── playwright │ │ └── responsive-image.spec.ts │ ├── public │ │ └── robots.txt │ ├── testem.js │ ├── tests │ │ ├── helpers │ │ │ └── index.js │ │ ├── index.html │ │ └── test-helper.ts │ ├── tsconfig.json │ └── types │ │ ├── glint.d.ts │ │ └── global.d.ts ├── react-vite │ ├── .gitignore │ ├── README.md │ ├── eslint.config.js │ ├── index.html │ ├── package.json │ ├── playwright.config.ts │ ├── public │ │ └── vite.svg │ ├── src │ │ ├── global.d.ts │ │ ├── images │ │ │ └── aurora.jpg │ │ ├── main.tsx │ │ └── vite-env.d.ts │ ├── tests │ │ └── responsive-image.spec.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ └── vite.config.ts ├── solid-start │ ├── .gitignore │ ├── README.md │ ├── app.config.ts │ ├── package.json │ ├── playwright.config.ts │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── app.css │ │ ├── app.tsx │ │ ├── entry-client.tsx │ │ ├── entry-server.tsx │ │ ├── global.d.ts │ │ ├── images │ │ │ └── aurora.jpg │ │ └── routes │ │ │ ├── [...404].tsx │ │ │ └── index.tsx │ ├── tests │ │ └── responsive-image.spec.ts │ └── tsconfig.json ├── svelte-kit │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── eslint.config.js │ ├── package.json │ ├── playwright.config.ts │ ├── src │ │ ├── app.d.ts │ │ ├── app.html │ │ ├── images │ │ │ └── aurora.jpg │ │ ├── lib │ │ │ └── index.ts │ │ ├── routes │ │ │ └── +page.svelte │ │ └── types │ │ │ └── global.d.ts │ ├── static │ │ └── favicon.png │ ├── svelte.config.js │ ├── tests │ │ └── responsive-image.spec.ts │ ├── tsconfig.json │ └── vite.config.ts └── wc-lit │ ├── .gitignore │ ├── CHANGELOG.md │ ├── index.html │ ├── package.json │ ├── playwright.config.ts │ ├── src │ ├── app.ts │ ├── global.d.ts │ ├── images │ │ └── aurora.jpg │ ├── index.css │ └── vite-env.d.ts │ ├── tests │ └── responsive-image.spec.ts │ ├── tsconfig.json │ └── vite.config.js ├── netlify.toml ├── package.json ├── packages ├── -internals │ ├── .gitignore │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── eslint │ │ │ ├── base.js │ │ │ ├── browser.js │ │ │ ├── index.js │ │ │ ├── node-cjs.js │ │ │ └── node-esm.js │ │ └── playwright │ │ │ ├── __screenshots__ │ │ │ ├── LQIP-blurhash-1.png │ │ │ ├── LQIP-blurhash-2.png │ │ │ ├── LQIP-color-1.png │ │ │ ├── LQIP-color-2.png │ │ │ ├── LQIP-inline-1.png │ │ │ ├── LQIP-inline-2.png │ │ │ ├── LQIP-thumbhash-1.png │ │ │ ├── LQIP-thumbhash-2.png │ │ │ ├── fixed-layout-1.png │ │ │ ├── fixed-layout-w-aspect-1.png │ │ │ ├── index.js │ │ │ └── responsive-layout-1.png │ │ │ ├── helpers.ts │ │ │ └── index.ts │ └── tsconfig.json ├── build-utils │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── index.ts │ │ ├── lqip.ts │ │ ├── resize.ts │ │ ├── serialize.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── tests │ │ ├── serialize.test.ts │ │ └── utils.test.ts │ ├── tsconfig.json │ ├── types │ │ └── base-n.d.ts │ └── vitest.config.ts ├── cdn │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.json │ ├── eslint.config.mjs │ ├── package.json │ ├── rollup.config.mjs │ ├── src │ │ ├── cloudinary.ts │ │ ├── fastly.ts │ │ ├── imgix.ts │ │ ├── index.ts │ │ ├── netlify.ts │ │ └── types.ts │ ├── tests │ │ ├── cloudinary.test.ts │ │ ├── fastly.test.ts │ │ ├── imgix.test.ts │ │ └── netlify.test.ts │ └── tsconfig.json ├── core │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.json │ ├── eslint.config.mjs │ ├── package.json │ ├── rollup.config.mjs │ ├── src │ │ ├── blurhash │ │ │ ├── decode.ts │ │ │ └── ssr.ts │ │ ├── config.ts │ │ ├── debug.ts │ │ ├── env.ts │ │ ├── index.ts │ │ ├── match.ts │ │ ├── resolve.ts │ │ ├── thumbhash │ │ │ ├── decode.ts │ │ │ └── ssr.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── tests │ │ ├── config.test.ts │ │ ├── env.test.ts │ │ ├── match.test.ts │ │ └── resolve.test.ts │ └── tsconfig.json ├── ember │ ├── .gitignore │ ├── .prettierrc.cjs │ ├── .template-lintrc.cjs │ ├── CHANGELOG.md │ ├── README.md │ ├── addon-main.cjs │ ├── babel.config.json │ ├── eslint.config.mjs │ ├── package.json │ ├── rollup.config.mjs │ ├── src │ │ ├── blurhash.ts │ │ ├── components │ │ │ ├── responsive-image.css │ │ │ └── responsive-image.gts │ │ ├── helpers │ │ │ ├── responsive-image-cloudinary.ts │ │ │ ├── responsive-image-fastly.ts │ │ │ ├── responsive-image-imgix.ts │ │ │ ├── responsive-image-netlify.ts │ │ │ └── responsive-image-resolve.ts │ │ ├── index.ts │ │ ├── template-registry.ts │ │ └── thumbhash.ts │ ├── tsconfig.json │ └── unpublished-development-types │ │ └── glint.d.ts ├── react │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.json │ ├── eslint.config.mjs │ ├── package.json │ ├── rollup.config.mjs │ ├── src │ │ ├── index.tsx │ │ ├── responsive-image.css │ │ └── responsive-image.tsx │ ├── tests │ │ ├── client.test.tsx │ │ ├── image.helper.ts │ │ └── server.test.tsx │ ├── tsconfig.json │ └── vitest.config.ts ├── solid │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── babel.config.json │ ├── eslint.config.mjs │ ├── package.json │ ├── rollup.config.mjs │ ├── src │ │ ├── index.tsx │ │ ├── responsive-image.css │ │ └── responsive-image.tsx │ ├── test-assets │ │ └── test-image.jpg │ ├── tests │ │ ├── client.test.tsx │ │ ├── image.helper.ts │ │ └── server.test.tsx │ ├── tsconfig.json │ └── vitest.config.ts ├── svelte │ ├── .gitignore │ ├── .npmrc │ ├── .prettierignore │ ├── .prettierrc │ ├── CHANGELOG.md │ ├── README.md │ ├── eslint.config.js │ ├── package.json │ ├── src │ │ └── lib │ │ │ ├── index.ts │ │ │ └── responsive-image.svelte │ ├── svelte.config.js │ ├── test-assets │ │ └── test-image.jpg │ ├── tests │ │ ├── image.helper.ts │ │ └── responsive-image.test.svelte.ts │ ├── tsconfig.json │ ├── turbo.json │ └── vite.config.ts ├── vite-plugin │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── export.ts │ │ ├── index.ts │ │ ├── loader.ts │ │ ├── lqip │ │ │ ├── blurhash.ts │ │ │ ├── color-css.ts │ │ │ ├── color.ts │ │ │ ├── inline-css.ts │ │ │ ├── inline.ts │ │ │ └── thumbhash.ts │ │ ├── resize.ts │ │ ├── serve.ts │ │ ├── types.ts │ │ └── utils.ts │ ├── tests │ │ ├── __image_snapshots__ │ │ │ ├── index-test-ts-custom-loader-options-are-supported-2-snap.png │ │ │ ├── index-test-ts-custom-loader-options-are-supported-3-snap.png │ │ │ ├── index-test-ts-custom-query-params-are-supported-2-snap.png │ │ │ ├── index-test-ts-custom-query-params-are-supported-3-snap.png │ │ │ ├── index-test-ts-different-aspect-ratio-2-snap.png │ │ │ ├── index-test-ts-different-aspect-ratio-3-snap.png │ │ │ ├── index-test-ts-imagetools-params-are-supported-2-snap.png │ │ │ ├── index-test-ts-imagetools-params-are-supported-3-snap.png │ │ │ └── index-test-ts-it-produces-expected-output-2-snap.png │ │ ├── __snapshots__ │ │ │ └── index.test.ts.snap │ │ ├── fixtures │ │ │ ├── image.jpg │ │ │ └── index.js │ │ ├── index.test.ts │ │ └── utils.ts │ ├── tsconfig.json │ ├── types │ │ └── base-n.d.ts │ └── vitest.config.ts ├── wc │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── responsive-image.ts │ ├── test-assets │ │ └── test-image.jpg │ ├── test │ │ ├── image-helper.ts │ │ └── responsive-image.test.ts │ ├── tsconfig.json │ ├── turbo.json │ └── vitest.config.ts └── webpack │ ├── .gitignore │ ├── CHANGELOG.md │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ ├── export.ts │ ├── index.ts │ ├── loader.ts │ ├── lqip │ │ ├── blurhash.ts │ │ ├── color-css.ts │ │ ├── color.ts │ │ ├── inline-css.ts │ │ ├── inline.ts │ │ └── thumbhash.ts │ ├── resize.ts │ ├── types.ts │ └── utils.ts │ ├── tests │ ├── __image_snapshots__ │ │ ├── index-test-ts-custom-loader-options-are-supported-2-snap.png │ │ ├── index-test-ts-custom-loader-options-are-supported-3-snap.png │ │ ├── index-test-ts-custom-query-params-are-supported-2-snap.png │ │ ├── index-test-ts-custom-query-params-are-supported-3-snap.png │ │ ├── index-test-ts-different-aspect-ratio-2-snap.png │ │ ├── index-test-ts-different-aspect-ratio-3-snap.png │ │ ├── index-test-ts-imagetools-params-are-supported-2-snap.png │ │ ├── index-test-ts-imagetools-params-are-supported-3-snap.png │ │ └── index-test-ts-it-produces-expected-output-2-snap.png │ ├── __snapshots__ │ │ └── index.test.ts.snap │ ├── compiler.ts │ ├── fixtures │ │ └── image.jpg │ └── index.test.ts │ ├── tsconfig.json │ ├── turbo.json │ ├── types │ ├── base-n.d.ts │ └── loader-utils.d.ts │ └── vitest.config.ts ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── renovate.json ├── scripts └── skip-netlify-build.js └── turbo.json /.changeset/README.md: -------------------------------------------------------------------------------- 1 | # Changesets 2 | 3 | Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works 4 | with multi-package repos, or single-package repos to help you version and publish your code. You can 5 | find the full documentation for it [in our repository](https://github.com/changesets/changesets) 6 | 7 | We have a quick list of common questions to get you started engaging with this project in 8 | [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md) 9 | -------------------------------------------------------------------------------- /.changeset/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", 3 | "changelog": [ 4 | "@changesets/changelog-github", 5 | { "repo": "simonihmig/responsive-image" } 6 | ], 7 | "commit": false, 8 | "fixed": [], 9 | "linked": [], 10 | "access": "public", 11 | "baseBranch": "main", 12 | "updateInternalDependencies": "patch", 13 | "ignore": ["ember-webpack", "ember-vite", "ember-test-app", "wc-lit", "docs", "react-vite", "solid-start", "svelte-kit"] 14 | } 15 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | concurrency: ${{ github.workflow }}-${{ github.ref }} 9 | 10 | jobs: 11 | release: 12 | name: Release 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout Repo 16 | uses: actions/checkout@v4 17 | - uses: wyvox/action-setup-pnpm@v3 18 | with: 19 | node-version: 20 20 | 21 | - name: Create Release Pull Request or Publish to npm 22 | id: changesets 23 | uses: changesets/action@v1 24 | with: 25 | publish: pnpm changeset publish 26 | env: 27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 28 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | dist/ 5 | 6 | # dependencies 7 | node_modules/ 8 | 9 | # misc 10 | /.env* 11 | /.pnp* 12 | /.sass-cache 13 | .eslintcache 14 | /connect.lock 15 | /coverage/ 16 | /libpeerconnection.log 17 | /npm-debug.log* 18 | /testem.log 19 | /yarn-error.log 20 | .DS_Store 21 | 22 | # ember-try 23 | /.node_modules.ember-try/ 24 | /package.json.ember-try 25 | /yarn.lock.ember-try 26 | 27 | # turbo 28 | .turbo 29 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | 7 | # misc 8 | /coverage/ 9 | !.* 10 | .*/ 11 | 12 | # ember-try 13 | /.node_modules.ember-try/ 14 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | singleQuote: true, 5 | }; 6 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /apps/docs/.gitignore: -------------------------------------------------------------------------------- 1 | .vitepress/cache 2 | -------------------------------------------------------------------------------- /apps/docs/.vitepress/theme/index.ts: -------------------------------------------------------------------------------- 1 | // https://vitepress.dev/guide/custom-theme 2 | import { h } from 'vue' 3 | import type { Theme } from 'vitepress' 4 | import DefaultTheme from 'vitepress/theme' 5 | import './style.css' 6 | 7 | export default { 8 | extends: DefaultTheme, 9 | Layout: () => { 10 | return h(DefaultTheme.Layout, null, { 11 | // https://vitepress.dev/guide/extending-default-theme#layout-slots 12 | }) 13 | }, 14 | enhanceApp({ app, router, siteData }) { 15 | // ... 16 | } 17 | } satisfies Theme 18 | -------------------------------------------------------------------------------- /apps/docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "description": "VitePress app powering the docs", 6 | "type": "module", 7 | "scripts": { 8 | "test": "vitepress build", 9 | "dev": "vitepress dev", 10 | "build": "vitepress build", 11 | "preview": "vitepress preview" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "vitepress": "1.6.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/docs/src/assets/aurora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/assets/aurora.jpg -------------------------------------------------------------------------------- /apps/docs/src/assets/formats/aurora.avif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/assets/formats/aurora.avif -------------------------------------------------------------------------------- /apps/docs/src/assets/formats/aurora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/assets/formats/aurora.jpg -------------------------------------------------------------------------------- /apps/docs/src/assets/formats/aurora.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/assets/formats/aurora.webp -------------------------------------------------------------------------------- /apps/docs/src/assets/lqip/blurhash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/assets/lqip/blurhash.png -------------------------------------------------------------------------------- /apps/docs/src/assets/lqip/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/assets/lqip/color.png -------------------------------------------------------------------------------- /apps/docs/src/assets/lqip/inline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/assets/lqip/inline.png -------------------------------------------------------------------------------- /apps/docs/src/assets/lqip/original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/assets/lqip/original.png -------------------------------------------------------------------------------- /apps/docs/src/assets/lqip/thumbhash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/assets/lqip/thumbhash.png -------------------------------------------------------------------------------- /apps/docs/src/build/index.md: -------------------------------------------------------------------------------- 1 | # Building local images 2 | 3 | If you have static images that are part of your local codebase (checked into your git repo), you can get these processed as part of your app's build using one of the provided build plugins, depending on which build system your app is using: 4 | 5 | - [vite](./vite) 6 | - [webpack](./webpack) 7 | -------------------------------------------------------------------------------- /apps/docs/src/cdn/index.md: -------------------------------------------------------------------------------- 1 | # Image CDNs 2 | 3 | This library supports multiple image CDNs to render [remote images](../usage/remote-images.md) with the image component. 4 | 5 | With image CDNs the image processing is offloaded to the Cloud. This allows for _dynamic_ image processing, in cases where your images are not available at build-time. For example you could have an API endpoint (that hydrates a client-side model) refer to a raw (large, unprocessed) image, and use an image CDN as a proxy to scale, optimize and deliver that image as needed, at _runtime_. 6 | 7 | The following image CDNs are supported by the `@responsive-image/cdn` package out of the box: 8 | 9 | - [Cloudinary](./cloudinary.md) 10 | - [Fastly](./fastly.md) 11 | - [Imgix](./imgix.md) 12 | - [Netlify](./netlify.md) 13 | -------------------------------------------------------------------------------- /apps/docs/src/credits.md: -------------------------------------------------------------------------------- 1 | # Credit where credit is due... 2 | 3 | I would like to thank the following people for their direct or indirect contributions to this project: 4 | 5 | - [Andreas Schacht](https://github.com/andreasschacht) for his fantastic work on the initial version of `ember-responsive-image`, the predecessor of this project. 6 | 7 | - [Lovell Fuller](https://lovell.info/) for [sharp](https://github.com/lovell/sharp), the library that is powering all image processing of local images. 8 | 9 | - [Jonas Kruckenberg](https://jonaskruckenberg.de/) for [imagetools](https://github.com/JonasKruckenberg/imagetools) to support (query) parameter based image adjustments and effects. 10 | 11 | - [Malte Ubl](https://www.industrialempathy.com/about/) for his blog post [Maximally optimizing image loading for the web in 2021](https://www.industrialempathy.com/posts/image-optimizations/) that inspired many of the image optimization techniques used here, and [Jake Archibald](https://jakearchibald.com/) for his [anatomy of responsive images](https://jakearchibald.com/2015/anatomy-of-responsive-images/) and [AVIF analysis](https://jakearchibald.com/2020/avif-has-landed/). 12 | 13 | - Key visual by [Jonatan Pie](https://unsplash.com/@r3dmax?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash) on Unsplash 14 | -------------------------------------------------------------------------------- /apps/docs/src/frameworks/index.md: -------------------------------------------------------------------------------- 1 | # Frontend frameworks 2 | 3 | The [image component](../usage/component.md) and other utilities are provided with framework specific packages: 4 | 5 | - [Ember](./ember.md) 6 | - [React](./react.md) 7 | - [Solid](./solid.md) 8 | - [Svelte](./svelte.md) 9 | - [Web Component](./wc.md) (built with Lit, but usable with any framework) 10 | 11 | > [!NOTE] 12 | > Multi-framework support is still WIP. More supported frameworks will be added here over time... 13 | -------------------------------------------------------------------------------- /apps/docs/src/intro/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | To get started, install at least the package for your [frontend framework](../frameworks/index.md), and if you want to process [local images](../usage/local-images.md) one of the [build plugins](../build/index.md). Please refer to the linked guides for more detailed instructions! 4 | 5 | For example, if you use Ember.js and webpack, then install these packages: 6 | 7 | ::: code-group 8 | 9 | ```bash [npm] 10 | npm install @responsive-image/ember @responsive-image/webpack 11 | ``` 12 | 13 | ```bash [yarn] 14 | yarn add @responsive-image/ember @responsive-image/webpack 15 | ``` 16 | 17 | ```bash [pnpm] 18 | pnpm add @responsive-image/ember @responsive-image/webpack 19 | ``` 20 | 21 | ::: 22 | 23 | Now let's dive a bit deeper into the [core concepts](../usage/concepts.md) for responsive images! 24 | -------------------------------------------------------------------------------- /apps/docs/src/public/aurora-home.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/public/aurora-home.webp -------------------------------------------------------------------------------- /apps/docs/src/public/aurora-original.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/docs/src/public/aurora-original.jpg -------------------------------------------------------------------------------- /apps/docs/src/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /apps/docs/src/team.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: page 3 | --- 4 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /apps/docs/src/usage/remote-images.md: -------------------------------------------------------------------------------- 1 | # Remote images 2 | 3 | Besides local images, provider functions allow to load remote images, i.e. images that are not static parts of your local project. The most common use case is to load responsive images from an [image CDN](https://web.dev/image-cdns/), that is then used to offload all image processing to the Cloud. This allows for _dynamic_ image processing, in cases where your images are not available at build-time. For example you could have an API endpoint refer to the raw (large, unprocessed) image, and use 4 | an image CDN as a proxy to scale, optimize and deliver that image as needed, at _runtime_. 5 | 6 | See the [image CDNs](../cdn/index.md) section for information on loading remote images from supported image CDNs. 7 | 8 | > [!TIP] 9 | > Besides the built-in support for selected image CDNs, you can also create your own custom integration by writing your own provider functions. The only requirement for the [image component](./component.md) is to receive an `ImageData` object as its `src` argument. See the [Concepts page](./concepts.md#image-source) for more information. 10 | -------------------------------------------------------------------------------- /apps/ember-test-app/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /apps/ember-test-app/.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript 4 | rather than JavaScript by default, when a TypeScript version of a given blueprint is available. 5 | */ 6 | "isTypeScriptProject": true 7 | } 8 | -------------------------------------------------------------------------------- /apps/ember-test-app/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /node_modules/ 7 | 8 | # misc 9 | /.env* 10 | /.pnp* 11 | /.eslintcache 12 | /coverage/ 13 | /npm-debug.log* 14 | /testem.log 15 | /yarn-error.log 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /npm-shrinkwrap.json.ember-try 20 | /package.json.ember-try 21 | /package-lock.json.ember-try 22 | /yarn.lock.ember-try 23 | 24 | # broccoli-debug 25 | /DEBUG/ 26 | -------------------------------------------------------------------------------- /apps/ember-test-app/.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | plugins: ['prettier-plugin-ember-template-tag'], 5 | overrides: [ 6 | { 7 | files: '*.{js,gjs,ts,gts,mjs,mts,cjs,cts}', 8 | options: { 9 | singleQuote: true, 10 | }, 11 | }, 12 | { 13 | files: '*.{gjs,gts}', 14 | options: { 15 | singleQuote: true, 16 | templateSingleQuote: false, 17 | }, 18 | }, 19 | ], 20 | }; 21 | -------------------------------------------------------------------------------- /apps/ember-test-app/.stylelintignore: -------------------------------------------------------------------------------- 1 | # unconventional files 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | 7 | # addons 8 | /.node_modules.ember-try/ 9 | -------------------------------------------------------------------------------- /apps/ember-test-app/.stylelintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: ['stylelint-config-standard', 'stylelint-prettier/recommended'], 5 | }; 6 | -------------------------------------------------------------------------------- /apps/ember-test-app/.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | }; 6 | -------------------------------------------------------------------------------- /apps/ember-test-app/.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["dist"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/ember-test-app/README.md: -------------------------------------------------------------------------------- 1 | # ember-test-app 2 | 3 | Integration tests for Ember. 4 | 5 | ## Prerequisites 6 | 7 | You will need the following things properly installed on your computer. 8 | 9 | - [Git](https://git-scm.com/) 10 | - [Node.js](https://nodejs.org/) 11 | - [pnpm](https://pnpm.io/) 12 | - [Ember CLI](https://cli.emberjs.com/release/) 13 | - [Google Chrome](https://google.com/chrome/) 14 | 15 | ## Installation 16 | 17 | - `git clone ` this repository 18 | - `cd apps/ember-test-app` 19 | - `pnpm install` 20 | 21 | ## Running / Development 22 | 23 | - `pnpm start` 24 | - Visit your app at [http://localhost:4200](http://localhost:4200). 25 | - Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests). 26 | 27 | ### Code Generators 28 | 29 | Make use of the many generators for code, try `ember help generate` for more details 30 | 31 | ### Running Tests 32 | 33 | - `pnpm test` 34 | - `pnpm test:ember --server` 35 | 36 | ### Linting 37 | 38 | - `pnpm lint` 39 | - `pnpm lint:fix` 40 | 41 | ### Building 42 | 43 | - `pnpm ember build` (development) 44 | - `pnpm build` (production) 45 | 46 | ## Further Reading / Useful Links 47 | 48 | - [ember.js](https://emberjs.com/) 49 | - [ember-cli](https://cli.emberjs.com/release/) 50 | - Development Browser Extensions 51 | - [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) 52 | - [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) 53 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/app.ts: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'ember-test-app/config/environment'; 5 | import { setConfig } from '@responsive-image/core'; 6 | import type { Config } from '@responsive-image/cdn'; 7 | 8 | export default class App extends Application { 9 | modulePrefix = config.modulePrefix; 10 | podModulePrefix = config.podModulePrefix; 11 | Resolver = Resolver; 12 | } 13 | 14 | setConfig('cdn', { 15 | cloudinary: { 16 | cloudName: 'responsive-image', 17 | }, 18 | fastly: { 19 | domain: 'www.fastly.io', 20 | }, 21 | imgix: { 22 | domain: 'responsive-image.imgix.net', 23 | }, 24 | netlify: { 25 | domain: 'responsive-image.dev', 26 | }, 27 | }); 28 | 29 | loadInitializers(App, config.modulePrefix); 30 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-test-app/app/components/.gitkeep -------------------------------------------------------------------------------- /apps/ember-test-app/app/config/environment.d.ts: -------------------------------------------------------------------------------- 1 | export default config; 2 | 3 | /** 4 | * Type declarations for 5 | * import config from 'my-app/config/environment' 6 | */ 7 | declare const config: { 8 | environment: string; 9 | modulePrefix: string; 10 | podModulePrefix: string; 11 | locationType: 'history' | 'hash' | 'none' | 'auto'; 12 | rootURL: string; 13 | APP: Record; 14 | }; 15 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-test-app/app/controllers/.gitkeep -------------------------------------------------------------------------------- /apps/ember-test-app/app/controllers/image.ts: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import testImage from 'ember-test-app/images/aurora.jpg?widths=50,100,640&formats=original,webp,avif&responsive'; 3 | import testImageLqipInline from 'ember-test-app/images/aurora.jpg?lqip=inline&widths=50,100,640&responsive'; 4 | import testImageLqipColor from 'ember-test-app/images/aurora.jpg?lqip=color&widths=50,100,640&responsive'; 5 | import testImageLqipBlurhash from 'ember-test-app/images/aurora.jpg?lqip=blurhash&widths=50,100,640&responsive'; 6 | import testImageLqipThumbhash from 'ember-test-app/images/aurora.jpg?lqip=thumbhash&widths=50,100,640&responsive'; 7 | 8 | export default class IndexController extends Controller { 9 | testImage = testImage; 10 | testImageLqipInline = testImageLqipInline; 11 | testImageLqipColor = testImageLqipColor; 12 | testImageLqipBlurhash = testImageLqipBlurhash; 13 | testImageLqipThumbhash = testImageLqipThumbhash; 14 | } 15 | 16 | export const raw = true; 17 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-test-app/app/helpers/.gitkeep -------------------------------------------------------------------------------- /apps/ember-test-app/app/images/aurora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-test-app/app/images/aurora.jpg -------------------------------------------------------------------------------- /apps/ember-test-app/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{content-for "head"}} 9 | 10 | 11 | 16 | 20 | 21 | {{content-for "head-footer"}} 22 | 23 | 24 | {{content-for "body"}} 25 | 26 | 30 | 31 | 32 | 33 | {{content-for "body-footer"}} 34 | 35 | 36 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-test-app/app/models/.gitkeep -------------------------------------------------------------------------------- /apps/ember-test-app/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'ember-test-app/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () { 10 | this.route('image'); 11 | this.route('cloudinary'); 12 | this.route('imgix'); 13 | this.route('netlify'); 14 | this.route('fastly'); 15 | }); 16 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-test-app/app/routes/.gitkeep -------------------------------------------------------------------------------- /apps/ember-test-app/app/styles/app.css: -------------------------------------------------------------------------------- 1 | /* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */ 2 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "ember-test-app"}} 2 | 3 | {{outlet}} -------------------------------------------------------------------------------- /apps/ember-test-app/app/templates/cloudinary.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/templates/fastly.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/templates/image.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | 15 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/templates/imgix.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/ember-test-app/app/templates/index.hbs: -------------------------------------------------------------------------------- 1 | Visit 2 | tests. -------------------------------------------------------------------------------- /apps/ember-test-app/app/templates/netlify.hbs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/ember-test-app/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "6.2.3", 7 | "blueprints": [ 8 | { 9 | "name": "app", 10 | "outputRepo": "https://github.com/ember-cli/ember-new-output", 11 | "codemodsSource": "ember-app-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": ["--no-welcome", "--pnpm"] 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /apps/ember-test-app/config/ember-try.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const getChannelURL = require('ember-source-channel-url'); 4 | const { embroiderSafe, embroiderOptimized } = require('@embroider/test-setup'); 5 | 6 | module.exports = async function () { 7 | return { 8 | usePnpm: true, 9 | scenarios: [ 10 | { 11 | name: 'ember-lts-4.12', 12 | npm: { 13 | devDependencies: { 14 | 'ember-source': '~4.12.0', 15 | }, 16 | }, 17 | }, 18 | { 19 | name: 'ember-lts-5.4', 20 | npm: { 21 | devDependencies: { 22 | 'ember-source': '~5.4.0', 23 | }, 24 | }, 25 | }, 26 | { 27 | name: 'ember-lts-5.8', 28 | npm: { 29 | devDependencies: { 30 | 'ember-source': '~5.8.0', 31 | }, 32 | }, 33 | }, 34 | { 35 | name: 'ember-release', 36 | npm: { 37 | devDependencies: { 38 | 'ember-source': await getChannelURL('release'), 39 | }, 40 | }, 41 | }, 42 | { 43 | name: 'ember-beta', 44 | npm: { 45 | devDependencies: { 46 | 'ember-source': await getChannelURL('beta'), 47 | }, 48 | }, 49 | }, 50 | { 51 | name: 'ember-canary', 52 | npm: { 53 | devDependencies: { 54 | 'ember-source': await getChannelURL('canary'), 55 | }, 56 | }, 57 | }, 58 | embroiderSafe(), 59 | embroiderOptimized(), 60 | ], 61 | }; 62 | }; 63 | -------------------------------------------------------------------------------- /apps/ember-test-app/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | const ENV = { 5 | modulePrefix: 'ember-test-app', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'history', 9 | EmberENV: { 10 | EXTEND_PROTOTYPES: false, 11 | FEATURES: { 12 | // Here you can enable experimental features on an ember canary build 13 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 14 | }, 15 | }, 16 | 17 | APP: { 18 | // Here you can pass flags/options to your application instance 19 | // when it is created 20 | }, 21 | }; 22 | 23 | if (environment === 'development') { 24 | // ENV.APP.LOG_RESOLVER = true; 25 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 26 | // ENV.APP.LOG_TRANSITIONS = true; 27 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 28 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 29 | } 30 | 31 | if (environment === 'test') { 32 | // Testem prefers this... 33 | ENV.locationType = 'none'; 34 | 35 | // keep test console output quieter 36 | ENV.APP.LOG_ACTIVE_GENERATION = false; 37 | ENV.APP.LOG_VIEW_LOOKUPS = false; 38 | 39 | ENV.APP.rootElement = '#ember-testing'; 40 | ENV.APP.autoboot = false; 41 | } 42 | 43 | if (environment === 'production') { 44 | // here you can enable a production-specific feature 45 | } 46 | 47 | return ENV; 48 | }; 49 | -------------------------------------------------------------------------------- /apps/ember-test-app/config/fastboot-testing.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function () { 4 | return { 5 | buildSandboxGlobals(defaultGlobals) { 6 | return { ...defaultGlobals, URL, URLSearchParams }; 7 | }, 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /apps/ember-test-app/config/fastboot.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function () { 4 | return { 5 | buildSandboxGlobals(defaultGlobals) { 6 | return { ...defaultGlobals, URL, URLSearchParams }; 7 | }, 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /apps/ember-test-app/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true, 6 | "no-implicit-route-model": true 7 | } 8 | -------------------------------------------------------------------------------- /apps/ember-test-app/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions', 7 | ]; 8 | 9 | module.exports = { 10 | browsers, 11 | node: 'current', 12 | }; 13 | -------------------------------------------------------------------------------- /apps/ember-test-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /apps/ember-test-app/public/test-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-test-app/public/test-image.jpg -------------------------------------------------------------------------------- /apps/ember-test-app/testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_start_timeout: 120, 9 | browser_args: { 10 | Chrome: { 11 | ci: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.CI ? '--no-sandbox' : null, 14 | '--headless', 15 | '--disable-dev-shm-usage', 16 | '--disable-software-rasterizer', 17 | '--mute-audio', 18 | '--remote-debugging-port=0', 19 | '--window-size=1440,900', 20 | ].filter(Boolean), 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /apps/ember-test-app/tests/fastboot/cloudinary-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { 3 | setup, 4 | visit /* mockServer */, 5 | } from 'ember-cli-fastboot-testing/test-support'; 6 | 7 | module('FastBoot | Cloudinary', function (hooks) { 8 | setup(hooks); 9 | 10 | test('it renders an image', async function (assert) { 11 | await visit('/cloudinary'); 12 | 13 | assert.dom('img[data-test-image]').exists(); 14 | assert 15 | .dom('img[data-test-image]') 16 | .hasAttribute( 17 | 'src', 18 | 'https://res.cloudinary.com/responsive-image/image/upload/w_3840,c_limit,q_auto/f_auto/aurora-original_w0sk6h', 19 | ); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /apps/ember-test-app/tests/fastboot/fastly-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { 3 | setup, 4 | visit /* mockServer */, 5 | } from 'ember-cli-fastboot-testing/test-support'; 6 | 7 | module('FastBoot | Fastly', function (hooks) { 8 | setup(hooks); 9 | 10 | test('it renders an image', async function (assert) { 11 | await visit('/fastly'); 12 | 13 | assert.dom('img[data-test-image]').exists(); 14 | assert 15 | .dom('img[data-test-image]') 16 | .hasAttribute( 17 | 'src', 18 | 'https://www.fastly.io/image.webp?format=auto&width=3840', 19 | ); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /apps/ember-test-app/tests/fastboot/imgix-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { 3 | setup, 4 | visit /* mockServer */, 5 | } from 'ember-cli-fastboot-testing/test-support'; 6 | 7 | module('FastBoot | Imgix', function (hooks) { 8 | setup(hooks); 9 | 10 | test('it renders an image', async function (assert) { 11 | await visit('/imgix'); 12 | 13 | assert.dom('img[data-test-image]').exists(); 14 | assert 15 | .dom('img[data-test-image]') 16 | .hasAttribute( 17 | 'src', 18 | 'https://responsive-image.imgix.net/aurora-original.jpg?auto=format&w=3840&fit=max', 19 | ); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /apps/ember-test-app/tests/fastboot/netlify-test.js: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { 3 | setup, 4 | visit /* mockServer */, 5 | } from 'ember-cli-fastboot-testing/test-support'; 6 | 7 | module('FastBoot | Netlify', function (hooks) { 8 | setup(hooks); 9 | 10 | test('it renders an image', async function (assert) { 11 | await visit('/netlify'); 12 | 13 | assert.dom('img[data-test-image]').exists(); 14 | assert 15 | .dom('img[data-test-image]') 16 | .hasAttribute( 17 | 'src', 18 | 'https://responsive-image.dev/.netlify/images?url=aurora-original.jpg&w=3840', 19 | ); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /apps/ember-test-app/tests/helpers/image.helper.ts: -------------------------------------------------------------------------------- 1 | import { settled } from '@ember/test-helpers'; 2 | 3 | interface ImageLoadedOptions { 4 | rejectOnError?: boolean; 5 | } 6 | 7 | export function getImg(el: HTMLElement): HTMLImageElement { 8 | const imgEl = 9 | el instanceof HTMLImageElement 10 | ? el 11 | : (el.querySelector('img') ?? el.shadowRoot?.querySelector('img')); 12 | 13 | if (!imgEl) { 14 | throw new Error('No found'); 15 | } 16 | 17 | return imgEl; 18 | } 19 | 20 | export async function loaded( 21 | el: HTMLElement, 22 | options: ImageLoadedOptions = {}, 23 | ): Promise { 24 | let resolve: () => void; 25 | let reject: (e: unknown) => void; 26 | const loaded = new Promise((res, rej) => { 27 | resolve = res; 28 | reject = rej; 29 | }); 30 | 31 | const imgEl = getImg(el); 32 | 33 | if (imgEl.complete) { 34 | resolve!(); 35 | } else { 36 | imgEl.addEventListener( 37 | 'load', 38 | () => { 39 | setTimeout(resolve, 0); 40 | }, 41 | { once: true }, 42 | ); 43 | if (options.rejectOnError) { 44 | imgEl.addEventListener( 45 | 'error', 46 | (e) => { 47 | console.error(e.message); 48 | reject(e); 49 | }, 50 | { once: true }, 51 | ); 52 | } 53 | } 54 | 55 | return loaded; 56 | } 57 | 58 | export async function trigger(el: HTMLElement) { 59 | const fakeEvent = new Event('load'); 60 | getImg(el).dispatchEvent(fakeEvent); 61 | 62 | await settled(); 63 | } 64 | -------------------------------------------------------------------------------- /apps/ember-test-app/tests/helpers/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | setupApplicationTest as upstreamSetupApplicationTest, 3 | setupRenderingTest as upstreamSetupRenderingTest, 4 | setupTest as upstreamSetupTest, 5 | } from 'ember-qunit'; 6 | 7 | // This file exists to provide wrappers around ember-qunit's 8 | // test setup functions. This way, you can easily extend the setup that is 9 | // needed per test type. 10 | 11 | function setupApplicationTest(hooks, options) { 12 | upstreamSetupApplicationTest(hooks, options); 13 | 14 | // Additional setup for application tests can be done here. 15 | // 16 | // For example, if you need an authenticated session for each 17 | // application test, you could do: 18 | // 19 | // hooks.beforeEach(async function () { 20 | // await authenticateSession(); // ember-simple-auth 21 | // }); 22 | // 23 | // This is also a good place to call test setup functions coming 24 | // from other addons: 25 | // 26 | // setupIntl(hooks, 'en-us'); // ember-intl 27 | // setupMirage(hooks); // ember-cli-mirage 28 | } 29 | 30 | function setupRenderingTest(hooks, options) { 31 | upstreamSetupRenderingTest(hooks, options); 32 | 33 | // Additional setup for rendering tests can be done here. 34 | } 35 | 36 | function setupTest(hooks, options) { 37 | upstreamSetupTest(hooks, options); 38 | 39 | // Additional setup for unit tests can be done here. 40 | } 41 | 42 | export { setupApplicationTest, setupRenderingTest, setupTest }; 43 | -------------------------------------------------------------------------------- /apps/ember-test-app/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TestApp Tests 6 | 7 | 8 | 9 | {{content-for "head"}} {{content-for "test-head"}} 10 | 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} {{content-for "test-head-footer"}} 16 | 17 | 18 | {{content-for "body"}} {{content-for "test-body"}} 19 | 20 |
21 |
22 |
23 |
24 |
25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | {{content-for "body-footer"}} {{content-for "test-body-footer"}} 35 | 36 | 37 | -------------------------------------------------------------------------------- /apps/ember-test-app/tests/integration/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-test-app/tests/integration/.gitkeep -------------------------------------------------------------------------------- /apps/ember-test-app/tests/integration/helpers/responsive-image-fastly-test.gts: -------------------------------------------------------------------------------- 1 | import { module, test } from 'qunit'; 2 | import { setupRenderingTest } from 'ember-qunit'; 3 | import { render } from '@ember/test-helpers'; 4 | import responsiveImageFastly from '@responsive-image/ember/helpers/responsive-image-fastly'; 5 | import type { ImageData } from '@responsive-image/ember'; 6 | 7 | module('Integration | Helper | responsive-image-fastly', function (hooks) { 8 | setupRenderingTest(hooks); 9 | 10 | let data: ImageData | undefined; 11 | const dump = (argument: ImageData) => { 12 | data = argument; 13 | }; 14 | 15 | test('it supports default image types', async function (assert) { 16 | await render( 17 | , 18 | ); 19 | 20 | assert.deepEqual(data?.imageTypes, 'auto'); 21 | }); 22 | 23 | test('it returns correct upload image URLs', async function (assert) { 24 | await render( 25 | , 26 | ); 27 | 28 | assert.strictEqual( 29 | data?.imageUrlFor(100, 'avif'), 30 | 'https://www.fastly.io/image.webp?format=avif&width=100', 31 | ); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /apps/ember-test-app/tests/test-helper.ts: -------------------------------------------------------------------------------- 1 | import Application from 'ember-test-app/app'; 2 | import config from 'ember-test-app/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { loadTests } from 'ember-qunit/test-loader'; 7 | import { start, setupEmberOnerrorValidation } from 'ember-qunit'; 8 | 9 | setApplication(Application.create(config.APP)); 10 | 11 | setup(QUnit.assert); 12 | setupEmberOnerrorValidation(); 13 | loadTests(); 14 | start(); 15 | -------------------------------------------------------------------------------- /apps/ember-test-app/tests/unit/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-test-app/tests/unit/.gitkeep -------------------------------------------------------------------------------- /apps/ember-test-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/ember/tsconfig.json", 3 | "compilerOptions": { 4 | // The combination of `baseUrl` with `paths` allows Ember's classic package 5 | // layout, which is not resolveable with the Node resolution algorithm, to 6 | // work with TypeScript. 7 | "baseUrl": ".", 8 | "paths": { 9 | "ember-test-app/tests/*": ["tests/*"], 10 | "ember-test-app/*": ["app/*"], 11 | "*": ["types/*"] 12 | }, 13 | "types": ["ember-source/types"] 14 | }, 15 | // "include": ["app/**/*", "tests/**/*", "types/**/*"], 16 | "glint": { 17 | "environment": ["ember-loose", "ember-template-imports"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/ember-test-app/types/glint.d.ts: -------------------------------------------------------------------------------- 1 | import '@glint/environment-ember-loose'; 2 | import AddonRegistry from '@responsive-image/ember/template-registry'; 3 | import type Helper from '@ember/component/helper'; 4 | 5 | declare module '@glint/environment-ember-loose/registry' { 6 | export default interface Registry extends AddonRegistry { 7 | 'page-title': new () => Helper<{ 8 | Args: { Positional: [string] }; 9 | Return: void; 10 | }>; 11 | dump: new () => Helper<{ 12 | Args: { Positional: [unknown] }; 13 | Return: void; 14 | }>; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/ember-test-app/types/global.d.ts: -------------------------------------------------------------------------------- 1 | // Types for compiled templates 2 | declare module 'ember-test-app/templates/*' { 3 | import { TemplateFactory } from 'ember-cli-htmlbars'; 4 | const tmpl: TemplateFactory; 5 | export default tmpl; 6 | } 7 | 8 | // To make TS understand our image imports, we have to tag them using a `responsive` query parameter, that has to come *last*! 9 | // We cannot use something like `*.jpg*` that works with queries, as TS only supports a single wildcard. 10 | // See https://github.com/microsoft/TypeScript/issues/38638 11 | declare module '*responsive' { 12 | import { ImageData } from '@responsive-image/ember'; 13 | const value: ImageData; 14 | export default value; 15 | } 16 | -------------------------------------------------------------------------------- /apps/ember-vite/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /apps/ember-vite/.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript 4 | rather than JavaScript by default, when a TypeScript version of a given blueprint is available. 5 | */ 6 | "isTypeScriptProject": false 7 | } 8 | -------------------------------------------------------------------------------- /apps/ember-vite/.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | pull_request: {} 9 | 10 | concurrency: 11 | group: ci-${{ github.head_ref || github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | lint: 16 | name: "Lint" 17 | runs-on: ubuntu-latest 18 | timeout-minutes: 10 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: pnpm/action-setup@v4 23 | with: 24 | version: 9 25 | - name: Install Node 26 | uses: actions/setup-node@v4 27 | with: 28 | node-version: 18 29 | cache: pnpm 30 | - name: Install Dependencies 31 | run: pnpm install --frozen-lockfile 32 | - name: Lint 33 | run: pnpm lint 34 | 35 | test: 36 | name: "Test" 37 | runs-on: ubuntu-latest 38 | timeout-minutes: 10 39 | 40 | steps: 41 | - uses: actions/checkout@v4 42 | - uses: pnpm/action-setup@v4 43 | with: 44 | version: 9 45 | - name: Install Node 46 | uses: actions/setup-node@v4 47 | with: 48 | node-version: 18 49 | cache: pnpm 50 | - name: Install Dependencies 51 | run: pnpm install --frozen-lockfile 52 | - name: Run Tests 53 | run: pnpm test 54 | -------------------------------------------------------------------------------- /apps/ember-vite/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /node_modules/ 7 | 8 | # misc 9 | /.env* 10 | /.pnp* 11 | /.eslintcache 12 | /coverage/ 13 | /npm-debug.log* 14 | /testem.log 15 | /yarn-error.log 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /npm-shrinkwrap.json.ember-try 20 | /package.json.ember-try 21 | /package-lock.json.ember-try 22 | /yarn.lock.ember-try 23 | 24 | # broccoli-debug 25 | /DEBUG/ 26 | 27 | # Playwright 28 | /test-results/ 29 | /playwright-report/ 30 | /blob-report/ 31 | /playwright/.cache/ 32 | -------------------------------------------------------------------------------- /apps/ember-vite/.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | 7 | # misc 8 | /coverage/ 9 | !.* 10 | .*/ 11 | 12 | # ember-try 13 | /.node_modules.ember-try/ 14 | -------------------------------------------------------------------------------- /apps/ember-vite/.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | plugins: ['prettier-plugin-ember-template-tag'], 5 | singleQuote: true, 6 | overrides: [ 7 | { 8 | files: ['*.js', '*.ts', '*.cjs', '.mjs', '.cts', '.mts', '.cts'], 9 | options: { 10 | trailingComma: 'es5', 11 | }, 12 | }, 13 | { 14 | files: ['*.html'], 15 | options: { 16 | singleQuote: false, 17 | }, 18 | }, 19 | { 20 | files: ['*.json'], 21 | options: { 22 | singleQuote: false, 23 | }, 24 | }, 25 | { 26 | files: ['*.hbs'], 27 | options: { 28 | singleQuote: false, 29 | }, 30 | }, 31 | { 32 | files: ['*.gjs', '*.gts'], 33 | options: { 34 | templateSingleQuote: false, 35 | trailingComma: 'es5', 36 | }, 37 | }, 38 | ], 39 | }; 40 | -------------------------------------------------------------------------------- /apps/ember-vite/.stylelintignore: -------------------------------------------------------------------------------- 1 | # unconventional files 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | 7 | # addons 8 | /.node_modules.ember-try/ 9 | -------------------------------------------------------------------------------- /apps/ember-vite/.stylelintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: ['stylelint-config-standard', 'stylelint-prettier/recommended'], 5 | }; 6 | -------------------------------------------------------------------------------- /apps/ember-vite/.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | }; 6 | -------------------------------------------------------------------------------- /apps/ember-vite/.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["dist"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/ember-vite/README.md: -------------------------------------------------------------------------------- 1 | # ember-vite 2 | 3 | Demo app and acceptance tests for Ember + Embroider + Vite. 4 | 5 | ## Prerequisites 6 | 7 | You will need the following things properly installed on your computer. 8 | 9 | - [Git](https://git-scm.com/) 10 | - [Node.js](https://nodejs.org/) 11 | - [pnpm](https://pnpm.io/) 12 | - [Ember CLI](https://cli.emberjs.com/release/) 13 | - [Google Chrome](https://google.com/chrome/) 14 | 15 | ## Installation 16 | 17 | - `git clone ` this repository 18 | - `cd apps/ember-vite` 19 | - `pnpm install` 20 | 21 | ## Running / Development 22 | 23 | - `pnpm start` 24 | - Visit your app at [http://localhost:4200](http://localhost:4200). 25 | - Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests). 26 | 27 | ### Code Generators 28 | 29 | Make use of the many generators for code, try `ember help generate` for more details 30 | 31 | ### Running Tests 32 | 33 | - `pnpm test` 34 | - `pnpm test:ember --server` 35 | 36 | ### Linting 37 | 38 | - `pnpm lint` 39 | - `pnpm lint:fix` 40 | 41 | ### Building 42 | 43 | - `pnpm ember build` (development) 44 | - `pnpm build` (production) 45 | 46 | ## Further Reading / Useful Links 47 | 48 | - [ember.js](https://emberjs.com/) 49 | - [ember-cli](https://cli.emberjs.com/release/) 50 | - Development Browser Extensions 51 | - [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) 52 | - [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) 53 | -------------------------------------------------------------------------------- /apps/ember-vite/app/app.js: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import compatModules from '@embroider/virtual/compat-modules'; 3 | import Resolver from 'ember-resolver'; 4 | import loadInitializers from 'ember-load-initializers'; 5 | import config from './config/environment'; 6 | import { setConfig } from '@responsive-image/core'; 7 | 8 | export default class App extends Application { 9 | modulePrefix = config.modulePrefix; 10 | podModulePrefix = config.podModulePrefix; 11 | Resolver = Resolver.withModules(compatModules); 12 | } 13 | 14 | setConfig('cdn', { 15 | cloudinary: { 16 | cloudName: 'responsive-image', 17 | }, 18 | fastly: { 19 | domain: 'www.fastly.io', 20 | }, 21 | imgix: { 22 | domain: 'responsive-image.imgix.net', 23 | }, 24 | netlify: { 25 | domain: 'responsive-image.dev', 26 | }, 27 | }); 28 | 29 | loadInitializers(App, config.modulePrefix, compatModules); 30 | -------------------------------------------------------------------------------- /apps/ember-vite/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-vite/app/components/.gitkeep -------------------------------------------------------------------------------- /apps/ember-vite/app/config/environment.js: -------------------------------------------------------------------------------- 1 | import loadConfigFromMeta from '@embroider/config-meta-loader'; 2 | 3 | export default loadConfigFromMeta('ember-vite'); 4 | -------------------------------------------------------------------------------- /apps/ember-vite/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-vite/app/controllers/.gitkeep -------------------------------------------------------------------------------- /apps/ember-vite/app/controllers/index.js: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import image from '../images/aurora.jpg?responsive'; 3 | import imageLqipColor from '../images/aurora.jpg?lqip=color&responsive'; 4 | import imageLqipInline from '../images/aurora.jpg?lqip={"type":"inline","targetPixels":16}&responsive'; 5 | import imageLqipBlurhash from '../images/aurora.jpg?lqip={"type":"blurhash","targetPixels":16}&responsive'; 6 | import imageLqipThumbhash from '../images/aurora.jpg?lqip={"type":"thumbhash"}&responsive'; 7 | import imagePortrait from '../images/aurora.jpg?aspect=2:3&responsive'; 8 | import imageGray from '../images/aurora.jpg?grayscale&responsive'; 9 | 10 | export default class IndexController extends Controller { 11 | image = image; 12 | imageLqipColor = imageLqipColor; 13 | imageLqipInline = imageLqipInline; 14 | imageLqipBlurhash = imageLqipBlurhash; 15 | imageLqipThumbhash = imageLqipThumbhash; 16 | imagePortrait = imagePortrait; 17 | imageGray = imageGray; 18 | } 19 | -------------------------------------------------------------------------------- /apps/ember-vite/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-vite/app/helpers/.gitkeep -------------------------------------------------------------------------------- /apps/ember-vite/app/images/aurora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-vite/app/images/aurora.jpg -------------------------------------------------------------------------------- /apps/ember-vite/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-vite/app/models/.gitkeep -------------------------------------------------------------------------------- /apps/ember-vite/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'ember-vite/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () {}); 10 | -------------------------------------------------------------------------------- /apps/ember-vite/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-vite/app/routes/.gitkeep -------------------------------------------------------------------------------- /apps/ember-vite/app/styles/app.css: -------------------------------------------------------------------------------- 1 | /* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */ 2 | -------------------------------------------------------------------------------- /apps/ember-vite/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "EmberVite"}} 2 | 3 | {{outlet}} -------------------------------------------------------------------------------- /apps/ember-vite/babel.config.cjs: -------------------------------------------------------------------------------- 1 | const { 2 | babelCompatSupport, 3 | templateCompatSupport, 4 | } = require('@embroider/compat/babel'); 5 | 6 | module.exports = { 7 | plugins: [ 8 | [ 9 | 'babel-plugin-ember-template-compilation', 10 | { 11 | compilerPath: 'ember-source/dist/ember-template-compiler.js', 12 | enableLegacyModules: [ 13 | 'ember-cli-htmlbars', 14 | 'ember-cli-htmlbars-inline-precompile', 15 | 'htmlbars-inline-precompile', 16 | ], 17 | transforms: [...templateCompatSupport()], 18 | }, 19 | ], 20 | [ 21 | 'module:decorator-transforms', 22 | { 23 | runtime: { 24 | import: require.resolve('decorator-transforms/runtime-esm'), 25 | }, 26 | }, 27 | ], 28 | [ 29 | '@babel/plugin-transform-runtime', 30 | { 31 | absoluteRuntime: __dirname, 32 | useESModules: true, 33 | regenerator: false, 34 | }, 35 | ], 36 | ...babelCompatSupport(), 37 | ], 38 | 39 | generatorOpts: { 40 | compact: false, 41 | }, 42 | }; 43 | -------------------------------------------------------------------------------- /apps/ember-vite/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "@embroider/app-blueprint", 6 | "version": "0.23.2", 7 | "blueprints": [ 8 | { 9 | "name": "@embroider/app-blueprint", 10 | "isBaseBlueprint": true, 11 | "options": ["--package-manager pnpm"] 12 | } 13 | ] 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /apps/ember-vite/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | const ENV = { 5 | modulePrefix: 'ember-vite', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'history', 9 | EmberENV: { 10 | EXTEND_PROTOTYPES: false, 11 | FEATURES: { 12 | // Here you can enable experimental features on an ember canary build 13 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 14 | }, 15 | }, 16 | 17 | APP: { 18 | // Here you can pass flags/options to your application instance 19 | // when it is created 20 | }, 21 | }; 22 | 23 | if (environment === 'development') { 24 | // ENV.APP.LOG_RESOLVER = true; 25 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 26 | // ENV.APP.LOG_TRANSITIONS = true; 27 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 28 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 29 | } 30 | 31 | if (environment === 'test') { 32 | // Testem prefers this... 33 | ENV.locationType = 'none'; 34 | 35 | // keep test console output quieter 36 | ENV.APP.LOG_ACTIVE_GENERATION = false; 37 | ENV.APP.LOG_VIEW_LOOKUPS = false; 38 | 39 | ENV.APP.rootElement = '#ember-testing'; 40 | ENV.APP.autoboot = false; 41 | } 42 | 43 | if (environment === 'production') { 44 | // here you can enable a production-specific feature 45 | } 46 | 47 | return ENV; 48 | }; 49 | -------------------------------------------------------------------------------- /apps/ember-vite/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true, 6 | "no-implicit-route-model": true 7 | } 8 | -------------------------------------------------------------------------------- /apps/ember-vite/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions', 7 | ]; 8 | 9 | module.exports = { 10 | browsers, 11 | }; 12 | -------------------------------------------------------------------------------- /apps/ember-vite/ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberApp = require('ember-cli/lib/broccoli/ember-app'); 4 | const { compatBuild } = require('@embroider/compat'); 5 | 6 | module.exports = async function (defaults) { 7 | const { buildOnce } = await import('@embroider/vite'); 8 | let app = new EmberApp(defaults, {}); 9 | 10 | return compatBuild(app, buildOnce); 11 | }; 12 | -------------------------------------------------------------------------------- /apps/ember-vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EmberVite 6 | 7 | 8 | 9 | {{content-for "head"}} 10 | 11 | 12 | 13 | 14 | {{content-for "head-footer"}} 15 | 16 | 17 | {{content-for "body"}} 18 | 19 | 20 | 26 | 27 | {{content-for "body-footer"}} 28 | 29 | 30 | -------------------------------------------------------------------------------- /apps/ember-vite/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /apps/ember-vite/testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | if (typeof module !== 'undefined') { 4 | module.exports = { 5 | test_page: 'tests/index.html?hidepassed', 6 | disable_watching: true, 7 | launch_in_ci: ['Chrome'], 8 | launch_in_dev: ['Chrome'], 9 | browser_start_timeout: 120, 10 | browser_args: { 11 | Chrome: { 12 | ci: [ 13 | // --no-sandbox is needed when running Chrome inside a container 14 | process.env.CI ? '--no-sandbox' : null, 15 | '--headless', 16 | '--disable-dev-shm-usage', 17 | '--disable-software-rasterizer', 18 | '--mute-audio', 19 | '--remote-debugging-port=0', 20 | '--window-size=1440,900', 21 | ].filter(Boolean), 22 | }, 23 | }, 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /apps/ember-vite/tests/responsive-image.spec.ts: -------------------------------------------------------------------------------- 1 | import { runTests } from '@responsive-image/internals/playwright'; 2 | 3 | runTests(); 4 | -------------------------------------------------------------------------------- /apps/ember-vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/ember/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "types": ["node"] 13 | }, 14 | "include": ["playwright.config.mts"] 15 | } 16 | -------------------------------------------------------------------------------- /apps/ember-vite/turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "extends": ["//"], 4 | "tasks": { 5 | "build": { 6 | "outputs": [ 7 | "dist/**", 8 | "declarations/**", 9 | "node_modules/.embroider", 10 | "tmp" 11 | ] 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/ember-vite/vite.config.mjs: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { extensions, classicEmberSupport, ember } from '@embroider/vite'; 3 | import { babel } from '@rollup/plugin-babel'; 4 | import { setupPlugins } from '@responsive-image/vite-plugin'; 5 | 6 | export default defineConfig({ 7 | plugins: [ 8 | classicEmberSupport(), 9 | ember(), 10 | // extra plugins here 11 | babel({ 12 | babelHelpers: 'runtime', 13 | extensions, 14 | }), 15 | setupPlugins({ 16 | include: /^[^?]+\.jpg\?.*responsive.*$/, 17 | }), 18 | ], 19 | preview: { 20 | port: 4203, 21 | }, 22 | }); 23 | -------------------------------------------------------------------------------- /apps/ember-webpack/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.hbs] 16 | insert_final_newline = false 17 | 18 | [*.{diff,md}] 19 | trim_trailing_whitespace = false 20 | -------------------------------------------------------------------------------- /apps/ember-webpack/.ember-cli: -------------------------------------------------------------------------------- 1 | { 2 | /** 3 | Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript 4 | rather than JavaScript by default, when a TypeScript version of a given blueprint is available. 5 | */ 6 | "isTypeScriptProject": true 7 | } 8 | -------------------------------------------------------------------------------- /apps/ember-webpack/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist/ 3 | /tmp/ 4 | 5 | # dependencies 6 | /node_modules/ 7 | 8 | # misc 9 | /.env* 10 | /.pnp* 11 | /.eslintcache 12 | /coverage/ 13 | /npm-debug.log* 14 | /testem.log 15 | /yarn-error.log 16 | 17 | # ember-try 18 | /.node_modules.ember-try/ 19 | /npm-shrinkwrap.json.ember-try 20 | /package.json.ember-try 21 | /package-lock.json.ember-try 22 | /yarn.lock.ember-try 23 | 24 | # broccoli-debug 25 | /DEBUG/ 26 | 27 | # Playwright 28 | /test-results/ 29 | /playwright-report/ 30 | /blob-report/ 31 | /playwright/.cache/ 32 | -------------------------------------------------------------------------------- /apps/ember-webpack/.prettierignore: -------------------------------------------------------------------------------- 1 | # unconventional js 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | 7 | # misc 8 | /coverage/ 9 | !.* 10 | .*/ 11 | 12 | # ember-try 13 | /.node_modules.ember-try/ 14 | -------------------------------------------------------------------------------- /apps/ember-webpack/.prettierrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | plugins: ['prettier-plugin-ember-template-tag'], 5 | overrides: [ 6 | { 7 | files: '*.{js,gjs,ts,gts,mjs,mts,cjs,cts}', 8 | options: { 9 | singleQuote: true, 10 | }, 11 | }, 12 | { 13 | files: '*.{gjs,gts}', 14 | options: { 15 | singleQuote: true, 16 | templateSingleQuote: false, 17 | }, 18 | }, 19 | ], 20 | }; 21 | -------------------------------------------------------------------------------- /apps/ember-webpack/.stylelintignore: -------------------------------------------------------------------------------- 1 | # unconventional files 2 | /blueprints/*/files/ 3 | 4 | # compiled output 5 | /dist/ 6 | 7 | # addons 8 | /.node_modules.ember-try/ 9 | -------------------------------------------------------------------------------- /apps/ember-webpack/.stylelintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: ['stylelint-config-standard', 'stylelint-prettier/recommended'], 5 | }; 6 | -------------------------------------------------------------------------------- /apps/ember-webpack/.template-lintrc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | }; 6 | -------------------------------------------------------------------------------- /apps/ember-webpack/.watchmanconfig: -------------------------------------------------------------------------------- 1 | { 2 | "ignore_dirs": ["dist"] 3 | } 4 | -------------------------------------------------------------------------------- /apps/ember-webpack/README.md: -------------------------------------------------------------------------------- 1 | # ember-webpack 2 | 3 | Demo app and acceptance tests for Ember + Embroider + Webpack. 4 | 5 | ## Prerequisites 6 | 7 | You will need the following things properly installed on your computer. 8 | 9 | - [Git](https://git-scm.com/) 10 | - [Node.js](https://nodejs.org/) 11 | - [pnpm](https://pnpm.io/) 12 | - [Ember CLI](https://cli.emberjs.com/release/) 13 | - [Google Chrome](https://google.com/chrome/) 14 | 15 | ## Installation 16 | 17 | - `git clone ` this repository 18 | - `cd apps/ember-webpack` 19 | - `pnpm install` 20 | 21 | ## Running / Development 22 | 23 | - `pnpm start` 24 | - Visit your app at [http://localhost:4200](http://localhost:4200). 25 | - Visit your tests at [http://localhost:4200/tests](http://localhost:4200/tests). 26 | 27 | ### Code Generators 28 | 29 | Make use of the many generators for code, try `ember help generate` for more details 30 | 31 | ### Running Tests 32 | 33 | - `pnpm test` 34 | - `pnpm test:ember --server` 35 | 36 | ### Linting 37 | 38 | - `pnpm lint` 39 | - `pnpm lint:fix` 40 | 41 | ### Building 42 | 43 | - `pnpm ember build` (development) 44 | - `pnpm build` (production) 45 | 46 | ## Further Reading / Useful Links 47 | 48 | - [ember.js](https://emberjs.com/) 49 | - [ember-cli](https://cli.emberjs.com/release/) 50 | - Development Browser Extensions 51 | - [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) 52 | - [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) 53 | -------------------------------------------------------------------------------- /apps/ember-webpack/app/app.ts: -------------------------------------------------------------------------------- 1 | import Application from '@ember/application'; 2 | import Resolver from 'ember-resolver'; 3 | import loadInitializers from 'ember-load-initializers'; 4 | import config from 'ember-webpack/config/environment'; 5 | import { setConfig } from '@responsive-image/core'; 6 | import type { Config } from '@responsive-image/cdn'; 7 | 8 | export default class App extends Application { 9 | modulePrefix = config.modulePrefix; 10 | podModulePrefix = config.podModulePrefix; 11 | Resolver = Resolver; 12 | } 13 | 14 | setConfig('cdn', { 15 | cloudinary: { 16 | cloudName: 'responsive-image', 17 | }, 18 | fastly: { 19 | domain: 'www.fastly.io', 20 | }, 21 | imgix: { 22 | domain: 'responsive-image.imgix.net', 23 | }, 24 | netlify: { 25 | domain: 'responsive-image.dev', 26 | }, 27 | }); 28 | 29 | loadInitializers(App, config.modulePrefix); 30 | -------------------------------------------------------------------------------- /apps/ember-webpack/app/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-webpack/app/components/.gitkeep -------------------------------------------------------------------------------- /apps/ember-webpack/app/config/environment.d.ts: -------------------------------------------------------------------------------- 1 | export default config; 2 | 3 | /** 4 | * Type declarations for 5 | * import config from 'my-app/config/environment' 6 | */ 7 | declare const config: { 8 | environment: string; 9 | modulePrefix: string; 10 | podModulePrefix: string; 11 | locationType: 'history' | 'hash' | 'none' | 'auto'; 12 | rootURL: string; 13 | APP: Record; 14 | }; 15 | -------------------------------------------------------------------------------- /apps/ember-webpack/app/controllers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-webpack/app/controllers/.gitkeep -------------------------------------------------------------------------------- /apps/ember-webpack/app/controllers/index.ts: -------------------------------------------------------------------------------- 1 | import Controller from '@ember/controller'; 2 | import image from '../images/aurora.jpg?responsive'; 3 | import imageLqipColor from '../images/aurora.jpg?lqip=color&responsive'; 4 | import imageLqipInline from '../images/aurora.jpg?lqip={"type":"inline","targetPixels":16}&responsive'; 5 | import imageLqipBlurhash from '../images/aurora.jpg?lqip={"type":"blurhash","targetPixels":16}&responsive'; 6 | import imageLqipThumbhash from '../images/aurora.jpg?lqip={"type":"thumbhash"}&responsive'; 7 | import imagePortrait from '../images/aurora.jpg?aspect=2:3&responsive'; 8 | import imageGray from '../images/aurora.jpg?grayscale&responsive'; 9 | 10 | export default class IndexController extends Controller { 11 | image = image; 12 | imageLqipColor = imageLqipColor; 13 | imageLqipInline = imageLqipInline; 14 | imageLqipBlurhash = imageLqipBlurhash; 15 | imageLqipThumbhash = imageLqipThumbhash; 16 | imagePortrait = imagePortrait; 17 | imageGray = imageGray; 18 | } 19 | -------------------------------------------------------------------------------- /apps/ember-webpack/app/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-webpack/app/helpers/.gitkeep -------------------------------------------------------------------------------- /apps/ember-webpack/app/images/aurora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-webpack/app/images/aurora.jpg -------------------------------------------------------------------------------- /apps/ember-webpack/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {{content-for "head"}} 9 | 10 | 11 | 16 | 20 | 24 | 25 | {{content-for "head-footer"}} 26 | 27 | 28 | {{content-for "body"}} 29 | 30 | 34 | 38 | 39 | 40 | 41 | {{content-for "body-footer"}} 42 | 43 | 44 | -------------------------------------------------------------------------------- /apps/ember-webpack/app/models/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-webpack/app/models/.gitkeep -------------------------------------------------------------------------------- /apps/ember-webpack/app/router.js: -------------------------------------------------------------------------------- 1 | import EmberRouter from '@ember/routing/router'; 2 | import config from 'ember-webpack/config/environment'; 3 | 4 | export default class Router extends EmberRouter { 5 | location = config.locationType; 6 | rootURL = config.rootURL; 7 | } 8 | 9 | Router.map(function () {}); 10 | -------------------------------------------------------------------------------- /apps/ember-webpack/app/routes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/ember-webpack/app/routes/.gitkeep -------------------------------------------------------------------------------- /apps/ember-webpack/app/styles/app.css: -------------------------------------------------------------------------------- 1 | /* Ember supports plain CSS out of the box. More info: https://cli.emberjs.com/release/advanced-use/stylesheets/ */ 2 | -------------------------------------------------------------------------------- /apps/ember-webpack/app/templates/application.hbs: -------------------------------------------------------------------------------- 1 | {{page-title "ember-responsive-image"}} 2 | 3 | {{outlet}} -------------------------------------------------------------------------------- /apps/ember-webpack/config/ember-cli-update.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": "1.0.0", 3 | "packages": [ 4 | { 5 | "name": "ember-cli", 6 | "version": "6.2.3", 7 | "blueprints": [ 8 | { 9 | "name": "app", 10 | "outputRepo": "https://github.com/ember-cli/ember-new-output", 11 | "codemodsSource": "ember-app-codemods-manifest@1", 12 | "isBaseBlueprint": true, 13 | "options": ["--no-welcome", "--pnpm", "--embroider"] 14 | } 15 | ] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /apps/ember-webpack/config/environment.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function (environment) { 4 | const ENV = { 5 | modulePrefix: 'ember-webpack', 6 | environment, 7 | rootURL: '/', 8 | locationType: 'history', 9 | EmberENV: { 10 | EXTEND_PROTOTYPES: false, 11 | FEATURES: { 12 | // Here you can enable experimental features on an ember canary build 13 | // e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true 14 | }, 15 | }, 16 | 17 | APP: { 18 | // Here you can pass flags/options to your application instance 19 | // when it is created 20 | }, 21 | }; 22 | 23 | if (environment === 'development') { 24 | // ENV.APP.LOG_RESOLVER = true; 25 | // ENV.APP.LOG_ACTIVE_GENERATION = true; 26 | // ENV.APP.LOG_TRANSITIONS = true; 27 | // ENV.APP.LOG_TRANSITIONS_INTERNAL = true; 28 | // ENV.APP.LOG_VIEW_LOOKUPS = true; 29 | } 30 | 31 | if (environment === 'test') { 32 | // Testem prefers this... 33 | ENV.locationType = 'none'; 34 | 35 | // keep test console output quieter 36 | ENV.APP.LOG_ACTIVE_GENERATION = false; 37 | ENV.APP.LOG_VIEW_LOOKUPS = false; 38 | 39 | ENV.APP.rootElement = '#ember-testing'; 40 | ENV.APP.autoboot = false; 41 | } 42 | 43 | if (environment === 'production') { 44 | // here you can enable a production-specific feature 45 | } 46 | 47 | return ENV; 48 | }; 49 | -------------------------------------------------------------------------------- /apps/ember-webpack/config/fastboot.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function () { 4 | return { 5 | buildSandboxGlobals(defaultGlobals) { 6 | return { ...defaultGlobals, URL, URLSearchParams }; 7 | }, 8 | }; 9 | }; 10 | -------------------------------------------------------------------------------- /apps/ember-webpack/config/optional-features.json: -------------------------------------------------------------------------------- 1 | { 2 | "application-template-wrapper": false, 3 | "default-async-observers": true, 4 | "jquery-integration": false, 5 | "template-only-glimmer-components": true, 6 | "no-implicit-route-model": true 7 | } 8 | -------------------------------------------------------------------------------- /apps/ember-webpack/config/targets.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const browsers = [ 4 | 'last 1 Chrome versions', 5 | 'last 1 Firefox versions', 6 | 'last 1 Safari versions', 7 | ]; 8 | 9 | module.exports = { 10 | browsers, 11 | node: 'current', 12 | }; 13 | -------------------------------------------------------------------------------- /apps/ember-webpack/ember-cli-build.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const EmberApp = require('ember-cli/lib/broccoli/ember-app'); 4 | const { setupLoaders } = require('@responsive-image/webpack'); 5 | 6 | const EmberResponsiveImageWebpackLoaders = setupLoaders(); 7 | 8 | module.exports = function (defaults) { 9 | let app = new EmberApp(defaults, { 10 | 'ember-cli-babel': { 11 | enableTypeScriptTransform: true, 12 | }, 13 | }); 14 | 15 | const { Webpack } = require('@embroider/webpack'); 16 | return require('@embroider/compat').compatBuild(app, Webpack, { 17 | staticAddonTestSupportTrees: true, 18 | staticAddonTrees: true, 19 | staticHelpers: true, 20 | staticModifiers: true, 21 | staticComponents: true, 22 | skipBabel: [ 23 | { 24 | package: 'qunit', 25 | }, 26 | ], 27 | // This breaks with ember-cli-fastboot: https://github.com/ember-fastboot/ember-cli-fastboot/issues/925 28 | staticEmberSource: false, 29 | packagerOptions: { 30 | webpackConfig: { 31 | module: { 32 | rules: [ 33 | { 34 | resourceQuery: /responsive/, 35 | use: EmberResponsiveImageWebpackLoaders, 36 | }, 37 | ], 38 | }, 39 | }, 40 | }, 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /apps/ember-webpack/playwright/responsive-image.spec.ts: -------------------------------------------------------------------------------- 1 | import { runTests } from '@responsive-image/internals/playwright'; 2 | 3 | runTests(); 4 | -------------------------------------------------------------------------------- /apps/ember-webpack/public/robots.txt: -------------------------------------------------------------------------------- 1 | # http://www.robotstxt.org 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /apps/ember-webpack/testem.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | test_page: 'tests/index.html?hidepassed', 5 | disable_watching: true, 6 | launch_in_ci: ['Chrome'], 7 | launch_in_dev: ['Chrome'], 8 | browser_start_timeout: 120, 9 | browser_args: { 10 | Chrome: { 11 | ci: [ 12 | // --no-sandbox is needed when running Chrome inside a container 13 | process.env.CI ? '--no-sandbox' : null, 14 | '--headless', 15 | '--disable-dev-shm-usage', 16 | '--disable-software-rasterizer', 17 | '--mute-audio', 18 | '--remote-debugging-port=0', 19 | '--window-size=1440,900', 20 | ].filter(Boolean), 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /apps/ember-webpack/tests/helpers/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | setupApplicationTest as upstreamSetupApplicationTest, 3 | setupRenderingTest as upstreamSetupRenderingTest, 4 | setupTest as upstreamSetupTest, 5 | } from 'ember-qunit'; 6 | 7 | // This file exists to provide wrappers around ember-qunit's 8 | // test setup functions. This way, you can easily extend the setup that is 9 | // needed per test type. 10 | 11 | function setupApplicationTest(hooks, options) { 12 | upstreamSetupApplicationTest(hooks, options); 13 | 14 | // Additional setup for application tests can be done here. 15 | // 16 | // For example, if you need an authenticated session for each 17 | // application test, you could do: 18 | // 19 | // hooks.beforeEach(async function () { 20 | // await authenticateSession(); // ember-simple-auth 21 | // }); 22 | // 23 | // This is also a good place to call test setup functions coming 24 | // from other addons: 25 | // 26 | // setupIntl(hooks, 'en-us'); // ember-intl 27 | // setupMirage(hooks); // ember-cli-mirage 28 | } 29 | 30 | function setupRenderingTest(hooks, options) { 31 | upstreamSetupRenderingTest(hooks, options); 32 | 33 | // Additional setup for rendering tests can be done here. 34 | } 35 | 36 | function setupTest(hooks, options) { 37 | upstreamSetupTest(hooks, options); 38 | 39 | // Additional setup for unit tests can be done here. 40 | } 41 | 42 | export { setupApplicationTest, setupRenderingTest, setupTest }; 43 | -------------------------------------------------------------------------------- /apps/ember-webpack/tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | TestApp Tests 6 | 7 | 8 | 9 | {{content-for "head"}} {{content-for "test-head"}} 10 | 11 | 12 | 13 | 14 | 15 | {{content-for "head-footer"}} {{content-for "test-head-footer"}} 16 | 17 | 18 | {{content-for "body"}} {{content-for "test-body"}} 19 | 20 |
21 |
22 |
23 |
24 |
25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | {{content-for "body-footer"}} {{content-for "test-body-footer"}} 35 | 36 | 37 | -------------------------------------------------------------------------------- /apps/ember-webpack/tests/test-helper.ts: -------------------------------------------------------------------------------- 1 | import Application from 'ember-webpack/app'; 2 | import config from 'ember-webpack/config/environment'; 3 | import * as QUnit from 'qunit'; 4 | import { setApplication } from '@ember/test-helpers'; 5 | import { setup } from 'qunit-dom'; 6 | import { loadTests } from 'ember-qunit/test-loader'; 7 | import { start, setupEmberOnerrorValidation } from 'ember-qunit'; 8 | 9 | setApplication(Application.create(config.APP)); 10 | 11 | setup(QUnit.assert); 12 | setupEmberOnerrorValidation(); 13 | loadTests(); 14 | start(); 15 | -------------------------------------------------------------------------------- /apps/ember-webpack/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/ember/tsconfig.json", 3 | "compilerOptions": { 4 | // The combination of `baseUrl` with `paths` allows Ember's classic package 5 | // layout, which is not resolveable with the Node resolution algorithm, to 6 | // work with TypeScript. 7 | "baseUrl": ".", 8 | "paths": { 9 | "ember-webpack/tests/*": ["tests/*"], 10 | "ember-webpack/*": ["app/*"], 11 | "*": ["types/*"] 12 | }, 13 | "types": ["ember-source/types", "node"] 14 | }, 15 | // "include": ["app/**/*", "tests/**/*", "types/**/*"], 16 | "glint": { 17 | "environment": ["ember-loose", "ember-template-imports"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/ember-webpack/types/glint.d.ts: -------------------------------------------------------------------------------- 1 | import '@glint/environment-ember-loose'; 2 | import AddonRegistry from '@responsive-image/ember/template-registry'; 3 | import type Helper from '@ember/component/helper'; 4 | 5 | declare module '@glint/environment-ember-loose/registry' { 6 | export default interface Registry extends AddonRegistry { 7 | 'page-title': new () => Helper<{ 8 | Args: { Positional: [string] }; 9 | Return: void; 10 | }>; 11 | dump: new () => Helper<{ 12 | Args: { Positional: [unknown] }; 13 | Return: void; 14 | }>; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/ember-webpack/types/global.d.ts: -------------------------------------------------------------------------------- 1 | // Types for compiled templates 2 | declare module 'ember-webpack/templates/*' { 3 | import { TemplateFactory } from 'ember-cli-htmlbars'; 4 | const tmpl: TemplateFactory; 5 | export default tmpl; 6 | } 7 | 8 | // To make TS understand our image imports, we have to tag them using a `responsive` query parameter, that has to come *last*! 9 | // We cannot use something like `*.jpg*` that works with queries, as TS only supports a single wildcard. 10 | // See https://github.com/microsoft/TypeScript/issues/38638 11 | declare module '*responsive' { 12 | import { ImageData } from '@responsive-image/ember'; 13 | const value: ImageData; 14 | export default value; 15 | } 16 | -------------------------------------------------------------------------------- /apps/react-vite/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | playwright-report/ 27 | test-results/ 28 | -------------------------------------------------------------------------------- /apps/react-vite/eslint.config.js: -------------------------------------------------------------------------------- 1 | import js from '@eslint/js' 2 | import globals from 'globals' 3 | import reactHooks from 'eslint-plugin-react-hooks' 4 | import reactRefresh from 'eslint-plugin-react-refresh' 5 | import tseslint from 'typescript-eslint' 6 | 7 | export default tseslint.config( 8 | { ignores: ['dist'] }, 9 | { 10 | extends: [js.configs.recommended, ...tseslint.configs.recommended], 11 | files: ['**/*.{ts,tsx}'], 12 | languageOptions: { 13 | ecmaVersion: 2020, 14 | globals: globals.browser, 15 | }, 16 | plugins: { 17 | 'react-hooks': reactHooks, 18 | 'react-refresh': reactRefresh, 19 | }, 20 | rules: { 21 | ...reactHooks.configs.recommended.rules, 22 | 'react-refresh/only-export-components': [ 23 | 'warn', 24 | { allowConstantExport: true }, 25 | ], 26 | }, 27 | }, 28 | ) 29 | -------------------------------------------------------------------------------- /apps/react-vite/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/react-vite/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-vite", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc -b && vite build", 9 | "lint": "eslint .", 10 | "preview": "vite preview", 11 | "test": "playwright test", 12 | "test:ui": "playwright test --ui" 13 | }, 14 | "dependencies": { 15 | "@responsive-image/cdn": "workspace:*", 16 | "@responsive-image/core": "workspace:*", 17 | "@responsive-image/react": "workspace:*", 18 | "react": "^19.1.0", 19 | "react-dom": "^19.1.0" 20 | }, 21 | "devDependencies": { 22 | "@eslint/js": "9.28.0", 23 | "@playwright/test": "1.53.0", 24 | "@responsive-image/internals": "workspace:*", 25 | "@responsive-image/vite-plugin": "workspace:*", 26 | "@types/node": "22.13.4", 27 | "@types/react": "19.1.8", 28 | "@types/react-dom": "19.1.6", 29 | "@vitejs/plugin-react": "4.5.2", 30 | "eslint": "9.28.0", 31 | "eslint-plugin-react-hooks": "5.2.0", 32 | "eslint-plugin-react-refresh": "0.4.20", 33 | "globals": "16.2.0", 34 | "typescript": "5.8.3", 35 | "typescript-eslint": "8.34.0", 36 | "vite": "6.3.5" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /apps/react-vite/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/react-vite/src/global.d.ts: -------------------------------------------------------------------------------- 1 | // To make TS understand our image imports, we have to tag them using a `responsive` query parameter, that has to come *last*! 2 | // We cannot use something like `*.jpg*` that works with queries, as TS only supports a single wildcard. 3 | // See https://github.com/microsoft/TypeScript/issues/38638 4 | declare module '*responsive' { 5 | import { ImageData } from '@responsive-image/core'; 6 | const value: ImageData; 7 | export default value; 8 | } 9 | -------------------------------------------------------------------------------- /apps/react-vite/src/images/aurora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/react-vite/src/images/aurora.jpg -------------------------------------------------------------------------------- /apps/react-vite/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/react-vite/tests/responsive-image.spec.ts: -------------------------------------------------------------------------------- 1 | import { runTests } from '@responsive-image/internals/playwright'; 2 | 3 | runTests({ isShadowDom: false }); 4 | -------------------------------------------------------------------------------- /apps/react-vite/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "module": "ESNext", 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "bundler", 12 | "allowImportingTsExtensions": true, 13 | "verbatimModuleSyntax": true, 14 | "moduleDetection": "force", 15 | "noEmit": true, 16 | "jsx": "react-jsx", 17 | 18 | /* Linting */ 19 | "strict": true, 20 | "noUnusedLocals": true, 21 | "noUnusedParameters": true, 22 | "erasableSyntaxOnly": true, 23 | "noFallthroughCasesInSwitch": true, 24 | "noUncheckedSideEffectImports": true 25 | }, 26 | "include": ["src"] 27 | } 28 | -------------------------------------------------------------------------------- /apps/react-vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "references": [ 4 | { "path": "./tsconfig.app.json" }, 5 | { "path": "./tsconfig.node.json" } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /apps/react-vite/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 4 | "target": "ES2022", 5 | "lib": ["ES2023"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "verbatimModuleSyntax": true, 13 | "moduleDetection": "force", 14 | "noEmit": true, 15 | 16 | /* Linting */ 17 | "strict": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "erasableSyntaxOnly": true, 21 | "noFallthroughCasesInSwitch": true, 22 | "noUncheckedSideEffectImports": true 23 | }, 24 | "include": ["vite.config.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /apps/react-vite/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import react from '@vitejs/plugin-react'; 3 | import { setupPlugins } from '@responsive-image/vite-plugin'; 4 | 5 | // https://vite.dev/config/ 6 | export default defineConfig({ 7 | plugins: [ 8 | react(), 9 | setupPlugins({ 10 | include: /^[^?]+\.jpg\?.*responsive.*$/, 11 | }), 12 | ], 13 | preview: { 14 | port: 4207, 15 | }, 16 | }); 17 | -------------------------------------------------------------------------------- /apps/solid-start/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | dist 3 | .solid 4 | .output 5 | .vercel 6 | .netlify 7 | .vinxi 8 | app.config.timestamp_*.js 9 | 10 | # Environment 11 | .env 12 | .env*.local 13 | 14 | # dependencies 15 | /node_modules 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | *.launch 22 | .settings/ 23 | 24 | # Temp 25 | gitignore 26 | 27 | # System Files 28 | .DS_Store 29 | Thumbs.db 30 | 31 | # Playwright 32 | /test-results/ 33 | /playwright-report/ 34 | /blob-report/ 35 | /playwright/.cache/ 36 | -------------------------------------------------------------------------------- /apps/solid-start/README.md: -------------------------------------------------------------------------------- 1 | # SolidStart 2 | 3 | Everything you need to build a Solid project, powered by [`solid-start`](https://start.solidjs.com); 4 | 5 | ## Creating a project 6 | 7 | ```bash 8 | # create a new project in the current directory 9 | npm init solid@latest 10 | 11 | # create a new project in my-app 12 | npm init solid@latest my-app 13 | ``` 14 | 15 | ## Developing 16 | 17 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 18 | 19 | ```bash 20 | npm run dev 21 | 22 | # or start the server and open the app in a new browser tab 23 | npm run dev -- --open 24 | ``` 25 | 26 | ## Building 27 | 28 | Solid apps are built with _presets_, which optimise your project for deployment to different environments. 29 | 30 | By default, `npm run build` will generate a Node app that you can run with `npm start`. To use a different preset, add it to the `devDependencies` in `package.json` and specify in your `app.config.js`. 31 | 32 | ## This project was created with the [Solid CLI](https://solid-cli.netlify.app) 33 | -------------------------------------------------------------------------------- /apps/solid-start/app.config.ts: -------------------------------------------------------------------------------- 1 | import { setupPlugins } from '@responsive-image/vite-plugin'; 2 | import { defineConfig } from '@solidjs/start/config'; 3 | 4 | export default defineConfig({ 5 | vite: { 6 | plugins: [ 7 | setupPlugins({ 8 | include: /^[^?]+\.jpg\?.*responsive.*$/, 9 | }), 10 | ], 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /apps/solid-start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solid-start", 3 | "private": true, 4 | "description": "Demo app using SolidStart", 5 | "repository": "", 6 | "license": "MIT", 7 | "type": "module", 8 | "scripts": { 9 | "dev": "vinxi dev", 10 | "build": "vinxi build", 11 | "start": "vinxi start", 12 | "version": "vinxi version", 13 | "test": "playwright test", 14 | "test:ui": "playwright test --ui" 15 | }, 16 | "dependencies": { 17 | "@playwright/test": "1.53.0", 18 | "@responsive-image/cdn": "workspace:*", 19 | "@responsive-image/core": "workspace:*", 20 | "@responsive-image/internals": "workspace:*", 21 | "@responsive-image/solid": "workspace:*", 22 | "@responsive-image/vite-plugin": "workspace:*", 23 | "@solidjs/meta": "^0.29.4", 24 | "@solidjs/router": "^0.15.0", 25 | "@solidjs/start": "^1.1.1", 26 | "solid-js": "^1.9.5", 27 | "vinxi": "^0.5.3" 28 | }, 29 | "engines": { 30 | "node": ">=18" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /apps/solid-start/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/solid-start/public/favicon.ico -------------------------------------------------------------------------------- /apps/solid-start/src/app.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue", sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /apps/solid-start/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { MetaProvider, Title } from '@solidjs/meta'; 2 | import { Router } from '@solidjs/router'; 3 | import { FileRoutes } from '@solidjs/start/router'; 4 | import { Suspense } from 'solid-js'; 5 | import './app.css'; 6 | import { setConfig } from '@responsive-image/core'; 7 | 8 | import type { Config } from '@responsive-image/cdn'; 9 | 10 | setConfig('cdn', { 11 | cloudinary: { 12 | cloudName: 'responsive-image', 13 | }, 14 | fastly: { 15 | domain: 'www.fastly.io', 16 | }, 17 | imgix: { 18 | domain: 'responsive-image.imgix.net', 19 | }, 20 | netlify: { 21 | domain: 'responsive-image.dev', 22 | }, 23 | }); 24 | 25 | export default function App() { 26 | return ( 27 | ( 29 | 30 | SolidStart - ResponsiveImage 31 | {props.children} 32 | 33 | )} 34 | > 35 | 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /apps/solid-start/src/entry-client.tsx: -------------------------------------------------------------------------------- 1 | // @refresh reload 2 | import { mount, StartClient } from "@solidjs/start/client"; 3 | 4 | mount(() => , document.getElementById("app")!); 5 | -------------------------------------------------------------------------------- /apps/solid-start/src/entry-server.tsx: -------------------------------------------------------------------------------- 1 | // @refresh reload 2 | import { createHandler, StartServer } from "@solidjs/start/server"; 3 | 4 | export default createHandler(() => ( 5 | ( 7 | 8 | 9 | 10 | 11 | 12 | {assets} 13 | 14 | 15 |
{children}
16 | {scripts} 17 | 18 | 19 | )} 20 | /> 21 | )); 22 | -------------------------------------------------------------------------------- /apps/solid-start/src/global.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | // To make TS understand our image imports, we have to tag them using a `responsive` query parameter, that has to come *last*! 4 | // We cannot use something like `*.jpg*` that works with queries, as TS only supports a single wildcard. 5 | // See https://github.com/microsoft/TypeScript/issues/38638 6 | declare module '*responsive' { 7 | import { ImageData } from '@responsive-image/core'; 8 | const value: ImageData; 9 | export default value; 10 | } 11 | -------------------------------------------------------------------------------- /apps/solid-start/src/images/aurora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/solid-start/src/images/aurora.jpg -------------------------------------------------------------------------------- /apps/solid-start/src/routes/[...404].tsx: -------------------------------------------------------------------------------- 1 | import { Title } from "@solidjs/meta"; 2 | import { HttpStatusCode } from "@solidjs/start"; 3 | 4 | export default function NotFound() { 5 | return ( 6 |
7 | Not Found 8 | 9 |

Page Not Found

10 |

11 | Visit{" "} 12 | 13 | start.solidjs.com 14 | {" "} 15 | to learn how to build SolidStart apps. 16 |

17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /apps/solid-start/tests/responsive-image.spec.ts: -------------------------------------------------------------------------------- 1 | import { runTests } from '@responsive-image/internals/playwright'; 2 | 3 | runTests(); 4 | -------------------------------------------------------------------------------- /apps/solid-start/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "allowSyntheticDefaultImports": true, 7 | "esModuleInterop": true, 8 | "jsx": "preserve", 9 | "jsxImportSource": "solid-js", 10 | "allowJs": true, 11 | "strict": true, 12 | "noEmit": true, 13 | "types": ["vinxi/types/client"], 14 | "isolatedModules": true, 15 | "paths": { 16 | "~/*": ["./src/*"] 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/svelte-kit/.gitignore: -------------------------------------------------------------------------------- 1 | test-results 2 | node_modules 3 | 4 | # Output 5 | .output 6 | .vercel 7 | .netlify 8 | .wrangler 9 | /.svelte-kit 10 | /build 11 | 12 | # OS 13 | .DS_Store 14 | Thumbs.db 15 | 16 | # Env 17 | .env 18 | .env.* 19 | !.env.example 20 | !.env.test 21 | 22 | # Vite 23 | vite.config.js.timestamp-* 24 | vite.config.ts.timestamp-* 25 | 26 | # Playwright 27 | /test-results/ 28 | /playwright-report/ 29 | /blob-report/ 30 | /playwright/.cache/ 31 | -------------------------------------------------------------------------------- /apps/svelte-kit/.prettierignore: -------------------------------------------------------------------------------- 1 | # Package Managers 2 | package-lock.json 3 | pnpm-lock.yaml 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /apps/svelte-kit/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "plugins": ["prettier-plugin-svelte"], 4 | "overrides": [ 5 | { 6 | "files": "*.svelte", 7 | "options": { 8 | "parser": "svelte" 9 | } 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /apps/svelte-kit/README.md: -------------------------------------------------------------------------------- 1 | # sv 2 | 3 | Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). 4 | 5 | ## Creating a project 6 | 7 | If you're seeing this, you've probably already done this step. Congrats! 8 | 9 | ```bash 10 | # create a new project in the current directory 11 | npx sv create 12 | 13 | # create a new project in my-app 14 | npx sv create my-app 15 | ``` 16 | 17 | ## Developing 18 | 19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 20 | 21 | ```bash 22 | npm run dev 23 | 24 | # or start the server and open the app in a new browser tab 25 | npm run dev -- --open 26 | ``` 27 | 28 | ## Building 29 | 30 | To create a production version of your app: 31 | 32 | ```bash 33 | npm run build 34 | ``` 35 | 36 | You can preview the production build with `npm run preview`. 37 | 38 | > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. 39 | -------------------------------------------------------------------------------- /apps/svelte-kit/eslint.config.js: -------------------------------------------------------------------------------- 1 | import prettier from 'eslint-config-prettier'; 2 | import js from '@eslint/js'; 3 | import { includeIgnoreFile } from '@eslint/compat'; 4 | import svelte from 'eslint-plugin-svelte'; 5 | import globals from 'globals'; 6 | import { fileURLToPath } from 'node:url'; 7 | import ts from 'typescript-eslint'; 8 | const gitignorePath = fileURLToPath(new URL('./.gitignore', import.meta.url)); 9 | 10 | export default ts.config( 11 | includeIgnoreFile(gitignorePath), 12 | js.configs.recommended, 13 | ...ts.configs.recommended, 14 | ...svelte.configs['flat/recommended'], 15 | prettier, 16 | ...svelte.configs['flat/prettier'], 17 | { 18 | languageOptions: { 19 | globals: { 20 | ...globals.browser, 21 | ...globals.node, 22 | }, 23 | }, 24 | }, 25 | { 26 | files: ['**/*.svelte'], 27 | 28 | languageOptions: { 29 | parserOptions: { 30 | parser: ts.parser, 31 | }, 32 | }, 33 | }, 34 | ); 35 | -------------------------------------------------------------------------------- /apps/svelte-kit/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://svelte.dev/docs/kit/types#app.d.ts 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {}; 14 | -------------------------------------------------------------------------------- /apps/svelte-kit/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 |
%sveltekit.body%
11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/svelte-kit/src/images/aurora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/svelte-kit/src/images/aurora.jpg -------------------------------------------------------------------------------- /apps/svelte-kit/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // place files you want to import through the `$lib` alias in this folder. 2 | -------------------------------------------------------------------------------- /apps/svelte-kit/src/types/global.d.ts: -------------------------------------------------------------------------------- 1 | // To make TS understand our image imports, we have to tag them using a `responsive` query parameter, that has to come *last*! 2 | // We cannot use something like `*.jpg*` that works with queries, as TS only supports a single wildcard. 3 | // See https://github.com/microsoft/TypeScript/issues/38638 4 | declare module '*responsive' { 5 | import { ImageData } from '@responsive-image/core'; 6 | const value: ImageData; 7 | export default value; 8 | } 9 | -------------------------------------------------------------------------------- /apps/svelte-kit/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/svelte-kit/static/favicon.png -------------------------------------------------------------------------------- /apps/svelte-kit/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto'; 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://svelte.dev/docs/kit/integrations 7 | // for more information about preprocessors 8 | preprocess: vitePreprocess(), 9 | 10 | kit: { 11 | // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. 12 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter. 13 | // See https://svelte.dev/docs/kit/adapters for more information about adapters. 14 | adapter: adapter(), 15 | }, 16 | }; 17 | 18 | export default config; 19 | -------------------------------------------------------------------------------- /apps/svelte-kit/tests/responsive-image.spec.ts: -------------------------------------------------------------------------------- 1 | import { runTests } from '@responsive-image/internals/playwright'; 2 | 3 | runTests(); 4 | -------------------------------------------------------------------------------- /apps/svelte-kit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler", 13 | "types": ["node"] 14 | }, 15 | "include": ["playwright.config.ts"] 16 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 17 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 18 | // 19 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 20 | // from the referenced tsconfig.json - TypeScript does not merge them in 21 | } 22 | -------------------------------------------------------------------------------- /apps/svelte-kit/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { setupPlugins } from '@responsive-image/vite-plugin'; 3 | import { defineConfig } from 'vite'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | sveltekit(), 8 | setupPlugins({ 9 | include: /^[^?]+\.jpg\?.*responsive.*$/, 10 | }), 11 | ], 12 | preview: { 13 | port: 4202, 14 | }, 15 | }); 16 | -------------------------------------------------------------------------------- /apps/wc-lit/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | 26 | # Playwright 27 | /test-results/ 28 | /playwright-report/ 29 | /blob-report/ 30 | /playwright/.cache/ 31 | -------------------------------------------------------------------------------- /apps/wc-lit/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # wc-lit 2 | 3 | ## 0.0.1-beta.0 4 | 5 | ### Patch Changes 6 | 7 | - Updated dependencies [[`fee0cb5`](https://github.com/simonihmig/responsive-image/commit/fee0cb5a6deb05556f556f1a5f25549fa9e05598), [`da0f40c`](https://github.com/simonihmig/responsive-image/commit/da0f40cfd1cfeca1b4005d6359a40b01c4f66b7d), [`8588cc6`](https://github.com/simonihmig/responsive-image/commit/8588cc6f8ed200c49353f7bd3652dd70e11aa9e2), [`486695e`](https://github.com/simonihmig/responsive-image/commit/486695e083446f6a9c7deda5c086fbb641cee967), [`92f957f`](https://github.com/simonihmig/responsive-image/commit/92f957fcc18fa9485a3f9591b77ca61ff3dd48dc), [`05815c0`](https://github.com/simonihmig/responsive-image/commit/05815c0095c2d3f00d7dcc2028a7a224dad5e349), [`55c502c`](https://github.com/simonihmig/responsive-image/commit/55c502c7da6e0fe86e0ffb2c5ec2f3d39e82679d)]: 8 | - @responsive-image/wc@1.0.0-beta.1 9 | - @responsive-image/core@1.0.0-beta.2 10 | - @responsive-image/cdn@1.0.0-beta.3 11 | -------------------------------------------------------------------------------- /apps/wc-lit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | wc-lit demo app 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/wc-lit/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wc-lit", 3 | "private": true, 4 | "version": "0.0.1-beta.0", 5 | "type": "module", 6 | "scripts": { 7 | "start": "vite", 8 | "build": "vite build", 9 | "preview": "vite preview", 10 | "test": "playwright test", 11 | "test:ui": "playwright test --ui" 12 | }, 13 | "dependencies": { 14 | "@responsive-image/cdn": "workspace:*", 15 | "@responsive-image/core": "workspace:*", 16 | "@responsive-image/wc": "workspace:*", 17 | "lit": "^3.2.1" 18 | }, 19 | "devDependencies": { 20 | "@playwright/test": "1.53.0", 21 | "@responsive-image/internals": "workspace:*", 22 | "@responsive-image/vite-plugin": "workspace:*", 23 | "@types/node": "22.13.4", 24 | "typescript": "5.8.3", 25 | "vite": "6.3.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/wc-lit/src/global.d.ts: -------------------------------------------------------------------------------- 1 | // To make TS understand our image imports, we have to tag them using a `responsive` query parameter, that has to come *last*! 2 | // We cannot use something like `*.jpg*` that works with queries, as TS only supports a single wildcard. 3 | // See https://github.com/microsoft/TypeScript/issues/38638 4 | declare module '*responsive' { 5 | import { ImageData } from '@responsive-image/core'; 6 | const value: ImageData; 7 | export default value; 8 | } 9 | -------------------------------------------------------------------------------- /apps/wc-lit/src/images/aurora.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/wc-lit/src/images/aurora.jpg -------------------------------------------------------------------------------- /apps/wc-lit/src/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/apps/wc-lit/src/index.css -------------------------------------------------------------------------------- /apps/wc-lit/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/wc-lit/tests/responsive-image.spec.ts: -------------------------------------------------------------------------------- 1 | import { runTests } from '@responsive-image/internals/playwright'; 2 | 3 | runTests({ isShadowDom: true }); 4 | -------------------------------------------------------------------------------- /apps/wc-lit/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "experimentalDecorators": true, 5 | "useDefineForClassFields": false, 6 | "module": "ESNext", 7 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 8 | "skipLibCheck": true, 9 | 10 | /* Bundler mode */ 11 | "moduleResolution": "Bundler", 12 | "allowImportingTsExtensions": true, 13 | "isolatedModules": true, 14 | "moduleDetection": "force", 15 | "noEmit": true, 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | "noUncheckedSideEffectImports": true 23 | }, 24 | "include": ["src", "playwright.config.ts"] 25 | } 26 | -------------------------------------------------------------------------------- /apps/wc-lit/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite'; 2 | import { setupPlugins } from '@responsive-image/vite-plugin'; 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | setupPlugins({ 7 | include: /^[^?]+\.jpg\?.*responsive.*$/, 8 | styles: 'inline', 9 | }), 10 | ], 11 | preview: { 12 | port: 4201, 13 | }, 14 | }); 15 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | ignore = "node scripts/skip-netlify-build.js" 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "responsive-image", 3 | "private": true, 4 | "repository": "github:simonihmig/responsive-image", 5 | "license": "MIT", 6 | "author": "Simon Ihmig ", 7 | "scripts": { 8 | "build": "pnpm turbo build", 9 | "build:packages": "pnpm turbo --filter='./packages/*' build", 10 | "lint": "pnpm turbo lint", 11 | "lint:fix": "pnpm run --filter '*' lint:fix", 12 | "prebuild": "pnpm turbo build --filter='./packages/*'", 13 | "test": "pnpm turbo test" 14 | }, 15 | "devDependencies": { 16 | "@changesets/changelog-github": "0.5.1", 17 | "@changesets/cli": "2.29.4", 18 | "concurrently": "9.1.2", 19 | "prettier": "3.5.3", 20 | "turbo": "2.5.4" 21 | }, 22 | "publishConfig": { 23 | "registry": "https://registry.npmjs.org" 24 | }, 25 | "dependencies": { 26 | "postinstall-postinstall": "^2.1.0" 27 | }, 28 | "volta": { 29 | "node": "20.19.2", 30 | "pnpm": "10.12.1" 31 | }, 32 | "packageManager": "pnpm@10.12.1", 33 | "pnpm": { 34 | "packageExtensions": { 35 | "ember-cli-fastboot-testing": { 36 | "peerDependencies": { 37 | "@ember/test-helpers": "^4.0.4" 38 | } 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /packages/-internals/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | 4 | -------------------------------------------------------------------------------- /packages/-internals/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { base, nodeESM } from './src/eslint/index.js'; 2 | 3 | export default [...base, ...nodeESM]; 4 | -------------------------------------------------------------------------------- /packages/-internals/src/eslint/base.js: -------------------------------------------------------------------------------- 1 | import { resolve } from 'node:path'; 2 | 3 | import { includeIgnoreFile } from '@eslint/compat'; 4 | import js from '@eslint/js'; 5 | import prettier from 'eslint-config-prettier'; 6 | import importPlugin from 'eslint-plugin-import'; 7 | import ts from 'typescript-eslint'; 8 | 9 | export default ts.config( 10 | includeIgnoreFile(resolve('./.gitignore')), 11 | js.configs.recommended, 12 | ...ts.configs.recommended.map((c) => ({ 13 | ...c, 14 | files: ['**/*.{ts,tsx,mts,cts,gts,svelte}'], 15 | })), 16 | prettier, 17 | { 18 | files: ['**/*.{ts,tsx,mts,cts,gts,svelte}'], 19 | rules: { 20 | '@typescript-eslint/consistent-type-imports': 'error', 21 | }, 22 | }, 23 | { 24 | plugins: { 25 | import: importPlugin, 26 | }, 27 | rules: { 28 | // we want to handle this by eslint-plugin-import 29 | 'n/no-missing-import': 'off', 30 | 'import/order': [ 31 | 'error', 32 | { 33 | groups: [ 34 | 'builtin', 35 | 'external', 36 | ['sibling', 'parent'], 37 | 'index', 38 | 'type', 39 | ], 40 | 'newlines-between': 'always', 41 | alphabetize: { 42 | order: 'asc', 43 | }, 44 | }, 45 | ], 46 | }, 47 | }, 48 | { 49 | linterOptions: { 50 | reportUnusedDisableDirectives: 'error', 51 | }, 52 | }, 53 | ); 54 | -------------------------------------------------------------------------------- /packages/-internals/src/eslint/browser.js: -------------------------------------------------------------------------------- 1 | import globals from 'globals'; 2 | import ts from 'typescript-eslint'; 3 | 4 | export default ts.config({ 5 | languageOptions: { 6 | globals: { 7 | ...globals.browser, 8 | }, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /packages/-internals/src/eslint/index.js: -------------------------------------------------------------------------------- 1 | export { default as base } from './base.js'; 2 | export { default as browser } from './browser.js'; 3 | export { default as nodeESM } from './node-esm.js'; 4 | export { default as nodeCJS } from './node-cjs.js'; 5 | -------------------------------------------------------------------------------- /packages/-internals/src/eslint/node-cjs.js: -------------------------------------------------------------------------------- 1 | import n from 'eslint-plugin-n'; 2 | import globals from 'globals'; 3 | import ts from 'typescript-eslint'; 4 | 5 | export default ts.config(n.configs['flat/recommended'], { 6 | languageOptions: { 7 | sourceType: 'script', 8 | globals: { 9 | ...globals.node, 10 | }, 11 | }, 12 | }); 13 | -------------------------------------------------------------------------------- /packages/-internals/src/eslint/node-esm.js: -------------------------------------------------------------------------------- 1 | import n from 'eslint-plugin-n'; 2 | import globals from 'globals'; 3 | import ts from 'typescript-eslint'; 4 | 5 | export default ts.config( 6 | n.configs['flat/recommended'], 7 | { 8 | languageOptions: { 9 | sourceType: 'module', 10 | globals: { 11 | ...globals.node, 12 | }, 13 | }, 14 | }, 15 | { 16 | rules: { 17 | // we want to handle this by eslint-plugin-import 18 | 'n/no-missing-import': 'off', 19 | }, 20 | }, 21 | ); 22 | -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/LQIP-blurhash-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/LQIP-blurhash-1.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/LQIP-blurhash-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/LQIP-blurhash-2.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/LQIP-color-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/LQIP-color-1.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/LQIP-color-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/LQIP-color-2.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/LQIP-inline-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/LQIP-inline-1.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/LQIP-inline-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/LQIP-inline-2.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/LQIP-thumbhash-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/LQIP-thumbhash-1.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/LQIP-thumbhash-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/LQIP-thumbhash-2.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/fixed-layout-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/fixed-layout-1.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/fixed-layout-w-aspect-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/fixed-layout-w-aspect-1.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/index.js -------------------------------------------------------------------------------- /packages/-internals/src/playwright/__screenshots__/responsive-layout-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/-internals/src/playwright/__screenshots__/responsive-layout-1.png -------------------------------------------------------------------------------- /packages/-internals/src/playwright/helpers.ts: -------------------------------------------------------------------------------- 1 | import type { Locator, Page } from '@playwright/test'; 2 | 3 | export async function delayImageLoading(page: Page) { 4 | let load: () => void = () => { 5 | throw new Error('load helper was not assigned properly'); 6 | }; 7 | let promise: Promise; 8 | 9 | await page.route('**/*.{png,jpg,webp,avif}', async (route) => { 10 | if (!promise) { 11 | const p = new Promise((r) => (load = () => r(undefined))); 12 | promise = p; 13 | } 14 | await promise; 15 | await route.continue(); 16 | }); 17 | 18 | return { 19 | load() { 20 | load(); 21 | }, 22 | }; 23 | } 24 | export function getImage( 25 | locator: Locator, 26 | isShadowDom: boolean, 27 | ): { img: Locator; picture: Locator } { 28 | const img = isShadowDom ? locator.locator('img') : locator; 29 | const picture = isShadowDom 30 | ? locator.locator('picture') 31 | : locator.page().locator('picture').filter({ has: img }); 32 | 33 | return { img, picture }; 34 | } 35 | -------------------------------------------------------------------------------- /packages/-internals/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "include": ["src/**/*", "unpublished-development-types/**/*"], 4 | "compilerOptions": { 5 | "module": "ESNext", 6 | "moduleResolution": "Bundler", 7 | "target": "ESNext", 8 | "noEmit": true, 9 | "allowImportingTsExtensions": true, 10 | "verbatimModuleSyntax": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/build-utils/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | 4 | -------------------------------------------------------------------------------- /packages/build-utils/README.md: -------------------------------------------------------------------------------- 1 | # @responsive-image/build-utils 2 | 3 | Internal package for shared build-related code. 4 | 5 | Part of the [ResponsiveImage](https://github.com/simonihmig/responsive-image) project. 6 | 7 | ## License 8 | 9 | This project is licensed under the [MIT License](../../LICENSE.md). 10 | -------------------------------------------------------------------------------- /packages/build-utils/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { base, nodeESM } from '@responsive-image/internals/eslint'; 2 | 3 | export default [...base, ...nodeESM]; 4 | -------------------------------------------------------------------------------- /packages/build-utils/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './resize'; 2 | export * from './lqip'; 3 | export * from './utils'; 4 | export * from './serialize'; 5 | export * from './types'; 6 | -------------------------------------------------------------------------------- /packages/build-utils/src/lqip.ts: -------------------------------------------------------------------------------- 1 | import baseN from 'base-n'; 2 | 3 | const generatedClassNames = new Map(); 4 | const b64 = baseN.create(); 5 | 6 | export function generateLqipClassName(resource: string): string { 7 | if (generatedClassNames.has(resource)) { 8 | return generatedClassNames.get(resource)!; 9 | } else { 10 | const className = `ri-dyn-${b64.encode(generatedClassNames.size)}`; 11 | generatedClassNames.set(resource, className); 12 | 13 | return className; 14 | } 15 | } 16 | 17 | export function blurrySvg(src: string, width: number, height: number): string { 18 | return ` 19 | 20 | 21 | `; 22 | } 23 | -------------------------------------------------------------------------------- /packages/build-utils/src/serialize.ts: -------------------------------------------------------------------------------- 1 | import { stringify } from 'javascript-stringify'; 2 | 3 | import type { SafeString } from './types'; 4 | 5 | export const SAFE_STRING = Symbol(); 6 | 7 | export function serialize(value: unknown) { 8 | return stringify( 9 | value, 10 | (value, _space, next) => { 11 | if (typeof value === 'object' && isSafeString(value)) { 12 | return value; 13 | } 14 | 15 | return next(value); 16 | }, 17 | 2, 18 | ); 19 | } 20 | 21 | /** 22 | * Represents a string that is safe to use as a POJO value, without escaping quotes. E.g. a function definition like '() => true' 23 | */ 24 | export function safeString(value: string): SafeString { 25 | const safeValue = Object.assign(value, { [SAFE_STRING]: true as const }); 26 | 27 | return safeValue; 28 | } 29 | 30 | export function isSafeString(value: string | SafeString): boolean { 31 | return (value as SafeString)[SAFE_STRING] === true; 32 | } 33 | -------------------------------------------------------------------------------- /packages/build-utils/tests/serialize.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from 'vitest'; 2 | 3 | import { isSafeString, safeString, serialize } from '../src'; 4 | 5 | describe('safeString', () => { 6 | test('safeString returns a safe string', () => { 7 | expect(isSafeString('foo')).toBe(false); 8 | expect(isSafeString(safeString('foo'))).toBe(true); 9 | }); 10 | }); 11 | 12 | describe('serialize', () => { 13 | test('serializes simple string', () => { 14 | expect(serialize({ foo: 'bar' })).toBe(`{ 15 | foo: 'bar' 16 | }`); 17 | }); 18 | 19 | test('serializes safe string', () => { 20 | expect(serialize({ cb: safeString(`() => 'foo'`) })).toBe( 21 | `{ 22 | cb: () => 'foo' 23 | }`, 24 | ); 25 | }); 26 | 27 | test('serializes pojo', () => { 28 | expect(serialize({ valid: true, foo: 'bar', nothing: undefined })).toBe( 29 | `{ 30 | valid: true, 31 | foo: 'bar', 32 | nothing: undefined 33 | }`, 34 | ); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/build-utils/tests/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from 'vitest'; 2 | 3 | import { generateLqipClassName } from '../src'; 4 | 5 | describe('generateLqipClassName', () => { 6 | test('it generates unique class names', () => { 7 | expect(generateLqipClassName('foo')).toBe('ri-dyn-0'); 8 | expect(generateLqipClassName('bar')).toBe('ri-dyn-1'); 9 | expect(generateLqipClassName('baz')).toBe('ri-dyn-2'); 10 | 11 | expect(generateLqipClassName('foo')).toBe('ri-dyn-0'); 12 | expect(generateLqipClassName('bar')).toBe('ri-dyn-1'); 13 | expect(generateLqipClassName('baz')).toBe('ri-dyn-2'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/build-utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "verbatimModuleSyntax": true 7 | }, 8 | "include": ["src/**/*", "types/**/*", "tests/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/build-utils/types/base-n.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'base-n' { 2 | class BaseN { 3 | encode(i: number): string; 4 | } 5 | 6 | class factory { 7 | static create: (options?: unknown) => BaseN; 8 | } 9 | export default factory; 10 | } 11 | -------------------------------------------------------------------------------- /packages/build-utils/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | testTimeout: 60000, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/cdn/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | 4 | -------------------------------------------------------------------------------- /packages/cdn/README.md: -------------------------------------------------------------------------------- 1 | # @responsive-image/cdn 2 | 3 | Support for loading remote images from Image CDNs. 4 | 5 | Part of the [ResponsiveImage](https://github.com/simonihmig/responsive-image) project. 6 | 7 | ## Documentation 8 | 9 | Visit [responsive-image.dev](https://responsive-image.dev) for our complete documentation site. 10 | 11 | ## License 12 | 13 | This project is licensed under the [MIT License](../../LICENSE.md). 14 | -------------------------------------------------------------------------------- /packages/cdn/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | [ 4 | "@babel/plugin-transform-typescript", 5 | { 6 | "allExtensions": true, 7 | "onlyRemoveTypeImports": true, 8 | "allowDeclareFields": true 9 | } 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/cdn/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { base, browser, nodeESM } from '@responsive-image/internals/eslint'; 2 | 3 | export default [ 4 | ...base, 5 | ...browser.map((c) => ({ 6 | ...c, 7 | files: ['src/**/*'], 8 | })), 9 | ...nodeESM.map((c) => ({ 10 | ...c, 11 | files: ['eslint.config.mjs', 'rollup.config.mjs'], 12 | })), 13 | ]; 14 | -------------------------------------------------------------------------------- /packages/cdn/rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import { babel } from '@rollup/plugin-babel'; 2 | import copy from 'rollup-plugin-copy'; 3 | 4 | export default { 5 | input: 'src/index.ts', 6 | output: { 7 | dir: './dist', 8 | }, 9 | 10 | plugins: [ 11 | babel({ 12 | extensions: ['.js', '.ts'], 13 | }), 14 | 15 | // Copy Readme and License into published package 16 | copy({ 17 | targets: [ 18 | // { src: '../../README.md', dest: '.' }, 19 | { src: '../../LICENSE.md', dest: '.' }, 20 | ], 21 | }), 22 | ], 23 | }; 24 | -------------------------------------------------------------------------------- /packages/cdn/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './cloudinary.ts'; 2 | export * from './fastly.ts'; 3 | export * from './imgix.ts'; 4 | export * from './netlify.ts'; 5 | export * from './types.ts'; 6 | -------------------------------------------------------------------------------- /packages/cdn/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { CloudinaryConfig } from './cloudinary'; 2 | import type { FastlyConfig } from './fastly'; 3 | import type { ImgixConfig } from './imgix'; 4 | import type { NetlifyConfig } from './netlify'; 5 | import type { ImageTypeAuto, ImageType } from '@responsive-image/core'; 6 | 7 | export interface Config { 8 | imgix?: ImgixConfig; 9 | fastly?: FastlyConfig; 10 | cloudinary?: CloudinaryConfig; 11 | netlify?: NetlifyConfig; 12 | } 13 | 14 | export interface CoreOptions { 15 | formats?: ImageType[] | ImageTypeAuto; 16 | quality?: number; 17 | aspectRatio?: number; 18 | } 19 | -------------------------------------------------------------------------------- /packages/cdn/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "include": ["src/**/*", "unpublished-development-types/**/*"], 4 | "compilerOptions": { 5 | "module": "ESNext", 6 | "moduleResolution": "Bundler", 7 | "target": "ESNext", 8 | "noEmit": true, 9 | "allowImportingTsExtensions": true, 10 | "verbatimModuleSyntax": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | 4 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # @responsive-image/core 2 | 3 | Internal package for shared runtime code. 4 | 5 | Part of the [ResponsiveImage](https://github.com/simonihmig/responsive-image) project. 6 | 7 | ## Documentation 8 | 9 | Visit [responsive-image.dev](https://responsive-image.dev) for our complete documentation site. 10 | 11 | ## License 12 | 13 | This project is licensed under the [MIT License](../../LICENSE.md). 14 | -------------------------------------------------------------------------------- /packages/core/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | [ 4 | "@babel/plugin-transform-typescript", 5 | { 6 | "allExtensions": true, 7 | "onlyRemoveTypeImports": true, 8 | "allowDeclareFields": true 9 | } 10 | ] 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { base, browser, nodeESM } from '@responsive-image/internals/eslint'; 2 | 3 | export default [ 4 | ...base, 5 | ...browser.map((c) => ({ 6 | ...c, 7 | files: ['src/**/*'], 8 | })), 9 | ...nodeESM.map((c) => ({ 10 | ...c, 11 | files: ['eslint.config.mjs', 'rollup.config.mjs'], 12 | })), 13 | ]; 14 | -------------------------------------------------------------------------------- /packages/core/rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import { babel } from '@rollup/plugin-babel'; 2 | import copy from 'rollup-plugin-copy'; 3 | 4 | export default { 5 | input: 'src/index.ts', 6 | output: { 7 | dir: './dist', 8 | }, 9 | 10 | plugins: [ 11 | babel({ 12 | extensions: ['.js', '.ts'], 13 | }), 14 | 15 | // Copy Readme and License into published package 16 | copy({ 17 | targets: [ 18 | // { src: '../../README.md', dest: '.' }, 19 | { src: '../../LICENSE.md', dest: '.' }, 20 | ], 21 | }), 22 | ], 23 | }; 24 | -------------------------------------------------------------------------------- /packages/core/src/blurhash/decode.ts: -------------------------------------------------------------------------------- 1 | import { decode } from 'blurhash'; 2 | 3 | const BLURHASH_SCALE_FACTOR = 4; 4 | 5 | export function decode2url( 6 | hash: string, 7 | width: number, 8 | height: number, 9 | ): string | undefined { 10 | const blurWidth = width * BLURHASH_SCALE_FACTOR; 11 | const blurHeight = height * BLURHASH_SCALE_FACTOR; 12 | const pixels = decode(hash, blurWidth, blurHeight); 13 | const canvas = document.createElement('canvas'); 14 | canvas.width = blurWidth; 15 | canvas.height = blurHeight; 16 | const ctx = canvas.getContext('2d'); 17 | if (!ctx) { 18 | return undefined; 19 | } 20 | 21 | const imageData = ctx.createImageData(blurWidth, blurHeight); 22 | imageData.data.set(pixels); 23 | ctx.putImageData(imageData, 0, 0); 24 | return canvas.toDataURL('image/png'); 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/blurhash/ssr.ts: -------------------------------------------------------------------------------- 1 | import { decode2url } from './decode'; 2 | 3 | const LQIP_ATTRIBUTE = 'data-ri-lqip'; 4 | 5 | export function applySSR(): void { 6 | const images = document.querySelectorAll( 7 | `img[${LQIP_ATTRIBUTE}^="bh:"]`, 8 | ); 9 | 10 | images.forEach((image) => { 11 | const value = image.getAttribute(LQIP_ATTRIBUTE); 12 | if (!value) { 13 | return; 14 | } 15 | 16 | const matches = /bh:(\d+):(\d+):(.+)/.exec(value); 17 | 18 | if (!matches) { 19 | return; 20 | } 21 | 22 | const [, width, height, hash] = matches; 23 | 24 | if (hash && width && height) { 25 | const url = decode2url(hash, parseInt(width, 10), parseInt(height, 10)); 26 | if (url) { 27 | image.style.backgroundImage = `url("${url}")`; 28 | } 29 | } 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /packages/core/src/config.ts: -------------------------------------------------------------------------------- 1 | const configNamespaces = new Map(); 2 | 3 | export function getConfig>( 4 | namespace: string, 5 | ): C | undefined { 6 | return configNamespaces.get(namespace) as C | undefined; 7 | } 8 | 9 | export function setConfig>( 10 | namespace: string, 11 | config: C, 12 | ): void { 13 | configNamespaces.set(namespace, config); 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/debug.ts: -------------------------------------------------------------------------------- 1 | export function assert(message: string, check: boolean): asserts check is true { 2 | if (!check) { 3 | throw new Error(`Assertion failed: ${message}`); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/env.ts: -------------------------------------------------------------------------------- 1 | import { getConfig } from './config.ts'; 2 | 3 | import type { Env, EnvConfig } from './types'; 4 | 5 | export const env: Env = { 6 | get screenWidth(): number { 7 | return typeof screen !== 'undefined' 8 | ? screen.width 9 | : (env.deviceWidths.at(-1) ?? 320); 10 | }, 11 | get physicalWidth(): number { 12 | return env.screenWidth * env.devicePixelRatio; 13 | }, 14 | get devicePixelRatio(): number { 15 | return typeof window !== 'undefined' ? (window?.devicePixelRatio ?? 1) : 1; 16 | }, 17 | get deviceWidths(): number[] { 18 | return ( 19 | getConfig('env')?.deviceWidths ?? [ 20 | 640, 750, 828, 1080, 1200, 1920, 2048, 3840, 21 | ] 22 | ); 23 | }, 24 | }; 25 | 26 | export function getDestinationWidthBySize(size = 100): number { 27 | const factor = size / 100; 28 | 29 | return env.physicalWidth * factor; 30 | } 31 | -------------------------------------------------------------------------------- /packages/core/src/index.ts: -------------------------------------------------------------------------------- 1 | export { findMatchingImage } from './match.ts'; 2 | export * from './env.ts'; 3 | export * from './config.ts'; 4 | export * from './debug.ts'; 5 | export * from './resolve.ts'; 6 | export * from './utils.ts'; 7 | 8 | export type * from './types.ts'; 9 | -------------------------------------------------------------------------------- /packages/core/src/match.ts: -------------------------------------------------------------------------------- 1 | import type { ImageOutputResult, ImageType } from './types.ts'; 2 | 3 | export function findMatchingImage( 4 | images: ImageOutputResult[], 5 | width: number, 6 | type: ImageType, 7 | ): ImageOutputResult | undefined { 8 | const matchingImageWidth = images 9 | .map((i) => i.width) 10 | .reduce((prevValue: number, w: number) => { 11 | if (w >= width && prevValue >= width) { 12 | return w >= prevValue ? prevValue : w; 13 | } else { 14 | return w >= prevValue ? w : prevValue; 15 | } 16 | }, 0); 17 | 18 | return images.find( 19 | (image) => image.width === matchingImageWidth && image.format === type, 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/src/resolve.ts: -------------------------------------------------------------------------------- 1 | import { getDestinationWidthBySize } from './env'; 2 | 3 | import type { ImageData, ImageUrlForType } from './types'; 4 | 5 | export interface ResolveImageOptions { 6 | /** 7 | * width in pixel to request the best matching image for 8 | */ 9 | width?: number; 10 | 11 | /** 12 | * size in vw (0 - 100) to request the best matching image for 13 | */ 14 | size?: number; 15 | 16 | /** 17 | * preferred image format 18 | */ 19 | format?: ImageUrlForType; 20 | } 21 | 22 | export function resolveImage( 23 | data: ImageData, 24 | { width, size, format }: ResolveImageOptions = {}, 25 | ): string | undefined { 26 | const imageType = 27 | format ?? 28 | (Array.isArray(data.imageTypes) ? data.imageTypes[0] : data.imageTypes); 29 | 30 | const url = data.imageUrlFor( 31 | width ?? getDestinationWidthBySize(size), 32 | imageType, 33 | ); 34 | 35 | return url; 36 | } 37 | -------------------------------------------------------------------------------- /packages/core/src/thumbhash/decode.ts: -------------------------------------------------------------------------------- 1 | // Replace this with the native feature once fully available in browsers! 2 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array/fromBase64 3 | // https://caniuse.com/?search=fromBase64 4 | import { decode } from 'base64-arraybuffer'; 5 | import { thumbHashToDataURL } from 'thumbhash'; 6 | 7 | export function decode2url(hash: string): string | undefined { 8 | const arrayBuffer = decode(hash); 9 | return thumbHashToDataURL(new Uint8Array(arrayBuffer)); 10 | } 11 | -------------------------------------------------------------------------------- /packages/core/src/thumbhash/ssr.ts: -------------------------------------------------------------------------------- 1 | import { decode2url } from './decode'; 2 | 3 | const LQIP_ATTRIBUTE = 'data-ri-lqip'; 4 | 5 | export function applySSR(): void { 6 | const images = document.querySelectorAll( 7 | `img[${LQIP_ATTRIBUTE}^="th:"]`, 8 | ); 9 | 10 | images.forEach((image) => { 11 | const value = image.getAttribute(LQIP_ATTRIBUTE); 12 | if (!value) { 13 | return; 14 | } 15 | 16 | const matches = /th:(.+)/.exec(value); 17 | 18 | if (!matches) { 19 | return; 20 | } 21 | 22 | const [, hash] = matches; 23 | 24 | if (hash) { 25 | const url = decode2url(hash); 26 | if (url) { 27 | image.style.backgroundImage = `url("${url}")`; 28 | } 29 | } 30 | }); 31 | } 32 | -------------------------------------------------------------------------------- /packages/core/src/utils.ts: -------------------------------------------------------------------------------- 1 | import type { NonFunction, ValueOrCallback } from './types'; 2 | 3 | export function getValueOrCallback( 4 | valueOrCb: ValueOrCallback, 5 | ): T { 6 | if (typeof valueOrCb === 'function') { 7 | return valueOrCb(); 8 | } else { 9 | return valueOrCb; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/tests/config.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from 'vitest'; 2 | 3 | import { getConfig, setConfig } from '../src'; 4 | 5 | test('setConfig and getConfig work', () => { 6 | const dummyConfig = { foo: 'bar' }; 7 | 8 | setConfig('dummy', dummyConfig); 9 | 10 | expect(getConfig('dummy')).toBe(dummyConfig); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/core/tests/match.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from 'vitest'; 2 | 3 | import { findMatchingImage } from '../src'; 4 | 5 | import type { ImageOutputResult } from '../src'; 6 | 7 | describe('findMatchingImage', function () { 8 | test('find matching image', function () { 9 | const images: ImageOutputResult[] = [ 10 | { url: 'jpg100', width: 100, format: 'jpeg' }, 11 | { url: 'jpg200', width: 200, format: 'jpeg' }, 12 | { url: 'jpg300', width: 300, format: 'jpeg' }, 13 | { url: 'jpg400', width: 400, format: 'jpeg' }, 14 | { url: 'webp100', width: 100, format: 'webp' }, 15 | { url: 'webp200', width: 200, format: 'webp' }, 16 | { url: 'webp300', width: 300, format: 'webp' }, 17 | { url: 'webp400', width: 400, format: 'webp' }, 18 | ]; 19 | 20 | expect(findMatchingImage(images, 200, 'jpeg')?.url).toBe('jpg200'); 21 | expect(findMatchingImage(images, 201, 'jpeg')?.url).toBe('jpg300'); 22 | expect(findMatchingImage(images, 200, 'webp')?.url).toBe('webp200'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /packages/core/tests/resolve.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, test } from 'vitest'; 2 | 3 | import { resolveImage } from '../src'; 4 | 5 | import type { ImageData } from '../src'; 6 | 7 | describe('resolveImage', () => { 8 | const imageData: ImageData = { 9 | imageTypes: ['webp', 'avif'], 10 | imageUrlFor(width, type = 'jpeg') { 11 | return `/${width}.${type}`; 12 | }, 13 | }; 14 | 15 | test('returns image matching device width with no args', () => { 16 | expect(resolveImage(imageData)).toBe('/3840.webp'); 17 | }); 18 | 19 | test('supports size', () => { 20 | expect(resolveImage(imageData, { size: 10 })).toBe('/384.webp'); 21 | }); 22 | 23 | test('supports width', () => { 24 | expect(resolveImage(imageData, { width: 1024 })).toBe('/1024.webp'); 25 | }); 26 | 27 | test('supports format', () => { 28 | expect(resolveImage(imageData, { format: 'avif' })).toBe('/3840.avif'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "include": ["src/**/*", "unpublished-development-types/**/*"], 4 | "compilerOptions": { 5 | "module": "ESNext", 6 | "moduleResolution": "Bundler", 7 | "target": "ESNext", 8 | "noEmit": true, 9 | "allowImportingTsExtensions": true, 10 | "verbatimModuleSyntax": true 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/ember/.gitignore: -------------------------------------------------------------------------------- 1 | # The authoritative copies of these live in the monorepo root (because they're 2 | # more useful on github that way), but the build copies them into here so they 3 | # will also appear in published NPM packages. 4 | /LICENSE.md 5 | 6 | # compiled output 7 | /dist 8 | /declarations 9 | 10 | # npm/pnpm/yarn pack output 11 | *.tgz 12 | -------------------------------------------------------------------------------- /packages/ember/.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | plugins: ['prettier-plugin-ember-template-tag'], 5 | singleQuote: true, 6 | templateSingleQuote: false, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/ember/.template-lintrc.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | extends: 'recommended', 5 | }; 6 | -------------------------------------------------------------------------------- /packages/ember/README.md: -------------------------------------------------------------------------------- 1 | # @responsive-image/ember 2 | 3 | Ember addon to render responsive images, provided as locally processed images or loaded remotely from Image CDNs. 4 | 5 | Part of the [ResponsiveImage](https://github.com/simonihmig/responsive-image) project. 6 | 7 | ## Compatibility 8 | 9 | - Ember.js v4.12 or above 10 | 11 | ## Documentation 12 | 13 | Visit [responsive-image.dev](https://responsive-image.dev) for our complete documentation site. 14 | 15 | ## License 16 | 17 | This project is licensed under the [MIT License](LICENSE.md). 18 | -------------------------------------------------------------------------------- /packages/ember/addon-main.cjs: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { addonV1Shim } = require('@embroider/addon-shim'); 4 | module.exports = addonV1Shim(__dirname); 5 | -------------------------------------------------------------------------------- /packages/ember/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | [ 4 | "@babel/plugin-transform-typescript", 5 | { 6 | "allExtensions": true, 7 | "onlyRemoveTypeImports": true, 8 | "allowDeclareFields": true 9 | } 10 | ], 11 | "@embroider/addon-dev/template-colocation-plugin", 12 | [ 13 | "babel-plugin-ember-template-compilation", 14 | { 15 | "targetFormat": "hbs", 16 | "transforms": [] 17 | } 18 | ], 19 | [ 20 | "module:decorator-transforms", 21 | { "runtime": { "import": "decorator-transforms/runtime" } } 22 | ] 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /packages/ember/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { 2 | base, 3 | browser, 4 | nodeCJS, 5 | nodeESM, 6 | } from '@responsive-image/internals/eslint'; 7 | import ember from 'eslint-plugin-ember/recommended'; 8 | 9 | export default [ 10 | ...base, 11 | ...browser, 12 | ember.configs.base, 13 | ember.configs.gjs, 14 | ember.configs.gts, 15 | { 16 | files: ['src/**/*'], 17 | rules: { 18 | // require relative imports use full extensions 19 | 'import/extensions': ['error', 'always', { ignorePackages: true }], 20 | }, 21 | }, 22 | ...nodeCJS.map((c) => ({ 23 | ...c, 24 | files: ['**/*.cjs'], 25 | })), 26 | ...nodeESM.map((c) => ({ 27 | ...c, 28 | files: ['**/*.mjs'], 29 | })), 30 | ]; 31 | -------------------------------------------------------------------------------- /packages/ember/src/blurhash.ts: -------------------------------------------------------------------------------- 1 | // BlurHash SSR support 2 | // Expose this as a public asset separate from the compiled addon source, to allow to load this in a SSR (FastBoot) environment ahead of the main bundle. 3 | // Note: this is only needed because of legacy output semantics in Ember's index.html. With input semantics in a Vite build, this shouldn't be needed, as 4 | // you should be able to import this directly and let the bundler figoure out the rest. 5 | export { applySSR } from '@responsive-image/core/blurhash/ssr'; 6 | -------------------------------------------------------------------------------- /packages/ember/src/components/responsive-image.css: -------------------------------------------------------------------------------- 1 | .ri-img { 2 | background-size: cover; 3 | } 4 | 5 | .ri-responsive { 6 | width: 100%; 7 | height: auto; 8 | } 9 | 10 | .ri-fixed, 11 | .ri-responsive { 12 | content-visibility: auto; 13 | } 14 | -------------------------------------------------------------------------------- /packages/ember/src/helpers/responsive-image-cloudinary.ts: -------------------------------------------------------------------------------- 1 | export { cloudinary as default } from '@responsive-image/cdn'; 2 | -------------------------------------------------------------------------------- /packages/ember/src/helpers/responsive-image-fastly.ts: -------------------------------------------------------------------------------- 1 | export { fastly as default } from '@responsive-image/cdn'; 2 | -------------------------------------------------------------------------------- /packages/ember/src/helpers/responsive-image-imgix.ts: -------------------------------------------------------------------------------- 1 | export { imgix as default } from '@responsive-image/cdn'; 2 | -------------------------------------------------------------------------------- /packages/ember/src/helpers/responsive-image-netlify.ts: -------------------------------------------------------------------------------- 1 | export { netlify as default } from '@responsive-image/cdn'; 2 | -------------------------------------------------------------------------------- /packages/ember/src/helpers/responsive-image-resolve.ts: -------------------------------------------------------------------------------- 1 | import { resolveImage } from '@responsive-image/core'; 2 | 3 | export default resolveImage; 4 | -------------------------------------------------------------------------------- /packages/ember/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ResponsiveImage } from './components/responsive-image.gts'; 2 | export { default as resolve } from './helpers/responsive-image-resolve.ts'; 3 | export type { ImageData } from '@responsive-image/core'; 4 | -------------------------------------------------------------------------------- /packages/ember/src/template-registry.ts: -------------------------------------------------------------------------------- 1 | import type ResponsiveImageComponent from './components/responsive-image.gts'; 2 | import type CloudinaryProvider from './helpers/responsive-image-cloudinary'; 3 | import type FastlyProvider from './helpers/responsive-image-fastly'; 4 | import type ImgixProvider from './helpers/responsive-image-imgix.ts'; 5 | import type NetlifyProvider from './helpers/responsive-image-netlify'; 6 | import type ResponsiveImageResolve from './helpers/responsive-image-resolve.ts'; 7 | 8 | export default interface Registry { 9 | ResponsiveImage: typeof ResponsiveImageComponent; 10 | 'responsive-image-resolve': typeof ResponsiveImageResolve; 11 | 'responsive-image-cloudinary': typeof CloudinaryProvider; 12 | 'responsive-image-fastly': typeof FastlyProvider; 13 | 'responsive-image-netlify': typeof NetlifyProvider; 14 | 'responsive-image-imgix': typeof ImgixProvider; 15 | } 16 | -------------------------------------------------------------------------------- /packages/ember/src/thumbhash.ts: -------------------------------------------------------------------------------- 1 | // ThumbHash SSR support 2 | // Expose this as a public asset separate from the compiled addon source, to allow to load this in a SSR (FastBoot) environment ahead of the main bundle. 3 | // Note: this is only needed because of legacy output semantics in Ember's index.html. With input semantics in a Vite build, this shouldn't be needed, as 4 | // you should be able to import this directly and let the bundler figoure out the rest. 5 | export { applySSR } from '@responsive-image/core/thumbhash/ssr'; 6 | -------------------------------------------------------------------------------- /packages/ember/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/ember/tsconfig.json", 3 | "include": ["src/**/*", "unpublished-development-types/**/*"], 4 | "glint": { 5 | "environment": ["ember-loose", "ember-template-imports"] 6 | }, 7 | "compilerOptions": { 8 | "allowJs": true, 9 | "declarationDir": "declarations", 10 | 11 | /** 12 | https://www.typescriptlang.org/tsconfig#rootDir 13 | "Default: The longest common path of all non-declaration input files." 14 | 15 | Because we want our declarations' structure to match our rollup output, 16 | we need this "rootDir" to match the "srcDir" in the rollup.config.mjs. 17 | 18 | This way, we can have simpler `package.json#exports` that matches 19 | imports to files on disk 20 | */ 21 | "rootDir": "./src", 22 | 23 | /** 24 | https://www.typescriptlang.org/tsconfig#verbatimModuleSyntax 25 | 26 | We don't want to include types dependencies in our compiled output, so tell TypeScript 27 | to enforce using `import type` instead of `import` for Types. 28 | */ 29 | "verbatimModuleSyntax": true, 30 | 31 | /** 32 | https://www.typescriptlang.org/tsconfig#allowImportingTsExtensions 33 | 34 | We want our tooling to know how to resolve our custom files so the appropriate plugins 35 | can do the proper transformations on those files. 36 | */ 37 | "allowImportingTsExtensions": true, 38 | 39 | "types": ["ember-source/types"] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/ember/unpublished-development-types/glint.d.ts: -------------------------------------------------------------------------------- 1 | import '@glint/environment-ember-loose'; 2 | import 'ember-cached-decorator-polyfill'; 3 | -------------------------------------------------------------------------------- /packages/react/.gitignore: -------------------------------------------------------------------------------- 1 | ## editors 2 | /.idea 3 | /.vscode 4 | 5 | ## system files 6 | .DS_Store 7 | 8 | ## npm 9 | /node_modules/ 10 | /npm-debug.log 11 | 12 | ## testing 13 | /coverage/ 14 | 15 | ## temp folders 16 | /.tmp/ 17 | 18 | # build 19 | /_site/ 20 | /dist/ 21 | /out-tsc/ 22 | 23 | storybook-static 24 | custom-elements.json 25 | -------------------------------------------------------------------------------- /packages/react/README.md: -------------------------------------------------------------------------------- 1 | # @responsive-image/react 2 | 3 | React component to render responsive images, provided as locally processed images or loaded remotely from Image CDNs. 4 | 5 | Part of the [ResponsiveImage](https://github.com/simonihmig/responsive-image) project. 6 | 7 | ## Compatibility 8 | 9 | - React 18 or above 10 | 11 | ## Documentation 12 | 13 | Visit [responsive-image.dev](https://responsive-image.dev) for our complete documentation site. 14 | 15 | ## License 16 | 17 | This project is licensed under the [MIT License](LICENSE.md). 18 | -------------------------------------------------------------------------------- /packages/react/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-react", "@babel/preset-typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/react/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { base, browser, nodeESM } from '@responsive-image/internals/eslint'; 2 | 3 | export default [ 4 | ...base, 5 | ...browser.map((c) => ({ 6 | ...c, 7 | files: ['src/**/*'], 8 | })), 9 | ...nodeESM.map((c) => ({ 10 | ...c, 11 | files: ['eslint.config.mjs', 'vitest.config.ts'], 12 | })), 13 | ]; 14 | -------------------------------------------------------------------------------- /packages/react/rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import { Addon } from '@embroider/addon-dev/rollup'; 2 | import { babel } from '@rollup/plugin-babel'; 3 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 4 | import externals from 'rollup-plugin-node-externals'; 5 | import ts from 'typescript'; 6 | 7 | const addon = new Addon({ 8 | srcDir: 'src', 9 | destDir: 'dist', 10 | }); 11 | 12 | export default { 13 | input: 'src/index.tsx', 14 | output: { 15 | dir: 'dist', 16 | format: 'es', 17 | }, 18 | 19 | plugins: [ 20 | babel({ 21 | extensions: ['.ts', '.tsx'], 22 | babelHelpers: 'bundled', 23 | }), 24 | nodeResolve(), 25 | externals(), 26 | addon.keepAssets(['**/*.css']), 27 | { 28 | name: 'ts', 29 | buildEnd() { 30 | const program = ts.createProgram(['src/index.tsx'], { 31 | target: ts.ScriptTarget.ESNext, 32 | module: ts.ModuleKind.ESNext, 33 | moduleResolution: ts.ModuleResolutionKind.NodeJs, 34 | jsx: ts.JsxEmit.React, 35 | allowImportingTsExtensions: true, 36 | rewriteRelativeImportExtensions: true, 37 | outDir: 'dist', 38 | declarationDir: 'dist', 39 | declaration: true, 40 | }); 41 | 42 | program.emit(); 43 | }, 44 | }, 45 | ], 46 | }; 47 | -------------------------------------------------------------------------------- /packages/react/src/index.tsx: -------------------------------------------------------------------------------- 1 | import './responsive-image.css'; // import the CSS here to have an export in package.json that is JS-only 2 | export { ResponsiveImage } from './responsive-image.tsx'; 3 | -------------------------------------------------------------------------------- /packages/react/src/responsive-image.css: -------------------------------------------------------------------------------- 1 | .ri-img { 2 | background-size: cover; 3 | } 4 | 5 | .ri-responsive { 6 | width: 100%; 7 | height: auto; 8 | } 9 | 10 | .ri-fixed, 11 | .ri-responsive { 12 | content-visibility: auto; 13 | } 14 | -------------------------------------------------------------------------------- /packages/react/tests/image.helper.ts: -------------------------------------------------------------------------------- 1 | export function getImg(el: HTMLElement): HTMLImageElement { 2 | const imgEl = 3 | el instanceof HTMLImageElement 4 | ? el 5 | : (el.querySelector('img') ?? el.shadowRoot?.querySelector('img')); 6 | 7 | if (!imgEl) { 8 | throw new Error('No found'); 9 | } 10 | 11 | return imgEl; 12 | } 13 | 14 | export async function trigger(el: HTMLElement) { 15 | const fakeEvent = new Event('load'); 16 | getImg(el).dispatchEvent(fakeEvent); 17 | } 18 | -------------------------------------------------------------------------------- /packages/react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "include": ["src/**/*", "tests/**/*", "./*.ts"], 4 | "compilerOptions": { 5 | "module": "ESNext", 6 | "moduleResolution": "Bundler", 7 | "target": "ESNext", 8 | "noEmit": true, 9 | "allowImportingTsExtensions": true, 10 | "jsx": "react", 11 | "verbatimModuleSyntax": true, 12 | "types": ["@vitest/browser/providers/playwright"] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/react/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig(({ mode }) => { 4 | // to test in server environment, run with "--mode ssr" or "--mode test:ssr" flag 5 | // loads only server.test.ts file 6 | const testSSR = mode === 'test:ssr' || mode === 'ssr'; 7 | 8 | return { 9 | test: { 10 | browser: { 11 | enabled: !testSSR, 12 | provider: 'playwright', 13 | instances: [ 14 | { 15 | name: 'Chrome', 16 | browser: 'chromium', 17 | launch: { channel: 'chrome' }, 18 | }, 19 | ], 20 | }, 21 | watch: false, 22 | isolate: !testSSR, 23 | env: { 24 | NODE_ENV: testSSR ? 'production' : 'development', 25 | DEV: testSSR ? '' : '1', 26 | SSR: testSSR ? '1' : '', 27 | PROD: testSSR ? '1' : '', 28 | }, 29 | transformMode: { web: [/\.[jt]sx$/] }, 30 | ...(testSSR 31 | ? { 32 | include: ['tests/*server.test.{ts,tsx}'], 33 | } 34 | : { 35 | include: ['tests/*.test.{ts,tsx}'], 36 | exclude: ['tests/*server.test.{ts,tsx}'], 37 | }), 38 | ...(testSSR ? { environment: 'node' } : {}), 39 | }, 40 | resolve: { 41 | conditions: testSSR ? ['node'] : ['browser', 'development'], 42 | }, 43 | }; 44 | }); 45 | -------------------------------------------------------------------------------- /packages/solid/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | gitignore 4 | 5 | -------------------------------------------------------------------------------- /packages/solid/README.md: -------------------------------------------------------------------------------- 1 | # @responsive-image/solid 2 | 3 | Solid component to render responsive images, provided as locally processed images or loaded remotely from Image CDNs. 4 | 5 | Part of the [ResponsiveImage](https://github.com/simonihmig/responsive-image) project. 6 | 7 | ## Compatibility 8 | 9 | - Solid 1.6 or above 10 | 11 | ## Documentation 12 | 13 | Visit [responsive-image.dev](https://responsive-image.dev) for our complete documentation site. 14 | 15 | ## License 16 | 17 | This project is licensed under the [MIT License](LICENSE.md). 18 | -------------------------------------------------------------------------------- /packages/solid/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["solid", "@babel/preset-typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /packages/solid/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { base, browser, nodeESM } from '@responsive-image/internals/eslint'; 2 | 3 | export default [ 4 | ...base, 5 | ...browser.map((c) => ({ 6 | ...c, 7 | files: ['src/**/*'], 8 | })), 9 | ...nodeESM.map((c) => ({ 10 | ...c, 11 | files: ['eslint.config.mjs', 'rollup.config.mjs', 'vitest.config.ts'], 12 | })), 13 | ]; 14 | -------------------------------------------------------------------------------- /packages/solid/rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import { Addon } from '@embroider/addon-dev/rollup'; 2 | import { babel } from '@rollup/plugin-babel'; 3 | import { nodeResolve } from '@rollup/plugin-node-resolve'; 4 | import externals from 'rollup-plugin-node-externals'; 5 | import ts from 'typescript'; 6 | 7 | const addon = new Addon({ 8 | srcDir: 'src', 9 | destDir: 'dist', 10 | }); 11 | 12 | export default { 13 | input: 'src/index.tsx', 14 | output: { 15 | dir: 'dist', 16 | format: 'es', 17 | }, 18 | 19 | plugins: [ 20 | babel({ 21 | extensions: ['.ts', '.tsx'], 22 | babelHelpers: 'bundled', 23 | }), 24 | nodeResolve(), 25 | externals(), 26 | addon.keepAssets(['**/*.css']), 27 | { 28 | name: 'ts', 29 | buildEnd() { 30 | const program = ts.createProgram(['src/index.tsx'], { 31 | target: ts.ScriptTarget.ESNext, 32 | module: ts.ModuleKind.ESNext, 33 | moduleResolution: ts.ModuleResolutionKind.NodeJs, 34 | jsx: ts.JsxEmit.Preserve, 35 | jsxImportSource: 'solid-js', 36 | allowImportingTsExtensions: true, 37 | rewriteRelativeImportExtensions: true, 38 | outDir: 'dist', 39 | declarationDir: 'dist', 40 | declaration: true, 41 | }); 42 | 43 | program.emit(); 44 | }, 45 | }, 46 | ], 47 | }; 48 | -------------------------------------------------------------------------------- /packages/solid/src/index.tsx: -------------------------------------------------------------------------------- 1 | export { ResponsiveImage } from './responsive-image.tsx'; 2 | -------------------------------------------------------------------------------- /packages/solid/src/responsive-image.css: -------------------------------------------------------------------------------- 1 | .ri-img { 2 | background-size: cover; 3 | } 4 | 5 | .ri-responsive { 6 | width: 100%; 7 | height: auto; 8 | } 9 | 10 | .ri-fixed, 11 | .ri-responsive { 12 | content-visibility: auto; 13 | } 14 | -------------------------------------------------------------------------------- /packages/solid/test-assets/test-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/solid/test-assets/test-image.jpg -------------------------------------------------------------------------------- /packages/solid/tests/image.helper.ts: -------------------------------------------------------------------------------- 1 | interface ImageLoadedOptions { 2 | rejectOnError?: boolean; 3 | } 4 | 5 | export function getImg(el: HTMLElement): HTMLImageElement { 6 | const imgEl = 7 | el instanceof HTMLImageElement 8 | ? el 9 | : (el.querySelector('img') ?? el.shadowRoot?.querySelector('img')); 10 | 11 | if (!imgEl) { 12 | throw new Error('No found'); 13 | } 14 | 15 | return imgEl; 16 | } 17 | 18 | export async function loaded( 19 | el: HTMLElement, 20 | options: ImageLoadedOptions = {}, 21 | ): Promise { 22 | let resolve: () => void; 23 | let reject: (e: unknown) => void; 24 | const loaded = new Promise((res, rej) => { 25 | resolve = res; 26 | reject = rej; 27 | }); 28 | 29 | const imgEl = getImg(el); 30 | 31 | if (imgEl.complete) { 32 | resolve!(); 33 | } else { 34 | imgEl.addEventListener( 35 | 'load', 36 | () => { 37 | setTimeout(resolve, 0); 38 | }, 39 | { once: true }, 40 | ); 41 | if (options.rejectOnError) { 42 | imgEl.addEventListener( 43 | 'error', 44 | (e) => { 45 | console.error(e.message); 46 | reject(e); 47 | }, 48 | { once: true }, 49 | ); 50 | } 51 | } 52 | 53 | return loaded; 54 | } 55 | 56 | export async function trigger(el: HTMLElement) { 57 | const fakeEvent = new Event('load'); 58 | getImg(el).dispatchEvent(fakeEvent); 59 | } 60 | -------------------------------------------------------------------------------- /packages/solid/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "include": ["src/**/*", "tests/**/*", "./*.ts"], 4 | "compilerOptions": { 5 | "module": "ESNext", 6 | "moduleResolution": "Bundler", 7 | "target": "ESNext", 8 | "noEmit": true, 9 | "allowImportingTsExtensions": true, 10 | "jsx": "preserve", 11 | "jsxImportSource": "solid-js", 12 | "verbatimModuleSyntax": true, 13 | "types": ["@vitest/browser/providers/playwright"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/svelte/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Output 4 | .output 5 | .vercel 6 | .netlify 7 | .wrangler 8 | /.svelte-kit 9 | /build 10 | /dist 11 | 12 | # OS 13 | .DS_Store 14 | Thumbs.db 15 | 16 | # Env 17 | .env 18 | .env.* 19 | !.env.example 20 | !.env.test 21 | 22 | # Vite 23 | vite.config.js.timestamp-* 24 | vite.config.ts.timestamp-* 25 | -------------------------------------------------------------------------------- /packages/svelte/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /packages/svelte/.prettierignore: -------------------------------------------------------------------------------- 1 | # Package Managers 2 | package-lock.json 3 | pnpm-lock.yaml 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /packages/svelte/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte"], 7 | "overrides": [ 8 | { 9 | "files": "*.svelte", 10 | "options": { 11 | "parser": "svelte" 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /packages/svelte/README.md: -------------------------------------------------------------------------------- 1 | # @responsive-image/svelte 2 | 3 | Svelte component to render responsive images, provided as locally processed images or loaded remotely from Image CDNs. 4 | 5 | Part of the [ResponsiveImage](https://github.com/simonihmig/responsive-image) project. 6 | 7 | ## Compatibility 8 | 9 | - Svelte 5 or above 10 | 11 | ## Documentation 12 | 13 | Visit [responsive-image.dev](https://responsive-image.dev) for our complete documentation site. 14 | 15 | ## License 16 | 17 | This project is licensed under the [MIT License](LICENSE.md). 18 | -------------------------------------------------------------------------------- /packages/svelte/eslint.config.js: -------------------------------------------------------------------------------- 1 | import { base, browser, nodeESM } from '@responsive-image/internals/eslint'; 2 | import svelte from 'eslint-plugin-svelte'; 3 | import ts from 'typescript-eslint'; 4 | 5 | export default [ 6 | ...base, 7 | ...browser.map((c) => ({ 8 | ...c, 9 | files: ['src/**/*'] 10 | })), 11 | ...svelte.configs['flat/recommended'], 12 | ...svelte.configs['flat/prettier'], 13 | { 14 | files: ['**/*.svelte', '**/*.svelte.ts'], 15 | languageOptions: { 16 | parserOptions: { 17 | parser: ts.parser 18 | } 19 | } 20 | }, 21 | ...nodeESM.map((c) => ({ 22 | ...c, 23 | files: ['eslint.config.js', 'vite.config.ts'] 24 | })) 25 | ]; 26 | -------------------------------------------------------------------------------- /packages/svelte/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // Reexport your entry components here 2 | 3 | export { default as ResponsiveImage } from './responsive-image.svelte'; 4 | -------------------------------------------------------------------------------- /packages/svelte/svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 2 | 3 | /** @type {import('@sveltejs/kit').Config} */ 4 | const config = { 5 | // Consult https://svelte.dev/docs/kit/integrations 6 | // for more information about preprocessors 7 | preprocess: vitePreprocess() 8 | }; 9 | 10 | export default config; 11 | -------------------------------------------------------------------------------- /packages/svelte/test-assets/test-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/svelte/test-assets/test-image.jpg -------------------------------------------------------------------------------- /packages/svelte/tests/image.helper.ts: -------------------------------------------------------------------------------- 1 | interface ImageLoadedOptions { 2 | rejectOnError?: boolean; 3 | } 4 | 5 | export function getImg(el: HTMLElement): HTMLImageElement { 6 | const imgEl = 7 | el instanceof HTMLImageElement 8 | ? el 9 | : (el.querySelector('img') ?? el.shadowRoot?.querySelector('img')); 10 | 11 | if (!imgEl) { 12 | throw new Error('No found'); 13 | } 14 | 15 | return imgEl; 16 | } 17 | 18 | export async function loaded(el: HTMLElement, options: ImageLoadedOptions = {}): Promise { 19 | let resolve: () => void; 20 | let reject: (e: unknown) => void; 21 | const loaded = new Promise((res, rej) => { 22 | resolve = res; 23 | reject = rej; 24 | }); 25 | 26 | const imgEl = getImg(el); 27 | 28 | if (imgEl.complete) { 29 | resolve!(); 30 | } else { 31 | imgEl.addEventListener( 32 | 'load', 33 | () => { 34 | setTimeout(resolve, 0); 35 | }, 36 | { once: true } 37 | ); 38 | if (options.rejectOnError) { 39 | imgEl.addEventListener( 40 | 'error', 41 | (e) => { 42 | console.error(e.message); 43 | reject(e); 44 | }, 45 | { once: true } 46 | ); 47 | } 48 | } 49 | 50 | return loaded; 51 | } 52 | 53 | export async function trigger(el: HTMLElement) { 54 | const fakeEvent = new Event('load'); 55 | getImg(el).dispatchEvent(fakeEvent); 56 | } 57 | -------------------------------------------------------------------------------- /packages/svelte/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/strictest/tsconfig.json", 3 | "include": [ 4 | "./vite.config.ts", 5 | "./src/**/*.ts", 6 | "./src/**/*.svelte", 7 | "./tests/**/*.ts", 8 | "./tests/**/*.svelte" 9 | ], 10 | "compilerOptions": { 11 | "forceConsistentCasingInFileNames": true, 12 | "resolveJsonModule": true, 13 | "sourceMap": true, 14 | "module": "esnext", 15 | "moduleResolution": "bundler", 16 | "target": "ESNext", 17 | "types": ["@vitest/browser/providers/playwright"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/svelte/turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "extends": ["//"], 4 | "tasks": { 5 | "lint": { 6 | "dependsOn": ["build"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/svelte/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { svelte } from '@sveltejs/vite-plugin-svelte'; 2 | import { defineConfig } from 'vitest/config'; 3 | 4 | export default defineConfig({ 5 | plugins: [svelte()], 6 | 7 | test: { 8 | include: ['tests/**/*.{test,test.svelte}.{js,ts}'], 9 | watch: false, 10 | browser: { 11 | provider: 'playwright', // or 'webdriverio' 12 | enabled: true, 13 | instances: [ 14 | { 15 | browser: 'chromium', 16 | launch: { channel: 'chrome' } 17 | } 18 | ] 19 | } 20 | } 21 | }); 22 | -------------------------------------------------------------------------------- /packages/vite-plugin/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | 4 | -------------------------------------------------------------------------------- /packages/vite-plugin/README.md: -------------------------------------------------------------------------------- 1 | # @responsive-image/vite 2 | 3 | Vite plugins that process and resize local images. 4 | 5 | Part of the ResponsiveImage project. 6 | 7 | ## Compatibility 8 | 9 | - Vite v5, v6 10 | 11 | ## Documentation 12 | 13 | Visit [responsive-image.dev](https://responsive-image.dev) for our complete documentation site. 14 | 15 | ## License 16 | 17 | This project is licensed under the [MIT License](../../LICENSE.md). 18 | -------------------------------------------------------------------------------- /packages/vite-plugin/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { base, nodeESM } from '@responsive-image/internals/eslint'; 2 | 3 | export default [...base, ...nodeESM]; 4 | -------------------------------------------------------------------------------- /packages/vite-plugin/src/index.ts: -------------------------------------------------------------------------------- 1 | import exportPlugin from './export'; 2 | import loaderPlugin from './loader'; 3 | import lqipBlurhashPlugin from './lqip/blurhash'; 4 | import lqipColorPlugin from './lqip/color'; 5 | import lqipColorCssPlugin from './lqip/color-css'; 6 | import lqipInlinePlugin from './lqip/inline'; 7 | import lqipInlineCssPlugin from './lqip/inline-css'; 8 | import lqipThumbhashPlugin from './lqip/thumbhash'; 9 | import resizePlugin from './resize'; 10 | import servePlugin from './serve'; 11 | 12 | import type { Options } from './types'; 13 | 14 | function setupPlugins(options?: Partial) { 15 | return [ 16 | loaderPlugin(options), 17 | resizePlugin(options), 18 | lqipBlurhashPlugin(options), 19 | lqipThumbhashPlugin(options), 20 | lqipColorPlugin(options), 21 | lqipColorCssPlugin(options), 22 | lqipInlinePlugin(options), 23 | lqipInlineCssPlugin(options), 24 | exportPlugin(options), 25 | servePlugin(options), 26 | ]; 27 | } 28 | 29 | export { setupPlugins }; 30 | -------------------------------------------------------------------------------- /packages/vite-plugin/src/loader.ts: -------------------------------------------------------------------------------- 1 | import { normalizeInput } from '@responsive-image/build-utils'; 2 | import { createFilter } from '@rollup/pluginutils'; 3 | 4 | import { getViteOptions, META_KEY } from './utils'; 5 | 6 | import type { Options } from './types'; 7 | import type { Plugin } from 'vite'; 8 | 9 | export default function loaderPlugin( 10 | userOptions: Partial = {}, 11 | ): Plugin { 12 | const filter = createFilter(userOptions.include, userOptions.exclude); 13 | 14 | return { 15 | name: 'responsive-image/loader', 16 | // TODO do we need this? 17 | enforce: 'pre', 18 | async load(id) { 19 | if (!filter(id)) { 20 | return; 21 | } 22 | 23 | const options = getViteOptions(id, userOptions); 24 | const data = normalizeInput(id, { generateHash: options.cache }); 25 | 26 | return { 27 | // Only the export plugin will actually return ESM code 28 | code: '', 29 | moduleSideEffects: false, 30 | meta: { 31 | [META_KEY]: data, 32 | }, 33 | }; 34 | }, 35 | }; 36 | } 37 | -------------------------------------------------------------------------------- /packages/vite-plugin/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { ImageOptions, LqipOptions } from '@responsive-image/build-utils'; 2 | import type { ImageType } from '@responsive-image/core'; 3 | 4 | export interface ViteOptions { 5 | name: string; 6 | cache: boolean; 7 | include?: string | RegExp | Array; 8 | exclude?: string | RegExp | Array; 9 | lqip?: LqipOptions; 10 | styles: 'external' | 'inline'; 11 | } 12 | 13 | export type Options = ViteOptions & ImageOptions; 14 | 15 | export interface ServedImageData { 16 | data: () => Promise; 17 | format: ImageType; 18 | } 19 | -------------------------------------------------------------------------------- /packages/vite-plugin/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getOptions, 3 | type ImageLoaderChainedResult, 4 | } from '@responsive-image/build-utils'; 5 | 6 | import type { Options, ViteOptions } from './types'; 7 | import type { PluginContext } from 'rollup'; 8 | import type { ResolvedConfig } from 'vite'; 9 | 10 | export const META_KEY = 'responsive-image'; 11 | 12 | export const defaultViteOptions = { 13 | name: '[name]-[width]w.[ext]', 14 | cache: true, 15 | styles: 'external', 16 | } satisfies ViteOptions; 17 | 18 | export const viteOptionKeys = [ 19 | 'exclude', 20 | 'include', 21 | 'lqip', 22 | 'name', 23 | 'styles', 24 | ] satisfies Array; 25 | 26 | export function getViteOptions( 27 | id: string | URL, 28 | loaderOptions: Partial, 29 | ): Options { 30 | return getOptions(id, defaultViteOptions, loaderOptions); 31 | } 32 | 33 | export function getInput( 34 | context: PluginContext, 35 | id: string, 36 | ): ImageLoaderChainedResult | undefined { 37 | const info = context.getModuleInfo(id); 38 | return info?.meta[META_KEY]; 39 | } 40 | 41 | export function getViteBasePath(viteConfig: ResolvedConfig): string { 42 | return `${ 43 | viteConfig.base.endsWith('/') 44 | ? viteConfig.base.slice(0, -1) 45 | : viteConfig.base 46 | }/@responsive-image/vite-plugin/`; 47 | } 48 | -------------------------------------------------------------------------------- /packages/vite-plugin/tests/__image_snapshots__/index-test-ts-custom-loader-options-are-supported-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/vite-plugin/tests/__image_snapshots__/index-test-ts-custom-loader-options-are-supported-2-snap.png -------------------------------------------------------------------------------- /packages/vite-plugin/tests/__image_snapshots__/index-test-ts-custom-loader-options-are-supported-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/vite-plugin/tests/__image_snapshots__/index-test-ts-custom-loader-options-are-supported-3-snap.png -------------------------------------------------------------------------------- /packages/vite-plugin/tests/__image_snapshots__/index-test-ts-custom-query-params-are-supported-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/vite-plugin/tests/__image_snapshots__/index-test-ts-custom-query-params-are-supported-2-snap.png -------------------------------------------------------------------------------- /packages/vite-plugin/tests/__image_snapshots__/index-test-ts-custom-query-params-are-supported-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/vite-plugin/tests/__image_snapshots__/index-test-ts-custom-query-params-are-supported-3-snap.png -------------------------------------------------------------------------------- /packages/vite-plugin/tests/__image_snapshots__/index-test-ts-different-aspect-ratio-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/vite-plugin/tests/__image_snapshots__/index-test-ts-different-aspect-ratio-2-snap.png -------------------------------------------------------------------------------- /packages/vite-plugin/tests/__image_snapshots__/index-test-ts-different-aspect-ratio-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/vite-plugin/tests/__image_snapshots__/index-test-ts-different-aspect-ratio-3-snap.png -------------------------------------------------------------------------------- /packages/vite-plugin/tests/__image_snapshots__/index-test-ts-imagetools-params-are-supported-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/vite-plugin/tests/__image_snapshots__/index-test-ts-imagetools-params-are-supported-2-snap.png -------------------------------------------------------------------------------- /packages/vite-plugin/tests/__image_snapshots__/index-test-ts-imagetools-params-are-supported-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/vite-plugin/tests/__image_snapshots__/index-test-ts-imagetools-params-are-supported-3-snap.png -------------------------------------------------------------------------------- /packages/vite-plugin/tests/__image_snapshots__/index-test-ts-it-produces-expected-output-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/vite-plugin/tests/__image_snapshots__/index-test-ts-it-produces-expected-output-2-snap.png -------------------------------------------------------------------------------- /packages/vite-plugin/tests/fixtures/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/vite-plugin/tests/fixtures/image.jpg -------------------------------------------------------------------------------- /packages/vite-plugin/tests/fixtures/index.js: -------------------------------------------------------------------------------- 1 | export * from 'entry.js'; 2 | -------------------------------------------------------------------------------- /packages/vite-plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "verbatimModuleSyntax": true 7 | }, 8 | "include": ["src/**/*", "types/**/*", "tests/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/vite-plugin/types/base-n.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'base-n' { 2 | class BaseN { 3 | encode(i: number): string; 4 | } 5 | 6 | class factory { 7 | static create: (options?: unknown) => BaseN; 8 | } 9 | export default factory; 10 | } 11 | -------------------------------------------------------------------------------- /packages/vite-plugin/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | testTimeout: 60000, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /packages/wc/.gitignore: -------------------------------------------------------------------------------- 1 | ## editors 2 | /.idea 3 | /.vscode 4 | 5 | ## system files 6 | .DS_Store 7 | 8 | ## npm 9 | /node_modules/ 10 | /npm-debug.log 11 | 12 | ## testing 13 | /coverage/ 14 | 15 | ## temp folders 16 | /.tmp/ 17 | 18 | # build 19 | /_site/ 20 | /dist/ 21 | /out-tsc/ 22 | 23 | storybook-static 24 | custom-elements.json 25 | -------------------------------------------------------------------------------- /packages/wc/README.md: -------------------------------------------------------------------------------- 1 | # @responsive-image/wc 2 | 3 | Web component built with [Lit](https://lit.dev/) to render responsive images, provided as locally processed images or loaded remotely from Image CDNs. 4 | 5 | Part of the [ResponsiveImage](https://github.com/simonihmig/responsive-image) project. 6 | 7 | ## Documentation 8 | 9 | Visit [responsive-image.dev](https://responsive-image.dev) for our complete documentation site. 10 | 11 | ## License 12 | 13 | This project is licensed under the [MIT License](LICENSE.md). 14 | -------------------------------------------------------------------------------- /packages/wc/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { base, browser, nodeESM } from '@responsive-image/internals/eslint'; 2 | import { configs } from 'eslint-plugin-lit'; 3 | 4 | export default [ 5 | ...base, 6 | ...browser.map((c) => ({ 7 | ...c, 8 | files: ['src/**/*'], 9 | })), 10 | configs['flat/recommended'], 11 | ...nodeESM.map((c) => ({ 12 | ...c, 13 | files: ['eslint.config.mjs', 'vitest.config.ts'], 14 | })), 15 | ]; 16 | -------------------------------------------------------------------------------- /packages/wc/src/index.ts: -------------------------------------------------------------------------------- 1 | export { ResponsiveImage } from './responsive-image.js'; 2 | -------------------------------------------------------------------------------- /packages/wc/test-assets/test-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/wc/test-assets/test-image.jpg -------------------------------------------------------------------------------- /packages/wc/test/image-helper.ts: -------------------------------------------------------------------------------- 1 | interface ImageLoadedOptions { 2 | rejectOnError?: boolean; 3 | } 4 | 5 | export function getImg(el: HTMLElement): HTMLImageElement { 6 | const imgEl = 7 | el instanceof HTMLImageElement 8 | ? el 9 | : (el.querySelector('img') ?? el.shadowRoot?.querySelector('img')); 10 | 11 | if (!imgEl) { 12 | throw new Error('No found'); 13 | } 14 | 15 | return imgEl; 16 | } 17 | 18 | export async function loaded( 19 | el: HTMLElement, 20 | options: ImageLoadedOptions = {}, 21 | ): Promise { 22 | let resolve: () => void; 23 | let reject: (e: unknown) => void; 24 | const loaded = new Promise((res, rej) => { 25 | resolve = res; 26 | reject = rej; 27 | }); 28 | 29 | const imgEl = getImg(el); 30 | 31 | if (imgEl.complete) { 32 | resolve!(); 33 | } else { 34 | imgEl.addEventListener( 35 | 'load', 36 | () => { 37 | setTimeout(resolve, 0); 38 | }, 39 | { once: true }, 40 | ); 41 | if (options.rejectOnError) { 42 | imgEl.addEventListener( 43 | 'error', 44 | (e) => { 45 | console.error(e.message); 46 | reject(e); 47 | }, 48 | { once: true }, 49 | ); 50 | } 51 | } 52 | 53 | return loaded; 54 | } 55 | 56 | export async function trigger(el: HTMLElement) { 57 | const fakeEvent = new Event('load'); 58 | getImg(el).dispatchEvent(fakeEvent); 59 | } 60 | -------------------------------------------------------------------------------- /packages/wc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "types": ["@vitest/browser/providers/playwright"], 4 | "target": "es2021", 5 | "module": "NodeNext", 6 | "moduleResolution": "NodeNext", 7 | "noEmitOnError": true, 8 | "lib": ["es2021", "dom", "DOM.Iterable"], 9 | "strict": true, 10 | "esModuleInterop": false, 11 | "allowSyntheticDefaultImports": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "outDir": "dist", 15 | "sourceMap": true, 16 | "inlineSources": true, 17 | "rootDir": "./", 18 | "declaration": true, 19 | "incremental": true, 20 | "skipLibCheck": true, 21 | "verbatimModuleSyntax": true 22 | }, 23 | "include": ["**/*.ts"] 24 | } 25 | -------------------------------------------------------------------------------- /packages/wc/turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "extends": ["//"], 4 | "tasks": { 5 | "test": { 6 | "dependsOn": ["build"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/wc/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | watch: false, 6 | browser: { 7 | provider: 'playwright', // or 'webdriverio' 8 | enabled: true, 9 | instances: [ 10 | { 11 | browser: 'chromium', 12 | launch: { channel: 'chrome' }, 13 | }, 14 | ], 15 | }, 16 | }, 17 | }); 18 | -------------------------------------------------------------------------------- /packages/webpack/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | 4 | -------------------------------------------------------------------------------- /packages/webpack/README.md: -------------------------------------------------------------------------------- 1 | # @responsive-image/webpack 2 | 3 | Wepack loaders that process and resize local images. 4 | 5 | Part of the ResponsiveImage project. 6 | 7 | ## Compatibility 8 | 9 | - Webpack v5 10 | 11 | ## Documentation 12 | 13 | Visit [responsive-image.dev](https://responsive-image.dev) for our complete documentation site. 14 | 15 | ## License 16 | 17 | This project is licensed under the [MIT License](../../LICENSE.md). 18 | -------------------------------------------------------------------------------- /packages/webpack/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { base, nodeESM } from '@responsive-image/internals/eslint'; 2 | 3 | export default [...base, ...nodeESM]; 4 | -------------------------------------------------------------------------------- /packages/webpack/src/index.ts: -------------------------------------------------------------------------------- 1 | import type { Options } from './types'; 2 | 3 | const defaultLoaders: string[] = [ 4 | '@responsive-image/webpack/export', 5 | '@responsive-image/webpack/lqip/blurhash', 6 | '@responsive-image/webpack/lqip/thumbhash', 7 | '@responsive-image/webpack/lqip/inline', 8 | '@responsive-image/webpack/lqip/color', 9 | '@responsive-image/webpack/resize', 10 | '@responsive-image/webpack/loader', 11 | ]; 12 | 13 | function setupLoaders(options?: Partial) { 14 | if (options) { 15 | return defaultLoaders.map((loader) => ({ loader, options })); 16 | } 17 | 18 | return defaultLoaders; 19 | } 20 | 21 | export { setupLoaders }; 22 | -------------------------------------------------------------------------------- /packages/webpack/src/loader.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type ImageLoaderChainedResult, 3 | normalizeInput, 4 | } from '@responsive-image/build-utils'; 5 | 6 | import { getWebpackOptions } from './utils'; 7 | 8 | import type { Options } from './types'; 9 | import type { LoaderContext } from 'webpack'; 10 | 11 | export default function loader( 12 | this: LoaderContext>, 13 | input: Buffer | ImageLoaderChainedResult, 14 | ): ImageLoaderChainedResult { 15 | const options = getWebpackOptions(this); 16 | const data = normalizeInput(input, { generateHash: options.cache }); 17 | 18 | return data; 19 | } 20 | 21 | // receive input as Buffer 22 | loader.raw = true; 23 | -------------------------------------------------------------------------------- /packages/webpack/src/lqip/color-css.ts: -------------------------------------------------------------------------------- 1 | import { parseQuery } from '@responsive-image/build-utils'; 2 | import sharp from 'sharp'; 3 | 4 | import type { Options } from '../types'; 5 | import type { LoaderContext } from 'webpack'; 6 | 7 | export default function lqipColorCssLoader( 8 | this: LoaderContext>, 9 | ): void { 10 | const loaderCallback = this.async(); 11 | 12 | const { className, inline } = parseQuery(this.resourceQuery); 13 | if (typeof className !== 'string') { 14 | throw new Error('Missing className'); 15 | } 16 | 17 | const file = this.resourcePath.replace(/\.css$/, ''); 18 | 19 | process(file, className, !!inline) 20 | .then((result) => { 21 | loaderCallback(null, result); 22 | }) 23 | .catch((err) => loaderCallback(err)); 24 | } 25 | 26 | async function process( 27 | file: string, 28 | className: string, 29 | inline: boolean, 30 | ): Promise { 31 | const image = sharp(file); 32 | const { dominant } = await image.stats(); 33 | const colorHex = 34 | dominant.r.toString(16).padStart(2, '0') + 35 | dominant.g.toString(16).padStart(2, '0') + 36 | dominant.b.toString(16).padStart(2, '0'); 37 | const color = '#' + colorHex; 38 | 39 | return inline 40 | ? `export default ${JSON.stringify({ 'background-color': color })}` 41 | : `.${className} { background-color: ${color}; }`; 42 | } 43 | 44 | lqipColorCssLoader.raw = true; 45 | -------------------------------------------------------------------------------- /packages/webpack/src/types.ts: -------------------------------------------------------------------------------- 1 | import type { ImageOptions, LqipOptions } from '@responsive-image/build-utils'; 2 | 3 | export interface WebpackLoaderOptions { 4 | name: string; 5 | webPath?: string; 6 | outputPath: string; 7 | lqip?: LqipOptions; 8 | cache: boolean; 9 | styles: 'external' | 'inline'; 10 | } 11 | 12 | export type Options = WebpackLoaderOptions & ImageOptions; 13 | -------------------------------------------------------------------------------- /packages/webpack/src/utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | getOptions, 3 | type ImageLoaderChainedResult, 4 | } from '@responsive-image/build-utils'; 5 | 6 | import type { Options, WebpackLoaderOptions } from './types'; 7 | import type { LoaderContext } from 'webpack'; 8 | 9 | export const defaultWebpackOptions = { 10 | name: '[name]-[width]w-[hash].[ext]', 11 | outputPath: 'assets', 12 | cache: true, 13 | styles: 'external', 14 | } as const; 15 | 16 | export const webpackOptionKeys = [ 17 | 'lqip', 18 | 'name', 19 | 'outputPath', 20 | 'webPath', 21 | ] satisfies Array; 22 | 23 | export function getWebpackOptions( 24 | context: LoaderContext>, 25 | ): Options { 26 | return getOptions( 27 | context.resource, 28 | defaultWebpackOptions, 29 | context.getOptions(), 30 | ); 31 | } 32 | 33 | export function assertInput( 34 | input: string | Buffer | ImageLoaderChainedResult, 35 | ): asserts input is ImageLoaderChainedResult { 36 | if (Buffer.isBuffer(input) || typeof input === 'string') { 37 | throw new Error( 38 | 'You cannot run this webpack loader on raw data, at least @responsive-image/loader is missing in the loader chain!', 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/webpack/tests/__image_snapshots__/index-test-ts-custom-loader-options-are-supported-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/webpack/tests/__image_snapshots__/index-test-ts-custom-loader-options-are-supported-2-snap.png -------------------------------------------------------------------------------- /packages/webpack/tests/__image_snapshots__/index-test-ts-custom-loader-options-are-supported-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/webpack/tests/__image_snapshots__/index-test-ts-custom-loader-options-are-supported-3-snap.png -------------------------------------------------------------------------------- /packages/webpack/tests/__image_snapshots__/index-test-ts-custom-query-params-are-supported-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/webpack/tests/__image_snapshots__/index-test-ts-custom-query-params-are-supported-2-snap.png -------------------------------------------------------------------------------- /packages/webpack/tests/__image_snapshots__/index-test-ts-custom-query-params-are-supported-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/webpack/tests/__image_snapshots__/index-test-ts-custom-query-params-are-supported-3-snap.png -------------------------------------------------------------------------------- /packages/webpack/tests/__image_snapshots__/index-test-ts-different-aspect-ratio-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/webpack/tests/__image_snapshots__/index-test-ts-different-aspect-ratio-2-snap.png -------------------------------------------------------------------------------- /packages/webpack/tests/__image_snapshots__/index-test-ts-different-aspect-ratio-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/webpack/tests/__image_snapshots__/index-test-ts-different-aspect-ratio-3-snap.png -------------------------------------------------------------------------------- /packages/webpack/tests/__image_snapshots__/index-test-ts-imagetools-params-are-supported-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/webpack/tests/__image_snapshots__/index-test-ts-imagetools-params-are-supported-2-snap.png -------------------------------------------------------------------------------- /packages/webpack/tests/__image_snapshots__/index-test-ts-imagetools-params-are-supported-3-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/webpack/tests/__image_snapshots__/index-test-ts-imagetools-params-are-supported-3-snap.png -------------------------------------------------------------------------------- /packages/webpack/tests/__image_snapshots__/index-test-ts-it-produces-expected-output-2-snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/webpack/tests/__image_snapshots__/index-test-ts-it-produces-expected-output-2-snap.png -------------------------------------------------------------------------------- /packages/webpack/tests/fixtures/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/simonihmig/responsive-image/77a55448b3d1cea1c7ebdd446b848a07cbc3d8f9/packages/webpack/tests/fixtures/image.jpg -------------------------------------------------------------------------------- /packages/webpack/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "verbatimModuleSyntax": true 7 | }, 8 | "include": ["src/**/*", "types/**/*", "tests/**/*"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/webpack/turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "extends": ["//"], 4 | "tasks": { 5 | "test": { 6 | "dependsOn": ["build"] 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/webpack/types/base-n.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'base-n' { 2 | class BaseN { 3 | encode(i: number): string; 4 | } 5 | 6 | class factory { 7 | static create: (options?: unknown) => BaseN; 8 | } 9 | export default factory; 10 | } 11 | -------------------------------------------------------------------------------- /packages/webpack/types/loader-utils.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | declare module 'loader-utils' { 3 | export function interpolateName( 4 | LoaderContext: any, 5 | name: string, 6 | options: any, 7 | ): any; 8 | } 9 | -------------------------------------------------------------------------------- /packages/webpack/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vitest/config'; 2 | 3 | export default defineConfig({ 4 | test: { 5 | testTimeout: 60000, 6 | }, 7 | }); 8 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'packages/*' 3 | - 'apps/*' 4 | 5 | # changesets in release CI workflow is failing to handle package with custom prettier plugins without this 6 | publicHoistPattern: 7 | - '*prettier-plugin*' 8 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended", 5 | ":automergePatch", 6 | ":automergeLinters", 7 | ":automergeTesters", 8 | ":automergeTypes", 9 | ":maintainLockFilesWeekly", 10 | ":semanticCommitsDisabled", 11 | "config:js-lib" 12 | ], 13 | "labels": ["dependencies"], 14 | "packageRules": [ 15 | { 16 | "matchCurrentVersion": ">= 1.0.0", 17 | "matchUpdateTypes": ["minor"], 18 | "automerge": true 19 | }, 20 | { 21 | "matchDepTypes": ["devDependencies"], 22 | "matchUpdateTypes": ["minor"], 23 | "automerge": true 24 | } 25 | ], 26 | "baseBranches": ["main"] 27 | } 28 | -------------------------------------------------------------------------------- /scripts/skip-netlify-build.js: -------------------------------------------------------------------------------- 1 | process.exitCode = process.env.BRANCH.startsWith('renovate/') ? 0 : 1; 2 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "tasks": { 4 | "dev": { 5 | "dependsOn": ["^build"], 6 | "outputs": [], 7 | "cache": false, 8 | "persistent": true 9 | }, 10 | "start": { 11 | "dependsOn": ["^build"], 12 | "outputs": [], 13 | "cache": false, 14 | "persistent": true 15 | }, 16 | "test": { 17 | "dependsOn": ["^build"], 18 | "outputs": ["playwright-report"], 19 | "env": ["EMBER_TRY_CURRENT_SCENARIO", "EMBROIDER_TEST_SETUP_OPTIONS"] 20 | }, 21 | "build": { 22 | "outputs": ["dist/**", "declarations/**"], 23 | "dependsOn": ["^build"] 24 | }, 25 | "lint": { 26 | "outputs": [], 27 | "dependsOn": ["^build"] 28 | } 29 | } 30 | } 31 | --------------------------------------------------------------------------------