From dc1bda69f3cd040b854adbf23029b42b17251bd9 Mon Sep 17 00:00:00 2001 From: Cezar Augusto Date: Fri, 28 Jun 2024 20:40:27 -0300 Subject: [PATCH] Add non-standard cross-browser fields support --- packages/manifest-compat-plugin/module.ts | 17 +++++ .../plugins/HandleBrowserSpecificFields.ts | 74 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 packages/manifest-compat-plugin/plugins/HandleBrowserSpecificFields.ts diff --git a/packages/manifest-compat-plugin/module.ts b/packages/manifest-compat-plugin/module.ts index 9d6e4052..b5e298a1 100644 --- a/packages/manifest-compat-plugin/module.ts +++ b/packages/manifest-compat-plugin/module.ts @@ -3,6 +3,7 @@ import {type Compiler} from 'webpack' import {type ManifestCompatInterface} from './types' import handleSchemaErrors from './handleSchemaErrors' import handleRuntimeErrors from './handleRuntimeErrors' +import HandleBrowserSpecificFields from './plugins/HandleBrowserSpecificFields' import {type ManifestBase} from './manifest-types' export default class ManifestCompatPlugin { @@ -13,6 +14,22 @@ export default class ManifestCompatPlugin { } apply(compiler: Compiler) { + // TODO: This should be split into multiple plugins + // - HandleMissingFields - When users are missing a required field in their manifest file. + // - HandleDeprecatedFields - When users use a manifest field that has been deprecated in their manifest file version. + // - HandleV2ToV3Changes - When users use a manifest field that has changed from v2 to v3 e.g. `web_accessible_resources`. + // - HandleFieldTypeMistakes - When users use a field with the wrong type, e.g. a string instead of an array. + + // - HandleBrowserSpecificFields - When users use non-compilant, browser-specific fields e.g. `chrome:`, `firefox:`. + new HandleBrowserSpecificFields({ + manifestPath: this.options.manifestPath, + browser: this.options.browser || 'chrome' + }).apply(compiler) + + // - HandleBrowserCompatErrors - When users use a field that is not compatible with the browser they are targeting. + // - HandleManifestVersionErrors - When users use a field that is not compatible with the browser they are targeting. + + // TODO: Refactor this compiler.hooks.afterCompile.tapAsync( 'CompatPlugin (module)', (compilation, done) => { diff --git a/packages/manifest-compat-plugin/plugins/HandleBrowserSpecificFields.ts b/packages/manifest-compat-plugin/plugins/HandleBrowserSpecificFields.ts new file mode 100644 index 00000000..9a553a54 --- /dev/null +++ b/packages/manifest-compat-plugin/plugins/HandleBrowserSpecificFields.ts @@ -0,0 +1,74 @@ +import {type Compiler, sources} from 'webpack' +import {ManifestBase} from '../manifest-types' + +interface Options { + manifestPath: string + browser: string + exclude?: string[] +} + +export default class HandleBrowserSpecificFields { + private readonly manifestPath: string + private browser: string + private chromiumBasedBrowsers = ['chrome', 'edge', 'opera', 'brave'] + + constructor(options: Options) { + this.manifestPath = options.manifestPath + this.browser = options.browser + } + + private processManifest(manifest: any): any { + const result: any = {} + + for (const key in manifest) { + if (manifest.hasOwnProperty(key)) { + if ( + typeof manifest[key] === 'object' && + !Array.isArray(manifest[key]) + ) { + result[key] = this.processManifest(manifest[key]) + } else if ( + key.startsWith(`${this.browser}:`) || + (key.startsWith('chromium:') && + this.chromiumBasedBrowsers.includes(this.browser)) + ) { + result[key.replace(`${this.browser}:`, '').replace('chromium:', '')] = + manifest[key] + } else if (!key.includes(':')) { + result[key] = manifest[key] + } + } + } + + return result + } + + apply(compiler: Compiler) { + compiler.hooks.emit.tapAsync( + 'HandleBrowserSpecificFields', + (compilation, done) => { + if (compilation.errors.length > 0) return + + const manifestSource = compilation + .getAsset('manifest.json') + ?.source.source() + const manifest: ManifestBase = JSON.parse( + (manifestSource || '').toString() + ) + const overrides = this.processManifest(manifest) + + const patchedManifest: ManifestBase = { + ...manifest, + ...JSON.parse(overrides) + } + + const source = JSON.stringify(patchedManifest, null, 2) + const rawSource = new sources.RawSource(source) + + compilation.updateAsset('manifest.json', rawSource) + + done() + } + ) + } +}