immer っぽい API だとうれしいなと思って書いてみました。
元となるコード
(async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.emulate(iPhone); await page.goto('https://example.com'); await page.screenshot({ path: 'screenshot.png' }); await browser.close(); })();
await が多くないですか?
関数でラップするタイプ
こんな感じになります。
(async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await removeAwait(page, (page) => { page.emulate(iPhone); page.goto('https://example.com'); page.screenshot({ path: 'screenshot.png' }); }); await browser.close(); })();
removeAwait の実装がこちら
const removeAwait = async <T extends object>( obj: T, callback: (obj: T) => void ) => { const commands: [string | symbol, unknown[]][] = []; const proxied = new Proxy(obj, { get: (...[, methodName]) => new Proxy(() => {}, { apply: (...[, , argumentsList]) => { commands.push([methodName, argumentsList]); }, }), }); callback(proxied); for (const [methodName, args] of commands) { await (obj[methodName as keyof typeof obj] as any)(...args); } };
ネストが増えない版
こんな感じになります。
(async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); const draft = createDraft(page); draft.emulate(iPhone); draft.goto('https://example.com'); draft.screenshot({ path: 'screenshot.png' }); await finish(draft); await browser.close(); })();
finish を呼び出すまでメソッド呼び出しが保留されます。createDraft の実装はこちら
const FinishKey = Symbol('finish'); const createDraft = <T extends object>(obj: T) => { const commands: [string | symbol, unknown[]][] = []; const processCommands = async () => { for (const [methodName, args] of commands) { await (obj[methodName as keyof typeof obj] as any)(...args); } }; return new Proxy(obj, { get: (...[, methodName]) => { if (methodName !== FinishKey) { return new Proxy(() => {}, { apply: (...[, , argumentsList]) => { commands.push([methodName, argumentsList]); }, }); } return processCommands; }, }) as T & { [FinishKey]: () => void }; }; const finish = (draft: { [FinishKey]: () => void }) => draft[FinishKey]();
書いておいてなんですが、割と微妙だな…というのが正直な感想です。
他に注意点として、Puppeteer で使うことを前提にいくらか実装をサボっている(関数であることを検証するのが面倒で as any 使ってたり、page.mouse.up みたいなのを想定してなかったり)ので、ほかの用途にこの関数は使わないでください