refactor: Migrate project from Deno to pnpm/Node

Replace Deno configs and workflows with pnpm/Node tooling Add
package.json, jsr.json, build.config.ts and pnpm-lock.yaml Remove
deno.json, deno.lock, Deno build scripts and workflow Move source files
into src/ and update imports and tests to vitest Add Test CI workflow
and adapt publish jobs for pnpm/node Update editor settings, tasks,
.gitignore and bump LICENSE year
This commit is contained in:
Shibo Lyu 2025-11-29 19:11:49 +08:00
parent 7923680e80
commit 32534084df
26 changed files with 2132 additions and 430 deletions

View file

@ -1,41 +0,0 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will install Deno then run `deno lint` and `deno test`.
# For more information see: https://github.com/denoland/setup-deno
name: Deno
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Setup repo
uses: actions/checkout@v4
- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x
# Uncomment this step to verify the use of 'deno fmt' on each commit.
- name: Verify formatting
run: deno fmt --check
- name: Run linter
run: deno lint
- name: Run tests
run: deno test -A

View file

@ -1,7 +1,7 @@
name: Publish on JSR name: Publish on JSR
on: on:
workflow_run: workflow_run:
workflows: ["Deno"] workflows: ["Test"]
types: [completed] types: [completed]
branches: branches:
- "main" - "main"

View file

@ -9,18 +9,18 @@ jobs:
contents: read contents: read
id-token: write id-token: write
steps: steps:
- uses: actions/checkout@v4 - uses: pnpm/action-setup@v4
- name: Setup Deno
uses: denoland/setup-deno@v2
with: with:
deno-version: v2.x run_install: false
# Setup .npmrc file to publish to npm # Setup .npmrc file to publish to npm
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
with: with:
node-version: "22.x" node-version: "24.x"
cache: "pnpm"
registry-url: "https://registry.npmjs.org" registry-url: "https://registry.npmjs.org"
- run: deno run -A scripts/build_npm.ts - run: pnpm install
- run: cd npm && npm ci - run: pnpm build
- run: cd npm && npm publish --provenance --access public - run: pnpm test
- run: pnpm publish --provenance --access public
env: env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

32
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,32 @@
name: Test
on:
push:
branches: ["main"]
pull_request:
branches: ["main"]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Setup repo
uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
run_install: false
- uses: actions/setup-node@v4
with:
node-version: "24.x"
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Run tests
run: pnpm test

3
.gitignore vendored
View file

@ -1,2 +1,3 @@
npm
.DS_Store .DS_Store
node_modules
dist

View file

@ -3,25 +3,6 @@
// For a full list of overridable settings, and general information on folder-specific settings, // For a full list of overridable settings, and general information on folder-specific settings,
// see the documentation: https://zed.dev/docs/configuring-zed#folder-specific-settings // see the documentation: https://zed.dev/docs/configuring-zed#folder-specific-settings
{ {
"languages": { "formatter": "prettier",
"TypeScript": {
"language_servers": ["deno", "!typescript-language-server", "!eslint"],
"formatter": {
"external": {
"command": "deno",
"arguments": ["fmt", "-"]
}
}
},
"TSX": {
"language_servers": ["deno", "!typescript-language-server", "!eslint"],
"formatter": {
"external": {
"command": "deno",
"arguments": ["fmt", "-"]
}
}
}
},
"format_on_save": "on" "format_on_save": "on"
} }

View file

@ -1,10 +1,6 @@
[ [
{ {
"label": "Test", "label": "Test",
"command": "deno test" "command": "pnpm test"
},
{
"label": "Test - Watch",
"command": "deno test --watch"
} }
] ]

View file

@ -1,5 +1,5 @@
The MIT License (MIT) The MIT License (MIT)
Copyright © 2024 Shibo Lyu <hi@lao.sb> Copyright © 2025 Shibo Lyu <hi@lao.sb>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

5
build.config.ts Normal file
View file

@ -0,0 +1,5 @@
import { defineBuildConfig } from "obuild/config";
export default defineBuildConfig({
entries: ["mod.ts"],
});

View file

@ -1,9 +0,0 @@
{
"name": "@textplace/core",
"version": "0.4.1",
"exports": "./mod.ts",
"imports": {
"@deno/dnt": "jsr:@deno/dnt@^0.41.3",
"@std/cli": "jsr:@std/cli@^1.0.11"
}
}

