Script Injection
Relevant source files
The following files were used as context for generating this wiki page:
Script injection and manipulation are fundamental to how Rendertron operates. The service is designed to render web pages that rely heavily on JavaScript by executing those scripts in a headless Chrome instance. After execution, it serializes the resulting DOM. This process involves not only running the page's native scripts but also strategically injecting polyfills and stripping out script tags to produce clean, static HTML suitable for crawlers and bots.
This system ensures that dynamic content is fully rendered and available in the output, which is critical for Search Engine Optimization (SEO) and for consumers that do not execute JavaScript. The two main facets of this feature are the injection of polyfills for compatibility (e.g., for Web Components) and the removal of all executable scripts from the final serialized content to prevent further resource loading or execution.
Sources: src/renderer.ts, README.md:14-19
Page Serialization and Script Handling
The core logic for handling scripts resides within the Renderer.serialize method. This process is responsible for navigating to a URL, waiting for the page to become idle, and then processing the DOM before returning the serialized HTML.
Sources: src/renderer.ts:32-34
Script Execution and Stripping
When a URL is rendered, Rendertron uses Puppeteer to navigate to the page and waits until the network is idle (networkidle0). This allows any scripts, including those loaded dynamically, to execute and manipulate the DOM.
Once the page is considered loaded, a function named stripPage is evaluated in the page's context. This function removes all <script> tags (except for specific types like application/ld+json) and <link rel="import"> tags. This step is crucial to prevent the client that receives the serialized HTML from re-executing scripts. Tests confirm that standard scripts are removed, while JSON-LD structured data scripts are preserved.
Sources: src/renderer.ts:41-47, src/renderer.ts:111-112, src/test/app-test.ts:95-101, src/test/app-test.ts:103-108
The following diagram illustrates the high-level serialization flow, including the script stripping step.
This diagram shows the sequence of operations during a render request, highlighting when scripts are executed and subsequently stripped.
Sources: src/renderer.ts:32-160
Dynamic Script Loading
Rendertron is capable of handling pages that dynamically load scripts after the initial page load. For example, a script can be attached to the window.onload event, which then creates and appends a new <script> element to the document. Because the serialize method waits for networkidle0, it gives these asynchronous operations time to complete.
The test case using script-after-load.html demonstrates this. The page loads inject-element.js after the onload event, and the test verifies that the element created by this injected script is present in the final rendered output.
Sources: src/test/app-test.ts:54-58, test-resources/script-after-load.html:16-21, test-resources/inject-element.js:16-18
Polyfill Injection for Web Components
A key feature of Rendertron is its ability to render content that uses Web Components, which often rely on polyfills for features like Shadow DOM. Rendertron injects the necessary polyfills to ensure these components are rendered correctly.
Forcing Shady DOM
To handle Web Components, Rendertron can force the Shady DOM and Shady CSS polyfills to be enabled. This is achieved by injecting scripts into the page context before the page's own scripts are executed, using Puppeteer's page.evaluateOnNewDocument method. This ensures that the polyfills are active when the custom elements are defined and rendered.
The following scripts are injected:
page.evaluateOnNewDocument('customElements.forcePolyfill = true');
page.evaluateOnNewDocument('ShadyDOM = {force: true}');
page.evaluateOnNewDocument('ShadyCSS = {shimcssproperties: true}');
Sources: src/renderer.ts:100-102
This injection can be triggered by a query parameter, wc-inject-shadydom=true, when making a request to the render service. This is particularly useful for sites using Web Components v1 with webcomponents-lite.js or webcomponents-loader.js.
Sources: README.md:80-83, src/test/app-test.ts:68-73
The sequence diagram below shows how a request with this parameter leads to polyfill injection.
This diagram shows the interaction for rendering a page with Web Components, where Rendertron injects polyfills before page load.
Sources: src/renderer.ts:98-112, README.md:80-83
Summary of Script-Related Functions
The following table summarizes the key functions and methods involved in script injection and manipulation within the Renderer class.
| Function / Method | Purpose | Source Location |
|---|---|---|
stripPage | Removes <script> and <link rel="import"> tags from the DOM before serialization, with an exception for JSON-LD scripts. | src/renderer.ts:41-47 |
page.evaluateOnNewDocument | Injects scripts to force Web Components polyfills (ShadyDOM, ShadyCSS) before any page scripts run. | src/renderer.ts:100-102 |
page.goto with networkidle0 | Navigates to a URL and waits for network activity to cease, allowing dynamically loaded scripts to execute. | src/renderer.ts:111-112 |
Conclusion
Script injection and manipulation are central to Rendertron's mission of making the modern, JavaScript-driven web crawlable. The system intelligently executes all necessary scripts to build a complete and dynamic page, including those loaded asynchronously. It then sanitizes the output by stripping out script tags, ensuring the final HTML is static and safe. Furthermore, its ability to inject polyfills on-the-fly provides critical support for rendering pages built with technologies like Web Components, making it a versatile solution for pre-rendering a wide variety of web applications.