@cronn/lib-file-snapshots 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -1,5 +1,29 @@
1
+ //#region src/models/table.d.ts
2
+ interface TableData<TRow extends TableRow = TableRow> {
3
+ columns: Array<string>;
4
+ rows: Array<TRow>;
5
+ }
6
+ type TableRow = Array<TableCell>;
7
+ type TableCell = string | number | boolean | null | undefined;
8
+ type TableRowRecord = Record<string, TableCell>;
9
+ declare class Table<TRow extends TableRow = TableRow> implements TableData<TRow> {
10
+ readonly columns: Array<string>;
11
+ readonly rows: Array<TRow>;
12
+ private constructor();
13
+ /**
14
+ * Defines a table
15
+ */
16
+ static define<TRow extends TableRow>(data: TableData<TRow>): Table<TRow>;
17
+ /**
18
+ * Defines a table from record-based rows
19
+ *
20
+ * Columns are derived from keys, rows from values.
21
+ */
22
+ static fromRecords<TRow extends TableRowRecord>(rows: Array<TRow>): Table;
23
+ }
24
+ //#endregion
1
25
  //#region src/types/serializer.d.ts
2
- interface SnapshotSerializer {
26
+ interface SnapshotSerializer<TValue> {
3
27
  /**
4
28
  * The file extension associated with the serialized value
5
29
  */
@@ -11,7 +35,7 @@ interface SnapshotSerializer {
11
35
  * @return {string} The serialized value
12
36
  * @throws {Error} Will throw an error if value cannot be serialized.
13
37
  */
14
- serialize(value: unknown): string;
38
+ serialize(value: TValue): string;
15
39
  }
16
40
  //#endregion
17
41
  //#region src/serializers/json-serializer.d.ts
@@ -38,7 +62,7 @@ interface JsonNormalizerContext {
38
62
  key?: string;
39
63
  index?: number;
40
64
  }
41
- declare class JsonSerializer implements SnapshotSerializer {
65
+ declare class JsonSerializer implements SnapshotSerializer<unknown> {
42
66
  readonly fileExtension = "json";
43
67
  private readonly includeUndefinedObjectProperties;
44
68
  private readonly normalizers;
@@ -57,6 +81,23 @@ declare class JsonSerializer implements SnapshotSerializer {
57
81
  private applyCustomNormalizers;
58
82
  }
59
83
  //#endregion
84
+ //#region src/serializers/markdown-table-serializer.d.ts
85
+ interface MarkdownTableSerializerOptions {
86
+ /**
87
+ * Custom normalizers to apply before serialization
88
+ */
89
+ normalizers?: Array<MarkdownTableNormalizer>;
90
+ }
91
+ type MarkdownTableNormalizer = (value: TableCell) => TableCell;
92
+ declare class MarkdownTableSerializer implements SnapshotSerializer<TableData> {
93
+ readonly fileExtension = "md";
94
+ private readonly normalizers;
95
+ constructor(options?: MarkdownTableSerializerOptions);
96
+ serialize(value: TableData): string;
97
+ private serializeCellValue;
98
+ private mapCellValueToString;
99
+ }
100
+ //#endregion
60
101
  //#region src/serializers/text-serializer.d.ts
61
102
  interface TextSerializerOptions {
62
103
  /**
@@ -71,16 +112,16 @@ interface TextSerializerOptions {
71
112
  fileExtension?: string;
72
113
  }
73
114
  type TextNormalizer = (value: string) => string;
74
- declare class TextSerializer implements SnapshotSerializer {
115
+ declare class TextSerializer implements SnapshotSerializer<string> {
75
116
  readonly fileExtension: string;
76
117
  private readonly normalizers;
77
118
  constructor(options?: TextSerializerOptions);
78
- serialize(value: unknown): string;
119
+ serialize(value: string): string;
79
120
  private normalizeValue;
80
121
  }
81
122
  //#endregion
82
123
  //#region src/types/matcher.d.ts
83
- interface ValidationFileMatcherConfig {
124
+ interface ValidationFileMatcherConfig<TValue> {
84
125
  /**
85
126
  * Directory in which golden masters are stored
86
127
  */
@@ -98,7 +139,7 @@ interface ValidationFileMatcherConfig {
98
139
  /**
99
140
  * The serializer to use for the snapshot
100
141
  */
101
- serializer: SnapshotSerializer;
142
+ serializer: SnapshotSerializer<TValue>;
102
143
  /**
103
144
  * Whether to update golden masters with the actual result.
104
145
  *
@@ -123,15 +164,15 @@ interface FilePathResolverParams {
123
164
  }
124
165
  //#endregion
125
166
  //#region src/matcher/validation-file-matcher.d.ts
126
- declare class ValidationFileMatcher {
167
+ declare class ValidationFileMatcher<TValue> {
127
168
  private readonly updateSnapshots;
128
169
  private readonly serializer;
129
170
  private readonly filePaths;
130
171
  private validationFile;
131
- constructor(config: ValidationFileMatcherConfig);
172
+ constructor(config: ValidationFileMatcherConfig<TValue>);
132
173
  get isValidationFileMissing(): boolean;
133
174
  get isUpdate(): boolean;
134
- matchFileSnapshot(actual: unknown): ValidationFileMatcherResult;
175
+ matchFileSnapshot(actual: TValue): ValidationFileMatcherResult;
135
176
  private buildFilePaths;
136
177
  private readValidationFile;
137
178
  private createMatcherResult;
@@ -152,4 +193,4 @@ declare function isArray(value: unknown): value is Array<unknown>;
152
193
  type PlainObject = Record<PropertyKey, unknown>;
153
194
  declare function isPlainObject(value: unknown): value is PlainObject;
154
195
  //#endregion
155
- export { type FilePathResolver, type FilePathResolverParams, type JsonNormalizer, type JsonNormalizerContext, JsonSerializer, type PlainObject, type SnapshotSerializer, type TextNormalizer, TextSerializer, type UpdateSnapshotsType, ValidationFileMatcher, type ValidationFileMatcherResult, isArray, isPlainObject, isString, normalizeFileName, resolveNameAsFile, resolveNameAsFileSuffix };
196
+ export { type FilePathResolver, type FilePathResolverParams, type JsonNormalizer, type JsonNormalizerContext, JsonSerializer, type MarkdownTableNormalizer, MarkdownTableSerializer, type PlainObject, type SnapshotSerializer, Table, type TableCell, type TableData, type TableRow, type TextNormalizer, TextSerializer, type UpdateSnapshotsType, ValidationFileMatcher, type ValidationFileMatcherResult, isArray, isPlainObject, isString, normalizeFileName, resolveNameAsFile, resolveNameAsFileSuffix };
package/dist/index.mjs CHANGED
@@ -3,7 +3,35 @@ import fs from "node:fs";
3
3
  import os from "node:os";
4
4
  import * as path$1 from "node:path";
5
5
  import path from "node:path";
6
-
6
+ import { markdownTable } from "markdown-table";
7
+ //#region src/models/table.ts
8
+ var Table = class Table {
9
+ columns;
10
+ rows;
11
+ constructor(data) {
12
+ this.columns = data.columns;
13
+ this.rows = data.rows;
14
+ }
15
+ /**
16
+ * Defines a table
17
+ */
18
+ static define(data) {
19
+ return new Table(data);
20
+ }
21
+ /**
22
+ * Defines a table from record-based rows
23
+ *
24
+ * Columns are derived from keys, rows from values.
25
+ */
26
+ static fromRecords(rows) {
27
+ const [firstRow] = rows;
28
+ return new Table({
29
+ columns: firstRow !== void 0 ? Object.keys(firstRow) : [],
30
+ rows: rows.map((row) => Object.values(row))
31
+ });
32
+ }
33
+ };
34
+ //#endregion
7
35
  //#region src/utils/file.ts
8
36
  const NEW_LINE_SEPARATOR = "\n";
9
37
  const EMOJI_REGEXP = /(?!(\*|#|\d))[\p{Extended_Pictographic}\p{Emoji_Component}]|[\u0030-\u0039]\ufe0f?[\u20e3]|[\u002A\u0023]?\ufe0f?[\u20e3]/gu;
@@ -28,7 +56,6 @@ function addTrailingNewLine(data) {
28
56
  function addMissingFileMarker(data) {
29
57
  return `===== missing file =====${NEW_LINE_SEPARATOR}${data}`;
30
58
  }
31
-
32
59
  //#endregion
33
60
  //#region src/utils/guards.ts
34
61
  function isString(value) {
@@ -45,13 +72,11 @@ function isPlainObject(value) {
45
72
  const proto = Object.getPrototypeOf(value);
46
73
  return proto === null || proto === Object.prototype;
47
74
  }
48
-
49
75
  //#endregion
50
76
  //#region src/utils/validators.ts
51
77
  function isPositiveInteger(value) {
52
78
  return Number.isInteger(value) && value > 0;
53
79
  }
54
-
55
80
  //#endregion
56
81
  //#region src/serializers/json-serializer.ts
57
82
  var JsonSerializer = class {
@@ -142,7 +167,30 @@ var JsonSerializer = class {
142
167
  return normalizedValue;
143
168
  }
144
169
  };
145
-
170
+ //#endregion
171
+ //#region src/serializers/markdown-table-serializer.ts
172
+ var MarkdownTableSerializer = class {
173
+ fileExtension = "md";
174
+ normalizers;
175
+ constructor(options = {}) {
176
+ this.normalizers = options.normalizers ?? [];
177
+ }
178
+ serialize(value) {
179
+ const { columns, rows } = value;
180
+ if (columns.length === 0) throw new Error("Markdown table must have at least one column.");
181
+ return addTrailingNewLine(markdownTable([columns, ...rows.map((row) => row.map((cellValue) => this.serializeCellValue(cellValue)))]));
182
+ }
183
+ serializeCellValue(value) {
184
+ let normalizedValue = value;
185
+ for (const normalizer of this.normalizers) normalizedValue = normalizer(normalizedValue);
186
+ return this.mapCellValueToString(normalizedValue);
187
+ }
188
+ mapCellValueToString(value) {
189
+ if (value === void 0) return "undefined";
190
+ if (value === null) return "null";
191
+ return value.toString().trim();
192
+ }
193
+ };
146
194
  //#endregion
147
195
  //#region src/serializers/text-serializer.ts
148
196
  var TextSerializer = class {
@@ -153,7 +201,6 @@ var TextSerializer = class {
153
201
  this.fileExtension = options.fileExtension ?? "txt";
154
202
  }
155
203
  serialize(value) {
156
- if (!isString(value)) throw new Error(`Value of type ${typeof value} cannot be serialized as text. Only strings are supported.`);
157
204
  return addTrailingNewLine(this.normalizeValue(value));
158
205
  }
159
206
  normalizeValue(value) {
@@ -162,7 +209,6 @@ var TextSerializer = class {
162
209
  return normalizedValue.trim();
163
210
  }
164
211
  };
165
-
166
212
  //#endregion
167
213
  //#region src/matcher/validation-file-matcher.ts
168
214
  var ValidationFileMatcher = class {
@@ -231,7 +277,6 @@ var ValidationFileMatcher = class {
231
277
  return this.validationFile;
232
278
  }
233
279
  };
234
-
235
280
  //#endregion
236
281
  //#region src/matcher/file-path-resolver.ts
237
282
  function resolveNameAsFile(params) {
@@ -254,6 +299,5 @@ function resolveFilePath(params, resolveNamedFilePath) {
254
299
  normalizedName: normalizeFileName(name)
255
300
  });
256
301
  }
257
-
258
302
  //#endregion
259
- export { JsonSerializer, TextSerializer, ValidationFileMatcher, isArray, isPlainObject, isString, normalizeFileName, resolveNameAsFile, resolveNameAsFileSuffix };
303
+ export { JsonSerializer, MarkdownTableSerializer, Table, TextSerializer, ValidationFileMatcher, isArray, isPlainObject, isString, normalizeFileName, resolveNameAsFile, resolveNameAsFileSuffix };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cronn/lib-file-snapshots",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "The library agnostic core for testing file snapshots",
5
5
  "keywords": [
6
6
  "file snapshots"
@@ -22,12 +22,12 @@
22
22
  "devEngines": {
23
23
  "runtime": {
24
24
  "name": "node",
25
- "version": ">=24.13 <25",
25
+ "version": ">=24.14.1 <25",
26
26
  "onFail": "error"
27
27
  },
28
28
  "packageManager": {
29
29
  "name": "pnpm",
30
- "version": ">=10.28.2 <11",
30
+ "version": ">=10.33.0 <11",
31
31
  "onFail": "error"
32
32
  }
33
33
  },
@@ -45,18 +45,20 @@
45
45
  "devDependencies": {
46
46
  "@arethetypeswrong/core": "0.18.2",
47
47
  "@trivago/prettier-plugin-sort-imports": "6.0.2",
48
- "@types/node": "24.11.0",
49
- "@typescript/native-preview": "7.0.0-dev.20260302.1",
50
- "@vitest/coverage-v8": "4.0.18",
51
- "eslint": "10.0.2",
48
+ "@types/node": "24.12.2",
49
+ "@typescript/native-preview": "7.0.0-dev.20260406.1",
50
+ "@vitest/coverage-v8": "4.1.4",
51
+ "eslint": "10.2.0",
52
52
  "eslint-config-prettier": "10.1.8",
53
53
  "eslint-plugin-check-file": "3.3.1",
54
54
  "eslint-plugin-unused-imports": "4.4.1",
55
+ "markdown-table": "3.0.4",
55
56
  "prettier": "3.8.1",
56
57
  "publint": "0.3.18",
57
- "tsdown": "0.20.3",
58
- "typescript-eslint": "8.56.1",
59
- "vitest": "4.0.18",
58
+ "rimraf": "6.1.3",
59
+ "tsdown": "0.21.7",
60
+ "typescript-eslint": "8.58.0",
61
+ "vitest": "4.1.4",
60
62
  "@cronn/shared-configs": "0.0.0"
61
63
  },
62
64
  "scripts": {
@@ -65,6 +67,7 @@
65
67
  "test": "vitest run",
66
68
  "test:coverage": "vitest run --coverage",
67
69
  "compile": "tsgo",
68
- "build": "tsdown"
70
+ "build": "tsdown",
71
+ "clean": "rimraf .turbo dist coverage"
69
72
  }
70
73
  }