feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,41 @@
<!-- Title -->
<h1 align="center">
👋 Welcome to <br><code>@expo/package-manager</code>
</h1>
<p align="center">A library for installing and finding packages in a project.</p>
<!-- Header -->
<p align="center">
<img src="https://flat.badgen.net/packagephobia/install/@expo/package-manager">
<a href="https://www.npmjs.com/package/@expo/package-manager">
<img src="https://flat.badgen.net/npm/dw/@expo/package-manager" target="_blank" />
</a>
</p>
---
<!-- Body -->
## 🏁 Setup
Install `@expo/package-manager` in your project.
```sh
yarn add @expo/package-manager
```
## ⚽️ Usage
```ts
import * as PackageManager from '@expo/package-manager';
const manager = PackageManager.createForProject(projectRoot);
await Promise.all([
manager.addDevAsync(['@expo/webpack-config']),
manager.addAsync(['expo', 'expo-camera']),
]);
```

View File

@@ -0,0 +1,48 @@
import type { SpawnOptions, SpawnPromise, SpawnResult } from '@expo/spawn-async';
import type { PendingSpawnPromise } from './utils/spawn';
export interface PackageManagerOptions extends SpawnOptions {
/**
* If the package manager should run in silent mode.
* Note, this will hide possible error output from executed commands.
* When running in silent mode, make sure you handle them properly.
*/
silent?: boolean;
/**
* The logging method used to communicate the command which is executed.
* Without `silent`, this defaults to `console.log`.
* When `silent` is set to `true`, this defaults to a no-op.
*/
log?: (...args: any[]) => void;
}
export interface PackageManager {
/** The options for this package manager */
readonly options: PackageManagerOptions;
/** Run any command using the package manager */
runAsync(command: string[], options?: SpawnOptions): SpawnPromise<SpawnResult>;
/** Invoke a binary from within a package, like "eslint" or "jest" */
runBinAsync(command: string[], options?: SpawnOptions): SpawnPromise<SpawnResult>;
/** Get the version of the used package manager */
versionAsync(): Promise<string>;
/** Get a single configuration property from the package manager */
getConfigAsync(key: string): Promise<string>;
/** Remove the lock file within the project, if any */
removeLockfileAsync(): Promise<void>;
/** Get the workspace root package manager, if this project is within a workspace/monorepo */
workspaceRoot(): PackageManager | null;
/** Install all current dependencies using the package manager */
installAsync(): Promise<SpawnResult> | SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Uninstall all current dependencies by removing the folder containing the packages */
uninstallAsync(): Promise<void>;
/** Add a normal dependency to the project */
addAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Add a development dependency to the project */
addDevAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Add a global dependency to the environment */
addGlobalAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Remove a normal dependency from the project */
removeAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Remove a development dependency from the project */
removeDevAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
/** Remove a global dependency from the environments */
removeGlobalAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
}

View File

@@ -0,0 +1,3 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=PackageManager.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PackageManager.js","sourceRoot":"","sources":["../src/PackageManager.ts"],"names":[],"mappings":""}

View File

@@ -0,0 +1,8 @@
export * from './PackageManager';
export * from './ios/CocoaPodsPackageManager';
export * from './node/NpmPackageManager';
export * from './node/PnpmPackageManager';
export * from './node/YarnPackageManager';
export * from './node/BunPackageManager';
export * from './utils/nodeManagers';
export { isYarnOfflineAsync } from './utils/yarn';

View File

@@ -0,0 +1,27 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isYarnOfflineAsync = void 0;
__exportStar(require("./PackageManager"), exports);
__exportStar(require("./ios/CocoaPodsPackageManager"), exports);
__exportStar(require("./node/NpmPackageManager"), exports);
__exportStar(require("./node/PnpmPackageManager"), exports);
__exportStar(require("./node/YarnPackageManager"), exports);
__exportStar(require("./node/BunPackageManager"), exports);
__exportStar(require("./utils/nodeManagers"), exports);
var yarn_1 = require("./utils/yarn");
Object.defineProperty(exports, "isYarnOfflineAsync", { enumerable: true, get: function () { return yarn_1.isYarnOfflineAsync; } });
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,mDAAiC;AAEjC,gEAA8C;AAE9C,2DAAyC;AACzC,4DAA0C;AAC1C,4DAA0C;AAC1C,2DAAyC;AAEzC,uDAAqC;AACrC,qCAAkD;AAAzC,0GAAA,kBAAkB,OAAA"}

View File

@@ -0,0 +1,88 @@
import type { SpawnOptions, SpawnResult } from '@expo/spawn-async';
import spawnAsync from '@expo/spawn-async';
import type { Ora } from 'ora';
export type CocoaPodsErrorCode = 'NON_INTERACTIVE' | 'NO_CLI' | 'COMMAND_FAILED';
export declare class CocoaPodsError extends Error {
code: CocoaPodsErrorCode;
cause?: Error | undefined;
readonly name = "CocoaPodsError";
readonly isPackageManagerError = true;
constructor(message: string, code: CocoaPodsErrorCode, cause?: Error | undefined);
}
export declare function extractMissingDependencyError(errorOutput: string): [string, string] | null;
export declare class CocoaPodsPackageManager {
#private;
options: SpawnOptions;
private silent;
private cwd;
static getPodProjectRoot(projectRoot: string): string | null;
static isUsingPods(projectRoot: string): boolean;
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static gemInstallCLIAsync(nonInteractive?: boolean, spawnOptions?: SpawnOptions): Promise<void>;
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static brewLinkCLIAsync(spawnOptions?: SpawnOptions): Promise<void>;
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static brewInstallCLIAsync(spawnOptions?: SpawnOptions): Promise<void>;
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static installCLIAsync({ nonInteractive, spawnOptions, }: {
nonInteractive?: boolean;
spawnOptions?: SpawnOptions;
}): Promise<boolean>;
static isAvailable(projectRoot: string, silent: boolean): boolean;
/** @deprecated: Use `CocoaPodsPackageManager#isCLIInstalledAsync` instead */
static isCLIInstalledAsync(spawnOptions?: SpawnOptions, { useBundler }?: {
useBundler?: boolean;
}): Promise<boolean>;
constructor({ cwd, silent, useBundler, }: {
cwd: string;
silent?: boolean;
useBundler?: boolean;
});
private _useBundler?;
get name(): string;
/** Runs `pod install` and attempts to automatically run known troubleshooting steps automatically. */
installAsync({ spinner }?: {
spinner?: Ora;
}): Promise<void>;
isCLIInstalledAsync(): Promise<boolean>;
installCLIAsync({ nonInteractive, spawnOptions, }?: {
nonInteractive?: boolean;
spawnOptions?: spawnAsync.SpawnOptions;
}): Promise<boolean>;
handleInstallErrorAsync({ error, shouldUpdate, updatedPackages, spinner, }: {
error: any;
spinner?: Ora;
shouldUpdate?: boolean;
updatedPackages?: string[];
}): Promise<SpawnResult>;
private _installAsync;
private runInstallTypeCommandAsync;
addWithParametersAsync(names: string[], parameters: string[]): Promise<void>;
addAsync(names?: string[]): void;
addDevAsync(names?: string[]): void;
addGlobalAsync(names?: string[]): void;
removeAsync(names?: string[]): void;
removeDevAsync(names?: string[]): void;
removeGlobalAsync(names?: string[]): void;
versionAsync(): Promise<string>;
configAsync(key: string): Promise<string>;
removeLockfileAsync(): Promise<void>;
uninstallAsync(): Promise<void>;
_runAsync(args: string[]): Promise<SpawnResult>;
}
export declare function getPodUpdateMessage(output: string): {
updatePackage: string | null;
shouldUpdateRepo: boolean;
};
export declare function getPodRepoUpdateMessage(errorOutput: string): {
updatePackage: string | null;
shouldUpdateRepo: boolean;
message: string;
};
/**
* Format the CocoaPods CLI install error.
*
* @param error Error from CocoaPods CLI `pod install` command.
* @returns
*/
export declare function getImprovedPodInstallError(error: SpawnResult & Error, { cwd }: Pick<SpawnOptions, 'cwd'>): Error;

View File

