1#!/usr/bin/env node 2'use strict'; 3 4const debug = require('debug')('workspaces'); 5const fs = require('fs'); 6const mkdirp = require('mkdirp'); 7const path = require('path'); 8 9const generatedComment = '// @generated by expo-yarn-workspaces\n'; 10 11/** 12 * Makes a module for the entry point of an Expo project based on the entry point specified by 13 * package.json, perhaps implicitly. 14 */ 15function makeEntryModule(projectPath) { 16 debug(`Checking the main module for the project at %s`, projectPath); 17 18 const packageJson = readPackageJson(projectPath); 19 if (!packageJson) { 20 debug(`The project doesn't have a package.json file; will not make a main module`); 21 return; 22 } 23 24 const mainModule = packageJson.main || 'index.js'; 25 debug(`Using %s as the main module of the project`, mainModule); 26 // For simplicity we don't handle directory module names that assume an "index.js" filename 27 if (!mainModule.endsWith('.js')) { 28 console.error(`The specified main module must be a file path ending with .js`); 29 return; 30 } 31 32 // Look for the main module to see if it already exists. For simplicity we don't consider 33 // platform-specific modules. 34 let mainModuleCode = null; 35 const mainModulePath = path.join(projectPath, mainModule); 36 if (fs.existsSync(mainModulePath)) { 37 mainModuleCode = fs.readFileSync(mainModulePath, 'utf8'); 38 if (!mainModuleCode.includes('@generated')) { 39 debug(`The main module at %s already exists; will not make a main module`, mainModule); 40 if (isSymlinkedPath(mainModulePath)) { 41 console.warn( 42 `The main module at %s is symlinked and may not be accessible to Metro`, 43 mainModulePath 44 ); 45 } 46 return; 47 } 48 } 49 50 // Generate the main module 51 const mainModuleDirectory = path.dirname(mainModulePath); 52 debug(`Ensuring that the directory for the main module exists: %s`, mainModuleDirectory); 53 mkdirp.sync(mainModuleDirectory); 54 if (isSymlinkedPath(mainModuleDirectory)) { 55 console.warn( 56 `The main module at %s is symlinked and may not be accessible to Metro`, 57 mainModulePath 58 ); 59 } 60 61 debug(`Generating main module code from template`); 62 const template = fs.readFileSync(path.join(__dirname, 'AppEntry.template.js'), 'utf8'); 63 // We use the physical path of the main module to compute the relative path to the project since 64 // Node's module system resolves symlinks before loading modules 65 let relativeProjectPath = path.relative(fs.realpathSync(mainModuleDirectory), projectPath); 66 if (relativeProjectPath.split(path.sep)[0] !== '..') { 67 relativeProjectPath = path.join('.', relativeProjectPath); 68 } 69 70 const code = 71 generatedComment + '\n' + template.replace('{{relativeProjectPath}}', relativeProjectPath); 72 if (code !== mainModuleCode) { 73 fs.writeFileSync(mainModulePath, code); 74 } 75 debug(`Wrote generated main module to %s`, mainModulePath); 76} 77 78function readPackageJson(projectPath) { 79 let json; 80 try { 81 json = fs.readFileSync(path.join(projectPath, 'package.json'), 'utf8'); 82 } catch (e) { 83 if (e.code === 'ENOENT') { 84 return null; 85 } 86 throw e; 87 } 88 89 return JSON.parse(json); 90} 91 92function isSymlinkedPath(filePath) { 93 return fs.realpathSync(filePath) !== path.resolve(filePath); 94} 95 96if (module === require.main) { 97 makeEntryModule(path.resolve()); 98} 99