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,6 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export declare const withAllowBackup: import("..").ConfigPlugin;
export declare function getAllowBackup(config: Pick<ExpoConfig, 'android'>): boolean;
export declare function setAllowBackup(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function getAllowBackupFromManifest(androidManifest: AndroidManifest): boolean | null;

View File

@@ -0,0 +1,45 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getAllowBackup = getAllowBackup;
exports.getAllowBackupFromManifest = getAllowBackupFromManifest;
exports.setAllowBackup = setAllowBackup;
exports.withAllowBackup = void 0;
function _Manifest() {
const data = require("./Manifest");
_Manifest = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
const withAllowBackup = exports.withAllowBackup = (0, _androidPlugins().createAndroidManifestPlugin)(setAllowBackup, 'withAllowBackup');
function getAllowBackup(config) {
// Defaults to true.
// https://docs.expo.dev/versions/latest/config/app/#allowbackup
return config.android?.allowBackup ?? true;
}
function setAllowBackup(config, androidManifest) {
const allowBackup = getAllowBackup(config);
const mainApplication = (0, _Manifest().getMainApplication)(androidManifest);
if (mainApplication?.$) {
mainApplication.$['android:allowBackup'] = String(allowBackup);
}
return androidManifest;
}
function getAllowBackupFromManifest(androidManifest) {
const mainApplication = (0, _Manifest().getMainApplication)(androidManifest);
if (mainApplication?.$) {
return String(mainApplication.$['android:allowBackup']) === 'true';
}
return null;
}
//# sourceMappingURL=AllowBackup.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"AllowBackup.js","names":["_Manifest","data","require","_androidPlugins","withAllowBackup","exports","createAndroidManifestPlugin","setAllowBackup","getAllowBackup","config","android","allowBackup","androidManifest","mainApplication","getMainApplication","$","String","getAllowBackupFromManifest"],"sources":["../../src/android/AllowBackup.ts"],"sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { AndroidManifest, getMainApplication, StringBoolean } from './Manifest';\nimport { createAndroidManifestPlugin } from '../plugins/android-plugins';\n\nexport const withAllowBackup = createAndroidManifestPlugin(setAllowBackup, 'withAllowBackup');\n\nexport function getAllowBackup(config: Pick<ExpoConfig, 'android'>) {\n // Defaults to true.\n // https://docs.expo.dev/versions/latest/config/app/#allowbackup\n return config.android?.allowBackup ?? true;\n}\n\nexport function setAllowBackup(\n config: Pick<ExpoConfig, 'android'>,\n androidManifest: AndroidManifest\n) {\n const allowBackup = getAllowBackup(config);\n\n const mainApplication = getMainApplication(androidManifest);\n if (mainApplication?.$) {\n mainApplication.$['android:allowBackup'] = String(allowBackup) as StringBoolean;\n }\n\n return androidManifest;\n}\n\nexport function getAllowBackupFromManifest(androidManifest: AndroidManifest): boolean | null {\n const mainApplication = getMainApplication(androidManifest);\n\n if (mainApplication?.$) {\n return String(mainApplication.$['android:allowBackup']) === 'true';\n }\n\n return null;\n}\n"],"mappings":";;;;;;;;;AAEA,SAAAA,UAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,SAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAE,gBAAA;EAAA,MAAAF,IAAA,GAAAC,OAAA;EAAAC,eAAA,YAAAA,CAAA;IAAA,OAAAF,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEO,MAAMG,eAAe,GAAAC,OAAA,CAAAD,eAAA,GAAG,IAAAE,6CAA2B,EAACC,cAAc,EAAE,iBAAiB,CAAC;AAEtF,SAASC,cAAcA,CAACC,MAAmC,EAAE;EAClE;EACA;EACA,OAAOA,MAAM,CAACC,OAAO,EAAEC,WAAW,IAAI,IAAI;AAC5C;AAEO,SAASJ,cAAcA,CAC5BE,MAAmC,EACnCG,eAAgC,EAChC;EACA,MAAMD,WAAW,GAAGH,cAAc,CAACC,MAAM,CAAC;EAE1C,MAAMI,eAAe,GAAG,IAAAC,8BAAkB,EAACF,eAAe,CAAC;EAC3D,IAAIC,eAAe,EAAEE,CAAC,EAAE;IACtBF,eAAe,CAACE,CAAC,CAAC,qBAAqB,CAAC,GAAGC,MAAM,CAACL,WAAW,CAAkB;EACjF;EAEA,OAAOC,eAAe;AACxB;AAEO,SAASK,0BAA0BA,CAACL,eAAgC,EAAkB;EAC3F,MAAMC,eAAe,GAAG,IAAAC,8BAAkB,EAACF,eAAe,CAAC;EAE3D,IAAIC,eAAe,EAAEE,CAAC,EAAE;IACtB,OAAOC,MAAM,CAACH,eAAe,CAACE,CAAC,CAAC,qBAAqB,CAAC,CAAC,KAAK,MAAM;EACpE;EAEA,OAAO,IAAI;AACb","ignoreList":[]}

View File

@@ -0,0 +1,28 @@
import type { ExpoConfig } from '@expo/config-types';
import type { PropertiesItem } from './Properties';
import type { ConfigPlugin } from '../Plugin.types';
import { BuildPropertiesConfig, ConfigToPropertyRuleType } from '../utils/BuildProperties.types';
/**
* Creates a `withGradleProperties` config-plugin based on given config to property mapping rules.
*
* The factory supports two modes from generic type inference
* ```ts
* // config-plugin without `props`, it will implicitly use the expo config as source config.
* createBuildGradlePropsConfigPlugin<ExpoConfig>(): ConfigPlugin<void>;
*
* // config-plugin with a parameter `props: CustomType`, it will use the `props` as source config.
* createBuildGradlePropsConfigPlugin<CustomType>(): ConfigPlugin<CustomType>;
* ```
*
* @param configToPropertyRules config to property mapping rules
* @param name the config plugin name
*/
export declare function createBuildGradlePropsConfigPlugin<SourceConfigType extends BuildPropertiesConfig>(configToPropertyRules: ConfigToPropertyRuleType<SourceConfigType>[], name?: string): ConfigPlugin<SourceConfigType extends ExpoConfig ? void : SourceConfigType>;
/**
* A config-plugin to update `android/gradle.properties` from the `jsEngine` in expo config
*/
export declare const withJsEngineGradleProps: ConfigPlugin<void>;
export declare function updateAndroidBuildPropertiesFromConfig<SourceConfigType extends BuildPropertiesConfig>(config: SourceConfigType, gradleProperties: PropertiesItem[], configToPropertyRules: ConfigToPropertyRuleType<SourceConfigType>[]): PropertiesItem[];
export declare function updateAndroidBuildProperty(gradleProperties: PropertiesItem[], name: string, value: string | null | undefined, options?: {
removePropWhenValueIsNull?: boolean;
}): PropertiesItem[];

View File

@@ -0,0 +1,93 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createBuildGradlePropsConfigPlugin = createBuildGradlePropsConfigPlugin;
exports.updateAndroidBuildPropertiesFromConfig = updateAndroidBuildPropertiesFromConfig;
exports.updateAndroidBuildProperty = updateAndroidBuildProperty;
exports.withJsEngineGradleProps = void 0;
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
/**
* Creates a `withGradleProperties` config-plugin based on given config to property mapping rules.
*
* The factory supports two modes from generic type inference
* ```ts
* // config-plugin without `props`, it will implicitly use the expo config as source config.
* createBuildGradlePropsConfigPlugin<ExpoConfig>(): ConfigPlugin<void>;
*
* // config-plugin with a parameter `props: CustomType`, it will use the `props` as source config.
* createBuildGradlePropsConfigPlugin<CustomType>(): ConfigPlugin<CustomType>;
* ```
*
* @param configToPropertyRules config to property mapping rules
* @param name the config plugin name
*/
function createBuildGradlePropsConfigPlugin(configToPropertyRules, name) {
const withUnknown = (config, sourceConfig) => (0, _androidPlugins().withGradleProperties)(config, config => {
config.modResults = updateAndroidBuildPropertiesFromConfig(sourceConfig ?? config, config.modResults, configToPropertyRules);
return config;
});
if (name) {
Object.defineProperty(withUnknown, 'name', {
value: name
});
}
return withUnknown;
}
/**
* A config-plugin to update `android/gradle.properties` from the `jsEngine` in expo config
*/
const withJsEngineGradleProps = exports.withJsEngineGradleProps = createBuildGradlePropsConfigPlugin([{
propName: 'hermesEnabled',
propValueGetter: config => ((config.android?.jsEngine ?? config.jsEngine ?? 'hermes') === 'hermes').toString()
}], 'withJsEngineGradleProps');
function updateAndroidBuildPropertiesFromConfig(config, gradleProperties, configToPropertyRules) {
for (const configToProperty of configToPropertyRules) {
const value = configToProperty.propValueGetter(config);
updateAndroidBuildProperty(gradleProperties, configToProperty.propName, value);
}
return gradleProperties;
}
function updateAndroidBuildProperty(gradleProperties, name, value, options) {
const oldPropIndex = gradleProperties.findIndex(prop => prop.type === 'property' && prop.key === name);
const oldProp = oldPropIndex >= 0 ? gradleProperties[oldPropIndex] : null;
if (value) {
// found the matched value, add or merge new property
const newProp = {
type: 'property',
key: name,
value
};
if (oldProp && oldProp.type === 'property') {
try {
const prevValue = JSON.parse(oldProp.value);
const newValue = JSON.parse(value);
if (Array.isArray(prevValue) && Array.isArray(newValue)) {
const prevArrayWithStringifiedValues = prevValue.map(v => JSON.stringify(v));
const newArrayWithStringifiedValues = newValue.map(v => JSON.stringify(v));
const mergedValues = [...new Set([...prevArrayWithStringifiedValues, ...newArrayWithStringifiedValues])].map(v => JSON.parse(v));
oldProp.value = JSON.stringify(mergedValues);
return gradleProperties;
}
} catch {}
oldProp.value = value;
return gradleProperties;
}
gradleProperties.push(newProp);
return gradleProperties;
}
if (options?.removePropWhenValueIsNull && oldPropIndex >= 0) {
gradleProperties.splice(oldPropIndex, 1);
return gradleProperties;
}
return gradleProperties;
}
//# sourceMappingURL=BuildProperties.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,29 @@
import { ResourceItemXML, ResourceKind, ResourceXML } from './Resources';
export declare function getProjectColorsXMLPathAsync(projectRoot: string, { kind }?: {
kind?: ResourceKind;
}): Promise<string>;
export declare function setColorItem(itemToAdd: ResourceItemXML, colorFileContentsJSON: ResourceXML): ResourceXML;
export declare function removeColorItem(named: string, contents: ResourceXML): ResourceXML;
/**
* Set or remove value in XML based on nullish factor of the `value` property.
*/
export declare function assignColorValue(xml: ResourceXML, { value, name, }: {
value?: string | null;
name: string;
}): ResourceXML;
/**
* Helper to convert a basic XML object into a simple k/v pair.
* `colors.xml` is a very basic XML file so this is pretty safe to do.
* Added for testing purposes.
*
* @param xml
* @returns
*/
export declare function getColorsAsObject(xml: ResourceXML): Record<string, string> | null;
/**
* Helper to convert a basic k/v object to a colors XML object.
*
* @param xml
* @returns
*/
export declare function getObjectAsColorsXml(obj: Record<string, string>): ResourceXML;

View File

@@ -0,0 +1,106 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.assignColorValue = assignColorValue;
exports.getColorsAsObject = getColorsAsObject;
exports.getObjectAsColorsXml = getObjectAsColorsXml;
exports.getProjectColorsXMLPathAsync = getProjectColorsXMLPathAsync;
exports.removeColorItem = removeColorItem;
exports.setColorItem = setColorItem;
function _Paths() {
const data = require("./Paths");
_Paths = function () {
return data;
};
return data;
}
function _Resources() {
const data = require("./Resources");
_Resources = function () {
return data;
};
return data;
}
function getProjectColorsXMLPathAsync(projectRoot, {
kind
} = {}) {
return (0, _Paths().getResourceXMLPathAsync)(projectRoot, {
kind,
name: 'colors'
});
}
function setColorItem(itemToAdd, colorFileContentsJSON) {
if (colorFileContentsJSON.resources?.color) {
const colorNameExists = colorFileContentsJSON.resources.color.filter(e => e.$.name === itemToAdd.$.name)[0];
if (colorNameExists) {
colorNameExists._ = itemToAdd._;
} else {
colorFileContentsJSON.resources.color.push(itemToAdd);
}
} else {
if (!colorFileContentsJSON.resources || typeof colorFileContentsJSON.resources === 'string') {
//file was empty and JSON is `{resources : ''}`
colorFileContentsJSON.resources = {};
}
colorFileContentsJSON.resources.color = [itemToAdd];
}
return colorFileContentsJSON;
}
function removeColorItem(named, contents) {
if (contents.resources?.color) {
const index = contents.resources.color.findIndex(e => e.$.name === named);
if (index > -1) {
// replace the previous value
contents.resources.color.splice(index, 1);
}
}
return contents;
}
/**
* Set or remove value in XML based on nullish factor of the `value` property.
*/
function assignColorValue(xml, {
value,
name
}) {
if (value) {
return setColorItem((0, _Resources().buildResourceItem)({
name,
value
}), xml);
}
return removeColorItem(name, xml);
}
/**
* Helper to convert a basic XML object into a simple k/v pair.
* `colors.xml` is a very basic XML file so this is pretty safe to do.
* Added for testing purposes.
*
* @param xml
* @returns
*/
function getColorsAsObject(xml) {
if (!xml?.resources?.color) {
return null;
}
return (0, _Resources().getResourceItemsAsObject)(xml.resources.color);
}
/**
* Helper to convert a basic k/v object to a colors XML object.
*
* @param xml
* @returns
*/
function getObjectAsColorsXml(obj) {
return {
resources: {
color: (0, _Resources().getObjectAsResourceItems)(obj)
}
};
}
//# sourceMappingURL=Colors.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
export declare function getEasBuildGradlePath(projectRoot: string): string;
export declare function configureEasBuildAsync(projectRoot: string): Promise<void>;
export declare function isEasBuildGradleConfiguredAsync(projectRoot: string): Promise<boolean>;

View File

@@ -0,0 +1,67 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.configureEasBuildAsync = configureEasBuildAsync;
exports.getEasBuildGradlePath = getEasBuildGradlePath;
exports.isEasBuildGradleConfiguredAsync = isEasBuildGradleConfiguredAsync;
function _fs() {
const data = _interopRequireDefault(require("fs"));
_fs = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _EasBuildGradleScript() {
const data = _interopRequireDefault(require("./EasBuildGradleScript"));
_EasBuildGradleScript = function () {
return data;
};
return data;
}
function Paths() {
const data = _interopRequireWildcard(require("./Paths"));
Paths = 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 APPLY_EAS_GRADLE = 'apply from: "./eas-build.gradle"';
function hasApplyLine(content, applyLine) {
return content.replace(/\r\n/g, '\n').split('\n')
// Check for both single and double quotes
.some(line => line === applyLine || line === applyLine.replace(/"/g, "'"));
}
function getEasBuildGradlePath(projectRoot) {
return _path().default.join(projectRoot, 'android', 'app', 'eas-build.gradle');
}
async function configureEasBuildAsync(projectRoot) {
const buildGradlePath = Paths().getAppBuildGradleFilePath(projectRoot);
const easGradlePath = getEasBuildGradlePath(projectRoot);
await _fs().default.promises.writeFile(easGradlePath, _EasBuildGradleScript().default);
const buildGradleContent = await _fs().default.promises.readFile(_path().default.join(buildGradlePath), 'utf8');
const hasEasGradleApply = hasApplyLine(buildGradleContent, APPLY_EAS_GRADLE);
if (!hasEasGradleApply) {
await _fs().default.promises.writeFile(buildGradlePath, `${buildGradleContent.trim()}\n${APPLY_EAS_GRADLE}\n`);
}
}
async function isEasBuildGradleConfiguredAsync(projectRoot) {
const buildGradlePath = Paths().getAppBuildGradleFilePath(projectRoot);
const easGradlePath = getEasBuildGradlePath(projectRoot);
const hasEasGradleFile = await _fs().default.existsSync(easGradlePath);
const buildGradleContent = await _fs().default.promises.readFile(_path().default.join(buildGradlePath), 'utf8');
const hasEasGradleApply = hasApplyLine(buildGradleContent, APPLY_EAS_GRADLE);
return hasEasGradleApply && hasEasGradleFile;
}
//# sourceMappingURL=EasBuild.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
declare const _default: "// Build integration with EAS\n\nimport java.nio.file.Paths\n\nandroid {\n signingConfigs {\n release {\n // This is necessary to avoid needing the user to define a release signing config manually\n // If no release config is defined, and this is not present, build for assembleRelease will crash\n }\n }\n\n buildTypes {\n release {\n // This is necessary to avoid needing the user to define a release build type manually\n }\n }\n}\n\ndef isEasBuildConfigured = false\n\ntasks.whenTaskAdded {\n def debug = gradle.startParameter.taskNames.any { it.toLowerCase().contains('debug') }\n\n if (debug) {\n return\n }\n\n // We only need to configure EAS build once\n if (isEasBuildConfigured) {\n return\n }\n\n isEasBuildConfigured = true;\n\n android.signingConfigs.release {\n def credentialsJson = rootProject.file(\"../credentials.json\");\n\n if (credentialsJson.exists()) {\n if (storeFile && !System.getenv(\"EAS_BUILD\")) {\n println(\"Path to release keystore file is already set, ignoring 'credentials.json'\")\n } else {\n try {\n def credentials = new groovy.json.JsonSlurper().parse(credentialsJson)\n def keystorePath = Paths.get(credentials.android.keystore.keystorePath);\n def storeFilePath = keystorePath.isAbsolute()\n ? keystorePath\n : rootProject.file(\"..\").toPath().resolve(keystorePath);\n\n storeFile storeFilePath.toFile()\n storePassword credentials.android.keystore.keystorePassword\n keyAlias credentials.android.keystore.keyAlias\n if (credentials.android.keystore.containsKey(\"keyPassword\")) {\n keyPassword credentials.android.keystore.keyPassword\n } else {\n // key password is required by Gradle, but PKCS keystores don't have one\n // using the keystore password seems to satisfy the requirement\n keyPassword credentials.android.keystore.keystorePassword\n }\n } catch (Exception e) {\n println(\"An error occurred while parsing 'credentials.json': \" + e.message)\n }\n }\n } else {\n if (storeFile == null) {\n println(\"Couldn't find a 'credentials.json' file, skipping release keystore configuration\")\n }\n }\n }\n\n android.buildTypes.release {\n signingConfig android.signingConfigs.release\n }\n}\n";
export default _default;

View File

@@ -0,0 +1,82 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _default = exports.default = `// Build integration with EAS
import java.nio.file.Paths
android {
signingConfigs {
release {
// This is necessary to avoid needing the user to define a release signing config manually
// If no release config is defined, and this is not present, build for assembleRelease will crash
}
}
buildTypes {
release {
// This is necessary to avoid needing the user to define a release build type manually
}
}
}
def isEasBuildConfigured = false
tasks.whenTaskAdded {
def debug = gradle.startParameter.taskNames.any { it.toLowerCase().contains('debug') }
if (debug) {
return
}
// We only need to configure EAS build once
if (isEasBuildConfigured) {
return
}
isEasBuildConfigured = true;
android.signingConfigs.release {
def credentialsJson = rootProject.file("../credentials.json");
if (credentialsJson.exists()) {
if (storeFile && !System.getenv("EAS_BUILD")) {
println("Path to release keystore file is already set, ignoring 'credentials.json'")
} else {
try {
def credentials = new groovy.json.JsonSlurper().parse(credentialsJson)
def keystorePath = Paths.get(credentials.android.keystore.keystorePath);
def storeFilePath = keystorePath.isAbsolute()
? keystorePath
: rootProject.file("..").toPath().resolve(keystorePath);
storeFile storeFilePath.toFile()
storePassword credentials.android.keystore.keystorePassword
keyAlias credentials.android.keystore.keyAlias
if (credentials.android.keystore.containsKey("keyPassword")) {
keyPassword credentials.android.keystore.keyPassword
} else {
// key password is required by Gradle, but PKCS keystores don't have one
// using the keystore password seems to satisfy the requirement
keyPassword credentials.android.keystore.keystorePassword
}
} catch (Exception e) {
println("An error occurred while parsing 'credentials.json': " + e.message)
}
}
} else {
if (storeFile == null) {
println("Couldn't find a 'credentials.json' file, skipping release keystore configuration")
}
}
}
android.buildTypes.release {
signingConfig android.signingConfigs.release
}
}
`;
//# sourceMappingURL=EasBuildGradleScript.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"EasBuildGradleScript.js","names":[],"sources":["../../src/android/EasBuildGradleScript.ts"],"sourcesContent":["export default `// Build integration with EAS\n\nimport java.nio.file.Paths\n\nandroid {\n signingConfigs {\n release {\n // This is necessary to avoid needing the user to define a release signing config manually\n // If no release config is defined, and this is not present, build for assembleRelease will crash\n }\n }\n\n buildTypes {\n release {\n // This is necessary to avoid needing the user to define a release build type manually\n }\n }\n}\n\ndef isEasBuildConfigured = false\n\ntasks.whenTaskAdded {\n def debug = gradle.startParameter.taskNames.any { it.toLowerCase().contains('debug') }\n\n if (debug) {\n return\n }\n\n // We only need to configure EAS build once\n if (isEasBuildConfigured) {\n return\n }\n\n isEasBuildConfigured = true;\n\n android.signingConfigs.release {\n def credentialsJson = rootProject.file(\"../credentials.json\");\n\n if (credentialsJson.exists()) {\n if (storeFile && !System.getenv(\"EAS_BUILD\")) {\n println(\"Path to release keystore file is already set, ignoring 'credentials.json'\")\n } else {\n try {\n def credentials = new groovy.json.JsonSlurper().parse(credentialsJson)\n def keystorePath = Paths.get(credentials.android.keystore.keystorePath);\n def storeFilePath = keystorePath.isAbsolute()\n ? keystorePath\n : rootProject.file(\"..\").toPath().resolve(keystorePath);\n\n storeFile storeFilePath.toFile()\n storePassword credentials.android.keystore.keystorePassword\n keyAlias credentials.android.keystore.keyAlias\n if (credentials.android.keystore.containsKey(\"keyPassword\")) {\n keyPassword credentials.android.keystore.keyPassword\n } else {\n // key password is required by Gradle, but PKCS keystores don't have one\n // using the keystore password seems to satisfy the requirement\n keyPassword credentials.android.keystore.keystorePassword\n }\n } catch (Exception e) {\n println(\"An error occurred while parsing 'credentials.json': \" + e.message)\n }\n }\n } else {\n if (storeFile == null) {\n println(\"Couldn't find a 'credentials.json' file, skipping release keystore configuration\")\n }\n }\n }\n\n android.buildTypes.release {\n signingConfig android.signingConfigs.release\n }\n}\n`;\n"],"mappings":";;;;;;iCAAe;AACf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC","ignoreList":[]}

View File

@@ -0,0 +1,5 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export declare const withGoogleMapsApiKey: import("..").ConfigPlugin;
export declare function getGoogleMapsApiKey(config: Pick<ExpoConfig, 'android'>): string | null;
export declare function setGoogleMapsApiKey(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): AndroidManifest;

View File

@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getGoogleMapsApiKey = getGoogleMapsApiKey;
exports.setGoogleMapsApiKey = setGoogleMapsApiKey;
exports.withGoogleMapsApiKey = void 0;
function _Manifest() {
const data = require("./Manifest");
_Manifest = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
const META_API_KEY = 'com.google.android.geo.API_KEY';
const LIB_HTTP = 'org.apache.http.legacy';
const withGoogleMapsApiKey = exports.withGoogleMapsApiKey = (0, _androidPlugins().createAndroidManifestPlugin)(setGoogleMapsApiKey, 'withGoogleMapsApiKey');
function getGoogleMapsApiKey(config) {
return config.android?.config?.googleMaps?.apiKey ?? null;
}
function setGoogleMapsApiKey(config, androidManifest) {
const apiKey = getGoogleMapsApiKey(config);
const mainApplication = (0, _Manifest().getMainApplicationOrThrow)(androidManifest);
if (apiKey) {
// If the item exists, add it back
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, META_API_KEY, apiKey);
(0, _Manifest().addUsesLibraryItemToMainApplication)(mainApplication, {
name: LIB_HTTP,
required: false
});
} else {
// Remove any existing item
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, META_API_KEY);
(0, _Manifest().removeUsesLibraryItemFromMainApplication)(mainApplication, LIB_HTTP);
}
return androidManifest;
}
//# sourceMappingURL=GoogleMapsApiKey.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"GoogleMapsApiKey.js","names":["_Manifest","data","require","_androidPlugins","META_API_KEY","LIB_HTTP","withGoogleMapsApiKey","exports","createAndroidManifestPlugin","setGoogleMapsApiKey","getGoogleMapsApiKey","config","android","googleMaps","apiKey","androidManifest","mainApplication","getMainApplicationOrThrow","addMetaDataItemToMainApplication","addUsesLibraryItemToMainApplication","name","required","removeMetaDataItemFromMainApplication","removeUsesLibraryItemFromMainApplication"],"sources":["../../src/android/GoogleMapsApiKey.ts"],"sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport {\n addMetaDataItemToMainApplication,\n addUsesLibraryItemToMainApplication,\n AndroidManifest,\n getMainApplicationOrThrow,\n removeMetaDataItemFromMainApplication,\n removeUsesLibraryItemFromMainApplication,\n} from './Manifest';\nimport { createAndroidManifestPlugin } from '../plugins/android-plugins';\n\nconst META_API_KEY = 'com.google.android.geo.API_KEY';\nconst LIB_HTTP = 'org.apache.http.legacy';\n\nexport const withGoogleMapsApiKey = createAndroidManifestPlugin(\n setGoogleMapsApiKey,\n 'withGoogleMapsApiKey'\n);\n\nexport function getGoogleMapsApiKey(config: Pick<ExpoConfig, 'android'>) {\n return config.android?.config?.googleMaps?.apiKey ?? null;\n}\n\nexport function setGoogleMapsApiKey(\n config: Pick<ExpoConfig, 'android'>,\n androidManifest: AndroidManifest\n) {\n const apiKey = getGoogleMapsApiKey(config);\n const mainApplication = getMainApplicationOrThrow(androidManifest);\n\n if (apiKey) {\n // If the item exists, add it back\n addMetaDataItemToMainApplication(mainApplication, META_API_KEY, apiKey);\n addUsesLibraryItemToMainApplication(mainApplication, {\n name: LIB_HTTP,\n required: false,\n });\n } else {\n // Remove any existing item\n removeMetaDataItemFromMainApplication(mainApplication, META_API_KEY);\n removeUsesLibraryItemFromMainApplication(mainApplication, LIB_HTTP);\n }\n\n return androidManifest;\n}\n"],"mappings":";;;;;;;;AAEA,SAAAA,UAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,SAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAQA,SAAAE,gBAAA;EAAA,MAAAF,IAAA,GAAAC,OAAA;EAAAC,eAAA,YAAAA,CAAA;IAAA,OAAAF,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,MAAMG,YAAY,GAAG,gCAAgC;AACrD,MAAMC,QAAQ,GAAG,wBAAwB;AAElC,MAAMC,oBAAoB,GAAAC,OAAA,CAAAD,oBAAA,GAAG,IAAAE,6CAA2B,EAC7DC,mBAAmB,EACnB,sBACF,CAAC;AAEM,SAASC,mBAAmBA,CAACC,MAAmC,EAAE;EACvE,OAAOA,MAAM,CAACC,OAAO,EAAED,MAAM,EAAEE,UAAU,EAAEC,MAAM,IAAI,IAAI;AAC3D;AAEO,SAASL,mBAAmBA,CACjCE,MAAmC,EACnCI,eAAgC,EAChC;EACA,MAAMD,MAAM,GAAGJ,mBAAmB,CAACC,MAAM,CAAC;EAC1C,MAAMK,eAAe,GAAG,IAAAC,qCAAyB,EAACF,eAAe,CAAC;EAElE,IAAID,MAAM,EAAE;IACV;IACA,IAAAI,4CAAgC,EAACF,eAAe,EAAEZ,YAAY,EAAEU,MAAM,CAAC;IACvE,IAAAK,+CAAmC,EAACH,eAAe,EAAE;MACnDI,IAAI,EAAEf,QAAQ;MACdgB,QAAQ,EAAE;IACZ,CAAC,CAAC;EACJ,CAAC,MAAM;IACL;IACA,IAAAC,iDAAqC,EAACN,eAAe,EAAEZ,YAAY,CAAC;IACpE,IAAAmB,oDAAwC,EAACP,eAAe,EAAEX,QAAQ,CAAC;EACrE;EAEA,OAAOU,eAAe;AACxB","ignoreList":[]}

View File

@@ -0,0 +1,17 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
export declare const withClassPath: ConfigPlugin;
export declare const withApplyPlugin: ConfigPlugin;
/**
* Add `google-services.json` to project
*/
export declare const withGoogleServicesFile: ConfigPlugin;
export declare function getGoogleServicesFilePath(config: Pick<ExpoConfig, 'android'>): string | null;
export declare function setGoogleServicesFile(config: Pick<ExpoConfig, 'android'>, projectRoot: string, targetPath?: string): Promise<boolean>;
/**
* Adding the Google Services plugin
* NOTE(brentvatne): string replacement is a fragile approach! we need a
* better solution than this.
*/
export declare function setClassPath(config: Pick<ExpoConfig, 'android'>, buildGradle: string): string;
export declare function applyPlugin(config: Pick<ExpoConfig, 'android'>, appBuildGradle: string): string;

View File

@@ -0,0 +1,138 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.applyPlugin = applyPlugin;
exports.getGoogleServicesFilePath = getGoogleServicesFilePath;
exports.setClassPath = setClassPath;
exports.setGoogleServicesFile = setGoogleServicesFile;
exports.withGoogleServicesFile = exports.withClassPath = exports.withApplyPlugin = void 0;
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
function _withDangerousMod() {
const data = require("../plugins/withDangerousMod");
_withDangerousMod = function () {
return data;
};
return data;
}
function _fs() {
const data = require("../utils/fs");
_fs = function () {
return data;
};
return data;
}
function _warnings() {
const data = require("../utils/warnings");
_warnings = function () {
return data;
};
return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const DEFAULT_TARGET_PATH = './android/app/google-services.json';
const googleServicesClassPath = 'com.google.gms:google-services';
const googleServicesPlugin = 'com.google.gms.google-services';
// NOTE(brentvatne): This may be annoying to keep up to date...
const googleServicesVersion = '4.4.1';
const withClassPath = config => {
return (0, _androidPlugins().withProjectBuildGradle)(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = setClassPath(config, config.modResults.contents);
} else {
(0, _warnings().addWarningAndroid)('android.googleServicesFile', `Cannot automatically configure project build.gradle if it's not groovy`);
}
return config;
});
};
exports.withClassPath = withClassPath;
const withApplyPlugin = config => {
return (0, _androidPlugins().withAppBuildGradle)(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = applyPlugin(config, config.modResults.contents);
} else {
(0, _warnings().addWarningAndroid)('android.googleServicesFile', `Cannot automatically configure app build.gradle if it's not groovy`);
}
return config;
});
};
/**
* Add `google-services.json` to project
*/
exports.withApplyPlugin = withApplyPlugin;
const withGoogleServicesFile = config => {
return (0, _withDangerousMod().withDangerousMod)(config, ['android', async config => {
await setGoogleServicesFile(config, config.modRequest.projectRoot);
return config;
}]);
};
exports.withGoogleServicesFile = withGoogleServicesFile;
function getGoogleServicesFilePath(config) {
return config.android?.googleServicesFile ?? null;
}
async function setGoogleServicesFile(config, projectRoot, targetPath = DEFAULT_TARGET_PATH) {
const partialSourcePath = getGoogleServicesFilePath(config);
if (!partialSourcePath) {
return false;
}
const completeSourcePath = _path().default.resolve(projectRoot, partialSourcePath);
const destinationPath = _path().default.resolve(projectRoot, targetPath);
try {
await (0, _fs().copyFilePathToPathAsync)(completeSourcePath, destinationPath);
} catch (e) {
console.log(e);
throw new Error(`Cannot copy google-services.json from ${completeSourcePath} to ${destinationPath}. Ensure the source and destination paths exist.`);
}
return true;
}
/**
* Adding the Google Services plugin
* NOTE(brentvatne): string replacement is a fragile approach! we need a
* better solution than this.
*/
function setClassPath(config, buildGradle) {
const googleServicesFile = getGoogleServicesFilePath(config);
if (!googleServicesFile) {
return buildGradle;
}
if (buildGradle.includes(googleServicesClassPath)) {
return buildGradle;
}
//
return buildGradle.replace(/dependencies\s?{/, `dependencies {
classpath '${googleServicesClassPath}:${googleServicesVersion}'`);
}
function applyPlugin(config, appBuildGradle) {
const googleServicesFile = getGoogleServicesFilePath(config);
if (!googleServicesFile) {
return appBuildGradle;
}
// Make sure the project does not have the plugin already
const pattern = new RegExp(`apply\\s+plugin:\\s+['"]${googleServicesPlugin}['"]`);
if (appBuildGradle.match(pattern)) {
return appBuildGradle;
}
// Add it to the end of the file
return appBuildGradle + `\napply plugin: '${googleServicesPlugin}'`;
}
//# sourceMappingURL=GoogleServices.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
import { Android, ExpoConfig } from '@expo/config-types';
import { AndroidManifest, ManifestIntentFilter } from './Manifest';
type AndroidIntentFilters = NonNullable<Android['intentFilters']>;
export declare const withAndroidIntentFilters: import("..").ConfigPlugin;
export declare function getIntentFilters(config: Pick<ExpoConfig, 'android'>): AndroidIntentFilters;
export declare function setAndroidIntentFilters(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): AndroidManifest;
export default function renderIntentFilters(intentFilters: AndroidIntentFilters): ManifestIntentFilter[];
export {};

View File

@@ -0,0 +1,83 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = renderIntentFilters;
exports.getIntentFilters = getIntentFilters;
exports.setAndroidIntentFilters = setAndroidIntentFilters;
exports.withAndroidIntentFilters = void 0;
function _Manifest() {
const data = require("./Manifest");
_Manifest = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
const GENERATED_TAG = 'data-generated';
const withAndroidIntentFilters = exports.withAndroidIntentFilters = (0, _androidPlugins().createAndroidManifestPlugin)(setAndroidIntentFilters, 'withAndroidIntentFilters');
function getIntentFilters(config) {
return config.android?.intentFilters ?? [];
}
function setAndroidIntentFilters(config, androidManifest) {
// Always ensure old tags are removed.
const mainActivity = (0, _Manifest().getMainActivityOrThrow)(androidManifest);
// Remove all generated tags from previous runs...
if (mainActivity['intent-filter']?.length) {
mainActivity['intent-filter'] = mainActivity['intent-filter'].filter(value => value.$?.[GENERATED_TAG] !== 'true');
}
const intentFilters = getIntentFilters(config);
if (!intentFilters.length) {
return androidManifest;
}
mainActivity['intent-filter'] = mainActivity['intent-filter']?.concat(renderIntentFilters(intentFilters));
return androidManifest;
}
function renderIntentFilters(intentFilters) {
return intentFilters.map(intentFilter => {
// <intent-filter>
return {
$: {
'android:autoVerify': intentFilter.autoVerify ? 'true' : undefined,
// Add a custom "generated" tag that we can query later to remove.
[GENERATED_TAG]: 'true'
},
action: [
// <action android:name="android.intent.action.VIEW"/>
{
$: {
'android:name': `android.intent.action.${intentFilter.action}`
}
}],
data: renderIntentFilterData(intentFilter.data),
category: renderIntentFilterCategory(intentFilter.category)
};
});
}
/** Like `<data android:scheme="exp"/>` */
function renderIntentFilterData(data) {
return (Array.isArray(data) ? data : [data]).filter(Boolean).map(datum => ({
$: Object.entries(datum ?? {}).reduce((prev, [key, value]) => ({
...prev,
[`android:${key}`]: value
}), {})
}));
}
/** Like `<category android:name="android.intent.category.DEFAULT"/>` */
function renderIntentFilterCategory(category) {
return (Array.isArray(category) ? category : [category]).filter(Boolean).map(cat => ({
$: {
'android:name': `android.intent.category.${cat}`
}
}));
}
//# sourceMappingURL=IntentFilters.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,8 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
import { LocaleJson } from '../utils/locales';
export declare const withLocales: ConfigPlugin;
export declare function getLocales(config: Pick<ExpoConfig, 'locales'>): Record<string, string | LocaleJson> | null;
export declare function setLocalesAsync(config: Pick<ExpoConfig, 'locales'>, { projectRoot }: {
projectRoot: string;
}): Promise<unknown>;

View File

@@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getLocales = getLocales;
exports.setLocalesAsync = setLocalesAsync;
exports.withLocales = void 0;
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _() {
const data = require("..");
_ = function () {
return data;
};
return data;
}
function _XML() {
const data = require("../utils/XML");
_XML = function () {
return data;
};
return data;
}
function _locales() {
const data = require("../utils/locales");
_locales = function () {
return data;
};
return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const withLocales = config => {
return (0, _().withDangerousMod)(config, ['android', async config => {
config.modResults = await setLocalesAsync(config, {
projectRoot: config.modRequest.projectRoot
});
return config;
}]);
};
exports.withLocales = withLocales;
function getLocales(config) {
return config.locales ?? null;
}
async function setLocalesAsync(config, {
projectRoot
}) {
const locales = getLocales(config);
if (!locales) {
return config;
}
const {
localesMap
} = await (0, _locales().getResolvedLocalesAsync)(projectRoot, locales, 'android');
for (const [lang, localizationObj] of Object.entries(localesMap)) {
const stringsFilePath = _path().default.join(await _().AndroidConfig.Paths.getResourceFolderAsync(projectRoot), `values-b+${lang.replaceAll('-', '+')}`, 'strings.xml');
(0, _XML().writeXMLAsync)({
path: stringsFilePath,
xml: {
resources: Object.entries(localizationObj).map(([k, v]) => ({
string: {
$: {
name: k
},
_: `"${v}"`
}
}))
}
});
}
return config;
}
//# sourceMappingURL=Locales.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Locales.js","names":["_path","data","_interopRequireDefault","require","_","_XML","_locales","e","__esModule","default","withLocales","config","withDangerousMod","modResults","setLocalesAsync","projectRoot","modRequest","exports","getLocales","locales","localesMap","getResolvedLocalesAsync","lang","localizationObj","Object","entries","stringsFilePath","path","join","AndroidConfig","Paths","getResourceFolderAsync","replaceAll","writeXMLAsync","xml","resources","map","k","v","string","$","name"],"sources":["../../src/android/Locales.ts"],"sourcesContent":["import { ExpoConfig } from '@expo/config-types';\nimport path from 'path';\n\nimport { AndroidConfig, withDangerousMod } from '..';\nimport { ConfigPlugin } from '../Plugin.types';\nimport { writeXMLAsync } from '../utils/XML';\nimport { getResolvedLocalesAsync, LocaleJson } from '../utils/locales';\n\nexport const withLocales: ConfigPlugin = (config) => {\n return withDangerousMod(config, [\n 'android',\n async (config) => {\n config.modResults = await setLocalesAsync(config, {\n projectRoot: config.modRequest.projectRoot,\n });\n return config;\n },\n ]);\n};\n\nexport function getLocales(\n config: Pick<ExpoConfig, 'locales'>\n): Record<string, string | LocaleJson> | null {\n return config.locales ?? null;\n}\n\nexport async function setLocalesAsync(\n config: Pick<ExpoConfig, 'locales'>,\n { projectRoot }: { projectRoot: string }\n): Promise<unknown> {\n const locales = getLocales(config);\n if (!locales) {\n return config;\n }\n const { localesMap } = await getResolvedLocalesAsync(projectRoot, locales, 'android');\n for (const [lang, localizationObj] of Object.entries(localesMap)) {\n const stringsFilePath = path.join(\n await AndroidConfig.Paths.getResourceFolderAsync(projectRoot),\n `values-b+${lang.replaceAll('-', '+')}`,\n 'strings.xml'\n );\n writeXMLAsync({\n path: stringsFilePath,\n xml: {\n resources: Object.entries(localizationObj).map(([k, v]) => ({\n string: {\n $: {\n name: k,\n },\n _: `\"${v}\"`,\n },\n })),\n },\n });\n }\n\n return config;\n}\n"],"mappings":";;;;;;;;AACA,SAAAA,MAAA;EAAA,MAAAC,IAAA,GAAAC,sBAAA,CAAAC,OAAA;EAAAH,KAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,SAAAG,EAAA;EAAA,MAAAH,IAAA,GAAAE,OAAA;EAAAC,CAAA,YAAAA,CAAA;IAAA,OAAAH,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,SAAAI,KAAA;EAAA,MAAAJ,IAAA,GAAAE,OAAA;EAAAE,IAAA,YAAAA,CAAA;IAAA,OAAAJ,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAK,SAAA;EAAA,MAAAL,IAAA,GAAAE,OAAA;EAAAG,QAAA,YAAAA,CAAA;IAAA,OAAAL,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAAuE,SAAAC,uBAAAK,CAAA,WAAAA,CAAA,IAAAA,CAAA,CAAAC,UAAA,GAAAD,CAAA,KAAAE,OAAA,EAAAF,CAAA;AAEhE,MAAMG,WAAyB,GAAIC,MAAM,IAAK;EACnD,OAAO,IAAAC,oBAAgB,EAACD,MAAM,EAAE,CAC9B,SAAS,EACT,MAAOA,MAAM,IAAK;IAChBA,MAAM,CAACE,UAAU,GAAG,MAAMC,eAAe,CAACH,MAAM,EAAE;MAChDI,WAAW,EAAEJ,MAAM,CAACK,UAAU,CAACD;IACjC,CAAC,CAAC;IACF,OAAOJ,MAAM;EACf,CAAC,CACF,CAAC;AACJ,CAAC;AAACM,OAAA,CAAAP,WAAA,GAAAA,WAAA;AAEK,SAASQ,UAAUA,CACxBP,MAAmC,EACS;EAC5C,OAAOA,MAAM,CAACQ,OAAO,IAAI,IAAI;AAC/B;AAEO,eAAeL,eAAeA,CACnCH,MAAmC,EACnC;EAAEI;AAAqC,CAAC,EACtB;EAClB,MAAMI,OAAO,GAAGD,UAAU,CAACP,MAAM,CAAC;EAClC,IAAI,CAACQ,OAAO,EAAE;IACZ,OAAOR,MAAM;EACf;EACA,MAAM;IAAES;EAAW,CAAC,GAAG,MAAM,IAAAC,kCAAuB,EAACN,WAAW,EAAEI,OAAO,EAAE,SAAS,CAAC;EACrF,KAAK,MAAM,CAACG,IAAI,EAAEC,eAAe,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACL,UAAU,CAAC,EAAE;IAChE,MAAMM,eAAe,GAAGC,eAAI,CAACC,IAAI,CAC/B,MAAMC,iBAAa,CAACC,KAAK,CAACC,sBAAsB,CAAChB,WAAW,CAAC,EAC7D,YAAYO,IAAI,CAACU,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EACvC,aACF,CAAC;IACD,IAAAC,oBAAa,EAAC;MACZN,IAAI,EAAED,eAAe;MACrBQ,GAAG,EAAE;QACHC,SAAS,EAAEX,MAAM,CAACC,OAAO,CAACF,eAAe,CAAC,CAACa,GAAG,CAAC,CAAC,CAACC,CAAC,EAAEC,CAAC,CAAC,MAAM;UAC1DC,MAAM,EAAE;YACNC,CAAC,EAAE;cACDC,IAAI,EAAEJ;YACR,CAAC;YACDjC,CAAC,EAAE,IAAIkC,CAAC;UACV;QACF,CAAC,CAAC;MACJ;IACF,CAAC,CAAC;EACJ;EAEA,OAAO3B,MAAM;AACf","ignoreList":[]}

View File

@@ -0,0 +1,173 @@
export type StringBoolean = 'true' | 'false';
type ManifestMetaDataAttributes = AndroidManifestAttributes & {
'android:value'?: string;
'android:resource'?: string;
};
type AndroidManifestAttributes = {
'android:name': string | 'android.intent.action.VIEW';
'tools:node'?: string | 'remove';
};
type ManifestAction = {
$: AndroidManifestAttributes;
};
type ManifestCategory = {
$: AndroidManifestAttributes;
};
type ManifestData = {
$: {
[key: string]: string | undefined;
'android:host'?: string;
'android:pathPrefix'?: string;
'android:scheme'?: string;
};
};
type ManifestReceiver = {
$: AndroidManifestAttributes & {
'android:exported'?: StringBoolean;
'android:enabled'?: StringBoolean;
};
'intent-filter'?: ManifestIntentFilter[];
};
export type ManifestIntentFilter = {
$?: {
'android:autoVerify'?: StringBoolean;
'data-generated'?: StringBoolean;
};
action?: ManifestAction[];
data?: ManifestData[];
category?: ManifestCategory[];
};
export type ManifestActivityAlias = {
$?: {
'android:name': string;
'android:enabled'?: StringBoolean;
'android:exported'?: StringBoolean;
'android:label'?: string;
'android:permission'?: string;
'android:icon'?: string;
'android:targetActivity': string;
};
'intent-filter'?: ManifestIntentFilter[];
'meta-data'?: ManifestMetaData[];
};
export type ManifestMetaData = {
$: ManifestMetaDataAttributes;
};
type ManifestServiceAttributes = AndroidManifestAttributes & {
'android:enabled'?: StringBoolean;
'android:exported'?: StringBoolean;
'android:permission'?: string;
'android:foregroundServiceType'?: string;
};
type ManifestService = {
$: ManifestServiceAttributes;
'intent-filter'?: ManifestIntentFilter[];
};
type ManifestApplicationAttributes = {
'android:name': string | '.MainApplication';
'android:icon'?: string;
'android:roundIcon'?: string;
'android:label'?: string;
'android:allowBackup'?: StringBoolean;
'android:largeHeap'?: StringBoolean;
'android:requestLegacyExternalStorage'?: StringBoolean;
'android:supportsPictureInPicture'?: StringBoolean;
'android:usesCleartextTraffic'?: StringBoolean;
'android:enableOnBackInvokedCallback'?: StringBoolean;
[key: string]: string | undefined;
};
export type ManifestActivity = {
$: ManifestApplicationAttributes & {
'android:exported'?: StringBoolean;
'android:launchMode'?: string;
'android:theme'?: string;
'android:windowSoftInputMode'?: string | 'stateUnspecified' | 'stateUnchanged' | 'stateHidden' | 'stateAlwaysHidden' | 'stateVisible' | 'stateAlwaysVisible' | 'adjustUnspecified' | 'adjustResize' | 'adjustPan';
[key: string]: string | undefined;
};
'intent-filter'?: ManifestIntentFilter[];
};
export type ManifestUsesLibrary = {
$: AndroidManifestAttributes & {
'android:required'?: StringBoolean;
};
};
export type ManifestApplication = {
$: ManifestApplicationAttributes;
activity?: ManifestActivity[];
service?: ManifestService[];
receiver?: ManifestReceiver[];
'meta-data'?: ManifestMetaData[];
'uses-library'?: ManifestUsesLibrary[];
'activity-alias'?: ManifestActivityAlias[];
};
type ManifestPermission = {
$: AndroidManifestAttributes & {
'android:protectionLevel'?: string | 'signature';
};
};
export type ManifestUsesPermission = {
$: AndroidManifestAttributes;
};
type ManifestUsesFeature = {
$: AndroidManifestAttributes & {
'android:glEsVersion'?: string;
'android:required': StringBoolean;
};
};
export type AndroidManifest = {
manifest: {
$: {
'xmlns:android': string;
'xmlns:tools'?: string;
package?: string;
[key: string]: string | undefined;
};
permission?: ManifestPermission[];
'uses-permission'?: ManifestUsesPermission[];
'uses-permission-sdk-23'?: ManifestUsesPermission[];
'uses-feature'?: ManifestUsesFeature[];
queries: ManifestQuery[];
application?: ManifestApplication[];
};
};
type ManifestQueryIntent = Omit<ManifestIntentFilter, '$'>;
export type ManifestQuery = {
package?: {
$: {
'android:name': string;
};
}[];
intent?: ManifestQueryIntent[];
provider?: {
$: {
'android:authorities': string;
};
}[];
};
export declare function writeAndroidManifestAsync(manifestPath: string, androidManifest: AndroidManifest): Promise<void>;
export declare function readAndroidManifestAsync(manifestPath: string): Promise<AndroidManifest>;
/** Returns the `manifest.application` tag ending in `.MainApplication` */
export declare function getMainApplication(androidManifest: AndroidManifest): ManifestApplication | null;
export declare function getMainApplicationOrThrow(androidManifest: AndroidManifest): ManifestApplication;
export declare function getMainActivityOrThrow(androidManifest: AndroidManifest): ManifestActivity;
export declare function getRunnableActivity(androidManifest: AndroidManifest): ManifestActivity | null;
export declare function getMainActivity(androidManifest: AndroidManifest): ManifestActivity | null;
export declare function addMetaDataItemToMainApplication(mainApplication: ManifestApplication, itemName: string, itemValue: string, itemType?: 'resource' | 'value'): ManifestApplication;
export declare function removeMetaDataItemFromMainApplication(mainApplication: any, itemName: string): any;
export declare function findMetaDataItem(mainApplication: any, itemName: string): number;
export declare function findUsesLibraryItem(mainApplication: any, itemName: string): number;
export declare function getMainApplicationMetaDataValue(androidManifest: AndroidManifest, name: string): string | null;
export declare function addUsesLibraryItemToMainApplication(mainApplication: ManifestApplication, item: {
name: string;
required?: boolean;
}): ManifestApplication;
export declare function removeUsesLibraryItemFromMainApplication(mainApplication: ManifestApplication, itemName: string): ManifestApplication;
export declare function prefixAndroidKeys<T extends Record<string, any> = Record<string, string>>(head: T): Record<string, any>;
/**
* Ensure the `tools:*` namespace is available in the manifest.
*
* @param manifest AndroidManifest.xml
* @returns manifest with the `tools:*` namespace available
*/
export declare function ensureToolsAvailable(manifest: AndroidManifest): AndroidManifest;
export {};

View File

@@ -0,0 +1,250 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.addMetaDataItemToMainApplication = addMetaDataItemToMainApplication;
exports.addUsesLibraryItemToMainApplication = addUsesLibraryItemToMainApplication;
exports.ensureToolsAvailable = ensureToolsAvailable;
exports.findMetaDataItem = findMetaDataItem;
exports.findUsesLibraryItem = findUsesLibraryItem;
exports.getMainActivity = getMainActivity;
exports.getMainActivityOrThrow = getMainActivityOrThrow;
exports.getMainApplication = getMainApplication;
exports.getMainApplicationMetaDataValue = getMainApplicationMetaDataValue;
exports.getMainApplicationOrThrow = getMainApplicationOrThrow;
exports.getRunnableActivity = getRunnableActivity;
exports.prefixAndroidKeys = prefixAndroidKeys;
exports.readAndroidManifestAsync = readAndroidManifestAsync;
exports.removeMetaDataItemFromMainApplication = removeMetaDataItemFromMainApplication;
exports.removeUsesLibraryItemFromMainApplication = removeUsesLibraryItemFromMainApplication;
exports.writeAndroidManifestAsync = writeAndroidManifestAsync;
function _assert() {
const data = _interopRequireDefault(require("assert"));
_assert = function () {
return data;
};
return data;
}
function _fs() {
const data = _interopRequireDefault(require("fs"));
_fs = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function XML() {
const data = _interopRequireWildcard(require("../utils/XML"));
XML = 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 }; }
// https://developer.android.com/guide/topics/manifest/activity-alias-element
async function writeAndroidManifestAsync(manifestPath, androidManifest) {
const manifestXml = XML().format(androidManifest);
await _fs().default.promises.mkdir(_path().default.dirname(manifestPath), {
recursive: true
});
await _fs().default.promises.writeFile(manifestPath, manifestXml);
}
async function readAndroidManifestAsync(manifestPath) {
const xml = await XML().readXMLAsync({
path: manifestPath
});
if (!isManifest(xml)) {
throw new Error('Invalid manifest found at: ' + manifestPath);
}
return xml;
}
function isManifest(xml) {
// TODO: Maybe more validation
return !!xml.manifest;
}
/** Returns the `manifest.application` tag ending in `.MainApplication` */
function getMainApplication(androidManifest) {
return androidManifest?.manifest?.application?.filter(e => e?.$?.['android:name'].endsWith('.MainApplication'))[0] ?? null;
}
function getMainApplicationOrThrow(androidManifest) {
const mainApplication = getMainApplication(androidManifest);
(0, _assert().default)(mainApplication, 'AndroidManifest.xml is missing the required MainApplication element');
return mainApplication;
}
function getMainActivityOrThrow(androidManifest) {
const mainActivity = getMainActivity(androidManifest);
(0, _assert().default)(mainActivity, 'AndroidManifest.xml is missing the required MainActivity element');
return mainActivity;
}
function getRunnableActivity(androidManifest) {
const firstApplication = androidManifest?.manifest?.application?.[0] ?? getMainApplication(androidManifest);
if (!firstApplication) {
return null;
}
// Get enabled activities
const enabledActivities = firstApplication.activity?.filter?.(e => e.$['android:enabled'] !== 'false' && e.$['android:enabled'] !== false);
if (!enabledActivities) {
return null;
}
const isIntentFilterRunnable = intentFilter => {
return !!intentFilter.action?.some(action => action.$['android:name'] === 'android.intent.action.MAIN') && !!intentFilter.category?.some(category => category.$['android:name'] === 'android.intent.category.LAUNCHER');
};
// Get the activity that has a runnable intent-filter
for (const activity of enabledActivities) {
if (Array.isArray(activity['intent-filter'])) {
for (const intentFilter of activity['intent-filter']) {
if (isIntentFilterRunnable(intentFilter)) {
return activity;
}
}
}
}
const enabledActivityNames = enabledActivities.map(e => e.$['android:name']);
// If no runnable activity is found, check for matching activity-alias that may be runnable
const aliases = (firstApplication['activity-alias'] ?? []).filter(
// https://developer.android.com/guide/topics/manifest/activity-alias-element
e => e.$['android:enabled'] !== 'false' && enabledActivityNames.includes(e.$['android:targetActivity']));
if (aliases.length) {
for (const alias of aliases) {
if (Array.isArray(alias['intent-filter'])) {
for (const intentFilter of alias['intent-filter']) {
if (isIntentFilterRunnable(intentFilter)) {
const matchingActivity = enabledActivities.find(e => e.$['android:name'] === alias.$['android:targetActivity']);
if (matchingActivity) {
return matchingActivity;
}
}
}
}
}
}
return null;
}
function getMainActivity(androidManifest) {
const mainActivity = androidManifest?.manifest?.application?.[0]?.activity?.filter?.(e => e.$['android:name'] === '.MainActivity');
return mainActivity?.[0] ?? null;
}
function addMetaDataItemToMainApplication(mainApplication, itemName, itemValue, itemType = 'value') {
let existingMetaDataItem;
const newItem = {
$: prefixAndroidKeys({
name: itemName,
[itemType]: itemValue
})
};
if (mainApplication['meta-data']) {
existingMetaDataItem = mainApplication['meta-data'].filter(e => e.$['android:name'] === itemName);
if (existingMetaDataItem.length) {
existingMetaDataItem[0].$[`android:${itemType}`] = itemValue;
} else {
mainApplication['meta-data'].push(newItem);
}
} else {
mainApplication['meta-data'] = [newItem];
}
return mainApplication;
}
function removeMetaDataItemFromMainApplication(mainApplication, itemName) {
const index = findMetaDataItem(mainApplication, itemName);
if (mainApplication?.['meta-data'] && index > -1) {
mainApplication['meta-data'].splice(index, 1);
}
return mainApplication;
}
function findApplicationSubItem(mainApplication, category, itemName) {
const parent = mainApplication[category];
if (Array.isArray(parent)) {
const index = parent.findIndex(e => e.$['android:name'] === itemName);
return index;
}
return -1;
}
function findMetaDataItem(mainApplication, itemName) {
return findApplicationSubItem(mainApplication, 'meta-data', itemName);
}
function findUsesLibraryItem(mainApplication, itemName) {
return findApplicationSubItem(mainApplication, 'uses-library', itemName);
}
function getMainApplicationMetaDataValue(androidManifest, name) {
const mainApplication = getMainApplication(androidManifest);
if (mainApplication?.hasOwnProperty('meta-data')) {
const item = mainApplication?.['meta-data']?.find(e => e.$['android:name'] === name);
return item?.$['android:value'] ?? null;
}
return null;
}
function addUsesLibraryItemToMainApplication(mainApplication, item) {
let existingMetaDataItem;
const newItem = {
$: prefixAndroidKeys(item)
};
if (mainApplication['uses-library']) {
existingMetaDataItem = mainApplication['uses-library'].filter(e => e.$['android:name'] === item.name);
if (existingMetaDataItem.length) {
existingMetaDataItem[0].$ = newItem.$;
} else {
mainApplication['uses-library'].push(newItem);
}
} else {
mainApplication['uses-library'] = [newItem];
}
return mainApplication;
}
function removeUsesLibraryItemFromMainApplication(mainApplication, itemName) {
const index = findUsesLibraryItem(mainApplication, itemName);
if (mainApplication?.['uses-library'] && index > -1) {
mainApplication['uses-library'].splice(index, 1);
}
return mainApplication;
}
function prefixAndroidKeys(head) {
// prefix all keys with `android:`
return Object.entries(head).reduce((prev, [key, curr]) => ({
...prev,
[`android:${key}`]: curr
}), {});
}
/**
* Ensure the `tools:*` namespace is available in the manifest.
*
* @param manifest AndroidManifest.xml
* @returns manifest with the `tools:*` namespace available
*/
function ensureToolsAvailable(manifest) {
return ensureManifestHasNamespace(manifest, {
namespace: 'xmlns:tools',
url: 'http://schemas.android.com/tools'
});
}
/**
* Ensure a particular namespace is available in the manifest.
*
* @param manifest `AndroidManifest.xml`
* @returns manifest with the provided namespace available
*/
function ensureManifestHasNamespace(manifest, {
namespace,
url
}) {
if (manifest?.manifest?.$?.[namespace]) {
return manifest;
}
manifest.manifest.$[namespace] = url;
return manifest;
}
//# sourceMappingURL=Manifest.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,20 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
/**
* Sanitize a name, this should be used for files and gradle names.
* - `[/, \, :, <, >, ", ?, *, |]` are not allowed
* https://docs.gradle.org/4.2/release-notes.html#path-separator-characters-in-names-are-deprecated
*
* @param name
*/
export declare function sanitizeNameForGradle(name: string): string;
export declare const withName: ConfigPlugin;
export declare const withNameSettingsGradle: ConfigPlugin;
export declare function getName(config: Pick<ExpoConfig, 'name'>): string | null;
/**
* Regex a name change -- fragile.
*
* @param config
* @param settingsGradle
*/
export declare function applyNameSettingsGradle(config: Pick<ExpoConfig, 'name'>, settingsGradle: string): string;

View File

@@ -0,0 +1,91 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.applyNameSettingsGradle = applyNameSettingsGradle;
exports.getName = getName;
exports.sanitizeNameForGradle = sanitizeNameForGradle;
exports.withNameSettingsGradle = exports.withName = void 0;
function _Resources() {
const data = require("./Resources");
_Resources = function () {
return data;
};
return data;
}
function _Strings() {
const data = require("./Strings");
_Strings = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
function _warnings() {
const data = require("../utils/warnings");
_warnings = function () {
return data;
};
return data;
}
/**
* Sanitize a name, this should be used for files and gradle names.
* - `[/, \, :, <, >, ", ?, *, |]` are not allowed
* https://docs.gradle.org/4.2/release-notes.html#path-separator-characters-in-names-are-deprecated
*
* @param name
*/
function sanitizeNameForGradle(name) {
// Remove escape characters which are valid in XML names but not in gradle.
name = name.replace(/[\n\r\t]/g, '');
// Gradle disallows these:
// The project name 'My-Special 😃 Co/ol_Project' must not contain any of the following characters: [/, \, :, <, >, ", ?, *, |]. Set the 'rootProject.name' or adjust the 'include' statement (see https://docs.gradle.org/6.2/dsl/org.gradle.api.initialization.Settings.html#org.gradle.api.initialization.Settings:include(java.lang.String[]) for more details).
return name.replace(/(\/|\\|:|<|>|"|\?|\*|\|)/g, '');
}
const withName = exports.withName = (0, _androidPlugins().createStringsXmlPlugin)(applyNameFromConfig, 'withName');
const withNameSettingsGradle = config => {
return (0, _androidPlugins().withSettingsGradle)(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = applyNameSettingsGradle(config, config.modResults.contents);
} else {
(0, _warnings().addWarningAndroid)('name', `Cannot automatically configure settings.gradle if it's not groovy`);
}
return config;
});
};
exports.withNameSettingsGradle = withNameSettingsGradle;
function getName(config) {
return typeof config.name === 'string' ? config.name : null;
}
function applyNameFromConfig(config, stringsJSON) {
const name = getName(config);
if (name) {
return (0, _Strings().setStringItem)([(0, _Resources().buildResourceItem)({
name: 'app_name',
value: name
})], stringsJSON);
}
return (0, _Strings().removeStringItem)('app_name', stringsJSON);
}
/**
* Regex a name change -- fragile.
*
* @param config
* @param settingsGradle
*/
function applyNameSettingsGradle(config, settingsGradle) {
const name = sanitizeNameForGradle(getName(config) ?? '');
// Select rootProject.name = '***' and replace the contents between the quotes.
return settingsGradle.replace(/rootProject.name\s?=\s?(["'])(?:(?=(\\?))\2.)*?\1/g, `rootProject.name = '${name.replace(/'/g, "\\'")}'`);
}
//# sourceMappingURL=Name.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export declare const SCREEN_ORIENTATION_ATTRIBUTE = "android:screenOrientation";
export declare const withOrientation: import("..").ConfigPlugin;
export declare function getOrientation(config: Pick<ExpoConfig, 'orientation'>): "default" | "portrait" | "landscape" | null;
export declare function setAndroidOrientation(config: Pick<ExpoConfig, 'orientation'>, androidManifest: AndroidManifest): AndroidManifest;

View File

@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SCREEN_ORIENTATION_ATTRIBUTE = void 0;
exports.getOrientation = getOrientation;
exports.setAndroidOrientation = setAndroidOrientation;
exports.withOrientation = void 0;
function _Manifest() {
const data = require("./Manifest");
_Manifest = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
const SCREEN_ORIENTATION_ATTRIBUTE = exports.SCREEN_ORIENTATION_ATTRIBUTE = 'android:screenOrientation';
const withOrientation = exports.withOrientation = (0, _androidPlugins().createAndroidManifestPlugin)(setAndroidOrientation, 'withOrientation');
function getOrientation(config) {
return typeof config.orientation === 'string' ? config.orientation : null;
}
function setAndroidOrientation(config, androidManifest) {
const orientation = getOrientation(config);
// TODO: Remove this if we decide to remove any orientation configuration when not specified
if (!orientation) {
return androidManifest;
}
const mainActivity = (0, _Manifest().getMainActivityOrThrow)(androidManifest);
mainActivity.$[SCREEN_ORIENTATION_ATTRIBUTE] = orientation !== 'default' ? orientation : 'unspecified';
return androidManifest;
}
//# sourceMappingURL=Orientation.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Orientation.js","names":["_Manifest","data","require","_androidPlugins","SCREEN_ORIENTATION_ATTRIBUTE","exports","withOrientation","createAndroidManifestPlugin","setAndroidOrientation","getOrientation","config","orientation","androidManifest","mainActivity","getMainActivityOrThrow","$"],"sources":["../../src/android/Orientation.ts"],"sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { AndroidManifest, getMainActivityOrThrow } from './Manifest';\nimport { createAndroidManifestPlugin } from '../plugins/android-plugins';\n\nexport const SCREEN_ORIENTATION_ATTRIBUTE = 'android:screenOrientation';\n\nexport const withOrientation = createAndroidManifestPlugin(\n setAndroidOrientation,\n 'withOrientation'\n);\n\nexport function getOrientation(config: Pick<ExpoConfig, 'orientation'>) {\n return typeof config.orientation === 'string' ? config.orientation : null;\n}\n\nexport function setAndroidOrientation(\n config: Pick<ExpoConfig, 'orientation'>,\n androidManifest: AndroidManifest\n) {\n const orientation = getOrientation(config);\n // TODO: Remove this if we decide to remove any orientation configuration when not specified\n if (!orientation) {\n return androidManifest;\n }\n\n const mainActivity = getMainActivityOrThrow(androidManifest);\n\n mainActivity.$[SCREEN_ORIENTATION_ATTRIBUTE] =\n orientation !== 'default' ? orientation : 'unspecified';\n\n return androidManifest;\n}\n"],"mappings":";;;;;;;;;AAEA,SAAAA,UAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,SAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAE,gBAAA;EAAA,MAAAF,IAAA,GAAAC,OAAA;EAAAC,eAAA,YAAAA,CAAA;IAAA,OAAAF,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEO,MAAMG,4BAA4B,GAAAC,OAAA,CAAAD,4BAAA,GAAG,2BAA2B;AAEhE,MAAME,eAAe,GAAAD,OAAA,CAAAC,eAAA,GAAG,IAAAC,6CAA2B,EACxDC,qBAAqB,EACrB,iBACF,CAAC;AAEM,SAASC,cAAcA,CAACC,MAAuC,EAAE;EACtE,OAAO,OAAOA,MAAM,CAACC,WAAW,KAAK,QAAQ,GAAGD,MAAM,CAACC,WAAW,GAAG,IAAI;AAC3E;AAEO,SAASH,qBAAqBA,CACnCE,MAAuC,EACvCE,eAAgC,EAChC;EACA,MAAMD,WAAW,GAAGF,cAAc,CAACC,MAAM,CAAC;EAC1C;EACA,IAAI,CAACC,WAAW,EAAE;IAChB,OAAOC,eAAe;EACxB;EAEA,MAAMC,YAAY,GAAG,IAAAC,kCAAsB,EAACF,eAAe,CAAC;EAE5DC,YAAY,CAACE,CAAC,CAACX,4BAA4B,CAAC,GAC1CO,WAAW,KAAK,SAAS,GAAGA,WAAW,GAAG,aAAa;EAEzD,OAAOC,eAAe;AACxB","ignoreList":[]}

View File

@@ -0,0 +1,23 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
export declare const withPackageGradle: ConfigPlugin;
export declare const withPackageRefactor: ConfigPlugin;
export declare function getPackage(config: Pick<ExpoConfig, 'android'>): string | null;
export declare function renamePackageOnDisk(config: Pick<ExpoConfig, 'android'>, projectRoot: string): Promise<void>;
export declare function renameJniOnDiskForType({ projectRoot, type, packageName, }: {
projectRoot: string;
type: string;
packageName: string;
}): Promise<void>;
export declare function renamePackageOnDiskForType({ projectRoot, type, packageName, }: {
projectRoot: string;
type: string;
packageName: string;
}): Promise<void>;
export declare function setPackageInBuildGradle(config: Pick<ExpoConfig, 'android'>, buildGradle: string): string;
export declare function getApplicationIdAsync(projectRoot: string): Promise<string | null>;
/**
* Make a package name safe to use in a kotlin file,
* e.g. is.pvin.hello -> `is`.pvin.hello
*/
export declare function kotlinSanitized(packageName: string): string;

View File

@@ -0,0 +1,323 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getApplicationIdAsync = getApplicationIdAsync;
exports.getPackage = getPackage;
exports.kotlinSanitized = kotlinSanitized;
exports.renameJniOnDiskForType = renameJniOnDiskForType;
exports.renamePackageOnDisk = renamePackageOnDisk;
exports.renamePackageOnDiskForType = renamePackageOnDiskForType;
exports.setPackageInBuildGradle = setPackageInBuildGradle;
exports.withPackageRefactor = exports.withPackageGradle = void 0;
function _debug() {
const data = _interopRequireDefault(require("debug"));
_debug = function () {
return data;
};
return data;
}
function _fs() {
const data = _interopRequireDefault(require("fs"));
_fs = function () {
return data;
};
return data;
}
function _glob() {
const data = require("glob");
_glob = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _Paths() {
const data = require("./Paths");
_Paths = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
function _withDangerousMod() {
const data = require("../plugins/withDangerousMod");
_withDangerousMod = function () {
return data;
};
return data;
}
function _modules() {
const data = require("../utils/modules");
_modules = function () {
return data;
};
return data;
}
function _warnings() {
const data = require("../utils/warnings");
_warnings = function () {
return data;
};
return data;
}
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const debug = (0, _debug().default)('expo:config-plugins:android:package');
const withPackageGradle = config => {
return (0, _androidPlugins().withAppBuildGradle)(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = setPackageInBuildGradle(config, config.modResults.contents);
} else {
(0, _warnings().addWarningAndroid)('android.package', `Cannot automatically configure app build.gradle if it's not groovy`);
}
return config;
});
};
exports.withPackageGradle = withPackageGradle;
const withPackageRefactor = config => {
return (0, _withDangerousMod().withDangerousMod)(config, ['android', async config => {
await renamePackageOnDisk(config, config.modRequest.projectRoot);
return config;
}]);
};
exports.withPackageRefactor = withPackageRefactor;
function getPackage(config) {
return config.android?.package ?? null;
}
function getPackageRoot(projectRoot, type) {
return _path().default.join(projectRoot, 'android', 'app', 'src', type, 'java');
}
function getCurrentPackageName(projectRoot, packageRoot) {
const mainApplication = (0, _Paths().getProjectFilePath)(projectRoot, 'MainApplication');
const packagePath = _path().default.dirname(mainApplication);
const packagePathParts = _path().default.relative(packageRoot, packagePath).split(_path().default.sep).filter(Boolean);
return packagePathParts.join('.');
}
function getCurrentPackageForProjectFile(projectRoot, packageRoot, fileName, type) {
const filePath = (0, _glob().globSync)(_path().default.join(projectRoot, `android/app/src/${type}/java/**/${fileName}.@(java|kt)`))[0];
if (!filePath) {
return null;
}
const packagePath = _path().default.dirname(filePath);
const packagePathParts = _path().default.relative(packageRoot, packagePath).split(_path().default.sep).filter(Boolean);
return packagePathParts.join('.');
}
function getCurrentPackageNameForType(projectRoot, type) {
const packageRoot = getPackageRoot(projectRoot, type);
if (type === 'main') {
return getCurrentPackageName(projectRoot, packageRoot);
}
// debug, etc..
return getCurrentPackageForProjectFile(projectRoot, packageRoot, '*', type);
}
// NOTE(brentvatne): this assumes that our MainApplication.java file is in the root of the package
// this makes sense for standard react-native projects but may not apply in customized projects, so if
// we want this to be runnable in any app we need to handle other possibilities
async function renamePackageOnDisk(config, projectRoot) {
const newPackageName = getPackage(config);
if (newPackageName === null) {
return;
}
for (const type of ['debug', 'main', 'release']) {
await renameJniOnDiskForType({
projectRoot,
type,
packageName: newPackageName
});
await renamePackageOnDiskForType({
projectRoot,
type,
packageName: newPackageName
});
}
}
async function renameJniOnDiskForType({
projectRoot,
type,
packageName
}) {
if (!packageName) {
return;
}
const currentPackageName = getCurrentPackageNameForType(projectRoot, type);
if (!currentPackageName || !packageName || currentPackageName === packageName) {
return;
}
const jniRoot = _path().default.join(projectRoot, 'android', 'app', 'src', type, 'jni');
const filesToUpdate = [...(0, _glob().globSync)('**/*', {
cwd: jniRoot,
absolute: true
})];
// Replace all occurrences of the path in the project
filesToUpdate.forEach(filepath => {
try {
if (_fs().default.lstatSync(filepath).isFile() && ['.h', '.cpp'].includes(_path().default.extname(filepath))) {
let contents = _fs().default.readFileSync(filepath).toString();
contents = contents.replace(new RegExp(transformJavaClassDescriptor(currentPackageName).replace(/\//g, '\\/'), 'g'), transformJavaClassDescriptor(packageName));
_fs().default.writeFileSync(filepath, contents);
}
} catch {
debug(`Error updating "${filepath}" for type "${type}"`);
}
});
}
async function renamePackageOnDiskForType({
projectRoot,
type,
packageName
}) {
if (!packageName) {
return;
}
const currentPackageName = getCurrentPackageNameForType(projectRoot, type);
debug(`Found package "${currentPackageName}" for type "${type}"`);
if (!currentPackageName || currentPackageName === packageName) {
return;
}
debug(`Refactor "${currentPackageName}" to "${packageName}" for type "${type}"`);
const packageRoot = getPackageRoot(projectRoot, type);
// Set up our paths
if (!(await (0, _modules().directoryExistsAsync)(packageRoot))) {
debug(`- skipping refactor of missing directory: ${packageRoot}`);
return;
}
const currentPackagePath = _path().default.join(packageRoot, ...currentPackageName.split('.'));
const newPackagePath = _path().default.join(packageRoot, ...packageName.split('.'));
// Create the new directory
_fs().default.mkdirSync(newPackagePath, {
recursive: true
});
// Move everything from the old directory over
(0, _glob().globSync)('**/*', {
cwd: currentPackagePath
}).forEach(relativePath => {
const filepath = _path().default.join(currentPackagePath, relativePath);
if (_fs().default.lstatSync(filepath).isFile()) {
moveFileSync(filepath, _path().default.join(newPackagePath, relativePath));
} else {
_fs().default.mkdirSync(filepath, {
recursive: true
});
}
});
// Remove the old directory recursively from com/old/package to com/old and com,
// as long as the directories are empty
const oldPathParts = currentPackageName.split('.');
while (oldPathParts.length) {
const pathToCheck = _path().default.join(packageRoot, ...oldPathParts);
try {
const files = _fs().default.readdirSync(pathToCheck);
if (files.length === 0) {
_fs().default.rmdirSync(pathToCheck);
}
} finally {
oldPathParts.pop();
}
}
const filesToUpdate = [...(0, _glob().globSync)('**/*', {
cwd: newPackagePath,
absolute: true
})];
// Only update the BUCK file to match the main package name
if (type === 'main') {
// NOTE(EvanBacon): We dropped this file in SDK 48 but other templates may still use it.
filesToUpdate.push(_path().default.join(projectRoot, 'android', 'app', 'BUCK'));
}
const kotlinSanitizedPackageName = kotlinSanitized(packageName);
// Replace all occurrences of the path in the project
filesToUpdate.forEach(filepath => {
try {
if (_fs().default.lstatSync(filepath).isFile()) {
let contents = _fs().default.readFileSync(filepath).toString();
if (_path().default.extname(filepath) === '.kt') {
contents = replacePackageName(contents, currentPackageName, kotlinSanitizedPackageName);
} else {
contents = replacePackageName(contents, currentPackageName, packageName);
}
if (['.h', '.cpp'].includes(_path().default.extname(filepath))) {
contents = contents.replace(new RegExp(transformJavaClassDescriptor(currentPackageName).replace(/\//g, '\\'), 'g'), transformJavaClassDescriptor(packageName));
}
_fs().default.writeFileSync(filepath, contents);
}
} catch {
debug(`Error updating "${filepath}" for type "${type}"`);
}
});
}
function moveFileSync(src, dest) {
_fs().default.mkdirSync(_path().default.dirname(dest), {
recursive: true
});
_fs().default.renameSync(src, dest);
}
function setPackageInBuildGradle(config, buildGradle) {
const packageName = getPackage(config);
if (packageName === null) {
return buildGradle;
}
const pattern = new RegExp(`(applicationId|namespace) ['"].*['"]`, 'g');
return buildGradle.replace(pattern, `$1 '${packageName}'`);
}
async function getApplicationIdAsync(projectRoot) {
const buildGradlePath = (0, _Paths().getAppBuildGradleFilePath)(projectRoot);
if (!_fs().default.existsSync(buildGradlePath)) {
return null;
}
const buildGradle = await _fs().default.promises.readFile(buildGradlePath, 'utf8');
const matchResult = buildGradle.match(/applicationId ['"](.*)['"]/);
// TODO add fallback for legacy cases to read from AndroidManifest.xml
return matchResult?.[1] ?? null;
}
/**
* Replace the package name with the new package name, in the given source.
* This has to be limited to avoid accidentally replacing imports when the old package name overlaps.
*/
function replacePackageName(content, oldName, newName) {
const oldNameEscaped = oldName.replace(/\./g, '\\.');
return content
// Replace any quoted instances "com.old" -> "com.new"
.replace(new RegExp(`"${oldNameEscaped}"`, 'g'), `"${newName}"`)
// Replace special non-quoted instances, only when prefixed by package or namespace
.replace(new RegExp(`(package|namespace)(\\s+)${oldNameEscaped}`, 'g'), `$1$2${newName}`)
// Replace special import instances, without overlapping with other imports (trailing `.` to close it off)
.replace(new RegExp(`(import\\s+)${oldNameEscaped}\\.`, 'g'), `$1${newName}.`);
}
/**
* Transform a java package name to java class descriptor,
* e.g. `com.helloworld` -> `Lcom/helloworld`.
*/
function transformJavaClassDescriptor(packageName) {
return `L${packageName.replace(/\./g, '/')}`;
}
/**
* Make a package name safe to use in a kotlin file,
* e.g. is.pvin.hello -> `is`.pvin.hello
*/
function kotlinSanitized(packageName) {
const stringsToWrap = ['is', 'in', 'as', 'fun'];
const parts = packageName.split('.');
const cleanParts = parts.map(part => stringsToWrap.includes(part) ? '`' + part + '`' : part);
const cleanName = cleanParts.join('.');
return cleanName;
}
//# sourceMappingURL=Package.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,30 @@
import { ResourceKind } from './Resources';
export interface ProjectFile<L extends string = string> {
path: string;
language: L;
contents: string;
}
export type ApplicationProjectFile = ProjectFile<'java' | 'kt'>;
export type GradleProjectFile = ProjectFile<'groovy' | 'kt'>;
export declare function getProjectFilePath(projectRoot: string, name: string): string;
export declare function getFileInfo(filePath: string): {
path: string;
contents: string;
language: any;
};
export declare function getMainApplicationAsync(projectRoot: string): Promise<ApplicationProjectFile>;
export declare function getMainActivityAsync(projectRoot: string): Promise<ApplicationProjectFile>;
export declare function getGradleFilePath(projectRoot: string, gradleName: string): string;
export declare function getProjectBuildGradleFilePath(projectRoot: string): string;
export declare function getProjectBuildGradleAsync(projectRoot: string): Promise<GradleProjectFile>;
export declare function getSettingsGradleFilePath(projectRoot: string): string;
export declare function getSettingsGradleAsync(projectRoot: string): Promise<GradleProjectFile>;
export declare function getAppBuildGradleFilePath(projectRoot: string): string;
export declare function getAppBuildGradleAsync(projectRoot: string): Promise<GradleProjectFile>;
export declare function getProjectPathOrThrowAsync(projectRoot: string): Promise<string>;
export declare function getAndroidManifestAsync(projectRoot: string): Promise<string>;
export declare function getResourceFolderAsync(projectRoot: string): Promise<string>;
export declare function getResourceXMLPathAsync(projectRoot: string, { kind, name }: {
kind?: ResourceKind;
name: 'colors' | 'strings' | 'styles' | string;
}): Promise<string>;

View File

@@ -0,0 +1,157 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getAndroidManifestAsync = getAndroidManifestAsync;
exports.getAppBuildGradleAsync = getAppBuildGradleAsync;
exports.getAppBuildGradleFilePath = getAppBuildGradleFilePath;
exports.getFileInfo = getFileInfo;
exports.getGradleFilePath = getGradleFilePath;
exports.getMainActivityAsync = getMainActivityAsync;
exports.getMainApplicationAsync = getMainApplicationAsync;
exports.getProjectBuildGradleAsync = getProjectBuildGradleAsync;
exports.getProjectBuildGradleFilePath = getProjectBuildGradleFilePath;
exports.getProjectFilePath = getProjectFilePath;
exports.getProjectPathOrThrowAsync = getProjectPathOrThrowAsync;
exports.getResourceFolderAsync = getResourceFolderAsync;
exports.getResourceXMLPathAsync = getResourceXMLPathAsync;
exports.getSettingsGradleAsync = getSettingsGradleAsync;
exports.getSettingsGradleFilePath = getSettingsGradleFilePath;
function _assert() {
const data = _interopRequireDefault(require("assert"));
_assert = function () {
return data;
};
return data;
}
function _fs() {
const data = _interopRequireDefault(require("fs"));
_fs = function () {
return data;
};
return data;
}
function _glob() {
const data = require("glob");
_glob = function () {
return data;
};
return data;
}
function path() {
const data = _interopRequireWildcard(require("path"));
path = function () {
return data;
};
return data;
}
function _errors() {
const data = require("../utils/errors");
_errors = function () {
return data;
};
return data;
}
function _modules() {
const data = require("../utils/modules");
_modules = 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 }; }
function getProjectFilePath(projectRoot, name) {
const filePath = (0, _glob().globSync)(`android/app/src/main/java/**/${name}.@(java|kt)`, {
cwd: projectRoot,
absolute: true
})[0];
(0, _assert().default)(filePath, `Project file "${name}" does not exist in android project for root "${projectRoot}"`);
return filePath;
}
function getLanguage(filePath) {
const extension = path().extname(filePath);
switch (extension) {
case '.java':
return 'java';
case '.kts':
case '.kt':
return 'kt';
case '.groovy':
case '.gradle':
return 'groovy';
default:
throw new (_errors().UnexpectedError)(`Unexpected Android file extension: ${extension}`);
}
}
function getFileInfo(filePath) {
return {
path: path().normalize(filePath),
contents: _fs().default.readFileSync(filePath, 'utf8'),
language: getLanguage(filePath)
};
}
async function getMainApplicationAsync(projectRoot) {
const filePath = getProjectFilePath(projectRoot, 'MainApplication');
return getFileInfo(filePath);
}
async function getMainActivityAsync(projectRoot) {
const filePath = getProjectFilePath(projectRoot, 'MainActivity');
return getFileInfo(filePath);
}
function getGradleFilePath(projectRoot, gradleName) {
const groovyPath = path().resolve(projectRoot, `${gradleName}.gradle`);
const ktPath = path().resolve(projectRoot, `${gradleName}.gradle.kts`);
const isGroovy = _fs().default.existsSync(groovyPath);
const isKotlin = !isGroovy && _fs().default.existsSync(ktPath);
if (!isGroovy && !isKotlin) {
throw new Error(`Failed to find '${gradleName}.gradle' file for project: ${projectRoot}.`);
}
const filePath = isGroovy ? groovyPath : ktPath;
return filePath;
}
function getProjectBuildGradleFilePath(projectRoot) {
return getGradleFilePath(path().join(projectRoot, 'android'), 'build');
}
async function getProjectBuildGradleAsync(projectRoot) {
return getFileInfo(getProjectBuildGradleFilePath(projectRoot));
}
function getSettingsGradleFilePath(projectRoot) {
return getGradleFilePath(path().join(projectRoot, 'android'), 'settings');
}
async function getSettingsGradleAsync(projectRoot) {
return getFileInfo(getSettingsGradleFilePath(projectRoot));
}
function getAppBuildGradleFilePath(projectRoot) {
return getGradleFilePath(path().join(projectRoot, 'android', 'app'), 'build');
}
async function getAppBuildGradleAsync(projectRoot) {
return getFileInfo(getAppBuildGradleFilePath(projectRoot));
}
async function getProjectPathOrThrowAsync(projectRoot) {
const projectPath = path().join(projectRoot, 'android');
if (await (0, _modules().directoryExistsAsync)(projectPath)) {
return projectPath;
}
throw new Error(`Android project folder is missing in project: ${projectRoot}`);
}
async function getAndroidManifestAsync(projectRoot) {
const projectPath = await getProjectPathOrThrowAsync(projectRoot);
const filePath = path().join(projectPath, 'app/src/main/AndroidManifest.xml');
return filePath;
}
async function getResourceFolderAsync(projectRoot) {
const projectPath = await getProjectPathOrThrowAsync(projectRoot);
return path().join(projectPath, `app/src/main/res`);
}
async function getResourceXMLPathAsync(projectRoot, {
kind = 'values',
name
}) {
const resourcePath = await getResourceFolderAsync(projectRoot);
const filePath = path().join(resourcePath, `${kind}/${name}.xml`);
return filePath;
}
//# sourceMappingURL=Paths.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,20 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest, ManifestUsesPermission } from './Manifest';
import { ConfigPlugin } from '../Plugin.types';
export declare const withPermissions: ConfigPlugin<string[] | void>;
/** Given a permission or list of permissions, block permissions in the final `AndroidManifest.xml` to ensure no installed library or plugin can add them. */
export declare const withBlockedPermissions: ConfigPlugin<string[] | string>;
export declare const withInternalBlockedPermissions: ConfigPlugin;
export declare function addBlockedPermissions(androidManifest: AndroidManifest, permissions: string[]): AndroidManifest;
export declare function getAndroidPermissions(config: Pick<ExpoConfig, 'android'>): string[];
export declare function setAndroidPermissions(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function isPermissionAlreadyRequested(permission: string, manifestPermissions: ManifestUsesPermission[]): boolean;
export declare function addPermissionToManifest(permission: string, manifestPermissions: ManifestUsesPermission[]): ManifestUsesPermission[];
export declare function removePermissions(androidManifest: AndroidManifest, permissionNames?: string[]): void;
export declare function addPermission(androidManifest: AndroidManifest, permissionName: string): void;
export declare function ensurePermissions(androidManifest: AndroidManifest, permissionNames: string[]): {
[permission: string]: boolean;
};
export declare function ensurePermission(androidManifest: AndroidManifest, permissionName: string): boolean;
export declare function ensurePermissionNameFormat(permissionName: string): string;
export declare function getPermissions(androidManifest: AndroidManifest): string[];

View File

@@ -0,0 +1,207 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.addBlockedPermissions = addBlockedPermissions;
exports.addPermission = addPermission;
exports.addPermissionToManifest = addPermissionToManifest;
exports.ensurePermission = ensurePermission;
exports.ensurePermissionNameFormat = ensurePermissionNameFormat;
exports.ensurePermissions = ensurePermissions;
exports.getAndroidPermissions = getAndroidPermissions;
exports.getPermissions = getPermissions;
exports.isPermissionAlreadyRequested = isPermissionAlreadyRequested;
exports.removePermissions = removePermissions;
exports.setAndroidPermissions = setAndroidPermissions;
exports.withPermissions = exports.withInternalBlockedPermissions = exports.withBlockedPermissions = void 0;
function _Manifest() {
const data = require("./Manifest");
_Manifest = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
const USES_PERMISSION = 'uses-permission';
const withPermissions = (config, permissions) => {
if (Array.isArray(permissions)) {
permissions = permissions.filter(Boolean);
if (!config.android) config.android = {};
if (!config.android.permissions) config.android.permissions = [];
config.android.permissions = [
// @ts-ignore
...new Set(config.android.permissions.concat(permissions))];
}
return (0, _androidPlugins().withAndroidManifest)(config, async config => {
config.modResults = await setAndroidPermissions(config, config.modResults);
return config;
});
};
/** Given a permission or list of permissions, block permissions in the final `AndroidManifest.xml` to ensure no installed library or plugin can add them. */
exports.withPermissions = withPermissions;
const withBlockedPermissions = (config, permissions) => {
const resolvedPermissions = prefixAndroidPermissionsIfNecessary((Array.isArray(permissions) ? permissions : [permissions]).filter(Boolean));
if (config?.android?.permissions && Array.isArray(config.android.permissions)) {
// Remove any static config permissions
config.android.permissions = prefixAndroidPermissionsIfNecessary(config.android.permissions).filter(permission => !resolvedPermissions.includes(permission));
}
return (0, _androidPlugins().withAndroidManifest)(config, async config => {
config.modResults = (0, _Manifest().ensureToolsAvailable)(config.modResults);
config.modResults = addBlockedPermissions(config.modResults, resolvedPermissions);
return config;
});
};
exports.withBlockedPermissions = withBlockedPermissions;
const withInternalBlockedPermissions = config => {
// Only add permissions if the user defined the property and added some values
// this ensures we don't add the `tools:*` namespace extraneously.
if (config.android?.blockedPermissions?.length) {
return withBlockedPermissions(config, config.android.blockedPermissions);
}
return config;
};
exports.withInternalBlockedPermissions = withInternalBlockedPermissions;
function addBlockedPermissions(androidManifest, permissions) {
if (!Array.isArray(androidManifest.manifest['uses-permission'])) {
androidManifest.manifest['uses-permission'] = [];
}
for (const permission of prefixAndroidPermissionsIfNecessary(permissions)) {
androidManifest.manifest['uses-permission'] = ensureBlockedPermission(androidManifest.manifest['uses-permission'], permission);
}
return androidManifest;
}
/**
* Filter any existing permissions matching the provided permission name, then add a
* restricted permission to overwrite any extra permissions that may be added in a
* third-party package's AndroidManifest.xml.
*
* @param manifestPermissions manifest `uses-permissions` array.
* @param permission `android:name` of the permission to restrict
* @returns
*/
function ensureBlockedPermission(manifestPermissions, permission) {
// Remove permission if it currently exists
manifestPermissions = manifestPermissions.filter(e => e.$['android:name'] !== permission);
// Add a permission with tools:node to overwrite any existing permission and ensure it's removed upon building.
manifestPermissions.push({
$: {
'android:name': permission,
'tools:node': 'remove'
}
});
return manifestPermissions;
}
function prefixAndroidPermissionsIfNecessary(permissions) {
return permissions.map(permission => {
if (!permission.includes('.')) {
return `android.permission.${permission}`;
}
return permission;
});
}
function getAndroidPermissions(config) {
return config.android?.permissions ?? [];
}
function setAndroidPermissions(config, androidManifest) {
const permissions = getAndroidPermissions(config);
const providedPermissions = prefixAndroidPermissionsIfNecessary(permissions);
const permissionsToAdd = [...providedPermissions];
if (!androidManifest.manifest.hasOwnProperty('uses-permission')) {
androidManifest.manifest['uses-permission'] = [];
}
// manifest.manifest['uses-permission'] = [];
const manifestPermissions = androidManifest.manifest['uses-permission'] ?? [];
permissionsToAdd.forEach(permission => {
if (!isPermissionAlreadyRequested(permission, manifestPermissions)) {
addPermissionToManifest(permission, manifestPermissions);
}
});
return androidManifest;
}
function isPermissionAlreadyRequested(permission, manifestPermissions) {
return manifestPermissions.some(e => e.$['android:name'] === permission);
}
function addPermissionToManifest(permission, manifestPermissions) {
manifestPermissions.push({
$: {
'android:name': permission
}
});
return manifestPermissions;
}
function removePermissions(androidManifest, permissionNames) {
const targetNames = permissionNames ? permissionNames.map(ensurePermissionNameFormat) : null;
const permissions = androidManifest.manifest[USES_PERMISSION] || [];
const nextPermissions = [];
for (const attribute of permissions) {
if (targetNames) {
// @ts-ignore: name isn't part of the type
const value = attribute.$['android:name'] || attribute.$.name;
if (!targetNames.includes(value)) {
nextPermissions.push(attribute);
}
}
}
androidManifest.manifest[USES_PERMISSION] = nextPermissions;
}
function addPermission(androidManifest, permissionName) {
const usesPermissions = androidManifest.manifest[USES_PERMISSION] || [];
usesPermissions.push({
$: {
'android:name': permissionName
}
});
androidManifest.manifest[USES_PERMISSION] = usesPermissions;
}
function ensurePermissions(androidManifest, permissionNames) {
const permissions = getPermissions(androidManifest);
const results = {};
for (const permissionName of permissionNames) {
const targetName = ensurePermissionNameFormat(permissionName);
if (!permissions.includes(targetName)) {
addPermission(androidManifest, targetName);
results[permissionName] = true;
} else {
results[permissionName] = false;
}
}
return results;
}
function ensurePermission(androidManifest, permissionName) {
const permissions = getPermissions(androidManifest);
const targetName = ensurePermissionNameFormat(permissionName);
if (!permissions.includes(targetName)) {
addPermission(androidManifest, targetName);
return true;
}
return false;
}
function ensurePermissionNameFormat(permissionName) {
if (permissionName.includes('.')) {
const com = permissionName.split('.');
const name = com.pop();
return [...com, name.toUpperCase()].join('.');
} else {
// If shorthand form like `WRITE_CONTACTS` is provided, expand it to `android.permission.WRITE_CONTACTS`.
return ensurePermissionNameFormat(`android.permission.${permissionName}`);
}
}
function getPermissions(androidManifest) {
const usesPermissions = androidManifest.manifest[USES_PERMISSION] || [];
const permissions = usesPermissions.map(permissionObject => {
return permissionObject.$['android:name'] || permissionObject.$.name;
});
return permissions;
}
//# sourceMappingURL=Permissions.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
import { ConfigPlugin } from '../Plugin.types';
export declare const withPredictiveBackGesture: ConfigPlugin;
export declare function setPredictiveBackGesture(config: Pick<ExpoConfig, 'android'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function getPredictiveBackGestureValue(config: Pick<ExpoConfig, 'android'>): "false" | "true";

View File

@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getPredictiveBackGestureValue = getPredictiveBackGestureValue;
exports.setPredictiveBackGesture = setPredictiveBackGesture;
exports.withPredictiveBackGesture = void 0;
function _Manifest() {
const data = require("./Manifest");
_Manifest = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
const ANDROID_ENABLE_ON_BACK_INVOKED_CALLBACK = 'android:enableOnBackInvokedCallback';
const withPredictiveBackGesture = config => {
return (0, _androidPlugins().withAndroidManifest)(config, async config => {
config.modResults = setPredictiveBackGesture(config, config.modResults);
return config;
});
};
exports.withPredictiveBackGesture = withPredictiveBackGesture;
function setPredictiveBackGesture(config, androidManifest) {
const app = (0, _Manifest().getMainApplicationOrThrow)(androidManifest);
app.$[ANDROID_ENABLE_ON_BACK_INVOKED_CALLBACK] = getPredictiveBackGestureValue(config);
return androidManifest;
}
function getPredictiveBackGestureValue(config) {
const value = config.android?.predictiveBackGestureEnabled;
return value === true ? 'true' : 'false';
}
//# sourceMappingURL=PredictiveBackGesture.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PredictiveBackGesture.js","names":["_Manifest","data","require","_androidPlugins","ANDROID_ENABLE_ON_BACK_INVOKED_CALLBACK","withPredictiveBackGesture","config","withAndroidManifest","modResults","setPredictiveBackGesture","exports","androidManifest","app","getMainApplicationOrThrow","$","getPredictiveBackGestureValue","value","android","predictiveBackGestureEnabled"],"sources":["../../src/android/PredictiveBackGesture.ts"],"sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { AndroidManifest, getMainApplicationOrThrow } from './Manifest';\nimport { ConfigPlugin } from '../Plugin.types';\nimport { withAndroidManifest } from '../plugins/android-plugins';\n\nconst ANDROID_ENABLE_ON_BACK_INVOKED_CALLBACK = 'android:enableOnBackInvokedCallback';\n\nexport const withPredictiveBackGesture: ConfigPlugin = (config) => {\n return withAndroidManifest(config, async (config) => {\n config.modResults = setPredictiveBackGesture(config, config.modResults);\n return config;\n });\n};\n\nexport function setPredictiveBackGesture(\n config: Pick<ExpoConfig, 'android'>,\n androidManifest: AndroidManifest\n) {\n const app = getMainApplicationOrThrow(androidManifest);\n app.$[ANDROID_ENABLE_ON_BACK_INVOKED_CALLBACK] = getPredictiveBackGestureValue(config);\n return androidManifest;\n}\n\nexport function getPredictiveBackGestureValue(config: Pick<ExpoConfig, 'android'>) {\n const value = config.android?.predictiveBackGestureEnabled;\n return value === true ? 'true' : 'false';\n}\n"],"mappings":";;;;;;;;AAEA,SAAAA,UAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,SAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,SAAAE,gBAAA;EAAA,MAAAF,IAAA,GAAAC,OAAA;EAAAC,eAAA,YAAAA,CAAA;IAAA,OAAAF,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,MAAMG,uCAAuC,GAAG,qCAAqC;AAE9E,MAAMC,yBAAuC,GAAIC,MAAM,IAAK;EACjE,OAAO,IAAAC,qCAAmB,EAACD,MAAM,EAAE,MAAOA,MAAM,IAAK;IACnDA,MAAM,CAACE,UAAU,GAAGC,wBAAwB,CAACH,MAAM,EAAEA,MAAM,CAACE,UAAU,CAAC;IACvE,OAAOF,MAAM;EACf,CAAC,CAAC;AACJ,CAAC;AAACI,OAAA,CAAAL,yBAAA,GAAAA,yBAAA;AAEK,SAASI,wBAAwBA,CACtCH,MAAmC,EACnCK,eAAgC,EAChC;EACA,MAAMC,GAAG,GAAG,IAAAC,qCAAyB,EAACF,eAAe,CAAC;EACtDC,GAAG,CAACE,CAAC,CAACV,uCAAuC,CAAC,GAAGW,6BAA6B,CAACT,MAAM,CAAC;EACtF,OAAOK,eAAe;AACxB;AAEO,SAASI,6BAA6BA,CAACT,MAAmC,EAAE;EACjF,MAAMU,KAAK,GAAGV,MAAM,CAACW,OAAO,EAAEC,4BAA4B;EAC1D,OAAOF,KAAK,KAAK,IAAI,GAAG,MAAM,GAAG,OAAO;AAC1C","ignoreList":[]}

View File

@@ -0,0 +1,6 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
export declare const withPrimaryColor: ConfigPlugin;
export declare const withPrimaryColorColors: ConfigPlugin;
export declare const withPrimaryColorStyles: ConfigPlugin;
export declare function getPrimaryColor(config: Pick<ExpoConfig, 'primaryColor'>): string;

View File

@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getPrimaryColor = getPrimaryColor;
exports.withPrimaryColorStyles = exports.withPrimaryColorColors = exports.withPrimaryColor = void 0;
function _Colors() {
const data = require("./Colors");
_Colors = function () {
return data;
};
return data;
}
function _Styles() {
const data = require("./Styles");
_Styles = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
const COLOR_PRIMARY_KEY = 'colorPrimary';
const DEFAULT_PRIMARY_COLOR = '#023c69';
const withPrimaryColor = config => {
config = withPrimaryColorColors(config);
config = withPrimaryColorStyles(config);
return config;
};
exports.withPrimaryColor = withPrimaryColor;
const withPrimaryColorColors = config => {
return (0, _androidPlugins().withAndroidColors)(config, config => {
config.modResults = (0, _Colors().assignColorValue)(config.modResults, {
name: COLOR_PRIMARY_KEY,
value: getPrimaryColor(config)
});
return config;
});
};
exports.withPrimaryColorColors = withPrimaryColorColors;
const withPrimaryColorStyles = config => {
return (0, _androidPlugins().withAndroidStyles)(config, config => {
config.modResults = (0, _Styles().assignStylesValue)(config.modResults, {
add: !!getPrimaryColor(config),
parent: (0, _Styles().getAppThemeGroup)(),
name: COLOR_PRIMARY_KEY,
value: `@color/${COLOR_PRIMARY_KEY}`
});
return config;
});
};
exports.withPrimaryColorStyles = withPrimaryColorStyles;
function getPrimaryColor(config) {
return config.primaryColor ?? DEFAULT_PRIMARY_COLOR;
}
//# sourceMappingURL=PrimaryColor.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"PrimaryColor.js","names":["_Colors","data","require","_Styles","_androidPlugins","COLOR_PRIMARY_KEY","DEFAULT_PRIMARY_COLOR","withPrimaryColor","config","withPrimaryColorColors","withPrimaryColorStyles","exports","withAndroidColors","modResults","assignColorValue","name","value","getPrimaryColor","withAndroidStyles","assignStylesValue","add","parent","getAppThemeGroup","primaryColor"],"sources":["../../src/android/PrimaryColor.ts"],"sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { assignColorValue } from './Colors';\nimport { assignStylesValue, getAppThemeGroup } from './Styles';\nimport { ConfigPlugin } from '../Plugin.types';\nimport { withAndroidColors, withAndroidStyles } from '../plugins/android-plugins';\n\nconst COLOR_PRIMARY_KEY = 'colorPrimary';\nconst DEFAULT_PRIMARY_COLOR = '#023c69';\n\nexport const withPrimaryColor: ConfigPlugin = (config) => {\n config = withPrimaryColorColors(config);\n config = withPrimaryColorStyles(config);\n return config;\n};\n\nexport const withPrimaryColorColors: ConfigPlugin = (config) => {\n return withAndroidColors(config, (config) => {\n config.modResults = assignColorValue(config.modResults, {\n name: COLOR_PRIMARY_KEY,\n value: getPrimaryColor(config),\n });\n return config;\n });\n};\n\nexport const withPrimaryColorStyles: ConfigPlugin = (config) => {\n return withAndroidStyles(config, (config) => {\n config.modResults = assignStylesValue(config.modResults, {\n add: !!getPrimaryColor(config),\n parent: getAppThemeGroup(),\n name: COLOR_PRIMARY_KEY,\n value: `@color/${COLOR_PRIMARY_KEY}`,\n });\n return config;\n });\n};\n\nexport function getPrimaryColor(config: Pick<ExpoConfig, 'primaryColor'>) {\n return config.primaryColor ?? DEFAULT_PRIMARY_COLOR;\n}\n"],"mappings":";;;;;;;AAEA,SAAAA,QAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,OAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AACA,SAAAE,QAAA;EAAA,MAAAF,IAAA,GAAAC,OAAA;EAAAC,OAAA,YAAAA,CAAA;IAAA,OAAAF,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,SAAAG,gBAAA;EAAA,MAAAH,IAAA,GAAAC,OAAA;EAAAE,eAAA,YAAAA,CAAA;IAAA,OAAAH,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,MAAMI,iBAAiB,GAAG,cAAc;AACxC,MAAMC,qBAAqB,GAAG,SAAS;AAEhC,MAAMC,gBAA8B,GAAIC,MAAM,IAAK;EACxDA,MAAM,GAAGC,sBAAsB,CAACD,MAAM,CAAC;EACvCA,MAAM,GAAGE,sBAAsB,CAACF,MAAM,CAAC;EACvC,OAAOA,MAAM;AACf,CAAC;AAACG,OAAA,CAAAJ,gBAAA,GAAAA,gBAAA;AAEK,MAAME,sBAAoC,GAAID,MAAM,IAAK;EAC9D,OAAO,IAAAI,mCAAiB,EAACJ,MAAM,EAAGA,MAAM,IAAK;IAC3CA,MAAM,CAACK,UAAU,GAAG,IAAAC,0BAAgB,EAACN,MAAM,CAACK,UAAU,EAAE;MACtDE,IAAI,EAAEV,iBAAiB;MACvBW,KAAK,EAAEC,eAAe,CAACT,MAAM;IAC/B,CAAC,CAAC;IACF,OAAOA,MAAM;EACf,CAAC,CAAC;AACJ,CAAC;AAACG,OAAA,CAAAF,sBAAA,GAAAA,sBAAA;AAEK,MAAMC,sBAAoC,GAAIF,MAAM,IAAK;EAC9D,OAAO,IAAAU,mCAAiB,EAACV,MAAM,EAAGA,MAAM,IAAK;IAC3CA,MAAM,CAACK,UAAU,GAAG,IAAAM,2BAAiB,EAACX,MAAM,CAACK,UAAU,EAAE;MACvDO,GAAG,EAAE,CAAC,CAACH,eAAe,CAACT,MAAM,CAAC;MAC9Ba,MAAM,EAAE,IAAAC,0BAAgB,EAAC,CAAC;MAC1BP,IAAI,EAAEV,iBAAiB;MACvBW,KAAK,EAAE,UAAUX,iBAAiB;IACpC,CAAC,CAAC;IACF,OAAOG,MAAM;EACf,CAAC,CAAC;AACJ,CAAC;AAACG,OAAA,CAAAD,sBAAA,GAAAA,sBAAA;AAEK,SAASO,eAAeA,CAACT,MAAwC,EAAE;EACxE,OAAOA,MAAM,CAACe,YAAY,IAAIjB,qBAAqB;AACrD","ignoreList":[]}

View File

@@ -0,0 +1,12 @@
export type PropertiesItem = {
type: 'comment';
value: string;
} | {
type: 'empty';
} | {
type: 'property';
key: string;
value: string;
};
export declare function parsePropertiesFile(contents: string): PropertiesItem[];
export declare function propertiesListToString(props: PropertiesItem[]): string;

View File

@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.parsePropertiesFile = parsePropertiesFile;
exports.propertiesListToString = propertiesListToString;
function parsePropertiesFile(contents) {
const propertiesList = [];
const lines = contents.split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (!line) {
propertiesList.push({
type: 'empty'
});
} else if (line.startsWith('#')) {
propertiesList.push({
type: 'comment',
value: line.substring(1).trimStart()
});
} else {
const eok = line.indexOf('=');
const key = line.slice(0, eok);
const value = line.slice(eok + 1, line.length);
propertiesList.push({
type: 'property',
key,
value
});
}
}
return propertiesList;
}
function propertiesListToString(props) {
let output = '';
for (let i = 0; i < props.length; i++) {
const prop = props[i];
if (prop.type === 'empty') {
output += '';
} else if (prop.type === 'comment') {
output += '# ' + prop.value;
} else if (prop.type === 'property') {
output += `${prop.key}=${prop.value}`;
} else {
// @ts-ignore: assertion
throw new Error(`Invalid properties type "${prop.type}"`);
}
if (i < props.length - 1) {
output += '\n';
}
}
return output;
}
//# sourceMappingURL=Properties.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Properties.js","names":["parsePropertiesFile","contents","propertiesList","lines","split","i","length","line","trim","push","type","startsWith","value","substring","trimStart","eok","indexOf","key","slice","propertiesListToString","props","output","prop","Error"],"sources":["../../src/android/Properties.ts"],"sourcesContent":["export type PropertiesItem =\n | {\n type: 'comment';\n value: string;\n }\n | {\n type: 'empty';\n }\n | {\n type: 'property';\n key: string;\n value: string;\n };\n\nexport function parsePropertiesFile(contents: string): PropertiesItem[] {\n const propertiesList: PropertiesItem[] = [];\n const lines = contents.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i].trim();\n if (!line) {\n propertiesList.push({ type: 'empty' });\n } else if (line.startsWith('#')) {\n propertiesList.push({ type: 'comment', value: line.substring(1).trimStart() });\n } else {\n const eok = line.indexOf('=');\n const key = line.slice(0, eok);\n const value = line.slice(eok + 1, line.length);\n propertiesList.push({ type: 'property', key, value });\n }\n }\n\n return propertiesList;\n}\n\nexport function propertiesListToString(props: PropertiesItem[]): string {\n let output = '';\n for (let i = 0; i < props.length; i++) {\n const prop = props[i];\n if (prop.type === 'empty') {\n output += '';\n } else if (prop.type === 'comment') {\n output += '# ' + prop.value;\n } else if (prop.type === 'property') {\n output += `${prop.key}=${prop.value}`;\n } else {\n // @ts-ignore: assertion\n throw new Error(`Invalid properties type \"${prop.type}\"`);\n }\n if (i < props.length - 1) {\n output += '\\n';\n }\n }\n return output;\n}\n"],"mappings":";;;;;;;AAcO,SAASA,mBAAmBA,CAACC,QAAgB,EAAoB;EACtE,MAAMC,cAAgC,GAAG,EAAE;EAC3C,MAAMC,KAAK,GAAGF,QAAQ,CAACG,KAAK,CAAC,IAAI,CAAC;EAClC,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGF,KAAK,CAACG,MAAM,EAAED,CAAC,EAAE,EAAE;IACrC,MAAME,IAAI,GAAGJ,KAAK,CAACE,CAAC,CAAC,CAACG,IAAI,CAAC,CAAC;IAC5B,IAAI,CAACD,IAAI,EAAE;MACTL,cAAc,CAACO,IAAI,CAAC;QAAEC,IAAI,EAAE;MAAQ,CAAC,CAAC;IACxC,CAAC,MAAM,IAAIH,IAAI,CAACI,UAAU,CAAC,GAAG,CAAC,EAAE;MAC/BT,cAAc,CAACO,IAAI,CAAC;QAAEC,IAAI,EAAE,SAAS;QAAEE,KAAK,EAAEL,IAAI,CAACM,SAAS,CAAC,CAAC,CAAC,CAACC,SAAS,CAAC;MAAE,CAAC,CAAC;IAChF,CAAC,MAAM;MACL,MAAMC,GAAG,GAAGR,IAAI,CAACS,OAAO,CAAC,GAAG,CAAC;MAC7B,MAAMC,GAAG,GAAGV,IAAI,CAACW,KAAK,CAAC,CAAC,EAAEH,GAAG,CAAC;MAC9B,MAAMH,KAAK,GAAGL,IAAI,CAACW,KAAK,CAACH,GAAG,GAAG,CAAC,EAAER,IAAI,CAACD,MAAM,CAAC;MAC9CJ,cAAc,CAACO,IAAI,CAAC;QAAEC,IAAI,EAAE,UAAU;QAAEO,GAAG;QAAEL;MAAM,CAAC,CAAC;IACvD;EACF;EAEA,OAAOV,cAAc;AACvB;AAEO,SAASiB,sBAAsBA,CAACC,KAAuB,EAAU;EACtE,IAAIC,MAAM,GAAG,EAAE;EACf,KAAK,IAAIhB,CAAC,GAAG,CAAC,EAAEA,CAAC,GAAGe,KAAK,CAACd,MAAM,EAAED,CAAC,EAAE,EAAE;IACrC,MAAMiB,IAAI,GAAGF,KAAK,CAACf,CAAC,CAAC;IACrB,IAAIiB,IAAI,CAACZ,IAAI,KAAK,OAAO,EAAE;MACzBW,MAAM,IAAI,EAAE;IACd,CAAC,MAAM,IAAIC,IAAI,CAACZ,IAAI,KAAK,SAAS,EAAE;MAClCW,MAAM,IAAI,IAAI,GAAGC,IAAI,CAACV,KAAK;IAC7B,CAAC,MAAM,IAAIU,IAAI,CAACZ,IAAI,KAAK,UAAU,EAAE;MACnCW,MAAM,IAAI,GAAGC,IAAI,CAACL,GAAG,IAAIK,IAAI,CAACV,KAAK,EAAE;IACvC,CAAC,MAAM;MACL;MACA,MAAM,IAAIW,KAAK,CAAC,4BAA4BD,IAAI,CAACZ,IAAI,GAAG,CAAC;IAC3D;IACA,IAAIL,CAAC,GAAGe,KAAK,CAACd,MAAM,GAAG,CAAC,EAAE;MACxBe,MAAM,IAAI,IAAI;IAChB;EACF;EACA,OAAOA,MAAM;AACf","ignoreList":[]}

View File

@@ -0,0 +1,87 @@
import { XMLObject } from '../utils/XML';
export type ResourceGroupXML = {
$: {
name: string;
parent: string;
};
item: ResourceItemXML[];
};
export type ResourceXML = {
resources: {
$?: {
'xmlns:tools'?: string;
};
color?: ResourceItemXML[];
string?: ResourceItemXML[];
style?: ResourceGroupXML[];
};
};
export type ResourceItemXML = {
_: string;
$: {
name: string;
'tools:targetApi'?: string;
translatable?: string;
};
};
/**
* Name of the resource folder.
*/
export type ResourceKind = 'values' | 'values-night' | 'values-v23' | 'values-night-v23' | 'drawable';
/**
* Read an XML file while providing a default fallback for resource files.
*
* @param options path to the XML file, returns a fallback XML if the path doesn't exist.
*/
export declare function readResourcesXMLAsync({ path, fallback, }: {
path: string;
fallback?: string | null;
}): Promise<ResourceXML>;
/**
* Ensure the provided xml has a `resources` object (the expected shape).
*
* @param xml
*/
export declare function ensureDefaultResourceXML(xml: XMLObject): ResourceXML;
/**
* Build a `ResourceItemXML` given its `name` and `value`. This makes things a bit more readable.
*
* - JSON: `{ $: { name }, _: value }`
* - XML: `<item name="NAME">VALUE</item>`
*
* @param props name and value strings.
*/
export declare function buildResourceItem({ name, value, targetApi, translatable, }: {
name: string;
value: string;
targetApi?: string;
translatable?: boolean;
}): ResourceItemXML;
export declare function buildResourceGroup(parent: {
name: string;
parent: string;
items?: ResourceItemXML[];
}): ResourceGroupXML;
export declare function findResourceGroup(xml: ResourceGroupXML[] | undefined, group: {
name: string;
parent?: string;
}): ResourceGroupXML | null;
/**
* Helper to convert a basic XML object into a simple k/v pair.
*
* @param xml
* @returns
*/
export declare function getResourceItemsAsObject(xml: ResourceItemXML[]): Record<string, string> | null;
/**
* Helper to convert a basic k/v object to a ResourceItemXML array.
*
* @param xml
* @returns
*/
export declare function getObjectAsResourceItems(obj: Record<string, string>): ResourceItemXML[];
export declare function getObjectAsResourceGroup(group: {
name: string;
parent: string;
item: Record<string, string>;
}): ResourceGroupXML;

View File

@@ -0,0 +1,150 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildResourceGroup = buildResourceGroup;
exports.buildResourceItem = buildResourceItem;
exports.ensureDefaultResourceXML = ensureDefaultResourceXML;
exports.findResourceGroup = findResourceGroup;
exports.getObjectAsResourceGroup = getObjectAsResourceGroup;
exports.getObjectAsResourceItems = getObjectAsResourceItems;
exports.getResourceItemsAsObject = getResourceItemsAsObject;
exports.readResourcesXMLAsync = readResourcesXMLAsync;
function _XML() {
const data = require("../utils/XML");
_XML = function () {
return data;
};
return data;
}
/**
* Name of the resource folder.
*/
const fallbackResourceString = `<?xml version="1.0" encoding="utf-8"?><resources></resources>`;
/**
* Read an XML file while providing a default fallback for resource files.
*
* @param options path to the XML file, returns a fallback XML if the path doesn't exist.
*/
async function readResourcesXMLAsync({
path,
fallback = fallbackResourceString
}) {
const xml = await (0, _XML().readXMLAsync)({
path,
fallback
});
// Ensure the type is expected.
if (!xml.resources) {
xml.resources = {};
}
return xml;
}
/**
* Ensure the provided xml has a `resources` object (the expected shape).
*
* @param xml
*/
function ensureDefaultResourceXML(xml) {
if (!xml) {
xml = {
resources: {}
};
}
if (!xml.resources) {
xml.resources = {};
}
return xml;
}
/**
* Build a `ResourceItemXML` given its `name` and `value`. This makes things a bit more readable.
*
* - JSON: `{ $: { name }, _: value }`
* - XML: `<item name="NAME">VALUE</item>`
*
* @param props name and value strings.
*/
function buildResourceItem({
name,
value,
targetApi,
translatable
}) {
const item = {
$: {
name
},
_: value
};
if (targetApi) {
item.$['tools:targetApi'] = targetApi;
}
if (translatable !== undefined) {
item.$['translatable'] = String(translatable);
}
return item;
}
function buildResourceGroup(parent) {
return {
$: {
name: parent.name,
parent: parent.parent
},
item: parent.items ?? []
};
}
function findResourceGroup(xml, group) {
const app = xml?.filter?.(({
$: head
}) => {
let matches = head.name === group.name;
if (group.parent != null && matches) {
matches = head.parent === group.parent;
}
return matches;
})?.[0];
return app ?? null;
}
/**
* Helper to convert a basic XML object into a simple k/v pair.
*
* @param xml
* @returns
*/
function getResourceItemsAsObject(xml) {
return xml.reduce((prev, curr) => ({
...prev,
[curr.$.name]: curr._
}), {});
}
/**
* Helper to convert a basic k/v object to a ResourceItemXML array.
*
* @param xml
* @returns
*/
function getObjectAsResourceItems(obj) {
return Object.entries(obj).map(([name, value]) => ({
$: {
name
},
_: value
}));
}
function getObjectAsResourceGroup(group) {
return {
$: {
name: group.name,
parent: group.parent
},
item: getObjectAsResourceItems(group.item)
};
}
//# sourceMappingURL=Resources.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,20 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
export type IntentFilterProps = {
actions: string[];
categories: string[];
data: {
scheme: string;
host?: string;
}[];
};
export declare const withScheme: import("..").ConfigPlugin;
export declare function getScheme(config: {
scheme?: string | string[];
}): string[];
export declare function setScheme(config: Pick<ExpoConfig, 'scheme' | 'android'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function getSchemesFromManifest(androidManifest: AndroidManifest, requestedHost?: string | null): string[];
export declare function ensureManifestHasValidIntentFilter(androidManifest: AndroidManifest): boolean;
export declare function hasScheme(scheme: string, androidManifest: AndroidManifest): boolean;
export declare function appendScheme(scheme: string, androidManifest: AndroidManifest): AndroidManifest;
export declare function removeScheme(scheme: string, androidManifest: AndroidManifest): AndroidManifest;

View File

@@ -0,0 +1,215 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.appendScheme = appendScheme;
exports.ensureManifestHasValidIntentFilter = ensureManifestHasValidIntentFilter;
exports.getScheme = getScheme;
exports.getSchemesFromManifest = getSchemesFromManifest;
exports.hasScheme = hasScheme;
exports.removeScheme = removeScheme;
exports.setScheme = setScheme;
exports.withScheme = void 0;
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
function _warnings() {
const data = require("../utils/warnings");
_warnings = function () {
return data;
};
return data;
}
const withScheme = exports.withScheme = (0, _androidPlugins().createAndroidManifestPlugin)(setScheme, 'withScheme');
function getScheme(config) {
if (Array.isArray(config.scheme)) {
const validate = value => typeof value === 'string';
return config.scheme.filter(validate);
} else if (typeof config.scheme === 'string') {
return [config.scheme];
}
return [];
}
// This plugin used to remove the unused schemes but this is unpredictable because other plugins could add schemes.
// The only way to reliably remove schemes from the project is to nuke the file and regenerate the code (`npx expo prebuild --clean`).
// Regardless, having extra schemes isn't a fatal issue and therefore a tolerable compromise is to just add new schemes that aren't currently present.
function setScheme(config, androidManifest) {
const schemes = [...getScheme(config), ...getScheme(config.android ?? {})];
if (schemes.length === 0) {
return androidManifest;
}
if (!ensureManifestHasValidIntentFilter(androidManifest)) {
(0, _warnings().addWarningAndroid)('scheme', `Cannot add schemes because the provided manifest does not have a valid Activity with \`android:launchMode="singleTask"\``, 'https://expo.fyi/setup-android-uri-scheme');
return androidManifest;
}
// Get the current schemes and remove them from the list of schemes to add.
const currentSchemes = getSchemesFromManifest(androidManifest);
for (const uri of currentSchemes) {
const index = schemes.indexOf(uri);
if (index > -1) schemes.splice(index, 1);
}
// Now add all of the remaining schemes.
for (const uri of schemes) {
androidManifest = appendScheme(uri, androidManifest);
}
return androidManifest;
}
function isValidRedirectIntentFilter({
actions,
categories
}) {
return actions.includes('android.intent.action.VIEW') && !categories.includes('android.intent.category.LAUNCHER');
}
function propertiesFromIntentFilter(intentFilter) {
const actions = intentFilter?.action?.map(data => data?.$?.['android:name']) ?? [];
const categories = intentFilter?.category?.map(data => data?.$?.['android:name']) ?? [];
const data = intentFilter?.data?.filter(data => data?.$?.['android:scheme'])?.map(data => ({
scheme: data?.$?.['android:scheme'],
host: data?.$?.['android:host']
})) ?? [];
return {
actions,
categories,
data
};
}
function getSingleTaskIntentFilters(androidManifest) {
if (!Array.isArray(androidManifest.manifest.application)) return [];
let outputSchemes = [];
for (const application of androidManifest.manifest.application) {
const {
activity
} = application;
// @ts-ignore
const activities = Array.isArray(activity) ? activity : [activity];
const singleTaskActivities = activities.filter(activity => activity?.$?.['android:launchMode'] === 'singleTask');
for (const activity of singleTaskActivities) {
const intentFilters = activity['intent-filter'];
outputSchemes = outputSchemes.concat(intentFilters);
}
}
return outputSchemes;
}
function getSchemesFromManifest(androidManifest, requestedHost = null) {
const outputSchemes = [];
const singleTaskIntentFilters = getSingleTaskIntentFilters(androidManifest);
for (const intentFilter of singleTaskIntentFilters) {
const properties = propertiesFromIntentFilter(intentFilter);
if (isValidRedirectIntentFilter(properties) && properties.data) {
for (const {
scheme,
host
} of properties.data) {
if (requestedHost === null || !host || host === requestedHost) {
outputSchemes.push(scheme);
}
}
}
}
return outputSchemes;
}
function ensureManifestHasValidIntentFilter(androidManifest) {
if (!Array.isArray(androidManifest.manifest.application)) {
return false;
}
for (const application of androidManifest.manifest.application) {
for (const activity of application.activity || []) {
if (activity?.$?.['android:launchMode'] === 'singleTask') {
for (const intentFilter of activity['intent-filter'] || []) {
// Parse valid intent filters...
const properties = propertiesFromIntentFilter(intentFilter);
if (isValidRedirectIntentFilter(properties)) {
return true;
}
}
if (!activity['intent-filter']) {
activity['intent-filter'] = [];
}
activity['intent-filter'].push({
action: [{
$: {
'android:name': 'android.intent.action.VIEW'
}
}],
category: [{
$: {
'android:name': 'android.intent.category.DEFAULT'
}
}, {
$: {
'android:name': 'android.intent.category.BROWSABLE'
}
}]
});
return true;
}
}
}
return false;
}
function hasScheme(scheme, androidManifest) {
const schemes = getSchemesFromManifest(androidManifest);
return schemes.includes(scheme);
}
function appendScheme(scheme, androidManifest) {
if (!Array.isArray(androidManifest.manifest.application)) {
return androidManifest;
}
if (!ensureManifestHasValidIntentFilter(androidManifest)) {
(0, _warnings().addWarningAndroid)('scheme', `Cannot add schemes because the provided manifest does not have a valid Activity with \`android:launchMode="singleTask"\``, 'https://expo.fyi/setup-android-uri-scheme');
return androidManifest;
}
for (const application of androidManifest.manifest.application) {
for (const activity of application.activity || []) {
if (activity?.$?.['android:launchMode'] === 'singleTask') {
for (const intentFilter of activity['intent-filter'] || []) {
const properties = propertiesFromIntentFilter(intentFilter);
if (isValidRedirectIntentFilter(properties)) {
if (!intentFilter.data) intentFilter.data = [];
intentFilter.data.push({
$: {
'android:scheme': scheme
}
});
}
}
break;
}
}
}
return androidManifest;
}
function removeScheme(scheme, androidManifest) {
if (!Array.isArray(androidManifest.manifest.application)) {
return androidManifest;
}
for (const application of androidManifest.manifest.application) {
for (const activity of application.activity || []) {
if (activity?.$?.['android:launchMode'] === 'singleTask') {
for (const intentFilter of activity['intent-filter'] || []) {
// Parse valid intent filters...
const properties = propertiesFromIntentFilter(intentFilter);
if (isValidRedirectIntentFilter(properties)) {
for (const dataKey in intentFilter?.data || []) {
const data = intentFilter.data?.[dataKey];
if (data?.$?.['android:scheme'] === scheme) {
delete intentFilter.data?.[dataKey];
}
}
}
}
break;
}
}
}
return androidManifest;
}
//# sourceMappingURL=Scheme.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import { ExpoConfig } from '@expo/config-types';
import { ResourceXML } from './Resources';
import { ConfigPlugin } from '../Plugin.types';
export declare const withStatusBar: ConfigPlugin;
export declare function setStatusBarStyles(config: Pick<ExpoConfig, 'androidStatusBar'>, styles: ResourceXML): ResourceXML;
export declare function getStatusBarStyle(config: Pick<ExpoConfig, 'androidStatusBar'>): "light-content" | "dark-content";

View File

@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getStatusBarStyle = getStatusBarStyle;
exports.setStatusBarStyles = setStatusBarStyles;
exports.withStatusBar = void 0;
function _Styles() {
const data = require("./Styles");
_Styles = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
function WarningAggregator() {
const data = _interopRequireWildcard(require("../utils/warnings"));
WarningAggregator = 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; }
const TAG = 'STATUS_BAR_PLUGIN';
// https://developer.android.com/reference/android/R.attr#windowLightStatusBar
const WINDOW_LIGHT_STATUS_BAR = 'android:windowLightStatusBar';
// https://developer.android.com/reference/android/R.attr#statusBarColor
const STATUS_BAR_COLOR = 'android:statusBarColor';
const withStatusBar = config => {
const {
androidStatusBar = {}
} = config;
if ('backgroundColor' in androidStatusBar) {
WarningAggregator().addWarningAndroid(TAG, 'Due to Android edge-to-edge enforcement, `androidStatusBar.backgroundColor` is deprecated and has no effect. This will be removed in a future release.');
}
if ('translucent' in androidStatusBar) {
WarningAggregator().addWarningAndroid(TAG, 'Due to Android edge-to-edge enforcement, `androidStatusBar.translucent` is deprecated and has no effect. This will be removed in a future release.');
}
config = withStatusBarStyles(config);
return config;
};
exports.withStatusBar = withStatusBar;
const withStatusBarStyles = config => {
return (0, _androidPlugins().withAndroidStyles)(config, config => {
config.modResults = setStatusBarStyles(config, config.modResults);
return config;
});
};
function setStatusBarStyles(config, styles) {
styles = (0, _Styles().assignStylesValue)(styles, {
parent: (0, _Styles().getAppThemeGroup)(),
name: WINDOW_LIGHT_STATUS_BAR,
value: 'true',
// Default is light-content, don't need to do anything to set it
add: getStatusBarStyle(config) === 'dark-content'
});
styles = (0, _Styles().assignStylesValue)(styles, {
parent: (0, _Styles().getAppThemeGroup)(),
name: STATUS_BAR_COLOR,
value: '@android:color/transparent',
add: true
});
return styles;
}
function getStatusBarStyle(config) {
return config.androidStatusBar?.barStyle || 'light-content';
}
//# sourceMappingURL=StatusBar.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import { ResourceItemXML, ResourceKind, ResourceXML } from './Resources';
export declare function getProjectStringsXMLPathAsync(projectRoot: string, { kind }?: {
kind?: ResourceKind;
}): Promise<string>;
export declare function setStringItem(itemToAdd: ResourceItemXML[], stringFileContentsJSON: ResourceXML): ResourceXML;
export declare function removeStringItem(named: string, stringFileContentsJSON: ResourceXML): ResourceXML;

View File

@@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getProjectStringsXMLPathAsync = getProjectStringsXMLPathAsync;
exports.removeStringItem = removeStringItem;
exports.setStringItem = setStringItem;
function _Paths() {
const data = require("./Paths");
_Paths = function () {
return data;
};
return data;
}
async function getProjectStringsXMLPathAsync(projectRoot, {
kind
} = {}) {
return (0, _Paths().getResourceXMLPathAsync)(projectRoot, {
kind,
name: 'strings'
});
}
function setStringItem(itemToAdd, stringFileContentsJSON) {
if (!stringFileContentsJSON?.resources?.string) {
if (!stringFileContentsJSON.resources || typeof stringFileContentsJSON.resources === 'string') {
// file was empty and JSON is `{resources : ''}`
stringFileContentsJSON.resources = {};
}
stringFileContentsJSON.resources.string = itemToAdd;
return stringFileContentsJSON;
}
for (const newItem of itemToAdd) {
const stringNameExists = stringFileContentsJSON.resources.string.findIndex(e => e.$.name === newItem.$.name);
if (stringNameExists > -1) {
// replace the previous item
stringFileContentsJSON.resources.string[stringNameExists] = newItem;
} else {
stringFileContentsJSON.resources.string = stringFileContentsJSON.resources.string.concat(newItem);
}
}
return stringFileContentsJSON;
}
function removeStringItem(named, stringFileContentsJSON) {
if (stringFileContentsJSON?.resources?.string) {
const stringNameExists = stringFileContentsJSON.resources.string.findIndex(e => e.$.name === named);
if (stringNameExists > -1) {
// replace the previous value
stringFileContentsJSON.resources.string.splice(stringNameExists, 1);
}
}
return stringFileContentsJSON;
}
//# sourceMappingURL=Strings.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"Strings.js","names":["_Paths","data","require","getProjectStringsXMLPathAsync","projectRoot","kind","getResourceXMLPathAsync","name","setStringItem","itemToAdd","stringFileContentsJSON","resources","string","newItem","stringNameExists","findIndex","e","$","concat","removeStringItem","named","splice"],"sources":["../../src/android/Strings.ts"],"sourcesContent":["import { getResourceXMLPathAsync } from './Paths';\nimport { ResourceItemXML, ResourceKind, ResourceXML } from './Resources';\n\nexport async function getProjectStringsXMLPathAsync(\n projectRoot: string,\n { kind }: { kind?: ResourceKind } = {}\n): Promise<string> {\n return getResourceXMLPathAsync(projectRoot, { kind, name: 'strings' });\n}\n\nexport function setStringItem(\n itemToAdd: ResourceItemXML[],\n stringFileContentsJSON: ResourceXML\n): ResourceXML {\n if (!stringFileContentsJSON?.resources?.string) {\n if (!stringFileContentsJSON.resources || typeof stringFileContentsJSON.resources === 'string') {\n // file was empty and JSON is `{resources : ''}`\n stringFileContentsJSON.resources = {};\n }\n stringFileContentsJSON.resources.string = itemToAdd;\n return stringFileContentsJSON;\n }\n\n for (const newItem of itemToAdd) {\n const stringNameExists = stringFileContentsJSON.resources.string.findIndex(\n (e: ResourceItemXML) => e.$.name === newItem.$.name\n );\n if (stringNameExists > -1) {\n // replace the previous item\n stringFileContentsJSON.resources.string[stringNameExists] = newItem;\n } else {\n stringFileContentsJSON.resources.string =\n stringFileContentsJSON.resources.string.concat(newItem);\n }\n }\n return stringFileContentsJSON;\n}\n\nexport function removeStringItem(named: string, stringFileContentsJSON: ResourceXML): ResourceXML {\n if (stringFileContentsJSON?.resources?.string) {\n const stringNameExists = stringFileContentsJSON.resources.string.findIndex(\n (e: ResourceItemXML) => e.$.name === named\n );\n if (stringNameExists > -1) {\n // replace the previous value\n stringFileContentsJSON.resources.string.splice(stringNameExists, 1);\n }\n }\n return stringFileContentsJSON;\n}\n"],"mappings":";;;;;;;;AAAA,SAAAA,OAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,MAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAGO,eAAeE,6BAA6BA,CACjDC,WAAmB,EACnB;EAAEC;AAA8B,CAAC,GAAG,CAAC,CAAC,EACrB;EACjB,OAAO,IAAAC,gCAAuB,EAACF,WAAW,EAAE;IAAEC,IAAI;IAAEE,IAAI,EAAE;EAAU,CAAC,CAAC;AACxE;AAEO,SAASC,aAAaA,CAC3BC,SAA4B,EAC5BC,sBAAmC,EACtB;EACb,IAAI,CAACA,sBAAsB,EAAEC,SAAS,EAAEC,MAAM,EAAE;IAC9C,IAAI,CAACF,sBAAsB,CAACC,SAAS,IAAI,OAAOD,sBAAsB,CAACC,SAAS,KAAK,QAAQ,EAAE;MAC7F;MACAD,sBAAsB,CAACC,SAAS,GAAG,CAAC,CAAC;IACvC;IACAD,sBAAsB,CAACC,SAAS,CAACC,MAAM,GAAGH,SAAS;IACnD,OAAOC,sBAAsB;EAC/B;EAEA,KAAK,MAAMG,OAAO,IAAIJ,SAAS,EAAE;IAC/B,MAAMK,gBAAgB,GAAGJ,sBAAsB,CAACC,SAAS,CAACC,MAAM,CAACG,SAAS,CACvEC,CAAkB,IAAKA,CAAC,CAACC,CAAC,CAACV,IAAI,KAAKM,OAAO,CAACI,CAAC,CAACV,IACjD,CAAC;IACD,IAAIO,gBAAgB,GAAG,CAAC,CAAC,EAAE;MACzB;MACAJ,sBAAsB,CAACC,SAAS,CAACC,MAAM,CAACE,gBAAgB,CAAC,GAAGD,OAAO;IACrE,CAAC,MAAM;MACLH,sBAAsB,CAACC,SAAS,CAACC,MAAM,GACrCF,sBAAsB,CAACC,SAAS,CAACC,MAAM,CAACM,MAAM,CAACL,OAAO,CAAC;IAC3D;EACF;EACA,OAAOH,sBAAsB;AAC/B;AAEO,SAASS,gBAAgBA,CAACC,KAAa,EAAEV,sBAAmC,EAAe;EAChG,IAAIA,sBAAsB,EAAEC,SAAS,EAAEC,MAAM,EAAE;IAC7C,MAAME,gBAAgB,GAAGJ,sBAAsB,CAACC,SAAS,CAACC,MAAM,CAACG,SAAS,CACvEC,CAAkB,IAAKA,CAAC,CAACC,CAAC,CAACV,IAAI,KAAKa,KACvC,CAAC;IACD,IAAIN,gBAAgB,GAAG,CAAC,CAAC,EAAE;MACzB;MACAJ,sBAAsB,CAACC,SAAS,CAACC,MAAM,CAACS,MAAM,CAACP,gBAAgB,EAAE,CAAC,CAAC;IACrE;EACF;EACA,OAAOJ,sBAAsB;AAC/B","ignoreList":[]}

View File

@@ -0,0 +1,69 @@
import { ResourceGroupXML, ResourceItemXML, ResourceKind, ResourceXML } from './Resources';
export declare function readStylesXMLAsync({ path, fallback, }: {
path: string;
fallback?: string | null;
}): Promise<ResourceXML>;
export declare function getProjectStylesXMLPathAsync(projectRoot: string, { kind }?: {
kind?: ResourceKind;
}): Promise<string>;
export declare function getStyleParent(xml: ResourceXML, group: {
name: string;
parent?: string;
}): ResourceGroupXML | null;
export declare function getStylesItem({ name, xml, parent, }: {
name: string;
xml: ResourceXML;
parent: {
name: string;
parent?: string;
};
}): ResourceItemXML | null;
export declare function setStylesItem({ item, xml, parent, }: {
item: ResourceItemXML;
xml: ResourceXML;
parent: {
name: string;
parent?: string;
};
}): ResourceXML;
export declare function removeStylesItem({ name, xml, parent, }: {
name: string;
xml: ResourceXML;
parent: {
name: string;
parent?: string;
};
}): ResourceXML;
/**
* @deprecated Use `getAppThemeGroup` instead.
* Matching on both style name and parent leads to prebuild issues, as `AppTheme`
* style parent might be changed (when edge-to-edge is enabled, for example).
*/
export declare function getAppThemeLightNoActionBarGroup(): {
name: string;
parent: string;
};
export declare function getAppThemeGroup(): {
name: string;
};
export declare function assignStylesValue(xml: ResourceXML, { add, value, targetApi, name, parent, }: {
add: boolean;
value: string;
targetApi?: string;
name: string;
parent: {
name: string;
parent?: string;
};
}): ResourceXML;
/**
* Helper to convert a styles.xml parent's children into a simple k/v pair.
* Added for testing purposes.
*
* @param xml
* @returns
*/
export declare function getStylesGroupAsObject(xml: ResourceXML, group: {
name: string;
parent?: string;
}): Record<string, string> | null;

View File

@@ -0,0 +1,185 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.assignStylesValue = assignStylesValue;
exports.getAppThemeGroup = getAppThemeGroup;
exports.getAppThemeLightNoActionBarGroup = getAppThemeLightNoActionBarGroup;
exports.getProjectStylesXMLPathAsync = getProjectStylesXMLPathAsync;
exports.getStyleParent = getStyleParent;
exports.getStylesGroupAsObject = getStylesGroupAsObject;
exports.getStylesItem = getStylesItem;
exports.readStylesXMLAsync = readStylesXMLAsync;
exports.removeStylesItem = removeStylesItem;
exports.setStylesItem = setStylesItem;
function _Paths() {
const data = require("./Paths");
_Paths = function () {
return data;
};
return data;
}
function _Resources() {
const data = require("./Resources");
_Resources = function () {
return data;
};
return data;
}
// Adds support for `tools:x`
const fallbackResourceString = `<?xml version="1.0" encoding="utf-8"?><resources xmlns:tools="http://schemas.android.com/tools"></resources>`;
async function readStylesXMLAsync({
path,
fallback = fallbackResourceString
}) {
return (0, _Resources().readResourcesXMLAsync)({
path,
fallback
});
}
async function getProjectStylesXMLPathAsync(projectRoot, {
kind
} = {}) {
return (0, _Paths().getResourceXMLPathAsync)(projectRoot, {
kind,
name: 'styles'
});
}
function ensureDefaultStyleResourceXML(xml) {
xml = (0, _Resources().ensureDefaultResourceXML)(xml);
if (!Array.isArray(xml?.resources?.style)) {
xml.resources.style = [];
}
return xml;
}
function getStyleParent(xml, group) {
return (0, _Resources().findResourceGroup)(xml.resources.style, group);
}
function getStylesItem({
name,
xml,
parent
}) {
xml = ensureDefaultStyleResourceXML(xml);
const appTheme = getStyleParent(xml, parent);
if (!appTheme) {
return null;
}
if (appTheme.item) {
const existingItem = appTheme.item.filter(({
$: head
}) => head.name === name)[0];
// Don't want to 2 of the same item, so if one exists, we overwrite it
if (existingItem) {
return existingItem;
}
}
return null;
}
function setStylesItem({
item,
xml,
parent
}) {
xml = ensureDefaultStyleResourceXML(xml);
let appTheme = getStyleParent(xml, parent);
if (!appTheme) {
appTheme = (0, _Resources().buildResourceGroup)({
parent: 'Theme.AppCompat.Light.NoActionBar',
// Default AppTheme parent
...parent
});
xml.resources.style.push(appTheme);
}
if (appTheme.item) {
const existingItem = appTheme.item.filter(({
$: head
}) => head.name === item.$.name)[0];
// Don't want to 2 of the same item, so if one exists, we overwrite it
if (existingItem) {
existingItem._ = item._;
existingItem.$ = item.$;
} else {
appTheme.item.push(item);
}
} else {
appTheme.item = [item];
}
return xml;
}
function removeStylesItem({
name,
xml,
parent
}) {
xml = ensureDefaultStyleResourceXML(xml);
const appTheme = getStyleParent(xml, parent);
if (appTheme?.item) {
const index = appTheme.item.findIndex(({
$: head
}) => head.name === name);
if (index > -1) {
appTheme.item.splice(index, 1);
}
}
return xml;
}
/**
* @deprecated Use `getAppThemeGroup` instead.
* Matching on both style name and parent leads to prebuild issues, as `AppTheme`
* style parent might be changed (when edge-to-edge is enabled, for example).
*/
function getAppThemeLightNoActionBarGroup() {
return {
name: 'AppTheme',
parent: 'Theme.AppCompat.Light.NoActionBar'
};
}
// This is a very common theme so make it reusable.
function getAppThemeGroup() {
return {
name: 'AppTheme'
};
}
function assignStylesValue(xml, {
add,
value,
targetApi,
name,
parent
}) {
if (add) {
return setStylesItem({
xml,
parent,
item: (0, _Resources().buildResourceItem)({
name,
targetApi,
value
})
});
}
return removeStylesItem({
xml,
parent,
name
});
}
/**
* Helper to convert a styles.xml parent's children into a simple k/v pair.
* Added for testing purposes.
*
* @param xml
* @returns
*/
function getStylesGroupAsObject(xml, group) {
const xmlGroup = getStyleParent(xml, group);
return xmlGroup?.item ? (0, _Resources().getResourceItemsAsObject)(xmlGroup.item) : null;
}
//# sourceMappingURL=Styles.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
import { AndroidManifest } from './Manifest';
import { ResourceXML } from './Resources';
import * as Resources from './Resources';
import { ConfigPlugin, ExportedConfigWithProps } from '../Plugin.types';
import { ExpoConfigUpdates } from '../utils/Updates';
export declare enum Config {
ENABLED = "expo.modules.updates.ENABLED",
CHECK_ON_LAUNCH = "expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH",
LAUNCH_WAIT_MS = "expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS",
RUNTIME_VERSION = "expo.modules.updates.EXPO_RUNTIME_VERSION",
UPDATE_URL = "expo.modules.updates.EXPO_UPDATE_URL",
UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY = "expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY",
UPDATES_HAS_EMBEDDED_UPDATE = "expo.modules.updates.HAS_EMBEDDED_UPDATE",
CODE_SIGNING_CERTIFICATE = "expo.modules.updates.CODE_SIGNING_CERTIFICATE",
CODE_SIGNING_METADATA = "expo.modules.updates.CODE_SIGNING_METADATA",
DISABLE_ANTI_BRICKING_MEASURES = "expo.modules.updates.DISABLE_ANTI_BRICKING_MEASURES",
BSDIFF_PATCH_SUPPORT = "expo.modules.updates.ENABLE_BSDIFF_PATCH_SUPPORT"
}
export declare const withUpdates: ConfigPlugin;
export declare function applyRuntimeVersionFromConfigAsync(config: ExportedConfigWithProps<Resources.ResourceXML>, stringsJSON: ResourceXML): Promise<ResourceXML>;
export declare function applyRuntimeVersionFromConfigForProjectRootAsync(projectRoot: string, config: ExpoConfigUpdates, stringsJSON: ResourceXML): Promise<ResourceXML>;
export declare function setUpdatesConfigAsync(projectRoot: string, config: ExpoConfigUpdates, androidManifest: AndroidManifest, expoUpdatesPackageVersion?: string | null): Promise<AndroidManifest>;
export declare function setVersionsConfigAsync(projectRoot: string, config: Pick<ExpoConfigUpdates, 'sdkVersion' | 'runtimeVersion'>, androidManifest: AndroidManifest): Promise<AndroidManifest>;

View File

@@ -0,0 +1,188 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Config = void 0;
exports.applyRuntimeVersionFromConfigAsync = applyRuntimeVersionFromConfigAsync;
exports.applyRuntimeVersionFromConfigForProjectRootAsync = applyRuntimeVersionFromConfigForProjectRootAsync;
exports.setUpdatesConfigAsync = setUpdatesConfigAsync;
exports.setVersionsConfigAsync = setVersionsConfigAsync;
exports.withUpdates = void 0;
function _BuildProperties() {
const data = require("./BuildProperties");
_BuildProperties = function () {
return data;
};
return data;
}
function _Manifest() {
const data = require("./Manifest");
_Manifest = function () {
return data;
};
return data;
}
function _Resources() {
const data = require("./Resources");
_Resources = function () {
return data;
};
return data;
}
function _Strings() {
const data = require("./Strings");
_Strings = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
function _withPlugins() {
const data = require("../plugins/withPlugins");
_withPlugins = function () {
return data;
};
return data;
}
function _Updates() {
const data = require("../utils/Updates");
_Updates = function () {
return data;
};
return data;
}
function _warnings() {
const data = require("../utils/warnings");
_warnings = function () {
return data;
};
return data;
}
let Config = exports.Config = /*#__PURE__*/function (Config) {
Config["ENABLED"] = "expo.modules.updates.ENABLED";
Config["CHECK_ON_LAUNCH"] = "expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH";
Config["LAUNCH_WAIT_MS"] = "expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS";
Config["RUNTIME_VERSION"] = "expo.modules.updates.EXPO_RUNTIME_VERSION";
Config["UPDATE_URL"] = "expo.modules.updates.EXPO_UPDATE_URL";
Config["UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY"] = "expo.modules.updates.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY";
Config["UPDATES_HAS_EMBEDDED_UPDATE"] = "expo.modules.updates.HAS_EMBEDDED_UPDATE";
Config["CODE_SIGNING_CERTIFICATE"] = "expo.modules.updates.CODE_SIGNING_CERTIFICATE";
Config["CODE_SIGNING_METADATA"] = "expo.modules.updates.CODE_SIGNING_METADATA";
Config["DISABLE_ANTI_BRICKING_MEASURES"] = "expo.modules.updates.DISABLE_ANTI_BRICKING_MEASURES";
Config["BSDIFF_PATCH_SUPPORT"] = "expo.modules.updates.ENABLE_BSDIFF_PATCH_SUPPORT";
return Config;
}({}); // when making changes to this config plugin, ensure the same changes are also made in eas-cli and build-tools
// Also ensure the docs are up-to-date: https://docs.expo.dev/bare/installing-updates/
const withUpdates = config => {
return (0, _withPlugins().withPlugins)(config, [withUpdatesManifest, withRuntimeVersionResource, withUpdatesNativeDebugGradleProps]);
};
/**
* A config-plugin to update `android/gradle.properties` from the `updates.useNativeDebug` in expo config
*/
exports.withUpdates = withUpdates;
const withUpdatesNativeDebugGradleProps = (0, _BuildProperties().createBuildGradlePropsConfigPlugin)([{
propName: 'EX_UPDATES_NATIVE_DEBUG',
propValueGetter: config => config?.updates?.useNativeDebug === true ? 'true' : undefined
}], 'withUpdatesNativeDebugGradleProps');
const withUpdatesManifest = config => {
return (0, _androidPlugins().withAndroidManifest)(config, async config => {
const projectRoot = config.modRequest.projectRoot;
const expoUpdatesPackageVersion = (0, _Updates().getExpoUpdatesPackageVersion)(projectRoot);
config.modResults = await setUpdatesConfigAsync(projectRoot, config, config.modResults, expoUpdatesPackageVersion);
return config;
});
};
const withRuntimeVersionResource = (0, _androidPlugins().createStringsXmlPlugin)(applyRuntimeVersionFromConfigAsync, 'withRuntimeVersionResource');
async function applyRuntimeVersionFromConfigAsync(config, stringsJSON) {
const projectRoot = config.modRequest.projectRoot;
return await applyRuntimeVersionFromConfigForProjectRootAsync(projectRoot, config, stringsJSON);
}
async function applyRuntimeVersionFromConfigForProjectRootAsync(projectRoot, config, stringsJSON) {
const runtimeVersion = await (0, _Updates().getRuntimeVersionNullableAsync)(projectRoot, config, 'android');
if (runtimeVersion) {
return (0, _Strings().setStringItem)([(0, _Resources().buildResourceItem)({
name: 'expo_runtime_version',
value: runtimeVersion
})], stringsJSON);
}
return (0, _Strings().removeStringItem)('expo_runtime_version', stringsJSON);
}
async function setUpdatesConfigAsync(projectRoot, config, androidManifest, expoUpdatesPackageVersion) {
const mainApplication = (0, _Manifest().getMainApplicationOrThrow)(androidManifest);
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.ENABLED, String((0, _Updates().getUpdatesEnabled)(config)));
const checkOnLaunch = (0, _Updates().getUpdatesCheckOnLaunch)(config, expoUpdatesPackageVersion);
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.CHECK_ON_LAUNCH, checkOnLaunch);
const timeout = (0, _Updates().getUpdatesTimeout)(config);
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.LAUNCH_WAIT_MS, String(timeout));
const useEmbeddedUpdate = (0, _Updates().getUpdatesUseEmbeddedUpdate)(config);
if (useEmbeddedUpdate) {
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, Config.UPDATES_HAS_EMBEDDED_UPDATE);
} else {
// TODO: is there a better place for this validation?
if (timeout === 0 && checkOnLaunch !== 'ALWAYS') {
(0, _warnings().addWarningAndroid)('updates.useEmbeddedUpdate', `updates.checkOnLaunch should be set to "ON_LOAD" and updates.fallbackToCacheTimeout should be set to a non-zero value when updates.useEmbeddedUpdate is set to false. This is because an update must be fetched on the initial launch, when no embedded update is available.`);
}
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.UPDATES_HAS_EMBEDDED_UPDATE, 'false');
}
const updateUrl = (0, _Updates().getUpdateUrl)(config);
if (updateUrl) {
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.UPDATE_URL, updateUrl);
} else {
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, Config.UPDATE_URL);
}
const codeSigningCertificate = (0, _Updates().getUpdatesCodeSigningCertificate)(projectRoot, config);
if (codeSigningCertificate) {
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.CODE_SIGNING_CERTIFICATE, codeSigningCertificate);
} else {
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, Config.CODE_SIGNING_CERTIFICATE);
}
const codeSigningMetadata = (0, _Updates().getUpdatesCodeSigningMetadataStringified)(config);
if (codeSigningMetadata) {
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.CODE_SIGNING_METADATA, codeSigningMetadata);
} else {
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, Config.CODE_SIGNING_METADATA);
}
const requestHeaders = (0, _Updates().getUpdatesRequestHeadersStringified)(config);
if (requestHeaders) {
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY, requestHeaders);
} else {
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, Config.UPDATES_CONFIGURATION_REQUEST_HEADERS_KEY);
}
const disableAntiBrickingMeasures = (0, _Updates().getDisableAntiBrickingMeasures)(config);
if (disableAntiBrickingMeasures) {
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.DISABLE_ANTI_BRICKING_MEASURES, 'true');
} else {
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, Config.DISABLE_ANTI_BRICKING_MEASURES);
}
const bsPatchSupport = (0, _Updates().getUpdatesBsdiffPatchSupportEnabled)(config);
if (bsPatchSupport) {
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.BSDIFF_PATCH_SUPPORT, 'true');
} else {
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, Config.BSDIFF_PATCH_SUPPORT);
}
return await setVersionsConfigAsync(projectRoot, config, androidManifest);
}
async function setVersionsConfigAsync(projectRoot, config, androidManifest) {
const mainApplication = (0, _Manifest().getMainApplicationOrThrow)(androidManifest);
const runtimeVersion = await (0, _Updates().getRuntimeVersionNullableAsync)(projectRoot, config, 'android');
if (!runtimeVersion && (0, _Manifest().findMetaDataItem)(mainApplication, Config.RUNTIME_VERSION) > -1) {
throw new Error('A runtime version is set in your AndroidManifest.xml, but is missing from your Expo app config (app.json/app.config.js). Either set runtimeVersion in your Expo app config or remove expo.modules.updates.EXPO_RUNTIME_VERSION from your AndroidManifest.xml.');
}
if (runtimeVersion) {
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, 'expo.modules.updates.EXPO_SDK_VERSION');
(0, _Manifest().addMetaDataItemToMainApplication)(mainApplication, Config.RUNTIME_VERSION, '@string/expo_runtime_version');
} else {
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, Config.RUNTIME_VERSION);
(0, _Manifest().removeMetaDataItemFromMainApplication)(mainApplication, 'expo.modules.updates.EXPO_SDK_VERSION');
}
return androidManifest;
}
//# sourceMappingURL=Updates.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
import { ExpoConfig } from '@expo/config-types';
import { ConfigPlugin } from '../Plugin.types';
export declare const withVersion: ConfigPlugin;
/** Sets a numeric version for a value in the project.gradle buildscript.ext object to be at least the provided props.minVersion, if the existing value is greater then no change will be made. */
export declare const withBuildScriptExtMinimumVersion: ConfigPlugin<{
name: string;
minVersion: number;
}>;
export declare function setMinBuildScriptExtVersion(buildGradle: string, { name, minVersion }: {
name: string;
minVersion: number;
}): string;
export declare function getVersionName(config: Pick<ExpoConfig, 'version' | 'android'>): string | null;
export declare function setVersionName(config: Pick<ExpoConfig, 'version' | 'android'>, buildGradle: string): string;
export declare function getVersionCode(config: Pick<ExpoConfig, 'android'>): number;
export declare function setVersionCode(config: Pick<ExpoConfig, 'android'>, buildGradle: string): string;

View File

@@ -0,0 +1,87 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getVersionCode = getVersionCode;
exports.getVersionName = getVersionName;
exports.setMinBuildScriptExtVersion = setMinBuildScriptExtVersion;
exports.setVersionCode = setVersionCode;
exports.setVersionName = setVersionName;
exports.withVersion = exports.withBuildScriptExtMinimumVersion = void 0;
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
function _warnings() {
const data = require("../utils/warnings");
_warnings = function () {
return data;
};
return data;
}
const withVersion = config => {
return (0, _androidPlugins().withAppBuildGradle)(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = setVersionCode(config, config.modResults.contents);
config.modResults.contents = setVersionName(config, config.modResults.contents);
} else {
(0, _warnings().addWarningAndroid)('android.versionCode', `Cannot automatically configure app build.gradle if it's not groovy`);
}
return config;
});
};
/** Sets a numeric version for a value in the project.gradle buildscript.ext object to be at least the provided props.minVersion, if the existing value is greater then no change will be made. */
exports.withVersion = withVersion;
const withBuildScriptExtMinimumVersion = (config, props) => {
return (0, _androidPlugins().withProjectBuildGradle)(config, config => {
if (config.modResults.language === 'groovy') {
config.modResults.contents = setMinBuildScriptExtVersion(config.modResults.contents, props);
} else {
(0, _warnings().addWarningAndroid)('withBuildScriptExtVersion', `Cannot automatically configure project build.gradle if it's not groovy`);
}
return config;
});
};
exports.withBuildScriptExtMinimumVersion = withBuildScriptExtMinimumVersion;
function setMinBuildScriptExtVersion(buildGradle, {
name,
minVersion
}) {
const regex = new RegExp(`(${name}\\s?=\\s?)(\\d+(?:\\.\\d+)?)`);
const currentVersion = buildGradle.match(regex)?.[2];
if (!currentVersion) {
(0, _warnings().addWarningAndroid)('withBuildScriptExtVersion', `Cannot set minimum buildscript.ext.${name} version because the property "${name}" cannot be found or does not have a numeric value.`);
// TODO: Maybe just add the property...
return buildGradle;
}
const currentVersionNum = Number(currentVersion);
return buildGradle.replace(regex, `$1${Math.max(minVersion, currentVersionNum)}`);
}
function getVersionName(config) {
return config.android?.version ?? config.version ?? null;
}
function setVersionName(config, buildGradle) {
const versionName = getVersionName(config);
if (versionName === null) {
return buildGradle;
}
const pattern = new RegExp(`versionName ".*"`);
return buildGradle.replace(pattern, `versionName "${versionName}"`);
}
function getVersionCode(config) {
return config.android?.versionCode ?? 1;
}
function setVersionCode(config, buildGradle) {
const versionCode = getVersionCode(config);
if (versionCode === null) {
return buildGradle;
}
const pattern = new RegExp(`versionCode.*`);
return buildGradle.replace(pattern, `versionCode ${versionCode}`);
}
//# sourceMappingURL=Version.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,6 @@
import { ExpoConfig } from '@expo/config-types';
import { AndroidManifest } from './Manifest';
import { ConfigPlugin } from '../Plugin.types';
export declare const withWindowSoftInputMode: ConfigPlugin;
export declare function setWindowSoftInputModeMode(config: Pick<ExpoConfig, 'android' | 'userInterfaceStyle'>, androidManifest: AndroidManifest): AndroidManifest;
export declare function getWindowSoftInputModeMode(config: Pick<ExpoConfig, 'android'>): string;

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getWindowSoftInputModeMode = getWindowSoftInputModeMode;
exports.setWindowSoftInputModeMode = setWindowSoftInputModeMode;
exports.withWindowSoftInputMode = void 0;
function _Manifest() {
const data = require("./Manifest");
_Manifest = function () {
return data;
};
return data;
}
function _androidPlugins() {
const data = require("../plugins/android-plugins");
_androidPlugins = function () {
return data;
};
return data;
}
const ANDROID_WINDOW_SOFT_INPUT_MODE = 'android:windowSoftInputMode';
const MAPPING = {
pan: 'adjustPan',
resize: 'adjustResize'
};
const withWindowSoftInputMode = config => {
return (0, _androidPlugins().withAndroidManifest)(config, async config => {
config.modResults = setWindowSoftInputModeMode(config, config.modResults);
return config;
});
};
exports.withWindowSoftInputMode = withWindowSoftInputMode;
function setWindowSoftInputModeMode(config, androidManifest) {
const app = (0, _Manifest().getMainActivityOrThrow)(androidManifest);
app.$[ANDROID_WINDOW_SOFT_INPUT_MODE] = getWindowSoftInputModeMode(config);
return androidManifest;
}
function getWindowSoftInputModeMode(config) {
const value = config.android?.softwareKeyboardLayoutMode;
if (!value) {
// Default to `adjustResize` or `resize`.
return 'adjustResize';
}
return MAPPING[value] ?? value;
}
//# sourceMappingURL=WindowSoftInputMode.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"WindowSoftInputMode.js","names":["_Manifest","data","require","_androidPlugins","ANDROID_WINDOW_SOFT_INPUT_MODE","MAPPING","pan","resize","withWindowSoftInputMode","config","withAndroidManifest","modResults","setWindowSoftInputModeMode","exports","androidManifest","app","getMainActivityOrThrow","$","getWindowSoftInputModeMode","value","android","softwareKeyboardLayoutMode"],"sources":["../../src/android/WindowSoftInputMode.ts"],"sourcesContent":["import { ExpoConfig } from '@expo/config-types';\n\nimport { AndroidManifest, getMainActivityOrThrow } from './Manifest';\nimport { ConfigPlugin } from '../Plugin.types';\nimport { withAndroidManifest } from '../plugins/android-plugins';\n\nconst ANDROID_WINDOW_SOFT_INPUT_MODE = 'android:windowSoftInputMode';\n\nconst MAPPING: Record<string, string> = {\n pan: 'adjustPan',\n resize: 'adjustResize',\n};\n\nexport const withWindowSoftInputMode: ConfigPlugin = (config) => {\n return withAndroidManifest(config, async (config) => {\n config.modResults = setWindowSoftInputModeMode(config, config.modResults);\n return config;\n });\n};\n\nexport function setWindowSoftInputModeMode(\n config: Pick<ExpoConfig, 'android' | 'userInterfaceStyle'>,\n androidManifest: AndroidManifest\n) {\n const app = getMainActivityOrThrow(androidManifest);\n app.$[ANDROID_WINDOW_SOFT_INPUT_MODE] = getWindowSoftInputModeMode(config);\n return androidManifest;\n}\n\nexport function getWindowSoftInputModeMode(config: Pick<ExpoConfig, 'android'>) {\n const value = config.android?.softwareKeyboardLayoutMode;\n\n if (!value) {\n // Default to `adjustResize` or `resize`.\n return 'adjustResize';\n }\n return MAPPING[value] ?? value;\n}\n"],"mappings":";;;;;;;;AAEA,SAAAA,UAAA;EAAA,MAAAC,IAAA,GAAAC,OAAA;EAAAF,SAAA,YAAAA,CAAA;IAAA,OAAAC,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,SAAAE,gBAAA;EAAA,MAAAF,IAAA,GAAAC,OAAA;EAAAC,eAAA,YAAAA,CAAA;IAAA,OAAAF,IAAA;EAAA;EAAA,OAAAA,IAAA;AAAA;AAEA,MAAMG,8BAA8B,GAAG,6BAA6B;AAEpE,MAAMC,OAA+B,GAAG;EACtCC,GAAG,EAAE,WAAW;EAChBC,MAAM,EAAE;AACV,CAAC;AAEM,MAAMC,uBAAqC,GAAIC,MAAM,IAAK;EAC/D,OAAO,IAAAC,qCAAmB,EAACD,MAAM,EAAE,MAAOA,MAAM,IAAK;IACnDA,MAAM,CAACE,UAAU,GAAGC,0BAA0B,CAACH,MAAM,EAAEA,MAAM,CAACE,UAAU,CAAC;IACzE,OAAOF,MAAM;EACf,CAAC,CAAC;AACJ,CAAC;AAACI,OAAA,CAAAL,uBAAA,GAAAA,uBAAA;AAEK,SAASI,0BAA0BA,CACxCH,MAA0D,EAC1DK,eAAgC,EAChC;EACA,MAAMC,GAAG,GAAG,IAAAC,kCAAsB,EAACF,eAAe,CAAC;EACnDC,GAAG,CAACE,CAAC,CAACb,8BAA8B,CAAC,GAAGc,0BAA0B,CAACT,MAAM,CAAC;EAC1E,OAAOK,eAAe;AACxB;AAEO,SAASI,0BAA0BA,CAACT,MAAmC,EAAE;EAC9E,MAAMU,KAAK,GAAGV,MAAM,CAACW,OAAO,EAAEC,0BAA0B;EAExD,IAAI,CAACF,KAAK,EAAE;IACV;IACA,OAAO,cAAc;EACvB;EACA,OAAOd,OAAO,CAACc,KAAK,CAAC,IAAIA,KAAK;AAChC","ignoreList":[]}

View File

@@ -0,0 +1,36 @@
import { CodeBlock } from '../utils/commonCodeMod';
/**
* Find java or kotlin new class instance code block
*
* @param contents source contents
* @param classDeclaration class declaration or just a class name
* @param language 'java' | 'kt'
* @returns `CodeBlock` for start/end offset and code block contents
*/
export declare function findNewInstanceCodeBlock(contents: string, classDeclaration: string, language: 'java' | 'kt'): CodeBlock | null;
/**
* Append contents to the end of code declaration block, support class or method declarations.
*
* @param srcContents source contents
* @param declaration class declaration or method declaration
* @param insertion code to append
* @returns updated contents
*/
export declare function appendContentsInsideDeclarationBlock(srcContents: string, declaration: string, insertion: string): string;
export declare function addImports(source: string, imports: string[], isJava: boolean): string;
/**
* Find code block of Gradle plugin block, will return only {} part
*
* @param contents source contents
* @param plugin plugin declaration name, e.g. `plugins` or `pluginManagement`
* @returns found CodeBlock, or null if not found.
*/
export declare function findGradlePluginCodeBlock(contents: string, plugin: string): CodeBlock | null;
/**
* Append contents to the end of Gradle plugin block
* @param srcContents source contents
* @param plugin plugin declaration name, e.g. `plugins` or `pluginManagement`
* @param insertion code to append
* @returns updated contents
*/
export declare function appendContentsInsideGradlePluginBlock(srcContents: string, plugin: string, insertion: string): string;

