[{"data":1,"prerenderedAt":759},["ShallowReactive",2],{"blog-pnpm":3,"blog-pnpm-favor-of":735},{"id":4,"title":5,"body":6,"cover_image":735,"date":736,"decidedDate":736,"decision":737,"decisionInFavorOf":735,"decisionReason":738,"description":739,"evaluatedScore":229,"extension":740,"link":741,"logoPath":735,"meta":742,"navigation":393,"path":743,"placements":744,"reviewTrigger":735,"satisfaction":298,"seo":748,"stage":737,"stem":749,"tags":750,"target":756,"type":757,"__hash__":758},"radar\u002Fradar\u002Fpnpm.md","pnpm",{"type":7,"value":8,"toc":720},"minimark",[9,14,41,53,56,61,73,78,88,93,121,123,127,139,144,150,169,171,175,190,235,243,249,319,350,358,363,412,414,418,435,438,503,505,509,649,651,655,665,668,716],[10,11,13],"h1",{"id":12},"pnpm-the-foundation-of-modern-node-typescript-workspaces","pnpm: The Foundation of Modern Node & TypeScript Workspaces",[15,16,17,18,26,27,33,34,40],"p",{},"In the modern TypeScript ecosystem, efficient package management is the bedrock of engineering velocity. After extensive evaluation against legacy ",[19,20,21],"strong",{},[22,23,25],"a",{"href":24},"\u002Fblog\u002Fnpm","npm"," and alternative runtimes like ",[19,28,29],{},[22,30,32],{"href":31},"\u002Fblog\u002Fbun","Bun",", ",[19,35,36,39],{},[37,38,5],"code",{}," is our firmly adopted standard"," for all Node.js and TypeScript projects and multi-package repositories.",[15,42,43,45,46,52],{},[37,44,5],{}," (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 ",[19,47,48],{},[22,49,51],{"href":50},"\u002Fblog\u002Fmonorepo","Monorepos",".",[54,55],"hr",{},[57,58,60],"h2",{"id":59},"_1-architectural-superpower-1-content-addressable-storage-cas","1. Architectural Superpower #1: Content-Addressable Storage (CAS)",[15,62,63,64,68,69,72],{},"Traditional package managers like ",[19,65,66],{},[22,67,25],{"href":24}," download and extract complete dependency trees into a local ",[37,70,71],{},"node_modules"," 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,74,75,77],{},[37,76,5],{}," completely redesigns this mechanism:",[79,80,86],"pre",{"className":81,"code":83,"language":84,"meta":85},[82],"language-text","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","text","",[37,87,83],{"__ignoreMap":85},[89,90,92],"h3",{"id":91},"the-benefits-of-hardlinks","The Benefits of Hardlinks:",[94,95,96,106,115],"ol",{},[97,98,99,102,103,52],"li",{},[19,100,101],{},"Zero Redundant Storage:"," If 50 projects on your machine use the same package, it consumes disk space ",[19,104,105],{},"exactly once",[97,107,108,111,112,114],{},[19,109,110],{},"Instant Warm Installs:"," Creating a new package or pulling a repo takes seconds because ",[37,113,5],{}," simply creates filesystem hardlinks rather than copying millions of tiny JavaScript files across the disk.",[97,116,117,120],{},[19,118,119],{},"CI Pipeline Acceleration:"," CI caches only need to persist the global store; subsequent job steps populate project trees nearly instantaneously.",[54,122],{},[57,124,126],{"id":125},"_2-architectural-superpower-2-strict-symlinks-phantom-dependency-immunity","2. Architectural Superpower #2: Strict Symlinks & Phantom Dependency Immunity",[15,128,129,130,135,136,138],{},"As detailed in our ",[19,131,132],{},[22,133,134],{"href":24},"npm evaluation report",", flat ",[37,137,71],{}," hoisting allows code to import undeclared transitive dependencies (\"phantom dependencies\"). When those sub-dependencies change or shift, builds fail unpredictably in production.",[15,140,141,143],{},[37,142,5],{}," solves this by constructing an isolated, symlinked dependency graph:",[79,145,148],{"className":146,"code":147,"language":84,"meta":85},[82],"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",[37,149,147],{"__ignoreMap":85},[151,152,153,163,166],"ul",{},[97,154,155,156,159,160,52],{},"In your code, you can ",[19,157,158],{},"only import packages explicitly listed"," in your project's ",[37,161,162],{},"package.json",[97,164,165],{},"If an engineer tries to import a transitive package without declaring it, TypeScript and Node immediately throw an import error at development time.",[97,167,168],{},"The result: zero phantom dependency drift between local development, staging, and production containers.",[54,170],{},[57,172,174],{"id":173},"_3-workspaces-monorepo-foundation","3. Workspaces & Monorepo Foundation",[15,176,177,179,180,185,186,189],{},[37,178,5],{}," is the undisputed premier package manager for ",[19,181,182],{},[22,183,184],{"href":50},"Monorepo Architecture",". It introduces a lightweight yet powerful workspace configuration via ",[37,187,188],{},"pnpm-workspace.yaml",":",[79,191,195],{"className":192,"code":193,"language":194,"meta":85,"style":85},"language-yaml shiki shiki-themes github-light github-dark","# pnpm-workspace.yaml\npackages:\n  - \"apps\u002F*\"\n  - \"packages\u002F*\"\n","yaml",[37,196,197,206,217,227],{"__ignoreMap":85},[198,199,202],"span",{"class":200,"line":201},"line",1,[198,203,205],{"class":204},"sJ8bj","# pnpm-workspace.yaml\n",[198,207,209,213],{"class":200,"line":208},2,[198,210,212],{"class":211},"s9eBZ","packages",[198,214,216],{"class":215},"sVt8B",":\n",[198,218,220,223],{"class":200,"line":219},3,[198,221,222],{"class":215},"  - ",[198,224,226],{"class":225},"sZZnC","\"apps\u002F*\"\n",[198,228,230,232],{"class":200,"line":229},4,[198,231,222],{"class":215},[198,233,234],{"class":225},"\"packages\u002F*\"\n",[89,236,238,239,242],{"id":237},"the-workspace-protocol","The ",[37,240,241],{},"workspace:*"," Protocol",[15,244,245,246,248],{},"Inside a monorepo, internal packages can declare dependencies on sibling packages using the ",[37,247,241],{}," protocol:",[79,250,254],{"className":251,"code":252,"language":253,"meta":85,"style":85},"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",[37,255,256,261,276,284,296,307,313],{"__ignoreMap":85},[198,257,258],{"class":200,"line":201},[198,259,260],{"class":215},"{\n",[198,262,263,267,270,273],{"class":200,"line":208},[198,264,266],{"class":265},"sj4cs","  \"name\"",[198,268,269],{"class":215},": ",[198,271,272],{"class":225},"\"web-frontend\"",[198,274,275],{"class":215},",\n",[198,277,278,281],{"class":200,"line":219},[198,279,280],{"class":265},"  \"dependencies\"",[198,282,283],{"class":215},": {\n",[198,285,286,289,291,294],{"class":200,"line":229},[198,287,288],{"class":265},"    \"@org\u002Fui-kit\"",[198,290,269],{"class":215},[198,292,293],{"class":225},"\"workspace:*\"",[198,295,275],{"class":215},[198,297,299,302,304],{"class":200,"line":298},5,[198,300,301],{"class":265},"    \"@org\u002Fapi-client\"",[198,303,269],{"class":215},[198,305,306],{"class":225},"\"workspace:^\"\n",[198,308,310],{"class":200,"line":309},6,[198,311,312],{"class":215},"  }\n",[198,314,316],{"class":200,"line":315},7,[198,317,318],{"class":215},"}\n",[151,320,321,336],{},[97,322,323,324,327,328,331,332,335],{},"During local development, ",[37,325,326],{},"@org\u002Fui-kit"," resolves directly to the source code in ",[37,329,330],{},"packages\u002Fui-kit",". Edits are immediately reflected in ",[37,333,334],{},"web-frontend"," without running build or republish cycles.",[97,337,338,339,342,343,345,346,349],{},"When publishing packages, ",[37,340,341],{},"pnpm publish"," automatically transforms ",[37,344,241],{}," into concrete semantic versions (e.g., ",[37,347,348],{},"\"^1.4.0\"",").",[89,351,353,354,357],{"id":352},"surgical-filtering-pnpm-filter","Surgical Filtering (",[37,355,356],{},"pnpm --filter",")",[15,359,360,362],{},[37,361,5],{}," includes native topological awareness:",[79,364,368],{"className":365,"code":366,"language":367,"meta":85,"style":85},"language-bash shiki shiki-themes github-light github-dark","# 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","bash",[37,369,370,375,389,395,400],{"__ignoreMap":85},[198,371,372],{"class":200,"line":201},[198,373,374],{"class":204},"# Run build for the web app and all its internal dependencies in topological order:\n",[198,376,377,380,383,386],{"class":200,"line":208},[198,378,5],{"class":379},"sScJk",[198,381,382],{"class":265}," --filter",[198,384,385],{"class":225}," web...",[198,387,388],{"class":225}," build\n",[198,390,391],{"class":200,"line":219},[198,392,394],{"emptyLinePlaceholder":393},true,"\n",[198,396,397],{"class":200,"line":229},[198,398,399],{"class":204},"# Run tests only on packages changed since origin\u002Fmain:\n",[198,401,402,404,406,409],{"class":200,"line":298},[198,403,5],{"class":379},[198,405,382],{"class":265},[198,407,408],{"class":225}," ...[origin\u002Fmain]",[198,410,411],{"class":225}," test\n",[54,413],{},[57,415,417],{"id":416},"_4-symbiosis-with-monorepo-build-engines","4. Symbiosis with Monorepo Build Engines",[15,419,420,421],{},"A frequent architectural question is: ",[422,423,424,425,429,430,434],"em",{},"“If we use pnpm workspaces, do we also need ",[22,426,428],{"href":427},"\u002Fblog\u002Fturborepo","Turborepo"," or ",[22,431,433],{"href":432},"\u002Fblog\u002Fmoon","Moon","?”",[15,436,437],{},"The answer depends on repository scale and language diversity:",[151,439,440,456],{},[97,441,442,445,446,429,449,451,452,455],{},[19,443,444],{},"Level 1 (Workspaces Only):"," For repositories with under 5–8 packages, ",[37,447,448],{},"pnpm -r run build",[37,450,356],{}," is completely sufficient. It links packages, generates unified lockfiles (",[37,453,454],{},"pnpm-lock.yaml","), and executes scripts.",[97,457,458,461,462,464,465],{},[19,459,460],{},"Level 2 (Orchestration Engine Added):"," As the repository scales to 15+ packages, native ",[37,463,5],{}," lacks distributed computation caching and cross-language task graphs.\n",[151,466,467,483],{},[97,468,469,470,473,474,478,479,482],{},"In ",[19,471,472],{},"pure TypeScript monorepos",", we layer ",[19,475,476],{},[22,477,428],{"href":427}," on top of ",[37,480,481],{},"pnpm workspaces"," to gain sub-millisecond remote caching.",[97,484,469,485,488,489,478,493,495,496,502],{},[19,486,487],{},"polyglot monorepos"," (mixing Python, TypeScript, and Rust), we layer ",[19,490,491],{},[22,492,433],{"href":432},[37,494,5],{}," and ",[19,497,498],{},[22,499,501],{"href":500},"\u002Fblog\u002Fuv","uv"," to coordinate unified cross-language pipelines.",[54,504],{},[57,506,508],{"id":507},"_5-comparison-pnpm-vs-npm-vs-bun","5. Comparison: pnpm vs. npm vs. Bun",[510,511,512,532],"table",{},[513,514,515],"thead",{},[516,517,518,523,526,529],"tr",{},[519,520,522],"th",{"align":521},"left","Dimension",[519,524,525],{"align":521},"pnpm (Adopted)",[519,527,528],{"align":521},"npm (Rejected)",[519,530,531],{"align":521},"Bun (Alternative)",[533,534,535,568,584,600,618,634],"tbody",{},[516,536,537,543,549,559],{},[538,539,540],"td",{"align":521},[19,541,542],{},"Status",[538,544,545,546],{"align":521},"✅ ",[19,547,548],{},"Adopted Standard",[538,550,551,552,555,556,357],{"align":521},"❌ ",[19,553,554],{},"Rejected"," (",[22,557,558],{"href":24},"Read Why",[538,560,561,562,555,565,357],{"align":521},"⚠️ ",[19,563,564],{},"Trial",[22,566,567],{"href":31},"Read Report",[516,569,570,575,578,581],{},[538,571,572],{"align":521},[19,573,574],{},"Disk Storage",[538,576,577],{"align":521},"Global Hardlink CAS",[538,579,580],{"align":521},"Redundant duplicates",[538,582,583],{"align":521},"Global module cache",[516,585,586,591,594,597],{},[538,587,588],{"align":521},[19,589,590],{},"Phantom Dependencies",[538,592,593],{"align":521},"🛡️ Strict (Immune)",[538,595,596],{"align":521},"⚠️ Vulnerable (Hoisted)",[538,598,599],{"align":521},"⚠️ Flat by default",[516,601,602,607,612,615],{},[538,603,604],{"align":521},[19,605,606],{},"Workspace Protocol",[538,608,609,610],{"align":521},"✅ First-class ",[37,611,241],{},[538,613,614],{"align":521},"❌ Limited",[538,616,617],{"align":521},"✅ Supported",[516,619,620,625,628,631],{},[538,621,622],{"align":521},[19,623,624],{},"Monorepo Ecosystem",[538,626,627],{"align":521},"Universal (Turbo\u002FNx\u002FMoon)",[538,629,630],{"align":521},"Basic",[538,632,633],{"align":521},"Developing",[516,635,636,641,644,646],{},[538,637,638],{"align":521},[19,639,640],{},"Node.js Compatibility",[538,642,643],{"align":521},"100% Native Node\u002FV8",[538,645,643],{"align":521},[538,647,648],{"align":521},"Custom JavaScriptCore runtime",[54,650],{},[57,652,654],{"id":653},"_6-radar-verdict-related-articles","6. Radar Verdict & Related Articles",[15,656,657,659,660,664],{},[37,658,5],{}," is our unequivocal recommendation and universal standard for JavaScript and TypeScript projects. It solves the operational flaws of ",[19,661,662],{},[22,663,25],{"href":24}," while serving as the reliable package foundation for modern monorepo build tools.",[15,666,667],{},"Explore the connected ecosystem:",[151,669,670,677,685,692,701,709],{},[97,671,672,676],{},[19,673,674,189],{},[22,675,528],{"href":24}," Detailed analysis of why npm's flat node_modules and hoisting were rejected.",[97,678,679,684],{},[19,680,681,189],{},[22,682,683],{"href":50},"Monorepo Architecture (Adopted)"," Complete architectural transition from workspaces to task runners.",[97,686,687,691],{},[19,688,689,189],{},[22,690,428],{"href":427}," Computation caching and task execution layered over pnpm.",[97,693,694,698,699,52],{},[19,695,696,189],{},[22,697,433],{"href":432}," Polyglot monorepo orchestrator uniting pnpm with ",[22,700,501],{"href":500},[97,702,703,708],{},[19,704,705,189],{},[22,706,707],{"href":500},"uv (Adopted)"," The Python counterpart to pnpm, providing equivalent speed and workspace ergonomics.",[97,710,711,715],{},[19,712,713,189],{},[22,714,32],{"href":31}," Fast JavaScript runtime and high-speed package manager for edge services.",[717,718,719],"style",{},"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":85,"searchDepth":208,"depth":208,"links":721},[722,725,726,732,733,734],{"id":59,"depth":208,"text":60,"children":723},[724],{"id":91,"depth":219,"text":92},{"id":125,"depth":208,"text":126},{"id":173,"depth":208,"text":174,"children":727},[728,730],{"id":237,"depth":219,"text":729},"The workspace:* Protocol",{"id":352,"depth":219,"text":731},"Surgical Filtering (pnpm --filter)",{"id":416,"depth":208,"text":417},{"id":507,"depth":208,"text":508},{"id":653,"depth":208,"text":654},null,"2026-08-08","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.","md","https:\u002F\u002Fpnpm.io\u002F",{},"\u002Fradar\u002Fpnpm",[745],{"category":746,"subCategory":747},"tools","js_tooling",{"title":5,"description":739},"radar\u002Fpnpm",[5,751,752,753,754,755],"nodejs","typescript","packaging","monorepo","workspaces","_blank","tech_report","xlWxtrrpaIUuJhiuiUjIKjWdr5x0tm8zpKpazZIiThk",1790263543149]