Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

unpic

Package Overview
Dependencies
Maintainers
1
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

unpic - npm Package Compare versions

Comparing version 3.2.0 to 3.3.0

esm/src/canonical.js

1

esm/mod.js

@@ -5,1 +5,2 @@ export * from "./src/types.js";

export * from "./src/parse.js";
export * from "./src/canonical.js";

@@ -7,3 +7,6 @@ import domains from "../data/domains.js";

export function getImageCdnForUrl(url) {
const { hostname, pathname } = new URL(url);
return getImageCdnForUrlByDomain(url) || getImageCdnForUrlByPath(url);
}
export function getImageCdnForUrlByDomain(url) {
const { hostname } = new URL(url);
if (cdnDomains.has(hostname)) {

@@ -17,2 +20,6 @@ return cdnDomains.get(hostname);

}
return false;
}
export function getImageCdnForUrlByPath(url) {
const { pathname } = new URL(url);
for (const [prefix, cdn] of Object.entries(paths)) {

@@ -19,0 +26,0 @@ if (pathname.startsWith(prefix)) {

26

esm/src/transform.js

@@ -16,2 +16,3 @@ import { getImageCdnForUrl } from "./detect.js";

import { transform as keycdn } from "./transformers/keycdn.js";
import { getCanonicalCdnForUrl } from "./canonical.js";
export const getTransformer = (cdn) => ({

@@ -34,6 +35,2 @@ imgix,

/**
* Returns a transformer function if the given URL is from a known image CDN
*/
export const getTransformerForUrl = (url) => getTransformerForCdn(getImageCdnForUrl(url));
/**
* Returns a transformer function if the given CDN is supported

@@ -52,6 +49,21 @@ */

export const transformUrl = (options) => {
if (options.cdn) {
return getTransformerForCdn(options.cdn)?.(options);
const cdn = options?.cdn ?? getImageCdnForUrl(options.url);
// Default to recursive
if (!(options.recursive ?? true)) {
return getTransformerForCdn(cdn)?.(options);
}
return getTransformerForUrl(options.url)?.(options);
const canonical = getCanonicalCdnForUrl(options.url, cdn);
if (!canonical || !canonical.cdn) {
return undefined;
}
return getTransformer(canonical.cdn)?.({
...options,
url: canonical.url,
});
};
/**
* Returns a transformer function if the given URL is from a known image CDN
*
* @deprecated Use `getCanonicalCdnForUrl` and `getTransformerForCdn` instead
*/
export const getTransformerForUrl = (url) => getTransformerForCdn(getImageCdnForUrl(url));

@@ -1,2 +0,2 @@

import { getNumericParam, setParamIfDefined } from "../utils.js";
import { getNumericParam, setParamIfDefined, setParamIfUndefined, } from "../utils.js";
export const parse = (url) => {

@@ -23,3 +23,4 @@ const parsedUrl = new URL(url);

setParamIfDefined(url, "quality", getNumericParam(url, "quality"), true);
setParamIfUndefined(url, "enlarge", 0);
return url;
};

@@ -0,3 +1,4 @@

export { delegateUrl } from "./vercel.js";
import { parse as vercelParse, transform as vercelTransform, } from "./vercel.js";
export const parse = (url) => ({ ...vercelParse(url), cdn: "nextjs" });
export const transform = (params) => vercelTransform({ ...params, cdn: "nextjs" });
import { setParamIfDefined, setParamIfUndefined, toRelativeUrl, } from "../utils.js";
import { getTransformerForCdn } from "../transform.js";
import { getImageCdnForUrl } from "../detect.js";
import { getImageCdnForUrlByDomain } from "../detect.js";
export const parse = (url) => {

@@ -15,2 +14,17 @@ const parsed = new URL(url);

};
export const delegateUrl = (url) => {
const parsed = new URL(url);
const source = parsed.searchParams.get("url");
if (!source || !source.startsWith("http")) {
return false;
}
const cdn = getImageCdnForUrlByDomain(source);
if (!cdn) {
return false;
}
return {
cdn,
url: source,
};
};
export const generate = ({ base, width, params: { quality = 75, root = "_vercel" } = {} }) => {

@@ -25,3 +39,3 @@ // If the base is a relative URL, we need to add a dummy host to the URL

};
export const transform = ({ url, width, height, cdn }) => {
export const transform = ({ url, width, cdn }) => {
// the URL might be relative, so we need to add a dummy host to it

@@ -35,11 +49,3 @@ const parsedUrl = new URL(url, "http://n");

}
if (src.startsWith("http")) {
const cdn = getImageCdnForUrl(src);
if (cdn && cdn !== "nextjs" && cdn !== "vercel") {
return getTransformerForCdn(cdn)?.({ url: src, width, height });
}
}
else {
setParamIfDefined(parsedUrl, "w", width, true, true);
}
setParamIfDefined(parsedUrl, "w", width, true, true);
if (isNextImage) {

@@ -46,0 +52,0 @@ if (parsedUrl.hostname === "n") {

@@ -6,5 +6,6 @@ {

"name": "unpic",
"version": "3.2.0",
"version": "3.3.0",
"description": "Universal image CDN translator",
"license": "MIT",
"homepage": "https://unpic.pics/lib",
"repository": {

@@ -11,0 +12,0 @@ "type": "git",

@@ -113,12 +113,15 @@ # 🖼 Unpic

## Usage with Vercel / Next.js
## Delegated URLs
Unpic has special handling for Vercel and Next.js image URLs. It detects supported
image CDNs, and falls back to `/_vercel/image` or `/_next/image` for local and
unsupported remote images.
Some transformers support URL delegation. This means that the source image URL
is also checked, and if it matches a CDN then the transform is applied directly
to the source image. For example: consider a `next/image` URL that points to an
image on Shopify. The URL is detected as a `nextjs` URL because it starts with
`/_next/image`. The `nextjs` transformer supports delegation, so the source
image URL is then checked. As it matches a Shopify domain, the transform is
applied directly to the Shopify URL. This means that the image is transformed on
the fly by Shopify, rather than by Next.js. However if the source image is not a
supported CDN, or is a local image then the `nextjs` transformer will return a
`/_next/image` URL.
For more information, see the
[Unpic Vercel / Next.js](https://github.com/ascorbic/unpic/blob/main/vercel.md)
documentation.
## FAQs

@@ -143,4 +146,4 @@

most CDN SDKs will not parse these URLs correctly.
- **Can you add support for CDN X?** If it supports a URL API and has a public
domain by which it can be identified then yes, please open an issue or PR.
- **Can you add support for CDN X?** If it supports a URL API then yes, please
open an issue or PR.
- **Can you add my domain to CDN X?** If you provide a service where end-users

@@ -170,18 +173,2 @@ use your URLs then probably. Examples may be image providers such as Unsplash,

To add new domains or subdomains to an existing CDN, add them to `domains.json`
or `subdomains.json` respectively.
To add a new CDN, add the following:
- a new source file in `src/transformers`. This should export a `transform`
function that implements the `UrlTransformer` interface, a `parse` function
that implements the `UrlParser` interface and optionally a `generate` function
that implements the `UrlGenerator` interface.
- a new test file in `src/transformers`. This should test all of the exported
API functions.
- at least one entry in `domains.json`, `subdomains.json` or `paths.json` to
detect the CDN
- add the new CDN to the types in `src/types.ts`, and import the new source file
in `src/transform.ts`
- add a sample image to `examples.json` in the demo site
- ensure tests pass by installing Deno and running `deno test src`
See the [contributing guide](CONTRIBUTING.md).

@@ -21,1 +21,2 @@ "use strict";

__exportStar(require("./src/parse.js"), exports);
__exportStar(require("./src/canonical.js"), exports);

@@ -6,3 +6,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.getImageCdnForUrl = void 0;
exports.getImageCdnForUrlByPath = exports.getImageCdnForUrlByDomain = exports.getImageCdnForUrl = void 0;
const domains_js_1 = __importDefault(require("../data/domains.js"));

@@ -14,3 +14,7 @@ const subdomains_js_1 = __importDefault(require("../data/subdomains.js"));

function getImageCdnForUrl(url) {
const { hostname, pathname } = new URL(url);
return getImageCdnForUrlByDomain(url) || getImageCdnForUrlByPath(url);
}
exports.getImageCdnForUrl = getImageCdnForUrl;
function getImageCdnForUrlByDomain(url) {
const { hostname } = new URL(url);
if (cdnDomains.has(hostname)) {

@@ -24,2 +28,7 @@ return cdnDomains.get(hostname);

}
return false;
}
exports.getImageCdnForUrlByDomain = getImageCdnForUrlByDomain;
function getImageCdnForUrlByPath(url) {
const { pathname } = new URL(url);
for (const [prefix, cdn] of Object.entries(paths_js_1.default)) {

@@ -32,2 +41,2 @@ if (pathname.startsWith(prefix)) {

}
exports.getImageCdnForUrl = getImageCdnForUrl;
exports.getImageCdnForUrlByPath = getImageCdnForUrlByPath;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformUrl = exports.getTransformerForCdn = exports.getTransformerForUrl = exports.getTransformer = void 0;
exports.getTransformerForUrl = exports.transformUrl = exports.getTransformerForCdn = exports.getTransformer = void 0;
const detect_js_1 = require("./detect.js");

@@ -19,2 +19,3 @@ const contentful_js_1 = require("./transformers/contentful.js");

const keycdn_js_1 = require("./transformers/keycdn.js");
const canonical_js_1 = require("./canonical.js");
const getTransformer = (cdn) => ({

@@ -38,7 +39,2 @@ imgix: imgix_js_1.transform,

/**
* Returns a transformer function if the given URL is from a known image CDN
*/
const getTransformerForUrl = (url) => (0, exports.getTransformerForCdn)((0, detect_js_1.getImageCdnForUrl)(url));
exports.getTransformerForUrl = getTransformerForUrl;
/**
* Returns a transformer function if the given CDN is supported

@@ -58,7 +54,23 @@ */

const transformUrl = (options) => {
if (options.cdn) {
return (0, exports.getTransformerForCdn)(options.cdn)?.(options);
const cdn = options?.cdn ?? (0, detect_js_1.getImageCdnForUrl)(options.url);
// Default to recursive
if (!(options.recursive ?? true)) {
return (0, exports.getTransformerForCdn)(cdn)?.(options);
}
return (0, exports.getTransformerForUrl)(options.url)?.(options);
const canonical = (0, canonical_js_1.getCanonicalCdnForUrl)(options.url, cdn);
if (!canonical || !canonical.cdn) {
return undefined;
}
return (0, exports.getTransformer)(canonical.cdn)?.({
...options,
url: canonical.url,
});
};
exports.transformUrl = transformUrl;
/**
* Returns a transformer function if the given URL is from a known image CDN
*
* @deprecated Use `getCanonicalCdnForUrl` and `getTransformerForCdn` instead
*/
const getTransformerForUrl = (url) => (0, exports.getTransformerForCdn)((0, detect_js_1.getImageCdnForUrl)(url));
exports.getTransformerForUrl = getTransformerForUrl;

@@ -27,4 +27,5 @@ "use strict";

(0, utils_js_1.setParamIfDefined)(url, "quality", (0, utils_js_1.getNumericParam)(url, "quality"), true);
(0, utils_js_1.setParamIfUndefined)(url, "enlarge", 0);
return url;
};
exports.transform = transform;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transform = exports.parse = void 0;
const vercel_js_1 = require("./vercel.js");
const parse = (url) => ({ ...(0, vercel_js_1.parse)(url), cdn: "nextjs" });
exports.transform = exports.parse = exports.delegateUrl = void 0;
var vercel_js_1 = require("./vercel.js");
Object.defineProperty(exports, "delegateUrl", { enumerable: true, get: function () { return vercel_js_1.delegateUrl; } });
const vercel_js_2 = require("./vercel.js");
const parse = (url) => ({ ...(0, vercel_js_2.parse)(url), cdn: "nextjs" });
exports.parse = parse;
const transform = (params) => (0, vercel_js_1.transform)({ ...params, cdn: "nextjs" });
const transform = (params) => (0, vercel_js_2.transform)({ ...params, cdn: "nextjs" });
exports.transform = transform;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transform = exports.generate = exports.parse = void 0;
exports.transform = exports.generate = exports.delegateUrl = exports.parse = void 0;
const utils_js_1 = require("../utils.js");
const transform_js_1 = require("../transform.js");
const detect_js_1 = require("../detect.js");

@@ -19,2 +18,18 @@ const parse = (url) => {

exports.parse = parse;
const delegateUrl = (url) => {
const parsed = new URL(url);
const source = parsed.searchParams.get("url");
if (!source || !source.startsWith("http")) {
return false;
}
const cdn = (0, detect_js_1.getImageCdnForUrlByDomain)(source);
if (!cdn) {
return false;
}
return {
cdn,
url: source,
};
};
exports.delegateUrl = delegateUrl;
const generate = ({ base, width, params: { quality = 75, root = "_vercel" } = {} }) => {

@@ -30,3 +45,3 @@ // If the base is a relative URL, we need to add a dummy host to the URL

exports.generate = generate;
const transform = ({ url, width, height, cdn }) => {
const transform = ({ url, width, cdn }) => {
// the URL might be relative, so we need to add a dummy host to it

@@ -40,11 +55,3 @@ const parsedUrl = new URL(url, "http://n");

}
if (src.startsWith("http")) {
const cdn = (0, detect_js_1.getImageCdnForUrl)(src);
if (cdn && cdn !== "nextjs" && cdn !== "vercel") {
return (0, transform_js_1.getTransformerForCdn)(cdn)?.({ url: src, width, height });
}
}
else {
(0, utils_js_1.setParamIfDefined)(parsedUrl, "w", width, true, true);
}
(0, utils_js_1.setParamIfDefined)(parsedUrl, "w", width, true, true);
if (isNextImage) {

@@ -51,0 +58,0 @@ if (parsedUrl.hostname === "n") {

@@ -5,1 +5,2 @@ export * from "./src/types.js";

export * from "./src/parse.js";
export * from "./src/canonical.js";
import { ImageCdn } from "./types.js";
export declare function getImageCdnForUrl(url: string | URL): ImageCdn | false;
export declare function getImageCdnForUrlByDomain(url: string | URL): ImageCdn | false;
export declare function getImageCdnForUrlByPath(url: string | URL): ImageCdn | false;
import { ImageCdn, UrlTransformer } from "./types.js";
export declare const getTransformer: (cdn: ImageCdn) => UrlTransformer;
/**
* Returns a transformer function if the given URL is from a known image CDN
*/
export declare const getTransformerForUrl: (url: string | URL) => UrlTransformer | undefined;
/**
* Returns a transformer function if the given CDN is supported

@@ -16,1 +12,7 @@ */

export declare const transformUrl: UrlTransformer;
/**
* Returns a transformer function if the given URL is from a known image CDN
*
* @deprecated Use `getCanonicalCdnForUrl` and `getTransformerForCdn` instead
*/
export declare const getTransformerForUrl: (url: string | URL) => UrlTransformer | undefined;
import { UrlParser, UrlTransformer } from "../types.js";
export { delegateUrl } from "./vercel.js";
export declare const parse: UrlParser;
export declare const transform: UrlTransformer;

@@ -1,3 +0,4 @@

import { UrlGenerator, UrlParser, UrlTransformer } from "../types.js";
import { ShouldDelegateUrl, UrlGenerator, UrlParser, UrlTransformer } from "../types.js";
export declare const parse: UrlParser;
export declare const delegateUrl: ShouldDelegateUrl;
export interface VercelParams {

@@ -4,0 +5,0 @@ quality?: number;

@@ -13,5 +13,26 @@ /**

format?: string;
/** Recursively find the the canonical CDN for a source image. Default is true */
recursive?: boolean;
/** Specify a CDN rather than auto-detecting */
cdn?: ImageCdn;
/** CDN-specific options. */
cdnOptions?: CdnOptions;
}
export interface CanonicalCdnUrl {
/** The source image URL */
url: string | URL;
/** The CDN to use */
cdn: ImageCdn;
}
/**
* Asks a CDN if there is a different canonical CDN for the given URL
* @param url The URL to check
* @returns The canonical CDN URL, or false if the given CDN will handle it itself
*/
export interface ShouldDelegateUrl {
(url: string | URL): CanonicalCdnUrl | false;
}
export declare type CdnOptions = {
[key in ImageCdn]: Record<string, unknown>;
};
export interface UrlGeneratorOptions<TParams = Record<string, string>> {

@@ -18,0 +39,0 @@ base: string | URL;

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc