Skip to content

Commit

Permalink
Polyfills for Node.js v18 and JSDOM runtimes (#875)
Browse files Browse the repository at this point in the history
Adds a new `@php-wasm/node-polyfills` package to polyfill the features
missing in Node 18 and/or JSDOM environments. The goal is to make wp-now
and other Playground-based Node.js packages work in Node 18, which is
the current LTS release.

The polyfilled JavaScript features are:

* `CustomEvent` class
* `File` class
* `Blob.text()` and `Blob.arrayBuffer()` methods
* `Blob.arrayBuffer()` and `File.text()` methods
* `Blob.stream()` and `File.stream()` methods
* Ensures `File.stream().getReader({ mode: 'byob' })` is supported –
this is relevant for #851

I adapted the Blob methods from
https://github.com/bjornstar/blob-polyfill/blob/master/Blob.js as they
seemed to provide just the logic needed here and they also worked right
away.

This PR is a part of
#851 split out
into a separate PR to make it easier to review and reason about.

Supersedes #865

## Testing instructions

Confirm the unit tests pass. This PR ships a set of vite tests to
confirm the polyfills work both in vanilla Node.js and in jsdom runtime
environments.
  • Loading branch information
adamziel authored Dec 19, 2023
1 parent ea0549a commit db306f7
Show file tree
Hide file tree
Showing 22 changed files with 579 additions and 46 deletions.
42 changes: 29 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions packages/php-wasm/node-polyfills/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"extends": ["../../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"rules": {}
},
{
"files": ["*.ts", "*.tsx"],
"rules": {}
},
{
"files": ["*.js", "*.jsx"],
"rules": {}
}
]
}
16 changes: 16 additions & 0 deletions packages/php-wasm/node-polyfills/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# php-wasm-node-polyfills

Polyfills JavaScript classes and methods required by WordPress Playground.

Ensures compatibility with the following environments:

- Node.js >= 18
- JSDom

## Building

Run `nx build php-wasm-node-polyfills` to build the library.

## Running unit tests

Run `nx test php-wasm-node-polyfills` to execute the unit tests via [Jest](https://jestjs.io).
33 changes: 33 additions & 0 deletions packages/php-wasm/node-polyfills/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "@php-wasm/node-polyfills",
"version": "0.0.1",
"description": "PHP.wasm – polyfills for Node.js",
"repository": {
"type": "git",
"url": "https://github.com/WordPress/wordpress-playground"
},
"homepage": "https://developer.wordpress.org/playground",
"author": "The WordPress contributors",
"contributors": [
{
"name": "Adam Zielinski",
"email": "[email protected]",
"url": "https://github.com/adamziel"
}
],
"type": "module",
"main": "./index.cjs",
"module": "./index.js",
"exports": {
".": {
"import": "./index.js",
"require": "./index.cjs"
},
"./package.json": "./package.json"
},
"publishConfig": {
"access": "public",
"directory": "../../../dist/packages/php-wasm/node-polyfills"
},
"license": "GPL-2.0-or-later"
}
55 changes: 55 additions & 0 deletions packages/php-wasm/node-polyfills/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"name": "php-wasm-node-polyfills",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/php-wasm/node-polyfills/src",
"projectType": "library",
"targets": {
"build": {
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/packages/php-wasm/node-polyfills"
}
},
"test": {
"executor": "nx:noop",
"dependsOn": ["test:vite:node", "test:vite:jsdom"]
},
"test:esmcjs": {
"executor": "@wp-playground/nx-extensions:assert-built-esm-and-cjs",
"options": {
"outputPath": "dist/packages/php-wasm/node-polyfills"
},
"dependsOn": ["build"]
},
"test:vite:node": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"config": "packages/php-wasm/node-polyfills/vitest-node.config.ts",
"passWithNoTests": true,
"reportsDirectory": "../../../coverage/packages/php-wasm/node-polyfills"
}
},
"test:vite:jsdom": {
"executor": "@nx/vite:test",
"outputs": ["{options.reportsDirectory}"],
"options": {
"config": "packages/php-wasm/node-polyfills/vitest-jsdom.config.ts",
"passWithNoTests": true,
"reportsDirectory": "../../../coverage/packages/php-wasm/node-polyfills"
}
},
"lint": {
"executor": "@nx/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
"packages/php-wasm/node-polyfills/**/*.ts",
"packages/php-wasm/node-polyfills/package.json"
]
}
}
},
"tags": []
}
2 changes: 2 additions & 0 deletions packages/php-wasm/node-polyfills/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import './lib/blob';
import './lib/custom-event';
56 changes: 56 additions & 0 deletions packages/php-wasm/node-polyfills/src/lib/blob.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import './blob';

describe('File class', () => {
it('Should exist', () => {
expect(File).not.toBe(undefined);
});
});

describe('File.arrayBuffer() method', () => {
it('should exist', async () => {
expect(typeof File.prototype.arrayBuffer).toBe('function');
});
it('should resolve to a valid array buffer', async () => {
const inputBytes = new Uint8Array([1, 2, 3, 4]);
const file = new File([inputBytes], 'test');
const outputBuffer = await file.arrayBuffer();
const outputBytes = new Uint8Array(outputBuffer);
expect(outputBytes).toEqual(inputBytes);
});
});

describe('File.stream() method', () => {
it('should exist', async () => {
expect(typeof File.prototype.stream).toBe('function');
});
it('should returns a valid stream', async () => {
const inputBytes = new Uint8Array([1, 2, 3, 4]);
const file = new File([inputBytes], 'test');
const stream = file.stream();
const reader = stream.getReader();

const firstRead = await reader.read();
expect(firstRead.value).toEqual(inputBytes);
expect(firstRead.done).toBe(false);

const secondRead = await reader.read();
expect(secondRead.done).toBe(true);
});
it('should be a valid BYOB stream that allows reading an arbitrary number of bytes', async () => {
const inputBytes = new Uint8Array([1, 2, 3, 4]);
const file = new File([inputBytes], 'test');
const stream = file.stream();
const reader = stream.getReader({ mode: 'byob' });

const firstRead = await reader.read(new Uint8Array(3));
expect(firstRead.value).toEqual(inputBytes.slice(0, 3));
expect(firstRead.done).toBe(false);

const secondRead = await reader.read(new Uint8Array(2));
expect(secondRead.value).toEqual(inputBytes.slice(3));
expect(secondRead.done).toBe(false);

const thirdRead = await reader.read(new Uint8Array(2));
expect(thirdRead.done).toBe(true);
});
});
Loading

0 comments on commit db306f7

Please sign in to comment.