From 874267cd506800dac471c4d133e1532623c361af Mon Sep 17 00:00:00 2001 From: Lexus Drumgold Date: Sun, 30 Jun 2024 15:30:06 -0400 Subject: [PATCH] chore(tests): refactor token snaphot serializer Signed-off-by: Lexus Drumgold --- __tests__/serializers/token.ts | 58 +------- __tests__/utils/inspect.ts | 68 +++++++++ __tests__/utils/is-point.ts | 29 ++++ __tests__/utils/is-token.ts | 44 ++++++ package.json | 3 +- src/__snapshots__/tokenize.integration.snap | 144 ++++++++++---------- yarn.lock | 51 +++++-- 7 files changed, 262 insertions(+), 135 deletions(-) create mode 100644 __tests__/utils/inspect.ts create mode 100644 __tests__/utils/is-point.ts create mode 100644 __tests__/utils/is-token.ts diff --git a/__tests__/serializers/token.ts b/__tests__/serializers/token.ts index 962d6f8..77a1f94 100644 --- a/__tests__/serializers/token.ts +++ b/__tests__/serializers/token.ts @@ -4,23 +4,12 @@ * @see https://vitest.dev/guide/snapshot */ -import type { Position, Token } from '#src/interfaces' -import { isObjectCurly } from '@flex-development/tutils' +import type { Token } from '#src/interfaces' +import inspect from '#tests/utils/inspect' +import isToken from '#tests/utils/is-token' import { ok } from 'devlop' -import { stringifyPosition as position } from 'unist-util-stringify-position' import type { SnapshotSerializer } from 'vitest' -/** - * Check if `value` is a {@linkcode Token}. - * - * @param {unknown} value - Value to check - * @return {value is Token} `true` if `value` is a token - */ -function test(value: unknown): value is Token { - if (!isObjectCurly(value)) return false - return 'end' in value && 'type' in value && 'start' in value -} - /** * Token snapshot serializer. * @@ -34,49 +23,14 @@ const serializer: SnapshotSerializer = { * @return {string} Snapshot value */ print(value: unknown): string { - ok(test(value), 'expected token') - - /** - * Index of current token. - * - * @var {number} index - */ - let index: number = -1 - - /** - * Snapshot value. - * - * @var {string} snapshot - */ - let snapshot: string = '' - - /** - * Current token. - * - * @var {Token | undefined} token - */ - let token: Token | undefined = value - - // get snapshot value - while (token) { - /** - * Token range. - * - * @const {Position} range - */ - const range: Position = { end: token.end, start: token.start } - - snapshot += `\t├─${++index} ${token.type} (${position(range)})\n` - token = token.next - } - - return `tokens[${index + 1}]\n\t${snapshot.trim()}` + ok(isToken(value), 'expected token') + return inspect(value) }, /** * Check if the given value is a {@linkcode Token}. */ - test + test: isToken } export default serializer diff --git a/__tests__/utils/inspect.ts b/__tests__/utils/inspect.ts new file mode 100644 index 0000000..97636ba --- /dev/null +++ b/__tests__/utils/inspect.ts @@ -0,0 +1,68 @@ +/** + * @file Test Utilities - inspect + * @module tests/utils/inspect + */ + +import type { Token } from '#src/interfaces' +import { omit } from '@flex-development/tutils' +import { u } from '@flex-development/unist-util-builder' +import * as i from '@flex-development/unist-util-inspect' +import type { Literal, Node } from 'unist' + +export default inspect + +/** + * Inspect a token list. + * + * @see {@linkcode i.Options} + * @see {@linkcode Token} + * + * @param {Token} token - Head token + * @param {(i.Options | null)?} [options] - Configuration options + * @return {string} Pretty printed token list + */ +function inspect(token: Token, options?: i.Options | null): string { + return i.inspectNoColor(u('tokens', nodes(token)), options) +} + +/** + * Convert a token to a list of nodes. + * + * @internal + * + * @param {Token} token - Head token + * @return {(Literal | Node)[]} Node list + */ +function nodes(token: Token): (Literal | Node)[] { + /** + * Node list. + * + * @const {(Literal | Node)[]} list + */ + const list: (Literal | Node)[] = [] + + /** + * Current token. + * + * @var {Token | undefined} tok + */ + let tok: Token | undefined = token + + // build list + while (tok) { + /** + * New node. + * + * @const {Literal | Node} node + */ + const node: Literal | Node = u(tok.type, { + ...omit(tok, ['end', 'next', 'previous', 'start']), + position: { end: tok.end, start: tok.start } + }) + + list.push(node) + tok = tok.next + } + + return list +} diff --git a/__tests__/utils/is-point.ts b/__tests__/utils/is-point.ts new file mode 100644 index 0000000..fc75dbf --- /dev/null +++ b/__tests__/utils/is-point.ts @@ -0,0 +1,29 @@ +/** + * @file Test Utilities - isPoint + * @module tests/utils/isPoint + */ + +import type { Point } from '@flex-development/vfile-reader' + +/** + * Check if the specified `value` is a point. + * + * @see {@linkcode Point} + * + * @param {unknown} value - Value to check + * @return {value is Point} `true` if `value` is point + */ +function isPoint(value: unknown): value is Point { + return ( + typeof value === 'object' && + value !== null && + 'column' in value && + 'line' in value && + 'offset' in value && + typeof value.column === 'number' && + typeof value.line === 'number' && + typeof value.offset === 'number' + ) +} + +export default isPoint diff --git a/__tests__/utils/is-token.ts b/__tests__/utils/is-token.ts new file mode 100644 index 0000000..c76b2e1 --- /dev/null +++ b/__tests__/utils/is-token.ts @@ -0,0 +1,44 @@ +/** + * @file Test Utilities - isToken + * @module tests/utils/isToken + */ + +import type { Token } from '#src/interfaces' +import isPoint from './is-point' + +/** + * Check if the specified `value` is a token. + * + * @see {@linkcode Token} + * + * @param {unknown} value - Value to check + * @return {value is Token} `true` if `value` is token + */ +function isToken(value: unknown): value is Token { + return ( + check(value) && + (value.next === undefined || check(value.next)) && + (value.previous === undefined || check(value.previous)) + ) + + /** + * Check if `value` is token like. + * + * @param {unknown} value - Value to check + * @return {value is Token} `true` if `value` is token like + */ + function check(value: unknown): value is Token { + return ( + typeof value === 'object' && + value !== null && + 'end' in value && + 'type' in value && + 'start' in value && + isPoint(value.end) && + isPoint(value.start) && + typeof value.type === 'string' + ) + } +} + +export default isToken diff --git a/package.json b/package.json index 6de2427..e2bf358 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,8 @@ "@flex-development/mlly": "1.0.0-alpha.18", "@flex-development/pathe": "2.0.0", "@flex-development/tutils": "6.0.0-alpha.25", + "@flex-development/unist-util-builder": "2.0.0", + "@flex-development/unist-util-inspect": "1.0.0", "@stylistic/eslint-plugin": "2.2.2", "@types/chai": "4.3.16", "@types/eslint": "8.56.10", @@ -146,7 +148,6 @@ "ts-dedent": "2.2.0", "typescript": "5.5.2", "typescript-eslint": "8.0.0-alpha.30", - "unist-util-stringify-position": "4.0.0", "vite-tsconfig-paths": "4.3.2", "vitest": "2.0.0-beta.11", "yaml-eslint-parser": "1.2.3" diff --git a/src/__snapshots__/tokenize.integration.snap b/src/__snapshots__/tokenize.integration.snap index d8e4286..2233e28 100644 --- a/src/__snapshots__/tokenize.integration.snap +++ b/src/__snapshots__/tokenize.integration.snap @@ -2,100 +2,100 @@ exports[`integration:tokenize > default constructs > should tokenize empty file 1`] = ` tokens[2] - ├─0 sof (1:1-1:1) - ├─1 eof (1:1-1:1) +├─0 sof (1:1-1:1, 0-0) +└─1 eof (1:1-1:1, 0-0) `; exports[`integration:tokenize > default constructs > should tokenize non-empty file 1`] = ` tokens[2] - ├─0 sof (1:1-1:1) - ├─1 eof (22:1-22:1) +├─0 sof (1:1-1:1, 0-0) +└─1 eof (22:1-22:1, 304-304) `; exports[`integration:tokenize > user constructs > non-empty file > sample 0 1`] = ` tokens[3] - ├─0 sof (1:1-1:1) - ├─1 inlineTag (1:1-1:17) - ├─2 eof (2:1-2:1) +├─0 sof (1:1-1:1, 0-0) +├─1 inlineTag (1:1-1:17, 0-16) +└─2 eof (2:1-2:1, 17-17) `; exports[`integration:tokenize > user constructs > non-empty file > sample 1 1`] = ` tokens[13] - ├─0 sof (1:1-1:1) - ├─1 punctuator (1:10-1:11) - ├─2 string (1:12-1:21) - ├─3 punctuator (1:21-1:22) - ├─4 punctuator (2:8-2:9) - ├─5 punctuator (2:12-2:13) - ├─6 punctuator (2:13-2:14) - ├─7 punctuator (2:19-2:20) - ├─8 punctuator (2:25-2:26) - ├─9 punctuator (2:26-2:27) - ├─10 punctuator (2:28-2:29) - ├─11 punctuator (2:29-2:30) - ├─12 eof (3:1-3:1) +├─0 sof (1:1-1:1, 0-0) +├─1 punctuator "=" (1:10-1:11, 9-10) +├─2 string "\\"hello 👋\\"" (1:12-1:21, 11-20) +├─3 punctuator ";" (1:21-1:22, 20-21) +├─4 punctuator "." (2:8-2:9, 29-30) +├─5 punctuator "(" (2:12-2:13, 33-34) +├─6 punctuator "\\\\" (2:13-2:14, 34-35) +├─7 punctuator "\\\\" (2:19-2:20, 40-41) +├─8 punctuator ")" (2:25-2:26, 46-47) +├─9 punctuator ";" (2:26-2:27, 47-48) +├─10 punctuator "/" (2:28-2:29, 49-50) +├─11 punctuator "/" (2:29-2:30, 50-51) +└─12 eof (3:1-3:1, 60-60) `; exports[`integration:tokenize > user constructs > non-empty file > sample 2 1`] = ` tokens[8] - ├─0 sof (1:1-1:1) - ├─1 string (1:1-1:4) - ├─2 string (2:1-2:4) - ├─3 punctuator (3:1-3:2) - ├─4 punctuator (3:2-3:3) - ├─5 punctuator (3:4-3:5) - ├─6 punctuator (3:5-3:6) - ├─7 eof (4:1-4:1) +├─0 sof (1:1-1:1, 0-0) +├─1 string "'😍'" (1:1-1:4, 0-3) +├─2 string "\\"👍\\"" (2:1-2:4, 4-7) +├─3 punctuator "\\\\" (3:1-3:2, 8-9) +├─4 punctuator "'" (3:2-3:3, 9-10) +├─5 punctuator "\\\\" (3:4-3:5, 11-12) +├─6 punctuator "'" (3:5-3:6, 12-13) +└─7 eof (4:1-4:1, 14-14) `; exports[`integration:tokenize > user constructs > non-empty file > sample 3 1`] = ` tokens[42] - ├─0 sof (1:1-1:1) - ├─1 number (1:1-1:2) - ├─2 whitespace (1:2-2:1) - ├─3 bigint (2:1-2:3) - ├─4 whitespace (2:3-3:1) - ├─5 number (3:1-3:2) - ├─6 whitespace (3:2-4:1) - ├─7 bigint (4:1-4:3) - ├─8 whitespace (4:3-5:1) - ├─9 number (5:1-5:2) - ├─10 whitespace (5:2-6:1) - ├─11 bigint (6:1-6:3) - ├─12 whitespace (6:3-7:1) - ├─13 number (7:1-7:2) - ├─14 whitespace (7:2-8:1) - ├─15 bigint (8:1-8:3) - ├─16 whitespace (8:3-9:1) - ├─17 number (9:1-9:2) - ├─18 whitespace (9:2-10:1) - ├─19 bigint (10:1-10:3) - ├─20 whitespace (10:3-11:1) - ├─21 number (11:1-11:2) - ├─22 whitespace (11:2-12:1) - ├─23 bigint (12:1-12:3) - ├─24 whitespace (12:3-13:1) - ├─25 number (13:1-13:2) - ├─26 whitespace (13:2-14:1) - ├─27 bigint (14:1-14:3) - ├─28 whitespace (14:3-15:1) - ├─29 number (15:1-15:2) - ├─30 whitespace (15:2-16:1) - ├─31 bigint (16:1-16:3) - ├─32 whitespace (16:3-17:1) - ├─33 number (17:1-17:2) - ├─34 whitespace (17:2-18:1) - ├─35 bigint (18:1-18:3) - ├─36 whitespace (18:3-19:1) - ├─37 number (19:1-19:2) - ├─38 whitespace (19:2-20:1) - ├─39 bigint (20:1-20:3) - ├─40 whitespace (20:3-21:1) - ├─41 eof (21:1-21:1) +├─0 sof (1:1-1:1, 0-0) +├─1 number "0" (1:1-1:2, 0-1) +├─2 whitespace "\\n" (1:2-2:1, 1-2) +├─3 bigint "0n" (2:1-2:3, 2-4) +├─4 whitespace "\\n" (2:3-3:1, 4-5) +├─5 number "1" (3:1-3:2, 5-6) +├─6 whitespace "\\n" (3:2-4:1, 6-7) +├─7 bigint "1n" (4:1-4:3, 7-9) +├─8 whitespace "\\n" (4:3-5:1, 9-10) +├─9 number "2" (5:1-5:2, 10-11) +├─10 whitespace "\\n" (5:2-6:1, 11-12) +├─11 bigint "2n" (6:1-6:3, 12-14) +├─12 whitespace "\\n" (6:3-7:1, 14-15) +├─13 number "3" (7:1-7:2, 15-16) +├─14 whitespace "\\n" (7:2-8:1, 16-17) +├─15 bigint "3n" (8:1-8:3, 17-19) +├─16 whitespace "\\n" (8:3-9:1, 19-20) +├─17 number "4" (9:1-9:2, 20-21) +├─18 whitespace "\\n" (9:2-10:1, 21-22) +├─19 bigint "4n" (10:1-10:3, 22-24) +├─20 whitespace "\\n" (10:3-11:1, 24-25) +├─21 number "5" (11:1-11:2, 25-26) +├─22 whitespace "\\n" (11:2-12:1, 26-27) +├─23 bigint "5n" (12:1-12:3, 27-29) +├─24 whitespace "\\n" (12:3-13:1, 29-30) +├─25 number "6" (13:1-13:2, 30-31) +├─26 whitespace "\\n" (13:2-14:1, 31-32) +├─27 bigint "6n" (14:1-14:3, 32-34) +├─28 whitespace "\\n" (14:3-15:1, 34-35) +├─29 number "7" (15:1-15:2, 35-36) +├─30 whitespace "\\n" (15:2-16:1, 36-37) +├─31 bigint "7n" (16:1-16:3, 37-39) +├─32 whitespace "\\n" (16:3-17:1, 39-40) +├─33 number "8" (17:1-17:2, 40-41) +├─34 whitespace "\\n" (17:2-18:1, 41-42) +├─35 bigint "8n" (18:1-18:3, 42-44) +├─36 whitespace "\\n" (18:3-19:1, 44-45) +├─37 number "9" (19:1-19:2, 45-46) +├─38 whitespace "\\n" (19:2-20:1, 46-47) +├─39 bigint "9n" (20:1-20:3, 47-49) +├─40 whitespace "\\n" (20:3-21:1, 49-50) +└─41 eof (21:1-21:1, 50-50) `; exports[`integration:tokenize > user constructs > should tokenize empty file 1`] = ` tokens[2] - ├─0 sof (1:1-1:1) - ├─1 eof (1:1-1:1) +├─0 sof (1:1-1:1, 0-0) +└─1 eof (1:1-1:1, 0-0) `; diff --git a/yarn.lock b/yarn.lock index 63f70c0..08e80f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1670,6 +1670,36 @@ __metadata: languageName: node linkType: hard +"@flex-development/unist-util-builder@npm:2.0.0": + version: 2.0.0 + resolution: "@flex-development/unist-util-builder@npm:2.0.0::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40flex-development%2Funist-util-builder%2F2.0.0%2F2589c6774a811c50c2d08501dfcd1a5ed551c883" + dependencies: + "@flex-development/unist-util-types": "npm:1.6.1" + "@types/unist": "npm:3.0.2" + checksum: 10/58e6dc90395b636d7c02d5ab5966a8fbdaf0e9a01c8ff99398e98f8570b422b9c08492365968a85517d6d7991f095a11732a37f775a5af8773c2dda2f673de6d + languageName: node + linkType: hard + +"@flex-development/unist-util-inspect@npm:1.0.0": + version: 1.0.0 + resolution: "@flex-development/unist-util-inspect@npm:1.0.0::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40flex-development%2Funist-util-inspect%2F1.0.0%2F2585616dd8111de66e7ddc2d846a5e077c57c982" + dependencies: + "@flex-development/unist-util-stringify-position": "npm:1.0.0" + "@types/unist": "npm:3.0.2" + checksum: 10/c1b81c3191e2be2bbbcff95f1a2e99afe4360cd72eebbe0b5401499fd9c89c9fa2e42adc7f875fbc7a6204bb75d33a2ad7f0b53a19bca4547182d784143a21d8 + languageName: node + linkType: hard + +"@flex-development/unist-util-stringify-position@npm:1.0.0": + version: 1.0.0 + resolution: "@flex-development/unist-util-stringify-position@npm:1.0.0::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40flex-development%2Funist-util-stringify-position%2F1.0.0%2Fb2a5cbc97f53cee6e215f15a908a78109e543b48" + dependencies: + "@flex-development/unist-util-types": "npm:1.6.1" + "@types/unist": "npm:3.0.2" + checksum: 10/085cfe2ce2398c1aa0f986ea54789a1cb1df69a348af7f2e013a443fd5672c76c8d2764cacefa0babe9545ed63a623876e0eb2c989a9516dad55e216d5bb9a53 + languageName: node + linkType: hard + "@flex-development/unist-util-types@npm:1.4.0": version: 1.4.0 resolution: "@flex-development/unist-util-types@npm:1.4.0::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40flex-development%2Funist-util-types%2F1.4.0%2F7bc3e76774ee0ff16f31bf3ca7caaec923795e07" @@ -1705,6 +1735,8 @@ __metadata: "@flex-development/mlly": "npm:1.0.0-alpha.18" "@flex-development/pathe": "npm:2.0.0" "@flex-development/tutils": "npm:6.0.0-alpha.25" + "@flex-development/unist-util-builder": "npm:2.0.0" + "@flex-development/unist-util-inspect": "npm:1.0.0" "@flex-development/unist-util-types": "npm:1.6.1" "@flex-development/vfile-reader": "npm:3.1.1" "@stylistic/eslint-plugin": "npm:2.2.2" @@ -1762,7 +1794,6 @@ __metadata: ts-dedent: "npm:2.2.0" typescript: "npm:5.5.2" typescript-eslint: "npm:8.0.0-alpha.30" - unist-util-stringify-position: "npm:4.0.0" vfile: "npm:6.0.1" vite-tsconfig-paths: "npm:4.3.2" vitest: "npm:2.0.0-beta.11" @@ -10076,15 +10107,6 @@ __metadata: languageName: node linkType: hard -"unist-util-stringify-position@npm:4.0.0, unist-util-stringify-position@npm:^4.0.0": - version: 4.0.0 - resolution: "unist-util-stringify-position@npm:4.0.0" - dependencies: - "@types/unist": "npm:^3.0.0" - checksum: 10/d15c88aca7a31902d95d5b5355bbe09583cf6f6ff6e59e134ef76c76d3c30bc1021f2d7ea5b7897c6d0858ed5f3770c1b19de9c78274f50d72f95a0d05f1af71 - languageName: node - linkType: hard - "unist-util-stringify-position@npm:^2.0.0": version: 2.0.3 resolution: "unist-util-stringify-position@npm:2.0.3" @@ -10094,6 +10116,15 @@ __metadata: languageName: node linkType: hard +"unist-util-stringify-position@npm:^4.0.0": + version: 4.0.0 + resolution: "unist-util-stringify-position@npm:4.0.0" + dependencies: + "@types/unist": "npm:^3.0.0" + checksum: 10/d15c88aca7a31902d95d5b5355bbe09583cf6f6ff6e59e134ef76c76d3c30bc1021f2d7ea5b7897c6d0858ed5f3770c1b19de9c78274f50d72f95a0d05f1af71 + languageName: node + linkType: hard + "unist-util-visit-parents@npm:^6.0.0": version: 6.0.1 resolution: "unist-util-visit-parents@npm:6.0.1"