From 3c4b87f7cee39471d9d4b28ebdf9f4516c8e15ff Mon Sep 17 00:00:00 2001 From: Cezar Augusto Date: Sat, 20 Jul 2024 16:11:31 -0300 Subject: [PATCH] Remove old intances of browser/reload plugins --- programs/develop/install-scripts.sh | 6 +- programs/develop/package.json | 3 +- .../develop/webpack/_plugin-reload/index.ts | 25 --- .../webpack/plugin-browsers/_package.json | 72 ++++++++ .../browser-lib/add-progress-bar.ts | 22 +++ .../plugin-browsers/browser-lib/messages.ts | 29 +++ .../plugin-browsers/browser-lib/utils.ts | 17 ++ .../develop/webpack/plugin-browsers/index.ts | 158 ++++++++-------- .../run-chromium/browser-config.ts | 99 ++++++++++ .../run-chromium/create-profile.ts | 47 +++++ .../plugin-browsers/run-chromium/index.ts | 94 ++++++++++ .../run-chromium/master-preferences.ts | 174 ++++++++++++++++++ .../develop/webpack/plugin-browsers/types.ts | 12 ++ programs/develop/webpack/webpack-config.ts | 16 +- 14 files changed, 664 insertions(+), 110 deletions(-) delete mode 100644 programs/develop/webpack/_plugin-reload/index.ts create mode 100644 programs/develop/webpack/plugin-browsers/_package.json create mode 100644 programs/develop/webpack/plugin-browsers/browser-lib/add-progress-bar.ts create mode 100644 programs/develop/webpack/plugin-browsers/browser-lib/messages.ts create mode 100644 programs/develop/webpack/plugin-browsers/browser-lib/utils.ts create mode 100644 programs/develop/webpack/plugin-browsers/run-chromium/browser-config.ts create mode 100644 programs/develop/webpack/plugin-browsers/run-chromium/create-profile.ts create mode 100644 programs/develop/webpack/plugin-browsers/run-chromium/index.ts create mode 100644 programs/develop/webpack/plugin-browsers/run-chromium/master-preferences.ts create mode 100644 programs/develop/webpack/plugin-browsers/types.ts diff --git a/programs/develop/install-scripts.sh b/programs/develop/install-scripts.sh index 5d4a2f5b..af0c483b 100644 --- a/programs/develop/install-scripts.sh +++ b/programs/develop/install-scripts.sh @@ -60,9 +60,9 @@ execute_command() { # Copy required files to dist directory copy_files_to_dist() { local files=( - "tailwind.config.js" - "stylelint.config.js" - "commands/dev/types" + # "tailwind.config.js" + # "stylelint.config.js" + # "commands/dev/types" "plugin-reload/extensions" ) for file in "${files[@]}"; do diff --git a/programs/develop/package.json b/programs/develop/package.json index 88b2bcf3..7f2963f0 100644 --- a/programs/develop/package.json +++ b/programs/develop/package.json @@ -22,6 +22,7 @@ "url": "https://cezaraugusto.com" }, "scripts": { + "postinstall": "sh ./install-scripts.sh", "clean": "rm -rf dist", "watch": "yarn compile --watch", "compile:types-folder": "node ./scripts/copyTypesFolder.js", @@ -97,4 +98,4 @@ "tsup": "^8.0.1", "typescript": "5.3.3" } -} +} \ No newline at end of file diff --git a/programs/develop/webpack/_plugin-reload/index.ts b/programs/develop/webpack/_plugin-reload/index.ts deleted file mode 100644 index b698d841..00000000 --- a/programs/develop/webpack/_plugin-reload/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -// ██████╗ ███████╗██╗ ██╗███████╗██╗ ██████╗ ██████╗ -// ██╔══██╗██╔════╝██║ ██║██╔════╝██║ ██╔═══██╗██╔══██╗ -// ██║ ██║█████╗ ██║ ██║█████╗ ██║ ██║ ██║██████╔╝ -// ██║ ██║██╔══╝ ╚██╗ ██╔╝██╔══╝ ██║ ██║ ██║██╔═══╝ -// ██████╔╝███████╗ ╚████╔╝ ███████╗███████╗╚██████╔╝██║ -// ╚═════╝ ╚══════╝ ╚═══╝ ╚══════╝╚══════╝ ╚═════╝ ╚═╝ - -import type webpack from 'webpack' -import {type DevOptions} from '../../extensionDev' -import ReactRefreshPlugin from '@pmmmwh/react-refresh-webpack-plugin' -import {isUsingReact} from '../options/react' -import {isUsingPreact} from '../options/preact' - -export default function reloadPlugins(projectPath: string, {mode}: DevOptions) { - return { - constructor: {name: 'ReloadPlugins'}, - apply: (compiler: webpack.Compiler) => { - if (mode !== 'development') return - - if (isUsingReact(projectPath) || isUsingPreact(projectPath)) { - new ReactRefreshPlugin().apply(compiler) - } - } - } -} diff --git a/programs/develop/webpack/plugin-browsers/_package.json b/programs/develop/webpack/plugin-browsers/_package.json new file mode 100644 index 00000000..20e132cc --- /dev/null +++ b/programs/develop/webpack/plugin-browsers/_package.json @@ -0,0 +1,72 @@ +{ + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/cezaraugusto/webpack-run-chromium-extension.git" + }, + "engines": { + "node": ">=18" + }, + "name": "webpack-run-chromium-extension", + "version": "1.3.0", + "description": "Run your extension on any Chromium-based browser", + "main": "./dist/module.js", + "types": "./dist/module.d.ts", + "author": { + "name": "Cezar Augusto", + "email": "boss@cezaraugusto.net", + "url": "https://cezaraugusto.com" + }, + "scripts": { + "clean": "rm -rf dist", + "compile:plugin": "tsup-node ./module.ts ./loaders/* --format cjs --dts --target=node18 --minify && node ./copyExtensions.js", + "compile:web": "tsup-node ./minimum-background-file.ts --format esm --dts --target=node18 --minify --config=tsconfig.web.json", + "compile": "yarn compile:plugin && yarn compile:web", + "lint": "eslint \"./**/*.ts*\"", + "test": "echo \"Note: no test specified\" && exit 0", + "test:fixture": "yarn --cwd=./fixtures/dev-server-no-hmr dev", + "demo": "yarn test:fixture" + }, + "files": [ + "dist" + ], + "keywords": [ + "webpack", + "plugin", + "chromium", + "run", + "open", + "plugin", + "browser", + "web", + "extension", + "web-ext", + "webextension" + ], + "peerDependencies": { + "webpack": "^5.00.0" + }, + "dependencies": { + "@colors/colors": "^1.6.0", + "browser-extension-manifest-fields": "*", + "chromium-location": "^1.2.1", + "content-security-policy-parser": "^0.6.0", + "loader-utils": "^3.3.1", + "prefers-yarn": "^1.0.1", + "progress": "^2.0.3", + "schema-utils": "^4.2.0", + "webpack-target-webextension": "^1.1.2", + "ws": "^8.17.1" + }, + "devDependencies": { + "@types/loader-utils": "^2.0.6", + "copy-webpack-plugin": "^12.0.2", + "eslint": "^8.56.0", + "eslint-config-extension-create": "*", + "html-webpack-plugin": "^5.6.0", + "tsconfig": "*", + "tsup": "^8.1.0", + "webpack": "~5.92.0", + "webpack-cli": "^5.1.4" + } +} diff --git a/programs/develop/webpack/plugin-browsers/browser-lib/add-progress-bar.ts b/programs/develop/webpack/plugin-browsers/browser-lib/add-progress-bar.ts new file mode 100644 index 00000000..bd6e6962 --- /dev/null +++ b/programs/develop/webpack/plugin-browsers/browser-lib/add-progress-bar.ts @@ -0,0 +1,22 @@ +// @ts-ignore +import ProgressBar from 'progress'; + +export function addProgressBar(text: string, completionCallback: () => void) { + const contentLength = 128 * 1024; + const bar = new ProgressBar(`${text} [:bar] :percent :etas`, { + complete: '=', + incomplete: ' ', + width: 50, + total: contentLength, + }); + + const timer = setInterval(() => { + const chunk = Math.random() * 10 * 1024; + bar.tick(chunk); + + if (bar.complete) { + clearInterval(timer); + completionCallback(); + } + }, 50); +} diff --git a/programs/develop/webpack/plugin-browsers/browser-lib/messages.ts b/programs/develop/webpack/plugin-browsers/browser-lib/messages.ts new file mode 100644 index 00000000..1d3c8294 --- /dev/null +++ b/programs/develop/webpack/plugin-browsers/browser-lib/messages.ts @@ -0,0 +1,29 @@ +import { bold, bgWhite, red } from '@colors/colors/safe'; + +export function watchModeClosed(browser: string, code: number, reason: Buffer) { + const message = reason.toString(); + return ( + `[😓] ${bgWhite(bold(` ${browser}-browser `))} ${red('✖︎✖︎✖︎')} Watch mode closed (code ${code}). ` + + `${message && '\n\nReason ' + message + '\n'}Exiting...\n` + ); +} + +export function browserNotFound(browser: string, binaryPath: string) { + const capitalize = (str: string) => + str.charAt(0).toUpperCase() + str.slice(1); + return `${bgWhite(bold(` ${browser}-browser `))} ${red('✖︎✖︎✖︎')} ${capitalize(browser)} not found at ${binaryPath}`; +} + +export function webSocketError(browser: string, error: any) { + error( + `[⛔️] ${bgWhite(bold(` ${browser}-browser `))} ${red('✖︎✖︎✖︎')} WebSocket error`, + error, + ); +} + +export function parseFileError(browser: string, error: any, filepath: string) { + return ( + `[⛔️] ${bgWhite(bold(` ${browser}-browser `))} ${red('✖︎✖︎✖︎')} ` + + `Error parsing file: ${filepath}. Reason: ${error.message}` + ); +} diff --git a/programs/develop/webpack/plugin-browsers/browser-lib/utils.ts b/programs/develop/webpack/plugin-browsers/browser-lib/utils.ts new file mode 100644 index 00000000..4740058d --- /dev/null +++ b/programs/develop/webpack/plugin-browsers/browser-lib/utils.ts @@ -0,0 +1,17 @@ +import { type Compilation } from 'webpack'; +import { type Manifest } from '../../types'; + +export function getManifestContent( + compilation: Compilation, + manifestPath: string, +): Manifest { + if ( + compilation.getAsset('manifest.json') || + compilation.assets['manifest.json'] + ) { + const manifest = compilation.assets['manifest.json'].source().toString(); + return JSON.parse(manifest || '{}'); + } + + return require(manifestPath); +} diff --git a/programs/develop/webpack/plugin-browsers/index.ts b/programs/develop/webpack/plugin-browsers/index.ts index 5ebaa85f..40aebf73 100644 --- a/programs/develop/webpack/plugin-browsers/index.ts +++ b/programs/develop/webpack/plugin-browsers/index.ts @@ -1,93 +1,93 @@ -// ██████╗ ███████╗██╗ ██╗███████╗██╗ ██████╗ ██████╗ -// ██╔══██╗██╔════╝██║ ██║██╔════╝██║ ██╔═══██╗██╔══██╗ -// ██║ ██║█████╗ ██║ ██║█████╗ ██║ ██║ ██║██████╔╝ -// ██║ ██║██╔══╝ ╚██╗ ██╔╝██╔══╝ ██║ ██║ ██║██╔═══╝ -// ██████╔╝███████╗ ╚████╔╝ ███████╗███████╗╚██████╔╝██║ -// ╚═════╝ ╚══════╝ ╚═══╝ ╚══════╝╚══════╝ ╚═════╝ ╚═╝ +import os from 'os'; +import path from 'path'; +import { type Compiler } from 'webpack'; +import { type PluginInterface } from './types'; +import { RunChromiumPlugin } from './run-chromium'; +// import { RunFirefoxPlugin } from './run-firefox'; -import path from 'path' -import os from 'os' -import type webpack from 'webpack' -import RunChromeExtension from 'webpack-run-chrome-extension' -import RunEdgeExtension from 'webpack-run-edge-extension' -import RunFirefoxAddon from 'webpack-run-firefox-addon' -import {type DevOptions} from '../../extensionDev' -import {getManifestPath, getOutputPath} from '../config/getPath' +/** + * RunChromiumPlugin works by creating a WebSockets server + * that listens to changes triggered by the user extension + * via webpack. When a change is detected, the server sends + * a message to an extension called reload-extension, which + * is injected into the browser. This extension is responsible + * for sending messages to the user extension. We do that by + * injecting a script into the background page that listens + * to messages from the reload-extension. + * + * Features supported: + * - Service worker - Full extension reload (chrome.runtime.reload) + * - manifest.json - Full extension reload (chrome.runtime.reload) + */ +export class BrowsersPlugin { + public readonly extension: string | string[]; + public readonly browser: string; + public readonly browserFlags: string[]; + public readonly userDataDir?: string; + public readonly profile: string; + public readonly preferences: string; + public readonly startingUrl: string; -function getProfilePath(devOptions: DevOptions) { - if (devOptions?.userDataDir) { - return devOptions.userDataDir + constructor(options: PluginInterface) { + this.extension = options.extension; + this.browser = options.browser || 'chrome'; + this.browserFlags = options.browserFlags || []; + this.userDataDir = options.userDataDir; + this.profile = options.profile || ''; + this.preferences = options.preferences || ''; + this.startingUrl = options.startingUrl || ''; } - // Ensure `start` runs in a fresh profile by default. - if (devOptions.mode === 'production') { - return path.join( - os.tmpdir(), - 'extension-js', - devOptions.browser!, - 'profile' - ) - } - - return devOptions?.userDataDir -} + private getProfilePath( + compiler: Compiler, + browser: string, + profile: string | undefined, + ) { + if (profile) { + return profile; + } -export default function browserPlugins( - projectPath: string, - devOptions: DevOptions -) { - if (!devOptions?.mode || process.env.NODE_ENV === 'test') { - return { - constructor: {name: 'BrowserPlugin'}, - apply: () => {} + // Ensure `start` runs in a fresh profile by default. + if (compiler.options.mode === 'production') { + return path.join(os.tmpdir(), 'extension-js', browser, 'profile'); } - } - const chromeConfig = { - port: 8000, - manifestPath: getManifestPath(projectPath), - // The final folder where the extension manifest file is located. - // This is used to load the extension into the browser. - extensionPath: getOutputPath(projectPath, devOptions.browser), - autoReload: true, - browserFlags: ['--enable-benchmarking'], - userDataDir: getProfilePath(devOptions), - stats: true + return path.resolve(__dirname, `run-${browser}-profile`); } - const edgeConfig = { - ...chromeConfig, - port: 8001, - stats: true - } + apply(compiler: Compiler) { + const config = { + stats: true, + extension: this.extension, + browser: this.browser, + browserFlags: this.browserFlags || [], + userDataDir: this.getProfilePath( + compiler, + this.browser || 'chrome', + this.userDataDir || this.profile, + ), + }; - const firefoxConfig = { - ...chromeConfig, - port: 8002, - // If all browsers are being used, we don't need to show the stats - // for each browser. This is because the stats will be the same for - // each browser. - // Note that a comma means that more than once browser is selected, - // so we show the user extension manifest output only once. - stats: true - } + // 1 - Bundle the reloader and manager extensions. The reloader extension + // is injected into the browser and is responsible for sending reload + // requests to the user extension. The manager extension is responsible + // for everything else, for now opening the chrome://extension page on startup. + // It starts a new browser instance with the user extension loaded. + switch (this.browser) { + case 'chrome': { + new RunChromiumPlugin({ ...config, browser: 'chrome' }).apply(compiler); + break; + } - return { - constructor: {name: 'BrowserPlugin'}, - apply: (compiler: webpack.Compiler) => { - switch (devOptions.browser) { - case 'chrome': - new RunChromeExtension(chromeConfig).apply(compiler) - break - case 'edge': - new RunEdgeExtension(edgeConfig).apply(compiler) - break - case 'firefox': - new RunFirefoxAddon(firefoxConfig).apply(compiler) - break - default: - new RunChromeExtension(chromeConfig).apply(compiler) - break + // case 'edge': + // new RunChromiumPlugin({ ...config, browser: 'edge' }).apply(compiler); + // break; + // case 'firefox': + // new RunFirefoxPlugin({...config, browser: 'firefox'}).apply(compiler); + // break; + default: { + new RunChromiumPlugin({ ...config, browser: 'chrome' }).apply(compiler); + break; } } } diff --git a/programs/develop/webpack/plugin-browsers/run-chromium/browser-config.ts b/programs/develop/webpack/plugin-browsers/run-chromium/browser-config.ts new file mode 100644 index 00000000..cd0125f9 --- /dev/null +++ b/programs/develop/webpack/plugin-browsers/run-chromium/browser-config.ts @@ -0,0 +1,99 @@ +import path from 'path'; +import fs from 'fs'; +import { type Compiler } from 'webpack'; +import { type PluginInterface } from '../types'; +import { createProfile } from './create-profile'; + +export function browserConfig( + compiler: Compiler, + configOptions: PluginInterface, +) { + const userBrowserExtension = + compiler.options.output.path || + path.join(process.cwd(), `dist/${configOptions.browser}`); + + if (!fs.existsSync(userBrowserExtension)) { + throw new Error( + `The ${configOptions.browser} extension is not found at ${userBrowserExtension}.`, + ); + } + + const extensionsToLoad = Array.isArray(configOptions.extension) + ? configOptions.extension + : [configOptions.extension]; + // Flags set by default: + // https://github.com/GoogleChrome/chrome-launcher/blob/master/src/flags.ts + // Added useful flags for tooling: + // Ref: https://github.com/GoogleChrome/chrome-launcher/blob/main/docs/chrome-flags-for-tools.md + return [ + `--load-extension=${extensionsToLoad.join()}`, + `--user-data-dir=${createProfile( + configOptions.browser || 'chrome', + configOptions.userDataDir || configOptions.profile, + )}`, + // Disable Chrome's native first run experience. + '--no-first-run', + // Disables client-side phishing detection + '--disable-client-side-phishing-detection', + // Disable some built-in extensions that aren't affected by '--disable-extensions' + '--disable-component-extensions-with-background-pages', + // Disable installation of default apps + '--disable-default-apps', + // Disables the Discover feed on NTP + '--disable-features=InterestFeedContentSuggestions', + // Disables Chrome translation, both the manual option and the popup prompt when a + // page with differing language is detected. + '--disable-features=Translate', + // Hide scrollbars from screenshots. + '--hide-scrollbars', + // Mute any audio + '--mute-audio', + // Disable the default browser check, do not prompt to set it as such + '--no-default-browser-check', + // Skip first run wizards + '--no-first-run', + // Avoids blue bubble "user education" nudges + // (eg., "… give your browser a new look", Memory Saver) + '--ash-no-nudges', + // Disable the 2023+ search engine choice screen + '--disable-search-engine-choice-screen', + // Avoid the startup dialog for + // `Do you want the application “Chromium.app” to accept incoming network connections?`. + // Also disables the Chrome Media Router which creates background networking activity + // to discover cast targets. + // A superset of disabling DialMediaRouteProvider. + '--disable-features=MediaRoute', + // Use mock keychain on Mac to prevent the blocking permissions dialog about + // "Chrome wants to use your confidential information stored in your keychain" + '--use-mock-keychain', + // Disable various background network services, including extension updating, + // safe browsing service, upgrade detector, translate, UMA + '--disable-background-networking', + // Disable crashdump collection (reporting is already disabled in Chromium) + '--disable-breakpad', + // Don't update the browser 'components' listed at chrome://components/ + '--disable-component-update', + // Disables Domain Reliability Monitoring, which tracks whether the browser + // has difficulty contacting Google-owned sites and uploads reports to Google. + '--disable-domain-reliability', + // Disables autofill server communication. This feature isn't disabled via other 'parent' flags. + '--disable-features=AutofillServerCommunicatio', + '--disable-features=CertificateTransparencyComponentUpdate', + // Disable syncing to a Google account + '--disable-sync', + // Used for turning on Breakpad crash reporting in a debug environment where crash + // reporting is typically compiled but disabled. + // Disable the Chrome Optimization Guide and networking with its service API + '--disable-features=OptimizationHints', + // A weaker form of disabling the MediaRouter feature. See that flag's details. + '--disable-features=DialMediaRouteProvider', + // Don't send hyperlink auditing pings + '--no-pings', + // Ensure the side panel is visible. This is used for testing the side panel feature. + '--enable-features=SidePanelUpdates', + + // Flags to pass to Chrome + // Any of http://peter.sh/experiments/chromium-command-line-switches/ + ...(configOptions.browserFlags || []), + ]; +} diff --git a/programs/develop/webpack/plugin-browsers/run-chromium/create-profile.ts b/programs/develop/webpack/plugin-browsers/run-chromium/create-profile.ts new file mode 100644 index 00000000..2bcdb2bd --- /dev/null +++ b/programs/develop/webpack/plugin-browsers/run-chromium/create-profile.ts @@ -0,0 +1,47 @@ +import path from 'path'; +import fs from 'fs'; +import { bold } from '@colors/colors/safe'; +import { addProgressBar } from '../browser-lib/add-progress-bar'; +import { + chromeMasterPreferences, + edgeMasterPreferences, +} from './master-preferences'; + +export function createProfile( + browser: string, + profilePath?: string, + silent?: boolean, +) { + if ( + profilePath || + fs.existsSync(path.resolve(__dirname, `run-${browser}-profile`)) + ) { + return profilePath || path.resolve(__dirname, `run-${browser}-profile`); + } + + const preferences = + browser === 'chrome' ? chromeMasterPreferences : edgeMasterPreferences; + + const userProfile = JSON.stringify(preferences); + const capitalBrowsername = browser.charAt(0).toUpperCase() + browser.slice(1); + + if (!silent) { + addProgressBar( + `👤 Creating ${bold(capitalBrowsername)} user data directory...`, + () => { + const profilePath = path.resolve(__dirname, `run-${browser}-profile`); + const preferences = path.join(profilePath, 'Default'); + + // Ensure directory exists + fs.mkdirSync(preferences, { recursive: true }); + + const preferencesPath = path.join(preferences, 'Preferences'); + + // Actually write the user preferences + fs.writeFileSync(preferencesPath, userProfile, 'utf8'); + }, + ); + } + + return path.resolve(__dirname, `run-${browser}-profile`); +} diff --git a/programs/develop/webpack/plugin-browsers/run-chromium/index.ts b/programs/develop/webpack/plugin-browsers/run-chromium/index.ts new file mode 100644 index 00000000..f98a118d --- /dev/null +++ b/programs/develop/webpack/plugin-browsers/run-chromium/index.ts @@ -0,0 +1,94 @@ +import fs from 'fs'; +import { type Compiler } from 'webpack'; +import { spawn } from 'child_process'; +import { bgWhite, bold, black, red, blue } from '@colors/colors/safe'; +import { browserConfig } from './browser-config'; +import { PluginInterface } from '../types'; + +process.on('SIGINT', () => { + process.exit(); +}); + +process.on('SIGTERM', () => { + process.exit(); +}); + +export class RunChromiumPlugin { + public readonly extension: string | string[]; + public readonly browser: string; + public readonly port?: number; + public readonly browserFlags?: string[]; + public readonly userDataDir?: string; + public readonly profile?: string; + public readonly preferences?: string; + public readonly startingUrl?: string; + public readonly autoReload?: boolean; + public readonly stats?: boolean; + + constructor(options: PluginInterface) { + this.extension = options.extension; + this.browser = options.browser || 'chrome'; + this.browserFlags = options.browserFlags || []; + this.userDataDir = options.userDataDir; + this.profile = options.profile; + this.preferences = options.preferences; + this.startingUrl = options.startingUrl; + } + + private browserNameCapitalized(browser: string) { + return browser.charAt(0).toUpperCase() + this.browser.slice(1); + } + + private launchChromium(compiler: Compiler, browser: string) { + const browserBinaryLocation: string = require(`${browser}-location`); + + if (!fs.existsSync(browserBinaryLocation) || '') { + console.error( + `${bgWhite(black(bold(` ${browser}-browser `)))} ${red('✖︎✖︎✖︎')} ` + + `{browser} browser ${ + typeof browserBinaryLocation === 'undefined' + ? 'is not installed.' + : `is not found at ${browserBinaryLocation}` + }. ` + + `Either install ${this.browserNameCapitalized(browser)} or choose a different browser via ${blue( + '--browser', + )}.`, + ); + process.exit(); + } + + const chromiumConfig = browserConfig(compiler, this); + const launchArgs = this.startingUrl + ? [this.startingUrl, ...chromiumConfig] + : [...chromiumConfig]; + + const stdio = + process.env.EXTENSION_ENV === 'development' ? 'inherit' : 'ignore'; + const child = spawn(browserBinaryLocation, launchArgs, { stdio }); + + if (process.env.EXTENSION_ENV === 'development') { + child.stdout?.pipe(process.stdout); + child.stderr?.pipe(process.stderr); + } + } + + apply(compiler: Compiler) { + let chromiumDidLaunch = false; + + compiler.hooks.done.tapAsync('run-chromium:module', (compilation, done) => { + if (compilation.hasErrors.length > 0) { + done(); + return; + } + + if (chromiumDidLaunch) { + done(); + return; + } + + this.launchChromium(compiler, this.browser); + chromiumDidLaunch = true; + done(); + }); + } +} diff --git a/programs/develop/webpack/plugin-browsers/run-chromium/master-preferences.ts b/programs/develop/webpack/plugin-browsers/run-chromium/master-preferences.ts new file mode 100644 index 00000000..df79472c --- /dev/null +++ b/programs/develop/webpack/plugin-browsers/run-chromium/master-preferences.ts @@ -0,0 +1,174 @@ +// PROFILE PREFS aka "Master Preferences" aka "User Preferences" +// * Official ref: https://www.chromium.org/administrators/configuring-other-preferences/ +// * Official Source code: https://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/pref_names.cc?view=markup +// * Extra ref (where this source code is adapted): https://serverfault.com/a/635203 +const masterPreferences = { + alternate_error_pages: { + enabled: false, + }, + autofill: { + enabled: false, + }, + browser: { + // Boolean that indicates whether we should check if we are the default browser + // on start-up. + check_default_browser: false, + + // Policy setting whether default browser check should be disabled and default + // browser registration should take place. + default_browser_setting_enabled: false, + }, + // A string property indicating whether default apps should be installed + // in this profile. Use the value "install" to enable defaults apps, or + // "noinstall" to disable them. This property is usually set in the + // master_preferences and copied into the profile preferences on first run. + // Defaults apps are installed only when creating a new profile. + default_apps: 'install', + distribution: { + // Boolean. Use alternate text for the shortcut. Cmd line override present. + alternate_shortcut_text: false, + + // Boolean. Whether to instruct the installer to auto-launch chrome on computer + // startup. The default (if not provided) is |false|. + auto_launch_chrome: false, + + // Boolean pref that disables all logging. + // disable_logging: true, + + // Boolean pref that triggers silent import of the default browser bookmarks. + import_bookmarks: false, + + // Boolean pref that triggers silent import of the default browser history. + import_history: false, + + // Boolean pref that triggers silent import of the default browser homepage. + import_home_page: false, + + // Boolean pref that triggers silent import of the default search engine. + import_search_engine: false, + + // Boolean. Do not show first run bubble, even if it would otherwise be shown. + suppress_first_run_bubble: true, + + // Boolean. Do not launch Chrome after first install. Cmd line override present. + // "do_not_launch_chrome": true, + + // Boolean. Do not register with Google Update to have Chrome launched after + // install. Cmd line override present. + do_not_register_for_update_launch: true, + + // Boolean. Register Chrome as default browser. Cmd line override present. + make_chrome_default: false, + + // Boolean. Register Chrome as default browser for the current user. + make_chrome_default_for_user: false, + + // Boolean. Expect to be run by an MSI installer. Cmd line override present. + // msi: true, + + // Boolean. Show EULA dialog before install. + require_eula: false, + + // Boolean. Indicates that the first-run 'set-as-default' dialog should not be + // shown. Relevant in Windows 8+ context only. If this is true, the standard + // 'set default browser' prompt on the butter-bar will appear during the first + // run. + suppress_first_run_default_browser_prompt: true, + }, + dns_prefetching: { + enabled: false, + }, + download: { + // The default directory to save downloaded files to. This is only used if + // the user does not select a directory for a download. It is recommended + // that you use this in conjunction with "prompt_for_download" to ensure + // that users are always prompted for a download location. + default_directory: '/tmp/', + + // Whether to prompt for download location for each file download. + directory_upgrade: true, + + // Whether downloaded PDFs should be opened in Adobe Acrobat Reader. + open_pdf_in_adobe_reader: false, + + // Whether to prompt for download location for each file download. + prompt_for_download: true, + }, + enable_do_not_track: true, + extensions: { + theme: { + use_system: false, + }, + toolbarsize: -1, + ui: { + developer_mode: true, + }, + }, + plugins: { + plugins_list: [ + { + enabled: false, + name: 'Java(TM)', + }, + ], + show_details: true, + }, + profile: { + password_manager_enabled: false, + }, + safebrowsing: { + // Boolean that is true when SafeBrowsing is enabled. + enabled: false, + + // Boolean that tell us whether malicious download feedback is enabled. + safebrowsingextended_reporting_enabled: false, + }, + savefile: { + default_directory: '/tmp', + + // Whether to prompt for download location for each file download. + type: 0, + }, + search: { + // Boolean that is true when Suggest support is enabled. + suggest_enabled: false, + }, + + session: { + // An integer pref. Holds one of several values: + // 0: (deprecated) open the homepage on startup. + // 1: restore the last session. + // 2: this was used to indicate a specific session should be restored. It is + // no longer used, but saved to avoid conflict with old preferences. + // 3: unused, previously indicated the user wants to restore a saved session. + // 4: restore the URLs defined in kURLsToRestoreOnStartup. + // 5: open the New Tab Page on startup. + restore_on_startup: 1, + }, + sync: { + suppress_start: true, + }, + sync_promo: { + // Boolean that specifies if the sign in promo is allowed to show on first run. + // This preference is specified in the master preference file to suppress the + // sign in promo for some installations. + show_on_first_run_allowed: false, + + // Boolean that specifies if we should show a bubble in the new tab page. + // The bubble is used to confirm that the user is signed into sync. + show_ntp_bubble: false, + }, + translate: { + enabled: false, + }, +}; + +const chromeMasterPreferences = { + ...masterPreferences, +}; + +const edgeMasterPreferences = { + ...masterPreferences, +}; + +export { chromeMasterPreferences, edgeMasterPreferences }; diff --git a/programs/develop/webpack/plugin-browsers/types.ts b/programs/develop/webpack/plugin-browsers/types.ts new file mode 100644 index 00000000..e6d5a732 --- /dev/null +++ b/programs/develop/webpack/plugin-browsers/types.ts @@ -0,0 +1,12 @@ +export interface PluginInterface extends PluginOptions { + browser?: string; + extension: string | string[]; +} + +export interface PluginOptions { + browserFlags?: string[]; + userDataDir?: string; + profile?: string; + preferences?: string; + startingUrl?: string; +} diff --git a/programs/develop/webpack/webpack-config.ts b/programs/develop/webpack/webpack-config.ts index 2039cd9f..ac653552 100644 --- a/programs/develop/webpack/webpack-config.ts +++ b/programs/develop/webpack/webpack-config.ts @@ -31,7 +31,7 @@ import extensionPlugin from './plugin-extension/extensionPlugins' import {ReloadPlugin} from './plugin-reload' import compatPlugin from './plugin-compat' import errorPlugin from './plugin-errors' -import browserPlugin from './plugin-browsers' +import {BrowsersPlugin} from './plugin-browsers' // Checks import getDevToolOption from './config/getDevtoolOption' @@ -119,7 +119,19 @@ export default function webpackConfig( // port: opts.port, stats: true }), - // browserPlugin(projectPath, devOptions), + new BrowsersPlugin({ + browser: devOptions.browser || 'chrome', + extension: [ + getOutputPath(projectPath, devOptions.browser), + // Output by the reload plugin + path.join(__dirname, 'extensions', 'manager-extension'), + path.join(__dirname, 'extensions', 'reload-extension') + ] + // profile: devOptions.profile || devOptions.userDataDir, + // preferences: devOptions.preferences, + // startingUrl: devOptions.startingUrl, + // browserFlags: devOptions.browserFlags + }), compatPlugin(projectPath, devOptions), errorPlugin(projectPath, devOptions), boringPlugins(projectPath, devOptions)