Skip to content

Commit fd5420a

Browse files
committed
chore(core): find input files with node:fs globSync
Replace `tinyglobby` and `glob-parent` with a small `#utils/glob.mjs` built on Node.js's `fs.globSync`, stable since 22.17 and 24.0 (doc-kit targets Node.js 24, per `.nvmrc`). It returns the same POSIX, cwd-relative paths as tinyglobby, including for absolute patterns, and leaves out directories. Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent ec7751f commit fd5420a

10 files changed

Lines changed: 129 additions & 21 deletions

File tree

‎.changeset/node-glob.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@doc-kit/core': patch
3+
---
4+
5+
Find input files with Node.js's `fs.globSync` instead of `tinyglobby` and `glob-parent`.

‎packages/core/package.json‎

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
"cosmiconfig": "^9.0.2",
6060
"dedent": "^1.7.2",
6161
"github-slugger": "^2.0.0",
62-
"glob-parent": "^6.0.2",
6362
"mdast-util-slice-markdown": "^2.0.1",
6463
"mdast-util-to-string": "^4.0.0",
6564
"piscina": "^5.3.2",
@@ -71,7 +70,6 @@
7170
"remark-stringify": "^11.0.0",
7271
"semver": "^7.8.5",
7372
"shiki": "^4.4.3",
74-
"tinyglobby": "^0.2.17",
7573
"unified": "^11.0.5",
7674
"unist-builder": "^4.0.0",
7775
"unist-util-find-after": "^5.0.0",

‎packages/core/src/generators/ast-js/generate.mjs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import { readFile } from 'node:fs/promises';
44
import { extname } from 'node:path';
55

66
import { parse } from 'acorn';
7-
import { globSync } from 'tinyglobby';
87

98
import getConfig from '#utils/configuration/index.mjs';
9+
import { findFiles } from '#utils/glob.mjs';
1010

1111
/**
1212
* Process a chunk of JavaScript files in a worker thread.
@@ -44,7 +44,7 @@ export async function processChunk(inputSlice, itemIndices) {
4444
export async function* generate(_, worker) {
4545
const config = getConfig('ast-js');
4646

47-
const files = globSync(config.input, { ignore: config.ignore }).filter(
47+
const files = findFiles(config.input, { ignore: config.ignore }).filter(
4848
p => extname(p) === '.js'
4949
);
5050

‎packages/core/src/generators/ast/generate.mjs‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
import { readFile } from 'node:fs/promises';
44
import { relative, sep } from 'node:path/posix';
55

6-
import globParent from 'glob-parent';
7-
import { globSync } from 'tinyglobby';
86
import { parse as parseYaml } from 'yaml';
97

108
import getConfig from '#utils/configuration/index.mjs';
119
import { withExt } from '#utils/file.mjs';
10+
import { findFiles, globParent } from '#utils/glob.mjs';
1211
import { getProcessor } from '#utils/markdown/processor.mjs';
1312
import { QUERIES } from '#utils/queries/index.mjs';
1413

@@ -117,7 +116,7 @@ export async function* generate(_, worker) {
117116
const files = config.input.flatMap(input => {
118117
const parent = globParent(input);
119118

120-
return globSync(input, { ignore: config.ignore }).map(child => [
119+
return findFiles(input, { ignore: config.ignore }).map(child => [
121120
child,
122121
parent,
123122
]);

‎packages/core/src/generators/json/__tests__/generate.test.mjs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { describe, it } from 'node:test';
55
import { fileURLToPath } from 'node:url';
66

77
import Ajv from 'ajv';
8-
import { globSync } from 'tinyglobby';
98

109
import { loadGenerator } from '#generators/loader.mjs';
1110
import { parseApiDoc } from '#generators/metadata/utils/parse.mjs';
11+
import { findFiles } from '#utils/glob.mjs';
1212
import { loadMarkdownPlugins } from '#utils/markdown/plugins.mjs';
1313
import { getProcessor } from '#utils/markdown/processor.mjs';
1414
import { QUERIES } from '#utils/queries/index.mjs';
@@ -77,7 +77,7 @@ const find = ({ children }, predicate) => {
7777
};
7878

7979
describe('json', () => {
80-
for (const name of globSync('*.md', { cwd: fileURLToPath(fixtures) })) {
80+
for (const name of findFiles('*.md', { cwd: fileURLToPath(fixtures) })) {
8181
it(`builds a valid document for ${name}`, async t => {
8282
const document = await buildFixture(name);
8383

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
'use strict';
2+
3+
import assert from 'node:assert/strict';
4+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
5+
import { tmpdir } from 'node:os';
6+
import { join } from 'node:path';
7+
import { after, before, describe, it } from 'node:test';
8+
import { pathToFileURL } from 'node:url';
9+
10+
import { findFiles, globParent } from '../glob.mjs';
11+
12+
describe('findFiles', () => {
13+
let root;
14+
15+
before(() => {
16+
root = mkdtempSync(join(tmpdir(), 'doc-kit-glob-'));
17+
mkdirSync(join(root, 'api', 'nested.md'), { recursive: true });
18+
writeFileSync(join(root, 'api', 'fs.md'), '');
19+
writeFileSync(join(root, 'api', 'path.md'), '');
20+
writeFileSync(join(root, 'api', 'quic.md'), '');
21+
writeFileSync(join(root, 'api', 'nested.md', 'deep.md'), '');
22+
});
23+
24+
after(() => rmSync(root, { recursive: true, force: true }));
25+
26+
it('should return matching files relative to cwd, without directories', () => {
27+
assert.deepStrictEqual(findFiles('api/*.md', { cwd: root }).sort(), [
28+
'api/fs.md',
29+
'api/path.md',
30+
'api/quic.md',
31+
]);
32+
});
33+
34+
it('should leave out ignored files', () => {
35+
assert.deepStrictEqual(
36+
findFiles('api/*.md', { cwd: root, ignore: ['api/quic.md'] }).sort(),
37+
['api/fs.md', 'api/path.md']
38+
);
39+
});
40+
41+
it('should return absolute patterns relative to cwd', () => {
42+
assert.deepStrictEqual(
43+
findFiles(`${root.split('\\').join('/')}/api/fs.md`, { cwd: root }),
44+
['api/fs.md']
45+
);
46+
});
47+
48+
it('should accept a URL as cwd', () => {
49+
assert.deepStrictEqual(
50+
findFiles('*.md', { cwd: pathToFileURL(join(root, 'api')) }).sort(),
51+
['fs.md', 'path.md', 'quic.md']
52+
);
53+
});
54+
});
55+
56+
describe('globParent', () => {
57+
it('should return the directory before the first glob segment', () => {
58+
assert.strictEqual(globParent('doc/api/*.md'), 'doc/api');
59+
assert.strictEqual(globParent('./doc/api/*.md'), './doc/api');
60+
assert.strictEqual(globParent('/root/*.js'), '/root');
61+
assert.strictEqual(globParent('lib/**/*.js'), 'lib');
62+
assert.strictEqual(globParent('a/{b,c}/*.md'), 'a');
63+
assert.strictEqual(globParent('a/b/[x-y]*.md'), 'a/b');
64+
assert.strictEqual(globParent('a/+(b|c)/*.md'), 'a');
65+
});
66+
67+
it('should return the parent directory of a plain path', () => {
68+
assert.strictEqual(globParent('doc/api/fs.md'), 'doc/api');
69+
});
70+
71+
it('should fall back to the current or root directory', () => {
72+
assert.strictEqual(globParent('*.md'), '.');
73+
assert.strictEqual(globParent('/*.md'), '/');
74+
});
75+
});

