first commit

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

View File

@@ -0,0 +1,89 @@
import { ConfigPlugin, ExportedConfigWithProps, Mod } from '../Plugin.types';
import { Manifest, Paths, Properties, Resources } from '../android';
type OptionalPromise<T> = T | Promise<T>;
type MutateDataAction<T> = (expo: ExportedConfigWithProps<T>, data: T) => OptionalPromise<T>;
/**
* Helper method for creating mods from existing config functions.
*
* @param action
*/
export declare function createAndroidManifestPlugin(action: MutateDataAction<Manifest.AndroidManifest>, name: string): ConfigPlugin;
export declare function createStringsXmlPlugin(action: MutateDataAction<Resources.ResourceXML>, name: string): ConfigPlugin;
/**
* Provides the AndroidManifest.xml for modification.
*
* @param config
* @param action
*/
export declare const withAndroidManifest: ConfigPlugin<Mod<Manifest.AndroidManifest>>;
/**
* Provides the strings.xml for modification.
*
* @param config
* @param action
*/
export declare const withStringsXml: ConfigPlugin<Mod<Resources.ResourceXML>>;
/**
* Provides the `android/app/src/main/res/values/colors.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).
*
* @param config
* @param action
*/
export declare const withAndroidColors: ConfigPlugin<Mod<Resources.ResourceXML>>;
/**
* Provides the `android/app/src/main/res/values-night/colors.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).
*
* @param config
* @param action
*/
export declare const withAndroidColorsNight: ConfigPlugin<Mod<Resources.ResourceXML>>;
/**
* Provides the `android/app/src/main/res/values/styles.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).
*
* @param config
* @param action
*/
export declare const withAndroidStyles: ConfigPlugin<Mod<Resources.ResourceXML>>;
/**
* Provides the project MainActivity for modification.
*
* @param config
* @param action
*/
export declare const withMainActivity: ConfigPlugin<Mod<Paths.ApplicationProjectFile>>;
/**
* Provides the project MainApplication for modification.
*
* @param config
* @param action
*/
export declare const withMainApplication: ConfigPlugin<Mod<Paths.ApplicationProjectFile>>;
/**
* Provides the project /build.gradle for modification.
*
* @param config
* @param action
*/
export declare const withProjectBuildGradle: ConfigPlugin<Mod<Paths.GradleProjectFile>>;
/**
* Provides the app/build.gradle for modification.
*
* @param config
* @param action
*/
export declare const withAppBuildGradle: ConfigPlugin<Mod<Paths.GradleProjectFile>>;
/**
* Provides the /settings.gradle for modification.
*
* @param config
* @param action
*/
export declare const withSettingsGradle: ConfigPlugin<Mod<Paths.GradleProjectFile>>;
/**
* Provides the /gradle.properties for modification.
*
* @param config
* @param action
*/
export declare const withGradleProperties: ConfigPlugin<Mod<Properties.PropertiesItem[]>>;
export {};

View File

@@ -0,0 +1,210 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createAndroidManifestPlugin = createAndroidManifestPlugin;
exports.createStringsXmlPlugin = createStringsXmlPlugin;
exports.withStringsXml = exports.withSettingsGradle = exports.withProjectBuildGradle = exports.withMainApplication = exports.withMainActivity = exports.withGradleProperties = exports.withAppBuildGradle = exports.withAndroidStyles = exports.withAndroidManifest = exports.withAndroidColorsNight = exports.withAndroidColors = void 0;
function _withMod() {
const data = require("./withMod");
_withMod = function () {
return data;
};
return data;
}
/**
* Helper method for creating mods from existing config functions.
*
* @param action
*/
function createAndroidManifestPlugin(action, name) {
const withUnknown = config => withAndroidManifest(config, async config => {
config.modResults = await action(config, config.modResults);
return config;
});
if (name) {
Object.defineProperty(withUnknown, 'name', {
value: name
});
}
return withUnknown;
}
function createStringsXmlPlugin(action, name) {
const withUnknown = config => withStringsXml(config, async config => {
config.modResults = await action(config, config.modResults);
return config;
});
if (name) {
Object.defineProperty(withUnknown, 'name', {
value: name
});
}
return withUnknown;
}
/**
* Provides the AndroidManifest.xml for modification.
*
* @param config
* @param action
*/
const withAndroidManifest = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'manifest',
action
});
};
/**
* Provides the strings.xml for modification.
*
* @param config
* @param action
*/
exports.withAndroidManifest = withAndroidManifest;
const withStringsXml = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'strings',
action
});
};
/**
* Provides the `android/app/src/main/res/values/colors.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).
*
* @param config
* @param action
*/
exports.withStringsXml = withStringsXml;
const withAndroidColors = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'colors',
action
});
};
/**
* Provides the `android/app/src/main/res/values-night/colors.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).
*
* @param config
* @param action
*/
exports.withAndroidColors = withAndroidColors;
const withAndroidColorsNight = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'colorsNight',
action
});
};
/**
* Provides the `android/app/src/main/res/values/styles.xml` as JSON (parsed with [`xml2js`](https://www.npmjs.com/package/xml2js)).
*
* @param config
* @param action
*/
exports.withAndroidColorsNight = withAndroidColorsNight;
const withAndroidStyles = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'styles',
action
});
};
/**
* Provides the project MainActivity for modification.
*
* @param config
* @param action
*/
exports.withAndroidStyles = withAndroidStyles;
const withMainActivity = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'mainActivity',
action
});
};
/**
* Provides the project MainApplication for modification.
*
* @param config
* @param action
*/
exports.withMainActivity = withMainActivity;
const withMainApplication = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'mainApplication',
action
});
};
/**
* Provides the project /build.gradle for modification.
*
* @param config
* @param action
*/
exports.withMainApplication = withMainApplication;
const withProjectBuildGradle = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'projectBuildGradle',
action
});
};
/**
* Provides the app/build.gradle for modification.
*
* @param config
* @param action
*/
exports.withProjectBuildGradle = withProjectBuildGradle;
const withAppBuildGradle = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'appBuildGradle',
action
});
};
/**
* Provides the /settings.gradle for modification.
*
* @param config
* @param action
*/
exports.withAppBuildGradle = withAppBuildGradle;
const withSettingsGradle = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'settingsGradle',
action
});
};
/**
* Provides the /gradle.properties for modification.
*
* @param config
* @param action
*/
exports.withSettingsGradle = withSettingsGradle;
const withGradleProperties = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'android',
mod: 'gradleProperties',
action
});
};
exports.withGradleProperties = withGradleProperties;
//# sourceMappingURL=android-plugins.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,31 @@
import { BaseModOptions } from './withMod';
import { ConfigPlugin, ExportedConfig, ExportedConfigWithProps, ModPlatform } from '../Plugin.types';
export type ForwardedBaseModOptions = Partial<Pick<BaseModOptions, 'saveToInternal' | 'skipEmptyMod'>>;
export type BaseModProviderMethods<ModType, Props extends ForwardedBaseModOptions = ForwardedBaseModOptions> = {
getFilePath: (config: ExportedConfigWithProps<ModType>, props: Props) => Promise<string> | string;
read: (filePath: string, config: ExportedConfigWithProps<ModType>, props: Props) => Promise<ModType> | ModType;
write: (filePath: string, config: ExportedConfigWithProps<ModType>, props: Props) => Promise<void> | void;
/**
* If the mod supports introspection, and avoids making any filesystem modifications during compilation.
* By enabling, this mod, and all of its descendants will be run in introspection mode.
* This should only be used for static files like JSON or XML, and not for application files that require regexes,
* or complex static files that require other files to be generated like Xcode `.pbxproj`.
*/
isIntrospective?: boolean;
};
export type CreateBaseModProps<ModType, Props extends ForwardedBaseModOptions = ForwardedBaseModOptions> = {
methodName: string;
platform: ModPlatform;
modName: string;
} & BaseModProviderMethods<ModType, Props>;
export declare function createBaseMod<ModType, Props extends ForwardedBaseModOptions = ForwardedBaseModOptions>({ methodName, platform, modName, getFilePath, read, write, isIntrospective, }: CreateBaseModProps<ModType, Props>): ConfigPlugin<Props | void>;
export declare function assertModResults(results: any, platformName: string, modName: string): any;
export declare function createPlatformBaseMod<ModType, Props extends ForwardedBaseModOptions = ForwardedBaseModOptions>({ modName, ...props }: Omit<CreateBaseModProps<ModType, Props>, 'methodName'>): ConfigPlugin<void | Props>;
/** A TS wrapper for creating provides */
export declare function provider<ModType, Props extends ForwardedBaseModOptions = ForwardedBaseModOptions>(props: BaseModProviderMethods<ModType, Props>): BaseModProviderMethods<ModType, Props>;
/** Plugin to create and append base mods from file providers */
export declare function withGeneratedBaseMods<ModName extends string>(config: ExportedConfig, { platform, providers, ...props }: ForwardedBaseModOptions & {
/** Officially supports `'ios' | 'android'` (`ModPlatform`). Arbitrary strings are supported for adding out-of-tree platforms. */
platform: ModPlatform & string;
providers: Partial<Record<ModName, BaseModProviderMethods<any, any>>>;
}): ExportedConfig;

View File

