@nitpicker/crawler 0.4.2 → 0.4.3

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.
Files changed (119) hide show
  1. package/package.json +5 -2
  2. package/CHANGELOG.md +0 -16
  3. package/src/archive/__mock__/.gitignore +0 -3
  4. package/src/archive/__mock__/mock.sqlite +0 -0
  5. package/src/archive/archive-accessor.ts +0 -337
  6. package/src/archive/archive.ts +0 -408
  7. package/src/archive/database.spec.ts +0 -469
  8. package/src/archive/database.ts +0 -1059
  9. package/src/archive/debug.ts +0 -10
  10. package/src/archive/filesystem/append-text.spec.ts +0 -26
  11. package/src/archive/filesystem/append-text.ts +0 -16
  12. package/src/archive/filesystem/copy-dir-sync.spec.ts +0 -27
  13. package/src/archive/filesystem/copy-dir-sync.ts +0 -10
  14. package/src/archive/filesystem/copy-dir.spec.ts +0 -33
  15. package/src/archive/filesystem/copy-dir.ts +0 -14
  16. package/src/archive/filesystem/exists.spec.ts +0 -33
  17. package/src/archive/filesystem/exists.ts +0 -10
  18. package/src/archive/filesystem/get-file-list.spec.ts +0 -37
  19. package/src/archive/filesystem/get-file-list.ts +0 -13
  20. package/src/archive/filesystem/index.ts +0 -17
  21. package/src/archive/filesystem/is-dir.spec.ts +0 -29
  22. package/src/archive/filesystem/is-dir.ts +0 -11
  23. package/src/archive/filesystem/mkdir.spec.ts +0 -37
  24. package/src/archive/filesystem/mkdir.ts +0 -16
  25. package/src/archive/filesystem/output-json.spec.ts +0 -34
  26. package/src/archive/filesystem/output-json.ts +0 -16
  27. package/src/archive/filesystem/output-text.spec.ts +0 -31
  28. package/src/archive/filesystem/output-text.ts +0 -35
  29. package/src/archive/filesystem/read-json.spec.ts +0 -26
  30. package/src/archive/filesystem/read-json.ts +0 -12
  31. package/src/archive/filesystem/read-text.spec.ts +0 -25
  32. package/src/archive/filesystem/read-text.ts +0 -11
  33. package/src/archive/filesystem/readline.spec.ts +0 -29
  34. package/src/archive/filesystem/readline.ts +0 -30
  35. package/src/archive/filesystem/remove.spec.ts +0 -34
  36. package/src/archive/filesystem/remove.ts +0 -11
  37. package/src/archive/filesystem/rename.spec.ts +0 -46
  38. package/src/archive/filesystem/rename.ts +0 -21
  39. package/src/archive/filesystem/tar.spec.ts +0 -33
  40. package/src/archive/filesystem/tar.ts +0 -27
  41. package/src/archive/filesystem/untar.spec.ts +0 -34
  42. package/src/archive/filesystem/untar.ts +0 -36
  43. package/src/archive/index.ts +0 -13
  44. package/src/archive/page.spec.ts +0 -368
  45. package/src/archive/page.ts +0 -420
  46. package/src/archive/resource.spec.ts +0 -101
  47. package/src/archive/resource.ts +0 -73
  48. package/src/archive/safe-path.spec.ts +0 -44
  49. package/src/archive/safe-path.ts +0 -18
  50. package/src/archive/types.ts +0 -227
  51. package/src/crawler/clear-destination-cache.spec.ts +0 -20
  52. package/src/crawler/clear-destination-cache.ts +0 -9
  53. package/src/crawler/crawler.ts +0 -873
  54. package/src/crawler/decompose-url.spec.ts +0 -48
  55. package/src/crawler/decompose-url.ts +0 -90
  56. package/src/crawler/destination-cache.spec.ts +0 -23
  57. package/src/crawler/destination-cache.ts +0 -8
  58. package/src/crawler/detect-pagination-pattern.spec.ts +0 -169
  59. package/src/crawler/detect-pagination-pattern.ts +0 -66
  60. package/src/crawler/fetch-destination.ts +0 -257
  61. package/src/crawler/fetch-robots-txt.spec.ts +0 -83
  62. package/src/crawler/fetch-robots-txt.ts +0 -91
  63. package/src/crawler/find-best-matching-scope.spec.ts +0 -39
  64. package/src/crawler/find-best-matching-scope.ts +0 -57
  65. package/src/crawler/generate-predicted-urls.spec.ts +0 -42
  66. package/src/crawler/generate-predicted-urls.ts +0 -34
  67. package/src/crawler/handle-ignore-and-skip.spec.ts +0 -66
  68. package/src/crawler/handle-ignore-and-skip.ts +0 -30
  69. package/src/crawler/handle-resource-response.spec.ts +0 -45
  70. package/src/crawler/handle-resource-response.ts +0 -21
  71. package/src/crawler/handle-scrape-end.spec.ts +0 -109
  72. package/src/crawler/handle-scrape-end.ts +0 -115
  73. package/src/crawler/handle-scrape-error.spec.ts +0 -105
  74. package/src/crawler/handle-scrape-error.ts +0 -58
  75. package/src/crawler/index.ts +0 -2
  76. package/src/crawler/inject-scope-auth.spec.ts +0 -36
  77. package/src/crawler/inject-scope-auth.ts +0 -27
  78. package/src/crawler/is-external-url.spec.ts +0 -31
  79. package/src/crawler/is-external-url.ts +0 -17
  80. package/src/crawler/is-in-any-lower-layer.spec.ts +0 -31
  81. package/src/crawler/is-in-any-lower-layer.ts +0 -22
  82. package/src/crawler/link-list.spec.ts +0 -355
  83. package/src/crawler/link-list.ts +0 -275
  84. package/src/crawler/link-to-page-data.spec.ts +0 -133
  85. package/src/crawler/link-to-page-data.ts +0 -34
  86. package/src/crawler/net-timeout-error.spec.ts +0 -25
  87. package/src/crawler/net-timeout-error.ts +0 -11
  88. package/src/crawler/protocol-agnostic-key.spec.ts +0 -40
  89. package/src/crawler/protocol-agnostic-key.ts +0 -11
  90. package/src/crawler/reconstruct-url.spec.ts +0 -37
  91. package/src/crawler/reconstruct-url.ts +0 -37
  92. package/src/crawler/robots-checker.spec.ts +0 -104
  93. package/src/crawler/robots-checker.ts +0 -73
  94. package/src/crawler/should-discard-predicted.spec.ts +0 -125
  95. package/src/crawler/should-discard-predicted.ts +0 -33
  96. package/src/crawler/should-skip-url.spec.ts +0 -77
  97. package/src/crawler/should-skip-url.ts +0 -37
  98. package/src/crawler/types.ts +0 -146
  99. package/src/crawler-orchestrator.ts +0 -401
  100. package/src/debug.ts +0 -10
  101. package/src/index.ts +0 -25
  102. package/src/types.ts +0 -30
  103. package/src/utils/array/each-splitted.spec.ts +0 -38
  104. package/src/utils/array/each-splitted.ts +0 -19
  105. package/src/utils/array/index.ts +0 -1
  106. package/src/utils/debug.ts +0 -6
  107. package/src/utils/error/dom-evaluation-error.spec.ts +0 -20
  108. package/src/utils/error/dom-evaluation-error.ts +0 -6
  109. package/src/utils/error/error-emitter.spec.ts +0 -78
  110. package/src/utils/error/error-emitter.ts +0 -44
  111. package/src/utils/error/index.ts +0 -3
  112. package/src/utils/index.ts +0 -5
  113. package/src/utils/object/clean-object.spec.ts +0 -24
  114. package/src/utils/object/clean-object.ts +0 -13
  115. package/src/utils/object/index.ts +0 -1
  116. package/src/utils/types/index.ts +0 -1
  117. package/src/utils/types/types.ts +0 -65
  118. package/tsconfig.json +0 -11
  119. package/tsconfig.tsbuildinfo +0 -1
