Skip to content

Commit

Permalink
Replace Deno with webpack and ts-loader
Browse files Browse the repository at this point in the history
  • Loading branch information
dgroomes committed Apr 30, 2022
1 parent b2e771b commit f459a11
Show file tree
Hide file tree
Showing 24 changed files with 5,673 additions and 107 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.DS_Store
.idea/
distribution/
node_modules/
dist/
39 changes: 29 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The source code layout:
* Various implementation code.
* `vendor/`
* Vendor-specific code. TypeScript type declaration files for browser (vendor) JavaScript APIs. This means there are `.d.ts` files for
Chromium's `chrome` JavaScript APIs and FireFox's `browser` JavaScript APIs. Yes there is probably an open source
Chromium's `chrome` JavaScript APIs and Firefox's `browser` JavaScript APIs. Yes there is probably an open source
version of this but I would prefer to minimize third-party dependencies where feasible (Update: I'm happy to depend
on first-party libraries from, for example, Mozilla. I think they have a nice polyfill).
* `rpc/`
Expand Down Expand Up @@ -72,14 +72,14 @@ necessity.

The source code is laid out in a file structure that groups code by the execution context that the code runs in:

* `rpc/rpc.js`
* `rpc/rpc.ts`
* The code in this file is foundational common code for the RPC framework. It is used in all contexts of a web
extension: background scripts, popup scripts, content scripts, and the web page.
* `rpc/rpc-web-page.js`
* `rpc/rpc-web-page.ts`
* The code in this file runs on the web page.
* `rpc/rpc-backend.js/`
* `rpc/rpc-backend.ts/`
* The code in this file runs in the extension *backend* contexts: background workers, popups, and content scripts.
* `rpc/content-script.js`
* `rpc/content-script-proxy.ts`
* The code in this file runs in a content script.

One thing I'm omitting with the RPC implementation is an "absolute unique identifier" to associate with each message.
Expand All @@ -103,11 +103,30 @@ extension and web page contexts:
1. Initialize objects in the web page
* The web page must initialize the RPC objects on the web page by calling `initRpcWebPage(...)`

## Development Instructions

Follow these instructions to build BrowserExtensionFramework:

1. Install dependencies
* ```shell
npm install
```
2. Build the library distributions:
* ```shell
npm run build
```
* Notice that this builds builds *distributions* (plural!) not just a single distribution. Most libraries will publish
a main artifact, but BrowserExtensionFramework needs to publish three equally important artifacts. There is an artifact
for each JavaScript execution environment in a browser extension architecture: 1) backend 2) content script and 3)
web page.

## Wish List

General clean ups, TODOs and things I wish to implement for this project:

