[Solved] Inline Script Module Errors

We are trying to use an inline tag in the head of a website for a 3rd party tool (live person chat), and are getting a pretty strange error:

Cannot find module ‘./util/EventsUtil’

Currently we are loading the tag in the same way this post suggested:

https://forums.meteor.com/t/how-to-add-a-script-to-head-in-1-3/20295/11

Using this package:

https://github.com/meteorhacks/meteor-inject-initial/

It does load the script, and the script actually works, until you refresh the page…that is when the error occurs.

Here is the server code:

Inject.rawModHtml('inject a script at the beginning of the head', function(html) {
  return html.replace("<head>", "<head> <script type='text/javascript'>window.lpTag=window.lpTag||{},'undefined'==typeof window.lpTag._tagCount?(window.lpTag={wl:lpTag.wl||null,scp:lpTag.scp||null,site:'siteidnumber'||'',section:lpTag.section||'',tagletSection:lpTag.tagletSection||null,autoStart:lpTag.autoStart!==!1,ovr:lpTag.ovr||{},_v:'1.10.0',_tagCount:1,protocol:'https:',events:{bind:function(t,e,i){lpTag.defer(function(){lpTag.events.bind(t,e,i)},0)},trigger:function(t,e,i){lpTag.defer(function(){lpTag.events.trigger(t,e,i)},1)}},defer:function(t,e){0===e?(this._defB=this._defB||[],this._defB.push(t)):1===e?(this._defT=this._defT||[],this._defT.push(t)):(this._defL=this._defL||[],this._defL.push(t))},load:function(t,e,i){var n=this;setTimeout(function(){n._load(t,e,i)},0)},_load:function(t,e,i){var n=t;t||(n=this.protocol+'//'+(this.ovr&&this.ovr.domain?this.ovr.domain:'lptag.liveperson.net')+'/tag/tag.js?site='+this.site);var o=document.createElement('script');o.setAttribute('charset',e?e:'UTF-8'),i&&o.setAttribute('id',i),o.setAttribute('src',n),document.getElementsByTagName('head').item(0).appendChild(o)},init:function(){this._timing=this._timing||{},this._timing.start=(new Date).getTime();var t=this;window.attachEvent?window.attachEvent('onload',function(){t._domReady('domReady')}):(window.addEventListener('DOMContentLoaded',function(){t._domReady('contReady')},!1),window.addEventListener('load',function(){t._domReady('domReady')},!1)),'undefined'===typeof window._lptStop&&this.load()},start:function(){this.autoStart=!0},_domReady:function(t){this.isDom||(this.isDom=!0,this.events.trigger('LPT','DOM_READY',{t:t})),this._timing[t]=(new Date).getTime()},vars:lpTag.vars||[],dbs:lpTag.dbs||[],ctn:lpTag.ctn||[],sdes:lpTag.sdes||[],hooks:lpTag.hooks||[],identities:lpTag.identities||[],ev:lpTag.ev||[]},lpTag.init()):window.lpTag._tagCount+=1;</script>");
});

We have tested the script (we removed our clients account number from the script for this post) in a static HTML page and it works fine. We just cant seem to identify what it is that is throwing the error and why it will work until the page is refreshed.

Any help would be GREATLY appreciated! :slight_smile:

Hi @jhochgreve

A few things come to mind…

  1. You should view-source the site and see how the script is being served to the user, and if it changes on views.

  2. Where are you running the code? Is it possible it’s being run more than once?

  3. Using Inject.rawHead('liveperson', yourScript) is cleaner and also does some extra escaping, but I don’t think this is the problem here.

My main advice is (1) and to get a better idea of what’s going on. What about a stacktrace for the error, from where is it being thrown?

1 Like

Hey @gadicc thanks for chiming in! :slight_smile:

I did do a stack trace, essentially the script I was given (live person) is actually appending another script to the <head> of the site. This script then utilizes a module that is called locally on their server (./util/EventsUtil), its my belief that Meteor is compiling this other file that is loaded into the <head> and then looking for the module locally. This would make sense because the chat window actually loads on entry into the website. Only on page refresh do we get the error. I am just not sure if there is some way around this with Meteor. :frowning:

Not sure if it matters much but the Inject package is very old and the comment at the top of the inject-core.js file - We are simply using this hack because, there is no way to alter Meteor’s html content on the server side - is factually incorrect since meteor 1.5 IIUC. 1.5 added the dynamicHead and dynamicBody properties on the request object to inject arbitrary html head/body content strings into the server-side boilerplate code.
I stumbled on these properties only last week