View File

@@ -0,0 +1,132 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.addImports = addImports;
exports.appendContentsInsideDeclarationBlock = appendContentsInsideDeclarationBlock;
exports.appendContentsInsideGradlePluginBlock = appendContentsInsideGradlePluginBlock;
exports.findGradlePluginCodeBlock = findGradlePluginCodeBlock;
exports.findNewInstanceCodeBlock = findNewInstanceCodeBlock;
function _commonCodeMod() {
const data = require("../utils/commonCodeMod");
_commonCodeMod = function () {
return data;
};
return data;
}
function _matchBrackets() {
const data = require("../utils/matchBrackets");
_matchBrackets = function () {
return data;
};
return data;
}
/**
* Find java or kotlin new class instance code block
*
* @param contents source contents
* @param classDeclaration class declaration or just a class name
* @param language 'java' | 'kt'
* @returns `CodeBlock` for start/end offset and code block contents
*/
function findNewInstanceCodeBlock(contents, classDeclaration, language) {
const isJava = language === 'java';
let start = isJava ? contents.indexOf(` new ${classDeclaration}(`) : contents.search(new RegExp(` (object\\s*:\\s*)?${classDeclaration}\\(`));
if (start < 0) {
return null;
}
// `+ 1` for the prefix space
start += 1;
let end = (0, _matchBrackets().findMatchingBracketPosition)(contents, '(', start);
// For anonymous class, should search further to the {} block.
// ```java
// new Foo() {
// @Override
// protected void interfaceMethod {}
// };
// ```
//
// ```kotlin
// object : Foo() {
// override fun interfaceMethod {}
// }
// ```
const nextBrace = contents.indexOf('{', end + 1);
const isAnonymousClass = nextBrace >= end && !!contents.substring(end + 1, nextBrace).match(/^\s*$/);
if (isAnonymousClass) {
end = (0, _matchBrackets().findMatchingBracketPosition)(contents, '{', end);
}
return {
start,
end,
code: contents.substring(start, end + 1)
};
}
/**
* Append contents to the end of code declaration block, support class or method declarations.
*
* @param srcContents source contents
* @param declaration class declaration or method declaration
* @param insertion code to append
* @returns updated contents
*/
function appendContentsInsideDeclarationBlock(srcContents, declaration, insertion) {
const start = srcContents.search(new RegExp(`\\s*${declaration}.*?[\\(\\{]`));
if (start < 0) {
throw new Error(`Unable to find code block - declaration[${declaration}]`);
}
const end = (0, _matchBrackets().findMatchingBracketPosition)(srcContents, '{', start);
return (0, _commonCodeMod().insertContentsAtOffset)(srcContents, insertion, end);
}
function addImports(source, imports, isJava) {
const lines = source.split('\n');
const lineIndexWithPackageDeclaration = lines.findIndex(line => line.match(/^package .*;?$/));
for (const javaImport of imports) {
if (!source.includes(javaImport)) {
const importStatement = `import ${javaImport}${isJava ? ';' : ''}`;
lines.splice(lineIndexWithPackageDeclaration + 1, 0, importStatement);
}
}
return lines.join('\n');
}
/**
* Find code block of Gradle plugin block, will return only {} part
*
* @param contents source contents
* @param plugin plugin declaration name, e.g. `plugins` or `pluginManagement`
* @returns found CodeBlock, or null if not found.
*/
function findGradlePluginCodeBlock(contents, plugin) {
const pluginStart = contents.search(new RegExp(`${plugin}\\s*\\{`, 'm'));
if (pluginStart < 0) {
return null;
}
const codeBlockStart = contents.indexOf('{', pluginStart);
const codeBlockEnd = (0, _matchBrackets().findMatchingBracketPosition)(contents, '{', codeBlockStart);
const codeBlock = contents.substring(codeBlockStart, codeBlockEnd + 1);
return {
start: codeBlockStart,
end: codeBlockEnd,
code: codeBlock
};
}
/**
* Append contents to the end of Gradle plugin block
* @param srcContents source contents
* @param plugin plugin declaration name, e.g. `plugins` or `pluginManagement`
* @param insertion code to append
* @returns updated contents
*/
function appendContentsInsideGradlePluginBlock(srcContents, plugin, insertion) {
const codeBlock = findGradlePluginCodeBlock(srcContents, plugin);
if (!codeBlock) {
throw new Error(`Unable to find Gradle plugin block - plugin[${plugin}]`);
}
return (0, _commonCodeMod().insertContentsAtOffset)(srcContents, insertion, codeBlock.end);
}
//# sourceMappingURL=codeMod.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,28 @@
import * as AllowBackup from './AllowBackup';
import * as BuildProperties from './BuildProperties';
import * as Colors from './Colors';
import * as EasBuild from './EasBuild';
import * as GoogleMapsApiKey from './GoogleMapsApiKey';
import * as GoogleServices from './GoogleServices';
import * as IntentFilters from './IntentFilters';
import * as Locales from './Locales';
import * as Manifest from './Manifest';
import * as Name from './Name';
import * as Orientation from './Orientation';
import * as Package from './Package';
import * as Paths from './Paths';
import * as Permissions from './Permissions';
import * as PredictiveBackGesture from './PredictiveBackGesture';
import * as PrimaryColor from './PrimaryColor';
import * as Properties from './Properties';
import * as Resources from './Resources';
import * as Scheme from './Scheme';
import * as StatusBar from './StatusBar';
import * as Strings from './Strings';
import * as Styles from './Styles';
import * as Updates from './Updates';
import * as Version from './Version';
import * as WindowSoftInputMode from './WindowSoftInputMode';
import * as CodeMod from './codeMod';
export { Manifest, Colors, Paths, Permissions, Properties, Resources, Scheme, Strings, Styles };
export { AllowBackup, BuildProperties, EasBuild, GoogleMapsApiKey, GoogleServices, IntentFilters, Name, Locales, Orientation, Package, PrimaryColor, StatusBar, Updates, Version, WindowSoftInputMode, PredictiveBackGesture, CodeMod, };

