Htmlrewriter to modify existing script, or append if missing

I’m searching for a technique that would insert a script tag into the head, IF a script tag with a specified ID doesn’t exist. With the additional requirement that if the tag does exist, it would be modified instead.

Setting flags and post-processing after onEndTag for HEAD doesn’t work, since the HEAD has already been closed.

So:

  • Loop through scripts
  • If script ID = x then edit
  • On end, append

That sounds like what you want right?

Assuming so, here’s some example code (not tested):

class Handler {
  modifiedScript = false;

  element(element) {
		// If there's a script tag and it has the "script-id" attribute which equals to "abc"
		// then change the "src" attribute to "/some-other-script.js" (changing what script is being served)
		// finally set modifiedScript to true so we know we did this
    if (element.name === 'script' && element.getAttribute('script-id') == 'abc') {
      element.setAttribute('src', '/some-other-script.js');
      this.modifiedScript = true;

		} else if (element.name === 'head') {
			// Attach a handler to run on </head>
			element.onEndTag((tag) => {
				// If we didn't modify a script tag then we want to insert one
				if (!this.modifiedScript) {
					// Append the script tag to _before_ the closing head tag.
					tag.before('<script src="/some-other-script.js" type="text/javascript"></script>', { html: true });
				}
			})
		}
  }
}

export default {
	fetch(request) {
		let resp = await fetch(request);
		return new HTMLRewriter()
			.on('script', new Handler())
			.transform(resp);
	}
}

2 Likes

That’s it after a few tweaks, the selector was off, I modified to be .on('head, head > script', new Handler()) otherwise the head tag wasn’t being hooked, and the script tag wouldn’t fire unless there were script tags.

Also the element.name properties should be element.tagName ? Or at least that’s what seemed to work on cloudflareworkers .com
I’m not sure why I had issues with modifying the head while inside the onEndTag handler - in my code I was getting an error that the head had already been closed and couldn’t be modified.

Thanks for your help

I had messed up the onEndTag implementation - I was using element instead of tag inside the callback - which resulted in a runtime error TypeError: This content token is no longer valid. Content tokens are only valid during the execution of the relevant content handler.

class HeadElement {
element(element) {
console.log(‘found head’)
element.onEndTag(tag => {
console.log(‘ending head’, tag)
tag.before(“”, {html: true})
})
}
}

Thanks again for your help

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.