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,2 @@
// @generated by expo-module-scripts
module.exports = require('expo-module-scripts/eslintrc.base.js');

View File

@@ -0,0 +1,302 @@
# Changelog
## Unpublished
### 🛠 Breaking changes
### 🎉 New features
### 🐛 Bug fixes
### 💡 Others
## 1.11.3 — 2024-10-02
_This version does not introduce any user-facing changes._
## 1.11.2 — 2024-08-14
### 🎉 New features
- Added `react-native-config` command to support core autolinking for react-native. ([#29818](https://github.com/expo/expo/pull/29818) by [@kudo](https://github.com/kudo))
### 🐛 Bug fixes
- Added missing `project.android.packageName` in react-native-config for Android core autolinking. ([#30913](https://github.com/expo/expo/pull/30913) by [@kudo](https://github.com/kudo))
- Fixed core autolinking for react-native-maps. ([#31190](https://github.com/expo/expo/pull/31190) by [@kudo](https://github.com/kudo))
## 1.11.1 — 2024-04-23
_This version does not introduce any user-facing changes._
## 1.11.0 — 2024-04-18
### 🎉 New features
- Expand Android auto-linking to support new expo-build-properties ([#26895](https://github.com/expo/expo/pull/26895) by [@bpeltonc](https://github.com/bpeltonc))
### 🐛 Bug fixes
- Support custom debug build configuration for debugOnly pods (expo-dev-client for example) ([#28085](https://github.com/expo/expo/pull/28085) by [@Titozzz](https://github.com/Titozzz))
### 💡 Others
- Read `extraPods` from **Podfile.properties.json** and `extraMavenRepos` from **gradle.properties**. ([#28106](https://github.com/expo/expo/pull/28106) by [@kudo](https://github.com/kudo))
## 1.10.3 - 2024-02-06
### 🐛 Bug fixes
- Fixed generating a list of app delegate subscribers. ([#26851](https://github.com/expo/expo/pull/26851) by [@tsapeta](https://github.com/tsapeta))
## 1.10.2 - 2024-01-18
### 🐛 Bug fixes
- Fixed a list of packages to include in the generated modules provider for tvOS and macOS platforms. ([#26497](https://github.com/expo/expo/pull/26497) by [@tsapeta](https://github.com/tsapeta))
## 1.10.1 - 2024-01-18
### 🎉 New features
- Introduced a universal `"apple"` platform as a replacement for `"ios"`, `"macos"` and `"tvos"`. ([#26398](https://github.com/expo/expo/pull/26398) by [@tsapeta](https://github.com/tsapeta))
## 1.10.0 - 2024-01-10
### 🎉 New features
- Added support for macOS and tvOS targets. ([#26287](https://github.com/expo/expo/pull/26287) by [@tsapeta](https://github.com/tsapeta))
## 1.9.0 — 2023-12-12
### 🐛 Bug fixes
- [iOS] Resolve `expo-modules-autolinking` from `expo` in the generated project integrator. ([#25817](https://github.com/expo/expo/pull/25817) by [@byCedric](https://github.com/byCedric))
## 1.8.0 — 2023-11-14
### 🎉 New features
- Added Expo CLI devtools plugins support. ([#24649](https://github.com/expo/expo/pull/24649) by [@kudo](https://github.com/kudo))
## 1.7.0 — 2023-10-17
### 💡 Others
- Transpile for Node 18 (LTS). ([#24471](https://github.com/expo/expo/pull/24471) by [@EvanBacon](https://github.com/EvanBacon))
## 1.6.0 — 2023-09-15
### 🐛 Bug fixes
- Maintain hierarchical order when linking isolated modules ([#24351](https://github.com/expo/expo/pull/24351) by [@byCedric](https://github.com/byCedric))
### 💡 Others
- [iOS] Disable packager and bundle JS when EX_UPDATES_NATIVE_DEBUG set. ([#24366](https://github.com/expo/expo/pull/24366) by [@douglowder](https://github.com/douglowder))
## 1.5.2 — 2023-09-04
### 🐛 Bug fixes
- Add support for pnpm isolated modules ([#23867](https://github.com/expo/expo/pull/23867) by [@byCedric](https://github.com/byCedric))
- Resolve cli for isolated modules before running node scripts. ([#23926](https://github.com/expo/expo/pull/23926) by [@byCedric](https://github.com/byCedric))
### 💡 Others
- [Android] Made `generateExpoModulesPackageList` task cacheable. ([#23847](https://github.com/expo/expo/pull/23847) by [@lukmccall](https://github.com/lukmccall))
## 1.5.1 - 2023-08-22
### 🐛 Bug fixes
- Fixed `expo_patch_react_imports!` missing some lines with spaces before or after the imports. ([#23923](https://github.com/expo/expo/pull/23923) by [@liamjones](https://github.com/liamjones))
## 1.5.0 — 2023-06-21
### 🎉 New features
- Added support for React Native 0.72. ([#22588](https://github.com/expo/expo/pull/22588) by [@kudo](https://github.com/kudo))
- Added extra CocoaPods dependencies and Maven repositories from `expo-build-properties`. ([#22785](https://github.com/expo/expo/pull/22785) by [@kudo](https://github.com/kudo))
## 1.4.0 — 2023-06-13
### 🎉 New features
- Added support for React Native 0.72. ([#22588](https://github.com/expo/expo/pull/22588) by [@kudo](https://github.com/kudo))
## 1.3.0 — 2023-05-08
### 🎉 New features
- Generating `ExpoModulesProvider.swift` in the build phase script instead of only `pod install`. ([#21108](https://github.com/expo/expo/pull/21108) by [@tsapeta](https://github.com/tsapeta))
## 1.2.0 - 2023-04-13
### 🎉 New features
- Added Gradle plugin autolinking support for Android. ([#21377](https://github.com/expo/expo/pull/21377) by [@kudo](https://github.com/kudo))
## 1.1.2 — 2023-02-14
### 💡 Others
- Suppress node warnings about deprecated exports mapping in 3rd-party dependencies. ([#21222](https://github.com/expo/expo/pull/21222) by [@tsapeta](https://github.com/tsapeta))
## 1.1.1 — 2023-02-09
_This version does not introduce any user-facing changes._
## 1.1.0 — 2023-02-03
_This version does not introduce any user-facing changes._
## 1.0.2 — 2023-01-10
### 🐛 Bug fixes
- Replace deprecated `File.exists?` with `File.exist?` to fix usage with `ruby@3.2`. ([#20470](https://github.com/expo/expo/pull/20757) by [@KiwiKilian](https://github.com/kiwikilian))
## 1.0.1 — 2022-12-30
### 🐛 Bug fixes
- Added React Native 0.71 support. ([#20470](https://github.com/expo/expo/pull/20470) by [@kudo](https://github.com/kudo))
## 1.0.0 — 2022-11-03
_This version does not introduce any user-facing changes._
## 0.12.0 — 2022-10-25
### 🎉 New features
- Automatically use modular headers for pod dependencies when the package has Swift modules to link. ([#19443](https://github.com/expo/expo/pull/19443) by [@tsapeta](https://github.com/tsapeta))
### 💡 Others
- Bump `@tsconfig/node` to match other Expo Modules packages development setup. ([#19671](https://github.com/expo/expo/pull/19671) by [@Simek](https://github.com/Simek))
## 0.11.0 — 2022-10-06
### 🎉 New features
- Added `includeTests` option to `use_expo_modules!` to include test specs from autolinked modules. ([#18496](https://github.com/expo/expo/pull/18496) by [@tsapeta](https://github.com/tsapeta))
### 🐛 Bug fixes
- Fixed node executable resolution errors on iOS when `pod install` is executed from package.json `scripts`. ([#18580](https://github.com/expo/expo/pull/18580) by [@kudo](https://github.com/kudo))
## 0.10.1 — 2022-07-25
### 🎉 New features
- Added a feature to automatically generate `.xcode.env.local` with correct `$NODE_BINARY` path when running `pod install`. ([#18330](https://github.com/expo/expo/pull/18330) by [@kudo](https://github.com/kudo))
## 0.10.0 — 2022-07-07
### 🐛 Bug fixes
- Added support for React Native 0.69.x ([#17629](https://github.com/expo/expo/pull/17629) by [@kudo](https://github.com/kudo))
- Use regex to match ignored modules in `expo_patch_react_imports!` and fix iOS build errors when the project is inside `react-native` named folder. ([#17968](https://github.com/expo/expo/pull/17968) by [@dmnkgrc](https://github.com/dmnkgrc))
## 0.9.0 — 2022-06-23
### 🎉 New features
- The `searchPaths` and `nativeModulesDir` options now support direct paths to specific module directories. ([#17922](https://github.com/expo/expo/pull/17922) by [@barthap](https://github.com/barthap))
## 0.8.1 — 2022-05-12
### 🐛 Bug fixes
- Fixed an infinite loop when the **package.json** is placed at the root path. ([#17440](https://github.com/expo/expo/pull/17440) by [@tsapeta](https://github.com/tsapeta))
## 0.8.0 — 2022-05-06
### 🎉 New features
- Add `ios.debugOnly` to module config. ([#17331](https://github.com/expo/expo/pull/17331) by [@lukmccall](https://github.com/lukmccall))
- Setting `EXPO_CONFIGURATION_DEBUG` or `EXPO_CONFIGURATION_RELEASE` Swift flags on project targets. ([#17378](https://github.com/expo/expo/pull/17378) by [@tsapeta](https://github.com/tsapeta))
### 🐛 Bug fixes
- Fix debug-only modules weren't installed if the `DEBUG` flag wasn't present in `OTHER_SWIFT_FLAGS`. ([#17383](https://github.com/expo/expo/pull/17383) by [@lukmccall](https://github.com/lukmccall))
- Fix iOS build if project config name is other than RELEASE or DEBUG ([#17439](https://github.com/expo/expo/pull/17439) by [@uloco](https://github.com/uloco))
### 💡 Others
## 0.7.0 — 2022-04-18
- Update require logic to find transitive deps that would not be hoisted at the top of the monorepo ([#16419](https://github.com/expo/expo/pull/16419) by [@Titozzz](https://github.com/Titozzz))
- Fix `cannot cast object 'ExpoAutolinkingManager@' with class 'ExpoAutolinkingManager' to class 'ExpoAutolinkingManager'` on Android when a project is using `buildSrc`. ([#16545](https://github.com/expo/expo/pull/16545) by [@lukmccall](https://github.com/lukmccall))
### 🎉 New features
- Add `ios.swiftModuleName` to module config. ([#16260](https://github.com/expo/expo/pull/16260) by [@esamelson](https://github.com/esamelson))
- Added support for linking multiple podspecs and Gradle projects in a package. ([#16511](https://github.com/expo/expo/pull/16511) by [@kudo](https://github.com/kudo))
### 🐛 Bug fixes
- Fixed `expo_patch_react_imports!` not work when the app project is in a folder with spaces. ([#16794](https://github.com/expo/expo/pull/16794) by [@Kudo](https://github.com/Kudo))
## 0.6.0 — 2022-01-26
### ⚠️ Notices
- Expose `findModulesAsync` from `expo-modules-autolinking/build/autolinking` again. ([#15950](https://github.com/expo/expo/pull/15950) by [@EvanBacon](https://github.com/EvanBacon))
- Deprecated `modulesClassNames` in favor of `modules` in the Expo module config. ([#15852](https://github.com/expo/expo/pull/15852) by [@tsapeta](https://github.com/tsapeta))
## 0.5.5 — 2022-01-05
### 🐛 Bug fixes
- Fix `umbrella directory '../../Public/React-Core/React' not found` build error when in `use_frameworks!` mode. ([#15773](https://github.com/expo/expo/pull/15773) by [@kudo](https://github.com/kudo))
## 0.5.4 — 2021-12-29
### 🐛 Bug fixes
- Add `expo_patch_react_imports!` support for React-Native 0.66. ([#15724](https://github.com/expo/expo/pull/15724) by [@kudo](https://github.com/kudo))
## 0.5.3 — 2021-12-28
### 🐛 Bug fixes
- Fix `expo_patch_react_imports!` error when there are pods with absolute path. ([#15699](https://github.com/expo/expo/pull/15699) by [@kudo](https://github.com/kudo))
## 0.5.2 — 2021-12-22
### 🐛 Bug fixes
- Introduce `expo_patch_react_imports!` to transform double-quoted React imports into angle-brackets in order to fix third-party libraries incompatibility with SDK 44. ([#15655](https://github.com/expo/expo/pull/15655) by [@kudo](https://github.com/kudo))
## 0.5.1 — 2021-12-15
_This version does not introduce any user-facing changes._
## 0.5.0 — 2021-12-03
### 🎉 New features
- Patch React podspecs on the fly to support Swift integration. ([#15299](https://github.com/expo/expo/pull/15299) by [@kudo](https://github.com/kudo))
- Add `nativeModulesDir` option to specify app's custom native modules location. ([#15415](https://github.com/expo/expo/pull/15415) by [@barthap](https://github.com/barthap))
## 0.4.0 — 2021-11-17
### 🎉 New features
- Added "silent" property for silencing resolution warnings. ([#14891](https://github.com/expo/expo/pull/14891) by [@EvanBacon](https://github.com/EvanBacon))
- Listing module's app delegate subscribers in the generated `ExpoModulesProvider.swift`. ([#14867](https://github.com/expo/expo/pull/14867) by [@tsapeta](https://github.com/tsapeta))
- Search for Android package in the entire source code other than just `src` directory. ([#14883](https://github.com/expo/expo/pull/14883) by [@kudo](https://github.com/kudo))
- Introduce React Native bridge delegate handlers on iOS. ([#15138](https://github.com/expo/expo/pull/15138) by [@kudo](https://github.com/kudo))
### 🐛 Bug fixes
- Fix Gradle error when running Gradle from outside of the project directory. ([#15109](https://github.com/expo/expo/pull/15109) by [@kudo](https://github.com/kudo))
## 0.3.3 — 2021-10-21
### 🐛 Bug fixes
- Resolved race condition when generating `ExpoModulesProvider.swift`. ([#14822](https://github.com/expo/expo/pull/14822) by [@awinograd](https://github.com/awinograd))

View File

@@ -0,0 +1,33 @@
<p>
<a href="https://docs.expo.dev/modules/autolinking/">
<img
src="../../.github/resources/expo-modules-autolinking.svg"
alt="expo-modules-autolinking"
height="64" />
</a>
</p>
Scripts that autolink Expo modules.
# API documentation
- [Documentation for the main branch](https://github.com/expo/expo/blob/main/docs/pages/versions/unversioned/sdk/module-autolinking.mdx)
- [Documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/module-autolinking/)
# Installation in managed Expo projects
For [managed](https://docs.expo.dev/archive/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](#api-documentation). If you follow the link and there is no documentation available then this library is not yet usable within managed projects &mdash; it is likely to be included in an upcoming Expo SDK release.
# Installation in bare React Native projects
For bare React Native projects, you must ensure that you have [installed and configured the `expo` package](https://docs.expo.dev/bare/installing-expo-modules/) before continuing.
### Add the package to your npm dependencies
```
npm install expo-modules-autolinking
```
# Contributing
Contributions are very welcome! Please refer to guidelines described in the [contributing guide](https://github.com/expo/expo#contributing).

View File

@@ -0,0 +1,8 @@
#!/usr/bin/env node
'use strict';
// Have to force color support — logs wouldn't have colors when spawned by another process.
// It must be set before `supports-color` (`chalk` dependency) module is imported.
process.env.FORCE_COLOR = 'true';
require('../build')(process.argv.slice(2));

View File

@@ -0,0 +1,60 @@
import { AndroidGradlePluginDescriptor, RawExpoModuleConfig, RawModuleConfigApple, SupportedPlatform } from './types';
/**
* A class that wraps the raw config (`expo-module.json` or `unimodule.json`).
*/
export declare class ExpoModuleConfig {
readonly rawConfig: RawExpoModuleConfig;
constructor(rawConfig: RawExpoModuleConfig);
/**
* Whether the module supports given platform.
*/
supportsPlatform(platform: SupportedPlatform): boolean;
/**
* Returns the generic config for all Apple platforms with a fallback to the legacy iOS config.
*/
getAppleConfig(): RawModuleConfigApple | null;
/**
* Returns a list of names of Swift native modules classes to put to the generated modules provider file.
*/
appleModules(): string[];
/**
* Returns a list of names of Swift classes that receives AppDelegate life-cycle events.
*/
appleAppDelegateSubscribers(): string[];
/**
* Returns a list of names of Swift classes that implement `ExpoReactDelegateHandler`.
*/
appleReactDelegateHandlers(): string[];
/**
* Returns podspec paths defined by the module author.
*/
applePodspecPaths(): string[];
/**
* Returns the product module names, if defined by the module author.
*/
appleSwiftModuleNames(): string[];
/**
* Returns whether this module will be added only to the debug configuration
*/
appleDebugOnly(): boolean;
/**
* Returns a list of names of Kotlin native modules classes to put to the generated package provider file.
*/
androidModules(): string[];
/**
* Returns build.gradle file paths defined by the module author.
*/
androidGradlePaths(): string[];
/**
* Returns gradle plugins descriptors defined by the module author.
*/
androidGradlePlugins(): AndroidGradlePluginDescriptor[];
/**
* Returns serializable raw config.
*/
toJSON(): RawExpoModuleConfig;
}
/**
* Reads the config at given path and returns the config wrapped by `ExpoModuleConfig` class.
*/
export declare function requireAndResolveExpoModuleConfig(path: string): ExpoModuleConfig;

View File

@@ -0,0 +1,112 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.requireAndResolveExpoModuleConfig = exports.ExpoModuleConfig = void 0;
function arrayize(value) {
if (Array.isArray(value)) {
return value;
}
return value != null ? [value] : [];
}
/**
* A class that wraps the raw config (`expo-module.json` or `unimodule.json`).
*/
class ExpoModuleConfig {
rawConfig;
constructor(rawConfig) {
this.rawConfig = rawConfig;
}
/**
* Whether the module supports given platform.
*/
supportsPlatform(platform) {
const supportedPlatforms = this.rawConfig.platforms ?? [];
if (platform === 'apple') {
// Apple platform is supported when any of iOS, macOS and tvOS is supported.
return supportedPlatforms.some((supportedPlatform) => {
return ['apple', 'ios', 'macos', 'tvos'].includes(supportedPlatform);
});
}
return supportedPlatforms.includes(platform);
}
/**
* Returns the generic config for all Apple platforms with a fallback to the legacy iOS config.
*/
getAppleConfig() {
return this.rawConfig.apple ?? this.rawConfig.ios ?? null;
}
/**
* Returns a list of names of Swift native modules classes to put to the generated modules provider file.
*/
appleModules() {
const appleConfig = this.getAppleConfig();
// `modulesClassNames` is a legacy name for the same config.
return appleConfig?.modules ?? appleConfig?.modulesClassNames ?? [];
}
/**
* Returns a list of names of Swift classes that receives AppDelegate life-cycle events.
*/
appleAppDelegateSubscribers() {
return this.getAppleConfig()?.appDelegateSubscribers ?? [];
}
/**
* Returns a list of names of Swift classes that implement `ExpoReactDelegateHandler`.
*/
appleReactDelegateHandlers() {
return this.getAppleConfig()?.reactDelegateHandlers ?? [];
}
/**
* Returns podspec paths defined by the module author.
*/
applePodspecPaths() {
return arrayize(this.getAppleConfig()?.podspecPath);
}
/**
* Returns the product module names, if defined by the module author.
*/
appleSwiftModuleNames() {
return arrayize(this.getAppleConfig()?.swiftModuleName);
}
/**
* Returns whether this module will be added only to the debug configuration
*/
appleDebugOnly() {
return this.getAppleConfig()?.debugOnly ?? false;
}
/**
* Returns a list of names of Kotlin native modules classes to put to the generated package provider file.
*/
androidModules() {
const androidConfig = this.rawConfig.android;
// `modulesClassNames` is a legacy name for the same config.
return androidConfig?.modules ?? androidConfig?.modulesClassNames ?? [];
}
/**
* Returns build.gradle file paths defined by the module author.
*/
androidGradlePaths() {
return arrayize(this.rawConfig.android?.gradlePath ?? []);
}
/**
* Returns gradle plugins descriptors defined by the module author.
*/
androidGradlePlugins() {
return arrayize(this.rawConfig.android?.gradlePlugins ?? []);
}
/**
* Returns serializable raw config.
*/
toJSON() {
return this.rawConfig;
}
}
exports.ExpoModuleConfig = ExpoModuleConfig;
/**
* Reads the config at given path and returns the config wrapped by `ExpoModuleConfig` class.
*/
function requireAndResolveExpoModuleConfig(path) {
// TODO: Validate the raw config against a schema.
// TODO: Support for `*.js` files, not only static `*.json`.
return new ExpoModuleConfig(require(path));
}
exports.requireAndResolveExpoModuleConfig = requireAndResolveExpoModuleConfig;
//# sourceMappingURL=ExpoModuleConfig.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,14 @@
import { PatchReactImportsOptions } from './types';
/**
* Remove all double-quoted react header imports
* @param dirs target directories to patch
* @param options PatchReactImportsOptions
*/
export declare function patchReactImportsAsync(dirs: string[], options: PatchReactImportsOptions): Promise<void>;
/**
* Patch imports from a file
* @param headerSet prebuilt React-Core header set
* @param file target patch file
* @param dryRun true if not writing changes to file
*/
export declare function patchFileAsync(headerSet: Set<string>, file: string, dryRun: boolean): Promise<void>;

View File

@@ -0,0 +1,89 @@
"use strict";
// Copyright 2018-present 650 Industries. All rights reserved.
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.patchFileAsync = exports.patchReactImportsAsync = void 0;
const fast_glob_1 = __importDefault(require("fast-glob"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
/**
* Remove all double-quoted react header imports
* @param dirs target directories to patch
* @param options PatchReactImportsOptions
*/
async function patchReactImportsAsync(dirs, options) {
const headerSet = await generateReactHeaderSetAsync(path_1.default.join(options.podsRoot, 'Headers', 'Public', 'React-Core', 'React'));
await Promise.all(dirs.map((dir) => patchDirAsync(headerSet, dir, options.dryRun)));
}
exports.patchReactImportsAsync = patchReactImportsAsync;
/**
* Generate `React-Core` public header names as a set, will transform necessary headers based on this set.
*/
async function generateReactHeaderSetAsync(reactHeaderDir) {
const files = await (0, fast_glob_1.default)('*.h', { cwd: reactHeaderDir });
return new Set(files);
}
/**
* Patch imports from a file
* @param headerSet prebuilt React-Core header set
* @param file target patch file
* @param dryRun true if not writing changes to file
*/
async function patchFileAsync(headerSet, file, dryRun) {
let changed = false;
const content = await fs_extra_1.default.readFile(file, 'utf-8');
let transformContent = content.replace(/(?<=^\s*)#import\s+"(.+)"(?=\s*$)/gm, (match, headerName) => {
// `#import "RCTBridge.h"` -> `#import <React/RCTBridge.h>`
if (headerSet.has(headerName)) {
changed = true;
return `#import <React/${headerName}>`;
}
// `#import "React/RCTBridge.h"` -> `#import <React/RCTBridge.h>`
if (headerName.startsWith('React/')) {
const name = headerName.substring(6);
if (headerSet.has(name)) {
changed = true;
return `#import <React/${name}>`;
}
}
// Otherwise, return original import
return match;
});
transformContent = transformContent.replace(/(?<=^\s*)#(if|elif)\s+__has_include\("(.+)"\)(?=\s*$)/gm, (match, ifPrefix, headerName) => {
// `#if __has_include("RCTBridge.h")` -> `#if __has_include(<React/RCTBridge.h>)`
if (headerSet.has(headerName)) {
changed = true;
return `#${ifPrefix} __has_include(<React/${headerName}>)`;
}
// `#if __has_include("React/RCTBridge.h")` -> `#if __has_include(<React/RCTBridge.h>)`
if (headerName.startsWith('React/')) {
const name = headerName.substring(6);
if (headerSet.has(name)) {
changed = true;
return `#${ifPrefix} __has_include(<React/${name}>)`;
}
}
// Otherwise, return original import
return match;
});
if (changed) {
console.log(`Patching imports for file: ${file}`);
if (!dryRun) {
await fs_extra_1.default.writeFile(file, transformContent);
}
}
}
exports.patchFileAsync = patchFileAsync;
/**
* Patch imports from a directory
* @param headerSet prebuilt React-Core header set
* @param file target patch file
* @param dryRun true if not writing changes to file
*/
async function patchDirAsync(headerSet, dir, dryRun) {
const files = await (0, fast_glob_1.default)('**/*.{h,m,mm}', { cwd: dir, absolute: true });
return Promise.all(files.map((file) => patchFileAsync(headerSet, file, dryRun)));
}
//# sourceMappingURL=ReactImportsPatcher.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
import { SearchOptions, SearchResults } from '../types';
/**
* Searches for modules to link based on given config.
*/
export declare function findModulesAsync(providedOptions: SearchOptions): Promise<SearchResults>;

View File

@@ -0,0 +1,214 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findModulesAsync = void 0;
const chalk_1 = __importDefault(require("chalk"));
const fast_glob_1 = __importDefault(require("fast-glob"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const module_1 = require("module");
const path_1 = __importDefault(require("path"));
const mergeLinkingOptions_1 = require("./mergeLinkingOptions");
const utils_1 = require("./utils");
const ExpoModuleConfig_1 = require("../ExpoModuleConfig");
// Names of the config files. From lowest to highest priority.
const EXPO_MODULE_CONFIG_FILENAMES = ['unimodule.json', 'expo-module.config.json'];
/**
* Searches for modules to link based on given config.
*/
async function findModulesAsync(providedOptions) {
const options = await (0, mergeLinkingOptions_1.mergeLinkingOptionsAsync)(providedOptions);
const results = new Map();
const nativeModuleNames = new Set();
// custom native modules should be resolved first so that they can override other modules
const searchPaths = new Set(options.nativeModulesDir && fs_extra_1.default.existsSync(options.nativeModulesDir)
? [options.nativeModulesDir, ...options.searchPaths]
: options.searchPaths);
// `searchPaths` can be mutated to discover all "isolated modules groups", when using isolated modules
for (const searchPath of searchPaths) {
const isNativeModulesDir = searchPath === options.nativeModulesDir;
const packageConfigPaths = await findPackagesConfigPathsAsync(searchPath);
for (const packageConfigPath of packageConfigPaths) {
const packagePath = await fs_extra_1.default.realpath(path_1.default.join(searchPath, path_1.default.dirname(packageConfigPath)));
const expoModuleConfig = (0, ExpoModuleConfig_1.requireAndResolveExpoModuleConfig)(path_1.default.join(packagePath, path_1.default.basename(packageConfigPath)));
const { name, version } = resolvePackageNameAndVersion(packagePath, {
fallbackToDirName: isNativeModulesDir,
});
const maybeIsolatedModulesPath = (0, utils_1.getIsolatedModulesPath)(packagePath, name);
if (maybeIsolatedModulesPath) {
searchPaths.add(maybeIsolatedModulesPath);
}
// we ignore the `exclude` option for custom native modules
if ((!isNativeModulesDir && options.exclude?.includes(name)) ||
!expoModuleConfig.supportsPlatform(options.platform)) {
continue;
}
// add the current revision to the results
const currentRevision = {
path: packagePath,
version,
config: expoModuleConfig,
};
addRevisionToResults(results, name, currentRevision);
// if the module is a native module, we need to add it to the nativeModuleNames set
if (isNativeModulesDir && !nativeModuleNames.has(name)) {
nativeModuleNames.add(name);
}
}
}
const searchResults = Object.fromEntries(results.entries());
// It doesn't make much sense to strip modules if there is only one search path.
// (excluding custom native modules path)
// Workspace root usually doesn't specify all its dependencies (see Expo Go),
// so in this case we should link everything.
if (options.searchPaths.length <= 1 || options.onlyProjectDeps === false) {
return searchResults;
}
return await filterToProjectDependenciesAsync(searchResults, {
...providedOptions,
// Custom native modules are not filtered out
// when they're not specified in package.json dependencies.
alwaysIncludedPackagesNames: nativeModuleNames,
});
}
exports.findModulesAsync = findModulesAsync;
/**
* Returns the priority of the config at given path. Higher number means higher priority.
*/
function configPriority(fullpath) {
return EXPO_MODULE_CONFIG_FILENAMES.indexOf(path_1.default.basename(fullpath));
}
/**
* Adds {@link revision} to the {@link results} map
* or to package duplicates if it already exists.
* @param results [mutable] yet resolved packages map
* @param name resolved package name
* @param revision resolved package revision
*/
function addRevisionToResults(results, name, revision) {
if (!results.has(name)) {
// The revision that was found first will be the main one.
// An array of duplicates and the config are needed only here.
results.set(name, {
...revision,
duplicates: [],
});
}
else if (results.get(name)?.path !== revision.path &&
results.get(name)?.duplicates?.every(({ path }) => path !== revision.path)) {
const { config, duplicates, ...duplicateEntry } = revision;
results.get(name)?.duplicates?.push(duplicateEntry);
}
}
/**
* Returns paths to the highest priority config files, relative to the {@link searchPath}.
* @example
* ```
* // Given the following file exists: /foo/myapp/modules/mymodule/expo-module.config.json
* await findPackagesConfigPathsAsync('/foo/myapp/modules');
* // returns ['mymodule/expo-module.config.json']
*
* await findPackagesConfigPathsAsync('/foo/myapp/modules/mymodule');
* // returns ['expo-module.config.json']
* ```
*/
async function findPackagesConfigPathsAsync(searchPath) {
const bracedFilenames = '{' + EXPO_MODULE_CONFIG_FILENAMES.join(',') + '}';
const paths = await (0, fast_glob_1.default)([`*/${bracedFilenames}`, `@*/*/${bracedFilenames}`, `./${bracedFilenames}`], {
cwd: searchPath,
});
// If the package has multiple configs (e.g. `unimodule.json` and `expo-module.config.json` during the transition time)
// then we want to give `expo-module.config.json` the priority.
return Object.values(paths.reduce((acc, configPath) => {
const dirname = path_1.default.dirname(configPath);
if (!acc[dirname] || configPriority(configPath) > configPriority(acc[dirname])) {
acc[dirname] = configPath;
}
return acc;
}, {}));
}
/**
* Resolves package name and version for the given {@link packagePath} from its `package.json`.
* if {@link fallbackToDirName} is true, it returns the dir name when `package.json` doesn't exist.
* @returns object with `name` and `version` properties. `version` falls back to `UNVERSIONED` if cannot be resolved.
*/
function resolvePackageNameAndVersion(packagePath, { fallbackToDirName } = {}) {
try {
const { name, version } = require(path_1.default.join(packagePath, 'package.json'));
return { name, version: version || 'UNVERSIONED' };
}
catch (e) {
if (fallbackToDirName) {
// we don't have the package.json name, so we'll use the directory name
return {
name: path_1.default.basename(packagePath),
version: 'UNVERSIONED',
};
}
else {
throw e;
}
}
}
/**
* Filters out packages that are not the dependencies of the project.
*/
async function filterToProjectDependenciesAsync(results, options) {
const filteredResults = {};
const visitedPackages = new Set();
// iterate through always included package names and add them to the visited packages
// if the results contains them
for (const name of options.alwaysIncludedPackagesNames ?? []) {
if (results[name] && !visitedPackages.has(name)) {
filteredResults[name] = results[name];
visitedPackages.add(name);
}
}
// Helper for traversing the dependency hierarchy.
function visitPackage(packageJsonPath) {
const packageJson = require(packageJsonPath);
// Prevent getting into the recursive loop.
if (visitedPackages.has(packageJson.name)) {
return;
}
visitedPackages.add(packageJson.name);
// Iterate over the dependencies to find transitive modules.
for (const dependencyName in packageJson.dependencies) {
const dependencyResult = results[dependencyName];
if (!filteredResults[dependencyName]) {
let dependencyPackageJsonPath;
if (dependencyResult) {
filteredResults[dependencyName] = dependencyResult;
dependencyPackageJsonPath = path_1.default.join(dependencyResult.path, 'package.json');
}
else {
try {
/**
* Custom `require` that resolves from the current working dir instead of this script path.
* **Requires Node v12.2.0**
*/
const projectRequire = (0, module_1.createRequire)(packageJsonPath);
dependencyPackageJsonPath = projectRequire.resolve(`${dependencyName}/package.json`);
}
catch (error) {
// Some packages don't include package.json in its `exports` field,
// but none of our packages do that, so it seems fine to just ignore that type of error.
// Related issue: https://github.com/react-native-community/cli/issues/1168
if (!options.silent && error.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
console.warn(chalk_1.default.yellow(`⚠️ Cannot resolve the path to "${dependencyName}" package.`));
}
continue;
}
}
// Visit the dependency package.
visitPackage(dependencyPackageJsonPath);
}
}
}
// Visit project's package.
const projectPackageJsonPath = await (0, mergeLinkingOptions_1.getProjectPackageJsonPathAsync)(options.projectRoot);
visitPackage(projectPackageJsonPath);
return filteredResults;
}
//# sourceMappingURL=findModules.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import { GenerateOptions, ModuleDescriptor } from '../types';
/**
* Generates a source file listing all packages to link.
* Right know it works only for Android platform.
*/
export declare function generatePackageListAsync(modules: ModuleDescriptor[], options: GenerateOptions): Promise<void>;

View File

@@ -0,0 +1,24 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generatePackageListAsync = void 0;
const chalk_1 = __importDefault(require("chalk"));
const utils_1 = require("./utils");
/**
* Generates a source file listing all packages to link.
* Right know it works only for Android platform.
*/
async function generatePackageListAsync(modules, options) {
try {
const platformLinking = (0, utils_1.getLinkingImplementationForPlatform)(options.platform);
await platformLinking.generatePackageListAsync(modules, options.target, options.namespace);
}
catch (e) {
console.error(chalk_1.default.red(`Generating package list is not available for platform: ${options.platform}`));
throw e;
}
}
exports.generatePackageListAsync = generatePackageListAsync;
//# sourceMappingURL=generatePackageList.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"generatePackageList.js","sourceRoot":"","sources":["../../src/autolinking/generatePackageList.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA0B;AAE1B,mCAA8D;AAG9D;;;GAGG;AACI,KAAK,UAAU,wBAAwB,CAC5C,OAA2B,EAC3B,OAAwB;IAExB,IAAI;QACF,MAAM,eAAe,GAAG,IAAA,2CAAmC,EAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC9E,MAAM,eAAe,CAAC,wBAAwB,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;KAC5F;IAAC,OAAO,CAAC,EAAE;QACV,OAAO,CAAC,KAAK,CACX,eAAK,CAAC,GAAG,CAAC,0DAA0D,OAAO,CAAC,QAAQ,EAAE,CAAC,CACxF,CAAC;QACF,MAAM,CAAC,CAAC;KACT;AACH,CAAC;AAbD,4DAaC","sourcesContent":["import chalk from 'chalk';\n\nimport { getLinkingImplementationForPlatform } from './utils';\nimport { GenerateOptions, ModuleDescriptor } from '../types';\n\n/**\n * Generates a source file listing all packages to link.\n * Right know it works only for Android platform.\n */\nexport async function generatePackageListAsync(\n modules: ModuleDescriptor[],\n options: GenerateOptions\n) {\n try {\n const platformLinking = getLinkingImplementationForPlatform(options.platform);\n await platformLinking.generatePackageListAsync(modules, options.target, options.namespace);\n } catch (e) {\n console.error(\n chalk.red(`Generating package list is not available for platform: ${options.platform}`)\n );\n throw e;\n }\n}\n"]}

View File

@@ -0,0 +1,12 @@
import { findModulesAsync } from './findModules';
import { getProjectPackageJsonPathAsync, mergeLinkingOptionsAsync, resolveSearchPathsAsync } from './mergeLinkingOptions';
import { resolveExtraBuildDependenciesAsync, resolveModulesAsync } from './resolveModules';
import type { ModuleDescriptor, SearchOptions } from '../types';
export { findModulesAsync, getProjectPackageJsonPathAsync, mergeLinkingOptionsAsync, resolveExtraBuildDependenciesAsync, resolveModulesAsync, resolveSearchPathsAsync, };
export { generatePackageListAsync } from './generatePackageList';
export { verifySearchResults } from './verifySearchResults';
export * from '../types';
/**
* Programmatic API to query autolinked modules for a project.
*/
export declare function queryAutolinkingModulesFromProjectAsync(projectRoot: string, options: Pick<SearchOptions, 'platform' | 'exclude' | 'onlyProjectDeps'>): Promise<ModuleDescriptor[]>;

View File

@@ -0,0 +1,42 @@
"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.queryAutolinkingModulesFromProjectAsync = exports.verifySearchResults = exports.generatePackageListAsync = exports.resolveSearchPathsAsync = exports.resolveModulesAsync = exports.resolveExtraBuildDependenciesAsync = exports.mergeLinkingOptionsAsync = exports.getProjectPackageJsonPathAsync = exports.findModulesAsync = void 0;
const findModules_1 = require("./findModules");
Object.defineProperty(exports, "findModulesAsync", { enumerable: true, get: function () { return findModules_1.findModulesAsync; } });
const mergeLinkingOptions_1 = require("./mergeLinkingOptions");
Object.defineProperty(exports, "getProjectPackageJsonPathAsync", { enumerable: true, get: function () { return mergeLinkingOptions_1.getProjectPackageJsonPathAsync; } });
Object.defineProperty(exports, "mergeLinkingOptionsAsync", { enumerable: true, get: function () { return mergeLinkingOptions_1.mergeLinkingOptionsAsync; } });
Object.defineProperty(exports, "resolveSearchPathsAsync", { enumerable: true, get: function () { return mergeLinkingOptions_1.resolveSearchPathsAsync; } });
const resolveModules_1 = require("./resolveModules");
Object.defineProperty(exports, "resolveExtraBuildDependenciesAsync", { enumerable: true, get: function () { return resolveModules_1.resolveExtraBuildDependenciesAsync; } });
Object.defineProperty(exports, "resolveModulesAsync", { enumerable: true, get: function () { return resolveModules_1.resolveModulesAsync; } });
var generatePackageList_1 = require("./generatePackageList");
Object.defineProperty(exports, "generatePackageListAsync", { enumerable: true, get: function () { return generatePackageList_1.generatePackageListAsync; } });
var verifySearchResults_1 = require("./verifySearchResults");
Object.defineProperty(exports, "verifySearchResults", { enumerable: true, get: function () { return verifySearchResults_1.verifySearchResults; } });
__exportStar(require("../types"), exports);
/**
* Programmatic API to query autolinked modules for a project.
*/
async function queryAutolinkingModulesFromProjectAsync(projectRoot, options) {
const searchPaths = await (0, mergeLinkingOptions_1.resolveSearchPathsAsync)(null, projectRoot);
const linkOptions = await (0, mergeLinkingOptions_1.mergeLinkingOptionsAsync)({ ...options, projectRoot, searchPaths });
const searchResults = await (0, findModules_1.findModulesAsync)(linkOptions);
return await (0, resolveModules_1.resolveModulesAsync)(searchResults, linkOptions);
}
exports.queryAutolinkingModulesFromProjectAsync = queryAutolinkingModulesFromProjectAsync;
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/autolinking/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,+CAAiD;AAU/C,iGAVO,8BAAgB,OAUP;AATlB,+DAI+B;AAM7B,+GATA,oDAA8B,OASA;AAC9B,yGATA,8CAAwB,OASA;AAGxB,wGAXA,6CAAuB,OAWA;AATzB,qDAA2F;AAOzF,mHAPO,mDAAkC,OAOP;AAClC,oGAR2C,oCAAmB,OAQ3C;AAGrB,6DAAiE;AAAxD,+HAAA,wBAAwB,OAAA;AACjC,6DAA4D;AAAnD,0HAAA,mBAAmB,OAAA;AAC5B,2CAAyB;AAEzB;;GAEG;AACI,KAAK,UAAU,uCAAuC,CAC3D,WAAmB,EACnB,OAAwE;IAExE,MAAM,WAAW,GAAG,MAAM,IAAA,6CAAuB,EAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACrE,MAAM,WAAW,GAAG,MAAM,IAAA,8CAAwB,EAAC,EAAE,GAAG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC,CAAC;IAC7F,MAAM,aAAa,GAAG,MAAM,IAAA,8BAAgB,EAAC,WAAW,CAAC,CAAC;IAC1D,OAAO,MAAM,IAAA,oCAAmB,EAAC,aAAa,EAAE,WAAW,CAAC,CAAC;AAC/D,CAAC;AARD,0FAQC","sourcesContent":["import { findModulesAsync } from './findModules';\nimport {\n getProjectPackageJsonPathAsync,\n mergeLinkingOptionsAsync,\n resolveSearchPathsAsync,\n} from './mergeLinkingOptions';\nimport { resolveExtraBuildDependenciesAsync, resolveModulesAsync } from './resolveModules';\nimport type { ModuleDescriptor, SearchOptions } from '../types';\n\nexport {\n findModulesAsync,\n getProjectPackageJsonPathAsync,\n mergeLinkingOptionsAsync,\n resolveExtraBuildDependenciesAsync,\n resolveModulesAsync,\n resolveSearchPathsAsync,\n};\nexport { generatePackageListAsync } from './generatePackageList';\nexport { verifySearchResults } from './verifySearchResults';\nexport * from '../types';\n\n/**\n * Programmatic API to query autolinked modules for a project.\n */\nexport async function queryAutolinkingModulesFromProjectAsync(\n projectRoot: string,\n options: Pick<SearchOptions, 'platform' | 'exclude' | 'onlyProjectDeps'>\n): Promise<ModuleDescriptor[]> {\n const searchPaths = await resolveSearchPathsAsync(null, projectRoot);\n const linkOptions = await mergeLinkingOptionsAsync({ ...options, projectRoot, searchPaths });\n const searchResults = await findModulesAsync(linkOptions);\n return await resolveModulesAsync(searchResults, linkOptions);\n}\n"]}

View File

@@ -0,0 +1,17 @@
import { SearchOptions } from '../types';
/**
* Find the path to the `package.json` of the closest project in the given project root.
*/
export declare function getProjectPackageJsonPathAsync(projectRoot: string): Promise<string>;
/**
* Merges autolinking options from different sources (the later the higher priority)
* - options defined in package.json's `expo.autolinking` field
* - platform-specific options from the above (e.g. `expo.autolinking.apple`)
* - options provided to the CLI command
*/
export declare function mergeLinkingOptionsAsync<OptionsType extends SearchOptions>(providedOptions: OptionsType): Promise<OptionsType>;
/**
* Resolves autolinking search paths. If none is provided, it accumulates all node_modules when
* going up through the path components. This makes workspaces work out-of-the-box without any configs.
*/
export declare function resolveSearchPathsAsync(searchPaths: string[] | null, cwd: string): Promise<string[]>;

View File

@@ -0,0 +1,91 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveSearchPathsAsync = exports.mergeLinkingOptionsAsync = exports.getProjectPackageJsonPathAsync = void 0;
const find_up_1 = __importDefault(require("find-up"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
/**
* Find the path to the `package.json` of the closest project in the given project root.
*/
async function getProjectPackageJsonPathAsync(projectRoot) {
const result = await (0, find_up_1.default)('package.json', { cwd: projectRoot });
if (!result) {
throw new Error(`Couldn't find "package.json" up from path "${projectRoot}"`);
}
return result;
}
exports.getProjectPackageJsonPathAsync = getProjectPackageJsonPathAsync;
/**
* Merges autolinking options from different sources (the later the higher priority)
* - options defined in package.json's `expo.autolinking` field
* - platform-specific options from the above (e.g. `expo.autolinking.apple`)
* - options provided to the CLI command
*/
async function mergeLinkingOptionsAsync(providedOptions) {
const packageJson = require(await getProjectPackageJsonPathAsync(providedOptions.projectRoot));
const baseOptions = packageJson.expo?.autolinking;
const platformOptions = getPlatformOptions(providedOptions.platform, baseOptions);
const finalOptions = Object.assign({}, baseOptions, platformOptions, providedOptions);
// Makes provided paths absolute or falls back to default paths if none was provided.
finalOptions.searchPaths = await resolveSearchPathsAsync(finalOptions.searchPaths, providedOptions.projectRoot);
finalOptions.nativeModulesDir = await resolveNativeModulesDirAsync(finalOptions.nativeModulesDir, providedOptions.projectRoot);
return finalOptions;
}
exports.mergeLinkingOptionsAsync = mergeLinkingOptionsAsync;
/**
* Resolves autolinking search paths. If none is provided, it accumulates all node_modules when
* going up through the path components. This makes workspaces work out-of-the-box without any configs.
*/
async function resolveSearchPathsAsync(searchPaths, cwd) {
return searchPaths && searchPaths.length > 0
? searchPaths.map((searchPath) => path_1.default.resolve(cwd, searchPath))
: await findDefaultPathsAsync(cwd);
}
exports.resolveSearchPathsAsync = resolveSearchPathsAsync;
/**
* Looks up for workspace's `node_modules` paths.
*/
async function findDefaultPathsAsync(cwd) {
const paths = [];
let dir = cwd;
let pkgJsonPath;
while ((pkgJsonPath = await (0, find_up_1.default)('package.json', { cwd: dir }))) {
dir = path_1.default.dirname(path_1.default.dirname(pkgJsonPath));
paths.push(path_1.default.join(pkgJsonPath, '..', 'node_modules'));
// This stops the infinite loop when the package.json is placed at the root dir.
if (path_1.default.dirname(dir) === dir) {
break;
}
}
return paths;
}
/**
* Finds the real path to custom native modules directory.
* - When {@link cwd} is inside the project directory, the path is searched relatively
* to the project root (directory with the `package.json` file).
* - When {@link cwd} is outside project directory (no `package.json` found), it is relative to
* the current working directory (the {@link cwd} param).
*
* @param nativeModulesDir path to custom native modules directory. Defaults to `"./modules"` if null.
* @param cwd current working directory
* @returns resolved native modules directory or `null` if it is not found or doesn't exist.
*/
async function resolveNativeModulesDirAsync(nativeModulesDir, cwd) {
const packageJsonPath = await (0, find_up_1.default)('package.json', { cwd });
const projectRoot = packageJsonPath != null ? path_1.default.join(packageJsonPath, '..') : cwd;
const resolvedPath = path_1.default.resolve(projectRoot, nativeModulesDir || 'modules');
return fs_extra_1.default.existsSync(resolvedPath) ? resolvedPath : null;
}
/**
* Gets the platform-specific autolinking options from the base options.
*/
function getPlatformOptions(platform, options) {
if (platform === 'apple') {
return options?.apple ?? options?.ios ?? {};
}
return options?.[platform] ?? {};
}
//# sourceMappingURL=mergeLinkingOptions.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
import type { ExtraDependencies, ModuleDescriptor, ResolveOptions, SearchResults } from '../types';
/**
* Resolves search results to a list of platform-specific configuration.
*/
export declare function resolveModulesAsync(searchResults: SearchResults, options: ResolveOptions): Promise<ModuleDescriptor[]>;
/**
* Resolves the extra build dependencies for the project, such as additional Maven repositories or CocoaPods pods.
*/
export declare function resolveExtraBuildDependenciesAsync(options: ResolveOptions): Promise<ExtraDependencies>;

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveExtraBuildDependenciesAsync = exports.resolveModulesAsync = void 0;
const utils_1 = require("./utils");
/**
* Resolves search results to a list of platform-specific configuration.
*/
async function resolveModulesAsync(searchResults, options) {
const platformLinking = (0, utils_1.getLinkingImplementationForPlatform)(options.platform);
return (await Promise.all(Object.entries(searchResults).map(async ([packageName, revision]) => {
const resolvedModule = await platformLinking.resolveModuleAsync(packageName, revision, options);
return resolvedModule
? {
packageName,
packageVersion: revision.version,
...resolvedModule,
}
: null;
})))
.filter(Boolean)
.sort((a, b) => a.packageName.localeCompare(b.packageName));
}
exports.resolveModulesAsync = resolveModulesAsync;
/**
* Resolves the extra build dependencies for the project, such as additional Maven repositories or CocoaPods pods.
*/
async function resolveExtraBuildDependenciesAsync(options) {
const platformLinking = (0, utils_1.getLinkingImplementationForPlatform)(options.platform);
const extraDependencies = await platformLinking.resolveExtraBuildDependenciesAsync(options.projectRoot);
return extraDependencies ?? [];
}
exports.resolveExtraBuildDependenciesAsync = resolveExtraBuildDependenciesAsync;
//# sourceMappingURL=resolveModules.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"resolveModules.js","sourceRoot":"","sources":["../../src/autolinking/resolveModules.ts"],"names":[],"mappings":";;;AAAA,mCAA8D;AAG9D;;GAEG;AACI,KAAK,UAAU,mBAAmB,CACvC,aAA4B,EAC5B,OAAuB;IAEvB,MAAM,eAAe,GAAG,IAAA,2CAAmC,EAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAE9E,OAAO,CACL,MAAM,OAAO,CAAC,GAAG,CACf,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,QAAQ,CAAC,EAAE,EAAE;QAClE,MAAM,cAAc,GAAG,MAAM,eAAe,CAAC,kBAAkB,CAC7D,WAAW,EACX,QAAQ,EACR,OAAO,CACR,CAAC;QACF,OAAO,cAAc;YACnB,CAAC,CAAC;gBACE,WAAW;gBACX,cAAc,EAAE,QAAQ,CAAC,OAAO;gBAChC,GAAG,cAAc;aAClB;YACH,CAAC,CAAC,IAAI,CAAC;IACX,CAAC,CAAC,CACH,CACF;SACE,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC;AAChE,CAAC;AA1BD,kDA0BC;AAED;;GAEG;AACI,KAAK,UAAU,kCAAkC,CACtD,OAAuB;IAEvB,MAAM,eAAe,GAAG,IAAA,2CAAmC,EAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC9E,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC,kCAAkC,CAChF,OAAO,CAAC,WAAW,CACpB,CAAC;IACF,OAAO,iBAAiB,IAAI,EAAE,CAAC;AACjC,CAAC;AARD,gFAQC","sourcesContent":["import { getLinkingImplementationForPlatform } from './utils';\nimport type { ExtraDependencies, ModuleDescriptor, ResolveOptions, SearchResults } from '../types';\n\n/**\n * Resolves search results to a list of platform-specific configuration.\n */\nexport async function resolveModulesAsync(\n searchResults: SearchResults,\n options: ResolveOptions\n): Promise<ModuleDescriptor[]> {\n const platformLinking = getLinkingImplementationForPlatform(options.platform);\n\n return (\n await Promise.all(\n Object.entries(searchResults).map(async ([packageName, revision]) => {\n const resolvedModule = await platformLinking.resolveModuleAsync(\n packageName,\n revision,\n options\n );\n return resolvedModule\n ? {\n packageName,\n packageVersion: revision.version,\n ...resolvedModule,\n }\n : null;\n })\n )\n )\n .filter(Boolean)\n .sort((a, b) => a.packageName.localeCompare(b.packageName));\n}\n\n/**\n * Resolves the extra build dependencies for the project, such as additional Maven repositories or CocoaPods pods.\n */\nexport async function resolveExtraBuildDependenciesAsync(\n options: ResolveOptions\n): Promise<ExtraDependencies> {\n const platformLinking = getLinkingImplementationForPlatform(options.platform);\n const extraDependencies = await platformLinking.resolveExtraBuildDependenciesAsync(\n options.projectRoot\n );\n return extraDependencies ?? [];\n}\n"]}

View File

@@ -0,0 +1,6 @@
import { SupportedPlatform } from '../types';
export declare function getLinkingImplementationForPlatform(platform: SupportedPlatform): any;
/**
* Get the possible path to the pnpm isolated modules folder.
*/
export declare function getIsolatedModulesPath(packagePath: string, packageName: string): string | null;

View File

@@ -0,0 +1,40 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getIsolatedModulesPath = exports.getLinkingImplementationForPlatform = void 0;
const path_1 = __importDefault(require("path"));
function getLinkingImplementationForPlatform(platform) {
switch (platform) {
case 'ios':
case 'macos':
case 'tvos':
case 'apple':
return require('../platforms/apple');
case 'android':
return require('../platforms/android');
case 'devtools':
return require('../platforms/devtools');
}
}
exports.getLinkingImplementationForPlatform = getLinkingImplementationForPlatform;
/**
* Get the possible path to the pnpm isolated modules folder.
*/
function getIsolatedModulesPath(packagePath, packageName) {
// Check if the project is using isolated modules, by checking
// if the parent dir of `packagePath` is a `node_modules` folder.
// Isolated modules installs dependencies in small groups such as:
// - /.pnpm/expo@50.x.x(...)/node_modules/@expo/cli
// - /.pnpm/expo@50.x.x(...)/node_modules/expo
// - /.pnpm/expo@50.x.x(...)/node_modules/expo-application
// When isolated modules are detected, expand the `searchPaths`
// to include possible nested dependencies.
const maybeIsolatedModulesPath = path_1.default.join(packagePath, packageName.startsWith('@') && packageName.includes('/') ? '../..' : '..' // scoped packages are nested deeper
);
const isIsolatedModulesPath = path_1.default.basename(maybeIsolatedModulesPath) === 'node_modules';
return isIsolatedModulesPath ? maybeIsolatedModulesPath : null;
}
exports.getIsolatedModulesPath = getIsolatedModulesPath;
//# sourceMappingURL=utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/autolinking/utils.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AAIxB,SAAgB,mCAAmC,CAAC,QAA2B;IAC7E,QAAQ,QAAQ,EAAE;QAChB,KAAK,KAAK,CAAC;QACX,KAAK,OAAO,CAAC;QACb,KAAK,MAAM,CAAC;QACZ,KAAK,OAAO;YACV,OAAO,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACvC,KAAK,SAAS;YACZ,OAAO,OAAO,CAAC,sBAAsB,CAAC,CAAC;QACzC,KAAK,UAAU;YACb,OAAO,OAAO,CAAC,uBAAuB,CAAC,CAAC;KAC3C;AACH,CAAC;AAZD,kFAYC;AAED;;GAEG;AACH,SAAgB,sBAAsB,CAAC,WAAmB,EAAE,WAAmB;IAC7E,8DAA8D;IAC9D,iEAAiE;IACjE,kEAAkE;IAClE,qDAAqD;IACrD,gDAAgD;IAChD,4DAA4D;IAC5D,+DAA+D;IAC/D,2CAA2C;IAC3C,MAAM,wBAAwB,GAAG,cAAI,CAAC,IAAI,CACxC,WAAW,EACX,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,oCAAoC;KAC/G,CAAC;IACF,MAAM,qBAAqB,GAAG,cAAI,CAAC,QAAQ,CAAC,wBAAwB,CAAC,KAAK,cAAc,CAAC;IACzF,OAAO,qBAAqB,CAAC,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC;AACjE,CAAC;AAfD,wDAeC","sourcesContent":["import path from 'path';\n\nimport { SupportedPlatform } from '../types';\n\nexport function getLinkingImplementationForPlatform(platform: SupportedPlatform) {\n switch (platform) {\n case 'ios':\n case 'macos':\n case 'tvos':\n case 'apple':\n return require('../platforms/apple');\n case 'android':\n return require('../platforms/android');\n case 'devtools':\n return require('../platforms/devtools');\n }\n}\n\n/**\n * Get the possible path to the pnpm isolated modules folder.\n */\nexport function getIsolatedModulesPath(packagePath: string, packageName: string): string | null {\n // Check if the project is using isolated modules, by checking\n // if the parent dir of `packagePath` is a `node_modules` folder.\n // Isolated modules installs dependencies in small groups such as:\n // - /.pnpm/expo@50.x.x(...)/node_modules/@expo/cli\n // - /.pnpm/expo@50.x.x(...)/node_modules/expo\n // - /.pnpm/expo@50.x.x(...)/node_modules/expo-application\n // When isolated modules are detected, expand the `searchPaths`\n // to include possible nested dependencies.\n const maybeIsolatedModulesPath = path.join(\n packagePath,\n packageName.startsWith('@') && packageName.includes('/') ? '../..' : '..' // scoped packages are nested deeper\n );\n const isIsolatedModulesPath = path.basename(maybeIsolatedModulesPath) === 'node_modules';\n return isIsolatedModulesPath ? maybeIsolatedModulesPath : null;\n}\n"]}

View File

@@ -0,0 +1,5 @@
import { SearchOptions, SearchResults } from '../types';
/**
* Verifies the search results by checking whether there are no duplicates.
*/
export declare function verifySearchResults(searchResults: SearchResults, options: SearchOptions): number;

View File

@@ -0,0 +1,32 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifySearchResults = void 0;
const chalk_1 = __importDefault(require("chalk"));
const path_1 = __importDefault(require("path"));
/**
* Verifies the search results by checking whether there are no duplicates.
*/
function verifySearchResults(searchResults, options) {
const relativePath = (pkg) => path_1.default.relative(options.projectRoot, pkg.path);
let counter = 0;
for (const moduleName in searchResults) {
const revision = searchResults[moduleName];
if (revision.duplicates?.length) {
console.warn(`⚠️ Found multiple revisions of ${chalk_1.default.green(moduleName)}`);
console.log(` - ${chalk_1.default.magenta(relativePath(revision))} (${chalk_1.default.cyan(revision.version)})`);
for (const duplicate of revision.duplicates) {
console.log(` - ${chalk_1.default.gray(relativePath(duplicate))} (${chalk_1.default.gray(duplicate.version)})`);
}
counter++;
}
}
if (counter > 0) {
console.warn('⚠️ Please get rid of multiple revisions as it may introduce some side effects or compatibility issues');
}
return counter;
}
exports.verifySearchResults = verifySearchResults;
//# sourceMappingURL=verifySearchResults.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"verifySearchResults.js","sourceRoot":"","sources":["../../src/autolinking/verifySearchResults.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA0B;AAC1B,gDAAwB;AAIxB;;GAEG;AACH,SAAgB,mBAAmB,CAAC,aAA4B,EAAE,OAAsB;IACtF,MAAM,YAAY,GAAqC,CAAC,GAAG,EAAE,EAAE,CAC7D,cAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAC/C,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,KAAK,MAAM,UAAU,IAAI,aAAa,EAAE;QACtC,MAAM,QAAQ,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;QAE3C,IAAI,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE;YAC/B,OAAO,CAAC,IAAI,CAAC,mCAAmC,eAAK,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAC3E,OAAO,CAAC,GAAG,CAAC,MAAM,eAAK,CAAC,OAAO,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,KAAK,eAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAE7F,KAAK,MAAM,SAAS,IAAI,QAAQ,CAAC,UAAU,EAAE;gBAC3C,OAAO,CAAC,GAAG,CAAC,MAAM,eAAK,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC,KAAK,eAAK,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;aAC7F;YACD,OAAO,EAAE,CAAC;SACX;KACF;IACD,IAAI,OAAO,GAAG,CAAC,EAAE;QACf,OAAO,CAAC,IAAI,CACV,wGAAwG,CACzG,CAAC;KACH;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAxBD,kDAwBC","sourcesContent":["import chalk from 'chalk';\nimport path from 'path';\n\nimport { PackageRevision, SearchOptions, SearchResults } from '../types';\n\n/**\n * Verifies the search results by checking whether there are no duplicates.\n */\nexport function verifySearchResults(searchResults: SearchResults, options: SearchOptions): number {\n const relativePath: (pkg: PackageRevision) => string = (pkg) =>\n path.relative(options.projectRoot, pkg.path);\n let counter = 0;\n\n for (const moduleName in searchResults) {\n const revision = searchResults[moduleName];\n\n if (revision.duplicates?.length) {\n console.warn(`⚠️ Found multiple revisions of ${chalk.green(moduleName)}`);\n console.log(` - ${chalk.magenta(relativePath(revision))} (${chalk.cyan(revision.version)})`);\n\n for (const duplicate of revision.duplicates) {\n console.log(` - ${chalk.gray(relativePath(duplicate))} (${chalk.gray(duplicate.version)})`);\n }\n counter++;\n }\n }\n if (counter > 0) {\n console.warn(\n '⚠️ Please get rid of multiple revisions as it may introduce some side effects or compatibility issues'\n );\n }\n return counter;\n}\n"]}

View File

@@ -0,0 +1 @@
export {};

View File

@@ -0,0 +1,129 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const commander_1 = __importDefault(require("commander"));
const path_1 = __importDefault(require("path"));
const ReactImportsPatcher_1 = require("./ReactImportsPatcher");
const autolinking_1 = require("./autolinking");
const reactNativeConfig_1 = require("./reactNativeConfig");
/**
* Registers a command that only searches for available expo modules.
*/
function registerSearchCommand(commandName, fn) {
return commander_1.default
.command(`${commandName} [paths...]`)
.option('-i, --ignore-paths <ignorePaths...>', 'Paths to ignore when looking up for modules.', (value, previous) => (previous ?? []).concat(value))
.option('-e, --exclude <exclude...>', 'Package names to exclude when looking up for modules.', (value, previous) => (previous ?? []).concat(value))
.option('-p, --platform [platform]', 'The platform that the resulting modules must support. Available options: "apple", "android"', 'apple')
.option('--silent', 'Silence resolution warnings')
.addOption(new commander_1.default.Option('--project-root <projectRoot>', 'The path to the root of the project').default(process.cwd(), 'process.cwd()'))
.option('--only-project-deps', 'For a monorepo, include only modules that are the project dependencies.', true)
.option('--no-only-project-deps', 'Opposite of --only-project-deps', false)
.action(async (searchPaths, providedOptions) => {
const options = await (0, autolinking_1.mergeLinkingOptionsAsync)({
...providedOptions,
searchPaths,
});
const searchResults = await (0, autolinking_1.findModulesAsync)(options);
return await fn(searchResults, options);
});
}
/**
* Registers a command that searches for modules and then resolves them for specific platform.
*/
function registerResolveCommand(commandName, fn) {
return registerSearchCommand(commandName, fn);
}
// Register for `patch-react-imports` command
function registerPatchReactImportsCommand() {
return commander_1.default
.command('patch-react-imports [paths...]')
.requiredOption('--pods-root <podsRoot>', 'The path to `Pods` directory')
.option('--dry-run', 'Only list files without writing changes to the file system')
.action(ReactImportsPatcher_1.patchReactImportsAsync);
}
/**
* Registry the `react-native-config` command.
*/
function registerReactNativeConfigCommand() {
return commander_1.default
.command('react-native-config [paths...]')
.option('-p, --platform [platform]', 'The platform that the resulting modules must support. Available options: "android", "ios"', 'ios')
.addOption(new commander_1.default.Option('--project-root <projectRoot>', 'The path to the root of the project').default(process.cwd(), 'process.cwd()'))
.option('-j, --json', 'Output results in the plain JSON format.', () => true, false)
.action(async (paths, options) => {
if (!['android', 'ios'].includes(options.platform)) {
throw new Error(`Unsupported platform: ${options.platform}`);
}
const projectRoot = path_1.default.dirname(await (0, autolinking_1.getProjectPackageJsonPathAsync)(options.projectRoot));
const searchPaths = await (0, autolinking_1.resolveSearchPathsAsync)(paths, projectRoot);
const providedOptions = {
platform: options.platform,
projectRoot,
searchPaths,
};
const results = await (0, reactNativeConfig_1.createReactNativeConfigAsync)(providedOptions);
if (options.json) {
console.log(JSON.stringify(results));
}
else {
console.log(require('util').inspect(results, false, null, true));
}
});
}
module.exports = async function (args) {
// Searches for available expo modules.
registerSearchCommand('search', async (results, options) => {
if (options.json) {
console.log(JSON.stringify(results));
}
else {
console.log(require('util').inspect(results, false, null, true));
}
}).option('-j, --json', 'Output results in the plain JSON format.', () => true, false);
// Checks whether there are no resolving issues in the current setup.
registerSearchCommand('verify', (results, options) => {
const numberOfDuplicates = (0, autolinking_1.verifySearchResults)(results, options);
if (!numberOfDuplicates) {
console.log('✅ Everything is fine!');
}
});
// Searches for available expo modules and resolves the results for given platform.
registerResolveCommand('resolve', async (results, options) => {
const modules = await (0, autolinking_1.resolveModulesAsync)(results, options);
const extraDependencies = await (0, autolinking_1.resolveExtraBuildDependenciesAsync)(options);
if (options.json) {
console.log(JSON.stringify({ extraDependencies, modules }));
}
else {
console.log(require('util').inspect({ extraDependencies, modules }, false, null, true));
}
}).option('-j, --json', 'Output results in the plain JSON format.', () => true, false);
// Generates a source file listing all packages to link.
// It's deprecated, use `generate-modules-provider` instead.
registerResolveCommand('generate-package-list', async (results, options) => {
const modules = options.empty ? [] : await (0, autolinking_1.resolveModulesAsync)(results, options);
(0, autolinking_1.generatePackageListAsync)(modules, options);
})
.option('-t, --target <path>', 'Path to the target file, where the package list should be written to.')
.option('-n, --namespace <namespace>', 'Java package name under which the package list should be placed.')
.option('--empty', 'Whether to only generate an empty list. Might be used when the user opts-out of autolinking.', false);
// Generates a source file listing all packages to link in the runtime.
registerResolveCommand('generate-modules-provider', async (results, options) => {
const packages = options.packages ?? [];
const modules = await (0, autolinking_1.resolveModulesAsync)(results, options);
const filteredModules = modules.filter((module) => packages.includes(module.packageName));
(0, autolinking_1.generatePackageListAsync)(filteredModules, options);
})
.option('-t, --target <path>', 'Path to the target file, where the package list should be written to.')
.option('-p, --packages <packages...>', 'Names of the packages to include in the generated modules provider.');
registerPatchReactImportsCommand();
registerReactNativeConfigCommand();
await commander_1.default
.version(require('expo-modules-autolinking/package.json').version)
.description('CLI command that searches for Expo modules to autolink them.')
.parseAsync(args, { from: 'user' });
};
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,28 @@
import type { ExtraDependencies, ModuleDescriptorAndroid, PackageRevision } from '../types';
/**
* Generates Java file that contains all autolinked packages.
*/
export declare function generatePackageListAsync(modules: ModuleDescriptorAndroid[], targetPath: string, namespace: string): Promise<void>;
export declare function resolveModuleAsync(packageName: string, revision: PackageRevision): Promise<ModuleDescriptorAndroid | null>;
export declare function resolveExtraBuildDependenciesAsync(projectNativeRoot: string): Promise<ExtraDependencies | null>;
/**
* Converts the package name and gradle file path to Android's project name.
* `$` to indicate subprojects
* `/` path will transform as `-`
*
* Example: `@expo/example` + `android/build.gradle` → `expo-example`
*
* Example: multiple projects
* - `expo-test` + `android/build.gradle` → `react-native-third-party`
* - `expo-test` + `subproject/build.gradle` → `react-native-third-party$subproject`
*/
export declare function convertPackageNameToProjectName(packageName: string, buildGradleFile: string): string;
/**
* Given the contents of a `gradle.properties` file,
* searches for a property with the given name.
*
* This function will return the first property found with the given name.
* The implementation follows config-plugins and
* tries to align the behavior with the `withGradleProperties` plugin.
*/
export declare function searchGradlePropertyFirst(contents: string, propertyName: string): string | null;

View File

@@ -0,0 +1,194 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.searchGradlePropertyFirst = exports.convertPackageNameToProjectName = exports.resolveExtraBuildDependenciesAsync = exports.resolveModuleAsync = exports.generatePackageListAsync = void 0;
const fast_glob_1 = __importDefault(require("fast-glob"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const ANDROID_PROPERTIES_FILE = 'gradle.properties';
const ANDROID_EXTRA_BUILD_DEPS_KEY = 'android.extraMavenRepos';
/**
* Generates Java file that contains all autolinked packages.
*/
async function generatePackageListAsync(modules, targetPath, namespace) {
const generatedFileContent = await generatePackageListFileContentAsync(modules, namespace);
await fs_extra_1.default.outputFile(targetPath, generatedFileContent);
}
exports.generatePackageListAsync = generatePackageListAsync;
async function findGradleFilesAsync(revision) {
const configGradlePaths = revision.config?.androidGradlePaths();
if (configGradlePaths && configGradlePaths.length) {
return configGradlePaths;
}
const buildGradleFiles = await (0, fast_glob_1.default)('*/build.gradle', {
cwd: revision.path,
ignore: ['**/node_modules/**'],
});
return buildGradleFiles;
}
async function resolveModuleAsync(packageName, revision) {
// TODO: Relative source dir should be configurable through the module config.
// Don't link itself... :D
if (packageName === '@unimodules/react-native-adapter') {
return null;
}
const buildGradleFiles = await findGradleFilesAsync(revision);
// Just in case where the module doesn't have its own `build.gradle`.
if (!buildGradleFiles.length) {
return null;
}
const projects = buildGradleFiles.map((buildGradleFile) => {
const gradleFilePath = path_1.default.join(revision.path, buildGradleFile);
return {
name: convertPackageNameToProjectName(packageName, path_1.default.relative(revision.path, gradleFilePath)),
sourceDir: path_1.default.dirname(gradleFilePath),
};
});
const plugins = (revision.config?.androidGradlePlugins() ?? []).map(({ id, group, sourceDir }) => ({
id,
group,
sourceDir: path_1.default.join(revision.path, sourceDir),
}));
return {
packageName,
projects,
...(plugins.length > 0 ? { plugins } : {}),
modules: revision.config?.androidModules() ?? [],
};
}
exports.resolveModuleAsync = resolveModuleAsync;
async function resolveExtraBuildDependenciesAsync(projectNativeRoot) {
const propsFile = path_1.default.join(projectNativeRoot, ANDROID_PROPERTIES_FILE);
try {
const contents = await fs_extra_1.default.readFile(propsFile, 'utf8');
const extraMavenReposString = searchGradlePropertyFirst(contents, ANDROID_EXTRA_BUILD_DEPS_KEY);
if (extraMavenReposString) {
const extraMavenRepos = JSON.parse(extraMavenReposString);
return extraMavenRepos;
}
}
catch { }
return null;
}
exports.resolveExtraBuildDependenciesAsync = resolveExtraBuildDependenciesAsync;
/**
* Generates the string to put into the generated package list.
*/
async function generatePackageListFileContentAsync(modules, namespace) {
// TODO: Instead of ignoring `expo` here, make the package class paths configurable from `expo-module.config.json`.
const packagesClasses = await findAndroidPackagesAsync(modules.filter((module) => module.packageName !== 'expo'));
const modulesClasses = await findAndroidModules(modules);
return `package ${namespace};
import java.util.Arrays;
import java.util.List;
import expo.modules.core.interfaces.Package;
import expo.modules.kotlin.modules.Module;
import expo.modules.kotlin.ModulesProvider;
public class ExpoModulesPackageList implements ModulesProvider {
private static class LazyHolder {
static final List<Package> packagesList = Arrays.<Package>asList(
${packagesClasses.map((packageClass) => ` new ${packageClass}()`).join(',\n')}
);
static final List<Class<? extends Module>> modulesList = Arrays.<Class<? extends Module>>asList(
${modulesClasses.map((moduleClass) => ` ${moduleClass}.class`).join(',\n')}
);
}
public static List<Package> getPackageList() {
return LazyHolder.packagesList;
}
@Override
public List<Class<? extends Module>> getModulesList() {
return LazyHolder.modulesList;
}
}
`;
}
function findAndroidModules(modules) {
const modulesToProvide = modules.filter((module) => module.modules.length > 0);
const classNames = [].concat(...modulesToProvide.map((module) => module.modules));
return classNames;
}
async function findAndroidPackagesAsync(modules) {
const classes = [];
const flattenedSourceDirList = [];
for (const module of modules) {
for (const project of module.projects) {
flattenedSourceDirList.push(project.sourceDir);
}
}
await Promise.all(flattenedSourceDirList.map(async (sourceDir) => {
const files = await (0, fast_glob_1.default)('**/*Package.{java,kt}', {
cwd: sourceDir,
});
for (const file of files) {
const fileContent = await fs_extra_1.default.readFile(path_1.default.join(sourceDir, file), 'utf8');
const packageRegex = (() => {
if (process.env.EXPO_SHOULD_USE_LEGACY_PACKAGE_INTERFACE) {
return /\bimport\s+org\.unimodules\.core\.(interfaces\.Package|BasePackage)\b/;
}
else {
return /\bimport\s+expo\.modules\.core\.(interfaces\.Package|BasePackage)\b/;
}
})();
// Very naive check to skip non-expo packages
if (!packageRegex.test(fileContent)) {
continue;
}
const classPathMatches = fileContent.match(/^package ([\w.]+)\b/m);
if (classPathMatches) {
const basename = path_1.default.basename(file, path_1.default.extname(file));
classes.push(`${classPathMatches[1]}.${basename}`);
}
}
}));
return classes.sort();
}
/**
* Converts the package name and gradle file path to Android's project name.
* `$` to indicate subprojects
* `/` path will transform as `-`
*
* Example: `@expo/example` + `android/build.gradle` → `expo-example`
*
* Example: multiple projects
* - `expo-test` + `android/build.gradle` → `react-native-third-party`
* - `expo-test` + `subproject/build.gradle` → `react-native-third-party$subproject`
*/
function convertPackageNameToProjectName(packageName, buildGradleFile) {
const name = packageName.replace(/^@/g, '').replace(/\W+/g, '-');
const baseDir = path_1.default.dirname(buildGradleFile).replace(/\//g, '-');
return baseDir === 'android' ? name : `${name}$${baseDir}`;
}
exports.convertPackageNameToProjectName = convertPackageNameToProjectName;
/**
* Given the contents of a `gradle.properties` file,
* searches for a property with the given name.
*
* This function will return the first property found with the given name.
* The implementation follows config-plugins and
* tries to align the behavior with the `withGradleProperties` plugin.
*/
function searchGradlePropertyFirst(contents, propertyName) {
const lines = contents.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line && !line.startsWith('#')) {
const eok = line.indexOf('=');
const key = line.slice(0, eok);
if (key === propertyName) {
const value = line.slice(eok + 1, line.length);
return value;
}
}
}
return null;
}
exports.searchGradlePropertyFirst = searchGradlePropertyFirst;
//# sourceMappingURL=android.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,15 @@
import type { ExtraDependencies, ModuleDescriptorIos, ModuleIosPodspecInfo, PackageRevision, SearchOptions } from '../types';
export declare function getSwiftModuleNames(pods: ModuleIosPodspecInfo[], swiftModuleNames: string[] | undefined): string[];
/**
* Resolves module search result with additional details required for iOS platform.
*/
export declare function resolveModuleAsync(packageName: string, revision: PackageRevision, options: SearchOptions): Promise<ModuleDescriptorIos | null>;
export declare function resolveExtraBuildDependenciesAsync(projectNativeRoot: string): Promise<ExtraDependencies | null>;
/**
* Generates Swift file that contains all autolinked Swift packages.
*/
export declare function generatePackageListAsync(modules: ModuleDescriptorIos[], targetPath: string): Promise<void>;
/**
* Formats an array of modules to Swift's array containing ReactDelegateHandlers
*/
export declare function formatArrayOfReactDelegateHandler(modules: ModuleDescriptorIos[]): string;

View File

@@ -0,0 +1,186 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatArrayOfReactDelegateHandler = exports.generatePackageListAsync = exports.resolveExtraBuildDependenciesAsync = exports.resolveModuleAsync = exports.getSwiftModuleNames = void 0;
const fast_glob_1 = __importDefault(require("fast-glob"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const path_1 = __importDefault(require("path"));
const APPLE_PROPERTIES_FILE = 'Podfile.properties.json';
const APPLE_EXTRA_BUILD_DEPS_KEY = 'apple.extraPods';
const indent = ' ';
async function findPodspecFiles(revision) {
const configPodspecPaths = revision.config?.applePodspecPaths();
if (configPodspecPaths && configPodspecPaths.length) {
return configPodspecPaths;
}
const podspecFiles = await (0, fast_glob_1.default)('*/*.podspec', {
cwd: revision.path,
ignore: ['**/node_modules/**'],
});
return podspecFiles;
}
function getSwiftModuleNames(pods, swiftModuleNames) {
if (swiftModuleNames && swiftModuleNames.length) {
return swiftModuleNames;
}
// by default, non-alphanumeric characters in the pod name are replaced by _ in the module name
return pods.map((pod) => pod.podName.replace(/[^a-zA-Z0-9]/g, '_'));
}
exports.getSwiftModuleNames = getSwiftModuleNames;
/**
* Resolves module search result with additional details required for iOS platform.
*/
async function resolveModuleAsync(packageName, revision, options) {
const podspecFiles = await findPodspecFiles(revision);
if (!podspecFiles.length) {
return null;
}
const pods = podspecFiles.map((podspecFile) => ({
podName: path_1.default.basename(podspecFile, path_1.default.extname(podspecFile)),
podspecDir: path_1.default.dirname(path_1.default.join(revision.path, podspecFile)),
}));
const swiftModuleNames = getSwiftModuleNames(pods, revision.config?.appleSwiftModuleNames());
return {
packageName,
pods,
swiftModuleNames,
flags: options.flags,
modules: revision.config?.appleModules() ?? [],
appDelegateSubscribers: revision.config?.appleAppDelegateSubscribers() ?? [],
reactDelegateHandlers: revision.config?.appleReactDelegateHandlers() ?? [],
debugOnly: revision.config?.appleDebugOnly() ?? false,
};
}
exports.resolveModuleAsync = resolveModuleAsync;
async function resolveExtraBuildDependenciesAsync(projectNativeRoot) {
const propsFile = path_1.default.join(projectNativeRoot, APPLE_PROPERTIES_FILE);
try {
const contents = await fs_extra_1.default.readFile(propsFile, 'utf8');
const podfileJson = JSON.parse(contents);
if (podfileJson[APPLE_EXTRA_BUILD_DEPS_KEY]) {
// expo-build-properties would serialize the extraPods as JSON string, we should parse it again.
const extraPods = JSON.parse(podfileJson[APPLE_EXTRA_BUILD_DEPS_KEY]);
return extraPods;
}
}
catch { }
return null;
}
exports.resolveExtraBuildDependenciesAsync = resolveExtraBuildDependenciesAsync;
/**
* Generates Swift file that contains all autolinked Swift packages.
*/
async function generatePackageListAsync(modules, targetPath) {
const className = path_1.default.basename(targetPath, path_1.default.extname(targetPath));
const generatedFileContent = await generatePackageListFileContentAsync(modules, className);
await fs_extra_1.default.outputFile(targetPath, generatedFileContent);
}
exports.generatePackageListAsync = generatePackageListAsync;
/**
* Generates the string to put into the generated package list.
*/
async function generatePackageListFileContentAsync(modules, className) {
const iosModules = modules.filter((module) => module.modules.length ||
module.appDelegateSubscribers.length ||
module.reactDelegateHandlers.length);
const modulesToImport = iosModules.filter((module) => !module.debugOnly);
const debugOnlyModules = iosModules.filter((module) => module.debugOnly);
const swiftModules = []
.concat(...modulesToImport.map((module) => module.swiftModuleNames))
.filter(Boolean);
const debugOnlySwiftModules = []
.concat(...debugOnlyModules.map((module) => module.swiftModuleNames))
.filter(Boolean);
const modulesClassNames = []
.concat(...modulesToImport.map((module) => module.modules))
.filter(Boolean);
const debugOnlyModulesClassNames = []
.concat(...debugOnlyModules.map((module) => module.modules))
.filter(Boolean);
const appDelegateSubscribers = [].concat(...modulesToImport.map((module) => module.appDelegateSubscribers));
const debugOnlyAppDelegateSubscribers = [].concat(...debugOnlyModules.map((module) => module.appDelegateSubscribers));
const reactDelegateHandlerModules = modulesToImport.filter((module) => !!module.reactDelegateHandlers.length);
const debugOnlyReactDelegateHandlerModules = debugOnlyModules.filter((module) => !!module.reactDelegateHandlers.length);
return `/**
* Automatically generated by expo-modules-autolinking.
*
* This autogenerated class provides a list of classes of native Expo modules,
* but only these that are written in Swift and use the new API for creating Expo modules.
*/
import ExpoModulesCore
${generateCommonImportList(swiftModules)}
${generateDebugOnlyImportList(debugOnlySwiftModules)}
@objc(${className})
public class ${className}: ModulesProvider {
public override func getModuleClasses() -> [AnyModule.Type] {
${generateModuleClasses(modulesClassNames, debugOnlyModulesClassNames)}
}
public override func getAppDelegateSubscribers() -> [ExpoAppDelegateSubscriber.Type] {
${generateModuleClasses(appDelegateSubscribers, debugOnlyAppDelegateSubscribers)}
}
public override func getReactDelegateHandlers() -> [ExpoReactDelegateHandlerTupleType] {
${generateReactDelegateHandlers(reactDelegateHandlerModules, debugOnlyReactDelegateHandlerModules)}
}
}
`;
}
function generateCommonImportList(swiftModules) {
return swiftModules.map((moduleName) => `import ${moduleName}`).join('\n');
}
function generateDebugOnlyImportList(swiftModules) {
if (!swiftModules.length) {
return '';
}
return (wrapInDebugConfigurationCheck(0, swiftModules.map((moduleName) => `import ${moduleName}`).join('\n')) + '\n');
}
function generateModuleClasses(classNames, debugOnlyClassName) {
const commonClassNames = formatArrayOfClassNames(classNames);
if (debugOnlyClassName.length > 0) {
return wrapInDebugConfigurationCheck(2, `return ${formatArrayOfClassNames(classNames.concat(debugOnlyClassName))}`, `return ${commonClassNames}`);
}
else {
return `${indent.repeat(2)}return ${commonClassNames}`;
}
}
/**
* Formats an array of class names to Swift's array containing these classes.
*/
function formatArrayOfClassNames(classNames) {
return `[${classNames.map((className) => `\n${indent.repeat(3)}${className}.self`).join(',')}
${indent.repeat(2)}]`;
}
function generateReactDelegateHandlers(module, debugOnlyModules) {
const commonModules = formatArrayOfReactDelegateHandler(module);
if (debugOnlyModules.length > 0) {
return wrapInDebugConfigurationCheck(2, `return ${formatArrayOfReactDelegateHandler(module.concat(debugOnlyModules))}`, `return ${commonModules}`);
}
else {
return `${indent.repeat(2)}return ${commonModules}`;
}
}
/**
* Formats an array of modules to Swift's array containing ReactDelegateHandlers
*/
function formatArrayOfReactDelegateHandler(modules) {
const values = [];
for (const module of modules) {
for (const handler of module.reactDelegateHandlers) {
values.push(`(packageName: "${module.packageName}", handler: ${handler}.self)`);
}
}
return `[${values.map((value) => `\n${indent.repeat(3)}${value}`).join(',')}
${indent.repeat(2)}]`;
}
exports.formatArrayOfReactDelegateHandler = formatArrayOfReactDelegateHandler;
function wrapInDebugConfigurationCheck(indentationLevel, debugBlock, releaseBlock = null) {
if (releaseBlock) {
return `${indent.repeat(indentationLevel)}#if EXPO_CONFIGURATION_DEBUG\n${indent.repeat(indentationLevel)}${debugBlock}\n${indent.repeat(indentationLevel)}#else\n${indent.repeat(indentationLevel)}${releaseBlock}\n${indent.repeat(indentationLevel)}#endif`;
}
return `${indent.repeat(indentationLevel)}#if EXPO_CONFIGURATION_DEBUG\n${indent.repeat(indentationLevel)}${debugBlock}\n${indent.repeat(indentationLevel)}#endif`;
}
//# sourceMappingURL=apple.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
import type { ExtraDependencies, ModuleDescriptorDevTools, PackageRevision } from '../types';
export declare function resolveModuleAsync(packageName: string, revision: PackageRevision): Promise<ModuleDescriptorDevTools | null>;
export declare function resolveExtraBuildDependenciesAsync(_projectNativeRoot: string): Promise<ExtraDependencies | null>;

View File

@@ -0,0 +1,24 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveExtraBuildDependenciesAsync = exports.resolveModuleAsync = void 0;
const path_1 = __importDefault(require("path"));
async function resolveModuleAsync(packageName, revision) {
const devtoolsConfig = revision.config?.toJSON().devtools;
if (devtoolsConfig == null) {
return null;
}
return {
packageName,
packageRoot: revision.path,
webpageRoot: path_1.default.join(revision.path, devtoolsConfig.webpageRoot),
};
}
exports.resolveModuleAsync = resolveModuleAsync;
async function resolveExtraBuildDependenciesAsync(_projectNativeRoot) {
return null;
}
exports.resolveExtraBuildDependenciesAsync = resolveExtraBuildDependenciesAsync;
//# sourceMappingURL=devtools.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"devtools.js","sourceRoot":"","sources":["../../src/platforms/devtools.ts"],"names":[],"mappings":";;;;;;AAAA,gDAAwB;AAIjB,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,QAAyB;IAEzB,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,QAAQ,CAAC;IAC1D,IAAI,cAAc,IAAI,IAAI,EAAE;QAC1B,OAAO,IAAI,CAAC;KACb;IAED,OAAO;QACL,WAAW;QACX,WAAW,EAAE,QAAQ,CAAC,IAAI;QAC1B,WAAW,EAAE,cAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,WAAW,CAAC;KAClE,CAAC;AACJ,CAAC;AAdD,gDAcC;AAEM,KAAK,UAAU,kCAAkC,CACtD,kBAA0B;IAE1B,OAAO,IAAI,CAAC;AACd,CAAC;AAJD,gFAIC","sourcesContent":["import path from 'path';\n\nimport type { ExtraDependencies, ModuleDescriptorDevTools, PackageRevision } from '../types';\n\nexport async function resolveModuleAsync(\n packageName: string,\n revision: PackageRevision\n): Promise<ModuleDescriptorDevTools | null> {\n const devtoolsConfig = revision.config?.toJSON().devtools;\n if (devtoolsConfig == null) {\n return null;\n }\n\n return {\n packageName,\n packageRoot: revision.path,\n webpageRoot: path.join(revision.path, devtoolsConfig.webpageRoot),\n };\n}\n\nexport async function resolveExtraBuildDependenciesAsync(\n _projectNativeRoot: string\n): Promise<ExtraDependencies | null> {\n return null;\n}\n"]}

View File

@@ -0,0 +1,19 @@
import type { RNConfigDependencyAndroid, RNConfigReactNativePlatformsConfigAndroid } from './reactNativeConfig.types';
export declare function resolveDependencyConfigImplAndroidAsync(packageRoot: string, reactNativeConfig: RNConfigReactNativePlatformsConfigAndroid | null | undefined): Promise<RNConfigDependencyAndroid | null>;
/**
* Parse the `RNConfigDependencyAndroid.packageName`
*/
export declare function parsePackageNameAsync(manifestPath: string | null, gradlePath: string | null): Promise<string | null>;
/**
* Parse the Java or Kotlin class name to for `ReactPackage` or `TurboReactPackage`.
*/
export declare function parseNativePackageClassNameAsync(packageRoot: string, androidDir: string): Promise<string | null>;
export declare function parseLibraryNameAsync(androidDir: string, packageJson: any): Promise<string | null>;
export declare function parseComponentDescriptorsAsync(packageRoot: string, pacakgeJson: any): Promise<string[]>;
export declare function findGradleAndManifestAsync({ androidDir, isLibrary, }: {
androidDir: string;
isLibrary: boolean;
}): Promise<{
gradle: string;
manifest: string;
}>;

View File

@@ -0,0 +1,208 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.findGradleAndManifestAsync = exports.parseComponentDescriptorsAsync = exports.parseLibraryNameAsync = exports.parseNativePackageClassNameAsync = exports.parsePackageNameAsync = exports.resolveDependencyConfigImplAndroidAsync = void 0;
const fast_glob_1 = __importDefault(require("fast-glob"));
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const utils_1 = require("./utils");
async function resolveDependencyConfigImplAndroidAsync(packageRoot, reactNativeConfig) {
if (reactNativeConfig === null) {
// Skip autolinking for this package.
return null;
}
const androidDir = path_1.default.join(packageRoot, 'android');
const { gradle, manifest } = await findGradleAndManifestAsync({ androidDir, isLibrary: true });
if (!manifest && !gradle) {
return null;
}
const packageName = reactNativeConfig?.packageName ||
(await parsePackageNameAsync(path_1.default.join(androidDir, manifest), path_1.default.join(androidDir, gradle)));
const nativePackageClassName = await parseNativePackageClassNameAsync(packageRoot, androidDir);
if (!nativePackageClassName) {
return null;
}
const packageJson = JSON.parse(await promises_1.default.readFile(path_1.default.join(packageRoot, 'package.json'), 'utf8'));
const packageImportPath = reactNativeConfig?.packageImportPath || `import ${packageName}.${nativePackageClassName};`;
const packageInstance = reactNativeConfig?.packageInstance || `new ${nativePackageClassName}()`;
const buildTypes = reactNativeConfig?.buildTypes || [];
const dependencyConfiguration = reactNativeConfig?.dependencyConfiguration;
const libraryName = reactNativeConfig?.libraryName || (await parseLibraryNameAsync(androidDir, packageJson));
const componentDescriptors = reactNativeConfig?.componentDescriptors ||
(await parseComponentDescriptorsAsync(packageRoot, packageJson));
let cmakeListsPath = reactNativeConfig?.cmakeListsPath
? path_1.default.join(androidDir, reactNativeConfig?.cmakeListsPath)
: path_1.default.join(androidDir, 'build/generated/source/codegen/jni/CMakeLists.txt');
const cxxModuleCMakeListsModuleName = reactNativeConfig?.cxxModuleCMakeListsModuleName || null;
const cxxModuleHeaderName = reactNativeConfig?.cxxModuleHeaderName || null;
let cxxModuleCMakeListsPath = reactNativeConfig?.cxxModuleCMakeListsPath
? path_1.default.join(androidDir, reactNativeConfig?.cxxModuleCMakeListsPath)
: null;
if (process.platform === 'win32') {
cmakeListsPath = cmakeListsPath.replace(/\\/g, '/');
if (cxxModuleCMakeListsPath) {
cxxModuleCMakeListsPath = cxxModuleCMakeListsPath.replace(/\\/g, '/');
}
}
const result = {
sourceDir: androidDir,
packageImportPath,
packageInstance,
dependencyConfiguration,
buildTypes,
libraryName,
componentDescriptors,
cmakeListsPath,
cxxModuleCMakeListsModuleName,
cxxModuleCMakeListsPath,
cxxModuleHeaderName,
};
if (!result.libraryName) {
delete result.libraryName;
}
if (!result.dependencyConfiguration) {
delete result.dependencyConfiguration;
}
return result;
}
exports.resolveDependencyConfigImplAndroidAsync = resolveDependencyConfigImplAndroidAsync;
/**
* Parse the `RNConfigDependencyAndroid.packageName`
*/
async function parsePackageNameAsync(manifestPath, gradlePath) {
if (gradlePath) {
const gradleContents = await promises_1.default.readFile(gradlePath, 'utf8');
const match = gradleContents.match(/namespace\s*[=]*\s*["'](.+?)["']/);
if (match) {
return match[1];
}
}
if (manifestPath) {
const manifestContents = await promises_1.default.readFile(manifestPath, 'utf8');
const match = manifestContents.match(/package="(.+?)"/);
if (match) {
return match[1];
}
}
return null;
}
exports.parsePackageNameAsync = parsePackageNameAsync;
/**
* Parse the Java or Kotlin class name to for `ReactPackage` or `TurboReactPackage`.
*/
async function parseNativePackageClassNameAsync(packageRoot, androidDir) {
const matched = await (0, utils_1.globMatchFunctorFirstAsync)('**/*Package.{java,kt}', matchNativePackageClassName, { cwd: androidDir });
if (matched) {
return matched;
}
// Early return if the module is an Expo module
if (await (0, utils_1.fileExistsAsync)(path_1.default.join(packageRoot, 'expo-module.config.json'))) {
return null;
}
return await (0, utils_1.globMatchFunctorFirstAsync)('**/*.{java,kt}', matchNativePackageClassName, {
cwd: androidDir,
});
}
exports.parseNativePackageClassNameAsync = parseNativePackageClassNameAsync;
let lazyReactPackageRegex = null;
let lazyTurboReactPackageRegex = null;
function matchNativePackageClassName(filePath, contents) {
const fileContents = contents.toString();
// [0] Match ReactPackage
if (!lazyReactPackageRegex) {
lazyReactPackageRegex =
/class\s+(\w+[^(\s]*)[\s\w():]*(\s+implements\s+|:)[\s\w():,]*[^{]*ReactPackage/;
}
const matchReactPackage = fileContents.match(lazyReactPackageRegex);
if (matchReactPackage) {
return matchReactPackage[1];
}
// [1] Match TurboReactPackage
if (!lazyTurboReactPackageRegex) {
lazyTurboReactPackageRegex =
/class\s+(\w+[^(\s]*)[\s\w():]*(\s+extends\s+|:)[\s\w():,]*[^{]*TurboReactPackage/;
}
const matchTurboReactPackage = fileContents.match(lazyTurboReactPackageRegex);
if (matchTurboReactPackage) {
return matchTurboReactPackage[1];
}
return null;
}
async function parseLibraryNameAsync(androidDir, packageJson) {
// [0] `codegenConfig.name` from package.json
if (packageJson.codegenConfig?.name) {
return packageJson.codegenConfig.name;
}
const libraryNameRegExp = /libraryName = ["'](.+)["']/;
const gradlePath = path_1.default.join(androidDir, 'build.gradle');
// [1] `libraryName` from build.gradle
if (await (0, utils_1.fileExistsAsync)(gradlePath)) {
const buildGradleContents = await promises_1.default.readFile(gradlePath, 'utf8');
const match = buildGradleContents.match(libraryNameRegExp);
if (match) {
return match[1];
}
}
// [2] `libraryName` from build.gradle.kts
const gradleKtsPath = path_1.default.join(androidDir, 'build.gradle.kts');
if (await (0, utils_1.fileExistsAsync)(gradleKtsPath)) {
const buildGradleContents = await promises_1.default.readFile(gradleKtsPath, 'utf8');
const match = buildGradleContents.match(libraryNameRegExp);
if (match) {
return match[1];
}
}
return null;
}
exports.parseLibraryNameAsync = parseLibraryNameAsync;
async function parseComponentDescriptorsAsync(packageRoot, pacakgeJson) {
const jsRoot = pacakgeJson?.codegenConfig?.jsSrcsDir
? path_1.default.join(packageRoot, pacakgeJson.codegenConfig.jsSrcsDir)
: packageRoot;
const results = await (0, utils_1.globMatchFunctorAllAsync)('**/*.{js,jsx,ts,tsx}', matchComponentDescriptors, {
cwd: jsRoot,
ignore: ['**/node_modules/**'],
});
// Filter out duplicates as it happens that libraries contain multiple outputs due to package publishing.
// TODO: consider using "codegenConfig" to avoid this.
return Array.from(new Set(results));
}
exports.parseComponentDescriptorsAsync = parseComponentDescriptorsAsync;
let lazyCodegenComponentRegex = null;
function matchComponentDescriptors(filePath, contents) {
const fileContents = contents.toString();
if (!lazyCodegenComponentRegex) {
lazyCodegenComponentRegex =
/codegenNativeComponent(<.*>)?\s*\(\s*["'`](\w+)["'`](,?[\s\S]+interfaceOnly:\s*(\w+))?/m;
}
const match = fileContents.match(lazyCodegenComponentRegex);
if (!(match?.[4] === 'true') && match?.[2]) {
return `${match[2]}ComponentDescriptor`;
}
return null;
}
async function findGradleAndManifestAsync({ androidDir, isLibrary, }) {
const globExcludes = [
'node_modules/**',
'**/build/**',
'**/debug/**',
'Examples/**',
'examples/**',
'**/Pods/**',
'**/sdks/hermes/android/**',
'**/src/androidTest/**',
'**/src/test/**',
];
const gradlePattern = isLibrary ? 'build.gradle{,.kts}' : 'app/build.gradle{,.kts}';
const [manifests, gradles] = await Promise.all([
(0, fast_glob_1.default)('**/AndroidManifest.xml', { cwd: androidDir, ignore: globExcludes }),
(0, fast_glob_1.default)(gradlePattern, { cwd: androidDir, ignore: globExcludes }),
]);
const manifest = manifests.find((manifest) => manifest.includes('src/main/')) ?? manifests[0];
const gradle = gradles[0];
return { gradle, manifest };
}
exports.findGradleAndManifestAsync = findGradleAndManifestAsync;
//# sourceMappingURL=androidResolver.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
import type { RNConfigReactNativeConfig } from './reactNativeConfig.types';
/**
* Load the `react-native.config.js` or `react-native.config.ts` from the package.
*/
export declare function loadConfigAsync<T extends RNConfigReactNativeConfig>(packageRoot: string): Promise<T | null>;

View File

@@ -0,0 +1,56 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.loadConfigAsync = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const require_from_string_1 = __importDefault(require("require-from-string"));
const resolve_from_1 = __importDefault(require("resolve-from"));
const utils_1 = require("./utils");
let tsMain = undefined;
/**
* Load the `react-native.config.js` or `react-native.config.ts` from the package.
*/
async function loadConfigAsync(packageRoot) {
const configJsPath = path_1.default.join(packageRoot, 'react-native.config.js');
if (await (0, utils_1.fileExistsAsync)(configJsPath)) {
try {
return require(configJsPath);
}
catch {
return null;
}
}
const configTsPath = path_1.default.join(packageRoot, 'react-native.config.ts');
if (await (0, utils_1.fileExistsAsync)(configTsPath)) {
if (tsMain === undefined) {
const tsPath = resolve_from_1.default.silent(packageRoot, 'typescript');
if (tsPath) {
tsMain = require(tsPath);
}
}
else if (tsMain == null) {
return null;
}
const configContents = await promises_1.default.readFile(configTsPath, 'utf8');
const transpiledContents = tsMain?.transpileModule(configContents, {
compilerOptions: {
module: tsMain.ModuleKind.NodeNext,
moduleResolution: tsMain.ModuleResolutionKind.NodeNext,
target: tsMain.ScriptTarget.ESNext,
},
});
const outputText = transpiledContents?.outputText;
let config;
try {
config = outputText ? (0, require_from_string_1.default)(outputText) : null;
}
catch { }
return config?.default ?? config ?? null;
}
return null;
}
exports.loadConfigAsync = loadConfigAsync;
//# sourceMappingURL=config.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/reactNativeConfig/config.ts"],"names":[],"mappings":";;;;;;AAAA,2DAA6B;AAC7B,gDAAwB;AACxB,8EAAoD;AACpD,gEAAuC;AAGvC,mCAA0C;AAE1C,IAAI,MAAM,GAAmD,SAAS,CAAC;AAEvE;;GAEG;AACI,KAAK,UAAU,eAAe,CACnC,WAAmB;IAEnB,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAC;IACtE,IAAI,MAAM,IAAA,uBAAe,EAAC,YAAY,CAAC,EAAE;QACvC,IAAI;YACF,OAAO,OAAO,CAAC,YAAY,CAAC,CAAC;SAC9B;QAAC,MAAM;YACN,OAAO,IAAI,CAAC;SACb;KACF;IAED,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,wBAAwB,CAAC,CAAC;IACtE,IAAI,MAAM,IAAA,uBAAe,EAAC,YAAY,CAAC,EAAE;QACvC,IAAI,MAAM,KAAK,SAAS,EAAE;YACxB,MAAM,MAAM,GAAG,sBAAW,CAAC,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;YAC7D,IAAI,MAAM,EAAE;gBACV,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;aAC1B;SACF;aAAM,IAAI,MAAM,IAAI,IAAI,EAAE;YACzB,OAAO,IAAI,CAAC;SACb;QAED,MAAM,cAAc,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAC/D,MAAM,kBAAkB,GAAG,MAAM,EAAE,eAAe,CAAC,cAAc,EAAE;YACjE,eAAe,EAAE;gBACf,MAAM,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ;gBAClC,gBAAgB,EAAE,MAAM,CAAC,oBAAoB,CAAC,QAAQ;gBACtD,MAAM,EAAE,MAAM,CAAC,YAAY,CAAC,MAAM;aACnC;SACF,CAAC,CAAC;QACH,MAAM,UAAU,GAAG,kBAAkB,EAAE,UAAU,CAAC;QAClD,IAAI,MAAM,CAAC;QACX,IAAI;YACF,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,IAAA,6BAAiB,EAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;SAC5D;QAAC,MAAM,GAAE;QACV,OAAO,MAAM,EAAE,OAAO,IAAI,MAAM,IAAI,IAAI,CAAC;KAC1C;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAxCD,0CAwCC","sourcesContent":["import fs from 'fs/promises';\nimport path from 'path';\nimport requireFromString from 'require-from-string';\nimport resolveFrom from 'resolve-from';\n\nimport type { RNConfigReactNativeConfig } from './reactNativeConfig.types';\nimport { fileExistsAsync } from './utils';\n\nlet tsMain: typeof import('typescript') | null | undefined = undefined;\n\n/**\n * Load the `react-native.config.js` or `react-native.config.ts` from the package.\n */\nexport async function loadConfigAsync<T extends RNConfigReactNativeConfig>(\n packageRoot: string\n): Promise<T | null> {\n const configJsPath = path.join(packageRoot, 'react-native.config.js');\n if (await fileExistsAsync(configJsPath)) {\n try {\n return require(configJsPath);\n } catch {\n return null;\n }\n }\n\n const configTsPath = path.join(packageRoot, 'react-native.config.ts');\n if (await fileExistsAsync(configTsPath)) {\n if (tsMain === undefined) {\n const tsPath = resolveFrom.silent(packageRoot, 'typescript');\n if (tsPath) {\n tsMain = require(tsPath);\n }\n } else if (tsMain == null) {\n return null;\n }\n\n const configContents = await fs.readFile(configTsPath, 'utf8');\n const transpiledContents = tsMain?.transpileModule(configContents, {\n compilerOptions: {\n module: tsMain.ModuleKind.NodeNext,\n moduleResolution: tsMain.ModuleResolutionKind.NodeNext,\n target: tsMain.ScriptTarget.ESNext,\n },\n });\n const outputText = transpiledContents?.outputText;\n let config;\n try {\n config = outputText ? requireFromString(outputText) : null;\n } catch {}\n return config?.default ?? config ?? null;\n }\n\n return null;\n}\n"]}

View File

@@ -0,0 +1,2 @@
export * from './reactNativeConfig';
export * from './reactNativeConfig.types';

View File

@@ -0,0 +1,19 @@
"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 });
__exportStar(require("./reactNativeConfig"), exports);
__exportStar(require("./reactNativeConfig.types"), exports);
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/reactNativeConfig/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,sDAAoC;AACpC,4DAA0C","sourcesContent":["export * from './reactNativeConfig';\nexport * from './reactNativeConfig.types';\n"]}

View File

@@ -0,0 +1,2 @@
import type { RNConfigDependencyIos, RNConfigReactNativePlatformsConfigIos } from './reactNativeConfig.types';
export declare function resolveDependencyConfigImplIosAsync(packageRoot: string, reactNativeConfig: RNConfigReactNativePlatformsConfigIos | null | undefined): Promise<RNConfigDependencyIos | null>;

View File

@@ -0,0 +1,31 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveDependencyConfigImplIosAsync = void 0;
const fast_glob_1 = __importDefault(require("fast-glob"));
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
async function resolveDependencyConfigImplIosAsync(packageRoot, reactNativeConfig) {
if (reactNativeConfig === null) {
// Skip autolinking for this package.
return null;
}
const podspecs = await (0, fast_glob_1.default)('*.podspec', { cwd: packageRoot });
if (!podspecs?.length) {
return null;
}
const mainPackagePodspec = path_1.default.basename(packageRoot) + '.podspec';
const podspecFile = podspecs.includes(mainPackagePodspec) ? mainPackagePodspec : podspecs[0];
const podspecPath = path_1.default.join(packageRoot, podspecFile);
const packageJson = JSON.parse(await promises_1.default.readFile(path_1.default.join(packageRoot, 'package.json'), 'utf8'));
return {
podspecPath,
version: packageJson.version,
configurations: reactNativeConfig?.configurations || [],
scriptPhases: reactNativeConfig?.scriptPhases || [],
};
}
exports.resolveDependencyConfigImplIosAsync = resolveDependencyConfigImplIosAsync;
//# sourceMappingURL=iosResolver.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"iosResolver.js","sourceRoot":"","sources":["../../src/reactNativeConfig/iosResolver.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAC7B,2DAA6B;AAC7B,gDAAwB;AAOjB,KAAK,UAAU,mCAAmC,CACvD,WAAmB,EACnB,iBAA2E;IAE3E,IAAI,iBAAiB,KAAK,IAAI,EAAE;QAC9B,qCAAqC;QACrC,OAAO,IAAI,CAAC;KACb;IAED,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAI,EAAC,WAAW,EAAE,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC,CAAC;IAC/D,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE;QACrB,OAAO,IAAI,CAAC;KACb;IACD,MAAM,kBAAkB,GAAG,cAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,UAAU,CAAC;IACnE,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC7F,MAAM,WAAW,GAAG,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAExD,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,kBAAE,CAAC,QAAQ,CAAC,cAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAElG,OAAO;QACL,WAAW;QACX,OAAO,EAAE,WAAW,CAAC,OAAO;QAC5B,cAAc,EAAE,iBAAiB,EAAE,cAAc,IAAI,EAAE;QACvD,YAAY,EAAE,iBAAiB,EAAE,YAAY,IAAI,EAAE;KACpD,CAAC;AACJ,CAAC;AAzBD,kFAyBC","sourcesContent":["import glob from 'fast-glob';\nimport fs from 'fs/promises';\nimport path from 'path';\n\nimport type {\n RNConfigDependencyIos,\n RNConfigReactNativePlatformsConfigIos,\n} from './reactNativeConfig.types';\n\nexport async function resolveDependencyConfigImplIosAsync(\n packageRoot: string,\n reactNativeConfig: RNConfigReactNativePlatformsConfigIos | null | undefined\n): Promise<RNConfigDependencyIos | null> {\n if (reactNativeConfig === null) {\n // Skip autolinking for this package.\n return null;\n }\n\n const podspecs = await glob('*.podspec', { cwd: packageRoot });\n if (!podspecs?.length) {\n return null;\n }\n const mainPackagePodspec = path.basename(packageRoot) + '.podspec';\n const podspecFile = podspecs.includes(mainPackagePodspec) ? mainPackagePodspec : podspecs[0];\n const podspecPath = path.join(packageRoot, podspecFile);\n\n const packageJson = JSON.parse(await fs.readFile(path.join(packageRoot, 'package.json'), 'utf8'));\n\n return {\n podspecPath,\n version: packageJson.version,\n configurations: reactNativeConfig?.configurations || [],\n scriptPhases: reactNativeConfig?.scriptPhases || [],\n };\n}\n"]}

View File

@@ -0,0 +1,12 @@
import type { RNConfigCommandOptions, RNConfigDependency, RNConfigReactNativeAppProjectConfig, RNConfigReactNativeProjectConfig, RNConfigResult } from './reactNativeConfig.types';
import type { SupportedPlatform } from '../types';
/**
* Create config for react-native core autolinking.
*/
export declare function createReactNativeConfigAsync({ platform, projectRoot, searchPaths, }: RNConfigCommandOptions): Promise<RNConfigResult>;
/**
* Find all dependencies and their directories from the project.
*/
export declare function findDependencyRootsAsync(projectRoot: string, searchPaths: string[]): Promise<Record<string, string>>;
export declare function resolveDependencyConfigAsync(platform: SupportedPlatform, name: string, packageRoot: string, projectConfig: RNConfigReactNativeProjectConfig | null): Promise<RNConfigDependency | null>;
export declare function resolveAppProjectConfigAsync(projectRoot: string, platform: SupportedPlatform): Promise<RNConfigReactNativeAppProjectConfig>;

View File

@@ -0,0 +1,117 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveAppProjectConfigAsync = exports.resolveDependencyConfigAsync = exports.findDependencyRootsAsync = exports.createReactNativeConfigAsync = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const androidResolver_1 = require("./androidResolver");
const config_1 = require("./config");
const iosResolver_1 = require("./iosResolver");
const utils_1 = require("./utils");
const utils_2 = require("../autolinking/utils");
/**
* Create config for react-native core autolinking.
*/
async function createReactNativeConfigAsync({ platform, projectRoot, searchPaths, }) {
const projectConfig = await (0, config_1.loadConfigAsync)(projectRoot);
const dependencyRoots = await findDependencyRootsAsync(projectRoot, searchPaths);
const reactNativePath = dependencyRoots['react-native'];
const dependencyConfigs = await Promise.all(Object.entries(dependencyRoots).map(async ([name, packageRoot]) => {
const config = await resolveDependencyConfigAsync(platform, name, packageRoot, projectConfig);
return [name, config];
}));
const dependencyResults = Object.fromEntries(dependencyConfigs.filter(([, config]) => config != null));
const projectData = await resolveAppProjectConfigAsync(projectRoot, platform);
return {
root: projectRoot,
reactNativePath,
dependencies: dependencyResults,
project: projectData,
};
}
exports.createReactNativeConfigAsync = createReactNativeConfigAsync;
/**
* Find all dependencies and their directories from the project.
*/
async function findDependencyRootsAsync(projectRoot, searchPaths) {
const packageJson = JSON.parse(await promises_1.default.readFile(path_1.default.join(projectRoot, 'package.json'), 'utf8'));
const dependencies = [
...Object.keys(packageJson.dependencies ?? {}),
...Object.keys(packageJson.devDependencies ?? {}),
];
const results = {};
// `searchPathSet` can be mutated to discover all "isolated modules groups", when using isolated modules
const searchPathSet = new Set(searchPaths);
for (const name of dependencies) {
for (const searchPath of searchPathSet) {
const packageConfigPath = path_1.default.resolve(searchPath, name, 'package.json');
if (await (0, utils_1.fileExistsAsync)(packageConfigPath)) {
const packageRoot = path_1.default.dirname(packageConfigPath);
results[name] = packageRoot;
const maybeIsolatedModulesPath = (0, utils_2.getIsolatedModulesPath)(packageRoot, name);
if (maybeIsolatedModulesPath) {
searchPathSet.add(maybeIsolatedModulesPath);
}
break;
}
}
}
return results;
}
exports.findDependencyRootsAsync = findDependencyRootsAsync;
async function resolveDependencyConfigAsync(platform, name, packageRoot, projectConfig) {
const libraryConfig = await (0, config_1.loadConfigAsync)(packageRoot);
const reactNativeConfig = {
...libraryConfig?.dependency,
...projectConfig?.dependencies[name],
};
if (Object.keys(libraryConfig?.platforms ?? {}).length > 0) {
// Package defines platforms would be a platform host package.
// The rnc-cli will skip this package.
// For example, the `react-native` package.
return null;
}
let platformData = null;
if (platform === 'android') {
platformData = await (0, androidResolver_1.resolveDependencyConfigImplAndroidAsync)(packageRoot, reactNativeConfig.platforms?.android);
}
else if (platform === 'ios') {
platformData = await (0, iosResolver_1.resolveDependencyConfigImplIosAsync)(packageRoot, reactNativeConfig.platforms?.ios);
}
if (!platformData) {
return null;
}
return {
root: packageRoot,
name,
platforms: {
[platform]: platformData,
},
};
}
exports.resolveDependencyConfigAsync = resolveDependencyConfigAsync;
async function resolveAppProjectConfigAsync(projectRoot, platform) {
if (platform === 'android') {
const androidDir = path_1.default.join(projectRoot, 'android');
const { gradle, manifest } = await (0, androidResolver_1.findGradleAndManifestAsync)({ androidDir, isLibrary: false });
const packageName = await (0, androidResolver_1.parsePackageNameAsync)(path_1.default.join(androidDir, manifest), path_1.default.join(androidDir, gradle));
return {
android: {
packageName: packageName ?? '',
sourceDir: path_1.default.join(projectRoot, 'android'),
},
};
}
if (platform === 'ios') {
return {
ios: {
sourceDir: path_1.default.join(projectRoot, 'ios'),
},
};
}
return {};
}
exports.resolveAppProjectConfigAsync = resolveAppProjectConfigAsync;
//# sourceMappingURL=reactNativeConfig.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,93 @@
import type { SupportedPlatform } from '../types';
/**
* Options for 'react-native-config' command.
*/
export interface RNConfigCommandOptions {
platform: SupportedPlatform;
projectRoot: string;
searchPaths: string[];
}
/**
* Dependency configuration for Android platform.
*/
export interface RNConfigDependencyAndroid {
sourceDir: string;
packageImportPath: string;
packageInstance: string;
dependencyConfiguration?: string;
buildTypes: string[];
libraryName?: string | null;
componentDescriptors?: string[] | null;
cmakeListsPath?: string | null;
cxxModuleCMakeListsModuleName?: string | null;
cxxModuleCMakeListsPath?: string | null;
cxxModuleHeaderName?: string | null;
}
/**
* Dependency configuration for iOS platform.
*/
export interface RNConfigDependencyIos {
podspecPath: string;
version: string;
configurations: string[];
scriptPhases: any[];
}
/**
* Dependency configuration.
*/
export interface RNConfigDependency {
root: string;
name: string;
platforms: {
android?: RNConfigDependencyAndroid;
ios?: RNConfigDependencyIos;
};
}
/**
* Result of 'react-native-config' command.
*/
export interface RNConfigResult {
root: string;
reactNativePath: string;
dependencies: Record<string, RNConfigDependency>;
project: {
ios?: {
sourceDir: string;
};
};
}
export type RNConfigReactNativePlatformsConfigAndroid = any;
export type RNConfigReactNativePlatformsConfigIos = any;
interface RNConfigReactNativePlatformsConfig {
platforms?: {
android?: RNConfigReactNativePlatformsConfigAndroid;
ios?: RNConfigReactNativePlatformsConfigIos;
};
}
/**
* The `react-native.config.js` config from projectRoot.
*/
export interface RNConfigReactNativeProjectConfig {
dependencies: Record<string, RNConfigReactNativePlatformsConfig>;
}
/**
* The `react-native.config.js` config from library packageRoot.
*/
export interface RNConfigReactNativeLibraryConfig {
dependency?: RNConfigReactNativePlatformsConfig;
platforms?: any;
}
export type RNConfigReactNativeConfig = RNConfigReactNativeProjectConfig | RNConfigReactNativeLibraryConfig;
/**
* The `project` config represents the app project configuration.
*/
export interface RNConfigReactNativeAppProjectConfig {
android?: {
sourceDir: string;
packageName: string;
};
ios?: {
sourceDir: string;
};
}
export {};

View File

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

View File

@@ -0,0 +1 @@
{"version":3,"file":"reactNativeConfig.types.js","sourceRoot":"","sources":["../../src/reactNativeConfig/reactNativeConfig.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { SupportedPlatform } from '../types';\n\n/**\n * Options for 'react-native-config' command.\n */\nexport interface RNConfigCommandOptions {\n platform: SupportedPlatform;\n projectRoot: string;\n searchPaths: string[];\n}\n\n/**\n * Dependency configuration for Android platform.\n */\nexport interface RNConfigDependencyAndroid {\n sourceDir: string;\n packageImportPath: string;\n packageInstance: string;\n dependencyConfiguration?: string;\n buildTypes: string[];\n libraryName?: string | null;\n componentDescriptors?: string[] | null;\n cmakeListsPath?: string | null;\n cxxModuleCMakeListsModuleName?: string | null;\n cxxModuleCMakeListsPath?: string | null;\n cxxModuleHeaderName?: string | null;\n}\n\n/**\n * Dependency configuration for iOS platform.\n */\nexport interface RNConfigDependencyIos {\n podspecPath: string;\n version: string;\n configurations: string[];\n scriptPhases: any[];\n}\n\n/**\n * Dependency configuration.\n */\nexport interface RNConfigDependency {\n root: string;\n name: string;\n platforms: {\n android?: RNConfigDependencyAndroid;\n ios?: RNConfigDependencyIos;\n };\n}\n\n/**\n * Result of 'react-native-config' command.\n */\nexport interface RNConfigResult {\n root: string;\n reactNativePath: string;\n dependencies: Record<string, RNConfigDependency>;\n project: {\n ios?: {\n sourceDir: string;\n };\n };\n}\n\nexport type RNConfigReactNativePlatformsConfigAndroid = any;\nexport type RNConfigReactNativePlatformsConfigIos = any;\n\ninterface RNConfigReactNativePlatformsConfig {\n platforms?: {\n android?: RNConfigReactNativePlatformsConfigAndroid;\n ios?: RNConfigReactNativePlatformsConfigIos;\n };\n}\n\n/**\n * The `react-native.config.js` config from projectRoot.\n */\nexport interface RNConfigReactNativeProjectConfig {\n dependencies: Record<string, RNConfigReactNativePlatformsConfig>;\n}\n\n/**\n * The `react-native.config.js` config from library packageRoot.\n */\nexport interface RNConfigReactNativeLibraryConfig {\n dependency?: RNConfigReactNativePlatformsConfig;\n platforms?: any;\n}\n\nexport type RNConfigReactNativeConfig =\n | RNConfigReactNativeProjectConfig\n | RNConfigReactNativeLibraryConfig;\n\n/**\n * The `project` config represents the app project configuration.\n */\nexport interface RNConfigReactNativeAppProjectConfig {\n android?: {\n sourceDir: string;\n packageName: string;\n };\n ios?: {\n sourceDir: string;\n };\n}\n"]}

View File

@@ -0,0 +1,20 @@
/// <reference types="node" />
import glob from 'fast-glob';
type GlobOptions = Parameters<typeof glob>[1];
/**
* A matching function that takes a file path and its contents and returns a string if it matches, or null otherwise.
*/
type MatchFunctor = (filePath: string, contents: Buffer) => string | null;
/**
* Check if the file exists.
*/
export declare function fileExistsAsync(file: string): Promise<boolean>;
/**
* Search files that match the glob pattern and return all matches from the matchFunctor.
*/
export declare function globMatchFunctorAllAsync(globPattern: string, matchFunctor: MatchFunctor, options?: GlobOptions): Promise<string[]>;
/**
* Search files that match the glob pattern and return the first match from the matchFunctor.
*/
export declare function globMatchFunctorFirstAsync(globPattern: string, matchFunctor: MatchFunctor, options?: GlobOptions): Promise<string | null>;
export {};

View File

@@ -0,0 +1,58 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.globMatchFunctorFirstAsync = exports.globMatchFunctorAllAsync = exports.fileExistsAsync = void 0;
const fast_glob_1 = __importDefault(require("fast-glob"));
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
/**
* Check if the file exists.
*/
async function fileExistsAsync(file) {
return (await promises_1.default.stat(file).catch(() => null))?.isFile() ?? false;
}
exports.fileExistsAsync = fileExistsAsync;
/**
* Search files that match the glob pattern and return all matches from the matchFunctor.
*/
async function globMatchFunctorAllAsync(globPattern, matchFunctor, options) {
const globStream = fast_glob_1.default.stream(globPattern, options);
const cwd = options?.cwd ?? process.cwd();
const results = [];
for await (const file of globStream) {
let filePath = file.toString();
if (!path_1.default.isAbsolute(filePath)) {
filePath = path_1.default.resolve(cwd, filePath);
}
const contents = await promises_1.default.readFile(filePath);
const matched = matchFunctor(filePath, contents);
if (matched != null) {
results.push(matched);
}
}
return results;
}
exports.globMatchFunctorAllAsync = globMatchFunctorAllAsync;
/**
* Search files that match the glob pattern and return the first match from the matchFunctor.
*/
async function globMatchFunctorFirstAsync(globPattern, matchFunctor, options) {
const globStream = fast_glob_1.default.stream(globPattern, options);
const cwd = options?.cwd ?? process.cwd();
for await (const file of globStream) {
let filePath = file.toString();
if (!path_1.default.isAbsolute(filePath)) {
filePath = path_1.default.resolve(cwd, filePath);
}
const contents = await promises_1.default.readFile(filePath);
const matched = matchFunctor(filePath, contents);
if (matched != null) {
return matched;
}
}
return null;
}
exports.globMatchFunctorFirstAsync = globMatchFunctorFirstAsync;
//# sourceMappingURL=utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/reactNativeConfig/utils.ts"],"names":[],"mappings":";;;;;;AAAA,0DAA6B;AAC7B,2DAA6B;AAC7B,gDAAwB;AASxB;;GAEG;AACI,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,OAAO,CAAC,MAAM,kBAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,KAAK,CAAC;AACpE,CAAC;AAFD,0CAEC;AAED;;GAEG;AACI,KAAK,UAAU,wBAAwB,CAC5C,WAAmB,EACnB,YAA0B,EAC1B,OAAqB;IAErB,MAAM,UAAU,GAAG,mBAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACrD,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1C,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,UAAU,EAAE;QACnC,IAAI,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,cAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;YAC9B,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;SACxC;QACD,MAAM,QAAQ,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACjD,IAAI,OAAO,IAAI,IAAI,EAAE;YACnB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;SACvB;KACF;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AApBD,4DAoBC;AAED;;GAEG;AACI,KAAK,UAAU,0BAA0B,CAC9C,WAAmB,EACnB,YAA0B,EAC1B,OAAqB;IAErB,MAAM,UAAU,GAAG,mBAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IACrD,MAAM,GAAG,GAAG,OAAO,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAC1C,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,UAAU,EAAE;QACnC,IAAI,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;QAC/B,IAAI,CAAC,cAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;YAC9B,QAAQ,GAAG,cAAI,CAAC,OAAO,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;SACxC;QACD,MAAM,QAAQ,GAAG,MAAM,kBAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC7C,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QACjD,IAAI,OAAO,IAAI,IAAI,EAAE;YACnB,OAAO,OAAO,CAAC;SAChB;KACF;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAnBD,gEAmBC","sourcesContent":["import glob from 'fast-glob';\nimport fs from 'fs/promises';\nimport path from 'path';\n\ntype GlobOptions = Parameters<typeof glob>[1];\n\n/**\n * A matching function that takes a file path and its contents and returns a string if it matches, or null otherwise.\n */\ntype MatchFunctor = (filePath: string, contents: Buffer) => string | null;\n\n/**\n * Check if the file exists.\n */\nexport async function fileExistsAsync(file: string): Promise<boolean> {\n return (await fs.stat(file).catch(() => null))?.isFile() ?? false;\n}\n\n/**\n * Search files that match the glob pattern and return all matches from the matchFunctor.\n */\nexport async function globMatchFunctorAllAsync(\n globPattern: string,\n matchFunctor: MatchFunctor,\n options?: GlobOptions\n): Promise<string[]> {\n const globStream = glob.stream(globPattern, options);\n const cwd = options?.cwd ?? process.cwd();\n const results: string[] = [];\n for await (const file of globStream) {\n let filePath = file.toString();\n if (!path.isAbsolute(filePath)) {\n filePath = path.resolve(cwd, filePath);\n }\n const contents = await fs.readFile(filePath);\n const matched = matchFunctor(filePath, contents);\n if (matched != null) {\n results.push(matched);\n }\n }\n return results;\n}\n\n/**\n * Search files that match the glob pattern and return the first match from the matchFunctor.\n */\nexport async function globMatchFunctorFirstAsync(\n globPattern: string,\n matchFunctor: MatchFunctor,\n options?: GlobOptions\n): Promise<string | null> {\n const globStream = glob.stream(globPattern, options);\n const cwd = options?.cwd ?? process.cwd();\n for await (const file of globStream) {\n let filePath = file.toString();\n if (!path.isAbsolute(filePath)) {\n filePath = path.resolve(cwd, filePath);\n }\n const contents = await fs.readFile(filePath);\n const matched = matchFunctor(filePath, contents);\n if (matched != null) {\n return matched;\n }\n }\n return null;\n}\n"]}

View File

@@ -0,0 +1,237 @@
import { ExpoModuleConfig } from './ExpoModuleConfig';
export type SupportedPlatform = 'apple' | 'ios' | 'android' | 'web' | 'macos' | 'tvos' | 'devtools';
/**
* Options that can be passed through `expo.autolinking` config in the package.json file.
*/
export type AutolinkingOptions = {
searchPaths?: string[] | null;
ignorePaths?: string[] | null;
exclude?: string[] | null;
flags?: Record<string, any>;
} & {
[key in SupportedPlatform]?: AutolinkingOptions;
};
export interface SearchOptions {
searchPaths: string[];
ignorePaths?: string[] | null;
exclude?: string[] | null;
platform: SupportedPlatform;
silent?: boolean;
nativeModulesDir?: string | null;
projectRoot: string;
/**
* Filter the search results to only include the project dependencies.
* In a monorepo, you may like to set this to false and link all modules from the monorepo.
* @default true
*/
onlyProjectDeps?: boolean;
flags?: Record<string, any>;
}
export interface ResolveOptions extends SearchOptions {
json?: boolean;
}
export interface GenerateOptions extends ResolveOptions {
target: string;
namespace?: string;
empty?: boolean;
}
export interface GenerateModulesProviderOptions extends ResolveOptions {
target: string;
packages: string[];
}
export interface PatchReactImportsOptions {
podsRoot: string;
dryRun: boolean;
}
export type PackageRevision = {
path: string;
version: string;
config?: ExpoModuleConfig;
duplicates?: PackageRevision[];
};
export type SearchResults = {
[moduleName: string]: PackageRevision;
};
export interface ModuleAndroidProjectInfo {
name: string;
sourceDir: string;
}
export interface ModuleAndroidPluginInfo {
id: string;
sourceDir: string;
}
export interface ModuleDescriptorAndroid {
packageName: string;
projects: ModuleAndroidProjectInfo[];
plugins?: ModuleAndroidPluginInfo[];
modules: string[];
}
export interface ModuleIosPodspecInfo {
podName: string;
podspecDir: string;
}
export interface ModuleDescriptorIos {
packageName: string;
pods: ModuleIosPodspecInfo[];
flags: Record<string, any> | undefined;
swiftModuleNames: string[];
modules: string[];
appDelegateSubscribers: string[];
reactDelegateHandlers: string[];
debugOnly: boolean;
}
export interface ModuleDescriptorDevTools {
packageName: string;
packageRoot: string;
webpageRoot: string;
}
export type ModuleDescriptor = ModuleDescriptorAndroid | ModuleDescriptorIos | ModuleDescriptorDevTools;
export interface AndroidGradlePluginDescriptor {
/**
* Gradle plugin ID
*/
id: string;
/**
* Artifact group
*/
group: string;
/**
* Relative path to the gradle plugin directory
*/
sourceDir: string;
}
/**
* Represents a raw config specific to Apple platforms.
*/
export type RawModuleConfigApple = {
/**
* Names of Swift native modules classes to put to the generated modules provider file.
*/
modules?: string[];
/**
* Names of Swift native modules classes to put to the generated modules provider file.
* @deprecated Deprecated in favor of `modules`. Might be removed in the future releases.
*/
modulesClassNames?: string[];
/**
* Names of Swift classes that hooks into `ExpoAppDelegate` to receive AppDelegate life-cycle events.
*/
appDelegateSubscribers?: string[];
/**
* Names of Swift classes that implement `ExpoReactDelegateHandler` to hook React instance creation.
*/
reactDelegateHandlers?: string[];
/**
* Podspec relative path.
* To have multiple podspecs, string array type is also supported.
*/
podspecPath?: string | string[];
/**
* Swift product module name. If empty, the pod name is used for Swift imports.
* To have multiple modules, string array is also supported.
*/
swiftModuleName?: string | string[];
/**
* Whether this module will be added only to the debug configuration.
* Defaults to false.
*/
debugOnly?: boolean;
};
/**
* Represents a raw config from `expo-module.json`.
*/
export interface RawExpoModuleConfig {
/**
* An array of supported platforms.
*/
platforms?: SupportedPlatform[];
/**
* A config for all Apple platforms.
*/
apple?: RawModuleConfigApple;
/**
* The legacy config previously used for iOS platform. For backwards compatibility it's used as the fallback for `apple`.
* Also due to backwards compatibility, it includes the deprecated `modulesClassNames` field.
* @deprecated As the module can now support more than iOS platform, use the generic `apple` config instead.
*/
ios?: RawModuleConfigApple;
/**
* Android-specific config.
*/
android?: {
/**
* Full names (package + class name) of Kotlin native modules classes to put to the generated package provider file.
*/
modules?: string[];
/**
* Full names (package + class name) of Kotlin native modules classes to put to the generated package provider file.
* @deprecated Deprecated in favor of `modules`. Might be removed in the future releases.
*/
modulesClassNames?: string[];
/**
* build.gradle relative path.
* To have multiple build.gradle projects, string array type is also supported.
*/
gradlePath?: string | string[];
/**
* Gradle plugins.
*/
gradlePlugins?: AndroidGradlePluginDescriptor[];
};
/**
* DevTools-specific config.
*/
devtools?: {
/**
* The webpage root directory for Expo CLI DevTools to serve the web resources.
*/
webpageRoot: string;
};
}
interface AndroidMavenRepositoryPasswordCredentials {
username: string;
password: string;
}
interface AndroidMavenRepositoryHttpHeaderCredentials {
name: string;
value: string;
}
interface AndroidMavenRepositoryAWSCredentials {
accessKey: string;
secretKey: string;
sessionToken?: string;
}
type AndroidMavenRepositoryCredentials = AndroidMavenRepositoryPasswordCredentials | AndroidMavenRepositoryHttpHeaderCredentials | AndroidMavenRepositoryAWSCredentials;
export interface AndroidMavenRepository {
/**
* The URL of the Maven repository.
*/
url: string;
/**
* The credentials to use when accessing the Maven repository.
* May be of type PasswordCredentials, HttpHeaderCredentials, or AWSCredentials.
*
* @see the authentication schemes section of [Gradle documentation](https://docs.gradle.org/current/userguide/declaring_repositories.html#sec:authentication_schemes) for more information.
*/
credentials?: AndroidMavenRepositoryCredentials;
/**
* The authentication scheme to use when accessing the Maven repository.
*/
authentication?: 'basic' | 'digest' | 'header';
}
interface ApplePod {
name: string;
version?: string;
configurations?: string[];
modular_headers?: boolean;
source?: string;
path?: string;
podspec?: string;
testspecs?: string[];
git?: string;
branch?: string;
tag?: string;
commit?: string;
}
export type ExtraDependencies = AndroidMavenRepository[] | ApplePod[];
export {};

View File

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

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,53 @@
import spawnAsync, { SpawnOptions, SpawnResult } from '@expo/spawn-async';
import { spawnSync } from 'child_process';
import { join } from 'path';
export const AUTOLINKINNG_CLI = join(__dirname, '../bin/expo-modules-autolinking.js');
function isSpawnResult(errorOrResult: Error): errorOrResult is Error & SpawnResult {
return 'pid' in errorOrResult && 'stdout' in errorOrResult && 'stderr' in errorOrResult;
}
export async function autolinkingRunAsync(
args: string[],
options?: SpawnOptions
): Promise<SpawnResult> {
const promise = spawnAsync(AUTOLINKINNG_CLI, args, {
...options,
env: { ...process.env, EXPO_SHOULD_USE_LEGACY_PACKAGE_INTERFACE: '1' },
});
try {
return await promise;
} catch (error) {
if (isSpawnResult(error)) {
if (error.stdout) error.message += `\n------\nSTDOUT:\n${error.stdout}`;
if (error.stderr) error.message += `\n------\nSTDERR:\n${error.stderr}`;
}
throw error;
}
}
// For some reason, it can't be async, cause otherwise we will get `yarn did not print valid JSON:` error
export function yarnSync(options?: SpawnOptions) {
spawnSync('yarn', ['install', '--silent'], options);
}
export function combinations<T, U>(
aKey: string,
a: T[],
bKey: string,
b: U[]
): { [key: string]: T | U }[] {
const result = [];
a.forEach(aValue => {
b.forEach(bValue => {
result.push({
[aKey]: aValue,
[bKey]: bValue,
});
});
});
return result;
}

View File

@@ -0,0 +1 @@
export * from './build/autolinking';

View File

@@ -0,0 +1 @@
module.exports = require('./build/autolinking');

View File

@@ -0,0 +1,5 @@
const sharedPreset = require('expo-module-scripts/jest-preset-plugin');
module.exports = {
...sharedPreset,
roots: ['__mocks__', 'src', 'e2e'],
};

View File

@@ -0,0 +1,902 @@
9.1.0 / 2021-01-19
------------------
- Add promise support for `fs.rm()` ([#841](https://github.com/jprichardson/node-fs-extra/issues/841), [#860](https://github.com/jprichardson/node-fs-extra/pull/860))
- Upgrade universalify for performance improvments ([#825](https://github.com/jprichardson/node-fs-extra/pull/825))
9.0.1 / 2020-06-03
------------------
- Fix issue with `ensureFile()` when used with Jest on Windows ([#804](https://github.com/jprichardson/node-fs-extra/issues/804), [#805](https://github.com/jprichardson/node-fs-extra/pull/805))
- Remove unneeded `process.umask()` call ([#791](https://github.com/jprichardson/node-fs-extra/pull/791))
- Docs improvements ([#753](https://github.com/jprichardson/node-fs-extra/pull/753), [#795](https://github.com/jprichardson/node-fs-extra/pull/795), [#797](https://github.com/jprichardson/node-fs-extra/pull/797))
9.0.0 / 2020-03-19
------------------
### Breaking changes
- Requires Node.js version 10 or greater ([#725](https://github.com/jprichardson/node-fs-extra/issues/725), [#751](https://github.com/jprichardson/node-fs-extra/pull/751))
- Switched `ensureDir*` to use a fork of https://github.com/sindresorhus/make-dir to make use of native recursive `fs.mkdir` where possible ([#619](https://github.com/jprichardson/node-fs-extra/issues/619), [#756](https://github.com/jprichardson/node-fs-extra/pull/756))
- Properly preserve `atime` for `copy*` with `preserveTimestamps` option ([#633](https://github.com/jprichardson/node-fs-extra/pull/633))
**The following changes, allthough technically breaking, will not affect the vast majority of users:**
- `outputJson` now outputs objects as they were when the function was called, even if they are mutated later ([#702](https://github.com/jprichardson/node-fs-extra/issues/702), [#768](https://github.com/jprichardson/node-fs-extra/pull/768))
- Cannot pass `null` as an options parameter to `*Json*` methods ([#745](https://github.com/jprichardson/node-fs-extra/issues/745), [#768](https://github.com/jprichardson/node-fs-extra/pull/768))
### Improvements
- Add promise shims for `fs.writev` & `fs.opendir` ([#747](https://github.com/jprichardson/node-fs-extra/pull/747))
- Better errors for `ensureFile` ([#696](https://github.com/jprichardson/node-fs-extra/issues/696), [#744](https://github.com/jprichardson/node-fs-extra/pull/744))
- Better file comparison for older Node versions ([#694](https://github.com/jprichardson/node-fs-extra/pull/694))
### Miscellaneous changes
- Peformance optimizations ([#762](https://github.com/jprichardson/node-fs-extra/issues/762), [#764](https://github.com/jprichardson/node-fs-extra/pull/764))
- Add missing documentation for aliases ([#758](https://github.com/jprichardson/node-fs-extra/issues/758), [#766](https://github.com/jprichardson/node-fs-extra/pull/766))
- Update `universalify` dependency ([#767](https://github.com/jprichardson/node-fs-extra/pull/767))
8.1.0 / 2019-06-28
------------------
- Add support for promisified `fs.realpath.native` in Node v9.2+ ([#650](https://github.com/jprichardson/node-fs-extra/issues/650), [#682](https://github.com/jprichardson/node-fs-extra/pull/682))
- Update `graceful-fs` dependency ([#700](https://github.com/jprichardson/node-fs-extra/pull/700))
- Use `graceful-fs` everywhere ([#700](https://github.com/jprichardson/node-fs-extra/pull/700))
8.0.1 / 2019-05-13
------------------
- Fix bug `Maximum call stack size exceeded` error in `util/stat` ([#679](https://github.com/jprichardson/node-fs-extra/pull/679))
8.0.0 / 2019-05-11
------------------
**NOTE:** Node.js v6 support is deprecated, and will be dropped in the next major release.
- Use `renameSync()` under the hood in `moveSync()`
- Fix bug with bind-mounted directories in `copy*()` ([#613](https://github.com/jprichardson/node-fs-extra/issues/613), [#618](https://github.com/jprichardson/node-fs-extra/pull/618))
- Fix bug in `move()` with case-insensitive file systems
- Use `fs.stat()`'s `bigint` option in `copy*()` & `move*()` where possible ([#657](https://github.com/jprichardson/node-fs-extra/issues/657))
7.0.1 / 2018-11-07
------------------
- Fix `removeSync()` on Windows, in some cases, it would error out with `ENOTEMPTY` ([#646](https://github.com/jprichardson/node-fs-extra/pull/646))
- Document `mode` option for `ensureDir*()` ([#587](https://github.com/jprichardson/node-fs-extra/pull/587))
- Don't include documentation files in npm package tarball ([#642](https://github.com/jprichardson/node-fs-extra/issues/642), [#643](https://github.com/jprichardson/node-fs-extra/pull/643))
7.0.0 / 2018-07-16
------------------
- **BREAKING:** Refine `copy*()` handling of symlinks to properly detect symlinks that point to the same file. ([#582](https://github.com/jprichardson/node-fs-extra/pull/582))
- Fix bug with copying write-protected directories ([#600](https://github.com/jprichardson/node-fs-extra/pull/600))
- Universalify `fs.lchmod()` ([#596](https://github.com/jprichardson/node-fs-extra/pull/596))
- Add `engines` field to `package.json` ([#580](https://github.com/jprichardson/node-fs-extra/pull/580))
6.0.1 / 2018-05-09
------------------
- Fix `fs.promises` `ExperimentalWarning` on Node v10.1.0 ([#578](https://github.com/jprichardson/node-fs-extra/pull/578))
6.0.0 / 2018-05-01
------------------
- Drop support for Node.js versions 4, 5, & 7 ([#564](https://github.com/jprichardson/node-fs-extra/pull/564))
- Rewrite `move` to use `fs.rename` where possible ([#549](https://github.com/jprichardson/node-fs-extra/pull/549))
- Don't convert relative paths to absolute paths for `filter` ([#554](https://github.com/jprichardson/node-fs-extra/pull/554))
- `copy*`'s behavior when `preserveTimestamps` is `false` has been OS-dependent since 5.0.0, but that's now explicitly noted in the docs ([#563](https://github.com/jprichardson/node-fs-extra/pull/563))
- Fix subdirectory detection for `copy*` & `move*` ([#541](https://github.com/jprichardson/node-fs-extra/pull/541))
- Handle case-insensitive paths correctly in `copy*` ([#568](https://github.com/jprichardson/node-fs-extra/pull/568))
5.0.0 / 2017-12-11
------------------
Significant refactor of `copy()` & `copySync()`, including breaking changes. No changes to other functions in this release.
Huge thanks to **[@manidlou](https://github.com/manidlou)** for doing most of the work on this release.
- The `filter` option can no longer be a RegExp (must be a function). This was deprecated since fs-extra v1.0.0. [#512](https://github.com/jprichardson/node-fs-extra/pull/512)
- `copy()`'s `filter` option can now be a function that returns a Promise. [#518](https://github.com/jprichardson/node-fs-extra/pull/518)
- `copy()` & `copySync()` now use `fs.copyFile()`/`fs.copyFileSync()` in environments that support it (currently Node 8.5.0+). Older Node versions still get the old implementation. [#505](https://github.com/jprichardson/node-fs-extra/pull/505)
- Don't allow copying a directory into itself. [#83](https://github.com/jprichardson/node-fs-extra/issues/83)
- Handle copying between identical files. [#198](https://github.com/jprichardson/node-fs-extra/issues/198)
- Error out when copying an empty folder to a path that already exists. [#464](https://github.com/jprichardson/node-fs-extra/issues/464)
- Don't create `dest`'s parent if the `filter` function aborts the `copy()` operation. [#517](https://github.com/jprichardson/node-fs-extra/pull/517)
- Fix `writeStream` not being closed if there was an error in `copy()`. [#516](https://github.com/jprichardson/node-fs-extra/pull/516)
4.0.3 / 2017-12-05
------------------
- Fix wrong `chmod` values in `fs.remove()` [#501](https://github.com/jprichardson/node-fs-extra/pull/501)
- Fix `TypeError` on systems that don't have some `fs` operations like `lchown` [#520](https://github.com/jprichardson/node-fs-extra/pull/520)
4.0.2 / 2017-09-12
------------------
- Added `EOL` option to `writeJson*` & `outputJson*` (via upgrade to jsonfile v4)
- Added promise support to [`fs.copyFile()`](https://nodejs.org/api/fs.html#fs_fs_copyfile_src_dest_flags_callback) in Node 8.5+
- Added `.js` extension to `main` field in `package.json` for better tooling compatibility. [#485](https://github.com/jprichardson/node-fs-extra/pull/485)
4.0.1 / 2017-07-31
------------------
### Fixed
- Previously, `ensureFile()` & `ensureFileSync()` would do nothing if the path was a directory. Now, they error out for consistency with `ensureDir()`. [#465](https://github.com/jprichardson/node-fs-extra/issues/465), [#466](https://github.com/jprichardson/node-fs-extra/pull/466), [#470](https://github.com/jprichardson/node-fs-extra/issues/470)
4.0.0 / 2017-07-14
------------------
### Changed
- **BREAKING:** The promisified versions of `fs.read()` & `fs.write()` now return objects. See [the docs](docs/fs-read-write.md) for details. [#436](https://github.com/jprichardson/node-fs-extra/issues/436), [#449](https://github.com/jprichardson/node-fs-extra/pull/449)
- `fs.move()` now errors out when destination is a subdirectory of source. [#458](https://github.com/jprichardson/node-fs-extra/pull/458)
- Applied upstream fixes from `rimraf` to `fs.remove()` & `fs.removeSync()`. [#459](https://github.com/jprichardson/node-fs-extra/pull/459)
### Fixed
- Got `fs.outputJSONSync()` working again; it was broken due to refactoring. [#428](https://github.com/jprichardson/node-fs-extra/pull/428)
Also clarified the docs in a few places.
3.0.1 / 2017-05-04
------------------
- Fix bug in `move()` & `moveSync()` when source and destination are the same, and source does not exist. [#415](https://github.com/jprichardson/node-fs-extra/pull/415)
3.0.0 / 2017-04-27
------------------
### Added
- **BREAKING:** Added Promise support. All asynchronous native fs methods and fs-extra methods now return a promise if the callback is not passed. [#403](https://github.com/jprichardson/node-fs-extra/pull/403)
- `pathExists()`, a replacement for the deprecated `fs.exists`. `pathExists` has a normal error-first callback signature. Also added `pathExistsSync`, an alias to `fs.existsSync`, for completeness. [#406](https://github.com/jprichardson/node-fs-extra/pull/406)
### Removed
- **BREAKING:** Removed support for setting the default spaces for `writeJson()`, `writeJsonSync()`, `outputJson()`, & `outputJsonSync()`. This was undocumented. [#402](https://github.com/jprichardson/node-fs-extra/pull/402)
### Changed
- Upgraded jsonfile dependency to v3.0.0:
- **BREAKING:** Changed behavior of `throws` option for `readJsonSync()`; now does not throw filesystem errors when `throws` is `false`.
- **BREAKING:** `writeJson()`, `writeJsonSync()`, `outputJson()`, & `outputJsonSync()` now output minified JSON by default for consistency with `JSON.stringify()`; set the `spaces` option to `2` to override this new behavior. [#402](https://github.com/jprichardson/node-fs-extra/pull/402)
- Use `Buffer.allocUnsafe()` instead of `new Buffer()` in environments that support it. [#394](https://github.com/jprichardson/node-fs-extra/pull/394)
### Fixed
- `removeSync()` silently failed on Windows in some cases. Now throws an `EBUSY` error. [#408](https://github.com/jprichardson/node-fs-extra/pull/408)
2.1.2 / 2017-03-16
------------------
### Fixed
- Weird windows bug that resulted in `ensureDir()`'s callback being called twice in some cases. This bug may have also affected `remove()`. See [#392](https://github.com/jprichardson/node-fs-extra/issues/392), [#393](https://github.com/jprichardson/node-fs-extra/pull/393)
2.1.1 / 2017-03-15
------------------
### Fixed
- Reverted [`5597bd`](https://github.com/jprichardson/node-fs-extra/commit/5597bd5b67f7d060f5f5bf26e9635be48330f5d7), this broke compatibility with Node.js versions v4+ but less than `v4.5.0`.
- Remove `Buffer.alloc()` usage in `moveSync()`.
2.1.0 / 2017-03-15
------------------
Thanks to [Mani Maghsoudlou (@manidlou)](https://github.com/manidlou) & [Jan Peer Stöcklmair (@JPeer264)](https://github.com/JPeer264) for their extraordinary help with this release!
### Added
- `moveSync()` See [#309], [#381](https://github.com/jprichardson/node-fs-extra/pull/381). ([@manidlou](https://github.com/manidlou))
- `copy()` and `copySync()`'s `filter` option now gets the destination path passed as the second parameter. [#366](https://github.com/jprichardson/node-fs-extra/pull/366) ([@manidlou](https://github.com/manidlou))
### Changed
- Use `Buffer.alloc()` instead of deprecated `new Buffer()` in `copySync()`. [#380](https://github.com/jprichardson/node-fs-extra/pull/380) ([@manidlou](https://github.com/manidlou))
- Refactored entire codebase to use ES6 features supported by Node.js v4+ [#355](https://github.com/jprichardson/node-fs-extra/issues/355). [(@JPeer264)](https://github.com/JPeer264)
- Refactored docs. ([@manidlou](https://github.com/manidlou))
### Fixed
- `move()` shouldn't error out when source and dest are the same. [#377](https://github.com/jprichardson/node-fs-extra/issues/377), [#378](https://github.com/jprichardson/node-fs-extra/pull/378) ([@jdalton](https://github.com/jdalton))
2.0.0 / 2017-01-16
------------------
### Removed
- **BREAKING:** Removed support for Node `v0.12`. The Node foundation stopped officially supporting it
on Jan 1st, 2017.
- **BREAKING:** Remove `walk()` and `walkSync()`. `walkSync()` was only part of `fs-extra` for a little
over two months. Use [klaw](https://github.com/jprichardson/node-klaw) instead of `walk()`, in fact, `walk()` was just
an alias to klaw. For `walkSync()` use [klaw-sync](https://github.com/mawni/node-klaw-sync). See: [#338], [#339]
### Changed
- **BREAKING:** Renamed `clobber` to `overwrite`. This affects `copy()`, `copySync()`, and `move()`. [#330], [#333]
- Moved docs, to `docs/`. [#340]
### Fixed
- Apply filters to directories in `copySync()` like in `copy()`. [#324]
- A specific condition when disk is under heavy use, `copy()` can fail. [#326]
1.0.0 / 2016-11-01
------------------
After five years of development, we finally have reach the 1.0.0 milestone! Big thanks goes
to [Ryan Zim](https://github.com/RyanZim) for leading the charge on this release!
### Added
- `walkSync()`
### Changed
- **BREAKING**: dropped Node v0.10 support.
- disabled `rimaf` globbing, wasn't used. [#280]
- deprecate `copy()/copySync()` option `filter` if it's a `RegExp`. `filter` should now be a function.
- inline `rimraf`. This is temporary and was done because `rimraf` depended upon the beefy `glob` which `fs-extra` does not use. [#300]
### Fixed
- bug fix proper closing of file handle on `utimesMillis()` [#271]
- proper escaping of files with dollar signs [#291]
- `copySync()` failed if user didn't own file. [#199], [#301]
0.30.0 / 2016-04-28
-------------------
- Brought back Node v0.10 support. I didn't realize there was still demand. Official support will end **2016-10-01**.
0.29.0 / 2016-04-27
-------------------
- **BREAKING**: removed support for Node v0.10. If you still want to use Node v0.10, everything should work except for `ensureLink()/ensureSymlink()`. Node v0.12 is still supported but will be dropped in the near future as well.
0.28.0 / 2016-04-17
-------------------
- **BREAKING**: removed `createOutputStream()`. Use https://www.npmjs.com/package/create-output-stream. See: [#192][#192]
- `mkdirs()/mkdirsSync()` check for invalid win32 path chars. See: [#209][#209], [#237][#237]
- `mkdirs()/mkdirsSync()` if drive not mounted, error. See: [#93][#93]
0.27.0 / 2016-04-15
-------------------
- add `dereference` option to `copySync()`. [#235][#235]
0.26.7 / 2016-03-16
-------------------
- fixed `copy()` if source and dest are the same. [#230][#230]
0.26.6 / 2016-03-15
-------------------
- fixed if `emptyDir()` does not have a callback: [#229][#229]
0.26.5 / 2016-01-27
-------------------
- `copy()` with two arguments (w/o callback) was broken. See: [#215][#215]
0.26.4 / 2016-01-05
-------------------
- `copySync()` made `preserveTimestamps` default consistent with `copy()` which is `false`. See: [#208][#208]
0.26.3 / 2015-12-17
-------------------
- fixed `copy()` hangup in copying blockDevice / characterDevice / `/dev/null`. See: [#193][#193]
0.26.2 / 2015-11-02
-------------------
- fixed `outputJson{Sync}()` spacing adherence to `fs.spaces`
0.26.1 / 2015-11-02
-------------------
- fixed `copySync()` when `clogger=true` and the destination is read only. See: [#190][#190]
0.26.0 / 2015-10-25
-------------------
- extracted the `walk()` function into its own module [`klaw`](https://github.com/jprichardson/node-klaw).
0.25.0 / 2015-10-24
-------------------
- now has a file walker `walk()`
0.24.0 / 2015-08-28
-------------------
- removed alias `delete()` and `deleteSync()`. See: [#171][#171]
0.23.1 / 2015-08-07
-------------------
- Better handling of errors for `move()` when moving across devices. [#170][#170]
- `ensureSymlink()` and `ensureLink()` should not throw errors if link exists. [#169][#169]
0.23.0 / 2015-08-06
-------------------
- added `ensureLink{Sync}()` and `ensureSymlink{Sync}()`. See: [#165][#165]
0.22.1 / 2015-07-09
-------------------
- Prevent calling `hasMillisResSync()` on module load. See: [#149][#149].
Fixes regression that was introduced in `0.21.0`.
0.22.0 / 2015-07-09
-------------------
- preserve permissions / ownership in `copy()`. See: [#54][#54]
0.21.0 / 2015-07-04
-------------------
- add option to preserve timestamps in `copy()` and `copySync()`. See: [#141][#141]
- updated `graceful-fs@3.x` to `4.x`. This brings in features from `amazing-graceful-fs` (much cleaner code / less hacks)
0.20.1 / 2015-06-23
-------------------
- fixed regression caused by latest jsonfile update: See: https://github.com/jprichardson/node-jsonfile/issues/26
0.20.0 / 2015-06-19
-------------------
- removed `jsonfile` aliases with `File` in the name, they weren't documented and probably weren't in use e.g.
this package had both `fs.readJsonFile` and `fs.readJson` that were aliases to each other, now use `fs.readJson`.
- preliminary walker created. Intentionally not documented. If you use it, it will almost certainly change and break your code.
- started moving tests inline
- upgraded to `jsonfile@2.1.0`, can now pass JSON revivers/replacers to `readJson()`, `writeJson()`, `outputJson()`
0.19.0 / 2015-06-08
-------------------
- `fs.copy()` had support for Node v0.8, dropped support
0.18.4 / 2015-05-22
-------------------
- fixed license field according to this: [#136][#136] and https://github.com/npm/npm/releases/tag/v2.10.0
0.18.3 / 2015-05-08
-------------------
- bugfix: handle `EEXIST` when clobbering on some Linux systems. [#134][#134]
0.18.2 / 2015-04-17
-------------------
- bugfix: allow `F_OK` ([#120][#120])
0.18.1 / 2015-04-15
-------------------
- improved windows support for `move()` a bit. https://github.com/jprichardson/node-fs-extra/commit/92838980f25dc2ee4ec46b43ee14d3c4a1d30c1b
- fixed a lot of tests for Windows (appveyor)
0.18.0 / 2015-03-31
-------------------
- added `emptyDir()` and `emptyDirSync()`
0.17.0 / 2015-03-28
-------------------
- `copySync` added `clobber` option (before always would clobber, now if `clobber` is `false` it throws an error if the destination exists).
**Only works with files at the moment.**
- `createOutputStream()` added. See: [#118][#118]
0.16.5 / 2015-03-08
-------------------
- fixed `fs.move` when `clobber` is `true` and destination is a directory, it should clobber. [#114][#114]
0.16.4 / 2015-03-01
-------------------
- `fs.mkdirs` fix infinite loop on Windows. See: See https://github.com/substack/node-mkdirp/pull/74 and https://github.com/substack/node-mkdirp/issues/66
0.16.3 / 2015-01-28
-------------------
- reverted https://github.com/jprichardson/node-fs-extra/commit/1ee77c8a805eba5b99382a2591ff99667847c9c9
0.16.2 / 2015-01-28
-------------------
- fixed `fs.copy` for Node v0.8 (support is temporary and will be removed in the near future)
0.16.1 / 2015-01-28
-------------------
- if `setImmediate` is not available, fall back to `process.nextTick`
0.16.0 / 2015-01-28
-------------------
- bugfix `fs.move()` into itself. Closes [#104]
- bugfix `fs.move()` moving directory across device. Closes [#108]
- added coveralls support
- bugfix: nasty multiple callback `fs.copy()` bug. Closes [#98]
- misc fs.copy code cleanups
0.15.0 / 2015-01-21
-------------------
- dropped `ncp`, imported code in
- because of previous, now supports `io.js`
- `graceful-fs` is now a dependency
0.14.0 / 2015-01-05
-------------------
- changed `copy`/`copySync` from `fs.copy(src, dest, [filters], callback)` to `fs.copy(src, dest, [options], callback)` [#100][#100]
- removed mockfs tests for mkdirp (this may be temporary, but was getting in the way of other tests)
0.13.0 / 2014-12-10
-------------------
- removed `touch` and `touchSync` methods (they didn't handle permissions like UNIX touch)
- updated `"ncp": "^0.6.0"` to `"ncp": "^1.0.1"`
- imported `mkdirp` => `minimist` and `mkdirp` are no longer dependences, should now appease people who wanted `mkdirp` to be `--use_strict` safe. See [#59]([#59][#59])
0.12.0 / 2014-09-22
-------------------
- copy symlinks in `copySync()` [#85][#85]
0.11.1 / 2014-09-02
-------------------
- bugfix `copySync()` preserve file permissions [#80][#80]
0.11.0 / 2014-08-11
-------------------
- upgraded `"ncp": "^0.5.1"` to `"ncp": "^0.6.0"`
- upgrade `jsonfile": "^1.2.0"` to `jsonfile": "^2.0.0"` => on write, json files now have `\n` at end. Also adds `options.throws` to `readJsonSync()`
see https://github.com/jprichardson/node-jsonfile#readfilesyncfilename-options for more details.
0.10.0 / 2014-06-29
------------------
* bugfix: upgaded `"jsonfile": "~1.1.0"` to `"jsonfile": "^1.2.0"`, bumped minor because of `jsonfile` dep change
from `~` to `^`. [#67]
0.9.1 / 2014-05-22
------------------
* removed Node.js `0.8.x` support, `0.9.0` was published moments ago and should have been done there
0.9.0 / 2014-05-22
------------------
* upgraded `ncp` from `~0.4.2` to `^0.5.1`, [#58]
* upgraded `rimraf` from `~2.2.6` to `^2.2.8`
* upgraded `mkdirp` from `0.3.x` to `^0.5.0`
* added methods `ensureFile()`, `ensureFileSync()`
* added methods `ensureDir()`, `ensureDirSync()` [#31]
* added `move()` method. From: https://github.com/andrewrk/node-mv
0.8.1 / 2013-10-24
------------------
* copy failed to return an error to the callback if a file doesn't exist (ulikoehler [#38], [#39])
0.8.0 / 2013-10-14
------------------
* `filter` implemented on `copy()` and `copySync()`. (Srirangan / [#36])
0.7.1 / 2013-10-12
------------------
* `copySync()` implemented (Srirangan / [#33])
* updated to the latest `jsonfile` version `1.1.0` which gives `options` params for the JSON methods. Closes [#32]
0.7.0 / 2013-10-07
------------------
* update readme conventions
* `copy()` now works if destination directory does not exist. Closes [#29]
0.6.4 / 2013-09-05
------------------
* changed `homepage` field in package.json to remove NPM warning
0.6.3 / 2013-06-28
------------------
* changed JSON spacing default from `4` to `2` to follow Node conventions
* updated `jsonfile` dep
* updated `rimraf` dep
0.6.2 / 2013-06-28
------------------
* added .npmignore, [#25]
0.6.1 / 2013-05-14
------------------
* modified for `strict` mode, closes [#24]
* added `outputJson()/outputJsonSync()`, closes [#23]
0.6.0 / 2013-03-18
------------------
* removed node 0.6 support
* added node 0.10 support
* upgraded to latest `ncp` and `rimraf`.
* optional `graceful-fs` support. Closes [#17]
0.5.0 / 2013-02-03
------------------
* Removed `readTextFile`.
* Renamed `readJSONFile` to `readJSON` and `readJson`, same with write.
* Restructured documentation a bit. Added roadmap.
0.4.0 / 2013-01-28
------------------
* Set default spaces in `jsonfile` from 4 to 2.
* Updated `testutil` deps for tests.
* Renamed `touch()` to `createFile()`
* Added `outputFile()` and `outputFileSync()`
* Changed creation of testing diretories so the /tmp dir is not littered.
* Added `readTextFile()` and `readTextFileSync()`.
0.3.2 / 2012-11-01
------------------
* Added `touch()` and `touchSync()` methods.
0.3.1 / 2012-10-11
------------------
* Fixed some stray globals.
0.3.0 / 2012-10-09
------------------
* Removed all CoffeeScript from tests.
* Renamed `mkdir` to `mkdirs`/`mkdirp`.
0.2.1 / 2012-09-11
------------------
* Updated `rimraf` dep.
0.2.0 / 2012-09-10
------------------
* Rewrote module into JavaScript. (Must still rewrite tests into JavaScript)
* Added all methods of [jsonfile](https://github.com/jprichardson/node-jsonfile)
* Added Travis-CI.
0.1.3 / 2012-08-13
------------------
* Added method `readJSONFile`.
0.1.2 / 2012-06-15
------------------
* Bug fix: `deleteSync()` didn't exist.
* Verified Node v0.8 compatibility.
0.1.1 / 2012-06-15
------------------
* Fixed bug in `remove()`/`delete()` that wouldn't execute the function if a callback wasn't passed.
0.1.0 / 2012-05-31
------------------
* Renamed `copyFile()` to `copy()`. `copy()` can now copy directories (recursively) too.
* Renamed `rmrf()` to `remove()`.
* `remove()` aliased with `delete()`.
* Added `mkdirp` capabilities. Named: `mkdir()`. Hides Node.js native `mkdir()`.
* Instead of exporting the native `fs` module with new functions, I now copy over the native methods to a new object and export that instead.
0.0.4 / 2012-03-14
------------------
* Removed CoffeeScript dependency
0.0.3 / 2012-01-11
------------------
* Added methods rmrf and rmrfSync
* Moved tests from Jasmine to Mocha
[#344]: https://github.com/jprichardson/node-fs-extra/issues/344 "Licence Year"
[#343]: https://github.com/jprichardson/node-fs-extra/pull/343 "Add klaw-sync link to readme"
[#342]: https://github.com/jprichardson/node-fs-extra/pull/342 "allow preserveTimestamps when use move"
[#341]: https://github.com/jprichardson/node-fs-extra/issues/341 "mkdirp(path.dirname(dest) in move() logic needs cleaning up [question]"
[#340]: https://github.com/jprichardson/node-fs-extra/pull/340 "Move docs to seperate docs folder [documentation]"
[#339]: https://github.com/jprichardson/node-fs-extra/pull/339 "Remove walk() & walkSync() [feature-walk]"
[#338]: https://github.com/jprichardson/node-fs-extra/issues/338 "Remove walk() and walkSync() [feature-walk]"
[#337]: https://github.com/jprichardson/node-fs-extra/issues/337 "copy doesn't return a yieldable value"
[#336]: https://github.com/jprichardson/node-fs-extra/pull/336 "Docs enhanced walk sync [documentation, feature-walk]"
[#335]: https://github.com/jprichardson/node-fs-extra/pull/335 "Refactor move() tests [feature-move]"
[#334]: https://github.com/jprichardson/node-fs-extra/pull/334 "Cleanup lib/move/index.js [feature-move]"
[#333]: https://github.com/jprichardson/node-fs-extra/pull/333 "Rename clobber to overwrite [feature-copy, feature-move]"
[#332]: https://github.com/jprichardson/node-fs-extra/pull/332 "BREAKING: Drop Node v0.12 & io.js support"
[#331]: https://github.com/jprichardson/node-fs-extra/issues/331 "Add support for chmodr [enhancement, future]"
[#330]: https://github.com/jprichardson/node-fs-extra/pull/330 "BREAKING: Do not error when copy destination exists & clobber: false [feature-copy]"
[#329]: https://github.com/jprichardson/node-fs-extra/issues/329 "Does .walk() scale to large directories? [question]"
[#328]: https://github.com/jprichardson/node-fs-extra/issues/328 "Copying files corrupts [feature-copy, needs-confirmed]"
[#327]: https://github.com/jprichardson/node-fs-extra/pull/327 "Use writeStream 'finish' event instead of 'close' [bug, feature-copy]"
[#326]: https://github.com/jprichardson/node-fs-extra/issues/326 "fs.copy fails with chmod error when disk under heavy use [bug, feature-copy]"
[#325]: https://github.com/jprichardson/node-fs-extra/issues/325 "ensureDir is difficult to promisify [enhancement]"
[#324]: https://github.com/jprichardson/node-fs-extra/pull/324 "copySync() should apply filter to directories like copy() [bug, feature-copy]"
[#323]: https://github.com/jprichardson/node-fs-extra/issues/323 "Support for `dest` being a directory when using `copy*()`?"
[#322]: https://github.com/jprichardson/node-fs-extra/pull/322 "Add fs-promise as fs-extra-promise alternative"
[#321]: https://github.com/jprichardson/node-fs-extra/issues/321 "fs.copy() with clobber set to false return EEXIST error [feature-copy]"
[#320]: https://github.com/jprichardson/node-fs-extra/issues/320 "fs.copySync: Error: EPERM: operation not permitted, unlink "
[#319]: https://github.com/jprichardson/node-fs-extra/issues/319 "Create directory if not exists"
[#318]: https://github.com/jprichardson/node-fs-extra/issues/318 "Support glob patterns [enhancement, future]"
[#317]: https://github.com/jprichardson/node-fs-extra/pull/317 "Adding copy sync test for src file without write perms"
[#316]: https://github.com/jprichardson/node-fs-extra/pull/316 "Remove move()'s broken limit option [feature-move]"
[#315]: https://github.com/jprichardson/node-fs-extra/pull/315 "Fix move clobber tests to work around graceful-fs bug."
[#314]: https://github.com/jprichardson/node-fs-extra/issues/314 "move() limit option [documentation, enhancement, feature-move]"
[#313]: https://github.com/jprichardson/node-fs-extra/pull/313 "Test that remove() ignores glob characters."
[#312]: https://github.com/jprichardson/node-fs-extra/pull/312 "Enhance walkSync() to return items with path and stats [feature-walk]"
[#311]: https://github.com/jprichardson/node-fs-extra/issues/311 "move() not work when dest name not provided [feature-move]"
[#310]: https://github.com/jprichardson/node-fs-extra/issues/310 "Edit walkSync to return items like what walk emits [documentation, enhancement, feature-walk]"
[#309]: https://github.com/jprichardson/node-fs-extra/issues/309 "moveSync support [enhancement, feature-move]"
[#308]: https://github.com/jprichardson/node-fs-extra/pull/308 "Fix incorrect anchor link"
[#307]: https://github.com/jprichardson/node-fs-extra/pull/307 "Fix coverage"
[#306]: https://github.com/jprichardson/node-fs-extra/pull/306 "Update devDeps, fix lint error"
[#305]: https://github.com/jprichardson/node-fs-extra/pull/305 "Re-add Coveralls"
[#304]: https://github.com/jprichardson/node-fs-extra/pull/304 "Remove path-is-absolute [enhancement]"
[#303]: https://github.com/jprichardson/node-fs-extra/pull/303 "Document copySync filter inconsistency [documentation, feature-copy]"
[#302]: https://github.com/jprichardson/node-fs-extra/pull/302 "fix(console): depreciated -> deprecated"
[#301]: https://github.com/jprichardson/node-fs-extra/pull/301 "Remove chmod call from copySync [feature-copy]"
[#300]: https://github.com/jprichardson/node-fs-extra/pull/300 "Inline Rimraf [enhancement, feature-move, feature-remove]"
[#299]: https://github.com/jprichardson/node-fs-extra/pull/299 "Warn when filter is a RegExp [feature-copy]"
[#298]: https://github.com/jprichardson/node-fs-extra/issues/298 "API Docs [documentation]"
[#297]: https://github.com/jprichardson/node-fs-extra/pull/297 "Warn about using preserveTimestamps on 32-bit node"
[#296]: https://github.com/jprichardson/node-fs-extra/pull/296 "Improve EEXIST error message for copySync [enhancement]"
[#295]: https://github.com/jprichardson/node-fs-extra/pull/295 "Depreciate using regular expressions for copy's filter option [documentation]"
[#294]: https://github.com/jprichardson/node-fs-extra/pull/294 "BREAKING: Refactor lib/copy/ncp.js [feature-copy]"
[#293]: https://github.com/jprichardson/node-fs-extra/pull/293 "Update CI configs"
[#292]: https://github.com/jprichardson/node-fs-extra/issues/292 "Rewrite lib/copy/ncp.js [enhancement, feature-copy]"
[#291]: https://github.com/jprichardson/node-fs-extra/pull/291 "Escape '$' in replacement string for async file copying"
[#290]: https://github.com/jprichardson/node-fs-extra/issues/290 "Exclude files pattern while copying using copy.config.js [question]"
[#289]: https://github.com/jprichardson/node-fs-extra/pull/289 "(Closes #271) lib/util/utimes: properly close file descriptors in the event of an error"
[#288]: https://github.com/jprichardson/node-fs-extra/pull/288 "(Closes #271) lib/util/utimes: properly close file descriptors in the event of an error"
[#287]: https://github.com/jprichardson/node-fs-extra/issues/287 "emptyDir() callback arguments are inconsistent [enhancement, feature-remove]"
[#286]: https://github.com/jprichardson/node-fs-extra/pull/286 "Added walkSync function"
[#285]: https://github.com/jprichardson/node-fs-extra/issues/285 "CITGM test failing on s390"
[#284]: https://github.com/jprichardson/node-fs-extra/issues/284 "outputFile method is missing a check to determine if existing item is a folder or not"
[#283]: https://github.com/jprichardson/node-fs-extra/pull/283 "Apply filter also on directories and symlinks for copySync()"
[#282]: https://github.com/jprichardson/node-fs-extra/pull/282 "Apply filter also on directories and symlinks for copySync()"
[#281]: https://github.com/jprichardson/node-fs-extra/issues/281 "remove function executes 'successfully' but doesn't do anything?"
[#280]: https://github.com/jprichardson/node-fs-extra/pull/280 "Disable rimraf globbing"
[#279]: https://github.com/jprichardson/node-fs-extra/issues/279 "Some code is vendored instead of included [awaiting-reply]"
[#278]: https://github.com/jprichardson/node-fs-extra/issues/278 "copy() does not preserve file/directory ownership"
[#277]: https://github.com/jprichardson/node-fs-extra/pull/277 "Mention defaults for clobber and dereference options"
[#276]: https://github.com/jprichardson/node-fs-extra/issues/276 "Cannot connect to Shared Folder [awaiting-reply]"
[#275]: https://github.com/jprichardson/node-fs-extra/issues/275 "EMFILE, too many open files on Mac OS with JSON API"
[#274]: https://github.com/jprichardson/node-fs-extra/issues/274 "Use with memory-fs? [enhancement, future]"
[#273]: https://github.com/jprichardson/node-fs-extra/pull/273 "tests: rename `remote.test.js` to `remove.test.js`"
[#272]: https://github.com/jprichardson/node-fs-extra/issues/272 "Copy clobber flag never err even when true [bug, feature-copy]"
[#271]: https://github.com/jprichardson/node-fs-extra/issues/271 "Unclosed file handle on futimes error"
[#270]: https://github.com/jprichardson/node-fs-extra/issues/270 "copy not working as desired on Windows [feature-copy, platform-windows]"
[#269]: https://github.com/jprichardson/node-fs-extra/issues/269 "Copying with preserveTimeStamps: true is inaccurate using 32bit node [feature-copy]"
[#268]: https://github.com/jprichardson/node-fs-extra/pull/268 "port fix for mkdirp issue #111"
[#267]: https://github.com/jprichardson/node-fs-extra/issues/267 "WARN deprecated wrench@1.5.9: wrench.js is deprecated!"
[#266]: https://github.com/jprichardson/node-fs-extra/issues/266 "fs-extra"
[#265]: https://github.com/jprichardson/node-fs-extra/issues/265 "Link the `fs.stat fs.exists` etc. methods for replace the `fs` module forever?"
[#264]: https://github.com/jprichardson/node-fs-extra/issues/264 "Renaming a file using move fails when a file inside is open (at least on windows) [wont-fix]"
[#263]: https://github.com/jprichardson/node-fs-extra/issues/263 "ENOSYS: function not implemented, link [needs-confirmed]"
[#262]: https://github.com/jprichardson/node-fs-extra/issues/262 "Add .exists() and .existsSync()"
[#261]: https://github.com/jprichardson/node-fs-extra/issues/261 "Cannot read property 'prototype' of undefined"
[#260]: https://github.com/jprichardson/node-fs-extra/pull/260 "use more specific path for method require"
[#259]: https://github.com/jprichardson/node-fs-extra/issues/259 "Feature Request: isEmpty"
[#258]: https://github.com/jprichardson/node-fs-extra/issues/258 "copy files does not preserve file timestamp"
[#257]: https://github.com/jprichardson/node-fs-extra/issues/257 "Copying a file on windows fails"
[#256]: https://github.com/jprichardson/node-fs-extra/pull/256 "Updated Readme "
[#255]: https://github.com/jprichardson/node-fs-extra/issues/255 "Update rimraf required version"
[#254]: https://github.com/jprichardson/node-fs-extra/issues/254 "request for readTree, readTreeSync, walkSync method"
[#253]: https://github.com/jprichardson/node-fs-extra/issues/253 "outputFile does not touch mtime when file exists"
[#252]: https://github.com/jprichardson/node-fs-extra/pull/252 "Fixing problem when copying file with no write permission"
[#251]: https://github.com/jprichardson/node-fs-extra/issues/251 "Just wanted to say thank you"
[#250]: https://github.com/jprichardson/node-fs-extra/issues/250 "`fs.remove()` not removing files (works with `rm -rf`)"
[#249]: https://github.com/jprichardson/node-fs-extra/issues/249 "Just a Question ... Remove Servers"
[#248]: https://github.com/jprichardson/node-fs-extra/issues/248 "Allow option to not preserve permissions for copy"
[#247]: https://github.com/jprichardson/node-fs-extra/issues/247 "Add TypeScript typing directly in the fs-extra package"
[#246]: https://github.com/jprichardson/node-fs-extra/issues/246 "fse.remove() && fse.removeSync() don't throw error on ENOENT file"
[#245]: https://github.com/jprichardson/node-fs-extra/issues/245 "filter for empty dir [enhancement]"
[#244]: https://github.com/jprichardson/node-fs-extra/issues/244 "copySync doesn't apply the filter to directories"
[#243]: https://github.com/jprichardson/node-fs-extra/issues/243 "Can I request fs.walk() to be synchronous?"
[#242]: https://github.com/jprichardson/node-fs-extra/issues/242 "Accidentally truncates file names ending with $$ [bug, feature-copy]"
[#241]: https://github.com/jprichardson/node-fs-extra/pull/241 "Remove link to createOutputStream"
[#240]: https://github.com/jprichardson/node-fs-extra/issues/240 "walkSync request"
[#239]: https://github.com/jprichardson/node-fs-extra/issues/239 "Depreciate regular expressions for copy's filter [documentation, feature-copy]"
[#238]: https://github.com/jprichardson/node-fs-extra/issues/238 "Can't write to files while in a worker thread."
[#237]: https://github.com/jprichardson/node-fs-extra/issues/237 ".ensureDir(..) fails silently when passed an invalid path..."
[#236]: https://github.com/jprichardson/node-fs-extra/issues/236 "[Removed] Filed under wrong repo"
[#235]: https://github.com/jprichardson/node-fs-extra/pull/235 "Adds symlink dereference option to `fse.copySync` (#191)"
[#234]: https://github.com/jprichardson/node-fs-extra/issues/234 "ensureDirSync fails silent when EACCES: permission denied on travis-ci"
[#233]: https://github.com/jprichardson/node-fs-extra/issues/233 "please make sure the first argument in callback is error object [feature-copy]"
[#232]: https://github.com/jprichardson/node-fs-extra/issues/232 "Copy a folder content to its child folder. "
[#231]: https://github.com/jprichardson/node-fs-extra/issues/231 "Adding read/write/output functions for YAML"
[#230]: https://github.com/jprichardson/node-fs-extra/pull/230 "throw error if src and dest are the same to avoid zeroing out + test"
[#229]: https://github.com/jprichardson/node-fs-extra/pull/229 "fix 'TypeError: callback is not a function' in emptyDir"
[#228]: https://github.com/jprichardson/node-fs-extra/pull/228 "Throw error when target is empty so file is not accidentally zeroed out"
[#227]: https://github.com/jprichardson/node-fs-extra/issues/227 "Uncatchable errors when there are invalid arguments [feature-move]"
[#226]: https://github.com/jprichardson/node-fs-extra/issues/226 "Moving to the current directory"
[#225]: https://github.com/jprichardson/node-fs-extra/issues/225 "EBUSY: resource busy or locked, unlink"
[#224]: https://github.com/jprichardson/node-fs-extra/issues/224 "fse.copy ENOENT error"
[#223]: https://github.com/jprichardson/node-fs-extra/issues/223 "Suspicious behavior of fs.existsSync"
[#222]: https://github.com/jprichardson/node-fs-extra/pull/222 "A clearer description of emtpyDir function"
[#221]: https://github.com/jprichardson/node-fs-extra/pull/221 "Update README.md"
[#220]: https://github.com/jprichardson/node-fs-extra/pull/220 "Non-breaking feature: add option 'passStats' to copy methods."
[#219]: https://github.com/jprichardson/node-fs-extra/pull/219 "Add closing parenthesis in copySync example"
[#218]: https://github.com/jprichardson/node-fs-extra/pull/218 "fix #187 #70 options.filter bug"
[#217]: https://github.com/jprichardson/node-fs-extra/pull/217 "fix #187 #70 options.filter bug"
[#216]: https://github.com/jprichardson/node-fs-extra/pull/216 "fix #187 #70 options.filter bug"
[#215]: https://github.com/jprichardson/node-fs-extra/pull/215 "fse.copy throws error when only src and dest provided [bug, documentation, feature-copy]"
[#214]: https://github.com/jprichardson/node-fs-extra/pull/214 "Fixing copySync anchor tag"
[#213]: https://github.com/jprichardson/node-fs-extra/issues/213 "Merge extfs with this repo"
[#212]: https://github.com/jprichardson/node-fs-extra/pull/212 "Update year to 2016 in README.md and LICENSE"
[#211]: https://github.com/jprichardson/node-fs-extra/issues/211 "Not copying all files"
[#210]: https://github.com/jprichardson/node-fs-extra/issues/210 "copy/copySync behave differently when copying a symbolic file [bug, documentation, feature-copy]"
[#209]: https://github.com/jprichardson/node-fs-extra/issues/209 "In Windows invalid directory name causes infinite loop in ensureDir(). [bug]"
[#208]: https://github.com/jprichardson/node-fs-extra/pull/208 "fix options.preserveTimestamps to false in copy-sync by default [feature-copy]"
[#207]: https://github.com/jprichardson/node-fs-extra/issues/207 "Add `compare` suite of functions"
[#206]: https://github.com/jprichardson/node-fs-extra/issues/206 "outputFileSync"
[#205]: https://github.com/jprichardson/node-fs-extra/issues/205 "fix documents about copy/copySync [documentation, feature-copy]"
[#204]: https://github.com/jprichardson/node-fs-extra/pull/204 "allow copy of block and character device files"
[#203]: https://github.com/jprichardson/node-fs-extra/issues/203 "copy method's argument options couldn't be undefined [bug, feature-copy]"
[#202]: https://github.com/jprichardson/node-fs-extra/issues/202 "why there is not a walkSync method?"
[#201]: https://github.com/jprichardson/node-fs-extra/issues/201 "clobber for directories [feature-copy, future]"
[#200]: https://github.com/jprichardson/node-fs-extra/issues/200 "'copySync' doesn't work in sync"
[#199]: https://github.com/jprichardson/node-fs-extra/issues/199 "fs.copySync fails if user does not own file [bug, feature-copy]"
[#198]: https://github.com/jprichardson/node-fs-extra/issues/198 "handle copying between identical files [feature-copy]"
[#197]: https://github.com/jprichardson/node-fs-extra/issues/197 "Missing documentation for `outputFile` `options` 3rd parameter [documentation]"
[#196]: https://github.com/jprichardson/node-fs-extra/issues/196 "copy filter: async function and/or function called with `fs.stat` result [future]"
[#195]: https://github.com/jprichardson/node-fs-extra/issues/195 "How to override with outputFile?"
[#194]: https://github.com/jprichardson/node-fs-extra/pull/194 "allow ensureFile(Sync) to provide data to be written to created file"
[#193]: https://github.com/jprichardson/node-fs-extra/issues/193 "`fs.copy` fails silently if source file is /dev/null [bug, feature-copy]"
[#192]: https://github.com/jprichardson/node-fs-extra/issues/192 "Remove fs.createOutputStream()"
[#191]: https://github.com/jprichardson/node-fs-extra/issues/191 "How to copy symlinks to target as normal folders [feature-copy]"
[#190]: https://github.com/jprichardson/node-fs-extra/pull/190 "copySync to overwrite destination file if readonly and clobber true"
[#189]: https://github.com/jprichardson/node-fs-extra/pull/189 "move.test fix to support CRLF on Windows"
[#188]: https://github.com/jprichardson/node-fs-extra/issues/188 "move.test failing on windows platform"
[#187]: https://github.com/jprichardson/node-fs-extra/issues/187 "Not filter each file, stops on first false [feature-copy]"
[#186]: https://github.com/jprichardson/node-fs-extra/issues/186 "Do you need a .size() function in this module? [future]"
[#185]: https://github.com/jprichardson/node-fs-extra/issues/185 "Doesn't work on NodeJS v4.x"
[#184]: https://github.com/jprichardson/node-fs-extra/issues/184 "CLI equivalent for fs-extra"
[#183]: https://github.com/jprichardson/node-fs-extra/issues/183 "with clobber true, copy and copySync behave differently if destination file is read only [bug, feature-copy]"
[#182]: https://github.com/jprichardson/node-fs-extra/issues/182 "ensureDir(dir, callback) second callback parameter not specified"
[#181]: https://github.com/jprichardson/node-fs-extra/issues/181 "Add ability to remove file securely [enhancement, wont-fix]"
[#180]: https://github.com/jprichardson/node-fs-extra/issues/180 "Filter option doesn't work the same way in copy and copySync [bug, feature-copy]"
[#179]: https://github.com/jprichardson/node-fs-extra/issues/179 "Include opendir"
[#178]: https://github.com/jprichardson/node-fs-extra/issues/178 "ENOTEMPTY is thrown on removeSync "
[#177]: https://github.com/jprichardson/node-fs-extra/issues/177 "fix `remove()` wildcards (introduced by rimraf) [feature-remove]"
[#176]: https://github.com/jprichardson/node-fs-extra/issues/176 "createOutputStream doesn't emit 'end' event"
[#175]: https://github.com/jprichardson/node-fs-extra/issues/175 "[Feature Request].moveSync support [feature-move, future]"
[#174]: https://github.com/jprichardson/node-fs-extra/pull/174 "Fix copy formatting and document options.filter"
[#173]: https://github.com/jprichardson/node-fs-extra/issues/173 "Feature Request: writeJson should mkdirs"
[#172]: https://github.com/jprichardson/node-fs-extra/issues/172 "rename `clobber` flags to `overwrite`"
[#171]: https://github.com/jprichardson/node-fs-extra/issues/171 "remove unnecessary aliases"
[#170]: https://github.com/jprichardson/node-fs-extra/pull/170 "More robust handling of errors moving across virtual drives"
[#169]: https://github.com/jprichardson/node-fs-extra/pull/169 "suppress ensureLink & ensureSymlink dest exists error"
[#168]: https://github.com/jprichardson/node-fs-extra/pull/168 "suppress ensurelink dest exists error"
[#167]: https://github.com/jprichardson/node-fs-extra/pull/167 "Adds basic (string, buffer) support for ensureFile content [future]"
[#166]: https://github.com/jprichardson/node-fs-extra/pull/166 "Adds basic (string, buffer) support for ensureFile content"
[#165]: https://github.com/jprichardson/node-fs-extra/pull/165 "ensure for link & symlink"
[#164]: https://github.com/jprichardson/node-fs-extra/issues/164 "Feature Request: ensureFile to take optional argument for file content"
[#163]: https://github.com/jprichardson/node-fs-extra/issues/163 "ouputJson not formatted out of the box [bug]"
[#162]: https://github.com/jprichardson/node-fs-extra/pull/162 "ensure symlink & link"
[#161]: https://github.com/jprichardson/node-fs-extra/pull/161 "ensure symlink & link"
[#160]: https://github.com/jprichardson/node-fs-extra/pull/160 "ensure symlink & link"
[#159]: https://github.com/jprichardson/node-fs-extra/pull/159 "ensure symlink & link"
[#158]: https://github.com/jprichardson/node-fs-extra/issues/158 "Feature Request: ensureLink and ensureSymlink methods"
[#157]: https://github.com/jprichardson/node-fs-extra/issues/157 "writeJson isn't formatted"
[#156]: https://github.com/jprichardson/node-fs-extra/issues/156 "Promise.promisifyAll doesn't work for some methods"
[#155]: https://github.com/jprichardson/node-fs-extra/issues/155 "Readme"
[#154]: https://github.com/jprichardson/node-fs-extra/issues/154 "/tmp/millis-test-sync"
[#153]: https://github.com/jprichardson/node-fs-extra/pull/153 "Make preserveTimes also work on read-only files. Closes #152"
[#152]: https://github.com/jprichardson/node-fs-extra/issues/152 "fs.copy fails for read-only files with preserveTimestamp=true [feature-copy]"
[#151]: https://github.com/jprichardson/node-fs-extra/issues/151 "TOC does not work correctly on npm [documentation]"
[#150]: https://github.com/jprichardson/node-fs-extra/issues/150 "Remove test file fixtures, create with code."
[#149]: https://github.com/jprichardson/node-fs-extra/issues/149 "/tmp/millis-test-sync"
[#148]: https://github.com/jprichardson/node-fs-extra/issues/148 "split out `Sync` methods in documentation"
[#147]: https://github.com/jprichardson/node-fs-extra/issues/147 "Adding rmdirIfEmpty"
[#146]: https://github.com/jprichardson/node-fs-extra/pull/146 "ensure test.js works"
[#145]: https://github.com/jprichardson/node-fs-extra/issues/145 "Add `fs.exists` and `fs.existsSync` if it doesn't exist."
[#144]: https://github.com/jprichardson/node-fs-extra/issues/144 "tests failing"
[#143]: https://github.com/jprichardson/node-fs-extra/issues/143 "update graceful-fs"
[#142]: https://github.com/jprichardson/node-fs-extra/issues/142 "PrependFile Feature"
[#141]: https://github.com/jprichardson/node-fs-extra/pull/141 "Add option to preserve timestamps"
[#140]: https://github.com/jprichardson/node-fs-extra/issues/140 "Json file reading fails with 'utf8'"
[#139]: https://github.com/jprichardson/node-fs-extra/pull/139 "Preserve file timestamp on copy. Closes #138"
[#138]: https://github.com/jprichardson/node-fs-extra/issues/138 "Preserve timestamps on copying files"
[#137]: https://github.com/jprichardson/node-fs-extra/issues/137 "outputFile/outputJson: Unexpected end of input"
[#136]: https://github.com/jprichardson/node-fs-extra/pull/136 "Update license attribute"
[#135]: https://github.com/jprichardson/node-fs-extra/issues/135 "emptyDir throws Error if no callback is provided"
[#134]: https://github.com/jprichardson/node-fs-extra/pull/134 "Handle EEXIST error when clobbering dir"
[#133]: https://github.com/jprichardson/node-fs-extra/pull/133 "Travis runs with `sudo: false`"
[#132]: https://github.com/jprichardson/node-fs-extra/pull/132 "isDirectory method"
[#131]: https://github.com/jprichardson/node-fs-extra/issues/131 "copySync is not working iojs 1.8.4 on linux [feature-copy]"
[#130]: https://github.com/jprichardson/node-fs-extra/pull/130 "Please review additional features."
[#129]: https://github.com/jprichardson/node-fs-extra/pull/129 "can you review this feature?"
[#128]: https://github.com/jprichardson/node-fs-extra/issues/128 "fsExtra.move(filepath, newPath) broken;"
[#127]: https://github.com/jprichardson/node-fs-extra/issues/127 "consider using fs.access to remove deprecated warnings for fs.exists"
[#126]: https://github.com/jprichardson/node-fs-extra/issues/126 " TypeError: Object #<Object> has no method 'access'"
[#125]: https://github.com/jprichardson/node-fs-extra/issues/125 "Question: What do the *Sync function do different from non-sync"
[#124]: https://github.com/jprichardson/node-fs-extra/issues/124 "move with clobber option 'ENOTEMPTY'"
[#123]: https://github.com/jprichardson/node-fs-extra/issues/123 "Only copy the content of a directory"
[#122]: https://github.com/jprichardson/node-fs-extra/pull/122 "Update section links in README to match current section ids."
[#121]: https://github.com/jprichardson/node-fs-extra/issues/121 "emptyDir is undefined"
[#120]: https://github.com/jprichardson/node-fs-extra/issues/120 "usage bug caused by shallow cloning methods of 'graceful-fs'"
[#119]: https://github.com/jprichardson/node-fs-extra/issues/119 "mkdirs and ensureDir never invoke callback and consume CPU indefinitely if provided a path with invalid characters on Windows"
[#118]: https://github.com/jprichardson/node-fs-extra/pull/118 "createOutputStream"
[#117]: https://github.com/jprichardson/node-fs-extra/pull/117 "Fixed issue with slash separated paths on windows"
[#116]: https://github.com/jprichardson/node-fs-extra/issues/116 "copySync can only copy directories not files [documentation, feature-copy]"
[#115]: https://github.com/jprichardson/node-fs-extra/issues/115 ".Copy & .CopySync [feature-copy]"
[#114]: https://github.com/jprichardson/node-fs-extra/issues/114 "Fails to move (rename) directory to non-empty directory even with clobber: true"
[#113]: https://github.com/jprichardson/node-fs-extra/issues/113 "fs.copy seems to callback early if the destination file already exists"
[#112]: https://github.com/jprichardson/node-fs-extra/pull/112 "Copying a file into an existing directory"
[#111]: https://github.com/jprichardson/node-fs-extra/pull/111 "Moving a file into an existing directory "
[#110]: https://github.com/jprichardson/node-fs-extra/pull/110 "Moving a file into an existing directory"
[#109]: https://github.com/jprichardson/node-fs-extra/issues/109 "fs.move across windows drives fails"
[#108]: https://github.com/jprichardson/node-fs-extra/issues/108 "fse.move directories across multiple devices doesn't work"
[#107]: https://github.com/jprichardson/node-fs-extra/pull/107 "Check if dest path is an existing dir and copy or move source in it"
[#106]: https://github.com/jprichardson/node-fs-extra/issues/106 "fse.copySync crashes while copying across devices D: [feature-copy]"
[#105]: https://github.com/jprichardson/node-fs-extra/issues/105 "fs.copy hangs on iojs"
[#104]: https://github.com/jprichardson/node-fs-extra/issues/104 "fse.move deletes folders [bug]"
[#103]: https://github.com/jprichardson/node-fs-extra/issues/103 "Error: EMFILE with copy"
[#102]: https://github.com/jprichardson/node-fs-extra/issues/102 "touch / touchSync was removed ?"
[#101]: https://github.com/jprichardson/node-fs-extra/issues/101 "fs-extra promisified"
[#100]: https://github.com/jprichardson/node-fs-extra/pull/100 "copy: options object or filter to pass to ncp"
[#99]: https://github.com/jprichardson/node-fs-extra/issues/99 "ensureDir() modes [future]"
[#98]: https://github.com/jprichardson/node-fs-extra/issues/98 "fs.copy() incorrect async behavior [bug]"
[#97]: https://github.com/jprichardson/node-fs-extra/pull/97 "use path.join; fix copySync bug"
[#96]: https://github.com/jprichardson/node-fs-extra/issues/96 "destFolderExists in copySync is always undefined."
[#95]: https://github.com/jprichardson/node-fs-extra/pull/95 "Using graceful-ncp instead of ncp"
[#94]: https://github.com/jprichardson/node-fs-extra/issues/94 "Error: EEXIST, file already exists '../mkdirp/bin/cmd.js' on fs.copySync() [enhancement, feature-copy]"
[#93]: https://github.com/jprichardson/node-fs-extra/issues/93 "Confusing error if drive not mounted [enhancement]"
[#92]: https://github.com/jprichardson/node-fs-extra/issues/92 "Problems with Bluebird"
[#91]: https://github.com/jprichardson/node-fs-extra/issues/91 "fs.copySync('/test', '/haha') is different with 'cp -r /test /haha' [enhancement]"
[#90]: https://github.com/jprichardson/node-fs-extra/issues/90 "Folder creation and file copy is Happening in 64 bit machine but not in 32 bit machine"
[#89]: https://github.com/jprichardson/node-fs-extra/issues/89 "Error: EEXIST using fs-extra's fs.copy to copy a directory on Windows"
[#88]: https://github.com/jprichardson/node-fs-extra/issues/88 "Stacking those libraries"
[#87]: https://github.com/jprichardson/node-fs-extra/issues/87 "createWriteStream + outputFile = ?"
[#86]: https://github.com/jprichardson/node-fs-extra/issues/86 "no moveSync?"
[#85]: https://github.com/jprichardson/node-fs-extra/pull/85 "Copy symlinks in copySync"
[#84]: https://github.com/jprichardson/node-fs-extra/issues/84 "Push latest version to npm ?"
[#83]: https://github.com/jprichardson/node-fs-extra/issues/83 "Prevent copying a directory into itself [feature-copy]"
[#82]: https://github.com/jprichardson/node-fs-extra/pull/82 "README updates for move"
[#81]: https://github.com/jprichardson/node-fs-extra/issues/81 "fd leak after fs.move"
[#80]: https://github.com/jprichardson/node-fs-extra/pull/80 "Preserve file mode in copySync"
[#79]: https://github.com/jprichardson/node-fs-extra/issues/79 "fs.copy only .html file empty"
[#78]: https://github.com/jprichardson/node-fs-extra/pull/78 "copySync was not applying filters to directories"
[#77]: https://github.com/jprichardson/node-fs-extra/issues/77 "Create README reference to bluebird"
[#76]: https://github.com/jprichardson/node-fs-extra/issues/76 "Create README reference to typescript"
[#75]: https://github.com/jprichardson/node-fs-extra/issues/75 "add glob as a dep? [question]"
[#74]: https://github.com/jprichardson/node-fs-extra/pull/74 "including new emptydir module"
[#73]: https://github.com/jprichardson/node-fs-extra/pull/73 "add dependency status in readme"
[#72]: https://github.com/jprichardson/node-fs-extra/pull/72 "Use svg instead of png to get better image quality"
[#71]: https://github.com/jprichardson/node-fs-extra/issues/71 "fse.copy not working on Windows 7 x64 OS, but, copySync does work"
[#70]: https://github.com/jprichardson/node-fs-extra/issues/70 "Not filter each file, stops on first false [bug]"
[#69]: https://github.com/jprichardson/node-fs-extra/issues/69 "How to check if folder exist and read the folder name"
[#68]: https://github.com/jprichardson/node-fs-extra/issues/68 "consider flag to readJsonSync (throw false) [enhancement]"
[#67]: https://github.com/jprichardson/node-fs-extra/issues/67 "docs for readJson incorrectly states that is accepts options"
[#66]: https://github.com/jprichardson/node-fs-extra/issues/66 "ENAMETOOLONG"
[#65]: https://github.com/jprichardson/node-fs-extra/issues/65 "exclude filter in fs.copy"
[#64]: https://github.com/jprichardson/node-fs-extra/issues/64 "Announce: mfs - monitor your fs-extra calls"
[#63]: https://github.com/jprichardson/node-fs-extra/issues/63 "Walk"
[#62]: https://github.com/jprichardson/node-fs-extra/issues/62 "npm install fs-extra doesn't work"
[#61]: https://github.com/jprichardson/node-fs-extra/issues/61 "No longer supports node 0.8 due to use of `^` in package.json dependencies"
[#60]: https://github.com/jprichardson/node-fs-extra/issues/60 "chmod & chown for mkdirs"
[#59]: https://github.com/jprichardson/node-fs-extra/issues/59 "Consider including mkdirp and making fs-extra '--use_strict' safe [question]"
[#58]: https://github.com/jprichardson/node-fs-extra/issues/58 "Stack trace not included in fs.copy error"
[#57]: https://github.com/jprichardson/node-fs-extra/issues/57 "Possible to include wildcards in delete?"
[#56]: https://github.com/jprichardson/node-fs-extra/issues/56 "Crash when have no access to write to destination file in copy "
[#55]: https://github.com/jprichardson/node-fs-extra/issues/55 "Is it possible to have any console output similar to Grunt copy module?"
[#54]: https://github.com/jprichardson/node-fs-extra/issues/54 "`copy` does not preserve file ownership and permissons"
[#53]: https://github.com/jprichardson/node-fs-extra/issues/53 "outputFile() - ability to write data in appending mode"
[#52]: https://github.com/jprichardson/node-fs-extra/pull/52 "This fixes (what I think) is a bug in copySync"
[#51]: https://github.com/jprichardson/node-fs-extra/pull/51 "Add a Bitdeli Badge to README"
[#50]: https://github.com/jprichardson/node-fs-extra/issues/50 "Replace mechanism in createFile"
[#49]: https://github.com/jprichardson/node-fs-extra/pull/49 "update rimraf to v2.2.6"
[#48]: https://github.com/jprichardson/node-fs-extra/issues/48 "fs.copy issue [bug]"
[#47]: https://github.com/jprichardson/node-fs-extra/issues/47 "Bug in copy - callback called on readStream 'close' - Fixed in ncp 0.5.0"
[#46]: https://github.com/jprichardson/node-fs-extra/pull/46 "update copyright year"
[#45]: https://github.com/jprichardson/node-fs-extra/pull/45 "Added note about fse.outputFile() being the one that overwrites"
[#44]: https://github.com/jprichardson/node-fs-extra/pull/44 "Proposal: Stream support"
[#43]: https://github.com/jprichardson/node-fs-extra/issues/43 "Better error reporting "
[#42]: https://github.com/jprichardson/node-fs-extra/issues/42 "Performance issue?"
[#41]: https://github.com/jprichardson/node-fs-extra/pull/41 "There does seem to be a synchronous version now"
[#40]: https://github.com/jprichardson/node-fs-extra/issues/40 "fs.copy throw unexplained error ENOENT, utime "
[#39]: https://github.com/jprichardson/node-fs-extra/pull/39 "Added regression test for copy() return callback on error"
[#38]: https://github.com/jprichardson/node-fs-extra/pull/38 "Return err in copy() fstat cb, because stat could be undefined or null"
[#37]: https://github.com/jprichardson/node-fs-extra/issues/37 "Maybe include a line reader? [enhancement, question]"
[#36]: https://github.com/jprichardson/node-fs-extra/pull/36 "`filter` parameter `fs.copy` and `fs.copySync`"
[#35]: https://github.com/jprichardson/node-fs-extra/pull/35 "`filter` parameter `fs.copy` and `fs.copySync` "
[#34]: https://github.com/jprichardson/node-fs-extra/issues/34 "update docs to include options for JSON methods [enhancement]"
[#33]: https://github.com/jprichardson/node-fs-extra/pull/33 "fs_extra.copySync"
[#32]: https://github.com/jprichardson/node-fs-extra/issues/32 "update to latest jsonfile [enhancement]"
[#31]: https://github.com/jprichardson/node-fs-extra/issues/31 "Add ensure methods [enhancement]"
[#30]: https://github.com/jprichardson/node-fs-extra/issues/30 "update package.json optional dep `graceful-fs`"
[#29]: https://github.com/jprichardson/node-fs-extra/issues/29 "Copy failing if dest directory doesn't exist. Is this intended?"
[#28]: https://github.com/jprichardson/node-fs-extra/issues/28 "homepage field must be a string url. Deleted."
[#27]: https://github.com/jprichardson/node-fs-extra/issues/27 "Update Readme"
[#26]: https://github.com/jprichardson/node-fs-extra/issues/26 "Add readdir recursive method. [enhancement]"
[#25]: https://github.com/jprichardson/node-fs-extra/pull/25 "adding an `.npmignore` file"
[#24]: https://github.com/jprichardson/node-fs-extra/issues/24 "[bug] cannot run in strict mode [bug]"
[#23]: https://github.com/jprichardson/node-fs-extra/issues/23 "`writeJSON()` should create parent directories"
[#22]: https://github.com/jprichardson/node-fs-extra/pull/22 "Add a limit option to mkdirs()"
[#21]: https://github.com/jprichardson/node-fs-extra/issues/21 "touch() in 0.10.0"
[#20]: https://github.com/jprichardson/node-fs-extra/issues/20 "fs.remove yields callback before directory is really deleted"
[#19]: https://github.com/jprichardson/node-fs-extra/issues/19 "fs.copy err is empty array"
[#18]: https://github.com/jprichardson/node-fs-extra/pull/18 "Exposed copyFile Function"
[#17]: https://github.com/jprichardson/node-fs-extra/issues/17 "Use `require('graceful-fs')` if found instead of `require('fs')`"
[#16]: https://github.com/jprichardson/node-fs-extra/pull/16 "Update README.md"
[#15]: https://github.com/jprichardson/node-fs-extra/issues/15 "Implement cp -r but sync aka copySync. [enhancement]"
[#14]: https://github.com/jprichardson/node-fs-extra/issues/14 "fs.mkdirSync is broken in 0.3.1"
[#13]: https://github.com/jprichardson/node-fs-extra/issues/13 "Thoughts on including a directory tree / file watcher? [enhancement, question]"
[#12]: https://github.com/jprichardson/node-fs-extra/issues/12 "copyFile & copyFileSync are global"
[#11]: https://github.com/jprichardson/node-fs-extra/issues/11 "Thoughts on including a file walker? [enhancement, question]"
[#10]: https://github.com/jprichardson/node-fs-extra/issues/10 "move / moveFile API [enhancement]"
[#9]: https://github.com/jprichardson/node-fs-extra/issues/9 "don't import normal fs stuff into fs-extra"
[#8]: https://github.com/jprichardson/node-fs-extra/pull/8 "Update rimraf to latest version"
[#6]: https://github.com/jprichardson/node-fs-extra/issues/6 "Remove CoffeeScript development dependency"
[#5]: https://github.com/jprichardson/node-fs-extra/issues/5 "comments on naming"
[#4]: https://github.com/jprichardson/node-fs-extra/issues/4 "version bump to 0.2"
[#3]: https://github.com/jprichardson/node-fs-extra/pull/3 "Hi! I fixed some code for you!"
[#2]: https://github.com/jprichardson/node-fs-extra/issues/2 "Merge with fs.extra and mkdirp"
[#1]: https://github.com/jprichardson/node-fs-extra/issues/1 "file-extra npm !exist"

View File

@@ -0,0 +1,15 @@
(The MIT License)
Copyright (c) 2011-2017 JP Richardson
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,264 @@
Node.js: fs-extra
=================
`fs-extra` adds file system methods that aren't included in the native `fs` module and adds promise support to the `fs` methods. It also uses [`graceful-fs`](https://github.com/isaacs/node-graceful-fs) to prevent `EMFILE` errors. It should be a drop in replacement for `fs`.
[![npm Package](https://img.shields.io/npm/v/fs-extra.svg)](https://www.npmjs.org/package/fs-extra)
[![License](https://img.shields.io/npm/l/express.svg)](https://github.com/jprichardson/node-fs-extra/blob/master/LICENSE)
[![build status](https://img.shields.io/travis/jprichardson/node-fs-extra/master.svg)](http://travis-ci.org/jprichardson/node-fs-extra)
[![windows Build status](https://img.shields.io/appveyor/ci/jprichardson/node-fs-extra/master.svg?label=windows%20build)](https://ci.appveyor.com/project/jprichardson/node-fs-extra/branch/master)
[![downloads per month](http://img.shields.io/npm/dm/fs-extra.svg)](https://www.npmjs.org/package/fs-extra)
[![Coverage Status](https://img.shields.io/coveralls/github/jprichardson/node-fs-extra/master.svg)](https://coveralls.io/github/jprichardson/node-fs-extra)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
Why?
----
I got tired of including `mkdirp`, `rimraf`, and `ncp` in most of my projects.
Installation
------------
npm install fs-extra
Usage
-----
`fs-extra` is a drop in replacement for native `fs`. All methods in `fs` are attached to `fs-extra`. All `fs` methods return promises if the callback isn't passed.
You don't ever need to include the original `fs` module again:
```js
const fs = require('fs') // this is no longer necessary
```
you can now do this:
```js
const fs = require('fs-extra')
```
or if you prefer to make it clear that you're using `fs-extra` and not `fs`, you may want
to name your `fs` variable `fse` like so:
```js
const fse = require('fs-extra')
```
you can also keep both, but it's redundant:
```js
const fs = require('fs')
const fse = require('fs-extra')
```
Sync vs Async vs Async/Await
-------------
Most methods are async by default. All async methods will return a promise if the callback isn't passed.
Sync methods on the other hand will throw if an error occurs.
Also Async/Await will throw an error if one occurs.
Example:
```js
const fs = require('fs-extra')
// Async with promises:
fs.copy('/tmp/myfile', '/tmp/mynewfile')
.then(() => console.log('success!'))
.catch(err => console.error(err))
// Async with callbacks:
fs.copy('/tmp/myfile', '/tmp/mynewfile', err => {
if (err) return console.error(err)
console.log('success!')
})
// Sync:
try {
fs.copySync('/tmp/myfile', '/tmp/mynewfile')
console.log('success!')
} catch (err) {
console.error(err)
}
// Async/Await:
async function copyFiles () {
try {
await fs.copy('/tmp/myfile', '/tmp/mynewfile')
console.log('success!')
} catch (err) {
console.error(err)
}
}
copyFiles()
```
Methods
-------
### Async
- [copy](docs/copy.md)
- [emptyDir](docs/emptyDir.md)
- [ensureFile](docs/ensureFile.md)
- [ensureDir](docs/ensureDir.md)
- [ensureLink](docs/ensureLink.md)
- [ensureSymlink](docs/ensureSymlink.md)
- [mkdirp](docs/ensureDir.md)
- [mkdirs](docs/ensureDir.md)
- [move](docs/move.md)
- [outputFile](docs/outputFile.md)
- [outputJson](docs/outputJson.md)
- [pathExists](docs/pathExists.md)
- [readJson](docs/readJson.md)
- [remove](docs/remove.md)
- [writeJson](docs/writeJson.md)
### Sync
- [copySync](docs/copy-sync.md)
- [emptyDirSync](docs/emptyDir-sync.md)
- [ensureFileSync](docs/ensureFile-sync.md)
- [ensureDirSync](docs/ensureDir-sync.md)
- [ensureLinkSync](docs/ensureLink-sync.md)
- [ensureSymlinkSync](docs/ensureSymlink-sync.md)
- [mkdirpSync](docs/ensureDir-sync.md)
- [mkdirsSync](docs/ensureDir-sync.md)
- [moveSync](docs/move-sync.md)
- [outputFileSync](docs/outputFile-sync.md)
- [outputJsonSync](docs/outputJson-sync.md)
- [pathExistsSync](docs/pathExists-sync.md)
- [readJsonSync](docs/readJson-sync.md)
- [removeSync](docs/remove-sync.md)
- [writeJsonSync](docs/writeJson-sync.md)
**NOTE:** You can still use the native Node.js methods. They are promisified and copied over to `fs-extra`. See [notes on `fs.read()`, `fs.write()`, & `fs.writev()`](docs/fs-read-write-writev.md)
### What happened to `walk()` and `walkSync()`?
They were removed from `fs-extra` in v2.0.0. If you need the functionality, `walk` and `walkSync` are available as separate packages, [`klaw`](https://github.com/jprichardson/node-klaw) and [`klaw-sync`](https://github.com/manidlou/node-klaw-sync).
Third Party
-----------
### CLI
[fse-cli](https://www.npmjs.com/package/@atao60/fse-cli) allows you to run `fs-extra` from a console or from [npm](https://www.npmjs.com) scripts.
### TypeScript
If you like TypeScript, you can use `fs-extra` with it: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/fs-extra
### File / Directory Watching
If you want to watch for changes to files or directories, then you should use [chokidar](https://github.com/paulmillr/chokidar).
### Obtain Filesystem (Devices, Partitions) Information
[fs-filesystem](https://github.com/arthurintelligence/node-fs-filesystem) allows you to read the state of the filesystem of the host on which it is run. It returns information about both the devices and the partitions (volumes) of the system.
### Misc.
- [fs-extra-debug](https://github.com/jdxcode/fs-extra-debug) - Send your fs-extra calls to [debug](https://npmjs.org/package/debug).
- [mfs](https://github.com/cadorn/mfs) - Monitor your fs-extra calls.
Hacking on fs-extra
-------------------
Wanna hack on `fs-extra`? Great! Your help is needed! [fs-extra is one of the most depended upon Node.js packages](http://nodei.co/npm/fs-extra.png?downloads=true&downloadRank=true&stars=true). This project
uses [JavaScript Standard Style](https://github.com/feross/standard) - if the name or style choices bother you,
you're gonna have to get over it :) If `standard` is good enough for `npm`, it's good enough for `fs-extra`.
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
What's needed?
- First, take a look at existing issues. Those are probably going to be where the priority lies.
- More tests for edge cases. Specifically on different platforms. There can never be enough tests.
- Improve test coverage. See coveralls output for more info.
Note: If you make any big changes, **you should definitely file an issue for discussion first.**
### Running the Test Suite
fs-extra contains hundreds of tests.
- `npm run lint`: runs the linter ([standard](http://standardjs.com/))
- `npm run unit`: runs the unit tests
- `npm test`: runs both the linter and the tests
### Windows
If you run the tests on the Windows and receive a lot of symbolic link `EPERM` permission errors, it's
because on Windows you need elevated privilege to create symbolic links. You can add this to your Windows's
account by following the instructions here: http://superuser.com/questions/104845/permission-to-make-symbolic-links-in-windows-7
However, I didn't have much luck doing this.
Since I develop on Mac OS X, I use VMWare Fusion for Windows testing. I create a shared folder that I map to a drive on Windows.
I open the `Node.js command prompt` and run as `Administrator`. I then map the network drive running the following command:
net use z: "\\vmware-host\Shared Folders"
I can then navigate to my `fs-extra` directory and run the tests.
Naming
------
I put a lot of thought into the naming of these functions. Inspired by @coolaj86's request. So he deserves much of the credit for raising the issue. See discussion(s) here:
* https://github.com/jprichardson/node-fs-extra/issues/2
* https://github.com/flatiron/utile/issues/11
* https://github.com/ryanmcgrath/wrench-js/issues/29
* https://github.com/substack/node-mkdirp/issues/17
First, I believe that in as many cases as possible, the [Node.js naming schemes](http://nodejs.org/api/fs.html) should be chosen. However, there are problems with the Node.js own naming schemes.
For example, `fs.readFile()` and `fs.readdir()`: the **F** is capitalized in *File* and the **d** is not capitalized in *dir*. Perhaps a bit pedantic, but they should still be consistent. Also, Node.js has chosen a lot of POSIX naming schemes, which I believe is great. See: `fs.mkdir()`, `fs.rmdir()`, `fs.chown()`, etc.
We have a dilemma though. How do you consistently name methods that perform the following POSIX commands: `cp`, `cp -r`, `mkdir -p`, and `rm -rf`?
My perspective: when in doubt, err on the side of simplicity. A directory is just a hierarchical grouping of directories and files. Consider that for a moment. So when you want to copy it or remove it, in most cases you'll want to copy or remove all of its contents. When you want to create a directory, if the directory that it's suppose to be contained in does not exist, then in most cases you'll want to create that too.
So, if you want to remove a file or a directory regardless of whether it has contents, just call `fs.remove(path)`. If you want to copy a file or a directory whether it has contents, just call `fs.copy(source, destination)`. If you want to create a directory regardless of whether its parent directories exist, just call `fs.mkdirs(path)` or `fs.mkdirp(path)`.
Credit
------
`fs-extra` wouldn't be possible without using the modules from the following authors:
- [Isaac Shlueter](https://github.com/isaacs)
- [Charlie McConnel](https://github.com/avianflu)
- [James Halliday](https://github.com/substack)
- [Andrew Kelley](https://github.com/andrewrk)
License
-------
Licensed under MIT
Copyright (c) 2011-2017 [JP Richardson](https://github.com/jprichardson)
[1]: http://nodejs.org/docs/latest/api/fs.html
[jsonfile]: https://github.com/jprichardson/node-jsonfile

View File

@@ -0,0 +1,166 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const mkdirsSync = require('../mkdirs').mkdirsSync
const utimesMillisSync = require('../util/utimes').utimesMillisSync
const stat = require('../util/stat')
function copySync (src, dest, opts) {
if (typeof opts === 'function') {
opts = { filter: opts }
}
opts = opts || {}
opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber
// Warn about using preserveTimestamps on 32-bit node
if (opts.preserveTimestamps && process.arch === 'ia32') {
console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
see https://github.com/jprichardson/node-fs-extra/issues/269`)
}
const { srcStat, destStat } = stat.checkPathsSync(src, dest, 'copy')
stat.checkParentPathsSync(src, srcStat, dest, 'copy')
return handleFilterAndCopy(destStat, src, dest, opts)
}
function handleFilterAndCopy (destStat, src, dest, opts) {
if (opts.filter && !opts.filter(src, dest)) return
const destParent = path.dirname(dest)
if (!fs.existsSync(destParent)) mkdirsSync(destParent)
return startCopy(destStat, src, dest, opts)
}
function startCopy (destStat, src, dest, opts) {
if (opts.filter && !opts.filter(src, dest)) return
return getStats(destStat, src, dest, opts)
}
function getStats (destStat, src, dest, opts) {
const statSync = opts.dereference ? fs.statSync : fs.lstatSync
const srcStat = statSync(src)
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts)
}
function onFile (srcStat, destStat, src, dest, opts) {
if (!destStat) return copyFile(srcStat, src, dest, opts)
return mayCopyFile(srcStat, src, dest, opts)
}
function mayCopyFile (srcStat, src, dest, opts) {
if (opts.overwrite) {
fs.unlinkSync(dest)
return copyFile(srcStat, src, dest, opts)
} else if (opts.errorOnExist) {
throw new Error(`'${dest}' already exists`)
}
}
function copyFile (srcStat, src, dest, opts) {
fs.copyFileSync(src, dest)
if (opts.preserveTimestamps) handleTimestamps(srcStat.mode, src, dest)
return setDestMode(dest, srcStat.mode)
}
function handleTimestamps (srcMode, src, dest) {
// Make sure the file is writable before setting the timestamp
// otherwise open fails with EPERM when invoked with 'r+'
// (through utimes call)
if (fileIsNotWritable(srcMode)) makeFileWritable(dest, srcMode)
return setDestTimestamps(src, dest)
}
function fileIsNotWritable (srcMode) {
return (srcMode & 0o200) === 0
}
function makeFileWritable (dest, srcMode) {
return setDestMode(dest, srcMode | 0o200)
}
function setDestMode (dest, srcMode) {
return fs.chmodSync(dest, srcMode)
}
function setDestTimestamps (src, dest) {
// The initial srcStat.atime cannot be trusted
// because it is modified by the read(2) system call
// (See https://nodejs.org/api/fs.html#fs_stat_time_values)
const updatedSrcStat = fs.statSync(src)
return utimesMillisSync(dest, updatedSrcStat.atime, updatedSrcStat.mtime)
}
function onDir (srcStat, destStat, src, dest, opts) {
if (!destStat) return mkDirAndCopy(srcStat.mode, src, dest, opts)
if (destStat && !destStat.isDirectory()) {
throw new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`)
}
return copyDir(src, dest, opts)
}
function mkDirAndCopy (srcMode, src, dest, opts) {
fs.mkdirSync(dest)
copyDir(src, dest, opts)
return setDestMode(dest, srcMode)
}
function copyDir (src, dest, opts) {
fs.readdirSync(src).forEach(item => copyDirItem(item, src, dest, opts))
}
function copyDirItem (item, src, dest, opts) {
const srcItem = path.join(src, item)
const destItem = path.join(dest, item)
const { destStat } = stat.checkPathsSync(srcItem, destItem, 'copy')
return startCopy(destStat, srcItem, destItem, opts)
}
function onLink (destStat, src, dest, opts) {
let resolvedSrc = fs.readlinkSync(src)
if (opts.dereference) {
resolvedSrc = path.resolve(process.cwd(), resolvedSrc)
}
if (!destStat) {
return fs.symlinkSync(resolvedSrc, dest)
} else {
let resolvedDest
try {
resolvedDest = fs.readlinkSync(dest)
} catch (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlinkSync(resolvedSrc, dest)
throw err
}
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) {
throw new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`)
}
// prevent copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (fs.statSync(dest).isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) {
throw new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`)
}
return copyLink(resolvedSrc, dest)
}
}
function copyLink (resolvedSrc, dest) {
fs.unlinkSync(dest)
return fs.symlinkSync(resolvedSrc, dest)
}
module.exports = copySync

View File

@@ -0,0 +1,5 @@
'use strict'
module.exports = {
copySync: require('./copy-sync')
}

View File

@@ -0,0 +1,232 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const mkdirs = require('../mkdirs').mkdirs
const pathExists = require('../path-exists').pathExists
const utimesMillis = require('../util/utimes').utimesMillis
const stat = require('../util/stat')
function copy (src, dest, opts, cb) {
if (typeof opts === 'function' && !cb) {
cb = opts
opts = {}
} else if (typeof opts === 'function') {
opts = { filter: opts }
}
cb = cb || function () {}
opts = opts || {}
opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber
// Warn about using preserveTimestamps on 32-bit node
if (opts.preserveTimestamps && process.arch === 'ia32') {
console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
see https://github.com/jprichardson/node-fs-extra/issues/269`)
}
stat.checkPaths(src, dest, 'copy', (err, stats) => {
if (err) return cb(err)
const { srcStat, destStat } = stats
stat.checkParentPaths(src, srcStat, dest, 'copy', err => {
if (err) return cb(err)
if (opts.filter) return handleFilter(checkParentDir, destStat, src, dest, opts, cb)
return checkParentDir(destStat, src, dest, opts, cb)
})
})
}
function checkParentDir (destStat, src, dest, opts, cb) {
const destParent = path.dirname(dest)
pathExists(destParent, (err, dirExists) => {
if (err) return cb(err)
if (dirExists) return startCopy(destStat, src, dest, opts, cb)
mkdirs(destParent, err => {
if (err) return cb(err)
return startCopy(destStat, src, dest, opts, cb)
})
})
}
function handleFilter (onInclude, destStat, src, dest, opts, cb) {
Promise.resolve(opts.filter(src, dest)).then(include => {
if (include) return onInclude(destStat, src, dest, opts, cb)
return cb()
}, error => cb(error))
}
function startCopy (destStat, src, dest, opts, cb) {
if (opts.filter) return handleFilter(getStats, destStat, src, dest, opts, cb)
return getStats(destStat, src, dest, opts, cb)
}
function getStats (destStat, src, dest, opts, cb) {
const stat = opts.dereference ? fs.stat : fs.lstat
stat(src, (err, srcStat) => {
if (err) return cb(err)
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts, cb)
})
}
function onFile (srcStat, destStat, src, dest, opts, cb) {
if (!destStat) return copyFile(srcStat, src, dest, opts, cb)
return mayCopyFile(srcStat, src, dest, opts, cb)
}
function mayCopyFile (srcStat, src, dest, opts, cb) {
if (opts.overwrite) {
fs.unlink(dest, err => {
if (err) return cb(err)
return copyFile(srcStat, src, dest, opts, cb)
})
} else if (opts.errorOnExist) {
return cb(new Error(`'${dest}' already exists`))
} else return cb()
}
function copyFile (srcStat, src, dest, opts, cb) {
fs.copyFile(src, dest, err => {
if (err) return cb(err)
if (opts.preserveTimestamps) return handleTimestampsAndMode(srcStat.mode, src, dest, cb)
return setDestMode(dest, srcStat.mode, cb)
})
}
function handleTimestampsAndMode (srcMode, src, dest, cb) {
// Make sure the file is writable before setting the timestamp
// otherwise open fails with EPERM when invoked with 'r+'
// (through utimes call)
if (fileIsNotWritable(srcMode)) {
return makeFileWritable(dest, srcMode, err => {
if (err) return cb(err)
return setDestTimestampsAndMode(srcMode, src, dest, cb)
})
}
return setDestTimestampsAndMode(srcMode, src, dest, cb)
}
function fileIsNotWritable (srcMode) {
return (srcMode & 0o200) === 0
}
function makeFileWritable (dest, srcMode, cb) {
return setDestMode(dest, srcMode | 0o200, cb)
}
function setDestTimestampsAndMode (srcMode, src, dest, cb) {
setDestTimestamps(src, dest, err => {
if (err) return cb(err)
return setDestMode(dest, srcMode, cb)
})
}
function setDestMode (dest, srcMode, cb) {
return fs.chmod(dest, srcMode, cb)
}
function setDestTimestamps (src, dest, cb) {
// The initial srcStat.atime cannot be trusted
// because it is modified by the read(2) system call
// (See https://nodejs.org/api/fs.html#fs_stat_time_values)
fs.stat(src, (err, updatedSrcStat) => {
if (err) return cb(err)
return utimesMillis(dest, updatedSrcStat.atime, updatedSrcStat.mtime, cb)
})
}
function onDir (srcStat, destStat, src, dest, opts, cb) {
if (!destStat) return mkDirAndCopy(srcStat.mode, src, dest, opts, cb)
if (destStat && !destStat.isDirectory()) {
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`))
}
return copyDir(src, dest, opts, cb)
}
function mkDirAndCopy (srcMode, src, dest, opts, cb) {
fs.mkdir(dest, err => {
if (err) return cb(err)
copyDir(src, dest, opts, err => {
if (err) return cb(err)
return setDestMode(dest, srcMode, cb)
})
})
}
function copyDir (src, dest, opts, cb) {
fs.readdir(src, (err, items) => {
if (err) return cb(err)
return copyDirItems(items, src, dest, opts, cb)
})
}
function copyDirItems (items, src, dest, opts, cb) {
const item = items.pop()
if (!item) return cb()
return copyDirItem(items, item, src, dest, opts, cb)
}
function copyDirItem (items, item, src, dest, opts, cb) {
const srcItem = path.join(src, item)
const destItem = path.join(dest, item)
stat.checkPaths(srcItem, destItem, 'copy', (err, stats) => {
if (err) return cb(err)
const { destStat } = stats
startCopy(destStat, srcItem, destItem, opts, err => {
if (err) return cb(err)
return copyDirItems(items, src, dest, opts, cb)
})
})
}
function onLink (destStat, src, dest, opts, cb) {
fs.readlink(src, (err, resolvedSrc) => {
if (err) return cb(err)
if (opts.dereference) {
resolvedSrc = path.resolve(process.cwd(), resolvedSrc)
}
if (!destStat) {
return fs.symlink(resolvedSrc, dest, cb)
} else {
fs.readlink(dest, (err, resolvedDest) => {
if (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlink(resolvedSrc, dest, cb)
return cb(err)
}
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) {
return cb(new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`))
}
// do not copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (destStat.isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) {
return cb(new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`))
}
return copyLink(resolvedSrc, dest, cb)
})
}
})
}
function copyLink (resolvedSrc, dest, cb) {
fs.unlink(dest, err => {
if (err) return cb(err)
return fs.symlink(resolvedSrc, dest, cb)
})
}
module.exports = copy

View File

@@ -0,0 +1,6 @@
'use strict'
const u = require('universalify').fromCallback
module.exports = {
copy: u(require('./copy'))
}

View File

@@ -0,0 +1,48 @@
'use strict'
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const path = require('path')
const mkdir = require('../mkdirs')
const remove = require('../remove')
const emptyDir = u(function emptyDir (dir, callback) {
callback = callback || function () {}
fs.readdir(dir, (err, items) => {
if (err) return mkdir.mkdirs(dir, callback)
items = items.map(item => path.join(dir, item))
deleteItem()
function deleteItem () {
const item = items.pop()
if (!item) return callback()
remove.remove(item, err => {
if (err) return callback(err)
deleteItem()
})
}
})
})
function emptyDirSync (dir) {
let items
try {
items = fs.readdirSync(dir)
} catch {
return mkdir.mkdirsSync(dir)
}
items.forEach(item => {
item = path.join(dir, item)
remove.removeSync(item)
})
}
module.exports = {
emptyDirSync,
emptydirSync: emptyDirSync,
emptyDir,
emptydir: emptyDir
}

View File

@@ -0,0 +1,69 @@
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const mkdir = require('../mkdirs')
function createFile (file, callback) {
function makeFile () {
fs.writeFile(file, '', err => {
if (err) return callback(err)
callback()
})
}
fs.stat(file, (err, stats) => { // eslint-disable-line handle-callback-err
if (!err && stats.isFile()) return callback()
const dir = path.dirname(file)
fs.stat(dir, (err, stats) => {
if (err) {
// if the directory doesn't exist, make it
if (err.code === 'ENOENT') {
return mkdir.mkdirs(dir, err => {
if (err) return callback(err)
makeFile()
})
}
return callback(err)
}
if (stats.isDirectory()) makeFile()
else {
// parent is not a directory
// This is just to cause an internal ENOTDIR error to be thrown
fs.readdir(dir, err => {
if (err) return callback(err)
})
}
})
})
}
function createFileSync (file) {
let stats
try {
stats = fs.statSync(file)
} catch {}
if (stats && stats.isFile()) return
const dir = path.dirname(file)
try {
if (!fs.statSync(dir).isDirectory()) {
// parent is not a directory
// This is just to cause an internal ENOTDIR error to be thrown
fs.readdirSync(dir)
}
} catch (err) {
// If the stat call above failed because the directory doesn't exist, create it
if (err && err.code === 'ENOENT') mkdir.mkdirsSync(dir)
else throw err
}
fs.writeFileSync(file, '')
}
module.exports = {
createFile: u(createFile),
createFileSync
}

View File

@@ -0,0 +1,23 @@
'use strict'
const file = require('./file')
const link = require('./link')
const symlink = require('./symlink')
module.exports = {
// file
createFile: file.createFile,
createFileSync: file.createFileSync,
ensureFile: file.createFile,
ensureFileSync: file.createFileSync,
// link
createLink: link.createLink,
createLinkSync: link.createLinkSync,
ensureLink: link.createLink,
ensureLinkSync: link.createLinkSync,
// symlink
createSymlink: symlink.createSymlink,
createSymlinkSync: symlink.createSymlinkSync,
ensureSymlink: symlink.createSymlink,
ensureSymlinkSync: symlink.createSymlinkSync
}

View File

@@ -0,0 +1,61 @@
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
function createLink (srcpath, dstpath, callback) {
function makeLink (srcpath, dstpath) {
fs.link(srcpath, dstpath, err => {
if (err) return callback(err)
callback(null)
})
}
pathExists(dstpath, (err, destinationExists) => {
if (err) return callback(err)
if (destinationExists) return callback(null)
fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureLink')
return callback(err)
}
const dir = path.dirname(dstpath)
pathExists(dir, (err, dirExists) => {
if (err) return callback(err)
if (dirExists) return makeLink(srcpath, dstpath)
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
makeLink(srcpath, dstpath)
})
})
})
})
}
function createLinkSync (srcpath, dstpath) {
const destinationExists = fs.existsSync(dstpath)
if (destinationExists) return undefined
try {
fs.lstatSync(srcpath)
} catch (err) {
err.message = err.message.replace('lstat', 'ensureLink')
throw err
}
const dir = path.dirname(dstpath)
const dirExists = fs.existsSync(dir)
if (dirExists) return fs.linkSync(srcpath, dstpath)
mkdir.mkdirsSync(dir)
return fs.linkSync(srcpath, dstpath)
}
module.exports = {
createLink: u(createLink),
createLinkSync
}

View File

@@ -0,0 +1,99 @@
'use strict'
const path = require('path')
const fs = require('graceful-fs')
const pathExists = require('../path-exists').pathExists
/**
* Function that returns two types of paths, one relative to symlink, and one
* relative to the current working directory. Checks if path is absolute or
* relative. If the path is relative, this function checks if the path is
* relative to symlink or relative to current working directory. This is an
* initiative to find a smarter `srcpath` to supply when building symlinks.
* This allows you to determine which path to use out of one of three possible
* types of source paths. The first is an absolute path. This is detected by
* `path.isAbsolute()`. When an absolute path is provided, it is checked to
* see if it exists. If it does it's used, if not an error is returned
* (callback)/ thrown (sync). The other two options for `srcpath` are a
* relative url. By default Node's `fs.symlink` works by creating a symlink
* using `dstpath` and expects the `srcpath` to be relative to the newly
* created symlink. If you provide a `srcpath` that does not exist on the file
* system it results in a broken symlink. To minimize this, the function
* checks to see if the 'relative to symlink' source file exists, and if it
* does it will use it. If it does not, it checks if there's a file that
* exists that is relative to the current working directory, if does its used.
* This preserves the expectations of the original fs.symlink spec and adds
* the ability to pass in `relative to current working direcotry` paths.
*/
function symlinkPaths (srcpath, dstpath, callback) {
if (path.isAbsolute(srcpath)) {
return fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureSymlink')
return callback(err)
}
return callback(null, {
toCwd: srcpath,
toDst: srcpath
})
})
} else {
const dstdir = path.dirname(dstpath)
const relativeToDst = path.join(dstdir, srcpath)
return pathExists(relativeToDst, (err, exists) => {
if (err) return callback(err)
if (exists) {
return callback(null, {
toCwd: relativeToDst,
toDst: srcpath
})
} else {
return fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureSymlink')
return callback(err)
}
return callback(null, {
toCwd: srcpath,
toDst: path.relative(dstdir, srcpath)
})
})
}
})
}
}
function symlinkPathsSync (srcpath, dstpath) {
let exists
if (path.isAbsolute(srcpath)) {
exists = fs.existsSync(srcpath)
if (!exists) throw new Error('absolute srcpath does not exist')
return {
toCwd: srcpath,
toDst: srcpath
}
} else {
const dstdir = path.dirname(dstpath)
const relativeToDst = path.join(dstdir, srcpath)
exists = fs.existsSync(relativeToDst)
if (exists) {
return {
toCwd: relativeToDst,
toDst: srcpath
}
} else {
exists = fs.existsSync(srcpath)
if (!exists) throw new Error('relative srcpath does not exist')
return {
toCwd: srcpath,
toDst: path.relative(dstdir, srcpath)
}
}
}
}
module.exports = {
symlinkPaths,
symlinkPathsSync
}

View File

@@ -0,0 +1,31 @@
'use strict'
const fs = require('graceful-fs')
function symlinkType (srcpath, type, callback) {
callback = (typeof type === 'function') ? type : callback
type = (typeof type === 'function') ? false : type
if (type) return callback(null, type)
fs.lstat(srcpath, (err, stats) => {
if (err) return callback(null, 'file')
type = (stats && stats.isDirectory()) ? 'dir' : 'file'
callback(null, type)
})
}
function symlinkTypeSync (srcpath, type) {
let stats
if (type) return type
try {
stats = fs.lstatSync(srcpath)
} catch {
return 'file'
}
return (stats && stats.isDirectory()) ? 'dir' : 'file'
}
module.exports = {
symlinkType,
symlinkTypeSync
}

View File

@@ -0,0 +1,63 @@
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const _mkdirs = require('../mkdirs')
const mkdirs = _mkdirs.mkdirs
const mkdirsSync = _mkdirs.mkdirsSync
const _symlinkPaths = require('./symlink-paths')
const symlinkPaths = _symlinkPaths.symlinkPaths
const symlinkPathsSync = _symlinkPaths.symlinkPathsSync
const _symlinkType = require('./symlink-type')
const symlinkType = _symlinkType.symlinkType
const symlinkTypeSync = _symlinkType.symlinkTypeSync
const pathExists = require('../path-exists').pathExists
function createSymlink (srcpath, dstpath, type, callback) {
callback = (typeof type === 'function') ? type : callback
type = (typeof type === 'function') ? false : type
pathExists(dstpath, (err, destinationExists) => {
if (err) return callback(err)
if (destinationExists) return callback(null)
symlinkPaths(srcpath, dstpath, (err, relative) => {
if (err) return callback(err)
srcpath = relative.toDst
symlinkType(relative.toCwd, type, (err, type) => {
if (err) return callback(err)
const dir = path.dirname(dstpath)
pathExists(dir, (err, dirExists) => {
if (err) return callback(err)
if (dirExists) return fs.symlink(srcpath, dstpath, type, callback)
mkdirs(dir, err => {
if (err) return callback(err)
fs.symlink(srcpath, dstpath, type, callback)
})
})
})
})
})
}
function createSymlinkSync (srcpath, dstpath, type) {
const destinationExists = fs.existsSync(dstpath)
if (destinationExists) return undefined
const relative = symlinkPathsSync(srcpath, dstpath)
srcpath = relative.toDst
type = symlinkTypeSync(relative.toCwd, type)
const dir = path.dirname(dstpath)
const exists = fs.existsSync(dir)
if (exists) return fs.symlinkSync(srcpath, dstpath, type)
mkdirsSync(dir)
return fs.symlinkSync(srcpath, dstpath, type)
}
module.exports = {
createSymlink: u(createSymlink),
createSymlinkSync
}

View File

@@ -0,0 +1,130 @@
'use strict'
// This is adapted from https://github.com/normalize/mz
// Copyright (c) 2014-2016 Jonathan Ong me@jongleberry.com and Contributors
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const api = [
'access',
'appendFile',
'chmod',
'chown',
'close',
'copyFile',
'fchmod',
'fchown',
'fdatasync',
'fstat',
'fsync',
'ftruncate',
'futimes',
'lchmod',
'lchown',
'link',
'lstat',
'mkdir',
'mkdtemp',
'open',
'opendir',
'readdir',
'readFile',
'readlink',
'realpath',
'rename',
'rm',
'rmdir',
'stat',
'symlink',
'truncate',
'unlink',
'utimes',
'writeFile'
].filter(key => {
// Some commands are not available on some systems. Ex:
// fs.opendir was added in Node.js v12.12.0
// fs.rm was added in Node.js v14.14.0
// fs.lchown is not available on at least some Linux
return typeof fs[key] === 'function'
})
// Export all keys:
Object.keys(fs).forEach(key => {
if (key === 'promises') {
// fs.promises is a getter property that triggers ExperimentalWarning
// Don't re-export it here, the getter is defined in "lib/index.js"
return
}
exports[key] = fs[key]
})
// Universalify async methods:
api.forEach(method => {
exports[method] = u(fs[method])
})
// We differ from mz/fs in that we still ship the old, broken, fs.exists()
// since we are a drop-in replacement for the native module
exports.exists = function (filename, callback) {
if (typeof callback === 'function') {
return fs.exists(filename, callback)
}
return new Promise(resolve => {
return fs.exists(filename, resolve)
})
}
// fs.read(), fs.write(), & fs.writev() need special treatment due to multiple callback args
exports.read = function (fd, buffer, offset, length, position, callback) {
if (typeof callback === 'function') {
return fs.read(fd, buffer, offset, length, position, callback)
}
return new Promise((resolve, reject) => {
fs.read(fd, buffer, offset, length, position, (err, bytesRead, buffer) => {
if (err) return reject(err)
resolve({ bytesRead, buffer })
})
})
}
// Function signature can be
// fs.write(fd, buffer[, offset[, length[, position]]], callback)
// OR
// fs.write(fd, string[, position[, encoding]], callback)
// We need to handle both cases, so we use ...args
exports.write = function (fd, buffer, ...args) {
if (typeof args[args.length - 1] === 'function') {
return fs.write(fd, buffer, ...args)
}
return new Promise((resolve, reject) => {
fs.write(fd, buffer, ...args, (err, bytesWritten, buffer) => {
if (err) return reject(err)
resolve({ bytesWritten, buffer })
})
})
}
// fs.writev only available in Node v12.9.0+
if (typeof fs.writev === 'function') {
// Function signature is
// s.writev(fd, buffers[, position], callback)
// We need to handle the optional arg, so we use ...args
exports.writev = function (fd, buffers, ...args) {
if (typeof args[args.length - 1] === 'function') {
return fs.writev(fd, buffers, ...args)
}
return new Promise((resolve, reject) => {
fs.writev(fd, buffers, ...args, (err, bytesWritten, buffers) => {
if (err) return reject(err)
resolve({ bytesWritten, buffers })
})
})
}
}
// fs.realpath.native only available in Node v9.2+
if (typeof fs.realpath.native === 'function') {
exports.realpath.native = u(fs.realpath.native)
}

View File

@@ -0,0 +1,27 @@
'use strict'
module.exports = {
// Export promiseified graceful-fs:
...require('./fs'),
// Export extra methods:
...require('./copy-sync'),
...require('./copy'),
...require('./empty'),
...require('./ensure'),
...require('./json'),
...require('./mkdirs'),
...require('./move-sync'),
...require('./move'),
...require('./output'),
...require('./path-exists'),
...require('./remove')
}
// Export fs.promises as a getter property so that we don't trigger
// ExperimentalWarning before fs.promises is actually accessed.
const fs = require('fs')
if (Object.getOwnPropertyDescriptor(fs, 'promises')) {
Object.defineProperty(module.exports, 'promises', {
get () { return fs.promises }
})
}

View File

@@ -0,0 +1,16 @@
'use strict'
const u = require('universalify').fromPromise
const jsonFile = require('./jsonfile')
jsonFile.outputJson = u(require('./output-json'))
jsonFile.outputJsonSync = require('./output-json-sync')
// aliases
jsonFile.outputJSON = jsonFile.outputJson
jsonFile.outputJSONSync = jsonFile.outputJsonSync
jsonFile.writeJSON = jsonFile.writeJson
jsonFile.writeJSONSync = jsonFile.writeJsonSync
jsonFile.readJSON = jsonFile.readJson
jsonFile.readJSONSync = jsonFile.readJsonSync
module.exports = jsonFile

View File

@@ -0,0 +1,11 @@
'use strict'
const jsonFile = require('jsonfile')
module.exports = {
// jsonfile exports
readJson: jsonFile.readFile,
readJsonSync: jsonFile.readFileSync,
writeJson: jsonFile.writeFile,
writeJsonSync: jsonFile.writeFileSync
}

View File

@@ -0,0 +1,12 @@
'use strict'
const { stringify } = require('jsonfile/utils')
const { outputFileSync } = require('../output')
function outputJsonSync (file, data, options) {
const str = stringify(data, options)
outputFileSync(file, str, options)
}
module.exports = outputJsonSync

View File

@@ -0,0 +1,12 @@
'use strict'
const { stringify } = require('jsonfile/utils')
const { outputFile } = require('../output')
async function outputJson (file, data, options = {}) {
const str = stringify(data, options)
await outputFile(file, str, options)
}
module.exports = outputJson

View File

@@ -0,0 +1,14 @@
'use strict'
const u = require('universalify').fromPromise
const { makeDir: _makeDir, makeDirSync } = require('./make-dir')
const makeDir = u(_makeDir)
module.exports = {
mkdirs: makeDir,
mkdirsSync: makeDirSync,
// alias
mkdirp: makeDir,
mkdirpSync: makeDirSync,
ensureDir: makeDir,
ensureDirSync: makeDirSync
}

View File

@@ -0,0 +1,141 @@
// Adapted from https://github.com/sindresorhus/make-dir
// Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
// 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.
'use strict'
const fs = require('../fs')
const path = require('path')
const atLeastNode = require('at-least-node')
const useNativeRecursiveOption = atLeastNode('10.12.0')
// https://github.com/nodejs/node/issues/8987
// https://github.com/libuv/libuv/pull/1088
const checkPath = pth => {
if (process.platform === 'win32') {
const pathHasInvalidWinCharacters = /[<>:"|?*]/.test(pth.replace(path.parse(pth).root, ''))
if (pathHasInvalidWinCharacters) {
const error = new Error(`Path contains invalid characters: ${pth}`)
error.code = 'EINVAL'
throw error
}
}
}
const processOptions = options => {
const defaults = { mode: 0o777 }
if (typeof options === 'number') options = { mode: options }
return { ...defaults, ...options }
}
const permissionError = pth => {
// This replicates the exception of `fs.mkdir` with native the
// `recusive` option when run on an invalid drive under Windows.
const error = new Error(`operation not permitted, mkdir '${pth}'`)
error.code = 'EPERM'
error.errno = -4048
error.path = pth
error.syscall = 'mkdir'
return error
}
module.exports.makeDir = async (input, options) => {
checkPath(input)
options = processOptions(options)
if (useNativeRecursiveOption) {
const pth = path.resolve(input)
return fs.mkdir(pth, {
mode: options.mode,
recursive: true
})
}
const make = async pth => {
try {
await fs.mkdir(pth, options.mode)
} catch (error) {
if (error.code === 'EPERM') {
throw error
}
if (error.code === 'ENOENT') {
if (path.dirname(pth) === pth) {
throw permissionError(pth)
}
if (error.message.includes('null bytes')) {
throw error
}
await make(path.dirname(pth))
return make(pth)
}
try {
const stats = await fs.stat(pth)
if (!stats.isDirectory()) {
// This error is never exposed to the user
// it is caught below, and the original error is thrown
throw new Error('The path is not a directory')
}
} catch {
throw error
}
}
}
return make(path.resolve(input))
}
module.exports.makeDirSync = (input, options) => {
checkPath(input)
options = processOptions(options)
if (useNativeRecursiveOption) {
const pth = path.resolve(input)
return fs.mkdirSync(pth, {
mode: options.mode,
recursive: true
})
}
const make = pth => {
try {
fs.mkdirSync(pth, options.mode)
} catch (error) {
if (error.code === 'EPERM') {
throw error
}
if (error.code === 'ENOENT') {
if (path.dirname(pth) === pth) {
throw permissionError(pth)
}
if (error.message.includes('null bytes')) {
throw error
}
make(path.dirname(pth))
return make(pth)
}
try {
if (!fs.statSync(pth).isDirectory()) {
// This error is never exposed to the user
// it is caught below, and the original error is thrown
throw new Error('The path is not a directory')
}
} catch {
throw error
}
}
}
return make(path.resolve(input))
}

View File

@@ -0,0 +1,5 @@
'use strict'
module.exports = {
moveSync: require('./move-sync')
}

View File

@@ -0,0 +1,47 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const copySync = require('../copy-sync').copySync
const removeSync = require('../remove').removeSync
const mkdirpSync = require('../mkdirs').mkdirpSync
const stat = require('../util/stat')
function moveSync (src, dest, opts) {
opts = opts || {}
const overwrite = opts.overwrite || opts.clobber || false
const { srcStat } = stat.checkPathsSync(src, dest, 'move')
stat.checkParentPathsSync(src, srcStat, dest, 'move')
mkdirpSync(path.dirname(dest))
return doRename(src, dest, overwrite)
}
function doRename (src, dest, overwrite) {
if (overwrite) {
removeSync(dest)
return rename(src, dest, overwrite)
}
if (fs.existsSync(dest)) throw new Error('dest already exists.')
return rename(src, dest, overwrite)
}
function rename (src, dest, overwrite) {
try {
fs.renameSync(src, dest)
} catch (err) {
if (err.code !== 'EXDEV') throw err
return moveAcrossDevice(src, dest, overwrite)
}
}
function moveAcrossDevice (src, dest, overwrite) {
const opts = {
overwrite,
errorOnExist: true
}
copySync(src, dest, opts)
return removeSync(src)
}
module.exports = moveSync

View File

@@ -0,0 +1,6 @@
'use strict'
const u = require('universalify').fromCallback
module.exports = {
move: u(require('./move'))
}

View File

@@ -0,0 +1,65 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const copy = require('../copy').copy
const remove = require('../remove').remove
const mkdirp = require('../mkdirs').mkdirp
const pathExists = require('../path-exists').pathExists
const stat = require('../util/stat')
function move (src, dest, opts, cb) {
if (typeof opts === 'function') {
cb = opts
opts = {}
}
const overwrite = opts.overwrite || opts.clobber || false
stat.checkPaths(src, dest, 'move', (err, stats) => {
if (err) return cb(err)
const { srcStat } = stats
stat.checkParentPaths(src, srcStat, dest, 'move', err => {
if (err) return cb(err)
mkdirp(path.dirname(dest), err => {
if (err) return cb(err)
return doRename(src, dest, overwrite, cb)
})
})
})
}
function doRename (src, dest, overwrite, cb) {
if (overwrite) {
return remove(dest, err => {
if (err) return cb(err)
return rename(src, dest, overwrite, cb)
})
}
pathExists(dest, (err, destExists) => {
if (err) return cb(err)
if (destExists) return cb(new Error('dest already exists.'))
return rename(src, dest, overwrite, cb)
})
}
function rename (src, dest, overwrite, cb) {
fs.rename(src, dest, err => {
if (!err) return cb()
if (err.code !== 'EXDEV') return cb(err)
return moveAcrossDevice(src, dest, overwrite, cb)
})
}
function moveAcrossDevice (src, dest, overwrite, cb) {
const opts = {
overwrite,
errorOnExist: true
}
copy(src, dest, opts, err => {
if (err) return cb(err)
return remove(src, cb)
})
}
module.exports = move

View File

@@ -0,0 +1,40 @@
'use strict'
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const path = require('path')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
function outputFile (file, data, encoding, callback) {
if (typeof encoding === 'function') {
callback = encoding
encoding = 'utf8'
}
const dir = path.dirname(file)
pathExists(dir, (err, itDoes) => {
if (err) return callback(err)
if (itDoes) return fs.writeFile(file, data, encoding, callback)
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
fs.writeFile(file, data, encoding, callback)
})
})
}
function outputFileSync (file, ...args) {
const dir = path.dirname(file)
if (fs.existsSync(dir)) {
return fs.writeFileSync(file, ...args)
}
mkdir.mkdirsSync(dir)
fs.writeFileSync(file, ...args)
}
module.exports = {
outputFile: u(outputFile),
outputFileSync
}

View File

@@ -0,0 +1,12 @@
'use strict'
const u = require('universalify').fromPromise
const fs = require('../fs')
function pathExists (path) {
return fs.access(path).then(() => true).catch(() => false)
}
module.exports = {
pathExists: u(pathExists),
pathExistsSync: fs.existsSync
}

View File

@@ -0,0 +1,9 @@
'use strict'
const u = require('universalify').fromCallback
const rimraf = require('./rimraf')
module.exports = {
remove: u(rimraf),
removeSync: rimraf.sync
}

Some files were not shown because too many files have changed in this diff Show More