‎packages/core/src/utils/glob.mjs‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { globSync } from 'node:fs';
2+
import { relative, resolve, sep } from 'node:path';
3+
import { fileURLToPath } from 'node:url';
4+
5+
// A path segment holding any glob syntax (wildcards, classes, braces or extglobs)
6+
const GLOB_SEGMENT = /[*?[{]|[!+@]\(/;
7+
8+
/**
9+
* Finds the files matching glob patterns, as POSIX paths relative to `cwd`
10+
* (absolute patterns included). Directories are left out.
11+
*
12+
* @param {string | string[]} patterns - Glob patterns to match
13+
* @param {{ cwd?: string | URL, ignore?: string[] }} [options]
14+
* @returns {string[]}
15+
*/
16+
export const findFiles = (patterns, { cwd = process.cwd(), ignore } = {}) => {
17+
const root = cwd instanceof URL ? fileURLToPath(cwd) : cwd;
18+
19+
return globSync(patterns, { cwd: root, exclude: ignore, withFileTypes: true })
20+
.filter(dirent => !dirent.isDirectory())
21+
.map(dirent =>
22+
relative(root, resolve(root, dirent.parentPath, dirent.name))
23+
.split(sep)
24+
.join('/')
25+
);
26+
};
27+
28+
/**
29+
* Returns the directory part of a glob pattern before its first glob segment,
30+
* e.g. `doc/api` for `doc/api/*.md`, or the parent directory of a plain path.
31+
*
32+
* @param {string} pattern - A glob pattern with POSIX separators
33+
* @returns {string}
34+
*/
35+
export const globParent = pattern => {
36+
const segments = pattern.split('/');
37+
const index = segments.findIndex(segment => GLOB_SEGMENT.test(segment));
38+
const parent = segments.slice(0, index === -1 ? -1 : index).join('/');
39+
40+
return parent || (pattern.startsWith('/') ? '/' : '.');
41+
};

‎packages/node/package.json‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
"unist-util-visit": "^5.1.0"
3232
},
3333
"devDependencies": {
34-
"tinyglobby": "^0.2.17",
3534
"unist-builder": "^4.0.0"
3635
}
3736
}

‎packages/node/src/api-links/__tests__/fixtures.test.mjs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import { loadGenerator } from '@doc-kit/core/generators/loader.mjs';
66
import createWorkerPool from '@doc-kit/core/threading/index.mjs';
77
import createParallelWorker from '@doc-kit/core/threading/parallel.mjs';
88
import { setConfig } from '@doc-kit/core/utils/configuration/index.mjs';
9-
import { globSync } from 'tinyglobby';
9+
import { findFiles } from '@doc-kit/core/utils/glob.mjs';
1010

1111
import { generate as apiLinksGenerate } from '../generate.mjs';
1212

1313
const relativePath = relative(process.cwd(), import.meta.dirname);
1414

15-
const sourceFiles = globSync('*.js', {
15+
const sourceFiles = findFiles('*.js', {
1616
cwd: new URL(import.meta.resolve('./fixtures')),
1717
});
1818

‎pnpm-lock.yaml‎

Lines changed: 0 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)