[{"data":1,"prerenderedAt":1504},["Reactive",2],{"content-query-HCsJ8CSNBZ":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"_empty":6,"title":8,"description":9,"date":10,"tags":11,"readingTime":15,"body":20,"_type":1499,"_id":1500,"_source":1501,"_file":1502,"_extension":1503},"/blog/nm-cleanup","blog",false,"","Keep your workspace clean with nm‑cleanup and cron","Everyone who's ever worked with npm modules knows how heavy node_modules dirs can get - in some projects I've had them close to a gig! And as long as you're actively working on a project, that's alright. But often times, when the work is finished or paused until no-one-knows-when, the node_modules dirs are just left sitting there without purpose, and slowly but surely eating up your disk space.",1728310204208,[12,13,14],"tutorial","tool","cli",{"text":16,"minutes":17,"time":18,"words":19},"9 min read",8.54,512400,1708,{"type":21,"children":22,"toc":1482},"root",[23,53,64,73,96,116,176,183,188,193,244,249,267,290,296,301,326,331,403,408,439,458,491,497,502,513,525,531,536,543,548,560,566,582,587,593,610,620,624,645,653,665,740,745,762,768,773,791,810,815,832,860,866,871,886,892,900,905,1015,1021,1026,1044,1056,1074,1160,1165,1188,1193,1210,1216,1234,1239,1246,1251,1269,1275,1280,1298,1304,1309,1336,1354,1372,1378,1394,1412,1430,1436,1459,1472,1477],{"type":24,"tag":25,"props":26,"children":27},"element","p",{},[28,31,37,39,45,47,51],{"type":29,"value":30},"text","Everyone who's ever worked with npm modules knows how heavy ",{"type":24,"tag":32,"props":33,"children":34},"code-inline",{},[35],{"type":29,"value":36},"node_modules",{"type":29,"value":38}," dirs can get - in some projects I've had them close to a gig! And ",{"type":24,"tag":40,"props":41,"children":42},"em",{},[43],{"type":29,"value":44},"as long as you're actively working on a project",{"type":29,"value":46},", that's alright. But often times, when the work is finished or paused until no-one-knows-when, the ",{"type":24,"tag":32,"props":48,"children":49},{},[50],{"type":29,"value":36},{"type":29,"value":52}," dirs are just left sitting there without purpose, and slowly but surely eating up your disk space.",{"type":24,"tag":25,"props":54,"children":55},{},[56,58,62],{"type":29,"value":57},"Chances are, you won't be needing some of those fat-bottomed ",{"type":24,"tag":32,"props":59,"children":60},{},[61],{"type":29,"value":36},{"type":29,"value":63}," anytime soon. And even if you do, you could always reinstall them in a matter of seconds! So why keep them around and waste the valuable disk space?",{"type":24,"tag":25,"props":65,"children":66},{},[67],{"type":24,"tag":68,"props":69,"children":72},"img",{"alt":70,"src":71},"accurate representation of node_modules folder size","/blog/nm-cleanup/node_modules_meme.png",[],{"type":24,"tag":25,"props":74,"children":75},{},[76,78,87,89,94],{"type":29,"value":77},"In this post, I'm going to walk you through a basic configuration of the cli tool called ✨",{"type":24,"tag":79,"props":80,"children":84},"a",{"href":81,"rel":82},"https://github.com/gVguy/nm-cleanup",[83],"nofollow",[85],{"type":29,"value":86},"nm-cleanup",{"type":29,"value":88},"✨, which I authored to solve exactly this problem. As you will shortly see, it's a highly customizable and ",{"type":24,"tag":40,"props":90,"children":91},{},[92],{"type":29,"value":93},"automatable",{"type":29,"value":95}," utility, that can help you maintain your workspace clean.",{"type":24,"tag":25,"props":97,"children":98},{},[99,101,107,109,114],{"type":29,"value":100},"I will also show you how we can set it up ",{"type":24,"tag":102,"props":103,"children":104},"strong",{},[105],{"type":29,"value":106},"to run on a regular basis",{"type":29,"value":108},", without requiring any user input - just ",{"type":24,"tag":40,"props":110,"children":111},{},[112],{"type":29,"value":113},"set it up once",{"type":29,"value":115}," and rest assured your disk isn't cluttered with useless folders!",{"type":24,"tag":117,"props":118,"children":119},"details",{},[120,126,131,158],{"type":24,"tag":121,"props":122,"children":123},"summary",{},[124],{"type":29,"value":125},"And what about other tools aimed at this same problem?",{"type":24,"tag":25,"props":127,"children":128},{},[129],{"type":29,"value":130},"While it's true that there are other tools meant to solve this very same problem, their behavior is different. They either:",{"type":24,"tag":132,"props":133,"children":134},"ul",{},[135,147],{"type":24,"tag":136,"props":137,"children":138},"li",{},[139,141,145],{"type":29,"value":140},"wipe all ",{"type":24,"tag":32,"props":142,"children":143},{},[144],{"type":29,"value":36},{"type":29,"value":146}," they find without checking the mtime,",{"type":24,"tag":136,"props":148,"children":149},{},[150,152,156],{"type":29,"value":151},"or give you a UI, where they list all the ",{"type":24,"tag":32,"props":153,"children":154},{},[155],{"type":29,"value":36},{"type":29,"value":157}," folders and let you manually select the projects you want to get rid of.",{"type":24,"tag":25,"props":159,"children":160},{},[161,163,168,170,174],{"type":29,"value":162},"And that makes them really hard (if not impossible) to ",{"type":24,"tag":102,"props":164,"children":165},{},[166],{"type":29,"value":167},"automate",{"type":29,"value":169},". Which is fully supported by ",{"type":24,"tag":32,"props":171,"children":172},{},[173],{"type":29,"value":86},{"type":29,"value":175},", as you will see in a second.",{"type":24,"tag":177,"props":178,"children":180},"h2",{"id":179},"install-the-cli",[181],{"type":29,"value":182},"Install the cli",{"type":24,"tag":25,"props":184,"children":185},{},[186],{"type":29,"value":187},"First things first, we need to install the cli.",{"type":24,"tag":25,"props":189,"children":190},{},[191],{"type":29,"value":192},"I think I can skip all the \"make sure Node is installed\", as we're trying to solve the node_modules problem, so you've likely used npm before ;)",{"type":24,"tag":194,"props":195,"children":199},"code",{"code":196,"language":197,"meta":198},"npm install -g nm-cleanup\n","shell",null,[200],{"type":24,"tag":201,"props":202,"children":203},"pre",{},[204],{"type":24,"tag":194,"props":205,"children":206},{"__ignoreMap":7},[207],{"type":24,"tag":208,"props":209,"children":212},"span",{"class":210,"line":211},"line",1,[213,219,225,230,236,240],{"type":24,"tag":208,"props":214,"children":216},{"class":215},"ct-be44c9",[217],{"type":29,"value":218},"npm ",{"type":24,"tag":208,"props":220,"children":222},{"class":221},"ct-d60cab",[223],{"type":29,"value":224},"install",{"type":24,"tag":208,"props":226,"children":227},{"class":215},[228],{"type":29,"value":229}," ",{"type":24,"tag":208,"props":231,"children":233},{"class":232},"ct-572368",[234],{"type":29,"value":235},"-g",{"type":24,"tag":208,"props":237,"children":238},{"class":215},[239],{"type":29,"value":229},{"type":24,"tag":208,"props":241,"children":242},{"class":221},[243],{"type":29,"value":86},{"type":24,"tag":25,"props":245,"children":246},{},[247],{"type":29,"value":248},"Make sure to install it globally to make the cli accessible from anywhere on your machine.",{"type":24,"tag":25,"props":250,"children":251},{},[252,254,258,260,265],{"type":29,"value":253},"Then ",{"type":24,"tag":32,"props":255,"children":256},{},[257],{"type":29,"value":86},{"type":29,"value":259}," cli (or simply ",{"type":24,"tag":32,"props":261,"children":262},{},[263],{"type":29,"value":264},"nmc",{"type":29,"value":266},") should be executable from the terminal. To test that, you can run:",{"type":24,"tag":194,"props":268,"children":270},{"code":269,"language":197,"meta":198},"nmc -V\n",[271],{"type":24,"tag":201,"props":272,"children":273},{},[274],{"type":24,"tag":194,"props":275,"children":276},{"__ignoreMap":7},[277],{"type":24,"tag":208,"props":278,"children":279},{"class":210,"line":211},[280,285],{"type":24,"tag":208,"props":281,"children":282},{"class":215},[283],{"type":29,"value":284},"nmc ",{"type":24,"tag":208,"props":286,"children":287},{"class":232},[288],{"type":29,"value":289},"-V",{"type":24,"tag":177,"props":291,"children":293},{"id":292},"how-it-works",[294],{"type":29,"value":295},"How it works?",{"type":24,"tag":25,"props":297,"children":298},{},[299],{"type":29,"value":300},"The main goal with this utility was to support automation. And even when ran in interactive mode, keep the count of required actions to a minimum.",{"type":24,"tag":25,"props":302,"children":303},{},[304,306,310,312,317,319,324],{"type":29,"value":305},"So when the tool runs, it finds all \"targets\" (by default ",{"type":24,"tag":32,"props":307,"children":308},{},[309],{"type":29,"value":36},{"type":29,"value":311},", but can be customized) in all project dirs recursively. And while it scans the filesystem, it also ",{"type":24,"tag":102,"props":313,"children":314},{},[315],{"type":29,"value":316},"measures when each project was last modified",{"type":29,"value":318}," based on the contained files. And if the modification time is older that the specified threshold (by default 30 days), the target is ",{"type":24,"tag":40,"props":320,"children":321},{},[322],{"type":29,"value":323},"marked for deletion",{"type":29,"value":325},".",{"type":24,"tag":25,"props":327,"children":328},{},[329],{"type":29,"value":330},"So, if you have a following fs structure...",{"type":24,"tag":194,"props":332,"children":334},{"code":333,"meta":198},"~/projects/\n├── old-project/\n│   ├── package.json\n│   └── node_modules\n└── fresh-project/\n    ├── package.json\n    └── node_modules\n",[335],{"type":24,"tag":201,"props":336,"children":337},{},[338],{"type":24,"tag":194,"props":339,"children":340},{"__ignoreMap":7},[341,349,358,367,376,385,394],{"type":24,"tag":208,"props":342,"children":343},{"class":210,"line":211},[344],{"type":24,"tag":208,"props":345,"children":346},{},[347],{"type":29,"value":348},"~/projects/\n",{"type":24,"tag":208,"props":350,"children":352},{"class":210,"line":351},2,[353],{"type":24,"tag":208,"props":354,"children":355},{},[356],{"type":29,"value":357},"├── old-project/\n",{"type":24,"tag":208,"props":359,"children":361},{"class":210,"line":360},3,[362],{"type":24,"tag":208,"props":363,"children":364},{},[365],{"type":29,"value":366},"│   ├── package.json\n",{"type":24,"tag":208,"props":368,"children":370},{"class":210,"line":369},4,[371],{"type":24,"tag":208,"props":372,"children":373},{},[374],{"type":29,"value":375},"│   └── node_modules\n",{"type":24,"tag":208,"props":377,"children":379},{"class":210,"line":378},5,[380],{"type":24,"tag":208,"props":381,"children":382},{},[383],{"type":29,"value":384},"└── fresh-project/\n",{"type":24,"tag":208,"props":386,"children":388},{"class":210,"line":387},6,[389],{"type":24,"tag":208,"props":390,"children":391},{},[392],{"type":29,"value":393},"    ├── package.json\n",{"type":24,"tag":208,"props":395,"children":397},{"class":210,"line":396},7,[398],{"type":24,"tag":208,"props":399,"children":400},{},[401],{"type":29,"value":402},"    └── node_modules",{"type":24,"tag":25,"props":404,"children":405},{},[406],{"type":29,"value":407},"...and you run...",{"type":24,"tag":194,"props":409,"children":411},{"code":410,"language":197,"meta":198},"nmc ~/projects -y\n",[412],{"type":24,"tag":201,"props":413,"children":414},{},[415],{"type":24,"tag":194,"props":416,"children":417},{"__ignoreMap":7},[418],{"type":24,"tag":208,"props":419,"children":420},{"class":210,"line":211},[421,425,430,434],{"type":24,"tag":208,"props":422,"children":423},{"class":215},[424],{"type":29,"value":284},{"type":24,"tag":208,"props":426,"children":427},{"class":221},[428],{"type":29,"value":429},"~/projects",{"type":24,"tag":208,"props":431,"children":432},{"class":215},[433],{"type":29,"value":229},{"type":24,"tag":208,"props":435,"children":436},{"class":232},[437],{"type":29,"value":438},"-y",{"type":24,"tag":25,"props":440,"children":441},{},[442,444,449,451,456],{"type":29,"value":443},"...only the ",{"type":24,"tag":32,"props":445,"children":446},{},[447],{"type":29,"value":448},"old-project/node_modules",{"type":29,"value":450}," will get cleaned up, and the ",{"type":24,"tag":32,"props":452,"children":453},{},[454],{"type":29,"value":455},"fresh-project/node_modules",{"type":29,"value":457}," will be left as is.",{"type":24,"tag":459,"props":460,"children":461},"blockquote",{},[462,475],{"type":24,"tag":25,"props":463,"children":464},{},[465,469,471],{"type":24,"tag":32,"props":466,"children":467},{},[468],{"type":29,"value":264},{"type":29,"value":470}," is an alias for ",{"type":24,"tag":32,"props":472,"children":473},{},[474],{"type":29,"value":86},{"type":24,"tag":25,"props":476,"children":477},{},[478,482,484,489],{"type":24,"tag":32,"props":479,"children":480},{},[481],{"type":29,"value":438},{"type":29,"value":483}," or ",{"type":24,"tag":32,"props":485,"children":486},{},[487],{"type":29,"value":488},"--yes",{"type":29,"value":490}," flag is used to run in the auto-confirm mode (we'll get to that in a second)",{"type":24,"tag":177,"props":492,"children":494},{"id":493},"running-manually",[495],{"type":29,"value":496},"Running manually",{"type":24,"tag":25,"props":498,"children":499},{},[500],{"type":29,"value":501},"In the real-world scenario, you would probably want to run the tool a couple of times in an interactive mode first, to see what targets it finds, and to customize your configuration if necessary.",{"type":24,"tag":25,"props":503,"children":504},{},[505,507,511],{"type":29,"value":506},"For that, run the tool without the ",{"type":24,"tag":32,"props":508,"children":509},{},[510],{"type":29,"value":438},{"type":29,"value":512}," flag. That way it'll display a confirmation prompt before taking any action.",{"type":24,"tag":25,"props":514,"children":515},{},[516,518,523],{"type":29,"value":517},"Or run it with the ",{"type":24,"tag":32,"props":519,"children":520},{},[521],{"type":29,"value":522},"--dry-run",{"type":29,"value":524}," flag - that way it'll only scan the system and print the targets without proceeding to the cleanup step at all.",{"type":24,"tag":177,"props":526,"children":528},{"id":527},"configuration",[529],{"type":29,"value":530},"Configuration",{"type":24,"tag":25,"props":532,"children":533},{},[534],{"type":29,"value":535},"Here are a couple of the most useful options you might want to tweak:",{"type":24,"tag":537,"props":538,"children":540},"h3",{"id":539},"t-time",[541],{"type":29,"value":542},"-t, --time",{"type":24,"tag":25,"props":544,"children":545},{},[546],{"type":29,"value":547},"Sets the threshold for how old a project should be (based on last modification time) before its directories are targeted for cleanup. Use this option when you want to clean up projects that haven’t been modified for a specific period.",{"type":24,"tag":25,"props":549,"children":550},{},[551,553,558],{"type":29,"value":552},"The threshold is set to ",{"type":24,"tag":32,"props":554,"children":555},{},[556],{"type":29,"value":557},"30",{"type":29,"value":559}," days by default.",{"type":24,"tag":537,"props":561,"children":563},{"id":562},"i-ignore",[564],{"type":29,"value":565},"-i, --ignore",{"type":24,"tag":25,"props":567,"children":568},{},[569,573,575,580],{"type":24,"tag":32,"props":570,"children":571},{},[572],{"type":29,"value":86},{"type":29,"value":574}," will never clean up the projects that were modified less than (whatever you specified) days ago. But if you have some projects you don't want to clean up ",{"type":24,"tag":40,"props":576,"children":577},{},[578],{"type":29,"value":579},"regardless of modification time",{"type":29,"value":581},", you can use this option.",{"type":24,"tag":25,"props":583,"children":584},{},[585],{"type":29,"value":586},"By default, nothing is ignored.",{"type":24,"tag":537,"props":588,"children":590},{"id":589},"n-name",[591],{"type":29,"value":592},"-n, --name",{"type":24,"tag":25,"props":594,"children":595},{},[596,598,602,604,609],{"type":29,"value":597},"Specifies a regex pattern to match directory names that should be removed. Use this option when you want to clean up other types of folders besides ",{"type":24,"tag":32,"props":599,"children":600},{},[601],{"type":29,"value":36},{"type":29,"value":603},", for example, ",{"type":24,"tag":32,"props":605,"children":606},{},[607],{"type":29,"value":608},"dist",{"type":29,"value":325},{"type":24,"tag":25,"props":611,"children":612},{},[613,615,619],{"type":29,"value":614},"By default (as you might have guessed) the name is ",{"type":24,"tag":32,"props":616,"children":617},{},[618],{"type":29,"value":36},{"type":29,"value":325},{"type":24,"tag":621,"props":622,"children":623},"hr",{},[],{"type":24,"tag":25,"props":625,"children":626},{},[627,629,636,638,643],{"type":29,"value":628},"These are not all of the options! As mentioned earlier, the tool is really customizable - but, in order to keep the things simple in this post, I'm not going to list all of the available configurations here. If you want to see what more there is to adjust, you're always welcome to check out the full list of options in the tool's ",{"type":24,"tag":79,"props":630,"children":633},{"href":631,"rel":632},"https://github.com/gVguy/nm-cleanup?tab=readme-ov-file#options",[83],[634],{"type":29,"value":635},"readme",{"type":29,"value":637}," (or with the ",{"type":24,"tag":32,"props":639,"children":640},{},[641],{"type":29,"value":642},"-h",{"type":29,"value":644}," flag).",{"type":24,"tag":459,"props":646,"children":647},{},[648],{"type":24,"tag":25,"props":649,"children":650},{},[651],{"type":29,"value":652},"Moreover, if you feel there's a use case for this tool that is not covered by currently available options, feel free to open an issue with feature request!",{"type":24,"tag":25,"props":654,"children":655},{},[656,658,663],{"type":29,"value":657},"Now that we have familiarized ourselves with the config, here's what your command ",{"type":24,"tag":40,"props":659,"children":660},{},[661],{"type":29,"value":662},"might",{"type":29,"value":664}," look like:",{"type":24,"tag":194,"props":666,"children":668},{"code":667,"language":197,"meta":198},"nmc ~/projects -t 14 -n 'node_modules|dist' -i 'my-special-project'\n",[669],{"type":24,"tag":201,"props":670,"children":671},{},[672],{"type":24,"tag":194,"props":673,"children":674},{"__ignoreMap":7},[675],{"type":24,"tag":208,"props":676,"children":677},{"class":210,"line":211},[678,682,686,690,695,699,704,708,713,717,722,726,731,735],{"type":24,"tag":208,"props":679,"children":680},{"class":215},[681],{"type":29,"value":284},{"type":24,"tag":208,"props":683,"children":684},{"class":221},[685],{"type":29,"value":429},{"type":24,"tag":208,"props":687,"children":688},{"class":215},[689],{"type":29,"value":229},{"type":24,"tag":208,"props":691,"children":692},{"class":232},[693],{"type":29,"value":694},"-t",{"type":24,"tag":208,"props":696,"children":697},{"class":215},[698],{"type":29,"value":229},{"type":24,"tag":208,"props":700,"children":701},{"class":232},[702],{"type":29,"value":703},"14",{"type":24,"tag":208,"props":705,"children":706},{"class":215},[707],{"type":29,"value":229},{"type":24,"tag":208,"props":709,"children":710},{"class":232},[711],{"type":29,"value":712},"-n",{"type":24,"tag":208,"props":714,"children":715},{"class":215},[716],{"type":29,"value":229},{"type":24,"tag":208,"props":718,"children":719},{"class":221},[720],{"type":29,"value":721},"'node_modules|dist'",{"type":24,"tag":208,"props":723,"children":724},{"class":215},[725],{"type":29,"value":229},{"type":24,"tag":208,"props":727,"children":728},{"class":232},[729],{"type":29,"value":730},"-i",{"type":24,"tag":208,"props":732,"children":733},{"class":215},[734],{"type":29,"value":229},{"type":24,"tag":208,"props":736,"children":737},{"class":221},[738],{"type":29,"value":739},"'my-special-project'",{"type":24,"tag":25,"props":741,"children":742},{},[743],{"type":29,"value":744},"Or just run it from your projects directory, without arguments if you're happy with the default values. This works too:",{"type":24,"tag":194,"props":746,"children":748},{"code":747,"language":197,"meta":198},"nmc\n",[749],{"type":24,"tag":201,"props":750,"children":751},{},[752],{"type":24,"tag":194,"props":753,"children":754},{"__ignoreMap":7},[755],{"type":24,"tag":208,"props":756,"children":757},{"class":210,"line":211},[758],{"type":24,"tag":208,"props":759,"children":760},{"class":215},[761],{"type":29,"value":264},{"type":24,"tag":177,"props":763,"children":765},{"id":764},"automation",[766],{"type":29,"value":767},"Automation",{"type":24,"tag":25,"props":769,"children":770},{},[771],{"type":29,"value":772},"After you've ran the command 1-2 times, and you're happy with how it responds to your config, the natural next step is to automate it to run, for example, once a day.",{"type":24,"tag":25,"props":774,"children":775},{},[776,778,783,785,790],{"type":29,"value":777},"That way, you can ",{"type":24,"tag":102,"props":779,"children":780},{},[781],{"type":29,"value":782},"always",{"type":29,"value":784}," be sure that the space on your system is not being occupied by useless folders in projects ",{"type":24,"tag":102,"props":786,"children":787},{},[788],{"type":29,"value":789},"older than N days",{"type":29,"value":325},{"type":24,"tag":25,"props":792,"children":793},{},[794,796,801,803,808],{"type":29,"value":795},"If you've run any of the previous commands, you've noticed that the tool ",{"type":24,"tag":40,"props":797,"children":798},{},[799],{"type":29,"value":800},"doesn't rely on user input during runtime",{"type":29,"value":802},". All the configuration is provided with options, and, after the targets are listed, the only action required from user is to answer the ",{"type":24,"tag":40,"props":804,"children":805},{},[806],{"type":29,"value":807},"\"are you sure?\"",{"type":29,"value":809}," dialog.",{"type":24,"tag":25,"props":811,"children":812},{},[813],{"type":29,"value":814},"Thanks to that design, we can rather straightforwardly skip the confirmation step and proceed directly to the cleanup right after we've locked on our targets.",{"type":24,"tag":25,"props":816,"children":817},{},[818,820,824,826,830],{"type":29,"value":819},"To skip the confirmation, simply add the ",{"type":24,"tag":32,"props":821,"children":822},{},[823],{"type":29,"value":438},{"type":29,"value":825}," (",{"type":24,"tag":32,"props":827,"children":828},{},[829],{"type":29,"value":488},{"type":29,"value":831},") flag:",{"type":24,"tag":194,"props":833,"children":834},{"code":410,"language":197,"meta":198},[835],{"type":24,"tag":201,"props":836,"children":837},{},[838],{"type":24,"tag":194,"props":839,"children":840},{"__ignoreMap":7},[841],{"type":24,"tag":208,"props":842,"children":843},{"class":210,"line":211},[844,848,852,856],{"type":24,"tag":208,"props":845,"children":846},{"class":215},[847],{"type":29,"value":284},{"type":24,"tag":208,"props":849,"children":850},{"class":221},[851],{"type":29,"value":429},{"type":24,"tag":208,"props":853,"children":854},{"class":215},[855],{"type":29,"value":229},{"type":24,"tag":208,"props":857,"children":858},{"class":232},[859],{"type":29,"value":438},{"type":24,"tag":537,"props":861,"children":863},{"id":862},"setting-up-a-cron-job",[864],{"type":29,"value":865},"Setting up a cron job",{"type":24,"tag":25,"props":867,"children":868},{},[869],{"type":29,"value":870},"And now that we understand how to run the cli in non-interactive mode, we can proceed to the final step - setting up a cron job to repeat our command, for example, every day at noon.",{"type":24,"tag":459,"props":872,"children":873},{},[874],{"type":24,"tag":25,"props":875,"children":876},{},[877,879,884],{"type":29,"value":878},"I'm going to describe how to set this up with ",{"type":24,"tag":32,"props":880,"children":881},{},[882],{"type":29,"value":883},"cron",{"type":29,"value":885}," as it's pretty much the default solution for creating scheduled jobs, and comes pre-installed on Mac and Linux. But if you're on a different system, feel free to use your job scheduler of choice. The general principle should remain the same.",{"type":24,"tag":537,"props":887,"children":889},{"id":888},"open-crontab",[890],{"type":29,"value":891},"Open crontab",{"type":24,"tag":25,"props":893,"children":894},{},[895],{"type":24,"tag":32,"props":896,"children":897},{},[898],{"type":29,"value":899},"crontab -e",{"type":24,"tag":25,"props":901,"children":902},{},[903],{"type":29,"value":904},"This will open the crontab file, where we can add our new job. By default the file opens in vim editor.",{"type":24,"tag":117,"props":906,"children":907},{},[908,913,918,1010],{"type":24,"tag":121,"props":909,"children":910},{},[911],{"type":29,"value":912},"First time using vim?",{"type":24,"tag":25,"props":914,"children":915},{},[916],{"type":29,"value":917},"No worries. I'm not going to over-complicate this post with detailed instructions on how to use vim - but here are the shortcuts that should get you through this time:",{"type":24,"tag":132,"props":919,"children":920},{},[921,931,948,953,970,986],{"type":24,"tag":136,"props":922,"children":923},{},[924,929],{"type":24,"tag":32,"props":925,"children":926},{},[927],{"type":29,"value":928},"G (shift + g)",{"type":29,"value":930}," go to the last line",{"type":24,"tag":136,"props":932,"children":933},{},[934,939,941,946],{"type":24,"tag":32,"props":935,"children":936},{},[937],{"type":29,"value":938},"A (shift + a)",{"type":29,"value":940}," activate ",{"type":24,"tag":40,"props":942,"children":943},{},[944],{"type":29,"value":945},"insert",{"type":29,"value":947}," mode at the end of this line",{"type":24,"tag":136,"props":949,"children":950},{},[951],{"type":29,"value":952},"go to the newline and type the command as usual (the command we want to add here is described below)",{"type":24,"tag":136,"props":954,"children":955},{},[956,961,963,968],{"type":24,"tag":32,"props":957,"children":958},{},[959],{"type":29,"value":960},"\u003CESC>",{"type":29,"value":962}," go back to ",{"type":24,"tag":40,"props":964,"children":965},{},[966],{"type":29,"value":967},"normal",{"type":29,"value":969}," mode",{"type":24,"tag":136,"props":971,"children":972},{},[973,978,979,984],{"type":24,"tag":32,"props":974,"children":975},{},[976],{"type":29,"value":977},":",{"type":29,"value":940},{"type":24,"tag":40,"props":980,"children":981},{},[982],{"type":29,"value":983},"command",{"type":29,"value":985}," mode (you should see the cursor jump to the lowest part of the editor window)",{"type":24,"tag":136,"props":987,"children":988},{},[989,994,996,1001,1003,1008],{"type":24,"tag":32,"props":990,"children":991},{},[992],{"type":29,"value":993},"wq\u003CENTER>",{"type":29,"value":995}," (you should see it typed in the lowest part of the editor window) ",{"type":24,"tag":102,"props":997,"children":998},{},[999],{"type":29,"value":1000},"w",{"type":29,"value":1002},"rite and ",{"type":24,"tag":102,"props":1004,"children":1005},{},[1006],{"type":29,"value":1007},"q",{"type":29,"value":1009},"uit",{"type":24,"tag":25,"props":1011,"children":1012},{},[1013],{"type":29,"value":1014},"I recommend you to further familiarize yourself with vim, as (a) it's awesome, and (b) knowing at least the basics will come handy in various situations.",{"type":24,"tag":537,"props":1016,"children":1018},{"id":1017},"add-a-new-cron-job",[1019],{"type":29,"value":1020},"Add a new cron job",{"type":24,"tag":25,"props":1022,"children":1023},{},[1024],{"type":29,"value":1025},"Crontab file lists all of your cron jobs. Let's add a new line with our command:",{"type":24,"tag":194,"props":1027,"children":1029},{"code":1028,"meta":198},"0 12 * * * nmc ~/projects/ -y\n",[1030],{"type":24,"tag":201,"props":1031,"children":1032},{},[1033],{"type":24,"tag":194,"props":1034,"children":1035},{"__ignoreMap":7},[1036],{"type":24,"tag":208,"props":1037,"children":1038},{"class":210,"line":211},[1039],{"type":24,"tag":208,"props":1040,"children":1041},{},[1042],{"type":29,"value":1043},"0 12 * * * nmc ~/projects/ -y",{"type":24,"tag":25,"props":1045,"children":1046},{},[1047,1049,1054],{"type":29,"value":1048},"Don't forget to replace the ",{"type":24,"tag":32,"props":1050,"children":1051},{},[1052],{"type":29,"value":1053},"~/projects/",{"type":29,"value":1055}," part with your projects directory (the folder containing all the project folders) - this is the entry point from where the recursive scan will start.",{"type":24,"tag":25,"props":1057,"children":1058},{},[1059,1061,1072],{"type":29,"value":1060},"Also, feel free to customize the options of the command as needed for your use-case, but make sure to ",{"type":24,"tag":102,"props":1062,"children":1063},{},[1064,1066,1070],{"type":29,"value":1065},"keep the ",{"type":24,"tag":32,"props":1067,"children":1068},{},[1069],{"type":29,"value":438},{"type":29,"value":1071}," flag",{"type":29,"value":1073}," to run in non-interactive (auto-confirm) mode.",{"type":24,"tag":117,"props":1075,"children":1076},{},[1077,1082,1087],{"type":24,"tag":121,"props":1078,"children":1079},{},[1080],{"type":29,"value":1081},"If you're not familiar with the cron format...",{"type":24,"tag":25,"props":1083,"children":1084},{},[1085],{"type":29,"value":1086},"...here's the breakdown of what that line says:",{"type":24,"tag":132,"props":1088,"children":1089},{},[1090,1100,1110,1120,1129,1138],{"type":24,"tag":136,"props":1091,"children":1092},{},[1093,1098],{"type":24,"tag":32,"props":1094,"children":1095},{},[1096],{"type":29,"value":1097},"0",{"type":29,"value":1099}," on the 0 minute",{"type":24,"tag":136,"props":1101,"children":1102},{},[1103,1108],{"type":24,"tag":32,"props":1104,"children":1105},{},[1106],{"type":29,"value":1107},"12",{"type":29,"value":1109}," of the 12 hour",{"type":24,"tag":136,"props":1111,"children":1112},{},[1113,1118],{"type":24,"tag":32,"props":1114,"children":1115},{},[1116],{"type":29,"value":1117},"*",{"type":29,"value":1119}," every day of the month",{"type":24,"tag":136,"props":1121,"children":1122},{},[1123,1127],{"type":24,"tag":32,"props":1124,"children":1125},{},[1126],{"type":29,"value":1117},{"type":29,"value":1128}," every month of the year",{"type":24,"tag":136,"props":1130,"children":1131},{},[1132,1136],{"type":24,"tag":32,"props":1133,"children":1134},{},[1135],{"type":29,"value":1117},{"type":29,"value":1137}," every day of the week",{"type":24,"tag":136,"props":1139,"children":1140},{},[1141,1146,1148,1152,1154,1158],{"type":24,"tag":32,"props":1142,"children":1143},{},[1144],{"type":29,"value":1145},"nmc ~/projects/ -y",{"type":29,"value":1147}," run the cleanup cli from ",{"type":24,"tag":32,"props":1149,"children":1150},{},[1151],{"type":29,"value":1053},{"type":29,"value":1153}," directory in non-interactive (",{"type":24,"tag":32,"props":1155,"children":1156},{},[1157],{"type":29,"value":438},{"type":29,"value":1159},") mode",{"type":24,"tag":25,"props":1161,"children":1162},{},[1163],{"type":29,"value":1164},"Save and exit. Then you can confirm the job is set with:",{"type":24,"tag":194,"props":1166,"children":1168},{"code":1167,"language":197,"meta":198},"crontab -l\n",[1169],{"type":24,"tag":201,"props":1170,"children":1171},{},[1172],{"type":24,"tag":194,"props":1173,"children":1174},{"__ignoreMap":7},[1175],{"type":24,"tag":208,"props":1176,"children":1177},{"class":210,"line":211},[1178,1183],{"type":24,"tag":208,"props":1179,"children":1180},{"class":215},[1181],{"type":29,"value":1182},"crontab ",{"type":24,"tag":208,"props":1184,"children":1185},{"class":232},[1186],{"type":29,"value":1187},"-l",{"type":24,"tag":25,"props":1189,"children":1190},{},[1191],{"type":29,"value":1192},"This should show the cron job you've just added.",{"type":24,"tag":25,"props":1194,"children":1195},{},[1196,1198,1202,1204,1208],{"type":29,"value":1197},"If you followed the instructions in this section, ",{"type":24,"tag":32,"props":1199,"children":1200},{},[1201],{"type":29,"value":883},{"type":29,"value":1203}," will automatically run the cleanup command every day at noon, keeping your projects directory free of any obsolete ",{"type":24,"tag":32,"props":1205,"children":1206},{},[1207],{"type":29,"value":36},{"type":29,"value":1209},"!",{"type":24,"tag":537,"props":1211,"children":1213},{"id":1212},"cron-job-troubleshooting",[1214],{"type":29,"value":1215},"Cron job troubleshooting",{"type":24,"tag":25,"props":1217,"children":1218},{},[1219,1221,1225,1227,1232],{"type":29,"value":1220},"Because cron runs in a very minimal environment, its default PATH is often much shorter than what you might have in your terminal. This can lead to commands (like ",{"type":24,"tag":32,"props":1222,"children":1223},{},[1224],{"type":29,"value":264},{"type":29,"value":1226},", ",{"type":24,"tag":32,"props":1228,"children":1229},{},[1230],{"type":29,"value":1231},"node",{"type":29,"value":1233}," or any other executable) not being found.",{"type":24,"tag":25,"props":1235,"children":1236},{},[1237],{"type":29,"value":1238},"If you've set up the cron job, but it doesn't seem to have any effect, you'll have to do some additional debugging. But don't worry, here are a couple of recipes that can help you sort this out:",{"type":24,"tag":1240,"props":1241,"children":1243},"h4",{"id":1242},"redirect-command-output-to-a-log-file",[1244],{"type":29,"value":1245},"Redirect command output to a log file",{"type":24,"tag":25,"props":1247,"children":1248},{},[1249],{"type":29,"value":1250},"This will let you see what output the command generates and if there are any errors. Feel free to use any file name and location that suits you.",{"type":24,"tag":194,"props":1252,"children":1254},{"code":1253,"meta":198},"0 12 * * * nmc ~/projects/ -y >> ~/desktop/nmc_cron.log 2>&1\n",[1255],{"type":24,"tag":201,"props":1256,"children":1257},{},[1258],{"type":24,"tag":194,"props":1259,"children":1260},{"__ignoreMap":7},[1261],{"type":24,"tag":208,"props":1262,"children":1263},{"class":210,"line":211},[1264],{"type":24,"tag":208,"props":1265,"children":1266},{},[1267],{"type":29,"value":1268},"0 12 * * * nmc ~/projects/ -y >> ~/desktop/nmc_cron.log 2>&1",{"type":24,"tag":1240,"props":1270,"children":1272},{"id":1271},"schedule-the-command-to-execute-more-often",[1273],{"type":29,"value":1274},"Schedule the command to execute more often",{"type":24,"tag":25,"props":1276,"children":1277},{},[1278],{"type":29,"value":1279},"For example we can adjust the job's schedule to run every fifth minute (5, 10, 15 etc) of every hour. So that we don't have to wait an entire day to see the debugging progress. Feel free to use any other time interval.",{"type":24,"tag":194,"props":1281,"children":1283},{"code":1282,"meta":198},"*/5 * * * * nmc ~/code/ -y\n",[1284],{"type":24,"tag":201,"props":1285,"children":1286},{},[1287],{"type":24,"tag":194,"props":1288,"children":1289},{"__ignoreMap":7},[1290],{"type":24,"tag":208,"props":1291,"children":1292},{"class":210,"line":211},[1293],{"type":24,"tag":208,"props":1294,"children":1295},{},[1296],{"type":29,"value":1297},"*/5 * * * * nmc ~/code/ -y",{"type":24,"tag":1240,"props":1299,"children":1301},{"id":1300},"use-full-path-to-the-command",[1302],{"type":29,"value":1303},"Use full path to the command",{"type":24,"tag":25,"props":1305,"children":1306},{},[1307],{"type":29,"value":1308},"As mentioned earlier, it's possible that nm-cleanup executable is not loaded in your cron environment. Work around that by providing full path to the nmc executable. In the terminal type:",{"type":24,"tag":194,"props":1310,"children":1312},{"code":1311,"language":197,"meta":198},"which nmc\n",[1313],{"type":24,"tag":201,"props":1314,"children":1315},{},[1316],{"type":24,"tag":194,"props":1317,"children":1318},{"__ignoreMap":7},[1319],{"type":24,"tag":208,"props":1320,"children":1321},{"class":210,"line":211},[1322,1328,1332],{"type":24,"tag":208,"props":1323,"children":1325},{"class":1324},"ct-1edf13",[1326],{"type":29,"value":1327},"which",{"type":24,"tag":208,"props":1329,"children":1330},{"class":215},[1331],{"type":29,"value":229},{"type":24,"tag":208,"props":1333,"children":1334},{"class":221},[1335],{"type":29,"value":264},{"type":24,"tag":25,"props":1337,"children":1338},{},[1339,1341,1346,1348,1352],{"type":29,"value":1340},"This should output ",{"type":24,"tag":32,"props":1342,"children":1343},{},[1344],{"type":29,"value":1345},"/full/path/to/nmc",{"type":29,"value":1347},". Copy that, and use it in the crontab in place of ",{"type":24,"tag":32,"props":1349,"children":1350},{},[1351],{"type":29,"value":264},{"type":29,"value":1353}," command:",{"type":24,"tag":194,"props":1355,"children":1357},{"code":1356,"meta":198},"0 12 * * * /full/path/to/nmc ~/code/ -y\n",[1358],{"type":24,"tag":201,"props":1359,"children":1360},{},[1361],{"type":24,"tag":194,"props":1362,"children":1363},{"__ignoreMap":7},[1364],{"type":24,"tag":208,"props":1365,"children":1366},{"class":210,"line":211},[1367],{"type":24,"tag":208,"props":1368,"children":1369},{},[1370],{"type":29,"value":1371},"0 12 * * * /full/path/to/nmc ~/code/ -y",{"type":24,"tag":1240,"props":1373,"children":1375},{"id":1374},"load-nvm-environment",[1376],{"type":29,"value":1377},"Load NVM environment",{"type":24,"tag":25,"props":1379,"children":1380},{},[1381,1386,1388,1392],{"type":24,"tag":40,"props":1382,"children":1383},{},[1384],{"type":29,"value":1385},"If you're using NVM",{"type":29,"value":1387},", it's possible that ",{"type":24,"tag":32,"props":1389,"children":1390},{},[1391],{"type":29,"value":1231},{"type":29,"value":1393}," itself is not available to the cron environment. To fix that we can manually load NVM in our cron job before executing the command.",{"type":24,"tag":194,"props":1395,"children":1397},{"code":1396,"meta":198},"0 12 * * * source $HOME/.nvm/nvm.sh && nmc ~/code/ -y\n",[1398],{"type":24,"tag":201,"props":1399,"children":1400},{},[1401],{"type":24,"tag":194,"props":1402,"children":1403},{"__ignoreMap":7},[1404],{"type":24,"tag":208,"props":1405,"children":1406},{"class":210,"line":211},[1407],{"type":24,"tag":208,"props":1408,"children":1409},{},[1410],{"type":29,"value":1411},"0 12 * * * source $HOME/.nvm/nvm.sh && nmc ~/code/ -y",{"type":24,"tag":459,"props":1413,"children":1414},{},[1415],{"type":24,"tag":25,"props":1416,"children":1417},{},[1418,1423,1425],{"type":24,"tag":32,"props":1419,"children":1420},{},[1421],{"type":29,"value":1422},"$HOME/.nvm/nvm.sh",{"type":29,"value":1424}," is the default nvm location, to make sure nvm is indeed installed there, you can run ",{"type":24,"tag":32,"props":1426,"children":1427},{},[1428],{"type":29,"value":1429},"ls -l $HOME/.nvm/nvm.sh",{"type":24,"tag":177,"props":1431,"children":1433},{"id":1432},"conclusion",[1434],{"type":29,"value":1435},"Conclusion",{"type":24,"tag":25,"props":1437,"children":1438},{},[1439,1441,1445,1447,1451,1453,1457],{"type":29,"value":1440},"Awesome! Today we've learned to use ",{"type":24,"tag":32,"props":1442,"children":1443},{},[1444],{"type":29,"value":86},{"type":29,"value":1446}," (paired with ",{"type":24,"tag":32,"props":1448,"children":1449},{},[1450],{"type":29,"value":883},{"type":29,"value":1452},") to keep our workspace clean, and free'd up some of the disk space taken up by unused ",{"type":24,"tag":32,"props":1454,"children":1455},{},[1456],{"type":29,"value":36},{"type":29,"value":1458}," directories.",{"type":24,"tag":25,"props":1460,"children":1461},{},[1462,1464,1470],{"type":29,"value":1463},"Feel free to ",{"type":24,"tag":79,"props":1465,"children":1467},{"href":81,"rel":1466},[83],[1468],{"type":29,"value":1469},"scout the tool's readme",{"type":29,"value":1471}," for more customization options, if you want to take it some steps further.",{"type":24,"tag":25,"props":1473,"children":1474},{},[1475],{"type":29,"value":1476},"Keep it clean everybody, and happy coding!",{"type":24,"tag":1478,"children":1479},"style",[1480],{"type":29,"value":1481},".ct-1edf13{color:#56B6C2}\n.ct-572368{color:#D19A66}\n.ct-d60cab{color:#98C379}\n.ct-be44c9{color:#ABB2BF}",{"title":7,"searchDepth":351,"depth":351,"links":1483},[1484,1485,1486,1487,1492,1498],{"id":179,"depth":351,"text":182},{"id":292,"depth":351,"text":295},{"id":493,"depth":351,"text":496},{"id":527,"depth":351,"text":530,"children":1488},[1489,1490,1491],{"id":539,"depth":360,"text":542},{"id":562,"depth":360,"text":565},{"id":589,"depth":360,"text":592},{"id":764,"depth":351,"text":767,"children":1493},[1494,1495,1496,1497],{"id":862,"depth":360,"text":865},{"id":888,"depth":360,"text":891},{"id":1017,"depth":360,"text":1020},{"id":1212,"depth":360,"text":1215},{"id":1432,"depth":351,"text":1435},"markdown","content:blog:nm-cleanup.md","content","blog/nm-cleanup.md","md",1760572598855]