View File

@@ -0,0 +1,347 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.WindowSoftInputMode = exports.Version = exports.Updates = exports.Styles = exports.Strings = exports.StatusBar = exports.Scheme = exports.Resources = exports.Properties = exports.PrimaryColor = exports.PredictiveBackGesture = exports.Permissions = exports.Paths = exports.Package = exports.Orientation = exports.Name = exports.Manifest = exports.Locales = exports.IntentFilters = exports.GoogleServices = exports.GoogleMapsApiKey = exports.EasBuild = exports.Colors = exports.CodeMod = exports.BuildProperties = exports.AllowBackup = void 0;
function AllowBackup() {
const data = _interopRequireWildcard(require("./AllowBackup"));
AllowBackup = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "AllowBackup", {
enumerable: true,
get: function () {
return AllowBackup();
}
});
function BuildProperties() {
const data = _interopRequireWildcard(require("./BuildProperties"));
BuildProperties = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "BuildProperties", {
enumerable: true,
get: function () {
return BuildProperties();
}
});
function Colors() {
const data = _interopRequireWildcard(require("./Colors"));
Colors = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Colors", {
enumerable: true,
get: function () {
return Colors();
}
});
function EasBuild() {
const data = _interopRequireWildcard(require("./EasBuild"));
EasBuild = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "EasBuild", {
enumerable: true,
get: function () {
return EasBuild();
}
});
function GoogleMapsApiKey() {
const data = _interopRequireWildcard(require("./GoogleMapsApiKey"));
GoogleMapsApiKey = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "GoogleMapsApiKey", {
enumerable: true,
get: function () {
return GoogleMapsApiKey();
}
});
function GoogleServices() {
const data = _interopRequireWildcard(require("./GoogleServices"));
GoogleServices = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "GoogleServices", {
enumerable: true,
get: function () {
return GoogleServices();
}
});
function IntentFilters() {
const data = _interopRequireWildcard(require("./IntentFilters"));
IntentFilters = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "IntentFilters", {
enumerable: true,
get: function () {
return IntentFilters();
}
});
function Locales() {
const data = _interopRequireWildcard(require("./Locales"));
Locales = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Locales", {
enumerable: true,
get: function () {
return Locales();
}
});
function Manifest() {
const data = _interopRequireWildcard(require("./Manifest"));
Manifest = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Manifest", {
enumerable: true,
get: function () {
return Manifest();
}
});
function Name() {
const data = _interopRequireWildcard(require("./Name"));
Name = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Name", {
enumerable: true,
get: function () {
return Name();
}
});
function Orientation() {
const data = _interopRequireWildcard(require("./Orientation"));
Orientation = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Orientation", {
enumerable: true,
get: function () {
return Orientation();
}
});
function Package() {
const data = _interopRequireWildcard(require("./Package"));
Package = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Package", {
enumerable: true,
get: function () {
return Package();
}
});
function Paths() {
const data = _interopRequireWildcard(require("./Paths"));
Paths = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Paths", {
enumerable: true,
get: function () {
return Paths();
}
});
function Permissions() {
const data = _interopRequireWildcard(require("./Permissions"));
Permissions = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Permissions", {
enumerable: true,
get: function () {
return Permissions();
}
});
function PredictiveBackGesture() {
const data = _interopRequireWildcard(require("./PredictiveBackGesture"));
PredictiveBackGesture = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "PredictiveBackGesture", {
enumerable: true,
get: function () {
return PredictiveBackGesture();
}
});
function PrimaryColor() {
const data = _interopRequireWildcard(require("./PrimaryColor"));
PrimaryColor = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "PrimaryColor", {
enumerable: true,
get: function () {
return PrimaryColor();
}
});
function Properties() {
const data = _interopRequireWildcard(require("./Properties"));
Properties = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Properties", {
enumerable: true,
get: function () {
return Properties();
}
});
function Resources() {
const data = _interopRequireWildcard(require("./Resources"));
Resources = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Resources", {
enumerable: true,
get: function () {
return Resources();
}
});
function Scheme() {
const data = _interopRequireWildcard(require("./Scheme"));
Scheme = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Scheme", {
enumerable: true,
get: function () {
return Scheme();
}
});
function StatusBar() {
const data = _interopRequireWildcard(require("./StatusBar"));
StatusBar = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "StatusBar", {
enumerable: true,
get: function () {
return StatusBar();
}
});
function Strings() {
const data = _interopRequireWildcard(require("./Strings"));
Strings = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Strings", {
enumerable: true,
get: function () {
return Strings();
}
});
function Styles() {
const data = _interopRequireWildcard(require("./Styles"));
Styles = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Styles", {
enumerable: true,
get: function () {
return Styles();
}
});
function Updates() {
const data = _interopRequireWildcard(require("./Updates"));
Updates = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Updates", {
enumerable: true,
get: function () {
return Updates();
}
});
function Version() {
const data = _interopRequireWildcard(require("./Version"));
Version = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "Version", {
enumerable: true,
get: function () {
return Version();
}
});
function WindowSoftInputMode() {
const data = _interopRequireWildcard(require("./WindowSoftInputMode"));
WindowSoftInputMode = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "WindowSoftInputMode", {
enumerable: true,
get: function () {
return WindowSoftInputMode();
}
});
function CodeMod() {
const data = _interopRequireWildcard(require("./codeMod"));
CodeMod = function () {
return data;
};
return data;
}
Object.defineProperty(exports, "CodeMod", {
enumerable: true,
get: function () {
return CodeMod();
}
});
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; }
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long