162
deno.lock generated
View file

@ -1,162 +0,0 @@
{
"version": "4",
"specifiers": {
"jsr:@david/code-block-writer@^13.0.2": "13.0.3",
"jsr:@deno/cache-dir@~0.10.3": "0.10.3",
"jsr:@deno/dnt@~0.41.3": "0.41.3",
"jsr:@std/assert@0.223": "0.223.0",
"jsr:@std/assert@0.226": "0.226.0",
"jsr:@std/bytes@0.223": "0.223.0",
"jsr:@std/cli@^1.0.11": "1.0.11",
"jsr:@std/fmt@0.223": "0.223.0",
"jsr:@std/fmt@1": "1.0.3",
"jsr:@std/fs@0.223": "0.223.0",
"jsr:@std/fs@1": "1.0.6",
"jsr:@std/fs@~0.229.3": "0.229.3",
"jsr:@std/io@0.223": "0.223.0",
"jsr:@std/path@0.223": "0.223.0",
"jsr:@std/path@1": "1.0.8",
"jsr:@std/path@1.0.0-rc.1": "1.0.0-rc.1",
"jsr:@std/path@^1.0.8": "1.0.8",
"jsr:@std/path@~0.225.2": "0.225.2",
"jsr:@ts-morph/bootstrap@0.24": "0.24.0",
"jsr:@ts-morph/common@0.24": "0.24.0"
},
"jsr": {
"@david/code-block-writer@13.0.3": {
"integrity": "f98c77d320f5957899a61bfb7a9bead7c6d83ad1515daee92dbacc861e13bb7f"
},
"@deno/cache-dir@0.10.3": {
"integrity": "eb022f84ecc49c91d9d98131c6e6b118ff63a29e343624d058646b9d50404776",
"dependencies": [
"jsr:@std/fmt@0.223",
"jsr:@std/fs@0.223",
"jsr:@std/io",
"jsr:@std/path@0.223"
]
},
"@deno/dnt@0.41.3": {
"integrity": "b2ef2c8a5111eef86cb5bfcae103d6a2938e8e649e2461634a7befb7fc59d6d2",
"dependencies": [
"jsr:@david/code-block-writer",
"jsr:@deno/cache-dir",
"jsr:@std/fmt@1",
"jsr:@std/fs@1",
"jsr:@std/path@1",
"jsr:@ts-morph/bootstrap"
]
},
"@std/assert@0.223.0": {
"integrity": "eb8d6d879d76e1cc431205bd346ed4d88dc051c6366365b1af47034b0670be24"
},
"@std/assert@0.226.0": {
"integrity": "0dfb5f7c7723c18cec118e080fec76ce15b4c31154b15ad2bd74822603ef75b3"
},
"@std/bytes@0.223.0": {
"integrity": "84b75052cd8680942c397c2631318772b295019098f40aac5c36cead4cba51a8"
},
"@std/cli@1.0.11": {
"integrity": "ec219619fdcd31bcf0d8e53bee1e2706ec9a02f70255365a094f69755dadd340"
},
"@std/fmt@0.223.0": {
"integrity": "6deb37794127dfc7d7bded2586b9fc6f5d50e62a8134846608baf71ffc1a5208"
},
"@std/fmt@1.0.3": {
"integrity": "97765c16aa32245ff4e2204ecf7d8562496a3cb8592340a80e7e554e0bb9149f"
},
"@std/fs@0.223.0": {
"integrity": "3b4b0550b2c524cbaaa5a9170c90e96cbb7354e837ad1bdaf15fc9df1ae9c31c"
},
"@std/fs@0.229.3": {
"integrity": "783bca21f24da92e04c3893c9e79653227ab016c48e96b3078377ebd5222e6eb",
"dependencies": [
"jsr:@std/path@1.0.0-rc.1"
]
},
"@std/fs@1.0.6": {
"integrity": "42b56e1e41b75583a21d5a37f6a6a27de9f510bcd36c0c85791d685ca0b85fa2",
"dependencies": [
"jsr:@std/path@^1.0.8"
]
},
"@std/io@0.223.0": {
"integrity": "2d8c3c2ab3a515619b90da2c6ff5ea7b75a94383259ef4d02116b228393f84f1",
"dependencies": [
"jsr:@std/assert@0.223",
"jsr:@std/bytes"
]
},
"@std/path@0.223.0": {
"integrity": "593963402d7e6597f5a6e620931661053572c982fc014000459edc1f93cc3989",
"dependencies": [
"jsr:@std/assert@0.223"
]
},
"@std/path@0.225.2": {
"integrity": "0f2db41d36b50ef048dcb0399aac720a5348638dd3cb5bf80685bf2a745aa506",
"dependencies": [
"jsr:@std/assert@0.226"
]
},
"@std/path@1.0.0-rc.1": {
"integrity": "b8c00ae2f19106a6bb7cbf1ab9be52aa70de1605daeb2dbdc4f87a7cbaf10ff6"
},
"@std/path@1.0.8": {
"integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be"
},
"@ts-morph/bootstrap@0.24.0": {
"integrity": "a826a2ef7fa8a7c3f1042df2c034d20744d94da2ee32bf29275bcd4dffd3c060",
"dependencies": [
"jsr:@ts-morph/common"
]
},
"@ts-morph/common@0.24.0": {
"integrity": "12b625b8e562446ba658cdbe9ad77774b4bd96b992ae8bd34c60dbf24d06c1f3",
"dependencies": [
"jsr:@std/fs@~0.229.3",
"jsr:@std/path@~0.225.2"
]
}
},
"remote": {
"https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975",
"https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
"https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293",
"https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7",
"https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74",
"https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd",
"https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff",
"https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46",
"https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b",
"https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c",
"https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491",
"https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68",
"https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3",
"https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7",
"https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29",
"https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a",
"https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a",
"https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8",
"https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693",
"https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31",
"https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5",
"https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8",
"https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb",
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
"https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47",
"https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68",
"https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3",
"https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73",
"https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19",
"https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5",
"https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6",
"https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2",
"https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e"
},
"workspace": {
"dependencies": [
"jsr:@deno/dnt@~0.41.3",
"jsr:@std/cli@^1.0.11"
]
}
}

6
jsr.json Normal file
View file

@ -0,0 +1,6 @@
{
"name": "@textplace/core",
"version": "0.5.0",
"exports": "./src/mod.ts",
"include": ["src/**/*.ts", "LICENSE", "README.md"]
}

34
package.json Normal file
View file

@ -0,0 +1,34 @@
{
"name": "@textplace/core",
"version": "0.5.0",
"description": "The core logic of TextPlace.",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/TextPlace/CoreTextPlace"
},
"bugs": {
"url": "https://github.com/TextPlace/CoreTextPlace/issues"
},
"scripts": {
"test": "vitest",
"build": "obuild"
},
"files": [
"dist"
],
"exports": {
".": {
"types": "./dist/mod.d.ts",
"import": "./dist/mod.mjs"
}
},
"dependencies": {
"@std/cli": "jsr:^1.0.24"
},
"devDependencies": {
"obuild": "^0.4.3",
"prettier": "^3.7.2",
"vitest": "^4.0.14"
}
}

1905
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,36 +0,0 @@
import { build, emptyDir } from "@deno/dnt";
import denoJson from "../deno.json" with { type: "json" };
await emptyDir("./npm");
await build({
entryPoints: ["./mod.ts"],
outDir: "./npm",
shims: {
// see JS docs for overview and more options
deno: true,
},
compilerOptions: {
lib: ["ES2022"],
},
package: {
// package.json properties
name: "@textplace/core",
version: denoJson.version,
description: "The core logic of TextPlace.",
license: "MIT",
repository: {
type: "git",
url: "https://github.com/TextPlace/CoreTextPlace",
},
bugs: {
url: "https://github.com/TextPlace/CoreTextPlace/issues",
},
},
postBuild() {
// steps to run after building and before running the tests
Deno.copyFileSync("LICENSE", "npm/LICENSE");
Deno.copyFileSync("README.md", "npm/README.md");
},
});

View file

View file

@ -1,22 +1,19 @@
import { import { describe, it, expect } from "vitest";
assert,
assertEquals,
} from "https://deno.land/std@0.224.0/assert/mod.ts";
import { import {
createBoard, createBoard,
getSectionOnBoard, getSectionOnBoard,
renderFullBoard, renderFullBoard,
} from "../logic/board.ts"; } from "../logic/board.ts";
import type { BoardData } from "../types/board.ts"; import type { BoardData } from "../src/types/board.ts";
import { checkFullBoard } from "./checkFullBoard.ts"; import { checkFullBoard } from "./checkFullBoard.ts";
import { locateSection } from "../logic/board.ts"; import { locateSection } from "../src/logic/board.ts";
import { applyChangeOnBoard } from "../logic/board.ts"; import { applyChangeOnBoard } from "../src/logic/board.ts";
Deno.test("board", async (t) => { describe("board", () => {
let board: BoardData | undefined; let board: BoardData | undefined;
await t.step("createBoard", () => { it("createBoard", () => {
board = createBoard({ board = createBoard({
xSections: 3, xSections: 3,
ySections: 3, ySections: 3,
@ -29,96 +26,96 @@ Deno.test("board", async (t) => {
}); });
// Sections are created on demand. // Sections are created on demand.
assertEquals(board.sections.length, 0); expect(board.sections.length).toBe(0);
}); });
await t.step("locateSection", () => { it("locateSection", () => {
assert(board); expect(board).toBeDefined();
const { sx, sy } = locateSection({ x: 0, y: 0 }, board.config); const { sx, sy } = locateSection({ x: 0, y: 0 }, board!.config);
assertEquals(sx, 0); expect(sx).toBe(0);
assertEquals(sy, 0); expect(sy).toBe(0);
const { sx: sx2, sy: sy2 } = locateSection({ x: 4, y: 0 }, board.config); const { sx: sx2, sy: sy2 } = locateSection({ x: 4, y: 0 }, board!.config);
assertEquals(sx2, 1); expect(sx2).toBe(1);
assertEquals(sy2, 0); expect(sy2).toBe(0);
}); });
await t.step("applyChangeOnBoard", () => { it("applyChangeOnBoard", () => {
assert(board); expect(board).toBeDefined();
applyChangeOnBoard({ x: 0, y: 0, ch: "A" }, board); applyChangeOnBoard({ x: 0, y: 0, ch: "A" }, board!);
applyChangeOnBoard({ x: 4, y: 0, ch: "B" }, board); applyChangeOnBoard({ x: 4, y: 0, ch: "B" }, board!);
applyChangeOnBoard({ x: 0, y: 3, ch: "C" }, board); applyChangeOnBoard({ x: 0, y: 3, ch: "C" }, board!);
applyChangeOnBoard({ x: 4, y: 3, ch: "D" }, board); applyChangeOnBoard({ x: 4, y: 3, ch: "D" }, board!);
applyChangeOnBoard({ x: 5, y: 3, ch: "E" }, board); applyChangeOnBoard({ x: 5, y: 3, ch: "E" }, board!);
assertEquals(board.sections[0][0].ch[0][0], "A"); expect(board!.sections[0][0].ch[0][0]).toBe("A");
assertEquals(board.sections[0][1].ch[0][0], "B"); expect(board!.sections[0][1].ch[0][0]).toBe("B");
assertEquals(board.sections[1][0].ch[0][0], "C"); expect(board!.sections[1][0].ch[0][0]).toBe("C");
assertEquals(board.sections[1][1].ch[0], ["D", "E", " ", " "]); expect(board!.sections[1][1].ch[0]).toEqual(["D", "E", " ", " "]);
applyChangeOnBoard({ x: 0, y: 1, ch: "你" }, board); applyChangeOnBoard({ x: 0, y: 1, ch: "你" }, board!);
applyChangeOnBoard({ x: 4, y: 2, ch: "好" }, board); applyChangeOnBoard({ x: 4, y: 2, ch: "好" }, board!);
applyChangeOnBoard({ x: 0, y: 4, ch: "嗎" }, board); applyChangeOnBoard({ x: 0, y: 4, ch: "嗎" }, board!);
applyChangeOnBoard({ x: 4, y: 4, ch: "嘛" }, board); applyChangeOnBoard({ x: 4, y: 4, ch: "嘛" }, board!);
assertEquals(board.sections[0][0].ch[1][0], "你"); expect(board!.sections[0][0].ch[1][0]).toBe("你");
assertEquals(board.sections[0][1].ch[2][0], "好"); expect(board!.sections[0][1].ch[2][0]).toBe("好");
assertEquals(board.sections[1][0].ch[1][0], "嗎"); expect(board!.sections[1][0].ch[1][0]).toBe("嗎");
assertEquals(board.sections[1][1].ch[1], ["嘛", " ", " ", " "]); expect(board!.sections[1][1].ch[1]).toEqual(["嘛", " ", " ", " "]);
applyChangeOnBoard({ x: 5, y: 4, ch: "啊" }, board); applyChangeOnBoard({ x: 5, y: 4, ch: "啊" }, board!);
assertEquals(board.sections[1][1].ch[1], ["啊", " ", " ", " "]); expect(board!.sections[1][1].ch[1]).toEqual(["啊", " ", " ", " "]);
}); });
await t.step("getSectionOnBoard: existing section", () => { it("getSectionOnBoard: existing section", () => {
assert(board); expect(board).toBeDefined();
const section = getSectionOnBoard({ sx: 1, sy: 1 }, board, { const section = getSectionOnBoard({ sx: 1, sy: 1 }, board!, {
readOnly: true, readOnly: true,
}); });
assertEquals(section.ch[0], ["D", "E", " ", " "]); expect(section.ch[0]).toEqual(["D", "E", " ", " "]);
assertEquals(section.color[0][0], "F"); expect(section.color[0][0]).toBe("F");
assertEquals(section.bgColor[0][0], "0"); expect(section.bgColor[0][0]).toBe("0");
assertEquals(section.width[0], [1, 1, 1, 1]); expect(section.width[0]).toEqual([1, 1, 1, 1]);
}); });
await t.step("getSectionOnBoard: non-existing row", () => { it("getSectionOnBoard: non-existing row", () => {
assert(board); expect(board).toBeDefined();
const section = getSectionOnBoard({ sx: 1, sy: 2 }, board, { const section = getSectionOnBoard({ sx: 1, sy: 2 }, board!, {
readOnly: true, readOnly: true,
}); });
assertEquals(section.ch[0][0], " "); expect(section.ch[0][0]).toBe(" ");
assertEquals(section.color[0][0], "F"); expect(section.color[0][0]).toBe("F");
assertEquals(section.bgColor[0][0], "0"); expect(section.bgColor[0][0]).toBe("0");
assertEquals(section.width[0][0], 1); expect(section.width[0][0]).toBe(1);
}); });
await t.step("getSectionOnBoard: non-existing section", () => { it("getSectionOnBoard: non-existing section", () => {
assert(board); expect(board).toBeDefined();
const section = getSectionOnBoard({ sx: 2, sy: 1 }, board, { const section = getSectionOnBoard({ sx: 2, sy: 1 }, board!, {
readOnly: true, readOnly: true,
}); });
assertEquals(section.ch[0][0], " "); expect(section.ch[0][0]).toBe(" ");
assertEquals(section.color[0][0], "F"); expect(section.color[0][0]).toBe("F");
assertEquals(section.bgColor[0][0], "0"); expect(section.bgColor[0][0]).toBe("0");
assertEquals(section.width[0][0], 1); expect(section.width[0][0]).toBe(1);
}); });
await t.step("renderFullBoard", () => { it("renderFullBoard", () => {
assert(board); expect(board).toBeDefined();
const rendered = renderFullBoard(board); const rendered = renderFullBoard(board!);
checkFullBoard(rendered); checkFullBoard(rendered);
}); });
await t.step("on-demand creation: only changed sections are saved", () => { it("on-demand creation: only changed sections are saved", () => {
assert(board); expect(board).toBeDefined();
assertEquals(board.sections[2], undefined); expect(board!.sections[2]).toBeUndefined();
assertEquals(board.sections[0][2], undefined); expect(board!.sections[0][2]).toBeUndefined();
}); });
}); });

View file

@ -1,43 +1,40 @@
import { import { it, expect } from "vitest";
assertEquals,
assertThrows,
} from "https://deno.land/std@0.224.0/assert/mod.ts";
import { getCharacterWidth } from "../mod.ts"; import { getCharacterWidth } from "../src/mod.ts";
Deno.test("getCharacterWidth ASCII", () => { it("getCharacterWidth ASCII", () => {
assertEquals(getCharacterWidth("a"), 1); expect(getCharacterWidth("a")).toBe(1);
assertEquals(getCharacterWidth("A"), 1); expect(getCharacterWidth("A")).toBe(1);
assertEquals(getCharacterWidth("1"), 1); expect(getCharacterWidth("1")).toBe(1);
assertEquals(getCharacterWidth("@"), 1); expect(getCharacterWidth("@")).toBe(1);
assertEquals(getCharacterWidth(" "), 1); expect(getCharacterWidth(" ")).toBe(1);
assertThrows(() => getCharacterWidth("")); expect(() => getCharacterWidth("")).toThrow();
assertThrows(() => getCharacterWidth("ab")); expect(() => getCharacterWidth("ab")).toThrow();
}); });
Deno.test("getCharacterWidth CJK", () => { it("getCharacterWidth CJK", () => {
assertEquals(getCharacterWidth("你"), 2); expect(getCharacterWidth("你")).toBe(2);
assertEquals(getCharacterWidth("好"), 2); expect(getCharacterWidth("好")).toBe(2);
assertEquals(getCharacterWidth("吗"), 2); expect(getCharacterWidth("吗")).toBe(2);
assertEquals(getCharacterWidth("ガ"), 2); expect(getCharacterWidth("ガ")).toBe(2);
assertEquals(getCharacterWidth("ギ"), 2); expect(getCharacterWidth("ギ")).toBe(2);
assertEquals(getCharacterWidth("グ"), 2); expect(getCharacterWidth("グ")).toBe(2);
assertEquals(getCharacterWidth("ソ"), 2); expect(getCharacterWidth("ソ")).toBe(2);
assertEquals(getCharacterWidth(""), 2); expect(getCharacterWidth("")).toBe(2);
assertEquals(getCharacterWidth(""), 2); expect(getCharacterWidth("")).toBe(2);
assertThrows(() => getCharacterWidth("你好")); expect(() => getCharacterWidth("你好")).toThrow();
assertThrows(() => getCharacterWidth("ヨスガノ")); expect(() => getCharacterWidth("ヨスガノ")).toThrow();
}); });
Deno.test("getCharacterWidth Emoji", () => { it("getCharacterWidth Emoji", () => {
assertEquals(getCharacterWidth("👋"), 2); expect(getCharacterWidth("👋")).toBe(2);
assertEquals(getCharacterWidth("🌲️"), 2); expect(getCharacterWidth("🌲️")).toBe(2);
assertEquals(getCharacterWidth("👨‍👩‍👧‍👦"), 2); expect(getCharacterWidth("👨‍👩‍👧‍👦")).toBe(2);
}); });
Deno.test("getCharacterWidth previously faulty cases", () => { it("getCharacterWidth previously faulty cases", () => {
assertEquals(getCharacterWidth("𤲶"), 2); expect(getCharacterWidth("𤲶")).toBe(2);
}); });

View file

@ -1,5 +1,5 @@
import { getCharacterWidth } from "../logic/character.ts"; import { getCharacterWidth } from "../src/logic/character.ts";
import type { FullBoard } from "../types/board.ts"; import type { FullBoard } from "../src/types/board.ts";
function isCorrectWidth(cWd: number, cCh: string): boolean { function isCorrectWidth(cWd: number, cCh: string): boolean {
return getCharacterWidth(cCh) === cWd; return getCharacterWidth(cCh) === cWd;

View file

@ -1,17 +1,13 @@
import { import { describe, it, expect } from "vitest";
assert,
assertEquals,
assertThrows,
} from "https://deno.land/std@0.224.0/assert/mod.ts";
import { applyChange, createSection } from "../logic/section.ts"; import { applyChange, createSection } from "../src/logic/section.ts";
import type { SectionData } from "../types/section.ts"; import type { SectionData } from "../src/types/section.ts";
Deno.test("section", async (t) => { describe("section", () => {
let section: SectionData | undefined; let section: SectionData | undefined;
await t.step("createSection non-lcm", () => { it("createSection non-lcm", () => {
assertThrows(() => { expect(() => {
createSection( createSection(
{ sx: 0, sy: 0 }, { sx: 0, sy: 0 },
{ {
@ -25,10 +21,10 @@ Deno.test("section", async (t) => {
defaultWidth: 1, defaultWidth: 1,
}, },
); );
}); }).toThrow();
}); });
await t.step("createSection non-origin section", () => { it("createSection non-origin section", () => {
section = createSection( section = createSection(
{ sx: 1, sy: 1 }, { sx: 1, sy: 1 },
{ {
@ -43,11 +39,11 @@ Deno.test("section", async (t) => {
}, },
); );
assertEquals(section.offsetX, 4); expect(section.offsetX).toBe(4);
assertEquals(section.offsetY, 3); expect(section.offsetY).toBe(3);
}); });
await t.step("createSection", () => { it("createSection", () => {
section = createSection( section = createSection(
{ sx: 0, sy: 0 }, { sx: 0, sy: 0 },
{ {
@ -62,8 +58,8 @@ Deno.test("section", async (t) => {
}, },
); );
assertEquals(section.offsetX, 0); expect(section.offsetX).toBe(0);
assertEquals(section.offsetY, 0); expect(section.offsetY).toBe(0);
function assertSectionContent<T>( function assertSectionContent<T>(
content: T[][], content: T[][],
@ -71,11 +67,11 @@ Deno.test("section", async (t) => {
columnCount: number, columnCount: number,
value: T, value: T,
) { ) {
assertEquals(content.length, rowCount); expect(content.length).toBe(rowCount);
for (const row of content) { for (const row of content) {
assertEquals(row.length, columnCount); expect(row.length).toBe(columnCount);
for (const item of row) { for (const item of row) {
assertEquals(item, value); expect(item).toBe(value);
} }
} }
} }
@ -86,44 +82,44 @@ Deno.test("section", async (t) => {
assertSectionContent(section.width, 3, 4, 1); assertSectionContent(section.width, 3, 4, 1);
}); });
await t.step("applyChange 1-width", () => { it("applyChange 1-width", () => {
assert(section); expect(section).toBeDefined();
applyChange({ x: 0, y: 0, ch: "t" }, section); applyChange({ x: 0, y: 0, ch: "t" }, section!);
assertEquals(section.ch[0], ["t", " ", " ", " "]); expect(section!.ch[0]).toEqual(["t", " ", " ", " "]);
assertEquals(section.ch[1], [" ", " ", " ", " "]); expect(section!.ch[1]).toEqual([" ", " ", " ", " "]);
assertEquals(section.width[0], [1, 1, 1, 1]); expect(section!.width[0]).toEqual([1, 1, 1, 1]);
}); });
await t.step("applyChange 1-width at odd position", () => { it("applyChange 1-width at odd position", () => {
assert(section); expect(section).toBeDefined();
applyChange({ x: 1, y: 0, ch: "t" }, section); applyChange({ x: 1, y: 0, ch: "t" }, section!);
assertEquals(section.ch[0], ["t", "t", " ", " "]); expect(section!.ch[0]).toEqual(["t", "t", " ", " "]);
assertEquals(section.width[0], [1, 1, 1, 1]); expect(section!.width[0]).toEqual([1, 1, 1, 1]);
}); });
await t.step("applyChange 2-width at a correct position", () => { it("applyChange 2-width at a correct position", () => {
assert(section); expect(section).toBeDefined();
applyChange({ x: 0, y: 0, ch: "あ" }, section); applyChange({ x: 0, y: 0, ch: "あ" }, section!);
assertEquals(section.ch[0], ["あ", "t", " ", " "]); expect(section!.ch[0]).toEqual(["あ", "t", " ", " "]);
assertEquals(section.width[0], [2, 1, 1, 1]); expect(section!.width[0]).toEqual([2, 1, 1, 1]);
}); });
await t.step("applyChange 2-width at an alternate position", () => { it("applyChange 2-width at an alternate position", () => {
assert(section); expect(section).toBeDefined();
applyChange({ x: 1, y: 0, ch: "あ" }, section); applyChange({ x: 1, y: 0, ch: "あ" }, section!);
assertEquals(section.ch[0], ["あ", "t", " ", " "]); expect(section!.ch[0]).toEqual(["あ", "t", " ", " "]);
assertEquals(section.width[0], [2, 1, 1, 1]); expect(section!.width[0]).toEqual([2, 1, 1, 1]);
}); });
await t.step("applyChange incorrect section", () => { it("applyChange incorrect section", () => {
assertThrows(() => { expect(section).toBeDefined();
assert(section);
applyChange({ x: 6, y: 3, ch: "あ" }, section); expect(() => {
}); applyChange({ x: 6, y: 3, ch: "あ" }, section!);
}).toThrow();
}); });
}); });