* [ ] Consider publishing to Deno or NPM. Consider publishing the compiled JavaScript.
* [ ] Consider publishing to NPM. Publishing the compiled JavaScript and the TypeScript declaration files. A useful step
to do before this would be to publish the distribution locally and consume it from the 'Detect Code Libraries' example
project.
* [ ] Runtime check the "externally_accessible" configuration list (I assume that's possible but I wouldn't be surprised if
it wasn't) and warn if the current web page is not in the list. (This was the problem I had when I developed the
example extension and I was confused).
Expand All @@ -119,12 +138,12 @@ General clean ups, TODOs and things I wish to implement for this project:
compiling TypeScript into 'entrypoint'-like files), the instructions have gotten stale. Also the whole 'RPC sub-framework'
isn't really clear anymore.
* [x] DONE Stop using import maps for differentiating between Chromium/Firefox things. When it comes to publishing
this library, I don't want to publish a FireFox artifact separately from a Chromium one. Node tooling is not equipped
this library, I don't want to publish a Firefox artifact separately from a Chromium one. Node tooling is not equipped
for consuming multi-flavor artifacts.
* [ ] Debugging in FireFox is broken for me on my mac. I can't get it to show logs or sources, even when I try an official
* [ ] Debugging in Firefox is broken for me on my mac. I can't get it to show logs or sources, even when I try an official
extension example like 'beastify'. It does not work like the [Extension workshop docs](https://extensionworkshop.com/documentation/develop/debugging/#debugging-popups)
say it should. I need to try on my Windows computer.
* [ ] Remove Deno for NPM and Webpack. It was a rewarding experience and a quick start. But I need to understand a build
* [x] DONE Remove Deno for NPM and Webpack. It was a rewarding experience and a quick start. But I need to understand a build
a prototypical library and user/developer experience. There are so many quirks of browser-based JS modules that I
can't afford to stray from mainstream.

Expand All @@ -136,7 +155,7 @@ Finished items:
* [X] DONE Consider abstracting away the required content script "thin bootstrap" files. For example, `dcl-content-script.ts`
shouldn't have to exist. I thought it did earlier, but it's not needed. It can be replaced with a generic middleware
content script.
* [x] DONE Support FireFox in the example. If the example supports both Chromium and FireFox, then I can build it, verify the
* [x] DONE Support Firefox in the example. If the example supports both Chromium and Firefox, then I can build it, verify the
behavior in both browsers, and have confidence that the framework still works.
* [x] DONE Are [import maps](https://deno.land/manual@v1.21.0/linking_to_external_code/import_maps) going to save me from the
awkward "installation-time" setter of the "browser descriptor? (e.g. 'chrome' of 'firefox')" and can it also be
Expand Down
14 changes: 3 additions & 11 deletions api/backend-wiring.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
import {BackendWiringImpl} from "../impl/backend-wiring-impl.ts";
import {RpcClient, RpcServer} from "../rpc/rpc.ts";
import {BrowserDescriptor} from "../browserDescriptor.ts";

export {BackendWiring}
import {RpcClient, RpcServer} from "../rpc/rpc";

/**
* The BackendWiring class is part of the BrowserExtensionFramework's public API. It must be used in code that runs
Expand All @@ -11,11 +7,11 @@ export {BackendWiring}
* It encapsulates the functionality and configuration (i.e. "wiring") that power the framework in background or popup
* scripts.
*
* Use the "initialize" method to get up and running.
* Use the "initialize" method in "BrowserExtensionFramework" to get up and running.
*
* This class would be used by the "dcl-popup-script.ts" file in the "Detect Code Libraries" example project.
*/
abstract class BackendWiring {
export abstract class BackendWiring {

rpcClient: RpcClient
rpcServer: RpcServer
Expand All @@ -25,10 +21,6 @@ abstract class BackendWiring {
this.rpcServer = rpcServer;
}

static initialize(browserDescriptor: BrowserDescriptor) : Promise<BackendWiring> {
return BackendWiringImpl.initialize(browserDescriptor)
}

/**
* Inject a web-page script. The page script must be instrumented with the BrowserExtensionFramework's "PageWiring"
* class because the injection flow is expecting the "page-script-satisfied" signal.
Expand Down
13 changes: 3 additions & 10 deletions api/page-wiring.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
import {RpcClient, RpcServer} from "../rpc/rpc.ts";
import {PageWiringImpl} from "../impl/page-wiring-impl.ts";

export {PageWiring}
import {RpcClient, RpcServer} from "../rpc/rpc";

/**
* The PageWiring class is part of the BrowserExtensionFramework's public API. It must be used in code that runs in the
* web page.
*
* It encapsulates the functionality and configuration (i.e. "wiring") that power the framework in the web page.
*
* Use the "initialize" method to get up and running.
* Use the "initialize" method in "BrowserExtensionFramework" to get up and running.
*
* This class would be used to instrument the "dcl-page-script.js" file as described in the "Detect Code Libraries"
* example in the README.
*/
abstract class PageWiring {
export abstract class PageWiring {

rpcClient: RpcClient
rpcServer: RpcServer
Expand All @@ -24,10 +21,6 @@ abstract class PageWiring {
this.rpcServer = rpcServer;
}

static initialize(): PageWiring {
return PageWiringImpl.initialize()
}

/**
* This function must be invoked when any initialization logic is finished. By calling this function, the
* "page-script-satisfied" signal is sent to the backend components of BrowserExtensionFramework.
Expand Down
20 changes: 20 additions & 0 deletions browser-extension-framework.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import {BrowserDescriptor} from "./browserDescriptor";
import {BackendWiringImpl} from "./impl/backend-wiring-impl";
import {BackendWiring} from "./api/backend-wiring";
import {PageWiringImpl} from "./impl/page-wiring-impl";
import {PageWiring} from "./api/page-wiring";

/**
* This is the entry point into BrowserExtensionFramework. You should use the static "initialize" functions to wire the
* framework into your own browser extension.
*/
export class BrowserExtensionFramework {

static initializeBackendWiring(browserDescriptor: BrowserDescriptor): Promise<BackendWiring> {
return BackendWiringImpl.initialize(browserDescriptor)
}

static initializePageWiring(): PageWiring {
return PageWiringImpl.initialize()
}
}
3 changes: 1 addition & 2 deletions content-script-middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@
// This middleware mainly exists to proxy RPC request/response message but it also services requests from the framework
// "backend wiring" to inject the web page with a page script.

import {chrome} from "./vendor/chrome-extension-types.d.ts";
import {backgroundRpcClientsListener, webPageRpcClientsListener} from "./rpc/rpc-content-script-proxy.ts";
import {backgroundRpcClientsListener, webPageRpcClientsListener} from "./rpc/rpc-content-script-proxy";

// Define a global "window" variable that we can use to keep track of the lifecycle.
declare global {
Expand Down
6 changes: 1 addition & 5 deletions example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,8 @@ BrowserExtensionFramework is able to abstract away the content script.

Follow these instructions to install the tool as a Chrome browser extension and use it:

1. Install Deno
* <https://deno.land>
* > A modern runtime for JavaScript and TypeScript.
1. Build the extension distribution:
1. Build the extension distributions:
* `./build.sh`
* This will take 20+ seconds. Deno is working hard to type check the TypeScript code.
1. Open Chrome's extension settings page
* Open Chrome to the URL: `chrome://extensions`
* Alternatively, follow the instructions in the [Firefox](#firefox) section below to install the extension in
Expand Down
36 changes: 20 additions & 16 deletions example/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Build the "Detect Code Libraries" (DCL) web extension distribution from the source code.
#
# Specifically, this will create the directories: "distribution/chromium-manifest-v2" and "firefox-manifest-v2. The
# contents of these directories are ready to be loaded into a Chromium browser (Chrome and Opera should work) or FireFox
# contents of these directories are ready to be loaded into a Chromium browser (Chrome and Opera should work) or Firefox
# as a web extension! See the README for instructions.

set -eu
Expand All @@ -11,18 +11,21 @@ set -eu
project_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"

preconditions() {
if ! which deno &> /dev/null; then
echo >&2 "The 'deno' command was not found. Please install Deno. See https://deno.land/."
local bef_dist="$project_dir/../dist/"

if [[ ! -d "$bef_dist" ]]; then
echo >&2 "The BrowserExtensionFramework distribution was not found in '$bef_dist'. Build BrowserExtensionFramework by following the instructions in its README"
exit 1
fi
}

# Delegate to the "deno bundle ..." command
deno_bundle() {
deno bundle --quiet --config ../deno.json "${@}"
build_source() {
pushd "$project_dir"
npm run build
popd
}

build_distribution() {
assemble_distribution() {
local vendor_dir_name="$1"
local output_dir="$project_dir/distribution/${vendor_dir_name}"

Expand All @@ -31,25 +34,26 @@ build_distribution() {
rm -rf "$output_dir"
mkdir -p "$output_dir"

# Copy over non-TypeScript files
# Copy over the non-JavaScript files
cp "$project_dir/src/$vendor_dir_name/manifest.json" "$output_dir"
cp "$project_dir/src/dcl-popup.html" "$output_dir"

# Compile ("bundle") the TypeScript entrypoint-type files into JavaScript
deno_bundle "$project_dir/src/dcl-popup-script.ts" "$output_dir/dcl-popup-script.js"
deno_bundle "$project_dir/src/dcl-page-script.ts" "$output_dir/dcl-page-script.js"
mkdir "$output_dir/rpc"
deno_bundle "$project_dir/../content-script-middleware.ts" "$output_dir/content-script-middleware.js"
# Copy over the entrypoint JavaScript files
cp "$project_dir/distribution/dcl-popup-script.js" "$output_dir"
cp "$project_dir/distribution/dcl-page-script.js" "$output_dir"
cp "$project_dir/../dist/content-script-middleware.js" "$output_dir"
}

build_all() {
echo "Building..."
local build_status=0

build_distribution "chromium-manifest-v2"
build_source

assemble_distribution "chromium-manifest-v2"
echo "Chromium distribution built! ✅"
build_distribution "firefox-manifest-v2"
echo "FireFox distribution built! ✅"
assemble_distribution "firefox-manifest-v2"
echo "Firefox distribution built! ✅"
}

preconditions
Expand Down
Loading

0 comments on commit f459a11

Please sign in to comment.