Skip to content

Commit

Permalink
fix(TypedMessenger): Make sure TypedMessenger still works in minified…
Browse files Browse the repository at this point in the history
… builds (#933)
  • Loading branch information
jespertheend committed Apr 16, 2024
1 parent 8abbbb1 commit 25960e2
Show file tree
Hide file tree
Showing 14 changed files with 645 additions and 114 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ deno_dir
studio/src/styles/projectSelectorStyles.js
studio/src/styles/studioStyles.js
studio/src/styles/shadowStyles.js
test/minified/out
2 changes: 1 addition & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ module.exports = {
"no-useless-constructor": "error",
"no-useless-rename": "error",
"no-var": "error",
"object-shorthand": "error",
"object-shorthand": ["error", "always", {avoidQuotes: true}],
"prefer-arrow-callback": "error",
"prefer-const": ["error", {destructuring: "all"}],
"prefer-numeric-literals": "error",
Expand Down
25 changes: 25 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,31 @@ jobs:
flags: unittests
token: ${{ secrets.CODECOV_TOKEN }}

minified-tests:
runs-on: ubuntu-latest
steps:
- name: Setup repo
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8

- name: Cache
uses: actions/cache@9b0c1fce7a93df8e3bb8926b0d6e9d89e92f20a7
with:
path: |
deno_dir
.denoTypes
npm_packages
studio/deps
key: ci-${{ github.run_id }}_${{ github.run_attempt }}
restore-keys: ci-

- name: Setup Deno
uses: denoland/setup-deno@041b854f97b325bd60e53e9dc2de9cb9f9ac0cba
with:
deno-version: ${{ env.DENO_VERSION }}

- name: Run minified tests
run: deno task test test/minified

e2e-tests:
runs-on: ubuntu-latest
steps:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ npm_packages
deno.lock
npmPackage
jsrPackage
test/minified/out/
3 changes: 2 additions & 1 deletion jsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
"test/**/*.ts",
],
"exclude": [
"test/e2e/studio/projects/"
"test/e2e/studio/projects/",
"test/minified/out"
],
"files": [
"src/mod.js",
Expand Down
25 changes: 25 additions & 0 deletions scripts/shared/rollupTerserPlugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { minify } from "terser";

const nameCache = {};

/**
* A rollup plugin for minifying builds.
* @param {import("terser").MinifyOptions} minifyOptions
* @returns {import("rollup").Plugin}
*/
export function rollupTerserPlugin(minifyOptions = {}) {
return {
name: "terser",
async renderChunk(code, chunk, outputOptions) {
const output = await minify(code, {
...minifyOptions,
nameCache,
});
if (!output.code) return null;
return {
code: output.code,
map: output.map,
};
},
};
}
127 changes: 124 additions & 3 deletions scripts/test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#!/usr/bin/env -S deno run --unstable --no-check --allow-run --allow-read --allow-write --allow-env --allow-net

import { join, resolve } from "std/path/mod.ts";
import * as path from "std/path/mod.ts";
import * as fs from "std/fs/mod.ts";
import { setCwd } from "chdir-anywhere";
import { dev } from "./dev.js";
import { parseArgs } from "../test/shared/testArgs.js";
import { buildEngine } from "./buildEngine.js";
import { rollup } from "rollup";
import { rollupTerserPlugin } from "./shared/rollupTerserPlugin.js";

setCwd();
Deno.chdir("..");
Expand Down Expand Up @@ -35,6 +39,7 @@ if (Deno.args.length > 0 && !Deno.args[0].startsWith("-")) {
}

const needsUnitTests = !filteredTests || filteredTests.startsWith("test/unit");
const needsMinifiedTests = !filteredTests || filteredTests.startsWith("test/minified");
const needsE2eTests = !filteredTests || filteredTests.startsWith("test/e2e");

await dev({
Expand All @@ -60,7 +65,7 @@ if (needsUnitTests) {
await removeMaybeDirectory(DENO_COVERAGE_DIR);
await removeMaybeDirectory(FAKE_IMPORTS_COVERAGE_DIR);
denoTestArgs.push(`--coverage=${DENO_COVERAGE_DIR}`);
const coverageMapPath = join(Deno.cwd(), FAKE_IMPORTS_COVERAGE_DIR);
const coverageMapPath = path.join(Deno.cwd(), FAKE_IMPORTS_COVERAGE_DIR);
applicationCmdArgs.add(`--fi-coverage-map=${coverageMapPath}`);
}
denoTestArgs.push(filteredTests || "test/unit/");
Expand All @@ -69,6 +74,122 @@ if (needsUnitTests) {
testCommands.push(cmd);
}

// Minified tests
if (needsMinifiedTests) {
const testMinifiedDir = path.resolve("test/minified");
const testsDir = path.resolve(testMinifiedDir, "tests");
const outDir = path.resolve(testMinifiedDir, "out");
const engineDir = path.resolve(outDir, "engine");
const testOutDir = path.resolve(outDir, "tests");
const minifiedRendaPath = path.resolve(testMinifiedDir, "shared/minifiedRenda.js");
const unminifiedRendaPath = path.resolve(testMinifiedDir, "shared/unminifiedRenda.js");

const noBuildFlag = Deno.args.includes("--no-build");

const denoTestArgs = [Deno.execPath(), "test", "--no-check", "--allow-env", "--allow-read", "--allow-net", "--parallel"];

if (noBuildFlag) {
denoTestArgs.push(filteredTests || "test/minified/tests/");
} else {
denoTestArgs.push("test/minified/out/tests/");

try {
await Deno.remove(outDir, { recursive: true });
} catch (e) {
if (e instanceof Deno.errors.NotFound) {
// Already removed
} else {
throw e;
}
}

await buildEngine(engineDir);

/**
* @returns {import("rollup").Plugin}
*/
function rollupRedirectBuildPlugin() {
return {
name: "redirectBuild",
async resolveId(id, importer) {
if (importer) {
const dirname = path.dirname(importer);
const resolved = path.resolve(dirname, id);
if (resolved == minifiedRendaPath) {
return path.resolve(engineDir, "renda.js");
} else if (resolved == unminifiedRendaPath) {
// We want to allow tests to export from both the built and non-built library.
// This allows us to simulate situations such as minified client code that wants to
// communicate with non-minified server code.
// By marking ./src/unminifiedRenda.js as external, we make sure that the build of our tests
// keep referencing this file directly, as opposed to including its contents in the bundle.
// But we do have to rewrite the imported path, since bundled tests will live at another location.
const newResolved = path.relative(testOutDir, unminifiedRendaPath);
return {
id: newResolved,
external: true,
};
}
}
// Treat std as external
if (id.startsWith("std/")) {
return false;
}
return null;
},
};
}

const testFiles = [];
for await (const entry of fs.walk(testsDir)) {
if (entry.name.endsWith(".test.js")) {
const relative = path.relative(testsDir, entry.path);
if (relative.startsWith("out/")) continue;
testFiles.push(entry.path);
}
}
const bundle = await rollup({
input: testFiles,
plugins: [rollupRedirectBuildPlugin()],
onwarn: (message) => {
if (message.code == "CIRCULAR_DEPENDENCY") return;
console.error(message.message);
},
});
const debug = Deno.args.includes("--debug") || Deno.args.includes("-d") || inspect;
await bundle.write({
dir: testOutDir,
plugins: [
rollupTerserPlugin({
/* eslint-disable camelcase */
module: true,
keep_classnames: debug,
keep_fnames: debug,
compress: {
drop_debugger: false,
},
sourceMap: debug,
mangle: {
module: true,
properties: {
debug,
keep_quoted: "strict",
reserved: ["Deno", "test", "fn"],
},
},
output: {
beautify: debug,
},
/* eslint-enable camelcase */
}),
],
});
}

if (inspect) denoTestArgs.push("--inspect-brk");
testCommands.push(denoTestArgs);
}

// E2e tests
if (needsE2eTests) {
const cmd = [Deno.execPath(), "run", "--allow-env", "--allow-read", "--allow-write", "--allow-run", "--allow-net"];
Expand Down Expand Up @@ -144,5 +265,5 @@ if (needsCoverage) {
throw e;
}
}
await Deno.rename(resolve(DENO_COVERAGE_DIR, "html"), DENO_HTML_COVERAGE_DIR);
await Deno.rename(path.resolve(DENO_COVERAGE_DIR, "html"), DENO_HTML_COVERAGE_DIR);
}
53 changes: 26 additions & 27 deletions src/util/TypedMessenger/TypedMessenger.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,10 +406,9 @@ export class TypedMessenger {
* ## Example
*
* ```js
* const result = await messenger.proxy.myFunction(1, 2, 3);
* const result = await messenger.send.myFunction(1, 2, 3);
* ```
* where `myFunction` is the name of one of the functions provided in {@linkcode initialize} or {@linkcode setResponseHandlers}.
*
* where `myFunction` is the name of one of the functions provided in {@linkcode setResponseHandlers} or one of the `initialize` methods.
*/
this.send = /** @type {TypedMessengerProxy<TReq>} */ (proxy);

Expand Down Expand Up @@ -453,7 +452,7 @@ export class TypedMessenger {
*/
initializeWorker(worker, responseHandlers) {
this.setSendHandler((data) => {
worker.postMessage(data.sendData, data.transfer);
worker.postMessage(data["sendData"], data["transfer"]);
});
worker.addEventListener("message", (event) => {
this.handleReceivedMessage(event.data);
Expand All @@ -480,8 +479,8 @@ export class TypedMessenger {
*/
initializeWorkerContext(responseHandlers) {
this.setSendHandler((data) => {
globalThis.postMessage(data.sendData, {
transfer: data.transfer,
globalThis.postMessage(data["sendData"], {
transfer: data["transfer"],
});
});
globalThis.addEventListener("message", (event) => {
Expand Down Expand Up @@ -528,7 +527,7 @@ export class TypedMessenger {
});
await promise;
}
webSocket.send(JSON.stringify(data.sendData));
webSocket.send(JSON.stringify(data["sendData"]));
});
webSocket.addEventListener("message", async (message) => {
try {
Expand Down Expand Up @@ -580,21 +579,21 @@ export class TypedMessenger {
* @param {TypedMessengerMessageSendData<TRes, TReq>} data
*/
async handleReceivedMessage(data) {
if (data.direction == "request") {
if (data["direction"] == "request") {
if (!this.responseHandlers) {
throw new Error("Failed to handle message, no request handlers set. Make sure to call `setResponseHandlers` before handling messages.");
}
if (!this.sendHandler) {
throw new Error("Failed to handle message, no send handler set. Make sure to call `setSendHandler` before handling messages.");
}
const handler = this.responseHandlers[data.type];
const handler = this.responseHandlers[data["type"]];
let returnValue;
/** @type {Transferable[]} */
let transfer = [];
let didThrow = false;
if (handler) {
try {
returnValue = await handler(...data.args);
returnValue = await handler(...data["args"]);
} catch (e) {
returnValue = e;
if (this.serializeErrorHook) {
Expand All @@ -618,27 +617,27 @@ export class TypedMessenger {
}

await this.sendHandler(/** @type {TypedMessengerResponseMessageHelper<TRes, typeof data.type>} */ ({
sendData: {
direction: "response",
id: data.id,
didThrow,
type: data.type,
returnValue,
"sendData": {
"direction": "response",
"id": data["id"],
"didThrow": didThrow,
"type": data["type"],
"returnValue": returnValue,
},
transfer,
}));

if (respondOptions && respondOptions.afterSendHook) {
respondOptions.afterSendHook();
}
} else if (data.direction == "response") {
const cbs = this.onRequestIdMessageCbs.get(data.id);
} else if (data["direction"] == "response") {
const cbs = this.onRequestIdMessageCbs.get(data["id"]);
if (cbs) {
for (const cb of cbs) {
cb(data);
}
}
this.onRequestIdMessageCbs.delete(data.id);
this.onRequestIdMessageCbs.delete(data["id"]);
}
}

Expand Down Expand Up @@ -726,9 +725,9 @@ export class TypedMessenger {
} else {
promise = new Promise((resolve, reject) => {
this.onResponseMessage(requestId, (message) => {
if (message.didThrow) {
if (message["didThrow"]) {
/** @type {unknown} */
let rejectValue = message.returnValue;
let rejectValue = message["returnValue"];
if (this.deserializeErrorHook) {
rejectValue = this.deserializeErrorHook(rejectValue);
}
Expand All @@ -737,18 +736,18 @@ export class TypedMessenger {
}
reject(rejectValue);
} else {
resolve(message.returnValue);
resolve(message["returnValue"]);
}
});
});
}

await this.sendHandler(/** @type {TypedMessengerRequestMessageHelper<TReq, T>} */ ({
sendData: {
direction: "request",
id: requestId,
type,
args,
"sendData": {
"direction": "request",
"id": requestId,
"type": type,
"args": args,
},
transfer: sendOptions.transfer || [],
}));
Expand Down
6 changes: 6 additions & 0 deletions test/minified/shared/minifiedRenda.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// This re-exports all engine files.
// When building the tests, this file will export from 'dist/renda.min.js' instead.
// This allows us to run the tests without building as well, and makes it easer
// to check types when the tests haven't been built yet.

export * from "../../../src/mod.js";

0 comments on commit 25960e2

Please sign in to comment.