From 9e64dc95e26e71a68bc246ccb7b93f0e521582de Mon Sep 17 00:00:00 2001 From: David Chen Date: Tue, 28 Jul 2026 16:54:09 -0700 Subject: [PATCH 1/2] refactor: move node-canvas integration to standalone repo --- package.json | 6 +- .../reactive-layout-node-canvas/CHANGELOG.md | 10 - .../reactive-layout-node-canvas/README.md | 6 - .../reactive-layout-node-canvas/package.json | 55 ----- .../src/index.test.ts | 38 ---- .../reactive-layout-node-canvas/src/index.ts | 104 --------- .../reactive-layout-node-canvas/tsconfig.json | 13 -- pnpm-lock.yaml | 92 ++++---- pnpm-workspace.yaml | 1 - scripts/check-node-canvas-package.mjs | 207 ------------------ 10 files changed, 57 insertions(+), 475 deletions(-) delete mode 100644 packages/reactive-layout-node-canvas/CHANGELOG.md delete mode 100644 packages/reactive-layout-node-canvas/README.md delete mode 100644 packages/reactive-layout-node-canvas/package.json delete mode 100644 packages/reactive-layout-node-canvas/src/index.test.ts delete mode 100644 packages/reactive-layout-node-canvas/src/index.ts delete mode 100644 packages/reactive-layout-node-canvas/tsconfig.json delete mode 100644 scripts/check-node-canvas-package.mjs diff --git a/package.json b/package.json index 7379b89..f740867 100644 --- a/package.json +++ b/package.json @@ -43,11 +43,11 @@ "test:watch": "vitest", "dashboard": "node dashboard/build.mjs", "dashboard:check": "node dashboard/build.mjs --check", - "build:all": "pnpm run build && pnpm --filter './packages/**' --workspace-concurrency=1 run build", - "test:all": "pnpm run test && pnpm --filter './packages/**' --workspace-concurrency=1 run test", + "build:all": "pnpm run build", + "test:all": "pnpm run test", "changeset": "changeset", "version-packages": "changeset version", - "release": "pnpm run build:all && changeset publish" + "release": "pnpm run build && changeset publish" }, "devDependencies": { "@biomejs/biome": "2.4.6", diff --git a/packages/reactive-layout-node-canvas/CHANGELOG.md b/packages/reactive-layout-node-canvas/CHANGELOG.md deleted file mode 100644 index cff6526..0000000 --- a/packages/reactive-layout-node-canvas/CHANGELOG.md +++ /dev/null @@ -1,10 +0,0 @@ -# @graphrefly/reactive-layout-node-canvas - -## 0.1.0 - -### Minor Changes - -- c62333b: Publish the independently owned React, Vue, Solid, Svelte, NestJS and - node-canvas integration packages. Each package consumes the strict - zero-dependency `@graphrefly/ts` core through focused package exports and owns - only its framework or runtime peer boundary. diff --git a/packages/reactive-layout-node-canvas/README.md b/packages/reactive-layout-node-canvas/README.md deleted file mode 100644 index 80737b6..0000000 --- a/packages/reactive-layout-node-canvas/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# @graphrefly/reactive-layout-node-canvas - -Concrete `canvas` package integration for GraphReFly reactive-layout text measurement. - -The framework-neutral `TextMeasureCapability` and caller-owned context helper remain in -`@graphrefly/ts`; this package owns only loading or accepting the third-party `canvas` runtime. diff --git a/packages/reactive-layout-node-canvas/package.json b/packages/reactive-layout-node-canvas/package.json deleted file mode 100644 index 6482bdb..0000000 --- a/packages/reactive-layout-node-canvas/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "@graphrefly/reactive-layout-node-canvas", - "version": "0.1.0", - "description": "node-canvas text measurement provider for GraphReFly reactive layout.", - "repository": { - "type": "git", - "url": "git+https://github.com/graphrefly/graphrefly-react.git", - "directory": "packages/reactive-layout-node-canvas" - }, - "type": "module", - "sideEffects": false, - "main": "dist/index.cjs", - "module": "dist/index.js", - "types": "dist/index.d.ts", - "exports": { - ".": { - "import": { - "types": "./dist/index.d.ts", - "default": "./dist/index.js" - }, - "require": { - "types": "./dist/index.d.cts", - "default": "./dist/index.cjs" - } - } - }, - "files": [ - "dist", - "README.md", - "package.json" - ], - "scripts": { - "build": "tsup src/index.ts --format esm,cjs --dts --clean && node ../../scripts/check-node-canvas-package.mjs .", - "test": "vitest run" - }, - "peerDependencies": { - "@graphrefly/ts": ">=0.6.2 <1.0.0", - "canvas": "^3.2.3" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - }, - "devDependencies": { - "@graphrefly/ts": "0.6.2", - "tsup": "^8.5.1", - "typescript": "^5.7.0", - "vitest": "^4.1.7" - }, - "publishConfig": { - "access": "public" - }, - "license": "MIT" -} diff --git a/packages/reactive-layout-node-canvas/src/index.test.ts b/packages/reactive-layout-node-canvas/src/index.test.ts deleted file mode 100644 index f23202e..0000000 --- a/packages/reactive-layout-node-canvas/src/index.test.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { graph } from "@graphrefly/ts"; -import { describe, expect, it } from "vitest"; -import { nodeCanvasPackageTextMeasurements } from "./index.js"; - -describe("@graphrefly/reactive-layout-node-canvas", () => { - it("uses an injected canvas package and restores the context font", () => { - const g = graph(); - const text = g.state("abc"); - const font = g.state("10px package"); - const context = { - font: "old", - measureText(segment: string) { - return { width: segment.length * (this.font.includes("package") ? 5 : 1) }; - }, - }; - let created: readonly [number, number] | undefined; - const measured = nodeCanvasPackageTextMeasurements({ - graph: g, - text, - font, - width: 2, - height: 3, - canvas: { - createCanvas(width, height) { - created = [width, height]; - return { getContext: () => context }; - }, - }, - }); - const messages: unknown[] = []; - const unsubscribe = measured.subscribe((message) => messages.push(message)); - - expect(created).toEqual([2, 3]); - expect(context.font).toBe("old"); - expect(JSON.stringify(messages)).toContain('"width":15'); - unsubscribe(); - }); -}); diff --git a/packages/reactive-layout-node-canvas/src/index.ts b/packages/reactive-layout-node-canvas/src/index.ts deleted file mode 100644 index 340d134..0000000 --- a/packages/reactive-layout-node-canvas/src/index.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { createRequire } from "node:module"; -import type { Graph, Node } from "@graphrefly/ts"; -import { - capabilityTextMeasurements, - type Measurements, - type SegmentAdapter, - type TextMeasureCapability, -} from "@graphrefly/ts/solutions/reactive-layout"; - -export interface NodeCanvasTextContextLike { - measureText(text: string): { readonly width: number }; - font: string; -} - -export interface NodeCanvasLike { - getContext(type: "2d"): NodeCanvasTextContextLike | null; -} - -export interface NodeCanvasPackageLike { - createCanvas(width: number, height: number): NodeCanvasLike; -} - -export interface NodeCanvasPackageTextMeasurementsOptions { - readonly graph: Graph; - readonly text: Node; - readonly font: Node; - readonly canvas?: NodeCanvasPackageLike; - readonly width?: number; - readonly height?: number; - readonly segmentAdapter?: Node; - readonly targetId?: string; - readonly source?: string; - readonly name?: string; -} - -const requireCanvas = createRequire(typeof __filename === "string" ? __filename : import.meta.url); - -function loadCanvasPackage(): NodeCanvasPackageLike { - try { - return requireCanvas("canvas") as NodeCanvasPackageLike; - } catch (error) { - throw new TypeError( - "nodeCanvasPackageTextMeasurements requires peer package 'canvas'. Install canvas or pass { canvas }.", - { cause: error }, - ); - } -} - -class NodeCanvasPackageTextCapability implements TextMeasureCapability { - private context: NodeCanvasTextContextLike | null = null; - - constructor( - private readonly canvasPackage: NodeCanvasPackageLike | undefined, - private readonly width: number, - private readonly height: number, - ) {} - - private getContext(): NodeCanvasTextContextLike { - if (this.context !== null) return this.context; - const context = (this.canvasPackage ?? loadCanvasPackage()) - .createCanvas(this.width, this.height) - .getContext("2d"); - if (context === null) { - throw new TypeError("nodeCanvasPackageTextMeasurements: failed to create a 2D context"); - } - this.context = context; - return context; - } - - measureText(text: string, font: string): { readonly width: number } { - const context = this.getContext(); - const previousFont = context.font; - context.font = font; - try { - return context.measureText(text); - } finally { - context.font = previousFont; - } - } -} - -export function nodeCanvasPackageTextMeasurements( - opts: NodeCanvasPackageTextMeasurementsOptions, -): Node { - const targetId = opts.targetId ?? "text"; - const capability = opts.graph.state( - new NodeCanvasPackageTextCapability(opts.canvas, opts.width ?? 0, opts.height ?? 0), - { - name: opts.name - ? `${opts.name}:node-canvas-measure-capability` - : `${targetId}-node-canvas-measure-capability`, - }, - ); - return capabilityTextMeasurements({ - graph: opts.graph, - text: opts.text, - font: opts.font, - capability, - segmentAdapter: opts.segmentAdapter, - targetId: opts.targetId, - source: opts.source ?? "nodeCanvasPackageTextMeasurements", - name: opts.name, - }); -} diff --git a/packages/reactive-layout-node-canvas/tsconfig.json b/packages/reactive-layout-node-canvas/tsconfig.json deleted file mode 100644 index 6ed3643..0000000 --- a/packages/reactive-layout-node-canvas/tsconfig.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2022", - "module": "ES2022", - "moduleResolution": "bundler", - "lib": ["ES2022"], - "declaration": true, - "strict": true, - "skipLibCheck": true - }, - "include": ["src"], - "exclude": ["src/**/*.test.ts"] -} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bcf5651..bdc9e2d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,25 +54,6 @@ importers: specifier: ^4.1.7 version: 4.1.7(@types/node@25.9.1)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.27.7)) - packages/reactive-layout-node-canvas: - dependencies: - canvas: - specifier: ^3.2.3 - version: 3.2.3 - devDependencies: - '@graphrefly/ts': - specifier: 0.6.2 - version: 0.6.2(@nestjs/common@11.1.28(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.28)(@nestjs/microservices@11.1.28)(@nestjs/websockets@11.1.28)(canvas@3.2.3)(react@19.2.6)(rxjs@7.8.2)(solid-js@1.9.14)(svelte@5.56.7)(vue@3.5.40(typescript@5.9.3)) - tsup: - specifier: ^8.5.1 - version: 8.5.1(postcss@8.5.22)(typescript@5.9.3) - typescript: - specifier: ^5.7.0 - version: 5.9.3 - vitest: - specifier: ^4.1.7 - version: 4.1.7(@types/node@25.9.1)(jsdom@29.1.1(canvas@3.2.3))(vite@8.0.14(@types/node@25.9.1)(esbuild@0.27.7)) - packages: '@asamuzakjp/css-color@5.1.11': @@ -3170,7 +3151,8 @@ snapshots: axobject-query@4.1.0: optional: true - base64-js@1.5.1: {} + base64-js@1.5.1: + optional: true better-path-resolve@1.0.0: dependencies: @@ -3185,6 +3167,7 @@ snapshots: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 + optional: true body-parser@2.3.0: dependencies: @@ -3212,6 +3195,7 @@ snapshots: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 + optional: true bundle-require@5.1.0(esbuild@0.27.7): dependencies: @@ -3244,6 +3228,7 @@ snapshots: dependencies: node-addon-api: 7.1.1 prebuild-install: 7.1.3 + optional: true chai@6.2.2: {} @@ -3253,7 +3238,8 @@ snapshots: dependencies: readdirp: 4.1.2 - chownr@1.1.4: {} + chownr@1.1.4: + optional: true clsx@2.1.1: optional: true @@ -3324,8 +3310,10 @@ snapshots: decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 + optional: true - deep-extend@0.6.0: {} + deep-extend@0.6.0: + optional: true depd@2.0.0: optional: true @@ -3361,6 +3349,7 @@ snapshots: end-of-stream@1.4.5: dependencies: once: 1.4.0 + optional: true enquirer@2.4.1: dependencies: @@ -3437,7 +3426,8 @@ snapshots: etag@1.8.1: optional: true - expand-template@2.0.3: {} + expand-template@2.0.3: + optional: true expect-type@1.3.0: {} @@ -3539,7 +3529,8 @@ snapshots: fresh@2.0.0: optional: true - fs-constants@1.0.0: {} + fs-constants@1.0.0: + optional: true fs-extra@7.0.1: dependencies: @@ -3579,7 +3570,8 @@ snapshots: es-object-atoms: 1.1.2 optional: true - github-from-package@0.0.0: {} + github-from-package@0.0.0: + optional: true glob-parent@5.1.2: dependencies: @@ -3628,13 +3620,16 @@ snapshots: dependencies: safer-buffer: 2.1.2 - ieee754@1.2.1: {} + ieee754@1.2.1: + optional: true ignore@5.3.2: {} - inherits@2.0.4: {} + inherits@2.0.4: + optional: true - ini@1.3.8: {} + ini@1.3.8: + optional: true ipaddr.js@1.9.1: optional: true @@ -3825,11 +3820,14 @@ snapshots: mime-db: 1.54.0 optional: true - mimic-response@3.1.0: {} + mimic-response@3.1.0: + optional: true - minimist@1.2.8: {} + minimist@1.2.8: + optional: true - mkdirp-classic@0.5.3: {} + mkdirp-classic@0.5.3: + optional: true mlly@1.8.2: dependencies: @@ -3858,7 +3856,8 @@ snapshots: nanoid@3.3.16: {} - napi-build-utils@2.0.0: {} + napi-build-utils@2.0.0: + optional: true negotiator@1.0.0: optional: true @@ -3866,8 +3865,10 @@ snapshots: node-abi@3.94.0: dependencies: semver: 7.8.5 + optional: true - node-addon-api@7.1.1: {} + node-addon-api@7.1.1: + optional: true object-assign@4.1.1: {} @@ -3887,6 +3888,7 @@ snapshots: once@1.4.0: dependencies: wrappy: 1.0.2 + optional: true outdent@0.5.0: {} @@ -3970,6 +3972,7 @@ snapshots: simple-get: 4.0.1 tar-fs: 2.1.5 tunnel-agent: 0.6.0 + optional: true prettier@2.8.8: {} @@ -3989,6 +3992,7 @@ snapshots: dependencies: end-of-stream: 1.4.5 once: 1.4.0 + optional: true punycode@2.3.1: {} @@ -4019,6 +4023,7 @@ snapshots: ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 + optional: true react-dom@19.2.6(react@19.2.6): dependencies: @@ -4041,6 +4046,7 @@ snapshots: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 + optional: true readdirp@4.1.2: {} @@ -4125,7 +4131,8 @@ snapshots: tslib: 2.8.1 optional: true - safe-buffer@5.2.1: {} + safe-buffer@5.2.1: + optional: true safer-buffer@2.1.2: {} @@ -4217,13 +4224,15 @@ snapshots: signal-exit@4.1.0: {} - simple-concat@1.0.1: {} + simple-concat@1.0.1: + optional: true simple-get@4.0.1: dependencies: decompress-response: 6.0.0 once: 1.4.0 simple-concat: 1.0.1 + optional: true slash@3.0.0: {} @@ -4258,6 +4267,7 @@ snapshots: string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 + optional: true strip-ansi@6.0.1: dependencies: @@ -4265,7 +4275,8 @@ snapshots: strip-bom@3.0.0: {} - strip-json-comments@2.0.1: {} + strip-json-comments@2.0.1: + optional: true strtok3@10.3.5: dependencies: @@ -4312,6 +4323,7 @@ snapshots: mkdirp-classic: 0.5.3 pump: 3.0.4 tar-stream: 2.2.0 + optional: true tar-stream@2.2.0: dependencies: @@ -4320,6 +4332,7 @@ snapshots: fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 + optional: true term-size@2.2.1: {} @@ -4410,6 +4423,7 @@ snapshots: tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 + optional: true type-is@1.6.18: dependencies: @@ -4448,7 +4462,8 @@ snapshots: unpipe@1.0.0: optional: true - util-deprecate@1.0.2: {} + util-deprecate@1.0.2: + optional: true vary@1.1.2: optional: true @@ -4529,7 +4544,8 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - wrappy@1.0.2: {} + wrappy@1.0.2: + optional: true xml-name-validator@5.0.0: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 089f7e3..dab8299 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,7 +4,6 @@ packages: - apps/* allowBuilds: '@biomejs/biome': true - canvas: false esbuild: true minimumReleaseAgeExclude: - '@graphrefly/ts@0.0.1||0.6.2' diff --git a/scripts/check-node-canvas-package.mjs b/scripts/check-node-canvas-package.mjs deleted file mode 100644 index 5105c6f..0000000 --- a/scripts/check-node-canvas-package.mjs +++ /dev/null @@ -1,207 +0,0 @@ -import { execFileSync } from "node:child_process"; -import { - cpSync, - existsSync, - mkdirSync, - mkdtempSync, - readFileSync, - rmSync, - symlinkSync, - writeFileSync, -} from "node:fs"; -import { tmpdir } from "node:os"; -import { dirname, join, resolve } from "node:path"; - -const ROOT = resolve(import.meta.dirname, ".."); -const packageRoot = resolve(process.argv[2] ?? "."); -const packageJson = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8")); -const expectedEntries = { - "@graphrefly/reactive-layout-node-canvas": { - ".": ["nodeCanvasPackageTextMeasurements"], - }, -}[packageJson.name]; - -function fail(message) { - throw new Error(`check-node-canvas-package: ${message}`); -} - -function assert(condition, message) { - if (!condition) fail(message); -} - -function resolveInstalledPackage(name) { - const segments = name.split("/"); - const local = join(packageRoot, "node_modules", ...segments); - if (existsSync(local)) return local; - const root = join(ROOT, "node_modules", ...segments); - if (existsSync(root)) return root; - return undefined; -} - -assert(expectedEntries, `unknown ecosystem package: ${packageJson.name}`); -assert(packageJson.dependencies === undefined, "package must not declare runtime dependencies"); -assert( - packageJson.optionalDependencies === undefined, - "package must not declare optional dependencies", -); -assert(packageJson.sideEffects === false, "sideEffects must be false"); - -assert( - JSON.stringify(Object.keys(packageJson.exports ?? {}).sort()) === - JSON.stringify(Object.keys(expectedEntries).sort()), - "package export keys do not match the reviewed entry set", -); -for (const [subpath, expectedExports] of Object.entries(expectedEntries)) { - const entry = packageJson.exports?.[subpath]; - for (const [label, target] of [ - ["ESM", entry?.import?.default], - ["CJS", entry?.require?.default], - ["ESM DTS", entry?.import?.types], - ["CJS DTS", entry?.require?.types], - ]) { - assert(typeof target === "string", `${subpath} ${label} export target must be declared`); - assert( - existsSync(join(packageRoot, target)), - `${subpath} ${label} export target is missing: ${target}`, - ); - } - assert(expectedExports.length > 0, `${subpath} must declare reviewed runtime exports`); -} - -const tmp = mkdtempSync(join(tmpdir(), "graphrefly-ecosystem-package-")); -const externalCwd = mkdtempSync(join(tmpdir(), "graphrefly-ecosystem-external-cwd-")); -try { - const packageInstall = join(tmp, "node_modules", ...packageJson.name.split("/")); - mkdirSync(packageInstall, { recursive: true }); - cpSync(join(packageRoot, "package.json"), join(packageInstall, "package.json")); - cpSync(join(packageRoot, "dist"), join(packageInstall, "dist"), { recursive: true }); - - for (const peerName of Object.keys(packageJson.peerDependencies ?? {})) { - const target = resolveInstalledPackage(peerName); - if (target === undefined) { - assert( - packageJson.peerDependenciesMeta?.[peerName]?.optional === true, - `required peer package is not installed: ${peerName}`, - ); - continue; - } - const link = join(tmp, "node_modules", ...peerName.split("/")); - mkdirSync(dirname(link), { recursive: true }); - symlinkSync(target, link, "dir"); - } - const nodeTypes = resolveInstalledPackage("@types/node"); - assert(nodeTypes !== undefined, "@types/node is required by the consumer type smoke"); - const nodeTypesLink = join(tmp, "node_modules", "@types", "node"); - mkdirSync(dirname(nodeTypesLink), { recursive: true }); - symlinkSync(nodeTypes, nodeTypesLink, "dir"); - if (packageJson.name === "@graphrefly/reactive-layout-node-canvas") { - const canvasInstall = join(tmp, "node_modules", "canvas"); - mkdirSync(canvasInstall, { recursive: true }); - writeFileSync( - join(canvasInstall, "package.json"), - JSON.stringify({ name: "canvas", version: "3.2.3", main: "index.cjs" }), - ); - writeFileSync( - join(canvasInstall, "index.cjs"), - `exports.createCanvas = () => ({ - getContext: () => ({ - font: "", - measureText: (text) => ({ width: text.length * 7 }), - }), -}); -`, - ); - } - - writeFileSync(join(tmp, "package.json"), JSON.stringify({ private: true, type: "module" })); - writeFileSync( - join(tmp, "esm-smoke.mjs"), - `import assert from "node:assert/strict"; -const expectedEntries = ${JSON.stringify(expectedEntries)}; -for (const [subpath, expected] of Object.entries(expectedEntries)) { - const specifier = ${JSON.stringify(packageJson.name)} + (subpath === "." ? "" : subpath.slice(1)); - const sdk = await import(specifier); - for (const name of expected) assert.equal(typeof sdk[name] === "function" || sdk[name] !== undefined, true); -} -`, - ); - writeFileSync( - join(tmp, "cjs-smoke.cjs"), - `const assert = require("node:assert/strict"); -const expectedEntries = ${JSON.stringify(expectedEntries)}; -for (const [subpath, expected] of Object.entries(expectedEntries)) { - const specifier = ${JSON.stringify(packageJson.name)} + (subpath === "." ? "" : subpath.slice(1)); - const sdk = require(specifier); - for (const name of expected) assert.equal(typeof sdk[name] === "function" || sdk[name] !== undefined, true); -} -`, - ); - const esmTypeImports = []; - const cjsTypeImports = []; - let entryIndex = 0; - for (const [subpath, expected] of Object.entries(expectedEntries)) { - const specifier = packageJson.name + (subpath === "." ? "" : subpath.slice(1)); - const bindings = expected.join(", "); - esmTypeImports.push( - `import { ${bindings} } from ${JSON.stringify(specifier)};\nvoid [${bindings}];`, - ); - const namespace = `entry${entryIndex}`; - cjsTypeImports.push( - `import ${namespace} = require(${JSON.stringify(specifier)});\nvoid [${expected - .map((name) => `${namespace}.${name}`) - .join(", ")}];`, - ); - entryIndex += 1; - } - writeFileSync(join(tmp, "esm-types.mts"), `${esmTypeImports.join("\n")}\n`); - writeFileSync(join(tmp, "cjs-types.cts"), `${cjsTypeImports.join("\n")}\n`); - writeFileSync( - join(tmp, "tsconfig.json"), - JSON.stringify({ - compilerOptions: { - module: "NodeNext", - moduleResolution: "NodeNext", - noEmit: true, - strict: true, - skipLibCheck: false, - target: "ES2022", - types: ["node"], - }, - include: ["esm-types.mts", "cjs-types.cts"], - }), - ); - execFileSync(process.execPath, ["esm-smoke.mjs"], { cwd: tmp, stdio: "pipe" }); - execFileSync(process.execPath, ["cjs-smoke.cjs"], { cwd: tmp, stdio: "pipe" }); - execFileSync(join(ROOT, "node_modules", ".bin", "tsc"), ["-p", "tsconfig.json"], { - cwd: tmp, - stdio: "inherit", - }); - if (packageJson.name === "@graphrefly/reactive-layout-node-canvas") { - writeFileSync( - join(tmp, "node-canvas-lazy.mjs"), - `import assert from "node:assert/strict"; -import { graph } from "@graphrefly/ts"; -import { nodeCanvasPackageTextMeasurements } from "@graphrefly/reactive-layout-node-canvas"; -const g = graph(); -const measured = nodeCanvasPackageTextMeasurements({ - graph: g, - text: g.state("abc"), - font: g.state("10px test"), -}); -const messages = []; -const unsubscribe = measured.subscribe((message) => messages.push(message)); -assert.match(JSON.stringify(messages), /"width":21/); -unsubscribe(); -`, - ); - execFileSync(process.execPath, [join(tmp, "node-canvas-lazy.mjs")], { - cwd: externalCwd, - stdio: "pipe", - }); - } -} finally { - rmSync(tmp, { recursive: true, force: true }); - rmSync(externalCwd, { recursive: true, force: true }); -} - -console.log(`check-node-canvas-package: ${packageJson.name} ESM/CJS/DTS smoke passed`); From 1242cc8d168bc2e66e0aa1560038588bd98f4f88 Mon Sep 17 00:00:00 2001 From: David Chen Date: Tue, 28 Jul 2026 16:55:30 -0700 Subject: [PATCH 2/2] fix: retain canvas build denial for ts 0.6.2 --- pnpm-workspace.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index dab8299..089f7e3 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,6 +4,7 @@ packages: - apps/* allowBuilds: '@biomejs/biome': true + canvas: false esbuild: true minimumReleaseAgeExclude: - '@graphrefly/ts@0.0.1||0.6.2'