@@ -0,0 +1,129 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.assertModResults = assertModResults;
exports.createBaseMod = createBaseMod;
exports.createPlatformBaseMod = createPlatformBaseMod;
exports.provider = provider;
exports.withGeneratedBaseMods = withGeneratedBaseMods;
function _debug() {
const data = _interopRequireDefault(require("debug"));
_debug = function () {
return data;
};
return data;
}
function _withMod() {
const data = require("./withMod");
_withMod = function () {
return data;
};
return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const debug = (0, _debug().default)('expo:config-plugins:base-mods');
function createBaseMod({
methodName,
platform,
modName,
getFilePath,
read,
write,
isIntrospective
}) {
const withUnknown = (config, _props) => {
const props = _props || {};
return (0, _withMod().withBaseMod)(config, {
platform,
mod: modName,
skipEmptyMod: props.skipEmptyMod ?? true,
saveToInternal: props.saveToInternal ?? false,
isProvider: true,
isIntrospective,
async action({
modRequest: {
nextMod,
...modRequest
},
...config
}) {
try {
let results = {
...config,
modRequest
};
const filePath = await getFilePath(results, props);
debug(`mods.${platform}.${modName}: file path: ${filePath || '[skipped]'}`);
const modResults = await read(filePath, results, props);
results = await nextMod({
...results,
modResults,
modRequest
});
assertModResults(results, modRequest.platform, modRequest.modName);
await write(filePath, results, props);
return results;
} catch (error) {
error.message = `[${platform}.${modName}]: ${methodName}: ${error.message}`;
throw error;
}
}
});
};
if (methodName) {
Object.defineProperty(withUnknown, 'name', {
value: methodName
});
}
return withUnknown;
}
function assertModResults(results, platformName, modName) {
// If the results came from a mod, they'd be in the form of [config, data].
// Ensure the results are an array and omit the data since it should've been written by a data provider plugin.
const ensuredResults = results;
// Sanity check to help locate non compliant mods.
if (!ensuredResults || typeof ensuredResults !== 'object' || !ensuredResults?.mods) {
throw new Error(`Mod \`mods.${platformName}.${modName}\` evaluated to an object that is not a valid project config. Instead got: ${JSON.stringify(ensuredResults)}`);
}
return ensuredResults;
}
function upperFirst(name) {
return name.charAt(0).toUpperCase() + name.slice(1);
}
function createPlatformBaseMod({
modName,
...props
}) {
// Generate the function name to ensure it's uniform and also to improve stack traces.
const methodName = `with${upperFirst(props.platform)}${upperFirst(modName)}BaseMod`;
return createBaseMod({
methodName,
modName,
...props
});
}
/** A TS wrapper for creating provides */
function provider(props) {
return props;
}
/** Plugin to create and append base mods from file providers */
function withGeneratedBaseMods(config, {
platform,
providers,
...props
}) {
return Object.entries(providers).reduce((config, [modName, value]) => {
const baseMod = createPlatformBaseMod({
platform,
modName,
...value
});
return baseMod(config, props);
}, config);
}
//# sourceMappingURL=createBaseMod.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,77 @@
import type { ExpoConfig } from '@expo/config-types';
import type { JSONObject } from '@expo/json-file';
import type { XcodeProject } from 'xcode';
import type { ConfigPlugin, Mod } from '../Plugin.types';
import type { ExpoPlist, InfoPlist } from '../ios/IosConfig.types';
import type { AppDelegateProjectFile, PodfileProjectFile } from '../ios/Paths';
type MutateInfoPlistAction = (expo: ExpoConfig, infoPlist: InfoPlist) => Promise<InfoPlist> | InfoPlist;
/**
* Helper method for creating mods from existing config functions.
*
* @param action
*/
export declare function createInfoPlistPlugin(action: MutateInfoPlistAction, name?: string): ConfigPlugin;
export declare function createInfoPlistPluginWithPropertyGuard(action: MutateInfoPlistAction, settings: {
infoPlistProperty: string;
expoConfigProperty: string;
expoPropertyGetter?: (config: ExpoConfig) => string;
}, name?: string): ConfigPlugin;
type MutateEntitlementsPlistAction = (expo: ExpoConfig, entitlements: JSONObject) => JSONObject;
/**
* Helper method for creating mods from existing config functions.
*
* @param action
*/
export declare function createEntitlementsPlugin(action: MutateEntitlementsPlistAction, name: string): ConfigPlugin;
/**
* Provides the AppDelegate file for modification.
*
* @param config
* @param action
*/
export declare const withAppDelegate: ConfigPlugin<Mod<AppDelegateProjectFile>>;
/**
* Provides the Info.plist file for modification.
* Keeps the config's expo.ios.infoPlist object in sync with the data.
*
* @param config
* @param action
*/
export declare const withInfoPlist: ConfigPlugin<Mod<InfoPlist>>;
/**
* Provides the main .entitlements file for modification.
* Keeps the config's expo.ios.entitlements object in sync with the data.
*
* @param config
* @param action
*/
export declare const withEntitlementsPlist: ConfigPlugin<Mod<JSONObject>>;
/**
* Provides the Expo.plist for modification.
*
* @param config
* @param action
*/
export declare const withExpoPlist: ConfigPlugin<Mod<ExpoPlist>>;
/**
* Provides the main .xcodeproj for modification.
*
* @param config
* @param action
*/
export declare const withXcodeProject: ConfigPlugin<Mod<XcodeProject>>;
/**
* Provides the Podfile for modification.
*
* @param config
* @param action
*/
export declare const withPodfile: ConfigPlugin<Mod<PodfileProjectFile>>;
/**
* Provides the Podfile.properties.json for modification.
*
* @param config
* @param action
*/
export declare const withPodfileProperties: ConfigPlugin<Mod<Record<string, string>>>;
export {};

View File

@@ -0,0 +1,205 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createEntitlementsPlugin = createEntitlementsPlugin;
exports.createInfoPlistPlugin = createInfoPlistPlugin;
exports.createInfoPlistPluginWithPropertyGuard = createInfoPlistPluginWithPropertyGuard;
exports.withXcodeProject = exports.withPodfileProperties = exports.withPodfile = exports.withInfoPlist = exports.withExpoPlist = exports.withEntitlementsPlist = exports.withAppDelegate = void 0;
function _withMod() {
const data = require("./withMod");
_withMod = function () {
return data;
};
return data;
}
function _obj() {
const data = require("../utils/obj");
_obj = function () {
return data;
};
return data;
}
function _warnings() {
const data = require("../utils/warnings");
_warnings = function () {
return data;
};
return data;
}
/**
* Helper method for creating mods from existing config functions.
*
* @param action
*/
function createInfoPlistPlugin(action, name) {
const withUnknown = config => withInfoPlist(config, async config => {
config.modResults = await action(config, config.modResults);
return config;
});
if (name) {
Object.defineProperty(withUnknown, 'name', {
value: name
});
}
return withUnknown;
}
function createInfoPlistPluginWithPropertyGuard(action, settings, name) {
const withUnknown = config => withInfoPlist(config, async config => {
const existingProperty = settings.expoPropertyGetter ? settings.expoPropertyGetter(config) : (0, _obj().get)(config, settings.expoConfigProperty);
// If the user explicitly sets a value in the infoPlist, we should respect that.
if (config.modRawConfig.ios?.infoPlist?.[settings.infoPlistProperty] === undefined) {
config.modResults = await action(config, config.modResults);
} else if (existingProperty !== undefined) {
// Only warn if there is a conflict.
(0, _warnings().addWarningIOS)(settings.expoConfigProperty, `"ios.infoPlist.${settings.infoPlistProperty}" is set in the config. Ignoring abstract property "${settings.expoConfigProperty}": ${existingProperty}`);
}
return config;
});
if (name) {
Object.defineProperty(withUnknown, 'name', {
value: name
});
}
return withUnknown;
}
/**
* Helper method for creating mods from existing config functions.
*
* @param action
*/
function createEntitlementsPlugin(action, name) {
const withUnknown = config => withEntitlementsPlist(config, async config => {
config.modResults = await action(config, config.modResults);
return config;
});
if (name) {
Object.defineProperty(withUnknown, 'name', {
value: name
});
}
return withUnknown;
}
/**
* Provides the AppDelegate file for modification.
*
* @param config
* @param action
*/
const withAppDelegate = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'ios',
mod: 'appDelegate',
action
});
};
/**
* Provides the Info.plist file for modification.
* Keeps the config's expo.ios.infoPlist object in sync with the data.
*
* @param config
* @param action
*/
exports.withAppDelegate = withAppDelegate;
const withInfoPlist = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'ios',
mod: 'infoPlist',
async action(config) {
config = await action(config);
if (!config.ios) {
config.ios = {};
}
config.ios.infoPlist = config.modResults;
return config;
}
});
};
/**
* Provides the main .entitlements file for modification.
* Keeps the config's expo.ios.entitlements object in sync with the data.
*
* @param config
* @param action
*/
exports.withInfoPlist = withInfoPlist;
const withEntitlementsPlist = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'ios',
mod: 'entitlements',
async action(config) {
config = await action(config);
if (!config.ios) {
config.ios = {};
}
config.ios.entitlements = config.modResults;
return config;
}
});
};
/**
* Provides the Expo.plist for modification.
*
* @param config
* @param action
*/
exports.withEntitlementsPlist = withEntitlementsPlist;
const withExpoPlist = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'ios',
mod: 'expoPlist',
action
});
};
/**
* Provides the main .xcodeproj for modification.
*
* @param config
* @param action
*/
exports.withExpoPlist = withExpoPlist;
const withXcodeProject = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'ios',
mod: 'xcodeproj',
action
});
};
/**
* Provides the Podfile for modification.
*
* @param config
* @param action
*/
exports.withXcodeProject = withXcodeProject;
const withPodfile = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'ios',
mod: 'podfile',
action
});
};
/**
* Provides the Podfile.properties.json for modification.
*
* @param config
* @param action
*/
exports.withPodfile = withPodfile;
const withPodfileProperties = (config, action) => {
return (0, _withMod().withMod)(config, {
platform: 'ios',
mod: 'podfileProperties',
action
});
};
exports.withPodfileProperties = withPodfileProperties;
//# sourceMappingURL=ios-plugins.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,39 @@
import { ForwardedBaseModOptions } from './createBaseMod';
import { ExportedConfig, ModPlatform } from '../Plugin.types';
export declare function withDefaultBaseMods(config: ExportedConfig, props?: ForwardedBaseModOptions): ExportedConfig;
/**
* Get a prebuild config that safely evaluates mods without persisting any changes to the file system.
* Currently this only supports infoPlist, entitlements, androidManifest, strings, gradleProperties, and expoPlist mods.
* This plugin should be evaluated directly:
*/
export declare function withIntrospectionBaseMods(config: ExportedConfig, props?: ForwardedBaseModOptions): ExportedConfig;
/**
*
* @param projectRoot
* @param config
*/
export declare function compileModsAsync(config: ExportedConfig, props: {
projectRoot: string;
platforms?: ModPlatform[];
introspect?: boolean;
assertMissingModProviders?: boolean;
ignoreExistingNativeFiles?: boolean;
}): Promise<ExportedConfig>;
export declare function sortMods(commands: [string, any][], precedences: Record<string, number>): [string, any][];
/**
* A generic plugin compiler.
*
* @param config
*/
export declare function evalModsAsync(config: ExportedConfig, { projectRoot, introspect, platforms, assertMissingModProviders, ignoreExistingNativeFiles, }: {
projectRoot: string;
introspect?: boolean;
platforms?: ModPlatform[];
/**
* Throw errors when mods are missing providers.
* @default true
*/
assertMissingModProviders?: boolean;
/** Ignore any existing native files, only use the generated prebuild results. */
ignoreExistingNativeFiles?: boolean;
}): Promise<ExportedConfig>;

