@open-mova/cli 0.1.18 → 0.1.20
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/README.md +24 -0
- package/dist/application/configuration.js +21 -0
- package/dist/commands/capacitor.js +74 -7
- package/dist/types.d.ts +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -165,6 +165,30 @@ mova cap sync android
|
|
|
165
165
|
mova cap sync
|
|
166
166
|
```
|
|
167
167
|
|
|
168
|
+
En Android, el CLI aplica también los requisitos técnicos de los plugins
|
|
169
|
+
incluidos por la shell: configura el repositorio AAR de Background Runner y
|
|
170
|
+
eleva `minSdkVersion` a 28 cuando está instalado Local LLM. También añade la
|
|
171
|
+
entrada obligatoria de Google Maps. Para usar mapas reales, define una clave
|
|
172
|
+
restringida antes de sincronizar:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
OPEN_MOVA_GOOGLE_MAPS_ANDROID_API_KEY=tu_clave mova cap sync android
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Como alternativa, guarda `native.googleMaps.androidApiKey` en
|
|
179
|
+
`mova.config.json`. Sin una clave configurada la aplicación sigue arrancando,
|
|
180
|
+
pero Google Maps no estará operativo.
|
|
181
|
+
|
|
182
|
+
```json
|
|
183
|
+
{
|
|
184
|
+
"native": {
|
|
185
|
+
"googleMaps": {
|
|
186
|
+
"androidApiKey": "tu_clave"
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
168
192
|
### `mova cap open`
|
|
169
193
|
|
|
170
194
|
Abre el proyecto nativo en Android Studio o Xcode.
|
|
@@ -77,11 +77,32 @@ function validateApplicationConfiguration(value, configurationPath) {
|
|
|
77
77
|
commit: value.shell.commit,
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
|
+
let native;
|
|
81
|
+
if (value.native !== undefined) {
|
|
82
|
+
if (!isRecord(value.native)) {
|
|
83
|
+
throw new Error(`${configurationPath} contiene una configuración nativa no válida.`);
|
|
84
|
+
}
|
|
85
|
+
if (value.native.googleMaps !== undefined) {
|
|
86
|
+
if (!isRecord(value.native.googleMaps) ||
|
|
87
|
+
typeof value.native.googleMaps.androidApiKey !== 'string') {
|
|
88
|
+
throw new Error(`${configurationPath} contiene una clave de Google Maps no válida.`);
|
|
89
|
+
}
|
|
90
|
+
native = {
|
|
91
|
+
googleMaps: {
|
|
92
|
+
androidApiKey: value.native.googleMaps.androidApiKey,
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
native = {};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
80
100
|
const microfrontends = value.microfrontends.map((entry) => validateMicrofrontend(entry, configurationPath));
|
|
81
101
|
return {
|
|
82
102
|
schemaVersion: 1,
|
|
83
103
|
name: value.name,
|
|
84
104
|
...(shell ? { shell } : {}),
|
|
105
|
+
...(native ? { native } : {}),
|
|
85
106
|
microfrontends,
|
|
86
107
|
};
|
|
87
108
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { readApplicationConfiguration, requireApplicationRoot, } from '../application/configuration.js';
|
|
5
5
|
import { localBinaryName, npmCommand, useCommandShell } from '../utils/platform.js';
|
|
@@ -12,7 +12,7 @@ export function registerCapacitorCommands(program) {
|
|
|
12
12
|
const root = requireApplicationRoot(process.cwd());
|
|
13
13
|
buildMobileShell(root);
|
|
14
14
|
runCapacitor(root, ['add', selectedPlatform]);
|
|
15
|
-
configureNativePlatform(root, selectedPlatform);
|
|
15
|
+
configureNativePlatform(root, selectedPlatform, readApplicationConfiguration(root));
|
|
16
16
|
});
|
|
17
17
|
capacitor.command('sync [platform]')
|
|
18
18
|
.description('Compila la shell y sincroniza sus assets y plugins nativos')
|
|
@@ -22,10 +22,10 @@ export function registerCapacitorCommands(program) {
|
|
|
22
22
|
const selectedPlatform = platform ? parsePlatform(platform) : undefined;
|
|
23
23
|
runCapacitor(root, ['sync', ...(selectedPlatform ? [selectedPlatform] : [])]);
|
|
24
24
|
if (selectedPlatform) {
|
|
25
|
-
configureNativePlatform(root, selectedPlatform);
|
|
25
|
+
configureNativePlatform(root, selectedPlatform, readApplicationConfiguration(root));
|
|
26
26
|
}
|
|
27
27
|
else {
|
|
28
|
-
configureExistingPlatforms(root);
|
|
28
|
+
configureExistingPlatforms(root, readApplicationConfiguration(root));
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
31
|
capacitor.command('open <platform>')
|
|
@@ -72,16 +72,83 @@ function isHttpsUrl(value) {
|
|
|
72
72
|
return false;
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
|
-
function configureExistingPlatforms(root) {
|
|
75
|
+
function configureExistingPlatforms(root, configuration) {
|
|
76
76
|
if (existsSync(join(root, 'android'))) {
|
|
77
|
-
configureNativePlatform(root, 'android');
|
|
77
|
+
configureNativePlatform(root, 'android', configuration);
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
-
function configureNativePlatform(root, platform) {
|
|
80
|
+
function configureNativePlatform(root, platform, configuration) {
|
|
81
81
|
if (platform === 'android') {
|
|
82
|
+
configureMinimumAndroidSdk(root);
|
|
82
83
|
configureBackgroundRunnerForAndroid(root);
|
|
84
|
+
configureGoogleMapsForAndroid(root, configuration);
|
|
83
85
|
}
|
|
84
86
|
}
|
|
87
|
+
function configureGoogleMapsForAndroid(root, configuration) {
|
|
88
|
+
const packageJson = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
89
|
+
if (!packageJson.dependencies?.['@capacitor/google-maps'])
|
|
90
|
+
return;
|
|
91
|
+
const manifestPath = join(root, 'android', 'app', 'src', 'main', 'AndroidManifest.xml');
|
|
92
|
+
if (!existsSync(manifestPath)) {
|
|
93
|
+
throw new Error('No se encuentra AndroidManifest.xml para configurar Google Maps.');
|
|
94
|
+
}
|
|
95
|
+
const manifest = readFileSync(manifestPath, 'utf8');
|
|
96
|
+
const apiKeyMetadata = 'android:name="com.google.android.geo.API_KEY"';
|
|
97
|
+
if (!manifest.includes(apiKeyMetadata)) {
|
|
98
|
+
const applicationCloseTag = '</application>';
|
|
99
|
+
if (!manifest.includes(applicationCloseTag)) {
|
|
100
|
+
throw new Error('No se ha encontrado el elemento application en AndroidManifest.xml.');
|
|
101
|
+
}
|
|
102
|
+
const metadata = [
|
|
103
|
+
' <meta-data',
|
|
104
|
+
` ${apiKeyMetadata}`,
|
|
105
|
+
' android:value="@string/open_mova_google_maps_api_key" />',
|
|
106
|
+
].join('\n');
|
|
107
|
+
writeFileSync(manifestPath, manifest.replace(applicationCloseTag, `${metadata}\n ${applicationCloseTag}`), 'utf8');
|
|
108
|
+
}
|
|
109
|
+
const valuesDirectory = join(root, 'android', 'app', 'src', 'main', 'res', 'values');
|
|
110
|
+
mkdirSync(valuesDirectory, { recursive: true });
|
|
111
|
+
const apiKey = process.env.OPEN_MOVA_GOOGLE_MAPS_ANDROID_API_KEY
|
|
112
|
+
?? configuration.native?.googleMaps?.androidApiKey
|
|
113
|
+
// Google Maps aborta la aplicación si falta por completo esta entrada.
|
|
114
|
+
// El valor explícito permite arrancar; Maps seguirá requiriendo una clave real al usarse.
|
|
115
|
+
?? 'OPEN_MOVA_GOOGLE_MAPS_API_KEY_NOT_CONFIGURED';
|
|
116
|
+
const resource = [
|
|
117
|
+
'<?xml version="1.0" encoding="utf-8"?>',
|
|
118
|
+
'<resources>',
|
|
119
|
+
` <string name="open_mova_google_maps_api_key" translatable="false">${escapeXml(apiKey)}</string>`,
|
|
120
|
+
'</resources>',
|
|
121
|
+
'',
|
|
122
|
+
].join('\n');
|
|
123
|
+
writeFileSync(join(valuesDirectory, 'open_mova_google_maps.xml'), resource, 'utf8');
|
|
124
|
+
}
|
|
125
|
+
function escapeXml(value) {
|
|
126
|
+
return value.replace(/[&<>"']/g, (character) => ({
|
|
127
|
+
'&': '&',
|
|
128
|
+
'<': '<',
|
|
129
|
+
'>': '>',
|
|
130
|
+
'"': '"',
|
|
131
|
+
"'": ''',
|
|
132
|
+
})[character] ?? character);
|
|
133
|
+
}
|
|
134
|
+
function configureMinimumAndroidSdk(root) {
|
|
135
|
+
const packageJson = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
136
|
+
if (!packageJson.dependencies?.['@capacitor/local-llm'])
|
|
137
|
+
return;
|
|
138
|
+
const variablesPath = join(root, 'android', 'variables.gradle');
|
|
139
|
+
if (!existsSync(variablesPath)) {
|
|
140
|
+
throw new Error('No se encuentra android/variables.gradle para configurar Local LLM.');
|
|
141
|
+
}
|
|
142
|
+
const variables = readFileSync(variablesPath, 'utf8');
|
|
143
|
+
const minimumSdkPattern = /minSdkVersion\s*=\s*(\d+)/;
|
|
144
|
+
const currentMinimumSdk = Number(minimumSdkPattern.exec(variables)?.[1]);
|
|
145
|
+
if (!Number.isFinite(currentMinimumSdk)) {
|
|
146
|
+
throw new Error('No se ha encontrado minSdkVersion en android/variables.gradle.');
|
|
147
|
+
}
|
|
148
|
+
if (currentMinimumSdk >= 28)
|
|
149
|
+
return;
|
|
150
|
+
writeFileSync(variablesPath, variables.replace(minimumSdkPattern, 'minSdkVersion = 28'), 'utf8');
|
|
151
|
+
}
|
|
85
152
|
function configureBackgroundRunnerForAndroid(root) {
|
|
86
153
|
const packageJson = JSON.parse(readFileSync(join(root, 'package.json'), 'utf8'));
|
|
87
154
|
if (!packageJson.dependencies?.['@capacitor/background-runner'])
|
package/dist/types.d.ts
CHANGED
|
@@ -6,8 +6,16 @@ export interface OpenMovaApplicationConfiguration {
|
|
|
6
6
|
readonly version: string;
|
|
7
7
|
readonly commit: string;
|
|
8
8
|
};
|
|
9
|
+
/** Configuración opcional de las capacidades nativas de la aplicación. */
|
|
10
|
+
readonly native?: NativeConfiguration;
|
|
9
11
|
readonly microfrontends: readonly MicrofrontendConfiguration[];
|
|
10
12
|
}
|
|
13
|
+
export interface NativeConfiguration {
|
|
14
|
+
readonly googleMaps?: {
|
|
15
|
+
/** Clave de Android Maps. También puede venir de OPEN_MOVA_GOOGLE_MAPS_ANDROID_API_KEY. */
|
|
16
|
+
readonly androidApiKey?: string;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
11
19
|
export interface MicrofrontendConfiguration {
|
|
12
20
|
readonly name: string;
|
|
13
21
|
readonly route: string;
|