umpordez 1.5.0 → 1.5.1

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.
@@ -57,7 +57,13 @@
57
57
  "Bash(git *)",
58
58
  "Bash(npm init *)",
59
59
  "Bash(awk 'NR>=383 && NR<=386 {print NR\": \"substr\\($0,1,120\\)}' '/Users/ligeiro/dev/umpordez-cli/template/mobile/{{PROJECT_SLUG}}/src/pages/auth/AuthLanding.tsx')",
60
- "Bash(awk 'NR>=394 && NR<=410 {print NR\": \"substr\\($0,1,100\\)}' '/Users/ligeiro/dev/umpordez-cli/template/mobile/{{PROJECT_SLUG}}/src/pages/auth/AuthLanding.tsx')"
60
+ "Bash(awk 'NR>=394 && NR<=410 {print NR\": \"substr\\($0,1,100\\)}' '/Users/ligeiro/dev/umpordez-cli/template/mobile/{{PROJECT_SLUG}}/src/pages/auth/AuthLanding.tsx')",
61
+ "Bash(diskutil info *)",
62
+ "Bash(mount)",
63
+ "Bash(bash -n template/scripts/clean-appledouble.sh)",
64
+ "Bash(bash -n template/scripts/prebuild.sh)",
65
+ "Bash(bash -n template/scripts/run-ios.sh)",
66
+ "Bash(bash -n template/install.sh)"
61
67
  ]
62
68
  }
63
69
  }
package/create.mjs CHANGED
@@ -394,6 +394,104 @@ function validatePort(v) {
394
394
  : 'Must be a valid port number';
395
395
  }
396
396
 
