1const createExpoWebpackConfigAsync = require('@expo/webpack-config');
2const debug = require('debug')('workspaces');
3const findYarnWorkspaceRoot = require('find-yarn-workspace-root');
4const globModule = require('glob');
5const path = require('path');
6const util = require('util');
7
8const glob = util.promisify(globModule);
9const getSymlinkedNodeModulesForDirectory = require('./common/get-symlinked-modules');
10
11/**
12 * Returns a webpack configuration object that:
13 *
14 *    * transpiles symlinked workspace packages
15 *    * watches for file changes in symlinked packages
16 *    * allows for additional custom packages to be transpiled via env.babel argument
17 */
18exports.createWebpackConfigAsync = async function createWebpackConfigAsync(env, argv) {
19  const workspacePackagesToTranspile = [];
20  const workspaceRootPath = findYarnWorkspaceRoot(env.projectRoot);
21
22  if (workspaceRootPath) {
23    debug(`Found Yarn workspace root at %s`, workspaceRootPath);
24
25    const symlinkedModules = getSymlinkedNodeModulesForDirectory(workspaceRootPath);
26    const symlinkedModulePaths = Object.values(symlinkedModules);
27    const workspacePackage = require(path.resolve(workspaceRootPath, 'package.json'));
28
29    // discover workspace package directories via glob - source yarn:
30    // https://github.com/yarnpkg/yarn/blob/a4708b29ac74df97bac45365cba4f1d62537ceb7/src/config.js#L812-L826
31    const patterns = workspacePackage.workspaces?.packages ?? workspacePackage.workspaces ?? [];
32    const registryFilenames = 'package.json';
33    const trailingPattern = `/+(${registryFilenames})`;
34
35    const files = await Promise.all(
36      patterns.map((pattern) =>
37        glob(pattern.replace(/\/?$/, trailingPattern), {
38          cwd: workspaceRootPath,
39          ignore: `/node_modules/**/+(${registryFilenames})`,
40        })
41      )
42    );
43
44    for (const file of new Set(files.flat())) {
45      const packageDirectory = path.join(workspaceRootPath, path.dirname(file));
46      if (symlinkedModulePaths.includes(packageDirectory)) {
47        workspacePackagesToTranspile.push(packageDirectory);
48      }
49    }
50  } else {
51    debug(`Could not find Yarn workspace root`);
52  }
53
54  env.babel = env.babel ?? {};
55
56  const config = await createExpoWebpackConfigAsync(
57    {
58      ...env,
59      babel: {
60        dangerouslyAddModulePathsToTranspile: [
61          ...workspacePackagesToTranspile,
62          ...(env.babel.dangerouslyAddModulePathsToTranspile ?? []),
63        ],
64      },
65    },
66    argv
67  );
68
69  // use symlink resolution so that webpack watches file changes
70  config.resolve.symlinks = true;
71
72  return config;
73};
74