From 51763011162f4a99479bf6da6ca1f671792f35af Mon Sep 17 00:00:00 2001 From: Ruben Carlo Benante Date: Mon, 10 Aug 2026 16:14:56 -0300 Subject: [PATCH] chrome+firefox v0.3.1 Makefile and dist/ build/ folders --- Makefile | 92 ++++++++++++++++++++++++----------- README.md | 32 +++++++----- VERSION | 2 +- dist/logsdu-0.3.0-chrome.zip | Bin 23060 -> 0 bytes dist/logsdu-0.3.0.xpi | Bin 23141 -> 0 bytes esbuild.config.mjs | 14 ++++-- package.json | 2 +- src/manifest.json | 2 +- tools/sw-smoke.mjs | 5 +- 9 files changed, 100 insertions(+), 49 deletions(-) delete mode 100644 dist/logsdu-0.3.0-chrome.zip delete mode 100644 dist/logsdu-0.3.0.xpi diff --git a/Makefile b/Makefile index 4479078..8a8b030 100644 --- a/Makefile +++ b/Makefile @@ -22,16 +22,19 @@ # Makefile for logsdu - build the extension for Firefox and Chrome. # # Usage: -# make # typecheck and bundle for Firefox into build/ -# make chrome # same, but with Chrome's manifest, into build/ +# make # everything: build and package both browsers +# make firefox # build Firefox only -> build/firefox/ +# make chrome # build Chrome only -> build/chrome/ # make test # run the unit tests # make smoke # check the background bundle works as a service worker -# make xpi # package the Firefox build -> dist/logsdu-.xpi -# make crx # package the Chrome build -> dist/logsdu--chrome.zip -# make packages # both of the above -# make clean # remove build/ and dist/ +# make xpi # package Firefox -> dist/logsdu--firefox.xpi +# make crx # package Chrome -> dist/logsdu--chrome.zip +# make packages # both of the above (same as plain "make") +# make clean # remove build/, dist/ and stray packages +# make distclean # clean, plus node_modules/ # -# build/ holds the unpacked extension and is regenerated from scratch; dist/ +# build// holds the unpacked extension for one browser; the two targets +# never share a directory, so neither can be left stale by the other. dist/ # holds the packages meant to be published, and is kept out of build/ so that # packaging never tries to include its own output. # @@ -40,22 +43,33 @@ # # Load the unpacked build while developing: # Firefox about:debugging -> This Firefox -> Load Temporary Add-on -> -# build/manifest.json (dropped when Firefox restarts) -# Chrome chrome://extensions -> Developer mode -> Load unpacked -> build/ +# build/firefox/manifest.json (dropped when Firefox restarts) +# Chrome chrome://extensions -> Developer mode -> Load unpacked -> +# build/chrome/ # # Publishing: -# Firefox upload dist/*.xpi at addons.mozilla.org -# Chrome upload dist/*-chrome.zip at chrome.google.com/webstore/devconsole +# Firefox upload dist/*-firefox.xpi at addons.mozilla.org +# Chrome upload dist/*-chrome.zip at chrome.google.com/webstore/devconsole +# +# Every package filename names its browser. The two are not interchangeable -- +# they differ in the manifest's background key -- and uploading the wrong one +# fails in ways that are not obvious from the error. EXT_ID := logsdu VERSION := $(shell node -p "require('./package.json').version") +FIREFOX_DIR := build/firefox +CHROME_DIR := build/chrome DIST := dist -XPI := $(DIST)/$(EXT_ID)-$(VERSION).xpi +XPI := $(DIST)/$(EXT_ID)-$(VERSION)-firefox.xpi CRX := $(DIST)/$(EXT_ID)-$(VERSION)-chrome.zip -.PHONY: all build chrome test smoke xpi crx packages clean check-deps +.PHONY: all firefox chrome typecheck test smoke xpi crx packages clean \ + distclean check-deps -all: build +# The default does the lot: build both browsers and package both. Packaging is +# only a zip of a directory that was going to be built anyway, so making it the +# default costs nothing and means dist/ is never quietly out of date with src/. +all: packages # Unit tests for the pure logic: the input formatters and the decision that # says whether a page load may press "Entrar". Run straight through Node's @@ -64,30 +78,40 @@ test: node --test "src/**/*.test.ts" # Runs the built background bundle in a service-worker-shaped sandbox, which -# is where a Chrome-only breakage would otherwise hide until runtime. -smoke: build - node tools/sw-smoke.mjs +# is where a Chrome-only breakage would otherwise hide until runtime. The +# bundle is identical for both targets, so checking one covers both. +smoke: chrome + node tools/sw-smoke.mjs $(CHROME_DIR)/background.js -# Call the local toolchain directly, so this works regardless of how pnpm is +# Typecheck once. Both build targets depend on it rather than each running tsc, +# which halves the work when building both. +# +# Calls the local toolchain directly, so this works regardless of how pnpm is # provided (corepack vs standalone). Run "corepack pnpm install" first. -build: check-deps +typecheck: check-deps node_modules/.bin/tsc -noEmit -skipLibCheck + +firefox: typecheck node esbuild.config.mjs production + @echo "Firefox build: $(CURDIR)/$(FIREFOX_DIR)" # The same sources with Chrome's manifest. Firefox and Chrome disagree on the # background key and on the gecko block, so the manifest is generated per # target rather than forked. -chrome: check-deps - node_modules/.bin/tsc -noEmit -skipLibCheck +chrome: typecheck TARGET=chrome node esbuild.config.mjs production - @echo "Chrome build in build/ -- chrome://extensions -> Developer mode -> Load unpacked." + @echo + @echo "Chrome build: $(CURDIR)/$(CHROME_DIR)" + @echo "Load it with chrome://extensions -> Developer mode -> Load unpacked." + @echo "Select the folder itself; Chrome wants the directory holding manifest.json." + @echo # An .xpi is just a zip of the extension directory, with the manifest at the # top level rather than inside a wrapper folder. -xpi: build +xpi: firefox @mkdir -p $(DIST) rm -f $(XPI) - cd build && zip -qr ../$(XPI) . + cd $(FIREFOX_DIR) && zip -qr $(CURDIR)/$(XPI) . @echo @echo "Built: $(CURDIR)/$(XPI)" @echo @@ -108,18 +132,30 @@ xpi: build crx: chrome @mkdir -p $(DIST) rm -f $(CRX) - cd build && zip -qr ../$(CRX) . + cd $(CHROME_DIR) && zip -qr $(CURDIR)/$(CRX) . @echo @echo "Built: $(CURDIR)/$(CRX)" @echo "Upload it at chrome.google.com/webstore/devconsole" @echo -# Both packages. The Firefox build runs last so build/ is left in the state -# the development instructions assume. -packages: crx xpi +# Both packages. Order no longer matters: each target has its own directory. +packages: xpi crx +# Removes everything the build produces, including packages from earlier +# versions, whose filenames carry their own version number and so are never +# overwritten by a later build. dist/ is tracked in git, so a clean shows the +# packages as deleted until the next "make packages" puts them back. clean: rm -rf build $(DIST) + rm -f $(EXT_ID)-*.xpi $(EXT_ID)-*.zip + find . -name '*.map' -not -path './node_modules/*' -delete + @echo "Removed build/, $(DIST)/ and any stray packages." + +# Everything clean removes, plus the installed dependencies. Recover with +# "corepack pnpm install" -- never with npm, see the note in README.md. +distclean: clean + rm -rf node_modules + @echo "Removed node_modules/. Run: corepack pnpm install" # Fail with a useful message rather than a confusing "tsc: not found". check-deps: diff --git a/README.md b/README.md index 15da879..264185c 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Once it is published, from addons.mozilla.org. Until then, build it yourself: ``` corepack pnpm install # first time, or after a dependency change -make xpi # typecheck, bundle, and package dist/logsdu-.xpi +make xpi # bundle and package dist/logsdu--firefox.xpi ``` `make xpi` prints the full path of the file and how to install it. The package @@ -119,12 +119,12 @@ check. ## Chrome ``` -make chrome # unpacked Chrome build in build/ +make chrome # unpacked Chrome build in build/chrome/ make crx # package it as dist/logsdu--chrome.zip ``` -Load `build/` via `chrome://extensions` -> Developer mode -> **Load unpacked**, -or upload the zip at +Load `build/chrome/` via `chrome://extensions` -> Developer mode -> **Load +unpacked** (select the folder itself), or upload the zip at [the Web Store dashboard](https://chrome.google.com/webstore/devconsole). One codebase, two manifests. Chrome MV3 requires a background *service worker* @@ -156,21 +156,29 @@ script instead. ``` corepack pnpm install +make # everything: build and package both browsers +make firefox # build Firefox only -> build/firefox/ +make chrome # build Chrome only -> build/chrome/ make test # unit tests make smoke # background bundle under a service worker -make # typecheck and bundle into build/ -make packages # both publishable packages into dist/ -corepack pnpm run dev # rebuild on change -make clean +corepack pnpm run dev # rebuild Firefox on change +make clean # remove build/, dist/ and stray packages +make distclean # clean, plus node_modules/ ``` -`build/` is the unpacked extension and is regenerated from scratch every time. -`dist/` holds the packages meant to be published, and is kept separate so that -packaging never tries to include its own output. +A bare `make` typechecks once, bundles for both browsers into `build/`, and +packages both into `dist/`. Packaging is only a zip of a directory that was +going to be built anyway, so it costs nothing and keeps `dist/` from drifting +out of step with the sources. + +Each browser gets its own directory under `build/`, so the two can coexist and +neither is ever left stale by the other. `dist/` holds the packages meant to be +published, and is kept out of `build/` so that packaging never tries to include +its own output. While iterating, load the unpacked directory rather than reinstalling an `.xpi` each time: `about:debugging` -> **This Firefox** -> **Load Temporary Add-on** -> -`build/manifest.json`, then press **Reload** there after each rebuild. That copy +`build/firefox/manifest.json`, then press **Reload** there after each rebuild. That copy disappears on restart, which is the point -- it is for development, not daily use. diff --git a/VERSION b/VERSION index 0d91a54..9e11b32 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.0 +0.3.1 diff --git a/dist/logsdu-0.3.0-chrome.zip b/dist/logsdu-0.3.0-chrome.zip deleted file mode 100644 index 19692ba33536917b2d494b87641ddb2814d48649..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23060 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 diff --git a/esbuild.config.mjs b/esbuild.config.mjs index 7422852..0fc5881 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -35,12 +35,17 @@ const banner = `/* `; const prod = process.argv[2] === 'production'; -const outdir = 'build'; // Target browser. Firefox and Chrome disagree on exactly one manifest key, so // the manifest is written per target rather than duplicated in the tree. const target = process.env.TARGET === 'chrome' ? 'chrome' : 'firefox'; +// Each target gets its own directory. They used to share one, and the result +// was that whichever build ran last silently won: loading the other browser's +// output then failed with a confusing manifest error. Separate directories +// mean both can exist at once and neither can be stale by accident. +const outdir = `build/${target}`; + /** * Write the manifest for the target browser. * @@ -60,8 +65,9 @@ async function writeManifest() { ); } -// Everything that is not TypeScript is copied verbatim into build/, so that -// the directory can be handed straight to about:debugging. +// Everything that is not TypeScript is copied verbatim into the output, so +// that the directory can be handed straight to about:debugging or to Chrome's +// "Load unpacked". async function copyStatic() { await mkdir(outdir, { recursive: true }); for (const name of await readdir('src')) { @@ -76,7 +82,7 @@ async function copyStatic() { await copyStatic(); // In watch mode the static files must follow every rebuild, otherwise editing -// manifest.json or a .html file silently changes nothing in build/. +// manifest.json or a .html file silently changes nothing in the output. const staticPlugin = { name: 'copy-static', setup(build) { diff --git a/package.json b/package.json index 5471603..cdfeb53 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "logsdu", - "version": "0.3.0", + "version": "0.3.1", "description": "Browser extension that fills and submits a three-field academic portal login.", "author": "Ruben Carlo Benante ", "type": "module", diff --git a/src/manifest.json b/src/manifest.json index 0e34fa1..6a79832 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, "name": "logsdu", - "version": "0.3.0", + "version": "0.3.1", "description": "Saves and fills logins that password managers cannot: registration number, date of birth and document number.", "author": "Ruben Carlo Benante (Dr. Beco)", "homepage_url": "https://code.beco.cc/beco/logsdu", diff --git a/tools/sw-smoke.mjs b/tools/sw-smoke.mjs index de67ed2..5ab6a0a 100644 --- a/tools/sw-smoke.mjs +++ b/tools/sw-smoke.mjs @@ -29,14 +29,15 @@ // // This evaluates the built bundle in a worker-shaped sandbox with a stubbed // extension API, and asserts that it registers exactly one content script, for -// exactly the configured origin. Run it against build/ after a build: +// exactly the configured origin. // // make smoke +// node tools/sw-smoke.mjs [path/to/background.js] import { readFileSync } from 'node:fs'; import vm from 'node:vm'; -const BUNDLE = 'build/background.js'; +const BUNDLE = process.argv[2] ?? 'build/chrome/background.js'; const CONFIGURED_ORIGIN = 'https://portal.example.br'; const permissionChecks = [];