397
+ // Filesystems that can't store extended attributes or symlinks. On
398
+ // macOS these make the OS scatter AppleDouble `._*` sidecar files next
399
+ // to everything npm installs — CocoaPods then fails parsing a
400
+ // `._Foo.podspec` during `expo prebuild`, and node_modules symlinks
401
+ // break. Generating an Expo project on one of these is a trap, so we
402
+ // warn before doing it.
403
+ const NON_NATIVE_FS = new Set([
404
+ 'exfat', 'msdos', 'fat32', 'vfat',
405
+ 'ntfs', 'ntfs-3g', 'fuseblk',
406
+ 'smbfs', 'cifs', 'nfs', 'afpfs', 'webdav',
407
+ ]);
408
+
409
+ // Resolve the filesystem type backing `targetPath`. The output dir
410
+ // usually doesn't exist yet, so we walk up to the nearest existing
411
+ // ancestor, then match it against the longest mount point in `mount`.
412
+ // Returns { mountPoint, fsType } or null (non-darwin, or undetectable).
413
+ function volumeInfo(targetPath) {
414
+ if (process.platform !== 'darwin') {
415
+ return null;
416
+ }
417
+
418
+ let dir = path.resolve(targetPath);
419
+ while (dir !== path.dirname(dir) && !fs.existsSync(dir)) {
420
+ dir = path.dirname(dir);
421
+ }
422
+
423
+ let mounts;
424
+ try {
425
+ mounts = execSync('mount', { encoding: 'utf-8' });
426
+ } catch {
427
+ return null;
428
+ }
429
+
430
+ // Lines look like: `/dev/disk10s1 on /Volumes/big (exfat, local, …)`
431
+ let best = null;
432
+ for (const line of mounts.split('\n')) {
433
+ const m = line.match(/ on (.+?) \(([^,)]+)/);
434
+ if (!m) {
435
+ continue;
436
+ }
437
+ const mountPoint = m[1];
438
+ const fsType = m[2].trim().toLowerCase();
439
+ const prefix = mountPoint.endsWith('/') ? mountPoint : mountPoint + '/';
440
+ if (dir === mountPoint || dir.startsWith(prefix)) {
441
+ if (!best || mountPoint.length > best.mountPoint.length) {
442
+ best = { mountPoint, fsType };
443
+ }
444
+ }
445
+ }
446
+ return best;
447
+ }
448
+
449
+ // Warn (and confirm) before generating a mobile project onto a
450
+ // filesystem macOS can't build native code on. Returns true to
451
+ // proceed, false to abort. Only meaningful when a mobile app is part
452
+ // of the project — the acute failure is CocoaPods + `expo prebuild`.
453
+ async function confirmFilesystem(outputDir, components) {
454
+ if (!components.mobile) {
455
+ return true;
456
+ }
457
+ const vol = volumeInfo(outputDir);
458
+ if (!vol || !NON_NATIVE_FS.has(vol.fsType)) {
459
+ return true;
460
+ }
461
+
462
+ console.log('');
463
+ console.log(red.bold(
464
+ ` ⚠ ${vol.mountPoint} is a ${vol.fsType} volume.`
465
+ ));
466
+ console.log(dim(
467
+ ' macOS can\'t store extended attributes or symlinks there,'
468
+ ));
469
+ console.log(dim(
470
+ ' so it scatters AppleDouble `._*` files next to everything'
471
+ ));
472
+ console.log(dim(
473
+ ' npm installs. `expo prebuild` then fails when CocoaPods'
474
+ ));
475
+ console.log(dim(
476
+ ' tries to parse a `._Foo.podspec`, and node_modules'
477
+ ));
478
+ console.log(dim(' symlinks break.'));
479
+ console.log('');
480
+ console.log(dim(
481
+ ' Recommended: generate on your internal drive (e.g. ~/dev),'
482
+ ));
483
+ console.log(dim(' an APFS / Mac OS Extended volume.'));
484
+ console.log('');
485
+
486
+ const { proceed } = await inquirer.prompt([{
487
+ type: 'confirm',
488
+ name: 'proceed',
489
+ message: `Generate on the ${vol.fsType} volume anyway?`,
490
+ default: false,
491
+ }]);
492
+ return proceed;
493
+ }
494
+
397
495
  function componentsLabel(c) {
398
496
  const parts = [];
399
497
  if (c.server) parts.push('server API');
@@ -604,6 +702,11 @@ export async function createProject() {
604
702
 
605
703
  const outputDir = path.resolve(details.outputDir);
606
704
 
705
+ if (!await confirmFilesystem(outputDir, details.components)) {
706
+ console.log(chalk.yellow('Aborted. No files were created.'));
707
+ return;
708
+ }
709
+
607
710
  if (fs.existsSync(outputDir)) {
608
711
  const { overwrite } = await inquirer.prompt([{
609
712
  type: 'confirm',
@@ -710,6 +813,7 @@ export async function createProject() {
710
813
  if (details.components.mobile) {
711
814
  scripts.push(
712
815
  'scripts/bump-version.sh',
816
+ 'scripts/clean-appledouble.sh',
713
817
  'scripts/prebuild.sh',
714
818
  'scripts/run-ios.sh',
715
819
  'scripts/release-android.sh',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "umpordez",
3
3
  "type": "module",
4
- "version": "1.5.0",
4
+ "version": "1.5.1",
5
5
  "description": "SaaS starter kit generator — server, admin SPA, public site (i18n), mobile app (offline-first, IAP, native push)",
6
6
  "main": "cli.mjs",
7
7
  "scripts": {
@@ -5,6 +5,8 @@ build/
5
5
  dist/
6
6
  *.log
7
7
  .DS_Store
8
+ ._*
9
+ .AppleDouble
8
10
  *.swp
9
11
  *.swo
10
12
  *~
@@ -55,6 +55,10 @@ if [ -d "mobile/{{PROJECT_SLUG}}" ]; then
55
55
 
56
56
  if [[ "$(uname)" == "Darwin" ]]; then
57
57
  if [ -d "mobile/{{PROJECT_SLUG}}/ios" ]; then
58
+ # Strip AppleDouble `._*` files (exFAT/NTFS/network
59
+ # volumes) so pod install doesn't choke on a
60
+ # `._Foo.podspec`. No-op on APFS.
61
+ ./scripts/clean-appledouble.sh "mobile/{{PROJECT_SLUG}}"
58
62
  echo "Installing iOS pods..."
59
63
  (cd "mobile/{{PROJECT_SLUG}}/ios" && pod install)
60
64
  fi
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # clean-appledouble — remove macOS AppleDouble `._*` sidecar files.
4
+ #
5
+ # macOS drops a `._<name>` metadata file next to every file that carries
6
+ # extended attributes when it lives on a filesystem that can't store
7
+ # xattrs natively (exFAT, MS-DOS/FAT, NTFS, SMB/NFS network mounts).
8
+ # CocoaPods then scans for `*.podspec`, finds `._Foo.podspec`, tries to
9
+ # parse the binary metadata as Ruby, and `pod install` — and therefore
10
+ # `expo prebuild` / `expo run:ios` — dies with:
11
+ #
12
+ # Invalid `Podfile` file: Invalid podspec file at path
13
+ # .../node_modules/.../ios/._Foo.podspec
14
+ #
15
+ # On APFS / Mac OS Extended there are none, so this is a fast no-op. Run
16
+ # it before any pod install / prebuild / native build.
17
+ #
18
+ # The real fix is to keep the project on an APFS / Mac OS Extended disk
19
+ # (e.g. ~/dev) — exFAT/NTFS regenerate these files on every npm install.
20
+ #
21
+ # Usage: ./scripts/clean-appledouble.sh [dir] (default: repo root)
22
+
23
+ set -euo pipefail
24
+
25
+ # AppleDouble files are a macOS-only artifact.
26
+ [[ "$(uname)" == "Darwin" ]] || exit 0
27
+
28
+ TARGET="${1:-$(cd "$(dirname "$0")/.." && pwd)}"
29
+ [[ -d "$TARGET" ]] || exit 0
30
+
31
+ n=$(
32
+ find "$TARGET" -name '._*' -type f -print -delete 2>/dev/null \
33
+ | wc -l | tr -d ' '
34
+ )
35
+
36
+ if [[ "${n:-0}" -gt 0 ]]; then
37
+ echo "==> Removed $n AppleDouble ._* file(s) under $TARGET"
38
+ echo " This disk can't store macOS extended attributes (likely"
39
+ echo " exFAT/NTFS/network), so they'll return on the next npm"
40
+ echo " install. For clean native builds, move the project to an"
41
+ echo " APFS / Mac OS Extended disk (e.g. ~/dev)."
42
+ fi
@@ -24,5 +24,12 @@ for arg in "$@"; do
24
24
  esac
25
25
  done
26
26
 
27
- cd "$(dirname "$0")/../mobile/{{PROJECT_SLUG}}"
27
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
28
+ cd "$SCRIPT_DIR/../mobile/{{PROJECT_SLUG}}"
29
+
30
+ # Strip macOS AppleDouble `._*` files before prebuild — CocoaPods
31
+ # chokes on `._Foo.podspec` when the project lives on exFAT/NTFS/network
32
+ # volumes. No-op on APFS. See scripts/clean-appledouble.sh.
33
+ "$SCRIPT_DIR/clean-appledouble.sh" "$PWD"
34
+
28
35
  exec npx expo prebuild "$@"
@@ -53,6 +53,11 @@ done
53
53
 
54
54
  cd "$APP_DIR"
55
55
 
56
+ # Strip macOS AppleDouble `._*` files so the pod install triggered by
57
+ # `expo run:ios` doesn't choke on `._Foo.podspec` (exFAT/NTFS/network
58
+ # volumes). No-op on APFS. See scripts/clean-appledouble.sh.
59
+ "$SCRIPT_DIR/clean-appledouble.sh" "$APP_DIR"
60
+
56
61
  if [[ $CLEAN -eq 1 ]]; then
57
62
  echo "==> Cleaning iOS build artifacts..."
58
63
  rm -rf ios/build