1import chalk from 'chalk';
2import { watchFile } from 'fs';
3import path from 'path';
4import resolveFrom from 'resolve-from';
5
6import * as Log from '../log';
7import { memoize } from './fn';
8
9const debug = require('debug')('expo:utils:fileNotifier') as typeof console.log;
10
11/** Observes and reports file changes. */
12export class FileNotifier {
13  constructor(
14    /** Project root to resolve the module IDs relative to. */
15    private projectRoot: string,
16    /** List of module IDs sorted by priority. Only the first file that exists will be observed. */
17    private moduleIds: string[],
18    private settings: {
19      /** An additional warning message to add to the notice. */
20      additionalWarning?: string;
21    } = {}
22  ) {}
23
24  /** Get the file in the project. */
25  private resolveFilePath(): string | null {
26    for (const moduleId of this.moduleIds) {
27      const filePath = resolveFrom.silent(this.projectRoot, moduleId);
28      if (filePath) {
29        return filePath;
30      }
31    }
32    return null;
33  }
34
35  public startObserving() {
36    const configPath = this.resolveFilePath();
37    if (configPath) {
38      debug(`Observing ${configPath}`);
39      return this.watchFile(configPath);
40    }
41    return configPath;
42  }
43
44  /** Watch the file and warn to reload the CLI if it changes. */
45  public watchFile = memoize(this.startWatchingFile.bind(this));
46
47  private startWatchingFile(filePath: string): string {
48    const configName = path.relative(this.projectRoot, filePath);
49    watchFile(filePath, (cur: any, prev: any) => {
50      if (prev.size || cur.size) {
51        Log.log(
52          `\u203A Detected a change in ${chalk.bold(
53            configName
54          )}. Restart the server to see the new results.` + (this.settings.additionalWarning || '')
55        );
56      }
57    });
58    return filePath;
59  }
60}
61