adwhale-sdk-react-native 2.7.201 → 2.7.202

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.
Files changed (3) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/plugin/index.js +236 -167
package/README.md CHANGED
@@ -43,7 +43,7 @@ npm install adwhale-sdk-react-native
43
43
  운영 안정성을 중시하는 경우
44
44
  ```json
45
45
  {
46
- "adwhale-sdk-react-native": "~2.7.201"
46
+ "adwhale-sdk-react-native": "~2.7.202"
47
47
  }
48
48
  ```
49
49
  - Android SDK 2.7.2 기반 유지
@@ -52,7 +52,7 @@ npm install adwhale-sdk-react-native
52
52
  ### 동일한 Android SDK 기반의 최신 RN 수정만 사용
53
53
  ```json
54
54
  {
55
- "adwhale-sdk-react-native": "^2.7.201"
55
+ "adwhale-sdk-react-native": "^2.7.202"
56
56
  }
57
57
  ```
58
58
  - Android SDK 2.7.x 범위 내 최신 RN 수정 자동 반영
@@ -62,7 +62,7 @@ npm install adwhale-sdk-react-native
62
62
  ### 특정 버전을 완전히 고정하여 사용하는 경우
63
63
  ```json
64
64
  {
65
- "adwhale-sdk-react-native": "2.7.201"
65
+ "adwhale-sdk-react-native": "2.7.202"
66
66
  }
67
67
  ```
68
68
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adwhale-sdk-react-native",
3
- "version": "2.7.201",
3
+ "version": "2.7.202",
4
4
  "description": "Adwhale SDK React Native library for integrating Adwhale advertising mediation SDK into React Native applications.",
5
5
  "main": "./lib/module/index.js",
6
6
  "react-native": "./lib/module/index.js",
package/plugin/index.js CHANGED
@@ -1,38 +1,36 @@
1
1
  /**
2
- * AdWhale Expo Config Plugin
2
+ * AdWhale Expo Config Plugin (Most Compatible)
3
3
  *
4
4
  * 목적:
5
5
  * - Expo Bare / Managed / prebuild / EAS Dev Client 환경에서
6
6
  * AdWhale Android SDK 의존성을 정상적으로 resolve 하기 위해
7
7
  * Maven Repository들을 자동으로 Gradle 설정에 추가한다.
8
8
  *
9
- * 전략:
9
+ * 왜 withDangerousMod?
10
+ * - 일부 Expo/@expo config-plugins 버전에서는 Android 전용 helper(mod)
11
+ * (예: withAndroidBuildGradle, withAndroidProjectBuildGradle)가 존재하지 않는다.
12
+ * - helper에 의존하면 TypeError가 발생할 수 있다.
13
+ * - withDangerousMod는 폭넓게 제공되며, 파일을 직접 수정하는 방식이라 호환성이 가장 높다.
14
+ *
15
+ * 적용 정책:
10
16
  * 1) android/settings.gradle
11
- * - dependencyResolutionManagement { repositories { ... } } 에 repo 추가
12
- * - rootProject.name 바로 다음 줄에 삽입 (가독성 + 충돌 최소화)
17
+ * - rootProject.name 바로 다음 줄에
18
+ * dependencyResolutionManagement { repositories { ... } } 추가/보강
13
19
  *
14
20
  * 2) android/build.gradle (project-level)
15
- * - allprojects { repositories { ... } } repo 추가
21
+ * - allprojects { repositories { ... } } 안에만 Maven repo 추가
22
+ * - buildscript.repositories에는 절대 추가하지 않음
16
23
  *
17
24
  * 3) 단, settings.gradle에
18
25
  * repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
19
- * 가 존재하는 경우:
20
- * - project-level repo 사용이 금지되므로
21
- * - build.gradle 수정은 생략
22
- *
23
- * 결과:
24
- * - AGP 7/8
25
- * - Expo SDK 49+
26
- * - Version Catalog / 강한 repo 정책
27
- * 모두에서 안전하게 동작
26
+ * 가 존재하면:
27
+ * - project-level repo 사용이 금지될 수 있으므로
28
+ * - build.gradle 수정은 생략
28
29
  */
29
30
 
30
- const {
31
- createRunOncePlugin,
32
- withAndroidProjectBuildGradle,
33
- withSettingsGradle,
34
- } = require('expo/config-plugins');
35
-
31
+ const fs = require('fs');
32
+ const path = require('path');
33
+ const { createRunOncePlugin, withDangerousMod } = require('expo/config-plugins');
36
34
  const pkg = require('../package.json');
37
35
 
38
36
  /**
@@ -63,37 +61,48 @@ const MAVEN_REPOSITORIES = [
63
61
  const MARKER_BEGIN = '// [AdWhale] BEGIN Maven Repositories';
64
62
  const MARKER_END = '// [AdWhale] END Maven Repositories';
65
63
 
64
+ function readTextIfExists(filePath) {
65
+ if (!fs.existsSync(filePath)) return null;
66
+ return fs.readFileSync(filePath, 'utf8');
67
+ }
68
+
69
+ function writeText(filePath, contents) {
70
+ fs.writeFileSync(filePath, contents, 'utf8');
71
+ }
72
+
66
73
  /**
67
- * repo 블록 문자열 생성
74
+ * settings.gradle에 FAIL_ON_PROJECT_REPOS가 있으면
75
+ * project-level repo를 금지할 수 있으므로 build.gradle 수정은 생략한다.
68
76
  */
69
- function generateReposBlock(indent = ' ') {
77
+ function hasFailOnProjectRepos(contents) {
78
+ return /repositoriesMode\.set\s*\(\s*RepositoriesMode\.FAIL_ON_PROJECT_REPOS\s*\)/.test(
79
+ contents
80
+ );
81
+ }
82
+
83
+ /**
84
+ * Maven repo 블록 생성 (마커 포함)
85
+ */
86
+ function generateReposBlock(indent = ' ') {
70
87
  const body = MAVEN_REPOSITORIES.map(
71
- (repo) =>
72
- `${indent}// ${repo.comment}\n` +
73
- `${indent}maven { url "${repo.url}" }`
88
+ (repo) => `${indent}// ${repo.comment}\n${indent}maven { url "${repo.url}" }`
74
89
  ).join('\n\n');
75
90
 
76
91
  return `${indent}${MARKER_BEGIN}\n\n${body}\n\n${indent}${MARKER_END}`;
77
92
  }
78
93
 
79
94
  /**
80
- * repositories { } 안에
81
- * google(), mavenCentral() 없으면 보장해준다
95
+ * repositories { ... } 내부에 google(), mavenCentral()이 없으면 추가해준다.
96
+ * (resolve 기본 repo 안전장치)
82
97
  */
83
- function ensureGoogleAndMavenCentral(reposBlock, indent = ' ') {
98
+ function ensureGoogleAndMavenCentral(reposBlock, indent = ' ') {
84
99
  if (!/google\(\)/.test(reposBlock)) {
85
- reposBlock = reposBlock.replace(
86
- /repositories\s*\{\s*/m,
87
- (m) => `${m}\n${indent}google()`
88
- );
100
+ reposBlock = reposBlock.replace(/repositories\s*\{\s*/m, (m) => `${m}\n${indent}google()`);
89
101
  }
90
102
 
91
103
  if (!/mavenCentral\(\)/.test(reposBlock)) {
92
104
  if (/google\(\)/.test(reposBlock)) {
93
- reposBlock = reposBlock.replace(
94
- /(google\(\))/m,
95
- `$1\n${indent}mavenCentral()`
96
- );
105
+ reposBlock = reposBlock.replace(/(google\(\))/m, `$1\n${indent}mavenCentral()`);
97
106
  } else {
98
107
  reposBlock = reposBlock.replace(
99
108
  /repositories\s*\{\s*/m,
@@ -106,32 +115,45 @@ function ensureGoogleAndMavenCentral(reposBlock, indent = ' ') {
106
115
  }
107
116
 
108
117
  /**
109
- * settings.gradle 에서
110
- * repositoriesMode = FAIL_ON_PROJECT_REPOS 있는지 검사
111
- *
112
- * 이 설정이 있으면 project-level(build.gradle) repo 사용이 금지됨
118
+ * '{'의 매칭 '}' 범위를 찾아서 {start,end} 반환 (end inclusive)
119
+ * - 중첩 brace를 고려하여 깊이(depth)로 스캔
113
120
  */
114
- function hasFailOnProjectRepos(contents) {
115
- return /repositoriesMode\.set\s*\(\s*RepositoriesMode\.FAIL_ON_PROJECT_REPOS\s*\)/
116
- .test(contents);
121
+ function extractBraceRange(text, openBraceIndex) {
122
+ if (openBraceIndex < 0 || openBraceIndex >= text.length) return null;
123
+ if (text[openBraceIndex] !== '{') return null;
124
+
125
+ let depth = 1;
126
+ let i = openBraceIndex;
127
+
128
+ while (i + 1 < text.length && depth > 0) {
129
+ i++;
130
+ const ch = text[i];
131
+ if (ch === '{') depth++;
132
+ else if (ch === '}') depth--;
133
+ }
134
+
135
+ if (depth !== 0) return null;
136
+ return { start: openBraceIndex, end: i };
117
137
  }
118
138
 
119
139
  /**
120
140
  * android/settings.gradle 패치
121
141
  *
122
- * - dependencyResolutionManagement 가 없으면 새로 생성
123
- * - 있으면 내부 repositories { } repo 추가
124
- * - rootProject.name 바로 다음 줄에 삽입
142
+ * 목표:
143
+ * - rootProject.name 바로 다음 줄에 dependencyResolutionManagement 블록을 추가한다.
144
+ * - 이미 DRM이 존재하면:
145
+ * - repositories 블록이 없으면 추가
146
+ * - 있으면 missing repo만 추가
125
147
  */
126
148
  function patchSettingsGradle(contents) {
127
149
  const failOnProjectRepos = hasFailOnProjectRepos(contents);
128
150
 
129
- // 이미 우리가 삽입한 흔적이 있으면 그대로 유지
151
+ // 이미 적용되어 있으면 스킵
130
152
  if (contents.includes(MARKER_BEGIN)) {
131
- return { contents, failOnProjectRepos };
153
+ return { contents, failOnProjectRepos, changed: false };
132
154
  }
133
155
 
134
- // dependencyResolutionManagement 아예 없는 경우
156
+ // DRM 자체가 없으면 rootProject.name 다음에 삽입
135
157
  if (!/dependencyResolutionManagement\s*\{/.test(contents)) {
136
158
  const drmBlock =
137
159
  `\n\ndependencyResolutionManagement {\n` +
@@ -142,159 +164,206 @@ function patchSettingsGradle(contents) {
142
164
  ` }\n` +
143
165
  `}\n`;
144
166
 
145
- // rootProject.name 바로 뒤에 삽입
146
- const rootNameRegex =
147
- /(^\s*rootProject\.name\s*=\s*['"][^'"]+['"]\s*$)/m;
148
-
167
+ const rootNameRegex = /(^\s*rootProject\.name\s*=\s*['"][^'"]+['"]\s*$)/m;
149
168
  const match = contents.match(rootNameRegex);
169
+
150
170
  if (match) {
151
171
  const insertAt = match.index + match[0].length;
152
- contents =
153
- contents.slice(0, insertAt) +
154
- drmBlock +
155
- contents.slice(insertAt);
156
- } else {
157
- contents += drmBlock;
172
+ const newContents = contents.slice(0, insertAt) + drmBlock + contents.slice(insertAt);
173
+ return { contents: newContents, failOnProjectRepos, changed: true };
158
174
  }
159
175
 
160
- return { contents, failOnProjectRepos };
176
+ // rootProject.name이 없으면 파일 끝에 append
177
+ return { contents: contents + drmBlock, failOnProjectRepos, changed: true };
161
178
  }
162
179
 
163
- // dependencyResolutionManagement 있으나 repositories 없는 경우
164
- if (
165
- !/dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{/
166
- .test(contents)
167
- ) {
168
- contents = contents.replace(
169
- /(dependencyResolutionManagement\s*\{)/,
180
+ // DRM은 있으나 repositories 블록이 없으면 추가
181
+ const drmHasRepos = /dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{/.test(
182
+ contents
183
+ );
184
+ if (!drmHasRepos) {
185
+ const newContents = contents.replace(
186
+ /(dependencyResolutionManagement\s*\{)/m,
170
187
  `$1\n repositories {\n google()\n mavenCentral()\n\n${generateReposBlock(
171
188
  ' '
172
189
  )}\n }\n`
173
190
  );
174
-
175
- return { contents, failOnProjectRepos };
191
+ return { contents: newContents, failOnProjectRepos, changed: true };
176
192
  }
177
193
 
178
- // repositories 블록 내부에 repo 추가
179
- const startMatch =
180
- contents.match(/dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{/);
181
-
182
- if (!startMatch) {
183
- return { contents, failOnProjectRepos };
184
- }
185
-
186
- const startIdx = contents.indexOf(startMatch[0]) + startMatch[0].length;
187
-
188
- // brace 스캔으로 repositories { } 범위 찾기
189
- let i = startIdx - 1;
190
- let brace = 1;
191
- while (i < contents.length && brace > 0) {
192
- i++;
193
- if (contents[i] === '{') brace++;
194
- else if (contents[i] === '}') brace--;
194
+ // DRM.repositories 존재: missing repo 추가
195
+ const missing = MAVEN_REPOSITORIES.filter((r) => !contents.includes(r.url));
196
+ if (missing.length === 0) {
197
+ return { contents, failOnProjectRepos, changed: false };
195
198
  }
196
199
 
197
- const reposBlockStart = contents.lastIndexOf('repositories', startIdx);
198
- let reposBlock = contents.slice(reposBlockStart, i + 1);
199
-
200
- reposBlock = ensureGoogleAndMavenCentral(reposBlock, ' ');
200
+ const indent = ' ';
201
+ const body = missing
202
+ .map((r) => `${indent}// ${r.comment}\n${indent}maven { url "${r.url}" }`)
203
+ .join('\n\n');
204
+ const block = `${indent}${MARKER_BEGIN}\n\n${body}\n\n${indent}${MARKER_END}`;
201
205
 
202
- const missing = MAVEN_REPOSITORIES.filter(
203
- (r) => !reposBlock.includes(r.url)
204
- );
206
+ let newContents = contents;
205
207
 
206
- if (missing.length === 0) {
207
- return { contents, failOnProjectRepos };
208
+ // 가장 무난한 위치: repositories 내 mavenCentral() 다음
209
+ if (/mavenCentral\(\)/.test(newContents)) {
210
+ newContents = newContents.replace(/(mavenCentral\(\)\s*)/m, `$1\n\n${block}\n`);
211
+ } else {
212
+ // mavenCentral이 아예 없으면 repositories { 다음에 삽입
213
+ newContents = newContents.replace(/(repositories\s*\{\s*)/m, `$1\n${block}\n`);
208
214
  }
209
215
 
210
- const block =
211
- ` ${MARKER_BEGIN}\n\n` +
212
- missing
213
- .map(
214
- (r) =>
215
- ` // ${r.comment}\n maven { url "${r.url}" }`
216
- )
217
- .join('\n\n') +
218
- `\n\n ${MARKER_END}`;
219
-
220
- reposBlock = reposBlock.replace(
221
- /(mavenCentral\(\)\s*)/,
222
- `$1\n\n${block}\n`
223
- );
224
-
225
- contents =
226
- contents.slice(0, reposBlockStart) +
227
- reposBlock +
228
- contents.slice(i + 1);
229
-
230
- return { contents, failOnProjectRepos };
216
+ return { contents: newContents, failOnProjectRepos, changed: true };
231
217
  }
232
218
 
233
219
  /**
234
- * android/build.gradle (project-level) 패치
220
+ * android/build.gradle(project-level) 패치
235
221
  *
236
- * - allprojects { repositories { ... } } 에 repo 추가
237
- * - settings.gradle FAIL_ON_PROJECT_REPOS 가 있으면 호출되지 않음
222
+ * 목표:
223
+ * - buildscript.repositories는 절대 건드리지 않는다.
224
+ * - allprojects { repositories { ... } } 블록 "안"에만 repo를 삽입한다.
225
+ *
226
+ * 삽입 위치 정책:
227
+ * - jitpack이 있으면: jitpack 다음 줄에 삽입 (원하는 형태)
228
+ * - jitpack이 없으면: repositories 블록 닫기 직전에 삽입
238
229
  */
239
230
  function patchProjectBuildGradle(contents) {
240
- if (contents.includes(MARKER_BEGIN)) return contents;
231
+ // 이미 적용되어 있으면 스킵
232
+ if (contents.includes(MARKER_BEGIN)) return { contents, changed: false };
241
233
 
234
+ // allprojects 없으면 append
242
235
  if (!/allprojects\s*\{/.test(contents)) {
243
- return (
244
- contents +
236
+ const block =
245
237
  `\n\nallprojects {\n` +
246
- ` repositories {\n` +
247
- ` google()\n` +
248
- ` mavenCentral()\n\n` +
249
- `${generateReposBlock(' ')}\n` +
250
- ` }\n` +
251
- `}\n`
252
- );
238
+ ` repositories {\n` +
239
+ ` google()\n` +
240
+ ` mavenCentral()\n` +
241
+ ` maven { url 'https://www.jitpack.io' }\n\n` +
242
+ `${generateReposBlock(' ')}\n` +
243
+ ` }\n` +
244
+ `}\n`;
245
+ return { contents: contents + block, changed: true };
253
246
  }
254
247
 
255
- if (
256
- !/allprojects\s*\{[\s\S]*?repositories\s*\{/.test(contents)
257
- ) {
258
- return contents.replace(
259
- /(allprojects\s*\{)/,
260
- `$1\n repositories {\n google()\n mavenCentral()\n\n${generateReposBlock(
261
- ' '
262
- )}\n }\n`
263
- );
248
+ // allprojects 헤더와 '{' 찾기
249
+ const apHeader = contents.match(/allprojects\s*\{/m);
250
+ if (!apHeader) return { contents, changed: false };
251
+
252
+ const apOpen = apHeader.index + apHeader[0].length - 1; // '{'
253
+ const apRange = extractBraceRange(contents, apOpen);
254
+ if (!apRange) return { contents, changed: false };
255
+
256
+ const apText = contents.slice(apHeader.index, apRange.end + 1); // "allprojects { ... }"
257
+
258
+ // allprojects 안에 repositories가 없으면 삽입
259
+ if (!/repositories\s*\{/.test(apText)) {
260
+ const insertion =
261
+ `\n repositories {\n` +
262
+ ` google()\n` +
263
+ ` mavenCentral()\n` +
264
+ ` maven { url 'https://www.jitpack.io' }\n\n` +
265
+ `${generateReposBlock(' ')}\n` +
266
+ ` }\n`;
267
+
268
+ const newApText = apText.replace(/allprojects\s*\{\s*/m, (m) => m + insertion);
269
+
270
+ const newContents =
271
+ contents.slice(0, apHeader.index) + newApText + contents.slice(apRange.end + 1);
272
+
273
+ return { contents: newContents, changed: true };
274
+ }
275
+
276
+ // allprojects.repositories 헤더와 '{' 찾기 (apText 내부 인덱스)
277
+ const repoHeader = apText.match(/repositories\s*\{/m);
278
+ if (!repoHeader) return { contents, changed: false };
279
+
280
+ const repoOpenInAp = repoHeader.index + repoHeader[0].length - 1;
281
+ const repoRangeInAp = extractBraceRange(apText, repoOpenInAp);
282
+ if (!repoRangeInAp) return { contents, changed: false };
283
+
284
+ let repoBlockText = apText.slice(repoHeader.index, repoRangeInAp.end + 1); // "repositories { ... }"
285
+
286
+ // 안전장치: 기본 repo 보장
287
+ repoBlockText = ensureGoogleAndMavenCentral(repoBlockText, ' ');
288
+
289
+ // missing만 추가
290
+ const missing = MAVEN_REPOSITORIES.filter((r) => !repoBlockText.includes(r.url));
291
+ if (missing.length === 0) return { contents, changed: false };
292
+
293
+ const indent = ' ';
294
+ const body = missing
295
+ .map((r) => `${indent}// ${r.comment}\n${indent}maven { url "${r.url}" }`)
296
+ .join('\n\n');
297
+ const markerBlock = `${indent}${MARKER_BEGIN}\n\n${body}\n\n${indent}${MARKER_END}`;
298
+
299
+ const jitpackRegex = /maven\s*\{\s*url\s*['"]https:\/\/www\.jitpack\.io['"]\s*\}/m;
300
+
301
+ if (jitpackRegex.test(repoBlockText)) {
302
+ // jitpack 다음에 삽입
303
+ repoBlockText = repoBlockText.replace(jitpackRegex, (m) => `${m}\n\n${markerBlock}`);
304
+ } else {
305
+ // repositories 닫기 직전에 삽입
306
+ repoBlockText = repoBlockText.replace(/\}\s*$/m, `\n\n${markerBlock}\n}`);
264
307
  }
265
308
 
266
- return contents;
309
+ // apText 안에서 repositories 블록 교체
310
+ const newApText =
311
+ apText.slice(0, repoHeader.index) + repoBlockText + apText.slice(repoRangeInAp.end + 1);
312
+
313
+ // 전체 파일에서 allprojects 블록 교체
314
+ const newContents =
315
+ contents.slice(0, apHeader.index) + newApText + contents.slice(apRange.end + 1);
316
+
317
+ return { contents: newContents, changed: true };
267
318
  }
268
319
 
269
320
  /**
270
321
  * 실제 적용 플러그인
322
+ *
323
+ * - android 폴더가 생성된 이후(prebuild) 시점에 실행되며,
324
+ * - settings.gradle 먼저 수정하여 정책을 판단하고,
325
+ * - FAIL_ON_PROJECT_REPOS가 아니면 build.gradle도 수정한다.
271
326
  */
272
- const withAdwhaleMavenRepositories = (config) => {
273
- let failOnProjectRepos = false;
274
-
275
- // 1) settings.gradle 먼저 처리
276
- config = withSettingsGradle(config, (c) => {
277
- const result = patchSettingsGradle(c.modResults.contents);
278
- c.modResults.contents = result.contents;
279
- failOnProjectRepos = result.failOnProjectRepos;
280
- return c;
281
- });
282
-
283
- // 2) FAIL_ON_PROJECT_REPOS 가 없을 때만 build.gradle 처리
284
- if (!failOnProjectRepos) {
285
- config = withAndroidProjectBuildGradle(config, (c) => {
286
- c.modResults.contents = patchProjectBuildGradle(
287
- c.modResults.contents
288
- );
289
- return c;
290
- });
291
- }
292
-
293
- return config;
294
- };
327
+ function withAdwhaleMavenRepositories(config) {
328
+ return withDangerousMod(config, [
329
+ 'android',
330
+ async (config) => {
331
+ const projectRoot = config.modRequest.projectRoot;
332
+ const androidDir = path.join(projectRoot, 'android');
333
+
334
+ const settingsGradlePath = path.join(androidDir, 'settings.gradle');
335
+ const buildGradlePath = path.join(androidDir, 'build.gradle');
336
+
337
+ // 1) settings.gradle 패치
338
+ const settingsText = readTextIfExists(settingsGradlePath);
339
+ let failOnProjectRepos = false;
340
+
341
+ if (settingsText) {
342
+ const res = patchSettingsGradle(settingsText);
343
+ failOnProjectRepos = res.failOnProjectRepos;
344
+
345
+ if (res.changed) {
346
+ writeText(settingsGradlePath, res.contents);
347
+ }
348
+ }
349
+
350
+ // 2) FAIL_ON_PROJECT_REPOS이면 build.gradle 수정 생략
351
+ if (failOnProjectRepos) {
352
+ return config;
353
+ }
354
+
355
+ // 3) build.gradle(project) 패치
356
+ const buildText = readTextIfExists(buildGradlePath);
357
+ if (buildText) {
358
+ const res2 = patchProjectBuildGradle(buildText);
359
+ if (res2.changed) {
360
+ writeText(buildGradlePath, res2.contents);
361
+ }
362
+ }
363
+
364
+ return config;
365
+ },
366
+ ]);
367
+ }
295
368
 
296
- module.exports = createRunOncePlugin(
297
- withAdwhaleMavenRepositories,
298
- pkg.name,
299
- pkg.version
300
- );
369
+ module.exports = createRunOncePlugin(withAdwhaleMavenRepositories, pkg.name, pkg.version);