diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 80c9821aea..bc2874299a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,7 @@ And then: ```sh cd /my/new/react-native/project/ -yarn link "@react-native-community/cli-platform-ios" "@react-native-community/cli-platform-android" "@react-native-community/cli" "@react-native-community/cli-server-api" "@react-native-community/cli-types" "@react-native-community/cli-tools" "@react-native-community/cli-debugger-ui" "@react-native-community/cli-hermes" "@react-native-community/cli-plugin-metro" "@react-native-community/cli-clean" "@react-native-community/cli-doctor" "@react-native-community/cli-config" +yarn link "@react-native-community/cli-platform-ios" "@react-native-community/cli-platform-android" "@react-native-community/cli" "@react-native-community/cli-server-api" "@react-native-community/cli-types" "@react-native-community/cli-tools" "@react-native-community/cli-debugger-ui" "@react-native-community/cli-hermes" "@react-native-community/cli-clean" "@react-native-community/cli-doctor" "@react-native-community/cli-config" ``` Once you're done with testing and you'd like to get back to regular setup, run `yarn unlink` instead of `yarn link` from above command. Then `yarn install --force`. diff --git a/docs/commands.md b/docs/commands.md index 4c96cc5bdd..e1b3b7dd2a 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -2,7 +2,7 @@ React Native CLI comes with following commands: -- [`bundle`](/packages/cli-plugin-metro/README.md#bundle) +- [`bundle`](https://github.com/facebook/react-native/tree/main/packages/community-cli-plugin#bundle) - [`clean`](/packages/cli-clean/README.md#clean) - [`config`](/packages/cli-config/README.md#config) - [`doctor`](/packages/cli-doctor/README.md#doctor) @@ -10,12 +10,12 @@ React Native CLI comes with following commands: - [`info`](/packages/cli-doctor/README.md#info) - [`log-android`](/packages/cli-platform-android/README.md#log-android) - [`log-ios`](/packages/cli-platform-ios/README.md#log-ios) -- [`ram-bundle`](/packages/cli-plugin-metro/README.md#ram-bundle) +- [`ram-bundle`](https://github.com/facebook/react-native/tree/main/packages/community-cli-plugin#ram-bundle) - [`run-android`](/packages/cli-platform-android/README.md#run-android) - [`build-android`](/packages/cli-platform-android/README.md#build-android) - [`run-ios`](/packages/cli-platform-ios/README.md#run-ios) - [`build-ios`](/packages/cli-platform-ios/README.md#build-ios) -- [`start`](/packages/cli-plugin-metro/README.md#start) +- [`start`](https://github.com/facebook/react-native/tree/main/packages/community-cli-plugin#start) - [`upgrade`](#upgrade) - [`profile-hermes`](/packages/cli-hermes/README.md#profile-hermes) diff --git a/packages/cli-doctor/tsconfig.json b/packages/cli-doctor/tsconfig.json index af61926b1d..89c5c48c20 100644 --- a/packages/cli-doctor/tsconfig.json +++ b/packages/cli-doctor/tsconfig.json @@ -10,6 +10,5 @@ {"path": "../cli-config"}, {"path": "../cli-platform-android"}, {"path": "../cli-platform-ios"}, - {"path": "../cli-plugin-metro"}, ] } diff --git a/packages/cli-platform-android/tsconfig.json b/packages/cli-platform-android/tsconfig.json index 3a2107b009..3552922dbe 100644 --- a/packages/cli-platform-android/tsconfig.json +++ b/packages/cli-platform-android/tsconfig.json @@ -7,6 +7,5 @@ "references": [ {"path": "../cli-tools"}, {"path": "../cli-types"}, - {"path": "../cli-plugin-metro"} ] } diff --git a/packages/cli-plugin-metro/CHANGELOG.md b/packages/cli-plugin-metro/CHANGELOG.md deleted file mode 100644 index e70a3ea3bc..0000000000 --- a/packages/cli-plugin-metro/CHANGELOG.md +++ /dev/null @@ -1,24 +0,0 @@ -# Change Log - -All notable changes to this project will be documented in this file. -See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. - -# 8.0.0-alpha.3 (2022-04-22) - -**Note:** Version bump only for package @react-native-community/cli-plugin-metro - - - - - -# 8.0.0-alpha.2 (2022-04-22) - -**Note:** Version bump only for package @react-native-community/cli-plugin-metro - - - - - -# 8.0.0-alpha.1 (2022-04-22) - -**Note:** Version bump only for package @react-native-community/cli-plugin-metro diff --git a/packages/cli-plugin-metro/README.md b/packages/cli-plugin-metro/README.md index 437c83749f..2110c037a1 100644 --- a/packages/cli-plugin-metro/README.md +++ b/packages/cli-plugin-metro/README.md @@ -1,206 +1,5 @@ # @react-native-community/cli-plugin-metro -This package is part of the [React Native CLI](../../README.md). -It contains commands for managing the Metro bundler. - -## Installation - -```sh -yarn add @react-native-community/cli-plugin-metro -``` - -## Commands - -### `start` - -Usage: - -```sh -react-native start [option] -``` - -Starts the server that communicates with connected devices - -#### Options - -#### `--port ` - -Specify port to listen on - -#### `--projectRoot ` - -Path to a custom project root - -#### `--watchFolders ` - -Specify any additional folders to be added to the watch list - -#### `--assetPlugins ` - -Specify any additional asset plugins to be used by the packager by full filepath - -#### `--sourceExts ` - -Specify any additional source extensions to be used by the packager - -#### `--max-workers ` - -Specifies the maximum number of workers the worker-pool will spawn for transforming files. This defaults to the number of the cores available on your machine - -#### `--transformer ` - -Specify a custom transformer to be used - -#### `--reset-cache, --resetCache` - -Removes cached files - -#### `--custom-log-reporter-path, --customLogReporterPath ` - -Path to a JavaScript file that exports a log reporter as a replacement for TerminalReporter - -#### `--https` - -Enables https connections to the server - -#### `--key ` - -Path to custom SSL key - -#### `--cert ` - -Path to custom SSL cert - -#### `--config ` - -Path to the CLI configuration file - -#### `--no-interactive` - -Disables interactive mode - -### `bundle` - -Usage: - -```sh -react-native bundle -``` - -Builds the JavaScript bundle for offline use. - -#### `--entry-file ` - -Path to the root JS file, either absolute or relative to JS root. - -#### `--platform ` - -> default: ios - -Either "ios" or "android". - -#### `--transformer ` - -Specify a custom transformer to be used. - -#### `--dev [boolean]` - -> default: true - -If false, warnings are disabled and the bundle is minified. - -#### `--minify [boolean]` - -Allows overriding whether bundle is minified. This defaults to false if dev is true, and true if dev is false. Disabling minification can be useful for speeding up production builds for testing purposes. - -#### `--bundle-output ` - -File name where to store the resulting bundle, ex. `/tmp/groups.bundle`. - -If you are planning on building a debug APK, that will run without the packager, by invoking `./gradlew assembleDebug` you can simply set `bundleInDebug: true` in your app/build.gradle file, inside the `project.ext.react` map. - -
-Alternatively if you want to run react-native bundle manually and then create the APK with ./gradlew assembleDebug you have to make sure to put the bundle into the right directory and give it the right name, so that gradle can find it. - -For react-native versions 0.57 and above the bundle output path should be: -android/app/build/generated/assets/react/debug/index.android.js - -To find out the correct path for previous react-native versions, take a look at the react.gradle file here: or inside your node_modules/react-native directory. - -The expected path for the js bundle can be found on the line that starts with jsBundleDir = . - -
- -#### `--bundle-encoding ` - -> default: utf8 - -Encoding the bundle should be written in (). - -#### `--max-workers ` - -Specifies the maximum number of workers the worker-pool will spawn for transforming files. This defaults to the number of the cores available on your machine. - -#### `--sourcemap-output ` - -File name where to store the sourcemap file for resulting bundle, ex. `/tmp/groups.map`. - -#### `--sourcemap-sources-root ` - -Path to make sourcemap sources entries relative to, ex. `/root/dir`. - -#### `--sourcemap-use-absolute-path` - -> default: false - -Report SourceMapURL using its full path. - -#### `--assets-dest ` - -Directory name where to store assets referenced in the bundle. - -If you are planning on building a debug APK that will run without the packager, see ([--bundle-output](https://github.com/react-native-community/cli/blob/main/packages/cli-plugin-metro/README.md#--bundle-output-string)) - -
- Alternatively if you want to run react-native bundle manually and then create the APK with ./gradlew assembleDebug you have to make sure to put the assets into the right directory, so that gradle can find them. - -For react-native versions 0.57 and above the --assets-dest path should be: -android/app/build/generated/res/react/debug - -The expected path for the assets can be found in the react.gradle file on the line that starts with resourcesDir = - -
- -#### `--reset-cache` - -> default: false - -Removes cached files. - -#### `--read-global-cache` - -> default: false - -Try to fetch transformed JS code from the global cache, if configured. - -#### `--config ` - -Path to the CLI configuration file. - -### `ram-bundle` - -Usage: - -```sh -react-native ram-bundle [options] -``` - -Builds JavaScript as a "Random Access Module" bundle for offline use. - -#### Options - -Accepts all of [bundle commands](#bundle) and following: - -#### `--indexed-ram-bundle` - -Force the "Indexed RAM" bundle file format, even when building for Android. +> **[Removed]** +> +> This package is now relocated as [`@react-native/community-cli-plugin`](https://github.com/facebook/react-native/tree/main/packages/community-cli-plugin). diff --git a/packages/cli-plugin-metro/package.json b/packages/cli-plugin-metro/package.json index 06ac616396..6918e3f52a 100644 --- a/packages/cli-plugin-metro/package.json +++ b/packages/cli-plugin-metro/package.json @@ -1,29 +1,12 @@ { "name": "@react-native-community/cli-plugin-metro", + "description": "[Removed since 12.0.0-alpha.10]", "version": "12.0.0-alpha.9", "license": "MIT", - "main": "build/index.js", "publishConfig": { "access": "public" }, - "dependencies": { - "@react-native-community/cli-server-api": "12.0.0-alpha.9", - "@react-native-community/cli-tools": "12.0.0-alpha.9", - "chalk": "^4.1.2", - "execa": "^5.0.0", - "metro": "0.78.0", - "metro-config": "0.78.0", - "metro-core": "0.78.0", - "readline": "^1.3.0" - }, - "devDependencies": { - "@react-native-community/cli-types": "12.0.0-alpha.9", - "metro-resolver": "0.78.0" - }, - "files": [ - "build", - "!*.map" - ], + "dependencies": {}, "homepage": "https://github.com/react-native-community/cli/tree/main/packages/cli-plugin-metro", "repository": { "type": "git", diff --git a/packages/cli-plugin-metro/src/commands/bundle/__mocks__/sign.js b/packages/cli-plugin-metro/src/commands/bundle/__mocks__/sign.js deleted file mode 100644 index 0b8c3a132a..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/__mocks__/sign.js +++ /dev/null @@ -1,14 +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 - */ - -function sign(source) { - return source; -} - -module.exports = sign; diff --git a/packages/cli-plugin-metro/src/commands/bundle/__tests__/filterPlatformAssetScales-test.ts b/packages/cli-plugin-metro/src/commands/bundle/__tests__/filterPlatformAssetScales-test.ts deleted file mode 100644 index 94364e6dcf..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/__tests__/filterPlatformAssetScales-test.ts +++ /dev/null @@ -1,40 +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 filterPlatformAssetScales from '../filterPlatformAssetScales'; - -jest.dontMock('../filterPlatformAssetScales').dontMock('../assetPathUtils'); - -describe('filterPlatformAssetScales', () => { - it('removes everything but 2x and 3x for iOS', () => { - expect(filterPlatformAssetScales('ios', [1, 1.5, 2, 3, 4])).toEqual([ - 1, - 2, - 3, - ]); - expect(filterPlatformAssetScales('ios', [3, 4])).toEqual([3]); - }); - - it('keeps closest largest one if nothing matches', () => { - expect(filterPlatformAssetScales('ios', [0.5, 4, 100])).toEqual([4]); - expect(filterPlatformAssetScales('ios', [0.5, 100])).toEqual([100]); - expect(filterPlatformAssetScales('ios', [0.5])).toEqual([0.5]); - expect(filterPlatformAssetScales('ios', [])).toEqual([]); - }); - - it('keeps all scales for unknown platform', () => { - expect(filterPlatformAssetScales('freebsd', [1, 1.5, 2, 3.7])).toEqual([ - 1, - 1.5, - 2, - 3.7, - ]); - }); -}); diff --git a/packages/cli-plugin-metro/src/commands/bundle/__tests__/getAssetDestPathAndroid-test.ts b/packages/cli-plugin-metro/src/commands/bundle/__tests__/getAssetDestPathAndroid-test.ts deleted file mode 100644 index 1fbe2329e2..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/__tests__/getAssetDestPathAndroid-test.ts +++ /dev/null @@ -1,85 +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 getAssetDestPathAndroid from '../getAssetDestPathAndroid'; - -jest.dontMock('../getAssetDestPathAndroid').dontMock('../assetPathUtils'); - -const path = require('path'); - -describe('getAssetDestPathAndroid', () => { - it('should use the right destination folder', () => { - const asset = { - name: 'icon', - type: 'png', - httpServerLocation: '/assets/test', - }; - - const expectDestPathForScaleToStartWith = (scale, location) => { - if (!getAssetDestPathAndroid(asset, scale).startsWith(location)) { - throw new Error( - `asset for scale ${scale} should start with path '${location}'`, - ); - } - }; - - expectDestPathForScaleToStartWith(1, 'drawable-mdpi'); - expectDestPathForScaleToStartWith(1.5, 'drawable-hdpi'); - expectDestPathForScaleToStartWith(2, 'drawable-xhdpi'); - expectDestPathForScaleToStartWith(3, 'drawable-xxhdpi'); - expectDestPathForScaleToStartWith(4, 'drawable-xxxhdpi'); - }); - - it('should lowercase path', () => { - const asset = { - name: 'Icon', - type: 'png', - httpServerLocation: '/assets/App/Test', - }; - - expect(getAssetDestPathAndroid(asset, 1)).toBe( - path.normalize('drawable-mdpi/app_test_icon.png'), - ); - }); - - it('should remove `assets/` prefix', () => { - const asset = { - name: 'icon', - type: 'png', - httpServerLocation: '/assets/RKJSModules/Apps/AndroidSample/Assets', - }; - - expect(getAssetDestPathAndroid(asset, 1).startsWith('assets_')).toBeFalsy(); - }); - - it('should put non-drawable resources to `raw/`', () => { - const asset = { - name: 'video', - type: 'mp4', - httpServerLocation: '/assets/app/test', - }; - - expect(getAssetDestPathAndroid(asset, 1)).toBe( - path.normalize('raw/app_test_video.mp4'), - ); - }); - - it('should handle assets with a relative path outside of root', () => { - const asset = { - name: 'icon', - type: 'png', - httpServerLocation: '/assets/../../test', - }; - - expect(getAssetDestPathAndroid(asset, 1)).toBe( - path.normalize('drawable-mdpi/__test_icon.png'), - ); - }); -}); diff --git a/packages/cli-plugin-metro/src/commands/bundle/__tests__/getAssetDestPathIOS-test.ts b/packages/cli-plugin-metro/src/commands/bundle/__tests__/getAssetDestPathIOS-test.ts deleted file mode 100644 index 502db89064..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/__tests__/getAssetDestPathIOS-test.ts +++ /dev/null @@ -1,56 +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 getAssetDestPathIOS from '../getAssetDestPathIOS'; - -jest.dontMock('../getAssetDestPathIOS'); - -const path = require('path'); - -describe('getAssetDestPathIOS', () => { - it('should build correct path', () => { - const asset = { - name: 'icon', - type: 'png', - httpServerLocation: '/assets/test', - }; - - expect(getAssetDestPathIOS(asset, 1)).toBe( - path.normalize('assets/test/icon.png'), - ); - }); - - it('should consider scale', () => { - const asset = { - name: 'icon', - type: 'png', - httpServerLocation: '/assets/test', - }; - - expect(getAssetDestPathIOS(asset, 2)).toBe( - path.normalize('assets/test/icon@2x.png'), - ); - expect(getAssetDestPathIOS(asset, 3)).toBe( - path.normalize('assets/test/icon@3x.png'), - ); - }); - - it('should handle assets with a relative path outside of root', () => { - const asset = { - name: 'icon', - type: 'png', - httpServerLocation: '/assets/../../test', - }; - - expect(getAssetDestPathIOS(asset, 1)).toBe( - path.normalize('assets/__test/icon.png'), - ); - }); -}); diff --git a/packages/cli-plugin-metro/src/commands/bundle/assetCatalogIOS.ts b/packages/cli-plugin-metro/src/commands/bundle/assetCatalogIOS.ts deleted file mode 100644 index e686ac59d1..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/assetCatalogIOS.ts +++ /dev/null @@ -1,73 +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'; -import type {AssetData} from 'metro'; -import assetPathUtils from './assetPathUtils'; - -export function cleanAssetCatalog(catalogDir: string): void { - const files = fs - .readdirSync(catalogDir) - .filter((file) => file.endsWith('.imageset')); - for (const file of files) { - fs.rmSync(path.join(catalogDir, file)); - } -} - -type ImageSet = { - basePath: string; - files: {name: string; src: string; scale: number}[]; -}; - -export function getImageSet( - catalogDir: string, - asset: AssetData, - scales: readonly number[], -): ImageSet { - const fileName = assetPathUtils.getResourceIdentifier(asset); - return { - basePath: path.join(catalogDir, `${fileName}.imageset`), - files: scales.map((scale, idx) => { - const suffix = scale === 1 ? '' : `@${scale}x`; - return { - name: `${fileName + suffix}.${asset.type}`, - scale, - src: asset.files[idx], - }; - }), - }; -} - -export function isCatalogAsset(asset: AssetData): boolean { - return asset.type === 'png' || asset.type === 'jpg' || asset.type === 'jpeg'; -} - -export function writeImageSet(imageSet: ImageSet): void { - fs.mkdirSync(imageSet.basePath, {recursive: true}); - - for (const file of imageSet.files) { - const dest = path.join(imageSet.basePath, file.name); - fs.copyFileSync(file.src, dest); - } - - fs.writeFileSync( - path.join(imageSet.basePath, 'Contents.json'), - JSON.stringify({ - images: imageSet.files.map((file) => ({ - filename: file.name, - idiom: 'universal', - scale: `${file.scale}x`, - })), - info: { - author: 'xcode', - version: 1, - }, - }), - ); -} diff --git a/packages/cli-plugin-metro/src/commands/bundle/assetPathUtils.ts b/packages/cli-plugin-metro/src/commands/bundle/assetPathUtils.ts deleted file mode 100644 index c26ddd1da5..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/assetPathUtils.ts +++ /dev/null @@ -1,89 +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. - * - */ - -export interface PackagerAsset { - httpServerLocation: string; - name: string; - type: string; -} - -/** - * FIXME: using number to represent discrete scale numbers is fragile in essence because of - * floating point numbers imprecision. - */ -function getAndroidAssetSuffix(scale: number): string { - switch (scale) { - case 0.75: - return 'ldpi'; - case 1: - return 'mdpi'; - case 1.5: - return 'hdpi'; - case 2: - return 'xhdpi'; - case 3: - return 'xxhdpi'; - case 4: - return 'xxxhdpi'; - default: - return ''; - } -} - -// See https://developer.android.com/guide/topics/resources/drawable-resource.html -const drawableFileTypes = new Set([ - 'gif', - 'jpeg', - 'jpg', - 'png', - 'webp', - 'xml', -]); - -function getAndroidResourceFolderName( - asset: PackagerAsset, - scale: number, -): string { - if (!drawableFileTypes.has(asset.type)) { - return 'raw'; - } - const suffix = getAndroidAssetSuffix(scale); - if (!suffix) { - throw new Error( - `Don't know which android drawable suffix to use for asset: ${JSON.stringify( - asset, - )}`, - ); - } - const androidFolder = `drawable-${suffix}`; - return androidFolder; -} - -function getResourceIdentifier(asset: PackagerAsset): string { - const folderPath = getBasePath(asset); - return `${folderPath}/${asset.name}` - .toLowerCase() - .replace(/\//g, '_') // Encode folder structure in file name - .replace(/([^a-z0-9_])/g, '') // Remove illegal chars - .replace(/^assets_/, ''); // Remove "assets_" prefix -} - -function getBasePath(asset: PackagerAsset): string { - let basePath = asset.httpServerLocation; - if (basePath[0] === '/') { - basePath = basePath.substr(1); - } - return basePath; -} - -export default { - getAndroidAssetSuffix, - getAndroidResourceFolderName, - getResourceIdentifier, - getBasePath, -}; diff --git a/packages/cli-plugin-metro/src/commands/bundle/buildBundle.ts b/packages/cli-plugin-metro/src/commands/bundle/buildBundle.ts deleted file mode 100644 index 5e45447443..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/buildBundle.ts +++ /dev/null @@ -1,120 +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 {logger} from '@react-native-community/cli-tools'; -import type {Config} from '@react-native-community/cli-types'; -import chalk from 'chalk'; -import fs from 'fs'; -import type {ConfigT} from 'metro-config'; -import Server from 'metro/src/Server'; -import outputBundle from 'metro/src/shared/output/bundle'; -import type {BundleOptions} from 'metro/src/shared/types'; -import path from 'path'; -import {default as loadMetroConfig} from '../../tools/loadMetroConfig'; -import {CommandLineArgs} from './bundleCommandLineArgs'; -import saveAssets from './saveAssets'; - -interface RequestOptions { - entryFile: string; - sourceMapUrl: string | undefined; - dev: boolean; - minify: boolean; - platform: string; - unstable_transformProfile: BundleOptions['unstable_transformProfile']; -} - -async function buildBundle( - args: CommandLineArgs, - ctx: Config, - output: typeof outputBundle = outputBundle, -) { - const config = await loadMetroConfig(ctx, { - maxWorkers: args.maxWorkers, - resetCache: args.resetCache, - config: args.config, - }); - - return buildBundleWithConfig(args, config, output); -} - -/** - * Create a bundle using a pre-loaded Metro config. The config can be - * re-used for several bundling calls if multiple platforms are being - * bundled. - */ -export async function buildBundleWithConfig( - args: CommandLineArgs, - config: ConfigT, - output: typeof outputBundle = outputBundle, -) { - if (config.resolver.platforms.indexOf(args.platform) === -1) { - logger.error( - `Invalid platform ${ - args.platform ? `"${chalk.bold(args.platform)}" ` : '' - }selected.`, - ); - - logger.info( - `Available platforms are: ${config.resolver.platforms - .map((x) => `"${chalk.bold(x)}"`) - .join( - ', ', - )}. If you are trying to bundle for an out-of-tree platform, it may not be installed.`, - ); - - throw new Error('Bundling failed'); - } - - // This is used by a bazillion of npm modules we don't control so we don't - // have other choice than defining it as an env variable here. - process.env.NODE_ENV = args.dev ? 'development' : 'production'; - - let sourceMapUrl = args.sourcemapOutput; - if (sourceMapUrl && !args.sourcemapUseAbsolutePath) { - sourceMapUrl = path.basename(sourceMapUrl); - } - - const requestOpts: RequestOptions = { - entryFile: args.entryFile, - sourceMapUrl, - dev: args.dev, - minify: args.minify !== undefined ? args.minify : !args.dev, - platform: args.platform, - unstable_transformProfile: args.unstableTransformProfile as BundleOptions['unstable_transformProfile'], - }; - const server = new Server(config); - - try { - const bundle = await output.build(server, requestOpts); - - // Ensure destination directory exists before saving the bundle - const mkdirOptions = {recursive: true, mode: 0o755} as const; - fs.mkdirSync(path.dirname(args.bundleOutput), mkdirOptions); - - await output.save(bundle, args, logger.info); - - // Save the assets of the bundle - const outputAssets = await server.getAssets({ - ...Server.DEFAULT_BUNDLE_OPTIONS, - ...requestOpts, - bundleType: 'todo', - }); - - // When we're done saving bundle output and the assets, we're done. - return await saveAssets( - outputAssets, - args.platform, - args.assetsDest, - args.assetCatalogDest, - ); - } finally { - server.end(); - } -} - -export default buildBundle; diff --git a/packages/cli-plugin-metro/src/commands/bundle/bundle.ts b/packages/cli-plugin-metro/src/commands/bundle/bundle.ts deleted file mode 100644 index fff01b95b1..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/bundle.ts +++ /dev/null @@ -1,35 +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 type {Config} from '@react-native-community/cli-types'; -import buildBundle from './buildBundle'; -import bundleCommandLineArgs, {CommandLineArgs} from './bundleCommandLineArgs'; - -/** - * Builds the bundle starting to look for dependencies at the given entry path. - */ -function bundleWithOutput( - _: Array, - config: Config, - args: CommandLineArgs, - output: any, // untyped metro/src/shared/output/bundle or metro/src/shared/output/RamBundle -) { - return buildBundle(args, config, output); -} - -export default { - name: 'bundle', - description: 'builds the javascript bundle for offline use', - func: bundleWithOutput, - options: bundleCommandLineArgs, - // Used by `ramBundle.js` - withOutput: bundleWithOutput, -}; - -const withOutput = bundleWithOutput; - -export {withOutput}; diff --git a/packages/cli-plugin-metro/src/commands/bundle/bundleCommandLineArgs.ts b/packages/cli-plugin-metro/src/commands/bundle/bundleCommandLineArgs.ts deleted file mode 100644 index 7bad012e2d..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/bundleCommandLineArgs.ts +++ /dev/null @@ -1,126 +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'; - -export interface CommandLineArgs { - assetsDest?: string; - assetCatalogDest?: string; - entryFile: string; - resetCache: boolean; - resetGlobalCache: boolean; - transformer?: string; - minify?: boolean; - config?: string; - platform: string; - dev: boolean; - bundleOutput: string; - bundleEncoding?: 'utf8' | 'utf16le' | 'ascii'; - maxWorkers?: number; - sourcemapOutput?: string; - sourcemapSourcesRoot?: string; - sourcemapUseAbsolutePath: boolean; - verbose: boolean; - unstableTransformProfile: string; -} - -export default [ - { - name: '--entry-file ', - description: - 'Path to the root JS file, either absolute or relative to JS root', - }, - { - name: '--platform ', - description: 'Either "ios" or "android"', - default: 'ios', - }, - { - name: '--transformer ', - description: 'Specify a custom transformer to be used', - }, - { - name: '--dev [boolean]', - description: 'If false, warnings are disabled and the bundle is minified', - parse: (val: string) => val !== 'false', - default: true, - }, - { - name: '--minify [boolean]', - description: - 'Allows overriding whether bundle is minified. This defaults to ' + - 'false if dev is true, and true if dev is false. Disabling minification ' + - 'can be useful for speeding up production builds for testing purposes.', - parse: (val: string) => val !== 'false', - }, - { - name: '--bundle-output ', - description: - 'File name where to store the resulting bundle, ex. /tmp/groups.bundle', - }, - { - name: '--bundle-encoding ', - description: - 'Encoding the bundle should be written in (https://nodejs.org/api/buffer.html#buffer_buffer).', - default: 'utf8', - }, - { - name: '--max-workers ', - description: - 'Specifies the maximum number of workers the worker-pool ' + - 'will spawn for transforming files. This defaults to the number of the ' + - 'cores available on your machine.', - parse: (workers: string) => Number(workers), - }, - { - name: '--sourcemap-output ', - description: - 'File name where to store the sourcemap file for resulting bundle, ex. /tmp/groups.map', - }, - { - name: '--sourcemap-sources-root ', - description: - "Path to make sourcemap's sources entries relative to, ex. /root/dir", - }, - { - name: '--sourcemap-use-absolute-path', - description: 'Report SourceMapURL using its full path', - default: false, - }, - { - name: '--assets-dest ', - description: - 'Directory name where to store assets referenced in the bundle', - }, - { - name: '--unstable-transform-profile ', - description: - 'Experimental, transform JS for a specific JS engine. Currently supported: hermes, hermes-canary, default', - default: 'default', - }, - { - name: '--asset-catalog-dest [string]', - description: 'Path where to create an iOS Asset Catalog for images', - }, - { - name: '--reset-cache', - description: 'Removes cached files', - default: false, - }, - { - name: '--read-global-cache', - description: - 'Try to fetch transformed JS code from the global cache, if configured.', - default: false, - }, - { - name: '--config ', - description: 'Path to the CLI configuration file', - parse: (val: string) => path.resolve(val), - }, -]; diff --git a/packages/cli-plugin-metro/src/commands/bundle/filterPlatformAssetScales.ts b/packages/cli-plugin-metro/src/commands/bundle/filterPlatformAssetScales.ts deleted file mode 100644 index a8c48b0a1e..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/filterPlatformAssetScales.ts +++ /dev/null @@ -1,42 +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. - * - */ - -const ALLOWED_SCALES: {[key: string]: number[]} = { - ios: [1, 2, 3], -}; - -function filterPlatformAssetScales( - platform: string, - scales: ReadonlyArray, -): ReadonlyArray { - const whitelist: number[] = ALLOWED_SCALES[platform]; - if (!whitelist) { - return scales; - } - const result = scales.filter((scale) => whitelist.indexOf(scale) > -1); - if (result.length === 0 && scales.length > 0) { - // No matching scale found, but there are some available. Ideally we don't - // want to be in this situation and should throw, but for now as a fallback - // let's just use the closest larger image - const maxScale = whitelist[whitelist.length - 1]; - for (const scale of scales) { - if (scale > maxScale) { - result.push(scale); - break; - } - } - - // There is no larger scales available, use the largest we have - if (result.length === 0) { - result.push(scales[scales.length - 1]); - } - } - return result; -} - -export default filterPlatformAssetScales; diff --git a/packages/cli-plugin-metro/src/commands/bundle/getAssetDestPathAndroid.ts b/packages/cli-plugin-metro/src/commands/bundle/getAssetDestPathAndroid.ts deleted file mode 100644 index 54ce337da9..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/getAssetDestPathAndroid.ts +++ /dev/null @@ -1,21 +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 assetPathUtils, {PackagerAsset} from './assetPathUtils'; - -function getAssetDestPathAndroid(asset: PackagerAsset, scale: number): string { - const androidFolder = assetPathUtils.getAndroidResourceFolderName( - asset, - scale, - ); - const fileName = assetPathUtils.getResourceIdentifier(asset); - return path.join(androidFolder, `${fileName}.${asset.type}`); -} - -export default getAssetDestPathAndroid; diff --git a/packages/cli-plugin-metro/src/commands/bundle/getAssetDestPathIOS.ts b/packages/cli-plugin-metro/src/commands/bundle/getAssetDestPathIOS.ts deleted file mode 100644 index 6544a8ca3c..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/getAssetDestPathIOS.ts +++ /dev/null @@ -1,24 +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 {PackagerAsset} from './assetPathUtils'; - -function getAssetDestPathIOS(asset: PackagerAsset, scale: number): string { - const suffix = scale === 1 ? '' : `@${scale}x`; - const fileName = `${asset.name + suffix}.${asset.type}`; - return path.join( - // Assets can have relative paths outside of the project root. - // Replace `../` with `_` to make sure they don't end up outside of - // the expected assets directory. - asset.httpServerLocation.substr(1).replace(/\.\.\//g, '_'), - fileName, - ); -} - -export default getAssetDestPathIOS; diff --git a/packages/cli-plugin-metro/src/commands/bundle/index.ts b/packages/cli-plugin-metro/src/commands/bundle/index.ts deleted file mode 100644 index 21b8261bd7..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export {default as bundleCommand} from './bundle'; -export {buildBundleWithConfig} from './buildBundle'; -export type {CommandLineArgs} from './bundleCommandLineArgs'; -export {default as ramBundleCommand} from './ramBundle'; diff --git a/packages/cli-plugin-metro/src/commands/bundle/ramBundle.ts b/packages/cli-plugin-metro/src/commands/bundle/ramBundle.ts deleted file mode 100644 index 6eb8d74eae..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/ramBundle.ts +++ /dev/null @@ -1,34 +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. - * - */ -// @ts-ignore - no typed definition for the package -import outputUnbundle from 'metro/src/shared/output/RamBundle'; -import {withOutput as bundleWithOutput} from './bundle'; -import bundleCommandLineArgs, {CommandLineArgs} from './bundleCommandLineArgs'; -import type {Config} from '@react-native-community/cli-types'; - -/** - * Builds the bundle starting to look for dependencies at the given entry path. - */ -function ramBundle(argv: Array, config: Config, args: CommandLineArgs) { - return bundleWithOutput(argv, config, args, outputUnbundle); -} - -export default { - name: 'ram-bundle', - description: - 'builds javascript as a "Random Access Module" bundle for offline use', - func: ramBundle, - options: bundleCommandLineArgs.concat({ - name: '--indexed-ram-bundle', - description: - 'Force the "Indexed RAM" bundle file format, even when building for android', - default: false, - }), -}; - -export {ramBundle}; diff --git a/packages/cli-plugin-metro/src/commands/bundle/saveAssets.ts b/packages/cli-plugin-metro/src/commands/bundle/saveAssets.ts deleted file mode 100644 index 28ed6330cb..0000000000 --- a/packages/cli-plugin-metro/src/commands/bundle/saveAssets.ts +++ /dev/null @@ -1,135 +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 {logger} from '@react-native-community/cli-tools'; -import fs from 'fs'; -import type {AssetData} from 'metro'; -import path from 'path'; -import { - cleanAssetCatalog, - getImageSet, - isCatalogAsset, - writeImageSet, -} from './assetCatalogIOS'; -import filterPlatformAssetScales from './filterPlatformAssetScales'; -import getAssetDestPathAndroid from './getAssetDestPathAndroid'; -import getAssetDestPathIOS from './getAssetDestPathIOS'; - -interface CopiedFiles { - [src: string]: string; -} - -function saveAssets( - assets: ReadonlyArray, - platform: string, - assetsDest: string | undefined, - assetCatalogDest: string | undefined, -) { - if (!assetsDest) { - logger.warn('Assets destination folder is not set, skipping...'); - return; - } - - const filesToCopy: CopiedFiles = Object.create(null); // Map src -> dest - - const getAssetDestPath = - platform === 'android' ? getAssetDestPathAndroid : getAssetDestPathIOS; - - const addAssetToCopy = (asset: AssetData) => { - const validScales = new Set( - filterPlatformAssetScales(platform, asset.scales), - ); - - asset.scales.forEach((scale, idx) => { - if (!validScales.has(scale)) { - return; - } - const src = asset.files[idx]; - const dest = path.join(assetsDest, getAssetDestPath(asset, scale)); - filesToCopy[src] = dest; - }); - }; - - if (platform === 'ios' && assetCatalogDest != null) { - // Use iOS Asset Catalog for images. This will allow Apple app thinning to - // remove unused scales from the optimized bundle. - const catalogDir = path.join(assetCatalogDest, 'RNAssets.xcassets'); - if (!fs.existsSync(catalogDir)) { - logger.error( - `Could not find asset catalog 'RNAssets.xcassets' in ${assetCatalogDest}. Make sure to create it if it does not exist.`, - ); - return; - } - - logger.info('Adding images to asset catalog', catalogDir); - cleanAssetCatalog(catalogDir); - for (const asset of assets) { - if (isCatalogAsset(asset)) { - const imageSet = getImageSet( - catalogDir, - asset, - filterPlatformAssetScales(platform, asset.scales), - ); - writeImageSet(imageSet); - } else { - addAssetToCopy(asset); - } - } - logger.info('Done adding images to asset catalog'); - } else { - assets.forEach(addAssetToCopy); - } - - return copyAll(filesToCopy); -} - -function copyAll(filesToCopy: CopiedFiles) { - const queue = Object.keys(filesToCopy); - if (queue.length === 0) { - return Promise.resolve(); - } - - logger.info(`Copying ${queue.length} asset files`); - return new Promise((resolve, reject) => { - const copyNext = (error?: NodeJS.ErrnoException) => { - if (error) { - reject(error); - return; - } - if (queue.length === 0) { - logger.info('Done copying assets'); - resolve(); - } else { - // queue.length === 0 is checked in previous branch, so this is string - const src = queue.shift() as string; - const dest = filesToCopy[src]; - copy(src, dest, copyNext); - } - }; - copyNext(); - }); -} - -function copy( - src: string, - dest: string, - callback: (error: NodeJS.ErrnoException) => void, -): void { - const destDir = path.dirname(dest); - fs.mkdir(destDir, {recursive: true}, (err?) => { - if (err) { - callback(err); - return; - } - fs.createReadStream(src) - .pipe(fs.createWriteStream(dest)) - .on('finish', callback); - }); -} - -export default saveAssets; diff --git a/packages/cli-plugin-metro/src/commands/index.ts b/packages/cli-plugin-metro/src/commands/index.ts deleted file mode 100644 index 0b9ffd88ff..0000000000 --- a/packages/cli-plugin-metro/src/commands/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import {bundleCommand, ramBundleCommand} from './bundle'; -import startCommand from './start'; - -export default [bundleCommand, ramBundleCommand, startCommand]; -export {buildBundleWithConfig} from './bundle'; -export type {CommandLineArgs} from './bundle'; diff --git a/packages/cli-plugin-metro/src/commands/start/index.ts b/packages/cli-plugin-metro/src/commands/start/index.ts deleted file mode 100644 index accfa96eba..0000000000 --- a/packages/cli-plugin-metro/src/commands/start/index.ts +++ /dev/null @@ -1,92 +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 runServer from './runServer'; - -export default { - name: 'start', - func: runServer, - description: 'starts the webserver', - options: [ - { - name: '--port ', - parse: Number, - }, - { - name: '--host ', - default: '', - }, - { - name: '--projectRoot ', - description: 'Path to a custom project root', - parse: (val: string) => path.resolve(val), - }, - { - name: '--watchFolders ', - description: - 'Specify any additional folders to be added to the watch list', - parse: (val: string) => - val.split(',').map((folder: string) => path.resolve(folder)), - }, - { - name: '--assetPlugins ', - description: - 'Specify any additional asset plugins to be used by the packager by full filepath', - parse: (val: string) => val.split(','), - }, - { - name: '--sourceExts ', - description: - 'Specify any additional source extensions to be used by the packager', - parse: (val: string) => val.split(','), - }, - { - name: '--max-workers ', - description: - 'Specifies the maximum number of workers the worker-pool ' + - 'will spawn for transforming files. This defaults to the number of the ' + - 'cores available on your machine.', - parse: (workers: string) => Number(workers), - }, - { - name: '--transformer ', - description: 'Specify a custom transformer to be used', - }, - { - name: '--reset-cache, --resetCache', - description: 'Removes cached files', - }, - { - name: '--custom-log-reporter-path, --customLogReporterPath ', - description: - 'Path to a JavaScript file that exports a log reporter as a replacement for TerminalReporter', - }, - { - name: '--https', - description: 'Enables https connections to the server', - }, - { - name: '--key ', - description: 'Path to custom SSL key', - }, - { - name: '--cert ', - description: 'Path to custom SSL cert', - }, - { - name: '--config ', - description: 'Path to the CLI configuration file', - parse: (val: string) => path.resolve(val), - }, - { - name: '--no-interactive', - description: 'Disables interactive mode', - }, - ], -}; diff --git a/packages/cli-plugin-metro/src/commands/start/runServer.ts b/packages/cli-plugin-metro/src/commands/start/runServer.ts deleted file mode 100644 index bc93adc48f..0000000000 --- a/packages/cli-plugin-metro/src/commands/start/runServer.ts +++ /dev/null @@ -1,180 +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 Metro from 'metro'; -import type {Reporter, ReportableEvent} from 'metro'; -import type Server from 'metro/src/Server'; -import type {Middleware} from 'metro-config'; -import {Terminal} from 'metro-core'; -import path from 'path'; -import { - createDevServerMiddleware, - indexPageMiddleware, -} from '@react-native-community/cli-server-api'; -import {Config} from '@react-native-community/cli-types'; - -import loadMetroConfig from '../../tools/loadMetroConfig'; -import { - isPackagerRunning, - logger, - version, - logAlreadyRunningBundler, - handlePortUnavailable, -} from '@react-native-community/cli-tools'; -import enableWatchMode from './watchMode'; -import chalk from 'chalk'; - -export type Args = { - assetPlugins?: string[]; - cert?: string; - customLogReporterPath?: string; - host?: string; - https?: boolean; - maxWorkers?: number; - key?: string; - platforms?: string[]; - port?: number; - resetCache?: boolean; - sourceExts?: string[]; - transformer?: string; - watchFolders?: string[]; - config?: string; - projectRoot?: string; - interactive: boolean; -}; - -async function runServer(_argv: Array, ctx: Config, args: Args) { - let port = args.port ?? 8081; - let packager = true; - - const packagerStatus = await isPackagerRunning(port); - - if ( - typeof packagerStatus === 'object' && - packagerStatus.status === 'running' - ) { - if (packagerStatus.root === ctx.root) { - packager = false; - logAlreadyRunningBundler(port); - } else { - const result = await handlePortUnavailable(port, ctx.root, packager); - [port, packager] = [result.port, result.packager]; - } - } else if (packagerStatus === 'unrecognized') { - const result = await handlePortUnavailable(port, ctx.root, packager); - [port, packager] = [result.port, result.packager]; - } - - if (packager === false) { - process.exit(); - } - - const metroConfig = await loadMetroConfig(ctx, { - config: args.config, - maxWorkers: args.maxWorkers, - port, - resetCache: args.resetCache, - watchFolders: args.watchFolders, - projectRoot: args.projectRoot, - sourceExts: args.sourceExts, - }); - // if customLogReporterPath is provided, use the custom reporter, otherwise use the default one - let reporter: Reporter = metroConfig.reporter; - if (args.customLogReporterPath) { - const terminal = new Terminal(process.stdout); - const ReporterImpl = getReporterImpl(args.customLogReporterPath); - reporter = new ReporterImpl(terminal); - } - - if (args.assetPlugins) { - // @ts-ignore - assigning to readonly property - metroConfig.transformer.assetPlugins = args.assetPlugins.map((plugin) => - require.resolve(plugin), - ); - } - - const { - middleware, - websocketEndpoints, - messageSocketEndpoint, - eventsSocketEndpoint, - } = createDevServerMiddleware({ - host: args.host, - port: metroConfig.server.port, - watchFolders: metroConfig.watchFolders, - }); - middleware.use(indexPageMiddleware); - - const customEnhanceMiddleware = metroConfig.server.enhanceMiddleware; - // @ts-ignore - assigning to readonly property - metroConfig.server.enhanceMiddleware = ( - metroMiddleware: Middleware, - server: Server, - ) => { - if (customEnhanceMiddleware) { - metroMiddleware = customEnhanceMiddleware(metroMiddleware, server); - } - return middleware.use(metroMiddleware); - }; - - const serverInstance = await Metro.runServer( - { - ...metroConfig, - reporter: { - update(event: ReportableEvent) { - reporter.update(event); - // Add reportEvent to the reporter update method. - eventsSocketEndpoint.reportEvent(event); - }, - }, - }, - { - host: args.host, - secure: args.https, - secureCert: args.cert, - secureKey: args.key, - // @ts-ignore - ws.Server types are incompatible - websocketEndpoints, - }, - ); - - if (args.interactive) { - enableWatchMode(messageSocketEndpoint, ctx); - } - - // In Node 8, the default keep-alive for an HTTP connection is 5 seconds. In - // early versions of Node 8, this was implemented in a buggy way which caused - // some HTTP responses (like those containing large JS bundles) to be - // terminated early. - // - // As a workaround, arbitrarily increase the keep-alive from 5 to 30 seconds, - // which should be enough to send even the largest of JS bundles. - // - // For more info: https://github.com/nodejs/node/issues/13391 - // - serverInstance.keepAliveTimeout = 30000; - - await version.logIfUpdateAvailable(ctx.root); - logger.info(`Started dev server at ${chalk.bold(port)}`); -} - -function getReporterImpl(customLogReporterPath: string) { - try { - // First we let require resolve it, so we can require packages in node_modules - // as expected. eg: require('my-package/reporter'); - return require(customLogReporterPath); - } catch (e) { - if ((e).code !== 'MODULE_NOT_FOUND') { - throw e; - } - // If that doesn't work, then we next try relative to the cwd, eg: - // require('./reporter'); - return require(path.resolve(customLogReporterPath)); - } -} - -export default runServer; diff --git a/packages/cli-plugin-metro/src/commands/start/watchMode.ts b/packages/cli-plugin-metro/src/commands/start/watchMode.ts deleted file mode 100644 index e0f2e6e1bc..0000000000 --- a/packages/cli-plugin-metro/src/commands/start/watchMode.ts +++ /dev/null @@ -1,83 +0,0 @@ -import readline from 'readline'; -import {logger, hookStdout} from '@react-native-community/cli-tools'; -import execa from 'execa'; -import chalk from 'chalk'; -import {Config} from '@react-native-community/cli-types'; -import {KeyPressHandler} from '../../tools/KeyPressHandler'; - -const CTRL_C = '\u0003'; -const CTRL_Z = '\u0026'; - -function printWatchModeInstructions() { - logger.log( - `${chalk.bold('r')} - reload the app\n${chalk.bold( - 'd', - )} - open developer menu\n${chalk.bold('i')} - run on iOS\n${chalk.bold( - 'a', - )} - run on Android`, - ); -} - -function enableWatchMode(messageSocket: any, ctx: Config) { - // We need to set this to true to catch key presses individually. - // As a result we have to implement our own method for exiting - // and other commands (e.g. ctrl+c & ctrl+z) - if (!process.stdin.setRawMode) { - logger.debug('Watch mode is not supported in this environment'); - return; - } - - readline.emitKeypressEvents(process.stdin); - - process.stdin.setRawMode(true); - - // We have no way of knowing when the dependency graph is done loading - // except by hooking into stdout itself. We want to print instructions - // right after its done loading. - const restore = hookStdout((output: string) => { - if (output.includes('Fast - Scalable - Integrated')) { - printWatchModeInstructions(); - restore(); - } - }); - - const onPress = (key: string) => { - switch (key) { - case 'r': - messageSocket.broadcast('reload', null); - logger.info('Reloading app...'); - break; - case 'd': - messageSocket.broadcast('devMenu', null); - logger.info('Opening Dev Menu...'); - break; - case 'i': - logger.info('Opening app on iOS...'); - execa('npx', [ - 'react-native', - 'run-ios', - ...(ctx.project.ios?.watchModeCommandParams ?? []), - ]).stdout?.pipe(process.stdout); - break; - case 'a': - logger.info('Opening app on Android...'); - execa('npx', [ - 'react-native', - 'run-android', - ...(ctx.project.android?.watchModeCommandParams ?? []), - ]).stdout?.pipe(process.stdout); - break; - case CTRL_Z: - process.emit('SIGTSTP', 'SIGTSTP'); - break; - case CTRL_C: - process.exit(); - } - }; - - const keyPressHandler = new KeyPressHandler(onPress); - keyPressHandler.createInteractionListener(); - keyPressHandler.startInterceptingKeyStrokes(); -} - -export default enableWatchMode; diff --git a/packages/cli-plugin-metro/src/index.ts b/packages/cli-plugin-metro/src/index.ts deleted file mode 100644 index 790f67dfa5..0000000000 --- a/packages/cli-plugin-metro/src/index.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type {MetroConfig} from 'metro-config'; -export { - Config, - ConfigLoadingContext, - default as loadMetroConfig, -} from './tools/loadMetroConfig'; -export { - default as commands, - buildBundleWithConfig, - CommandLineArgs, -} from './commands'; diff --git a/packages/cli-plugin-metro/src/tools/KeyPressHandler.ts b/packages/cli-plugin-metro/src/tools/KeyPressHandler.ts deleted file mode 100644 index d9397eb87b..0000000000 --- a/packages/cli-plugin-metro/src/tools/KeyPressHandler.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { - CLIError, - addInteractionListener, - logger, -} from '@react-native-community/cli-tools'; - -/** An abstract key stroke interceptor. */ -export class KeyPressHandler { - private isInterceptingKeyStrokes = false; - - constructor(public onPress: (key: string) => void) {} - - /** Start observing interaction pause listeners. */ - createInteractionListener() { - // Support observing prompts. - let wasIntercepting = false; - - const listener = ({pause}: {pause: boolean}) => { - if (pause) { - // Track if we were already intercepting key strokes before pausing, so we can - // resume after pausing. - wasIntercepting = this.isInterceptingKeyStrokes; - this.stopInterceptingKeyStrokes(); - } else if (wasIntercepting) { - // Only start if we were previously intercepting. - this.startInterceptingKeyStrokes(); - } - }; - - addInteractionListener(listener); - } - - private handleKeypress = async (key: string) => { - try { - logger.debug(`Key pressed: ${key}`); - this.onPress(key); - } catch (error) { - return new CLIError( - 'There was an error with the key press handler.', - (error as Error).message, - ); - } finally { - return; - } - }; - - /** Start intercepting all key strokes and passing them to the input `onPress` method. */ - startInterceptingKeyStrokes() { - if (this.isInterceptingKeyStrokes) { - return; - } - this.isInterceptingKeyStrokes = true; - const {stdin} = process; - stdin.setRawMode(true); - stdin.resume(); - stdin.setEncoding('utf8'); - stdin.on('data', this.handleKeypress); - } - - /** Stop intercepting all key strokes. */ - stopInterceptingKeyStrokes() { - if (!this.isInterceptingKeyStrokes) { - return; - } - this.isInterceptingKeyStrokes = false; - const {stdin} = process; - stdin.removeListener('data', this.handleKeypress); - stdin.setRawMode(false); - stdin.resume(); - } -} diff --git a/packages/cli-plugin-metro/src/tools/loadMetroConfig.ts b/packages/cli-plugin-metro/src/tools/loadMetroConfig.ts deleted file mode 100644 index d5f09495db..0000000000 --- a/packages/cli-plugin-metro/src/tools/loadMetroConfig.ts +++ /dev/null @@ -1,116 +0,0 @@ -import path from 'path'; -import { - ConfigT, - InputConfigT, - loadConfig, - mergeConfig, - resolveConfig, - ResolverConfigT, - YargArguments, -} from 'metro-config'; -import {CLIError, logger} from '@react-native-community/cli-tools'; -import type {Config} from '@react-native-community/cli-types'; -import {reactNativePlatformResolver} from './metroPlatformResolver'; - -export type {Config}; - -export type ConfigLoadingContext = Pick< - Config, - 'root' | 'reactNativePath' | 'platforms' ->; - -declare global { - var __REACT_NATIVE_METRO_CONFIG_LOADED: boolean; -} - -/** - * Get the config options to override based on RN CLI inputs. - */ -function getOverrideConfig(ctx: ConfigLoadingContext): InputConfigT { - const outOfTreePlatforms = Object.keys(ctx.platforms).filter( - (platform) => ctx.platforms[platform].npmPackageName, - ); - const resolver: Partial = { - platforms: [...Object.keys(ctx.platforms), 'native'], - }; - - if (outOfTreePlatforms.length) { - resolver.resolveRequest = reactNativePlatformResolver( - outOfTreePlatforms.reduce<{[platform: string]: string}>( - (result, platform) => { - result[platform] = ctx.platforms[platform].npmPackageName!; - return result; - }, - {}, - ), - ); - } - - return { - resolver, - serializer: { - // We can include multiple copies of InitializeCore here because metro will - // only add ones that are already part of the bundle - getModulesRunBeforeMainModule: () => [ - require.resolve( - path.join(ctx.reactNativePath, 'Libraries/Core/InitializeCore'), - ), - ...outOfTreePlatforms.map((platform) => - require.resolve( - `${ctx.platforms[platform] - .npmPackageName!}/Libraries/Core/InitializeCore`, - {paths: [ctx.root]}, - ), - ), - ], - }, - }; -} - -/** - * Load Metro config. - * - * Allows the CLI to override select values in `metro.config.js` based on - * dynamic user options in `ctx`. - */ -export default async function loadMetroConfig( - ctx: ConfigLoadingContext, - options: YargArguments = {}, -): Promise { - const overrideConfig = getOverrideConfig(ctx); - - const cwd = ctx.root; - const projectConfig = await resolveConfig(options.config, cwd); - - if (projectConfig.isEmpty) { - throw new CLIError(`No Metro config found in ${cwd}`); - } - - logger.debug(`Reading Metro config from ${projectConfig.filepath}`); - - if (!global.__REACT_NATIVE_METRO_CONFIG_LOADED) { - const warning = ` -================================================================================================= - -From React Native 0.73, your project's Metro config should extend '@react-native/metro-config' -or it will fail to build. Please copy the template at: -https://github.com/facebook/react-native/blob/main/packages/react-native/template/metro.config.js - -This warning will be removed in future (https://github.com/facebook/metro/issues/1018). - -================================================================================================= - `; - - for (const line of warning.trim().split('\n')) { - logger.warn(line); - } - } - - return mergeConfig( - await loadConfig({ - cwd, - ...options, - }), - overrideConfig, - ); -} diff --git a/packages/cli-plugin-metro/src/tools/metroPlatformResolver.ts b/packages/cli-plugin-metro/src/tools/metroPlatformResolver.ts deleted file mode 100644 index 39845f3842..0000000000 --- a/packages/cli-plugin-metro/src/tools/metroPlatformResolver.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * This is an implementation of a metro resolveRequest option which will remap react-native imports - * to different npm packages based on the platform requested. This allows a single metro instance/config - * to produce bundles for multiple out of tree platforms at a time. - * - * @param platformImplementations - * A map of platform to npm package that implements that platform - * - * Ex: - * { - * windows: 'react-native-windows' - * macos: 'react-native-macos' - * } - */ - -import type {CustomResolver} from 'metro-resolver'; - -export function reactNativePlatformResolver(platformImplementations: { - [platform: string]: string; -}): CustomResolver { - return (context, moduleName, platform) => { - let modifiedModuleName = moduleName; - if (platform != null && platformImplementations[platform]) { - if (moduleName === 'react-native') { - modifiedModuleName = platformImplementations[platform]; - } else if (moduleName.startsWith('react-native/')) { - modifiedModuleName = `${ - platformImplementations[platform] - }/${modifiedModuleName.slice('react-native/'.length)}`; - } - } - return context.resolveRequest(context, modifiedModuleName, platform); - }; -} diff --git a/packages/cli-plugin-metro/tsconfig.json b/packages/cli-plugin-metro/tsconfig.json index bc964d301d..7bb06bce6d 100644 --- a/packages/cli-plugin-metro/tsconfig.json +++ b/packages/cli-plugin-metro/tsconfig.json @@ -3,10 +3,5 @@ "compilerOptions": { "rootDir": "src", "outDir": "build" - }, - "references": [ - {"path": "../cli-types"}, - {"path": "../cli-server-api"}, - {"path": "../cli-tools"}, - ] + } } diff --git a/packages/cli-plugin-metro/types/@react-native/metro-config/index.d.ts b/packages/cli-plugin-metro/types/@react-native/metro-config/index.d.ts deleted file mode 100644 index eaf8bba350..0000000000 --- a/packages/cli-plugin-metro/types/@react-native/metro-config/index.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module '@react-native/metro-config' { - import type {ConfigT} from 'metro-config'; - - export function getDefaultConfig(projectRoot: string): ConfigT; -} diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index b2c897a8ce..c9c15cee3f 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -2,13 +2,11 @@ import {Command, DetachedCommand} from '@react-native-community/cli-types'; import {commands as cleanCommands} from '@react-native-community/cli-clean'; import {commands as doctorCommands} from '@react-native-community/cli-doctor'; import {commands as configCommands} from '@react-native-community/cli-config'; -import {commands as metroCommands} from '@react-native-community/cli-plugin-metro'; import profileHermes from '@react-native-community/cli-hermes'; import upgrade from './upgrade/upgrade'; import init from './init'; export const projectCommands = [ - ...metroCommands, ...configCommands, cleanCommands.clean, doctorCommands.info, diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index c11f08bbe3..2607dd0853 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -9,7 +9,6 @@ {"path": "../cli-config"}, {"path": "../cli-doctor"}, {"path": "../cli-hermes"}, - {"path": "../cli-plugin-metro"}, {"path": "../cli-server-api"}, {"path": "../cli-types"}, {"path": "../cli-debugger-ui"},