@capacitor-plus/cli 8.5.3 → 8.6.1
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/assets/android-template.tar.gz +0 -0
- package/assets/capacitor-cordova-android-plugins.tar.gz +0 -0
- package/assets/capacitor-cordova-ios-plugins.tar.gz +0 -0
- package/assets/ios-pods-template.tar.gz +0 -0
- package/assets/ios-spm-template.tar.gz +0 -0
- package/dist/declarations.d.ts +21 -3
- package/dist/index.js +4 -14
- package/dist/ios/update.js +5 -0
- package/dist/tasks/copy.js +3 -1
- package/dist/tasks/doctor.js +6 -1
- package/dist/tasks/migrate.js +18 -2
- package/dist/telemetry.js +188 -5
- package/dist/util/npm.js +197 -0
- package/dist/util/spm.js +63 -8
- package/package.json +2 -3
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/declarations.d.ts
CHANGED
|
@@ -709,13 +709,31 @@ export interface PluginsConfig {
|
|
|
709
709
|
*
|
|
710
710
|
* This option is only supported on Android.
|
|
711
711
|
*
|
|
712
|
-
* `
|
|
712
|
+
* `native` = (recommended) For older Chromium versions (< v140) this embeds the webview with padding and sets the `env(safe-area-inset-*)` variables to `0px`. For newer Chromium versions (>= v140) this makes sure the webview adheres to the `viewport-fit` meta tag. If set to `viewport-fit="cover"` this will make the webview edge-to-edge and the `env(safe-area-inset-*)` variables will contain the correct values. With those values you could set padding for example to make sure the webview is shown correctly.
|
|
713
713
|
*
|
|
714
|
-
* `
|
|
714
|
+
* `css` = This is the same as `native`, but it also injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview.
|
|
715
|
+
*
|
|
716
|
+
* `disable` = (not recommended) Disable safe area insets handling completely.
|
|
717
|
+
* This shifts the responsibility from Capacitor to your own code to handle the insets.
|
|
718
|
+
* Be aware that this might result in a visually broken UI if your native app code and the content loaded into the webview do not correctly handle safe area insets.
|
|
715
719
|
*
|
|
716
720
|
* @default "css"
|
|
717
721
|
*/
|
|
718
|
-
insetsHandling?: 'css' | 'disable';
|
|
722
|
+
insetsHandling?: 'native' | 'css' | 'disable';
|
|
723
|
+
/**
|
|
724
|
+
* Set an initial value for the to be detected `viewport-fit=` meta tag value.
|
|
725
|
+
* For most apps that support edge-to-edge this value will eventually be `cover`.
|
|
726
|
+
* Therefore you might want to set this value to `cover` to help prevent layout jumps and glitches.
|
|
727
|
+
* If you know the value to be `cover` initially, you can set it here.
|
|
728
|
+
* The value will always end up correctly, no matter what you set here,
|
|
729
|
+
* as long as `insetsHandling` is set to `native` or `css`.
|
|
730
|
+
* It only exists to help prevent layout jumps and glitches.
|
|
731
|
+
*
|
|
732
|
+
* This option is only supported on Android.
|
|
733
|
+
*
|
|
734
|
+
* @default undefined
|
|
735
|
+
*/
|
|
736
|
+
initialViewportFitValueHint?: 'auto' | 'contain' | 'cover';
|
|
719
737
|
/**
|
|
720
738
|
* The style of the text and icons of the system bars.
|
|
721
739
|
*
|
package/dist/index.js
CHANGED
|
@@ -105,24 +105,14 @@ function runProgram(config) {
|
|
|
105
105
|
.option('--configuration <name>', 'Configuration name of the iOS Scheme')
|
|
106
106
|
.option('--keystorealiaspass <keystoreAliasPass>', 'Password for the Key Alias')
|
|
107
107
|
.addOption(new commander_1.Option('--androidreleasetype <androidreleasetype>', 'Android release type; APK or AAB').choices([
|
|
108
|
-
|
|
109
|
-
'APK',
|
|
108
|
+
...telemetry_1.TELEMETRY_SAFE_CHOICES.androidreleasetype,
|
|
110
109
|
]))
|
|
111
110
|
.addOption(new commander_1.Option('--signing-type <signingtype>', 'Program used to sign apps (default: jarsigner)').choices([
|
|
112
|
-
|
|
113
|
-
'jarsigner',
|
|
111
|
+
...telemetry_1.TELEMETRY_SAFE_CHOICES.signingType,
|
|
114
112
|
]))
|
|
115
113
|
.addOption(new commander_1.Option('--xcode-team-id <xcodeTeamID>', 'The Developer team to use for building and exporting the archive'))
|
|
116
|
-
.addOption(new commander_1.Option('--xcode-export-method <xcodeExportMethod>', 'Describes how xcodebuild should export the archive (default: app-store-connect)').choices([
|
|
117
|
-
'
|
|
118
|
-
'release-testing',
|
|
119
|
-
'enterprise',
|
|
120
|
-
'debugging',
|
|
121
|
-
'developer-id',
|
|
122
|
-
'mac-application',
|
|
123
|
-
'validation',
|
|
124
|
-
]))
|
|
125
|
-
.addOption(new commander_1.Option('--xcode-signing-style <xcodeSigningStyle>', 'The iOS signing style to use when building the app for distribution (default: automatic)').choices(['automatic', 'manual']))
|
|
114
|
+
.addOption(new commander_1.Option('--xcode-export-method <xcodeExportMethod>', 'Describes how xcodebuild should export the archive (default: app-store-connect)').choices([...telemetry_1.TELEMETRY_SAFE_CHOICES.xcodeExportMethod]))
|
|
115
|
+
.addOption(new commander_1.Option('--xcode-signing-style <xcodeSigningStyle>', 'The iOS signing style to use when building the app for distribution (default: automatic)').choices([...telemetry_1.TELEMETRY_SAFE_CHOICES.xcodeSigningStyle]))
|
|
126
116
|
.addOption(new commander_1.Option('--xcode-signing-certificate <xcodeSigningCertificate>', 'A certificate name, SHA-1 hash, or automatic selector to use for signing for iOS builds'))
|
|
127
117
|
.addOption(new commander_1.Option('--xcode-provisioning-profile <xcodeProvisioningProfile>', 'A provisioning profile name or UUID for iOS builds'))
|
|
128
118
|
.action((0, cli_1.wrapAction)((0, telemetry_1.telemetryAction)(config, async (platform, { scheme, flavor, keystorepath, keystorepass, keystorealias, keystorealiaspass, androidreleasetype, signingType, configuration, xcodeTeamId, xcodeExportMethod, xcodeSigningStyle, xcodeSigningCertificate, xcodeProvisioningProfile, }) => {
|
package/dist/ios/update.js
CHANGED
|
@@ -546,6 +546,7 @@ async function copyPluginsNativeFiles(config, cordovaPlugins) {
|
|
|
546
546
|
const sourceFiles = (0, plugin_1.getPlatformElement)(p, platform, 'source-file');
|
|
547
547
|
const headerFiles = (0, plugin_1.getPlatformElement)(p, platform, 'header-file');
|
|
548
548
|
const codeFiles = sourceFiles.concat(headerFiles);
|
|
549
|
+
const pluginSuppliesAppDelegate = codeFiles.some((codeFile) => codeFile.$.src.split('/').pop() === 'AppDelegate.h');
|
|
549
550
|
const frameworks = (0, plugin_1.getPlatformElement)(p, platform, 'framework');
|
|
550
551
|
let sourcesFolderName = 'sources';
|
|
551
552
|
if (!isSPM && (0, cordova_1.needsStaticPod)(p)) {
|
|
@@ -592,6 +593,10 @@ async function copyPluginsNativeFiles(config, cordovaPlugins) {
|
|
|
592
593
|
fileContent = fileContent.replace(/self.webView.superview/g, 'self.viewController.view');
|
|
593
594
|
await (0, fs_extra_1.writeFile)(fileDest, fileContent, { encoding: 'utf-8' });
|
|
594
595
|
}
|
|
596
|
+
if (isSPM && !pluginSuppliesAppDelegate && fileContent.includes(`#import "AppDelegate.h"`)) {
|
|
597
|
+
fileContent = fileContent.replaceAll(`#import "AppDelegate.h"`, `#import <Cordova/AppDelegate.h>`);
|
|
598
|
+
await (0, fs_extra_1.writeFile)(fileDest, fileContent, { encoding: 'utf-8' });
|
|
599
|
+
}
|
|
595
600
|
}
|
|
596
601
|
}
|
|
597
602
|
}
|
package/dist/tasks/copy.js
CHANGED
|
@@ -57,7 +57,9 @@ async function copy(config, platformName, inline = false) {
|
|
|
57
57
|
usesLiveUpdates = true;
|
|
58
58
|
}
|
|
59
59
|
let usesSSLPinning = false;
|
|
60
|
-
if (allPlugins.filter((plugin) => plugin.id === '@ionic-enterprise/ssl-pinning' ||
|
|
60
|
+
if (allPlugins.filter((plugin) => plugin.id === '@ionic-enterprise/ssl-pinning' ||
|
|
61
|
+
plugin.id === '@capacitor/ssl-pinning' ||
|
|
62
|
+
plugin.id === '@capacitor-community/ssl-pinning').length > 0) {
|
|
61
63
|
usesSSLPinning = true;
|
|
62
64
|
}
|
|
63
65
|
if (platformName === config.ios.name) {
|
package/dist/tasks/doctor.js
CHANGED
|
@@ -10,6 +10,7 @@ const doctor_2 = require("../ios/doctor");
|
|
|
10
10
|
const log_1 = require("../log");
|
|
11
11
|
const emoji_1 = require("../util/emoji");
|
|
12
12
|
const node_1 = require("../util/node");
|
|
13
|
+
const npm_1 = require("../util/npm");
|
|
13
14
|
const subprocess_1 = require("../util/subprocess");
|
|
14
15
|
async function doctorCommand(config, selectedPlatformName) {
|
|
15
16
|
log_1.output.write(`${(0, emoji_1.emoji)('💊', '')} ${colors_1.default.strong('Capacitor Doctor')} ${(0, emoji_1.emoji)('💊', '')} \n\n`);
|
|
@@ -21,7 +22,7 @@ async function doctorCommand(config, selectedPlatformName) {
|
|
|
21
22
|
}
|
|
22
23
|
exports.doctorCommand = doctorCommand;
|
|
23
24
|
async function doctorCore(config) {
|
|
24
|
-
const [cliVersionPlus, coreVersionPlus, androidVersionPlus, iosVersionPlus, cliVersion, coreVersion, androidVersion, iosVersion,] = await Promise.all([
|
|
25
|
+
const [cliVersionPlus, coreVersionPlus, androidVersionPlus, iosVersionPlus, cliVersion, coreVersion, androidVersion, iosVersion, blockedInstallScripts,] = await Promise.all([
|
|
25
26
|
(0, subprocess_1.getCommandOutput)('npm', ['info', '@capacitor-plus/cli', 'version']),
|
|
26
27
|
(0, subprocess_1.getCommandOutput)('npm', ['info', '@capacitor-plus/core', 'version']),
|
|
27
28
|
(0, subprocess_1.getCommandOutput)('npm', ['info', '@capacitor-plus/android', 'version']),
|
|
@@ -30,6 +31,7 @@ async function doctorCore(config) {
|
|
|
30
31
|
(0, subprocess_1.getCommandOutput)('npm', ['info', '@capacitor/core', 'version']),
|
|
31
32
|
(0, subprocess_1.getCommandOutput)('npm', ['info', '@capacitor/android', 'version']),
|
|
32
33
|
(0, subprocess_1.getCommandOutput)('npm', ['info', '@capacitor/ios', 'version']),
|
|
34
|
+
(0, npm_1.getBlockedInstallScripts)(config.app.rootDir),
|
|
33
35
|
]);
|
|
34
36
|
log_1.output.write(`${colors_1.default.strong('Latest Dependencies:')}\n\n` +
|
|
35
37
|
` @capacitor-plus/cli: ${colors_1.default.weak(cliVersionPlus !== null && cliVersionPlus !== void 0 ? cliVersionPlus : 'unknown')}\n` +
|
|
@@ -43,6 +45,9 @@ async function doctorCore(config) {
|
|
|
43
45
|
`${colors_1.default.strong('Installed Dependencies:')}\n\n`);
|
|
44
46
|
await printInstalledPackages(config);
|
|
45
47
|
log_1.output.write('\n');
|
|
48
|
+
if (blockedInstallScripts !== null) {
|
|
49
|
+
(0, npm_1.logBlockedInstallScripts)(blockedInstallScripts);
|
|
50
|
+
}
|
|
46
51
|
}
|
|
47
52
|
exports.doctorCore = doctorCore;
|
|
48
53
|
async function printInstalledPackages(config) {
|
package/dist/tasks/migrate.js
CHANGED
|
@@ -12,6 +12,7 @@ const errors_1 = require("../errors");
|
|
|
12
12
|
const common_2 = require("../ios/common");
|
|
13
13
|
const log_1 = require("../log");
|
|
14
14
|
const fs_1 = require("../util/fs");
|
|
15
|
+
const npm_1 = require("../util/npm");
|
|
15
16
|
const subprocess_1 = require("../util/subprocess");
|
|
16
17
|
const template_1 = require("../util/template");
|
|
17
18
|
const migrate_uiscene_1 = require("./migrate-uiscene");
|
|
@@ -84,8 +85,10 @@ const pluginVersion = '^8.0.0';
|
|
|
84
85
|
const gradleVersion = '8.14.3';
|
|
85
86
|
const iOSVersion = '15';
|
|
86
87
|
const kotlinVersion = '2.2.20';
|
|
87
|
-
let installFailed = false;
|
|
88
88
|
async function migrateCommand(config, noprompt, packagemanager) {
|
|
89
|
+
let installFailed = false;
|
|
90
|
+
let scriptsBlocked = false;
|
|
91
|
+
let scriptsUnverified = false;
|
|
89
92
|
if (config === null) {
|
|
90
93
|
(0, errors_1.fatal)('Config data missing');
|
|
91
94
|
}
|
|
@@ -153,6 +156,16 @@ async function migrateCommand(config, noprompt, packagemanager) {
|
|
|
153
156
|
log_1.logger.error(`${installerType} install failed. Try deleting node_modules folder and running ${colors_1.default.input(`${installerType} install --force`)} manually.`);
|
|
154
157
|
installFailed = true;
|
|
155
158
|
}
|
|
159
|
+
if (runNpmInstall && !installFailed) {
|
|
160
|
+
const blocked = await (0, npm_1.getBlockedInstallScripts)(config.app.rootDir, installerType);
|
|
161
|
+
if (blocked === null) {
|
|
162
|
+
scriptsUnverified = true;
|
|
163
|
+
}
|
|
164
|
+
else if (blocked.length > 0) {
|
|
165
|
+
(0, npm_1.logBlockedInstallScripts)(blocked);
|
|
166
|
+
scriptsBlocked = (0, npm_1.hasBlockedCapacitorScripts)(blocked);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
156
169
|
// Update iOS Projects
|
|
157
170
|
if ((allDependencies['@capacitor-plus/ios'] || allDependencies['@capacitor/ios']) &&
|
|
158
171
|
(0, fs_extra_1.existsSync)(config.ios.platformDirAbs)) {
|
|
@@ -277,9 +290,12 @@ async function migrateCommand(config, noprompt, packagemanager) {
|
|
|
277
290
|
await (0, common_1.runTask)(`Writing breaking changes.`, () => {
|
|
278
291
|
return writeBreakingChanges();
|
|
279
292
|
});
|
|
280
|
-
if (!installFailed) {
|
|
293
|
+
if (!installFailed && !scriptsBlocked && !scriptsUnverified) {
|
|
281
294
|
(0, log_1.logSuccess)(`Migration to Capacitor ${coreVersion} is complete. Run and test your app!`);
|
|
282
295
|
}
|
|
296
|
+
else if (scriptsBlocked) {
|
|
297
|
+
log_1.logger.warn(`Migration to Capacitor ${coreVersion} is incomplete because npm blocked install scripts. Approve them as described above, then run ${colors_1.default.input('npx cap sync')}.`);
|
|
298
|
+
}
|
|
283
299
|
else {
|
|
284
300
|
log_1.logger.warn(`Migration to Capacitor ${coreVersion} is incomplete. Check the log messages for more information.`);
|
|
285
301
|
}
|
package/dist/telemetry.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getIOSPackageManager = exports.sendMetric = exports.telemetryAction = void 0;
|
|
3
|
+
exports.getIOSPackageManager = exports.sendMetric = exports.getTelemetryVersion = exports.getTelemetryArguments = exports.getTelemetryError = exports.getTelemetryOptions = exports.telemetryAction = exports.TELEMETRY_SAFE_CHOICES = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const commander_1 = require("commander");
|
|
6
6
|
const debug_1 = tslib_1.__importDefault(require("debug"));
|
|
@@ -15,6 +15,104 @@ const debug = (0, debug_1.default)('capacitor:telemetry');
|
|
|
15
15
|
const THANK_YOU = `\nThank you for helping improve Capacitor by sharing anonymous usage data! 💖` +
|
|
16
16
|
`\nInformation about the data we collect is available on our website: ${colors_1.default.strong('https://capacitorjs.com/docs/next/cli/telemetry')}` +
|
|
17
17
|
`\nYou can disable telemetry at any time by using the ${colors_1.default.input('npx cap telemetry off')} command.`;
|
|
18
|
+
/**
|
|
19
|
+
* The option values that are safe to report, keyed by the option's resolved
|
|
20
|
+
* name (commander's `attributeName()`, so `--signing-type` is `signingType`).
|
|
21
|
+
* This is an allowlist by design: a denylist of password-like names would leak
|
|
22
|
+
* the first sensitive option someone adds.
|
|
23
|
+
*
|
|
24
|
+
* The option declarations in `index.ts` spread these same lists into
|
|
25
|
+
* `.choices()`, so an option cannot offer a choice this does not list.
|
|
26
|
+
*/
|
|
27
|
+
exports.TELEMETRY_SAFE_CHOICES = {
|
|
28
|
+
androidreleasetype: ['AAB', 'APK'],
|
|
29
|
+
signingType: ['apksigner', 'jarsigner'],
|
|
30
|
+
xcodeExportMethod: [
|
|
31
|
+
'app-store-connect',
|
|
32
|
+
'release-testing',
|
|
33
|
+
'enterprise',
|
|
34
|
+
'debugging',
|
|
35
|
+
'developer-id',
|
|
36
|
+
'mac-application',
|
|
37
|
+
'validation',
|
|
38
|
+
],
|
|
39
|
+
xcodeSigningStyle: ['automatic', 'manual'],
|
|
40
|
+
};
|
|
41
|
+
const telemetrySafeChoices = new Map(Object.entries(exports.TELEMETRY_SAFE_CHOICES).map(([name, choices]) => [name, new Set(choices)]));
|
|
42
|
+
/**
|
|
43
|
+
* Positional argument values that are safe to report. Everything else is
|
|
44
|
+
* dropped, since arguments like `cap init`'s app name are user content.
|
|
45
|
+
*/
|
|
46
|
+
const TELEMETRY_ARGUMENT_ALLOWLIST = new Set(['android', 'ios', 'web']);
|
|
47
|
+
/**
|
|
48
|
+
* Upper bound on the reported error, so a failed Gradle or Xcode build cannot
|
|
49
|
+
* ship its entire log as a metric.
|
|
50
|
+
*/
|
|
51
|
+
const TELEMETRY_ERROR_MAX_LENGTH = 500;
|
|
52
|
+
const TELEMETRY_COMMAND_FAILED = 'command_failed';
|
|
53
|
+
/**
|
|
54
|
+
* Lines from a failed subprocess that are safe to report. Build-setting dumps,
|
|
55
|
+
* environment echoes, and arbitrary log lines can still carry secrets after
|
|
56
|
+
* value redaction, so only tool error summaries are kept.
|
|
57
|
+
*/
|
|
58
|
+
function pickAllowlistedTelemetryErrorLines(redacted) {
|
|
59
|
+
const lines = redacted.split('\n').filter((line) => {
|
|
60
|
+
const trimmed = line.trim();
|
|
61
|
+
if (!trimmed) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
if (/^Build settings from command line:/i.test(trimmed)) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
if (/^\S+\s=/.test(trimmed)) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
if (/^Error:/i.test(trimmed)) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (/^FAILURE:/i.test(trimmed)) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
if (/platform has not been added/i.test(trimmed)) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
if (/no device matched target/i.test(trimmed)) {
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
if (/keystore password was not accepted/i.test(trimmed)) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
if (/^[\w.-]+: error:/i.test(trimmed)) {
|
|
86
|
+
return true;
|
|
87
|
+
}
|
|
88
|
+
if (/^jarsigner:/i.test(trimmed)) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (/^xcodebuild:/i.test(trimmed)) {
|
|
92
|
+
return true;
|
|
93
|
+
}
|
|
94
|
+
return false;
|
|
95
|
+
});
|
|
96
|
+
return lines.join('\n');
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Shortest value worth scrubbing out of a reported error. An empty string would
|
|
100
|
+
* match between every character, and a one- or two-character option value--a
|
|
101
|
+
* port, a target index--appears throughout unrelated output, so redacting
|
|
102
|
+
* either destroys the error instead of sanitizing it.
|
|
103
|
+
*/
|
|
104
|
+
const TELEMETRY_REDACTION_MIN_LENGTH = 3;
|
|
105
|
+
/**
|
|
106
|
+
* The characters a dependency spec may contain to be reported as-is. A version
|
|
107
|
+
* range has none of the `:/@` a URL or path spec needs, so specs such as
|
|
108
|
+
* `git+https://x-access-token:<token>@github.com/acme/plugin.git` and
|
|
109
|
+
* `file:/Users/<name>/dev/plugin` are reported as {@link NON_REGISTRY_VERSION}
|
|
110
|
+
* instead of shipping a credential or a local path. The pnpm and yarn workspace
|
|
111
|
+
* protocols are allowed through as the only exception, since they resolve
|
|
112
|
+
* within the repo and so cannot carry either.
|
|
113
|
+
*/
|
|
114
|
+
const TELEMETRY_SAFE_VERSION_PATTERN = /^(?:(?:workspace|catalog):)?[\w.\-+^~><=|* ]*$/;
|
|
115
|
+
const NON_REGISTRY_VERSION = 'non-registry';
|
|
18
116
|
function telemetryAction(config, action) {
|
|
19
117
|
return async (...actionArgs) => {
|
|
20
118
|
const start = new Date();
|
|
@@ -41,15 +139,16 @@ function telemetryAction(config, action) {
|
|
|
41
139
|
const capacitorPackages = packages.filter(([k]) => k.startsWith('@capacitor-plus/'));
|
|
42
140
|
const versions = capacitorPackages.map(([k, v]) => [
|
|
43
141
|
`${k.replace(/^@capacitor-plus\//, '').replace(/-/g, '_')}_version`,
|
|
44
|
-
v,
|
|
142
|
+
getTelemetryVersion(v),
|
|
45
143
|
]);
|
|
144
|
+
const options = getTelemetryOptions(cmd);
|
|
46
145
|
const data = {
|
|
47
146
|
app_id: await getAppIdentifier(config),
|
|
48
147
|
command,
|
|
49
|
-
arguments: cmd
|
|
50
|
-
options: JSON.stringify(
|
|
148
|
+
arguments: getTelemetryArguments(cmd),
|
|
149
|
+
options: JSON.stringify(options),
|
|
51
150
|
duration,
|
|
52
|
-
error:
|
|
151
|
+
error: getTelemetryError(cmd, error, config, options),
|
|
53
152
|
node_version: process.version,
|
|
54
153
|
os: config.cli.os,
|
|
55
154
|
ios_package_manager: await getIOSPackageManager(config),
|
|
@@ -72,6 +171,90 @@ function telemetryAction(config, action) {
|
|
|
72
171
|
};
|
|
73
172
|
}
|
|
74
173
|
exports.telemetryAction = telemetryAction;
|
|
174
|
+
/**
|
|
175
|
+
* Build the reported option map: every option name the command resolved, but
|
|
176
|
+
* only the values that cannot carry a secret. Booleans are kept because they
|
|
177
|
+
* cannot encode one, allowlisted options are kept when the value is one this
|
|
178
|
+
* file lists, and every other value--keystore passwords, paths, signing
|
|
179
|
+
* identities--becomes `null`.
|
|
180
|
+
*/
|
|
181
|
+
function getTelemetryOptions(cmd) {
|
|
182
|
+
return Object.fromEntries(Object.entries(cmd.opts()).map(([name, value]) => [name, isTelemetrySafeValue(name, value) ? value : null]));
|
|
183
|
+
}
|
|
184
|
+
exports.getTelemetryOptions = getTelemetryOptions;
|
|
185
|
+
function isTelemetrySafeValue(name, value) {
|
|
186
|
+
var _a, _b;
|
|
187
|
+
if (typeof value === 'boolean') {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
return typeof value === 'string' && ((_b = (_a = telemetrySafeChoices.get(name)) === null || _a === void 0 ? void 0 : _a.has(value)) !== null && _b !== void 0 ? _b : false);
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* The signing credentials `capacitor.config` can supply. `cap build` falls back
|
|
194
|
+
* to the config for each of these when the flag is absent, and the resolved
|
|
195
|
+
* value still reaches the `xcodebuild` and `jarsigner` command lines--so it can
|
|
196
|
+
* come back in their output without ever having appeared in `cmd.opts()`.
|
|
197
|
+
*/
|
|
198
|
+
function getConfiguredCredentials(config) {
|
|
199
|
+
return [
|
|
200
|
+
config.android.buildOptions.keystorePath,
|
|
201
|
+
config.android.buildOptions.keystorePassword,
|
|
202
|
+
config.android.buildOptions.keystoreAlias,
|
|
203
|
+
config.android.buildOptions.keystoreAliasPassword,
|
|
204
|
+
config.ios.buildOptions.teamId,
|
|
205
|
+
config.ios.buildOptions.signingCertificate,
|
|
206
|
+
config.ios.buildOptions.provisioningProfile,
|
|
207
|
+
].filter((value) => typeof value === 'string');
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Build the reported error, scrubbed of every option value and positional
|
|
211
|
+
* argument the rest of the payload drops, plus the signing credentials the
|
|
212
|
+
* config can supply in place of a flag, then truncated.
|
|
213
|
+
*
|
|
214
|
+
* Failed subprocesses rethrow the tool's combined output, which echoes back
|
|
215
|
+
* the very values that were redacted out of the option map--`xcodebuild`
|
|
216
|
+
* prints its command-line build settings, `jarsigner` prints the keystore it
|
|
217
|
+
* was pointed at.
|
|
218
|
+
*/
|
|
219
|
+
function getTelemetryError(cmd, error, config, options = getTelemetryOptions(cmd)) {
|
|
220
|
+
if (!error) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
const values = cmd.opts();
|
|
224
|
+
const redacted = [
|
|
225
|
+
...Object.entries(options)
|
|
226
|
+
.filter(([, value]) => value === null)
|
|
227
|
+
.map(([name]) => values[name]),
|
|
228
|
+
...cmd.args.filter((arg) => !TELEMETRY_ARGUMENT_ALLOWLIST.has(arg)),
|
|
229
|
+
...getConfiguredCredentials(config),
|
|
230
|
+
]
|
|
231
|
+
.filter((value) => typeof value === 'string' && value.length >= TELEMETRY_REDACTION_MIN_LENGTH)
|
|
232
|
+
.sort((a, b) => b.length - a.length);
|
|
233
|
+
const scrubbed = redacted.reduce((message, value) => message.replaceAll(value, '<redacted>'), error.message ? error.message : String(error));
|
|
234
|
+
const allowlisted = pickAllowlistedTelemetryErrorLines(scrubbed);
|
|
235
|
+
if (!allowlisted) {
|
|
236
|
+
return TELEMETRY_COMMAND_FAILED;
|
|
237
|
+
}
|
|
238
|
+
return allowlisted.slice(0, TELEMETRY_ERROR_MAX_LENGTH);
|
|
239
|
+
}
|
|
240
|
+
exports.getTelemetryError = getTelemetryError;
|
|
241
|
+
/**
|
|
242
|
+
* Build the reported positional arguments, keeping only allowlisted values.
|
|
243
|
+
*/
|
|
244
|
+
function getTelemetryArguments(cmd) {
|
|
245
|
+
return cmd.args.filter((arg) => TELEMETRY_ARGUMENT_ALLOWLIST.has(arg)).join(' ');
|
|
246
|
+
}
|
|
247
|
+
exports.getTelemetryArguments = getTelemetryArguments;
|
|
248
|
+
/**
|
|
249
|
+
* Build the reported version of a Capacitor dependency, keeping the spec only
|
|
250
|
+
* when it is a version range. Specs that point somewhere--a git URL, a local
|
|
251
|
+
* path--are reported as being just that, since they can carry a token or a
|
|
252
|
+
* username.
|
|
253
|
+
*/
|
|
254
|
+
function getTelemetryVersion(spec) {
|
|
255
|
+
return spec && TELEMETRY_SAFE_VERSION_PATTERN.test(spec) ? spec : NON_REGISTRY_VERSION;
|
|
256
|
+
}
|
|
257
|
+
exports.getTelemetryVersion = getTelemetryVersion;
|
|
75
258
|
/**
|
|
76
259
|
* If telemetry is enabled, send a metric via IPC to a forked process for uploading.
|
|
77
260
|
*/
|
package/dist/util/npm.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logBlockedInstallScripts = exports.hasBlockedCapacitorScripts = exports.parseBlockedInstallScripts = exports.getBlockedInstallScripts = exports.isNpmProject = exports.logAmbiguousLockfiles = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs_extra_1 = require("fs-extra");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const colors_1 = tslib_1.__importDefault(require("../colors"));
|
|
8
|
+
const log_1 = require("../log");
|
|
9
|
+
const subprocess_1 = require("./subprocess");
|
|
10
|
+
const LOCKFILE_MANAGERS = [
|
|
11
|
+
['package-lock.json', 'npm'],
|
|
12
|
+
['npm-shrinkwrap.json', 'npm'],
|
|
13
|
+
['pnpm-lock.yaml', 'pnpm'],
|
|
14
|
+
['yarn.lock', 'yarn'],
|
|
15
|
+
['bun.lock', 'bun'],
|
|
16
|
+
['bun.lockb', 'bun'],
|
|
17
|
+
];
|
|
18
|
+
function detectLockfileManagers(rootDir) {
|
|
19
|
+
const managers = new Set();
|
|
20
|
+
for (const [lockfile, manager] of LOCKFILE_MANAGERS) {
|
|
21
|
+
if ((0, fs_extra_1.existsSync)((0, path_1.join)(rootDir, lockfile))) {
|
|
22
|
+
managers.add(manager);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return managers;
|
|
26
|
+
}
|
|
27
|
+
function hasNpmLockfileDisabled(rootDir) {
|
|
28
|
+
const npmrcPath = (0, path_1.join)(rootDir, '.npmrc');
|
|
29
|
+
if (!(0, fs_extra_1.existsSync)(npmrcPath)) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
for (const line of (0, fs_extra_1.readFileSync)(npmrcPath, 'utf-8').split('\n')) {
|
|
34
|
+
const trimmed = line.trim();
|
|
35
|
+
if (trimmed === '' || trimmed.startsWith('#') || trimmed.startsWith(';')) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const match = trimmed.match(/^package-lock\s*=\s*(false|0)\s*$/i);
|
|
39
|
+
if (match) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (e) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
function logAmbiguousLockfiles(rootDir) {
|
|
50
|
+
const managers = detectLockfileManagers(rootDir);
|
|
51
|
+
if (managers.size <= 1) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const { packageManager } = (0, fs_extra_1.readJSONSync)((0, path_1.join)(rootDir, 'package.json'));
|
|
56
|
+
if (typeof packageManager === 'string') {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (e) {
|
|
61
|
+
// fall through
|
|
62
|
+
}
|
|
63
|
+
log_1.output.write(`${colors_1.default.strong('Ambiguous package manager:')}\n\n` +
|
|
64
|
+
` Multiple lockfiles are present (${[...managers].join(', ')}), so blocked install scripts cannot be checked reliably.\n` +
|
|
65
|
+
` Remove stale lockfiles or set ${colors_1.default.input('packageManager')} in package.json.\n\n`);
|
|
66
|
+
}
|
|
67
|
+
exports.logAmbiguousLockfiles = logAmbiguousLockfiles;
|
|
68
|
+
/**
|
|
69
|
+
* `npm install-scripts ls` compares the tree against npm's own `allowScripts` map, which yarn and
|
|
70
|
+
* pnpm never write. It therefore reports every install script in a yarn or pnpm project as
|
|
71
|
+
* blocked, even when that package manager ran it, so the check has to know who did the install.
|
|
72
|
+
*
|
|
73
|
+
* Only a positive signal counts. `rootDir` is the app package, which in a workspace holds neither
|
|
74
|
+
* a lockfile nor a `packageManager` field, and guessing npm there reports blocked scripts for an
|
|
75
|
+
* install npm never performed.
|
|
76
|
+
*/
|
|
77
|
+
async function isNpmProject(rootDir) {
|
|
78
|
+
try {
|
|
79
|
+
const { packageManager } = await (0, fs_extra_1.readJSON)((0, path_1.join)(rootDir, 'package.json'));
|
|
80
|
+
if (typeof packageManager === 'string') {
|
|
81
|
+
return packageManager.split('@')[0] === 'npm';
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (e) {
|
|
85
|
+
// no package.json, or an unreadable one: fall through to the lockfiles
|
|
86
|
+
}
|
|
87
|
+
const managers = detectLockfileManagers(rootDir);
|
|
88
|
+
if (managers.size === 1) {
|
|
89
|
+
return managers.has('npm');
|
|
90
|
+
}
|
|
91
|
+
if (managers.size > 1) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
if ((0, fs_extra_1.existsSync)((0, path_1.join)(rootDir, 'package.json')) && hasNpmLockfileDisabled(rootDir)) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
exports.isNpmProject = isNpmProject;
|
|
100
|
+
/**
|
|
101
|
+
* npm 12 blocks dependency install scripts by default and still exits 0, so the only reliable
|
|
102
|
+
* signal is to ask npm directly. `npm install-scripts ls` lists every package whose scripts are
|
|
103
|
+
* still awaiting a decision, and does not exist before npm 12, where the command exits non-zero
|
|
104
|
+
* and `getCommandOutput` hands back null.
|
|
105
|
+
*
|
|
106
|
+
* Pass `packageManager` when the caller already knows which one ran the install; otherwise it is
|
|
107
|
+
* inferred from the project.
|
|
108
|
+
*/
|
|
109
|
+
async function getBlockedInstallScripts(rootDir, packageManager) {
|
|
110
|
+
if (packageManager === undefined) {
|
|
111
|
+
logAmbiguousLockfiles(rootDir);
|
|
112
|
+
}
|
|
113
|
+
const isNpm = packageManager === undefined ? await isNpmProject(rootDir) : packageManager.toLowerCase() === 'npm';
|
|
114
|
+
if (!isNpm) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
const npmVersionOutput = await (0, subprocess_1.getCommandOutput)('npm', ['--version'], { cwd: rootDir });
|
|
118
|
+
if (npmVersionOutput === null) {
|
|
119
|
+
log_1.output.write(`${colors_1.default.strong('Blocked install scripts:')}\n\n` +
|
|
120
|
+
` Could not determine the npm version, so blocked install scripts were not checked.\n\n`);
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
const npmMajor = parseInt(npmVersionOutput.split('.')[0], 10);
|
|
124
|
+
if (!Number.isFinite(npmMajor) || npmMajor < 12) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
const commandOutput = await (0, subprocess_1.getCommandOutput)('npm', ['install-scripts', 'ls', '--json', '--all=false'], {
|
|
128
|
+
cwd: rootDir,
|
|
129
|
+
});
|
|
130
|
+
if (commandOutput === null) {
|
|
131
|
+
log_1.output.write(`${colors_1.default.strong('Blocked install scripts:')}\n\n` +
|
|
132
|
+
` Could not query npm for blocked install scripts. ` +
|
|
133
|
+
`If ${colors_1.default.input('all=true')} is set in ${colors_1.default.input('.npmrc')}, run ${colors_1.default.input('npm install-scripts ls --all=false')} manually.\n\n`);
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return parseBlockedInstallScripts(commandOutput);
|
|
137
|
+
}
|
|
138
|
+
exports.getBlockedInstallScripts = getBlockedInstallScripts;
|
|
139
|
+
function parseBlockedInstallScripts(commandOutput) {
|
|
140
|
+
if (!commandOutput) {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
let parsed;
|
|
144
|
+
try {
|
|
145
|
+
parsed = JSON.parse(commandOutput);
|
|
146
|
+
}
|
|
147
|
+
catch (e) {
|
|
148
|
+
return [];
|
|
149
|
+
}
|
|
150
|
+
if (!Array.isArray(parsed === null || parsed === void 0 ? void 0 : parsed.allowScripts)) {
|
|
151
|
+
return [];
|
|
152
|
+
}
|
|
153
|
+
const blocked = [];
|
|
154
|
+
for (const entry of parsed.allowScripts) {
|
|
155
|
+
const name = entry === null || entry === void 0 ? void 0 : entry.name;
|
|
156
|
+
if (typeof name !== 'string' || !Array.isArray(entry.changes)) {
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
for (const change of entry.changes) {
|
|
160
|
+
if ((change === null || change === void 0 ? void 0 : change.change) !== 'pending' || typeof change.key !== 'string') {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
blocked.push({
|
|
164
|
+
name,
|
|
165
|
+
version: change.key.startsWith(`${name}@`) ? change.key.slice(name.length + 1) : change.key,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return blocked;
|
|
170
|
+
}
|
|
171
|
+
exports.parseBlockedInstallScripts = parseBlockedInstallScripts;
|
|
172
|
+
/**
|
|
173
|
+
* npm 12 denies install scripts by default, so a typical project has pending entries for packages
|
|
174
|
+
* like esbuild or sharp that a Capacitor migration has no bearing on. Only Capacitor's own blocked
|
|
175
|
+
* scripts say anything about whether the migration finished.
|
|
176
|
+
*/
|
|
177
|
+
function hasBlockedCapacitorScripts(blocked) {
|
|
178
|
+
return blocked.some(({ name }) => name.startsWith('@capacitor-plus/') || name.startsWith('@capacitor/'));
|
|
179
|
+
}
|
|
180
|
+
exports.hasBlockedCapacitorScripts = hasBlockedCapacitorScripts;
|
|
181
|
+
function logBlockedInstallScripts(blocked) {
|
|
182
|
+
if (blocked.length === 0) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const names = [...new Set(blocked.map(({ name }) => name))];
|
|
186
|
+
const packages = blocked.map(({ name, version }) => ` ${colors_1.default.strong(`${name}@${version}`)}\n`).join('');
|
|
187
|
+
const approvals = names.map((name) => ` ${colors_1.default.input(`npm install-scripts approve ${name}`)}\n`).join('');
|
|
188
|
+
log_1.output.write(`${colors_1.default.strong('Blocked Install Scripts:')}\n\n` +
|
|
189
|
+
` npm blocked install scripts for ${names.length === 1 ? '1 package' : `${names.length} packages`}. ` +
|
|
190
|
+
`Features that rely on them may not work.\n\n` +
|
|
191
|
+
packages +
|
|
192
|
+
`\n To allow them, run:\n\n` +
|
|
193
|
+
approvals +
|
|
194
|
+
` ${colors_1.default.input('npm rebuild')}\n\n` +
|
|
195
|
+
` ${colors_1.default.weak('Approvals are pinned to an exact version, so repeat this after upgrading the package.')}\n\n`);
|
|
196
|
+
}
|
|
197
|
+
exports.logBlockedInstallScripts = logBlockedInstallScripts;
|
package/dist/util/spm.js
CHANGED
|
@@ -125,7 +125,9 @@ let package = Package(
|
|
|
125
125
|
const options = packageOptions[plugin.id];
|
|
126
126
|
const symlink = options === null || options === void 0 ? void 0 : options.symlink;
|
|
127
127
|
const symlinkFolder = (0, path_1.join)('symlinks', plugin.name);
|
|
128
|
-
const relPath =
|
|
128
|
+
const relPath = symlink
|
|
129
|
+
? (0, fs_1.convertToUnixPath)((0, path_1.join)('symlinks', plugin.name))
|
|
130
|
+
: (0, fs_1.convertToUnixPath)((0, path_1.relative)(config.ios.nativeXcodeProjDirAbs, plugin.rootPath));
|
|
129
131
|
if (symlink) {
|
|
130
132
|
await (0, fs_extra_1.ensureSymlink)(plugin.rootPath, (0, path_1.resolve)(config.ios.nativeProjectDirAbs, 'CapApp-SPM', symlinkFolder));
|
|
131
133
|
}
|
|
@@ -231,6 +233,51 @@ function hasSceneManifest(config) {
|
|
|
231
233
|
return entries['UIApplicationSceneManifest'] !== undefined;
|
|
232
234
|
}
|
|
233
235
|
exports.hasSceneManifest = hasSceneManifest;
|
|
236
|
+
function storyboardResourceExists(nativeTargetDirAbs, storyboardName) {
|
|
237
|
+
const fileName = `${storyboardName}.storyboard`;
|
|
238
|
+
const stack = [nativeTargetDirAbs];
|
|
239
|
+
while (stack.length > 0) {
|
|
240
|
+
const dir = stack.pop();
|
|
241
|
+
for (const entry of (0, fs_extra_1.readdirSync)(dir, { withFileTypes: true })) {
|
|
242
|
+
if (entry.isFile() && entry.name === fileName) {
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
if (entry.isDirectory()) {
|
|
246
|
+
stack.push((0, path_1.resolve)(dir, entry.name));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
function resolvedSceneStoryboardFile(entries, nativeTargetDirAbs) {
|
|
253
|
+
const mainStoryboard = entries['UIMainStoryboardFile'];
|
|
254
|
+
if (typeof mainStoryboard === 'string' && mainStoryboard.trim() !== '') {
|
|
255
|
+
if (storyboardResourceExists(nativeTargetDirAbs, mainStoryboard)) {
|
|
256
|
+
return mainStoryboard;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
function syncSceneStoryboardInManifest(entries, nativeTargetDirAbs) {
|
|
262
|
+
const manifest = entries['UIApplicationSceneManifest'];
|
|
263
|
+
if (!manifest) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
const configs = manifest['UISceneConfigurations'];
|
|
267
|
+
const roleArray = configs === null || configs === void 0 ? void 0 : configs['UIWindowSceneSessionRoleApplication'];
|
|
268
|
+
if (!(roleArray === null || roleArray === void 0 ? void 0 : roleArray.length)) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
const storyboard = resolvedSceneStoryboardFile(entries, nativeTargetDirAbs);
|
|
272
|
+
let changed = false;
|
|
273
|
+
for (const role of roleArray) {
|
|
274
|
+
if (storyboard && role['UISceneStoryboardFile'] === undefined) {
|
|
275
|
+
role['UISceneStoryboardFile'] = storyboard;
|
|
276
|
+
changed = true;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return changed;
|
|
280
|
+
}
|
|
234
281
|
async function addSceneManifestIfNeeded(config) {
|
|
235
282
|
const infoPlist = (0, path_1.resolve)(config.ios.nativeTargetDirAbs, 'Info.plist');
|
|
236
283
|
if (!(0, fs_extra_1.existsSync)(infoPlist)) {
|
|
@@ -239,18 +286,26 @@ async function addSceneManifestIfNeeded(config) {
|
|
|
239
286
|
}
|
|
240
287
|
const entries = (0, plist_1.parse)((0, fs_extra_1.readFileSync)(infoPlist, 'utf-8'));
|
|
241
288
|
if (entries['UIApplicationSceneManifest'] !== undefined) {
|
|
242
|
-
|
|
289
|
+
if (syncSceneStoryboardInManifest(entries, config.ios.nativeTargetDirAbs)) {
|
|
290
|
+
(0, fs_extra_1.writeFileSync)(infoPlist, (0, plist_1.build)(entries));
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
log_1.logger.warn('Found UIApplicationSceneManifest in ' + infoPlist + ', skipping.');
|
|
294
|
+
}
|
|
243
295
|
return;
|
|
244
296
|
}
|
|
297
|
+
const sceneConfiguration = {
|
|
298
|
+
UISceneConfigurationName: 'Default Configuration',
|
|
299
|
+
UISceneDelegateClassName: '$(PRODUCT_MODULE_NAME).SceneDelegate',
|
|
300
|
+
};
|
|
301
|
+
const storyboard = resolvedSceneStoryboardFile(entries, config.ios.nativeTargetDirAbs);
|
|
302
|
+
if (storyboard) {
|
|
303
|
+
sceneConfiguration['UISceneStoryboardFile'] = storyboard;
|
|
304
|
+
}
|
|
245
305
|
entries['UIApplicationSceneManifest'] = {
|
|
246
306
|
UIApplicationSupportsMultipleScenes: false,
|
|
247
307
|
UISceneConfigurations: {
|
|
248
|
-
UIWindowSceneSessionRoleApplication: [
|
|
249
|
-
{
|
|
250
|
-
UISceneConfigurationName: 'Default Configuration',
|
|
251
|
-
UISceneDelegateClassName: '$(PRODUCT_MODULE_NAME).SceneDelegate',
|
|
252
|
-
},
|
|
253
|
-
],
|
|
308
|
+
UIWindowSceneSessionRoleApplication: [sceneConfiguration],
|
|
254
309
|
},
|
|
255
310
|
};
|
|
256
311
|
(0, fs_extra_1.writeFileSync)(infoPlist, (0, plist_1.build)(entries));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capacitor-plus/cli",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.6.1",
|
|
4
4
|
"description": "Capacitor+: Enhanced Capacitor with automated upstream sync - Cross-platform apps with JavaScript and the web",
|
|
5
5
|
"homepage": "https://capgo.app/docs/plugins/capacitor-plus/",
|
|
6
6
|
"author": "Capgo Team <support@capgo.app> (https://capgo.app)",
|
|
@@ -60,8 +60,7 @@
|
|
|
60
60
|
"semver": "^7.6.3",
|
|
61
61
|
"tar": "^7.5.3",
|
|
62
62
|
"tslib": "^2.8.1",
|
|
63
|
-
"
|
|
64
|
-
"xcode": "^3.0.1",
|
|
63
|
+
"xcode": "^4.0.0",
|
|
65
64
|
"xml2js": "^0.6.2"
|
|
66
65
|
},
|
|
67
66
|
"devDependencies": {
|