Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/node-glob.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@doc-kit/core': patch
---

Find input files with Node.js's `fs.globSync` instead of `tinyglobby` and `glob-parent`.
2 changes: 0 additions & 2 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@
"cosmiconfig": "^9.0.2",
"dedent": "^1.7.2",
"github-slugger": "^2.0.0",
"glob-parent": "^6.0.2",
"mdast-util-slice-markdown": "^2.0.1",
"mdast-util-to-string": "^4.0.0",
"piscina": "^5.3.2",
Expand All @@ -71,7 +70,6 @@
"remark-stringify": "^11.0.0",
"semver": "^7.8.5",
"shiki": "^4.4.3",
"tinyglobby": "^0.2.17",
"unified": "^11.0.5",
"unist-builder": "^4.0.0",
"unist-util-find-after": "^5.0.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/generators/ast-js/generate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { readFile } from 'node:fs/promises';
import { extname } from 'node:path';

import { parse } from 'acorn';
import { globSync } from 'tinyglobby';

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

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

const files = globSync(config.input, { ignore: config.ignore }).filter(
const files = findFiles(config.input, { ignore: config.ignore }).filter(
p => extname(p) === '.js'
);

Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/generators/ast/generate.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@
import { readFile } from 'node:fs/promises';
import { relative, sep } from 'node:path/posix';

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

import getConfig from '#utils/configuration/index.mjs';
import { withExt } from '#utils/file.mjs';
import { findFiles, globParent } from '#utils/glob.mjs';
import { getProcessor } from '#utils/markdown/processor.mjs';
import { QUERIES } from '#utils/queries/index.mjs';

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

return globSync(input, { ignore: config.ignore }).map(child => [
return findFiles(input, { ignore: config.ignore }).map(child => [
child,
parent,
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import { describe, it } from 'node:test';
import { fileURLToPath } from 'node:url';

import Ajv from 'ajv';
import { globSync } from 'tinyglobby';

import { loadGenerator } from '#generators/loader.mjs';
import { parseApiDoc } from '#generators/metadata/utils/parse.mjs';
import { findFiles } from '#utils/glob.mjs';
import { loadMarkdownPlugins } from '#utils/markdown/plugins.mjs';
import { getProcessor } from '#utils/markdown/processor.mjs';
import { QUERIES } from '#utils/queries/index.mjs';
Expand Down Expand Up @@ -77,7 +77,7 @@ const find = ({ children }, predicate) => {
};

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

Expand Down
86 changes: 86 additions & 0 deletions packages/core/src/utils/__tests__/glob.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
'use strict';

import assert from 'node:assert/strict';
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { after, before, describe, it } from 'node:test';
import { pathToFileURL } from 'node:url';

import { findFiles, globParent } from '../glob.mjs';

describe('findFiles', () => {
let root;

before(() => {
root = mkdtempSync(join(tmpdir(), 'doc-kit-glob-'));
mkdirSync(join(root, 'api', 'nested.md'), { recursive: true });
writeFileSync(join(root, 'api', 'fs.md'), '');
writeFileSync(join(root, 'api', 'path.md'), '');
writeFileSync(join(root, 'api', 'quic.md'), '');
writeFileSync(join(root, 'api', 'nested.md', 'deep.md'), '');
});

after(() => rmSync(root, { recursive: true, force: true }));

it('should return matching files relative to cwd, without directories', () => {
assert.deepStrictEqual(findFiles('api/*.md', { cwd: root }).sort(), [
'api/fs.md',
'api/path.md',
'api/quic.md',
]);
});

it('should leave out ignored files', () => {
assert.deepStrictEqual(
findFiles('api/*.md', { cwd: root, ignore: ['api/quic.md'] }).sort(),
['api/fs.md', 'api/path.md']
);
});

it('should return absolute patterns relative to cwd', () => {
assert.deepStrictEqual(
findFiles(`${root.split('\\').join('/')}/api/fs.md`, { cwd: root }),
['api/fs.md']
);
});

it('should accept a URL as cwd', () => {
assert.deepStrictEqual(
findFiles('*.md', { cwd: pathToFileURL(join(root, 'api')) }).sort(),
['fs.md', 'path.md', 'quic.md']
);
});
});

describe('globParent', () => {
it('should return the directory before the first glob segment', () => {
assert.strictEqual(globParent('doc/api/*.md'), 'doc/api');
assert.strictEqual(globParent('./doc/api/*.md'), './doc/api');
assert.strictEqual(globParent('/root/*.js'), '/root');
assert.strictEqual(globParent('lib/**/*.js'), 'lib');
assert.strictEqual(globParent('a/{b,c}/*.md'), 'a');
assert.strictEqual(globParent('a/b/[x-y]*.md'), 'a/b');
assert.strictEqual(globParent('a/+(b|c)/*.md'), 'a');
assert.strictEqual(globParent('a/b?d/*.md'), 'a');
});

it('should treat as plain what fs.globSync matches literally', () => {
assert.strictEqual(globParent('a/(b|c)/*.md'), 'a/(b|c)');
assert.strictEqual(globParent('a/b+c/*.md'), 'a/b+c');
});

it('should read backslashes as separators, like fs.globSync', () => {
assert.strictEqual(globParent('doc\\api\\*.md'), 'doc/api');
assert.strictEqual(globParent('C:\\docs\\*.md'), 'C:/docs');
});

it('should return the parent directory of a plain path', () => {
assert.strictEqual(globParent('doc/api/fs.md'), 'doc/api');
});

it('should fall back to the current or root directory', () => {
assert.strictEqual(globParent('*.md'), '.');
assert.strictEqual(globParent('/*.md'), '/');
});
});
42 changes: 42 additions & 0 deletions packages/core/src/utils/glob.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { globSync } from 'node:fs';
import { relative, resolve, sep } from 'node:path';
import { fileURLToPath } from 'node:url';

// A path segment holding any glob syntax (wildcards, classes, braces or extglobs)
const GLOB_SEGMENT = /[*?[{]|[!+@]\(/;
Comment thread
avivkeller marked this conversation as resolved.

/**
* Finds the files matching glob patterns, as POSIX paths relative to `cwd`
* (absolute patterns included). Directories are left out.
*
* @param {string | string[]} patterns - Glob patterns to match
* @param {{ cwd?: string | URL, ignore?: string[] }} [options]
* @returns {string[]}
*/
export const findFiles = (patterns, { cwd = process.cwd(), ignore } = {}) => {
const root = cwd instanceof URL ? fileURLToPath(cwd) : cwd;

return globSync(patterns, { cwd: root, exclude: ignore, withFileTypes: true })
.filter(dirent => !dirent.isDirectory())
.map(dirent =>
relative(root, resolve(root, dirent.parentPath, dirent.name))
.split(sep)
.join('/')
);
};

/**
* Returns the directory part of a glob pattern before its first glob segment,
* e.g. `doc/api` for `doc/api/*.md`, or the parent directory of a plain path.
* Like `fs.globSync`, it reads backslashes as separators, never as escapes.
*
* @param {string} pattern - A glob pattern
* @returns {string}
*/
export const globParent = pattern => {
const segments = pattern.replaceAll('\\', '/').split('/');
const index = segments.findIndex(segment => GLOB_SEGMENT.test(segment));
const parent = segments.slice(0, index === -1 ? -1 : index).join('/');

return parent || (segments[0] === '' ? '/' : '.');
};
1 change: 0 additions & 1 deletion packages/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"unist-util-visit": "^5.1.0"
},
"devDependencies": {
"tinyglobby": "^0.2.17",
"unist-builder": "^4.0.0"
}
}
4 changes: 2 additions & 2 deletions packages/node/src/api-links/__tests__/fixtures.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { loadGenerator } from '@doc-kit/core/generators/loader.mjs';
import createWorkerPool from '@doc-kit/core/threading/index.mjs';
import createParallelWorker from '@doc-kit/core/threading/parallel.mjs';
import { setConfig } from '@doc-kit/core/utils/configuration/index.mjs';
import { globSync } from 'tinyglobby';
import { findFiles } from '@doc-kit/core/utils/glob.mjs';

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

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

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

Expand Down
9 changes: 0 additions & 9 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading