xref: /expo/tools/src/Transforms.ts (revision 2d0cd754)
1import fs from 'fs-extra';
2import minimatch from 'minimatch';
3import path from 'path';
4
5import { CopyFileOptions, CopyFileResult, StringTransform } from './Transforms.types';
6import { arrayize } from './Utils';
7
8export * from './Transforms.types';
9
10/**
11 * Transforms input string according to the given transform rules.
12 */
13export function transformString(
14  input: string,
15  transforms: StringTransform[] | null | undefined
16): string {
17  if (!transforms) {
18    return input;
19  }
20  return transforms.reduce(
21    // @ts-ignore @tsapeta: TS gets crazy on `replaceWith` being a function.
22    (acc, { find, replaceWith }) => acc.replace(find, replaceWith),
23    input
24  );
25}
26
27/**
28 * Transforms file's content in-place.
29 */
30export async function transformFileAsync(
31  filePath: string,
32  transforms: StringTransform[] | null | undefined
33): Promise<void> {
34  const content = await fs.readFile(filePath, 'utf8');
35  await fs.outputFile(filePath, transformString(content, transforms));
36}
37
38/**
39 * Copies a file from source directory to target directory with transformed relative path and content.
40 */
41export async function copyFileWithTransformsAsync(
42  options: CopyFileOptions
43): Promise<CopyFileResult> {
44  const { sourceFile, sourceDirectory, targetDirectory, transforms } = options;
45  const sourcePath = path.join(sourceDirectory, sourceFile);
46
47  // Transform the target path according to rename rules.
48  const targetFile = transformString(sourceFile, transforms.path);
49  const targetPath = path.join(targetDirectory, targetFile);
50
51  // Filter out transforms that don't match paths patterns.
52  const filteredContentTransforms =
53    transforms.content?.filter(
54      ({ paths }) =>
55        !paths ||
56        arrayize(paths).some((pattern) => minimatch(sourceFile, pattern, { matchBase: true }))
57    ) ?? [];
58
59  // Transform source content.
60  const content = transformString(await fs.readFile(sourcePath, 'utf8'), filteredContentTransforms);
61
62  // Save transformed source file at renamed target path.
63  await fs.outputFile(targetPath, content);
64
65  return {
66    content,
67    targetFile,
68  };
69}
70