Aller au contenu

Nettoyer les projets React Native

Une copie de travail React Native transporte plusieurs gigaoctets de fichiers générés : node_modules, ios/Pods, les builds Xcode et Gradle, et les fichiers intermédiaires C++ natifs. Multipliez cela par chaque projet ouvert cette année et par chaque worktree d'agent IA, et le total grimpe vite. Le scanner d'artefacts trouve ces dossiers dans tous vos projets, comme npkill ou kondo, mais il sait où React Native, Expo et Xcode placent leurs sorties. Il ne touche jamais à vos sources.

Fenêtre de terminal
lu-cleaner artifacts # sélecteur sur tous les projets et worktrees
lu-cleaner artifacts ~/dev/my-app # uniquement ce dossier (ni worktrees ni dossiers supplémentaires)
lu-cleaner artifacts --list --all # afficher la liste complète à la place
lu-cleaner artifacts --older-than 30d # projets inchangés depuis un mois

Dans le tableau de bord (lu-cleaner), ces mêmes éléments se trouvent dans la catégorie Project artifacts.

Dossier Type d'élément Risque Doit se trouver à côté de Revient avec
node_modules node_modules moderate package.json L'installation de votre gestionnaire de paquets (voir plus bas)
ios/Pods ios-pods moderate Podfile ou Podfile.lock pod install, npx pod-install ou npx expo run:ios (1 à 5 minutes avec un cache CocoaPods déjà rempli)
ios/build ios-build safe, moderate avec codegen *.xcodeproj, *.xcworkspace ou Podfile Le prochain build Xcode ou npx expo run:ios
android/app/build, android/build android-build safe build.gradle ou build.gradle.kts Le prochain build Gradle ou npx expo run:android (3 à 10 minutes)
android/.gradle android-gradle safe settings.gradle, gradlew ou build.gradle La prochaine exécution de Gradle
android/.kotlin android-kotlin safe Comme ci-dessus Le prochain build Gradle
.cxx, .externalNativeBuild android-cxx safe CMakeLists.txt ou build.gradle Le prochain build natif (plusieurs minutes de C++). Le supprimer corrige aussi les erreurs de build dues à un .cxx périmé.
.expo expo safe package.json, app.json ou app.config.* expo start ou expo run:*
.metro-cache, .metro metro-cache safe package.json ou metro.config.* Rien à faire : le premier bundle est simplement plus lent
.next next safe next.config.* ou package.json next dev ou next build
.turbo turbo safe package.json ou turbo.json Rien à faire : les tâches s'exécutent une fois sans cache
dist, build, web-build d'un paquet JS dist, js-build, web-build safe, moderate pour les sorties de bibliothèque package.json Le script de build du projet
.yarn/cache, .yarn/unplugged yarn-cache, yarn-unplugged moderate .yarnrc.yml yarn install
coverage coverage safe package.json La prochaine mesure de couverture
playwright-report, test-results playwright-report moderate playwright.config.* ou package.json La prochaine exécution des tests (les traces et vidéos des exécutions passées sont perdues)
vendor/bundle ruby-vendor-bundle moderate Gemfile bundle install

Le scanner connaît aussi les artefacts d'autres écosystèmes, comme Flutter, Rust, Swift Package Manager, les virtualenvs Python, CMake et .NET. Consultez la référence des scanners. Passez le type d'élément à --target/-t avec artifacts, ou à --kind/-k partout :

Fenêtre de terminal
lu-cleaner artifacts -t node_modules -t ios-pods

-t n'accepte que des types d'artefacts et quelques alias (pods, pod, cocoapods, node-modules) : une faute de frappe est une erreur d'utilisation, pas un scan vide.

Un dossier nommé build peut contenir la sortie de Xcode, ou du code source que quelqu'un a rangé dans un dossier build. lu-cleaner ne propose un dossier que si trois vérifications réussissent. Chacune existe pour écarter une erreur précise.

