Skip to content

Commit 54c0727

Browse files
committed
refactor: split getRequireString into its own function
1 parent 6362393 commit 54c0727

File tree

6 files changed

+329
-80
lines changed

6 files changed

+329
-80
lines changed

package-lock.json

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { parseQuery } from "loader-utils";
2+
3+
import { FullOptions } from "..";
4+
5+
// look through remaining loaders and add options to resize loader
6+
export default function getRequireStringWithModifiedResizeLoaderOptions(
7+
remainingRequest: string,
8+
loaders: {
9+
path: string;
10+
request: string;
11+
options?: Record<string, any> | string;
12+
}[],
13+
loaderIndex: number,
14+
options: { width?: number; scale?: number },
15+
resizeLoaderName: string,
16+
customOptionsFactory: FullOptions["customOptionsFactory"]
17+
): string {
18+
const resizeLoaderResolvedPath = require.resolve(resizeLoaderName);
19+
const resizeLoader = loaders.find(
20+
({ path }, index) =>
21+
index > loaderIndex && path === resizeLoaderResolvedPath
22+
);
23+
24+
if (resizeLoader === undefined)
25+
throw new Error(`Can't find ${resizeLoaderName} in the list of loaders`);
26+
27+
const resizeLoaderOptions =
28+
typeof resizeLoader.options === "string"
29+
? parseQuery("?" + resizeLoader.options)
30+
: resizeLoader.options;
31+
const resizeLoaderRequest = resizeLoader.request;
32+
const resizeLoaderPath = resizeLoader.path;
33+
34+
if (customOptionsFactory)
35+
return remainingRequest.replace(
36+
resizeLoaderRequest,
37+
resizeLoaderPath +
38+
"?" +
39+
escapeJsonStringForLoader(
40+
JSON.stringify(
41+
customOptionsFactory(
42+
options.width,
43+
options.scale,
44+
resizeLoaderOptions
45+
)
46+
)
47+
)
48+
);
49+
50+
return remainingRequest.replace(
51+
resizeLoaderRequest,
52+
resizeLoaderPath +
53+
"?" +
54+
escapeJsonStringForLoader(
55+
JSON.stringify({
56+
scaleUp: true,
57+
...resizeLoaderOptions,
58+
...options,
59+
fileLoaderOptions: {
60+
...resizeLoaderOptions?.fileLoaderOptions,
61+
esModule: false, // because we're using require in pitch
62+
},
63+
})
64+
)
65+
);
66+
}
67+
68+
// needed so webpack doesn't mistake "!" for query operator "!"
69+
function escapeJsonStringForLoader(s: string): string {
70+
return s.replace(/!/g, "\\\\x21");
71+
}

src/index.ts

Lines changed: 12 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { parseQuery } from "loader-utils";
21
import { validate } from "schema-utils";
32
import { Schema } from "schema-utils/declarations/validate";
43
import sharp from "sharp";
@@ -8,24 +7,24 @@ import { getOptions } from "@calvin-l/webpack-loader-util";
87

98
import getMaxDensity from "./helpers/getMaxDensity";
109
import getOptionFromSize from "./helpers/getOptionFromSize";
10+
import getRequireString from "./helpers/getRequireStringWithModifiedResizeLoaderOptions";
1111
import validateSizes from "./helpers/validateSizes";
1212
import schema from "./options.json";
1313

1414
export interface Options {
15-
// Waiting for typescript 4.2.0 to fix https://github.com/microsoft/TypeScript/issues/41651
16-
// readonly sizes?: (`${number}w` | `${number}x` | "original")[];
17-
readonly sizes?: string[];
15+
readonly sizes?: (`${number}w` | `${number}x` | "original")[];
1816
readonly scaleUp?: boolean;
17+
readonly resizeLoader?: string;
1918
readonly customOptionsFactory?: (
2019
width: number | undefined,
2120
scale: number | undefined,
2221
existingOptions: Record<string, any> | undefined
23-
) => string;
22+
) => Record<string, any>;
2423
readonly esModule?: boolean;
2524
}
2625

2726
export type FullOptions = Options &
28-
Required<Pick<Options, "sizes" | "scaleUp" | "esModule">>;
27+
Required<Pick<Options, "sizes" | "scaleUp" | "resizeLoader" | "esModule">>;
2928

3029
export const raw = true;
3130

@@ -38,6 +37,7 @@ export function pitch(
3837
// @ts-expect-error setting sizes as undefined and let validate throw error if it doesn't exist
3938
sizes: undefined,
4039
scaleUp: false,
40+
resizeLoader: "webpack-image-resize-loader",
4141
esModule: true,
4242
};
4343
const options: FullOptions = {
@@ -77,6 +77,7 @@ export default function (this: loader.LoaderContext, source: Buffer): Buffer {
7777
return source;
7878
}
7979

80+
// return require('-!some-loader?{...}!resize-loader?{...}!file.png')
8081
async function generateSrcSetString(
8182
remainingRequest: string,
8283
loaders: any[],
@@ -95,12 +96,14 @@ async function generateSrcSetString(
9596
const requireEnd = "')}";
9697

9798
for (const size of sizes) {
99+
// no need for options.scale or options.width if size === "orignal"
98100
if (size === "original") {
99-
result += `${requireStart}${addOptionsToResizeLoader(
101+
result += `${requireStart}${getRequireString(
100102
remainingRequest,
101103
loaders,
102104
loaderIndex,
103105
{},
106+
options.resizeLoader,
104107
options.customOptionsFactory
105108
)}${requireEnd}${separator}`;
106109

@@ -118,11 +121,12 @@ async function generateSrcSetString(
118121
if (width !== undefined && resizeLoaderOption.width > width) continue;
119122
}
120123

121-
result += `${requireStart}${addOptionsToResizeLoader(
124+
result += `${requireStart}${getRequireString(
122125
remainingRequest,
123126
loaders,
124127
loaderIndex,
125128
resizeLoaderOption,
129+
options.resizeLoader,
126130
options.customOptionsFactory
127131
)}${requireEnd} ${size}${separator}`;
128132
}
@@ -131,58 +135,3 @@ async function generateSrcSetString(
131135

132136
return result;
133137
}
134-
135-
function addOptionsToResizeLoader(
136-
remainingRequest: string,
137-
loaders: any[],
138-
loaderIndex: number,
139-
options: { width?: number; scale?: number },
140-
customOptionsFactory: FullOptions["customOptionsFactory"]
141-
): string {
142-
const nextLoader = loaders[loaderIndex + 1];
143-
144-
const resizeLoaderOptions =
145-
typeof nextLoader.options === "string"
146-
? parseQuery("?" + nextLoader.options)
147-
: nextLoader.options;
148-
const resizeLoaderRequest = nextLoader.request;
149-
const resizeLoaderPath = nextLoader.path;
150-
151-
if (customOptionsFactory)
152-
return remainingRequest.replace(
153-
resizeLoaderRequest,
154-
resizeLoaderPath +
155-
"?" +
156-
escapeJsonStringForLoader(
157-
JSON.stringify(
158-
customOptionsFactory(
159-
options.width,
160-
options.scale,
161-
resizeLoaderOptions
162-
)
163-
)
164-
)
165-
);
166-
167-
return remainingRequest.replace(
168-
resizeLoaderRequest,
169-
resizeLoaderPath +
170-
"?" +
171-
escapeJsonStringForLoader(
172-
JSON.stringify({
173-
scaleUp: true,
174-
...resizeLoaderOptions,
175-
...options,
176-
fileLoaderOptions: {
177-
...resizeLoaderOptions?.fileLoaderOptions,
178-
esModule: false, // because we're using require in pitch
179-
},
180-
})
181-
)
182-
);
183-
}
184-
185-
// needed so webpack doesn't mistake "!" for query operator "!"
186-
function escapeJsonStringForLoader(s: string): string {
187-
return s.replace(/!/g, "\\\\x21");
188-
}

src/options.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
"description": "Whether or not to scale up the image when the desired size is larger than the image size. Default to false.",
1515
"type": "boolean"
1616
},
17+
"resizeLoader": {
18+
"description": "Name of resize loader. Default to \"webpack-image-resize-loader\".",
19+
"type": "string"
20+
},
1721
"customOptionsFactory": {
1822
"description": "A function that returns the option to be passed on to the next loader.",
1923
"instanceof": "Function"

test/e2e/customOptionsFactory-option.test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ describe.each([4, 5] as const)(
44
'v%d "customOptionsFactory" option',
55
(webpackVersion) => {
66
it("should work with a function", async () => {
7-
const mockCustomOptionsFactory = jest.fn().mockReturnValue({
7+
const mockCustomOptionsFactory: (
8+
width: number | undefined,
9+
scale: number | undefined,
10+
existingOptions: Record<string, any>
11+
) => Record<string, any> = jest.fn().mockReturnValue({
812
fileLoaderOptions: {
913
esModule: false,
1014
},
@@ -17,7 +21,7 @@ describe.each([4, 5] as const)(
1721
customOptionsFactory: (
1822
width: number | undefined,
1923
scale: number | undefined,
20-
existingOptions: Record<string, unknown>
24+
existingOptions: Record<string, any>
2125
) => mockCustomOptionsFactory(width, scale, existingOptions),
2226
},
2327
});

0 commit comments

Comments
 (0)