expo-tiddlywiki-filesystem-android-external-storage 2.12.0 → 2.12.2

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 CHANGED
@@ -122,7 +122,9 @@ Checks if the `MANAGE_EXTERNAL_STORAGE` permission is granted (Android 11+).
122
122
 
123
123
  This module bundles **JGit 6.2.x** (not the latest 6.10.x) for Android compatibility.
124
124
 
125
- Newer JGit versions (6.3+) call Java 9+ APIs (`InputStream.readNBytes`, `InputStream.transferTo`) that are **not available on Android at any API level** — Android's `desugar_jdk_libs` only backfills Java 8 language APIs, not these Java 9 I/O methods. This has been confirmed on Android 12 (API 31) and is expected to affect all current Android versions.
125
+ Newer JGit versions (6.3+) call Java 9+ APIs (`InputStream.readNBytes`, `InputStream.transferTo`) that require `coreLibraryDesugaringEnabled` — Android's `desugar_jdk_libs` provides these at build time. The config plugin included in this package automatically enables desugaring in the generated `android/app/build.gradle`.
126
+
127
+ If you encounter `NoSuchMethodError` for these methods at runtime, verify that the config plugin ran during `expo prebuild` (check that `android/app/build.gradle` contains `coreLibraryDesugaringEnabled true` and `desugar_jdk_libs`).
126
128
 
127
129
  JGit 6.2.x is the latest release that avoids these Java 9+ API calls while still providing all the Git primitives needed by this module (clone, fetch, push, status, diff, log, etc.). One API trade-off: `CloneCommand.setDepth()` (shallow clone) was introduced in JGit 6.3, so this module does not support `--depth` on clone.
128
130
 
@@ -15,9 +15,17 @@ internal object TiddlyWikiParser {
15
15
  * Returns a JSON array string ready for injection into TiddlyWiki boot store.
16
16
  */
17
17
  fun batchParseTidFiles(filePaths: List<String>, quickLoadMode: Boolean): String {
18
- val results = filePaths.parallelStream().map { path ->
18
+ // Use sequential stream (not parallelStream) to preserve input order.
19
+ // The JS side relies on result ordering to map titles back to file paths
20
+ // when building the tiddler index.
21
+ val results = filePaths.stream().map { path ->
19
22
  try {
20
- parseTiddlerFile(path, quickLoadMode)
23
+ val parsed = parseTiddlerFile(path, quickLoadMode)
24
+ // Tag result with filepath so JS side can map correctly regardless of order
25
+ when (parsed) {
26
+ is JSONObject -> parsed.put("_filepath", path)
27
+ }
28
+ parsed
21
29
  } catch (e: Exception) {
22
30
  null
23
31
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-tiddlywiki-filesystem-android-external-storage",
3
- "version": "2.12.0",
3
+ "version": "2.12.2",
4
4
  "description": "Expo native module for TidGi-Mobile: filesystem I/O + TiddlyWiki .tid/.meta/.json batch parsing + git status in Kotlin (Android) and Swift (iOS)",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -9,7 +9,7 @@
9
9
  "build": "tsc -p tsconfig.json",
10
10
  "clean": "expo-module clean",
11
11
  "lint": "expo-module lint",
12
- "test": "expo-module test",
12
+ "test": "jest --config jest.config.js",
13
13
  "prepack": "tsc -p tsconfig.json"
14
14
  },
15
15
  "keywords": [
@@ -27,13 +27,16 @@
27
27
  "react-native": "*"
28
28
  },
29
29
  "devDependencies": {
30
+ "@types/jest": "29.5.14",
30
31
  "@types/react": "^18.2.0",
31
32
  "@types/react-native": "^0.73.0",
32
33
  "expo": "^50.0.0",
33
34
  "expo-module-scripts": "^3.0.0",
34
35
  "expo-modules-core": "^1.11.0",
36
+ "jest": "29.7.0",
35
37
  "react": "^18.2.0",
36
38
  "react-native": "^0.73.0",
39
+ "ts-jest": "~29.0.5",
37
40
  "typescript": "^5.0.0"
38
41
  },
39
42
  "files": [
@@ -0,0 +1,57 @@
1
+ type ExternalStorageModule = typeof import('../index');
2
+
3
+ function loadModuleWithPlatform(
4
+ platformOS: string,
5
+ nativeModule: Record<string, unknown> = {},
6
+ ): { module: ExternalStorageModule; requireNativeModule: jest.Mock } {
7
+ jest.resetModules();
8
+ const requireNativeModule = jest.fn(() => nativeModule);
9
+
10
+ jest.doMock('react-native', () => ({
11
+ Platform: {
12
+ OS: platformOS,
13
+ },
14
+ }));
15
+ jest.doMock('expo-modules-core', () => ({
16
+ requireNativeModule,
17
+ }));
18
+
19
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
20
+ const module = require('../index') as ExternalStorageModule;
21
+ return { module, requireNativeModule };
22
+ }
23
+
24
+ describe('toPlainPath', () => {
25
+ it('strips only the file URI scheme from filesystem paths', () => {
26
+ const { module } = loadModuleWithPlatform('android');
27
+
28
+ expect(module.toPlainPath('file:///storage/emulated/0/TidGi/wiki')).toBe('/storage/emulated/0/TidGi/wiki');
29
+ expect(module.toPlainPath('/storage/emulated/0/TidGi/wiki')).toBe('/storage/emulated/0/TidGi/wiki');
30
+ expect(module.toPlainPath('content://com.android.externalstorage.documents/tree/primary%3ATidGi')).toBe('content://com.android.externalstorage.documents/tree/primary%3ATidGi');
31
+ });
32
+ });
33
+
34
+ describe('ExternalStorage proxy', () => {
35
+ it('loads the native module lazily when a method is accessed', () => {
36
+ const nativeExists = jest.fn();
37
+ const nativeMkdir = jest.fn();
38
+ const { module, requireNativeModule } = loadModuleWithPlatform('android', {
39
+ exists: nativeExists,
40
+ mkdir: nativeMkdir,
41
+ });
42
+
43
+ expect(requireNativeModule).not.toHaveBeenCalled();
44
+ expect(module.ExternalStorage.exists).toBe(nativeExists);
45
+ expect(module.ExternalStorage.mkdir).toBe(nativeMkdir);
46
+ expect(requireNativeModule).toHaveBeenCalledTimes(1);
47
+ expect(requireNativeModule).toHaveBeenCalledWith('ExternalStorage');
48
+ });
49
+
50
+ it('throws only when the native proxy is accessed on unsupported platforms', () => {
51
+ const { module, requireNativeModule } = loadModuleWithPlatform('web');
52
+
53
+ expect(module.toPlainPath('file:///tmp/wiki')).toBe('/tmp/wiki');
54
+ expect(requireNativeModule).not.toHaveBeenCalled();
55
+ expect(() => module.ExternalStorage.exists).toThrow('ExternalStorage native module is only available on Android and iOS');
56
+ });
57
+ });
package/tsconfig.json CHANGED
@@ -4,5 +4,6 @@
4
4
  "outDir": "./build",
5
5
  "rootDir": "./src"
6
6
  },
7
- "include": ["src"]
7
+ "include": ["src"],
8
+ "exclude": ["src/**/__tests__/**", "src/**/*.test.ts"]
8
9
  }