@@ -0,0 +1,428 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CocoaPodsPackageManager = exports.CocoaPodsError = void 0;
exports.extractMissingDependencyError = extractMissingDependencyError;
exports.getPodUpdateMessage = getPodUpdateMessage;
exports.getPodRepoUpdateMessage = getPodRepoUpdateMessage;
exports.getImprovedPodInstallError = getImprovedPodInstallError;
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const chalk_1 = __importDefault(require("chalk"));
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const gemfile_1 = require("./gemfile");
class CocoaPodsError extends Error {
code;
cause;
name = 'CocoaPodsError';
isPackageManagerError = true;
constructor(message, code, cause) {
super(cause ? `${message}\n└─ Cause: ${cause.message}` : message);
this.code = code;
this.cause = cause;
}
}
exports.CocoaPodsError = CocoaPodsError;
function extractMissingDependencyError(errorOutput) {
// [!] Unable to find a specification for `expo-dev-menu-interface` depended upon by `expo-dev-launcher`
const results = errorOutput.match(/Unable to find a specification for ['"`]([\w-_\d\s]+)['"`] depended upon by ['"`]([\w-_\d\s]+)['"`]/);
if (results != null && results[1] != null && results[2] != null) {
return [results[1], results[2]];
}
return null;
}
class CocoaPodsPackageManager {
options;
silent;
cwd;
static getPodProjectRoot(projectRoot) {
if (CocoaPodsPackageManager.isUsingPods(projectRoot))
return projectRoot;
const iosProject = path_1.default.join(projectRoot, 'ios');
if (CocoaPodsPackageManager.isUsingPods(iosProject))
return iosProject;
const macOsProject = path_1.default.join(projectRoot, 'macos');
if (CocoaPodsPackageManager.isUsingPods(macOsProject))
return macOsProject;
return null;
}
static isUsingPods(projectRoot) {
return fs_1.default.existsSync(path_1.default.join(projectRoot, 'Podfile'));
}
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static async gemInstallCLIAsync(nonInteractive = false, spawnOptions = { stdio: 'inherit' }) {
const options = ['install', 'cocoapods', '--no-document'];
try {
// In case the user has run sudo before running the command we can properly install CocoaPods without prompting for an interaction.
await (0, spawn_async_1.default)('gem', options, spawnOptions);
}
catch (error) {
if (nonInteractive) {
throw new CocoaPodsError('Failed to install CocoaPods CLI with gem (recommended)', 'COMMAND_FAILED', error);
}
// If the user doesn't have permission then we can prompt them to use sudo.
console.log('Your password might be needed to install CocoaPods CLI: https://guides.cocoapods.org/using/getting-started.html#installation');
await (0, spawn_async_1.default)('sudo', ['gem', ...options], spawnOptions);
}
}
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static async brewLinkCLIAsync(spawnOptions = { stdio: 'inherit' }) {
await (0, spawn_async_1.default)('brew', ['link', 'cocoapods'], spawnOptions);
}
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static async brewInstallCLIAsync(spawnOptions = { stdio: 'inherit' }) {
await (0, spawn_async_1.default)('brew', ['install', 'cocoapods'], spawnOptions);
}
/** @deprecated: Use `CocoaPodsPackageManager#installCLIAsync` instead */
static async installCLIAsync({ nonInteractive = false, spawnOptions = { stdio: 'inherit' }, }) {
if (!spawnOptions) {
spawnOptions = { stdio: 'inherit' };
}
const silent = !!spawnOptions.ignoreStdio;
try {
!silent && console.log(`\u203A Attempting to install CocoaPods CLI with Gem`);
await CocoaPodsPackageManager.gemInstallCLIAsync(nonInteractive, spawnOptions);
!silent && console.log(`\u203A Successfully installed CocoaPods CLI with Gem`);
return true;
}
catch (error) {
if (!silent) {
console.log(chalk_1.default.yellow(`\u203A Failed to install CocoaPods CLI with Gem`));
console.log(chalk_1.default.red(error.stderr ?? error.message));
console.log(`\u203A Attempting to install CocoaPods CLI with Homebrew`);
}
try {
await CocoaPodsPackageManager.brewInstallCLIAsync(spawnOptions);
if (!(await CocoaPodsPackageManager.isCLIInstalledAsync(spawnOptions))) {
try {
await CocoaPodsPackageManager.brewLinkCLIAsync(spawnOptions);
// Still not available after linking? Bail out
if (!(await CocoaPodsPackageManager.isCLIInstalledAsync(spawnOptions))) {
throw new CocoaPodsError('CLI could not be installed automatically with gem or Homebrew, please install CocoaPods manually and try again', 'NO_CLI', error);
}
}
catch (error) {
throw new CocoaPodsError('Homebrew installation appeared to succeed but CocoaPods CLI not found in PATH and unable to link.', 'NO_CLI', error);
}
}
!silent && console.log(`\u203A Successfully installed CocoaPods CLI with Homebrew`);
return true;
}
catch (error) {
!silent &&
console.warn(chalk_1.default.yellow(`\u203A Failed to install CocoaPods with Homebrew. Install CocoaPods CLI and try again: https://cocoapods.org/`));
throw new CocoaPodsError(`Failed to install CocoaPods with Homebrew. Install CocoaPods CLI and try again: https://cocoapods.org/`, 'NO_CLI', error);
}
}
}
static isAvailable(projectRoot, silent) {
if (process.platform !== 'darwin') {
!silent && console.log(chalk_1.default.red('CocoaPods is only supported on macOS machines'));
return false;
}
if (!CocoaPodsPackageManager.isUsingPods(projectRoot)) {
!silent && console.log(chalk_1.default.yellow('CocoaPods is not supported in this project'));
return false;
}
return true;
}
/** @deprecated: Use `CocoaPodsPackageManager#isCLIInstalledAsync` instead */
static async isCLIInstalledAsync(spawnOptions = { stdio: 'inherit' }, { useBundler = false } = {}) {
try {
if (useBundler) {
await (0, spawn_async_1.default)('bundle', ['exec', 'pod', '--version'], spawnOptions);
}
else {
await (0, spawn_async_1.default)('pod', ['--version'], spawnOptions);
}
return true;
}
catch (error) {
// NOTE(@kitten): We abort here to prevent any command from proceeding
// We don't want to trigger `installCLIAsync` and let users resolve this
if (useBundler) {
console.warn(chalk_1.default.yellow(`\u203A Failed to run \`bundle exec pod\``));
throw new CocoaPodsError(error.stderr, 'COMMAND_FAILED');
}
return false;
}
}
constructor({ cwd, silent, useBundler, }) {
this.silent = !!silent;
this.cwd = cwd;
this.options = {
cwd,
// We use pipe by default instead of inherit so that we can capture stderr/stdout and process it for errors.
// Later we'll also pipe the stdout/stderr to the terminal when silent is false.
stdio: 'pipe',
};
// NOTE(@kitten): Override for tests
if (useBundler != null) {
this._useBundler = Promise.resolve(useBundler);
}
}
_useBundler;
async #useBundlerAsync() {
return (this._useBundler ||= (0, gemfile_1.isUsingBundlerAsync)(this.cwd));
}
get name() {
return 'CocoaPods';
}
/** Runs `pod install` and attempts to automatically run known troubleshooting steps automatically. */
async installAsync({ spinner } = {}) {
await this._installAsync({ spinner });
}
async isCLIInstalledAsync() {
return CocoaPodsPackageManager.isCLIInstalledAsync(this.options, {
useBundler: await this.#useBundlerAsync(),
});
}
installCLIAsync({ nonInteractive = true, spawnOptions, } = {}) {
return CocoaPodsPackageManager.installCLIAsync({
nonInteractive,
spawnOptions: {
...this.options,
...spawnOptions,
},
});
}
async handleInstallErrorAsync({ error, shouldUpdate = true, updatedPackages = [], spinner, }) {
// Unknown errors are rethrown.
if (!error.output) {
throw error;
}
// To emulate a `pod install --repo-update` error, enter your `ios/Podfile.lock` and change one of `PODS` version numbers to some lower value.
// const isPodRepoUpdateError = shouldPodRepoUpdate(output);
if (!shouldUpdate) {
// If we can't automatically fix the error, we'll just rethrow it with some known troubleshooting info.
throw getImprovedPodInstallError(error, {
cwd: this.options.cwd,
});
}
// Collect all of the spawn info.
const errorOutput = error.output.join(os_1.default.EOL).trim();
// Extract useful information from the error message and push it to the spinner.
const { updatePackage, shouldUpdateRepo } = getPodUpdateMessage(errorOutput);
if (!updatePackage || updatedPackages.includes(updatePackage)) {
// `pod install --repo-update`...
// Attempt to install again but this time with install --repo-update enabled.
return await this._installAsync({
spinner,
shouldRepoUpdate: true,
// Include a boolean to ensure pod install --repo-update isn't invoked in the unlikely case where the pods fail to update.
shouldUpdate: false,
updatedPackages,
});
}
// Store the package we should update to prevent a loop.
updatedPackages.push(updatePackage);
// If a single package is broken, we'll try to update it.
// You can manually test this by changing a version number in your `Podfile.lock`.
// Attempt `pod update <package> <--no-repo-update>` and then try again.
return await this.runInstallTypeCommandAsync(['update', updatePackage, shouldUpdateRepo ? '' : '--no-repo-update'].filter(Boolean), {
formatWarning() {
const updateMessage = `Failed to update ${chalk_1.default.bold(updatePackage)}. Attempting to update the repo instead.`;
return updateMessage;
},
spinner,
updatedPackages,
});
// // If update succeeds, we'll try to install again (skipping `pod install --repo-update`).
// return await this._installAsync({
// spinner,
// shouldUpdate: false,
// updatedPackages,
// });
}
async _installAsync({ shouldRepoUpdate, ...props } = {}) {
return await this.runInstallTypeCommandAsync(['install', shouldRepoUpdate ? '--repo-update' : ''].filter(Boolean), {
formatWarning(error) {
// Extract useful information from the error message and push it to the spinner.
return getPodRepoUpdateMessage(error.output.join(os_1.default.EOL).trim()).message;
},
...props,
});
}
async runInstallTypeCommandAsync(command, { formatWarning, ...props } = {}) {
try {
return await this._runAsync(command);
}
catch (error) {
if (formatWarning) {
const warning = formatWarning(error);
if (props.spinner) {
props.spinner.text = chalk_1.default.bold(warning);
}
if (!this.silent) {
console.warn(chalk_1.default.yellow(warning));
}
}
return await this.handleInstallErrorAsync({ error, ...props });
}
}
async addWithParametersAsync(names, parameters) {
throw new Error('Unimplemented');
}
addAsync(names = []) {
throw new Error('Unimplemented');
}
addDevAsync(names = []) {
throw new Error('Unimplemented');
}
addGlobalAsync(names = []) {
throw new Error('Unimplemented');
}
removeAsync(names = []) {
throw new Error('Unimplemented');
}
removeDevAsync(names = []) {
throw new Error('Unimplemented');
}
removeGlobalAsync(names = []) {
throw new Error('Unimplemented');
}
async versionAsync() {
const { stdout } = (await this.#useBundlerAsync())
? await (0, spawn_async_1.default)('bundle', ['exec', 'pod', '--version'], this.options)
: await (0, spawn_async_1.default)('pod', ['--version'], this.options);
return stdout.trim();
}
async configAsync(key) {
throw new Error('Unimplemented');
}
async removeLockfileAsync() {
throw new Error('Unimplemented');
}
async uninstallAsync() {
throw new Error('Unimplemented');
}
// Exposed for testing
async _runAsync(args) {
const useBundler = await this.#useBundlerAsync();
const [command, commandArgs] = useBundler
? ['bundle', ['exec', 'pod', ...args, '--ansi']]
: ['pod', [...args, '--ansi']];
if (!this.silent) {
console.log(`> ${useBundler ? 'bundle exec pod' : 'pod'} ${args.join(' ')}`);
}
const promise = (0, spawn_async_1.default)(command, commandArgs, {
// Add the cwd and other options to the spawn options.
...this.options,
// We use pipe by default instead of inherit so that we can capture stderr/stdout and process it for errors.
// This is particularly required for the `pod install --repo-update` error.
// Later we'll also pipe the stdout/stderr to the terminal when silent is false,
// currently this means we lose out on the ansi colors unless passing the `--ansi` flag to every command.
stdio: 'pipe',
});
if (!this.silent) {
// If not silent, pipe the stdout/stderr to the terminal.
// We only do this when the `stdio` is set to `pipe` (collect the results for parsing), `inherit` won't contain `promise.child`.
if (promise.child.stdout) {
promise.child.stdout.pipe(process.stdout);
}
}
return await promise;
}
}
exports.CocoaPodsPackageManager = CocoaPodsPackageManager;
/** When pods are outdated, they'll throw an error informing you to run "pod install --repo-update" */
function shouldPodRepoUpdate(errorOutput) {
const output = errorOutput;
const isPodRepoUpdateError = output.includes('pod repo update') || output.includes('--no-repo-update');
return isPodRepoUpdateError;
}
function getPodUpdateMessage(output) {
const props = output.match(/run ['"`]pod update ([\w-_\d/]+)( --no-repo-update)?['"`] to apply changes/);
return {
updatePackage: props?.[1] ?? null,
shouldUpdateRepo: !props?.[2],
};
}
function getPodRepoUpdateMessage(errorOutput) {
const warningInfo = extractMissingDependencyError(errorOutput);
const brokenPackage = getPodUpdateMessage(errorOutput);
let message;
if (warningInfo) {
message = `Couldn't install: ${warningInfo[1]} » ${chalk_1.default.underline(warningInfo[0])}.`;
}
else if (brokenPackage?.updatePackage) {
message = `Couldn't install: ${brokenPackage?.updatePackage}.`;
}
else {
message = `Couldn't install Pods.`;
}
message += ` Updating the Pods project and trying again...`;
return { message, ...brokenPackage };
}
/**
* Format the CocoaPods CLI install error.
*
* @param error Error from CocoaPods CLI `pod install` command.
* @returns
*/
function getImprovedPodInstallError(error, { cwd = process.cwd() }) {
// Collect all of the spawn info.
const errorOutput = error.output.join(os_1.default.EOL).trim();
if (error.stdout.match(/No [`'"]Podfile[`'"] found in the project directory/)) {
// Ran pod install but no Podfile was found.
error.message = `No Podfile found in directory: ${cwd}. Ensure CocoaPods is setup any try again.`;
}
else if (shouldPodRepoUpdate(errorOutput)) {
// Ran pod install but the install --repo-update step failed.
const warningInfo = extractMissingDependencyError(errorOutput);
let reason;
if (warningInfo) {
reason = `Couldn't install: ${warningInfo[1]} » ${chalk_1.default.underline(warningInfo[0])}`;
}
else {
reason = `This is often due to native package versions mismatching`;
}
// Attempt to provide a helpful message about the missing NPM dependency (containing a CocoaPod) since React Native
// developers will almost always be using autolinking and not interacting with CocoaPods directly.
let solution;
if (warningInfo?.[0]) {
// If the missing package is named `expo-dev-menu`, `react-native`, etc. then it might not be installed in the project.
if (warningInfo[0].match(/^(?:@?expo|@?react)(-|\/)/)) {
solution = `Ensure the node module "${warningInfo[0]}" is installed in your project, then run 'npx pod-install' to try again.`;
}
else {
solution = `Ensure the CocoaPod "${warningInfo[0]}" is installed in your project, then run 'npx pod-install' to try again.`;
}
}
else {
// Brute force
solution = `Try deleting the 'ios/Pods' folder or the 'ios/Podfile.lock' file and running 'npx pod-install' to resolve.`;
}
error.message = `${reason}. ${solution}`;
// Attempt to provide the troubleshooting info from CocoaPods CLI at the bottom of the error message.
if (error.stdout) {
const cocoapodsDebugInfo = error.stdout.split(os_1.default.EOL);
// The troubleshooting info starts with `[!]`, capture everything after that.
const firstWarning = cocoapodsDebugInfo.findIndex((v) => v.startsWith('[!]'));
if (firstWarning !== -1) {
const warning = cocoapodsDebugInfo.slice(firstWarning).join(os_1.default.EOL);
error.message += `\n\n${chalk_1.default.gray(warning)}`;
}
}
return new CocoaPodsError('Command `pod install --repo-update` failed.', 'COMMAND_FAILED', error);
}
else {
let stderr = error.stderr.trim();
// CocoaPods CLI prints the useful error to stdout...
const usefulError = error.stdout.match(/\[!\]\s((?:.|\n)*)/)?.[1];
// If there is a useful error message then prune the less useful info.
if (usefulError) {
// Delete unhelpful CocoaPods CLI error message.
if (error.message?.match(/pod exited with non-zero code: 1/)) {
error.message = '';
}
stderr = null;
}
error.message = [usefulError, error.message, stderr].filter(Boolean).join('\n');
}
return new CocoaPodsError('Command `pod install` failed.', 'COMMAND_FAILED', error);
}
//# sourceMappingURL=CocoaPodsPackageManager.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
/** Finds a Gemfile in the target directory or a parent, stopping at the Git or workspace root. */
export declare function findGemfile(root?: string): Promise<string | null>;
/**
* Check if the project uses Bundler to manage CocoaPods.
* Returns `true` if a `Gemfile` exists in the project root (or a parent)
* that lists `cocoapods` as a dependency, and `bundle exec pod --version` succeeds.
*/
export declare function isUsingBundlerAsync(projectRoot: string): Promise<boolean>;

View File

@@ -0,0 +1,79 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findGemfile = findGemfile;
exports.isUsingBundlerAsync = isUsingBundlerAsync;
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const fs_1 = __importDefault(require("fs"));
const os_1 = __importDefault(require("os"));
const path_1 = __importDefault(require("path"));
const resolve_workspace_root_1 = require("resolve-workspace-root");
const maybeStat = (targetPath) => fs_1.default.promises.stat(path_1.default.resolve(targetPath)).catch(() => null);
async function getGitTopLevelAsync(root = process.cwd()) {
try {
const output = await (0, spawn_async_1.default)('git', ['rev-parse', '--show-toplevel'], { cwd: root });
const gitRoot = path_1.default.resolve(output.stdout.trim());
return (await maybeStat(gitRoot))?.isDirectory() ? gitRoot : null;
}
catch {
return null;
}
}
async function getGitRootPathAsync(root = process.cwd()) {
const homeDir = path_1.default.normalize(os_1.default.homedir());
for (let dir = root; path_1.default.dirname(dir) !== dir; dir = path_1.default.dirname(dir)) {
if ((await maybeStat(path_1.default.join(dir, '.git')))?.isDirectory()) {
return dir;
}
if (dir === homeDir)
break;
}
return null;
}
async function getRootPathAsync(root = process.cwd()) {
const rootPath = (await getGitTopLevelAsync(root)) ||
(await getGitRootPathAsync(root)) ||
(await (0, resolve_workspace_root_1.resolveWorkspaceRootAsync)(root)) ||
process.cwd();
return rootPath;
}
/** Finds a Gemfile in the target directory or a parent, stopping at the Git or workspace root. */
async function findGemfile(root = process.cwd()) {
const rootBoundary = path_1.default.normalize(await getRootPathAsync(root));
for (let dir = root; path_1.default.dirname(dir) !== dir; dir = path_1.default.dirname(dir)) {
const candidate = path_1.default.join(dir, 'Gemfile');
if ((await maybeStat(candidate))?.isFile()) {
return candidate;
}
if (dir === rootBoundary)
break;
}
return null;
}
/**
* Check if the project uses Bundler to manage CocoaPods.
* Returns `true` if a `Gemfile` exists in the project root (or a parent)
* that lists `cocoapods` as a dependency, and `bundle exec pod --version` succeeds.
*/
async function isUsingBundlerAsync(projectRoot) {
const gemfilePath = await findGemfile(projectRoot);
if (!gemfilePath) {
return false;
}
const [gemfileContents, podExec] = await Promise.allSettled([
fs_1.default.promises.readFile(gemfilePath, 'utf8'),
(0, spawn_async_1.default)('bundle', ['exec', 'pod', '--version'], {
cwd: projectRoot,
stdio: 'ignore',
}),
]);
if (gemfileContents.status === 'rejected' || podExec.status === 'rejected') {
return false;
}
else {
return /gem\s*\(?\s*(?:'cocoapods'|"cocoapods")/.test(gemfileContents.value);
}
}
//# sourceMappingURL=gemfile.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"gemfile.js","sourceRoot":"","sources":["../../src/ios/gemfile.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,33 @@
import type { SpawnOptions, SpawnPromise, SpawnResult } from '@expo/spawn-async';
import type { PackageManager, PackageManagerOptions } from '../PackageManager';
import type { PendingSpawnPromise } from '../utils/spawn';
export declare abstract class BasePackageManager implements PackageManager {
readonly silent: boolean;
readonly log?: (...args: any) => void;
readonly options: PackageManagerOptions;
constructor({ silent, log, env, ...options }?: PackageManagerOptions);
/** Get the name of the package manager */
abstract readonly name: string;
/** Get the executable binary of the package manager */
abstract readonly bin: string;
/** Get the lockfile for this package manager */
abstract readonly lockFile: string;
/** Get the default environment variables used when running the package manager. */
protected getDefaultEnvironment(): Record<string, string>;
abstract addAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
abstract addDevAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
abstract addGlobalAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
abstract removeAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
abstract removeDevAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
abstract removeGlobalAsync(namesOrFlags: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
abstract workspaceRoot(): PackageManager | null;
/** Ensure the CWD is set to a non-empty string */
protected ensureCwdDefined(method?: string): string;
runAsync(command: string[], options?: SpawnOptions): SpawnPromise<SpawnResult>;
runBinAsync(command: string[], options?: SpawnOptions): SpawnPromise<SpawnResult>;
versionAsync(): Promise<string>;
getConfigAsync(key: string): Promise<string>;
removeLockfileAsync(): Promise<void>;
installAsync(flags?: string[]): SpawnPromise<SpawnResult> | PendingSpawnPromise<SpawnResult>;
uninstallAsync(): Promise<void>;
}

View File

@@ -0,0 +1,70 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BasePackageManager = void 0;
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const assert_1 = __importDefault(require("assert"));
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
class BasePackageManager {
silent;
log;
options;
constructor({ silent, log, env = process.env, ...options } = {}) {
this.silent = !!silent;
this.log = log ?? (!silent ? console.log : undefined);
this.options = {
stdio: silent ? undefined : 'inherit',
...options,
env: { ...this.getDefaultEnvironment(), ...env },
};
}
/** Get the default environment variables used when running the package manager. */
getDefaultEnvironment() {
return {
ADBLOCK: '1',
DISABLE_OPENCOLLECTIVE: '1',
};
}
/** Ensure the CWD is set to a non-empty string */
ensureCwdDefined(method) {
const cwd = this.options.cwd?.toString();
const className = this.constructor.name;
const methodName = method ? `.${method}` : '';
(0, assert_1.default)(cwd, `cwd is required for ${className}${methodName}`);
return cwd;
}
runAsync(command, options = {}) {
this.log?.(`> ${this.name} ${command.join(' ')}`);
return (0, spawn_async_1.default)(this.bin, command, { ...this.options, ...options });
}
runBinAsync(command, options = {}) {
this.log?.(`> ${this.name} ${command.join(' ')}`);
return (0, spawn_async_1.default)(this.bin, command, { ...this.options, ...options });
}
async versionAsync() {
const { stdout } = await this.runAsync(['--version'], { stdio: undefined });
return stdout.trim();
}
async getConfigAsync(key) {
const { stdout } = await this.runAsync(['config', 'get', key]);
return stdout.trim();
}
async removeLockfileAsync() {
const cwd = this.ensureCwdDefined('removeLockFile');
const filePath = path_1.default.join(cwd, this.lockFile);
await fs_1.default.promises.rm(filePath, { force: true });
}
installAsync(flags = []) {
return this.runAsync(['install', ...flags]);
}
async uninstallAsync() {
const cwd = this.ensureCwdDefined('uninstallAsync');
const modulesPath = path_1.default.join(cwd, 'node_modules');
await fs_1.default.promises.rm(modulesPath, { force: true, recursive: true });
}
}
exports.BasePackageManager = BasePackageManager;
//# sourceMappingURL=BasePackageManager.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BasePackageManager.js","sourceRoot":"","sources":["../../src/node/BasePackageManager.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,14 @@
import { BasePackageManager } from './BasePackageManager';
export declare class BunPackageManager extends BasePackageManager {
readonly name = "bun";
readonly bin = "bun";
get lockFile(): "bun.lockb" | "bun.lock";
workspaceRoot(): BunPackageManager | null;
installAsync(namesOrFlags?: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
addAsync(namesOrFlags?: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
addDevAsync(namesOrFlags?: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
addGlobalAsync(namesOrFlags?: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
removeAsync(namesOrFlags: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
removeDevAsync(namesOrFlags: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
removeGlobalAsync(namesOrFlags: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
}

View File

@@ -0,0 +1,62 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BunPackageManager = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const BasePackageManager_1 = require("./BasePackageManager");
const nodeManagers_1 = require("../utils/nodeManagers");
class BunPackageManager extends BasePackageManager_1.BasePackageManager {
name = 'bun';
bin = 'bun';
get lockFile() {
const cwd = this.options.cwd?.toString() || process.cwd();
return fs_1.default.existsSync(path_1.default.join(cwd, nodeManagers_1.BUN_LOCK_FILE)) ? nodeManagers_1.BUN_LOCK_FILE : nodeManagers_1.BUN_TEXT_LOCK_FILE;
}
workspaceRoot() {
const root = (0, nodeManagers_1.resolveWorkspaceRoot)(this.ensureCwdDefined('workspaceRoot'));
if (root) {
return new BunPackageManager({
...this.options,
silent: this.silent,
log: this.log,
cwd: root,
});
}
return null;
}
installAsync(namesOrFlags = []) {
return this.runAsync(['install', ...namesOrFlags]);
}
addAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
return this.runAsync(['add', ...namesOrFlags]);
}
addDevAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
return this.runAsync(['add', '--dev', ...namesOrFlags]);
}
addGlobalAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
return this.runAsync(['add', '--global', ...namesOrFlags]);
}
removeAsync(namesOrFlags) {
return this.runAsync(['remove', ...namesOrFlags]);
}
removeDevAsync(namesOrFlags) {
return this.runAsync(['remove', ...namesOrFlags]);
}
removeGlobalAsync(namesOrFlags) {
return this.runAsync(['remove', '--global', ...namesOrFlags]);
}
}
exports.BunPackageManager = BunPackageManager;
//# sourceMappingURL=BunPackageManager.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"BunPackageManager.js","sourceRoot":"","sources":["../../src/node/BunPackageManager.ts"],"names":[],"mappings":";;;;;;AAAA,4CAAoB;AACpB,gDAAwB;AAExB,6DAA0D;AAC1D,wDAAgG;AAEhG,MAAa,iBAAkB,SAAQ,uCAAkB;IAC9C,IAAI,GAAG,KAAK,CAAC;IACb,GAAG,GAAG,KAAK,CAAC;IACrB,IAAI,QAAQ;QACV,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAC1D,OAAO,YAAE,CAAC,UAAU,CAAC,cAAI,CAAC,IAAI,CAAC,GAAG,EAAE,4BAAa,CAAC,CAAC,CAAC,CAAC,CAAC,4BAAa,CAAC,CAAC,CAAC,iCAAkB,CAAC;IAC3F,CAAC;IAED,aAAa;QACX,MAAM,IAAI,GAAG,IAAA,mCAAoB,EAAC,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC,CAAC;QAC1E,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,IAAI,iBAAiB,CAAC;gBAC3B,GAAG,IAAI,CAAC,OAAO;gBACf,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,GAAG,EAAE,IAAI;aACV,CAAC,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAY,CAAC,eAAyB,EAAE;QACtC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,QAAQ,CAAC,eAAyB,EAAE;QAClC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,WAAW,CAAC,eAAyB,EAAE;QACrC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,cAAc,CAAC,eAAyB,EAAE;QACxC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,WAAW,CAAC,YAAsB;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,cAAc,CAAC,YAAsB;QACnC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,iBAAiB,CAAC,YAAsB;QACtC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IAChE,CAAC;CACF;AA7DD,8CA6DC"}

View File

@@ -0,0 +1,29 @@
import type { SpawnOptions } from '@expo/spawn-async';
import spawnAsync from '@expo/spawn-async';
import { BasePackageManager } from './BasePackageManager';
export declare class NpmPackageManager extends BasePackageManager {
readonly name = "npm";
readonly bin = "npm";
readonly lockFile = "package-lock.json";
workspaceRoot(): NpmPackageManager | null;
addAsync(namesOrFlags?: string[]): spawnAsync.SpawnPromise<spawnAsync.SpawnResult> | import("../utils/spawn").PendingSpawnPromise<spawnAsync.SpawnResult>;
addDevAsync(namesOrFlags?: string[]): spawnAsync.SpawnPromise<spawnAsync.SpawnResult> | import("../utils/spawn").PendingSpawnPromise<spawnAsync.SpawnResult>;
addGlobalAsync(namesOrFlags?: string[]): spawnAsync.SpawnPromise<spawnAsync.SpawnResult> | import("../utils/spawn").PendingSpawnPromise<spawnAsync.SpawnResult>;
removeAsync(namesOrFlags: string[]): spawnAsync.SpawnPromise<spawnAsync.SpawnResult>;
removeDevAsync(namesOrFlags: string[]): spawnAsync.SpawnPromise<spawnAsync.SpawnResult>;
removeGlobalAsync(namesOrFlags: string[]): spawnAsync.SpawnPromise<spawnAsync.SpawnResult>;
runBinAsync(command: string[], options?: SpawnOptions): spawnAsync.SpawnPromise<spawnAsync.SpawnResult>;
/**
* Parse all package specifications from the names or flag list.
* The result from this method can be used for `.updatePackageFileAsync`.
*/
private parsePackageSpecs;
/** Sort dependencies by keys (case-insensitive, stable). Sorting algorithm is taken from https://github.com/npm/package-json/blob/f5db81bdfbba5e9d3bfc0732f8bfe511825a20aa/lib/update-dependencies.js#L9 */
private orderDependencies;
/**
* Older npm versions have issues with mismatched nested dependencies when adding exact versions.
* This propagates as issues like mismatched `@expo/config-pugins` versions.
* As a workaround, we update the `package.json` directly and run `npm install`.
*/
private updatePackageFileAsync;
}

View File

@@ -0,0 +1,135 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NpmPackageManager = void 0;
const json_file_1 = __importDefault(require("@expo/json-file"));
const spawn_async_1 = __importDefault(require("@expo/spawn-async"));
const npm_package_arg_1 = __importDefault(require("npm-package-arg"));
const path_1 = __importDefault(require("path"));
const BasePackageManager_1 = require("./BasePackageManager");
const nodeManagers_1 = require("../utils/nodeManagers");
const spawn_1 = require("../utils/spawn");
class NpmPackageManager extends BasePackageManager_1.BasePackageManager {
name = 'npm';
bin = 'npm';
lockFile = nodeManagers_1.NPM_LOCK_FILE;
workspaceRoot() {
const root = (0, nodeManagers_1.resolveWorkspaceRoot)(this.ensureCwdDefined('workspaceRoot'));
if (root) {
return new NpmPackageManager({
...this.options,
silent: this.silent,
log: this.log,
cwd: root,
});
}
return null;
}
addAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
const { flags, versioned, unversioned } = this.parsePackageSpecs(namesOrFlags);
return (0, spawn_1.createPendingSpawnAsync)(() => this.updatePackageFileAsync(versioned, 'dependencies'), () => !unversioned.length
? this.runAsync(['install', ...flags])
: this.runAsync(['install', '--save', ...flags, ...unversioned.map((spec) => spec.raw)]));
}
addDevAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
const { flags, versioned, unversioned } = this.parsePackageSpecs(namesOrFlags);
return (0, spawn_1.createPendingSpawnAsync)(() => this.updatePackageFileAsync(versioned, 'devDependencies'), () => !unversioned.length
? this.runAsync(['install', ...flags])
: this.runAsync([
'install',
'--save-dev',
...flags,
...unversioned.map((spec) => spec.raw),
]));
}
addGlobalAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
return this.runAsync(['install', '--global', ...namesOrFlags]);
}
removeAsync(namesOrFlags) {
return this.runAsync(['uninstall', ...namesOrFlags]);
}
removeDevAsync(namesOrFlags) {
return this.runAsync(['uninstall', '--save-dev', ...namesOrFlags]);
}
removeGlobalAsync(namesOrFlags) {
return this.runAsync(['uninstall', '--global', ...namesOrFlags]);
}
runBinAsync(command, options = {}) {
this.log?.(`> npx ${command.join(' ')}`);
return (0, spawn_async_1.default)('npx', command, { ...this.options, ...options });
}
/**
* Parse all package specifications from the names or flag list.
* The result from this method can be used for `.updatePackageFileAsync`.
*/
parsePackageSpecs(namesOrFlags) {
const result = { flags: [], versioned: [], unversioned: [] };
namesOrFlags
.map((name) => {
if (name.trim().startsWith('-')) {
result.flags.push(name);
return null;
}
return (0, npm_package_arg_1.default)(name);
})
.forEach((spec) => {
// When using a dist-tag version of a library, we need to consider it as "unversioned".
// Doing so will install that version with `npm install --save(-dev)`, and resolve the dist-tag properly.
const hasExactSpec = !!spec && spec.rawSpec !== '' && spec.rawSpec !== '*';
if (spec && hasExactSpec && spec.type !== 'tag') {
result.versioned.push(spec);
}
else if (spec) {
result.unversioned.push(spec);
}
});
return result;
}
/** Sort dependencies by keys (case-insensitive, stable). Sorting algorithm is taken from https://github.com/npm/package-json/blob/f5db81bdfbba5e9d3bfc0732f8bfe511825a20aa/lib/update-dependencies.js#L9 */
orderDependencies(deps) {
if (!deps) {
return {};
}
const sorted = Object.keys(deps)
.sort((a, b) => a.localeCompare(b, 'en'))
.reduce((res, key) => {
const dep = deps[key];
if (dep != null) {
res[key] = dep;
}
return res;
}, {});
return sorted;
}
/**
* Older npm versions have issues with mismatched nested dependencies when adding exact versions.
* This propagates as issues like mismatched `@expo/config-pugins` versions.
* As a workaround, we update the `package.json` directly and run `npm install`.
*/
async updatePackageFileAsync(packageSpecs, packageType) {
if (!packageSpecs.length) {
return;
}
const pkgPath = path_1.default.join(this.options.cwd?.toString() || '.', 'package.json');
const pkg = await json_file_1.default.readAsync(pkgPath);
packageSpecs.forEach((spec) => {
pkg[packageType] = pkg[packageType] || {};
pkg[packageType][spec.name] = spec.rawSpec;
});
pkg[packageType] = this.orderDependencies(pkg[packageType]);
await json_file_1.default.writeAsync(pkgPath, pkg, { json5: false });
}
}
exports.NpmPackageManager = NpmPackageManager;
//# sourceMappingURL=NpmPackageManager.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"NpmPackageManager.js","sourceRoot":"","sources":["../../src/node/NpmPackageManager.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,14 @@
import { BasePackageManager } from './BasePackageManager';
export declare class PnpmPackageManager extends BasePackageManager {
readonly name = "pnpm";
readonly bin = "pnpm";
readonly lockFile = "pnpm-lock.yaml";
workspaceRoot(): PnpmPackageManager | null;
installAsync(namesOrFlags?: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
addAsync(namesOrFlags?: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
addDevAsync(namesOrFlags?: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
addGlobalAsync(namesOrFlags?: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
removeAsync(namesOrFlags: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
removeDevAsync(namesOrFlags: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
removeGlobalAsync(namesOrFlags: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
}

View File

@@ -0,0 +1,61 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PnpmPackageManager = void 0;
const BasePackageManager_1 = require("./BasePackageManager");
const env_1 = __importDefault(require("../utils/env"));
const nodeManagers_1 = require("../utils/nodeManagers");
class PnpmPackageManager extends BasePackageManager_1.BasePackageManager {
name = 'pnpm';
bin = 'pnpm';
lockFile = nodeManagers_1.PNPM_LOCK_FILE;
workspaceRoot() {
const root = (0, nodeManagers_1.resolveWorkspaceRoot)(this.ensureCwdDefined('workspaceRoot'));
if (root) {
return new PnpmPackageManager({
...this.options,
silent: this.silent,
log: this.log,
cwd: root,
});
}
return null;
}
installAsync(namesOrFlags = []) {
if (env_1.default.CI && !namesOrFlags.join(' ').includes('frozen-lockfile')) {
namesOrFlags.unshift('--no-frozen-lockfile');
}
return this.runAsync(['install', ...namesOrFlags]);
}
addAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
return this.runAsync(['add', ...namesOrFlags]);
}
addDevAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
return this.runAsync(['add', '--save-dev', ...namesOrFlags]);
}
addGlobalAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
return this.runAsync(['add', '--global', ...namesOrFlags]);
}
removeAsync(namesOrFlags) {
return this.runAsync(['remove', ...namesOrFlags]);
}
removeDevAsync(namesOrFlags) {
return this.runAsync(['remove', '--save-dev', ...namesOrFlags]);
}
removeGlobalAsync(namesOrFlags) {
return this.runAsync(['remove', '--global', ...namesOrFlags]);
}
}
exports.PnpmPackageManager = PnpmPackageManager;
//# sourceMappingURL=PnpmPackageManager.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PnpmPackageManager.js","sourceRoot":"","sources":["../../src/node/PnpmPackageManager.ts"],"names":[],"mappings":";;;;;;AAAA,6DAA0D;AAC1D,uDAA+B;AAC/B,wDAA6E;AAE7E,MAAa,kBAAmB,SAAQ,uCAAkB;IAC/C,IAAI,GAAG,MAAM,CAAC;IACd,GAAG,GAAG,MAAM,CAAC;IACb,QAAQ,GAAG,6BAAc,CAAC;IAEnC,aAAa;QACX,MAAM,IAAI,GAAG,IAAA,mCAAoB,EAAC,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC,CAAC;QAC1E,IAAI,IAAI,EAAE,CAAC;YACT,OAAO,IAAI,kBAAkB,CAAC;gBAC5B,GAAG,IAAI,CAAC,OAAO;gBACf,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,GAAG,EAAE,IAAI;aACV,CAAC,CAAC;QACL,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,YAAY,CAAC,eAAyB,EAAE;QACtC,IAAI,aAAG,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;YAClE,YAAY,CAAC,OAAO,CAAC,sBAAsB,CAAC,CAAC;QAC/C,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,SAAS,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,QAAQ,CAAC,eAAyB,EAAE;QAClC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,WAAW,CAAC,eAAyB,EAAE;QACrC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,cAAc,CAAC,eAAyB,EAAE;QACxC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YACzB,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IAC7D,CAAC;IAED,WAAW,CAAC,YAAsB;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,cAAc,CAAC,YAAsB;QACnC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,iBAAiB,CAAC,YAAsB;QACtC,OAAO,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,UAAU,EAAE,GAAG,YAAY,CAAC,CAAC,CAAC;IAChE,CAAC;CACF;AA9DD,gDA8DC"}

View File

@@ -0,0 +1,16 @@
import { BasePackageManager } from './BasePackageManager';
export declare class YarnPackageManager extends BasePackageManager {
readonly name = "yarn";
readonly bin = "yarnpkg";
readonly lockFile = "yarn.lock";
/** Check if Yarn is running in offline mode, and add the `--offline` flag */
private withOfflineFlagAsync;
workspaceRoot(): YarnPackageManager | null;
installAsync(flags?: string[]): import("../utils/spawn").PendingSpawnPromise<import("@expo/spawn-async").SpawnResult>;
addAsync(namesOrFlags?: string[]): import("../utils/spawn").PendingSpawnPromise<import("@expo/spawn-async").SpawnResult>;
addDevAsync(namesOrFlags?: string[]): import("../utils/spawn").PendingSpawnPromise<import("@expo/spawn-async").SpawnResult>;
addGlobalAsync(namesOrFlags?: string[]): import("../utils/spawn").PendingSpawnPromise<import("@expo/spawn-async").SpawnResult>;
removeAsync(namesOrFlags: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
removeDevAsync(namesOrFlags: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
removeGlobalAsync(namesOrFlags: string[]): import("@expo/spawn-async").SpawnPromise<import("@expo/spawn-async").SpawnResult>;
}

View File

@@ -0,0 +1,60 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.YarnPackageManager = void 0;
const BasePackageManager_1 = require("./BasePackageManager");
const nodeManagers_1 = require("../utils/nodeManagers");
const spawn_1 = require("../utils/spawn");
const yarn_1 = require("../utils/yarn");
class YarnPackageManager extends BasePackageManager_1.BasePackageManager {
name = 'yarn';
bin = 'yarnpkg';
lockFile = nodeManagers_1.YARN_LOCK_FILE;
/** Check if Yarn is running in offline mode, and add the `--offline` flag */
async withOfflineFlagAsync(namesOrFlags) {
return (await (0, yarn_1.isYarnOfflineAsync)()) ? [...namesOrFlags, '--offline'] : namesOrFlags;
}
workspaceRoot() {
const root = (0, nodeManagers_1.resolveWorkspaceRoot)(this.ensureCwdDefined('workspaceRoot'));
if (root) {
return new YarnPackageManager({
...this.options,
silent: this.silent,
log: this.log,
cwd: root,
});
}
return null;
}
installAsync(flags = []) {
return (0, spawn_1.createPendingSpawnAsync)(() => this.withOfflineFlagAsync(['install']), (args) => this.runAsync([...args, ...flags]));
}
addAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
return (0, spawn_1.createPendingSpawnAsync)(() => this.withOfflineFlagAsync(['add', ...namesOrFlags]), (args) => this.runAsync(args));
}
addDevAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
return (0, spawn_1.createPendingSpawnAsync)(() => this.withOfflineFlagAsync(['add', '--dev', ...namesOrFlags]), (args) => this.runAsync(args));
}
addGlobalAsync(namesOrFlags = []) {
if (!namesOrFlags.length) {
return this.installAsync();
}
return (0, spawn_1.createPendingSpawnAsync)(() => this.withOfflineFlagAsync(['global', 'add', ...namesOrFlags]), (args) => this.runAsync(args));
}
removeAsync(namesOrFlags) {
return this.runAsync(['remove', ...namesOrFlags]);
}
removeDevAsync(namesOrFlags) {
return this.runAsync(['remove', ...namesOrFlags]);
}
removeGlobalAsync(namesOrFlags) {
return this.runAsync(['global', 'remove', ...namesOrFlags]);
}
}
exports.YarnPackageManager = YarnPackageManager;
//# sourceMappingURL=YarnPackageManager.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"YarnPackageManager.js","sourceRoot":"","sources":["../../src/node/YarnPackageManager.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,6 @@
declare class Env {
/** Determine if the package manager is running in a CI environment. */
get CI(): boolean;
}
declare const _default: Env;
export default _default;

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class Env {
/** Determine if the package manager is running in a CI environment. */
get CI() {
// See: https://github.com/ctavan/node-getenv/blob/5b02feebde5d8edd56fff7d81c4b43403a20aff8/index.js#L63-L74
const { CI } = process.env;
return CI?.toLowerCase() === 'true' || CI === '1';
}
}
exports.default = new Env();
//# sourceMappingURL=env.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/utils/env.ts"],"names":[],"mappings":";;AAAA,MAAM,GAAG;IACP,uEAAuE;IACvE,IAAI,EAAE;QACJ,4GAA4G;QAC5G,MAAM,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;QAC3B,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,GAAG,CAAC;IACpD,CAAC;CACF;AAED,kBAAe,IAAI,GAAG,EAAE,CAAC"}

View File

@@ -0,0 +1,27 @@
import type { PackageManagerOptions } from '../PackageManager';
import { BunPackageManager } from '../node/BunPackageManager';
import { NpmPackageManager } from '../node/NpmPackageManager';
import { PnpmPackageManager } from '../node/PnpmPackageManager';
import { YarnPackageManager } from '../node/YarnPackageManager';
export { resolveWorkspaceRoot } from 'resolve-workspace-root';
export type NodePackageManager = NpmPackageManager | PnpmPackageManager | YarnPackageManager | BunPackageManager;
export type NodePackageManagerForProject = PackageManagerOptions & Partial<Record<NodePackageManager['name'], boolean>>;
export declare const NPM_LOCK_FILE = "package-lock.json";
export declare const YARN_LOCK_FILE = "yarn.lock";
export declare const PNPM_LOCK_FILE = "pnpm-lock.yaml";
export declare const BUN_LOCK_FILE = "bun.lockb";
export declare const BUN_TEXT_LOCK_FILE = "bun.lock";
/** The order of the package managers to use when resolving automatically */
export declare const RESOLUTION_ORDER: NodePackageManager['name'][];
/**
* Resolve the used node package manager for a project by checking the lockfile.
* This also tries to resolve the workspace root, if its part of a monorepo.
* Optionally, provide a preferred packager to only resolve that one specifically.
*/
export declare function resolvePackageManager(projectRoot: string, preferredManager?: NodePackageManager['name']): NodePackageManager['name'] | null;
/**
* This creates a Node package manager from the provided options.
* If these options are not provided, it will infer the package manager from lockfiles.
* When no package manager is found, it falls back to npm.
*/
export declare function createForProject(projectRoot: string, options?: NodePackageManagerForProject): NodePackageManager;

View File

@@ -0,0 +1,80 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.RESOLUTION_ORDER = exports.BUN_TEXT_LOCK_FILE = exports.BUN_LOCK_FILE = exports.PNPM_LOCK_FILE = exports.YARN_LOCK_FILE = exports.NPM_LOCK_FILE = exports.resolveWorkspaceRoot = void 0;
exports.resolvePackageManager = resolvePackageManager;
exports.createForProject = createForProject;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const resolve_workspace_root_1 = require("resolve-workspace-root");
const BunPackageManager_1 = require("../node/BunPackageManager");
const NpmPackageManager_1 = require("../node/NpmPackageManager");
const PnpmPackageManager_1 = require("../node/PnpmPackageManager");
const YarnPackageManager_1 = require("../node/YarnPackageManager");
var resolve_workspace_root_2 = require("resolve-workspace-root");
Object.defineProperty(exports, "resolveWorkspaceRoot", { enumerable: true, get: function () { return resolve_workspace_root_2.resolveWorkspaceRoot; } });
exports.NPM_LOCK_FILE = 'package-lock.json';
exports.YARN_LOCK_FILE = 'yarn.lock';
exports.PNPM_LOCK_FILE = 'pnpm-lock.yaml';
exports.BUN_LOCK_FILE = 'bun.lockb';
exports.BUN_TEXT_LOCK_FILE = 'bun.lock';
/** The order of the package managers to use when resolving automatically */
exports.RESOLUTION_ORDER = ['bun', 'yarn', 'npm', 'pnpm'];
/**
* Resolve the used node package manager for a project by checking the lockfile.
* This also tries to resolve the workspace root, if its part of a monorepo.
* Optionally, provide a preferred packager to only resolve that one specifically.
*/
function resolvePackageManager(projectRoot, preferredManager) {
const root = (0, resolve_workspace_root_1.resolveWorkspaceRoot)(projectRoot) ?? projectRoot;
const lockFiles = {
npm: [exports.NPM_LOCK_FILE],
pnpm: [exports.PNPM_LOCK_FILE],
yarn: [exports.YARN_LOCK_FILE],
bun: [exports.BUN_TEXT_LOCK_FILE, exports.BUN_LOCK_FILE],
};
if (preferredManager) {
return lockFiles[preferredManager].some((file) => fs_1.default.existsSync(path_1.default.join(root, file)))
? preferredManager
: null;
}
for (const managerName of exports.RESOLUTION_ORDER) {
if (lockFiles[managerName].some((file) => fs_1.default.existsSync(path_1.default.join(root, file)))) {
return managerName;
}
}
return null;
}
/**
* This creates a Node package manager from the provided options.
* If these options are not provided, it will infer the package manager from lockfiles.
* When no package manager is found, it falls back to npm.
*/
function createForProject(projectRoot, options = {}) {
if (options.npm) {
return new NpmPackageManager_1.NpmPackageManager({ cwd: projectRoot, ...options });
}
else if (options.yarn) {
return new YarnPackageManager_1.YarnPackageManager({ cwd: projectRoot, ...options });
}
else if (options.pnpm) {
return new PnpmPackageManager_1.PnpmPackageManager({ cwd: projectRoot, ...options });
}
else if (options.bun) {
return new BunPackageManager_1.BunPackageManager({ cwd: projectRoot, ...options });
}
switch (resolvePackageManager(projectRoot)) {
case 'pnpm':
return new PnpmPackageManager_1.PnpmPackageManager({ cwd: projectRoot, ...options });
case 'yarn':
return new YarnPackageManager_1.YarnPackageManager({ cwd: projectRoot, ...options });
case 'bun':
return new BunPackageManager_1.BunPackageManager({ cwd: projectRoot, ...options });
case 'npm':
default:
return new NpmPackageManager_1.NpmPackageManager({ cwd: projectRoot, ...options });
}
}
//# sourceMappingURL=nodeManagers.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"nodeManagers.js","sourceRoot":"","sources":["../../src/utils/nodeManagers.ts"],"names":[],"mappings":"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"}

View File

@@ -0,0 +1,15 @@
import type { SpawnPromise } from '@expo/spawn-async';
/**
* The pending spawn promise is similar to the spawn promise from `@expo/spawn-async`.
* Instead of the `child` process being available immediately, the `child` is behind another promise.
* We need this to perform async tasks before running the actual spawn promise.
* Use it like: `await manager.installAsync().child`
*/
export interface PendingSpawnPromise<T> extends Promise<T> {
/**
* The child process from the delayed spawn.
* This is `null` whenever the promise before the spawn promise is rejected.
*/
child: Promise<SpawnPromise<T>['child'] | null>;
}
export declare function createPendingSpawnAsync<V, T>(actionAsync: () => Promise<V>, spawnAsync: (result: V) => SpawnPromise<T>): PendingSpawnPromise<T>;

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createPendingSpawnAsync = createPendingSpawnAsync;
function createPendingSpawnAsync(actionAsync, spawnAsync) {
// Manually rsolve the child promise whenever the prepending async action is resolved.
// Avoid `childReject` to prevent "unhandled promise rejection" for one of the two promises.
let childResolve;
const child = new Promise((resolve, reject) => {
childResolve = resolve;
});
const pendingPromise = new Promise((spawnResolve, spawnReject) => {
actionAsync()
.then((result) => {
const spawnPromise = spawnAsync(result);
childResolve(spawnPromise.child);
spawnPromise.then(spawnResolve).catch(spawnReject);
})
.catch((error) => {
childResolve(null);
spawnReject(error);
});
});
pendingPromise.child = child;
return pendingPromise;
}
//# sourceMappingURL=spawn.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../src/utils/spawn.ts"],"names":[],"mappings":";;AAgBA,0DA0BC;AA1BD,SAAgB,uBAAuB,CACrC,WAA6B,EAC7B,UAA0C;IAE1C,sFAAsF;IACtF,4FAA4F;IAC5F,IAAI,YAA8D,CAAC;IACnE,MAAM,KAAK,GAA6C,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACtF,YAAY,GAAG,OAAO,CAAC;IACzB,CAAC,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,IAAI,OAAO,CAAI,CAAC,YAAY,EAAE,WAAW,EAAE,EAAE;QAClE,WAAW,EAAE;aACV,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YACf,MAAM,YAAY,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;YACxC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;YACjC,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACrD,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;YACf,YAAY,CAAC,IAAI,CAAC,CAAC;YACnB,WAAW,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;IAEF,cAAyC,CAAC,KAAK,GAAG,KAAK,CAAC;IACzD,OAAO,cAAwC,CAAC;AAClD,CAAC"}

View File

@@ -0,0 +1,4 @@
/** Determine if you should use yarn offline or not */
export declare function isYarnOfflineAsync(): Promise<boolean>;
/** Exposed for testing */
export declare function getNpmProxy(): string | null;

View File

@@ -0,0 +1,46 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isYarnOfflineAsync = isYarnOfflineAsync;
exports.getNpmProxy = getNpmProxy;
const child_process_1 = require("child_process");
const dns_1 = __importDefault(require("dns"));
const url_1 = __importDefault(require("url"));
/** Determine if you should use yarn offline or not */
async function isYarnOfflineAsync() {
if (await isUrlAvailableAsync('registry.yarnpkg.com')) {
return false;
}
const proxy = getNpmProxy();
if (!proxy) {
return true;
}
const { hostname } = url_1.default.parse(proxy);
if (!hostname) {
return true;
}
return !(await isUrlAvailableAsync(hostname));
}
/** Exposed for testing */
function getNpmProxy() {
if (process.env.https_proxy) {
return process.env.https_proxy ?? null;
}
try {
const httpsProxy = (0, child_process_1.execSync)('npm config get https-proxy').toString().trim();
return httpsProxy !== 'null' ? httpsProxy : null;
}
catch {
return null;
}
}
function isUrlAvailableAsync(url) {
return new Promise((resolve) => {
dns_1.default.lookup(url, (err) => {
resolve(!err);
});
});
}
//# sourceMappingURL=yarn.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"yarn.js","sourceRoot":"","sources":["../../src/utils/yarn.ts"],"names":[],"mappings":";;;;;AAKA,gDAiBC;AAGD,kCAWC;AApCD,iDAAyC;AACzC,8CAAsB;AACtB,8CAAsB;AAEtB,sDAAsD;AAC/C,KAAK,UAAU,kBAAkB;IACtC,IAAI,MAAM,mBAAmB,CAAC,sBAAsB,CAAC,EAAE,CAAC;QACtD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,EAAE,CAAC;IAE5B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,GAAG,aAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,CAAC,CAAC,MAAM,mBAAmB,CAAC,QAAQ,CAAC,CAAC,CAAC;AAChD,CAAC;AAED,0BAA0B;AAC1B,SAAgB,WAAW;IACzB,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;QAC5B,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,IAAI,CAAC;IACzC,CAAC;IAED,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,IAAA,wBAAQ,EAAC,4BAA4B,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QAC5E,OAAO,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAW;IACtC,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;QACtC,aAAG,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE;YACtB,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}

View File

@@ -0,0 +1,22 @@
MIT License
Copyright (c) 2014-present Sebastian McKenzie and other contributors
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,19 @@
# @babel/code-frame
> Generate errors that contain a code frame that point to source locations.
See our website [@babel/code-frame](https://babeljs.io/docs/babel-code-frame) for more information.
## Install
Using npm:
```sh
npm install --save-dev @babel/code-frame
```
or using yarn:
```sh
yarn add @babel/code-frame --dev
```

View File

@@ -0,0 +1,217 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var picocolors = require('picocolors');
var jsTokens = require('js-tokens');
var helperValidatorIdentifier = require('@babel/helper-validator-identifier');
function isColorSupported() {
return (typeof process === "object" && (process.env.FORCE_COLOR === "0" || process.env.FORCE_COLOR === "false") ? false : picocolors.isColorSupported
);
}
const compose = (f, g) => v => f(g(v));
function buildDefs(colors) {
return {
keyword: colors.cyan,
capitalized: colors.yellow,
jsxIdentifier: colors.yellow,
punctuator: colors.yellow,
number: colors.magenta,
string: colors.green,
regex: colors.magenta,
comment: colors.gray,
invalid: compose(compose(colors.white, colors.bgRed), colors.bold),
gutter: colors.gray,
marker: compose(colors.red, colors.bold),
message: compose(colors.red, colors.bold),
reset: colors.reset
};
}
const defsOn = buildDefs(picocolors.createColors(true));
const defsOff = buildDefs(picocolors.createColors(false));
function getDefs(enabled) {
return enabled ? defsOn : defsOff;
}
const sometimesKeywords = new Set(["as", "async", "from", "get", "of", "set"]);
const NEWLINE$1 = /\r\n|[\n\r\u2028\u2029]/;
const BRACKET = /^[()[\]{}]$/;
let tokenize;
const JSX_TAG = /^[a-z][\w-]*$/i;
const getTokenType = function (token, offset, text) {
if (token.type === "name") {
const tokenValue = token.value;
if (helperValidatorIdentifier.isKeyword(tokenValue) || helperValidatorIdentifier.isStrictReservedWord(tokenValue, true) || sometimesKeywords.has(tokenValue)) {
return "keyword";
}
if (JSX_TAG.test(tokenValue) && (text[offset - 1] === "<" || text.slice(offset - 2, offset) === "</")) {
return "jsxIdentifier";
}
const firstChar = String.fromCodePoint(tokenValue.codePointAt(0));
if (firstChar !== firstChar.toLowerCase()) {
return "capitalized";
}
}
if (token.type === "punctuator" && BRACKET.test(token.value)) {
return "bracket";
}
if (token.type === "invalid" && (token.value === "@" || token.value === "#")) {
return "punctuator";
}
return token.type;
};
tokenize = function* (text) {
let match;
while (match = jsTokens.default.exec(text)) {
const token = jsTokens.matchToToken(match);
yield {
type: getTokenType(token, match.index, text),
value: token.value
};
}
};
function highlight(text) {
if (text === "") return "";
const defs = getDefs(true);
let highlighted = "";
for (const {
type,
value
} of tokenize(text)) {
if (type in defs) {
highlighted += value.split(NEWLINE$1).map(str => defs[type](str)).join("\n");
} else {
highlighted += value;
}
}
return highlighted;
}
let deprecationWarningShown = false;
const NEWLINE = /\r\n|[\n\r\u2028\u2029]/;
function getMarkerLines(loc, source, opts, startLineBaseZero) {
const startLoc = Object.assign({
column: 0,
line: -1
}, loc.start);
const endLoc = Object.assign({}, startLoc, loc.end);
const {
linesAbove = 2,
linesBelow = 3
} = opts || {};
const startLine = startLoc.line - startLineBaseZero;
const startColumn = startLoc.column;
const endLine = endLoc.line - startLineBaseZero;
const endColumn = endLoc.column;
let start = Math.max(startLine - (linesAbove + 1), 0);
let end = Math.min(source.length, endLine + linesBelow);
if (startLine === -1) {
start = 0;
}
if (endLine === -1) {
end = source.length;
}
const lineDiff = endLine - startLine;
const markerLines = {};
if (lineDiff) {
for (let i = 0; i <= lineDiff; i++) {
const lineNumber = i + startLine;
if (!startColumn) {
markerLines[lineNumber] = true;
} else if (i === 0) {
const sourceLength = source[lineNumber - 1].length;
markerLines[lineNumber] = [startColumn, sourceLength - startColumn + 1];
} else if (i === lineDiff) {
markerLines[lineNumber] = [0, endColumn];
} else {
const sourceLength = source[lineNumber - i].length;
markerLines[lineNumber] = [0, sourceLength];
}
}
} else {
if (startColumn === endColumn) {
if (startColumn) {
markerLines[startLine] = [startColumn, 0];
} else {
markerLines[startLine] = true;
}
} else {
markerLines[startLine] = [startColumn, endColumn - startColumn];
}
}
return {
start,
end,
markerLines
};
}
function codeFrameColumns(rawLines, loc, opts = {}) {
const shouldHighlight = opts.forceColor || isColorSupported() && opts.highlightCode;
const startLineBaseZero = (opts.startLine || 1) - 1;
const defs = getDefs(shouldHighlight);
const lines = rawLines.split(NEWLINE);
const {
start,
end,
markerLines
} = getMarkerLines(loc, lines, opts, startLineBaseZero);
const hasColumns = loc.start && typeof loc.start.column === "number";
const numberMaxWidth = String(end + startLineBaseZero).length;
const highlightedLines = shouldHighlight ? highlight(rawLines) : rawLines;
let frame = highlightedLines.split(NEWLINE, end).slice(start, end).map((line, index) => {
const number = start + 1 + index;
const paddedNumber = ` ${number + startLineBaseZero}`.slice(-numberMaxWidth);
const gutter = ` ${paddedNumber} |`;
const hasMarker = markerLines[number];
const lastMarkerLine = !markerLines[number + 1];
if (hasMarker) {
let markerLine = "";
if (Array.isArray(hasMarker)) {
const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).replace(/[^\t]/g, " ");
const numberOfMarkers = hasMarker[1] || 1;
markerLine = ["\n ", defs.gutter(gutter.replace(/\d/g, " ")), " ", markerSpacing, defs.marker("^").repeat(numberOfMarkers)].join("");
if (lastMarkerLine && opts.message) {
markerLine += " " + defs.message(opts.message);
}
}
return [defs.marker(">"), defs.gutter(gutter), line.length > 0 ? ` ${line}` : "", markerLine].join("");
} else {
return ` ${defs.gutter(gutter)}${line.length > 0 ? ` ${line}` : ""}`;
}
}).join("\n");
if (opts.message && !hasColumns) {
frame = `${" ".repeat(numberMaxWidth + 1)}${opts.message}\n${frame}`;
}
if (shouldHighlight) {
return defs.reset(frame);
} else {
return frame;
}
}
function index (rawLines, lineNumber, colNumber, opts = {}) {
if (!deprecationWarningShown) {
deprecationWarningShown = true;
const message = "Passing lineNumber and colNumber is deprecated to @babel/code-frame. Please use `codeFrameColumns`.";
if (process.emitWarning) {
process.emitWarning(message, "DeprecationWarning");
} else {
const deprecationError = new Error(message);
deprecationError.name = "DeprecationWarning";
console.warn(new Error(message));
}
}
colNumber = Math.max(colNumber, 0);
const location = {
start: {
column: colNumber,
line: lineNumber
}
};
return codeFrameColumns(rawLines, location, opts);
}
exports.codeFrameColumns = codeFrameColumns;
exports.default = index;
exports.highlight = highlight;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,32 @@
{
"name": "@babel/code-frame",
"version": "7.29.7",
"description": "Generate errors that contain a code frame that point to source locations.",
"author": "The Babel Team (https://babel.dev/team)",
"homepage": "https://babel.dev/docs/en/next/babel-code-frame",
"bugs": "https://github.com/babel/babel/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/babel/babel.git",
"directory": "packages/babel-code-frame"
},
"main": "./lib/index.js",
"dependencies": {
"@babel/helper-validator-identifier": "^7.29.7",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
"devDependencies": {
"charcodes": "^0.2.0",
"import-meta-resolve": "^4.1.0",
"strip-ansi": "^4.0.0"
},
"engines": {
"node": ">=6.9.0"
},
"type": "commonjs"
}

View File

@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,29 @@
<!-- Title -->
<h1 align="center">
👋 Welcome to <br><code>@expo/json-file</code>
</h1>
<p align="center">A library for reading and writing JSON files.</p>
<!-- Body -->
## 🏁 Setup
Install `@expo/json-file` in your project.
```sh
yarn add @expo/json-file
```
## ⚽️ Usage
```ts
import JsonFile, { JSONObject } from '@expo/json-file';
// Create a file instance
const jsonFile = new JsonFile<JSONObject>(filePath);
// Interact with the file
await jsonFile.readAsync();
await jsonFile.writeAsync({ some: 'data' });
```

View File

@@ -0,0 +1,84 @@
import fs from 'node:fs';
export type JSONValue = boolean | number | string | null | JSONArray | JSONObject;
export interface JSONArray extends Array<JSONValue> {
}
export interface JSONObject {
[key: string]: JSONValue | undefined;
}
type Defined<T> = T extends undefined ? never : T;
type Options<TJSONObject extends JSONObject> = {
badJsonDefault?: TJSONObject;
jsonParseErrorDefault?: TJSONObject;
cantReadFileDefault?: TJSONObject;
ensureDir?: boolean;
mode?: fs.Mode;
default?: TJSONObject;
json5?: boolean;
space?: number;
addNewLineAtEOF?: boolean;
};
/**
* The JsonFile class represents the contents of json file.
*
* It's polymorphic on "JSONObject", which is a simple type representing
* and object with string keys and either objects or primitive types as values.
* @type {[type]}
*/
export default class JsonFile<TJSONObject extends JSONObject> {
file: string;
options: Options<TJSONObject>;
static read: typeof read;
static readAsync: typeof readAsync;
static parseJsonString: typeof parseJsonString;
static write: typeof write;
static writeAsync: typeof writeAsync;
static get: typeof getSync;
static getAsync: typeof getAsync;
static set: typeof setSync;
static setAsync: typeof setAsync;
static merge: typeof merge;
static mergeAsync: typeof mergeAsync;
static deleteKey: typeof deleteKey;
static deleteKeyAsync: typeof deleteKeyAsync;
static deleteKeys: typeof deleteKeys;
static deleteKeysAsync: typeof deleteKeysAsync;
static rewrite: typeof rewrite;
static rewriteAsync: typeof rewriteAsync;
constructor(file: string, options?: Options<TJSONObject>);
read(options?: Options<TJSONObject>): TJSONObject;
readAsync(options?: Options<TJSONObject>): Promise<TJSONObject>;
write(object: TJSONObject, options?: Options<TJSONObject>): TJSONObject;
writeAsync(object: TJSONObject, options?: Options<TJSONObject>): Promise<TJSONObject>;
parseJsonString(json: string, options?: Options<TJSONObject>): TJSONObject;
get<K extends keyof TJSONObject, TDefault extends TJSONObject[K] | null>(key: K, defaultValue: TDefault, options?: Options<TJSONObject>): Defined<TJSONObject[K]> | TDefault;
getAsync<K extends keyof TJSONObject, TDefault extends TJSONObject[K] | null>(key: K, defaultValue: TDefault, options?: Options<TJSONObject>): Promise<Defined<TJSONObject[K]> | TDefault>;
set(key: string, value: unknown, options?: Options<TJSONObject>): TJSONObject;
setAsync(key: string, value: unknown, options?: Options<TJSONObject>): Promise<TJSONObject>;
merge(sources: Partial<TJSONObject> | Partial<TJSONObject>[], options?: Options<TJSONObject>): Promise<TJSONObject>;
mergeAsync(sources: Partial<TJSONObject> | Partial<TJSONObject>[], options?: Options<TJSONObject>): Promise<TJSONObject>;
deleteKey(key: string, options?: Options<TJSONObject>): TJSONObject;
deleteKeyAsync(key: string, options?: Options<TJSONObject>): Promise<TJSONObject>;
deleteKeys(keys: string[], options?: Options<TJSONObject>): TJSONObject;
deleteKeysAsync(keys: string[], options?: Options<TJSONObject>): Promise<TJSONObject>;
rewrite(options?: Options<TJSONObject>): TJSONObject;
rewriteAsync(options?: Options<TJSONObject>): Promise<TJSONObject>;
_getOptions(options?: Options<TJSONObject>): Options<TJSONObject>;
}
declare function read<TJSONObject extends JSONObject>(file: string, options?: Options<TJSONObject>): TJSONObject;
declare function readAsync<TJSONObject extends JSONObject>(file: string, options?: Options<TJSONObject>): Promise<TJSONObject>;
declare function parseJsonString<TJSONObject extends JSONObject>(json: string, options?: Options<TJSONObject>, fileName?: string): TJSONObject;
declare function getSync<TJSONObject extends JSONObject, K extends keyof TJSONObject, DefaultValue>(file: string, key: K, defaultValue: DefaultValue, options?: Options<TJSONObject>): any;
declare function getAsync<TJSONObject extends JSONObject, K extends keyof TJSONObject, DefaultValue>(file: string, key: K, defaultValue: DefaultValue, options?: Options<TJSONObject>): Promise<any>;
declare function write<TJSONObject extends JSONObject>(file: string, object: TJSONObject, options?: Options<TJSONObject>): TJSONObject;
declare function writeAsync<TJSONObject extends JSONObject>(file: string, object: TJSONObject, options?: Options<TJSONObject>): Promise<TJSONObject>;
declare function setSync<TJSONObject extends JSONObject>(file: string, key: string, value: unknown, options?: Options<TJSONObject>): TJSONObject;
declare function setAsync<TJSONObject extends JSONObject>(file: string, key: string, value: unknown, options?: Options<TJSONObject>): Promise<TJSONObject>;
declare function mergeAsync<TJSONObject extends JSONObject>(file: string, sources: Partial<TJSONObject> | Partial<TJSONObject>[], options?: Options<TJSONObject>): Promise<TJSONObject>;
declare function merge<TJSONObject extends JSONObject>(file: string, sources: Partial<TJSONObject> | Partial<TJSONObject>[], options?: Options<TJSONObject>): TJSONObject;
declare function deleteKeyAsync<TJSONObject extends JSONObject>(file: string, key: string, options?: Options<TJSONObject>): Promise<TJSONObject>;
declare function deleteKey<TJSONObject extends JSONObject>(file: string, key: string, options?: Options<TJSONObject>): TJSONObject;
declare function deleteKeysAsync<TJSONObject extends JSONObject>(file: string, keys: string[], options?: Options<TJSONObject>): Promise<TJSONObject>;
declare function deleteKeys<TJSONObject extends JSONObject>(file: string, keys: string[], options?: Options<TJSONObject>): TJSONObject;
declare function rewriteAsync<TJSONObject extends JSONObject>(file: string, options?: Options<TJSONObject>): Promise<TJSONObject>;
declare function rewrite<TJSONObject extends JSONObject>(file: string, options?: Options<TJSONObject>): TJSONObject;
export {};

View File

@@ -0,0 +1,393 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const json5_1 = __importDefault(require("json5"));
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const JsonFileError_1 = __importStar(require("./JsonFileError"));
const writeAtomic_1 = require("./writeAtomic");
const DEFAULT_OPTIONS = {
badJsonDefault: undefined,
jsonParseErrorDefault: undefined,
cantReadFileDefault: undefined,
ensureDir: false,
mode: undefined,
default: undefined,
json5: false,
space: 2,
addNewLineAtEOF: true,
};
/**
* The JsonFile class represents the contents of json file.
*
* It's polymorphic on "JSONObject", which is a simple type representing
* and object with string keys and either objects or primitive types as values.
* @type {[type]}
*/
class JsonFile {
file;
options;
static read = read;
static readAsync = readAsync;
static parseJsonString = parseJsonString;
static write = write;
static writeAsync = writeAsync;
static get = getSync;
static getAsync = getAsync;
static set = setSync;
static setAsync = setAsync;
static merge = merge;
static mergeAsync = mergeAsync;
static deleteKey = deleteKey;
static deleteKeyAsync = deleteKeyAsync;
static deleteKeys = deleteKeys;
static deleteKeysAsync = deleteKeysAsync;
static rewrite = rewrite;
static rewriteAsync = rewriteAsync;
constructor(file, options = {}) {
this.file = file;
this.options = options;
}
read(options) {
return read(this.file, this._getOptions(options));
}
async readAsync(options) {
return readAsync(this.file, this._getOptions(options));
}
write(object, options) {
return write(this.file, object, this._getOptions(options));
}
async writeAsync(object, options) {
return writeAsync(this.file, object, this._getOptions(options));
}
parseJsonString(json, options) {
return parseJsonString(json, options);
}
get(key, defaultValue, options) {
return getSync(this.file, key, defaultValue, this._getOptions(options));
}
async getAsync(key, defaultValue, options) {
return getAsync(this.file, key, defaultValue, this._getOptions(options));
}
set(key, value, options) {
return setSync(this.file, key, value, this._getOptions(options));
}
async setAsync(key, value, options) {
return setAsync(this.file, key, value, this._getOptions(options));
}
async merge(sources, options) {
return merge(this.file, sources, this._getOptions(options));
}
async mergeAsync(sources, options) {
return mergeAsync(this.file, sources, this._getOptions(options));
}
deleteKey(key, options) {
return deleteKey(this.file, key, this._getOptions(options));
}
async deleteKeyAsync(key, options) {
return deleteKeyAsync(this.file, key, this._getOptions(options));
}
deleteKeys(keys, options) {
return deleteKeys(this.file, keys, this._getOptions(options));
}
async deleteKeysAsync(keys, options) {
return deleteKeysAsync(this.file, keys, this._getOptions(options));
}
rewrite(options) {
return rewrite(this.file, this._getOptions(options));
}
async rewriteAsync(options) {
return rewriteAsync(this.file, this._getOptions(options));
}
_getOptions(options) {
return {
...this.options,
...options,
};
}
}
exports.default = JsonFile;
function read(file, options) {
let json;
try {
json = node_fs_1.default.readFileSync(file, 'utf8');
}
catch (error) {
assertEmptyJsonString(json, file);
const defaultValue = cantReadFileDefault(options);
if (defaultValue === undefined) {
throw new JsonFileError_1.default(`Can't read JSON file: ${file}`, error, error.code, file);
}
else {
return defaultValue;
}
}
return parseJsonString(json, options, file);
}
async function readAsync(file, options) {
let json;
try {
json = await node_fs_1.default.promises.readFile(file, 'utf8');
}
catch (error) {
assertEmptyJsonString(json, file);
const defaultValue = cantReadFileDefault(options);
if (defaultValue === undefined) {
throw new JsonFileError_1.default(`Can't read JSON file: ${file}`, error, error.code);
}
else {
return defaultValue;
}
}
return parseJsonString(json, options);
}
function parseJsonString(json, options, fileName) {
assertEmptyJsonString(json, fileName);
try {
if (_getOption(options, 'json5')) {
return json5_1.default.parse(json);
}
else {
return JSON.parse(json);
}
}
catch (e) {
const defaultValue = jsonParseErrorDefault(options);
if (defaultValue === undefined) {
const location = locationFromSyntaxError(e, json);
if (location) {
const { codeFrameColumns, } = require('@babel/code-frame');
const codeFrame = codeFrameColumns(json, { start: location });
e.codeFrame = codeFrame;
e.message += `\n${codeFrame}`;
}
throw new JsonFileError_1.default(`Error parsing JSON: ${json}`, e, 'EJSONPARSE', fileName);
}
else {
return defaultValue;
}
}
}
function getSync(file, key, defaultValue, options) {
const object = read(file, options);
if (key in object) {
return object[key];
}
if (defaultValue === undefined) {
throw new JsonFileError_1.default(`No value at key path "${String(key)}" in JSON object from: ${file}`);
}
return defaultValue;
}
async function getAsync(file, key, defaultValue, options) {
const object = await readAsync(file, options);
if (key in object) {
return object[key];
}
if (defaultValue === undefined) {
throw new JsonFileError_1.default(`No value at key path "${String(key)}" in JSON object from: ${file}`);
}
return defaultValue;
}
function write(file, object, options) {
if (options?.ensureDir) {
node_fs_1.default.mkdirSync(node_path_1.default.dirname(file), { recursive: true });
}
const space = _getOption(options, 'space');
const json5 = _getOption(options, 'json5');
const addNewLineAtEOF = _getOption(options, 'addNewLineAtEOF');
let json;
try {
if (json5) {
json = json5_1.default.stringify(object, null, space);
}
else {
json = JSON.stringify(object, null, space);
}
}
catch (e) {
throw new JsonFileError_1.default(`Couldn't JSON.stringify object for file: ${file}`, e);
}
const data = addNewLineAtEOF ? `${json}\n` : json;
(0, writeAtomic_1.writeFileAtomicSync)(file, data, { mode: options?.mode });
return object;
}
async function writeAsync(file, object, options) {
if (options?.ensureDir) {
await node_fs_1.default.promises.mkdir(node_path_1.default.dirname(file), { recursive: true });
}
const space = _getOption(options, 'space');
const json5 = _getOption(options, 'json5');
const addNewLineAtEOF = _getOption(options, 'addNewLineAtEOF');
let json;
try {
if (json5) {
json = json5_1.default.stringify(object, null, space);
}
else {
json = JSON.stringify(object, null, space);
}
}
catch (e) {
throw new JsonFileError_1.default(`Couldn't JSON.stringify object for file: ${file}`, e);
}
const data = addNewLineAtEOF ? `${json}\n` : json;
await (0, writeAtomic_1.writeFileAtomic)(file, data, { mode: options?.mode });
return object;
}
function setSync(file, key, value, options) {
// TODO: Consider implementing some kind of locking mechanism, but
// it's not critical for our use case, so we'll leave it out for now
const object = read(file, options);
return write(file, { ...object, [key]: value }, options);
}
async function setAsync(file, key, value, options) {
// TODO: Consider implementing some kind of locking mechanism, but
// it's not critical for our use case, so we'll leave it out for now
const object = await readAsync(file, options);
return writeAsync(file, { ...object, [key]: value }, options);
}
async function mergeAsync(file, sources, options) {
const object = await readAsync(file, options);
if (Array.isArray(sources)) {
Object.assign(object, ...sources);
}
else {
Object.assign(object, sources);
}
return writeAsync(file, object, options);
}
function merge(file, sources, options) {
const object = read(file, options);
if (Array.isArray(sources)) {
Object.assign(object, ...sources);
}
else {
Object.assign(object, sources);
}
return write(file, object, options);
}
async function deleteKeyAsync(file, key, options) {
return deleteKeysAsync(file, [key], options);
}
function deleteKey(file, key, options) {
return deleteKeys(file, [key], options);
}
async function deleteKeysAsync(file, keys, options) {
const object = await readAsync(file, options);
let didDelete = false;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key != null && object.hasOwnProperty(key)) {
delete object[key];
didDelete = true;
}
}
if (didDelete) {
return writeAsync(file, object, options);
}
return object;
}
function deleteKeys(file, keys, options) {
const object = read(file, options);
let didDelete = false;
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
if (key != null && object.hasOwnProperty(key)) {
delete object[key];
didDelete = true;
}
}
if (didDelete) {
return write(file, object, options);
}
return object;
}
async function rewriteAsync(file, options) {
const object = await readAsync(file, options);
return writeAsync(file, object, options);
}
function rewrite(file, options) {
return write(file, read(file, options), options);
}
function jsonParseErrorDefault(options = {}) {
if (options.jsonParseErrorDefault === undefined) {
return options.default;
}
else {
return options.jsonParseErrorDefault;
}
}
function cantReadFileDefault(options = {}) {
if (options.cantReadFileDefault === undefined) {
return options.default;
}
else {
return options.cantReadFileDefault;
}
}
function _getOption(options, field) {
if (options) {
if (options[field] !== undefined) {
return options[field];
}
}
return DEFAULT_OPTIONS[field];
}
function locationFromSyntaxError(error, sourceString) {
// JSON5 SyntaxError has lineNumber and columnNumber.
if ('lineNumber' in error && 'columnNumber' in error) {
return { line: error.lineNumber, column: error.columnNumber };
}
// JSON SyntaxError only includes the index in the message.
const match = /at position (\d+)/.exec(error.message);
if (match && match[1] != null) {
const index = parseInt(match[1], 10);
const lines = sourceString.slice(0, index + 1).split('\n');
const lastLine = lines[lines.length - 1];
if (lastLine != null) {
return { line: lines.length, column: lastLine.length };
}
}
return null;
}
function assertEmptyJsonString(json, file) {
if (json?.trim() === '') {
throw new JsonFileError_1.EmptyJsonFileError(file);
}
}
//# sourceMappingURL=JsonFile.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,13 @@
/**
* Note that instances of this class do NOT pass `instanceof JsonFileError`.
*/
export default class JsonFileError extends Error {
cause: Error | undefined;
code: string | undefined;
fileName: string | undefined;
isJsonFileError: true;
constructor(message: string, cause?: Error, code?: string, fileName?: string);
}
export declare class EmptyJsonFileError extends JsonFileError {
constructor(fileName?: string);
}

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.EmptyJsonFileError = void 0;
/**
* Note that instances of this class do NOT pass `instanceof JsonFileError`.
*/
class JsonFileError extends Error {
cause;
code;
fileName;
isJsonFileError;
constructor(message, cause, code, fileName) {
let fullMessage = message;
if (fileName) {
fullMessage += `\n${cause ? '├' : '└'}─ File: ${fileName}`;
}
if (cause) {
fullMessage += `\n└─ Cause: ${cause.name}: ${cause.message}`;
}
super(fullMessage);
this.name = this.constructor.name;
this.cause = cause;
this.code = code;
this.fileName = fileName;
this.isJsonFileError = true;
}
}
exports.default = JsonFileError;
class EmptyJsonFileError extends JsonFileError {
constructor(fileName) {
super(`Cannot parse an empty JSON string`, undefined, 'EJSONEMPTY', fileName);
}
}
exports.EmptyJsonFileError = EmptyJsonFileError;
//# sourceMappingURL=JsonFileError.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"JsonFileError.js","sourceRoot":"","sources":["../src/JsonFileError.ts"],"names":[],"mappings":";;;AAAA;;GAEG;AACH,MAAqB,aAAc,SAAQ,KAAK;IAC9C,KAAK,CAAoB;IACzB,IAAI,CAAqB;IACzB,QAAQ,CAAqB;IAC7B,eAAe,CAAO;IAEtB,YAAY,OAAe,EAAE,KAAa,EAAE,IAAa,EAAE,QAAiB;QAC1E,IAAI,WAAW,GAAG,OAAO,CAAC;QAC1B,IAAI,QAAQ,EAAE,CAAC;YACb,WAAW,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,WAAW,QAAQ,EAAE,CAAC;QAC7D,CAAC;QACD,IAAI,KAAK,EAAE,CAAC;YACV,WAAW,IAAI,eAAe,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;QAC/D,CAAC;QACD,KAAK,CAAC,WAAW,CAAC,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAClC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;IAC9B,CAAC;CACF;AArBD,gCAqBC;AAED,MAAa,kBAAmB,SAAQ,aAAa;IACnD,YAAY,QAAiB;QAC3B,KAAK,CAAC,mCAAmC,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC;IAChF,CAAC;CACF;AAJD,gDAIC"}

View File

@@ -0,0 +1,6 @@
import * as fs from 'node:fs';
export interface WriteFileAtomicOptions {
mode?: fs.Mode;
}
export declare function writeFileAtomicSync(filename: string, data: string | Buffer, options?: WriteFileAtomicOptions): void;
export declare function writeFileAtomic(filename: string, data: string | Buffer, options?: WriteFileAtomicOptions): Promise<void>;

View File

@@ -0,0 +1,61 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.writeFileAtomicSync = writeFileAtomicSync;
exports.writeFileAtomic = writeFileAtomic;
const node_crypto_1 = require("node:crypto");
const fs = __importStar(require("node:fs"));
function getTarget(filename, data) {
const hash = (0, node_crypto_1.createHash)('sha256').update(data).digest('base64url');
return `${filename}.${hash}`;
}
function writeFileAtomicSync(filename, data, options = {}) {
const tmpfile = getTarget(filename, data);
fs.writeFileSync(tmpfile, data, options.mode !== undefined ? { mode: options.mode } : undefined);
fs.renameSync(tmpfile, filename);
// rename preserves any pre-existing destination mode; chmod after to enforce the requested mode.
if (options.mode !== undefined) {
fs.chmodSync(filename, options.mode);
}
}
async function writeFileAtomic(filename, data, options = {}) {
const tmpfile = getTarget(filename, data);
await fs.promises.writeFile(tmpfile, data, options.mode !== undefined ? { mode: options.mode } : undefined);
await fs.promises.rename(tmpfile, filename);
if (options.mode !== undefined) {
await fs.promises.chmod(filename, options.mode);
}
}
//# sourceMappingURL=writeAtomic.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"writeAtomic.js","sourceRoot":"","sources":["../src/writeAtomic.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAYA,kDAYC;AAED,0CAeC;AAzCD,6CAAyC;AACzC,4CAA8B;AAE9B,SAAS,SAAS,CAAC,QAAgB,EAAE,IAAqB;IACxD,MAAM,IAAI,GAAG,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACnE,OAAO,GAAG,QAAQ,IAAI,IAAI,EAAE,CAAC;AAC/B,CAAC;AAMD,SAAgB,mBAAmB,CACjC,QAAgB,EAChB,IAAqB,EACrB,UAAkC,EAAE;IAEpC,MAAM,OAAO,GAAG,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC1C,EAAE,CAAC,aAAa,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACjG,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IACjC,iGAAiG;IACjG,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/B,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,eAAe,CACnC,QAAgB,EAChB,IAAqB,EACrB,UAAkC,EAAE;IAEpC,MAAM,OAAO,GAAG,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC1C,MAAM,EAAE,CAAC,QAAQ,CAAC,SAAS,CACzB,OAAO,EACP,IAAI,EACJ,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAChE,CAAC;IACF,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC5C,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC;AACH,CAAC"}

View File

@@ -0,0 +1,45 @@
{
"name": "@expo/json-file",
"version": "10.2.0",
"description": "A module for reading, writing, and manipulating JSON files",
"main": "build/JsonFile.js",
"repository": {
"type": "git",
"url": "https://github.com/expo/expo.git",
"directory": "packages/@expo/json-file"
},
"keywords": [
"json"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/expo/expo/issues"
},
"homepage": "https://github.com/expo/expo/tree/main/packages/@expo/json-file#readme",
"files": [
"build"
],
"dependencies": {
"@babel/code-frame": "^7.20.0",
"json5": "^2.2.3"
},
"devDependencies": {
"@types/babel__code-frame": "^7.27.0",
"@types/json5": "^2.2.0",
"@types/node": "^22.14.0",
"memfs": "^3.2.0",
"expo-module-scripts": "56.0.2"
},
"publishConfig": {
"access": "public"
},
"gitHead": "c4c9867a0bcbb188e55ecaec4998e38d33108a5d",
"scripts": {
"build": "expo-module tsc",
"clean": "expo-module clean",
"lint": "expo-module lint",
"test": "expo-module test",
"typecheck": "expo-module typecheck",
"watch": "expo-module tsc --watch --preserveWatchOutput"
}
}

View File

@@ -0,0 +1,13 @@
Copyright (c) 2015, Rebecca Turner
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,133 @@
# hosted-git-info
This will let you identify and transform various git hosts URLs between
protocols. It also can tell you what the URL is for the raw path for
particular file for direct access without git.
## Example
```javascript
const hostedGitInfo = require("hosted-git-info")
const info = hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git", opts)
/* info looks like:
{
type: "github",
domain: "github.com",
user: "npm",
project: "hosted-git-info"
}
*/
```
If the URL can't be matched with a git host, `null` will be returned. We
can match git, ssh and https urls. Additionally, we can match ssh connect
strings (`git@github.com:npm/hosted-git-info`) and shortcuts (eg,
`github:npm/hosted-git-info`). GitHub specifically, is detected in the case
of a third, unprefixed, form: `npm/hosted-git-info`.
If it does match, the returned object has properties of:
* info.type -- The short name of the service
* info.domain -- The domain for git protocol use
* info.user -- The name of the user/org on the git host
* info.project -- The name of the project on the git host
## Version Contract
The major version will be bumped any time…
* The constructor stops accepting URLs that it previously accepted.
* A method is removed.
* A method can no longer accept the number and type of arguments it previously accepted.
* A method can return a different type than it currently returns.
Implications:
* I do not consider the specific format of the urls returned from, say
`.https()` to be a part of the contract. The contract is that it will
return a string that can be used to fetch the repo via HTTPS. But what
that string looks like, specifically, can change.
* Dropping support for a hosted git provider would constitute a breaking
change.
## Usage
### const info = hostedGitInfo.fromUrl(gitSpecifier[, options])
* *gitSpecifer* is a URL of a git repository or a SCP-style specifier of one.
* *options* is an optional object. It can have the following properties:
* *noCommittish* — If true then committishes won't be included in generated URLs.
* *noGitPlus* — If true then `git+` won't be prefixed on URLs.
## Methods
All of the methods take the same options as the `fromUrl` factory. Options
provided to a method override those provided to the constructor.
* info.file(path, opts)
Given the path of a file relative to the repository, returns a URL for
directly fetching it from the githost. If no committish was set then
`HEAD` will be used as the default.
For example `hostedGitInfo.fromUrl("git@github.com:npm/hosted-git-info.git#v1.0.0").file("package.json")`
would return `https://raw.githubusercontent.com/npm/hosted-git-info/v1.0.0/package.json`
* info.shortcut(opts)
eg, `github:npm/hosted-git-info`
* info.browse(path, fragment, opts)
eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0`,
`https://github.com/npm/hosted-git-info/tree/v1.2.0/package.json`,
`https://github.com/npm/hosted-git-info/tree/v1.2.0/README.md#supported-hosts`
* info.bugs(opts)
eg, `https://github.com/npm/hosted-git-info/issues`
* info.docs(opts)
eg, `https://github.com/npm/hosted-git-info/tree/v1.2.0#readme`
* info.https(opts)
eg, `git+https://github.com/npm/hosted-git-info.git`
* info.sshurl(opts)
eg, `git+ssh://git@github.com/npm/hosted-git-info.git`
* info.ssh(opts)
eg, `git@github.com:npm/hosted-git-info.git`
* info.path(opts)
eg, `npm/hosted-git-info`
* info.tarball(opts)
eg, `https://github.com/npm/hosted-git-info/archive/v1.2.0.tar.gz`
* info.getDefaultRepresentation()
Returns the default output type. The default output type is based on the
string you passed in to be parsed
* info.toString(opts)
Uses the getDefaultRepresentation to call one of the other methods to get a URL for
this resource. As such `hostedGitInfo.fromUrl(url).toString()` will give
you a normalized version of the URL that still uses the same protocol.
Shortcuts will still be returned as shortcuts, but the special case github
form of `org/project` will be normalized to `github:org/project`.
SSH connect strings will be normalized into `git+ssh` URLs.
## Supported hosts
Currently this supports GitHub (including Gists), Bitbucket, GitLab and Sourcehut.
Pull requests for additional hosts welcome.

View File

@@ -0,0 +1,122 @@
'use strict'
const parseUrl = require('./parse-url')
// look for github shorthand inputs, such as npm/cli
const isGitHubShorthand = (arg) => {
// it cannot contain whitespace before the first #
// it cannot start with a / because that's probably an absolute file path
// but it must include a slash since repos are username/repository
// it cannot start with a . because that's probably a relative file path
// it cannot start with an @ because that's a scoped package if it passes the other tests
// it cannot contain a : before a # because that tells us that there's a protocol
// a second / may not exist before a #
const firstHash = arg.indexOf('#')
const firstSlash = arg.indexOf('/')
const secondSlash = arg.indexOf('/', firstSlash + 1)
const firstColon = arg.indexOf(':')
const firstSpace = /\s/.exec(arg)
const firstAt = arg.indexOf('@')
const spaceOnlyAfterHash = !firstSpace || (firstHash > -1 && firstSpace.index > firstHash)
const atOnlyAfterHash = firstAt === -1 || (firstHash > -1 && firstAt > firstHash)
const colonOnlyAfterHash = firstColon === -1 || (firstHash > -1 && firstColon > firstHash)
const secondSlashOnlyAfterHash = secondSlash === -1 || (firstHash > -1 && secondSlash > firstHash)
const hasSlash = firstSlash > 0
// if a # is found, what we really want to know is that the character
// immediately before # is not a /
const doesNotEndWithSlash = firstHash > -1 ? arg[firstHash - 1] !== '/' : !arg.endsWith('/')
const doesNotStartWithDot = !arg.startsWith('.')
return spaceOnlyAfterHash && hasSlash && doesNotEndWithSlash &&
doesNotStartWithDot && atOnlyAfterHash && colonOnlyAfterHash &&
secondSlashOnlyAfterHash
}
module.exports = (giturl, opts, { gitHosts, protocols }) => {
if (!giturl) {
return
}
const correctedUrl = isGitHubShorthand(giturl) ? `github:${giturl}` : giturl
const parsed = parseUrl(correctedUrl, protocols)
if (!parsed) {
return
}
const gitHostShortcut = gitHosts.byShortcut[parsed.protocol]
const gitHostDomain = gitHosts.byDomain[parsed.hostname.startsWith('www.')
? parsed.hostname.slice(4)
: parsed.hostname]
const gitHostName = gitHostShortcut || gitHostDomain
if (!gitHostName) {
return
}
const gitHostInfo = gitHosts[gitHostShortcut || gitHostDomain]
let auth = null
if (protocols[parsed.protocol]?.auth && (parsed.username || parsed.password)) {
auth = `${parsed.username}${parsed.password ? ':' + parsed.password : ''}`
}
let committish = null
let user = null
let project = null
let defaultRepresentation = null
try {
if (gitHostShortcut) {
let pathname = parsed.pathname.startsWith('/') ? parsed.pathname.slice(1) : parsed.pathname
const firstAt = pathname.indexOf('@')
// we ignore auth for shortcuts, so just trim it out
if (firstAt > -1) {
pathname = pathname.slice(firstAt + 1)
}
const lastSlash = pathname.lastIndexOf('/')
if (lastSlash > -1) {
user = decodeURIComponent(pathname.slice(0, lastSlash))
// we want nulls only, never empty strings
if (!user) {
user = null
}
project = decodeURIComponent(pathname.slice(lastSlash + 1))
} else {
project = decodeURIComponent(pathname)
}
if (project.endsWith('.git')) {
project = project.slice(0, -4)
}
if (parsed.hash) {
committish = decodeURIComponent(parsed.hash.slice(1))
}
defaultRepresentation = 'shortcut'
} else {
if (!gitHostInfo.protocols.includes(parsed.protocol)) {
return
}
const segments = gitHostInfo.extract(parsed)
if (!segments) {
return
}
user = segments.user && decodeURIComponent(segments.user)
project = decodeURIComponent(segments.project)
committish = decodeURIComponent(segments.committish)
defaultRepresentation = protocols[parsed.protocol]?.name || parsed.protocol.slice(0, -1)
}
} catch (err) {
/* istanbul ignore else */
if (err instanceof URIError) {
return
} else {
throw err
}
}
return [gitHostName, user, auth, project, committish, defaultRepresentation, opts]
}

View File

@@ -0,0 +1,227 @@
/* eslint-disable max-len */
'use strict'
const maybeJoin = (...args) => args.every(arg => arg) ? args.join('') : ''
const maybeEncode = (arg) => arg ? encodeURIComponent(arg) : ''
const formatHashFragment = (f) => f.toLowerCase().replace(/^\W+|\/|\W+$/g, '').replace(/\W+/g, '-')
const defaults = {
sshtemplate: ({ domain, user, project, committish }) =>
`git@${domain}:${user}/${project}.git${maybeJoin('#', committish)}`,
sshurltemplate: ({ domain, user, project, committish }) =>
`git+ssh://git@${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
edittemplate: ({ domain, user, project, committish, editpath, path }) =>
`https://${domain}/${user}/${project}${maybeJoin('/', editpath, '/', maybeEncode(committish || 'HEAD'), '/', path)}`,
browsetemplate: ({ domain, user, project, committish, treepath }) =>
`https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}`,
browsetreetemplate: ({ domain, user, project, committish, treepath, path, fragment, hashformat }) =>
`https://${domain}/${user}/${project}/${treepath}/${maybeEncode(committish || 'HEAD')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`,
browseblobtemplate: ({ domain, user, project, committish, blobpath, path, fragment, hashformat }) =>
`https://${domain}/${user}/${project}/${blobpath}/${maybeEncode(committish || 'HEAD')}/${path}${maybeJoin('#', hashformat(fragment || ''))}`,
docstemplate: ({ domain, user, project, treepath, committish }) =>
`https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish))}#readme`,
httpstemplate: ({ auth, domain, user, project, committish }) =>
`git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
filetemplate: ({ domain, user, project, committish, path }) =>
`https://${domain}/${user}/${project}/raw/${maybeEncode(committish || 'HEAD')}/${path}`,
shortcuttemplate: ({ type, user, project, committish }) =>
`${type}:${user}/${project}${maybeJoin('#', committish)}`,
pathtemplate: ({ user, project, committish }) =>
`${user}/${project}${maybeJoin('#', committish)}`,
bugstemplate: ({ domain, user, project }) =>
`https://${domain}/${user}/${project}/issues`,
hashformat: formatHashFragment,
}
const hosts = {}
hosts.github = {
// First two are insecure and generally shouldn't be used any more, but
// they are still supported.
protocols: ['git:', 'http:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'github.com',
treepath: 'tree',
blobpath: 'blob',
editpath: 'edit',
filetemplate: ({ auth, user, project, committish, path }) =>
`https://${maybeJoin(auth, '@')}raw.githubusercontent.com/${user}/${project}/${maybeEncode(committish || 'HEAD')}/${path}`,
gittemplate: ({ auth, domain, user, project, committish }) =>
`git://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ domain, user, project, committish }) =>
`https://codeload.${domain}/${user}/${project}/tar.gz/${maybeEncode(committish || 'HEAD')}`,
extract: (url) => {
let [, user, project, type, committish] = url.pathname.split('/', 5)
if (type && type !== 'tree') {
return
}
if (!type) {
committish = url.hash.slice(1)
}
if (project && project.endsWith('.git')) {
project = project.slice(0, -4)
}
if (!user || !project) {
return
}
return { user, project, committish }
},
}
hosts.bitbucket = {
protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'bitbucket.org',
treepath: 'src',
blobpath: 'src',
editpath: '?mode=edit',
edittemplate: ({ domain, user, project, committish, treepath, path, editpath }) =>
`https://${domain}/${user}/${project}${maybeJoin('/', treepath, '/', maybeEncode(committish || 'HEAD'), '/', path, editpath)}`,
tarballtemplate: ({ domain, user, project, committish }) =>
`https://${domain}/${user}/${project}/get/${maybeEncode(committish || 'HEAD')}.tar.gz`,
extract: (url) => {
let [, user, project, aux] = url.pathname.split('/', 4)
if (['get'].includes(aux)) {
return
}
if (project && project.endsWith('.git')) {
project = project.slice(0, -4)
}
if (!user || !project) {
return
}
return { user, project, committish: url.hash.slice(1) }
},
}
hosts.gitlab = {
protocols: ['git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'gitlab.com',
treepath: 'tree',
blobpath: 'tree',
editpath: '-/edit',
httpstemplate: ({ auth, domain, user, project, committish }) =>
`git+https://${maybeJoin(auth, '@')}${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ domain, user, project, committish }) =>
`https://${domain}/${user}/${project}/repository/archive.tar.gz?ref=${maybeEncode(committish || 'HEAD')}`,
extract: (url) => {
const path = url.pathname.slice(1)
if (path.includes('/-/') || path.includes('/archive.tar.gz')) {
return
}
const segments = path.split('/')
let project = segments.pop()
if (project.endsWith('.git')) {
project = project.slice(0, -4)
}
const user = segments.join('/')
if (!user || !project) {
return
}
return { user, project, committish: url.hash.slice(1) }
},
}
hosts.gist = {
protocols: ['git:', 'git+ssh:', 'git+https:', 'ssh:', 'https:'],
domain: 'gist.github.com',
editpath: 'edit',
sshtemplate: ({ domain, project, committish }) =>
`git@${domain}:${project}.git${maybeJoin('#', committish)}`,
sshurltemplate: ({ domain, project, committish }) =>
`git+ssh://git@${domain}/${project}.git${maybeJoin('#', committish)}`,
edittemplate: ({ domain, user, project, committish, editpath }) =>
`https://${domain}/${user}/${project}${maybeJoin('/', maybeEncode(committish))}/${editpath}`,
browsetemplate: ({ domain, project, committish }) =>
`https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`,
browsetreetemplate: ({ domain, project, committish, path, hashformat }) =>
`https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}${maybeJoin('#', hashformat(path))}`,
browseblobtemplate: ({ domain, project, committish, path, hashformat }) =>
`https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}${maybeJoin('#', hashformat(path))}`,
docstemplate: ({ domain, project, committish }) =>
`https://${domain}/${project}${maybeJoin('/', maybeEncode(committish))}`,
httpstemplate: ({ domain, project, committish }) =>
`git+https://${domain}/${project}.git${maybeJoin('#', committish)}`,
filetemplate: ({ user, project, committish, path }) =>
`https://gist.githubusercontent.com/${user}/${project}/raw${maybeJoin('/', maybeEncode(committish))}/${path}`,
shortcuttemplate: ({ type, project, committish }) =>
`${type}:${project}${maybeJoin('#', committish)}`,
pathtemplate: ({ project, committish }) =>
`${project}${maybeJoin('#', committish)}`,
bugstemplate: ({ domain, project }) =>
`https://${domain}/${project}`,
gittemplate: ({ domain, project, committish }) =>
`git://${domain}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ project, committish }) =>
`https://codeload.github.com/gist/${project}/tar.gz/${maybeEncode(committish || 'HEAD')}`,
extract: (url) => {
let [, user, project, aux] = url.pathname.split('/', 4)
if (aux === 'raw') {
return
}
if (!project) {
if (!user) {
return
}
project = user
user = null
}
if (project.endsWith('.git')) {
project = project.slice(0, -4)
}
return { user, project, committish: url.hash.slice(1) }
},
hashformat: function (fragment) {
return fragment && 'file-' + formatHashFragment(fragment)
},
}
hosts.sourcehut = {
protocols: ['git+ssh:', 'https:'],
domain: 'git.sr.ht',
treepath: 'tree',
blobpath: 'tree',
filetemplate: ({ domain, user, project, committish, path }) =>
`https://${domain}/${user}/${project}/blob/${maybeEncode(committish) || 'HEAD'}/${path}`,
httpstemplate: ({ domain, user, project, committish }) =>
`https://${domain}/${user}/${project}.git${maybeJoin('#', committish)}`,
tarballtemplate: ({ domain, user, project, committish }) =>
`https://${domain}/${user}/${project}/archive/${maybeEncode(committish) || 'HEAD'}.tar.gz`,
bugstemplate: () => null,
extract: (url) => {
let [, user, project, aux] = url.pathname.split('/', 4)
// tarball url
if (['archive'].includes(aux)) {
return
}
if (project && project.endsWith('.git')) {
project = project.slice(0, -4)
}
if (!user || !project) {
return
}
return { user, project, committish: url.hash.slice(1) }
},
}
for (const [name, host] of Object.entries(hosts)) {
hosts[name] = Object.assign({}, defaults, host)
}
module.exports = hosts

View File

@@ -0,0 +1,179 @@
'use strict'
const { LRUCache } = require('lru-cache')
const hosts = require('./hosts.js')
const fromUrl = require('./from-url.js')
const parseUrl = require('./parse-url.js')
const cache = new LRUCache({ max: 1000 })
class GitHost {
constructor (type, user, auth, project, committish, defaultRepresentation, opts = {}) {
Object.assign(this, GitHost.#gitHosts[type], {
type,
user,
auth,
project,
committish,
default: defaultRepresentation,
opts,
})
}
static #gitHosts = { byShortcut: {}, byDomain: {} }
static #protocols = {
'git+ssh:': { name: 'sshurl' },
'ssh:': { name: 'sshurl' },
'git+https:': { name: 'https', auth: true },
'git:': { auth: true },
'http:': { auth: true },
'https:': { auth: true },
'git+http:': { auth: true },
}
static addHost (name, host) {
GitHost.#gitHosts[name] = host
GitHost.#gitHosts.byDomain[host.domain] = name
GitHost.#gitHosts.byShortcut[`${name}:`] = name
GitHost.#protocols[`${name}:`] = { name }
}
static fromUrl (giturl, opts) {
if (typeof giturl !== 'string') {
return
}
const key = giturl + JSON.stringify(opts || {})
if (!cache.has(key)) {
const hostArgs = fromUrl(giturl, opts, {
gitHosts: GitHost.#gitHosts,
protocols: GitHost.#protocols,
})
cache.set(key, hostArgs ? new GitHost(...hostArgs) : undefined)
}
return cache.get(key)
}
static parseUrl (url) {
return parseUrl(url)
}
#fill (template, opts) {
if (typeof template !== 'function') {
return null
}
const options = { ...this, ...this.opts, ...opts }
// the path should always be set so we don't end up with 'undefined' in urls
if (!options.path) {
options.path = ''
}
// template functions will insert the leading slash themselves
if (options.path.startsWith('/')) {
options.path = options.path.slice(1)
}
if (options.noCommittish) {
options.committish = null
}
const result = template(options)
return options.noGitPlus && result.startsWith('git+') ? result.slice(4) : result
}
hash () {
return this.committish ? `#${this.committish}` : ''
}
ssh (opts) {
return this.#fill(this.sshtemplate, opts)
}
sshurl (opts) {
return this.#fill(this.sshurltemplate, opts)
}
browse (path, ...args) {
// not a string, treat path as opts
if (typeof path !== 'string') {
return this.#fill(this.browsetemplate, path)
}
if (typeof args[0] !== 'string') {
return this.#fill(this.browsetreetemplate, { ...args[0], path })
}
return this.#fill(this.browsetreetemplate, { ...args[1], fragment: args[0], path })
}
// If the path is known to be a file, then browseFile should be used. For some hosts
// the url is the same as browse, but for others like GitHub a file can use both `/tree/`
// and `/blob/` in the path. When using a default committish of `HEAD` then the `/tree/`
// path will redirect to a specific commit. Using the `/blob/` path avoids this and
// does not redirect to a different commit.
browseFile (path, ...args) {
if (typeof args[0] !== 'string') {
return this.#fill(this.browseblobtemplate, { ...args[0], path })
}
return this.#fill(this.browseblobtemplate, { ...args[1], fragment: args[0], path })
}
docs (opts) {
return this.#fill(this.docstemplate, opts)
}
bugs (opts) {
return this.#fill(this.bugstemplate, opts)
}
https (opts) {
return this.#fill(this.httpstemplate, opts)
}
git (opts) {
return this.#fill(this.gittemplate, opts)
}
shortcut (opts) {
return this.#fill(this.shortcuttemplate, opts)
}
path (opts) {
return this.#fill(this.pathtemplate, opts)
}
tarball (opts) {
return this.#fill(this.tarballtemplate, { ...opts, noCommittish: false })
}
file (path, opts) {
return this.#fill(this.filetemplate, { ...opts, path })
}
edit (path, opts) {
return this.#fill(this.edittemplate, { ...opts, path })
}
getDefaultRepresentation () {
return this.default
}
toString (opts) {
if (this.default && typeof this[this.default] === 'function') {
return this[this.default](opts)
}
return this.sshurl(opts)
}
}
for (const [name, host] of Object.entries(hosts)) {
GitHost.addHost(name, host)
}
module.exports = GitHost

View File

@@ -0,0 +1,78 @@
const url = require('url')
const lastIndexOfBefore = (str, char, beforeChar) => {
const startPosition = str.indexOf(beforeChar)
return str.lastIndexOf(char, startPosition > -1 ? startPosition : Infinity)
}
const safeUrl = (u) => {
try {
return new url.URL(u)
} catch {
// this fn should never throw
}
}
// accepts input like git:github.com:user/repo and inserts the // after the first :
const correctProtocol = (arg, protocols) => {
const firstColon = arg.indexOf(':')
const proto = arg.slice(0, firstColon + 1)
if (Object.prototype.hasOwnProperty.call(protocols, proto)) {
return arg
}
const firstAt = arg.indexOf('@')
if (firstAt > -1) {
if (firstAt > firstColon) {
return `git+ssh://${arg}`
} else {
return arg
}
}
const doubleSlash = arg.indexOf('//')
if (doubleSlash === firstColon + 1) {
return arg
}
return `${arg.slice(0, firstColon + 1)}//${arg.slice(firstColon + 1)}`
}
// attempt to correct an scp style url so that it will parse with `new URL()`
const correctUrl = (giturl) => {
// ignore @ that come after the first hash since the denotes the start
// of a committish which can contain @ characters
const firstAt = lastIndexOfBefore(giturl, '@', '#')
// ignore colons that come after the hash since that could include colons such as:
// git@github.com:user/package-2#semver:^1.0.0
const lastColonBeforeHash = lastIndexOfBefore(giturl, ':', '#')
if (lastColonBeforeHash > firstAt) {
// the last : comes after the first @ (or there is no @)
// like it would in:
// proto://hostname.com:user/repo
// username@hostname.com:user/repo
// :password@hostname.com:user/repo
// username:password@hostname.com:user/repo
// proto://username@hostname.com:user/repo
// proto://:password@hostname.com:user/repo
// proto://username:password@hostname.com:user/repo
// then we replace the last : with a / to create a valid path
giturl = giturl.slice(0, lastColonBeforeHash) + '/' + giturl.slice(lastColonBeforeHash + 1)
}
if (lastIndexOfBefore(giturl, ':', '#') === -1 && giturl.indexOf('//') === -1) {
// we have no : at all
// as it would be in:
// username@hostname.com/user/repo
// then we prepend a protocol
giturl = `git+ssh://${giturl}`
}
return giturl
}
module.exports = (giturl, protocols) => {
const withProtocol = protocols ? correctProtocol(giturl, protocols) : giturl
return safeUrl(withProtocol) || safeUrl(correctUrl(withProtocol))
}

View File

@@ -0,0 +1,60 @@
{
"name": "hosted-git-info",
"version": "7.0.2",
"description": "Provides metadata and conversions from repository urls for GitHub, Bitbucket and GitLab",
"main": "./lib/index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/npm/hosted-git-info.git"
},
"keywords": [
"git",
"github",
"bitbucket",
"gitlab"
],
"author": "GitHub Inc.",
"license": "ISC",
"bugs": {
"url": "https://github.com/npm/hosted-git-info/issues"
},
"homepage": "https://github.com/npm/hosted-git-info",
"scripts": {
"posttest": "npm run lint",
"snap": "tap",
"test": "tap",
"test:coverage": "tap --coverage-report=html",
"lint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"",
"postlint": "template-oss-check",
"lintfix": "npm run lint -- --fix",
"template-oss-apply": "template-oss-apply --force"
},
"dependencies": {
"lru-cache": "^10.0.1"
},
"devDependencies": {
"@npmcli/eslint-config": "^4.0.0",
"@npmcli/template-oss": "4.22.0",
"tap": "^16.0.1"
},
"files": [
"bin/",
"lib/"
],
"engines": {
"node": "^16.14.0 || >=18.0.0"
},
"tap": {
"color": 1,
"coverage": true,
"nyc-arg": [
"--exclude",
"tap-snapshots/**"
]
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.22.0",
"publish": "true"
}
}

View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) 2010-2023 Isaac Z. Schlueter and Contributors
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,331 @@
# lru-cache
A cache object that deletes the least-recently-used items.
Specify a max number of the most recently used items that you
want to keep, and this cache will keep that many of the most
recently accessed items.
This is not primarily a TTL cache, and does not make strong TTL
guarantees. There is no preemptive pruning of expired items by
default, but you _may_ set a TTL on the cache or on a single
`set`. If you do so, it will treat expired items as missing, and
delete them when fetched. If you are more interested in TTL
caching than LRU caching, check out
[@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
As of version 7, this is one of the most performant LRU
implementations available in JavaScript, and supports a wide
diversity of use cases. However, note that using some of the
features will necessarily impact performance, by causing the
cache to have to do more work. See the "Performance" section
below.
## Installation
```bash
npm install lru-cache --save
```
## Usage
```js
// hybrid module, either works
import { LRUCache } from 'lru-cache'
// or:
const { LRUCache } = require('lru-cache')
// or in minified form for web browsers:
import { LRUCache } from 'http://unpkg.com/lru-cache@9/dist/mjs/index.min.mjs'
// At least one of 'max', 'ttl', or 'maxSize' is required, to prevent
// unsafe unbounded storage.
//
// In most cases, it's best to specify a max for performance, so all
// the required memory allocation is done up-front.
//
// All the other options are optional, see the sections below for
// documentation on what each one does. Most of them can be
// overridden for specific items in get()/set()
const options = {
max: 500,
// for use with tracking overall storage size
maxSize: 5000,
sizeCalculation: (value, key) => {
return 1
},
// for use when you need to clean up something when objects
// are evicted from the cache
dispose: (value, key) => {
freeFromMemoryOrWhatever(value)
},
// how long to live in ms
ttl: 1000 * 60 * 5,
// return stale items before removing from cache?
allowStale: false,
updateAgeOnGet: false,
updateAgeOnHas: false,
// async method to use for cache.fetch(), for
// stale-while-revalidate type of behavior
fetchMethod: async (
key,
staleValue,
{ options, signal, context }
) => {},
}
const cache = new LRUCache(options)
cache.set('key', 'value')
cache.get('key') // "value"
// non-string keys ARE fully supported
// but note that it must be THE SAME object, not
// just a JSON-equivalent object.
var someObject = { a: 1 }
cache.set(someObject, 'a value')
// Object keys are not toString()-ed
cache.set('[object Object]', 'a different value')
assert.equal(cache.get(someObject), 'a value')
// A similar object with same keys/values won't work,
// because it's a different object identity
assert.equal(cache.get({ a: 1 }), undefined)
cache.clear() // empty the cache
```
If you put more stuff in the cache, then less recently used items
will fall out. That's what an LRU cache is.
For full description of the API and all options, please see [the
LRUCache typedocs](https://isaacs.github.io/node-lru-cache/)
## Storage Bounds Safety
This implementation aims to be as flexible as possible, within
the limits of safe memory consumption and optimal performance.
At initial object creation, storage is allocated for `max` items.
If `max` is set to zero, then some performance is lost, and item
count is unbounded. Either `maxSize` or `ttl` _must_ be set if
`max` is not specified.
If `maxSize` is set, then this creates a safe limit on the
maximum storage consumed, but without the performance benefits of
pre-allocation. When `maxSize` is set, every item _must_ provide
a size, either via the `sizeCalculation` method provided to the
constructor, or via a `size` or `sizeCalculation` option provided
to `cache.set()`. The size of every item _must_ be a positive
integer.
If neither `max` nor `maxSize` are set, then `ttl` tracking must
be enabled. Note that, even when tracking item `ttl`, items are
_not_ preemptively deleted when they become stale, unless
`ttlAutopurge` is enabled. Instead, they are only purged the
next time the key is requested. Thus, if `ttlAutopurge`, `max`,
and `maxSize` are all not set, then the cache will potentially
grow unbounded.
In this case, a warning is printed to standard error. Future
versions may require the use of `ttlAutopurge` if `max` and
`maxSize` are not specified.
If you truly wish to use a cache that is bound _only_ by TTL
expiration, consider using a `Map` object, and calling
`setTimeout` to delete entries when they expire. It will perform
much better than an LRU cache.
Here is an implementation you may use, under the same
[license](./LICENSE) as this package:
```js
// a storage-unbounded ttl cache that is not an lru-cache
const cache = {
data: new Map(),
timers: new Map(),
set: (k, v, ttl) => {
if (cache.timers.has(k)) {
clearTimeout(cache.timers.get(k))
}
cache.timers.set(
k,
setTimeout(() => cache.delete(k), ttl)
)
cache.data.set(k, v)
},
get: k => cache.data.get(k),
has: k => cache.data.has(k),
delete: k => {
if (cache.timers.has(k)) {
clearTimeout(cache.timers.get(k))
}
cache.timers.delete(k)
return cache.data.delete(k)
},
clear: () => {
cache.data.clear()
for (const v of cache.timers.values()) {
clearTimeout(v)
}
cache.timers.clear()
},
}
```
If that isn't to your liking, check out
[@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
## Storing Undefined Values
This cache never stores undefined values, as `undefined` is used
internally in a few places to indicate that a key is not in the
cache.
You may call `cache.set(key, undefined)`, but this is just
an alias for `cache.delete(key)`. Note that this has the effect
that `cache.has(key)` will return _false_ after setting it to
undefined.
```js
cache.set(myKey, undefined)
cache.has(myKey) // false!
```
If you need to track `undefined` values, and still note that the
key is in the cache, an easy workaround is to use a sigil object
of your own.
```js
import { LRUCache } from 'lru-cache'
const undefinedValue = Symbol('undefined')
const cache = new LRUCache(...)
const mySet = (key, value) =>
cache.set(key, value === undefined ? undefinedValue : value)
const myGet = (key, value) => {
const v = cache.get(key)
return v === undefinedValue ? undefined : v
}
```
## Performance
As of January 2022, version 7 of this library is one of the most
performant LRU cache implementations in JavaScript.
Benchmarks can be extremely difficult to get right. In
particular, the performance of set/get/delete operations on
objects will vary _wildly_ depending on the type of key used. V8
is highly optimized for objects with keys that are short strings,
especially integer numeric strings. Thus any benchmark which
tests _solely_ using numbers as keys will tend to find that an
object-based approach performs the best.
Note that coercing _anything_ to strings to use as object keys is
unsafe, unless you can be 100% certain that no other type of
value will be used. For example:
```js
const myCache = {}
const set = (k, v) => (myCache[k] = v)
const get = k => myCache[k]
set({}, 'please hang onto this for me')
set('[object Object]', 'oopsie')
```
Also beware of "Just So" stories regarding performance. Garbage
collection of large (especially: deep) object graphs can be
incredibly costly, with several "tipping points" where it
increases exponentially. As a result, putting that off until
later can make it much worse, and less predictable. If a library
performs well, but only in a scenario where the object graph is
kept shallow, then that won't help you if you are using large
objects as keys.
In general, when attempting to use a library to improve
performance (such as a cache like this one), it's best to choose
an option that will perform well in the sorts of scenarios where
you'll actually use it.
This library is optimized for repeated gets and minimizing
eviction time, since that is the expected need of a LRU. Set
operations are somewhat slower on average than a few other
options, in part because of that optimization. It is assumed
that you'll be caching some costly operation, ideally as rarely
as possible, so optimizing set over get would be unwise.
If performance matters to you:
1. If it's at all possible to use small integer values as keys,
and you can guarantee that no other types of values will be
used as keys, then do that, and use a cache such as
[lru-fast](https://npmjs.com/package/lru-fast), or
[mnemonist's
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache)
which uses an Object as its data store.
2. Failing that, if at all possible, use short non-numeric
strings (ie, less than 256 characters) as your keys, and use
[mnemonist's
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache).
3. If the types of your keys will be anything else, especially
long strings, strings that look like floats, objects, or some
mix of types, or if you aren't sure, then this library will
work well for you.
If you do not need the features that this library provides
(like asynchronous fetching, a variety of TTL staleness
options, and so on), then [mnemonist's
LRUMap](https://yomguithereal.github.io/mnemonist/lru-map) is
a very good option, and just slightly faster than this module
(since it does considerably less).
4. Do not use a `dispose` function, size tracking, or especially
ttl behavior, unless absolutely needed. These features are
convenient, and necessary in some use cases, and every attempt
has been made to make the performance impact minimal, but it
isn't nothing.
## Breaking Changes in Version 7
This library changed to a different algorithm and internal data
structure in version 7, yielding significantly better
performance, albeit with some subtle changes as a result.
If you were relying on the internals of LRUCache in version 6 or
before, it probably will not work in version 7 and above.
## Breaking Changes in Version 8
- The `fetchContext` option was renamed to `context`, and may no
longer be set on the cache instance itself.
- Rewritten in TypeScript, so pretty much all the types moved
around a lot.
- The AbortController/AbortSignal polyfill was removed. For this
reason, **Node version 16.14.0 or higher is now required**.
- Internal properties were moved to actual private class
properties.
- Keys and values must not be `null` or `undefined`.
- Minified export available at `'lru-cache/min'`, for both CJS
and MJS builds.
## Breaking Changes in Version 9
- Named export only, no default export.
- AbortController polyfill returned, albeit with a warning when
used.
## Breaking Changes in Version 10
- `cache.fetch()` return type is now `Promise<V | undefined>`
instead of `Promise<V | void>`. This is an irrelevant change
practically speaking, but can require changes for TypeScript
users.
For more info, see the [change log](CHANGELOG.md).

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"type": "commonjs"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
{
"type": "module"
}

View File

@@ -0,0 +1,116 @@
{
"name": "lru-cache",
"publishConfig": {
"tag": "legacy-v10"
},
"description": "A cache object that deletes the least-recently-used items.",
"version": "10.4.3",
"author": "Isaac Z. Schlueter <i@izs.me>",
"keywords": [
"mru",
"lru",
"cache"
],
"sideEffects": false,
"scripts": {
"build": "npm run prepare",
"prepare": "tshy && bash fixup.sh",
"pretest": "npm run prepare",
"presnap": "npm run prepare",
"test": "tap",
"snap": "tap",
"preversion": "npm test",
"postversion": "npm publish",
"prepublishOnly": "git push origin --follow-tags",
"format": "prettier --write .",
"typedoc": "typedoc --tsconfig ./.tshy/esm.json ./src/*.ts",
"benchmark-results-typedoc": "bash scripts/benchmark-results-typedoc.sh",
"prebenchmark": "npm run prepare",
"benchmark": "make -C benchmark",
"preprofile": "npm run prepare",
"profile": "make -C benchmark profile"
},
"main": "./dist/commonjs/index.js",
"types": "./dist/commonjs/index.d.ts",
"tshy": {
"exports": {
".": "./src/index.ts",
"./min": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.min.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.min.js"
}
}
}
},
"repository": {
"type": "git",
"url": "git://github.com/isaacs/node-lru-cache.git"
},
"devDependencies": {
"@types/node": "^20.2.5",
"@types/tap": "^15.0.6",
"benchmark": "^2.1.4",
"esbuild": "^0.17.11",
"eslint-config-prettier": "^8.5.0",
"marked": "^4.2.12",
"mkdirp": "^2.1.5",
"prettier": "^2.6.2",
"tap": "^20.0.3",
"tshy": "^2.0.0",
"tslib": "^2.4.0",
"typedoc": "^0.25.3",
"typescript": "^5.2.2"
},
"license": "ISC",
"files": [
"dist"
],
"prettier": {
"semi": false,
"printWidth": 70,
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"jsxSingleQuote": false,
"bracketSameLine": true,
"arrowParens": "avoid",
"endOfLine": "lf"
},
"tap": {
"node-arg": [
"--expose-gc"
],
"plugin": [
"@tapjs/clock"
]
},
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.js"
}
},
"./min": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.min.js"
},
"require": {
"types": "./dist/commonjs/index.d.ts",
"default": "./dist/commonjs/index.min.js"
}
}
},
"type": "module",
"module": "./dist/esm/index.js"
}

View File

@@ -0,0 +1,15 @@
The ISC License
Copyright (c) npm, Inc.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,96 @@
# npm-package-arg
[![Build Status](https://img.shields.io/github/actions/workflow/status/npm/npm-package-arg/ci.yml?branch=main)](https://github.com/npm/npm-package-arg)
Parses package name and specifier passed to commands like `npm install` or
`npm cache add`, or as found in `package.json` dependency sections.
## EXAMPLES
```javascript
var assert = require("assert")
var npa = require("npm-package-arg")
// Pass in the descriptor, and it'll return an object
try {
var parsed = npa("@bar/foo@1.2")
} catch (ex) {
}
```
## USING
`var npa = require('npm-package-arg')`
### var result = npa(*arg*[, *where*])
* *arg* - a string that you might pass to `npm install`, like:
`foo@1.2`, `@bar/foo@1.2`, `foo@user/foo`, `http://x.com/foo.tgz`,
`git+https://github.com/user/foo`, `bitbucket:user/foo`, `foo.tar.gz`,
`../foo/bar/` or `bar`. If the *arg* you provide doesn't have a specifier
part, eg `foo` then the specifier will default to `latest`.
* *where* - Optionally the path to resolve file paths relative to. Defaults to `process.cwd()`
**Throws** if the package name is invalid, a dist-tag is invalid or a URL's protocol is not supported.
### var result = npa.resolve(*name*, *spec*[, *where*])
* *name* - The name of the module you want to install. For example: `foo` or `@bar/foo`.
* *spec* - The specifier indicating where and how you can get this module. Something like:
`1.2`, `^1.7.17`, `http://x.com/foo.tgz`, `git+https://github.com/user/foo`,
`bitbucket:user/foo`, `file:foo.tar.gz` or `file:../foo/bar/`. If not
included then the default is `latest`.
* *where* - Optionally the path to resolve file paths relative to. Defaults to `process.cwd()`
**Throws** if the package name is invalid, a dist-tag is invalid or a URL's protocol is not supported.
### var purl = npa.toPurl(*arg*, *reg*)
Returns the [purl (package URL)](https://github.com/package-url/purl-spec) form of the given package name/spec.
* *arg* - A package/version string. For example: `foo@1.0.0` or `@bar/foo@2.0.0-alpha.1`.
* *reg* - Optionally the URL to the package registry. If not specified, assumes the default
`https://registry.npmjs.org`.
**Throws** if the package name is invalid, or the supplied arg can't be resolved to a purl.
## RESULT OBJECT
The objects that are returned by npm-package-arg contain the following
keys:
* `type` - One of the following strings:
* `git` - A git repo
* `tag` - A tagged version, like `"foo@latest"`
* `version` - A specific version number, like `"foo@1.2.3"`
* `range` - A version range, like `"foo@2.x"`
* `file` - A local `.tar.gz`, `.tar` or `.tgz` file.
* `directory` - A local directory.
* `remote` - An http url (presumably to a tgz)
* `alias` - A specifier with an alias, like `myalias@npm:foo@1.2.3`
* `registry` - If true this specifier refers to a resource hosted on a
registry. This is true for `tag`, `version` and `range` types.
* `name` - If known, the `name` field expected in the resulting pkg.
* `scope` - If a name is something like `@org/module` then the `scope`
field will be set to `@org`. If it doesn't have a scoped name, then
scope is `null`.
* `escapedName` - A version of `name` escaped to match the npm scoped packages
specification. Mostly used when making requests against a registry. When
`name` is `null`, `escapedName` will also be `null`.
* `rawSpec` - The specifier part that was parsed out in calls to `npa(arg)`,
or the value of `spec` in calls to `npa.resolve(name, spec).
* `saveSpec` - The normalized specifier, for saving to package.json files.
`null` for registry dependencies.
* `fetchSpec` - The version of the specifier to be used to fetch this
resource. `null` for shortcuts to hosted git dependencies as there isn't
just one URL to try with them.
* `gitRange` - If set, this is a semver specifier to match against git tags with
* `gitCommittish` - If set, this is the specific committish to use with a git dependency.
* `hosted` - If `from === 'hosted'` then this will be a `hosted-git-info`
object. This property is not included when serializing the object as
JSON.
* `raw` - The original un-modified string that was provided. If called as
`npa.resolve(name, spec)` then this will be `name + '@' + spec`.
* `subSpec` - If `type === 'alias'`, this is a Result Object for parsing the
target specifier for the alias.

View File

@@ -0,0 +1,415 @@
'use strict'
module.exports = npa
module.exports.resolve = resolve
module.exports.toPurl = toPurl
module.exports.Result = Result
const { URL } = require('url')
const HostedGit = require('hosted-git-info')
const semver = require('semver')
const path = global.FAKE_WINDOWS ? require('path').win32 : require('path')
const validatePackageName = require('validate-npm-package-name')
const { homedir } = require('os')
const { log } = require('proc-log')
const isWindows = process.platform === 'win32' || global.FAKE_WINDOWS
const hasSlashes = isWindows ? /\\|[/]/ : /[/]/
const isURL = /^(?:git[+])?[a-z]+:/i
const isGit = /^[^@]+@[^:.]+\.[^:]+:.+$/i
const isFilename = /[.](?:tgz|tar.gz|tar)$/i
function npa (arg, where) {
let name
let spec
if (typeof arg === 'object') {
if (arg instanceof Result && (!where || where === arg.where)) {
return arg
} else if (arg.name && arg.rawSpec) {
return npa.resolve(arg.name, arg.rawSpec, where || arg.where)
} else {
return npa(arg.raw, where || arg.where)
}
}
const nameEndsAt = arg[0] === '@' ? arg.slice(1).indexOf('@') + 1 : arg.indexOf('@')
const namePart = nameEndsAt > 0 ? arg.slice(0, nameEndsAt) : arg
if (isURL.test(arg)) {
spec = arg
} else if (isGit.test(arg)) {
spec = `git+ssh://${arg}`
} else if (namePart[0] !== '@' && (hasSlashes.test(namePart) || isFilename.test(namePart))) {
spec = arg
} else if (nameEndsAt > 0) {
name = namePart
spec = arg.slice(nameEndsAt + 1) || '*'
} else {
const valid = validatePackageName(arg)
if (valid.validForOldPackages) {
name = arg
spec = '*'
} else {
spec = arg
}
}
return resolve(name, spec, where, arg)
}
const isFilespec = isWindows ? /^(?:[.]|~[/]|[/\\]|[a-zA-Z]:)/ : /^(?:[.]|~[/]|[/]|[a-zA-Z]:)/
function resolve (name, spec, where, arg) {
const res = new Result({
raw: arg,
name: name,
rawSpec: spec,
fromArgument: arg != null,
})
if (name) {
res.setName(name)
}
if (spec && (isFilespec.test(spec) || /^file:/i.test(spec))) {
return fromFile(res, where)
} else if (spec && /^npm:/i.test(spec)) {
return fromAlias(res, where)
}
const hosted = HostedGit.fromUrl(spec, {
noGitPlus: true,
noCommittish: true,
})
if (hosted) {
return fromHostedGit(res, hosted)
} else if (spec && isURL.test(spec)) {
return fromURL(res)
} else if (spec && (hasSlashes.test(spec) || isFilename.test(spec))) {
return fromFile(res, where)
} else {
return fromRegistry(res)
}
}
const defaultRegistry = 'https://registry.npmjs.org'
function toPurl (arg, reg = defaultRegistry) {
const res = npa(arg)
if (res.type !== 'version') {
throw invalidPurlType(res.type, res.raw)
}
// URI-encode leading @ of scoped packages
let purl = 'pkg:npm/' + res.name.replace(/^@/, '%40') + '@' + res.rawSpec
if (reg !== defaultRegistry) {
purl += '?repository_url=' + reg
}
return purl
}
function invalidPackageName (name, valid, raw) {
// eslint-disable-next-line max-len
const err = new Error(`Invalid package name "${name}" of package "${raw}": ${valid.errors.join('; ')}.`)
err.code = 'EINVALIDPACKAGENAME'
return err
}
function invalidTagName (name, raw) {
// eslint-disable-next-line max-len
const err = new Error(`Invalid tag name "${name}" of package "${raw}": Tags may not have any characters that encodeURIComponent encodes.`)
err.code = 'EINVALIDTAGNAME'
return err
}
function invalidPurlType (type, raw) {
// eslint-disable-next-line max-len
const err = new Error(`Invalid type "${type}" of package "${raw}": Purl can only be generated for "version" types.`)
err.code = 'EINVALIDPURLTYPE'
return err
}
function Result (opts) {
this.type = opts.type
this.registry = opts.registry
this.where = opts.where
if (opts.raw == null) {
this.raw = opts.name ? opts.name + '@' + opts.rawSpec : opts.rawSpec
} else {
this.raw = opts.raw
}
this.name = undefined
this.escapedName = undefined
this.scope = undefined
this.rawSpec = opts.rawSpec || ''
this.saveSpec = opts.saveSpec
this.fetchSpec = opts.fetchSpec
if (opts.name) {
this.setName(opts.name)
}
this.gitRange = opts.gitRange
this.gitCommittish = opts.gitCommittish
this.gitSubdir = opts.gitSubdir
this.hosted = opts.hosted
}
Result.prototype.setName = function (name) {
const valid = validatePackageName(name)
if (!valid.validForOldPackages) {
throw invalidPackageName(name, valid, this.raw)
}
this.name = name
this.scope = name[0] === '@' ? name.slice(0, name.indexOf('/')) : undefined
// scoped packages in couch must have slash url-encoded, e.g. @foo%2Fbar
this.escapedName = name.replace('/', '%2f')
return this
}
Result.prototype.toString = function () {
const full = []
if (this.name != null && this.name !== '') {
full.push(this.name)
}
const spec = this.saveSpec || this.fetchSpec || this.rawSpec
if (spec != null && spec !== '') {
full.push(spec)
}
return full.length ? full.join('@') : this.raw
}
Result.prototype.toJSON = function () {
const result = Object.assign({}, this)
delete result.hosted
return result
}
// sets res.gitCommittish, res.gitRange, and res.gitSubdir
function setGitAttrs (res, committish) {
if (!committish) {
res.gitCommittish = null
return
}
// for each :: separated item:
for (const part of committish.split('::')) {
// if the item has no : the n it is a commit-ish
if (!part.includes(':')) {
if (res.gitRange) {
throw new Error('cannot override existing semver range with a committish')
}
if (res.gitCommittish) {
throw new Error('cannot override existing committish with a second committish')
}
res.gitCommittish = part
continue
}
// split on name:value
const [name, value] = part.split(':')
// if name is semver do semver lookup of ref or tag
if (name === 'semver') {
if (res.gitCommittish) {
throw new Error('cannot override existing committish with a semver range')
}
if (res.gitRange) {
throw new Error('cannot override existing semver range with a second semver range')
}
res.gitRange = decodeURIComponent(value)
continue
}
if (name === 'path') {
if (res.gitSubdir) {
throw new Error('cannot override existing path with a second path')
}
res.gitSubdir = `/${value}`
continue
}
log.warn('npm-package-arg', `ignoring unknown key "${name}"`)
}
}
function fromFile (res, where) {
if (!where) {
where = process.cwd()
}
res.type = isFilename.test(res.rawSpec) ? 'file' : 'directory'
res.where = where
// always put the '/' on where when resolving urls, or else
// file:foo from /path/to/bar goes to /path/to/foo, when we want
// it to be /path/to/bar/foo
let specUrl
let resolvedUrl
const prefix = (!/^file:/.test(res.rawSpec) ? 'file:' : '')
const rawWithPrefix = prefix + res.rawSpec
let rawNoPrefix = rawWithPrefix.replace(/^file:/, '')
try {
resolvedUrl = new URL(rawWithPrefix, `file://${path.resolve(where)}/`)
specUrl = new URL(rawWithPrefix)
} catch (originalError) {
const er = new Error('Invalid file: URL, must comply with RFC 8089')
throw Object.assign(er, {
raw: res.rawSpec,
spec: res,
where,
originalError,
})
}
// XXX backwards compatibility lack of compliance with RFC 8089
if (resolvedUrl.host && resolvedUrl.host !== 'localhost') {
const rawSpec = res.rawSpec.replace(/^file:\/\//, 'file:///')
resolvedUrl = new URL(rawSpec, `file://${path.resolve(where)}/`)
specUrl = new URL(rawSpec)
rawNoPrefix = rawSpec.replace(/^file:/, '')
}
// turn file:/../foo into file:../foo
// for 1, 2 or 3 leading slashes since we attempted
// in the previous step to make it a file protocol url with a leading slash
if (/^\/{1,3}\.\.?(\/|$)/.test(rawNoPrefix)) {
const rawSpec = res.rawSpec.replace(/^file:\/{1,3}/, 'file:')
resolvedUrl = new URL(rawSpec, `file://${path.resolve(where)}/`)
specUrl = new URL(rawSpec)
rawNoPrefix = rawSpec.replace(/^file:/, '')
}
// XXX end RFC 8089 violation backwards compatibility section
// turn /C:/blah into just C:/blah on windows
let specPath = decodeURIComponent(specUrl.pathname)
let resolvedPath = decodeURIComponent(resolvedUrl.pathname)
if (isWindows) {
specPath = specPath.replace(/^\/+([a-z]:\/)/i, '$1')
resolvedPath = resolvedPath.replace(/^\/+([a-z]:\/)/i, '$1')
}
// replace ~ with homedir, but keep the ~ in the saveSpec
// otherwise, make it relative to where param
if (/^\/~(\/|$)/.test(specPath)) {
res.saveSpec = `file:${specPath.substr(1)}`
resolvedPath = path.resolve(homedir(), specPath.substr(3))
} else if (!path.isAbsolute(rawNoPrefix)) {
res.saveSpec = `file:${path.relative(where, resolvedPath)}`
} else {
res.saveSpec = `file:${path.resolve(resolvedPath)}`
}
res.fetchSpec = path.resolve(where, resolvedPath)
return res
}
function fromHostedGit (res, hosted) {
res.type = 'git'
res.hosted = hosted
res.saveSpec = hosted.toString({ noGitPlus: false, noCommittish: false })
res.fetchSpec = hosted.getDefaultRepresentation() === 'shortcut' ? null : hosted.toString()
setGitAttrs(res, hosted.committish)
return res
}
function unsupportedURLType (protocol, spec) {
const err = new Error(`Unsupported URL Type "${protocol}": ${spec}`)
err.code = 'EUNSUPPORTEDPROTOCOL'
return err
}
function fromURL (res) {
let rawSpec = res.rawSpec
res.saveSpec = rawSpec
if (rawSpec.startsWith('git+ssh:')) {
// git ssh specifiers are overloaded to also use scp-style git
// specifiers, so we have to parse those out and treat them special.
// They are NOT true URIs, so we can't hand them to URL.
// This regex looks for things that look like:
// git+ssh://git@my.custom.git.com:username/project.git#deadbeef
// ...and various combinations. The username in the beginning is *required*.
const matched = rawSpec.match(/^git\+ssh:\/\/([^:#]+:[^#]+(?:\.git)?)(?:#(.*))?$/i)
if (matched && !matched[1].match(/:[0-9]+\/?.*$/i)) {
res.type = 'git'
setGitAttrs(res, matched[2])
res.fetchSpec = matched[1]
return res
}
} else if (rawSpec.startsWith('git+file://')) {
// URL can't handle windows paths
rawSpec = rawSpec.replace(/\\/g, '/')
}
const parsedUrl = new URL(rawSpec)
// check the protocol, and then see if it's git or not
switch (parsedUrl.protocol) {
case 'git:':
case 'git+http:':
case 'git+https:':
case 'git+rsync:':
case 'git+ftp:':
case 'git+file:':
case 'git+ssh:':
res.type = 'git'
setGitAttrs(res, parsedUrl.hash.slice(1))
if (parsedUrl.protocol === 'git+file:' && /^git\+file:\/\/[a-z]:/i.test(rawSpec)) {
// URL can't handle drive letters on windows file paths, the host can't contain a :
res.fetchSpec = `git+file://${parsedUrl.host.toLowerCase()}:${parsedUrl.pathname}`
} else {
parsedUrl.hash = ''
res.fetchSpec = parsedUrl.toString()
}
if (res.fetchSpec.startsWith('git+')) {
res.fetchSpec = res.fetchSpec.slice(4)
}
break
case 'http:':
case 'https:':
res.type = 'remote'
res.fetchSpec = res.saveSpec
break
default:
throw unsupportedURLType(parsedUrl.protocol, rawSpec)
}
return res
}
function fromAlias (res, where) {
const subSpec = npa(res.rawSpec.substr(4), where)
if (subSpec.type === 'alias') {
throw new Error('nested aliases not supported')
}
if (!subSpec.registry) {
throw new Error('aliases only work for registry deps')
}
if (!subSpec.name) {
throw new Error('aliases must have a name')
}
res.subSpec = subSpec
res.registry = true
res.type = 'alias'
res.saveSpec = null
res.fetchSpec = null
return res
}
function fromRegistry (res) {
res.registry = true
const spec = res.rawSpec.trim()
// no save spec for registry components as we save based on the fetched
// version, not on the argument so this can't compute that.
res.saveSpec = null
res.fetchSpec = spec
const version = semver.valid(spec, true)
const range = semver.validRange(spec, true)
if (version) {
res.type = 'version'
} else if (range) {
res.type = 'range'
} else {
if (encodeURIComponent(spec) !== spec) {
throw invalidTagName(spec, res.raw)
}
res.type = 'tag'
}
return res
}

View File

@@ -0,0 +1,61 @@
{
"name": "npm-package-arg",
"version": "11.0.3",
"description": "Parse the things that can be arguments to `npm install`",
"main": "./lib/npa.js",
"directories": {
"test": "test"
},
"files": [
"bin/",
"lib/"
],
"dependencies": {
"hosted-git-info": "^7.0.0",
"proc-log": "^4.0.0",
"semver": "^7.3.5",
"validate-npm-package-name": "^5.0.0"
},
"devDependencies": {
"@npmcli/eslint-config": "^4.0.0",
"@npmcli/template-oss": "4.23.1",
"tap": "^16.0.1"
},
"scripts": {
"test": "tap",
"snap": "tap",
"npmclilint": "npmcli-lint",
"lint": "npm run eslint",
"lintfix": "npm run eslint -- --fix",
"posttest": "npm run lint",
"postsnap": "npm run lintfix --",
"postlint": "template-oss-check",
"template-oss-apply": "template-oss-apply --force",
"eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
},
"repository": {
"type": "git",
"url": "git+https://github.com/npm/npm-package-arg.git"
},
"author": "GitHub Inc.",
"license": "ISC",
"bugs": {
"url": "https://github.com/npm/npm-package-arg/issues"
},
"homepage": "https://github.com/npm/npm-package-arg",
"engines": {
"node": "^16.14.0 || >=18.0.0"
},
"tap": {
"branches": 97,
"nyc-arg": [
"--exclude",
"tap-snapshots/**"
]
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.23.1",
"publish": true
}
}

View File

@@ -0,0 +1,6 @@
Copyright (c) 2015, npm, Inc
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@@ -0,0 +1,120 @@
# validate-npm-package-name
Give me a string and I'll tell you if it's a valid `npm` package name.
This package exports a single synchronous function that takes a `string` as
input and returns an object with two properties:
- `validForNewPackages` :: `Boolean`
- `validForOldPackages` :: `Boolean`
## Contents
- [Naming rules](#naming-rules)
- [Examples](#examples)
+ [Valid Names](#valid-names)
+ [Invalid Names](#invalid-names)
- [Legacy Names](#legacy-names)
- [Tests](#tests)
- [License](#license)
## Naming Rules
Below is a list of rules that valid `npm` package name should conform to.
- package name length should be greater than zero
- all the characters in the package name must be lowercase i.e., no uppercase or mixed case names are allowed
- package name *can* consist of hyphens
- package name must *not* contain any non-url-safe characters (since name ends up being part of a URL)
- package name should not start with `.` or `_`
- package name should *not* contain any spaces
- package name should *not* contain any of the following characters: `~)('!*`
- package name *cannot* be the same as a node.js/io.js core module nor a reserved/blacklisted name. For example, the following names are invalid:
+ http
+ stream
+ node_modules
+ favicon.ico
- package name length cannot exceed 214
## Examples
### Valid Names
```js
var validate = require("validate-npm-package-name")
validate("some-package")
validate("example.com")
validate("under_score")
validate("123numeric")
validate("@npm/thingy")
validate("@jane/foo.js")
```
All of the above names are valid, so you'll get this object back:
```js
{
validForNewPackages: true,
validForOldPackages: true
}
```
### Invalid Names
```js
validate("excited!")
validate(" leading-space:and:weirdchars")
```
That was never a valid package name, so you get this:
```js
{
validForNewPackages: false,
validForOldPackages: false,
errors: [
'name cannot contain leading or trailing spaces',
'name can only contain URL-friendly characters'
]
}
```
## Legacy Names
In the old days of npm, package names were wild. They could have capital
letters in them. They could be really long. They could be the name of an
existing module in node core.
If you give this function a package name that **used to be valid**, you'll see
a change in the value of `validForNewPackages` property, and a warnings array
will be present:
```js
validate("eLaBorAtE-paCkAgE-with-mixed-case-and-more-than-214-characters-----------------------------------------------------------------------------------------------------------------------------------------------------------")
```
returns:
```js
{
validForNewPackages: false,
validForOldPackages: true,
warnings: [
"name can no longer contain capital letters",
"name can no longer contain more than 214 characters"
]
}
```
## Tests
```sh
npm install
npm test
```
## License
ISC

View File

@@ -0,0 +1,105 @@
'use strict'
const { builtinModules: builtins } = require('module')
var scopedPackagePattern = new RegExp('^(?:@([^/]+?)[/])?([^/]+?)$')
var blacklist = [
'node_modules',
'favicon.ico',
]
function validate (name) {
var warnings = []
var errors = []
if (name === null) {
errors.push('name cannot be null')
return done(warnings, errors)
}
if (name === undefined) {
errors.push('name cannot be undefined')
return done(warnings, errors)
}
if (typeof name !== 'string') {
errors.push('name must be a string')
return done(warnings, errors)
}
if (!name.length) {
errors.push('name length must be greater than zero')
}
if (name.match(/^\./)) {
errors.push('name cannot start with a period')
}
if (name.match(/^_/)) {
errors.push('name cannot start with an underscore')
}
if (name.trim() !== name) {
errors.push('name cannot contain leading or trailing spaces')
}
// No funny business
blacklist.forEach(function (blacklistedName) {
if (name.toLowerCase() === blacklistedName) {
errors.push(blacklistedName + ' is a blacklisted name')
}
})
// Generate warnings for stuff that used to be allowed
// core module names like http, events, util, etc
if (builtins.includes(name.toLowerCase())) {
warnings.push(name + ' is a core module name')
}
if (name.length > 214) {
warnings.push('name can no longer contain more than 214 characters')
}
// mIxeD CaSe nAMEs
if (name.toLowerCase() !== name) {
warnings.push('name can no longer contain capital letters')
}
if (/[~'!()*]/.test(name.split('/').slice(-1)[0])) {
warnings.push('name can no longer contain special characters ("~\'!()*")')
}
if (encodeURIComponent(name) !== name) {
// Maybe it's a scoped package name, like @user/package
var nameMatch = name.match(scopedPackagePattern)
if (nameMatch) {
var user = nameMatch[1]
var pkg = nameMatch[2]
if (encodeURIComponent(user) === user && encodeURIComponent(pkg) === pkg) {
return done(warnings, errors)
}
}
errors.push('name can only contain URL-friendly characters')
}
return done(warnings, errors)
}
var done = function (warnings, errors) {
var result = {
validForNewPackages: errors.length === 0 && warnings.length === 0,
validForOldPackages: errors.length === 0,
warnings: warnings,
errors: errors,
}
if (!result.warnings.length) {
delete result.warnings
}
if (!result.errors.length) {
delete result.errors
}
return result
}
module.exports = validate

View File

@@ -0,0 +1,60 @@
{
"name": "validate-npm-package-name",
"version": "5.0.1",
"description": "Give me a string and I'll tell you if it's a valid npm package name",
"main": "lib/",
"directories": {
"test": "test"
},
"devDependencies": {
"@npmcli/eslint-config": "^4.0.0",
"@npmcli/template-oss": "4.22.0",
"tap": "^16.0.1"
},
"scripts": {
"cov:test": "TAP_FLAGS='--cov' npm run test:code",
"test:code": "tap ${TAP_FLAGS:-'--'} test/*.js",
"test:style": "standard",
"test": "tap",
"lint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\"",
"postlint": "template-oss-check",
"template-oss-apply": "template-oss-apply --force",
"lintfix": "npm run lint -- --fix",
"snap": "tap",
"posttest": "npm run lint"
},
"repository": {
"type": "git",
"url": "git+https://github.com/npm/validate-npm-package-name.git"
},
"keywords": [
"npm",
"package",
"names",
"validation"
],
"author": "GitHub Inc.",
"license": "ISC",
"bugs": {
"url": "https://github.com/npm/validate-npm-package-name/issues"
},
"homepage": "https://github.com/npm/validate-npm-package-name",
"files": [
"bin/",
"lib/"
],
"engines": {
"node": "^14.17.0 || ^16.13.0 || >=18.0.0"
},
"templateOSS": {
"//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
"version": "4.22.0",
"publish": true
},
"tap": {
"nyc-arg": [
"--exclude",
"tap-snapshots/**"
]
}
}

View File

@@ -0,0 +1,54 @@
{
"name": "@expo/package-manager",
"version": "1.12.1",
"description": "A library for installing and finding packages in a project",
"main": "build/index.js",
"types": "build/index.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/expo/expo.git",
"directory": "packages/@expo/package-manager"
},
"keywords": [
"react-native",
"package-manager",
"package-json",
"node",
"yarn",
"yarnpkg",
"pnpm",
"bun"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/expo/expo/issues"
},
"homepage": "https://github.com/expo/expo/tree/main/packages/@expo/package-manager#readme",
"files": [
"build"
],
"dependencies": {
"@expo/json-file": "^10.2.0",
"@expo/spawn-async": "^1.8.0",
"chalk": "^4.0.0",
"npm-package-arg": "^11.0.0",
"ora": "^3.4.0",
"resolve-workspace-root": "^2.0.0"
},
"devDependencies": {
"@types/micromatch": "^4.0.2",
"@types/npm-package-arg": "^6.1.0",
"expo-module-scripts": "56.0.2"
},
"publishConfig": {
"access": "public"
},
"gitHead": "a7adc95c1747db1e92655feba56d0e62660098db",
"scripts": {
"build": "expo-module build",
"clean": "expo-module clean",
"lint": "expo-module lint",
"test": "expo-module test",
"typecheck": "expo-module typecheck"
}
}