mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-09 09:09:58 +00:00
Replace webpack with esbuild
This commit is contained in:
@@ -16,7 +16,8 @@ const paths = require("./lib/paths");
|
||||
const { author
|
||||
, homepage } = require("../../package.json");
|
||||
|
||||
const { __extensionId: extensionId } = require("../../ext/package.json");
|
||||
|
||||
const EXTENSION_ID = "fx_cast@matt.tf";
|
||||
|
||||
|
||||
// Command line args
|
||||
@@ -98,7 +99,7 @@ async function build () {
|
||||
"name": meta.__applicationName
|
||||
, "description": ""
|
||||
, "type": "stdio"
|
||||
, "allowed_extensions": [ extensionId ]
|
||||
, "allowed_extensions": [ EXTENSION_ID ]
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
231
ext/bin/build.js
231
ext/bin/build.js
@@ -1,29 +1,26 @@
|
||||
"use strict";
|
||||
|
||||
const esbuild = require("esbuild");
|
||||
const fs = require("fs-extra");
|
||||
const path = require("path");
|
||||
const minimist = require("minimist");
|
||||
const webpack = require("webpack");
|
||||
const webExt = require("web-ext");
|
||||
|
||||
const { ROOT
|
||||
, INCLUDE_PATH
|
||||
, DIST_PATH
|
||||
, UNPACKED_PATH } = require("./lib/paths");
|
||||
|
||||
const packageMeta = require(`${ROOT}/../package.json`);
|
||||
const extPackageMeta = require(`${ROOT}/package.json`);
|
||||
const appPackageMeta = require(`${ROOT}/../app/package.json`);
|
||||
const BRIDGE_NAME = "fx_cast_bridge";
|
||||
const BRIDGE_VERSION = "0.1.0";
|
||||
|
||||
const MIRRORING_APP_ID = "19A6F4AE";
|
||||
|
||||
|
||||
const argv = minimist(process.argv.slice(2), {
|
||||
boolean: [ "package", "watch" ]
|
||||
, string: [ "mirroringAppId", "mode" ]
|
||||
, default: {
|
||||
package: false // Should package with web-ext
|
||||
, watch: false // Should run webpack in watch mode
|
||||
, mirroringAppId: extPackageMeta.__mirroringAppId // Chromecast receiver app ID
|
||||
, mode: "development" // webpack mode
|
||||
package: false
|
||||
, watch: false
|
||||
, mirroringAppId: MIRRORING_APP_ID
|
||||
, mode: "development"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -38,107 +35,161 @@ if (argv.package) {
|
||||
}
|
||||
|
||||
|
||||
// Import webpack config and specify env values
|
||||
const webpackConfig = require(`${ROOT}/webpack.config.js`)({
|
||||
includePath: INCLUDE_PATH
|
||||
// Paths
|
||||
const rootPath = path.resolve(__dirname, "../");
|
||||
const srcPath = path.join(rootPath, "src");
|
||||
|
||||
const distPath = path.join(rootPath, "../dist/ext/");
|
||||
const unpackedPath = path.join(distPath, "unpacked");
|
||||
|
||||
const outPath = argv.package ? unpackedPath : distPath;
|
||||
|
||||
/** @type esbuild.Plugin */
|
||||
const preactCompatPlugin = {
|
||||
/**
|
||||
* If watching files, output directly to dist. Unpacked
|
||||
* directory is used as a staging area for web-ext builds.
|
||||
* Handle react/react-dom preact compat modules.
|
||||
*/
|
||||
, outputPath: argv.package
|
||||
? UNPACKED_PATH
|
||||
: DIST_PATH
|
||||
name: "preact-compat",
|
||||
setup(build) {
|
||||
const preactPath = path.resolve(__dirname
|
||||
, "../node_modules/preact/compat/dist/compat.module.js");
|
||||
|
||||
, extensionName: extPackageMeta.__extensionName
|
||||
, extensionId: extPackageMeta.__extensionId
|
||||
, extensionVersion: extPackageMeta.__extensionVersion
|
||||
, applicationName: appPackageMeta.__applicationName
|
||||
, applicationVersion: appPackageMeta.__applicationVersion
|
||||
, mirroringAppId: argv.mirroringAppId
|
||||
|
||||
// eval source map needs special CSP
|
||||
, contentSecurityPolicy: argv.mode === "production"
|
||||
? "script-src 'self'; object-src 'self'"
|
||||
: "script-src 'self' 'unsafe-eval'; object-src 'self'"
|
||||
|
||||
// Developer info
|
||||
, author: packageMeta.author
|
||||
, authorHomepage: packageMeta.homepage
|
||||
});
|
||||
|
||||
// Add mode to config
|
||||
webpackConfig.mode = argv.mode;
|
||||
if (argv.mode !== "production") {
|
||||
webpackConfig.devtool = "source-map";
|
||||
build.onResolve(
|
||||
{ filter: /^(react|react-dom)$/ }
|
||||
, (args) => ({ path: preactPath }));
|
||||
}
|
||||
}
|
||||
|
||||
/** @type esbuild.BuildOptions */
|
||||
const buildOpts = {
|
||||
bundle: true
|
||||
, target: "firefox64"
|
||||
, logLevel: "info"
|
||||
, sourcemap: "inline"
|
||||
|
||||
, outdir: outPath
|
||||
, outbase: srcPath
|
||||
|
||||
, entryPoints: [
|
||||
// Main
|
||||
`${srcPath}/background/background.ts`
|
||||
// Media sender
|
||||
, `${srcPath}/senders/media/index.ts`
|
||||
, `${srcPath}/senders/media/overlay/overlayContent.ts`
|
||||
, `${srcPath}/senders/media/overlay/overlayContentLoader.ts`
|
||||
// Mirroring sender
|
||||
, `${srcPath}/senders/mirroring.ts`
|
||||
// Shim
|
||||
, `${srcPath}/shim/index.ts`
|
||||
, `${srcPath}/shim/content.ts`
|
||||
, `${srcPath}/shim/contentBridge.ts`
|
||||
// UI
|
||||
, `${srcPath}/ui/popup/index.tsx`
|
||||
, `${srcPath}/ui/options/index.tsx`
|
||||
]
|
||||
, define: {
|
||||
BRIDGE_NAME: `"${BRIDGE_NAME}"`
|
||||
, BRIDGE_VERSION: `"${BRIDGE_VERSION}"`
|
||||
, MIRRORING_APP_ID: `"${argv.mirroringAppId}"`
|
||||
}
|
||||
, plugins: [ preactCompatPlugin ]
|
||||
};
|
||||
|
||||
// Set production options
|
||||
if (argv.mode === "production") {
|
||||
buildOpts.minify = true;
|
||||
buildOpts.sourcemap = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle build results.
|
||||
*
|
||||
* @param {esbuild.BuildResult} result
|
||||
*/
|
||||
function onBuildResult(result) {
|
||||
if (result.errors.length) {
|
||||
console.error("Build error!");
|
||||
return;
|
||||
}
|
||||
|
||||
const manifest = JSON.parse(
|
||||
fs.readFileSync(`${srcPath}/manifest.json`
|
||||
, { encoding: "utf-8" }));
|
||||
|
||||
manifest.content_security_policy = argv.mode === "production"
|
||||
? "script-src 'self'; object-src 'self'"
|
||||
: "script-src 'self' 'unsafe-eval'; object-src 'self'";
|
||||
|
||||
fs.writeFileSync(`${outPath}/manifest.json`, JSON.stringify(manifest));
|
||||
|
||||
copy(srcPath, outPath, /^(manifest\.json|.*\.(ts|tsx|js|jsx))$/);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively copy directory contents.
|
||||
*
|
||||
* @param {string} src Source path
|
||||
* @param {string} dest Destination path
|
||||
* @param {RegExp} excludeRegex Match for file exclusion
|
||||
*/
|
||||
function copy(src, dest, excludeRegex) {
|
||||
if (!fs.existsSync(src)) return;
|
||||
|
||||
const stats = fs.statSync(src);
|
||||
if (!stats.isDirectory()) {
|
||||
const dirName = path.dirname(dest);
|
||||
if (!fs.existsSync(dirName)) {
|
||||
fs.mkdirSync(dirName , { recursive: true });
|
||||
}
|
||||
fs.copyFileSync(src, dest);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const file of fs.readdirSync(src)) {
|
||||
if (excludeRegex.test(file)) continue;
|
||||
copy(path.join(src, file), path.join(dest, file), excludeRegex);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean
|
||||
fs.removeSync(DIST_PATH);
|
||||
|
||||
|
||||
// Create webpack compiler instance
|
||||
const compiler = webpack(webpackConfig);
|
||||
fs.removeSync(distPath);
|
||||
|
||||
if (argv.watch) {
|
||||
// Start webpack watch
|
||||
compiler.watch({}, handleCompilerOutput);
|
||||
esbuild.build({
|
||||
...buildOpts
|
||||
, watch: {
|
||||
onRebuild(_err, result) {
|
||||
return onBuildResult(result);
|
||||
}
|
||||
}
|
||||
}).then(onBuildResult);
|
||||
} else {
|
||||
compiler.run((...args) => {
|
||||
handleCompilerOutput(...args);
|
||||
esbuild.build(buildOpts).then(result => {
|
||||
onBuildResult(result);
|
||||
|
||||
if (argv.package) {
|
||||
webExt.cmd.build({
|
||||
/**
|
||||
* Webpack output at sourceDir is built into an extension
|
||||
* archive at artifactsDir.
|
||||
*/
|
||||
sourceDir: UNPACKED_PATH
|
||||
, artifactsDir: DIST_PATH
|
||||
* Webpack output at sourceDir is built into an extension
|
||||
* archive at artifactsDir.
|
||||
*/
|
||||
sourceDir: unpackedPath
|
||||
, artifactsDir: distPath
|
||||
, overwriteDest: true
|
||||
}, {
|
||||
// Prevent auto-exit
|
||||
shouldExitProgram: false
|
||||
|
||||
}).then(result => {
|
||||
const outputName = path.basename(result.extensionPath);
|
||||
|
||||
|
||||
// Rename output extension to XPI
|
||||
fs.moveSync(path.join(DIST_PATH, outputName)
|
||||
, path.join(DIST_PATH, outputName.replace("zip", "xpi")));
|
||||
|
||||
fs.moveSync(path.join(distPath, outputName)
|
||||
, path.join(distPath, outputName.replace(
|
||||
"zip", "xpi")));
|
||||
|
||||
// Only need the built extension archive
|
||||
fs.remove(UNPACKED_PATH);
|
||||
fs.remove(unpackedPath);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Log errors and output formatted compilation info.
|
||||
*/
|
||||
function handleCompilerOutput (err, stats) {
|
||||
// If there are fatal errors, log and exit
|
||||
if (err) {
|
||||
console.error(err.stack || err);
|
||||
if (err.details) {
|
||||
console.error(err.details);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Get compilation info
|
||||
const info = stats.toJson();
|
||||
|
||||
// Log errors/warnings
|
||||
if (stats.hasErrors()) {
|
||||
console.error(info.errors);
|
||||
}
|
||||
if (stats.hasWarnings()) {
|
||||
console.warn(info.warnings);
|
||||
}
|
||||
|
||||
// Log formatted output
|
||||
console.log(stats.toString());
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const path = require("path");
|
||||
|
||||
exports.ROOT = path.resolve(__dirname, "../../");
|
||||
exports.INCLUDE_PATH = path.resolve(exports.ROOT, "src");
|
||||
exports.DIST_PATH = path.join(exports.ROOT, "../dist/ext");
|
||||
exports.UNPACKED_PATH = path.join(exports.DIST_PATH, "unpacked");
|
||||
8618
ext/package-lock.json
generated
8618
ext/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"__extensionName": "fx_cast",
|
||||
"__extensionId": "fx_cast@matt.tf",
|
||||
"__extensionVersion": "0.1.2",
|
||||
"__mirroringAppId": "19A6F4AE",
|
||||
"scripts": {
|
||||
"build": "node bin/build.js",
|
||||
"package": "node bin/build.js --package",
|
||||
@@ -12,14 +8,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/firefox-webext-browser": "^82.0.0",
|
||||
"@types/react": "^17.0.2",
|
||||
"@types/react-dom": "^17.0.1",
|
||||
"@types/react": "^17.0.7",
|
||||
"@types/react-dom": "^17.0.5",
|
||||
"@types/semver": "^7.3.4",
|
||||
"@types/uuid": "^8.3.0",
|
||||
"copy-webpack-plugin": "^7.0.0",
|
||||
"esbuild": "^0.12.3",
|
||||
"html-webpack-plugin": "^5.2.0",
|
||||
"preact": "^10.5.12",
|
||||
"preact-compat": "^3.19.0",
|
||||
"preact": "^10.5.13",
|
||||
"semver": "^7.3.4",
|
||||
"ts-loader": "^8.0.17",
|
||||
"typescript": "^4.1.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"extensionName": {
|
||||
"message": "EXTENSION_NAME"
|
||||
"message": "fx_cast"
|
||||
, "description": "Name of the extension and the native receiver selector window title."
|
||||
}
|
||||
, "extensionDescription": {
|
||||
|
||||
@@ -144,7 +144,7 @@ browser.menus.onClicked.addListener(async (info, tab) => {
|
||||
});
|
||||
|
||||
await browser.tabs.executeScript(tab.id, {
|
||||
file: "senders/media/bundle.js"
|
||||
file: "senders/media/index.js"
|
||||
, frameId: info.frameId
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -178,7 +178,7 @@ async function onBeforeCastSDKRequest(details: OnBeforeRequestDetails) {
|
||||
});
|
||||
|
||||
return {
|
||||
redirectUrl: browser.runtime.getURL("shim/bundle.js")
|
||||
redirectUrl: browser.runtime.getURL("shim/index.js")
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Options } from "./lib/options";
|
||||
|
||||
|
||||
export default {
|
||||
bridgeApplicationName: APPLICATION_NAME
|
||||
bridgeApplicationName: BRIDGE_NAME
|
||||
, bridgeBackupEnabled: false
|
||||
, bridgeBackupHost: "localhost"
|
||||
, bridgeBackupPort: 9556
|
||||
|
||||
6
ext/src/global.d.ts
vendored
6
ext/src/global.d.ts
vendored
@@ -1,8 +1,6 @@
|
||||
// Define replacement types
|
||||
declare const BRIDGE_VERSION: string;
|
||||
declare const BRIDGE_NAME: string;
|
||||
declare const MIRRORING_APP_ID: string;
|
||||
declare const APPLICATION_NAME: string;
|
||||
declare const APPLICATION_VERSION: string;
|
||||
|
||||
|
||||
declare type Nullable<T> = T | null;
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ const getInfo = () => new Promise<BridgeInfo>(async (resolve, reject) => {
|
||||
|
||||
// Print compatibility info to console
|
||||
if (!isVersionCompatible) {
|
||||
logger.error(`Expecting ${applicationName} v${APPLICATION_VERSION}, found v${applicationVersion}. ${
|
||||
logger.error(`Expecting ${applicationName} v${BRIDGE_VERSION}, found v${applicationVersion}. ${
|
||||
isVersionOlder
|
||||
? "Try updating the native app to the latest version."
|
||||
: "Try updating the extension to the latest version"}`);
|
||||
@@ -110,7 +110,7 @@ const getInfo = () => new Promise<BridgeInfo>(async (resolve, reject) => {
|
||||
resolve({
|
||||
name: applicationName
|
||||
, version: applicationVersion
|
||||
, expectedVersion: APPLICATION_VERSION
|
||||
, expectedVersion: BRIDGE_VERSION
|
||||
|
||||
// Version info
|
||||
, isVersionExact
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "__MSG_extensionName__"
|
||||
, "description": "__MSG_extensionDescription__"
|
||||
, "version": "EXTENSION_VERSION"
|
||||
, "version": "0.1.2"
|
||||
, "developer": {
|
||||
"name": "AUTHOR"
|
||||
, "url": "AUTHOR_HOMEPAGE"
|
||||
"name": "Matt Hensman"
|
||||
, "url": "https://matt.tf/"
|
||||
}
|
||||
|
||||
, "applications": {
|
||||
"gecko": {
|
||||
"id": "EXTENSION_ID"
|
||||
"id": "fx_cast@matt.tf"
|
||||
, "strict_min_version": "64.0"
|
||||
, "update_url": "https://hensm.github.io/fx_cast/updates.json"
|
||||
}
|
||||
@@ -25,7 +25,7 @@
|
||||
}
|
||||
|
||||
, "background": {
|
||||
"scripts": [ "background.js" ]
|
||||
"scripts": [ "background/background.js" ]
|
||||
}
|
||||
|
||||
, "content_scripts": [
|
||||
@@ -39,7 +39,6 @@
|
||||
}
|
||||
]
|
||||
|
||||
, "content_security_policy": "CONTENT_SECURITY_POLICY"
|
||||
, "default_locale": "en"
|
||||
|
||||
, "icons": {
|
||||
@@ -66,7 +65,7 @@
|
||||
, "<all_urls>"
|
||||
]
|
||||
, "web_accessible_resources": [
|
||||
"shim/bundle.js"
|
||||
"shim/index.js"
|
||||
, "senders/media/overlay/overlayContent.js"
|
||||
, "senders/media/overlay/AirPlay_Audio.svg"
|
||||
, "senders/media/overlay/AirPlay_Video.svg"
|
||||
|
||||
@@ -133,8 +133,8 @@ class OptionsApp extends Component<
|
||||
return (
|
||||
<div>
|
||||
<form id="form" ref={ form => { this.form = form; }}
|
||||
onSubmit={ this.handleFormSubmit }
|
||||
onChange={ this.handleFormChange }>
|
||||
onSubmit={ this.handleFormSubmit }
|
||||
onChange={ this.handleFormChange }>
|
||||
|
||||
<Bridge info={ this.state.bridgeInfo }
|
||||
loading={ this.state.bridgeLoading }
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<link rel="stylesheet" href="../photon-colors.css">
|
||||
<link rel="stylesheet" href="../photon-widgets.css">
|
||||
<link rel="stylesheet" href="styles/index.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,116 +0,0 @@
|
||||
"use strict";
|
||||
|
||||
const path = require("path");
|
||||
|
||||
const webpack = require("webpack");
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
const HtmlWebpackPlugin = require("html-webpack-plugin");
|
||||
|
||||
|
||||
const sourceFileExtensions = [
|
||||
".js", ".jsx"
|
||||
, ".ts", ".tsx"
|
||||
];
|
||||
|
||||
module.exports = (env) => ({
|
||||
entry: {
|
||||
"background": `${env.includePath}/background/background.ts`
|
||||
|
||||
// Sender apps
|
||||
, "senders/media/bundle": `${env.includePath}/senders/media/index.ts`
|
||||
, "senders/media/overlay/overlayContent": `${env.includePath}/senders/media/overlay/overlayContent.ts`
|
||||
, "senders/media/overlay/overlayContentLoader": `${env.includePath}/senders/media/overlay/overlayContentLoader.ts`
|
||||
, "senders/mirroring": `${env.includePath}/senders/mirroring.ts`
|
||||
|
||||
// Shim entries
|
||||
, "shim/bundle": `${env.includePath}/shim/index.ts`
|
||||
, "shim/content": `${env.includePath}/shim/content.ts`
|
||||
, "shim/contentBridge": `${env.includePath}/shim/contentBridge.ts`
|
||||
|
||||
// UI
|
||||
, "ui/popup/bundle": `${env.includePath}/ui/popup/index.tsx`
|
||||
, "ui/options/bundle": `${env.includePath}/ui/options/index.tsx`
|
||||
}
|
||||
, output: {
|
||||
filename: "[name].js"
|
||||
, path: env.outputPath
|
||||
}
|
||||
, plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
"MIRRORING_APP_ID": JSON.stringify(env.mirroringAppId)
|
||||
, "APPLICATION_NAME": JSON.stringify(env.applicationName)
|
||||
, "APPLICATION_VERSION": JSON.stringify(env.applicationVersion)
|
||||
})
|
||||
|
||||
// Copy static assets
|
||||
, new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: env.includePath
|
||||
, to: env.outputPath
|
||||
, globOptions: {
|
||||
ignore: sourceFileExtensions.map(ext => `**${ext}`)
|
||||
}
|
||||
, transform (content, path) {
|
||||
// Access to variables in static files
|
||||
if (path.endsWith(".json")) {
|
||||
return Buffer.from(content.toString()
|
||||
.replace("EXTENSION_NAME", env.extensionName)
|
||||
.replace("EXTENSION_ID", env.extensionId)
|
||||
.replace("EXTENSION_VERSION", env.extensionVersion)
|
||||
.replace("CONTENT_SECURITY_POLICY", env.contentSecurityPolicy)
|
||||
.replace("AUTHOR", env.author)
|
||||
.replace("AUTHOR_HOMEPAGE", env.authorHomepage));
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
, new HtmlWebpackPlugin({
|
||||
inject: true
|
||||
, template: `${env.includePath}/ui/template.html`
|
||||
, filename: `${env.outputPath}/ui/popup/index.html`
|
||||
, chunks: [ "ui/popup/bundle" ]
|
||||
})
|
||||
, new HtmlWebpackPlugin({
|
||||
inject: true
|
||||
, template: `${env.includePath}/ui/template.html`
|
||||
, filename: `${env.outputPath}/ui/options/index.html`
|
||||
, chunks: [ "ui/options/bundle" ]
|
||||
})
|
||||
]
|
||||
, optimization: {
|
||||
splitChunks: {
|
||||
cacheGroups: {
|
||||
vendor: {
|
||||
test: /[\\/]node_modules[\\/](preact|preact\/compat)[\\/]/
|
||||
, name: "vendor"
|
||||
, chunks: "initial"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
, module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.(js|ts)x?$/
|
||||
, resolve: {
|
||||
extensions: sourceFileExtensions
|
||||
}
|
||||
, include: env.includePath
|
||||
, use: {
|
||||
loader: "ts-loader"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
, resolve: {
|
||||
alias: {
|
||||
"react": "preact/compat"
|
||||
, "react-dom": "preact/compat"
|
||||
}
|
||||
}
|
||||
});
|
||||
2045
package-lock.json
generated
2045
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user