@@ -1,10 +0,0 @@
1
- import { log as globalLog } from '../utils/debug.js';
2
-
3
- /** Debug logger for the archive package. Namespace: `Nitpicker:Utils:Archive`. */
4
- export const log = globalLog.extend('Archive');
5
- /** Debug logger for archive save operations. Namespace: `Nitpicker:Utils:Archive:Save`. */
6
- export const saveLog = log.extend('Save');
7
- /** Debug logger for database operations. Namespace: `Nitpicker:Utils:Archive:DB`. */
8
- export const dbLog = log.extend('DB');
9
- /** Debug logger for archive errors. Namespace: `Nitpicker:Utils:Archive:Error`. */
10
- export const errorLog = log.extend('Error');
@@ -1,26 +0,0 @@
1
- import { writeFileSync, readFileSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { appendText } from './append-text.js';
8
-
9
- describe('appendText', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-append-text');
11
-
12
- beforeEach(() => {
13
- mkdirSync(testDir, { recursive: true });
14
- });
15
-
16
- afterEach(() => {
17
- rmSync(testDir, { recursive: true, force: true });
18
- });
19
-
20
- it('appends text with a preceding newline', async () => {
21
- const filePath = path.join(testDir, 'file.txt');
22
- writeFileSync(filePath, 'line1');
23
- await appendText(filePath, 'line2');
24
- expect(readFileSync(filePath, 'utf8')).toBe('line1\nline2');
25
- });
26
- });
@@ -1,16 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
-
3
- import { mkdir } from './mkdir.js';
4
-
5
- /**
6
- * Appends text data to a file at the specified path.
7
- *
8
- * Creates parent directories if they do not exist.
9
- * A newline character is prepended to the data before appending.
10
- * @param filePath - The absolute or relative path to the file to append to.
11
- * @param data - The text content to append to the file.
12
- */
13
- export async function appendText(filePath: string, data: string) {
14
- mkdir(filePath);
15
- await fs.appendFile(filePath, `\n${data}`, { encoding: 'utf8' });
16
- }
@@ -1,27 +0,0 @@
1
- import { writeFileSync, readFileSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { copyDirSync } from './copy-dir-sync.js';
8
-
9
- describe('copyDirSync', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-copy-dir-sync');
11
- const srcDir = path.join(testDir, 'src');
12
- const destDir = path.join(testDir, 'dest');
13
-
14
- beforeEach(() => {
15
- mkdirSync(srcDir, { recursive: true });
16
- });
17
-
18
- afterEach(() => {
19
- rmSync(testDir, { recursive: true, force: true });
20
- });
21
-
22
- it('synchronously copies directory contents', () => {
23
- writeFileSync(path.join(srcDir, 'a.txt'), 'sync');
24
- copyDirSync(srcDir, destDir);
25
- expect(readFileSync(path.join(destDir, 'a.txt'), 'utf8')).toBe('sync');
26
- });
27
- });
@@ -1,10 +0,0 @@
1
- import fsx from 'fs-extra';
2
-
3
- /**
4
- * Synchronously copies a directory and its contents from one location to another.
5
- * @param from - The source directory path to copy from.
6
- * @param to - The destination directory path to copy to.
7
- */
8
- export function copyDirSync(from: string, to: string) {
9
- fsx.copySync(from, to);
10
- }
@@ -1,33 +0,0 @@
1
- import { writeFileSync, readFileSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { copyDir } from './copy-dir.js';
8
-
9
- describe('copyDir', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-copy-dir');
11
- const srcDir = path.join(testDir, 'src');
12
- const destDir = path.join(testDir, 'dest');
13
-
14
- beforeEach(() => {
15
- mkdirSync(srcDir, { recursive: true });
16
- });
17
-
18
- afterEach(() => {
19
- rmSync(testDir, { recursive: true, force: true });
20
- });
21
-
22
- it('copies directory contents', async () => {
23
- writeFileSync(path.join(srcDir, 'a.txt'), 'hello');
24
- const result = await copyDir(srcDir, destDir);
25
- expect(result).toBe(true);
26
- expect(readFileSync(path.join(destDir, 'a.txt'), 'utf8')).toBe('hello');
27
- });
28
-
29
- it('returns false on error', async () => {
30
- const result = await copyDir('/nonexistent/path', destDir);
31
- expect(result).toBe(false);
32
- });
33
- });
@@ -1,14 +0,0 @@
1
- import fsx from 'fs-extra';
2
-
3
- /**
4
- * Recursively copies a directory and its contents from one location to another.
5
- * @param from - The source directory path to copy from.
6
- * @param to - The destination directory path to copy to.
7
- * @returns `true` if the copy succeeded, `false` if an error occurred.
8
- */
9
- export async function copyDir(from: string, to: string) {
10
- return fsx
11
- .copy(from, to)
12
- .then(() => true)
13
- .catch(() => false);
14
- }
@@ -1,33 +0,0 @@
1
- import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { exists } from './exists.js';
8
-
9
- describe('exists', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-exists');
11
-
12
- beforeEach(() => {
13
- mkdirSync(testDir, { recursive: true });
14
- });
15
-
16
- afterEach(() => {
17
- rmSync(testDir, { recursive: true, force: true });
18
- });
19
-
20
- it('returns true for an existing file', () => {
21
- const filePath = path.join(testDir, 'file.txt');
22
- writeFileSync(filePath, 'test');
23
- expect(exists(filePath)).toBe(true);
24
- });
25
-
26
- it('returns false for a non-existing file', () => {
27
- expect(exists(path.join(testDir, 'nonexistent.txt'))).toBe(false);
28
- });
29
-
30
- it('returns true for an existing directory', () => {
31
- expect(exists(testDir)).toBe(true);
32
- });
33
- });
@@ -1,10 +0,0 @@
1
- import { existsSync } from 'node:fs';
2
-
3
- /**
4
- * Checks whether a file or directory exists at the given path.
5
- * @param filePath - The path to check for existence.
6
- * @returns `true` if the path exists, `false` otherwise.
7
- */
8
- export function exists(filePath: string) {
9
- return existsSync(filePath);
10
- }
@@ -1,37 +0,0 @@
1
- import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { getFileList } from './get-file-list.js';
8
-
9
- describe('getFileList', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-get-file-list');
11
-
12
- beforeEach(() => {
13
- mkdirSync(testDir, { recursive: true });
14
- writeFileSync(path.join(testDir, 'a.txt'), '');
15
- writeFileSync(path.join(testDir, 'b.json'), '');
16
- writeFileSync(path.join(testDir, 'c.txt'), '');
17
- });
18
-
19
- afterEach(() => {
20
- rmSync(testDir, { recursive: true, force: true });
21
- });
22
-
23
- it('lists all files without filter', async () => {
24
- const list = await getFileList(testDir);
25
- expect(list.toSorted()).toEqual(['a.txt', 'b.json', 'c.txt']);
26
- });
27
-
28
- it('filters by regex', async () => {
29
- const list = await getFileList(testDir, /\.txt$/);
30
- expect(list.toSorted()).toEqual(['a.txt', 'c.txt']);
31
- });
32
-
33
- it('filters by string', async () => {
34
- const list = await getFileList(testDir, '.json');
35
- expect(list).toEqual(['b.json']);
36
- });
37
- });
@@ -1,13 +0,0 @@
1
- import fsx from 'fs-extra';
2
-
3
- /**
4
- * Lists the file names in a directory, optionally filtered by a pattern.
5
- * @param dirPath - The directory path to list files from.
6
- * @param filter - An optional RegExp or string pattern to filter file names.
7
- * Only file names matching this pattern are included in the result.
8
- * @returns An array of file names in the directory that match the filter (or all if no filter is provided).
9
- */
10
- export async function getFileList(dirPath: string, filter?: RegExp | string) {
11
- const list = await fsx.readdir(dirPath);
12
- return filter ? list.filter((fileName) => fileName.match(filter)) : list;
13
- }
@@ -1,17 +0,0 @@
1
- export { outputJSON } from './output-json.js';
2
- export { readJSON } from './read-json.js';
3
- export { outputText } from './output-text.js';
4
- export { appendText } from './append-text.js';
5
- export { readText } from './read-text.js';
6
- export { copyDir } from './copy-dir.js';
7
- export { copyDirSync } from './copy-dir-sync.js';
8
- export { isDir } from './is-dir.js';
9
- export { remove } from './remove.js';
10
- export { rename } from './rename.js';
11
- export { getFileList } from './get-file-list.js';
12
- export { readline } from './readline.js';
13
- export { mkdir } from './mkdir.js';
14
- export { exists } from './exists.js';
15
- export { tar } from './tar.js';
16
- export { untar } from './untar.js';
17
- export { zip, unzip, extractZip } from '@d-zero/fs/zip';
@@ -1,29 +0,0 @@
1
- import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { isDir } from './is-dir.js';
8
-
9
- describe('isDir', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-is-dir');
11
-
12
- beforeEach(() => {
13
- mkdirSync(testDir, { recursive: true });
14
- });
15
-
16
- afterEach(() => {
17
- rmSync(testDir, { recursive: true, force: true });
18
- });
19
-
20
- it('returns true for a directory', async () => {
21
- expect(await isDir(testDir)).toBe(true);
22
- });
23
-
24
- it('returns false for a file', async () => {
25
- const filePath = path.join(testDir, 'file.txt');
26
- writeFileSync(filePath, '');
27
- expect(await isDir(filePath)).toBe(false);
28
- });
29
- });
@@ -1,11 +0,0 @@
1
- import fsx from 'fs-extra';
2
-
3
- /**
4
- * Checks whether the given path points to a directory.
5
- * @param dirPath - The path to check.
6
- * @returns `true` if the path is a directory, `false` otherwise.
7
- */
8
- export async function isDir(dirPath: string) {
9
- const stat = await fsx.stat(dirPath);
10
- return stat.isDirectory();
11
- }
@@ -1,37 +0,0 @@
1
- import { existsSync, rmSync, statSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, describe, expect, it } from 'vitest';
6
-
7
- import { mkdir } from './mkdir.js';
8
-
9
- describe('mkdir', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-mkdir');
11
-
12
- afterEach(() => {
13
- rmSync(testDir, { recursive: true, force: true });
14
- });
15
-
16
- it('creates parent directory if it does not exist', () => {
17
- const filePath = path.join(testDir, 'sub', 'file.txt');
18
- mkdir(filePath);
19
- expect(existsSync(path.join(testDir, 'sub'))).toBe(true);
20
- });
21
-
22
- it('does nothing if directory already exists', () => {
23
- const filePath = path.join(testDir, 'sub', 'file.txt');
24
- mkdir(filePath);
25
- mkdir(filePath); // second call should not throw
26
- expect(existsSync(path.join(testDir, 'sub'))).toBe(true);
27
- });
28
-
29
- it('creates directory with 0o700 permissions', () => {
30
- const filePath = path.join(testDir, 'secure', 'file.txt');
31
- mkdir(filePath);
32
- const stats = statSync(path.join(testDir, 'secure'));
33
-
34
- const mode = stats.mode & 0o777;
35
- expect(mode).toBe(0o700);
36
- });
37
- });
@@ -1,16 +0,0 @@
1
- import { existsSync, mkdirSync } from 'node:fs';
2
- import path from 'node:path';
3
-
4
- /**
5
- * Ensures the parent directory of the given file path exists.
6
- *
7
- * If the parent directory does not exist, it is created recursively
8
- * with permissions `0o700` (owner-only access).
9
- * @param filePath - The file path whose parent directory should be created.
10
- */
11
- export function mkdir(filePath: string) {
12
- const { dir } = path.parse(filePath);
13
- if (!existsSync(dir)) {
14
- mkdirSync(path.resolve(dir), { recursive: true, mode: 0o700 });
15
- }
16
- }
@@ -1,34 +0,0 @@
1
- import { readFileSync, rmSync, mkdirSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { outputJSON } from './output-json.js';
8
-
9
- describe('outputJSON', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-output-json');
11
-
12
- beforeEach(() => {
13
- mkdirSync(testDir, { recursive: true });
14
- });
15
-
16
- afterEach(() => {
17
- rmSync(testDir, { recursive: true, force: true });
18
- });
19
-
20
- it('writes JSON data to file with 2-space indentation', async () => {
21
- const filePath = path.join(testDir, 'data.json');
22
- await outputJSON(filePath, { key: 'value' });
23
- const content = readFileSync(filePath, 'utf8');
24
- expect(JSON.parse(content)).toEqual({ key: 'value' });
25
- expect(content).toContain(' '); // 2-space indent
26
- });
27
-
28
- it('creates parent directories if they do not exist', async () => {
29
- const filePath = path.join(testDir, 'nested', 'dir', 'data.json');
30
- await outputJSON(filePath, [1, 2, 3]);
31
- const content = readFileSync(filePath, 'utf8');
32
- expect(JSON.parse(content)).toEqual([1, 2, 3]);
33
- });
34
- });
@@ -1,16 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
-
3
- import { mkdir } from './mkdir.js';
4
-
5
- /**
6
- * Writes data to a JSON file at the specified path.
7
- *
8
- * Creates parent directories if they do not exist.
9
- * The output is formatted with 2-space indentation.
10
- * @param filePath - The absolute or relative path to the JSON file to write.
11
- * @param data - The data to serialize as JSON and write to the file.
12
- */
13
- export async function outputJSON(filePath: string, data: unknown) {
14
- mkdir(filePath);
15
- await fs.writeFile(filePath, JSON.stringify(data, null, 2), { encoding: 'utf8' });
16
- }
@@ -1,31 +0,0 @@
1
- import { readFileSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { outputText } from './output-text.js';
8
-
9
- describe('outputText', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-output-text');
11
-
12
- beforeEach(() => {
13
- mkdirSync(testDir, { recursive: true });
14
- });
15
-
16
- afterEach(() => {
17
- rmSync(testDir, { recursive: true, force: true });
18
- });
19
-
20
- it('writes text to file', async () => {
21
- const filePath = path.join(testDir, 'file.txt');
22
- await outputText(filePath, 'hello world');
23
- expect(readFileSync(filePath, 'utf8')).toBe('hello world');
24
- });
25
-
26
- it('creates parent directories if needed', async () => {
27
- const filePath = path.join(testDir, 'sub', 'file.txt');
28
- await outputText(filePath, 'nested');
29
- expect(readFileSync(filePath, 'utf8')).toBe('nested');
30
- });
31
- });
@@ -1,35 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
- import path from 'node:path';
3
-
4
- import { mkdir } from './mkdir.js';
5
-
6
- let filePathTooLongCount = 0;
7
-
8
- /**
9
- * Writes text data to a file at the specified path.
10
- *
11
- * Creates parent directories if they do not exist.
12
- * If the file path exceeds the OS limit (ENAMETOOLONG), the file is saved
13
- * with an auto-generated short name and an accompanying `.meta.txt` file
14
- * that records the original file path.
15
- * @param filePath - The absolute or relative path to the text file to write.
16
- * @param data - The text content to write to the file.
17
- */
18
- export async function outputText(filePath: string, data: string) {
19
- mkdir(filePath);
20
- await fs.writeFile(filePath, data, { encoding: 'utf8' }).catch(async (error) => {
21
- if (error instanceof Error && 'code' in error && error.code === 'ENAMETOOLONG') {
22
- // eslint-disable-next-line no-console
23
- console.error(`File path too long: ${filePath}`);
24
- const dir = path.dirname(filePath);
25
- const altFileName = `__file_path_too_long_${(filePathTooLongCount++).toString().padStart(4, '0')}`;
26
- const ext = path.extname(filePath);
27
- const altFilePath = path.resolve(dir, `${altFileName}${ext}`);
28
- // eslint-disable-next-line no-console
29
- console.error(`Try to save to: ${altFilePath}`);
30
- const altMetaFilePath = path.resolve(dir, `${altFileName}.meta.txt`);
31
- await outputText(altFilePath, data);
32
- await outputText(altMetaFilePath, `Original file path: ${filePath}`);
33
- }
34
- });
35
- }
@@ -1,26 +0,0 @@
1
- import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { readJSON } from './read-json.js';
8
-
9
- describe('readJSON', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-read-json');
11
-
12
- beforeEach(() => {
13
- mkdirSync(testDir, { recursive: true });
14
- });
15
-
16
- afterEach(() => {
17
- rmSync(testDir, { recursive: true, force: true });
18
- });
19
-
20
- it('reads and parses JSON from file', async () => {
21
- const filePath = path.join(testDir, 'data.json');
22
- writeFileSync(filePath, JSON.stringify({ foo: 'bar' }));
23
- const result = await readJSON<{ foo: string }>(filePath);
24
- expect(result).toEqual({ foo: 'bar' });
25
- });
26
- });
@@ -1,12 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
-
3
- /**
4
- * Reads and parses a JSON file from the specified path.
5
- * @template T - The expected type of the parsed JSON content. Defaults to `unknown`.
6
- * @param filePath - The absolute or relative path to the JSON file to read.
7
- * @returns The parsed JSON content, cast to the specified generic type.
8
- */
9
- export async function readJSON<T = unknown>(filePath: string) {
10
- const data = await fs.readFile(filePath, { encoding: 'utf8' });
11
- return JSON.parse(data) as T;
12
- }
@@ -1,25 +0,0 @@
1
- import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { readText } from './read-text.js';
8
-
9
- describe('readText', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-read-text');
11
-
12
- beforeEach(() => {
13
- mkdirSync(testDir, { recursive: true });
14
- });
15
-
16
- afterEach(() => {
17
- rmSync(testDir, { recursive: true, force: true });
18
- });
19
-
20
- it('reads text content from file', async () => {
21
- const filePath = path.join(testDir, 'file.txt');
22
- writeFileSync(filePath, 'test content');
23
- expect(await readText(filePath)).toBe('test content');
24
- });
25
- });
@@ -1,11 +0,0 @@
1
- import { promises as fs } from 'node:fs';
2
-
3
- /**
4
- * Reads the entire contents of a text file as a UTF-8 string.
5
- * @param filePath - The absolute or relative path to the text file to read.
6
- * @returns The text content of the file.
7
- */
8
- export async function readText(filePath: string) {
9
- const data = await fs.readFile(filePath, { encoding: 'utf8' });
10
- return data;
11
- }
@@ -1,29 +0,0 @@
1
- import { writeFileSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { readline } from './readline.js';
8
-
9
- describe('readline', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-readline');
11
-
12
- beforeEach(() => {
13
- mkdirSync(testDir, { recursive: true });
14
- });
15
-
16
- afterEach(() => {
17
- rmSync(testDir, { recursive: true, force: true });
18
- });
19
-
20
- it('invokes callback for each line', async () => {
21
- const filePath = path.join(testDir, 'lines.txt');
22
- writeFileSync(filePath, 'line1\nline2\nline3');
23
- const lines: string[] = [];
24
- await readline(filePath, (line) => {
25
- lines.push(line);
26
- });
27
- expect(lines).toEqual(['line1', 'line2', 'line3']);
28
- });
29
- });
@@ -1,30 +0,0 @@
1
- import { createReadStream } from 'node:fs';
2
- import Readline from 'node:readline';
3
-
4
- /**
5
- * Reads a file line by line and invokes the callback for each line.
6
- *
7
- * The callback may return a Promise for asynchronous processing.
8
- * All callback results are collected and awaited via `Promise.all` before returning.
9
- * @param filePath - The path to the file to read line by line.
10
- * @param callback - A function invoked for each line of the file.
11
- * May return a Promise for asynchronous operations.
12
- * @returns A promise that resolves when all line callbacks have completed.
13
- */
14
- export async function readline(
15
- filePath: string,
16
- callback: (line: string) => Promise<void> | void,
17
- ) {
18
- const stream = createReadStream(filePath);
19
- const rLine = Readline.createInterface(stream);
20
- const promiseBuffer: (Promise<void> | void)[] = [];
21
- await new Promise<void>((resolve) => {
22
- rLine.on('line', (line) => {
23
- promiseBuffer.push(callback(line));
24
- });
25
- rLine.on('close', () => {
26
- resolve();
27
- });
28
- });
29
- return Promise.all(promiseBuffer);
30
- }
@@ -1,34 +0,0 @@
1
- import { writeFileSync, existsSync, mkdirSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import path from 'node:path';
4
-
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
-
7
- import { remove } from './remove.js';
8
-
9
- describe('remove', () => {
10
- const testDir = path.join(tmpdir(), 'nitpicker-test-remove');
11
-
12
- beforeEach(() => {
13
- mkdirSync(testDir, { recursive: true });
14
- });
15
-
16
- afterEach(() => {
17
- rmSync(testDir, { recursive: true, force: true });
18
- });
19
-
20
- it('removes a file', async () => {
21
- const filePath = path.join(testDir, 'file.txt');
22
- writeFileSync(filePath, 'data');
23
- await remove(filePath);
24
- expect(existsSync(filePath)).toBe(false);
25
- });
26
-
27
- it('removes a directory recursively', async () => {
28
- const subDir = path.join(testDir, 'sub');
29
- mkdirSync(subDir);
30
- writeFileSync(path.join(subDir, 'file.txt'), 'data');
31
- await remove(subDir);
32
- expect(existsSync(subDir)).toBe(false);
33
- });
34
- });