first commit

This commit is contained in:
2026-03-10 16:18:05 +00:00
commit 11f9c069b5
31635 changed files with 3187747 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = createDevMiddlewareLogger;
function createDevMiddlewareLogger(reporter) {
return {
info: makeLogger(reporter, "info"),
warn: makeLogger(reporter, "warn"),
error: makeLogger(reporter, "error"),
};
}
function makeLogger(reporter, level) {
return (...data) =>
reporter.update({
type: "unstable_server_log",
level,
data,
});
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type { TerminalReporter } from "metro";
type LoggerFn = (...message: $ReadOnlyArray<string>) => void;
/**
* Create a dev-middleware logger object that will emit logs via Metro's
* terminal reporter.
*/
declare export default function createDevMiddlewareLogger(
reporter: TerminalReporter,
): $ReadOnly<{
info: LoggerFn,
error: LoggerFn,
warn: LoggerFn,
}>;

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.inlineString = exports.UnknownProjectError = exports.CLIError = void 0;
class CLIError extends Error {
constructor(msg, originalError) {
super(inlineString(msg));
if (originalError != null) {
this.stack =
typeof originalError === "string"
? originalError
: originalError.stack || "".split("\n").slice(0, 2).join("\n");
} else {
this.stack = "";
}
}
}
exports.CLIError = CLIError;
class UnknownProjectError extends Error {}
exports.UnknownProjectError = UnknownProjectError;
const inlineString = (str = "") => str.replace(/(\s{2,})/gm, " ").trim();
exports.inlineString = inlineString;

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
/**
* A custom Error that creates a single-lined message to match current styling inside CLI.
* Uses original stack trace when `originalError` is passed or erase the stack if it's not defined.
*/
declare export class CLIError extends Error {
constructor(msg: string, originalError?: Error | string): void;
}
/**
* Raised when we're unable to find a package.json
*/
declare export class UnknownProjectError extends Error {}
declare export const inlineString: (str?: string) => string;

View File

@@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = isDevServerRunning;
var _net = _interopRequireDefault(require("net"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
async function isDevServerRunning(devServerUrl, projectRoot) {
const { hostname, port } = new URL(devServerUrl);
try {
if (!(await isPortOccupied(hostname, port))) {
return "not_running";
}
const statusResponse = await fetch(`${devServerUrl}/status`);
const body = await statusResponse.text();
return body === "packager-status:running" &&
statusResponse.headers.get("X-React-Native-Project-Root") === projectRoot
? "matched_server_running"
: "port_taken";
} catch (e) {
return "unknown";
}
}
async function isPortOccupied(hostname, port) {
let result = false;
const server = _net.default.createServer();
return new Promise((resolve, reject) => {
server.once("error", (e) => {
server.close();
if (e.code === "EADDRINUSE") {
result = true;
} else {
reject(e);
}
});
server.once("listening", () => {
result = false;
server.close();
});
server.once("close", () => {
resolve(result);
});
server.listen({
host: hostname,
port,
});
});
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
/**
* Determine whether we can run the dev server.
*
* Return values:
* - `not_running`: The port is unoccupied.
* - `matched_server_running`: The port is occupied by another instance of this
* dev server (matching the passed `projectRoot`).
* - `port_taken`: The port is occupied by another process.
* - `unknown`: An error was encountered; attempt server creation anyway.
*/
declare export default function isDevServerRunning(
devServerUrl: string,
projectRoot: string,
): Promise<"not_running" | "matched_server_running" | "port_taken" | "unknown">;

View File

@@ -0,0 +1,103 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = loadMetroConfig;
var _errors = require("./errors");
var _metroPlatformResolver = require("./metroPlatformResolver");
var _metroConfig = require("metro-config");
var _path = _interopRequireDefault(require("path"));
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const debug = require("debug")("ReactNative:CommunityCliPlugin");
function getCommunityCliDefaultConfig(ctx, config) {
const outOfTreePlatforms = Object.keys(ctx.platforms).filter(
(platform) => ctx.platforms[platform].npmPackageName,
);
const resolver = {
platforms: [...Object.keys(ctx.platforms), "native"],
};
if (outOfTreePlatforms.length) {
resolver.resolveRequest = (0,
_metroPlatformResolver.reactNativePlatformResolver)(
outOfTreePlatforms.reduce((result, platform) => {
result[platform] = ctx.platforms[platform].npmPackageName;
return result;
}, {}),
config.resolver?.resolveRequest,
);
}
return {
resolver,
serializer: {
getModulesRunBeforeMainModule: () => [
require.resolve(
_path.default.join(
ctx.reactNativePath,
"Libraries/Core/InitializeCore",
),
{
paths: [ctx.root],
},
),
...outOfTreePlatforms.map((platform) =>
require.resolve(
`${ctx.platforms[platform].npmPackageName}/Libraries/Core/InitializeCore`,
{
paths: [ctx.root],
},
),
),
],
},
};
}
async function loadMetroConfig(ctx, options = {}) {
let RNMetroConfig = null;
try {
RNMetroConfig = require("@react-native/metro-config");
} catch (e) {
throw new Error(
"Cannot resolve `@react-native/metro-config`. Ensure it is listed in your project's `devDependencies`.",
);
}
const defaultConfig = RNMetroConfig.getDefaultConfig(ctx.root);
global.__REACT_NATIVE_METRO_CONFIG_LOADED = false;
if (typeof RNMetroConfig.setFrameworkDefaults !== "function") {
throw new Error(
"`@react-native/metro-config` does not have the expected API. Ensure it matches your React Native version.",
);
}
RNMetroConfig.setFrameworkDefaults(
getCommunityCliDefaultConfig(ctx, defaultConfig),
);
const cwd = ctx.root;
const projectConfig = await (0, _metroConfig.resolveConfig)(
options.config,
cwd,
);
if (projectConfig.isEmpty) {
throw new _errors.CLIError(`No Metro config found in ${cwd}`);
}
debug(`Reading Metro config from ${projectConfig.filepath}`);
if (!global.__REACT_NATIVE_METRO_CONFIG_LOADED) {
for (const line of `
=================================================================================================
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/react-native-community/template/blob/main/template/metro.config.js
This warning will be removed in future (https://github.com/facebook/metro/issues/1018).
=================================================================================================
`
.trim()
.split("\n")) {
console.warn(line);
}
}
return (0, _metroConfig.loadConfig)({
cwd,
...options,
});
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type { Config } from "@react-native-community/cli-types";
import type { ConfigT, YargArguments } from "metro-config";
export type { Config };
export type ConfigLoadingContext = $ReadOnly<{
root: Config["root"],
reactNativePath: Config["reactNativePath"],
platforms: Config["platforms"],
...
}>;
/**
* Load Metro config.
*
* Allows the CLI to override select values in `metro.config.js` based on
* dynamic user options in `ctx`.
*/
declare export default function loadMetroConfig(
ctx: ConfigLoadingContext,
options?: YargArguments,
): Promise<ConfigT>;

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.reactNativePlatformResolver = reactNativePlatformResolver;
function reactNativePlatformResolver(platformImplementations, 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)}`;
}
}
if (customResolver) {
return customResolver(context, modifiedModuleName, platform);
}
return context.resolveRequest(context, modifiedModuleName, platform);
};
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type { CustomResolver } from "metro-resolver";
/**
* 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'
* }
*/
declare export function reactNativePlatformResolver(
platformImplementations: {
[platform: string]: string,
},
customResolver: ?CustomResolver,
): CustomResolver;

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = parseKeyValueParamArray;
function parseKeyValueParamArray(keyValueArray) {
const result = {};
for (const item of keyValueArray) {
if (item.indexOf("=") === -1) {
throw new Error('Expected parameter to include "=" but found: ' + item);
}
if (item.indexOf("&") !== -1) {
throw new Error('Parameter cannot include "&" but found: ' + item);
}
const params = new URLSearchParams(item);
params.forEach((value, key) => {
result[key] = value;
});
}
return result;
}

View File

@@ -0,0 +1,13 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
declare export default function parseKeyValueParamArray(
keyValueArray: $ReadOnlyArray<string>,
): Record<string, string>;

View File

@@ -0,0 +1,111 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
exports.default = getLatestRelease;
exports.logIfUpdateAvailable = logIfUpdateAvailable;
var _semver = _interopRequireDefault(require("semver"));
var _util = require("util");
function _interopRequireDefault(e) {
return e && e.__esModule ? e : { default: e };
}
const debug = require("debug")("ReactNative:CommunityCliPlugin");
async function logIfUpdateAvailable(cliConfig, reporter) {
const { reactNativeVersion: currentVersion } = cliConfig;
let newVersion = null;
try {
const upgrade = await getLatestRelease(currentVersion);
if (upgrade) {
newVersion = upgrade;
}
} catch (e) {
debug(
"Cannot detect current version of React Native, " +
"skipping check for a newer release",
);
debug(e);
}
if (newVersion == null) {
return;
}
if (_semver.default.gt(newVersion.stable, currentVersion)) {
reporter.update({
type: "unstable_server_log",
level: "info",
data: `React Native v${newVersion.stable} is now available (your project is running on v${currentVersion}).
Changelog: ${(0, _util.styleText)(["dim", "underline"], newVersion?.changelogUrl ?? "none")}
Diff: ${(0, _util.styleText)(["dim", "underline"], newVersion?.diffUrl ?? "none")}
`,
});
}
}
function isDiffPurgeEntry(data) {
return (
[data.name, data.zipball_url, data.tarball_url, data.node_id].filter(
(e) => typeof e !== "undefined",
).length === 0
);
}
async function getLatestRelease(currentVersion) {
debug("Checking for a newer version of React Native");
try {
debug(`Current version: ${currentVersion}`);
if (["-canary", "-nightly"].some((s) => currentVersion.includes(s))) {
return;
}
debug("Checking for newer releases on GitHub");
const latestVersion = await getLatestRnDiffPurgeVersion();
if (latestVersion == null) {
debug("Failed to get latest release");
return;
}
const { stable, candidate } = latestVersion;
debug(`Latest release: ${stable} (${candidate ?? ""})`);
if (_semver.default.compare(stable, currentVersion) >= 0) {
return {
stable,
candidate,
changelogUrl: buildChangelogUrl(stable),
diffUrl: buildDiffUrl(currentVersion, stable),
};
}
} catch (e) {
debug("Something went wrong with remote version checking, moving on");
debug(e);
}
}
function buildChangelogUrl(version) {
return `https://github.com/facebook/react-native/releases/tag/v${version}`;
}
function buildDiffUrl(oldVersion, newVersion) {
return `https://react-native-community.github.io/upgrade-helper/?from=${oldVersion}&to=${newVersion}`;
}
async function getLatestRnDiffPurgeVersion() {
const resp = await fetch(
"https://api.github.com/repos/react-native-community/rn-diff-purge/tags",
{
headers: {
"User-Agent": "@react-native/community-cli-plugin",
},
},
);
const result = {
stable: "0.0.0",
};
if (resp.status !== 200) {
return;
}
const body = (await resp.json()).filter(isDiffPurgeEntry);
for (const { name: version } of body) {
if (result.candidate != null && version.includes("-rc")) {
result.candidate = version.substring(8);
continue;
}
if (!version.includes("-rc")) {
result.stable = version.substring(8);
return result;
}
}
return result;
}

View File

@@ -0,0 +1,40 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
*/
import type { Config } from "@react-native-community/cli-types";
import type { TerminalReporter } from "metro";
type Release = {
// The current stable release
stable: string,
// The current candidate release. These are only populated if the latest release is a candidate release.
candidate?: string,
changelogUrl: string,
diffUrl: string,
};
/**
* Logs out a message if the user's version is behind a stable version of React Native
*/
declare export function logIfUpdateAvailable(
cliConfig: Config,
reporter: TerminalReporter,
): Promise<void>;
/**
* Checks via GitHub API if there is a newer stable React Native release and,
* if it exists, returns the release data.
*
* If the latest release is not newer or if it's a prerelease, the function
* will return undefined.
*/
declare export default function getLatestRelease(
currentVersion: string,
): Promise<Release | void>;