From 92365a0cc1b0b6e4acd36e5103543ce314163255 Mon Sep 17 00:00:00 2001 From: Alex <52292902+alexrudd2@users.noreply.github.com> Date: Fri, 28 Feb 2025 23:04:31 -0600 Subject: [PATCH 1/6] Switch to ESM and ECMA2022 (#178) --- .eslintrc.js => .eslintrc.cjs | 2 +- build.mjs | 4 ++-- cli.js | 2 -- cli.mjs | 3 +++ index.js | 3 --- index.mjs | 2 ++ package-lock.json | 2 +- package.json | 9 +++++---- tsconfig.json | 4 ++-- tsconfig.web.json | 4 ++-- 10 files changed, 18 insertions(+), 17 deletions(-) rename .eslintrc.js => .eslintrc.cjs (97%) delete mode 100755 cli.js create mode 100755 cli.mjs delete mode 100644 index.js create mode 100644 index.mjs diff --git a/.eslintrc.js b/.eslintrc.cjs similarity index 97% rename from .eslintrc.js rename to .eslintrc.cjs index 52d0f44..bc8b4af 100644 --- a/.eslintrc.js +++ b/.eslintrc.cjs @@ -7,7 +7,7 @@ module.exports = { 'plugin:@typescript-eslint/recommended', ], parserOptions: { - ecmaVersion: 2018, + ecmaVersion: 2022, sourceType: 'module', }, root: true, diff --git a/build.mjs b/build.mjs index 6830065..3b0d9d8 100644 --- a/build.mjs +++ b/build.mjs @@ -6,7 +6,7 @@ const buildOptions = { entryPoints: ['src/ui.tsx'], bundle: true, platform: 'browser', - target: 'es2020', + target: 'es2022', minify: true, sourcemap: true, metafile: true, @@ -27,7 +27,7 @@ const buildOptions = { }] }) ], - resolveExtensions: ['.js', '.ts', '.tsx', '.svg', '.worker.js'], + resolveExtensions: ['.mjs', '.js', '.ts', '.tsx', '.svg', '.worker.js'], }; (async () => { diff --git a/cli.js b/cli.js deleted file mode 100755 index 637ce36..0000000 --- a/cli.js +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/env node -require('./dist/server/cli').cli() diff --git a/cli.mjs b/cli.mjs new file mode 100755 index 0000000..1efddf5 --- /dev/null +++ b/cli.mjs @@ -0,0 +1,3 @@ +#!/usr/bin/env node +import { cli } from './dist/server/cli.js'; +cli(); diff --git a/index.js b/index.js deleted file mode 100644 index 80011b8..0000000 --- a/index.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - server: require('./dist/server/server'), -} diff --git a/index.mjs b/index.mjs new file mode 100644 index 0000000..d492cf8 --- /dev/null +++ b/index.mjs @@ -0,0 +1,2 @@ +import server from './dist/server/server.js'; +export { server }; diff --git a/package-lock.json b/package-lock.json index 265dc8d..7e024c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,7 +20,7 @@ "yargs": "^17.0.0" }, "bin": { - "saxi": "cli.js" + "saxi": "cli.mjs" }, "devDependencies": { "@craftamap/esbuild-plugin-html": "^0.7.0 || ^0.8.0", diff --git a/package.json b/package.json index 4434662..91737a2 100644 --- a/package.json +++ b/package.json @@ -14,9 +14,9 @@ "hardware", "robot" ], - "main": "index.js", + "main": "index.mjs", "bin": { - "saxi": "cli.js" + "saxi": "cli.mjs" }, "scripts": { "prebuild": "npm run lint", @@ -25,7 +25,7 @@ "build:server": "tsc", "build:ui": "node --experimental-modules build.mjs", "prepare": "rimraf dist && npm run build", - "start": "npm run build && node cli.js", + "start": "npm run build && node cli.mjs", "dev": "BUILD_MODE=development npm start", "deploy": "rimraf dist/ui && IS_WEB=1 npm run build:ui && gh-pages --dist dist/ui", "test": "jest" @@ -79,12 +79,13 @@ "engines": { "node": ">=18.0.0" }, + "type": "module", "jest": { "preset": "ts-jest" }, "files": [ "/dist", - "cli.js" + "cli.mjs" ], "optionalDependencies": { "@esbuild/linux-arm": "^0.25.0", diff --git a/tsconfig.json b/tsconfig.json index 60d91cd..ef46e63 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,9 @@ { "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { - "module": "commonjs", + "module": "ES2022", "esModuleInterop": true, - "target": "es6", + "target": "ES2022", "noImplicitAny": true, "moduleResolution": "node", "sourceMap": true, diff --git a/tsconfig.web.json b/tsconfig.web.json index 0322c81..a6f6156 100644 --- a/tsconfig.web.json +++ b/tsconfig.web.json @@ -3,8 +3,8 @@ "compilerOptions": { "esModuleInterop": true, "noImplicitAny": true, - "module": "es6", - "target": "es6", + "module": "es2022", + "target": "es2022", "moduleResolution": "node", "jsx": "react", "allowJs": true From 76a0e599cd0f54032c008142b25366369a56608f Mon Sep 17 00:00:00 2001 From: Alex Ruddick Date: Sun, 2 Mar 2025 18:40:36 -0600 Subject: [PATCH 2/6] strict import resolution needs .js extensions --- src/cli.ts | 14 +++++++------- src/ebb.ts | 6 +++--- src/massager.ts | 6 +++--- src/paper-size.ts | 2 +- src/planning.ts | 6 +++--- src/server.ts | 11 +++++------ src/ui.tsx | 8 ++++---- src/util.ts | 4 ++-- 8 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 173cab9..74dd71d 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -1,15 +1,15 @@ import yargs from "yargs"; import { hideBin } from 'yargs/helpers'; -import { connectEBB, startServer } from "./server"; -import { replan } from "./massager"; +import { connectEBB, startServer } from "./server.js"; +import { replan } from "./massager.js"; import { Window } from "svgdom"; import { readFileSync } from "node:fs"; import { flattenSVG, type Line } from "flatten-svg"; -import type { Vec2 } from "./vec"; -import { formatDuration } from "./util"; -import { Device, type PlanOptions, defaultPlanOptions } from "./planning"; -import { PaperSize } from "./paper-size"; -import type { Hardware } from "./ebb"; +import type { Vec2 } from "./vec.js"; +import { formatDuration } from "./util.js"; +import { Device, type PlanOptions, defaultPlanOptions } from "./planning.js"; +import { PaperSize } from "./paper-size.js"; +import type { Hardware } from "./ebb.js"; function parseSvg(svg: string) { const window = new Window; diff --git a/src/ebb.ts b/src/ebb.ts index e6b34b2..948ee83 100644 --- a/src/ebb.ts +++ b/src/ebb.ts @@ -1,6 +1,6 @@ -import { type Block, type Motion, PenMotion, type Plan, XYMotion } from "./planning"; -import { RegexParser } from "./regex-transform-stream"; -import { type Vec2, vsub } from "./vec"; +import { type Block, type Motion, PenMotion, type Plan, XYMotion } from "./planning.js"; +import { RegexParser } from "./regex-transform-stream.js"; +import { type Vec2, vsub } from "./vec.js"; /** Split d into its fractional and integral parts */ function modf(d: number): [number, number] { diff --git a/src/massager.ts b/src/massager.ts index 2483214..4674632 100644 --- a/src/massager.ts +++ b/src/massager.ts @@ -1,7 +1,7 @@ import { reorder as sortPaths, elideShorterThan, merge as joinNearbyPaths } from "optimize-paths"; -import { Device, type Plan, type PlanOptions, plan } from "./planning"; -import { dedupPoints, scaleToPaper, cropToMargins } from "./util"; -import { type Vec2, vmul, vrot } from "./vec"; +import { Device, type Plan, type PlanOptions, plan } from "./planning.js"; +import { dedupPoints, scaleToPaper, cropToMargins } from "./util.js"; +import { type Vec2, vmul, vrot } from "./vec.js"; // CSS, and thus SVG, defines 1px = 1/96th of 1in // https://www.w3.org/TR/css-values-4/#absolute-lengths diff --git a/src/paper-size.ts b/src/paper-size.ts index 5345862..e6521df 100644 --- a/src/paper-size.ts +++ b/src/paper-size.ts @@ -1,4 +1,4 @@ -import { type Vec2, vmul } from "./vec"; +import { type Vec2, vmul } from "./vec.js"; function vround(v: Vec2, digits = 2): Vec2 { return { x: Number(v.x.toFixed(digits)), y: Number(v.y.toFixed(digits)) }; diff --git a/src/planning.ts b/src/planning.ts index 7f6fa59..cec4558 100644 --- a/src/planning.ts +++ b/src/planning.ts @@ -1,7 +1,7 @@ // Cribbed from https://github.com/fogleman/axi/blob/master/axi/planner.py -import type { Hardware } from './ebb'; -import { PaperSize } from './paper-size'; -import { vadd, vdot, type Vec2, vlen, vmul, vnorm, vsub } from './vec'; +import type { Hardware } from './ebb.js'; +import { PaperSize } from './paper-size.js'; +import { vadd, vdot, type Vec2, vlen, vmul, vnorm, vsub } from './vec.js'; const epsilon = 1e-9; export interface PlanOptions { diff --git a/src/server.ts b/src/server.ts index a17174a..9283d1d 100644 --- a/src/server.ts +++ b/src/server.ts @@ -9,13 +9,12 @@ import type { PortInfo } from "@serialport/bindings-interface"; import { WakeLock } from "wake-lock"; import type WebSocket from 'ws'; import { WebSocketServer } from 'ws'; -import { SerialPortSerialPort } from "./serialport-serialport"; -import { PenMotion, type Motion, Plan } from "./planning"; -import { formatDuration } from "./util"; +import { SerialPortSerialPort } from "./serialport-serialport.js"; +import { PenMotion, type Motion, Plan } from "./planning.js"; +import { formatDuration } from "./util.js"; import { autoDetect } from '@serialport/bindings-cpp'; -import * as _self from './server'; // use self-import for test mocking - -import { EBB, type Hardware } from './ebb'; +import * as _self from './server.js'; // use self-import for test mocking +import { EBB, type Hardware } from './ebb.js'; type Com = string diff --git a/src/ui.tsx b/src/ui.tsx index 3dffd8f..fcb597e 100644 --- a/src/ui.tsx +++ b/src/ui.tsx @@ -6,11 +6,11 @@ import colormap from "colormap"; import { flattenSVG } from "flatten-svg"; import { PaperSize } from "./paper-size"; -import { Device, Plan, type PlanOptions, defaultPlanOptions, XYMotion, PenMotion } from "./planning"; -import { formatDuration } from "./util"; -import type { Vec2 } from "./vec"; +import { Device, Plan, type PlanOptions, defaultPlanOptions, XYMotion, PenMotion } from "./planning.js"; +import { formatDuration } from "./util.js"; +import type { Vec2 } from "./vec.js"; -import PlanWorker from "./plan.worker"; +import PlanWorker from "./plan.worker.js"; import "./style.css"; diff --git a/src/util.ts b/src/util.ts index ba57f0a..416d06c 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1,5 +1,5 @@ -import type { PaperSize } from "./paper-size"; -import { vadd, type Vec2, vlen2, vmul, vsub } from "./vec"; +import type { PaperSize } from "./paper-size.js"; +import { vadd, type Vec2, vlen2, vmul, vsub } from "./vec.js"; /** Format a smallish duration in 2h30m15s form */ export function formatDuration(seconds: number): string { From 1c8008ac1c64b93a2ea25cb3559bd85c3883cef3 Mon Sep 17 00:00:00 2001 From: Alex Ruddick Date: Sun, 2 Mar 2025 21:55:51 -0600 Subject: [PATCH 3/6] tell jest about the .js imports --- jest.config.cjs | 14 ++++++++++++++ package.json | 3 --- 2 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 jest.config.cjs diff --git a/jest.config.cjs b/jest.config.cjs new file mode 100644 index 0000000..a69c816 --- /dev/null +++ b/jest.config.cjs @@ -0,0 +1,14 @@ +module.exports = { + preset: 'ts-jest', + moduleNameMapper: { + './ebb.js': './ebb.ts', + './massager.js': './massager.ts', + './paper-size.js': './paper-size.ts', + './planning.js': './planning.ts', + './regex-transform-stream.js': './regex-transform-stream.ts', + './serialport-serialport.js': './serialport-serialport.ts', + './server.js': './server.ts', + './util.js': './util.ts', + './vec.js': './vec.ts', + }, +}; diff --git a/package.json b/package.json index 91737a2..a44cb75 100644 --- a/package.json +++ b/package.json @@ -80,9 +80,6 @@ "node": ">=18.0.0" }, "type": "module", - "jest": { - "preset": "ts-jest" - }, "files": [ "/dist", "cli.mjs" From 1665999fe79534cda705b5a18e929d3c99b87341 Mon Sep 17 00:00:00 2001 From: Alex Ruddick Date: Sun, 2 Mar 2025 22:13:31 -0600 Subject: [PATCH 4/6] .cjs -> .mjs --- jest.config.cjs => jest.config.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename jest.config.cjs => jest.config.mjs (95%) diff --git a/jest.config.cjs b/jest.config.mjs similarity index 95% rename from jest.config.cjs rename to jest.config.mjs index a69c816..f971b27 100644 --- a/jest.config.cjs +++ b/jest.config.mjs @@ -1,4 +1,4 @@ -module.exports = { +export default { preset: 'ts-jest', moduleNameMapper: { './ebb.js': './ebb.ts', From a20e102ef44a87238c8bd2217eeff6bd2eb9b0f3 Mon Sep 17 00:00:00 2001 From: Alex Ruddick Date: Mon, 3 Mar 2025 17:23:13 -0600 Subject: [PATCH 5/6] workaround for __dirname not being available in ESM --- src/server.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/server.ts b/src/server.ts index 9283d1d..a174da1 100644 --- a/src/server.ts +++ b/src/server.ts @@ -4,7 +4,6 @@ import type { Response, Request } from "express"; import express from "express"; import http from "node:http"; import type { AddressInfo } from "node:net"; -import path from "node:path"; import type { PortInfo } from "@serialport/bindings-interface"; import { WakeLock } from "wake-lock"; import type WebSocket from 'ws'; @@ -15,6 +14,9 @@ import { formatDuration } from "./util.js"; import { autoDetect } from '@serialport/bindings-cpp'; import * as _self from './server.js'; // use self-import for test mocking import { EBB, type Hardware } from './ebb.js'; +import { fileURLToPath } from 'node:url'; +import { dirname } from 'node:path'; +import path from 'node:path'; type Com = string @@ -24,6 +26,7 @@ const getDeviceInfo = (ebb: EBB | null, com: Com) => { export async function startServer (port: number, hardware: Hardware = 'v3', com: Com = null, enableCors = false, maxPayloadSize = '200mb') { const app = express(); + const __dirname = dirname(fileURLToPath(import.meta.url)); app.use('/', express.static(path.join(__dirname, '..', 'ui'))); app.use(express.json({ limit: maxPayloadSize })); if (enableCors) { From 4b64b9c5af78d39f520dba4b122f30316bcda5e8 Mon Sep 17 00:00:00 2001 From: Alex Ruddick Date: Mon, 3 Mar 2025 18:02:58 -0600 Subject: [PATCH 6/6] another __dirname strategy --- src/server.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/server.ts b/src/server.ts index a174da1..a7df249 100644 --- a/src/server.ts +++ b/src/server.ts @@ -14,8 +14,6 @@ import { formatDuration } from "./util.js"; import { autoDetect } from '@serialport/bindings-cpp'; import * as _self from './server.js'; // use self-import for test mocking import { EBB, type Hardware } from './ebb.js'; -import { fileURLToPath } from 'node:url'; -import { dirname } from 'node:path'; import path from 'node:path'; type Com = string @@ -26,8 +24,7 @@ const getDeviceInfo = (ebb: EBB | null, com: Com) => { export async function startServer (port: number, hardware: Hardware = 'v3', com: Com = null, enableCors = false, maxPayloadSize = '200mb') { const app = express(); - const __dirname = dirname(fileURLToPath(import.meta.url)); - app.use('/', express.static(path.join(__dirname, '..', 'ui'))); + app.use('/', express.static(path.join(path.resolve(), 'dist', 'ui'))); app.use(express.json({ limit: maxPayloadSize })); if (enableCors) { app.use(cors());