miaoda-expo-dev-client-55 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.js +2 -0
- package/README.md +28 -0
- package/android/build.gradle +73 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/app.plugin.js +1 -0
- package/build/DevClient.d.ts +2 -0
- package/build/DevClient.d.ts.map +1 -0
- package/build/DevClient.js +1 -0
- package/build/DevClient.js.map +1 -0
- package/dependencies.js +1 -0
- package/expo-module.config.json +3 -0
- package/getDefaultScheme.js +1 -0
- package/ios/expo-dev-client.podspec +27 -0
- package/metro.config.js +14 -0
- package/package.json +53 -0
- package/plugin/build/constants.d.ts +1 -0
- package/plugin/build/constants.js +4 -0
- package/plugin/build/getDefaultScheme.d.ts +2 -0
- package/plugin/build/getDefaultScheme.js +29 -0
- package/plugin/build/index.d.ts +3 -0
- package/plugin/build/index.js +3 -0
- package/plugin/build/withAndroidDevLoadingString.js +18 -0
- package/plugin/build/withDevClient.d.ts +10 -0
- package/plugin/build/withDevClient.js +26 -0
- package/plugin/build/withGeneratedAndroidScheme.d.ts +14 -0
- package/plugin/build/withGeneratedAndroidScheme.js +76 -0
- package/plugin/build/withGeneratedIosScheme.d.ts +4 -0
- package/plugin/build/withGeneratedIosScheme.js +24 -0
- package/plugin/index.js +1 -0
- package/plugin/src/constants.ts +1 -0
- package/plugin/src/getDefaultScheme.ts +30 -0
- package/plugin/src/index.ts +3 -0
- package/plugin/src/withAndroidDevLoadingString.ts +18 -0
- package/plugin/src/withDevClient.ts +37 -0
- package/plugin/src/withGeneratedAndroidScheme.ts +93 -0
- package/plugin/src/withGeneratedIosScheme.ts +24 -0
- package/plugin/tsconfig.json +10 -0
- package/src/DevClient.ts +1 -0
- package/src/types.d.ts +3 -0
- package/tsconfig.json +9 -0
package/.eslintrc.js
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<p>
|
|
2
|
+
<a href="https://docs.expo.dev/versions/latest/sdk/dev-client/">
|
|
3
|
+
<img
|
|
4
|
+
src="../../.github/resources/expo-dev-client.svg"
|
|
5
|
+
alt="expo-dev-client"
|
|
6
|
+
height="64" />
|
|
7
|
+
</a>
|
|
8
|
+
</p>
|
|
9
|
+
|
|
10
|
+
`expo-dev-client` adds various useful development tools to your debug builds:
|
|
11
|
+
|
|
12
|
+
- A configurable launcher UI, so you can launch updates (such as from [PR previews](https://docs.expo.dev/develop/development-builds/development-workflows#pr-previews)) and switch between development servers without needing to recompile the native app
|
|
13
|
+
- Improved debugging tools (such as support for [inspecting network requests](https://docs.expo.dev/debugging/tools/#inspecting-network-requests))
|
|
14
|
+
- [A powerful and extensible developer menu UI](https://docs.expo.dev/debugging/tools#developer-menu)
|
|
15
|
+
|
|
16
|
+
Expo documentation refers to debug builds that include `expo-dev-client` as [development builds](https://docs.expo.dev/develop/development-builds/introduction/).
|
|
17
|
+
|
|
18
|
+
## Documentation
|
|
19
|
+
|
|
20
|
+
You can find more information in the [Expo documentation](https://docs.expo.dev/versions/latest/sdk/dev-client).
|
|
21
|
+
|
|
22
|
+
## Issues
|
|
23
|
+
|
|
24
|
+
If you encounter any issues using this package in your project, please [report your issue here](https://github.com/expo/expo/issues/new?template=dev_client_bug_report.yml).
|
|
25
|
+
|
|
26
|
+
## Contributing
|
|
27
|
+
|
|
28
|
+
Contributions are very welcome! Please refer to guidelines described in the [contributing guide](https://github.com/expo/expo#contributing).
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
plugins {
|
|
2
|
+
id 'com.android.library'
|
|
3
|
+
id 'expo-module-gradle-plugin'
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
expoModule {
|
|
7
|
+
canBePublished false
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
group = "host.exp.exponent"
|
|
11
|
+
version = "55.0.9"
|
|
12
|
+
|
|
13
|
+
android {
|
|
14
|
+
namespace "expo.modules.devclient"
|
|
15
|
+
defaultConfig {
|
|
16
|
+
versionCode 1
|
|
17
|
+
versionName "55.0.9"
|
|
18
|
+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
buildTypes {
|
|
22
|
+
buildTypes.each {
|
|
23
|
+
it.buildConfigField 'String', 'VERSION', "\"${defaultConfig.versionName}\""
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
buildFeatures {
|
|
28
|
+
buildConfig true
|
|
29
|
+
viewBinding true
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
packaging {
|
|
33
|
+
resources {
|
|
34
|
+
merges.add("/META-INF/LICENSE*")
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
dependencies {
|
|
40
|
+
androidTestImplementation project(":expo-dev-menu-interface")
|
|
41
|
+
androidTestImplementation project(":expo-updates-interface")
|
|
42
|
+
androidTestImplementation project(":expo-dev-menu")
|
|
43
|
+
androidTestImplementation project(":expo-dev-launcher")
|
|
44
|
+
androidTestImplementation project(":expo-manifests")
|
|
45
|
+
|
|
46
|
+
androidTestImplementation 'com.facebook.react:react-android'
|
|
47
|
+
androidTestImplementation 'com.facebook.react:hermes-android'
|
|
48
|
+
|
|
49
|
+
androidTestImplementation('androidx.test.espresso:espresso-core:3.7.0')
|
|
50
|
+
androidTestImplementation('androidx.test:core:1.7.0')
|
|
51
|
+
androidTestImplementation('androidx.test:core-ktx:1.7.0')
|
|
52
|
+
androidTestImplementation('androidx.test.ext:junit:1.3.0')
|
|
53
|
+
androidTestImplementation('androidx.test.ext:junit-ktx:1.3.0')
|
|
54
|
+
androidTestImplementation('androidx.test:runner:1.7.0')
|
|
55
|
+
androidTestImplementation('androidx.test:rules:1.7.0')
|
|
56
|
+
|
|
57
|
+
androidTestImplementation 'org.webkit:android-jsc:+'
|
|
58
|
+
|
|
59
|
+
androidTestImplementation "io.insert-koin:koin-test:3.1.2"
|
|
60
|
+
androidTestImplementation "io.insert-koin:koin-test-junit4:3.1.2"
|
|
61
|
+
|
|
62
|
+
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2"
|
|
63
|
+
androidTestImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2"
|
|
64
|
+
|
|
65
|
+
androidTestImplementation "androidx.appcompat:appcompat:1.7.1"
|
|
66
|
+
|
|
67
|
+
androidTestImplementation 'io.mockk:mockk-android:1.13.11'
|
|
68
|
+
androidTestImplementation "com.google.truth:truth:1.4.5"
|
|
69
|
+
|
|
70
|
+
// Fixes "e: java.lang.AssertionError: No such enum entry LIBRARY_GROUP_PREFIX in org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl@b254b575"
|
|
71
|
+
// According to the https://stackoverflow.com/a/67736351
|
|
72
|
+
implementation 'androidx.annotation:annotation:1.9.1'
|
|
73
|
+
}
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./plugin/build/withDevClient');
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevClient.d.ts","sourceRoot":"","sources":["../src/DevClient.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'expo-dev-menu';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DevClient.js","sourceRoot":"","sources":["../src/DevClient.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC","sourcesContent":["export * from 'expo-dev-menu';\n"]}
|
package/dependencies.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('./plugin/build/getDefaultScheme').default;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, '../package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'expo-dev-client'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.description = package['description']
|
|
10
|
+
s.license = package['license']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.homepage = package['homepage']
|
|
13
|
+
s.platforms = {
|
|
14
|
+
:ios => '16.4',
|
|
15
|
+
:tvos => '16.4',
|
|
16
|
+
:osx => '13.4'
|
|
17
|
+
}
|
|
18
|
+
s.source = { git: 'https://github.com/expo/expo.git' }
|
|
19
|
+
s.static_framework = true
|
|
20
|
+
s.header_dir = 'EXDevClient'
|
|
21
|
+
|
|
22
|
+
s.dependency 'expo-dev-launcher', :configurations => :debug
|
|
23
|
+
s.dependency 'expo-dev-menu', :configurations => :debug
|
|
24
|
+
s.dependency 'expo-dev-menu-interface'
|
|
25
|
+
s.dependency 'EXManifests', :configurations => :debug
|
|
26
|
+
s.dependency 'EXUpdatesInterface'
|
|
27
|
+
end
|
package/metro.config.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const { getDefaultConfig } = require('expo/metro-config');
|
|
2
|
+
|
|
3
|
+
const config = getDefaultConfig(__dirname);
|
|
4
|
+
|
|
5
|
+
// Copied from expo-yarn-workspaces
|
|
6
|
+
config.transformer.enableBabelRCLookup = false;
|
|
7
|
+
config.resolver.assetExts.push('db');
|
|
8
|
+
config.resolver.blockList = [
|
|
9
|
+
/\/__tests__\//,
|
|
10
|
+
/\/android\/React(Android|Common)\//,
|
|
11
|
+
/\/versioned-react-native\//,
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
module.exports = config;
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "miaoda-expo-dev-client-55",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Miaoda customized Expo Development Client for Expo SDK 55",
|
|
5
|
+
"main": "build/DevClient.js",
|
|
6
|
+
"types": "build/DevClient.d.ts",
|
|
7
|
+
"nx": {
|
|
8
|
+
"tags": [
|
|
9
|
+
"type:vendor",
|
|
10
|
+
"scope:expo-sdk-55"
|
|
11
|
+
],
|
|
12
|
+
"includedScripts": []
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"react-native",
|
|
16
|
+
"expo",
|
|
17
|
+
"expo-dev-client"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/expo/expo.git",
|
|
22
|
+
"directory": "packages/expo-dev-client"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/expo/expo/issues"
|
|
26
|
+
},
|
|
27
|
+
"author": "650 Industries, Inc.",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"homepage": "https://docs.expo.dev/versions/latest/sdk/dev-client/",
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"expo-manifests": "~55.0.9",
|
|
32
|
+
"expo-updates-interface": "~55.1.3",
|
|
33
|
+
"expo-dev-menu-interface": "npm:miaoda-expo-dev-menu-interface-55@0.1.0",
|
|
34
|
+
"expo-dev-menu": "npm:miaoda-expo-dev-menu-55@0.1.0",
|
|
35
|
+
"expo-dev-launcher": "npm:miaoda-expo-dev-launcher-55@0.1.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^22.14.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"expo": "*"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"registry": "https://registry.npmjs.org/",
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "expo-module build",
|
|
49
|
+
"clean": "expo-module clean",
|
|
50
|
+
"lint": "expo-module lint",
|
|
51
|
+
"expo-module": "expo-module"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const InstallationPage = "https://docs.expo.dev/clients/installation/";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
|
+
exports.default = getDefaultScheme;
|
|
4
|
+
/*
|
|
5
|
+
* Converts the slug from app configuration to a string that's a valid URI scheme.
|
|
6
|
+
* From RFC3986 Section 3.1.
|
|
7
|
+
* scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
|
8
|
+
*/
|
|
9
|
+
function getDefaultScheme(config) {
|
|
10
|
+
if (typeof config !== 'object') {
|
|
11
|
+
throw new TypeError('getDefaultScheme: config is not object');
|
|
12
|
+
}
|
|
13
|
+
if (!config.slug || typeof config.slug !== 'string') {
|
|
14
|
+
throw new TypeError('getDefaultScheme: config missing required property "slug"');
|
|
15
|
+
}
|
|
16
|
+
// Remove unallowed characters. Also remove `-` to keep this shorter.
|
|
17
|
+
let scheme = config.slug.replace(/[^A-Za-z0-9+\-.]/g, '');
|
|
18
|
+
// Edge case: if the slug didn't include any allowed characters we may end up with an empty string.
|
|
19
|
+
if (scheme.length === 0) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Unable to generate a scheme based on the "slug" (${config.slug}), ` +
|
|
22
|
+
'because it does not contain any URL-friendly characters.'
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
// Lowercasing might not be strictly necessary, but let's do it for stylistic purposes.
|
|
26
|
+
scheme = scheme.toLowerCase();
|
|
27
|
+
// Add a prefix to avoid leading digits and to distinguish from user-defined schemes.
|
|
28
|
+
return `exp+${scheme}`;
|
|
29
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
3
|
+
exports.withAndroidDevLoadingString = void 0;
|
|
4
|
+
const config_plugins_1 = require('expo/config-plugins');
|
|
5
|
+
const withAndroidDevLoadingString = (config) => {
|
|
6
|
+
return (0, config_plugins_1.withStringsXml)(config, function (config) {
|
|
7
|
+
// Replace React Native's URL-bearing loading label with the localized launcher text.
|
|
8
|
+
config.modResults = config_plugins_1.AndroidConfig.Strings.setStringItem([
|
|
9
|
+
config_plugins_1.AndroidConfig.Resources.buildResourceItem({
|
|
10
|
+
name: 'catalyst_loading_from_url',
|
|
11
|
+
value: '加载中',
|
|
12
|
+
translatable: false,
|
|
13
|
+
}),
|
|
14
|
+
], config.modResults);
|
|
15
|
+
return config;
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
exports.withAndroidDevLoadingString = withAndroidDevLoadingString;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PluginConfigType } from 'expo-dev-launcher/plugin/build/pluginConfig';
|
|
2
|
+
export type DevClientPluginConfigType = PluginConfigType & {
|
|
3
|
+
/**
|
|
4
|
+
* Whether to register a custom URL scheme to open a project.
|
|
5
|
+
* @default true
|
|
6
|
+
*/
|
|
7
|
+
addGeneratedScheme?: boolean;
|
|
8
|
+
};
|
|
9
|
+
declare const _default: import("expo/config-plugins").ConfigPlugin<DevClientPluginConfigType>;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { default: mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
6
|
+
const config_plugins_1 = require('expo/config-plugins');
|
|
7
|
+
// @ts-expect-error missing types
|
|
8
|
+
const app_plugin_1 = __importDefault(require('expo-dev-launcher/app.plugin'));
|
|
9
|
+
// @ts-expect-error missing types
|
|
10
|
+
const app_plugin_2 = __importDefault(require('expo-dev-menu/app.plugin'));
|
|
11
|
+
const withAndroidDevLoadingString_1 = require('./withAndroidDevLoadingString');
|
|
12
|
+
const withGeneratedAndroidScheme_1 = require('./withGeneratedAndroidScheme');
|
|
13
|
+
const withGeneratedIosScheme_1 = require('./withGeneratedIosScheme');
|
|
14
|
+
const pkg = require('../../package.json');
|
|
15
|
+
function withDevClient(config, props) {
|
|
16
|
+
config = (0, app_plugin_2.default)(config);
|
|
17
|
+
config = (0, app_plugin_1.default)(config, props);
|
|
18
|
+
config = (0, withAndroidDevLoadingString_1.withAndroidDevLoadingString)(config);
|
|
19
|
+
const mySchemeProps = { addGeneratedScheme: true, ...props };
|
|
20
|
+
if (mySchemeProps.addGeneratedScheme) {
|
|
21
|
+
config = (0, withGeneratedAndroidScheme_1.withGeneratedAndroidScheme)(config);
|
|
22
|
+
config = (0, withGeneratedIosScheme_1.withGeneratedIosScheme)(config);
|
|
23
|
+
}
|
|
24
|
+
return config;
|
|
25
|
+
}
|
|
26
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(withDevClient, pkg.name, pkg.version);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ExpoConfig } from 'expo/config';
|
|
2
|
+
import { AndroidConfig, type AndroidManifest, type ConfigPlugin } from 'expo/config-plugins';
|
|
3
|
+
export declare const withGeneratedAndroidScheme: ConfigPlugin;
|
|
4
|
+
export declare function setGeneratedAndroidScheme(config: Pick<ExpoConfig, 'scheme' | 'slug'>, androidManifest: AndroidManifest): AndroidManifest;
|
|
5
|
+
/**
|
|
6
|
+
* Remove the custom Expo dev client scheme from intent filters, which are set to `autoVerify=true`.
|
|
7
|
+
* The custom scheme `<data android:scheme="exp+<slug>"/>` seems to block verification for these intent filters.
|
|
8
|
+
* This plugin makes sure there is no scheme in the autoVerify intent filters, that starts with `exp+`.
|
|
9
|
+
|
|
10
|
+
* Iterate over all `autoVerify=true` intent filters, and pull out schemes matching with `exp+<slug>`.
|
|
11
|
+
*
|
|
12
|
+
* @param {AndroidManifest} androidManifest
|
|
13
|
+
*/
|
|
14
|
+
export declare function removeExpoSchemaFromVerifiedIntentFilters(config: Pick<ExpoConfig, 'scheme' | 'slug'>, androidManifest: AndroidManifest): AndroidConfig.Manifest.AndroidManifest;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { default: mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
6
|
+
exports.withGeneratedAndroidScheme = void 0;
|
|
7
|
+
exports.setGeneratedAndroidScheme = setGeneratedAndroidScheme;
|
|
8
|
+
exports.removeExpoSchemaFromVerifiedIntentFilters = removeExpoSchemaFromVerifiedIntentFilters;
|
|
9
|
+
const config_plugins_1 = require('expo/config-plugins');
|
|
10
|
+
const getDefaultScheme_1 = __importDefault(require('./getDefaultScheme'));
|
|
11
|
+
const withGeneratedAndroidScheme = (config) => {
|
|
12
|
+
return (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
13
|
+
config.modResults = setGeneratedAndroidScheme(config, config.modResults);
|
|
14
|
+
config.modResults = removeExpoSchemaFromVerifiedIntentFilters(config, config.modResults);
|
|
15
|
+
return config;
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
exports.withGeneratedAndroidScheme = withGeneratedAndroidScheme;
|
|
19
|
+
function setGeneratedAndroidScheme(config, androidManifest) {
|
|
20
|
+
// Generate a cross-platform scheme used to launch the dev client.
|
|
21
|
+
const scheme = (0, getDefaultScheme_1.default)(config);
|
|
22
|
+
if (!config_plugins_1.AndroidConfig.Scheme.hasScheme(scheme, androidManifest)) {
|
|
23
|
+
androidManifest = config_plugins_1.AndroidConfig.Scheme.appendScheme(scheme, androidManifest);
|
|
24
|
+
}
|
|
25
|
+
return androidManifest;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Remove the custom Expo dev client scheme from intent filters, which are set to `autoVerify=true`.
|
|
29
|
+
* The custom scheme `<data android:scheme="exp+<slug>"/>` seems to block verification for these intent filters.
|
|
30
|
+
* This plugin makes sure there is no scheme in the autoVerify intent filters, that starts with `exp+`.
|
|
31
|
+
*
|
|
32
|
+
* Iterate over all `autoVerify=true` intent filters, and pull out schemes matching with `exp+<slug>`.
|
|
33
|
+
*
|
|
34
|
+
* @param {AndroidManifest} androidManifest
|
|
35
|
+
*/
|
|
36
|
+
function removeExpoSchemaFromVerifiedIntentFilters(config, androidManifest) {
|
|
37
|
+
// Generate a cross-platform scheme used to launch the dev client.
|
|
38
|
+
const defaultScheme = (0, getDefaultScheme_1.default)(config);
|
|
39
|
+
// See expo-cli's Android Scheme implementation for the original traversal logic.
|
|
40
|
+
for (const application of androidManifest.manifest.application || []) {
|
|
41
|
+
for (const activity of application.activity || []) {
|
|
42
|
+
if (activityHasSingleTaskLaunchMode(activity)) {
|
|
43
|
+
for (const intentFilter of activity['intent-filter'] || []) {
|
|
44
|
+
if (intentFilterHasAutoVerification(intentFilter) && intentFilter?.data) {
|
|
45
|
+
intentFilter.data = intentFilterRemoveSchemeFromData(
|
|
46
|
+
intentFilter,
|
|
47
|
+
(scheme) => scheme === defaultScheme
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return androidManifest;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Determine if the activity should contain the intent filters to clean.
|
|
59
|
+
*
|
|
60
|
+
* @see Expo CLI Android Scheme implementation.
|
|
61
|
+
*/
|
|
62
|
+
function activityHasSingleTaskLaunchMode(activity) {
|
|
63
|
+
return activity?.$?.['android:launchMode'] === 'singleTask';
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Determine if the intent filter has `autoVerify=true`.
|
|
67
|
+
*/
|
|
68
|
+
function intentFilterHasAutoVerification(intentFilter) {
|
|
69
|
+
return intentFilter?.$?.['android:autoVerify'] === 'true';
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Remove schemes from the intent filter that matches the function.
|
|
73
|
+
*/
|
|
74
|
+
function intentFilterRemoveSchemeFromData(intentFilter, schemeMatcher) {
|
|
75
|
+
return intentFilter?.data?.filter((entry) => !schemeMatcher(entry?.$['android:scheme'] || ''));
|
|
76
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ExpoConfig } from 'expo/config';
|
|
2
|
+
import { type ConfigPlugin, type InfoPlist, IOSConfig } from 'expo/config-plugins';
|
|
3
|
+
export declare const withGeneratedIosScheme: ConfigPlugin;
|
|
4
|
+
export declare function setGeneratedIosScheme(config: Pick<ExpoConfig, 'scheme' | 'slug'>, infoPlist: InfoPlist): IOSConfig.InfoPlist;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
const __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { default: mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
6
|
+
exports.withGeneratedIosScheme = void 0;
|
|
7
|
+
exports.setGeneratedIosScheme = setGeneratedIosScheme;
|
|
8
|
+
const config_plugins_1 = require('expo/config-plugins');
|
|
9
|
+
const getDefaultScheme_1 = __importDefault(require('./getDefaultScheme'));
|
|
10
|
+
const withGeneratedIosScheme = (config) => {
|
|
11
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
12
|
+
config.modResults = setGeneratedIosScheme(config, config.modResults);
|
|
13
|
+
return config;
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
exports.withGeneratedIosScheme = withGeneratedIosScheme;
|
|
17
|
+
function setGeneratedIosScheme(config, infoPlist) {
|
|
18
|
+
// Generate a cross-platform scheme used to launch the dev client.
|
|
19
|
+
const scheme = (0, getDefaultScheme_1.default)(config);
|
|
20
|
+
if (!config_plugins_1.IOSConfig.Scheme.hasScheme(scheme, infoPlist)) {
|
|
21
|
+
infoPlist = config_plugins_1.IOSConfig.Scheme.appendScheme(scheme, infoPlist);
|
|
22
|
+
}
|
|
23
|
+
return infoPlist;
|
|
24
|
+
}
|
package/plugin/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from './build';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const InstallationPage = 'https://docs.expo.dev/clients/installation/';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ExpoConfig } from 'expo/config';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Converts the slug from app configuration to a string that's a valid URI scheme.
|
|
5
|
+
* From RFC3986 Section 3.1.
|
|
6
|
+
* scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
|
7
|
+
*/
|
|
8
|
+
export default function getDefaultScheme(config: Pick<ExpoConfig, 'slug'>): string {
|
|
9
|
+
if (typeof config !== 'object') {
|
|
10
|
+
throw new TypeError('getDefaultScheme: config is not object');
|
|
11
|
+
}
|
|
12
|
+
if (!config.slug || typeof config.slug !== 'string') {
|
|
13
|
+
throw new TypeError('getDefaultScheme: config missing required property "slug"');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Remove unallowed characters. Also remove `-` to keep this shorter.
|
|
17
|
+
let scheme = config.slug.replace(/[^A-Za-z0-9+\-.]/g, '');
|
|
18
|
+
// Edge case: if the slug didn't include any allowed characters we may end up with an empty string.
|
|
19
|
+
if (scheme.length === 0) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
`Unable to generate a scheme based on the "slug" (${config.slug}), because it does not contain any URL-friendly characters.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Lowercasing might not be strictly necessary, but let's do it for stylistic purposes.
|
|
26
|
+
scheme = scheme.toLowerCase();
|
|
27
|
+
|
|
28
|
+
// Add a prefix to avoid leading digits and to distinguish from user-defined schemes.
|
|
29
|
+
return `exp+${scheme}`;
|
|
30
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AndroidConfig, type ConfigPlugin, withStringsXml } from 'expo/config-plugins';
|
|
2
|
+
|
|
3
|
+
export const withAndroidDevLoadingString: ConfigPlugin = (config) => {
|
|
4
|
+
return withStringsXml(config, (config) => {
|
|
5
|
+
// Replace React Native's URL-bearing loading label with the localized launcher text.
|
|
6
|
+
config.modResults = AndroidConfig.Strings.setStringItem(
|
|
7
|
+
[
|
|
8
|
+
AndroidConfig.Resources.buildResourceItem({
|
|
9
|
+
name: 'catalyst_loading_from_url',
|
|
10
|
+
value: '加载中',
|
|
11
|
+
translatable: false,
|
|
12
|
+
}),
|
|
13
|
+
],
|
|
14
|
+
config.modResults,
|
|
15
|
+
);
|
|
16
|
+
return config;
|
|
17
|
+
});
|
|
18
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ExpoConfig } from 'expo/config';
|
|
2
|
+
import { createRunOncePlugin } from 'expo/config-plugins';
|
|
3
|
+
// @ts-expect-error missing types
|
|
4
|
+
import withDevLauncher from 'expo-dev-launcher/app.plugin';
|
|
5
|
+
import type { PluginConfigType } from 'expo-dev-launcher/plugin/build/pluginConfig';
|
|
6
|
+
// @ts-expect-error missing types
|
|
7
|
+
import withDevMenu from 'expo-dev-menu/app.plugin';
|
|
8
|
+
|
|
9
|
+
import { withAndroidDevLoadingString } from './withAndroidDevLoadingString';
|
|
10
|
+
import { withGeneratedAndroidScheme } from './withGeneratedAndroidScheme';
|
|
11
|
+
import { withGeneratedIosScheme } from './withGeneratedIosScheme';
|
|
12
|
+
|
|
13
|
+
const pkg = require('../../package.json');
|
|
14
|
+
|
|
15
|
+
export type DevClientPluginConfigType = PluginConfigType & {
|
|
16
|
+
/**
|
|
17
|
+
* Whether to register a custom URL scheme to open a project.
|
|
18
|
+
* @default true
|
|
19
|
+
*/
|
|
20
|
+
addGeneratedScheme?: boolean;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function withDevClient(config: ExpoConfig, props: DevClientPluginConfigType) {
|
|
24
|
+
config = withDevMenu(config);
|
|
25
|
+
config = withDevLauncher(config, props);
|
|
26
|
+
config = withAndroidDevLoadingString(config);
|
|
27
|
+
|
|
28
|
+
const mySchemeProps = { addGeneratedScheme: true, ...props };
|
|
29
|
+
|
|
30
|
+
if (mySchemeProps.addGeneratedScheme) {
|
|
31
|
+
config = withGeneratedAndroidScheme(config);
|
|
32
|
+
config = withGeneratedIosScheme(config);
|
|
33
|
+
}
|
|
34
|
+
return config;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default createRunOncePlugin<DevClientPluginConfigType>(withDevClient, pkg.name, pkg.version);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ExpoConfig } from 'expo/config';
|
|
2
|
+
import {
|
|
3
|
+
AndroidConfig,
|
|
4
|
+
type AndroidManifest,
|
|
5
|
+
type ConfigPlugin,
|
|
6
|
+
withAndroidManifest,
|
|
7
|
+
} from 'expo/config-plugins';
|
|
8
|
+
|
|
9
|
+
import getDefaultScheme from './getDefaultScheme';
|
|
10
|
+
|
|
11
|
+
export const withGeneratedAndroidScheme: ConfigPlugin = (config) => {
|
|
12
|
+
return withAndroidManifest(config, (config) => {
|
|
13
|
+
config.modResults = setGeneratedAndroidScheme(config, config.modResults);
|
|
14
|
+
config.modResults = removeExpoSchemaFromVerifiedIntentFilters(config, config.modResults);
|
|
15
|
+
return config;
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function setGeneratedAndroidScheme(
|
|
20
|
+
config: Pick<ExpoConfig, 'scheme' | 'slug'>,
|
|
21
|
+
androidManifest: AndroidManifest
|
|
22
|
+
): AndroidManifest {
|
|
23
|
+
// Generate a cross-platform scheme used to launch the dev client.
|
|
24
|
+
const scheme = getDefaultScheme(config);
|
|
25
|
+
if (!AndroidConfig.Scheme.hasScheme(scheme, androidManifest)) {
|
|
26
|
+
androidManifest = AndroidConfig.Scheme.appendScheme(scheme, androidManifest);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return androidManifest;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Remove the custom Expo dev client scheme from intent filters, which are set to `autoVerify=true`.
|
|
34
|
+
* The custom scheme `<data android:scheme="exp+<slug>"/>` seems to block verification for these intent filters.
|
|
35
|
+
* This plugin makes sure there is no scheme in the autoVerify intent filters, that starts with `exp+`.
|
|
36
|
+
|
|
37
|
+
* Iterate over all `autoVerify=true` intent filters, and pull out schemes matching with `exp+<slug>`.
|
|
38
|
+
*
|
|
39
|
+
* @param {AndroidManifest} androidManifest
|
|
40
|
+
*/
|
|
41
|
+
export function removeExpoSchemaFromVerifiedIntentFilters(
|
|
42
|
+
config: Pick<ExpoConfig, 'scheme' | 'slug'>,
|
|
43
|
+
androidManifest: AndroidManifest
|
|
44
|
+
) {
|
|
45
|
+
// Generate a cross-platform scheme used to launch the dev client.
|
|
46
|
+
const defaultScheme = getDefaultScheme(config);
|
|
47
|
+
// see: https://github.com/expo/expo-cli/blob/f1624c75b52cc1c4f99354ec4021494e0eff74aa/packages/config-plugins/src/android/Scheme.ts#L164-L179
|
|
48
|
+
for (const application of androidManifest.manifest.application || []) {
|
|
49
|
+
for (const activity of application.activity || []) {
|
|
50
|
+
if (activityHasSingleTaskLaunchMode(activity)) {
|
|
51
|
+
for (const intentFilter of activity['intent-filter'] || []) {
|
|
52
|
+
if (intentFilterHasAutoVerification(intentFilter) && intentFilter?.data) {
|
|
53
|
+
intentFilter.data = intentFilterRemoveSchemeFromData(
|
|
54
|
+
intentFilter,
|
|
55
|
+
(scheme: string) => scheme === defaultScheme
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return androidManifest;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Determine if the activity should contain the intent filters to clean.
|
|
69
|
+
*
|
|
70
|
+
* @see https://github.com/expo/expo-cli/blob/f1624c75b52cc1c4f99354ec4021494e0eff74aa/packages/config-plugins/src/android/Scheme.ts#L166
|
|
71
|
+
*/
|
|
72
|
+
function activityHasSingleTaskLaunchMode(activity: AndroidConfig.Manifest.ManifestActivity) {
|
|
73
|
+
return activity?.$?.['android:launchMode'] === 'singleTask';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Determine if the intent filter has `autoVerify=true`.
|
|
78
|
+
*/
|
|
79
|
+
function intentFilterHasAutoVerification(
|
|
80
|
+
intentFilter: AndroidConfig.Manifest.ManifestIntentFilter
|
|
81
|
+
) {
|
|
82
|
+
return intentFilter?.$?.['android:autoVerify'] === 'true';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Remove schemes from the intent filter that matches the function.
|
|
87
|
+
*/
|
|
88
|
+
function intentFilterRemoveSchemeFromData(
|
|
89
|
+
intentFilter: AndroidConfig.Manifest.ManifestIntentFilter,
|
|
90
|
+
schemeMatcher: any
|
|
91
|
+
) {
|
|
92
|
+
return intentFilter?.data?.filter((entry) => !schemeMatcher(entry?.$['android:scheme'] || ''));
|
|
93
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ExpoConfig } from 'expo/config';
|
|
2
|
+
import { type ConfigPlugin, type InfoPlist, IOSConfig, withInfoPlist } from 'expo/config-plugins';
|
|
3
|
+
|
|
4
|
+
import getDefaultScheme from './getDefaultScheme';
|
|
5
|
+
|
|
6
|
+
export const withGeneratedIosScheme: ConfigPlugin = (config) => {
|
|
7
|
+
return withInfoPlist(config, (config) => {
|
|
8
|
+
config.modResults = setGeneratedIosScheme(config, config.modResults);
|
|
9
|
+
return config;
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export function setGeneratedIosScheme(
|
|
14
|
+
config: Pick<ExpoConfig, 'scheme' | 'slug'>,
|
|
15
|
+
infoPlist: InfoPlist
|
|
16
|
+
): IOSConfig.InfoPlist {
|
|
17
|
+
// Generate a cross-platform scheme used to launch the dev client.
|
|
18
|
+
const scheme = getDefaultScheme(config);
|
|
19
|
+
|
|
20
|
+
if (!IOSConfig.Scheme.hasScheme(scheme, infoPlist)) {
|
|
21
|
+
infoPlist = IOSConfig.Scheme.appendScheme(scheme, infoPlist);
|
|
22
|
+
}
|
|
23
|
+
return infoPlist;
|
|
24
|
+
}
|
package/src/DevClient.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'expo-dev-menu';
|
package/src/types.d.ts
ADDED
package/tsconfig.json
ADDED