summaryrefslogtreecommitdiffstats
path: root/remote/test/puppeteer/packages/puppeteer-core/src/util/Function.ts
diff options
context:
space:
mode:
Diffstat (limited to 'remote/test/puppeteer/packages/puppeteer-core/src/util/Function.ts')
-rw-r--r--remote/test/puppeteer/packages/puppeteer-core/src/util/Function.ts98
1 files changed, 98 insertions, 0 deletions
diff --git a/remote/test/puppeteer/packages/puppeteer-core/src/util/Function.ts b/remote/test/puppeteer/packages/puppeteer-core/src/util/Function.ts
new file mode 100644
index 0000000000..cdf09ba195
--- /dev/null
+++ b/remote/test/puppeteer/packages/puppeteer-core/src/util/Function.ts
@@ -0,0 +1,98 @@
+/**
+ * Copyright 2023 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+const createdFunctions = new Map<string, (...args: unknown[]) => unknown>();
+
+/**
+ * Creates a function from a string.
+ *
+ * @internal
+ */
+export const createFunction = (
+ functionValue: string
+): ((...args: unknown[]) => unknown) => {
+ let fn = createdFunctions.get(functionValue);
+ if (fn) {
+ return fn;
+ }
+ fn = new Function(`return ${functionValue}`)() as (
+ ...args: unknown[]
+ ) => unknown;
+ createdFunctions.set(functionValue, fn);
+ return fn;
+};
+
+/**
+ * @internal
+ */
+export function stringifyFunction(fn: (...args: never) => unknown): string {
+ let value = fn.toString();
+ try {
+ new Function(`(${value})`);
+ } catch {
+ // This means we might have a function shorthand (e.g. `test(){}`). Let's
+ // try prefixing.
+ let prefix = 'function ';
+ if (value.startsWith('async ')) {
+ prefix = `async ${prefix}`;
+ value = value.substring('async '.length);
+ }
+ value = `${prefix}${value}`;
+ try {
+ new Function(`(${value})`);
+ } catch {
+ // We tried hard to serialize, but there's a weird beast here.
+ throw new Error('Passed function cannot be serialized!');
+ }
+ }
+ return value;
+}
+
+/**
+ * Replaces `PLACEHOLDER`s with the given replacements.
+ *
+ * All replacements must be valid JS code.
+ *
+ * @example
+ *
+ * ```ts
+ * interpolateFunction(() => PLACEHOLDER('test'), {test: 'void 0'});
+ * // Equivalent to () => void 0
+ * ```
+ *
+ * @internal
+ */
+export const interpolateFunction = <T extends (...args: never[]) => unknown>(
+ fn: T,
+ replacements: Record<string, string>
+): T => {
+ let value = stringifyFunction(fn);
+ for (const [name, jsValue] of Object.entries(replacements)) {
+ value = value.replace(
+ new RegExp(`PLACEHOLDER\\(\\s*(?:'${name}'|"${name}")\\s*\\)`, 'g'),
+ jsValue
+ );
+ }
+ return createFunction(value) as unknown as T;
+};
+
+declare global {
+ /**
+ * Used for interpolation with {@link interpolateFunction}.
+ *
+ * @internal
+ */
+ function PLACEHOLDER<T>(name: string): T;
+}