diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e6e824275..d8a14edc8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -91,10 +91,10 @@ npm config set registry https://registry.npmjs.org/ ## Running `start` command -In order for symlinks to work correctly when running `start` locally, set REACT_NATIVE_APP_ROOT as the root folder of your cli project: +In order for linked dependencies to work correctly when running `start` locally, set `--watchFolders` with a path to the root folder of the CLI project: ``` -REACT_NATIVE_APP_ROOT=path/to/cli node path/to/cli/packages/cli/build/bin.js start +node path/to/cli/packages/cli/build/bin.js start --watchFolders path/to/cli ``` ## Running CLI with React Native from the source diff --git a/packages/cli/src/tools/__tests__/findSymlinkedModules-test.js b/packages/cli/src/tools/__tests__/findSymlinkedModules-test.js deleted file mode 100644 index f283917d3..000000000 --- a/packages/cli/src/tools/__tests__/findSymlinkedModules-test.js +++ /dev/null @@ -1,354 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @emails oncall+javascript_foundation - */ - -import findSymlinkedModules from '../findSymlinkedModules'; - -jest.mock('path'); -jest.mock('fs'); - -const fs = require('fs'); - -describe('findSymlinksForProjectRoot', () => { - it('correctly finds normal module symlinks', () => { - fs.__setMockFilesystem({ - root: { - projectA: { - 'package.json': JSON.stringify({ - name: 'projectA', - main: 'main.js', - }), - node_modules: { - depFoo: { - 'package.json': JSON.stringify({ - name: 'depFoo', - main: 'main.js', - }), - }, - projectB: { - SYMLINK: '/root/projectB', - }, - }, - }, - projectB: { - 'package.json': JSON.stringify({ - name: 'projectB', - main: 'main.js', - }), - node_modules: { - depBar: { - 'package.json': JSON.stringify({ - name: 'depBar', - main: 'main.js', - }), - }, - }, - }, - }, - }); - - const symlinkedModules = findSymlinkedModules('/root/projectA', []); - expect(symlinkedModules).toEqual(['/root/projectB']); - }); - - it('correctly finds scoped module symlinks', () => { - fs.__setMockFilesystem({ - root: { - projectA: { - 'package.json': JSON.stringify({ - name: 'projectA', - main: 'main.js', - }), - node_modules: { - depFoo: { - 'package.json': JSON.stringify({ - name: 'depFoo', - main: 'main.js', - }), - }, - '@scoped': { - projectC: { - SYMLINK: '/root/@scoped/projectC', - }, - }, - projectB: { - SYMLINK: '/root/projectB', - }, - }, - }, - projectB: { - 'package.json': JSON.stringify({ - name: 'projectB', - main: 'main.js', - }), - node_modules: { - depBar: { - 'package.json': JSON.stringify({ - name: 'depBar', - main: 'main.js', - }), - }, - }, - }, - '@scoped': { - projectC: { - 'package.json': JSON.stringify({ - name: '@scoped/projectC', - main: 'main.js', - }), - }, - }, - }, - }); - - const symlinkedModules = findSymlinkedModules('/root/projectA', []); - expect(symlinkedModules).toEqual([ - '/root/@scoped/projectC', - '/root/projectB', - ]); - }); - - it('correctly finds module symlinks within other module symlinks', () => { - fs.__setMockFilesystem({ - root: { - projectA: { - 'package.json': JSON.stringify({ - name: 'projectA', - main: 'main.js', - }), - node_modules: { - depFoo: { - 'package.json': JSON.stringify({ - name: 'depFoo', - main: 'main.js', - }), - }, - '@scoped': { - projectC: { - SYMLINK: '/root/@scoped/projectC', - }, - }, - projectB: { - SYMLINK: '/root/projectB', - }, - }, - }, - projectB: { - 'package.json': JSON.stringify({ - name: 'projectB', - main: 'main.js', - }), - node_modules: { - depBar: { - 'package.json': JSON.stringify({ - name: 'depBar', - main: 'main.js', - }), - }, - projectD: { - SYMLINK: '/root/projectD', - }, - }, - }, - '@scoped': { - projectC: { - 'package.json': JSON.stringify({ - name: '@scoped/projectC', - main: 'main.js', - }), - }, - }, - projectD: { - 'package.json': JSON.stringify({ - name: 'projectD', - main: 'main.js', - }), - }, - }, - }); - - const symlinkedModules = findSymlinkedModules('/root/projectA', []); - expect(symlinkedModules).toEqual([ - '/root/@scoped/projectC', - '/root/projectB', - '/root/projectD', - ]); - }); - - it('correctly handles duplicate symlink paths', () => { - // projectA -> - // -> projectC - // -> projectB -> projectC - // Final list should only contain projectC once - fs.__setMockFilesystem({ - root: { - projectA: { - 'package.json': JSON.stringify({ - name: 'projectA', - main: 'main.js', - }), - node_modules: { - depFoo: { - 'package.json': JSON.stringify({ - name: 'depFoo', - main: 'main.js', - }), - }, - '@scoped': { - projectC: { - SYMLINK: '/root/@scoped/projectC', - }, - }, - projectB: { - SYMLINK: '/root/projectB', - }, - }, - }, - projectB: { - 'package.json': JSON.stringify({ - name: 'projectB', - main: 'main.js', - }), - node_modules: { - depBar: { - 'package.json': JSON.stringify({ - name: 'depBar', - main: 'main.js', - }), - }, - '@scoped': { - projectC: { - SYMLINK: '/root/@scoped/projectC', - }, - }, - }, - }, - '@scoped': { - projectC: { - 'package.json': JSON.stringify({ - name: '@scoped/projectC', - main: 'main.js', - }), - }, - }, - }, - }); - - const symlinkedModules = findSymlinkedModules('/root/projectA', []); - expect(symlinkedModules).toEqual([ - '/root/@scoped/projectC', - '/root/projectB', - ]); - }); - - it('correctly handles symlink recursion', () => { - // projectA -> - // -> projectC -> projectD -> projectA - // -> projectB -> projectC -> projectA - // -> projectD -> projectC -> projectA - // Should not infinite loop, should not contain projectA - fs.__setMockFilesystem({ - root: { - projectA: { - 'package.json': JSON.stringify({ - name: 'projectA', - main: 'main.js', - }), - node_modules: { - depFoo: { - 'package.json': JSON.stringify({ - name: 'depFoo', - main: 'main.js', - }), - }, - '@scoped': { - projectC: { - SYMLINK: '/root/@scoped/projectC', - }, - }, - projectB: { - SYMLINK: '/root/projectB', - }, - }, - }, - projectB: { - 'package.json': JSON.stringify({ - name: 'projectB', - main: 'main.js', - }), - node_modules: { - depBar: { - 'package.json': JSON.stringify({ - name: 'depBar', - main: 'main.js', - }), - }, - projectD: { - SYMLINK: '/root/projectD', - }, - '@scoped': { - projectC: { - SYMLINK: '/root/@scoped/projectC', - }, - }, - }, - }, - '@scoped': { - projectC: { - 'package.json': JSON.stringify({ - name: '@scoped/projectC', - main: 'main.js', - }), - node_modules: { - projectA: { - SYMLINK: '/root/projectA', - }, - projectD: { - SYMLINK: '/root/projectD', - }, - projectE: { - SYMLINK: '/root/projectE', - }, - }, - }, - }, - projectD: { - 'package.json': JSON.stringify({ - name: 'projectD', - main: 'main.js', - }), - node_modules: { - '@scoped': { - projectC: { - SYMLINK: '/root/@scoped/projectC', - }, - }, - projectE: { - SYMLINK: '/root/projectE', - }, - }, - }, - projectE: { - 'package.json': JSON.stringify({ - name: 'projectD', - main: 'main.js', - }), - }, - }, - }); - - const symlinkedModules = findSymlinkedModules('/root/projectA'); - expect(symlinkedModules).toEqual([ - '/root/@scoped/projectC', - '/root/projectB', - '/root/projectD', - '/root/projectE', - ]); - }); -}); diff --git a/packages/cli/src/tools/findSymlinkedModules.ts b/packages/cli/src/tools/findSymlinkedModules.ts deleted file mode 100644 index 0e7befe6c..000000000 --- a/packages/cli/src/tools/findSymlinkedModules.ts +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Copyright (c) Facebook, Inc. and its affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import path from 'path'; -import fs from 'fs'; - -/** - * Find symlinked modules inside "node_modules." - * - * Naively, we could just perform a depth-first search of all folders in - * node_modules, recursing when we find a symlink. - * - * We can be smarter than this due to our knowledge of how npm/Yarn lays out - * "node_modules" / how tools that build on top of npm/Yarn (such as Lerna) - * install dependencies. - * - * Starting from a given root node_modules folder, this algorithm will look at - * both the top level descendants of the node_modules folder or second level - * descendants of folders that start with "@" (which indicates a scoped - * package). If any of those folders is a symlink, it will recurse into the - * link, and perform the same search in the linked folder. - * - * The end result should be a list of all resolved module symlinks for a given - * root. - */ -export default function findSymlinkedModules( - projectRoot: string, - ignoredRoots: Array = [], -) { - const nodeModuleRoot = path.join(projectRoot, 'node_modules'); - const resolvedSymlinks = findModuleSymlinks(nodeModuleRoot, [ - ...ignoredRoots, - projectRoot, - ]); - return resolvedSymlinks; -} - -function findModuleSymlinks( - modulesPath: string, - ignoredPaths: Array = [], -): Array { - if (!fs.existsSync(modulesPath)) { - return []; - } - - // Find module symlinks - const moduleFolders = fs.readdirSync(modulesPath); - const symlinks = moduleFolders.reduce( - (links, folderName) => { - const folderPath = path.join(modulesPath, folderName); - const maybeSymlinkPaths = []; - if (folderName.startsWith('@')) { - const scopedModuleFolders = fs.readdirSync(folderPath); - maybeSymlinkPaths.push( - ...scopedModuleFolders.map(name => path.join(folderPath, name)), - ); - } else { - maybeSymlinkPaths.push(folderPath); - } - return links.concat(resolveSymlinkPaths(maybeSymlinkPaths, ignoredPaths)); - }, - [] as string[], - ); - - // For any symlinks found, look in _that_ modules node_modules directory - // and find any symlinked modules - const nestedSymlinks = symlinks.reduce( - (links, symlinkPath) => - links.concat( - // We ignore any found symlinks or anything from the ignored list, - // to prevent infinite recursion - findModuleSymlinks(path.join(symlinkPath, 'node_modules'), [ - ...ignoredPaths, - ...symlinks, - ]), - ), - [] as string[], - ); - - return [...new Set([...symlinks, ...nestedSymlinks])]; -} - -function resolveSymlinkPaths( - maybeSymlinkPaths: string[], - ignoredPaths: string[], -) { - return maybeSymlinkPaths.reduce( - (links, maybeSymlinkPath) => { - if (fs.lstatSync(maybeSymlinkPath).isSymbolicLink()) { - const resolved = path.resolve( - path.dirname(maybeSymlinkPath), - fs.readlinkSync(maybeSymlinkPath), - ); - if (ignoredPaths.indexOf(resolved) === -1 && fs.existsSync(resolved)) { - links.push(resolved); - } - } - return links; - }, - [] as string[], - ); -} diff --git a/packages/cli/src/tools/loadMetroConfig.ts b/packages/cli/src/tools/loadMetroConfig.ts index 7bc29d7a5..b1f463887 100644 --- a/packages/cli/src/tools/loadMetroConfig.ts +++ b/packages/cli/src/tools/loadMetroConfig.ts @@ -5,19 +5,6 @@ import path from 'path'; // @ts-ignore - no typed definition for the package import {loadConfig} from 'metro-config'; import {Config} from '@react-native-community/cli-types'; -import findSymlinkedModules from './findSymlinkedModules'; - -function resolveSymlinksForRoots(roots: string[]): string[] { - return roots.reduce( - (arr, rootPath) => arr.concat(findSymlinkedModules(rootPath, roots)), - [...roots], - ); -} - -function getWatchFolders(): string[] { - const root = process.env.REACT_NATIVE_APP_ROOT; - return root ? resolveSymlinksForRoots([path.resolve(root)]) : []; -} const INTERNAL_CALLSITES_REGEX = new RegExp( [ @@ -100,7 +87,7 @@ export const getDefaultConfig = (ctx: Config): MetroConfig => { 'Libraries/Image/AssetRegistry', ), }, - watchFolders: getWatchFolders(), + watchFolders: [], }; };