Add esbuild plugin for static file copying with file watching

This commit is contained in:
hensm
2022-05-15 07:29:31 +01:00
parent ff2bdef5c6
commit 2629be4a01
2 changed files with 147 additions and 38 deletions

View File

@@ -1,11 +1,15 @@
// @ts-check
"use strict";
const esbuild = require("esbuild");
const fs = require("fs-extra");
const path = require("path");
const esbuild = require("esbuild");
const minimist = require("minimist");
const webExt = require("web-ext");
const { copyFilesPlugin } = require("./lib/copyFilesPlugin.js");
const BRIDGE_NAME = "fx_cast_bridge";
const BRIDGE_VERSION = "0.2.0";
@@ -71,25 +75,34 @@ const buildOpts = {
entryPoints: [
// Main
`${srcPath}/background/background.ts`,
path.join(srcPath, "background/background.ts"),
// Cast
`${srcPath}/cast/index.ts`,
`${srcPath}/cast/content.ts`,
`${srcPath}/cast/contentBridge.ts`,
path.join(srcPath, "cast/index.ts"),
path.join(srcPath, "cast/content.ts"),
path.join(srcPath, "cast/contentBridge.ts"),
// Media sender
`${srcPath}/cast/senders/media/index.ts`,
path.join(srcPath, "cast/senders/media/index.ts"),
// Mirroring sender
`${srcPath}/cast/senders/mirroring.ts`,
path.join(srcPath, "/cast/senders/mirroring.ts"),
// UI
`${srcPath}/ui/popup/index.tsx`,
`${srcPath}/ui/options/index.tsx`
path.join(srcPath, "ui/popup/index.tsx"),
path.join(srcPath, "ui/options/index.tsx")
],
define: {
BRIDGE_NAME: `"${BRIDGE_NAME}"`,
BRIDGE_VERSION: `"${BRIDGE_VERSION}"`,
MIRRORING_APP_ID: `"${argv.mirroringAppId}"`
},
plugins: [preactCompatPlugin]
plugins: [
preactCompatPlugin,
// Copy static files
copyFilesPlugin({
src: srcPath,
dest: outPath,
excludePattern: /^(manifest\.json|.*\.(ts|tsx|js|jsx))$/
})
]
};
// Set production options
@@ -119,34 +132,6 @@ function onBuildResult(result) {
: "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

View File

@@ -0,0 +1,124 @@
// @ts-check
"use strict";
const path = require("path");
const fs = require("fs");
// eslint-disable-next-line no-unused-vars
const esbuild = require("esbuild");
/**
* Escape meta characters in a regular expression.
*
* @param {string} patternSource
* @returns {string} Escaped expression source
*/
function escapeRegExp(patternSource) {
let metaChars = ".+*?()|[]{}^$\\";
return [...patternSource]
.map(c => (metaChars.includes(c) ? `\\${c}` : c))
.join("");
}
/**
* @typedef {object} CopyFilesPluginOpts
* @prop {string} src Source path
* @prop {string} dest Destination path
* @prop {RegExp=} excludePattern Exclude path pattern
*/
/**
* Plugin that copies files from specified source to destination after
* each build.
*
* @type {(opts: CopyFilesPluginOpts) => esbuild.Plugin}
*/
exports.copyFilesPlugin = opts => {
// Get matching file paths
const matchingFiles = (function getMatchingPaths(relPath = "") {
const fullPath = path.join(opts.src, relPath);
// Must exist
if (!fs.existsSync(fullPath)) return;
// Must not match exclude pattern
if (opts.excludePattern?.test(fullPath)) return;
if (fs.statSync(fullPath).isFile()) {
return [relPath];
}
/** @type string[] */
let files = [];
for (const entry of fs.readdirSync(fullPath)) {
const matchingFiles = getMatchingPaths(path.join(relPath, entry));
if (matchingFiles) {
files = files.concat(matchingFiles);
}
}
return files;
})();
return {
name: "copy-files",
setup(build) {
/** First run for the set of import paths in each build. */
let isFirstRun = true;
build.onResolve(
{
filter: new RegExp(`^${escapeRegExp(opts.src + path.sep)}`)
},
() => {
/**
* Attach watch files to first resolve result.
* Presumably there is a much better way of doing
* this?
*/
if (isFirstRun) {
isFirstRun = false;
return {
watchFiles: matchingFiles.map(file =>
path.join(opts.src, file)
)
};
}
}
);
build.onEnd(() => {
isFirstRun = true;
// Copy any watched files that changed
for (const file of matchingFiles) {
const srcPath = path.join(opts.src, file);
const destPath = path.join(opts.dest, file);
// Ignore if source file is missing
if (!fs.existsSync(srcPath)) {
if (fs.existsSync(destPath)) {
fs.rmSync(destPath);
}
continue;
}
// Ensure containing destination directory exists
const dirName = path.dirname(destPath);
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName, { recursive: true });
}
// Check if files match
if (fs.existsSync(destPath)) {
const srcContent = fs.readFileSync(srcPath);
const destContent = fs.readFileSync(destPath);
if (srcContent.equals(destContent)) {
continue;
}
}
fs.copyFileSync(srcPath, destPath);
}
});
}
};
};