1import { Task } from './Task';
2import path from 'path';
3import chalk from 'chalk';
4import fs from 'fs-extra';
5import { toRepoPath, findFiles } from '../utils';
6
7export type CopyFilesSettings = {
8  from?: string;
9  subDirectory?: string;
10  filePattern: string | string[];
11  to: string;
12};
13
14/**
15 *  A task which will copy files from `workingDirectory()/subDirectory/filePatterns` to the provided path.
16 *
17 * It's searching for file names which match `filePatterns` and then it copies them into `to/<matched_part_of_file_name>`.
18 * So the `subDirectory` part won't be copied.
19 *
20 * If for this file structure:
21 *
22 * ```
23 * android/
24 *   src/
25 *     main.java
26 *   lib/
27 *     lib.java
28 * ```
29 *
30 * you runs CopyFiles with:
31 * ```
32 * {
33 *   from: 'android',
34 *   subDirectory: 'src|lib',
35 *   to: 'copied',
36 *   filePatterns: '*'
37 * }
38 * ```
39 * you gets:
40 * ```
41 * android/
42 *   src/
43 *     main.java
44 *   lib/
45 *     lib.java
46 * lib/
47 *   main.java
48 *   lib.java
49 * ```
50 */
51export class CopyFiles extends Task {
52  private from?: string;
53  private subDirectory?: string;
54  private readonly filePattern: string[];
55  private readonly to: string;
56
57  /**
58   * Using `from` key, you can override the work directory.
59   * @param settings
60   */
61  constructor({ from, subDirectory, filePattern, to }: CopyFilesSettings) {
62    super();
63    this.from = from;
64    this.subDirectory = subDirectory;
65    this.to = toRepoPath(to);
66    if (typeof filePattern === 'string') {
67      this.filePattern = [filePattern];
68    } else {
69      this.filePattern = filePattern;
70    }
71  }
72
73  protected overrideWorkingDirectory(): string | undefined {
74    return this.from;
75  }
76
77  async execute() {
78    const workDirectory = this.getWorkingDirectory();
79
80    for (const pattern of this.filePattern) {
81      const subPath = this.subDirectory
82        ? path.join(workDirectory, this.subDirectory)
83        : workDirectory;
84
85      this.logSubStep(
86        `�� copy ${chalk.green(this.from || '<workingDirectory>')}/${chalk.green(
87          this.subDirectory ? this.subDirectory + '/' : ''
88        )}${chalk.yellow(pattern)} into ${chalk.magenta(this.to)}`
89      );
90
91      const files = await findFiles(subPath, pattern);
92      await Promise.all(
93        files.map(async (file) => {
94          const relativeFilePath = path.relative(subPath, file);
95          const destinationFullPath = path.join(this.to, relativeFilePath);
96
97          await fs.mkdirs(path.dirname(destinationFullPath));
98          return await fs.copy(file, destinationFullPath);
99        })
100      );
101    }
102  }
103}
104