@pi-r/android 0.12.4 → 0.13.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.
@@ -2,9 +2,10 @@
2
2
 
3
3
  const path = require('node:path');
4
4
  const fs = require('node:fs');
5
+ const toml = require('toml');
5
6
  const Document = require('@e-mc/document');
6
7
  const { escapePattern, isArray, isString } = require('@e-mc/types');
7
- const { getIndent, getNewline, spliceString, trimQuote, wrapQuote } = require('@e-mc/document/util');
8
+ const { getIndent, getNewline, spliceMatch, spliceString, splitEnclosing, trimQuote, wrapQuote } = require('@e-mc/document/util');
8
9
  const REGEXP_SEMVER = /^(?:(?:\s*,\s*)?(?:[([\]](?:(?:\s*,\s*)?\d+\.\d+(?:\s*,\s*)?)+[)[\]]|!!\d+\.\d+\.\d+|\d+\.\d+\.\d+!!))+$/;
9
10
  const REGEXP_DEPENDENCIES = /dependencies\s+\{((?:{[^{]*{|{[^}]*}|}[^}]*}|\\}*?|[^{}+])+)\}/;
10
11
  const REGEXP_DEPENDENCY = /([ \t]*)([A-Za-z_$][\w$]*)(?:\s*\(?\s*(?:module\(?\s*)?["']([^"']+)["']\s*\)?|\s+((?:\s*(?:group|name|version)\s*:\s*(?:"(?:[^"]|(?<=\\)")+"|'(?:[^']|(?<=\\)')+')\s*,?){3}))?/g;
@@ -13,8 +14,7 @@ const REGEXP_LIBRARY = /([ \t]*)implementation\s+(?:(project|files)\(\s*("(?:[^"
13
14
  const REGEXP_LIBRARY_KOTLIN = /([ \t]*)implementation\(\s*(?:(project|files)\(\s*"(.+)"\s*\)|(fileTree)\(\s*mapOf\(((?:\s*"(?:dir|include)"\s+to\s+(?:"(?:[^"]|(?<=\\)")+"|listOf\([^)]+\))\s*,?){2})\)\s*\))\s*\)[ \t\r]*\n/g;
14
15
  const REGEXP_PLATFORM = /([ \t]*)implementation\s+platform\(\s*("(?:[^"]|(?<=\\)")+"|'(?:[^']|(?<=\\)')+')\s*\)[ \t\r]*\n/g;
15
16
  const REGEXP_PLATFORM_KOTLIN = /([ \t]*)implementation\(\s*platform\(\s*"(.+)"\s*\)\s*\)[ \t\r]*\n/g;
16
- const REGEXP_VERSIONS = /(\S+)\s*=\s*(?:"([^"]+)"|'([^']+)')/g;
17
- const REGEXP_VERSIONS_LIB = /(\S+)\s*=\s*(?:\{\s*(group|name|module|version(?:\.ref)?)\s*=\s*(?:"([^"]+)"|'([^']+)'|(\{[^}]+\}))\s*,\s*(group|name|module|version(?:\.ref)?)\s*=\s*(?:"([^"]+)"|'([^']+)'|(\{[^}]+\}))(?:\s*,\s*(group|name|module|version(?:\.ref)?)\s*=\s*(?:"([^"]+)"|'([^']+)')|(\{[^}]+\}))?\s*\}|(?:"([^:]+:[^"]+)"|'([^:]+:[^']+)'))/gd;
17
+ const REGEXP_VERSIONS_ATTR = /\b(group|name|module|version(?:\.ref)?)\s*=\s*(?:"([^"]+)"|'([^']+)'|(\{[^}]+\}))/gd;
18
18
  function isUpgrade(version, pending) {
19
19
  if (isSemVer(pending)) {
20
20
  return true;
@@ -99,6 +99,21 @@ function writeMethod(kotlin, named, item, args = '', prefix = 'implementation')
99
99
  }
100
100
  return prefix + dependency;
101
101
  }
