@webority/mobile-tools 0.115.0 → 0.116.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.
- package/README.md +2 -2
- package/lib/doctor.js +1 -1
- package/lib/sync.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ npx mobile-tools hooks install
|
|
|
73
73
|
- On a mismatch it prints the fix command and exits 1, well inside a second.
|
|
74
74
|
- `sync` installs from the lockfile and writes the stamp: `npm ci`, or `pnpm install --frozen-lockfile` when the app has `pnpm-lock.yaml`.
|
|
75
75
|
- When a package with an `android/` folder was added, removed or changed version, and the app has been built before, it runs `gradlew generateCodegenArtifactsFromSchema`. It then deletes `android/app/.cxx` and `android/app/build/generated/autolinking`.
|
|
76
|
-
- On
|
|
76
|
+
- On the xOS build host it runs `pod install` through the xos shim when the Pods are missing or stale; a `Gemfile` is ignored there.
|
|
77
77
|
- `sync --stamp` only records that `node_modules` matches the lockfile now. Run it from the app's `postinstall`, so a plain `npm ci` or `pnpm install` also leaves an accurate stamp.
|
|
78
78
|
- `hooks install` writes git `post-merge` and `post-checkout` hooks that run `sync --check` after every pull and branch switch.
|
|
79
79
|
- It honours `core.hooksPath`, and an app in a subfolder of the repo gets its own block.
|
|
@@ -216,7 +216,7 @@ Checks the toolchain and the app tree, one check per code:
|
|
|
216
216
|
- `firebaseConfig`: `android/app/google-services.json` exists, when the app uses `@react-native-firebase`.
|
|
217
217
|
- `xcode`: `xcode-select` has an Xcode (macOS, for the Simulator).
|
|
218
218
|
- `xos`: `xos` is on `PATH` and `xos doctor --json` exits 0 (a host that is not macOS).
|
|
219
|
-
- `cocoapods`:
|
|
219
|
+
- `cocoapods`: `pod` is on `PATH` (macOS and a host with xos).
|
|
220
220
|
|
|
221
221
|
`--fix` removes the stale autolinking caches (`android/build/generated/autolinking` and `android/app/build/generated/autolinking`) and nothing else. Android checks run for `--platform android` or when `android/` exists; iOS checks run for `--platform ios`, or when `ios/` exists on macOS or on a host with `xos` on `PATH`.
|
|
222
222
|
|
package/lib/doctor.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{existsSync,readFileSync,rmSync}from"node:fs";import path from"node:path";import{capture}from"./exec.js";import{fail,isJsonOutput,say,sayJson}from"./output.js";import{adbPath,findOnPath}from"./platform.js";import{checkNdkHook,usesFirebase}from"./prebuild.js";import{checkSync}from"./sync.js";import{xosPath}from"./xos.js";export const JDK_MAJOR=17;const JDK_FIX="install JDK 17 and set JAVA_HOME",ADB_FIX="set ANDROID_HOME to the Android SDK (adb lives in its platform-tools folder) or put adb on PATH",NINJA_FIX="install ninja 1.11 or newer (scoop install ninja) and put it first on PATH",XCODE_FIX="install Xcode, then xcode-select -s /Applications/Xcode.app",XOS_FIX="install xOS Tools (xos) and put it on PATH",AUTOLINKING_FILE=path.join("android","build","generated","autolinking","autolinking.json"),AUTOLINKING_DIRS=[path.join("android","build","generated","autolinking"),path.join("android","app","build","generated","autolinking")];export const javaMajor=o=>{const e=/version\b[^"]*"([^"]+)"/.exec(o);if(!e)return;const s=e[1].split(/[._+-]/),t=Number(s[0]),n=1===t?Number(s[1]):t;return Number.isInteger(n)?n:void 0};export const gradleSdkVersions=o=>{const e={},s=/ndkVersion\s*=\s*["']([^"']+)["']/.exec(o),t=/buildToolsVersion\s*=\s*["']([^"']+)["']/.exec(o),n=/compileSdkVersion\s*=\s*(\d+)/.exec(o);return s&&(e.ndk=s[1]),t&&(e.buildTools=t[1]),n&&(e.compileSdk=n[1]),e};const samePath=(o,e)=>{const norm=o=>{const e=path.resolve(o).replace(/[\\/]+$/,"");return"win32"===process.platform?e.toLowerCase():e};return norm(o)===norm(e)};export const autolinkingProblem=(o,e,s)=>{let t;try{t=JSON.parse(o)}catch{return"android/build/generated/autolinking/autolinking.json does not parse"}if("string"==typeof t?.root&&!samePath(t.root,e))return`the autolinking cache was made for ${t.root}, not ${e}`;for(const[o,e]of Object.entries(t?.dependencies??{}))if("string"==typeof e?.root&&!s(e.root))return`the autolinking cache names ${o} at ${e.root}, which no longer exists`};const ok=(o,e)=>({code:o,status:"ok",message:e}),skipped=(o,e)=>({code:o,status:"skipped",message:e}),problem=(o,e,s)=>({code:o,status:"problem",message:e,fix:s}),probe=(o,e,s)=>{try{return s()}catch(s){return problem(o,s instanceof Error?s.message:String(s),e)}},outputOf=o=>[o.stdout,o.stderr].join("\n").trim()||`exited with status ${o.status}`,SDK_SPECS=[{code:"ndk",key:"ndkVersion",field:"ndk",folder:(o,e)=>path.join(o,"ndk",e),install:o=>`sdkmanager "ndk;${o}"`},{code:"buildTools",key:"buildToolsVersion",field:"buildTools",folder:(o,e)=>path.join(o,"build-tools",e),install:o=>`sdkmanager "build-tools;${o}"`},{code:"androidPlatform",key:"compileSdkVersion",field:"compileSdk",folder:(o,e)=>path.join(o,"platforms",`android-${e}`),install:o=>`sdkmanager "platforms;android-${o}"`}],sdkCheck=(o,e)=>probe(e.code,"fix the Android SDK under ANDROID_HOME",()=>{const s=path.join(o,"android","build.gradle");if(!existsSync(s))return skipped(e.code,"no android/build.gradle");const t=process.env.ANDROID_HOME;if(!t)return skipped(e.code,"ANDROID_HOME is not set");const n=gradleSdkVersions(readFileSync(s,"utf8"))[e.field];if(void 0===n)return skipped(e.code,`${e.key} is not declared in android/build.gradle`);const r=e.folder(t,n);return existsSync(r)?ok(e.code,r):problem(e.code,`${r} is missing`,e.install(n))}),androidChecks=o=>{const{appRoot:e}=o,s=[];s.push(probe("nodeVersion","install Node 24",()=>{const o=process.versions.node;return 24===Number(o.split(".")[0])?ok("nodeVersion",`Node ${o}`):problem("nodeVersion",`Node ${o} is not Node 24`,"install Node 24")})),s.push(probe("androidHome","set ANDROID_HOME to the Android SDK folder",()=>{const o=process.env.ANDROID_HOME;return o?existsSync(o)?ok("androidHome",o):problem("androidHome",`ANDROID_HOME ${o} does not exist`,"set ANDROID_HOME to the Android SDK folder"):problem("androidHome","ANDROID_HOME is not set","set ANDROID_HOME to the Android SDK folder")}));const t={};s.push(probe("adb",ADB_FIX,()=>{const o=adbPath(process.env,process.platform),s=capture({file:o,args:["version"]},e);if(0!==s.status)return problem("adb",outputOf(s),ADB_FIX);t.path=o;const n=s.stdout.split(/\r?\n/).find(o=>o.startsWith("Version"));return ok("adb",n?`${o}, ${n.trim()}`:o)}));const n=t.path;s.push(void 0===n?skipped("adbServer","adb is not available"):probe("adbServer","use one adb, the SDK platform-tools one: MuMu ships its own adb",()=>{const o=capture({file:n,args:["devices"]},e),s=`${o.stdout}\n${o.stderr}`.split(/\r?\n/).find(o=>o.includes("doesn't match this client"));return void 0===s?ok("adbServer","one adb server version"):problem("adbServer",`another adb restarted the adb server at a different version: ${s.trim()}`,"use one adb, the SDK platform-tools one: MuMu ships its own adb")})),s.push(probe("jdk",JDK_FIX,()=>{const o=process.env.JAVA_HOME,s="win32"===process.platform?"java.exe":"java",t=o?path.join(o,"bin",s):void 0,n=void 0!==t&&existsSync(t)?t:findOnPath("java",process.env,process.platform);if(void 0===n)return problem("jdk","java not found",JDK_FIX);const r=capture({file:n,args:["-version"]},e),i=javaMajor(`${r.stderr}${r.stdout}`);return 17===i?ok("jdk",`Java ${i} at ${n}`):problem("jdk",`Java ${i??"unknown"} at ${n}; React Native builds need Java 17`,JDK_FIX)}));for(const o of SDK_SPECS)s.push(sdkCheck(e,o));s.push("win32"!==process.platform?skipped("ninja","only Windows builds need ninja on PATH"):probe("ninja",NINJA_FIX,()=>{const o=findOnPath("ninja",process.env,process.platform);if(void 0===o)return problem("ninja","ninja not found on PATH",NINJA_FIX);const s=capture({file:o,args:["--version"]},e).stdout.trim(),t=/^(\d+)\.(\d+)/.exec(s);if(!t)return problem("ninja",`ninja --version printed no version at ${o}`,NINJA_FIX);return Number(t[1])>1||1===Number(t[1])&&Number(t[2])>=11?ok("ninja",`ninja ${s} at ${o}`):problem("ninja",`ninja ${s} at ${o} is older than 1.11`,NINJA_FIX)})),s.push(probe("install","npx mobile-tools sync",()=>{const o=checkSync(e,process.platform);return 0===o.length?ok("install","node_modules matches the lockfile"):problem("install",o.join("; "),"npx mobile-tools sync")}));const r="npx mobile-tools doctor --fix";return s.push(probe("autolinkingCache",r,()=>{const s=path.join(e,AUTOLINKING_FILE);if(!existsSync(s))return ok("autolinkingCache","no autolinking cache yet");const t=autolinkingProblem(readFileSync(s,"utf8"),e,existsSync);if(void 0===t)return ok("autolinkingCache","matches this tree");if(!o.fix)return problem("autolinkingCache",t,r);for(const o of AUTOLINKING_DIRS)rmSync(path.join(e,o),{recursive:!0,force:!0});return{code:"autolinkingCache",status:"fixed",message:"removed both autolinking caches; the next build regenerates them"}})),s.push(probe("ndkHook","add the subprojects block the message names to android/build.gradle",()=>(checkNdkHook(e),ok("ndkHook","library modules get the app NDK")))),s.push(probe("firebaseConfig","add the Firebase Android config for this app",()=>{if(!usesFirebase(e))return skipped("firebaseConfig","the app does not use @react-native-firebase");const o=path.join(e,"android","app","google-services.json");return existsSync(o)?ok("firebaseConfig","android/app/google-services.json"):problem("firebaseConfig","android/app/google-services.json is missing","add the Firebase Android config for this app")})),s},xcodeCheck=o=>probe("xcode",XCODE_FIX,()=>{const e=capture({file:"/usr/bin/xcode-select",args:["-p"]},o);return 0===e.status?ok("xcode",e.stdout.trim()):problem("xcode","no Xcode developer directory is selected",XCODE_FIX)}),xosCheck=o=>probe("xos",XOS_FIX,()=>{const e=xosPath(process.env,process.platform),s=capture({file:e,args:["doctor","--json"]},o);return 0===s.status?ok("xos",e):problem("xos",s.stderr.trim().split(/\r?\n/)[0]||`xos doctor exited with status ${s.status}`,"run xos doctor and install what it names")});export const iosChecks=(o,e)=>["darwin"===e?xcodeCheck(o):xosCheck(o),probe("cocoapods","install CocoaPods",()=>{const
|
|
1
|
+
import{existsSync,readFileSync,rmSync}from"node:fs";import path from"node:path";import{capture}from"./exec.js";import{fail,isJsonOutput,say,sayJson}from"./output.js";import{adbPath,findOnPath}from"./platform.js";import{checkNdkHook,usesFirebase}from"./prebuild.js";import{checkSync}from"./sync.js";import{xosPath}from"./xos.js";export const JDK_MAJOR=17;const JDK_FIX="install JDK 17 and set JAVA_HOME",ADB_FIX="set ANDROID_HOME to the Android SDK (adb lives in its platform-tools folder) or put adb on PATH",NINJA_FIX="install ninja 1.11 or newer (scoop install ninja) and put it first on PATH",XCODE_FIX="install Xcode, then xcode-select -s /Applications/Xcode.app",XOS_FIX="install xOS Tools (xos) and put it on PATH",AUTOLINKING_FILE=path.join("android","build","generated","autolinking","autolinking.json"),AUTOLINKING_DIRS=[path.join("android","build","generated","autolinking"),path.join("android","app","build","generated","autolinking")];export const javaMajor=o=>{const e=/version\b[^"]*"([^"]+)"/.exec(o);if(!e)return;const s=e[1].split(/[._+-]/),t=Number(s[0]),n=1===t?Number(s[1]):t;return Number.isInteger(n)?n:void 0};export const gradleSdkVersions=o=>{const e={},s=/ndkVersion\s*=\s*["']([^"']+)["']/.exec(o),t=/buildToolsVersion\s*=\s*["']([^"']+)["']/.exec(o),n=/compileSdkVersion\s*=\s*(\d+)/.exec(o);return s&&(e.ndk=s[1]),t&&(e.buildTools=t[1]),n&&(e.compileSdk=n[1]),e};const samePath=(o,e)=>{const norm=o=>{const e=path.resolve(o).replace(/[\\/]+$/,"");return"win32"===process.platform?e.toLowerCase():e};return norm(o)===norm(e)};export const autolinkingProblem=(o,e,s)=>{let t;try{t=JSON.parse(o)}catch{return"android/build/generated/autolinking/autolinking.json does not parse"}if("string"==typeof t?.root&&!samePath(t.root,e))return`the autolinking cache was made for ${t.root}, not ${e}`;for(const[o,e]of Object.entries(t?.dependencies??{}))if("string"==typeof e?.root&&!s(e.root))return`the autolinking cache names ${o} at ${e.root}, which no longer exists`};const ok=(o,e)=>({code:o,status:"ok",message:e}),skipped=(o,e)=>({code:o,status:"skipped",message:e}),problem=(o,e,s)=>({code:o,status:"problem",message:e,fix:s}),probe=(o,e,s)=>{try{return s()}catch(s){return problem(o,s instanceof Error?s.message:String(s),e)}},outputOf=o=>[o.stdout,o.stderr].join("\n").trim()||`exited with status ${o.status}`,SDK_SPECS=[{code:"ndk",key:"ndkVersion",field:"ndk",folder:(o,e)=>path.join(o,"ndk",e),install:o=>`sdkmanager "ndk;${o}"`},{code:"buildTools",key:"buildToolsVersion",field:"buildTools",folder:(o,e)=>path.join(o,"build-tools",e),install:o=>`sdkmanager "build-tools;${o}"`},{code:"androidPlatform",key:"compileSdkVersion",field:"compileSdk",folder:(o,e)=>path.join(o,"platforms",`android-${e}`),install:o=>`sdkmanager "platforms;android-${o}"`}],sdkCheck=(o,e)=>probe(e.code,"fix the Android SDK under ANDROID_HOME",()=>{const s=path.join(o,"android","build.gradle");if(!existsSync(s))return skipped(e.code,"no android/build.gradle");const t=process.env.ANDROID_HOME;if(!t)return skipped(e.code,"ANDROID_HOME is not set");const n=gradleSdkVersions(readFileSync(s,"utf8"))[e.field];if(void 0===n)return skipped(e.code,`${e.key} is not declared in android/build.gradle`);const r=e.folder(t,n);return existsSync(r)?ok(e.code,r):problem(e.code,`${r} is missing`,e.install(n))}),androidChecks=o=>{const{appRoot:e}=o,s=[];s.push(probe("nodeVersion","install Node 24",()=>{const o=process.versions.node;return 24===Number(o.split(".")[0])?ok("nodeVersion",`Node ${o}`):problem("nodeVersion",`Node ${o} is not Node 24`,"install Node 24")})),s.push(probe("androidHome","set ANDROID_HOME to the Android SDK folder",()=>{const o=process.env.ANDROID_HOME;return o?existsSync(o)?ok("androidHome",o):problem("androidHome",`ANDROID_HOME ${o} does not exist`,"set ANDROID_HOME to the Android SDK folder"):problem("androidHome","ANDROID_HOME is not set","set ANDROID_HOME to the Android SDK folder")}));const t={};s.push(probe("adb",ADB_FIX,()=>{const o=adbPath(process.env,process.platform),s=capture({file:o,args:["version"]},e);if(0!==s.status)return problem("adb",outputOf(s),ADB_FIX);t.path=o;const n=s.stdout.split(/\r?\n/).find(o=>o.startsWith("Version"));return ok("adb",n?`${o}, ${n.trim()}`:o)}));const n=t.path;s.push(void 0===n?skipped("adbServer","adb is not available"):probe("adbServer","use one adb, the SDK platform-tools one: MuMu ships its own adb",()=>{const o=capture({file:n,args:["devices"]},e),s=`${o.stdout}\n${o.stderr}`.split(/\r?\n/).find(o=>o.includes("doesn't match this client"));return void 0===s?ok("adbServer","one adb server version"):problem("adbServer",`another adb restarted the adb server at a different version: ${s.trim()}`,"use one adb, the SDK platform-tools one: MuMu ships its own adb")})),s.push(probe("jdk",JDK_FIX,()=>{const o=process.env.JAVA_HOME,s="win32"===process.platform?"java.exe":"java",t=o?path.join(o,"bin",s):void 0,n=void 0!==t&&existsSync(t)?t:findOnPath("java",process.env,process.platform);if(void 0===n)return problem("jdk","java not found",JDK_FIX);const r=capture({file:n,args:["-version"]},e),i=javaMajor(`${r.stderr}${r.stdout}`);return 17===i?ok("jdk",`Java ${i} at ${n}`):problem("jdk",`Java ${i??"unknown"} at ${n}; React Native builds need Java 17`,JDK_FIX)}));for(const o of SDK_SPECS)s.push(sdkCheck(e,o));s.push("win32"!==process.platform?skipped("ninja","only Windows builds need ninja on PATH"):probe("ninja",NINJA_FIX,()=>{const o=findOnPath("ninja",process.env,process.platform);if(void 0===o)return problem("ninja","ninja not found on PATH",NINJA_FIX);const s=capture({file:o,args:["--version"]},e).stdout.trim(),t=/^(\d+)\.(\d+)/.exec(s);if(!t)return problem("ninja",`ninja --version printed no version at ${o}`,NINJA_FIX);return Number(t[1])>1||1===Number(t[1])&&Number(t[2])>=11?ok("ninja",`ninja ${s} at ${o}`):problem("ninja",`ninja ${s} at ${o} is older than 1.11`,NINJA_FIX)})),s.push(probe("install","npx mobile-tools sync",()=>{const o=checkSync(e,process.platform);return 0===o.length?ok("install","node_modules matches the lockfile"):problem("install",o.join("; "),"npx mobile-tools sync")}));const r="npx mobile-tools doctor --fix";return s.push(probe("autolinkingCache",r,()=>{const s=path.join(e,AUTOLINKING_FILE);if(!existsSync(s))return ok("autolinkingCache","no autolinking cache yet");const t=autolinkingProblem(readFileSync(s,"utf8"),e,existsSync);if(void 0===t)return ok("autolinkingCache","matches this tree");if(!o.fix)return problem("autolinkingCache",t,r);for(const o of AUTOLINKING_DIRS)rmSync(path.join(e,o),{recursive:!0,force:!0});return{code:"autolinkingCache",status:"fixed",message:"removed both autolinking caches; the next build regenerates them"}})),s.push(probe("ndkHook","add the subprojects block the message names to android/build.gradle",()=>(checkNdkHook(e),ok("ndkHook","library modules get the app NDK")))),s.push(probe("firebaseConfig","add the Firebase Android config for this app",()=>{if(!usesFirebase(e))return skipped("firebaseConfig","the app does not use @react-native-firebase");const o=path.join(e,"android","app","google-services.json");return existsSync(o)?ok("firebaseConfig","android/app/google-services.json"):problem("firebaseConfig","android/app/google-services.json is missing","add the Firebase Android config for this app")})),s},xcodeCheck=o=>probe("xcode",XCODE_FIX,()=>{const e=capture({file:"/usr/bin/xcode-select",args:["-p"]},o);return 0===e.status?ok("xcode",e.stdout.trim()):problem("xcode","no Xcode developer directory is selected",XCODE_FIX)}),xosCheck=o=>probe("xos",XOS_FIX,()=>{const e=xosPath(process.env,process.platform),s=capture({file:e,args:["doctor","--json"]},o);return 0===s.status?ok("xos",e):problem("xos",s.stderr.trim().split(/\r?\n/)[0]||`xos doctor exited with status ${s.status}`,"run xos doctor and install what it names")});export const iosChecks=(o,e)=>["darwin"===e?xcodeCheck(o):xosCheck(o),probe("cocoapods","install CocoaPods",()=>{const o=findOnPath("pod",process.env,e);return void 0===o?problem("cocoapods","pod not found on PATH","install CocoaPods"):ok("cocoapods",o)})];export const doctor=async o=>{const e=path.join(o.appRoot,"android"),s=path.join(o.appRoot,"ios");"android"!==o.platform||existsSync(e)||fail("appConfigInvalid",`no android/ folder in ${o.appRoot}`);const t=[];("android"===o.platform||void 0===o.platform&&existsSync(e))&&t.push(...androidChecks(o));const n="darwin"===process.platform||void 0!==findOnPath("xos",process.env,process.platform);("ios"===o.platform||void 0===o.platform&&n&&existsSync(s))&&t.push(...iosChecks(o.appRoot,process.platform));const r=t.filter(o=>"problem"===o.status).length;if(isJsonOutput())sayJson({checks:t,problems:r});else{for(const o of t){const e="problem"===o.status&&o.fix?` Fix: ${o.fix}`:"";say(`doctor: ${o.status} ${o.code}: ${o.message}${e}`)}say(0===r?"doctor: ready":`doctor: ${r} problem(s)`)}r>0&&(process.exitCode=1)};
|
package/lib/sync.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{createHash}from"node:crypto";import{existsSync,readdirSync,readFileSync,rmSync,statSync,writeFileSync}from"node:fs";import path from"node:path";import{runInherit}from"./exec.js";import{fail,say}from"./output.js";import{findOnPath,gradleInvocation,npmInvocation,pnpmInvocation}from"./platform.js";import{prependXosShims,xosPath}from"./xos.js";export const STAMP_FILE=path.join("node_modules",".webority-install.json");export const PODS_RECORD=path.join("ios","Pods",".webority-native.json");const INSTALL_FIELDS=["dependencies","devDependencies","optionalDependencies","peerDependencies","bundleDependencies","overrides","resolutions","pnpm","workspaces"];export const lockfileOf=e=>{const n=path.join(e,"pnpm-lock.yaml"),s=path.join(e,"package-lock.json");return existsSync(n)&&existsSync(s)?fail("appConfigInvalid","both pnpm-lock.yaml and package-lock.json exist: delete the one not in use"):existsSync(n)?{manager:"pnpm",file:n}:existsSync(s)?{manager:"npm",file:s}:fail("appConfigInvalid",`no package-lock.json or pnpm-lock.yaml in ${e}`)};const filesUnder=(e,n="")=>readdirSync(e,{withFileTypes:!0}).flatMap(s=>{const o=""===n?s.name:`${n}/${s.name}`;return s.isDirectory()?filesUnder(path.join(e,s.name),o):[o]}).sort();export const installHash=e=>{const{file:n}=lockfileOf(e),s=JSON.parse(readFileSync(path.join(e,"package.json"),"utf8")),o=createHash("sha256");o.update(`lockfile:${path.basename(n)}\0`),o.update(readFileSync(n)),o.update("\0package.json\0"),o.update(JSON.stringify(INSTALL_FIELDS.map(e=>[e,s[e]??null])));const t=path.join(e,"patches");if(existsSync(t))for(const e of filesUnder(t))o.update(`\0patches/${e}\0`),o.update(readFileSync(path.join(t,...e.split("/"))));return o.digest("hex")};export const readStamp=e=>{const n=path.join(e,STAMP_FILE);if(existsSync(n))try{return JSON.parse(readFileSync(n,"utf8"))}catch{return}};const
|
|
1
|
+
import{createHash}from"node:crypto";import{existsSync,readdirSync,readFileSync,rmSync,statSync,writeFileSync}from"node:fs";import path from"node:path";import{runInherit}from"./exec.js";import{fail,say}from"./output.js";import{findOnPath,gradleInvocation,npmInvocation,pnpmInvocation}from"./platform.js";import{prependXosShims,xosPath}from"./xos.js";export const STAMP_FILE=path.join("node_modules",".webority-install.json");export const PODS_RECORD=path.join("ios","Pods",".webority-native.json");const INSTALL_FIELDS=["dependencies","devDependencies","optionalDependencies","peerDependencies","bundleDependencies","overrides","resolutions","pnpm","workspaces"];export const lockfileOf=e=>{const n=path.join(e,"pnpm-lock.yaml"),s=path.join(e,"package-lock.json");return existsSync(n)&&existsSync(s)?fail("appConfigInvalid","both pnpm-lock.yaml and package-lock.json exist: delete the one not in use"):existsSync(n)?{manager:"pnpm",file:n}:existsSync(s)?{manager:"npm",file:s}:fail("appConfigInvalid",`no package-lock.json or pnpm-lock.yaml in ${e}`)};const filesUnder=(e,n="")=>readdirSync(e,{withFileTypes:!0}).flatMap(s=>{const o=""===n?s.name:`${n}/${s.name}`;return s.isDirectory()?filesUnder(path.join(e,s.name),o):[o]}).sort();export const installHash=e=>{const{file:n}=lockfileOf(e),s=JSON.parse(readFileSync(path.join(e,"package.json"),"utf8")),o=createHash("sha256");o.update(`lockfile:${path.basename(n)}\0`),o.update(readFileSync(n)),o.update("\0package.json\0"),o.update(JSON.stringify(INSTALL_FIELDS.map(e=>[e,s[e]??null])));const t=path.join(e,"patches");if(existsSync(t))for(const e of filesUnder(t))o.update(`\0patches/${e}\0`),o.update(readFileSync(path.join(t,...e.split("/"))));return o.digest("hex")};export const readStamp=e=>{const n=path.join(e,STAMP_FILE);if(existsSync(n))try{return JSON.parse(readFileSync(n,"utf8"))}catch{return}};const POD_FIX="cd ios && pod install";export const podsStale=e=>{const n=path.join(e,"ios","Podfile.lock"),s=path.join(e,"ios","Pods","Manifest.lock");return!(!existsSync(n)||!existsSync(s))&&!readFileSync(n).equals(readFileSync(s))};const nodeModulesNewerThanPods=e=>{const n=[path.join(e,"node_modules",".modules.yaml"),path.join(e,"node_modules",".pnpm","lock.yaml")].find(e=>existsSync(e)),s=path.join(e,"ios","Pods","Manifest.lock");return!(!n||!existsSync(s))&&statSync(n).mtimeMs>statSync(s).mtimeMs};export const checkSync=(e,n)=>{const s=[],o=readStamp(e),{file:t}=lockfileOf(e);if(o?o.hash!==installHash(e)&&s.push(`node_modules is stale: ${path.basename(t)}, package.json or patches/ changed since the last install. Fix: npx mobile-tools sync`):s.push("node_modules has no install stamp (never installed through mobile-tools). Fix: npx mobile-tools sync"),o){const n=changedNativePackages(o.nativePackages??{},nativePackages(e));n.length>0&&s.push(`node_modules native packages changed since the last install: ${n.join(", ")}. Fix: npx mobile-tools sync`)}return void 0!==findOnPath("xos",process.env,n)&&podsStale(e)&&s.push(`ios/Pods is stale: ios/Podfile.lock differs from ios/Pods/Manifest.lock. Fix: ${POD_FIX}`),void 0!==findOnPath("xos",process.env,n)&&existsSync(path.join(e,"ios","Podfile.lock"))&&nodeModulesNewerThanPods(e)&&s.push("node_modules changed after the last pod install; run pod install in ios/ (or mobile-tools sync)"),s};export const requireSynced=e=>{const n=checkSync(e,process.platform);n.length>0&&fail("installStale",n.join("\n"))};export const nativePackages=e=>{const n=path.join(e,"node_modules");if(!existsSync(n))return{};const s=[];for(const e of readdirSync(n,{withFileTypes:!0}))if(!e.name.startsWith(".")&&e.isDirectory())if(e.name.startsWith("@"))for(const o of readdirSync(path.join(n,e.name),{withFileTypes:!0}))o.isDirectory()&&s.push(path.join(n,e.name,o.name));else s.push(path.join(n,e.name));const o={};for(const e of s){const n=path.join(e,"android"),s=path.join(e,"package.json");if(existsSync(n)&&statSync(n).isDirectory()&&existsSync(s))try{const e=JSON.parse(readFileSync(s,"utf8"));e.name&&e.version&&(o[e.name]=e.version)}catch{}}return o};export const changedNativePackages=(e,n)=>[...new Set([...Object.keys(e),...Object.keys(n)])].filter(s=>e[s]!==n[s]).sort();export const writeStamp=e=>{const{manager:n}=lockfileOf(e),s={hash:installHash(e),manager:n,nativePackages:nativePackages(e)};return writeFileSync(path.join(e,STAMP_FILE),`${JSON.stringify(s,null,2)}\n`,"utf8"),s};export const installInvocation=(e,n)=>"pnpm"===e?pnpmInvocation(["install","--frozen-lockfile"],process.env,n,process.execPath):npmInvocation(["ci"],process.env,n,process.execPath);export const ensurePods=(e,n)=>{if(!existsSync(path.join(e,"ios","Podfile")))return;const s=path.join(e,"ios","Pods"),o=path.join(e,PODS_RECORD);let t;if(existsSync(s)?existsSync(path.join(e,"ios","Podfile.lock"))?existsSync(path.join(s,"Manifest.lock"))?podsStale(e)?t="ios/Podfile.lock differs from ios/Pods/Manifest.lock":nodeModulesNewerThanPods(e)?t="node_modules changed after the last pod install":existsSync(o)?readFileSync(o,"utf8")!==JSON.stringify(nativePackages(e))&&(t="native packages changed since the last pod install"):t="no pod install record":t="ios/Pods/Manifest.lock is missing":t="ios/Podfile.lock is missing":t="ios/Pods is missing",void 0===t)return;say(`pods: ${t}; running pod install`);const i=findOnPath("pod",process.env,n);i||fail("toolMissing",`pod not found on PATH; check xos doctor --json names its shims folder, then ${POD_FIX}`),runInherit({file:i,args:["install"]},path.join(e,"ios"),"pod install");const a=path.join(e,"ios","build","ModuleCache.noindex");existsSync(a)&&(rmSync(a,{recursive:!0,force:!0}),say(`sync: removed ${path.relative(e,a)}`)),writeFileSync(o,JSON.stringify(nativePackages(e)),"utf8")};export const sync=(e,n)=>{const{manager:s}=lockfileOf(e),o=readStamp(e)?.nativePackages??nativePackages(e);say("sync: "+("pnpm"===s?"pnpm install --frozen-lockfile":"npm ci")),runInherit(installInvocation(s,n),e,`${s} install`);const t=writeStamp(e).nativePackages,i=changedNativePackages(o,t),a=path.join(e,"android"),c=existsSync(path.join(a,"app",".cxx"))||existsSync(path.join(a,"app","build"));if(i.length>0&&c){say(`sync: native packages changed (${i.join(", ")}): regenerating codegen`),runInherit(gradleInvocation(a,["generateCodegenArtifactsFromSchema"],n,process.env),a,"gradlew generateCodegenArtifactsFromSchema");for(const n of[path.join(a,"app",".cxx"),path.join(a,"app","build","generated","autolinking")])rmSync(n,{recursive:!0,force:!0}),say(`sync: removed ${path.relative(e,n)}`)}void 0!==findOnPath("xos",process.env,n)&&existsSync(path.join(e,"ios","Podfile"))&&(prependXosShims(xosPath(process.env,n),e),ensurePods(e,n)),say("sync: done")};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/mobile-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.116.0",
|
|
4
4
|
"description": "Dev-time CLI for Webority React Native apps: one prebuild, a dependency guard, a device-aware run and per-environment launcher icons, the same on Windows and macOS.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native",
|