From 443b310dcc09e41f8ef6ca2e081001e22911c114 Mon Sep 17 00:00:00 2001 From: Matt Keeter Date: Thu, 18 Apr 2024 08:38:46 -0400 Subject: [PATCH] Use typescript in web demo (#91) strong types good --- wasm-demo/{index.js => index.ts} | 78 ++++++---- wasm-demo/package-lock.json | 247 +++++++++++++++++++++++++++++++ wasm-demo/package.json | 2 + wasm-demo/tsconfig.json | 11 ++ wasm-demo/webpack.config.js | 14 +- 5 files changed, 325 insertions(+), 27 deletions(-) rename wasm-demo/{index.js => index.ts} (83%) create mode 100644 wasm-demo/tsconfig.json diff --git a/wasm-demo/index.js b/wasm-demo/index.ts similarity index 83% rename from wasm-demo/index.js rename to wasm-demo/index.ts index b050f5f4..33234234 100644 --- a/wasm-demo/index.js +++ b/wasm-demo/index.ts @@ -6,10 +6,10 @@ import { defaultKeymap } from "@codemirror/commands"; const RENDER_SIZE = 512; async function setup() { - const fidget = await import("./pkg").catch(console.error); + const fidget: any = await import("./pkg").catch(console.error); const draw = glInit(); - function setScript(text) { + function setScript(text: string) { let shape = null; let result = null; try { @@ -37,7 +37,7 @@ async function setup() { } } - var timeout = null; + var timeout: any = null; const script = new EditorView({ doc: "hello", extensions: [ @@ -79,16 +79,27 @@ async function setup() { } // WebGL wrangling is based on https://github.com/mdn/dom-examples (CC0) +class Buffers { + pos: WebGLBuffer; -function initBuffers(gl) { + constructor(pos: WebGLBuffer) { + this.pos = pos; + } +} + +class ProgramInfo { + program: WebGLProgram; + vertexPositionAttrib: number; + uSampler: WebGLUniformLocation; +} + +function initBuffers(gl: WebGLRenderingContext): Buffers { const positionBuffer = initPositionBuffer(gl); - return { - position: positionBuffer, - }; + return new Buffers(positionBuffer); } -function initPositionBuffer(gl) { +function initPositionBuffer(gl: WebGLRenderingContext): WebGLBuffer { // Create a buffer for the square's positions. const positionBuffer = gl.createBuffer(); @@ -108,9 +119,9 @@ function initPositionBuffer(gl) { } function glInit() { - const canvas = document.querySelector("#glcanvas"); + const canvas = document.querySelector("#glcanvas"); // Initialize the GL context - const gl = canvas.getContext("webgl"); + const gl = canvas.getContext("webgl"); // Only continue if WebGL is available and working if (gl === null) { @@ -150,28 +161,32 @@ function glInit() { // Collect all the info needed to use the shader program. // Look up which attribute our shader program is using // for aVertexPosition and look up uniform locations. - const programInfo = { + const programInfo: ProgramInfo = { program: shaderProgram, - attribLocations: { - vertexPosition: gl.getAttribLocation(shaderProgram, "aVertexPosition"), - }, - uniformLocations: { - uSampler: gl.getUniformLocation(shaderProgram, "uSampler"), - }, + vertexPositionAttrib: gl.getAttribLocation( + shaderProgram, + "aVertexPosition", + ), + uSampler: gl.getUniformLocation(shaderProgram, "uSampler"), // TODO unused? }; // Here's where we call the routine that builds all the // objects we'll be drawing. const buffers = initBuffers(gl); - return (data) => { + return (data: Uint8Array) => { const texture = loadTexture(gl, data); drawScene(gl, programInfo, buffers, texture); }; } // We're just drawing a single textured quad, as dumb as possible -function drawScene(gl, programInfo, buffers, texture) { +function drawScene( + gl: WebGLRenderingContext, + programInfo: ProgramInfo, + buffers: Buffers, + texture: WebGLTexture, +) { gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear to black, fully opaque // Clear the canvas before we start drawing on it. @@ -200,29 +215,37 @@ function drawScene(gl, programInfo, buffers, texture) { // Tell WebGL how to pull out the positions from the position // buffer into the vertexPosition attribute. -function setPositionAttribute(gl, buffers, programInfo) { +function setPositionAttribute( + gl: WebGLRenderingContext, + buffers: Buffers, + programInfo: ProgramInfo, +) { const numComponents = 2; // pull out 2 values per iteration const type = gl.FLOAT; // the data in the buffer is 32bit floats const normalize = false; // don't normalize const stride = 0; // how many bytes to get from one set of values to the next // 0 = use type and numComponents above const offset = 0; // how many bytes inside the buffer to start from - gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); + gl.bindBuffer(gl.ARRAY_BUFFER, buffers.pos); gl.vertexAttribPointer( - programInfo.attribLocations.vertexPosition, + programInfo.vertexPositionAttrib, numComponents, type, normalize, stride, offset, ); - gl.enableVertexAttribArray(programInfo.attribLocations.vertexPosition); + gl.enableVertexAttribArray(programInfo.vertexPositionAttrib); } // // Initialize a shader program, so WebGL knows how to draw our data // -function initShaderProgram(gl, vsSource, fsSource) { +function initShaderProgram( + gl: WebGLRenderingContext, + vsSource: string, + fsSource: string, +) { const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource); @@ -251,7 +274,7 @@ function initShaderProgram(gl, vsSource, fsSource) { // creates a shader of the given type, uploads the source and // compiles it. // -function loadShader(gl, type, source) { +function loadShader(gl: WebGLRenderingContext, type: number, source: string) { const shader = gl.createShader(type); // Send the source to the shader object @@ -275,7 +298,10 @@ function loadShader(gl, type, source) { return shader; } -function loadTexture(gl, data) { +function loadTexture( + gl: WebGLRenderingContext, + data: Uint8Array, +): WebGLTexture { const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); diff --git a/wasm-demo/package-lock.json b/wasm-demo/package-lock.json index c0c0353c..7df9bd4c 100644 --- a/wasm-demo/package-lock.json +++ b/wasm-demo/package-lock.json @@ -11,6 +11,8 @@ "@wasm-tool/wasm-pack-plugin": "1.5.0", "html-webpack-plugin": "^5.3.2", "prettier": "3.2.5", + "ts-loader": "^9.5.1", + "typescript": "^5.4.5", "webpack": "^5.49.0", "webpack-cli": "^4.7.2", "webpack-dev-server": "^4.15.1" @@ -2427,6 +2429,18 @@ "tslib": "^2.0.3" } }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -3177,6 +3191,21 @@ "node": ">=10" } }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -3651,6 +3680,105 @@ "node": ">=0.6" } }, + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ts-loader/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ts-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -3670,6 +3798,19 @@ "node": ">= 0.6" } }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -4174,6 +4315,12 @@ "optional": true } } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } }, "dependencies": { @@ -6077,6 +6224,15 @@ "tslib": "^2.0.3" } }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -6633,6 +6789,15 @@ "node-forge": "^1" } }, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -7002,6 +7167,76 @@ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "dev": true }, + "ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "tslib": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", @@ -7018,6 +7253,12 @@ "mime-types": "~2.1.24" } }, + "typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true + }, "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -7357,6 +7598,12 @@ "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "requires": {} + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } } diff --git a/wasm-demo/package.json b/wasm-demo/package.json index 0b0875c4..609f6259 100644 --- a/wasm-demo/package.json +++ b/wasm-demo/package.json @@ -11,6 +11,8 @@ "@wasm-tool/wasm-pack-plugin": "1.5.0", "html-webpack-plugin": "^5.3.2", "prettier": "3.2.5", + "ts-loader": "^9.5.1", + "typescript": "^5.4.5", "webpack": "^5.49.0", "webpack-cli": "^4.7.2", "webpack-dev-server": "^4.15.1" diff --git a/wasm-demo/tsconfig.json b/wasm-demo/tsconfig.json new file mode 100644 index 00000000..88fd0b33 --- /dev/null +++ b/wasm-demo/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + "noImplicitAny": true, + "module": "es2020", + "target": "es5", + "jsx": "react", + "allowJs": true, + "moduleResolution": "node" + } +} diff --git a/wasm-demo/webpack.config.js b/wasm-demo/webpack.config.js index 3f94f15c..fdbda582 100644 --- a/wasm-demo/webpack.config.js +++ b/wasm-demo/webpack.config.js @@ -3,7 +3,19 @@ const HtmlWebpackPlugin = require("html-webpack-plugin"); const WasmPackPlugin = require("@wasm-tool/wasm-pack-plugin"); module.exports = { - entry: "./index.js", + entry: "./index.ts", + module: { + rules: [ + { + test: /\.tsx?$/, + use: "ts-loader", + exclude: /node_modules/, + }, + ], + }, + resolve: { + extensions: [".tsx", ".ts", ".js"], + }, output: { path: path.resolve(__dirname, "dist"), filename: "index.js",