diff --git a/.changeset/thirty-meals-pay.md b/.changeset/thirty-meals-pay.md
new file mode 100644
index 00000000..c5395ba2
--- /dev/null
+++ b/.changeset/thirty-meals-pay.md
@@ -0,0 +1,5 @@
+---
+"@codemod-utils/files": minor
+---
+
+Added JSDoc
diff --git a/packages/files/README.md b/packages/files/README.md
index e88e1d39..37491547 100644
--- a/packages/files/README.md
+++ b/packages/files/README.md
@@ -12,31 +12,29 @@ _Utilities for handling files_
## API
-Many of the methods make use of the **file path**, a string that represents the location of a file. Therefore, I recommend learning [`findFiles`](#findfiles-unionize) first, as it returns the paths of all files that match the search criteria.
+Many of the methods make use of the **file path**, a string that represents the location of a file. Therefore, I recommend learning [`findFiles`](#findfiles) first, as it returns the paths of all files that match the search criteria.
### copyFiles
-Copy files from one directory to another.
+Copies files from one directory (source) to another (destination). Creates the destination directory if it doesn't exist.
Example
-In [`ember-codemod-v1-to-v2`](https://github.com/ijlee2/ember-codemod-v1-to-v2/), we want to copy some files from the project root to the addon package.
+Copy `LICENSE.md` and `README.md` from the project root to the folder `ember-container-query`.
-```js
-import { copyFiles, mapFilePaths } from '@codemod-utils/files';
+```ts
+import { copyFiles } from '@codemod-utils/files';
-const filePaths = [/* ... */];
-
-const filePathMap = mapFilePaths(filePaths, {
- from: '',
- to: '__addonLocation__',
-});
+const filePathMap = new Map([
+ ['LICENSE.md', 'ember-container-query/LICENSE.md'],
+ ['README.md', 'ember-container-query/README.md'],
+]);
copyFiles(filePathMap, {
- projectRoot: '__projectRoot__',
+ projectRoot,
});
```
@@ -45,20 +43,39 @@ copyFiles(filePathMap, {
### createDirectory
-Ensure that, given a file path, the directories exist.
+Creates the directories specified in the file path, if they don't exist yet.
+
+⚠️ Likely, you won't need this method but [`createFiles`](#createfiles) instead.
+
+
+
+Example
+
+Create the folder `ember-container-query` if it doesn't exist.
+
+```ts
+import { createDirectory } from '@codemod-utils/files';
-⚠️ Likely, you won't need this method. Call [`createFiles`](#createfiles) instead.
+const newFilePath = 'ember-container-query/LICENSE.md';
+const destination = join(projectRoot, newFilePath);
+
+createDirectory(destination);
+```
+
+
### createFiles
-Create files in bulk. You will need to provide a mapping between file paths and file contents.
+Create files. Creates the destination directory if it doesn't exist.
Example
-```js
+Create `LICENSE.md` and `README.md` in the project root.
+
+```ts
import { createFiles } from '@codemod-utils/files';
const fileMap = new Map([
@@ -67,14 +84,14 @@ const fileMap = new Map([
]);
createFiles(fileMap, {
- projectRoot: '__projectRoot__',
+ projectRoot,
});
```
-### findFiles, unionize
+### findFiles
Often, you will want a codemod step to apply to select files. `findFiles` provides the paths of all files that match your search criteria (i.e. [glob pattern](https://github.com/isaacs/node-glob#glob-primer), ignore list, and project root). The paths are sorted in alphabetical order.
@@ -82,13 +99,13 @@ Often, you will want a codemod step to apply to select files. `findFiles` provid
Example
-In [`ember-codemod-v1-to-v2`](https://github.com/ijlee2/ember-codemod-v1-to-v2/), we want to move the `tests/dummy` folder to the test-app package.
+Find all component templates in an Ember app.
-```js
+```ts
import { findfiles } from '@codemod-utils/files';
-const filePaths = findFiles('tests/dummy/**/*', {
- projectRoot: '__projectRoot__',
+const filePaths = findFiles('app/components/**/*.hbs', {
+ projectRoot,
});
```
@@ -100,32 +117,44 @@ You can provide `ignoreList`, an array of file paths or glob patterns, to exclud
Example
-In [`ember-codemod-v1-to-v2`](https://github.com/ijlee2/ember-codemod-v1-to-v2/), we want to move some files in `tests` to the test-app package's `tests` folder.
+Find all component classes in an Ember app.
-```js
+```ts
import { findfiles } from '@codemod-utils/files';
-const filePaths = findFiles('tests/**/*', {
- ignoreList: ['tests/dummy/**/*'],
- projectRoot: '__projectRoot__',
+const filePaths = findFiles('app/components/**/*.{js,ts}', {
+ ignoreList: ['**/*.d.ts'],
+ projectRoot,
});
```
-Lastly, you can use `unionize` to look for multiple files ("possibly file A or file B or ..."):
+To look for multiple types of files, you can pass an array of glob patterns (pattern A or pattern B or ...).
-Example
+Examples
+
+```ts
+import { findfiles } from '@codemod-utils/files';
-```js
-import { findfiles, unionize } from '@codemod-utils/files';
+const filePaths = findFiles([
+ 'LICENSE.md',
+ 'README.md',
+], {
+ projectRoot,
+});
+```
-const files = ['LICENSE.md', 'README.md'];
+```ts
+import { findfiles } from '@codemod-utils/files';
-const filePaths = findFiles(unionize(files), {
- projectRoot: '__projectRoot__',
+const filePaths = findFiles([
+ 'app/components/**/*.hbs',
+ 'tests/integration/components/**/*-test.{js,ts}',
+], {
+ projectRoot,
});
```
@@ -134,31 +163,48 @@ const filePaths = findFiles(unionize(files), {
### mapFilePaths
-Create a mapping of file paths, which can be passed to [`createFiles`](#createfiles) or [`moveFiles`](#movefiles).
-
-
-### moveFiles
-
-Move files from one directory to another.
+Creates a mapping of file paths, which can then be passed to [`copyFiles`](#copyfiles) or [`moveFiles`](#movefiles).
Example
-In [`ember-codemod-v1-to-v2`](https://github.com/ijlee2/ember-codemod-v1-to-v2/), we want to move some files from the project root to the test-app package.
+Map `LICENSE.md` to `ember-container-query/LICENSE.md` (and similarly for `README.md`).
-```js
-import { mapFilePaths, moveFiles } from '@codemod-utils/files';
+```ts
+import { mapFilePaths } from '@codemod-utils/files';
-const filePaths = [/* ... */];
+const filePaths = ['LICENSE.md', 'README.md'];
const filePathMap = mapFilePaths(filePaths, {
from: '',
- to: '__testAppLocation__',
+ to: 'ember-container-query',
});
+```
+
+
+
+
+### moveFiles
+
+Moves files from one directory (source) to another (destination). Creates the destination directory if it doesn't exist. Removes the source directory if it is empty.
+
+
+
+Example
+
+Move `LICENSE.md` and `README.md` from the project root to a folder named `ember-container-query`.
+
+```ts
+import { moveFiles } from '@codemod-utils/files';
+
+const filePathMap = new Map([
+ ['LICENSE.md', 'ember-container-query/LICENSE.md'],
+ ['README.md', 'ember-container-query/README.md'],
+]);
moveFiles(filePathMap, {
- projectRoot: '__projectRoot__',
+ projectRoot,
});
```
@@ -167,24 +213,22 @@ moveFiles(filePathMap, {
### parseFilePath
-Parse a file path, just like you can with `parse` from `node:path`. `parseFilePath` can handle file extensions with more than one `.` (e.g. `.d.ts`, `.css.d.ts`).
+Parses a file path, similarly to `parse()` from `node:path`, but correctly handles file extensions with more than one `.`, e.g. `.d.ts` and `.css.d.ts`.
Example
-```js
+```ts
import { parseFilePath } from '@codemod-utils/files';
const filePath = 'src/components/navigation-menu.d.ts';
const { base, dir, ext, name } = parseFilePath(filePath);
-/*
- base = 'navigation-menu.d.ts'
- dir = 'src/components'
- ext = '.d.ts'
- name = 'navigation-menu'
-*/
+// base -> 'navigation-menu.d.ts'
+// dir -> 'src/components'
+// ext -> '.d.ts'
+// name -> 'navigation-menu'
```
@@ -192,26 +236,46 @@ const { base, dir, ext, name } = parseFilePath(filePath);
### removeDirectoryIfEmpty
-Ensure that, after deleting a file, the directories in the file path are removed if empty.
+Removes the directories specified in the file path, if they are empty.
-⚠️ Likely, you won't need this method. Call [`removeFiles`](#removefiles) instead.
+⚠️ Likely, you won't need this method but [`removeFiles`](#removefiles) instead.
+
+
+
+Example
+
+Remove the folder `ember-container-query` if it is empty.
+
+```ts
+import { removeDirectoryIfEmpty } from '@codemod-utils/files';
+
+const filePath = 'ember-container-query/LICENSE.md';
+
+removeDirectoryIfEmpty(filePath, {
+ projectRoot,
+});
+```
+
+
### removeFiles
-Remove files in bulk.
+Removes files. Removes the source directory if it is empty.
Example
-```js
+Remove `LICENSE.md` and `README.md` from the project root.
+
+```ts
import { removeFiles } from '@codemod-utils/files';
const filePaths = ['LICENSE.md', 'README.md'];
removeFiles(filePaths, {
- projectRoot: '__projectRoot__',
+ projectRoot,
});
```
@@ -220,33 +284,25 @@ removeFiles(filePaths, {
### renamePathByDirectory
-Get a new file path by altering the path's directory.
+Forms a new file path by altering the path's directory.
Example
-In [`ember-codemod-v1-to-v2`](https://github.com/ijlee2/ember-codemod-v1-to-v2/), we want to compute `appReexports`. To do so, we find all files in the `app` folder, then remove the word `app` from each file path.
+Prepare to move components from `addon` to `ember-container-query/src`.
-```js
+```ts
import { findFiles, renamePathByDirectory } from '@codemod-utils/files';
-function getAppReexports(options) {
- const { projectRoot } = options;
-
- const filePaths = findFiles('app/**/*.js', {
- cwd: projectRoot,
- });
-
- return filePaths
- .map((filePath) => {
- return renameDirectory(filePath, {
- from: 'app',
- to: '',
- });
- })
- .sort();
-}
+const oldFilePath = 'addon/components/container-query.hbs';
+
+const newFilePath = renamePathByDirectory(oldFilePath, {
+ from: 'addon',
+ to: 'ember-container-query/src',
+});
+
+// newFilePath -> 'ember-container-query/src/components/container-query.hbs'
```
@@ -254,38 +310,30 @@ function getAppReexports(options) {
### renamePathByFile
-Get a new file path by altering the path's file name.
+Forms a new file path by altering the path's file name.
Example
-In [`ember-codemod-pod-to-octane`](https://github.com/ijlee2/ember-codemod-pod-to-octane/), we want to "un-pod" components. To do so, we find all files in the `app/components` folder, then adjust the file name.
+Prepare to un-pod components.
-```js
+```ts
import { findFiles, renamePathByFile } from '@codemod-utils/files';
-function migrationStrategyForComponentClasses(options) {
- const { projectRoot } = options;
-
- const filePaths = findFiles('app/components/**/component.{d.ts,js,ts}', {
- projectRoot,
- });
-
- return filePaths.map((oldFilePath) => {
- const newFilePath = renamePathByFile(oldFilePath, {
- find: {
- directory: 'app/components',
- file: 'component',
- },
- replace: (key) => {
- return `app/components/${key}`;
- },
- });
-
- return [oldFilePath, newFilePath];
- });
-}
+const oldFilePath = 'app/components/navigation-menu/template.hbs';
+
+const newFilePath = renamePathByFile(oldFilePath, {
+ find: {
+ directory: 'app/components',
+ file: 'template',
+ },
+ replace: (key: string) => {
+ return `app/components/${key}`;
+ },
+});
+
+// newFilePath -> 'app/components/navigation-menu.hbs'
```
diff --git a/packages/files/src/files/copy-files.ts b/packages/files/src/files/copy-files.ts
index 5fd661f0..17e30e7e 100644
--- a/packages/files/src/files/copy-files.ts
+++ b/packages/files/src/files/copy-files.ts
@@ -4,7 +4,40 @@ import { join } from 'node:path';
import type { FilePathMap, Options } from '../types/index.js';
import { createDirectory } from './create-directory.js';
-export function copyFiles(filePathMap: FilePathMap, options: Options): void {
+/**
+ * Copies files from one directory (source) to another (destination).
+ * Creates the destination directory if it doesn't exist.
+ *
+ * @param filePathMap
+ *
+ * A mapping from source to destination.
+ *
+ * @param options
+ *
+ * An object with `projectRoot`.
+ *
+ * @example
+ *
+ * Copy `LICENSE.md` and `README.md` from the project root to the
+ * folder `ember-container-query`.
+ *
+ * ```ts
+ * const filePathMap = new Map([
+ * ['LICENSE.md', 'ember-container-query/LICENSE.md'],
+ * ['README.md', 'ember-container-query/README.md'],
+ * ]);
+ *
+ * copyFiles(filePathMap, {
+ * projectRoot,
+ * });
+ * ```
+ */
+export function copyFiles(
+ filePathMap: FilePathMap,
+ options: Options & {
+ projectRoot: string;
+ },
+): void {
const { projectRoot } = options;
filePathMap.forEach((newFilePath, oldFilePath) => {
diff --git a/packages/files/src/files/create-directory.ts b/packages/files/src/files/create-directory.ts
index f8e1b546..e8e30b1d 100644
--- a/packages/files/src/files/create-directory.ts
+++ b/packages/files/src/files/create-directory.ts
@@ -3,6 +3,27 @@ import { dirname } from 'node:path';
import type { FilePath } from '../types/index.js';
+/**
+ * Creates the directories specified in the file path, if they don't
+ * exist yet.
+ *
+ * ⚠️ Likely, you won't need this method but `createFiles()` instead.
+ *
+ * @param filePath
+ *
+ * A file path.
+ *
+ * @example
+ *
+ * Create the folder `ember-container-query` if it doesn't exist.
+ *
+ * ```ts
+ * const newFilePath = 'ember-container-query/LICENSE.md';
+ * const destination = join(projectRoot, newFilePath);
+ *
+ * createDirectory(destination);
+ * ```
+ */
export function createDirectory(filePath: FilePath): void {
const directory = dirname(filePath);
diff --git a/packages/files/src/files/create-files.ts b/packages/files/src/files/create-files.ts
index 9d57aba8..d16869bd 100644
--- a/packages/files/src/files/create-files.ts
+++ b/packages/files/src/files/create-files.ts
@@ -4,7 +4,39 @@ import { join } from 'node:path';
import type { FileMap, Options } from '../types/index.js';
import { createDirectory } from './create-directory.js';
-export function createFiles(fileMap: FileMap, options: Options): void {
+/**
+ * Creates files. Creates the destination directory if it doesn't
+ * exist.
+ *
+ * @param fileMap
+ *
+ * A mapping between the file path and the file content (UTF-8).
+ *
+ * @param options
+ *
+ * An object with `projectRoot`.
+ *
+ * @example
+ *
+ * Create `LICENSE.md` and `README.md` in the project root.
+ *
+ * ```ts
+ * const fileMap = new Map([
+ * ['LICENSE.md', 'The MIT License (MIT)'],
+ * ['README.md', '# ember-container-query'],
+ * ]);
+ *
+ * createFiles(fileMap, {
+ * projectRoot,
+ * });
+ * ```
+ */
+export function createFiles(
+ fileMap: FileMap,
+ options: Options & {
+ projectRoot: string;
+ },
+): void {
const { projectRoot } = options;
fileMap.forEach((file, filePath) => {
diff --git a/packages/files/src/files/find-files.ts b/packages/files/src/files/find-files.ts
index 4ab2e8cd..85e5ecbd 100644
--- a/packages/files/src/files/find-files.ts
+++ b/packages/files/src/files/find-files.ts
@@ -2,9 +2,70 @@ import { globSync } from 'glob';
import type { FilePath, Options } from '../types/index.js';
+/**
+ * Returns the paths of all files that match your search criteria
+ * (i.e. {@link https://github.com/isaacs/node-glob#glob-primer | glob pattern}, ignore list, and project root).
+ * The paths are sorted in alphabetical order.
+ *
+ * @param pattern
+ *
+ * A glob pattern that describes which files you are looking for.
+ *
+ * @param options
+ *
+ * An object with `ignoreList` (an array of file paths or glob
+ * patterns) and `projectRoot`.
+ *
+ * @return
+ *
+ * Paths of all files that match your search criteria.
+ *
+ * @example
+ *
+ * Find all component templates in an Ember app.
+ *
+ * ```ts
+ * const filePaths = findFiles('app/components/**\/*.hbs', {
+ * projectRoot,
+ * });
+ * ```
+ *
+ * @example
+ *
+ * Find all component classes in an Ember app.
+ *
+ * ```ts
+ * const filePaths = findFiles('app/components/**\/*.{js,ts}', {
+ * ignoreList: ['**\/*.d.ts'],
+ * projectRoot,
+ * });
+ * ```
+ *
+ * @example
+ *
+ * Pass an array of glob patterns (pattern A or pattern B or ...).
+ *
+ * ```ts
+ * const filePaths = findFiles([
+ * 'LICENSE.md',
+ * 'README.md',
+ * ], {
+ * projectRoot,
+ * });
+ * ```
+ *
+ * ```ts
+ * const filePaths = findFiles([
+ * 'app/components/**\/*.hbs',
+ * 'tests/integration/components/**\/*-test.{js,ts}',
+ * ], {
+ * projectRoot,
+ * });
+ * ```
+ */
export function findFiles(
pattern: string | string[],
- options: Options & { ignoreList?: string[] },
+ options: Options & { ignoreList?: string[]; projectRoot: string },
): FilePath[] {
const { ignoreList = [], projectRoot } = options;
@@ -25,11 +86,3 @@ export function findFiles(
return filePaths.sort();
}
-
-export function unionize(files: string[]): string {
- if (files.length <= 1) {
- return files.join(',');
- }
-
- return `{${files.join(',')}}`;
-}
diff --git a/packages/files/src/files/map-file-paths.ts b/packages/files/src/files/map-file-paths.ts
index 87974f0d..0a0825d6 100644
--- a/packages/files/src/files/map-file-paths.ts
+++ b/packages/files/src/files/map-file-paths.ts
@@ -1,12 +1,39 @@
import type { FilePath } from '../types/index.js';
import { renamePathByDirectory } from './rename-path-by-directory.js';
-type Options = {
- from: string;
- to: string;
-};
-
-export function mapFilePaths(filePaths: FilePath[], options: Options) {
+/**
+ * Creates a mapping of file paths, which can then be passed to
+ * `copyFiles()` or `moveFiles()`.
+ *
+ * @param filePaths
+ *
+ * An array of file paths. The array may come from `findFiles()`.
+ *
+ * @param options
+ *
+ * An object with `from` and `to`.
+ *
+ * @example
+ *
+ * Map `LICENSE.md` to `ember-container-query/LICENSE.md` (and
+ * similarly for `README.md`).
+ *
+ * ```ts
+ * const filePaths = ['LICENSE.md', 'README.md'];
+ *
+ * const filePathMap = mapFilePaths(filePaths, {
+ * from: '',
+ * to: 'ember-container-query',
+ * });
+ * ```
+ */
+export function mapFilePaths(
+ filePaths: FilePath[],
+ options: {
+ from: string;
+ to: string;
+ },
+) {
const { from, to } = options;
return new Map(
diff --git a/packages/files/src/files/move-files.ts b/packages/files/src/files/move-files.ts
index 52abcbef..0ab59c81 100644
--- a/packages/files/src/files/move-files.ts
+++ b/packages/files/src/files/move-files.ts
@@ -5,7 +5,41 @@ import type { FilePathMap, Options } from '../types/index.js';
import { createDirectory } from './create-directory.js';
import { removeDirectoryIfEmpty } from './remove-directory-if-empty.js';
-export function moveFiles(filePathMap: FilePathMap, options: Options): void {
+/**
+ * Moves files from one directory (source) to another (destination).
+ * Creates the destination directory if it doesn't exist. Removes
+ * the source directory if it is empty.
+ *
+ * @param filePathMap
+ *
+ * A mapping from source to destination.
+ *
+ * @param options
+ *
+ * An object with `projectRoot`.
+ *
+ * @example
+ *
+ * Move `LICENSE.md` and `README.md` from the project root to a
+ * folder named `ember-container-query`.
+ *
+ * ```ts
+ * const filePathMap = new Map([
+ * ['LICENSE.md', 'ember-container-query/LICENSE.md'],
+ * ['README.md', 'ember-container-query/README.md'],
+ * ]);
+ *
+ * moveFiles(filePathMap, {
+ * projectRoot,
+ * });
+ * ```
+ */
+export function moveFiles(
+ filePathMap: FilePathMap,
+ options: Options & {
+ projectRoot: string;
+ },
+): void {
const { projectRoot } = options;
filePathMap.forEach((newFilePath, oldFilePath) => {
diff --git a/packages/files/src/files/parse-file-path.ts b/packages/files/src/files/parse-file-path.ts
index 1e54ad5a..c14a3a3d 100644
--- a/packages/files/src/files/parse-file-path.ts
+++ b/packages/files/src/files/parse-file-path.ts
@@ -2,6 +2,31 @@ import { parse } from 'node:path';
import type { FilePath, ParsedPath } from '../types/index.js';
+/**
+ * Parses a file path, similarly to `parse()` from `node:path`,
+ * but correctly handles file extensions with more than one `.`,
+ * e.g. `.d.ts` and `.css.d.ts`.
+ *
+ * @param filePath
+ *
+ * A file path.
+ *
+ * @return
+ *
+ * An object with `base`, `dir`, `ext`, and `name`.
+ *
+ * @example
+ *
+ * ```ts
+ * const filePath = 'src/components/navigation-menu.d.ts';
+ * const { base, dir, ext, name } = parseFilePath(filePath);
+ *
+ * // base -> 'navigation-menu.d.ts'
+ * // dir -> 'src/components'
+ * // ext -> '.d.ts'
+ * // name -> 'navigation-menu'
+ * ```
+ */
export function parseFilePath(filePath: FilePath): ParsedPath {
// eslint-disable-next-line prefer-const
let { base, dir, ext, name } = parse(filePath);
diff --git a/packages/files/src/files/remove-directory-if-empty.ts b/packages/files/src/files/remove-directory-if-empty.ts
index 81b5a630..4f7ebebd 100644
--- a/packages/files/src/files/remove-directory-if-empty.ts
+++ b/packages/files/src/files/remove-directory-if-empty.ts
@@ -3,9 +3,37 @@ import { dirname, join } from 'node:path';
import type { FilePath, Options } from '../types/index.js';
+/**
+ * Removes the directories specified in the file path, if they are
+ * empty.
+ *
+ * ⚠️ Likely, you won't need this method but `removeFiles()` instead.
+ *
+ * @param filePath
+ *
+ * A file path.
+ *
+ * @param options
+ *
+ * An object with `projectRoot`.
+ *
+ * @example
+ *
+ * Remove the folder `ember-container-query` if it is empty.
+ *
+ * ```ts
+ * const filePath = 'ember-container-query/LICENSE.md';
+ *
+ * removeDirectoryIfEmpty(filePath, {
+ * projectRoot,
+ * });
+ * ```
+ */
export function removeDirectoryIfEmpty(
filePath: FilePath,
- options: Options,
+ options: Options & {
+ projectRoot: string;
+ },
): void {
const { projectRoot } = options;
diff --git a/packages/files/src/files/remove-files.ts b/packages/files/src/files/remove-files.ts
index 27d52b7b..a3aec8c4 100644
--- a/packages/files/src/files/remove-files.ts
+++ b/packages/files/src/files/remove-files.ts
@@ -4,7 +4,35 @@ import { join } from 'node:path';
import type { FilePath, Options } from '../types/index.js';
import { removeDirectoryIfEmpty } from './remove-directory-if-empty.js';
-export function removeFiles(filePaths: FilePath[], options: Options): void {
+/**
+ * Removes files. Removes the source directory if it is empty.
+ *
+ * @param filePaths
+ *
+ * An array of file paths.
+ *
+ * @param options
+ *
+ * An object with `projectRoot`.
+ *
+ * @example
+ *
+ * Remove `LICENSE.md` and `README.md` from the project root.
+ *
+ * ```ts
+ * const filePaths = ['LICENSE.md', 'README.md'];
+ *
+ * removeFiles(filePaths, {
+ * projectRoot,
+ * });
+ * ```
+ */
+export function removeFiles(
+ filePaths: FilePath[],
+ options: Options & {
+ projectRoot: string;
+ },
+): void {
const { projectRoot } = options;
filePaths.forEach((filePath) => {
diff --git a/packages/files/src/files/rename-path-by-directory.ts b/packages/files/src/files/rename-path-by-directory.ts
index eac48cde..285ba260 100644
--- a/packages/files/src/files/rename-path-by-directory.ts
+++ b/packages/files/src/files/rename-path-by-directory.ts
@@ -2,14 +2,42 @@ import { join } from 'node:path';
import type { FilePath } from '../types/index.js';
-type Options = {
- from: string;
- to: string;
-};
-
+/**
+ * Forms a new file path by altering the path's directory.
+ *
+ * @param filePath
+ *
+ * A file path.
+ *
+ * @param options
+ *
+ * An object with `from` and `to`.
+ *
+ * @return
+ *
+ * A file path.
+ *
+ * @example
+ *
+ * Prepare to move components from `addon` to `ember-container-query/src`.
+ *
+ * ```ts
+ * const oldFilePath = 'addon/components/container-query.hbs';
+ *
+ * const newFilePath = renamePathByDirectory(oldFilePath, {
+ * from: 'addon',
+ * to: 'ember-container-query/src',
+ * });
+ *
+ * // newFilePath -> 'ember-container-query/src/components/container-query.hbs'
+ * ```
+ */
export function renamePathByDirectory(
filePath: FilePath,
- options: Options,
+ options: {
+ from: string;
+ to: string;
+ },
): FilePath {
const { from, to } = options;
diff --git a/packages/files/src/files/rename-path-by-file.ts b/packages/files/src/files/rename-path-by-file.ts
index de2e27b3..42277821 100644
--- a/packages/files/src/files/rename-path-by-file.ts
+++ b/packages/files/src/files/rename-path-by-file.ts
@@ -1,17 +1,50 @@
import type { FilePath } from '../types/index.js';
import { parseFilePath } from './parse-file-path.js';
-type Options = {
- find: {
- directory: string;
- file: string;
- };
- replace: (key: string) => string;
-};
-
+/**
+ * Forms a new file path by altering the path's file name.
+ *
+ * @param filePath
+ *
+ * A file path.
+ *
+ * @param options
+ *
+ * An object with `find` and `replace`.
+ *
+ * @return
+ *
+ * A file path.
+ *
+ * @example
+ *
+ * Prepare to un-pod components.
+ *
+ * ```ts
+ * const oldFilePath = 'app/components/navigation-menu/template.hbs';
+ *
+ * const newFilePath = renamePathByFile(oldFilePath, {
+ * find: {
+ * directory: 'app/components',
+ * file: 'template',
+ * },
+ * replace: (key: string) => {
+ * return `app/components/${key}`;
+ * },
+ * });
+ *
+ * // newFilePath -> 'app/components/navigation-menu.hbs'
+ * ```
+ */
export function renamePathByFile(
filePath: FilePath,
- options: Options,
+ options: {
+ find: {
+ directory: string;
+ file: string;
+ };
+ replace: (key: string) => string;
+ },
): FilePath {
const { dir, ext, name } = parseFilePath(filePath);
const { find, replace } = options;
diff --git a/packages/files/src/files/unionize.ts b/packages/files/src/files/unionize.ts
new file mode 100644
index 00000000..3a1a5a94
--- /dev/null
+++ b/packages/files/src/files/unionize.ts
@@ -0,0 +1,40 @@
+/**
+ * Returns the glob pattern that can search multiple files
+ * ("file A or file B or ..."). The glob pattern is to be
+ * passed to `findFiles()`.
+ *
+ * @deprecated
+ *
+ * Pass an array of glob patterns to `findFiles()` instead.
+ *
+ * @param files
+ *
+ * An array of file paths.
+ *
+ * @return
+ *
+ * A glob pattern that can be passed to `findFiles()`.
+ *
+ * @example
+ *
+ * Look for multiple files:
+ *
+ * ```ts
+ * const pattern = unionize([
+ * 'package-lock.json',
+ * 'pnpm-lock.yaml',
+ * 'yarn.lock',
+ * ]);
+ *
+ * const filePaths = findFiles(pattern, {
+ * projectRoot,
+ * });
+ * ```
+ */
+export function unionize(files: string[]): string {
+ if (files.length <= 1) {
+ return files.join(',');
+ }
+
+ return `{${files.join(',')}}`;
+}
diff --git a/packages/files/src/index.ts b/packages/files/src/index.ts
index f38108d1..95d5b92c 100644
--- a/packages/files/src/index.ts
+++ b/packages/files/src/index.ts
@@ -9,4 +9,5 @@ export * from './files/remove-directory-if-empty.js';
export * from './files/remove-files.js';
export * from './files/rename-path-by-directory.js';
export * from './files/rename-path-by-file.js';
+export * from './files/unionize.js';
export * from './types/index.js';
diff --git a/packages/files/src/types/index.ts b/packages/files/src/types/index.ts
index a223f746..43d9585a 100644
--- a/packages/files/src/types/index.ts
+++ b/packages/files/src/types/index.ts
@@ -6,10 +6,7 @@ type FilePath = string;
type FilePathMap = Map;
-type Options = {
- [key: string]: unknown;
- projectRoot: string;
-};
+type Options = Record;
type ParsedPath = {
base: string;
diff --git a/packages/files/tests/files/find-files/edge-case-projectRoot-does-not-exist.test.ts b/packages/files/tests/files/find-files/edge-case-projectRoot-does-not-exist.test.ts
index 29aa165c..5b84b56a 100644
--- a/packages/files/tests/files/find-files/edge-case-projectRoot-does-not-exist.test.ts
+++ b/packages/files/tests/files/find-files/edge-case-projectRoot-does-not-exist.test.ts
@@ -35,7 +35,7 @@ test('files | find-files > edge case (projectRoot does not exist)', function ()
loadFixture(inputProject, codemodOptions);
const filePaths = findFiles('tests/dummy/**/*.{js,ts}', {
- projectRoot: 'foo',
+ projectRoot: 'path/to/somewhere/else',
});
assert.deepStrictEqual(filePaths, []);
diff --git a/packages/files/tests/files/find-files/multiple-patterns.test.ts b/packages/files/tests/files/find-files/multiple-patterns.test.ts
new file mode 100644
index 00000000..ea4fbb27
--- /dev/null
+++ b/packages/files/tests/files/find-files/multiple-patterns.test.ts
@@ -0,0 +1,48 @@
+import { assert, loadFixture, test } from '@codemod-utils/tests';
+
+import { findFiles } from '../../../src/index.js';
+import { codemodOptions, options } from '../../shared-test-setups/index.js';
+
+test('files | find-files > multiple patterns', function () {
+ const inputProject = {
+ tests: {
+ dummy: {
+ app: {
+ '.eslintrc.js': '',
+ 'app.ts': '',
+ 'index.html': '',
+ 'router.ts': '',
+ },
+ config: {
+ 'environment.js': '',
+ 'optional-features.json': '',
+ 'targets.js': '',
+ },
+ },
+ integration: {
+ components: {
+ 'container-query-test.ts': '',
+ },
+ },
+ 'index.html': '',
+ 'test-helper.ts': '',
+ },
+ 'ember-cli-build.js': '',
+ 'index.js': '',
+ 'package.json': '',
+ };
+
+ loadFixture(inputProject, codemodOptions);
+
+ const filePaths = findFiles(['**/*.json', '**/index.*'], {
+ projectRoot: options.projectRoot,
+ });
+
+ assert.deepStrictEqual(filePaths, [
+ 'index.js',
+ 'package.json',
+ 'tests/dummy/app/index.html',
+ 'tests/dummy/config/optional-features.json',
+ 'tests/index.html',
+ ]);
+});
diff --git a/packages/files/tests/files/map-file-paths/base-case.test.ts b/packages/files/tests/files/map-file-paths/base-case.test.ts
index f801dbaf..dd0e8b78 100644
--- a/packages/files/tests/files/map-file-paths/base-case.test.ts
+++ b/packages/files/tests/files/map-file-paths/base-case.test.ts
@@ -3,21 +3,21 @@ import { assert, test } from '@codemod-utils/tests';
import { mapFilePaths } from '../../../src/index.js';
test('files | map-file-paths > base case', function () {
- const filePaths = ['addon/some-folder/some-file.ts', 'addon/.gitkeep'];
+ const filePaths = ['addon/components/container-query.hbs', 'addon/.gitkeep'];
const filePathMap = mapFilePaths(filePaths, {
from: 'addon',
- to: 'new-location/src',
+ to: 'ember-container-query/src',
});
assert.deepStrictEqual(
filePathMap,
new Map([
[
- 'addon/some-folder/some-file.ts',
- 'new-location/src/some-folder/some-file.ts',
+ 'addon/components/container-query.hbs',
+ 'ember-container-query/src/components/container-query.hbs',
],
- ['addon/.gitkeep', 'new-location/src/.gitkeep'],
+ ['addon/.gitkeep', 'ember-container-query/src/.gitkeep'],
]),
);
});
diff --git a/packages/files/tests/files/map-file-paths/edge-case-no-match.test.ts b/packages/files/tests/files/map-file-paths/edge-case-no-match.test.ts
index 74460dc4..686068f2 100644
--- a/packages/files/tests/files/map-file-paths/edge-case-no-match.test.ts
+++ b/packages/files/tests/files/map-file-paths/edge-case-no-match.test.ts
@@ -7,7 +7,7 @@ test('files | map-file-paths > edge case (no match)', function () {
const filePathMap = mapFilePaths(filePaths, {
from: 'addon',
- to: 'new-location/src',
+ to: 'ember-container-query/src',
});
assert.deepStrictEqual(
diff --git a/packages/files/tests/files/rename-path-by-directory/base-case.test.ts b/packages/files/tests/files/rename-path-by-directory/base-case.test.ts
index b79f727e..db2162dd 100644
--- a/packages/files/tests/files/rename-path-by-directory/base-case.test.ts
+++ b/packages/files/tests/files/rename-path-by-directory/base-case.test.ts
@@ -3,12 +3,15 @@ import { assert, test } from '@codemod-utils/tests';
import { renamePathByDirectory } from '../../../src/index.js';
test('files | rename-path-by-directory > base case', function () {
- const oldFilePath = 'addon/some-folder/some-file.ts';
+ const oldFilePath = 'addon/components/container-query.hbs';
const newFilePath = renamePathByDirectory(oldFilePath, {
from: 'addon',
- to: 'new-location/src',
+ to: 'ember-container-query/src',
});
- assert.strictEqual(newFilePath, 'new-location/src/some-folder/some-file.ts');
+ assert.strictEqual(
+ newFilePath,
+ 'ember-container-query/src/components/container-query.hbs',
+ );
});
diff --git a/packages/files/tests/files/rename-path-by-directory/edge-case-from-is-empty.test.ts b/packages/files/tests/files/rename-path-by-directory/edge-case-from-is-empty.test.ts
index 63d52110..e18a21f4 100644
--- a/packages/files/tests/files/rename-path-by-directory/edge-case-from-is-empty.test.ts
+++ b/packages/files/tests/files/rename-path-by-directory/edge-case-from-is-empty.test.ts
@@ -3,15 +3,15 @@ import { assert, test } from '@codemod-utils/tests';
import { renamePathByDirectory } from '../../../src/index.js';
test('files | rename-path-by-directory > edge case (from is empty)', function () {
- const oldFilePath = 'addon/some-folder/some-file.ts';
+ const oldFilePath = 'addon/components/container-query.hbs';
const newFilePath = renamePathByDirectory(oldFilePath, {
from: '',
- to: 'new-location/src',
+ to: 'ember-container-query/src',
});
assert.strictEqual(
newFilePath,
- 'new-location/src/addon/some-folder/some-file.ts',
+ 'ember-container-query/src/addon/components/container-query.hbs',
);
});
diff --git a/packages/files/tests/files/rename-path-by-directory/edge-case-no-match.test.ts b/packages/files/tests/files/rename-path-by-directory/edge-case-no-match.test.ts
index 4ec06172..dc380c83 100644
--- a/packages/files/tests/files/rename-path-by-directory/edge-case-no-match.test.ts
+++ b/packages/files/tests/files/rename-path-by-directory/edge-case-no-match.test.ts
@@ -7,7 +7,7 @@ test('files | rename-path-by-directory > edge case (no match)', function () {
const newFilePath = renamePathByDirectory(oldFilePath, {
from: 'addon',
- to: 'new-location/src',
+ to: 'ember-container-query/src',
});
assert.strictEqual(newFilePath, 'addon');
diff --git a/packages/files/tests/files/rename-path-by-directory/edge-case-to-is-empty.test.ts b/packages/files/tests/files/rename-path-by-directory/edge-case-to-is-empty.test.ts
index cb5be2ee..fd3e5f9e 100644
--- a/packages/files/tests/files/rename-path-by-directory/edge-case-to-is-empty.test.ts
+++ b/packages/files/tests/files/rename-path-by-directory/edge-case-to-is-empty.test.ts
@@ -3,12 +3,12 @@ import { assert, test } from '@codemod-utils/tests';
import { renamePathByDirectory } from '../../../src/index.js';
test('files | rename-path-by-directory > edge case (to is empty)', function () {
- const oldFilePath = 'addon/some-folder/some-file.ts';
+ const oldFilePath = 'addon/components/container-query.hbs';
const newFilePath = renamePathByDirectory(oldFilePath, {
from: 'addon',
to: '',
});
- assert.strictEqual(newFilePath, 'some-folder/some-file.ts');
+ assert.strictEqual(newFilePath, 'components/container-query.hbs');
});
diff --git a/packages/files/tests/files/unionize/base-case.test.ts b/packages/files/tests/files/unionize/base-case.test.ts
new file mode 100644
index 00000000..2fa7a8f7
--- /dev/null
+++ b/packages/files/tests/files/unionize/base-case.test.ts
@@ -0,0 +1,13 @@
+import { assert, test } from '@codemod-utils/tests';
+
+import { unionize } from '../../../src/index.js';
+
+test('files | unionize > base case', function () {
+ const pattern = unionize([
+ 'package-lock.json',
+ 'pnpm-lock.yaml',
+ 'yarn.lock',
+ ]);
+
+ assert.strictEqual(pattern, '{package-lock.json,pnpm-lock.yaml,yarn.lock}');
+});
diff --git a/packages/files/tests/files/unionize/edge-case-no-file-paths.test.ts b/packages/files/tests/files/unionize/edge-case-no-file-paths.test.ts
new file mode 100644
index 00000000..8d2c0354
--- /dev/null
+++ b/packages/files/tests/files/unionize/edge-case-no-file-paths.test.ts
@@ -0,0 +1,9 @@
+import { assert, test } from '@codemod-utils/tests';
+
+import { unionize } from '../../../src/index.js';
+
+test('files | unionize > edge case (no file paths)', function () {
+ const pattern = unionize([]);
+
+ assert.strictEqual(pattern, '');
+});
diff --git a/packages/files/tests/files/unionize/edge-case-one-file-path.test.ts b/packages/files/tests/files/unionize/edge-case-one-file-path.test.ts
new file mode 100644
index 00000000..86a66b56
--- /dev/null
+++ b/packages/files/tests/files/unionize/edge-case-one-file-path.test.ts
@@ -0,0 +1,9 @@
+import { assert, test } from '@codemod-utils/tests';
+
+import { unionize } from '../../../src/index.js';
+
+test('files | unionize > edge case (1 file path)', function () {
+ const pattern = unionize(['README.md']);
+
+ assert.strictEqual(pattern, 'README.md');
+});