Skip to content

Commit

Permalink
feat: add configuration to run specific scripts on main thread (#200)
Browse files Browse the repository at this point in the history
* add configuration to run scripts on main thread

* refactor to comments

* remove unused export from module

Co-authored-by: Daniel Beck <daniel_beck@next.co.uk>
  • Loading branch information
DanBeckDev and Daniel Beck committed Jul 22, 2022
1 parent c2b72c2 commit 999c13c
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/integration/api.md
Expand Up @@ -24,6 +24,7 @@ export interface PartytownConfig {
get?: GetHook;
globalFns?: string[];
lib?: string;
loadScriptsOnMainThread?: string[];
logCalls?: boolean;
logGetters?: boolean;
logImageRequests?: boolean;
Expand Down
12 changes: 11 additions & 1 deletion src/lib/types.ts
Expand Up @@ -425,6 +425,14 @@ export interface PartytownConfig {
* when a third-party script rudely pollutes `window` with functions.
*/
globalFns?: string[];
/**
* This array can be used to filter which script are executed via
* Partytown and which you would like to execute on the main thread.
*
* @example loadScriptsOnMainThread:['https://test.com/analytics.js']
* // Loads the `https://test.com/analytics.js` script on the main thread
*/
loadScriptsOnMainThread?: string[]
get?: GetHook;
set?: SetHook;
apply?: ApplyHook;
Expand Down Expand Up @@ -654,7 +662,9 @@ export interface WorkerInstance {
[InstanceStateKey]: { [key: string]: any };
}

export interface WorkerNode extends WorkerInstance, Node {}
export interface WorkerNode extends WorkerInstance, Node {
type: string | undefined;
}

export interface WorkerWindow extends WorkerInstance {
[key: string]: any;
Expand Down
13 changes: 13 additions & 0 deletions src/lib/web-worker/worker-script.ts
Expand Up @@ -4,6 +4,7 @@ import { getter, setter } from './worker-proxy';
import { HTMLSrcElementDescriptorMap } from './worker-src-element';
import { resolveUrl } from './worker-exec';
import { StateProp, WebWorkerEnvironment, WorkerNode } from '../types';
import { webWorkerCtx } from './worker-constants';

export const patchHTMLScriptElement = (WorkerHTMLScriptElement: any, env: WebWorkerEnvironment) => {
const HTMLScriptDescriptorMap: PropertyDescriptorMap & ThisType<WorkerNode> = {
Expand All @@ -16,12 +17,24 @@ export const patchHTMLScriptElement = (WorkerHTMLScriptElement: any, env: WebWor
},
set(url: string) {
const orgUrl = resolveUrl(env, url, null);
const config = webWorkerCtx.$config$;
url = resolveUrl(env, url, 'script');
setInstanceStateValue(this, StateProp.url, url);
setter(this, ['src'], url);

if (orgUrl !== url) {
setter(this, ['dataset', 'ptsrc'], orgUrl);
}

if (this.type && config.loadScriptsOnMainThread) {
const shouldExecuteScriptViaMainThread = config.loadScriptsOnMainThread.some(scriptUrl =>
scriptUrl === url
)

if (shouldExecuteScriptViaMainThread) {
setter(this, ['type'], 'text/javascript');
}
}
},
},

Expand Down
1 change: 1 addition & 0 deletions tests/index.html
Expand Up @@ -117,6 +117,7 @@ <h2>Service Integration Tests</h2>
<li><a href="/tests/integrations/config/">Config</a></li>
<li><a href="/tests/integrations/event-forwarding/">Event Forwarding</a></li>
<li><a href="/tests/integrations/main-window-accessors/">Main Window Accessors</a></li>
<li><a href="/tests/integrations/load-scripts-on-main-thread/">Load Scripts on Main Thread</a></li>
<li><a href="/tests/integrations/facebook-pixel/">Facebook Pixel</a></li>
<li><a href="/tests/integrations/gtm/">Google Tag Manager (GTM)</a></li>
<li><a href="/tests/integrations/hubspot/forms.html">Hubspot Forms</a></li>
Expand Down
121 changes: 121 additions & 0 deletions tests/integrations/load-scripts-on-main-thread/index.html
@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='UTF-8' />
<meta name='viewport' content='width=device-width, initial-scale=1' />
<meta name='description' content='Partytown Test Page' />
<title>Load scripts on main thread</title>

<script>
partytown = {
logCalls: true,
logGetters: true,
logSetters: true,
logStackTraces: false,
logScriptExecution: true,
loadScriptsOnMainThread: [`http://${location.host}/tests/integrations/load-scripts-on-main-thread/test-script.js`],
};
</script>
<script src='/~partytown/debug/partytown.js'></script>
<script type='text/partytown'>
(() => {
const scriptElement = document.createElement("script");
scriptElement.type = "text/javascript";
scriptElement.src = "./test-script.js";
scriptElement.id = "testScript";
document.head.appendChild(scriptElement);
})()
</script>

<style>
body {
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif,
Apple Color Emoji, Segoe UI Emoji;
font-size: 12px;
}

h1 {
margin: 0 0 15px 0;
}

ul {
list-style-type: none;
margin: 0;
padding: 0;
}

a {
display: block;
padding: 16px 8px;
}

a:link,
a:visited {
text-decoration: none;
color: blue;
}

a:hover {
background-color: #eee;
}

li {
display: flex;
margin: 15px 0;
}

li strong,
li code,
li button {
white-space: nowrap;
flex: 1;
margin: 0 5px;
}
</style>
</head>
<body>
<h1>Load scripts on main thread 🎉</h1>
<ul>
<li>
<strong>Script Type:</strong>
<code id='testScriptType'></code>
<script type='text/partytown'>
(() => {
const scriptElement = document.getElementById('testScript');
const codeElement = document.getElementById('testScriptType');

codeElement.innerText = scriptElement.type;
})()
</script>
</li>
<li>
<strong>Script Source:</strong>
<code id='testScriptSource'></code>
<script type='text/partytown'>
(() => {
const scriptElement = document.getElementById('testScript');
const codeElement = document.getElementById('testScriptSource');

codeElement.innerText = scriptElement.src;
})()
</script>
</li>
<li>
<strong>Partytown Config:</strong>
<code id='testConfig'></code>
<script>
(() => {
const partyTownConfig = window.partytown;
const codeElement = document.getElementById('testConfig');

codeElement.innerText = partyTownConfig.loadScriptsOnMainThread;
})()
</script>
</li>
</ul>

<hr />
<p><a href='/tests/'>All Tests</a></p>

</body>
</html>
@@ -0,0 +1,9 @@
import { test, expect } from '@playwright/test';

test('integration window accessor', async ({ page }) => {
await page.goto('/tests/integrations/load-scripts-on-main-thread/');
await page.waitForSelector('.completed');

const scriptElement = page.locator('#testScript');
await expect(scriptElement).toHaveAttribute('type', 'text/javascript')
});
3 changes: 3 additions & 0 deletions tests/integrations/load-scripts-on-main-thread/test-script.js
@@ -0,0 +1,3 @@
(() => {
document.body.classList.add('completed');
})()

1 comment on commit 999c13c

@vercel
Copy link

@vercel vercel bot commented on 999c13c Jul 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.