102
+ function compareFileTree(from, orig) {
103
+ try {
104
+ return isArray(from) && from.sort().toString() === JSON.parse(`[${orig}]`).sort().toString();
105
+ }
106
+ catch {
107
+ return false;
108
+ }
109
+ }
110
+ function removeGroup(items, id) {
111
+ for (let i = 0; i < items.length; ++i) {
112
+ if (items[i][0] === id) {
113
+ items.splice(i--, 1);
114
+ }
115
+ }
116
+ }
102
117
  function valueFor(name, value, kotlin, quote = false) {
103
118
  value = value.toString();
104
119
  const q = quote ? kotlin && !value.includes('"') || value.includes("'") ? '"' : "'" : '';
@@ -107,8 +122,8 @@ function valueFor(name, value, kotlin, quote = false) {
107
122
  const isLatest = (value) => value === '' || value.startsWith('latest.');
108
123
  const isSemVer = (value) => isLatest(value) || REGEXP_SEMVER.test(value);
109
124
  const updateOpts = (updateOnly = false) => ({ upgrade: true, updateOnly });
110
- const writeOutput = (match, source, value) => spliceString(source, match.index, match.index + match[0].length, value);
111
125
  const toCamelCase = (value) => value.replace(/-([a-z])/gi, (...capture) => capture[1].toUpperCase());
126
+ const createBoundary = (name) => new RegExp(`(?<=^|\\n[ \\t]*)\\[${name}\\]\\s*`);
112
127
  function finalize(instance) {
113
128
  const { profileable, dependencies, libraries, dataBinding, jvmToolchain, versionName, versionCode, namespace, applicationId, mainParentDir } = instance.config;
114
129
  if (!profileable && !dependencies && !libraries && !dataBinding && !jvmToolchain && !versionName && !versionCode && !namespace && !applicationId) {
@@ -128,7 +143,7 @@ function finalize(instance) {
128
143
  };
129
144
  const setDependencies = (match, output) => {
130
145
  if (modified || !existing) {
131
- setModified(spliceString(source, match.index, match.index + match[0].length, `dependencies {${output}}`));
146
+ setModified(spliceMatch(source, match, `dependencies {${output}}`));
132
147
  }
133
148
  };
134
149
  if (dependencies) {
@@ -243,80 +258,101 @@ function finalize(instance) {
243
258
  const newLine = getNewline(source);
244
259
  const gradleDir = path.join(this.baseDirectory, 'gradle');
245
260
  const template = instance.findTemplate(gradleDir, "libs.versions.toml", { detect: true, subDir: language, throwsDoesNotExist: false });
246
- const removeGroup = (id) => {
247
- for (let i = 0; i < items.length; ++i) {
248
- if (items[i][0] === id) {
249
- items.splice(i--, 1);
250
- }
251
- }
252
- };
253
261
  let content = match[1], named = false, versioning = false, indent;
254
262
  for (const platform of content.matchAll(kotlin ? REGEXP_PLATFORM_KOTLIN : REGEXP_PLATFORM)) {
255
263
  const [group, name] = trimQuote(platform[1]).trim().split(':');
256
264
  if (name.endsWith('-bom')) {
257
- removeGroup(group);
265
+ removeGroup(items, group);
258
266
  }
259
267
  }
260
- if (template.source && template.localUri && instance.canWrite(template.localUri, { ownPermissionOnly: true })) {
261
- versioning = kotlin && versionCatalog !== false || !!versionCatalog;
262
- if (versioning || template.existing) {
263
- const libs = {};
264
- const versions = {};
265
- let config = template.source, catalog;
266
- for (const ref of config.matchAll(REGEXP_VERSIONS)) {
267
- versions[ref[1]] = (ref[2] || ref[3]).trim();
268
- }
269
- while (catalog = REGEXP_VERSIONS_LIB.exec(config)) {
270
- const lib = {};
271
- const inline = catalog[14] || catalog[15];
272
- if (inline) {
273
- [lib.group, lib.name] = inline.trim().split(':');
268
+ if (template.source && template.localUri && instance.canWrite(template.localUri, { ownPermissionOnly: true }) && (versioning = kotlin && versionCatalog !== false || !!versionCatalog)) {
269
+ const libs = {};
270
+ try {
271
+ let config = template.source;
272
+ const { versions = {}, libraries: packages = {} } = toml.parse(config);
273
+ if (Object.keys(packages).length > 0) {
274
+ const locations = ['versions', 'plugins', 'libraries'].map(name => createBoundary(name).exec(config)?.index ?? NaN).filter(value => !isNaN(value));
275
+ const lowerBound = locations.pop();
276
+ let upperBound = Infinity;
277
+ if (Math.min(lowerBound, ...locations) === lowerBound) {
278
+ upperBound = Math.min(...locations);
274
279
  }
275
- else {
276
- let i = 0;
277
- for (const attr of ['group', 'name', 'module', 'version.ref', 'version']) {
278
- const index = catalog.findIndex(seg => seg === attr);
279
- if (index !== -1) {
280
- lib[attr] = (catalog[i = index + 1] || catalog[i = index + 2] || catalog[i = index + 3]).trim();
281
- if (attr === 'version') {
282
- lib.version_indices = catalog.indices[i];
280
+ else if (Math.max(lowerBound, ...locations) !== lowerBound) {
281
+ upperBound = Math.max(...locations);
282
+ }
283
+ for (const key in packages) {
284
+ const pattern = new RegExp(`(\\n[ \\t]*${escapePattern(key)})\\s*=\\s*(?:(["'])(.+)\\2|\\{)`, 'gd');
285
+ pattern.lastIndex = lowerBound;
286
+ const catalog = pattern.exec(config);
287
+ const indices = catalog?.indices;
288
+ if (indices && catalog.index < upperBound) {
289
+ const lib = {};
290
+ let position;
291
+ if (catalog[3]) {
292
+ let version;
293
+ [lib.group, lib.name, version] = catalog[3].trim().split(':');
294
+ if (version && /^\d+\.\d+\.\d+$/.test(version) && (position = indices[3])) {
295
+ const start = position[0] + catalog[3].indexOf(version);
296
+ lib.version = version;
297
+ lib.version_indices = [start, start + version.length];
283
298
  }
284
299
  }
285
- }
286
- }
287
- let { group, name } = lib;
288
- if (lib.module) {
289
- [group, name] = lib.module.split(':');
290
- }
291
- if (group && name) {
292
- if (name.endsWith('-bom')) {
293
- removeGroup(group);
294
- continue;
295
- }
296
- const index = items.findIndex(seg => seg[0] === group && seg[1] === name);
297
- if (index !== -1) {
298
- const item = items[index];
299
- items.splice(index, 1);
300
- const ref = lib['version.ref'];
301
- const version = ref ? versions[ref] : lib.version;
302
- if (version && checkVersion(item, version)) {
303
- if (ref) {
304
- if (versions[ref] !== item[2]) {
305
- const target = new RegExp(`\\s${escapePattern(ref)}\\s*=\\s*(?:"([^"]+)"|'([^']+)')`, 'd').exec(config);
306
- const indices = target?.indices;
307
- if (indices) {
308
- const [start, end] = indices[1] || indices[2];
309
- config = spliceString(config, start, end, versions[ref] = item[2], REGEXP_VERSIONS_LIB);
300
+ else if (position = indices[1]) {
301
+ let start = position[1];
302
+ const [first, second] = splitEnclosing(config.slice(start), ['{', '}'], 1);
303
+ if (second.at(-1) === '}') {
304
+ let attr, i = 0;
305
+ while (attr = REGEXP_VERSIONS_ATTR.exec(second)) {
306
+ lib[attr[1]] = (attr[i = 2] || attr[i = 3] || attr[i = -1]).trim();
307
+ if (attr[1] === 'version' && (position = attr.indices?.[i])) {
308
+ start += first.length;
309
+ lib.version_indices = position.map(pos => start + pos);
310
310
  }
311
311
  }
312
+ REGEXP_VERSIONS_ATTR.lastIndex = 0;
313
+ }
314
+ else {
315
+ continue;
312
316
  }
313
- else if (lib.version_indices) {
314
- const [start, end] = lib.version_indices;
315
- config = spliceString(config, start, end, lib.version = item[2], REGEXP_VERSIONS_LIB);
317
+ }
318
+ let { group, name } = lib;
319
+ if (lib.module) {
320
+ [group, name] = lib.module.split(':');
321
+ }
322
+ if (group && name) {
323
+ if (name.endsWith('-bom')) {
324
+ removeGroup(items, group);
325
+ }
326
+ else {
327
+ const index = items.findIndex(seg => seg[0] === group && seg[1] === name);
328
+ if (index !== -1) {
329
+ const item = items[index];
330
+ items.splice(index, 1);
331
+ const ref = lib['version.ref'];
332
+ const version = ref ? versions[ref] : lib.version;
333
+ if (isString(version) && checkVersion(item, version)) {
334
+ if (ref) {
335
+ if (versions[ref] !== item[2]) {
336
+ const target = new RegExp(`\\n[ \\t]*${escapePattern(ref)}\\s*=\\s*(?:"([^"]+)"|'([^']+)')`, 'd').exec(config);
337
+ const found = target?.indices;
338
+ if (found) {
339
+ const [start, end] = found[1] || found[2];
340
+ config = spliceString(config, start, end, item[2]);
341
+ versions[ref] = item[2];
342
+ }
343
+ }
344
+ }
345
+ else if (lib.version_indices) {
346
+ const [start, end] = lib.version_indices;
347
+ config = spliceString(config, start, end, item[2]);
348
+ lib.version = item[2];
349
+ }
350
+ }
351
+ }
352
+ libs[key] = lib;
316
353
  }
317
354
  }
318
355
  }
319
- libs[catalog[1]] = lib;
320
356
  }
321
357
  }
322
358
  if (versioning && items.length > 0) {
@@ -361,30 +397,26 @@ function finalize(instance) {
361
397
  item[2] = '';
362
398
  libs[key] = { group, name, "version.ref": versionRef };
363
399
  }
364
- const lib = /\[libraries\]\s*/.exec(config);
400
+ const lib = createBoundary('libraries').exec(config);
365
401
  if (lib) {
366
- config = writeOutput(lib, config, output);
402
+ config = spliceMatch(config, lib, output);
367
403
  }
368
404
  else {
369
405
  config += (config.endsWith(newLine) ? '' : newLine) + output;
370
406
  }
371
407
  if (outputVer) {
372
408
  outputVer = '[versions]' + newLine + outputVer;
373
- const ver = /\[versions\]\s*/.exec(config);
374
- config = ver ? writeOutput(ver, config, outputVer) : outputVer + newLine + config;
409
+ const ver = createBoundary('versions').exec(config);
410
+ config = ver ? spliceMatch(config, ver, outputVer) : outputVer + newLine + config;
375
411
  }
376
- REGEXP_VERSIONS.lastIndex = 0;
377
412
  }
378
413
  if (Document.createDir(gradleDir)) {
379
- try {
380
- fs.writeFileSync(template.localUri, config, 'utf8');
381
- this.add(template.localUri);
382
- }
383
- catch (err) {
384
- this.writeFail(["Unable to write file", path.basename(template.localUri)], err, 32);
385
- }
414
+ fs.writeFileSync(template.localUri, config, 'utf8');
415
+ this.add(template.localUri);
386
416
  }
387
- REGEXP_VERSIONS_LIB.lastIndex = 0;
417
+ }
418
+ catch (err) {
419
+ this.writeFail([Object.keys(libs).length > 0 ? "Unable to write file" : "Unable to load configuration", path.basename(template.localUri)], err, 32);
388
420
  }
389
421
  }
390
422
  if (items.length > 0) {
@@ -429,7 +461,7 @@ function finalize(instance) {
429
461
  version = '';
430
462
  }
431
463
  if (checkVersion(item, version, method[0])) {
432
- content = spliceString(content, method.index, method.index + method[0].length, method[1] + writeMethod(kotlin, named, item, method[4], method[2]) + newLine, pattern);
464
+ content = spliceMatch(content, method, method[1] + writeMethod(kotlin, named, item, method[4], method[2]) + newLine, pattern);
433
465
  modified = true;
434
466
  }
435
467
  }
@@ -469,19 +501,11 @@ function finalize(instance) {
469
501
  }
470
502
  case 'fileTree': {
471
503
  const dir = (kotlin ? /"dir"\s+to\s+"(.+)"(?:\s*,|$)/ : /\bdir\s*:\s*["'](.+)["'](?:\s*,|$)/).exec(match[5])?.[1];
472
- const include = (kotlin ? /"include"\s+to\s+listOf\(([^)]+)\)(?:\s*,|$)/ : /\binclude\s*:\s*\[(.+)\](?:\s*,|$)/).exec(match[5])?.[1];
504
+ const include = (kotlin ? /"include"\s+to\s+listOf\(([^)]+)\)(?:\s*,|$)/ : /\binclude\s*:\s*\[([^\]]+)\](?:\s*,|$)/).exec(match[5])?.[1];
473
505
  if (dir && include) {
474
- const compare = (from, orig) => {
475
- try {
476
- return isArray(from) && from.sort().toString() === JSON.parse(`[${orig}]`).sort().toString();
477
- }
478
- catch {
479
- return false;
480
- }
481
- };
482
506
  const target = libraries[attr];
483
507
  for (let i = 0; i < target.length; ++i) {
484
- if (target[i].dir === dir && compare(target[i].include, include)) {
508
+ if (target[i].dir === dir && compareFileTree(target[i].include, include)) {
485
509
  index = i;
486
510
  break;
487
511
  }
@@ -645,7 +669,7 @@ function finalize(instance) {
645
669
  fs.readdirSync(subDir, { withFileTypes: true }).forEach(item => {
646
670
  const srcName = path.join(subDir, item.name);
647
671
  if (item.isDirectory()) {
648
- switch (/^\$\{([^}]+)\}$/.exec(item.name)?.[1]) {
672
+ switch (/^\$\{(.+)\}$/.exec(item.name)?.[1]) {
649
673
  case 'app':
650
674
  if (instance.config.mainParentDir) {
651
675
  recurse.call(this, srcName, path.join(targetDir, instance.config.mainParentDir));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-r/android",
3
- "version": "0.12.4",
3
+ "version": "0.13.0",
4
4
  "description": "Android document constructor for E-mc.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -17,12 +17,13 @@
17
17
  "license": "MIT",
18
18
  "homepage": "https://github.com/anpham6/pi-r#readme",
19
19
  "dependencies": {
20
- "@e-mc/document": "^0.14.5",
21
- "@e-mc/types": "^0.14.5",
20
+ "@e-mc/document": "^0.14.6",
21
+ "@e-mc/types": "^0.14.6",
22
22
  "dom-serializer": "^2.0.0",
23
23
  "domhandler": "^5.0.3",
24
24
  "domutils": "^3.2.2",
25
25
  "htmlparser2": "^10.1.0",
26
+ "toml": "^4.3.0",
26
27
  "which": "^4.0.0"
27
28
  }
28
29
  }
@@ -1,5 +1,5 @@
1
1
  distributionBase=GRADLE_USER_HOME
2
2
  distributionPath=wrapper/dists
3
- distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip
3
+ distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
4
4
  zipStoreBase=GRADLE_USER_HOME
5
5
  zipStorePath=wrapper/dists
@@ -1,4 +1,4 @@
1
1
  plugins {
2
- id 'com.android.application' version '9.2.1' apply false
3
- id 'com.android.library' version '9.2.1' apply false
2
+ id 'com.android.application' version '9.3.2' apply false
3
+ id 'com.android.library' version '9.3.2' apply false
4
4
  }
@@ -7,7 +7,7 @@ android {
7
7
  namespace = ''
8
8
  compileSdk {
9
9
  version = release(37) {
10
- minorApiLevel = 0
10
+ minorApiLevel = 1
11
11
  }
12
12
  }
13
13
 
@@ -1,7 +1,7 @@
1
1
  [versions]
2
- agp = "9.2.1"
2
+ agp = "9.3.2"
3
3
  coreKtx = "1.19.0"
4
- kotlin = "2.4.0"
4
+ kotlin = "2.4.10"
5
5
  junit = "4.13.2"
6
6
  junitVersion = "1.3.0"
7
7
  espressoCore = "3.7.0"
@@ -1,13 +1,14 @@
1
1
  plugins {
2
2
  alias(libs.plugins.android.application)
3
3
  alias(libs.plugins.kotlin.compose)
4
+ alias(libs.plugins.google.devtools.ksp)
4
5
  }
5
6
 
6
7
  android {
7
8
  namespace = ""
8
9
  compileSdk {
9
10
  version = release(37) {
10
- minorApiLevel = 0
11
+ minorApiLevel = 1
11
12
  }
12
13
  }
13
14
 
@@ -43,6 +44,7 @@ android {
43
44
  dependencies {
44
45
  implementation(libs.jetbrains.kotlin)
45
46
  implementation(libs.androidx.core.ktx)
47
+ implementation(libs.androidx.navigation.fragment.ktx)
46
48
  implementation(libs.androidx.appcompat)
47
49
  implementation(libs.androidx.constraintlayout)
48
50
  implementation(libs.android.material)
@@ -1,17 +1,21 @@
1
1
  [versions]
2
- agp = "9.2.1"
2
+ agp = "9.3.2"
3
3
  coreKtx = "1.19.0"
4
- kotlin = "2.4.0"
4
+ navigationFragmentKtx = "2.10.0"
5
+ kotlin = "2.4.10"
6
+ ksp = "2.3.11"
5
7
  junit = "4.13.2"
6
8
  junitVersion = "1.3.0"
7
9
  espressoCore = "3.7.0"
8
- appcompat = "1.7.1"
10
+ appcompat = "1.8.0"
9
11
  material = "1.14.0"
10
- constraintlayout = "2.2.1"
12
+ constraintlayout = "2.2.2"
11
13
 
12
14
  [libraries]
13
15
  androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
16
+ androidx-navigation-fragment-ktx = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigationFragmentKtx" }
14
17
  jetbrains-kotlin = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin" }
18
+ google-devtools-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
15
19
  junit = { group = "junit", name = "junit", version.ref = "junit" }
16
20
  androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
17
21
  androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
@@ -2,4 +2,5 @@ plugins {
2
2
  alias(libs.plugins.android.application) apply false
3
3
  alias(libs.plugins.kotlin.android) apply false
4
4
  alias(libs.plugins.kotlin.compose) apply false
5
+ alias(libs.plugins.google.devtools.ksp) apply false
5
6
  }