View File

@@ -0,0 +1,226 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.compileModsAsync = compileModsAsync;
exports.evalModsAsync = evalModsAsync;
exports.sortMods = sortMods;
exports.withDefaultBaseMods = withDefaultBaseMods;
exports.withIntrospectionBaseMods = withIntrospectionBaseMods;
function _debug() {
const data = _interopRequireDefault(require("debug"));
_debug = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _createBaseMod() {
const data = require("./createBaseMod");
_createBaseMod = function () {
return data;
};
return data;
}
function _withAndroidBaseMods() {
const data = require("./withAndroidBaseMods");
_withAndroidBaseMods = function () {
return data;
};
return data;
}
function _withIosBaseMods() {
const data = require("./withIosBaseMods");
_withIosBaseMods = function () {
return data;
};
return data;
}
function _Xcodeproj() {
const data = require("../ios/utils/Xcodeproj");
_Xcodeproj = function () {
return data;
};
return data;
}
function _errors() {
const data = require("../utils/errors");
_errors = function () {
return data;
};
return data;
}
function Warnings() {
const data = _interopRequireWildcard(require("../utils/warnings"));
Warnings = function () {
return data;
};
return data;
}
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const debug = (0, _debug().default)('expo:config-plugins:mod-compiler');
function withDefaultBaseMods(config, props = {}) {
config = (0, _withIosBaseMods().withIosBaseMods)(config, props);
config = (0, _withAndroidBaseMods().withAndroidBaseMods)(config, props);
return config;
}
/**
* Get a prebuild config that safely evaluates mods without persisting any changes to the file system.
* Currently this only supports infoPlist, entitlements, androidManifest, strings, gradleProperties, and expoPlist mods.
* This plugin should be evaluated directly:
*/
function withIntrospectionBaseMods(config, props = {}) {
config = (0, _withIosBaseMods().withIosBaseMods)(config, {
saveToInternal: true,
// This writing optimization can be skipped since we never write in introspection mode.
// Including empty mods will ensure that all mods get introspected.
skipEmptyMod: false,
...props
});
config = (0, _withAndroidBaseMods().withAndroidBaseMods)(config, {
saveToInternal: true,
skipEmptyMod: false,
...props
});
if (config.mods) {
// Remove all mods that don't have an introspection base mod, for instance `dangerous` mods.
for (const platform of Object.keys(config.mods)) {
// const platformPreserve = preserve[platform];
for (const key of Object.keys(config.mods[platform] || {})) {
// @ts-ignore
if (!config.mods[platform]?.[key]?.isIntrospective) {
debug(`removing non-idempotent mod: ${platform}.${key}`);
// @ts-ignore
delete config.mods[platform]?.[key];
}
}
}
}
return config;
}
/**
*
* @param projectRoot
* @param config
*/
async function compileModsAsync(config, props) {
if (props.introspect === true) {
config = withIntrospectionBaseMods(config);
} else {
config = withDefaultBaseMods(config);
}
return await evalModsAsync(config, props);
}
function sortMods(commands, precedences) {
const seen = new Set();
const dedupedCommands = commands.filter(([key]) => {
const duplicate = seen.has(key);
seen.add(key);
return !duplicate;
});
return dedupedCommands.sort(([keyA], [keyB]) => {
const precedenceA = precedences[keyA] || 0;
const precedenceB = precedences[keyB] || 0;
return precedenceA - precedenceB;
});
}
function getRawClone({
mods,
...config
}) {
// Configs should be fully serializable, so we can clone them without worrying about
// the mods.
return Object.freeze(JSON.parse(JSON.stringify(config)));
}
const precedences = {
ios: {
// dangerous runs first
dangerous: -2,
// run the XcodeProject mod second because many plugins attempt to read from it.
xcodeproj: -1,
// put the finalized mod at the last
finalized: 1
}
};
/**
* A generic plugin compiler.
*
* @param config
*/
async function evalModsAsync(config, {
projectRoot,
introspect,
platforms,
assertMissingModProviders,
ignoreExistingNativeFiles = false
}) {
const modRawConfig = getRawClone(config);
for (const [platformName, platform] of Object.entries(config.mods ?? {})) {
if (platforms && !platforms.includes(platformName)) {
debug(`skip platform: ${platformName}`);
continue;
}
let entries = Object.entries(platform);
if (entries.length) {
// Move dangerous item to the first position and finalized item to the last position if it exists.
// This ensures that all dangerous code runs first and finalized applies last.
entries = sortMods(entries, precedences[platformName] ?? {
dangerous: -1,
finalized: 1
});
debug(`run in order: ${entries.map(([name]) => name).join(', ')}`);
const platformProjectRoot = _path().default.join(projectRoot, platformName);
const projectName = platformName === 'ios' ? (0, _Xcodeproj().getHackyProjectName)(projectRoot, config) : undefined;
for (const [modName, mod] of entries) {
const modRequest = {
projectRoot,
projectName,
platformProjectRoot,
platform: platformName,
modName,
introspect: !!introspect,
ignoreExistingNativeFiles
};
if (!mod.isProvider) {
// In strict mode, throw an error.
const errorMessage = `Initial base modifier for "${platformName}.${modName}" is not a provider and therefore will not provide modResults to child mods`;
if (assertMissingModProviders !== false) {
throw new (_errors().PluginError)(errorMessage, 'MISSING_PROVIDER');
} else {
Warnings().addWarningForPlatform(platformName, `${platformName}.${modName}`, `Skipping: Initial base modifier for "${platformName}.${modName}" is not a provider and therefore will not provide modResults to child mods. This may be due to an outdated version of Expo CLI.`);
// In loose mode, just skip the mod entirely.
continue;
}
}
const results = await mod({
...config,
modResults: null,
modRequest,
modRawConfig
});
// Sanity check to help locate non compliant mods.
config = (0, _createBaseMod().assertModResults)(results, platformName, modName);
// @ts-ignore: `modResults` is added for modifications
delete config.modResults;
// @ts-ignore: `modRequest` is added for modifications
delete config.modRequest;
// @ts-ignore: `modRawConfig` is added for modifications
delete config.modRawConfig;
}
}
}
return config;
}
//# sourceMappingURL=mod-compiler.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,40 @@
import { ForwardedBaseModOptions } from './createBaseMod';
import { ExportedConfig } from '../Plugin.types';
import { Manifest, Paths, Properties, Resources } from '../android';
import { AndroidManifest } from '../android/Manifest';
export declare function sortAndroidManifest(obj: AndroidManifest): Manifest.AndroidManifest;
declare const defaultProviders: {
dangerous: import("./createBaseMod").BaseModProviderMethods<unknown, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
finalized: import("./createBaseMod").BaseModProviderMethods<unknown, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
manifest: import("./createBaseMod").BaseModProviderMethods<Manifest.AndroidManifest, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
gradleProperties: import("./createBaseMod").BaseModProviderMethods<Properties.PropertiesItem[], Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
strings: import("./createBaseMod").BaseModProviderMethods<Resources.ResourceXML, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
colors: import("./createBaseMod").BaseModProviderMethods<Resources.ResourceXML, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
colorsNight: import("./createBaseMod").BaseModProviderMethods<Resources.ResourceXML, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
styles: import("./createBaseMod").BaseModProviderMethods<Resources.ResourceXML, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
projectBuildGradle: import("./createBaseMod").BaseModProviderMethods<Paths.GradleProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
settingsGradle: import("./createBaseMod").BaseModProviderMethods<Paths.GradleProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
appBuildGradle: import("./createBaseMod").BaseModProviderMethods<Paths.GradleProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
mainActivity: import("./createBaseMod").BaseModProviderMethods<Paths.ApplicationProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
mainApplication: import("./createBaseMod").BaseModProviderMethods<Paths.ApplicationProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
};
type AndroidDefaultProviders = typeof defaultProviders;
export declare function withAndroidBaseMods(config: ExportedConfig, { providers, ...props }?: ForwardedBaseModOptions & {
providers?: Partial<AndroidDefaultProviders>;
}): ExportedConfig;
export declare function getAndroidModFileProviders(): {
dangerous: import("./createBaseMod").BaseModProviderMethods<unknown, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
finalized: import("./createBaseMod").BaseModProviderMethods<unknown, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
manifest: import("./createBaseMod").BaseModProviderMethods<Manifest.AndroidManifest, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
gradleProperties: import("./createBaseMod").BaseModProviderMethods<Properties.PropertiesItem[], Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
strings: import("./createBaseMod").BaseModProviderMethods<Resources.ResourceXML, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
colors: import("./createBaseMod").BaseModProviderMethods<Resources.ResourceXML, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
colorsNight: import("./createBaseMod").BaseModProviderMethods<Resources.ResourceXML, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
styles: import("./createBaseMod").BaseModProviderMethods<Resources.ResourceXML, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
projectBuildGradle: import("./createBaseMod").BaseModProviderMethods<Paths.GradleProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
settingsGradle: import("./createBaseMod").BaseModProviderMethods<Paths.GradleProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
appBuildGradle: import("./createBaseMod").BaseModProviderMethods<Paths.GradleProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
mainActivity: import("./createBaseMod").BaseModProviderMethods<Paths.ApplicationProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
mainApplication: import("./createBaseMod").BaseModProviderMethods<Paths.ApplicationProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
};
export {};

View File

@@ -0,0 +1,509 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getAndroidModFileProviders = getAndroidModFileProviders;
exports.sortAndroidManifest = sortAndroidManifest;
exports.withAndroidBaseMods = withAndroidBaseMods;
function _fs() {
const data = require("fs");
_fs = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _createBaseMod() {
const data = require("./createBaseMod");
_createBaseMod = function () {
return data;
};
return data;
}
function _android() {
const data = require("../android");
_android = function () {
return data;
};
return data;
}
function _XML() {
const data = require("../utils/XML");
_XML = function () {
return data;
};
return data;
}
function _sortObject() {
const data = require("../utils/sortObject");
_sortObject = function () {
return data;
};
return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const {
readFile,
writeFile
} = _fs().promises;
function getAndroidManifestTemplate(config) {
// Keep in sync with https://github.com/expo/expo/blob/main/templates/expo-template-bare-minimum/android/app/src/main/AndroidManifest.xml
// TODO: Read from remote template when possible
return (0, _XML().parseXMLAsync)(`
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="${config.android?.package ?? 'com.placeholder.appid'}">
<uses-permission android:name="android.permission.INTERNET"/>
<!-- OPTIONAL PERMISSIONS, REMOVE WHATEVER YOU DO NOT NEED -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<!-- These require runtime permissions on M -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:replace="android:maxSdkVersion"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" tools:replace="android:maxSdkVersion"/>
<!-- END OPTIONAL PERMISSIONS -->
<queries>
<!-- Support checking for http(s) links via the Linking API -->
<intent>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
</intent>
</queries>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="false" android:theme="@style/AppTheme" android:supportsRtl="true">
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode|smallestScreenSize" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
`);
}
function sortAndroidManifest(obj) {
if (obj.manifest) {
// Reverse sort so application is last and permissions are first
obj.manifest = (0, _sortObject().sortObject)(obj.manifest, _sortObject().reverseSortString);
if (Array.isArray(obj.manifest['uses-permission'])) {
// Sort permissions alphabetically
obj.manifest['uses-permission'].sort((a, b) => {
if (a.$['android:name'] < b.$['android:name']) return -1;
if (a.$['android:name'] > b.$['android:name']) return 1;
return 0;
});
}
if (Array.isArray(obj.manifest.application)) {
// reverse sort applications so activity is towards the end and meta-data is towards the front.
obj.manifest.application = obj.manifest.application.map(application => {
application = (0, _sortObject().sortObjWithOrder)(application, ['meta-data', 'service', 'activity']);
if (Array.isArray(application['meta-data'])) {
// Sort metadata alphabetically
application['meta-data'].sort((a, b) => {
if (a.$['android:name'] < b.$['android:name']) return -1;
if (a.$['android:name'] > b.$['android:name']) return 1;
return 0;
});
}
return application;
});
}
}
return obj;
}
const defaultProviders = {
dangerous: (0, _createBaseMod().provider)({
getFilePath() {
return '';
},
async read() {
return {
filePath: '',
modResults: {}
};
},
async write() {}
}),
finalized: (0, _createBaseMod().provider)({
getFilePath() {
return '';
},
async read() {
return {
filePath: '',
modResults: {}
};
},
async write() {}
}),
// Append a rule to supply gradle.properties data to mods on `mods.android.gradleProperties`
manifest: (0, _createBaseMod().provider)({
isIntrospective: true,
getFilePath({
modRequest: {
platformProjectRoot
}
}) {
return _path().default.join(platformProjectRoot, 'app/src/main/AndroidManifest.xml');
},
async read(filePath, config) {
try {
return await _android().Manifest.readAndroidManifestAsync(filePath);
} catch (error) {
if (!config.modRequest.introspect) {
throw error;
}
}
return await getAndroidManifestTemplate(config);
},
async write(filePath, {
modResults,
modRequest: {
introspect
}
}) {
if (introspect) return;
await _android().Manifest.writeAndroidManifestAsync(filePath, sortAndroidManifest(modResults));
}
}),
// Append a rule to supply gradle.properties data to mods on `mods.android.gradleProperties`
gradleProperties: (0, _createBaseMod().provider)({
isIntrospective: true,
getFilePath({
modRequest: {
platformProjectRoot
}
}) {
return _path().default.join(platformProjectRoot, 'gradle.properties');
},
async read(filePath, config) {
try {
return await _android().Properties.parsePropertiesFile(await readFile(filePath, 'utf8'));
} catch (error) {
if (!config.modRequest.introspect) {
throw error;
}
}
return [];
},
async write(filePath, {
modResults,
modRequest: {
introspect
}
}) {
if (introspect) return;
await writeFile(filePath, _android().Properties.propertiesListToString(modResults));
}
}),
// Append a rule to supply strings.xml data to mods on `mods.android.strings`
strings: (0, _createBaseMod().provider)({
isIntrospective: true,
async getFilePath({
modRequest: {
projectRoot,
introspect
}
}) {
try {
return await _android().Strings.getProjectStringsXMLPathAsync(projectRoot);
} catch (error) {
if (!introspect) {
throw error;
}
}
return '';
},
async read(filePath, config) {
try {
return await _android().Resources.readResourcesXMLAsync({
path: filePath
});
} catch (error) {
if (!config.modRequest.introspect) {
throw error;
}
}
return {
resources: {}
};
},
async write(filePath, {
modResults,
modRequest: {
introspect
}
}) {
if (introspect) return;
await (0, _XML().writeXMLAsync)({
path: filePath,
xml: modResults
});
}
}),
colors: (0, _createBaseMod().provider)({
isIntrospective: true,
async getFilePath({
modRequest: {
projectRoot,
introspect
}
}) {
try {
return await _android().Colors.getProjectColorsXMLPathAsync(projectRoot);
} catch (error) {
if (!introspect) {
throw error;
}
}
return '';
},
async read(filePath, {
modRequest: {
introspect
}
}) {
try {
return await _android().Resources.readResourcesXMLAsync({
path: filePath
});
} catch (error) {
if (!introspect) {
throw error;
}
}
return {
resources: {}
};
},
async write(filePath, {
modResults,
modRequest: {
introspect
}
}) {
if (introspect) return;
await (0, _XML().writeXMLAsync)({
path: filePath,
xml: modResults
});
}
}),
colorsNight: (0, _createBaseMod().provider)({
isIntrospective: true,
async getFilePath({
modRequest: {
projectRoot,
introspect
}
}) {
try {
return await _android().Colors.getProjectColorsXMLPathAsync(projectRoot, {
kind: 'values-night'
});
} catch (error) {
if (!introspect) {
throw error;
}
}
return '';
},
async read(filePath, config) {
try {
return await _android().Resources.readResourcesXMLAsync({
path: filePath
});
} catch (error) {
if (!config.modRequest.introspect) {
throw error;
}
}
return {
resources: {}
};
},
async write(filePath, {
modResults,
modRequest: {
introspect
}
}) {
if (introspect) return;
await (0, _XML().writeXMLAsync)({
path: filePath,
xml: modResults
});
}
}),
styles: (0, _createBaseMod().provider)({
isIntrospective: true,
async getFilePath({
modRequest: {
projectRoot,
introspect
}
}) {
try {
return await _android().Styles.getProjectStylesXMLPathAsync(projectRoot);
} catch (error) {
if (!introspect) {
throw error;
}
}
return '';
},
async read(filePath, config) {
let styles = {
resources: {}
};
try {
// Adds support for `tools:x`
styles = await _android().Resources.readResourcesXMLAsync({
path: filePath,
fallback: `<?xml version="1.0" encoding="utf-8"?><resources xmlns:tools="http://schemas.android.com/tools"></resources>`
});
} catch (error) {
if (!config.modRequest.introspect) {
throw error;
}
}
// Ensure support for tools is added...
if (!styles.resources.$) {
styles.resources.$ = {};
}
if (!styles.resources.$?.['xmlns:tools']) {
styles.resources.$['xmlns:tools'] = 'http://schemas.android.com/tools';
}
return styles;
},
async write(filePath, {
modResults,
modRequest: {
introspect
}
}) {
if (introspect) return;
await (0, _XML().writeXMLAsync)({
path: filePath,
xml: modResults
});
}
}),
projectBuildGradle: (0, _createBaseMod().provider)({
getFilePath({
modRequest: {
projectRoot
}
}) {
return _android().Paths.getProjectBuildGradleFilePath(projectRoot);
},
async read(filePath) {
return _android().Paths.getFileInfo(filePath);
},
async write(filePath, {
modResults: {
contents
}
}) {
await writeFile(filePath, contents);
}
}),
settingsGradle: (0, _createBaseMod().provider)({
getFilePath({
modRequest: {
projectRoot
}
}) {
return _android().Paths.getSettingsGradleFilePath(projectRoot);
},
async read(filePath) {
return _android().Paths.getFileInfo(filePath);
},
async write(filePath, {
modResults: {
contents
}
}) {
await writeFile(filePath, contents);
}
}),
appBuildGradle: (0, _createBaseMod().provider)({
getFilePath({
modRequest: {
projectRoot
}
}) {
return _android().Paths.getAppBuildGradleFilePath(projectRoot);
},
async read(filePath) {
return _android().Paths.getFileInfo(filePath);
},
async write(filePath, {
modResults: {
contents
}
}) {
await writeFile(filePath, contents);
}
}),
mainActivity: (0, _createBaseMod().provider)({
getFilePath({
modRequest: {
projectRoot
}
}) {
return _android().Paths.getProjectFilePath(projectRoot, 'MainActivity');
},
async read(filePath) {
return _android().Paths.getFileInfo(filePath);
},
async write(filePath, {
modResults: {
contents
}
}) {
await writeFile(filePath, contents);
}
}),
mainApplication: (0, _createBaseMod().provider)({
getFilePath({
modRequest: {
projectRoot
}
}) {
return _android().Paths.getProjectFilePath(projectRoot, 'MainApplication');
},
async read(filePath) {
return _android().Paths.getFileInfo(filePath);
},
async write(filePath, {
modResults: {
contents
}
}) {
await writeFile(filePath, contents);
}
})
};
function withAndroidBaseMods(config, {
providers,
...props
} = {}) {
return (0, _createBaseMod().withGeneratedBaseMods)(config, {
...props,
platform: 'android',
providers: providers ?? getAndroidModFileProviders()
});
}
function getAndroidModFileProviders() {
return defaultProviders;
}
//# sourceMappingURL=withAndroidBaseMods.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
import { ConfigPlugin, Mod, ModPlatform } from '../Plugin.types';
/**
* Mods that don't modify any data, all unresolved functionality is performed inside a dangerous mod.
* All dangerous mods run first before other mods.
*
* @param config
* @param platform
* @param action
*/
export declare const withDangerousMod: ConfigPlugin<[ModPlatform, Mod<unknown>]>;

View File

@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.withDangerousMod = void 0;
function _withMod() {
const data = require("./withMod");
_withMod = function () {
return data;
};
return data;
}
/**
* Mods that don't modify any data, all unresolved functionality is performed inside a dangerous mod.
* All dangerous mods run first before other mods.
*
* @param config
* @param platform
* @param action
*/
const withDangerousMod = (config, [platform, action]) => {
return (0, _withMod().withMod)(config, {
platform,
mod: 'dangerous',
action
});
};
exports.withDangerousMod = withDangerousMod;
//# sourceMappingURL=withDangerousMod.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"withDangerousMod.js","names":["_withMod","data","require","withDangerousMod","config","platform","action","withMod","mod","exports"],"sources":["../../src/plugins/withDangerousMod.ts"],"sourcesContent":["import { withMod } from './withMod';\nimport { ConfigPlugin, Mod, ModPlatform } from '../Plugin.types';\n\n/**\n * Mods that don't modify any data, all unresolved functionality is performed inside a dangerous mod.\n * All dangerous mods run first before other mods.\n *\n * @param config\n * @param platform\n * @param action\n */\nexport const withDangerousMod: ConfigPlugin<[ModPlatform, Mod<unknown>]> = (\n config,\n [platform, action]\n) => {\n return withMod(config, {\n platform,\n mod: 'dangerous',\n action,\n });\n};\n"],"mappings":";;;;;;AAAA,SAAAA,SAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,QAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,gBAA2D,GAAGA,CACzEC,MAAM,EACN,CAACC,QAAQ,EAAEC,MAAM,CAAC,KACf;EACH,OAAO,IAAAC,kBAAO,EAACH,MAAM,EAAE;IACrBC,QAAQ;IACRG,GAAG,EAAE,WAAW;IAChBF;EACF,CAAC,CAAC;AACJ,CAAC;AAACG,OAAA,CAAAN,gBAAA,GAAAA,gBAAA","ignoreList":[]}

View File

@@ -0,0 +1,10 @@
import { ConfigPlugin, Mod, ModPlatform } from '../Plugin.types';
/**
* Mods that don't modify any data, all unresolved functionality is performed inside a finalized mod.
* All finalized mods run after all the other mods.
*
* @param config
* @param platform
* @param action
*/
export declare const withFinalizedMod: ConfigPlugin<[ModPlatform, Mod<unknown>]>;

View File

@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.withFinalizedMod = void 0;
function _withMod() {
const data = require("./withMod");
_withMod = function () {
return data;
};
return data;
}
/**
* Mods that don't modify any data, all unresolved functionality is performed inside a finalized mod.
* All finalized mods run after all the other mods.
*
* @param config
* @param platform
* @param action
*/
const withFinalizedMod = (config, [platform, action]) => {
return (0, _withMod().withMod)(config, {
platform,
mod: 'finalized',
action
});
};
exports.withFinalizedMod = withFinalizedMod;
//# sourceMappingURL=withFinalizedMod.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"withFinalizedMod.js","names":["_withMod","data","require","withFinalizedMod","config","platform","action","withMod","mod","exports"],"sources":["../../src/plugins/withFinalizedMod.ts"],"sourcesContent":["import { withMod } from './withMod';\nimport { ConfigPlugin, Mod, ModPlatform } from '../Plugin.types';\n\n/**\n * Mods that don't modify any data, all unresolved functionality is performed inside a finalized mod.\n * All finalized mods run after all the other mods.\n *\n * @param config\n * @param platform\n * @param action\n */\nexport const withFinalizedMod: ConfigPlugin<[ModPlatform, Mod<unknown>]> = (\n config,\n [platform, action]\n) => {\n return withMod(config, {\n platform,\n mod: 'finalized',\n action,\n });\n};\n"],"mappings":";;;;;;AAAA,SAAAA,SAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,QAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,gBAA2D,GAAGA,CACzEC,MAAM,EACN,CAACC,QAAQ,EAAEC,MAAM,CAAC,KACf;EACH,OAAO,IAAAC,kBAAO,EAACH,MAAM,EAAE;IACrBC,QAAQ;IACRG,GAAG,EAAE,WAAW;IAChBF;EACF,CAAC,CAAC;AACJ,CAAC;AAACG,OAAA,CAAAN,gBAAA,GAAAA,gBAAA","ignoreList":[]}

View File

@@ -0,0 +1,33 @@
import { JSONObject, JSONValue } from '@expo/json-file';
import xcode from 'xcode';
import { ForwardedBaseModOptions } from './createBaseMod';
import { ExportedConfig } from '../Plugin.types';
import { Paths } from '../ios';
import { InfoPlist } from '../ios/IosConfig.types';
declare const defaultProviders: {
dangerous: import("./createBaseMod").BaseModProviderMethods<unknown, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
finalized: import("./createBaseMod").BaseModProviderMethods<unknown, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
appDelegate: import("./createBaseMod").BaseModProviderMethods<Paths.AppDelegateProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
expoPlist: import("./createBaseMod").BaseModProviderMethods<JSONObject, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
xcodeproj: import("./createBaseMod").BaseModProviderMethods<xcode.XcodeProject, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
infoPlist: import("./createBaseMod").BaseModProviderMethods<InfoPlist, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
entitlements: import("./createBaseMod").BaseModProviderMethods<JSONObject, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
podfile: import("./createBaseMod").BaseModProviderMethods<Paths.PodfileProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
podfileProperties: import("./createBaseMod").BaseModProviderMethods<Record<string, JSONValue>, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
};
type IosDefaultProviders = typeof defaultProviders;
export declare function withIosBaseMods(config: ExportedConfig, { providers, ...props }?: ForwardedBaseModOptions & {
providers?: Partial<IosDefaultProviders>;
}): ExportedConfig;
export declare function getIosModFileProviders(): {
dangerous: import("./createBaseMod").BaseModProviderMethods<unknown, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
finalized: import("./createBaseMod").BaseModProviderMethods<unknown, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
appDelegate: import("./createBaseMod").BaseModProviderMethods<Paths.AppDelegateProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
expoPlist: import("./createBaseMod").BaseModProviderMethods<JSONObject, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
xcodeproj: import("./createBaseMod").BaseModProviderMethods<xcode.XcodeProject, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
infoPlist: import("./createBaseMod").BaseModProviderMethods<InfoPlist, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
entitlements: import("./createBaseMod").BaseModProviderMethods<JSONObject, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
podfile: import("./createBaseMod").BaseModProviderMethods<Paths.PodfileProjectFile, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
podfileProperties: import("./createBaseMod").BaseModProviderMethods<Record<string, JSONValue>, Partial<Pick<import("./withMod").BaseModOptions, "skipEmptyMod" | "saveToInternal">>>;
};
export {};

View File

@@ -0,0 +1,443 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getIosModFileProviders = getIosModFileProviders;
exports.withIosBaseMods = withIosBaseMods;
function _jsonFile() {
const data = _interopRequireDefault(require("@expo/json-file"));
_jsonFile = function () {
return data;
};
return data;
}
function _plist() {
const data = _interopRequireDefault(require("@expo/plist"));
_plist = function () {
return data;
};
return data;
}
function _assert() {
const data = _interopRequireDefault(require("assert"));
_assert = function () {
return data;
};
return data;
}
function _fs() {
const data = _interopRequireWildcard(require("fs"));
_fs = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _xcode() {
const data = _interopRequireDefault(require("xcode"));
_xcode = function () {
return data;
};
return data;
}
function _createBaseMod() {
const data = require("./createBaseMod");
_createBaseMod = function () {
return data;
};
return data;
}
function _ios() {
const data = require("../ios");
_ios = function () {
return data;
};
return data;
}
function _Entitlements() {
const data = require("../ios/Entitlements");
_Entitlements = function () {
return data;
};
return data;
}
function _Xcodeproj() {
const data = require("../ios/utils/Xcodeproj");
_Xcodeproj = function () {
return data;
};
return data;
}
function _getInfoPlistPath() {
const data = require("../ios/utils/getInfoPlistPath");
_getInfoPlistPath = function () {
return data;
};
return data;
}
function _modules() {
const data = require("../utils/modules");
_modules = function () {
return data;
};
return data;
}
function _sortObject() {
const data = require("../utils/sortObject");
_sortObject = function () {
return data;
};
return data;
}
function _warnings() {
const data = require("../utils/warnings");
_warnings = function () {
return data;
};
return data;
}
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const {
readFile,
writeFile
} = _fs().promises;
function getEntitlementsPlistTemplate() {
// TODO: Fetch the versioned template file if possible
return {};
}
function getInfoPlistTemplate() {
// TODO: Fetch the versioned template file if possible
return {
CFBundleDevelopmentRegion: '$(DEVELOPMENT_LANGUAGE)',
CFBundleExecutable: '$(EXECUTABLE_NAME)',
CFBundleIdentifier: '$(PRODUCT_BUNDLE_IDENTIFIER)',
CFBundleName: '$(PRODUCT_NAME)',
CFBundlePackageType: '$(PRODUCT_BUNDLE_PACKAGE_TYPE)',
CFBundleInfoDictionaryVersion: '6.0',
CFBundleSignature: '????',
LSMinimumSystemVersion: '12.0',
LSRequiresIPhoneOS: true,
NSAppTransportSecurity: {
NSAllowsArbitraryLoads: true,
NSExceptionDomains: {
localhost: {
NSExceptionAllowsInsecureHTTPLoads: true
}
}
},
UILaunchStoryboardName: 'SplashScreen',
UIRequiredDeviceCapabilities: ['armv7'],
UIViewControllerBasedStatusBarAppearance: false,
UIStatusBarStyle: 'UIStatusBarStyleDefault',
CADisableMinimumFrameDurationOnPhone: true
};
}
const defaultProviders = {
dangerous: (0, _createBaseMod().provider)({
getFilePath() {
return '';
},
async read() {
return {};
},
async write() {}
}),
finalized: (0, _createBaseMod().provider)({
getFilePath() {
return '';
},
async read() {
return {};
},
async write() {}
}),
// Append a rule to supply AppDelegate data to mods on `mods.ios.appDelegate`
appDelegate: (0, _createBaseMod().provider)({
getFilePath({
modRequest: {
projectRoot
}
}) {
// TODO: Get application AppDelegate file from pbxproj.
return _ios().Paths.getAppDelegateFilePath(projectRoot);
},
async read(filePath) {
return _ios().Paths.getFileInfo(filePath);
},
async write(filePath, {
modResults: {
contents
}
}) {
await writeFile(filePath, contents);
}
}),
// Append a rule to supply Expo.plist data to mods on `mods.ios.expoPlist`
expoPlist: (0, _createBaseMod().provider)({
isIntrospective: true,
getFilePath({
modRequest: {
platformProjectRoot,
projectName
}
}) {
const supportingDirectory = _path().default.join(platformProjectRoot, projectName, 'Supporting');
return _path().default.resolve(supportingDirectory, 'Expo.plist');
},
async read(filePath, {
modRequest: {
introspect
}
}) {
try {
return _plist().default.parse(await readFile(filePath, 'utf8'));
} catch (error) {
if (introspect) {
return {};
}
throw error;
}
},
async write(filePath, {
modResults,
modRequest: {
introspect
}
}) {
if (introspect) {
return;
}
await writeFile(filePath, _plist().default.build((0, _sortObject().sortObject)(modResults)));
}
}),
// Append a rule to supply .xcodeproj data to mods on `mods.ios.xcodeproj`
xcodeproj: (0, _createBaseMod().provider)({
getFilePath({
modRequest: {
projectRoot
}
}) {
return _ios().Paths.getPBXProjectPath(projectRoot);
},
async read(filePath) {
const project = _xcode().default.project(filePath);
project.parseSync();
return project;
},
async write(filePath, {
modResults
}) {
await writeFile(filePath, modResults.writeSync());
}
}),
// Append a rule to supply Info.plist data to mods on `mods.ios.infoPlist`
infoPlist: (0, _createBaseMod().provider)({
isIntrospective: true,
async getFilePath(config) {
let project = null;
try {
project = (0, _Xcodeproj().getPbxproj)(config.modRequest.projectRoot);
} catch {
// noop
}
// Only check / warn if a project actually exists, this'll provide
// more accurate warning messages for users in managed projects.
if (project) {
const infoPlistBuildProperty = (0, _getInfoPlistPath().getInfoPlistPathFromPbxproj)(project);
if (infoPlistBuildProperty) {
//: [root]/myapp/ios/MyApp/Info.plist
const infoPlistPath = _path().default.join(
//: myapp/ios
config.modRequest.platformProjectRoot,
//: MyApp/Info.plist
infoPlistBuildProperty);
if ((0, _modules().fileExists)(infoPlistPath)) {
return infoPlistPath;
}
(0, _warnings().addWarningIOS)('mods.ios.infoPlist', `Info.plist file linked to Xcode project does not exist: ${infoPlistPath}`);
} else {
(0, _warnings().addWarningIOS)('mods.ios.infoPlist', 'Failed to find Info.plist linked to Xcode project.');
}
}
try {
// Fallback on glob...
return await _ios().Paths.getInfoPlistPath(config.modRequest.projectRoot);
} catch (error) {
if (config.modRequest.introspect) {
// fallback to an empty string in introspection mode.
return '';
}
throw error;
}
},
async read(filePath, config) {
// Apply all of the Info.plist values to the expo.ios.infoPlist object
// TODO: Remove this in favor of just overwriting the Info.plist with the Expo object. This will enable people to actually remove values.
if (!config.ios) config.ios = {};
if (!config.ios.infoPlist) config.ios.infoPlist = {};
let modResults;
try {
const contents = await readFile(filePath, 'utf8');
(0, _assert().default)(contents, 'Info.plist is empty');
modResults = _plist().default.parse(contents);
} catch (error) {
// Throw errors in introspection mode.
if (!config.modRequest.introspect) {
throw error;
}
// Fallback to using the infoPlist object from the Expo config.
modResults = getInfoPlistTemplate();
}
config.ios.infoPlist = {
...(modResults || {}),
...config.ios.infoPlist
};
return config.ios.infoPlist;
},
async write(filePath, config) {
// Update the contents of the static infoPlist object
if (!config.ios) {
config.ios = {};
}
config.ios.infoPlist = config.modResults;
// Return early without writing, in introspection mode.
if (config.modRequest.introspect) {
return;
}
await writeFile(filePath, _plist().default.build((0, _sortObject().sortObject)(config.modResults)));
}
}),
// Append a rule to supply .entitlements data to mods on `mods.ios.entitlements`
entitlements: (0, _createBaseMod().provider)({
isIntrospective: true,
async getFilePath(config) {
try {
(0, _Entitlements().ensureApplicationTargetEntitlementsFileConfigured)(config.modRequest.projectRoot);
return _ios().Entitlements.getEntitlementsPath(config.modRequest.projectRoot) ?? '';
} catch (error) {
if (config.modRequest.introspect) {
// fallback to an empty string in introspection mode.
return '';
}
throw error;
}
},
async read(filePath, config) {
let modResults;
try {
if (!config.modRequest.ignoreExistingNativeFiles && _fs().default.existsSync(filePath)) {
const contents = await readFile(filePath, 'utf8');
(0, _assert().default)(contents, 'Entitlements plist is empty');
modResults = _plist().default.parse(contents);
} else {
modResults = getEntitlementsPlistTemplate();
}
} catch (error) {
// Throw errors in introspection mode.
if (!config.modRequest.introspect) {
throw error;
}
// Fallback to using the template file.
modResults = getEntitlementsPlistTemplate();
}
// Apply all of the .entitlements values to the expo.ios.entitlements object
// TODO: Remove this in favor of just overwriting the .entitlements with the Expo object. This will enable people to actually remove values.
if (!config.ios) config.ios = {};
if (!config.ios.entitlements) config.ios.entitlements = {};
config.ios.entitlements = {
...(modResults || {}),
...config.ios.entitlements
};
return config.ios.entitlements;
},
async write(filePath, config) {
// Update the contents of the static entitlements object
if (!config.ios) {
config.ios = {};
}
config.ios.entitlements = config.modResults;
// Return early without writing, in introspection mode.
if (config.modRequest.introspect) {
return;
}
await writeFile(filePath, _plist().default.build((0, _sortObject().sortObject)(config.modResults)));
}
}),
podfile: (0, _createBaseMod().provider)({
getFilePath({
modRequest: {
projectRoot
}
}) {
return _ios().Paths.getPodfilePath(projectRoot);
},
// @ts-expect-error
async read(filePath) {
// Note(cedric): this file is ruby, which is a 1-value subset of AppleLanguage and fails the type check
return _ios().Paths.getFileInfo(filePath);
},
async write(filePath, {
modResults: {
contents
}
}) {
await writeFile(filePath, contents);
}
}),
// Append a rule to supply Podfile.properties.json data to mods on `mods.ios.podfileProperties`
podfileProperties: (0, _createBaseMod().provider)({
isIntrospective: true,
getFilePath({
modRequest: {
platformProjectRoot
}
}) {
return _path().default.resolve(platformProjectRoot, 'Podfile.properties.json');
},
async read(filePath) {
let results = {};
try {
results = await _jsonFile().default.readAsync(filePath);
} catch {}
return results;
},
async write(filePath, {
modResults,
modRequest: {
introspect
}
}) {
if (introspect) {
return;
}
await _jsonFile().default.writeAsync(filePath, modResults);
}
})
};
function withIosBaseMods(config, {
providers,
...props
} = {}) {
return (0, _createBaseMod().withGeneratedBaseMods)(config, {
...props,
platform: 'ios',
providers: providers ?? getIosModFileProviders()
});
}
function getIosModFileProviders() {
return defaultProviders;
}
//# sourceMappingURL=withIosBaseMods.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,46 @@
import { ExportedConfig, Mod, ModPlatform } from '../Plugin.types';
export type BaseModOptions = {
/** Officially supports `'ios' | 'android'` (`ModPlatform`). Arbitrary strings are supported for adding out-of-tree platforms. */
platform: ModPlatform & string;
mod: string;
isProvider?: boolean;
skipEmptyMod?: boolean;
saveToInternal?: boolean;
/**
* If the mod supports introspection, and avoids making any filesystem modifications during compilation.
* By enabling, this mod, and all of its descendants will be run in introspection mode.
* This should only be used for static files like JSON or XML, and not for application files that require regexes,
* or complex static files that require other files to be generated like Xcode `.pbxproj`.
*/
isIntrospective?: boolean;
};
/**
* Plugin to intercept execution of a given `mod` with the given `action`.
* If an action was already set on the given `config` config for `mod`, then it
* will be provided to the `action` as `nextMod` when it's evaluated, otherwise
* `nextMod` will be an identity function.
*
* @param config exported config
* @param platform platform to target (ios or android)
* @param mod name of the platform function to intercept
* @param skipEmptyMod should skip running the action if there is no existing mod to intercept
* @param saveToInternal should save the results to `_internal.modResults`, only enable this when the results are pure JSON.
* @param isProvider should provide data up to the other mods.
* @param action method to run on the mod when the config is compiled
*/
export declare function withBaseMod<T>(config: ExportedConfig, { platform, mod, action, skipEmptyMod, isProvider, isIntrospective, saveToInternal, }: BaseModOptions & {
action: Mod<T>;
}): ExportedConfig;
/**
* Plugin to extend a mod function in the plugins config.
*
* @param config exported config
* @param platform platform to target (ios or android)
* @param mod name of the platform function to extend
* @param action method to run on the mod when the config is compiled
*/
export declare function withMod<T>(config: ExportedConfig, { platform, mod, action, }: {
platform: ModPlatform;
mod: string;
action: Mod<T>;
}): ExportedConfig;

View File

@@ -0,0 +1,208 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.withBaseMod = withBaseMod;
exports.withMod = withMod;
function _chalk() {
const data = _interopRequireDefault(require("chalk"));
_chalk = function () {
return data;
};
return data;
}
function _getenv() {
const data = require("getenv");
_getenv = function () {
return data;
};
return data;
}
function _errors() {
const data = require("../utils/errors");
_errors = function () {
return data;
};
return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const EXPO_DEBUG = (0, _getenv().boolish)('EXPO_DEBUG', false);
/**
* Plugin to intercept execution of a given `mod` with the given `action`.
* If an action was already set on the given `config` config for `mod`, then it
* will be provided to the `action` as `nextMod` when it's evaluated, otherwise
* `nextMod` will be an identity function.
*
* @param config exported config
* @param platform platform to target (ios or android)
* @param mod name of the platform function to intercept
* @param skipEmptyMod should skip running the action if there is no existing mod to intercept
* @param saveToInternal should save the results to `_internal.modResults`, only enable this when the results are pure JSON.
* @param isProvider should provide data up to the other mods.
* @param action method to run on the mod when the config is compiled
*/
function withBaseMod(config, {
platform,
mod,
action,
skipEmptyMod,
isProvider,
isIntrospective,
saveToInternal
}) {
if (!config.mods) {
config.mods = {};
}
if (!config.mods[platform]) {
config.mods[platform] = {};
}
let interceptedMod = config.mods[platform][mod];
// No existing mod to intercept
if (!interceptedMod) {
if (skipEmptyMod) {
// Skip running the action
return config;
}
// Use a noop mod and continue
const noopMod = config => config;
interceptedMod = noopMod;
}
// Create a stack trace for debugging ahead of time
let debugTrace = '';
// Use the possibly user defined value. Otherwise fallback to the env variable.
// We support the env variable because user mods won't have _internal defined in time.
const isDebug = config._internal?.isDebug ?? EXPO_DEBUG;
if (isDebug) {
// Get a stack trace via the Error API
const stack = new Error().stack;
// Format the stack trace to create the debug log
debugTrace = getDebugPluginStackFromStackTrace(stack);
const modStack = _chalk().default.bold(`${platform}.${mod}`);
debugTrace = `${modStack}: ${debugTrace}`;
}
// Prevent adding multiple providers to a mod.
// Base mods that provide files ignore any incoming modResults and therefore shouldn't have provider mods as parents.
if (interceptedMod.isProvider) {
if (isProvider) {
throw new (_errors().PluginError)(`Cannot set provider mod for "${platform}.${mod}" because another is already being used.`, 'CONFLICTING_PROVIDER');
} else {
throw new (_errors().PluginError)(`Cannot add mod to "${platform}.${mod}" because the provider has already been added. Provider must be the last mod added.`, 'INVALID_MOD_ORDER');
}
}
async function interceptingMod({
modRequest,
...config
}) {
if (isDebug) {
// In debug mod, log the plugin stack in the order which they were invoked
console.log(debugTrace);
}
const results = await action({
...config,
modRequest: {
...modRequest,
nextMod: interceptedMod
}
});
if (saveToInternal) {
saveToInternalObject(results, platform, mod, results.modResults);
}
return results;
}
// Ensure this base mod is registered as the provider.
interceptingMod.isProvider = isProvider;
if (isIntrospective) {
// Register the mode as idempotent so introspection doesn't remove it.
interceptingMod.isIntrospective = isIntrospective;
}
config.mods[platform][mod] = interceptingMod;
return config;
}
function saveToInternalObject(config, platformName, modName, results) {
if (!config._internal) config._internal = {};
if (!config._internal.modResults) config._internal.modResults = {};
if (!config._internal.modResults[platformName]) config._internal.modResults[platformName] = {};
config._internal.modResults[platformName][modName] = results;
}
function getDebugPluginStackFromStackTrace(stacktrace) {
if (!stacktrace) {
return '';
}
const treeStackLines = [];
for (const line of stacktrace.split('\n')) {
const [first, second] = line.trim().split(' ');
if (first === 'at') {
treeStackLines.push(second);
}
}
const plugins = treeStackLines.map(first => {
// Match the first part of the stack trace against the plugin naming convention
// "with" followed by a capital letter.
return first?.match(/^(\bwith[A-Z].*?\b)/)?.[1]?.trim() ?? first?.match(/\.(\bwith[A-Z].*?\b)/)?.[1]?.trim() ?? null;
}).filter(Boolean).filter(plugin => {
// redundant as all debug logs are captured in withBaseMod
return !['withMod', 'withBaseMod', 'withExtendedMod'].includes(plugin);
});
const commonPlugins = ['withPlugins', 'withRunOnce', 'withStaticPlugin'];
return plugins.reverse().map((pluginName, index) => {
// Base mods indicate a logical section.
if (pluginName.includes('BaseMod')) {
pluginName = _chalk().default.bold(pluginName);
}
// highlight dangerous mods
if (pluginName.toLowerCase().includes('dangerous')) {
pluginName = _chalk().default.red(pluginName);
}
if (index === 0) {
return _chalk().default.blue(pluginName);
} else if (commonPlugins.includes(pluginName)) {
// Common mod names often clutter up the logs, dim them out
return _chalk().default.dim(pluginName);
}
return pluginName;
})
// Join the results:
// withAndroidExpoPlugins ➜ withPlugins ➜ withIcons ➜ withDangerousMod ➜ withMod
.join(' ➜ ');
}
/**
* Plugin to extend a mod function in the plugins config.
*
* @param config exported config
* @param platform platform to target (ios or android)
* @param mod name of the platform function to extend
* @param action method to run on the mod when the config is compiled
*/
function withMod(config, {
platform,
mod,
action
}) {
return withBaseMod(config, {
platform,
mod,
isProvider: false,
async action({
modRequest: {
nextMod,
...modRequest
},
modResults,
...config
}) {
const results = await action({
modRequest,
modResults: modResults,
...config
});
return nextMod(results);
}
});
}
//# sourceMappingURL=withMod.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
import { ConfigPlugin, StaticPlugin } from '../Plugin.types';
/**
* Resolves a list of plugins.
*
* @param config exported config
* @param plugins list of config plugins to apply to the exported config
*/
export declare const withPlugins: ConfigPlugin<(StaticPlugin | ConfigPlugin | string)[]>;

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.withPlugins = void 0;
function _assert() {
const data = _interopRequireDefault(require("assert"));
_assert = function () {
return data;
};
return data;
}
function _withStaticPlugin() {
const data = require("./withStaticPlugin");
_withStaticPlugin = function () {
return data;
};
return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* Resolves a list of plugins.
*
* @param config exported config
* @param plugins list of config plugins to apply to the exported config
*/
const withPlugins = (config, plugins) => {
(0, _assert().default)(Array.isArray(plugins), 'withPlugins expected a valid array of plugins or plugin module paths');
return plugins.reduce((prev, plugin) => (0, _withStaticPlugin().withStaticPlugin)(prev, {
plugin
}), config);
};
exports.withPlugins = withPlugins;
//# sourceMappingURL=withPlugins.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"withPlugins.js","names":["_assert","data","_interopRequireDefault","require","_withStaticPlugin","e","__esModule","default","withPlugins","config","plugins","assert","Array","isArray","reduce","prev","plugin","withStaticPlugin","exports"],"sources":["../../src/plugins/withPlugins.ts"],"sourcesContent":["import assert from 'assert';\n\nimport { withStaticPlugin } from './withStaticPlugin';\nimport { ConfigPlugin, StaticPlugin } from '../Plugin.types';\n\n/**\n * Resolves a list of plugins.\n *\n * @param config exported config\n * @param plugins list of config plugins to apply to the exported config\n */\nexport const withPlugins: ConfigPlugin<(StaticPlugin | ConfigPlugin | string)[]> = (\n config,\n plugins\n) => {\n assert(\n Array.isArray(plugins),\n 'withPlugins expected a valid array of plugins or plugin module paths'\n );\n return plugins.reduce((prev, plugin) => withStaticPlugin(prev, { plugin }), config);\n};\n"],"mappings":";;;;;;AAAA,SAAAA,QAAA;EAAA,MAAAC,IAAA,GAAAC,sBAAA,CAAAC,OAAA;EAAAH,OAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,SAAAG,kBAAA;EAAA,MAAAH,IAAA,GAAAE,OAAA;EAAAC,iBAAA,YAAAA,CAAA;IAAA,OAAAH,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAAsD,SAAAC,uBAAAG,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAGtD;AACA;AACA;AACA;AACA;AACA;AACO,MAAMG,WAAmE,GAAGA,CACjFC,MAAM,EACNC,OAAO,KACJ;EACH,IAAAC,iBAAM,EACJC,KAAK,CAACC,OAAO,CAACH,OAAO,CAAC,EACtB,sEACF,CAAC;EACD,OAAOA,OAAO,CAACI,MAAM,CAAC,CAACC,IAAI,EAAEC,MAAM,KAAK,IAAAC,oCAAgB,EAACF,IAAI,EAAE;IAAEC;EAAO,CAAC,CAAC,EAAEP,MAAM,CAAC;AACrF,CAAC;AAACS,OAAA,CAAAV,WAAA,GAAAA,WAAA","ignoreList":[]}

View File

@@ -0,0 +1,20 @@
import { ConfigPlugin } from '../Plugin.types';
import { PluginHistoryItem } from '../utils/history';
/**
* Prevents the same plugin from being run twice.
* Used for migrating from unversioned expo config plugins to versioned plugins.
*
* @param config
* @param name
*/
export declare const withRunOnce: ConfigPlugin<{
plugin: ConfigPlugin<void>;
name: PluginHistoryItem['name'];
version?: PluginHistoryItem['version'];
}>;
/**
* Helper method for creating mods from existing config functions.
*
* @param action
*/
export declare function createRunOncePlugin<T>(plugin: ConfigPlugin<T>, name: string, version?: string): ConfigPlugin<T>;

View File

@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createRunOncePlugin = createRunOncePlugin;
exports.withRunOnce = void 0;
function _history() {
const data = require("../utils/history");
_history = function () {
return data;
};
return data;
}
/**
* Prevents the same plugin from being run twice.
* Used for migrating from unversioned expo config plugins to versioned plugins.
*
* @param config
* @param name
*/
const withRunOnce = (config, {
plugin,
name,
version
}) => {
// Detect if a plugin has already been run on this config.
if ((0, _history().getHistoryItem)(config, name)) {
return config;
}
// Push the history item so duplicates cannot be run.
config = (0, _history().addHistoryItem)(config, {
name,
version
});
return plugin(config);
};
/**
* Helper method for creating mods from existing config functions.
*
* @param action
*/
exports.withRunOnce = withRunOnce;
function createRunOncePlugin(plugin, name, version) {
return (config, props) => {
return withRunOnce(config, {
plugin: config => plugin(config, props),
name,
version
});
};
}
//# sourceMappingURL=withRunOnce.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"withRunOnce.js","names":["_history","data","require","withRunOnce","config","plugin","name","version","getHistoryItem","addHistoryItem","exports","createRunOncePlugin","props"],"sources":["../../src/plugins/withRunOnce.ts"],"sourcesContent":["import { ConfigPlugin } from '../Plugin.types';\nimport { addHistoryItem, getHistoryItem, PluginHistoryItem } from '../utils/history';\n\n/**\n * Prevents the same plugin from being run twice.\n * Used for migrating from unversioned expo config plugins to versioned plugins.\n *\n * @param config\n * @param name\n */\nexport const withRunOnce: ConfigPlugin<{\n plugin: ConfigPlugin<void>;\n name: PluginHistoryItem['name'];\n version?: PluginHistoryItem['version'];\n}> = (config, { plugin, name, version }) => {\n // Detect if a plugin has already been run on this config.\n if (getHistoryItem(config, name)) {\n return config;\n }\n\n // Push the history item so duplicates cannot be run.\n config = addHistoryItem(config, { name, version });\n\n return plugin(config);\n};\n\n/**\n * Helper method for creating mods from existing config functions.\n *\n * @param action\n */\nexport function createRunOncePlugin<T>(\n plugin: ConfigPlugin<T>,\n name: string,\n version?: string\n): ConfigPlugin<T> {\n return (config, props) => {\n return withRunOnce(config, { plugin: (config) => plugin(config, props), name, version });\n };\n}\n"],"mappings":";;;;;;;AACA,SAAAA,SAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,QAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,MAAME,WAIX,GAAGA,CAACC,MAAM,EAAE;EAAEC,MAAM;EAAEC,IAAI;EAAEC;AAAQ,CAAC,KAAK;EAC1C;EACA,IAAI,IAAAC,yBAAc,EAACJ,MAAM,EAAEE,IAAI,CAAC,EAAE;IAChC,OAAOF,MAAM;EACf;;EAEA;EACAA,MAAM,GAAG,IAAAK,yBAAc,EAACL,MAAM,EAAE;IAAEE,IAAI;IAAEC;EAAQ,CAAC,CAAC;EAElD,OAAOF,MAAM,CAACD,MAAM,CAAC;AACvB,CAAC;;AAED;AACA;AACA;AACA;AACA;AAJAM,OAAA,CAAAP,WAAA,GAAAA,WAAA;AAKO,SAASQ,mBAAmBA,CACjCN,MAAuB,EACvBC,IAAY,EACZC,OAAgB,EACC;EACjB,OAAO,CAACH,MAAM,EAAEQ,KAAK,KAAK;IACxB,OAAOT,WAAW,CAACC,MAAM,EAAE;MAAEC,MAAM,EAAGD,MAAM,IAAKC,MAAM,CAACD,MAAM,EAAEQ,KAAK,CAAC;MAAEN,IAAI;MAAEC;IAAQ,CAAC,CAAC;EAC1F,CAAC;AACH","ignoreList":[]}

View File

@@ -0,0 +1,17 @@
import { ConfigPlugin, StaticPlugin } from '../Plugin.types';
/**
* Resolves static module plugin and potentially falls back on a provided plugin if the module cannot be resolved
*
* @param config
* @param fallback Plugin with `_resolverError` explaining why the module couldn't be used
* @param projectRoot optional project root, fallback to _internal.projectRoot. Used for testing.
* @param _isLegacyPlugin Used to suppress errors thrown by plugins that are applied automatically
*/
export declare const withStaticPlugin: ConfigPlugin<{
plugin: StaticPlugin | ConfigPlugin | string;
fallback?: ConfigPlugin<{
_resolverError: Error;
} & any>;
projectRoot?: string;
_isLegacyPlugin?: boolean;
}>;

View File

@@ -0,0 +1,150 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.withStaticPlugin = void 0;
function _assert() {
const data = _interopRequireDefault(require("assert"));
_assert = function () {
return data;
};
return data;
}
function _getenv() {
const data = require("getenv");
_getenv = function () {
return data;
};
return data;
}
function _errors() {
const data = require("../utils/errors");
_errors = function () {
return data;
};
return data;
}
function _pluginResolver() {
const data = require("../utils/plugin-resolver");
_pluginResolver = function () {
return data;
};
return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const EXPO_DEBUG = (0, _getenv().boolish)('EXPO_DEBUG', false);
// Show all error info related to plugin resolution.
const EXPO_CONFIG_PLUGIN_VERBOSE_ERRORS = (0, _getenv().boolish)('EXPO_CONFIG_PLUGIN_VERBOSE_ERRORS', false);
// Force using the fallback unversioned plugin instead of a local versioned copy,
// this should only be used for testing the CLI.
const EXPO_USE_UNVERSIONED_PLUGINS = (0, _getenv().boolish)('EXPO_USE_UNVERSIONED_PLUGINS', false);
function isModuleMissingError(name, error) {
// @ts-ignore
if (['MODULE_NOT_FOUND', 'PLUGIN_NOT_FOUND'].includes(error.code)) {
return true;
}
return error.message.includes(`Cannot find module '${name}'`);
}
function isUnexpectedTokenError(error) {
if (error instanceof SyntaxError || error instanceof _errors().PluginError && error.code === 'INVALID_PLUGIN_IMPORT') {
return (
// These are the most common errors that'll be thrown when a package isn't transpiled correctly.
!!error.message.match(/Unexpected token/) || !!error.message.match(/Cannot use import statement/)
);
}
return false;
}
/**
* Resolves static module plugin and potentially falls back on a provided plugin if the module cannot be resolved
*
* @param config
* @param fallback Plugin with `_resolverError` explaining why the module couldn't be used
* @param projectRoot optional project root, fallback to _internal.projectRoot. Used for testing.
* @param _isLegacyPlugin Used to suppress errors thrown by plugins that are applied automatically
*/
const withStaticPlugin = (config, props) => {
let projectRoot = props.projectRoot;
if (!projectRoot) {
projectRoot = config._internal?.projectRoot;
(0, _pluginResolver().assertInternalProjectRoot)(projectRoot);
}
let [pluginResolve, pluginProps] = (0, _pluginResolver().normalizeStaticPlugin)(props.plugin);
// Ensure no one uses this property by accident.
(0, _assert().default)(!pluginProps?._resolverError, `Plugin property '_resolverError' is a reserved property of \`withStaticPlugin\``);
let withPlugin;
if (
// Function was provided, no need to resolve: [withPlugin, {}]
typeof pluginResolve === 'function') {
withPlugin = pluginResolve;
} else if (typeof pluginResolve === 'string') {
try {
// Resolve and evaluate plugins.
withPlugin = (0, _pluginResolver().resolveConfigPluginFunction)(projectRoot, pluginResolve);
// Only force if the project has the versioned plugin, otherwise use default behavior.
// This helps see which plugins are being skipped.
if (EXPO_USE_UNVERSIONED_PLUGINS && !!withPlugin && !!props._isLegacyPlugin && !!props.fallback) {
console.log(`Force "${pluginResolve}" to unversioned plugin`);
withPlugin = props.fallback;
}
} catch (error) {
if (EXPO_DEBUG) {
if (EXPO_CONFIG_PLUGIN_VERBOSE_ERRORS) {
// Log the error in debug mode for plugins with fallbacks (like the Expo managed plugins).
console.log(`Error resolving plugin "${pluginResolve}"`);
console.log(error);
console.log();
} else {
const shouldMuteWarning = props._isLegacyPlugin && (isModuleMissingError(pluginResolve, error) || isUnexpectedTokenError(error));
if (!shouldMuteWarning) {
if (isModuleMissingError(pluginResolve, error)) {
// Prevent causing log spew for basic resolution errors.
console.log(`Could not find plugin "${pluginResolve}"`);
} else {
// Log the error in debug mode for plugins with fallbacks (like the Expo managed plugins).
console.log(`Error resolving plugin "${pluginResolve}"`);
console.log(error);
console.log();
}
}
}
}
// TODO: Maybe allow for `PluginError`s to be thrown so external plugins can assert invalid options.
// If the static module failed to resolve, attempt to use a fallback.
// This enables support for built-in plugins with versioned variations living in other packages.
if (props.fallback) {
if (!pluginProps) pluginProps = {};
// Pass this to the fallback plugin for potential warnings about needing to install a versioned package.
pluginProps._resolverError = error;
withPlugin = props.fallback;
} else {
// If no fallback, throw the resolution error.
throw error;
}
}
} else {
if (typeof pluginResolve === 'object') {
throw new (_errors().PluginError)(`Plugin is an unexpected object, with keys: "${Object.keys(pluginResolve).join(', ')}".\n
If you tried to provide parameters to a config plugin, make sure the plugin configuration is wrapped by square brackets. Ex:\n
[
"some-config-plugin",
{
"someParam": "someValue"
}
]
\n
See the package documentation on how to correctly configure the plugin.`, 'INVALID_PLUGIN_TYPE');
}
throw new (_errors().PluginError)(`Plugin is an unexpected type: ${typeof pluginResolve}. See the package documentation on how to correctly configure the plugin.`, 'INVALID_PLUGIN_TYPE');
}
// Execute the plugin.
config = withPlugin(config, pluginProps);
return config;
};
exports.withStaticPlugin = withStaticPlugin;
//# sourceMappingURL=withStaticPlugin.js.map

File diff suppressed because one or more lines are too long