Vérification Exemple Pourquoi
Un fichier marqueur se trouve à côté package.json à côté de node_modules, un Podfile à côté de Pods, un build.gradle à côté de android/app/build Prouve que le dossier appartient à un projet de ce type. Le marqueur est revérifié juste avant la suppression. S'il a disparu, l'élément est ignoré.
Il ne contient aucun fichier suivi par git ios/Pods commité, node_modules embarqué dans le dépôt, cache Yarn zero-install Quelqu'un a commité ces fichiers volontairement. Les supprimer modifierait le dépôt au lieu de libérer un cache.
Les noms génériques doivent être ignorés par git build, dist, out, coverage, .yarn/*, vendor/bundle Un projet qui ignore le dossier le déclare jetable. Une sortie de build sans ambiguïté est aussi acceptée, comme le XCBuildData de Xcode ou un fichier CACHEDIR.TAG, tant que le dossier n'est pas suivi. En dehors d'un dépôt git, les dossiers génériques doivent contenir une sortie de build reconnaissable.

En dehors de git, un dossier de sortie JS (build, dist, out, web-build) qui ne contient que ce à quoi ressemblent aussi des fichiers écrits à la main (fichiers .js, index.html, ressources), sans rien de ce que seul un bundler écrit (source maps, noms de bundles hachés comme main.3f2a1b9c.js, asset-manifest.json, _next…), n'est proposé qu'en caution. Il porte l'avertissement not under git: nothing proves it is build output — check before deleting et n'est jamais recommandé.

Le scanner a aussi des limites strictes :

  • Un dossier qui contient une entrée .git de n'importe quel type (un dépôt, un worktree lié comme une branche gh-pages extraite dans dist, un sous-module) ou un dépôt bare est une copie de travail, jamais un artefact. Une sortie générique qui contient une copie de travail quelque part à l'intérieur est en rapport seul : contains a git repository (…) — not proposed.
  • Il ne suit jamais les liens symboliques, et ne passe jamais d'une racine à un autre volume.
  • Il n'entre jamais dans un artefact, un dossier .git, ~/Library, la Corbeille, ni dans les bundles d'applications comme .app et .xcodeproj.
  • Il ignore les dossiers cachés, sauf .claude/worktrees et .worktrees, où les agents rangent leurs copies de travail.
  • Un artefact situé sur un volume externe est listé mais ne peut pas être sélectionné, car le supprimer ne libère rien sur votre disque interne.
  • Juste avant la suppression, un dossier devenu une copie de travail depuis le scan (git init, git clone ou git worktree add dedans) est laissé intact.

Certains dossiers correspondent au nom et aux marqueurs d'une règle mais contiennent des fichiers suivis, comme un ios/Pods commité. Ils sont listés en rapport seul avec une raison, par exemple not proposed: contains files tracked by git (ios/Pods/Manifest.lock). Ils n'ont pas de taille : pour les voir, ouvrez le sélecteur avec lu-cleaner artifacts --min-size 0 et appuyez sur H.

Consultez le modèle de sécurité pour les vérifications qui s'appliquent à chaque suppression.

Avec la New Architecture, le codegen de React Native peut écrire des pod specs dans ios/build/generated/ios, et Podfile.lock y fait alors référence. Quand lu-cleaner trouve build/generated/ios dans Podfile.lock, il fait passer ios/build en moderate et ajoute un indicateur codegen. La note indique quoi faire après la suppression : lancer pod install, puis reconstruire. Si vous reconstruisez sans pod install, CocoaPods ne trouve pas les specs générées.

Dans un monorepo, un paquet partagé fait souvent pointer son package.json (main, module, types, exports, bin ou files) vers son propre dossier dist. Les applications et les config plugins Expo qui utilisent ce paquet ne fonctionnent plus tant qu'il n'est pas reconstruit. lu-cleaner le détecte, fait passer le dossier en moderate et lui ajoute l'étiquette library_output. Après la suppression, lancez le script build ou prepare du paquet.

Avertissements qui excluent un élément de la sélection intelligente

Section intitulée « Avertissements qui excluent un élément de la sélection intelligente »
Avertissement Signification
in use: process 4242 runs inside the project (dev server, agent, shell?) Quelque chose tourne dans le projet, comme Metro ou un agent. Ses dépendances pourraient disparaître sous ses pieds.
xcodebuild is running — quit it before cleaning ios/Pods, ios/build et DerivedData sont ignorés tant que xcodebuild tourne.
not under git: nothing proves it is build output — check before deleting Une sortie JS générique en dehors de git, avec seulement des indices faibles (voir plus haut).
contains built app binaries (app-release.apk…) Un dossier de build contient directement des fichiers .apk, .aab ou .ipa. Gardez-le si vous avez encore besoin de ce build.

Les éléments porteurs d'un avertissement peuvent tout de même être sélectionnés à la main. --yes les retient, sauf si vous passez --risk caution.

Le scanner parcourt tout le dépôt. Il ne s'arrête pas au premier package.json, et trouve donc les artefacts propres à chaque paquet du workspace. Les éléments sont nommés <dépôt> › <chemin dans le dépôt>, et le type de projet (expo, react-native, next…) vient du paquet auquel appartient le dossier :

  • Répertoiremy-monorepo/
    • pnpm-lock.yaml
    • package.json
    • Répertoirenode_modules/ my-monorepo › node_modules
      • …
    • Répertoireapps/
      • Répertoiremobile/
        • package.json
        • Répertoirenode_modules/ note : reinstall with pnpm i at the workspace root
          • …
        • Répertoireios/
          • Podfile
          • RépertoirePods/ my-monorepo › apps/mobile/ios/Pods
            • …
          • Répertoirebuild/ sortie Xcode, moderate si des specs codegen s'y trouvent
            • …
        • Répertoireandroid/
          • Répertoire.gradle/
            • …
          • Répertoireapp/
            • build.gradle
            • Répertoirebuild/ sortie Gradle
              • …
            • Répertoire.cxx/ intermédiaires C++ natifs
              • …
    • Répertoirepackages/
      • Répertoireui/
        • package.json son champ main pointe vers dist/index.js
        • Répertoiredist/ sortie de bibliothèque, moderate
          • …

Chaque node_modules est un élément distinct. Sa note donne la commande pour le réinstaller et l'endroit où la lancer : lu-cleaner cherche le lockfile depuis le dossier du paquet jusqu'à la racine du dépôt. Dans l'arborescence ci-dessus, la note indique reinstall with pnpm i at the workspace root.

Liens physiques, clones et espace réellement libéré

Section intitulée « Liens physiques, clones et espace réellement libéré »

pnpm, bun et Yarn Berry peuvent installer node_modules sous forme de liens vers un store partagé, ou de clones APFS de ce store (des copies en copy-on-write qui partagent leurs blocs disque tant qu'aucun des deux côtés n'est modifié ; c'est le comportement par défaut de bun sur macOS), plutôt que de vraies copies. Supprimer un tel node_modules ne libère que les données qui n'existent nulle part ailleurs. lu-cleaner mesure les deux mécanismes et affiche deux valeurs :

  • La taille (Size) est la taille allouée, chaque fichier lié physiquement n'étant compté qu'une fois.
  • Reclaim est ce que la suppression du dossier libère réellement. Cette valeur exclut les fichiers qui ont aussi des liens hors du dossier, par exemple dans le store pnpm, et ne compte un clone que pour ses blocs privés, sauf si toutes les copies de ses données se trouvent dans le dossier.

Quand les deux valeurs diffèrent :

  • le tableau de scan marque la taille d'un * ;
  • les détails du sélecteur affichent only 120 MB really freed (of 1.40 GB): shared with other files (hardlinks or APFS clones) ;
  • la note de l'élément nomme le store, par exemple Shared with other files (hardlinks or APFS clones of the pnpm store): only 120 MB is really freed ;
  • la sortie JSON contient les champs reclaim et freed (Sortie JSON).

Les totaux et le plan utilisent la valeur reclaim.

L'inverse est vrai aussi : tant que des projets pointent encore vers le store global, supprimer le store libère peu. Consultez Outils JS.

L'âge d'un artefact est celui de son projet, pas celui du dossier lui-même. Les installations et les builds modifient sans cesse le dossier de l'artefact, donc sa propre date ne dit rien sur le fait que vous travailliez encore sur le projet. lu-cleaner retient la date de modification la plus récente parmi les éléments suivants, en ignorant les dossiers d'artefacts eux-mêmes :

  • les fichiers de premier niveau du projet (lockfiles, configurations…) ;
  • les fichiers de premier niveau du paquet auquel appartient l'artefact ;
  • l'index git, HEAD et le reflog ;
  • chaque fichier avec des modifications non commitées, et chaque fichier non suivi, n'importe où dans la copie de travail : modifier src/components/Button.tsx compte avant même que vous ne le commitiez.

Les dates des dossiers sont ignorées : supprimer android/.gradle change la date de android/, ce qui ferait sinon paraître le projet actif juste après un nettoyage. En dehors d'un dépôt git, lu-cleaner cherche à la place le fichier source le plus récent au cours d'un parcours limité du projet.

--older-than accepte h, d, w, m (30 jours) et y :

Fenêtre de terminal
# Sélecteur limité aux projets inchangés depuis 30 jours
lu-cleaner artifacts --older-than 30d
# Tableau de tout ce qui se trouve dans les projets inactifs depuis 3 mois
lu-cleaner artifacts --list --all --older-than 3m
# Simulation : node_modules des projets inactifs depuis 60 jours
lu-cleaner artifacts -y -t node_modules --older-than 60d -n
# Simulation : sorties de build safe des projets inactifs depuis 2 semaines
lu-cleaner clean -y -c artifacts --risk safe --older-than 2w -n

Les éléments dont l'âge est inconnu ne correspondent jamais à --older-than.

La sélection intelligente (la touche a, ou --smart) applique ces règles aux artefacts :

  • Elle ne retient jamais rien d'un projet actif au cours des dernières 24 heures.
  • Elle retient les sorties safe (builds Xcode et Gradle, .expo, .cxx…) dès que le projet est inactif depuis 1 jour.
  • Elle retient les éléments moderate (node_modules, Pods…) dès que le projet est inactif depuis plus longtemps que stale_after, soit 14 jours par défaut.
  • Elle retient un .yarn/cache de Yarn Berry que Yarn ne lit plus (.yarnrc.yml définit enableGlobalCache: true comme clé de premier niveau) sans attendre stale_after.
  • Elle ne retient jamais les éléments porteurs d'un avertissement ni ceux de risque caution.

Reconstruire le projet quand vous en avez de nouveau besoin

Section intitulée « Reconstruire le projet quand vous en avez de nouveau besoin »

Pour node_modules, la note de l'élément donne la commande qui correspond à votre lockfile :

lu-cleaner trouve (du paquet jusqu'à la racine du dépôt) Commande dans la note
bun.lock ou bun.lockb bun i
pnpm-lock.yaml pnpm i
yarn.lock (avec .yarnrc.yml : Yarn Berry) yarn
package-lock.json ou npm-shrinkwrap.json npm ci
Aucun lockfile D'après le champ packageManager de package.json ; npm install à défaut

Un projet React Native typique se reconstruit ainsi :

Fenêtre de terminal
cd ~/dev/my-app
pnpm i # node_modules : utilisez la commande indiquée dans la note
cd ios && pod install && cd .. # ios/Pods (ou : npx pod-install)
npx expo run:ios # reconstruit ios/build (ou : npx react-native run-ios)
npx expo run:android # reconstruit android/app/build, .gradle et .cxx

lu-cleaner n'a pas de règle pour les dossiers ios/ et android/ eux-mêmes. Avec la Continuous Native Generation d'Expo, ces dossiers sont ignorés par git. L'un d'eux n'apparaît que s'il contient plus de 200 Mo en dehors de ses artefacts connus, et alors sous la forme d'un élément ignored-dir de risque caution (section suivante). npx expo prebuild régénère les deux dossiers.

Les projets accumulent aussi de gros dossiers qu'aucune règle ne connaît, et les agents IA aggravent le phénomène : captures d'écran et enregistrements de QA, copies de préproduction, exports temporaires. Quand un dossier ignoré par git contient plus de 200 Mo en plus des artefacts connus qu'il renferme, lu-cleaner le liste sous la forme my-app › tmp-recordings (git-ignored), avec le type ignored-dir :

  • Le risque est caution, car lu-cleaner ne peut pas savoir ce que contient le dossier. Il n'est jamais retenu par la sélection intelligente. clean --yes l'écarte sauf si vous passez --risk caution, et le sélecteur vous demande de taper yes.
  • Son âge est la date du dernier fichier écrit à l'intérieur, et non l'activité du projet.
  • S'il contient une copie de travail git ou un worktree, il est en rapport seul.
  • Les dossiers d'outils et d'éditeurs ne sont jamais signalés : .git, .claude, .cursor, .codex, .conductor, .vscode, .idea, .env, worktrees et similaires.
  • Sa taille inclut les artefacts connus trouvés à l'intérieur. Ces artefacts sont aussi listés comme éléments à part entière.

Ouvrez-le avec o (afficher dans le Finder) avant de décider.

Source Ce qu'elle ajoute
Racines de projets Les roots de la configuration ou, si elles sont vides, les dossiers existants parmi ~/dev, ~/Developer, ~/Projects, ~/code, ~/src, ~/repos, ~/conductor/repos et d'autres.
Dossiers de worktrees ~/.codex/worktrees, ~/.cursor/worktrees, ~/conductor/workspaces, ~/.claude/worktrees et les autres listés dans Worktrees des agents IA, plus worktree_roots
Dossiers supplémentaires ~/Documents/Codex, ~/conductor/archived-contexts et ~/.gemini/antigravity/scratch, où des outils copient des projets. Seules les règles de dépendances et de build s'y appliquent.

Les racines indiquées en ligne de commande (les arguments de lu-cleaner artifacts, ou --root) remplacent ces trois sources le temps d'une exécution : seuls ces dossiers sont parcourus, et les artefacts situés en dehors sont écartés. / est refusé comme racine. Elles ne restreignent que le scan des artefacts : les autres scanners conservent vos racines configurées pour savoir quelles versions de Node, Gradle ou NDK vos projets utilisent.

Le parcours descend jusqu'à max_depth niveaux, 8 par défaut, sous chaque racine. Chaque worktree lié rencontré en chemin repart avec la profondeur complète. Les artefacts situés dans un Bureau ou un dossier Documents synchronisés avec iCloud reçoivent une note, car les supprimer les retire aussi d'iCloud.

Pour ajouter vos propres noms d'artefacts, utilisez extra_artifacts dans la configuration. Dans un dépôt git, ces dossiers doivent être ignorés par git :

extra_artifacts = ["tmp-build", "e2e-artifacts"]