1 Like

@permb I would love to give this a shot! Do you happen to know where they talk about this in the docs? I see it in the change log, but cant seem to find any further documentation on it.

@permb Never mind! :slight_smile: I found this post where they detail it:

https://forums.meteor.com/t/can-i-add-to-the-head-tag-from-the-server/21436/2

Unfortunately while it is much cleaner, I still have the issue where the script works fine, until the page is reloaded. Then I go right back to the same error, until I clear cache and reload…then it will work again. :frowning:

Ooh yes, @permb is right, I think that’s one of the reasons we stopped working on inject-initial, although the Meteor API still didn’t cover all the cases we were using it for. Anyways!

@jhochgreve, is the site deployed anywhere, or can you give me a small reproduction on github, that I can take a closer look? TBH I haven’t used Meteor in years but I do enjoy solving these kinds of mysteries.

I think you’re on the right track though. Meteor has it’s own module loading code. And while I don’t believe Meteor is trying to compile the liveperson script, I think when LP “requires” or imports ./util/EventsUtil, that’s going to Meteor instead of the browser. This is just my best guess though. Failing a full reproduction, could we see https://lptag.liveperson.net/tag/tag.js?site=XX with any inline client details removed?

Since Javascript is single threaded, there is probably just some stuff we can set before the import and change back (after the script is loaded) to get this to work (or maybe the LP script should better detect it’s environment to help solve this for all their Meteor customers).

1 Like

Hey @gadicc!

I may be able to get approval to give you BitBucket permissions, though to be honest…I am pretty well back to my stock code with loading the from the server side as @permb had suggested.

I you go to the file: https://lptag.liveperson.net/tag/tag.js you can certainly see what they are up too. Heck I even found a copy of what I believe is the util file here: https://codeclimate.com/github/LivePersonInc/chronosjs/src/util/EventsUtil.js

Fair warning…I have not a clue if this is the real and updated file…but might be good for reference! I did try loading it locally…but that crashed and burned mainly because I was trying to do it via the public folder…and I dont think ES6 Imports or requires will work there.

Don’t worry about the BitBucket permissions. I created a quick reproduction and had a look.

Regrettably, I haven’t used Meteor for the last two years or so, and although I used to know the Meteor module code quite intimately, it’s not behaving quite how I remember it. require and exports are defined in the main scope. I’m not sure if that’s a feature or a bug. I feel like the “correct” fix would be from Meteor’s side as I imagine this could break a lot of things and I wonder if there are any other forum posts on the topic.

There is what I hope is an easy (but not ideal) workaround, and that is to host tag.js on your Meteor server and wrap it to ensure exports is undefined in the local scope, to ensure it’s module detection works as intended. When I did this, we no longer get the above error, and it starts loading more LivePerson code, but I eventually get an error since we haven’t set the siteidnumber.

Anyway, you can take a look at https://github.com/gadicc/repro-meteor-liveperson/commits/master. The commits, from bottom to top, are:

  1. first commit - minimal Meteor setup, with the tiny LP code via dynamicHead in server/main.js (also no reason not to do it in client/main.html, depending on your setup).

  2. saves tag.js locally in public, adjusts dynamicHead code to load from server. It’s a bit hard to see what’s going on because it’s all in one line, but basically we change:

t||(n=this.protocol+'//'+(this.ovr&&this.ovr.domain?this.ovr.domain:'lptag.liveperson.net')+'/tag.js?site='+this.site);
to
t||(n='/tag.js?site='+this.site)

  1. wraps tag.js in a closure where exports will be undefined - the commit is pretty clear, basically:
(function(exports) {  // -- add this line to the top
   // [ the entire original contents of tag.js here ]
})(undefined);        // -- add this line to the bottom

I say “not ideal” as now you’ll have to update your local tag.js whenever LivePerson changes it on their server. Also because, I don’t think this is the correct way to address the problem, but probably you just want to get this working and to move on.

Let us know how it works out. If there are more problems after this once a valid siteidnumber is used, we’ll probably need a “proper fix”.

1 Like

@gadicc super smart idea! I am going to scope out the repo and test it out! I will report back. Thanks a lot man!

1 Like

I wanted to follow up. @gadicc your answer worked GREAT! If anyone else is having a similar issue I would recommend this fix! Thanks again man!

1 Like

Great news! Thanks for taking the time to report back :smiley: