Exploring a chrome sidebar extension

2023-10-01

Created a new sveltekit project

Setup a new chrome extension as per usual sveltekit extension development.

Explored these documentation: https://developer.chrome.com/docs/extensions/reference/sidePanel/

In the manifest.json added sidePanel into the permission array

{
...
	"permissions": ["sidePanel"]
}

Add a sidepanel.html

<!DOCTYPE html>
<html>
	<head>
		<title>My Sidepanel</title>
	</head>
	<body>
		<h1>All sites sidepanel extension</h1>
		<p>This side panel is enabled on all sites</p>
    <script src=""sidepanel.js"></script>
	</body>
</html>

Create a new file sidepanel.js .

Doesn’t work on all browsers right now though

In Edge, use of an extension in the sidebar has to be accessed through the the sidebar icon when selecting the extension.

Getting the svelte app into the sidebar

Create a new route called sidepanel . Since this will create the static html route sidepanel.html , remove the one in the static folder.

From here this is now the entry point for the sidebar.

In the app.html in src , add <script src ="" sidepanel.js "></script> into the head .

From here, when you build the app it will load the svelte built page, but there will be an error since SvelteKit generates inline javascript in the built file.

To get around this, there is a script that uses tiny-glob to transport this script to an external module.

Install tiny-glob

>pnpm install tiny-glob

Add remmoveinlineScript.cjs to the root of the project

// / removeInlineScript.cjs
const glob = require('tiny-glob');
const path = require('path');
const fs = require('fs');

function hash(value) {
	let hash = 5381;
	let i = value.length;
	if (typeof value === 'string') {
		while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
	} else {
		while (i) hash = (hash * 33) ^ value[--i];
	}
	return (hash >>> 0).toString(36);
}

async function removeInlineScript(directory) {
	console.log('Removing Inline Scripts');
	const scriptRegx = /<script>([\s\S]+)<\/script>/;
	const files = await glob('**/*.{html}', {
		cwd: directory,
		dot: true,
		aboslute: true,
		filesOnly: true
	});
	files
		.map((f) => path.join(directory, f))
		.forEach((file) => {
			console.log(`edit file: ${file}`);
			const f = fs.readFileSync(file, { encoding: 'utf-8' });

			const script = f.match(scriptRegx);
			if (script && script[1]) {
				const inlineContent = script[1]
					.replace('__sveltekit', 'const __sveltekit')
					.replace('document.currentScript.parentElement', 'document.body.firstElementChild');
				const fn = `/script-${hash(inlineContent)}.js`;
				const newHtml = f.replace(scriptRegx, `<script type="module" src="${fn}"></script>`);
				fs.writeFileSync(file, newHtml);
				fs.writeFileSync(`${directory}${fn}`, inlineContent);
				console.log(`Inline script extracted and saved at: ${directory}${fn}`);
			}
		});
}

removeInlineScript(path.resolve(__dirname, 'build'));

Update the build command in the package.json

{
...
"build": "vite build && node removeInlineScript.cjs",
...
}