GithubHelp home page GithubHelp logo

builderio / partytown Goto Github PK

View Code? Open in Web Editor NEW
12.7K 69.0 411.0 21.31 MB

Relocate resource intensive third-party scripts off of the main thread and into a web worker. πŸŽ‰

Home Page: https://partytown.builder.io

License: MIT License

JavaScript 2.83% TypeScript 97.17%
webworker 3rd-party 3rdparty web-worker performance analytics core-web-vitals lighthouse lighthouse-score javascript

partytown's Introduction

Partytown πŸŽ‰

Partytown github fit 2x

A fun location for your third-party scripts to hang out

Partytown is a lazy-loaded library to help relocate resource intensive scripts into a web worker, and off of the main thread. Its goal is to help speed up sites by dedicating the main thread to your code, and offloading third-party scripts to a web worker.

Note: Partytown is still in beta and not guaranteed to work in every scenario. Please see our FAQ and Trade-Off sections for more info.

The philosophy is that the main thread should be dedicated to your code, and any scripts that are not required to be in the critical path should be moved to a web worker. Main thread performance is, without question, more important than web worker thread performance.

Without Partytown and With Partytown: Your code and third-party code compete for main thread resources

Community

Related Projects

  • Qwik: An open-source framework designed for best possible time to interactive, by focusing on resumability of server-side-rendering of HTML, and fine-grained lazy-loading of code.
  • Mitosis: Write components once, run everywhere. Compiles to Vue, React, Solid, Angular, Svelte, and more.
  • Builder: Drag and drop page builder and CMS for React, Vue, Angular, and more.


Made with love by Builder.io

partytown's People

Contributors

adamdbradley avatar amiryonatan avatar arturovt avatar benmccann avatar codercatdev avatar danbeckdev avatar danielroe avatar esjs avatar fl0pzz avatar franpeza avatar gioboa avatar hackmd-deploy avatar hamatoyogi avatar j0shi82 avatar kavian77 avatar khalwat avatar manucorporat avatar mhevery avatar molszanski avatar montalvomiguelo avatar nicksrandall avatar oyemade avatar priestch avatar ryanflorence avatar sanyamkamat avatar shyam-builder avatar slawekkolodziej avatar steve8708 avatar vinaygosain avatar westonruter avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

partytown's Issues

Probably broken in Dev & Canary Chrome Builds

Please be advised: https://bugs.chromium.org/p/chromium/issues/detail?id=1078821

Error: Uncaught DOMException: Failed to execute 'importScripts' on 'WorkerGlobalScope': The script at 'http://localhost/...' failed to load.

As a quick summary, this bug affects Workers that call importScripts() and are loaded from localhost or constructed from a data/Blob URL. E.g. Essentially anything using WebPack running in Cordova or Ionic Capacitor.

The above bug will likely break this package (and any use of Worker in production WebPack apps) if it is not fixed before affected Chrome Dev & Canary builds get to the production / stable branch. It does not appear to be a bug in the Partytown package at this time. I do not know if there are any workarounds.

I am calling attention to this so that anyone here who depends on Workers actually working in production will see this coming ahead of time, and maybe bring some more priority to the issue over at Chromium. Please add any feedback you have to that issue / star / vote for this issue in the Chromium bug tracker

And for anyone else who comes here looking for why Workers are broken, if you get an error about importScripts(), this is probably why.

Thank you! Feel free to close this issue if it is not helpful.

CORS issues

First off, I'm so excited for this project πŸš€ great work so far πŸ”₯

I wanted to report some CORS errors I'm seeing in my application,

partytown-ww-sw.js:333 Access to XMLHttpRequest at 'https://4273294.fls.doubleclick.net/activityi;src=4273294;type=sonos035;cat=sonos00h;ord=4456809462774;gtm=2wg9r0;auiddc=855366606.1630431318;~oref=http%3A%2F%2Flocalhost%3A3000%2Fen-us%2Fhome?' (redirected from 'http://4273294.fls.doubleclick.net/activityi;src=4273294;type=sonos035;cat=sonos00h;ord=4456809462774;gtm=2wg9r0;auiddc=855366606.1630431318;~oref=http%3A%2F%2Flocalhost%3A3000%2Fen-us%2Fhome?') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

This is the invoking code that triggers the error,

