expo-iap 4.3.2 → 4.3.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-iap",
3
- "version": "4.3.2",
3
+ "version": "4.3.3",
4
4
  "description": "In App Purchase module in Expo",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -8,6 +8,8 @@ type LocalPathOption = string | {
8
8
  ios?: string;
9
9
  android?: string;
10
10
  };
11
+ type GradleLanguage = 'groovy' | 'kotlin';
12
+ export declare const ensureLocalOpenIapFlavorStrategy: (contents: string, flavor: "play" | "horizon", language?: GradleLanguage) => string;
11
13
  declare const withLocalOpenIAP: ConfigPlugin<{
12
14
  localPath?: LocalPathOption;
13
15
  iosAlternativeBilling?: IOSAlternativeBillingConfig;
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ensureLocalOpenIapFlavorStrategy = void 0;
36
37
  const config_plugins_1 = require("expo/config-plugins");
37
38
  const fs = __importStar(require("fs"));
38
39
  const path = __importStar(require("path"));
@@ -72,6 +73,42 @@ const logOnce = (() => {
72
73
  }
73
74
  };
74
75
  })();
76
+ const LOCAL_OPENIAP_FLAVOR_BLOCK_START = '// Added by expo-iap (local openiap-google flavor selection)';
77
+ const LOCAL_OPENIAP_FLAVOR_BLOCK_END = '// End expo-iap local openiap-google flavor selection';
78
+ const normalizeGradleLanguage = (language) => language === 'kotlin' ? 'kotlin' : 'groovy';
79
+ const ensureLocalOpenIapFlavorStrategy = (contents, flavor, language = 'groovy') => {
80
+ const existingBlockPattern = new RegExp(`\\n?${escapeRegExp(LOCAL_OPENIAP_FLAVOR_BLOCK_START)}[\\s\\S]*?${escapeRegExp(LOCAL_OPENIAP_FLAVOR_BLOCK_END)}\\n?`, 'gm');
81
+ const cleaned = contents
82
+ .replace(existingBlockPattern, '\n')
83
+ .replace(/\n{3,}/g, '\n\n')
84
+ .trimEnd();
85
+ const strategyBlock = language === 'kotlin'
86
+ ? `subprojects {
87
+ plugins.withId("com.android.library") {
88
+ extensions.configure<com.android.build.gradle.LibraryExtension>("android") {
89
+ defaultConfig {
90
+ missingDimensionStrategy("platform", "${flavor}")
91
+ }
92
+ }
93
+ }
94
+ }`
95
+ : `subprojects { subproject ->
96
+ subproject.plugins.withId("com.android.library") {
97
+ subproject.android {
98
+ defaultConfig {
99
+ missingDimensionStrategy "platform", "${flavor}"
100
+ }
101
+ }
102
+ }
103
+ }`;
104
+ return `${cleaned}
105
+
106
+ ${LOCAL_OPENIAP_FLAVOR_BLOCK_START}
107
+ ${strategyBlock}
108
+ ${LOCAL_OPENIAP_FLAVOR_BLOCK_END}
109
+ `;
110
+ };
111
+ exports.ensureLocalOpenIapFlavorStrategy = ensureLocalOpenIapFlavorStrategy;
75
112
  const withLocalOpenIAP = (config, props) => {
76
113
  // Import and apply iOS alternative billing configuration if provided
77
114
  if (props?.iosAlternativeBilling) {
@@ -162,9 +199,15 @@ const withLocalOpenIAP = (config, props) => {
162
199
  .replace(/\\/g, '/');
163
200
  // 1) settings.gradle: include and map projectDir
164
201
  const settings = config.modResults;
165
- const includeLine = "include ':openiap-google'";
166
- const projectDirLine = `project(':openiap-google').projectDir = new File(settingsDir, '${relativeAndroidModulePath}')`;
167
- const projectDirPattern = /^project\(':openiap-google'\)\.projectDir\s*=.*$/gm;
202
+ const settingsLanguage = normalizeGradleLanguage(settings.language);
203
+ const includeLine = settingsLanguage === 'kotlin'
204
+ ? 'include(":openiap-google")'
205
+ : "include ':openiap-google'";
206
+ const projectDirLine = settingsLanguage === 'kotlin'
207
+ ? `project(":openiap-google").projectDir = File(settingsDir, "${relativeAndroidModulePath}")`
208
+ : `project(':openiap-google').projectDir = new File(settingsDir, '${relativeAndroidModulePath}')`;
209
+ const includePattern = /include\s*(?:\(\s*)?["']:openiap-google["']\s*\)?/;
210
+ const projectDirPattern = /^\s*project\(["']:openiap-google["']\)\.projectDir\s*=.*$/gm;
168
211
  let contents = settings.contents ?? '';
169
212
  // Ensure pluginManagement has plugin mappings required by the included module
170
213
  const injectPluginManagement = () => {
@@ -208,7 +251,7 @@ const withLocalOpenIAP = (config, props) => {
208
251
  }
209
252
  };
210
253
  injectPluginManagement();
211
- if (!contents.includes(includeLine))
254
+ if (!includePattern.test(contents))
212
255
  contents += `\n${includeLine}\n`;
213
256
  if (projectDirPattern.test(contents)) {
214
257
  contents = contents.replace(projectDirPattern, projectDirLine);
@@ -232,9 +275,14 @@ const withLocalOpenIAP = (config, props) => {
232
275
  return config;
233
276
  }
234
277
  const gradle = config.modResults;
235
- const dependencyLine = ` implementation project(':openiap-google')`;
278
+ const appLanguage = normalizeGradleLanguage(gradle.language);
279
+ const dependencyLine = appLanguage === 'kotlin'
280
+ ? ` implementation(project(":openiap-google"))`
281
+ : ` implementation project(':openiap-google')`;
236
282
  const flavor = props?.isHorizonEnabled ? 'horizon' : 'play';
237
- const strategyLine = ` missingDimensionStrategy "platform", "${flavor}"`;
283
+ const strategyLine = appLanguage === 'kotlin'
284
+ ? ` missingDimensionStrategy("platform", "${flavor}")`
285
+ : ` missingDimensionStrategy "platform", "${flavor}"`;
238
286
  let contents = gradle.contents;
239
287
  // Remove Maven deps (both openiap-google and openiap-google-horizon)
240
288
  // to avoid duplicate classes with local module
@@ -273,6 +321,23 @@ const withLocalOpenIAP = (config, props) => {
273
321
  gradle.contents = contents;
274
322
  return config;
275
323
  });
324
+ // 2b) project build.gradle: Expo autolinked library modules can consume the
325
+ // local flavored OpenIAP module transitively, so give them the same default.
326
+ config = (0, config_plugins_1.withProjectBuildGradle)(config, (config) => {
327
+ const projectRoot = config.modRequest.projectRoot;
328
+ const raw = props?.localPath;
329
+ const androidInput = typeof raw === 'string' ? undefined : raw?.android;
330
+ const androidModulePath = resolveAndroidModulePath(androidInput) ||
331
+ resolveAndroidModulePath(path.resolve(projectRoot, 'openiap-google')) ||
332
+ null;
333
+ if (!androidModulePath || !fs.existsSync(androidModulePath)) {
334
+ return config;
335
+ }
336
+ const flavor = props?.isHorizonEnabled ? 'horizon' : 'play';
337
+ config.modResults.contents = (0, exports.ensureLocalOpenIapFlavorStrategy)(config.modResults.contents, flavor, normalizeGradleLanguage(config.modResults.language));
338
+ logOnce(`🛠️ expo-iap: Added local OpenIAP flavor strategy for ${flavor}`);
339
+ return config;
340
+ });
276
341
  // 3) Set horizonEnabled in gradle.properties
277
342
  config = (0, config_plugins_1.withDangerousMod)(config, [
278
343
  'android',
@@ -3,6 +3,7 @@ import {
3
3
  withDangerousMod,
4
4
  withSettingsGradle,
5
5
  withAppBuildGradle,
6
+ withProjectBuildGradle,
6
7
  } from 'expo/config-plugins';
7
8
  import * as fs from 'fs';
8
9
  import * as path from 'path';
@@ -16,6 +17,7 @@ import {
16
17
  * This is only for local development with openiap-apple library
17
18
  */
18
19
  type LocalPathOption = string | {ios?: string; android?: string};
20
+ type GradleLanguage = 'groovy' | 'kotlin';
19
21
 
20
22
  interface AndroidGradlePluginVersions {
21
23
  kotlin: string;
@@ -86,6 +88,59 @@ const logOnce = (() => {
86
88
  };
87
89
  })();
88
90
 
91
+ const LOCAL_OPENIAP_FLAVOR_BLOCK_START =
92
+ '// Added by expo-iap (local openiap-google flavor selection)';
93
+ const LOCAL_OPENIAP_FLAVOR_BLOCK_END =
94
+ '// End expo-iap local openiap-google flavor selection';
95
+
96
+ const normalizeGradleLanguage = (language?: string): GradleLanguage =>
97
+ language === 'kotlin' ? 'kotlin' : 'groovy';
98
+
99
+ export const ensureLocalOpenIapFlavorStrategy = (
100
+ contents: string,
101
+ flavor: 'play' | 'horizon',
102
+ language: GradleLanguage = 'groovy',
103
+ ): string => {
104
+ const existingBlockPattern = new RegExp(
105
+ `\\n?${escapeRegExp(
106
+ LOCAL_OPENIAP_FLAVOR_BLOCK_START,
107
+ )}[\\s\\S]*?${escapeRegExp(LOCAL_OPENIAP_FLAVOR_BLOCK_END)}\\n?`,
108
+ 'gm',
109
+ );
110
+ const cleaned = contents
111
+ .replace(existingBlockPattern, '\n')
112
+ .replace(/\n{3,}/g, '\n\n')
113
+ .trimEnd();
114
+
115
+ const strategyBlock =
116
+ language === 'kotlin'
117
+ ? `subprojects {
118
+ plugins.withId("com.android.library") {
119
+ extensions.configure<com.android.build.gradle.LibraryExtension>("android") {
120
+ defaultConfig {
121
+ missingDimensionStrategy("platform", "${flavor}")
122
+ }
123
+ }
124
+ }
125
+ }`
126
+ : `subprojects { subproject ->
127
+ subproject.plugins.withId("com.android.library") {
128
+ subproject.android {
129
+ defaultConfig {
130
+ missingDimensionStrategy "platform", "${flavor}"
131
+ }
132
+ }
133
+ }
134
+ }`;
135
+
136
+ return `${cleaned}
137
+
138
+ ${LOCAL_OPENIAP_FLAVOR_BLOCK_START}
139
+ ${strategyBlock}
140
+ ${LOCAL_OPENIAP_FLAVOR_BLOCK_END}
141
+ `;
142
+ };
143
+
89
144
  const withLocalOpenIAP: ConfigPlugin<
90
145
  {
91
146
  localPath?: LocalPathOption;
@@ -203,10 +258,18 @@ const withLocalOpenIAP: ConfigPlugin<
203
258
 
204
259
  // 1) settings.gradle: include and map projectDir
205
260
  const settings = config.modResults;
206
- const includeLine = "include ':openiap-google'";
207
- const projectDirLine = `project(':openiap-google').projectDir = new File(settingsDir, '${relativeAndroidModulePath}')`;
261
+ const settingsLanguage = normalizeGradleLanguage(settings.language);
262
+ const includeLine =
263
+ settingsLanguage === 'kotlin'
264
+ ? 'include(":openiap-google")'
265
+ : "include ':openiap-google'";
266
+ const projectDirLine =
267
+ settingsLanguage === 'kotlin'
268
+ ? `project(":openiap-google").projectDir = File(settingsDir, "${relativeAndroidModulePath}")`
269
+ : `project(':openiap-google').projectDir = new File(settingsDir, '${relativeAndroidModulePath}')`;
270
+ const includePattern = /include\s*(?:\(\s*)?["']:openiap-google["']\s*\)?/;
208
271
  const projectDirPattern =
209
- /^project\(':openiap-google'\)\.projectDir\s*=.*$/gm;
272
+ /^\s*project\(["']:openiap-google["']\)\.projectDir\s*=.*$/gm;
210
273
  let contents = settings.contents ?? '';
211
274
 
212
275
  // Ensure pluginManagement has plugin mappings required by the included module
@@ -279,7 +342,7 @@ const withLocalOpenIAP: ConfigPlugin<
279
342
  };
280
343
 
281
344
  injectPluginManagement();
282
- if (!contents.includes(includeLine)) contents += `\n${includeLine}\n`;
345
+ if (!includePattern.test(contents)) contents += `\n${includeLine}\n`;
283
346
  if (projectDirPattern.test(contents)) {
284
347
  contents = contents.replace(projectDirPattern, projectDirLine);
285
348
  } else if (!contents.includes(projectDirLine)) {
@@ -305,9 +368,16 @@ const withLocalOpenIAP: ConfigPlugin<
305
368
  }
306
369
 
307
370
  const gradle = config.modResults;
308
- const dependencyLine = ` implementation project(':openiap-google')`;
371
+ const appLanguage = normalizeGradleLanguage(gradle.language);
372
+ const dependencyLine =
373
+ appLanguage === 'kotlin'
374
+ ? ` implementation(project(":openiap-google"))`
375
+ : ` implementation project(':openiap-google')`;
309
376
  const flavor = props?.isHorizonEnabled ? 'horizon' : 'play';
310
- const strategyLine = ` missingDimensionStrategy "platform", "${flavor}"`;
377
+ const strategyLine =
378
+ appLanguage === 'kotlin'
379
+ ? ` missingDimensionStrategy("platform", "${flavor}")`
380
+ : ` missingDimensionStrategy "platform", "${flavor}"`;
311
381
 
312
382
  let contents = gradle.contents;
313
383
 
@@ -358,6 +428,31 @@ const withLocalOpenIAP: ConfigPlugin<
358
428
  return config;
359
429
  });
360
430
 
431
+ // 2b) project build.gradle: Expo autolinked library modules can consume the
432
+ // local flavored OpenIAP module transitively, so give them the same default.
433
+ config = withProjectBuildGradle(config, (config) => {
434
+ const projectRoot = (config.modRequest as any).projectRoot as string;
435
+ const raw = props?.localPath;
436
+ const androidInput = typeof raw === 'string' ? undefined : raw?.android;
437
+ const androidModulePath =
438
+ resolveAndroidModulePath(androidInput) ||
439
+ resolveAndroidModulePath(path.resolve(projectRoot, 'openiap-google')) ||
440
+ null;
441
+
442
+ if (!androidModulePath || !fs.existsSync(androidModulePath)) {
443
+ return config;
444
+ }
445
+
446
+ const flavor = props?.isHorizonEnabled ? 'horizon' : 'play';
447
+ config.modResults.contents = ensureLocalOpenIapFlavorStrategy(
448
+ config.modResults.contents,
449
+ flavor,
450
+ normalizeGradleLanguage(config.modResults.language),
451
+ );
452
+ logOnce(`🛠️ expo-iap: Added local OpenIAP flavor strategy for ${flavor}`);
453
+ return config;
454
+ });
455
+
361
456
  // 3) Set horizonEnabled in gradle.properties
362
457
  config = withDangerousMod(config, [
363
458
  'android',