···11-/**
22- * See https://github.com/vercel/next.js/blob/64702a9/test/unit/image-optimizer/match-remote-pattern.test.ts
33- */
44-51import pm from "picomatch";
62import { describe, expect, it } from "vitest";
7388-import { matchRemotePattern as m } from "./images.js";
44+import type { LocalPattern } from "./images.js";
55+import { matchLocalPattern, matchRemotePattern as mRP } from "./images.js";
9677+/**
88+ * See https://github.com/vercel/next.js/blob/64702a9/test/unit/image-optimizer/match-remote-pattern.test.ts
99+ */
1010describe("matchRemotePattern", () => {
1111 it("should match literal hostname", () => {
1212 const p = { hostname: pm.makeRe("example.com") } as const;
1313- expect(m(p, new URL("https://example.com"))).toBe(true);
1414- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
1515- expect(m(p, new URL("https://example.net"))).toBe(false);
1616- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
1717- expect(m(p, new URL("https://com"))).toBe(false);
1818- expect(m(p, new URL("https://example.com/path"))).toBe(true);
1919- expect(m(p, new URL("https://example.com/path/to"))).toBe(true);
2020- expect(m(p, new URL("https://example.com/path/to/file"))).toBe(true);
2121- expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(true);
2222- expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(true);
2323- expect(m(p, new URL("http://example.com:81/path/to/file"))).toBe(true);
1313+ expect(mRP(p, new URL("https://example.com"))).toBe(true);
1414+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
1515+ expect(mRP(p, new URL("https://example.net"))).toBe(false);
1616+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
1717+ expect(mRP(p, new URL("https://com"))).toBe(false);
1818+ expect(mRP(p, new URL("https://example.com/path"))).toBe(true);
1919+ expect(mRP(p, new URL("https://example.com/path/to"))).toBe(true);
2020+ expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(true);
2121+ expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(true);
2222+ expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(true);
2323+ expect(mRP(p, new URL("http://example.com:81/path/to/file"))).toBe(true);
2424 });
25252626 it("should match literal protocol and hostname", () => {
2727 const p = { protocol: "https", hostname: pm.makeRe("example.com") } as const;
2828- expect(m(p, new URL("https://example.com"))).toBe(true);
2929- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
3030- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
3131- expect(m(p, new URL("https://com"))).toBe(false);
3232- expect(m(p, new URL("https://example.com/path/to"))).toBe(true);
3333- expect(m(p, new URL("https://example.com/path/to/file"))).toBe(true);
3434- expect(m(p, new URL("https://example.com/path/to/file"))).toBe(true);
3535- expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(true);
3636- expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(true);
3737- expect(m(p, new URL("http://example.com:81/path/to/file"))).toBe(false);
3838- expect(m(p, new URL("ftp://example.com:81/path/to/file"))).toBe(false);
2828+ expect(mRP(p, new URL("https://example.com"))).toBe(true);
2929+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
3030+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
3131+ expect(mRP(p, new URL("https://com"))).toBe(false);
3232+ expect(mRP(p, new URL("https://example.com/path/to"))).toBe(true);
3333+ expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(true);
3434+ expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(true);
3535+ expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(true);
3636+ expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(true);
3737+ expect(mRP(p, new URL("http://example.com:81/path/to/file"))).toBe(false);
3838+ expect(mRP(p, new URL("ftp://example.com:81/path/to/file"))).toBe(false);
3939 });
40404141 it("should match literal protocol, hostname, no port", () => {
4242 const p = { protocol: "https", hostname: pm.makeRe("example.com"), port: "" } as const;
4343- expect(m(p, new URL("https://example.com"))).toBe(true);
4444- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
4545- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
4646- expect(m(p, new URL("https://com"))).toBe(false);
4747- expect(m(p, new URL("https://example.com/path/to/file"))).toBe(true);
4848- expect(m(p, new URL("https://example.com/path/to/file?q=1"))).toBe(true);
4949- expect(m(p, new URL("http://example.com/path/to/file"))).toBe(false);
5050- expect(m(p, new URL("ftp://example.com/path/to/file"))).toBe(false);
5151- expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(false);
5252- expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false);
5353- expect(m(p, new URL("http://example.com:81/path/to/file"))).toBe(false);
4343+ expect(mRP(p, new URL("https://example.com"))).toBe(true);
4444+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
4545+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
4646+ expect(mRP(p, new URL("https://com"))).toBe(false);
4747+ expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(true);
4848+ expect(mRP(p, new URL("https://example.com/path/to/file?q=1"))).toBe(true);
4949+ expect(mRP(p, new URL("http://example.com/path/to/file"))).toBe(false);
5050+ expect(mRP(p, new URL("ftp://example.com/path/to/file"))).toBe(false);
5151+ expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(false);
5252+ expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false);
5353+ expect(mRP(p, new URL("http://example.com:81/path/to/file"))).toBe(false);
5454 });
55555656 it("should match literal protocol, hostname, no port, no search", () => {
···6060 port: "",
6161 search: "",
6262 } as const;
6363- expect(m(p, new URL("https://example.com"))).toBe(true);
6464- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
6565- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
6666- expect(m(p, new URL("https://com"))).toBe(false);
6767- expect(m(p, new URL("https://example.com/path/to/file"))).toBe(true);
6868- expect(m(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false);
6969- expect(m(p, new URL("http://example.com/path/to/file"))).toBe(false);
7070- expect(m(p, new URL("ftp://example.com/path/to/file"))).toBe(false);
7171- expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(false);
7272- expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false);
7373- expect(m(p, new URL("http://example.com:81/path/to/file"))).toBe(false);
6363+ expect(mRP(p, new URL("https://example.com"))).toBe(true);
6464+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
6565+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
6666+ expect(mRP(p, new URL("https://com"))).toBe(false);
6767+ expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(true);
6868+ expect(mRP(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false);
6969+ expect(mRP(p, new URL("http://example.com/path/to/file"))).toBe(false);
7070+ expect(mRP(p, new URL("ftp://example.com/path/to/file"))).toBe(false);
7171+ expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(false);
7272+ expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false);
7373+ expect(mRP(p, new URL("http://example.com:81/path/to/file"))).toBe(false);
7474 });
75757676 it("should match literal protocol, hostname, port 42", () => {
···7979 hostname: pm.makeRe("example.com"),
8080 port: "42",
8181 } as const;
8282- expect(m(p, new URL("https://example.com:42"))).toBe(true);
8383- expect(m(p, new URL("https://example.com.uk:42"))).toBe(false);
8484- expect(m(p, new URL("https://sub.example.com:42"))).toBe(false);
8585- expect(m(p, new URL("https://com:42"))).toBe(false);
8686- expect(m(p, new URL("https://example.com:42/path/to/file"))).toBe(true);
8787- expect(m(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(true);
8888- expect(m(p, new URL("http://example.com:42/path/to/file"))).toBe(false);
8989- expect(m(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false);
9090- expect(m(p, new URL("https://example.com"))).toBe(false);
9191- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
9292- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
9393- expect(m(p, new URL("https://com"))).toBe(false);
9494- expect(m(p, new URL("https://example.com/path/to/file"))).toBe(false);
9595- expect(m(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false);
9696- expect(m(p, new URL("http://example.com/path/to/file"))).toBe(false);
9797- expect(m(p, new URL("ftp://example.com/path/to/file"))).toBe(false);
9898- expect(m(p, new URL("https://example.com:81"))).toBe(false);
9999- expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(false);
100100- expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false);
8282+ expect(mRP(p, new URL("https://example.com:42"))).toBe(true);
8383+ expect(mRP(p, new URL("https://example.com.uk:42"))).toBe(false);
8484+ expect(mRP(p, new URL("https://sub.example.com:42"))).toBe(false);
8585+ expect(mRP(p, new URL("https://com:42"))).toBe(false);
8686+ expect(mRP(p, new URL("https://example.com:42/path/to/file"))).toBe(true);
8787+ expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(true);
8888+ expect(mRP(p, new URL("http://example.com:42/path/to/file"))).toBe(false);
8989+ expect(mRP(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false);
9090+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
9191+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
9292+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
9393+ expect(mRP(p, new URL("https://com"))).toBe(false);
9494+ expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(false);
9595+ expect(mRP(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false);
9696+ expect(mRP(p, new URL("http://example.com/path/to/file"))).toBe(false);
9797+ expect(mRP(p, new URL("ftp://example.com/path/to/file"))).toBe(false);
9898+ expect(mRP(p, new URL("https://example.com:81"))).toBe(false);
9999+ expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(false);
100100+ expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false);
101101 });
102102103103 it("should match literal protocol, hostname, port, pathname", () => {
···107107 port: "42",
108108 pathname: pm.makeRe("/path/to/file", { dot: true }),
109109 } as const;
110110- expect(m(p, new URL("https://example.com:42"))).toBe(false);
111111- expect(m(p, new URL("https://example.com.uk:42"))).toBe(false);
112112- expect(m(p, new URL("https://sub.example.com:42"))).toBe(false);
113113- expect(m(p, new URL("https://example.com:42/path"))).toBe(false);
114114- expect(m(p, new URL("https://example.com:42/path/to"))).toBe(false);
115115- expect(m(p, new URL("https://example.com:42/file"))).toBe(false);
116116- expect(m(p, new URL("https://example.com:42/path/to/file"))).toBe(true);
117117- expect(m(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(true);
118118- expect(m(p, new URL("http://example.com:42/path/to/file"))).toBe(false);
119119- expect(m(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false);
120120- expect(m(p, new URL("https://example.com"))).toBe(false);
121121- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
122122- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
123123- expect(m(p, new URL("https://example.com/path"))).toBe(false);
124124- expect(m(p, new URL("https://example.com/path/to"))).toBe(false);
125125- expect(m(p, new URL("https://example.com/path/to/file"))).toBe(false);
126126- expect(m(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false);
127127- expect(m(p, new URL("http://example.com/path/to/file"))).toBe(false);
128128- expect(m(p, new URL("ftp://example.com/path/to/file"))).toBe(false);
129129- expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(false);
130130- expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false);
110110+ expect(mRP(p, new URL("https://example.com:42"))).toBe(false);
111111+ expect(mRP(p, new URL("https://example.com.uk:42"))).toBe(false);
112112+ expect(mRP(p, new URL("https://sub.example.com:42"))).toBe(false);
113113+ expect(mRP(p, new URL("https://example.com:42/path"))).toBe(false);
114114+ expect(mRP(p, new URL("https://example.com:42/path/to"))).toBe(false);
115115+ expect(mRP(p, new URL("https://example.com:42/file"))).toBe(false);
116116+ expect(mRP(p, new URL("https://example.com:42/path/to/file"))).toBe(true);
117117+ expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(true);
118118+ expect(mRP(p, new URL("http://example.com:42/path/to/file"))).toBe(false);
119119+ expect(mRP(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false);
120120+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
121121+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
122122+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
123123+ expect(mRP(p, new URL("https://example.com/path"))).toBe(false);
124124+ expect(mRP(p, new URL("https://example.com/path/to"))).toBe(false);
125125+ expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(false);
126126+ expect(mRP(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false);
127127+ expect(mRP(p, new URL("http://example.com/path/to/file"))).toBe(false);
128128+ expect(mRP(p, new URL("ftp://example.com/path/to/file"))).toBe(false);
129129+ expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(false);
130130+ expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false);
131131 });
132132133133 it("should match literal protocol, hostname, port, pathname, search", () => {
···138138 pathname: pm.makeRe("/path/to/file", { dot: true }),
139139 search: "?q=1&a=two&s=!@$^&-_+/()[]{};:~",
140140 } as const;
141141- expect(m(p, new URL("https://example.com:42"))).toBe(false);
142142- expect(m(p, new URL("https://example.com.uk:42"))).toBe(false);
143143- expect(m(p, new URL("https://sub.example.com:42"))).toBe(false);
144144- expect(m(p, new URL("https://example.com:42/path"))).toBe(false);
145145- expect(m(p, new URL("https://example.com:42/path/to"))).toBe(false);
146146- expect(m(p, new URL("https://example.com:42/file"))).toBe(false);
147147- expect(m(p, new URL("https://example.com:42/path/to/file"))).toBe(false);
148148- expect(m(p, new URL("http://example.com:42/path/to/file"))).toBe(false);
149149- expect(m(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false);
150150- expect(m(p, new URL("https://example.com"))).toBe(false);
151151- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
152152- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
153153- expect(m(p, new URL("https://example.com/path"))).toBe(false);
154154- expect(m(p, new URL("https://example.com/path/to"))).toBe(false);
155155- expect(m(p, new URL("https://example.com/path/to/file"))).toBe(false);
156156- expect(m(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false);
157157- expect(m(p, new URL("http://example.com/path/to/file"))).toBe(false);
158158- expect(m(p, new URL("ftp://example.com/path/to/file"))).toBe(false);
159159- expect(m(p, new URL("https://example.com:81/path/to/file"))).toBe(false);
160160- expect(m(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false);
161161- expect(m(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(false);
162162- expect(m(p, new URL("https://example.com:42/path/to/file?q=1&a=two"))).toBe(false);
163163- expect(m(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s"))).toBe(false);
164164- expect(m(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s="))).toBe(false);
165165- expect(m(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s=!@"))).toBe(false);
166166- expect(m(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s=!@$^&-_+/()[]{};:~"))).toBe(true);
167167- expect(m(p, new URL("https://example.com:42/path/to/file?q=1&s=!@$^&-_+/()[]{};:~&a=two"))).toBe(false);
168168- expect(m(p, new URL("https://example.com:42/path/to/file?a=two&q=1&s=!@$^&-_+/()[]{};:~"))).toBe(false);
141141+ expect(mRP(p, new URL("https://example.com:42"))).toBe(false);
142142+ expect(mRP(p, new URL("https://example.com.uk:42"))).toBe(false);
143143+ expect(mRP(p, new URL("https://sub.example.com:42"))).toBe(false);
144144+ expect(mRP(p, new URL("https://example.com:42/path"))).toBe(false);
145145+ expect(mRP(p, new URL("https://example.com:42/path/to"))).toBe(false);
146146+ expect(mRP(p, new URL("https://example.com:42/file"))).toBe(false);
147147+ expect(mRP(p, new URL("https://example.com:42/path/to/file"))).toBe(false);
148148+ expect(mRP(p, new URL("http://example.com:42/path/to/file"))).toBe(false);
149149+ expect(mRP(p, new URL("ftp://example.com:42/path/to/file"))).toBe(false);
150150+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
151151+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
152152+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
153153+ expect(mRP(p, new URL("https://example.com/path"))).toBe(false);
154154+ expect(mRP(p, new URL("https://example.com/path/to"))).toBe(false);
155155+ expect(mRP(p, new URL("https://example.com/path/to/file"))).toBe(false);
156156+ expect(mRP(p, new URL("https://example.com/path/to/file?q=1"))).toBe(false);
157157+ expect(mRP(p, new URL("http://example.com/path/to/file"))).toBe(false);
158158+ expect(mRP(p, new URL("ftp://example.com/path/to/file"))).toBe(false);
159159+ expect(mRP(p, new URL("https://example.com:81/path/to/file"))).toBe(false);
160160+ expect(mRP(p, new URL("https://example.com:81/path/to/file?q=1"))).toBe(false);
161161+ expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1"))).toBe(false);
162162+ expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&a=two"))).toBe(false);
163163+ expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s"))).toBe(false);
164164+ expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s="))).toBe(false);
165165+ expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s=!@"))).toBe(false);
166166+ expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&a=two&s=!@$^&-_+/()[]{};:~"))).toBe(true);
167167+ expect(mRP(p, new URL("https://example.com:42/path/to/file?q=1&s=!@$^&-_+/()[]{};:~&a=two"))).toBe(false);
168168+ expect(mRP(p, new URL("https://example.com:42/path/to/file?a=two&q=1&s=!@$^&-_+/()[]{};:~"))).toBe(false);
169169 });
170170171171 it("should match hostname pattern with single asterisk by itself", () => {
172172 const p = { hostname: pm.makeRe("avatars.*.example.com") } as const;
173173- expect(m(p, new URL("https://com"))).toBe(false);
174174- expect(m(p, new URL("https://example.com"))).toBe(false);
175175- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
176176- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
177177- expect(m(p, new URL("https://sub.example.com.uk"))).toBe(false);
178178- expect(m(p, new URL("https://avatars.example.com"))).toBe(false);
179179- expect(m(p, new URL("https://avatars.sfo1.example.com"))).toBe(true);
180180- expect(m(p, new URL("https://avatars.iad1.example.com"))).toBe(true);
181181- expect(m(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false);
173173+ expect(mRP(p, new URL("https://com"))).toBe(false);
174174+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
175175+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
176176+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
177177+ expect(mRP(p, new URL("https://sub.example.com.uk"))).toBe(false);
178178+ expect(mRP(p, new URL("https://avatars.example.com"))).toBe(false);
179179+ expect(mRP(p, new URL("https://avatars.sfo1.example.com"))).toBe(true);
180180+ expect(mRP(p, new URL("https://avatars.iad1.example.com"))).toBe(true);
181181+ expect(mRP(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false);
182182 });
183183184184 it("should match hostname pattern with single asterisk at beginning", () => {
185185 const p = { hostname: pm.makeRe("avatars.*1.example.com") } as const;
186186- expect(m(p, new URL("https://com"))).toBe(false);
187187- expect(m(p, new URL("https://example.com"))).toBe(false);
188188- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
189189- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
190190- expect(m(p, new URL("https://sub.example.com.uk"))).toBe(false);
191191- expect(m(p, new URL("https://avatars.example.com"))).toBe(false);
192192- expect(m(p, new URL("https://avatars.sfo1.example.com"))).toBe(true);
193193- expect(m(p, new URL("https://avatars.iad1.example.com"))).toBe(true);
194194- expect(m(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false);
195195- expect(m(p, new URL("https://avatars.sfo2.example.com"))).toBe(false);
196196- expect(m(p, new URL("https://avatars.iad2.example.com"))).toBe(false);
197197- expect(m(p, new URL("https://avatars.1.example.com"))).toBe(true);
186186+ expect(mRP(p, new URL("https://com"))).toBe(false);
187187+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
188188+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
189189+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
190190+ expect(mRP(p, new URL("https://sub.example.com.uk"))).toBe(false);
191191+ expect(mRP(p, new URL("https://avatars.example.com"))).toBe(false);
192192+ expect(mRP(p, new URL("https://avatars.sfo1.example.com"))).toBe(true);
193193+ expect(mRP(p, new URL("https://avatars.iad1.example.com"))).toBe(true);
194194+ expect(mRP(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false);
195195+ expect(mRP(p, new URL("https://avatars.sfo2.example.com"))).toBe(false);
196196+ expect(mRP(p, new URL("https://avatars.iad2.example.com"))).toBe(false);
197197+ expect(mRP(p, new URL("https://avatars.1.example.com"))).toBe(true);
198198 });
199199200200 it("should match hostname pattern with single asterisk in middle", () => {
201201 const p = { hostname: pm.makeRe("avatars.*a*.example.com") } as const;
202202- expect(m(p, new URL("https://com"))).toBe(false);
203203- expect(m(p, new URL("https://example.com"))).toBe(false);
204204- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
205205- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
206206- expect(m(p, new URL("https://sub.example.com.uk"))).toBe(false);
207207- expect(m(p, new URL("https://avatars.example.com"))).toBe(false);
208208- expect(m(p, new URL("https://avatars.sfo1.example.com"))).toBe(false);
209209- expect(m(p, new URL("https://avatars.iad1.example.com"))).toBe(true);
210210- expect(m(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false);
211211- expect(m(p, new URL("https://avatars.sfo2.example.com"))).toBe(false);
212212- expect(m(p, new URL("https://avatars.iad2.example.com"))).toBe(true);
213213- expect(m(p, new URL("https://avatars.a.example.com"))).toBe(true);
202202+ expect(mRP(p, new URL("https://com"))).toBe(false);
203203+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
204204+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
205205+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
206206+ expect(mRP(p, new URL("https://sub.example.com.uk"))).toBe(false);
207207+ expect(mRP(p, new URL("https://avatars.example.com"))).toBe(false);
208208+ expect(mRP(p, new URL("https://avatars.sfo1.example.com"))).toBe(false);
209209+ expect(mRP(p, new URL("https://avatars.iad1.example.com"))).toBe(true);
210210+ expect(mRP(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false);
211211+ expect(mRP(p, new URL("https://avatars.sfo2.example.com"))).toBe(false);
212212+ expect(mRP(p, new URL("https://avatars.iad2.example.com"))).toBe(true);
213213+ expect(mRP(p, new URL("https://avatars.a.example.com"))).toBe(true);
214214 });
215215216216 it("should match hostname pattern with single asterisk at end", () => {
217217 const p = { hostname: pm.makeRe("avatars.ia*.example.com") } as const;
218218- expect(m(p, new URL("https://com"))).toBe(false);
219219- expect(m(p, new URL("https://example.com"))).toBe(false);
220220- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
221221- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
222222- expect(m(p, new URL("https://sub.example.com.uk"))).toBe(false);
223223- expect(m(p, new URL("https://avatars.example.com"))).toBe(false);
224224- expect(m(p, new URL("https://avatars.sfo1.example.com"))).toBe(false);
225225- expect(m(p, new URL("https://avatars.iad1.example.com"))).toBe(true);
226226- expect(m(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false);
227227- expect(m(p, new URL("https://avatars.sfo2.example.com"))).toBe(false);
228228- expect(m(p, new URL("https://avatars.iad2.example.com"))).toBe(true);
229229- expect(m(p, new URL("https://avatars.ia.example.com"))).toBe(true);
218218+ expect(mRP(p, new URL("https://com"))).toBe(false);
219219+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
220220+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
221221+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
222222+ expect(mRP(p, new URL("https://sub.example.com.uk"))).toBe(false);
223223+ expect(mRP(p, new URL("https://avatars.example.com"))).toBe(false);
224224+ expect(mRP(p, new URL("https://avatars.sfo1.example.com"))).toBe(false);
225225+ expect(mRP(p, new URL("https://avatars.iad1.example.com"))).toBe(true);
226226+ expect(mRP(p, new URL("https://more.avatars.iad1.example.com"))).toBe(false);
227227+ expect(mRP(p, new URL("https://avatars.sfo2.example.com"))).toBe(false);
228228+ expect(mRP(p, new URL("https://avatars.iad2.example.com"))).toBe(true);
229229+ expect(mRP(p, new URL("https://avatars.ia.example.com"))).toBe(true);
230230 });
231231232232 it("should match hostname pattern with double asterisk", () => {
233233 const p = { hostname: pm.makeRe("**.example.com") } as const;
234234- expect(m(p, new URL("https://com"))).toBe(false);
235235- expect(m(p, new URL("https://example.com"))).toBe(false);
236236- expect(m(p, new URL("https://sub.example.com"))).toBe(true);
237237- expect(m(p, new URL("https://deep.sub.example.com"))).toBe(true);
238238- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
239239- expect(m(p, new URL("https://sub.example.com.uk"))).toBe(false);
240240- expect(m(p, new URL("https://avatars.example.com"))).toBe(true);
241241- expect(m(p, new URL("https://avatars.sfo1.example.com"))).toBe(true);
242242- expect(m(p, new URL("https://avatars.iad1.example.com"))).toBe(true);
243243- expect(m(p, new URL("https://more.avatars.iad1.example.com"))).toBe(true);
234234+ expect(mRP(p, new URL("https://com"))).toBe(false);
235235+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
236236+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(true);
237237+ expect(mRP(p, new URL("https://deep.sub.example.com"))).toBe(true);
238238+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
239239+ expect(mRP(p, new URL("https://sub.example.com.uk"))).toBe(false);
240240+ expect(mRP(p, new URL("https://avatars.example.com"))).toBe(true);
241241+ expect(mRP(p, new URL("https://avatars.sfo1.example.com"))).toBe(true);
242242+ expect(mRP(p, new URL("https://avatars.iad1.example.com"))).toBe(true);
243243+ expect(mRP(p, new URL("https://more.avatars.iad1.example.com"))).toBe(true);
244244 });
245245246246 it("should match pathname pattern with single asterisk by itself", () => {
···248248 hostname: pm.makeRe("example.com"),
249249 pathname: pm.makeRe("/act123/*/pic.jpg", { dot: true }),
250250 } as const;
251251- expect(m(p, new URL("https://com"))).toBe(false);
252252- expect(m(p, new URL("https://example.com"))).toBe(false);
253253- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
254254- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
255255- expect(m(p, new URL("https://example.com/act123"))).toBe(false);
256256- expect(m(p, new URL("https://example.com/act123/usr4"))).toBe(false);
257257- expect(m(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false);
258258- expect(m(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false);
259259- expect(m(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true);
260260- expect(m(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true);
261261- expect(m(p, new URL("https://example.com/act123/usr6/pic.jpg"))).toBe(true);
262262- expect(m(p, new URL("https://example.com/act123/team/pic.jpg"))).toBe(true);
263263- expect(m(p, new URL("https://example.com/act456/team/pic.jpg"))).toBe(false);
264264- expect(m(p, new URL("https://example.com/act123/.a/pic.jpg"))).toBe(true);
265265- expect(m(p, new URL("https://example.com/act123/team/usr4/pic.jpg"))).toBe(false);
266266- expect(m(p, new URL("https://example.com/team/pic.jpg"))).toBe(false);
251251+ expect(mRP(p, new URL("https://com"))).toBe(false);
252252+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
253253+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
254254+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
255255+ expect(mRP(p, new URL("https://example.com/act123"))).toBe(false);
256256+ expect(mRP(p, new URL("https://example.com/act123/usr4"))).toBe(false);
257257+ expect(mRP(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false);
258258+ expect(mRP(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false);
259259+ expect(mRP(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true);
260260+ expect(mRP(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true);
261261+ expect(mRP(p, new URL("https://example.com/act123/usr6/pic.jpg"))).toBe(true);
262262+ expect(mRP(p, new URL("https://example.com/act123/team/pic.jpg"))).toBe(true);
263263+ expect(mRP(p, new URL("https://example.com/act456/team/pic.jpg"))).toBe(false);
264264+ expect(mRP(p, new URL("https://example.com/act123/.a/pic.jpg"))).toBe(true);
265265+ expect(mRP(p, new URL("https://example.com/act123/team/usr4/pic.jpg"))).toBe(false);
266266+ expect(mRP(p, new URL("https://example.com/team/pic.jpg"))).toBe(false);
267267 });
268268269269 it("should match pathname pattern with single asterisk at the beginning", () => {
···271271 hostname: pm.makeRe("example.com"),
272272 pathname: pm.makeRe("/act123/*4/pic.jpg", { dot: true }),
273273 } as const;
274274- expect(m(p, new URL("https://com"))).toBe(false);
275275- expect(m(p, new URL("https://example.com"))).toBe(false);
276276- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
277277- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
278278- expect(m(p, new URL("https://example.com/act123"))).toBe(false);
279279- expect(m(p, new URL("https://example.com/act123/usr4"))).toBe(false);
280280- expect(m(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false);
281281- expect(m(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false);
282282- expect(m(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true);
283283- expect(m(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(false);
284284- expect(m(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(true);
285285- expect(m(p, new URL("https://example.com/act456/team5/pic.jpg"))).toBe(false);
286286- expect(m(p, new URL("https://example.com/team/pic.jpg"))).toBe(false);
287287- expect(m(p, new URL("https://example.com/act123/4/pic.jpg"))).toBe(true);
274274+ expect(mRP(p, new URL("https://com"))).toBe(false);
275275+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
276276+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
277277+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
278278+ expect(mRP(p, new URL("https://example.com/act123"))).toBe(false);
279279+ expect(mRP(p, new URL("https://example.com/act123/usr4"))).toBe(false);
280280+ expect(mRP(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false);
281281+ expect(mRP(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false);
282282+ expect(mRP(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true);
283283+ expect(mRP(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(false);
284284+ expect(mRP(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(true);
285285+ expect(mRP(p, new URL("https://example.com/act456/team5/pic.jpg"))).toBe(false);
286286+ expect(mRP(p, new URL("https://example.com/team/pic.jpg"))).toBe(false);
287287+ expect(mRP(p, new URL("https://example.com/act123/4/pic.jpg"))).toBe(true);
288288 });
289289290290 it("should match pathname pattern with single asterisk in the middle", () => {
···292292 hostname: pm.makeRe("example.com"),
293293 pathname: pm.makeRe("/act123/*sr*/pic.jpg", { dot: true }),
294294 } as const;
295295- expect(m(p, new URL("https://com"))).toBe(false);
296296- expect(m(p, new URL("https://example.com"))).toBe(false);
297297- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
298298- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
299299- expect(m(p, new URL("https://example.com/act123"))).toBe(false);
300300- expect(m(p, new URL("https://example.com/act123/usr4"))).toBe(false);
301301- expect(m(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false);
302302- expect(m(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false);
303303- expect(m(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true);
304304- expect(m(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true);
305305- expect(m(p, new URL("https://example.com/act123/.sr6/pic.jpg"))).toBe(true);
306306- expect(m(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(false);
307307- expect(m(p, new URL("https://example.com/act123/team5/pic.jpg"))).toBe(false);
308308- expect(m(p, new URL("https://example.com/team/pic.jpg"))).toBe(false);
309309- expect(m(p, new URL("https://example.com/act123/sr/pic.jpg"))).toBe(true);
295295+ expect(mRP(p, new URL("https://com"))).toBe(false);
296296+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
297297+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
298298+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
299299+ expect(mRP(p, new URL("https://example.com/act123"))).toBe(false);
300300+ expect(mRP(p, new URL("https://example.com/act123/usr4"))).toBe(false);
301301+ expect(mRP(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false);
302302+ expect(mRP(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false);
303303+ expect(mRP(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true);
304304+ expect(mRP(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true);
305305+ expect(mRP(p, new URL("https://example.com/act123/.sr6/pic.jpg"))).toBe(true);
306306+ expect(mRP(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(false);
307307+ expect(mRP(p, new URL("https://example.com/act123/team5/pic.jpg"))).toBe(false);
308308+ expect(mRP(p, new URL("https://example.com/team/pic.jpg"))).toBe(false);
309309+ expect(mRP(p, new URL("https://example.com/act123/sr/pic.jpg"))).toBe(true);
310310 });
311311312312 it("should match pathname pattern with single asterisk at the end", () => {
···314314 hostname: pm.makeRe("example.com"),
315315 pathname: pm.makeRe("/act123/usr*/pic.jpg", { dot: true }),
316316 } as const;
317317- expect(m(p, new URL("https://com"))).toBe(false);
318318- expect(m(p, new URL("https://example.com"))).toBe(false);
319319- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
320320- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
321321- expect(m(p, new URL("https://example.com/act123"))).toBe(false);
322322- expect(m(p, new URL("https://example.com/act123/usr4"))).toBe(false);
323323- expect(m(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false);
324324- expect(m(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false);
325325- expect(m(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true);
326326- expect(m(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true);
327327- expect(m(p, new URL("https://example.com/act123/usr/pic.jpg"))).toBe(true);
328328- expect(m(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(false);
329329- expect(m(p, new URL("https://example.com/act456/team5/pic.jpg"))).toBe(false);
330330- expect(m(p, new URL("https://example.com/team/pic.jpg"))).toBe(false);
331331- expect(m(p, new URL("https://sub.example.com/act123/usr6/pic.jpg"))).toBe(false);
317317+ expect(mRP(p, new URL("https://com"))).toBe(false);
318318+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
319319+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
320320+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
321321+ expect(mRP(p, new URL("https://example.com/act123"))).toBe(false);
322322+ expect(mRP(p, new URL("https://example.com/act123/usr4"))).toBe(false);
323323+ expect(mRP(p, new URL("https://example.com/act123/usr4/pic"))).toBe(false);
324324+ expect(mRP(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(false);
325325+ expect(mRP(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true);
326326+ expect(mRP(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true);
327327+ expect(mRP(p, new URL("https://example.com/act123/usr/pic.jpg"))).toBe(true);
328328+ expect(mRP(p, new URL("https://example.com/act123/team4/pic.jpg"))).toBe(false);
329329+ expect(mRP(p, new URL("https://example.com/act456/team5/pic.jpg"))).toBe(false);
330330+ expect(mRP(p, new URL("https://example.com/team/pic.jpg"))).toBe(false);
331331+ expect(mRP(p, new URL("https://sub.example.com/act123/usr6/pic.jpg"))).toBe(false);
332332 });
333333334334 it("should match pathname pattern with double asterisk", () => {
···336336 hostname: pm.makeRe("example.com"),
337337 pathname: pm.makeRe("/act123/**", { dot: true }),
338338 } as const;
339339- expect(m(p, new URL("https://com"))).toBe(false);
340340- expect(m(p, new URL("https://example.com"))).toBe(false);
341341- expect(m(p, new URL("https://sub.example.com"))).toBe(false);
342342- expect(m(p, new URL("https://example.com.uk"))).toBe(false);
343343- expect(m(p, new URL("https://example.com/act123"))).toBe(true);
344344- expect(m(p, new URL("https://example.com/act123/usr4"))).toBe(true);
345345- expect(m(p, new URL("https://example.com/act123/usr4/pic"))).toBe(true);
346346- expect(m(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(true);
347347- expect(m(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true);
348348- expect(m(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true);
349349- expect(m(p, new URL("https://example.com/act123/usr6/pic.jpg"))).toBe(true);
350350- expect(m(p, new URL("https://example.com/act123/team/pic.jpg"))).toBe(true);
351351- expect(m(p, new URL("https://example.com/act123/.a/pic.jpg"))).toBe(true);
352352- expect(m(p, new URL("https://example.com/act123/team/.pic.jpg"))).toBe(true);
353353- expect(m(p, new URL("https://example.com/act456/team/pic.jpg"))).toBe(false);
354354- expect(m(p, new URL("https://example.com/team/pic.jpg"))).toBe(false);
355355- expect(m(p, new URL("https://sub.example.com/act123/team/pic.jpg"))).toBe(false);
339339+ expect(mRP(p, new URL("https://com"))).toBe(false);
340340+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
341341+ expect(mRP(p, new URL("https://sub.example.com"))).toBe(false);
342342+ expect(mRP(p, new URL("https://example.com.uk"))).toBe(false);
343343+ expect(mRP(p, new URL("https://example.com/act123"))).toBe(true);
344344+ expect(mRP(p, new URL("https://example.com/act123/usr4"))).toBe(true);
345345+ expect(mRP(p, new URL("https://example.com/act123/usr4/pic"))).toBe(true);
346346+ expect(mRP(p, new URL("https://example.com/act123/usr4/picsjpg"))).toBe(true);
347347+ expect(mRP(p, new URL("https://example.com/act123/usr4/pic.jpg"))).toBe(true);
348348+ expect(mRP(p, new URL("https://example.com/act123/usr5/pic.jpg"))).toBe(true);
349349+ expect(mRP(p, new URL("https://example.com/act123/usr6/pic.jpg"))).toBe(true);
350350+ expect(mRP(p, new URL("https://example.com/act123/team/pic.jpg"))).toBe(true);
351351+ expect(mRP(p, new URL("https://example.com/act123/.a/pic.jpg"))).toBe(true);
352352+ expect(mRP(p, new URL("https://example.com/act123/team/.pic.jpg"))).toBe(true);
353353+ expect(mRP(p, new URL("https://example.com/act456/team/pic.jpg"))).toBe(false);
354354+ expect(mRP(p, new URL("https://example.com/team/pic.jpg"))).toBe(false);
355355+ expect(mRP(p, new URL("https://sub.example.com/act123/team/pic.jpg"))).toBe(false);
356356 });
357357358358 it("should throw when hostname is missing", () => {
359359 const p = { protocol: "https" } as const;
360360 // @ts-ignore testing invalid input
361361- expect(m(p, new URL("https://example.com"))).toBe(false);
361361+ expect(mRP(p, new URL("https://example.com"))).toBe(false);
362362+ });
363363+});
364364+365365+/**
366366+ * See https://github.com/vercel/next.js/blob/64702a9/test/unit/image-optimizer/match-local-pattern.test.ts
367367+ */
368368+describe("matchLocalPattern", () => {
369369+ const mLP = (p: LocalPattern, urlPathAndQuery: string) =>
370370+ matchLocalPattern(p, new URL(urlPathAndQuery, "http://n"));
371371+372372+ it("should match anything when no pattern is defined", () => {
373373+ const p = {} as const;
374374+ expect(mLP(p, "/")).toBe(true);
375375+ expect(mLP(p, "/path")).toBe(true);
376376+ expect(mLP(p, "/path/to")).toBe(true);
377377+ expect(mLP(p, "/path/to/file")).toBe(true);
378378+ expect(mLP(p, "/path/to/file.txt")).toBe(true);
379379+ expect(mLP(p, "/path/to/file?q=1")).toBe(true);
380380+ expect(mLP(p, "/path/to/file?q=1&a=two")).toBe(true);
381381+ });
382382+383383+ it("should match any path without a search query string", () => {
384384+ const p = {
385385+ search: "",
386386+ } as const;
387387+ expect(mLP(p, "/")).toBe(true);
388388+ expect(mLP(p, "/path")).toBe(true);
389389+ expect(mLP(p, "/path/to")).toBe(true);
390390+ expect(mLP(p, "/path/to/file")).toBe(true);
391391+ expect(mLP(p, "/path/to/file.txt")).toBe(true);
392392+ expect(mLP(p, "/path/to/file?q=1")).toBe(false);
393393+ expect(mLP(p, "/path/to/file?q=1&a=two")).toBe(false);
394394+ expect(mLP(p, "/path/to/file.txt?q=1&a=two")).toBe(false);
395395+ });
396396+397397+ it("should match literal pathname and any search query string", () => {
398398+ const p = {
399399+ pathname: pm.makeRe("/path/to/file", {
400400+ dot: true,
401401+ }),
402402+ } as const;
403403+ expect(mLP(p, "/")).toBe(false);
404404+ expect(mLP(p, "/path")).toBe(false);
405405+ expect(mLP(p, "/path/to")).toBe(false);
406406+ expect(mLP(p, "/path/to/file")).toBe(true);
407407+ expect(mLP(p, "/path/to/file.txt")).toBe(false);
408408+ expect(mLP(p, "/path/to/file?q=1")).toBe(true);
409409+ expect(mLP(p, "/path/to/file?q=1&a=two")).toBe(true);
410410+ expect(mLP(p, "/path/to/file.txt?q=1&a=two")).toBe(false);
411411+ });
412412+413413+ it("should match pathname with double asterisk", () => {
414414+ const p = {
415415+ pathname: pm.makeRe("/path/to/**", {
416416+ dot: true,
417417+ }),
418418+ } as const;
419419+ expect(mLP(p, "/")).toBe(false);
420420+ expect(mLP(p, "/path")).toBe(false);
421421+ expect(mLP(p, "/path/to")).toBe(true);
422422+ expect(mLP(p, "/path/to/file")).toBe(true);
423423+ expect(mLP(p, "/path/to/file.txt")).toBe(true);
424424+ expect(mLP(p, "/path/to/file?q=1")).toBe(true);
425425+ expect(mLP(p, "/path/to/file?q=1&a=two")).toBe(true);
426426+ expect(mLP(p, "/path/to/file.txt?q=1&a=two")).toBe(true);
362427 });
363428});
+29-2
packages/cloudflare/src/cli/templates/images.ts
···77 search?: string;
88};
991010+export type LocalPattern = {
1111+ // pathname is always set in the manifest
1212+ pathname: string;
1313+ search?: string;
1414+};
1515+1016/**
1117 * Fetches an images.
1218 *
···2228 // Local
2329 if (imageUrl.startsWith("/")) {
2430 let pathname: string;
3131+ let url: URL;
2532 try {
2626- const url = new URL(imageUrl, "http://n");
3333+ // We only need pathname and search
3434+ url = new URL(imageUrl, "http://n");
2735 pathname = decodeURIComponent(url.pathname);
2836 } catch {
2937 return getUrlErrorResponse();
3038 }
3939+3140 if (/\/_next\/image($|\/)/.test(pathname)) {
3241 return getUrlErrorResponse();
3342 }
34434444+ // If localPatterns are not defined all local images are allowed.
4545+ if (
4646+ __IMAGES_LOCAL_PATTERNS__.length > 0 &&
4747+ !__IMAGES_LOCAL_PATTERNS__.some((p: LocalPattern) => matchLocalPattern(p, url))
4848+ ) {
4949+ return getUrlErrorResponse();
5050+ }
5151+3552 return fetcher?.fetch(`http://assets.local${imageUrl}`);
3653 }
3754···4764 return getUrlErrorResponse();
4865 }
49666767+ // The remotePatterns is used to allow images from specific remote external paths and block all others.
5068 if (!__IMAGES_REMOTE_PATTERNS__.some((p: RemotePattern) => matchRemotePattern(p, url))) {
5169 return getUrlErrorResponse();
5270 }
···83101 return true;
84102}
85103104104+export function matchLocalPattern(pattern: LocalPattern, url: URL): boolean {
105105+ // https://github.com/vercel/next.js/blob/d76f0b1/packages/next/src/shared/lib/match-local-pattern.ts
106106+ if (pattern.search !== undefined && pattern.search !== url.search) {
107107+ return false;
108108+ }
109109+110110+ return new RegExp(pattern.pathname).test(url.pathname);
111111+}
112112+86113/**
87114 * @returns same error as Next.js when the url query parameter is not accepted.
88115 */
···93120/* eslint-disable no-var */
94121declare global {
95122 var __IMAGES_REMOTE_PATTERNS__: RemotePattern[];
9696- var __IMAGES_LOCAL_PATTERNS__: unknown[];
123123+ var __IMAGES_LOCAL_PATTERNS__: LocalPattern[];
97124}
98125/* eslint-enable no-var */