, gc = function(a, b) {
        var c = H.createElement("iframe");
        c.height = "0";
        c.width = "0";
        c.style.display = "none";
        c.style.visibility = "hidden";
        var d = H.body && H.body.lastChild || H.body || H.head;
        d.parentNode.insertBefore(c, d);
        cc(c, b);
        void 0 !== a && (c.src = a); // CORS error is reported here
        return c

image

This is what I see in the DOM

image

Investigate embedding Partytown via a cross-origin iframe

I believe that Partytown's isolation could be improved by embedding it via a cross-origin iframe rather than directly as a worker.

Problem

Because the worker inherits the embedding page's origin, it can access data from that origin via APIs like self.indexedDB that are available in the WorkerGlobalScope.

Proposed solution

Instead of integrating Partytown directly into the embedding document as described in the documentation, users could instead host the service worker, the web worker, and a minimal HTML document on a separate origin:

  • document.com
    • /index.html - the embedding document
  • sandbox.com
    • /sandbox.html - the sandbox iframe
    • /web-worker.js - the web worker script
    • /service-worker.js - the service worker script

The sandbox iframe would be responsible for

  1. registering the service worker
  2. starting the web worker
  3. exposing Partytown's API to the embedding document via postMessage

This will impose a small latency cost for the extra postMessage hop between the iframe and the embedding document, but would ensure that the web worker cannot access any resources from the embedding document that are subject to the same origin policy.

Scripts not being intercepted on a client-side transition

Hi folks! I'm noticing an interesting issue adding Partytown to a Next.js app and I'm not sure if it's intended behavior or if I'm just doing something wrong πŸ˜….

Consider a basic Next.js application with two routes that have the <Partytown/> React component included in its head along with a 3P script with type="text/partytown":

Page 1:

import { Partytown } from "@builder.io/partytown/react";

const Home = () => {
  return (
    <>
      <Head>
        <Partytown debug={true} logScriptExecution={true} />
        <script
          src="https://cdn.jsdelivr.net/npm/cookieconsent@3/build/cookieconsent.min.js"
          type="text/partytown"
        />
      </Head>
      <Link href="/about">About</Link>
    </>
  );
};

Page 2:

import { Partytown } from "@builder.io/partytown/react";

const About = () => {
  return (
    <>
      <Head>
        <Partytown debug={true} logScriptExecution={true} />
        <script
          src="https://connect.facebook.net/en_US/sdk.js"
          type="text/partytown"
        />
      </Head>
      <Link href="/">About</Link>
    </>
  );
};

When I reload either of the two pages where the Partytown snippet gets injected server-side, the script seems to be intercepted by the service worker correctly and I see that it gets executed via the debug logs:

Screen Shot 2022-02-12 at 12 29 15 PM

However when I navigate from one page to the other via a client-side transition, the script on the second page doesn't seem to be fetched or executed. Here's a video that shows how the script on the second page doesn't load on a client-side navigation but does during an SSR reload:

Screen.Recording.2022-02-12.at.2.49.17.PM.mov

Similarly, if I remove the script in Page 1 and then navigate to Page 2 where the Partytown snippet should get injected and instantiated client-side, it doesn't seem to work as well πŸ€”.


I set up a reproduction here: https://codesandbox.io/s/mystifying-goldberg-o12j7?file=/pages/index.js. Please let me know if you need any more details and/or if I can help debug where the potential issue may be happening.

Support for Google Tag Manager Community Templates (Sandboxed JS)

I tried converting our existing GTM setup to run through partytown, however I ran into issues with the majority of our tags. Official templates worked as expected (Google Adwords, Analytics etc.), as do Custom HTML/Image tags. The problem is with Community Templates which are already sandboxed by GTM. Functionality isn't affected (these tags can still inject scripts), however window variables aren't being forwarded from GTM to the sandboxed tag:

partytown = {
  forward: [ 
    'dataLayer.push', 'fbq' 
  ] 
};

This works fine and fbq can be accessed if the fb pixel is included directly on the page or via GTM using the Custom HTML tag.

Same goes for dataLayer variables that are set when configuring the template.

Code within GTM Template

// data.value = dataLayer.ecommerce.value (forwarded from partytown)
const value = data.value; // works

// sandbox within a sandbox
let fbq = copyFromWindow('fbq'); // This is where it breaks down

I couldn't find details regarding the sandbox implementation that GTM uses, so I'm not sure if this is something that can be solved or not. Alternatively we could always rewrite the templates as custom html tags, though ideally I would like to avoid having duplicate tags instead of a single template.

Info regarding the sandbox can be found here:
https://developers.google.com/tag-platform/tag-manager/templates/sandboxed-javascript

Thanks in advance.

Get Partytown to Beta Status

Overall Goal

Have Partytown in production and we can collect performance metrics so that we can write use-case studies.

Criterion for when done:

  • GTM works with Partytown
    • GTM is running in production on builder.io all sites
  • HubSpot integration
    • HubSpot running in production
  • Intercom integration
    • Intercom running in production
  • https://www.webpagetest.org/ testing
  • Platform APIs
    • Implement HTML constructor names on the global 0a018c2
    • MutationObserver 09ed37b
    • IntersectionObserver
    • #23
    • #25
  • #10

Make static file location configurable (to store lib somewhere other than /~partytown/)

Hello!

We're trying to implement PartyTown, but we'd like to serve the lib directory up from an alternate location, rather than the root of the application. We could do this with a reverse proxy, but we think having the optionality on the partytown package would improve optionality in implementing the library.

What would be required to load your lib from a different location other than /~partytown/? I'm not sure if I'm just not seeing the functionality that is already present or if it is something that would need to be adjusted (looking at the code, it looks like all of the references to the location were meant to be overridden easily, but are coded to use the rootDir rather an optional alternate location parameter - or am I oversimplifying this?)

Thanks!

Partytown with `analytics` library.

I love the analytics library because it's a single abstraction on many analytics/tracking providers.

I'm wondering if anybody has experimented with getting it to work with partytown? IMO, that would be a killer feature.

Partytown discord invite link is invalid

Partytown discord invite link is not reachable/valid, Kindly put a new invite link. Make sure to put a permanent link.

To create a permanent invite, select "never" under "expire after" and "no limit" under "max uses." By doing so, you'll generate a link that has infinite uses and won't become obsolete over any period of time

image

Support for Google Tag Assistant (Google Tag Manager preview)

When using Google Tag Assistant to test a Proxytown implementation of Google Tag Manager, it fails to connect:
Screenshot 2022-02-11 at 16 01 04
At first there were some CORS errors for the googletagmanager.com domain. After proxying those I can see the iframe getting added to the page, but it fails to load and some Partytown error occurs:

Screenshot 2022-02-11 at 16 07 01

In the page's <head> I can see the following scripts:

<script type="text/partytown-x" src="http://localhost:3003/~partytownproxy/google-analytics/analytics.js" data-ptsrc="https://www.google-analytics.com/analytics.js" data-ptid="351043767"></script>
<script type="text/partytown-x" src="http://localhost:3003/~partytownproxy/googletagmanager/gtag/js" data-ptsrc="http://www.googletagmanager.com/gtag/js?id=G-44K8TKMTS9&amp;l=dataLayer&amp;cx=c" data-ptid="574473788"></script>
<script type="text/partytown" src="http://localhost:3003/~partytownproxy/googletagmanager/debug/bootstrap" data-ptsrc="https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290" data-ptid="433709864" data-pterror="Error: Error finding instance &quot;1&quot; on window 2 (259405394)
    at sendToMain (http://localhost:3003/~partytown/debug/partytown-ww-sw.js:253:27)
    at queue (http://localhost:3003/~partytown/debug/partytown-ww-sw.js:226:20)
    at callMethod (http://localhost:3003/~partytown/debug/partytown-ww-sw.js:333:20)
    at Node.<anonymous> (http://localhost:3003/~partytown/debug/partytown-ww-sw.js:1338:24)
    at wc (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:53:163)
    at eval (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:66:331)
    at lc (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:47:924)
    at Bc.start (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:65:932)
    at eval (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:70:1364)
    at Proxy.eval (https://www.googletagmanager.com/debug/bootstrap?id=GTM-XXXXXXX&amp;src=GTM&amp;cond=2&amp;gtm=2wg290:77:3)"></script>

And just before the </body>:

<iframe src="about:blank" class="__TAG_ASSISTANT_BADGE" frameborder="0" style="border: 0 !important; bottom: 24px !important; border-radius: 8px !important; box-shadow: 0 3px 5px -1px rgb(0 0 0 / 20%), 0 5px 8px 0 rgb(0 0 0 / 14%), 0 1px 14px 0 rgb(0 0 0 / 12%) !important; clip: initial !important; display: inline !important; height: 200px !important; left: auto !important; margin: 0 !important; max-width: 95% !important; opacity: 1 !important; padding: 0 !important; position: fixed !important; right: 24px !important; top: auto !important; visibility: visible !important; width: 450px !important; z-index: 2147483647 !important;" data-ptwindow="259405394"></iframe>

When switching back to the default GTM implementation (without Partytown), everything works as expected.

I

I am u see

Problem with the JSONP callback

I'm trying to use Partytown with a GTM script in our project. The problem is, inside of the GTM we have a custom snippet that is using JSONP to get the data. Just a regular:

<script>
    function jsonpCallback(data) {
        // some code here
    }
</script>
<script src="https://some-url?callback=jsonpCallback"></script>

After the Partytown is enabled this will no longer work. It looks like the jsonpCallback from the first script tag is not accessible in the the second script tag.

Minimal reproduction:
https://github.com/jakubsobel/partytown-jsonp-issue
Just run it, go to the localhost:3000 and you will see that the log is missing. That's because the response is:

typeof jsonpCallback === 'function' && jsonpCallback([/*endpoint data*/]);

and for some reason while using Partytown the previously defined jsonpCallback is not defined.

Remove the type="text/partytown" from both of the script tags and the jsonpCallback will execute showing the log.

Trying to use Partytown on Shopify 2.0 theme

I'm hoping to use Partytown on Shopify themes moving forward. I understand you guys are working on a Shopify app to make the integration a bit more seamless, but was hoping to get some direction on how to manually configure parytown on a 2.0 Shopify theme.

I've copied all the files from lib folder ( partytown-atomics.js, partytown-media.js, partytown-sw.js) and placed them within the /assets/~partytown/ directory. I placed the inline script in the<head>.

I tried loading a jquery from the assets folder and see the following error when using <script type="text/partytown" src="/assets/jquery.js"></script>

<script type="text/partytown" src="/assets/jquery.js" data-ptid="156465513" data-pterror="TypeError: r.implementation.createHTMLDocument is not a function
    at eval (http://127.0.0.1:9292/assets/jquery.js:4:89469)
    at eval (http://127.0.0.1:9292/assets/jquery.js:4:89569)
    at eval (http://127.0.0.1:9292/assets/jquery.js:4:220)
    at Proxy.eval (http://127.0.0.1:9292/assets/jquery.js:4:225)
    at ge (blob:http://127.0.0.1:9292/c0240a1b-11b4-4bbf-8b01-3ce374a48b93:2:4250)
    at blob:http://127.0.0.1:9292/c0240a1b-11b4-4bbf-8b01-3ce374a48b93:2:16046"></script>

Any tips on how to troubleshoot would be appreciated. Thanks!

Problem with the zone.js (Angular 13)

I'm trying to use Partytown with an Angular 13 project, but it looks like the Partytown initialisation fails because of the zone.js patching of the IntersectionObserver and MutationObserver.

It looks like the Partytown's getConstructorName function is returning an empty string for the IntersectionObserver and MutationObserver when zone.js is in use, and that breaks the mainWindow[cstrName].prototype line in the partytown-sandbox-sw.js.

Minimal reproduction:
https://github.com/jakubsobel/partytown-zonejs-issue
Run using the ng serve, go to the localhost:4200, you will see the problem in the console:
Screenshot 2022-03-03 at 12 23 34

Uncomment the lines in the zone-flags.ts file. That will disable the patching of the IntersectionObserver and MutationObserver. Run the project again, Partytown will then work correctly.

[GatsbyJS + GTM] Events not being sent when pushed to DataLayer

Hi, πŸ‘‹ !

I'm trying to integrate the Partytown + Gatsby, but I'm facing some problems sending events.

Basically, I added the GTM and Partytown components in the head. Inside the components, call window.dataLayer.push. But I realized that just send page_view events.

// gatsby-ssr.js
export const onRenderBody = ({ setHeadComponents }) => {
  setHeadComponents([
    <GoogleTagManager
      key="gtm"
      containerId={storeConfig.analytics.gtmContainerId}
      enablePartytown
    />,
    <Partytown key="party" />,
  ])
}
// sending events to dataLayer
useAnalyticsEvent((event) => {
  console.log('EVENT', event)
  window.dataLayer.push(event)
})

I'm missing something?
I tried to use the debug flag, but I didn't see the console.logs 😞 .

For more information:

Partytown Version: 0.0.29
Gatsby Version: 3.14.3

Uncaught TypeError: Cannot read properties of undefined (reading 'prototype')

Hi. I just tried to use this library for the first time, got the error:

Uncaught TypeError: Cannot read properties of undefined (reading 'prototype')
    at partytown-sandbox-sw.js:304
    at Array.map (<anonymous>)
    at readMainPlatform (partytown-sandbox-sw.js:300)
    at accessRsp.$msgId$ (partytown-sandbox-sw.js:373)
    at Worker.worker.onmessage (partytown-sandbox-sw.js:422)
(anonymous) @ partytown-sandbox-sw.js:304
readMainPlatform @ partytown-sandbox-sw.js:300
accessRsp.$msgId$ @ partytown-sandbox-sw.js:373
worker.onmessage @ partytown-sandbox-sw.js:422

here docImpl.createElement("i").constructor.name returns b, and win.b is undefined. πŸ€·β€β™€οΈ

Partytown unable to import into GatsbyJS template

Very excited to try out this package. However I've been unable to import the React version into our GatsbyJS-based template, and also unable to use the vanilla script. I've created a sample project using the basic Gatsby starter page as a demo.

Reproduction steps:

  • Clone the project
  • npm i to install dependencies, including Partytown
  • gatsby develop to start local dev

./src/components/seo.js has import { Partytown } from '@builder.io/partytown/react' at the top, and that should throw a "Module not found" error. If you comment that out, we are including the vanilla script via React Helmet and that will throw a syntax error.

Screenshots of those errors below:
Screen Shot 2021-09-23 at 2 03 27 PM
Screen Shot 2021-09-23 at 2 03 54 PM

I am running the latest Node LTS version (v14).

Transferable objects support

I noticed that JS Transferable objects are not currently supported.

I have a use case where I want to pass a MessageChannel to my external script in order to have a direct communication channel between main thread and the script inside the web worker. I defined a function in my script that takes MessageChannel as a parameter, and I declared the function in forward config. That unfortunately does not work without explicitly passing the transferable object to postMessage second parameter.

I made a quick POC where I don't serialize transferables as objects and before passing them to postMessage I recursively find all transferable objects and pass them to second parameter.

Is my use case in scope of this project? I could make a PR.

Illegal Invocation webWorkerRefsByReId

Working on a Gatsby + Partytown example w/ GTM and seeing this error:

TypeError: Illegal invocation.

I don't see any CORS errors which is the only other instances of this I have found. The site is up at https://gatsbypartytownfeaturepartytow.gatsbyjs.io/ in debug mode. The code is here: https://github.com/graysonhicks/gatsby-partytown/tree/feature/partytown. The error comes from (anonymous) @ partytown-ww-sw.js:1363 (in debug mode), which is the catch here:

if (webWorkerRefsByRefId[$refId$]) {
         try {
             const thisArg = deserializeFromMain($instanceId$, [], $thisArg$);
             const args = deserializeFromMain($instanceId$, [], $args$);
                 webWorkerRefsByRefId[$refId$].apply(thisArg, args);
             } catch (e) {
                  console.error(e);
             }
}

The GTM setup is the bare minimum and loads the FB pixel script, which is also the bare minimum pixel setup.

Syntax errors when executing some scripts from `doubleclick`.

I receive the following error every time a script from https://googleads.g.doubleclick.net/pagead/viewthroughconversion/${SOME_NUMBER_ID}/ is executed by Partytown:

Screenshot 2022-01-20 at 18 03 37

I don't know how to further debug, or if this info is somehow useful for you, but I thought it was worth it at least reporting it.

Let me know if I there is something else I could provide that would be helpful.

resolveUrl security concerns

Currently, it looks like the recommendation for solving CORS issues is to proxy those requests from the same domain. I'm a bit hesitant to do this, because it would be a potential security concern (a malicious user could run their script from our domain).

I would feel a lot better if the script was encoded in such a way that is wasn't a valid JS file, but that would require to post process the file. Another thing that would be great is to have the importing script url in the resolve method so CORS could be reimplemented on the proxy.

Tawk.to Integration

Hello I tried to integrated the tawk.to widget on my NextJS app.

the service worker is appearing correctly but the widget do not show up and I see some error in the dev tool : "SyntaxError: Unexpected token ')'"
Screenshot 2022-01-12 at 15 14 35

I tried to forward multiple Tawk strings in the Partytown component but without success.

What am I missing ?
Thanks for your help.

Screenshot 2022-01-12 at 15 13 11

Screenshot 2022-01-12 at 15 14 53

Screenshot 2022-01-12 at 15 17 00

How to integrate partytown with angular?

@adamdbradley I am trying to connect GTM, Gtag as well as some basic scripts in Angular index.html, but seems like they aren't working.
I am searching for solution but no idea how to implement it.

GTag Script-

<script type="text/partytown">
  window.dataLayer = window.dataLayer || [];
  function gtag(){
    dataLayer.push(arguments);
  }
  gtag('js', new Date());
</script>

GTM Scripts-

<script>
      ;(function (w, d, s, l, i) {
        w[l] = w[l] || []
        w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' })
        var f = d.getElementsByTagName(s)[0],
          j = d.createElement(s),
          dl = l != 'dataLayer' ? '&l=' + l : ''
        j.async = true
        j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl
        f.parentNode.insertBefore(j, f)
      })(window, document, 'script', 'dataLayer', 'GTM-ID')
    </script>

Some basic scripts-
<script type="text/partytown" src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

[Help] I can't make Party Town work

Hey, i need some help to setup Party Town

I'm building a Remix application and i added the library with the React component you provide <Partytown debug={true} forward={['dataLayer.push']} />
The following script is being added to the head

!(function(w,p,f,c){c=w[p]=Object.assign(w[p]||{},{"debug":true});c[f]=(c[f]||[]).concat(["dataLayer.push"])})(window,'partytown','forward');/* Partytown 0.3.6 - MIT builder.io */
!function(t,e,n,i,r,o,a,d,s,c,p,l){function u(){l||(l=1,"/"==(a=(o.lib||"/~partytown/")+(o.debug?"debug/":""))[0]&&(s=e.querySelectorAll('script[type="text/partytown"]'),i!=t?i.dispatchEvent(new CustomEvent("pt1",{detail:t})):s.length&&(d=setTimeout(f,1e4),e.addEventListener("pt0",g),r?h(1):n.serviceWorker?n.serviceWorker.register(a+"partytown-sw.js",{scope:a}).then((function(t){t.active?h():t.installing&&t.installing.addEventListener("statechange",(function(t){"activated"==t.target.state&&h()}))}),console.error):f())))}function h(t){c=e.createElement(t?"script":"iframe"),t||(c.setAttribute("style","display:block;width:0;height:0;border:0;visibility:hidden"),c.setAttribute("aria-hidden",!0)),c.src=a+"partytown-"+(t?"atomics.js":"sandbox-sw.html?"+Date.now()),e.body.appendChild(c)}function f(t,n){for(g(),t=0;t<s.length;t++)(n=e.createElement("script")).innerHTML=s[t].innerHTML,e.head.appendChild(n);c&&c.parentNode.removeChild(c)}function g(){clearTimeout(d)}o=t.partytown||{},i==t&&(o.forward||[]).map((function(e){p=t,e.split(".").map((function(e,n,i){p=p[i[n]]=n+1<i.length?"push"==i[n+1]?[]:p[i[n]]||{}:function(){(t._ptf=t._ptf||[]).push(i,arguments)}}))})),"complete"==e.readyState?u():(t.addEventListener("DOMContentLoaded",u),t.addEventListener("load",u))}(window,document,navigator,top,top.crossOriginIsolated);document.currentScript.dataset.partytown="";

I have also added GTM script following the docs.

But it looks like nothing happens, it doesn't log anything to the console, it doesn't load the service worker.

Am i missing something?

Failed to load proxy town in NextJS

I am not able to run party town on one specific nextJS project.

Configuration

NextJS : ^12.0.4
@builder.io/partytown : ^0.3.6

I am only facing this issue in one of my project and the same was working in the other newly created project. I can't find the reason for the proxytown not being able to connect.

Network Tab

Screenshot 2022-02-23 at 12 34 43 PM

Page Source

Screenshot 2022-02-23 at 12 53 29 PM

Console Log

Screenshot 2022-02-23 at 12 33 57 PM

Implementation inside _document.tsx

Screenshot 2022-02-23 at 12 47 51 PM

partytown-sandbox-sw.html Not Found on Nextjs

Hi, πŸ‘‹.

I'm very excited to see this project going into prod mode, it is an amazing idea!

I've been unable to import the React version into our Nextjs@11, I also tried with the vanilla version, and didn't work as well. I've created a sample project using the basic Nextjs starter page as a demo.

Reproduction steps:

  • Clone the project
  • install dependencies, including Partytown
  • yarn build:watch & yarn dev
  • when the project was initialized I was not able to see the Party on my console :(

Screenshots of those errors are below:

image

image

Seems that one .html is missing from the lib build.

image

I don't know if I'm missing some configuration, probably πŸ˜… , but I can't for the life of my figure out what it might be. Have you ever seen this before or know what might be going on.

Thank you enormously in advance πŸ™‡πŸ½β€β™€οΈ

Error: Cannot find module '../integration/index.MODULE_EXT'

Steps to reproduce:

  1. Clone this project
  2. npm install
  3. npm run dev

NPM 7.23.0
Node v14.8.0

Error output:

`error - Error: Cannot find module '../integration/index.MODULE_EXT'
Require stack:

  • /Users/jacobwise/Next/partytown-test/node_modules/@builder.io/partytown/react/index.cjs
  • /Users/jacobwise/Next/partytown-test/.next/server/pages/_document.js
  • /Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/require.js
  • /Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/load-components.js
  • /Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/api-utils.js
  • /Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/next-server.js
  • /Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/next.js
  • /Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/lib/start-server.js
  • /Users/jacobwise/Next/partytown-test/node_modules/next/dist/cli/next-dev.js
  • /Users/jacobwise/Next/partytown-test/node_modules/next/dist/bin/next
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:1080:15)
    at Function.mod._resolveFilename (/Users/jacobwise/Next/partytown-test/node_modules/next/dist/build/webpack/require-hook.js:96:28)
    at Function.Module._load (internal/modules/cjs/loader.js:923:27)
    at Module.require (internal/modules/cjs/loader.js:1140:19)
    at require (internal/modules/cjs/helpers.js:75:18)
    at Object. (/Users/jacobwise/Next/partytown-test/node_modules/@builder.io/partytown/react/index.cjs:6:17)
    at Module._compile (internal/modules/cjs/loader.js:1251:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1272:10)
    at Module.load (internal/modules/cjs/loader.js:1100:32)
    at Function.Module._load (internal/modules/cjs/loader.js:962:14) {
    code: 'MODULE_NOT_FOUND',
    requireStack: [
    '/Users/jacobwise/Next/partytown-test/node_modules/@builder.io/partytown/react/index.cjs',
    '/Users/jacobwise/Next/partytown-test/.next/server/pages/_document.js',
    '/Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/require.js',
    '/Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/load-components.js',
    '/Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/api-utils.js',
    '/Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/next-server.js',
    '/Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/next.js',
    '/Users/jacobwise/Next/partytown-test/node_modules/next/dist/server/lib/start-server.js',
    '/Users/jacobwise/Next/partytown-test/node_modules/next/dist/cli/next-dev.js',
    '/Users/jacobwise/Next/partytown-test/node_modules/next/dist/bin/next'
    ],
    page: '/'
    }`

Allow alternate lib location

Hello!

We're trying to implement PartyTown, but we'd like to serve the lib directory up from an alternate location, rather than the root of the application. We could do this with a reverse proxy, but we think having the optionality on the partytown package would improve developers ability to use the package.

What would be required to load your lib from a different location other than /~partytown/? I'm not sure if I'm just not seeing the functionality that is already present or if it is something that would need to be adjusted (looking at the code, it looks like all of the references to the location were meant to be overridden easily, but are coded to use the rootDir rather an optional alternate location parameter - or am I oversimplifying this?)

Thanks!

window.visualViewport is not supported

window.visualViewport fails with TypeError: PropCstr is not a constructor.

Example code:

        <script type="text/partytown">
          (function () {
            console.log('VISUAL VIEWPORT', window.visualViewport);
          })();
        </script>

Implement TrustedHTML

  1. Clone
  2. npm install
  3. npm run dev
TypeError: Illegal invocation
    at callWorkerRefHandler (partytown-ww-sw.js:618)
    at receiveMessageFromSandboxToWorker (partytown-ww-sw.js:753)
callWorkerRefHandler @ partytown-ww-sw.js:620
receiveMessageFromSandboxToWorker @ partytown-ww-sw.js:753

How to initialize scripts that rely on values from the window. i.e Fullstory

This library is awesome. Thanks for all the hard work!

I am using NextJs for my application and I am trying to load Fullstory onto my site. However I'm unable to get the script to load. This is my file.

import { Partytown } from '@builder.io/partytown/react';

<Head>
     <Partytown />
     
        <script
            type="text/partytown"
            dangerouslySetInnerHTML={{
              __html: `
              window['_fs_debug'] = false;
              window['_fs_host'] = 'fullstory.com';
              window['_fs_script'] = 'edge.fullstory.com/s/fs.js';
              window['_fs_org'] = 'XXX';
              window['_fs_namespace'] = 'FS';
              (function(m,n,e,t,l,o,g,y){
                  if (e in m) {if(m.console && m.console.log) { m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].');} return;}
                  g=m[e]=function(a,b,s){g.q?g.q.push([a,b,s]):g._api(a,b,s);};g.q=[];
                  o=n.createElement(t);o.async=1;o.crossOrigin='anonymous';o.src='https://'+_fs_script;
                  y=n.getElementsByTagName(t)[0];y.parentNode.insertBefore(o,y);
                  g.identify=function(i,v,s){g(l,{uid:i},s);if(v)g(l,v,s)};g.setUserVars=function(v,s){g(l,v,s)};g.event=function(i,v,s){g('event',{n:i,p:v},s)};
                  g.anonymize=function(){g.identify(!!0)};
                  g.shutdown=function(){g("rec",!1)};g.restart=function(){g("rec",!0)};
                  g.log = function(a,b){g("log",[a,b])};
                  g.consent=function(a){g("consent",!arguments.length||a)};
                  g.identifyAccount=function(i,v){o='account';v=v||{};v.acctId=i;g(o,v)};
                  g.clearUserCookie=function(){};
                  g.setVars=function(n, p){g('setVars',[n,p]);};
                  g._w={};y='XMLHttpRequest';g._w[y]=m[y];y='fetch';g._w[y]=m[y];
                  if(m[y])m[y]=function(){return g._w[y].apply(this,arguments)};
                  g._v="1.3.0";
              })(window,document,window['_fs_namespace'],'script','user');
              `,
            }}
          ></script>
</Head>

I've tried adding the forward properties as well, but no luck.

<Partytown forward={['_fs_debug', '_fs_host', '_fs_script', '_fs_org', '_fs_namespace']}

If anyone has implemented FS, i'd love to understand what im doing wrong. This is my console to hopefully add any more information. Thank you!

image

Proxytown request stuck on pending

I am attempting to integrate TrustArc, a third party cookie consent manager, with PartyTown. However it isn't showing the consent dialog as expected in an incognito window.

I notice that the the last request to "proxytown" in the network tab is stuck on pending.

Screenshot 2022-02-28 at 11 05 52

The request payload is:

{
  "$msgId$": 939543253,
  "$tasks$": [
    {
      "$winId$": 728533565,
      "$instanceId$": 1,
      "$applyPath$": [
        "addEventListener",
        [
          1,
          [
            [
              0,
              "readystatechange"
            ],
            [
              4,
              {
                "$winId$": 728533565,
                "$instanceId$": 1,
                "$refId$": 392264080
              }
            ],
            [
              0,
              false
            ]
          ]
        ]
      ],
      "$debug$": "document.addEventListener.1,0,readystatechange,4,[object Object],0,false (non-blocking)"
    },
    {
      "$winId$": 728533565,
      "$instanceId$": 0,
      "$applyPath$": [
        "addEventListener",
        [
          1,
          [
            [
              0,
              "message"
            ],
            [
              4,
              {
                "$winId$": 728533565,
                "$instanceId$": 0,
                "$refId$": 563258573
              }
            ],
            [
              0,
              false
            ]
          ]
        ]
      ],
      "$debug$": "addEventListener.1,0,message,4,[object Object],0,false (non-blocking)"
    },
    {
      "$winId$": 728533565,
      "$instanceId$": 1,
      "$applyPath$": [
        "getElementsByTagName",
        [
          1,
          [
            [
              0,
              "SCRIPT"
            ]
          ]
        ]
      ],
      "$debug$": "document.getElementsByTagName.1,0,SCRIPT (blocking)"
    }
  ]
}

Any help would be greatly appreciated.

virtualization

hey @adamdbradley, thanks for putting effort on this, this is a great area of exploration. there are few things that I will like to suggest or hint based on the little that I know so far about this project (which isn't much, I haven't get a chance to read the whole code yet).

based on the goals (from the readme), what you're trying to achieve is basically a virtualization mechanism, specifically, a virtual environment for the DOM, and from that perspective, there are few things that I will suggest to consider:

  1. the object graph that is reachable from the DOM is vast, but the entry points to it are not so much, in fact, it is less than 1k in property descriptors (basically, language + global names in Web IDL). If you get to virtualize those, then any program running in the virtual environment must go thru them to access the DOM.
  2. reconstructing the DOM APIs is a tricky business, many have tried, it is privative if you try to boot it up, it is also always falling behind, it is a catch up game if you try to replicate the APIs. This is where a membrane comes handy, because you can basically reconstruct the DOM APIs in a lazy manner (on first access), and will only require to virtualize the global names first. Keep in mind that a complex program only really touches a tiny fragment of the vast Web IDL.
  3. I see many similarities between the work you're doing here and the ShadowRealm (former Realm) TC39 Proposal that we have been pushing for quite some time as a new addition to the language. Obviously the biggest difference is that a ShadowRealm runs in the same process, but the rest is very similar, in principle. A ShadowRealm does not allow connection with the object graph of the incubator realm, just like the worker doesn't. You can have actionable integration between the two realms (in the case of the ShadowRealm via wrapped callables, in the case of the worker, via the sync XHR). The beauty here is that, in theory, you can build wrapped callables on top of the sync XHR protocol, and here is where things becomes a lot more interesting.
  4. The DOM is very tricky to virtualize in part because of the many "sinks" that turn into evaluations, a good example of it is what to do when the virtualize script inserts another script tag. The way we have been thinking about this is by providing what we call "distortions", which will allow to control the behavior in a way that is not observable by the virtualized code. Membranes are pretty good at this.
  5. Interaction with multiple window objects is also very tricky, imagine a script virtualized that creates an iframe, and attempt to interact with the contentWindow of that iframe. That gets very tricky because the identity discontinuity nature of that interaction, you can't use the worker's realm for the iframe, because that's not the expectation of the script creating the iframe.

These are just some of the ideas and challenges that we have encountered along the way, we can chat more about it.

Zendesk integration

Would love to see official Zendesk support/integration post-beta. It is one of our heaviest hitters in main-thread work as far as third-party scripts are concerned and I love the concept of using web workers for this.

Enable running Partytown on a subdirectory

I'm able to get Partytown running on my GatsbyJS project in dev! All the proxytown requests are appearing in my network tab.

However, when I try to deploy my project to its live URL, I noticed Partytown's service worker appears to try to install directly to the domain root and fails. In our setup, we do not control what files are added to our domain root, we only control the /projects path of our domain, so ideally there could be some option passed into the Partytown config that would set the scope that the service worker should target.

Forgive me if that's already an option that I'm not seeing.

Unexpected token '<' when using Next

Hello πŸ‘‹ this library looks like exactly what we need for a client project and so we decided to give it a try! We are using next so have added the tags described in the documentation to _document.tsx.

<GoogleTagManager containerId={"GTM-XXXXXXXX"} />
<Partytown debug={true} />

When we run the site we see:

image

When we follow the link to the file that caused the error we see HTML in the file with a .js extension, presumably the opening < here is what it is complaining about?

<!DOCTYPE html><html lang="en"><head><script>document.currentScript.dataset.ptScript="gtm";!(function(w,p,f,c){c=w[p]=w[p]||{};(c[f]=c[f]||[]).push(["dataLayer",1])})(window,'partytown','forward');</script><script type="text/partytown">

I'm almost 100% sure this is something specific to our setup πŸ˜… but I can't for the life of my figure out what it might be. Have you ever seen this before or know what might be going on.

Thank you enormously in advance πŸ™‡β€β™‚οΈ

Analytics CORS issue

I did my implementation exactly like your example, but instead of using Google Tag Manager, I am using the Google Analytics snippet:

    <script type="text/partytown">
      (function (i, s, o, g, r, a, m) {
        i['GoogleAnalyticsObject'] = r;
        (i[r] =
          i[r] ||
          function () {
            (i[r].q = i[r].q || []).push(arguments);
          }),
          (i[r].l = 1 * new Date());
        (a = s.createElement(o)), (m = s.getElementsByTagName(o)[0]);
        a.async = 1;
        a.src = g;
        m.parentNode.insertBefore(a, m);
      })(
        window,
        document,
        'script',
        'https://www.google-analytics.com/analytics.js',
        'ga'
      );
    </script>
    <script type="text/partytown" src="https://securepubads.g.doubleclick.net/tag/js/gpt.js"></script>
    <script>
      partytown = {
        forward: ['ga']
      };
    </script>
    <script>/* ---- Partytown 0.0.30 inlined ---- */</script>

When I do that, I get

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://www.google-analytics.com/analytics.js. (Reason: CORS header β€˜Access-Control-Allow-Origin’ missing). Status code: 200.

Am I missing something?

Can't get Partytown to work with anything (Google Maps, Hotjar, TrustPilot, ...)

Hi everyone,

The vision of Partytown and the potential utility of this tool are noble and valuable.

However, after implementing it in a NextJs application, I quickly found myself covered with CORS issues. Both scripts loaded using GTM (Hotjar) and included in the <head> (Google Maps, TrustPilot, Sentry) seem to throw the same errors.

I've read the docs about reverse proxy and tried implementing it with Hotjar, but it doesn't seem to work either as it requires query params (which you can pass along ok), but it then makes other requests that are out of my control and ends up throwing CORS issues anyway. In addition, downloading and serving scripts from the same origin makes you lose new version updates (lousy security practice).

As of now, I fail to see the advantage of using this tool given the fact that:

  1. each script requires specific care and attention;
  2. you may not be able to have all scripts loaded using this strategy;
  3. if they end up working under a proxy or naturally, the value of GTM gets lost as non-dev can't add scripts autonomously (Google Optimize, etc.);
  4. (case-specific and in reply to custom Nginx configuration) I don't see this configuring neatly with Terraform and AWS ElasticBeanstalk, which provide incredible high-level cleanliness and organization in my cloud infrastructure.

In conclusion, since I recognize Partytown's potential, I'd like to ask if anyone has better solutions to get it to work in any other automated, out-of-the-box way. I've learned the hard way that too many patches and workarounds end up creating hidden issues and messing with the team's productivity and performance in the long term.

What are your thoughts?

Thanks!

sending all dataLayer properties from the worker thread to the main

I have been trying to understand how partytown handles the dataLayer event and possible pitfalls we might encounter in the future. There is an application written with Next.js, and I have the intention to test partytown in our test environments. However, there is a blind spot for me that I want to clarify.

  1. I have added the google tag manager in our next application using with GoogleTagManager and also Partytown βœ…
  <GoogleTagManager containerId={gtmID} forward={['dataLayer.push']} />  
  <Partytown forward={[['dataLayer', 1]]} />
  1. I have been observing datalayer events with an extension which is Adswerve - dataLayer Inspector+, and I can say that it works well for now according to the console activity. βœ…
  2. Now, I want to access datalayer properties as global variables within the main thread because many things depend on it. According to the code I shared above, I couldn't push or catch these data under the main window.

What is the correct way to accomplish that?

In addition, with this config above dataLayer events are not sent to the cloud tag manager.

MediaList objects are not supported

Hi,

Thanks for this amazing library! πŸŽ‰

I noticed that CSS @media queries are not supported. They fail with error DOMException: Failed to execute 'postMessage' on 'ServiceWorker': MediaList object could not be cloned.

Example test to reproduce:

      <li>
        <strong>@media query</strong>
        <code id="testMediaQuery"></code>
        <script type="text/partytown">
          (function () {
            const style = document.createElement('style');
            style.innerHTML = `@media only screen and (min-width: 600px) { #testMediaQuery { color: red; } }`;
            document.head.appendChild(style);
            const elm = document.getElementById('testMediaQuery');
            const sheet = style.sheet;
            const cssRules = sheet.cssRules;
            const cssRule = cssRules[0];
            elm.textContent = cssRule.cssText;
          })();
        </script>
      </li>

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.