Skip to content

request: port walk from subpath-updater to a proper function in utilities package #463

Closed
@favna

Description

Is there an existing issue or pull request for this?

  • I have searched the existing issues and pull requests

Feature description

In #454 we added a script subpath-updater.mjs which has a function walk to deeply walk a directory path and find files matching a file extension. It would be useful if this would be ported to a proper function in the utilities package.

Desired solution

The following is a write-up of the function as it should approximately be. The name of the function is still open to discussion.

Other than that unit tests should be written.

Furthermore, the turbo.json should be updated to require @sapphire/utilities to be built before check-subpath is ran.

import type { PathLike } from 'node:fs';
import { opendir } from 'node:fs/promises';
import { join } from 'node:path';

/**
 *
 * @param path The path in which to find files. This can be a string, buffer or {@link URL}.
 * @param fileNameEndsWith The string pattern with which the file name must end.
 * Ideally this is a file extension, however you can also provide more parts of the end of the file.
 *
 * Note that we do **not** support a full globby pattern using asterisk for wildcards. It has to be an exact match with {@link String.endsWith}
 *
 * @return An {@link AsyncIterableIterator<string>} of all the files. To loop over these use `for await (const file of walk(path, fileNameEndsWith)) {}`
 *
 *
 * @example
 * ```typescript
 * // With CommonJS: To find all files ending with `.ts` in the src directory:
 * const path = require('node:path');
 *
 * for await (const file of walk(path.join(__dirname, 'src'), '.ts')) {
 *   console.log(file);
 * }
 * ```
 *
 * @example
 * ```typescript
 * // With ESM: To find all files ending with `.ts` in the src directory:
 * for await (const file of walk(new URL('src', import.meta.url), '.ts')) {
 *   console.log(file);
 * }
 * ```
 */
async function* findFilesRecursively(path: PathLike, fileNameEndsWith: string): AsyncIterableIterator<string> {
	try {
		const dir = await opendir(path);

		for await (const item of dir) {
			if (item.isFile() && (!fileNameEndsWith || item.name.endsWith(fileNameEndsWith))) {
				yield join(dir.path, item.name);
			} else if (item.isDirectory()) {
				yield* findFilesRecursively(join(dir.path, item.name), fileNameEndsWith);
			}
		}
	} catch (error) {
		if ((error as any).code !== 'ENOENT') {
			console.error(error);
		}
	}
}

Alternatives considered

N.A.

Additional context

The name of the function is still open for discussing. Likewise we can also considering extending this to several functions using 1 base function and passing a predicate so those several functions can do startsWith, endsWith, includes and regex. For example taking the code above for findFilesRecursively we could have findFilesRecursivelyStringStartsWith, findFilesRecursivelyStringEndsWith, etc.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Type

No type

Projects

  • Status

    Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions