Skip to content

Commit ac62022

Browse files
committed
Remove polyfills for fetch, globalThis, URL and URLSearchParams
We are not targeting browsers, we don't need polyfills for them. This also removes extra dependencies, which improves compatibility.
1 parent cfddfc3 commit ac62022

3 files changed

Lines changed: 55 additions & 98 deletions

File tree

scripts/build_npm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { build, emptyDir } from "https://deno.land/x/dnt@0.32.1/mod.ts";
1+
import { build, emptyDir } from "https://deno.land/x/dnt@0.34.0/mod.ts";
22
import { version } from "../version.ts";
33

44
await emptyDir("./npm");

src/serpapi.ts

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import {
1111
} from "./types.ts";
1212
import {
1313
_internals,
14-
execute,
1514
extractNextParameters,
1615
haveParametersChanged,
1716
} from "./utils.ts";
@@ -90,7 +89,7 @@ export async function getJson<
9089
},
9190
timeout,
9291
);
93-
const json = await response.json() as BaseResponse<E>;
92+
const json = JSON.parse(response) as BaseResponse<E>;
9493
const nextParametersFromResponse = await extractNextParameters<E>(json);
9594
if (
9695
// https://github.com/serpapi/public-roadmap/issues/562
@@ -137,7 +136,7 @@ export async function getHtml<
137136
) {
138137
const key = validateApiKey(parameters.api_key, true);
139138
const timeout = validateTimeout(parameters.timeout);
140-
const response = await _internals.execute(
139+
const html = await _internals.execute(
141140
SEARCH_PATH,
142141
{
143142
...parameters,
@@ -147,7 +146,6 @@ export async function getHtml<
147146
},
148147
timeout,
149148
);
150-
const html = await response.text();
151149
callback?.(html);
152150
return html;
153151
}
@@ -191,7 +189,7 @@ export async function getJsonBySearchId<
191189
},
192190
timeout,
193191
);
194-
const json = await response.json() as R;
192+
const json = JSON.parse(response) as R;
195193
callback?.(json);
196194
return json;
197195
}
@@ -226,15 +224,14 @@ export async function getHtmlBySearchId(
226224
) {
227225
const key = validateApiKey(parameters.api_key);
228226
const timeout = validateTimeout(parameters.timeout);
229-
const response = await _internals.execute(
227+
const html = await _internals.execute(
230228
`${SEARCH_ARCHIVE_PATH}/${searchId}`,
231229
{
232230
api_key: key,
233231
output: "html",
234232
},
235233
timeout,
236234
);
237-
const html = await response.text();
238235
callback?.(html);
239236
return html;
240237
}
@@ -260,10 +257,10 @@ export async function getAccount(
260257
) {
261258
const key = validateApiKey(parameters.api_key);
262259
const timeout = validateTimeout(parameters.timeout);
263-
const response = await execute(ACCOUNT_PATH, {
260+
const response = await _internals.execute(ACCOUNT_PATH, {
264261
api_key: key,
265262
}, timeout);
266-
const info = await response.json() as AccountInformation;
263+
const info = JSON.parse(response) as AccountInformation;
267264
callback?.(info);
268265
return info;
269266
}
@@ -289,12 +286,12 @@ export async function getLocations(
289286
callback?: (locations: Locations) => void,
290287
) {
291288
const timeout = validateTimeout(parameters.timeout);
292-
const response = await execute(
289+
const response = await _internals.execute(
293290
LOCATIONS_PATH,
294291
parameters,
295292
timeout,
296293
);
297-
const locations = await response.json() as Locations;
294+
const locations = JSON.parse(response) as Locations;
298295
callback?.(locations);
299296
return locations;
300297
}

src/utils.ts

Lines changed: 46 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
import type { EngineName, EngineParameters } from "./types.ts";
22
import { version } from "../version.ts";
3-
4-
type UrlParameters = Record<
5-
string,
6-
string | number | boolean | undefined | null
7-
>;
3+
import https from "node:https";
4+
import qs from "node:querystring";
85

96
/**
107
* This `_internals` object is needed to support stubbing/spying of
@@ -14,42 +11,8 @@ type UrlParameters = Record<
1411
* It's also useful to encapsulate functions that are polyfilled.
1512
*/
1613
export const _internals = {
17-
get fetch() {
18-
return (async () => {
19-
// Use runtime's `fetch` if it exists, otherwise fallback to `cross-fetch`.
20-
return typeof fetch === "function"
21-
? Promise.resolve(fetch)
22-
: (await import("npm:cross-fetch@3.1.4")).default;
23-
})();
24-
},
2514
execute: execute,
2615
getBaseUrl: getBaseUrl,
27-
get globalThis() {
28-
return (async () => {
29-
// Use runtime's `globalThis` if it exists, otherwise fallback to `core-js`'s implementation.
30-
// dnt-shim-ignore
31-
const gt = typeof globalThis !== "undefined" ? globalThis : undefined;
32-
return gt !== undefined
33-
? Promise.resolve(gt)
34-
: (await import("npm:core-js-pure@3.28.0")).default.globalThis;
35-
})();
36-
},
37-
get URL(): Promise<typeof URL> {
38-
return (async () => {
39-
// Use runtime's `URL` if it exists, otherwise fallback to `core-js`'s implementation.
40-
return typeof URL !== "undefined"
41-
? Promise.resolve(URL)
42-
: (await import("npm:core-js-pure@3.28.0")).default.URL;
43-
})();
44-
},
45-
get URLSearchParams(): Promise<typeof URLSearchParams> {
46-
return (async () => {
47-
// Use runtime's `URLSearchParams` if it exists, otherwise fallback to `core-js`'s implementation.
48-
return typeof URLSearchParams !== "undefined"
49-
? Promise.resolve(URLSearchParams)
50-
: (await import("npm:core-js-pure@3.28.0")).default.URLSearchParams;
51-
})();
52-
},
5316
};
5417

5518
/** Facilitates stubbing in tests, e.g. localhost as the base url */
@@ -65,7 +28,7 @@ type NextParameters<E extends EngineName = EngineName> = {
6528
>
6629
]: string;
6730
};
68-
export async function extractNextParameters<E extends EngineName = EngineName>(
31+
export function extractNextParameters<E extends EngineName = EngineName>(
6932
json: {
7033
serpapi_pagination?: { next: string };
7134
pagination?: { next: string };
@@ -75,7 +38,6 @@ export async function extractNextParameters<E extends EngineName = EngineName>(
7538
json["pagination"]?.["next"];
7639

7740
if (nextUrlString) {
78-
const URL = await _internals.URL;
7941
const nextUrl = new URL(nextUrlString);
8042
const nextParameters: Record<string, string> = {};
8143
for (const [k, v] of nextUrl.searchParams.entries()) {
@@ -100,65 +62,63 @@ export function haveParametersChanged(
10062
);
10163
}
10264

103-
export async function getSource() {
65+
export function getSource() {
10466
const moduleSource = `serpapi@${version}`;
105-
try {
106-
const gt = await _internals.globalThis;
107-
108-
// Check if running in Node.js
109-
const nodeVersion = gt.process?.versions?.node;
110-
if (nodeVersion) {
111-
return `nodejs@${nodeVersion},${moduleSource}`;
112-
}
113-
114-
// Assumes running in Deno instead. https://deno.land/api?s=Deno.version
115-
// Deno.version is not shimmed since it's not used when ran in a Node env.
116-
const denoVersion = gt.Deno?.version?.deno;
67+
if (typeof Deno == "object") {
68+
const denoVersion = Deno.version?.deno;
11769
if (denoVersion) {
11870
return `deno@${denoVersion},${moduleSource}`;
11971
}
120-
121-
return `nodejs,${moduleSource}`;
122-
} catch {
123-
// If something unexpectedly occurs, revert to "nodejs".
124-
return `nodejs,${moduleSource}`;
72+
// @ts-ignore: scope of nodejs
73+
} else if (typeof process == "object") {
74+
// @ts-ignore: scope of nodejs
75+
const nodeVersion = process.versions?.node;
76+
if (nodeVersion) {
77+
return `nodejs@${nodeVersion},${moduleSource}`;
78+
}
12579
}
80+
return `nodejs,${moduleSource}`;
12681
}
12782

128-
export async function buildUrl<P extends UrlParameters>(
83+
export function buildUrl(
12984
path: string,
130-
parameters: P,
131-
): Promise<string> {
132-
const nonUndefinedParams: [string, string][] = Object.entries(parameters)
133-
.filter(([_, value]) => value !== undefined)
134-
.map(([key, value]) => [key, `${value}`]);
135-
const URLSearchParams = await _internals.URLSearchParams;
136-
const searchParams = new URLSearchParams(nonUndefinedParams);
137-
return `${_internals.getBaseUrl()}${path}?${searchParams}`;
85+
parameters: qs.ParsedUrlQueryInput,
86+
): string {
87+
return `${_internals.getBaseUrl()}${path}?${qs.stringify(parameters)}`;
13888
}
13989

140-
export async function execute<P extends UrlParameters>(
90+
export function execute(
14191
path: string,
142-
parameters: P,
92+
parameters: qs.ParsedUrlQueryInput,
14393
timeout: number,
144-
): Promise<Response> {
145-
const url = await buildUrl(path, {
94+
): Promise<string> {
95+
const url = buildUrl(path, {
14696
...parameters,
147-
source: await getSource(),
97+
source: getSource(),
14898
});
149-
// https://github.com/github/fetch/issues/175#issuecomment-216791333
15099
return new Promise((resolve, reject) => {
151-
const timer = setTimeout(() => reject(new Error("Timeout")), timeout);
152-
_internals.fetch.then((fetch) =>
153-
fetch(url)
154-
.then((res) => {
155-
clearTimeout(timer);
156-
resolve(res);
157-
})
158-
.catch((err) => {
159-
clearTimeout(timer);
160-
reject(err);
161-
})
162-
);
100+
https.get(url, { timeout }, (resp) => {
101+
let data = "";
102+
103+
// A chunk of data has been recieved.
104+
resp.on("data", (chunk) => {
105+
data += chunk;
106+
});
107+
108+
// The whole response has been received. Print out the result.
109+
resp.on("end", () => {
110+
try {
111+
if (resp.statusCode == 200) {
112+
resolve(data);
113+
} else {
114+
reject(data);
115+
}
116+
} catch (e) {
117+
reject(e);
118+
}
119+
});
120+
}).on("error", (err) => {
121+
reject(err);
122+
});
163123
});
164124
}

0 commit comments

Comments
 (0)