[{"data":1,"prerenderedAt":1445},["ShallowReactive",2],{"blog-npm":3,"blog-npm-favor-of":778},{"id":4,"title":5,"body":6,"cover_image":757,"date":758,"decidedDate":758,"decision":759,"decisionInFavorOf":38,"decisionReason":760,"description":761,"evaluatedScore":595,"extension":762,"link":757,"logoPath":757,"meta":763,"navigation":612,"path":764,"placements":765,"reviewTrigger":757,"satisfaction":757,"seo":769,"stage":770,"stem":771,"tags":772,"target":757,"type":776,"__hash__":777},"radar\u002Fradar\u002Fnpm.md","npm",{"type":7,"value":8,"toc":746},"minimark",[9,14,22,47,50,59,77,84,94,104,138,152,157,185,203,205,209,215,248,263,285,287,291,307,388,390,394,560,562,566,575,673,683,685,689,694,697,742],[10,11,13],"h1",{"id":12},"npm-why-the-default-package-manager-was-rejected","npm: Why the Default Package Manager Was Rejected",[15,16,17,18,21],"p",{},"As the default package manager distributed with Node.js, ",[19,20,5],"code",{}," is universally known and ubiquitous across the JavaScript ecosystem. It established the registry standards that power the entire web ecosystem.",[15,23,24,25,27,28,39,40,46],{},"However, as applications scale in complexity—particularly in multi-package repositories and enterprise TypeScript codebases—",[19,26,5],{},"'s architectural decisions create critical liabilities. On our Tech Radar, ",[29,30,31,33,34],"strong",{},[19,32,5],{}," is officially rejected in favor of ",[35,36,38],"a",{"href":37},"\u002Fblog\u002Fpnpm","pnpm"," (with ",[29,41,42],{},[35,43,45],{"href":44},"\u002Fblog\u002Fbun","Bun"," evaluated for dedicated high-speed standalone services).",[48,49],"hr",{},[51,52,54,55,58],"h2",{"id":53},"_1-the-core-architectural-problem-flat-node_modules-phantom-dependencies","1. The Core Architectural Problem: Flat ",[19,56,57],{},"node_modules"," & Phantom Dependencies",[15,60,61,62,65,66,69,70,72,73,76],{},"To solve the nested directory length limits historically present in Windows, ",[19,63,64],{},"npm v3"," introduced ",[29,67,68],{},"dependency hoisting",". Instead of nesting dependencies under each package, ",[19,71,5],{}," flattens dependencies into the top-level ",[19,74,75],{},"node_modules\u002F"," directory.",[15,78,79,80,83],{},"While this prevented path-length errors, it introduced a far more insidious problem: ",[29,81,82],{},"Phantom Dependencies",".",[85,86,92],"pre",{"className":87,"code":89,"language":90,"meta":91},[88],"language-text","# Flat npm node_modules structure:\nnode_modules\u002F\n├── my-package\u002F          \u003C-- Declared in your package.json\n├── transitive-dep-a\u002F    \u003C-- NOT in your package.json, but hoisted to root!\n└── transitive-dep-b\u002F    \u003C-- NOT in your package.json, but hoisted to root!\n","text","",[19,93,89],{"__ignoreMap":91},[15,95,96,97,100,101,103],{},"Because ",[19,98,99],{},"transitive-dep-a"," is hoisted into the root ",[19,102,57],{},", Node's module resolution algorithm allows your code to execute:",[85,105,109],{"className":106,"code":107,"language":108,"meta":91,"style":91},"language-typescript shiki shiki-themes github-light github-dark","import { something } from \"transitive-dep-a\"; \u002F\u002F Compiles and runs with npm!\n","typescript",[19,110,111],{"__ignoreMap":91},[112,113,116,120,124,127,131,134],"span",{"class":114,"line":115},"line",1,[112,117,119],{"class":118},"szBVR","import",[112,121,123],{"class":122},"sVt8B"," { something } ",[112,125,126],{"class":118},"from",[112,128,130],{"class":129},"sZZnC"," \"transitive-dep-a\"",[112,132,133],{"class":122},"; ",[112,135,137],{"class":136},"sJ8bj","\u002F\u002F Compiles and runs with npm!\n",[15,139,140,141,143,144,147,148,151],{},"Even though ",[19,142,99],{}," is ",[29,145,146],{},"never declared"," in your ",[19,149,150],{},"package.json","!",[153,154,156],"h3",{"id":155},"why-phantom-dependencies-cause-production-outages","Why Phantom Dependencies Cause Production Outages",[158,159,160,170,176,182],"ol",{},[161,162,163,164,167,168,83],"li",{},"Tomorrow, ",[19,165,166],{},"my-package"," updates its internal implementation and removes its dependency on ",[19,169,99],{},[161,171,172,173,83],{},"You run ",[19,174,175],{},"npm update",[161,177,178,179,83],{},"Suddenly, your production build crashes with ",[19,180,181],{},"Cannot find module 'transitive-dep-a'",[161,183,184],{},"Your application was secretly relying on an invisible dependency that you did not control or pin.",[15,186,187,188,192,193,196,197,199,200,202],{},"In contrast, ",[29,189,190],{},[35,191,38],{"href":37}," uses a ",[29,194,195],{},"strict symlink layout",". Only packages explicitly declared in your ",[19,198,150],{}," are placed in the root of ",[19,201,57],{},". Phantom imports are caught immediately by the compiler and linter.",[48,204],{},[51,206,208],{"id":207},"_2-disk-space-explosion-no-content-addressable-storage","2. Disk Space Explosion: No Content-Addressable Storage",[15,210,211,212,214],{},"When you work on multiple projects or services using ",[19,213,5],{},":",[216,217,218,242],"ul",{},[161,219,220,221,223,224,227,228,231,232,223,235,237,238,241],{},"If 15 projects each depend on ",[19,222,108],{},", ",[19,225,226],{},"@types\u002Fnode",", and ",[19,229,230],{},"next"," or ",[19,233,234],{},"nuxt",[19,236,5],{}," downloads and writes ",[29,239,240],{},"15 identical physical copies"," of those dependencies to your drive.",[161,243,244,245,247],{},"A single frontend developer's machine easily accumulates 50GB–100GB of redundant ",[19,246,57],{}," junk.",[15,249,250,254,255,258,259,262],{},[29,251,252],{},[35,253,38],{"href":37}," completely eliminates this waste using a ",[29,256,257],{},"Global Content-Addressable Store (CAS)"," (",[19,260,261],{},"~\u002F.local\u002Fshare\u002Fpnpm\u002Fstore","):",[216,264,265,272,282],{},[161,266,267,268,271],{},"Every unique version of a file is stored ",[29,269,270],{},"exactly once"," on disk.",[161,273,274,275,277,278,281],{},"Project ",[19,276,57],{}," directories are populated with ",[29,279,280],{},"hard links"," pointing to the global store.",[161,283,284],{},"If 20 projects use the same Next.js version, it consumes disk space exactly once, and installing it into project #2 through #20 is nearly instantaneous.",[48,286],{},[51,288,290],{"id":289},"_3-subpar-monorepo-workspace-ergonomics","3. Subpar Monorepo & Workspace Ergonomics",[15,292,293,294,296,297,300,301,214],{},"While ",[19,295,5],{}," added basic ",[19,298,299],{},"workspaces"," support in npm v7, it lacks the rigor required for enterprise ",[29,302,303],{},[35,304,306],{"href":305},"\u002Fblog\u002Fmonorepo","Monorepos",[158,308,309,331],{},[161,310,311,318,319,321,322,324,325,231,327,330],{},[29,312,313,314,317],{},"No ",[19,315,316],{},"workspace:*"," Protocol:"," With ",[19,320,5],{},", declaring internal package dependencies often requires manual version synchronization or confusing symlink behavior. ",[35,323,38],{"href":37}," provides the explicit ",[19,326,316],{},[19,328,329],{},"workspace:^"," protocol, ensuring internal packages resolve to local source code during development and transform to exact semantic versions during publish.",[161,332,333,336,337,368,370,371,374,375,231,381,387],{},[29,334,335],{},"Missing Advanced Filtering:"," When operating in a monorepo with 25+ packages, you need to execute tasks against topological subsets:\n",[85,338,342],{"className":339,"code":340,"language":341,"meta":91,"style":91},"language-bash shiki shiki-themes github-light github-dark","# pnpm allows surgical dependency tree execution:\npnpm --filter @org\u002Fweb... test   # Runs tests for @org\u002Fweb and all its dependencies\n","bash",[19,343,344,349],{"__ignoreMap":91},[112,345,346],{"class":114,"line":115},[112,347,348],{"class":136},"# pnpm allows surgical dependency tree execution:\n",[112,350,352,355,359,362,365],{"class":114,"line":351},2,[112,353,38],{"class":354},"sScJk",[112,356,358],{"class":357},"sj4cs"," --filter",[112,360,361],{"class":129}," @org\u002Fweb...",[112,363,364],{"class":129}," test",[112,366,367],{"class":136},"   # Runs tests for @org\u002Fweb and all its dependencies\n",[19,369,5],{},"'s ",[19,372,373],{},"--workspace"," flag is flat and lacks dependency-graph awareness, requiring dedicated build orchestrators like ",[29,376,377],{},[35,378,380],{"href":379},"\u002Fblog\u002Fturborepo","Turborepo",[29,382,383],{},[35,384,386],{"href":385},"\u002Fblog\u002Fmoon","Moon"," just to filter basic package chains.",[48,389],{},[51,391,393],{"id":392},"_4-head-to-head-comparison-npm-vs-pnpm-vs-bun","4. Head-to-Head Comparison: npm vs. pnpm vs. Bun",[395,396,397,417],"table",{},[398,399,400],"thead",{},[401,402,403,408,411,414],"tr",{},[404,405,407],"th",{"align":406},"left","Feature",[404,409,410],{"align":406},"npm (Default)",[404,412,413],{"align":406},"pnpm (Adopted Standard)",[404,415,416],{"align":406},"Bun (Alternative Runtime)",[418,419,420,446,462,477,493,509,525,544],"tbody",{},[401,421,422,428,434,440],{},[423,424,425],"td",{"align":406},[29,426,427],{},"Status on Radar",[423,429,430,431],{"align":406},"❌ ",[29,432,433],{},"Rejected",[423,435,436,437],{"align":406},"✅ ",[29,438,439],{},"Adopted",[423,441,442,443],{"align":406},"⚠️ ",[29,444,445],{},"Trial",[401,447,448,453,456,459],{},[423,449,450],{"align":406},[29,451,452],{},"Node_modules Layout",[423,454,455],{"align":406},"Flat (Hoisted)",[423,457,458],{"align":406},"Strict Symlinked Structure",[423,460,461],{"align":406},"Flat \u002F Isolated binary cache",[401,463,464,468,471,474],{},[423,465,466],{"align":406},[29,467,82],{},[423,469,470],{"align":406},"⚠️ Vulnerable",[423,472,473],{"align":406},"🛡️ Immune",[423,475,476],{"align":406},"⚠️ Vulnerable (standard mode)",[401,478,479,484,487,490],{},[423,480,481],{"align":406},[29,482,483],{},"Storage Architecture",[423,485,486],{"align":406},"Redundant copies per repo",[423,488,489],{"align":406},"Global Content-Addressable Store (Hardlinks)",[423,491,492],{"align":406},"Global module cache",[401,494,495,500,503,506],{},[423,496,497],{"align":406},[29,498,499],{},"Installation Speed (Cold)",[423,501,502],{"align":406},"Baseline (Slow)",[423,504,505],{"align":406},"2x – 3x faster",[423,507,508],{"align":406},"10x – 20x faster",[401,510,511,516,519,522],{},[423,512,513],{"align":406},[29,514,515],{},"Installation Speed (Warm \u002F Cached)",[423,517,518],{"align":406},"Slow",[423,520,521],{"align":406},"Sub-second (Hardlink linking)",[423,523,524],{"align":406},"Instantaneous",[401,526,527,535,538,541],{},[423,528,529],{"align":406},[29,530,531,532,534],{},"Workspace Protocol (",[19,533,316],{},")",[423,536,537],{"align":406},"❌ No",[423,539,540],{"align":406},"✅ Native first-class support",[423,542,543],{"align":406},"✅ Supported",[401,545,546,551,554,557],{},[423,547,548],{"align":406},[29,549,550],{},"Enterprise Monorepo Readiness",[423,552,553],{"align":406},"Basic",[423,555,556],{"align":406},"Industry Standard",[423,558,559],{"align":406},"Developing",[48,561],{},[51,563,565],{"id":564},"_5-migration-moving-from-npm-to-pnpm","5. Migration: Moving from npm to pnpm",[15,567,568,569,571,572,574],{},"Transitioning an existing repository from ",[19,570,5],{}," to ",[19,573,38],{}," is straightforward and deterministic:",[85,576,578],{"className":339,"code":577,"language":341,"meta":91,"style":91},"# 1. Install pnpm globally (or via corepack)\ncorepack enable\ncorepack prepare pnpm@latest --activate\n\n# 2. Import existing package-lock.json to create pnpm-lock.yaml\npnpm import\n\n# 3. Remove legacy artifacts\nrm -rf node_modules package-lock.json\n\n# 4. Install with strict symlink verification\npnpm install\n",[19,579,580,585,593,607,614,620,628,633,639,654,659,665],{"__ignoreMap":91},[112,581,582],{"class":114,"line":115},[112,583,584],{"class":136},"# 1. Install pnpm globally (or via corepack)\n",[112,586,587,590],{"class":114,"line":351},[112,588,589],{"class":354},"corepack",[112,591,592],{"class":129}," enable\n",[112,594,596,598,601,604],{"class":114,"line":595},3,[112,597,589],{"class":354},[112,599,600],{"class":129}," prepare",[112,602,603],{"class":129}," pnpm@latest",[112,605,606],{"class":357}," --activate\n",[112,608,610],{"class":114,"line":609},4,[112,611,613],{"emptyLinePlaceholder":612},true,"\n",[112,615,617],{"class":114,"line":616},5,[112,618,619],{"class":136},"# 2. Import existing package-lock.json to create pnpm-lock.yaml\n",[112,621,623,625],{"class":114,"line":622},6,[112,624,38],{"class":354},[112,626,627],{"class":129}," import\n",[112,629,631],{"class":114,"line":630},7,[112,632,613],{"emptyLinePlaceholder":612},[112,634,636],{"class":114,"line":635},8,[112,637,638],{"class":136},"# 3. Remove legacy artifacts\n",[112,640,642,645,648,651],{"class":114,"line":641},9,[112,643,644],{"class":354},"rm",[112,646,647],{"class":357}," -rf",[112,649,650],{"class":129}," node_modules",[112,652,653],{"class":129}," package-lock.json\n",[112,655,657],{"class":114,"line":656},10,[112,658,613],{"emptyLinePlaceholder":612},[112,660,662],{"class":114,"line":661},11,[112,663,664],{"class":136},"# 4. Install with strict symlink verification\n",[112,666,668,670],{"class":114,"line":667},12,[112,669,38],{"class":354},[112,671,672],{"class":129}," install\n",[15,674,675,676,679,680,682],{},"If your project relied on hidden phantom dependencies, ",[19,677,678],{},"pnpm install"," or your TypeScript build will immediately surface the missing imports, allowing you to explicitly declare them in ",[19,681,150],{}," where they belong.",[48,684],{},[51,686,688],{"id":687},"_6-radar-verdict-related-articles","6. Radar Verdict & Related Articles",[15,690,691,693],{},[19,692,5],{}," remains essential as the public registry protocol and package registry backend. However, as an active CLI client and package manager, it has been completely superseded by superior tooling.",[15,695,696],{},"Explore our connected toolchain reports:",[216,698,699,706,714,722,729],{},[161,700,701,705],{},[29,702,703,214],{},[35,704,413],{"href":37}," How pnpm anchors our TypeScript and monorepo workflows.",[161,707,708,713],{},[29,709,710,214],{},[35,711,712],{"href":44},"Bun (Trial)"," Fast JavaScript runtime and high-speed package manager for edge services.",[161,715,716,721],{},[29,717,718,214],{},[35,719,720],{"href":305},"Monorepo Architecture (Adopted)"," Scalable code organization using native package workspaces.",[161,723,724,728],{},[29,725,726,214],{},[35,727,380],{"href":379}," Task runner and computation cache built on top of pnpm workspaces.",[161,730,731,737,738,83],{},[29,732,733,214],{},[35,734,736],{"href":735},"\u002Fblog\u002Fuv","uv"," The parallel revolution occurring in the Python ecosystem, replacing ",[35,739,741],{"href":740},"\u002Fblog\u002Fpip","pip",[743,744,745],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":91,"searchDepth":351,"depth":351,"links":747},[748,752,753,754,755,756],{"id":53,"depth":351,"text":749,"children":750},"1. The Core Architectural Problem: Flat node_modules & Phantom Dependencies",[751],{"id":155,"depth":595,"text":156},{"id":207,"depth":351,"text":208},{"id":289,"depth":351,"text":290},{"id":392,"depth":351,"text":393},{"id":564,"depth":351,"text":565},{"id":687,"depth":351,"text":688},null,"2026-08-08","reject","Rejected in favor of pnpm for Node and TypeScript projects. Flat node_modules hoisting causes phantom dependencies, duplicated disk space waste across repositories, and slow CI installations without a content-addressable store.","Why npm was rejected for enterprise Node.js and TypeScript projects in favor of pnpm's content-addressable architecture and workspace isolation.","md",{},"\u002Fradar\u002Fnpm",[766],{"category":767,"subCategory":768},"tools","js_tooling",{"title":5,"description":761},"trial","radar\u002Fnpm",[5,38,773,108,774,775],"nodejs","packaging","tooling","tech_report","cPm9zN9eeXQuD-GZrO1heuJ1hLjBiYRdAnnA7FaieJE",{"id":779,"title":38,"body":780,"cover_image":757,"date":758,"decidedDate":758,"decision":1431,"decisionInFavorOf":757,"decisionReason":1432,"description":1433,"evaluatedScore":609,"extension":762,"link":1434,"logoPath":757,"meta":1435,"navigation":612,"path":1436,"placements":1437,"reviewTrigger":757,"satisfaction":616,"seo":1439,"stage":1431,"stem":1440,"tags":1441,"target":1443,"type":776,"__hash__":1444},"radar\u002Fradar\u002Fpnpm.md",{"type":7,"value":781,"toc":1416},[782,786,805,814,816,820,831,836,842,846,871,873,877,889,894,900,917,919,923,937,973,980,986,1052,1083,1090,1095,1138,1140,1144,1156,1159,1222,1224,1228,1350,1352,1354,1364,1367,1413],[10,783,785],{"id":784},"pnpm-the-foundation-of-modern-node-typescript-workspaces","pnpm: The Foundation of Modern Node & TypeScript Workspaces",[15,787,788,789,794,795,223,799,804],{},"In the modern TypeScript ecosystem, efficient package management is the bedrock of engineering velocity. After extensive evaluation against legacy ",[29,790,791],{},[35,792,5],{"href":793},"\u002Fblog\u002Fnpm"," and alternative runtimes like ",[29,796,797],{},[35,798,45],{"href":44},[29,800,801,803],{},[19,802,38],{}," is our firmly adopted standard"," for all Node.js and TypeScript projects and multi-package repositories.",[15,806,807,809,810,83],{},[19,808,38],{}," (performant npm) delivers three non-negotiable architectural advantages: a global content-addressable storage model that saves tens of gigabytes of disk space, a strict symlink topology that eliminates phantom dependencies, and an industry-leading workspace protocol designed for ",[29,811,812],{},[35,813,306],{"href":305},[48,815],{},[51,817,819],{"id":818},"_1-architectural-superpower-1-content-addressable-storage-cas","1. Architectural Superpower #1: Content-Addressable Storage (CAS)",[15,821,822,823,827,828,830],{},"Traditional package managers like ",[29,824,825],{},[35,826,5],{"href":793}," download and extract complete dependency trees into a local ",[19,829,57],{}," folder inside every project. If ten repositories or services use the same framework version, you have ten physical duplicates consuming disk I\u002FO and storage.",[15,832,833,835],{},[19,834,38],{}," completely redesigns this mechanism:",[85,837,840],{"className":838,"code":839,"language":90,"meta":91},[88],"Global Content-Addressable Store (~\u002F.local\u002Fshare\u002Fpnpm\u002Fstore)\n  │  (Contains every unique content blob hashed by SHA-512)\n  │\n  ├── [Hardlink] ──> Project A \u002F node_modules \u002F .pnpm \u002F vue@3.5 \u002F node_modules \u002F vue\n  └── [Hardlink] ──> Project B \u002F node_modules \u002F .pnpm \u002F vue@3.5 \u002F node_modules \u002F vue\n",[19,841,839],{"__ignoreMap":91},[153,843,845],{"id":844},"the-benefits-of-hardlinks","The Benefits of Hardlinks:",[158,847,848,856,865],{},[161,849,850,853,854,83],{},[29,851,852],{},"Zero Redundant Storage:"," If 50 projects on your machine use the same package, it consumes disk space ",[29,855,270],{},[161,857,858,861,862,864],{},[29,859,860],{},"Instant Warm Installs:"," Creating a new package or pulling a repo takes seconds because ",[19,863,38],{}," simply creates filesystem hardlinks rather than copying millions of tiny JavaScript files across the disk.",[161,866,867,870],{},[29,868,869],{},"CI Pipeline Acceleration:"," CI caches only need to persist the global store; subsequent job steps populate project trees nearly instantaneously.",[48,872],{},[51,874,876],{"id":875},"_2-architectural-superpower-2-strict-symlinks-phantom-dependency-immunity","2. Architectural Superpower #2: Strict Symlinks & Phantom Dependency Immunity",[15,878,879,880,885,886,888],{},"As detailed in our ",[29,881,882],{},[35,883,884],{"href":793},"npm evaluation report",", flat ",[19,887,57],{}," hoisting allows code to import undeclared transitive dependencies (\"phantom dependencies\"). When those sub-dependencies change or shift, builds fail unpredictably in production.",[15,890,891,893],{},[19,892,38],{}," solves this by constructing an isolated, symlinked dependency graph:",[85,895,898],{"className":896,"code":897,"language":90,"meta":91},[88],"node_modules\u002F\n├── my-package\u002F          \u003C-- Symlink to .pnpm\u002Fmy-package@1.0.0\u002F... (ONLY what's in package.json)\n└── .pnpm\u002F               \u003C-- Virtual store containing hardlinks to the global CAS\n    ├── my-package@1.0.0\u002Fnode_modules\u002F\n    │   ├── my-package\u002F\n    │   └── transitive-dep-a -> ..\u002F..\u002Ftransitive-dep-a@2.0.0\u002F...\n    └── transitive-dep-a@2.0.0\u002Fnode_modules\u002F\n        └── transitive-dep-a\u002F\n",[19,899,897],{"__ignoreMap":91},[216,901,902,911,914],{},[161,903,904,905,908,909,83],{},"In your code, you can ",[29,906,907],{},"only import packages explicitly listed"," in your project's ",[19,910,150],{},[161,912,913],{},"If an engineer tries to import a transitive package without declaring it, TypeScript and Node immediately throw an import error at development time.",[161,915,916],{},"The result: zero phantom dependency drift between local development, staging, and production containers.",[48,918],{},[51,920,922],{"id":921},"_3-workspaces-monorepo-foundation","3. Workspaces & Monorepo Foundation",[15,924,925,927,928,933,934,214],{},[19,926,38],{}," is the undisputed premier package manager for ",[29,929,930],{},[35,931,932],{"href":305},"Monorepo Architecture",". It introduces a lightweight yet powerful workspace configuration via ",[19,935,936],{},"pnpm-workspace.yaml",[85,938,942],{"className":939,"code":940,"language":941,"meta":91,"style":91},"language-yaml shiki shiki-themes github-light github-dark","# pnpm-workspace.yaml\npackages:\n  - \"apps\u002F*\"\n  - \"packages\u002F*\"\n","yaml",[19,943,944,949,958,966],{"__ignoreMap":91},[112,945,946],{"class":114,"line":115},[112,947,948],{"class":136},"# pnpm-workspace.yaml\n",[112,950,951,955],{"class":114,"line":351},[112,952,954],{"class":953},"s9eBZ","packages",[112,956,957],{"class":122},":\n",[112,959,960,963],{"class":114,"line":595},[112,961,962],{"class":122},"  - ",[112,964,965],{"class":129},"\"apps\u002F*\"\n",[112,967,968,970],{"class":114,"line":609},[112,969,962],{"class":122},[112,971,972],{"class":129},"\"packages\u002F*\"\n",[153,974,976,977,979],{"id":975},"the-workspace-protocol","The ",[19,978,316],{}," Protocol",[15,981,982,983,985],{},"Inside a monorepo, internal packages can declare dependencies on sibling packages using the ",[19,984,316],{}," protocol:",[85,987,991],{"className":988,"code":989,"language":990,"meta":91,"style":91},"language-json shiki shiki-themes github-light github-dark","{\n  \"name\": \"web-frontend\",\n  \"dependencies\": {\n    \"@org\u002Fui-kit\": \"workspace:*\",\n    \"@org\u002Fapi-client\": \"workspace:^\"\n  }\n}\n","json",[19,992,993,998,1012,1020,1032,1042,1047],{"__ignoreMap":91},[112,994,995],{"class":114,"line":115},[112,996,997],{"class":122},"{\n",[112,999,1000,1003,1006,1009],{"class":114,"line":351},[112,1001,1002],{"class":357},"  \"name\"",[112,1004,1005],{"class":122},": ",[112,1007,1008],{"class":129},"\"web-frontend\"",[112,1010,1011],{"class":122},",\n",[112,1013,1014,1017],{"class":114,"line":595},[112,1015,1016],{"class":357},"  \"dependencies\"",[112,1018,1019],{"class":122},": {\n",[112,1021,1022,1025,1027,1030],{"class":114,"line":609},[112,1023,1024],{"class":357},"    \"@org\u002Fui-kit\"",[112,1026,1005],{"class":122},[112,1028,1029],{"class":129},"\"workspace:*\"",[112,1031,1011],{"class":122},[112,1033,1034,1037,1039],{"class":114,"line":616},[112,1035,1036],{"class":357},"    \"@org\u002Fapi-client\"",[112,1038,1005],{"class":122},[112,1040,1041],{"class":129},"\"workspace:^\"\n",[112,1043,1044],{"class":114,"line":622},[112,1045,1046],{"class":122},"  }\n",[112,1048,1049],{"class":114,"line":630},[112,1050,1051],{"class":122},"}\n",[216,1053,1054,1069],{},[161,1055,1056,1057,1060,1061,1064,1065,1068],{},"During local development, ",[19,1058,1059],{},"@org\u002Fui-kit"," resolves directly to the source code in ",[19,1062,1063],{},"packages\u002Fui-kit",". Edits are immediately reflected in ",[19,1066,1067],{},"web-frontend"," without running build or republish cycles.",[161,1070,1071,1072,1075,1076,1078,1079,1082],{},"When publishing packages, ",[19,1073,1074],{},"pnpm publish"," automatically transforms ",[19,1077,316],{}," into concrete semantic versions (e.g., ",[19,1080,1081],{},"\"^1.4.0\"",").",[153,1084,1086,1087,534],{"id":1085},"surgical-filtering-pnpm-filter","Surgical Filtering (",[19,1088,1089],{},"pnpm --filter",[15,1091,1092,1094],{},[19,1093,38],{}," includes native topological awareness:",[85,1096,1098],{"className":339,"code":1097,"language":341,"meta":91,"style":91},"# Run build for the web app and all its internal dependencies in topological order:\npnpm --filter web... build\n\n# Run tests only on packages changed since origin\u002Fmain:\npnpm --filter ...[origin\u002Fmain] test\n",[19,1099,1100,1105,1117,1121,1126],{"__ignoreMap":91},[112,1101,1102],{"class":114,"line":115},[112,1103,1104],{"class":136},"# Run build for the web app and all its internal dependencies in topological order:\n",[112,1106,1107,1109,1111,1114],{"class":114,"line":351},[112,1108,38],{"class":354},[112,1110,358],{"class":357},[112,1112,1113],{"class":129}," web...",[112,1115,1116],{"class":129}," build\n",[112,1118,1119],{"class":114,"line":595},[112,1120,613],{"emptyLinePlaceholder":612},[112,1122,1123],{"class":114,"line":609},[112,1124,1125],{"class":136},"# Run tests only on packages changed since origin\u002Fmain:\n",[112,1127,1128,1130,1132,1135],{"class":114,"line":616},[112,1129,38],{"class":354},[112,1131,358],{"class":357},[112,1133,1134],{"class":129}," ...[origin\u002Fmain]",[112,1136,1137],{"class":129}," test\n",[48,1139],{},[51,1141,1143],{"id":1142},"_4-symbiosis-with-monorepo-build-engines","4. Symbiosis with Monorepo Build Engines",[15,1145,1146,1147],{},"A frequent architectural question is: ",[1148,1149,1150,1151,231,1153,1155],"em",{},"“If we use pnpm workspaces, do we also need ",[35,1152,380],{"href":379},[35,1154,386],{"href":385},"?”",[15,1157,1158],{},"The answer depends on repository scale and language diversity:",[216,1160,1161,1177],{},[161,1162,1163,1166,1167,231,1170,1172,1173,1176],{},[29,1164,1165],{},"Level 1 (Workspaces Only):"," For repositories with under 5–8 packages, ",[19,1168,1169],{},"pnpm -r run build",[19,1171,1089],{}," is completely sufficient. It links packages, generates unified lockfiles (",[19,1174,1175],{},"pnpm-lock.yaml","), and executes scripts.",[161,1178,1179,1182,1183,1185,1186],{},[29,1180,1181],{},"Level 2 (Orchestration Engine Added):"," As the repository scales to 15+ packages, native ",[19,1184,38],{}," lacks distributed computation caching and cross-language task graphs.\n",[216,1187,1188,1204],{},[161,1189,1190,1191,1194,1195,1199,1200,1203],{},"In ",[29,1192,1193],{},"pure TypeScript monorepos",", we layer ",[29,1196,1197],{},[35,1198,380],{"href":379}," on top of ",[19,1201,1202],{},"pnpm workspaces"," to gain sub-millisecond remote caching.",[161,1205,1190,1206,1209,1210,1199,1214,1216,1217,1221],{},[29,1207,1208],{},"polyglot monorepos"," (mixing Python, TypeScript, and Rust), we layer ",[29,1211,1212],{},[35,1213,386],{"href":385},[19,1215,38],{}," and ",[29,1218,1219],{},[35,1220,736],{"href":735}," to coordinate unified cross-language pipelines.",[48,1223],{},[51,1225,1227],{"id":1226},"_5-comparison-pnpm-vs-npm-vs-bun","5. Comparison: pnpm vs. npm vs. Bun",[395,1229,1230,1246],{},[398,1231,1232],{},[401,1233,1234,1237,1240,1243],{},[404,1235,1236],{"align":406},"Dimension",[404,1238,1239],{"align":406},"pnpm (Adopted)",[404,1241,1242],{"align":406},"npm (Rejected)",[404,1244,1245],{"align":406},"Bun (Alternative)",[418,1247,1248,1274,1289,1304,1321,1335],{},[401,1249,1250,1255,1260,1267],{},[423,1251,1252],{"align":406},[29,1253,1254],{},"Status",[423,1256,436,1257],{"align":406},[29,1258,1259],{},"Adopted Standard",[423,1261,430,1262,258,1264,534],{"align":406},[29,1263,433],{},[35,1265,1266],{"href":793},"Read Why",[423,1268,442,1269,258,1271,534],{"align":406},[29,1270,445],{},[35,1272,1273],{"href":44},"Read Report",[401,1275,1276,1281,1284,1287],{},[423,1277,1278],{"align":406},[29,1279,1280],{},"Disk Storage",[423,1282,1283],{"align":406},"Global Hardlink CAS",[423,1285,1286],{"align":406},"Redundant duplicates",[423,1288,492],{"align":406},[401,1290,1291,1295,1298,1301],{},[423,1292,1293],{"align":406},[29,1294,82],{},[423,1296,1297],{"align":406},"🛡️ Strict (Immune)",[423,1299,1300],{"align":406},"⚠️ Vulnerable (Hoisted)",[423,1302,1303],{"align":406},"⚠️ Flat by default",[401,1305,1306,1311,1316,1319],{},[423,1307,1308],{"align":406},[29,1309,1310],{},"Workspace Protocol",[423,1312,1313,1314],{"align":406},"✅ First-class ",[19,1315,316],{},[423,1317,1318],{"align":406},"❌ Limited",[423,1320,543],{"align":406},[401,1322,1323,1328,1331,1333],{},[423,1324,1325],{"align":406},[29,1326,1327],{},"Monorepo Ecosystem",[423,1329,1330],{"align":406},"Universal (Turbo\u002FNx\u002FMoon)",[423,1332,553],{"align":406},[423,1334,559],{"align":406},[401,1336,1337,1342,1345,1347],{},[423,1338,1339],{"align":406},[29,1340,1341],{},"Node.js Compatibility",[423,1343,1344],{"align":406},"100% Native Node\u002FV8",[423,1346,1344],{"align":406},[423,1348,1349],{"align":406},"Custom JavaScriptCore runtime",[48,1351],{},[51,1353,688],{"id":687},[15,1355,1356,1358,1359,1363],{},[19,1357,38],{}," is our unequivocal recommendation and universal standard for JavaScript and TypeScript projects. It solves the operational flaws of ",[29,1360,1361],{},[35,1362,5],{"href":793}," while serving as the reliable package foundation for modern monorepo build tools.",[15,1365,1366],{},"Explore the connected ecosystem:",[216,1368,1369,1376,1383,1390,1399,1407],{},[161,1370,1371,1375],{},[29,1372,1373,214],{},[35,1374,1242],{"href":793}," Detailed analysis of why npm's flat node_modules and hoisting were rejected.",[161,1377,1378,1382],{},[29,1379,1380,214],{},[35,1381,720],{"href":305}," Complete architectural transition from workspaces to task runners.",[161,1384,1385,1389],{},[29,1386,1387,214],{},[35,1388,380],{"href":379}," Computation caching and task execution layered over pnpm.",[161,1391,1392,1396,1397,83],{},[29,1393,1394,214],{},[35,1395,386],{"href":385}," Polyglot monorepo orchestrator uniting pnpm with ",[35,1398,736],{"href":735},[161,1400,1401,1406],{},[29,1402,1403,214],{},[35,1404,1405],{"href":735},"uv (Adopted)"," The Python counterpart to pnpm, providing equivalent speed and workspace ergonomics.",[161,1408,1409,713],{},[29,1410,1411,214],{},[35,1412,45],{"href":44},[743,1414,1415],{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}",{"title":91,"searchDepth":351,"depth":351,"links":1417},[1418,1421,1422,1428,1429,1430],{"id":818,"depth":351,"text":819,"children":1419},[1420],{"id":844,"depth":595,"text":845},{"id":875,"depth":351,"text":876},{"id":921,"depth":351,"text":922,"children":1423},[1424,1426],{"id":975,"depth":595,"text":1425},"The workspace:* Protocol",{"id":1085,"depth":595,"text":1427},"Surgical Filtering (pnpm --filter)",{"id":1142,"depth":351,"text":1143},{"id":1226,"depth":351,"text":1227},{"id":687,"depth":351,"text":688},"adopt","Adopted in favor of plain npm for all Node and TypeScript projects. Content-addressable hardlink store eliminates duplicated node_modules across projects, strict symlink topology prevents phantom dependencies, and first-class workspace protocol powers our monorepo architecture.","Fast, disk-efficient package manager and workspace engine adopted in favor of npm for all Node.js and TypeScript projects.","https:\u002F\u002Fpnpm.io\u002F",{},"\u002Fradar\u002Fpnpm",[1438],{"category":767,"subCategory":768},{"title":38,"description":1433},"radar\u002Fpnpm",[38,773,108,774,1442,299],"monorepo","_blank","xlWxtrrpaIUuJhiuiUjIKjWdr5x0tm8zpKpazZIiThk",1790263543144]