do the manifest rewriting stuff smart hooly fuck
This commit is contained in:
parent
d750550c8d
commit
bd778cbd1c
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
node_modules
|
89
entrypoints.ts
Normal file
89
entrypoints.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
/** The kind of an entrypoint in an extension manifest. */
|
||||||
|
enum ManifestEntrypointKind {
|
||||||
|
/** Javascript run as part of a content script */
|
||||||
|
CONTENT_SCRIPT_JS,
|
||||||
|
/** CSS applied as part of a content script */
|
||||||
|
CONTENT_SCRIPT_CSS,
|
||||||
|
/** Javascript run in the background page of an MV2 extension */
|
||||||
|
BACKGROUND_SCRIPT,
|
||||||
|
/** Javascript run the service worker of an MV3 extension */
|
||||||
|
BACKGROUND_SERVICE_WORKER,
|
||||||
|
/** Image file loaded as the extension's icon at a particular size */
|
||||||
|
ICON,
|
||||||
|
/** Path listed in the `web_accessible_resources` of an MV2 extension */
|
||||||
|
WEB_ACCESSIBLE_RESOURCE_V2,
|
||||||
|
/** Path listed in the `web_accessible_resources` of an MV3 extension */
|
||||||
|
WEB_ACCESSIBLE_RESOURCE_V3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A handle to a single entrypoint from a manifest, which allows for rewriting
|
||||||
|
* the path of the asset in the manifest.
|
||||||
|
*/
|
||||||
|
interface ManifestEntrypoint {
|
||||||
|
type: ManifestEntrypointKind;
|
||||||
|
/** The full path to the entrypoint file. */
|
||||||
|
path: string;
|
||||||
|
/**
|
||||||
|
* Replaces this entry path with the given new path. This is an in-place
|
||||||
|
* operation and directly affects the original manifest object.
|
||||||
|
*/
|
||||||
|
replacePath: (path: string) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all script entrypoints from a manifest, returning them as
|
||||||
|
* {@link ManifestEntrypoint} objects which allow for rewriting the path of each
|
||||||
|
* individual entrypoint in place.
|
||||||
|
* @param manifest Parsed `manifest.json` data.
|
||||||
|
*/
|
||||||
|
export const getScriptEntrypoints = (manifest: chrome.runtime.Manifest): ManifestEntrypoint[] => [
|
||||||
|
...(manifest.content_scripts ?? []).flatMap(script => (script.js ?? []).map((path, i) => ({
|
||||||
|
type: ManifestEntrypointKind.CONTENT_SCRIPT_JS,
|
||||||
|
path,
|
||||||
|
replacePath: newPath => script.js!.splice(i, 1, newPath),
|
||||||
|
}))),
|
||||||
|
...(manifest.manifest_version === 2
|
||||||
|
? (manifest.background?.scripts || []).map((path, i) => ({
|
||||||
|
type: ManifestEntrypointKind.BACKGROUND_SCRIPT,
|
||||||
|
path,
|
||||||
|
replacePath: newPath => manifest.background!.scripts!.splice(i, 1, newPath),
|
||||||
|
}))
|
||||||
|
: (manifest.background?.service_worker ? [{
|
||||||
|
type: ManifestEntrypointKind.BACKGROUND_SERVICE_WORKER,
|
||||||
|
path: manifest.background.service_worker,
|
||||||
|
replacePath: newPath => manifest.background!.service_worker = newPath,
|
||||||
|
}] : [])
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets all asset entrypoints from a manifest, returning them as
|
||||||
|
* {@link ManifestEntrypoint} objects which allow for rewriting the path of each
|
||||||
|
* individual entrypoint in place.
|
||||||
|
* @param manifest Parsed `manifest.json` data.
|
||||||
|
*/
|
||||||
|
export const getAssetEntrypoints = (manifest: chrome.runtime.Manifest): ManifestEntrypoint[] => [
|
||||||
|
...(manifest.content_scripts ?? []).flatMap(script => (script.css ?? []).map((path, i) => ({
|
||||||
|
type: ManifestEntrypointKind.CONTENT_SCRIPT_CSS,
|
||||||
|
path,
|
||||||
|
replacePath: newPath => script.css!.splice(i, 1, newPath),
|
||||||
|
}))),
|
||||||
|
...Object.entries(manifest.icons || {}).map(([iconSize, path]) => ({
|
||||||
|
type: ManifestEntrypointKind.ICON,
|
||||||
|
path,
|
||||||
|
replacePath: newPath => manifest.icons![iconSize] = newPath,
|
||||||
|
})),
|
||||||
|
...(manifest.manifest_version === 2
|
||||||
|
? (manifest.web_accessible_resources ?? []).map((path, i) => ({
|
||||||
|
type: ManifestEntrypointKind.WEB_ACCESSIBLE_RESOURCE_V2,
|
||||||
|
path,
|
||||||
|
replacePath: newPath => manifest.web_accessible_resources![i] = newPath,
|
||||||
|
}))
|
||||||
|
: (manifest.web_accessible_resources ?? []).flatMap(entry => entry.resources.map((path, i) => ({
|
||||||
|
type: ManifestEntrypointKind.WEB_ACCESSIBLE_RESOURCE_V3,
|
||||||
|
path,
|
||||||
|
replacePath: newPath => entry.resources.splice(i, 1, newPath),
|
||||||
|
})))
|
||||||
|
)
|
||||||
|
];
|
98
index.mjs
98
index.mjs
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
import {readFileSync} from 'node:fs';
|
import {readFileSync} from 'node:fs';
|
||||||
import {resolve, basename, extname, dirname, relative, join} from 'node:path';
|
import {resolve, basename, extname, dirname, relative, join} from 'node:path';
|
||||||
|
import { getAssetEntrypoints, getScriptEntrypoints } from './entrypoints';
|
||||||
|
|
||||||
export function buildConfig ({
|
export function buildConfig ({
|
||||||
manifest: manifestPathRelative,
|
manifest: manifestPathRelative,
|
||||||
|
@ -47,81 +48,31 @@ export function buildConfig ({
|
||||||
return join(base, uniqueFileNameSegment(entryPath, ext));
|
return join(base, uniqueFileNameSegment(entryPath, ext));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Scans a manifest for entrypoints */
|
const scripts = getScriptEntrypoints(manifestContent);
|
||||||
function getEntryPointsFromManifest (manifestContent) {
|
const assets = getAssetEntrypoints(manifestContent);
|
||||||
const scriptEntrypointAbsolutePaths = [];
|
|
||||||
const styleEntrypointAbsolutePathss = [];
|
|
||||||
const otherAssetAbsolutePaths = [];
|
|
||||||
// Gather all JS entry points specified in the manifest
|
|
||||||
(manifestContent.content_scripts || []).forEach(({css, js}) => {
|
|
||||||
css && css.forEach((filename, i) => {
|
|
||||||
const id = resolve(manifestDirname, filename);
|
|
||||||
styleEntrypointAbsolutePathss.push(id);
|
|
||||||
css.splice(i, 1, getOutputFilename(id, 'css'));
|
|
||||||
});
|
|
||||||
js && js.forEach((filename, i) => {
|
|
||||||
const id = resolve(manifestDirname, filename);
|
|
||||||
scriptEntrypointAbsolutePaths.push(id);
|
|
||||||
js.splice(i, 1, getOutputFilename(id, 'js'));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
manifestContent.background?.scripts && manifestContent.background.scripts.forEach((filename, i) => {
|
|
||||||
const id = resolve(manifestDirname, filename);
|
|
||||||
scriptEntrypointAbsolutePaths.push(id);
|
|
||||||
manifestContent.background.scripts.splice(i, 1, getOutputFilename(id));
|
|
||||||
});
|
|
||||||
if (manifestContent.background?.service_worker) {
|
|
||||||
const id = resolve(manifestDirname, manifestContent.background.service_worker);
|
|
||||||
scriptEntrypointAbsolutePaths.push(id);
|
|
||||||
manifestContent.background.service_worker = getOutputFilename(id);
|
|
||||||
}
|
|
||||||
manifestContent.icons && Object.entries(manifestContent.icons).forEach(([size, filename]) => {
|
|
||||||
const id = resolve(manifestDirname, filename);
|
|
||||||
// console.log('icon:', size, filename, getOutputFilename(id));
|
|
||||||
otherAssetAbsolutePaths.push(id);
|
|
||||||
manifestContent.icons[size] = getOutputFilename(id);
|
|
||||||
});
|
|
||||||
(manifestContent.web_accessible_resources || []).forEach((entry, i) => {
|
|
||||||
if (typeof entry === 'string') {
|
|
||||||
// mv2 - single top-level array of items
|
|
||||||
const id = resolve(manifestDirname, entry);
|
|
||||||
otherAssetAbsolutePaths.push(id);
|
|
||||||
manifestContent.web_accessible_resources.splice(i, 1, getOutputFilename(id));
|
|
||||||
} else {
|
|
||||||
// mv3 - array of objects with `resources` keys
|
|
||||||
const {resources} = entry;
|
|
||||||
resources && resources.forEach((filename, j) => {
|
|
||||||
const id = resolve(manifestDirname, filename);
|
|
||||||
otherAssetAbsolutePaths.push(id);
|
|
||||||
resources.splice(j, 1, getOutputFilename(id));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
scripts: scriptEntrypointAbsolutePaths,
|
|
||||||
assets: [
|
|
||||||
...styleEntrypointAbsolutePathss,
|
|
||||||
...otherAssetAbsolutePaths,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const {scripts, assets} = getEntryPointsFromManifest(manifestContent);
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
...scripts.map(entrypointPath => ({
|
// Process each script entrypoint independently
|
||||||
input: relative(process.cwd(), entrypointPath),
|
...scripts.map(({path, replacePath}) => {
|
||||||
|
// Figure out where this bundle will live in the output
|
||||||
|
const outPath = getOutputFilename(path, 'js');
|
||||||
|
|
||||||
|
// Rewrite the manifest with that path
|
||||||
|
replacePath(outPath);
|
||||||
|
|
||||||
|
// Build the bundle
|
||||||
|
return {
|
||||||
|
input: relative(process.cwd(), path),
|
||||||
output: {
|
output: {
|
||||||
file: join(outDir, getOutputFilename(entrypointPath, 'js')),
|
file: join(outDir, outPath),
|
||||||
format: 'iife',
|
format: 'iife',
|
||||||
sourcemap,
|
sourcemap,
|
||||||
},
|
},
|
||||||
plugins: scriptPlugins,
|
plugins: scriptPlugins,
|
||||||
})),
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
// A special step that processes the manifest and copies over non-JS
|
// Special step that processes the manifest and injects other assets
|
||||||
// assets in the meantime
|
|
||||||
{
|
{
|
||||||
input: manifestPathRelative,
|
input: manifestPathRelative,
|
||||||
output: {
|
output: {
|
||||||
|
@ -131,11 +82,20 @@ export function buildConfig ({
|
||||||
{
|
{
|
||||||
// emit other assets
|
// emit other assets
|
||||||
buildStart () {
|
buildStart () {
|
||||||
assets.forEach(absolutePath => this.emitFile({
|
assets.forEach(({path, replacePath}) => {
|
||||||
|
// Figure out where the asset will live in output
|
||||||
|
const outPath = getOutputFilename(path);
|
||||||
|
|
||||||
|
// Rewrite the manifest with that path
|
||||||
|
replacePath(outPath);
|
||||||
|
|
||||||
|
// Emit the asset as part of the build step
|
||||||
|
this.emitFile({
|
||||||
type: 'asset',
|
type: 'asset',
|
||||||
fileName: getOutputFilename(absolutePath),
|
fileName: getOutputFilename(absolutePath),
|
||||||
source: readFileSync(absolutePath),
|
source: readFileSync(absolutePath),
|
||||||
}));
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// hacks to make sure the manifest is emitted as bare JSON
|
// hacks to make sure the manifest is emitted as bare JSON
|
||||||
|
|
46
package-lock.json
generated
Normal file
46
package-lock.json
generated
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"name": "rollup-create-webext-config",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"lockfileVersion": 3,
|
||||||
|
"requires": true,
|
||||||
|
"packages": {
|
||||||
|
"": {
|
||||||
|
"name": "rollup-create-webext-config",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/chrome": "^0.0.242"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/chrome": {
|
||||||
|
"version": "0.0.242",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.242.tgz",
|
||||||
|
"integrity": "sha512-SeMXBSfcAGX9ezTz7Pro7n/AiNdIH3cetkdbM+Kfg3zD24jmbnm0IAEIxzx8ccqrnJenLCfD7fR+4WIYAbeQHw==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/filesystem": "*",
|
||||||
|
"@types/har-format": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/filesystem": {
|
||||||
|
"version": "0.0.32",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.32.tgz",
|
||||||
|
"integrity": "sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/filewriter": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@types/filewriter": {
|
||||||
|
"version": "0.0.29",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz",
|
||||||
|
"integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/@types/har-format": {
|
||||||
|
"version": "1.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.11.tgz",
|
||||||
|
"integrity": "sha512-T232/TneofqK30AD1LRrrf8KnjLvzrjWDp7eWST5KoiSzrBfRsLrWDPk4STQPW4NZG6v2MltnduBVmakbZOBIQ==",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "rollup-create-webext-config",
|
"name": "rollup-create-webext-config",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"main": "index.mjs"
|
"main": "index.mjs",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/chrome": "^0.0.242"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue