Skip to content

Commit

Permalink
Add inline sourcemap support to content scripts (#701)
Browse files Browse the repository at this point in the history
* return correct values in transform hook

* fix transform hook

* add inline sourcemap support to content scripts

* Create rotten-snakes-brush.md

* disable truncation in test error diff

* scrub sourcemaps in test snapshots
  • Loading branch information
jacksteamdev committed May 5, 2023
1 parent bbc4d33 commit 936ed77
Show file tree
Hide file tree
Showing 18 changed files with 431 additions and 3 deletions.
5 changes: 5 additions & 0 deletions .changeset/rotten-snakes-brush.md
@@ -0,0 +1,5 @@
---
"@crxjs/vite-plugin": patch
---

Add inline sourcemap support to content scripts
2 changes: 2 additions & 0 deletions packages/vite-plugin/package.json
Expand Up @@ -68,6 +68,7 @@
"acorn-walk": "^8.2.0",
"cheerio": "^1.0.0-rc.10",
"connect-injector": "^0.4.4",
"convert-source-map": "^1.7.0",
"debug": "^4.3.3",
"es-module-lexer": "^0.10.0",
"fast-glob": "^3.2.11",
Expand All @@ -89,6 +90,7 @@
"@sveltejs/vite-plugin-svelte": "1.1.0",
"@types/acorn": "4.0.6",
"@types/chrome": "0.0.209",
"@types/convert-source-map": "^2.0.0",
"@types/debug": "4.1.7",
"@types/fs-extra": "9.0.13",
"@types/jest-image-snapshot": "^5.1.0",
Expand Down
22 changes: 20 additions & 2 deletions packages/vite-plugin/src/node/fileWriter-rxjs.ts
Expand Up @@ -22,6 +22,7 @@ import { outputFiles } from './fileWriter-filesMap'
import { getFileName, getOutputPath, getViteUrl } from './fileWriter-utilities'
import { join } from './path'
import { CrxDevAssetId, CrxDevScriptId, CrxPlugin } from './types'
import convertSourceMap from 'convert-source-map'

/* ----------------- SERVER EVENTS ----------------- */

Expand Down Expand Up @@ -146,8 +147,25 @@ function prepScript(
const transformResult = await server.transformRequest(viteUrl)
if (!transformResult)
throw new TypeError(`Unable to load "${script.id}" from server.`)
const { code, deps = [], dynamicDeps = [] } = transformResult
return { target, code, deps: [...deps, ...dynamicDeps].flat(), server }
const { deps = [], dynamicDeps = [], map } = transformResult
let { code } = transformResult
try {
if (map && server.config.build.sourcemap === 'inline') {
// remove existing source map (might be a url, which doesn't work in content scripts)
code = code.replace(/\n*\/\/# sourceMappingURL=[^\n]+/g, '')
// create a new inline source map
const sourceMap = convertSourceMap.fromObject(map).toComment()
code += `\n${sourceMap}\n`
}
} catch (error) {
console.warn('Failed to inline source map', error)
}
return {
target,
code,
deps: [...deps, ...dynamicDeps].flat(),
server,
}
}),
// retry in case of dependency rebundle
retry({ count: 10, delay: 100 }),
Expand Down
2 changes: 1 addition & 1 deletion packages/vite-plugin/src/node/plugin-manifest.ts
Expand Up @@ -250,7 +250,7 @@ export const pluginManifest: CrxPluginFn = () => {
}

const encoded = encodeManifest(manifest)
return encoded
return { code: encoded, map: null }
},
async generateBundle(options, bundle) {
const manifestName = this.getFileName(refId)
Expand Down
@@ -0,0 +1,159 @@
// Vitest Snapshot v1

exports[`build fs output > _00 manifest.json 1`] = `
Object {
"action": Object {
"default_popup": "src/popup.html",
},
"background": Object {
"service_worker": "service-worker-loader.js",
"type": "module",
},
"content_scripts": Array [
Object {
"js": Array [
"assets/content.ts-loader.hash0.js",
],
"matches": Array [
"https://*/*",
"http://*/*",
],
},
],
"description": "test extension",
"manifest_version": 3,
"name": "test extension",
"version": "0.1.0",
"web_accessible_resources": Array [
Object {
"matches": Array [
"http://*/*",
"https://*/*",
],
"resources": Array [
"assets/content.ts.hash1.js",
],
"use_dynamic_url": true,
},
],
}
`;

exports[`build fs output > _01 output files 1`] = `
Array [
"assets/background.ts.hash2.js",
"assets/content.ts-loader.hash0.js",
"assets/content.ts.hash1.js",
"assets/popup.html.hash3.js",
"assets/vendor.hash4.js",
"manifest.json",
"service-worker-loader.js",
"src/popup.html",
]
`;

exports[`build fs output > assets/background.ts.hash2.js 1`] = `
"console.log(\\"service_worker.ts\\");
// # sourceMappingURL=data:application/json;charset=utf-8;base64,<base64>
"
`;
exports[`build fs output > assets/content.ts.hash1.js 1`] = `
"const message = \\"content script\\";
console.log(message);
// # sourceMappingURL=data:application/json;charset=utf-8;base64,<base64>
"
`;
exports[`build fs output > assets/content.ts-loader.hash0.js 1`] = `
"(function () {
'use strict';
const injectTime = performance.now();
(async () => {
const { onExecute } = await import(
/* @vite-ignore */
chrome.runtime.getURL(\\"assets/content.ts.hash1.js\\")
);
onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });
})().catch(console.error);
})();
"
`;
exports[`build fs output > assets/popup.html.hash3.js 1`] = `
"import { R as React, r as reactDom } from \\"./vendor.hash4.js\\";
(function polyfill() {
const relList = document.createElement(\\"link\\").relList;
if (relList && relList.supports && relList.supports(\\"modulepreload\\")) {
return;
}
for (const link of document.querySelectorAll('link[rel=\\"modulepreload\\"]')) {
processPreload(link);
}
new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.type !== \\"childList\\") {
continue;
}
for (const node of mutation.addedNodes) {
if (node.tagName === \\"LINK\\" && node.rel === \\"modulepreload\\")
processPreload(node);
}
}
}).observe(document, { childList: true, subtree: true });
function getFetchOpts(script) {
const fetchOpts = {};
if (script.integrity)
fetchOpts.integrity = script.integrity;
if (script.referrerpolicy)
fetchOpts.referrerPolicy = script.referrerpolicy;
if (script.crossorigin === \\"use-credentials\\")
fetchOpts.credentials = \\"include\\";
else if (script.crossorigin === \\"anonymous\\")
fetchOpts.credentials = \\"omit\\";
else
fetchOpts.credentials = \\"same-origin\\";
return fetchOpts;
}
function processPreload(link) {
if (link.ep)
return;
link.ep = true;
const fetchOpts = getFetchOpts(link);
fetch(link.href, fetchOpts);
}
})();
const App = () => {
return /* @__PURE__ */ React.createElement(\\"div\\", null, /* @__PURE__ */ React.createElement(\\"h1\\", null, \\"Popup Page\\"), /* @__PURE__ */ React.createElement(\\"p\\", null, \\"If you are seeing this, React is working!\\"));
};
console.log(\\"popup script\\");
const root = document.querySelector(\\"#root\\");
reactDom.exports.render(/* @__PURE__ */ React.createElement(App, null), root);
// # sourceMappingURL=data:application/json;charset=utf-8;base64,<base64>
"
`;
exports[`build fs output > service-worker-loader.js 1`] = `
"import './assets/background.ts.hash2.js';
"
`;
exports[`build fs output > src/popup.html 1`] = `
"<!DOCTYPE html>
<html lang=\\"en\\">
<head>
<meta charset=\\"UTF-8\\" />
<meta name=\\"viewport\\" content=\\"width=1000, initial-scale=1.0\\" />
<title>Popup Page</title>
<script type=\\"module\\" crossorigin src=\\"/assets/popup.html.hash3.js\\"></script>
<link rel=\\"modulepreload\\" crossorigin href=\\"/assets/vendor.hash4.js\\">
</head>
<body>
<div id=\\"root\\"></div>
</body>
</html>
"
`;
@@ -0,0 +1,131 @@
// Vitest Snapshot v1

exports[`serve fs output > _00 manifest.json 1`] = `
Object {
"action": Object {
"default_popup": "src/popup.html",
},
"background": Object {
"service_worker": "service-worker-loader.js",
"type": "module",
},
"content_scripts": Array [
Object {
"js": Array [
"src/content.ts-loader.js",
],
"matches": Array [
"https://*/*",
"http://*/*",
],
},
],
"description": "test extension",
"manifest_version": 3,
"name": "test extension",
"version": "0.1.0",
"web_accessible_resources": Array [
Object {
"matches": Array [
"<all_urls>",
],
"resources": Array [
"*",
"**/*",
],
"use_dynamic_url": true,
},
],
}
`;

exports[`serve fs output > _01 output files 1`] = `
Array [
"assets/precontroller.hash0.js",
"manifest.json",
"service-worker-loader.js",
"src/content.ts-loader.js",
"src/content.ts.js",
"src/popup.html",
"vendor/crx-client-port.js",
"vendor/vite-client.js",
"vendor/vite-dist-client-env.mjs.js",
"vendor/webcomponents-custom-elements.js",
]
`;

exports[`serve fs output > _02 optimized deps 1`] = `
Set {
"src/content.ts",
"src/background.ts",
"src/popup.html",
}
`;

exports[`serve fs output > assets/precontroller.hash0.js 1`] = `
"const id = setInterval(() => location.reload(), 100);
setTimeout(() => clearInterval(id), 5e3);
"
`;

exports[`serve fs output > service-worker-loader.js 1`] = `
"import 'http://localhost:3000/@vite/env';
import 'http://localhost:3000/@crx/client-worker';
import 'http://localhost:3000/src/background.ts';
"
`;

exports[`serve fs output > src/content.ts.js 1`] = `
"const message = \\"content script\\";
console.log(message);
export {};
// # sourceMappingURL=data:application/json;charset=utf-8;base64,<base64>
"
`;
exports[`serve fs output > src/content.ts-loader.js 1`] = `
"(function () {
'use strict';
const injectTime = performance.now();
(async () => {
if (\\"\\")
await import(
/* @vite-ignore */
chrome.runtime.getURL(\\"\\")
);
await import(
/* @vite-ignore */
chrome.runtime.getURL(\\"vendor/vite-client.js\\")
);
const { onExecute } = await import(
/* @vite-ignore */
chrome.runtime.getURL(\\"src/content.ts.js\\")
);
onExecute?.({ perf: { injectTime, loadTime: performance.now() - injectTime } });
})().catch(console.error);
})();
"
`;
exports[`serve fs output > src/popup.html 1`] = `
"<!DOCTYPE html>
<html lang=\\"en\\">
<head>
<title>Waiting for the extension service worker...</title>
<script src=\\"/assets/precontroller.hash0.js\\"></script>
</head>
<body>
<h1>Waiting for service worker</h1>
<p>
If you see this message, it means the service worker has not loaded fully.
</p>
<p>This page is never added in production.</p>
</body>
</html>
"
`;
8 changes: 8 additions & 0 deletions packages/vite-plugin/tests/out/with-sourcemaps/build.test.ts
@@ -0,0 +1,8 @@
import { build } from 'tests/runners'
import { testOutput } from 'tests/testOutput'
import { test } from 'vitest'

test('build fs output', async () => {
const result = await build(__dirname)
await testOutput(result)
})
18 changes: 18 additions & 0 deletions packages/vite-plugin/tests/out/with-sourcemaps/manifest.json
@@ -0,0 +1,18 @@
{
"action": {
"default_popup": "src/popup.html"
},
"background": {
"service_worker": "src/background.ts"
},
"content_scripts": [
{
"js": ["src/content.ts"],
"matches": ["https://*/*", "http://*/*"]
}
],
"manifest_version": 3,
"name": "test extension",
"description": "test extension",
"version": "0.1.0"
}

0 comments on commit 936ed77

Please sign in to comment.