diff --git a/.gitignore b/.gitignore index 3c3629e6..0ca34d04 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ node_modules +astroViteConfigs.js +demo/dist diff --git a/.prettierignore b/.prettierignore index bd5535a6..2a056f33 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1 +1,2 @@ pnpm-lock.yaml +demo/dist diff --git a/demo/public/images/public.jpeg b/demo/public/images/public.jpeg new file mode 100644 index 00000000..d5b648a7 Binary files /dev/null and b/demo/public/images/public.jpeg differ diff --git a/demo/src/pages/index.md b/demo/src/pages/index.md index 5ab76966..66d5df0d 100644 --- a/demo/src/pages/index.md +++ b/demo/src/pages/index.md @@ -49,20 +49,28 @@ The `placeholder` property tells the image what to show while loading. ## Internal Image -The following is an example of a reference to an internal image. +The following is an example of a reference to an internal image in the `src` directory (`../images/elva-800w.jpg`). -![A father holding his beloved daughter in his arms](../images//elva-800w.jpg) +![A father holding his beloved daughter in his arms](../images/elva-800w.jpg)
## External Image -The following is an example of a reference to an external image. +The following is an example of a reference to an external image (`https://picsum.photos/1024/768`). ![A random image](https://picsum.photos/1024/768)
+## Image in /public + +This image is in the public directory (`/images/public.jpeg`). + +![A random image](/images/public.jpeg) + +
+ ## Learn More You can optionally configure many more things! diff --git a/packages/astro-imagetools/.npmignore b/packages/astro-imagetools/.npmignore new file mode 100644 index 00000000..4f257ea7 --- /dev/null +++ b/packages/astro-imagetools/.npmignore @@ -0,0 +1,4 @@ +*.test.ts +test-fixtures +astroViteConfigs.js +vitest.config.ts diff --git a/packages/astro-imagetools/api/utils/getProcessedImage.js b/packages/astro-imagetools/api/utils/getProcessedImage.js index 4aa9e43c..90d3170f 100644 --- a/packages/astro-imagetools/api/utils/getProcessedImage.js +++ b/packages/astro-imagetools/api/utils/getProcessedImage.js @@ -9,6 +9,7 @@ import { supportedImageTypes, } from "../../utils/runtimeChecks.js"; import { fileURLToPath } from "node:url"; +import { getSrcPath } from "./getSrcPath.js"; const { getImageDetails } = await (sharp ? import("./imagetools.js") @@ -93,7 +94,7 @@ export default async (src, transformConfigs) => { const path = src.replace(/\\/g, `/`); const { image, imageWidth, imageHeight, imageFormat } = await getImageDetails( - `./${src}`, + getSrcPath(src), width, height, aspect diff --git a/packages/astro-imagetools/api/utils/getSrcPath.js b/packages/astro-imagetools/api/utils/getSrcPath.js new file mode 100644 index 00000000..fd0253b4 --- /dev/null +++ b/packages/astro-imagetools/api/utils/getSrcPath.js @@ -0,0 +1,30 @@ +import fs from "fs"; +import path from "path"; + +// To strip off params when checking for file on disk. +const paramPattern = /\?.*/; + +const { default: astroViteConfigs } = await import("../../astroViteConfigs.js"); + +/** + * getSrcPath allows the use of `src` attributes relative to either the public folder or project root. + * + * It first checks to see if the src is a file relative to the project root. + * If the file isn't found, it will look in the public folder. + * Finally, if it still can't be found, the original input will be returned. + */ +export function getSrcPath(src) { + // If this is already resolved to a file, return it. + if (fs.existsSync(src.replace(paramPattern, ""))) return src; + + const rootPath = path.join(astroViteConfigs.rootDir, src); + const rootTest = rootPath.replace(paramPattern, ""); + if (fs.existsSync(rootTest)) return rootPath; + + const publicPath = path.join(astroViteConfigs.publicDir, src); + const publicTest = publicPath.replace(paramPattern, ""); + if (fs.existsSync(publicTest)) return publicPath; + + // Fallback + return src; +} diff --git a/packages/astro-imagetools/api/utils/getSrcPath.test.ts b/packages/astro-imagetools/api/utils/getSrcPath.test.ts new file mode 100644 index 00000000..21166a86 --- /dev/null +++ b/packages/astro-imagetools/api/utils/getSrcPath.test.ts @@ -0,0 +1,67 @@ +import path from "node:path"; +import { describe, expect, it, afterAll, vi } from "vitest"; +import { getSrcPath } from "./getSrcPath"; + +vi.mock("../../astroViteConfigs.js", () => { + return { + default: { + rootDir: buildPath(), + // Custom publicDir + publicDir: buildPath("out"), + }, + }; +}); + +/** + * Build an absolute path to the target in the fixture directory + */ +function buildPath(target = "") { + return path.resolve(__dirname, "../../test-fixtures/getSrcPath", target); +} + +describe("getLinkElement", () => { + afterAll(() => { + vi.unmock("../../astroViteConfigs.js"); + }); + + it("finds a file in the root of the project", () => { + const result = getSrcPath("root.jpeg"); + expect(result).toBe(buildPath("root.jpeg")); + }); + + it("finds a file in the public folder", () => { + const result = getSrcPath("out.jpeg"); + expect(result).toBe(buildPath("out/out.jpeg")); + }); + + it("returns an absolute path unchanged, if it exists", () => { + const result = getSrcPath(buildPath("out/out.jpeg")); + expect(result).toBe(buildPath("out/out.jpeg")); + }); + + it("handles query parameters", () => { + const result = getSrcPath("root.jpeg?w=200"); + expect(result).toBe(buildPath("root.jpeg?w=200")); + }); + + it("handles query parameters for public-resolved files", () => { + const result = getSrcPath("out.jpeg?w=200"); + expect(result).toBe(buildPath("out/out.jpeg?w=200")); + }); + + it("returns the original input if the file is not found", () => { + const result = getSrcPath( + "https://cdn.nedis.com/images/products_high_res/TVRC2080BK_P30.JPG" + ); + expect(result).toBe( + "https://cdn.nedis.com/images/products_high_res/TVRC2080BK_P30.JPG" + ); + }); + + it("finds relative paths correctly", () => { + const outResult = getSrcPath("./out/out.jpeg"); + const rootResult = getSrcPath("./root.jpeg"); + expect(outResult).toBe(buildPath("out/out.jpeg")); + expect(rootResult).toBe(buildPath("root.jpeg")); + }); +}); diff --git a/packages/astro-imagetools/api/utils/getSrcset.js b/packages/astro-imagetools/api/utils/getSrcset.js index 0d9141b3..a4bf1743 100644 --- a/packages/astro-imagetools/api/utils/getSrcset.js +++ b/packages/astro-imagetools/api/utils/getSrcset.js @@ -1,5 +1,5 @@ // @ts-check -import { cwd } from "../../utils/runtimeChecks.js"; +import { getSrcPath } from "./getSrcPath"; export default async function getSrcset(src, breakpoints, format, options) { options = { @@ -23,7 +23,7 @@ export default async function getSrcset(src, breakpoints, format, options) { const id = `${src}?${params.slice(1)}`; if (process.env.npm_lifecycle_event !== "dev") { - const fullPath = cwd + id; + const fullPath = getSrcPath(id); const { default: load } = await import("../../plugin/hooks/load.js"); diff --git a/packages/astro-imagetools/integration/index.js b/packages/astro-imagetools/integration/index.js index 173b46ae..351d374b 100644 --- a/packages/astro-imagetools/integration/index.js +++ b/packages/astro-imagetools/integration/index.js @@ -31,6 +31,8 @@ export default { environment, isSsrBuild, projectBase, + publicDir: fileURLToPath(config.publicDir.href), + rootDir: fileURLToPath(config.root.href), }; await fs.promises.writeFile( diff --git a/packages/astro-imagetools/plugin/hooks/load.js b/packages/astro-imagetools/plugin/hooks/load.js index f339b62f..4659c034 100644 --- a/packages/astro-imagetools/plugin/hooks/load.js +++ b/packages/astro-imagetools/plugin/hooks/load.js @@ -1,6 +1,7 @@ // @ts-check import path from "node:path"; import objectHash from "object-hash"; +import { getSrcPath } from "../../api/utils/getSrcPath.js"; import { getCachedBuffer } from "../utils/cache.js"; import { getAssetPath, getConfigOptions } from "../utils/shared.js"; import { cwd, sharp, supportedImageTypes } from "../../utils/runtimeChecks.js"; @@ -31,7 +32,7 @@ export default async function load(id) { const { environment, projectBase, assetFileNames } = astroViteConfigs; - const src = id.startsWith(cwd) ? id : cwd + id; + const src = getSrcPath(id); const config = Object.fromEntries(searchParams); diff --git a/packages/astro-imagetools/test-fixtures/getSrcPath/out/out.jpeg b/packages/astro-imagetools/test-fixtures/getSrcPath/out/out.jpeg new file mode 100644 index 00000000..c8b7e655 Binary files /dev/null and b/packages/astro-imagetools/test-fixtures/getSrcPath/out/out.jpeg differ diff --git a/packages/astro-imagetools/test-fixtures/getSrcPath/root.jpeg b/packages/astro-imagetools/test-fixtures/getSrcPath/root.jpeg new file mode 100644 index 00000000..87f9f0c7 Binary files /dev/null and b/packages/astro-imagetools/test-fixtures/getSrcPath/root.jpeg differ