[{"data":1,"prerenderedAt":3440},["ShallowReactive",2],{"blog-global-hotkey-to-toggle-cmux-with-hammerspoon":3,"blog-surround-global-hotkey-to-toggle-cmux-with-hammerspoon":596},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"body":11,"_type":590,"_id":591,"_source":592,"_file":593,"_stem":594,"_extension":595},"/blog/global-hotkey-to-toggle-cmux-with-hammerspoon","blog",false,"","Global hotkey to toggle cmux with Hammerspoon","How to set up a global Ctrl+Space hotkey to summon and hide cmux, since cmux does not have a built-in global hotkey feature.","2026-03-14T10:00:00+07:00",{"type":12,"children":13,"toc":585},"root",[14,33,40,45,50,70,84,90,113,405,410,445,450,456,468,574,579],{"type":15,"tag":16,"props":17,"children":18},"element","p",{},[19,22,31],{"type":20,"value":21},"text","I recently switched to ",{"type":15,"tag":23,"props":24,"children":28},"a",{"href":25,"rel":26},"https://www.cmux.dev/",[27],"nofollow",[29],{"type":20,"value":30},"cmux",{"type":20,"value":32}," as my main terminal. It's a Ghostty-based macOS terminal built for AI coding agents, with vertical tabs, split panes, and notifications. It's great, but it's missing one feature I consider essential: a global hotkey to summon the terminal from anywhere.",{"type":15,"tag":34,"props":35,"children":37},"h2",{"id":36},"why-ctrlspace",[38],{"type":20,"value":39},"Why Ctrl+Space",{"type":15,"tag":16,"props":41,"children":42},{},[43],{"type":20,"value":44},"Most terminal apps I've used (Ghostty, Windows Terminal, iTerm2) have a built-in option to set a system-wide hotkey that toggles the terminal window. You press the key combo, the terminal appears. Press it again, it hides. This eliminates Cmd+Tab or Alt+Tab cycling.",{"type":15,"tag":16,"props":46,"children":47},{},[48],{"type":20,"value":49},"I picked Ctrl+Space specifically because:",{"type":15,"tag":51,"props":52,"children":53},"ul",{},[54,60,65],{"type":15,"tag":55,"props":56,"children":57},"li",{},[58],{"type":20,"value":59},"It's easy to hit with one hand, right on the home row area.",{"type":15,"tag":55,"props":61,"children":62},{},[63],{"type":20,"value":64},"It doesn't conflict with common app shortcuts. macOS used to use it for input source switching, but I have that disabled.",{"type":15,"tag":55,"props":66,"children":67},{},[68],{"type":20,"value":69},"It's what I used with Ghostty, Windows Terminal, and iTerm before, so the muscle memory is already there.",{"type":15,"tag":16,"props":71,"children":72},{},[73,75,82],{"type":20,"value":74},"cmux doesn't offer this setting anywhere in its preferences. There's an ",{"type":15,"tag":23,"props":76,"children":79},{"href":77,"rel":78},"https://github.com/manaflow-ai/cmux/issues/135",[27],[80],{"type":20,"value":81},"open issue",{"type":20,"value":83}," requesting customizable keybindings, but as of now it's not available. Terminal keybindings in cmux are read from your Ghostty config, and cmux-specific shortcuts are in its Settings, but neither of those covers a global summon hotkey.",{"type":15,"tag":34,"props":85,"children":87},{"id":86},"the-hammerspoon-solution",[88],{"type":20,"value":89},"The Hammerspoon solution",{"type":15,"tag":16,"props":91,"children":92},{},[93,95,102,104,111],{"type":20,"value":94},"Since I already use ",{"type":15,"tag":23,"props":96,"children":99},{"href":97,"rel":98},"https://www.hammerspoon.org/",[27],[100],{"type":20,"value":101},"Hammerspoon",{"type":20,"value":103}," for other macOS automation, adding a global hotkey for cmux is just a few lines in ",{"type":15,"tag":105,"props":106,"children":108},"code",{"className":107},[],[109],{"type":20,"value":110},"~/.hammerspoon/init.lua",{"type":20,"value":112},":",{"type":15,"tag":114,"props":115,"children":119},"pre",{"className":116,"code":117,"language":118,"meta":7,"style":7},"language-lua shiki shiki-themes one-dark-pro","hs.hotkey.bind({\"ctrl\"}, \"space\", function()\n    local app = hs.application.find(\"cmux\")\n    if app then\n        if app:isFrontmost() then\n            app:hide()\n        else\n            app:activate()\n        end\n    else\n        hs.application.launchOrFocus(\"cmux\")\n    end\nend)\n","lua",[120],{"type":15,"tag":105,"props":121,"children":122},{"__ignoreMap":7},[123,184,232,250,283,305,314,335,344,353,383,392],{"type":15,"tag":124,"props":125,"children":128},"span",{"class":126,"line":127},"line",1,[129,135,141,147,152,158,163,168,173,179],{"type":15,"tag":124,"props":130,"children":132},{"style":131},"--shiki-default:#E06C75",[133],{"type":20,"value":134},"hs",{"type":15,"tag":124,"props":136,"children":138},{"style":137},"--shiki-default:#ABB2BF",[139],{"type":20,"value":140},".hotkey.",{"type":15,"tag":124,"props":142,"children":144},{"style":143},"--shiki-default:#61AFEF",[145],{"type":20,"value":146},"bind",{"type":15,"tag":124,"props":148,"children":149},{"style":137},[150],{"type":20,"value":151},"({",{"type":15,"tag":124,"props":153,"children":155},{"style":154},"--shiki-default:#98C379",[156],{"type":20,"value":157},"\"ctrl\"",{"type":15,"tag":124,"props":159,"children":160},{"style":137},[161],{"type":20,"value":162},"}, ",{"type":15,"tag":124,"props":164,"children":165},{"style":154},[166],{"type":20,"value":167},"\"space\"",{"type":15,"tag":124,"props":169,"children":170},{"style":137},[171],{"type":20,"value":172},", ",{"type":15,"tag":124,"props":174,"children":176},{"style":175},"--shiki-default:#C678DD",[177],{"type":20,"value":178},"function",{"type":15,"tag":124,"props":180,"children":181},{"style":137},[182],{"type":20,"value":183},"()\n",{"type":15,"tag":124,"props":185,"children":187},{"class":126,"line":186},2,[188,193,198,203,207,212,217,222,227],{"type":15,"tag":124,"props":189,"children":190},{"style":175},[191],{"type":20,"value":192},"    local",{"type":15,"tag":124,"props":194,"children":195},{"style":131},[196],{"type":20,"value":197}," app",{"type":15,"tag":124,"props":199,"children":200},{"style":137},[201],{"type":20,"value":202}," = ",{"type":15,"tag":124,"props":204,"children":205},{"style":131},[206],{"type":20,"value":134},{"type":15,"tag":124,"props":208,"children":209},{"style":137},[210],{"type":20,"value":211},".application.",{"type":15,"tag":124,"props":213,"children":214},{"style":143},[215],{"type":20,"value":216},"find",{"type":15,"tag":124,"props":218,"children":219},{"style":137},[220],{"type":20,"value":221},"(",{"type":15,"tag":124,"props":223,"children":224},{"style":154},[225],{"type":20,"value":226},"\"cmux\"",{"type":15,"tag":124,"props":228,"children":229},{"style":137},[230],{"type":20,"value":231},")\n",{"type":15,"tag":124,"props":233,"children":235},{"class":126,"line":234},3,[236,241,245],{"type":15,"tag":124,"props":237,"children":238},{"style":175},[239],{"type":20,"value":240},"    if",{"type":15,"tag":124,"props":242,"children":243},{"style":131},[244],{"type":20,"value":197},{"type":15,"tag":124,"props":246,"children":247},{"style":175},[248],{"type":20,"value":249}," then\n",{"type":15,"tag":124,"props":251,"children":253},{"class":126,"line":252},4,[254,259,264,268,273,278],{"type":15,"tag":124,"props":255,"children":256},{"style":175},[257],{"type":20,"value":258},"        if",{"type":15,"tag":124,"props":260,"children":262},{"style":261},"--shiki-default:#E5C07B",[263],{"type":20,"value":197},{"type":15,"tag":124,"props":265,"children":266},{"style":137},[267],{"type":20,"value":112},{"type":15,"tag":124,"props":269,"children":270},{"style":143},[271],{"type":20,"value":272},"isFrontmost",{"type":15,"tag":124,"props":274,"children":275},{"style":137},[276],{"type":20,"value":277},"() ",{"type":15,"tag":124,"props":279,"children":280},{"style":175},[281],{"type":20,"value":282},"then\n",{"type":15,"tag":124,"props":284,"children":286},{"class":126,"line":285},5,[287,292,296,301],{"type":15,"tag":124,"props":288,"children":289},{"style":261},[290],{"type":20,"value":291},"            app",{"type":15,"tag":124,"props":293,"children":294},{"style":137},[295],{"type":20,"value":112},{"type":15,"tag":124,"props":297,"children":298},{"style":143},[299],{"type":20,"value":300},"hide",{"type":15,"tag":124,"props":302,"children":303},{"style":137},[304],{"type":20,"value":183},{"type":15,"tag":124,"props":306,"children":308},{"class":126,"line":307},6,[309],{"type":15,"tag":124,"props":310,"children":311},{"style":175},[312],{"type":20,"value":313},"        else\n",{"type":15,"tag":124,"props":315,"children":317},{"class":126,"line":316},7,[318,322,326,331],{"type":15,"tag":124,"props":319,"children":320},{"style":261},[321],{"type":20,"value":291},{"type":15,"tag":124,"props":323,"children":324},{"style":137},[325],{"type":20,"value":112},{"type":15,"tag":124,"props":327,"children":328},{"style":143},[329],{"type":20,"value":330},"activate",{"type":15,"tag":124,"props":332,"children":333},{"style":137},[334],{"type":20,"value":183},{"type":15,"tag":124,"props":336,"children":338},{"class":126,"line":337},8,[339],{"type":15,"tag":124,"props":340,"children":341},{"style":175},[342],{"type":20,"value":343},"        end\n",{"type":15,"tag":124,"props":345,"children":347},{"class":126,"line":346},9,[348],{"type":15,"tag":124,"props":349,"children":350},{"style":175},[351],{"type":20,"value":352},"    else\n",{"type":15,"tag":124,"props":354,"children":356},{"class":126,"line":355},10,[357,362,366,371,375,379],{"type":15,"tag":124,"props":358,"children":359},{"style":131},[360],{"type":20,"value":361},"        hs",{"type":15,"tag":124,"props":363,"children":364},{"style":137},[365],{"type":20,"value":211},{"type":15,"tag":124,"props":367,"children":368},{"style":143},[369],{"type":20,"value":370},"launchOrFocus",{"type":15,"tag":124,"props":372,"children":373},{"style":137},[374],{"type":20,"value":221},{"type":15,"tag":124,"props":376,"children":377},{"style":154},[378],{"type":20,"value":226},{"type":15,"tag":124,"props":380,"children":381},{"style":137},[382],{"type":20,"value":231},{"type":15,"tag":124,"props":384,"children":386},{"class":126,"line":385},11,[387],{"type":15,"tag":124,"props":388,"children":389},{"style":175},[390],{"type":20,"value":391},"    end\n",{"type":15,"tag":124,"props":393,"children":395},{"class":126,"line":394},12,[396,401],{"type":15,"tag":124,"props":397,"children":398},{"style":175},[399],{"type":20,"value":400},"end",{"type":15,"tag":124,"props":402,"children":403},{"style":137},[404],{"type":20,"value":231},{"type":15,"tag":16,"props":406,"children":407},{},[408],{"type":20,"value":409},"This does three things depending on the current state:",{"type":15,"tag":411,"props":412,"children":413},"ol",{},[414,425,435],{"type":15,"tag":55,"props":415,"children":416},{},[417,423],{"type":15,"tag":418,"props":419,"children":420},"strong",{},[421],{"type":20,"value":422},"cmux is focused",{"type":20,"value":424}," - hides it, sending you back to whatever app you were using.",{"type":15,"tag":55,"props":426,"children":427},{},[428,433],{"type":15,"tag":418,"props":429,"children":430},{},[431],{"type":20,"value":432},"cmux is running but not focused",{"type":20,"value":434}," - brings it to the front.",{"type":15,"tag":55,"props":436,"children":437},{},[438,443],{"type":15,"tag":418,"props":439,"children":440},{},[441],{"type":20,"value":442},"cmux is not running",{"type":20,"value":444}," - launches it.",{"type":15,"tag":16,"props":446,"children":447},{},[448],{"type":20,"value":449},"After adding the snippet, reload Hammerspoon (click the menu bar icon and select \"Reload Config\", or set up a reload hotkey).",{"type":15,"tag":34,"props":451,"children":453},{"id":452},"works-with-any-app",[454],{"type":20,"value":455},"Works with any app",{"type":15,"tag":16,"props":457,"children":458},{},[459,461,466],{"type":20,"value":460},"There's nothing cmux-specific about this pattern. You can use the same snippet for any app by replacing ",{"type":15,"tag":105,"props":462,"children":464},{"className":463},[],[465],{"type":20,"value":226},{"type":20,"value":467}," with the app's process name. To find the exact process name, run this in the Hammerspoon console:",{"type":15,"tag":114,"props":469,"children":471},{"className":116,"code":470,"language":118,"meta":7,"style":7},"hs.fnutils.each(hs.application.runningApplications(), function(app)\n    print(app:name())\nend)\n",[472],{"type":15,"tag":105,"props":473,"children":474},{"__ignoreMap":7},[475,532,563],{"type":15,"tag":124,"props":476,"children":477},{"class":126,"line":127},[478,482,487,492,496,500,504,509,514,518,522,528],{"type":15,"tag":124,"props":479,"children":480},{"style":131},[481],{"type":20,"value":134},{"type":15,"tag":124,"props":483,"children":484},{"style":137},[485],{"type":20,"value":486},".fnutils.",{"type":15,"tag":124,"props":488,"children":489},{"style":143},[490],{"type":20,"value":491},"each",{"type":15,"tag":124,"props":493,"children":494},{"style":137},[495],{"type":20,"value":221},{"type":15,"tag":124,"props":497,"children":498},{"style":131},[499],{"type":20,"value":134},{"type":15,"tag":124,"props":501,"children":502},{"style":137},[503],{"type":20,"value":211},{"type":15,"tag":124,"props":505,"children":506},{"style":143},[507],{"type":20,"value":508},"runningApplications",{"type":15,"tag":124,"props":510,"children":511},{"style":137},[512],{"type":20,"value":513},"(), ",{"type":15,"tag":124,"props":515,"children":516},{"style":175},[517],{"type":20,"value":178},{"type":15,"tag":124,"props":519,"children":520},{"style":137},[521],{"type":20,"value":221},{"type":15,"tag":124,"props":523,"children":525},{"style":524},"--shiki-default:#ABB2BF;--shiki-default-font-style:italic",[526],{"type":20,"value":527},"app",{"type":15,"tag":124,"props":529,"children":530},{"style":137},[531],{"type":20,"value":231},{"type":15,"tag":124,"props":533,"children":534},{"class":126,"line":186},[535,541,545,549,553,558],{"type":15,"tag":124,"props":536,"children":538},{"style":537},"--shiki-default:#56B6C2",[539],{"type":20,"value":540},"    print",{"type":15,"tag":124,"props":542,"children":543},{"style":137},[544],{"type":20,"value":221},{"type":15,"tag":124,"props":546,"children":547},{"style":261},[548],{"type":20,"value":527},{"type":15,"tag":124,"props":550,"children":551},{"style":137},[552],{"type":20,"value":112},{"type":15,"tag":124,"props":554,"children":555},{"style":143},[556],{"type":20,"value":557},"name",{"type":15,"tag":124,"props":559,"children":560},{"style":137},[561],{"type":20,"value":562},"())\n",{"type":15,"tag":124,"props":564,"children":565},{"class":126,"line":234},[566,570],{"type":15,"tag":124,"props":567,"children":568},{"style":175},[569],{"type":20,"value":400},{"type":15,"tag":124,"props":571,"children":572},{"style":137},[573],{"type":20,"value":231},{"type":15,"tag":16,"props":575,"children":576},{},[577],{"type":20,"value":578},"Or check Activity Monitor.",{"type":15,"tag":580,"props":581,"children":582},"style",{},[583],{"type":20,"value":584},"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);}",{"title":7,"searchDepth":186,"depth":186,"links":586},[587,588,589],{"id":36,"depth":186,"text":39},{"id":86,"depth":186,"text":89},{"id":452,"depth":186,"text":455},"markdown","content:blog:global-hotkey-to-toggle-cmux-with-hammerspoon.md","content","blog/global-hotkey-to-toggle-cmux-with-hammerspoon.md","blog/global-hotkey-to-toggle-cmux-with-hammerspoon","md",[597,2152],{"_path":598,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":599,"description":600,"date":601,"body":602,"_type":590,"_id":2149,"_source":592,"_file":2150,"_stem":2151,"_extension":595},"/blog/cutting-powershell-startup-time-in-half","Cutting PowerShell startup time in half","How I brought pwsh startup from ~900ms down to ~440ms by caching init scripts and lazy-loading modules.","2026-04-05T16:30:00+07:00",{"type":12,"children":603,"toc":2140},[604,609,616,629,680,685,736,741,754,759,765,777,1063,1068,1135,1140,1146,1157,1446,1451,1457,1484,1718,1730,1750,1756,1768,2007,2020,2026,2074,2079,2085,2131,2136],{"type":15,"tag":16,"props":605,"children":606},{},[607],{"type":20,"value":608},"Every time I opened a new PowerShell tab in Windows Terminal, it felt sluggish. Not unbearable, but enough that I noticed the lag before my prompt appeared. I finally sat down to figure out what was eating all that time, and the fix turned out to be small changes that added up to roughly half a second saved on every single shell launch.",{"type":15,"tag":610,"props":611,"children":613},"h3",{"id":612},"measure-first-guess-later",[614],{"type":20,"value":615},"Measure first, guess later",{"type":15,"tag":16,"props":617,"children":618},{},[619,621,627],{"type":20,"value":620},"PowerShell has a built in ",{"type":15,"tag":105,"props":622,"children":624},{"className":623},[],[625],{"type":20,"value":626},"Measure-Command",{"type":20,"value":628}," cmdlet that returns how long a script block takes to run. To benchmark a cold startup (profile loading included) from a running shell:",{"type":15,"tag":114,"props":630,"children":634},{"className":631,"code":632,"language":633,"meta":7,"style":7},"language-powershell shiki shiki-themes one-dark-pro","Measure-Command { pwsh -Command 'exit' } | Select-Object TotalMilliseconds\n","powershell",[635],{"type":15,"tag":105,"props":636,"children":637},{"__ignoreMap":7},[638],{"type":15,"tag":124,"props":639,"children":640},{"class":126,"line":127},[641,645,650,655,660,665,670,675],{"type":15,"tag":124,"props":642,"children":643},{"style":537},[644],{"type":20,"value":626},{"type":15,"tag":124,"props":646,"children":647},{"style":137},[648],{"type":20,"value":649}," { pwsh ",{"type":15,"tag":124,"props":651,"children":652},{"style":537},[653],{"type":20,"value":654},"-",{"type":15,"tag":124,"props":656,"children":657},{"style":137},[658],{"type":20,"value":659},"Command ",{"type":15,"tag":124,"props":661,"children":662},{"style":154},[663],{"type":20,"value":664},"'exit'",{"type":15,"tag":124,"props":666,"children":667},{"style":137},[668],{"type":20,"value":669}," } | ",{"type":15,"tag":124,"props":671,"children":672},{"style":537},[673],{"type":20,"value":674},"Select-Object",{"type":15,"tag":124,"props":676,"children":677},{"style":137},[678],{"type":20,"value":679}," TotalMilliseconds\n",{"type":15,"tag":16,"props":681,"children":682},{},[683],{"type":20,"value":684},"And to compare against a clean startup without your profile:",{"type":15,"tag":114,"props":686,"children":688},{"className":631,"code":687,"language":633,"meta":7,"style":7},"Measure-Command { pwsh -NoProfile -Command 'exit' } | Select-Object TotalMilliseconds\n",[689],{"type":15,"tag":105,"props":690,"children":691},{"__ignoreMap":7},[692],{"type":15,"tag":124,"props":693,"children":694},{"class":126,"line":127},[695,699,703,707,712,716,720,724,728,732],{"type":15,"tag":124,"props":696,"children":697},{"style":537},[698],{"type":20,"value":626},{"type":15,"tag":124,"props":700,"children":701},{"style":137},[702],{"type":20,"value":649},{"type":15,"tag":124,"props":704,"children":705},{"style":537},[706],{"type":20,"value":654},{"type":15,"tag":124,"props":708,"children":709},{"style":137},[710],{"type":20,"value":711},"NoProfile ",{"type":15,"tag":124,"props":713,"children":714},{"style":537},[715],{"type":20,"value":654},{"type":15,"tag":124,"props":717,"children":718},{"style":137},[719],{"type":20,"value":659},{"type":15,"tag":124,"props":721,"children":722},{"style":154},[723],{"type":20,"value":664},{"type":15,"tag":124,"props":725,"children":726},{"style":137},[727],{"type":20,"value":669},{"type":15,"tag":124,"props":729,"children":730},{"style":537},[731],{"type":20,"value":674},{"type":15,"tag":124,"props":733,"children":734},{"style":137},[735],{"type":20,"value":679},{"type":15,"tag":16,"props":737,"children":738},{},[739],{"type":20,"value":740},"The difference is all profile overhead. Mine was:",{"type":15,"tag":51,"props":742,"children":743},{},[744,749],{"type":15,"tag":55,"props":745,"children":746},{},[747],{"type":20,"value":748},"With profile: ~905ms",{"type":15,"tag":55,"props":750,"children":751},{},[752],{"type":20,"value":753},"Without profile: ~225ms",{"type":15,"tag":16,"props":755,"children":756},{},[757],{"type":20,"value":758},"So my profile was adding nearly 700ms. Time to find out where.",{"type":15,"tag":610,"props":760,"children":762},{"id":761},"finding-the-slow-parts",[763],{"type":20,"value":764},"Finding the slow parts",{"type":15,"tag":16,"props":766,"children":767},{},[768,770,775],{"type":20,"value":769},"I wrapped each suspicious line in the profile with ",{"type":15,"tag":105,"props":771,"children":773},{"className":772},[],[774],{"type":20,"value":626},{"type":20,"value":776}," and ran them one at a time in a fresh no-profile pwsh:",{"type":15,"tag":114,"props":778,"children":780},{"className":631,"code":779,"language":633,"meta":7,"style":7},"Write-Host 'oh-my-posh init: ' -NoNewline\n(Measure-Command {\n    (@(& 'C:\\path\\to\\oh-my-posh.exe' init pwsh --config='C:\\path\\to\\config.json' --print) -join \"`n\") | Invoke-Expression\n}).TotalMilliseconds\n\nWrite-Host 'Terminal-Icons import: ' -NoNewline\n(Measure-Command { Import-Module -Name Terminal-Icons }).TotalMilliseconds\n\nWrite-Host 'fnm env: ' -NoNewline\n(Measure-Command { fnm env --use-on-cd --shell powershell | Out-String | Invoke-Expression }).TotalMilliseconds\n",[781],{"type":15,"tag":105,"props":782,"children":783},{"__ignoreMap":7},[784,807,823,905,913,922,942,981,988,1008],{"type":15,"tag":124,"props":785,"children":786},{"class":126,"line":127},[787,792,797,802],{"type":15,"tag":124,"props":788,"children":789},{"style":537},[790],{"type":20,"value":791},"Write-Host",{"type":15,"tag":124,"props":793,"children":794},{"style":154},[795],{"type":20,"value":796}," 'oh-my-posh init: '",{"type":15,"tag":124,"props":798,"children":799},{"style":537},[800],{"type":20,"value":801}," -",{"type":15,"tag":124,"props":803,"children":804},{"style":137},[805],{"type":20,"value":806},"NoNewline\n",{"type":15,"tag":124,"props":808,"children":809},{"class":126,"line":186},[810,814,818],{"type":15,"tag":124,"props":811,"children":812},{"style":137},[813],{"type":20,"value":221},{"type":15,"tag":124,"props":815,"children":816},{"style":537},[817],{"type":20,"value":626},{"type":15,"tag":124,"props":819,"children":820},{"style":137},[821],{"type":20,"value":822}," {\n",{"type":15,"tag":124,"props":824,"children":825},{"class":126,"line":234},[826,831,836,841,846,851,856,861,866,871,876,881,886,891,895,900],{"type":15,"tag":124,"props":827,"children":828},{"style":137},[829],{"type":20,"value":830},"    (",{"type":15,"tag":124,"props":832,"children":833},{"style":175},[834],{"type":20,"value":835},"@",{"type":15,"tag":124,"props":837,"children":838},{"style":137},[839],{"type":20,"value":840},"(& ",{"type":15,"tag":124,"props":842,"children":843},{"style":154},[844],{"type":20,"value":845},"'C:\\path\\to\\oh-my-posh.exe'",{"type":15,"tag":124,"props":847,"children":848},{"style":137},[849],{"type":20,"value":850}," init pwsh ",{"type":15,"tag":124,"props":852,"children":853},{"style":537},[854],{"type":20,"value":855},"--",{"type":15,"tag":124,"props":857,"children":858},{"style":137},[859],{"type":20,"value":860},"config",{"type":15,"tag":124,"props":862,"children":863},{"style":537},[864],{"type":20,"value":865},"=",{"type":15,"tag":124,"props":867,"children":868},{"style":154},[869],{"type":20,"value":870},"'C:\\path\\to\\config.json'",{"type":15,"tag":124,"props":872,"children":873},{"style":537},[874],{"type":20,"value":875}," --",{"type":15,"tag":124,"props":877,"children":878},{"style":137},[879],{"type":20,"value":880},"print) -join ",{"type":15,"tag":124,"props":882,"children":883},{"style":154},[884],{"type":20,"value":885},"\"",{"type":15,"tag":124,"props":887,"children":888},{"style":537},[889],{"type":20,"value":890},"`n",{"type":15,"tag":124,"props":892,"children":893},{"style":154},[894],{"type":20,"value":885},{"type":15,"tag":124,"props":896,"children":897},{"style":137},[898],{"type":20,"value":899},") | ",{"type":15,"tag":124,"props":901,"children":902},{"style":537},[903],{"type":20,"value":904},"Invoke-Expression\n",{"type":15,"tag":124,"props":906,"children":907},{"class":126,"line":252},[908],{"type":15,"tag":124,"props":909,"children":910},{"style":137},[911],{"type":20,"value":912},"}).TotalMilliseconds\n",{"type":15,"tag":124,"props":914,"children":915},{"class":126,"line":285},[916],{"type":15,"tag":124,"props":917,"children":919},{"emptyLinePlaceholder":918},true,[920],{"type":20,"value":921},"\n",{"type":15,"tag":124,"props":923,"children":924},{"class":126,"line":307},[925,929,934,938],{"type":15,"tag":124,"props":926,"children":927},{"style":537},[928],{"type":20,"value":791},{"type":15,"tag":124,"props":930,"children":931},{"style":154},[932],{"type":20,"value":933}," 'Terminal-Icons import: '",{"type":15,"tag":124,"props":935,"children":936},{"style":537},[937],{"type":20,"value":801},{"type":15,"tag":124,"props":939,"children":940},{"style":137},[941],{"type":20,"value":806},{"type":15,"tag":124,"props":943,"children":944},{"class":126,"line":316},[945,949,953,958,963,967,972,976],{"type":15,"tag":124,"props":946,"children":947},{"style":137},[948],{"type":20,"value":221},{"type":15,"tag":124,"props":950,"children":951},{"style":537},[952],{"type":20,"value":626},{"type":15,"tag":124,"props":954,"children":955},{"style":137},[956],{"type":20,"value":957}," { ",{"type":15,"tag":124,"props":959,"children":960},{"style":537},[961],{"type":20,"value":962},"Import-Module",{"type":15,"tag":124,"props":964,"children":965},{"style":537},[966],{"type":20,"value":801},{"type":15,"tag":124,"props":968,"children":969},{"style":137},[970],{"type":20,"value":971},"Name Terminal",{"type":15,"tag":124,"props":973,"children":974},{"style":537},[975],{"type":20,"value":654},{"type":15,"tag":124,"props":977,"children":978},{"style":137},[979],{"type":20,"value":980},"Icons }).TotalMilliseconds\n",{"type":15,"tag":124,"props":982,"children":983},{"class":126,"line":337},[984],{"type":15,"tag":124,"props":985,"children":986},{"emptyLinePlaceholder":918},[987],{"type":20,"value":921},{"type":15,"tag":124,"props":989,"children":990},{"class":126,"line":346},[991,995,1000,1004],{"type":15,"tag":124,"props":992,"children":993},{"style":537},[994],{"type":20,"value":791},{"type":15,"tag":124,"props":996,"children":997},{"style":154},[998],{"type":20,"value":999}," 'fnm env: '",{"type":15,"tag":124,"props":1001,"children":1002},{"style":537},[1003],{"type":20,"value":801},{"type":15,"tag":124,"props":1005,"children":1006},{"style":137},[1007],{"type":20,"value":806},{"type":15,"tag":124,"props":1009,"children":1010},{"class":126,"line":355},[1011,1015,1019,1024,1029,1034,1038,1043,1048,1053,1058],{"type":15,"tag":124,"props":1012,"children":1013},{"style":137},[1014],{"type":20,"value":221},{"type":15,"tag":124,"props":1016,"children":1017},{"style":537},[1018],{"type":20,"value":626},{"type":15,"tag":124,"props":1020,"children":1021},{"style":137},[1022],{"type":20,"value":1023}," { fnm env ",{"type":15,"tag":124,"props":1025,"children":1026},{"style":537},[1027],{"type":20,"value":1028},"--use-on-",{"type":15,"tag":124,"props":1030,"children":1031},{"style":137},[1032],{"type":20,"value":1033},"cd ",{"type":15,"tag":124,"props":1035,"children":1036},{"style":537},[1037],{"type":20,"value":855},{"type":15,"tag":124,"props":1039,"children":1040},{"style":137},[1041],{"type":20,"value":1042},"shell powershell | ",{"type":15,"tag":124,"props":1044,"children":1045},{"style":537},[1046],{"type":20,"value":1047},"Out-String",{"type":15,"tag":124,"props":1049,"children":1050},{"style":137},[1051],{"type":20,"value":1052}," | ",{"type":15,"tag":124,"props":1054,"children":1055},{"style":537},[1056],{"type":20,"value":1057},"Invoke-Expression",{"type":15,"tag":124,"props":1059,"children":1060},{"style":137},[1061],{"type":20,"value":1062}," }).TotalMilliseconds\n",{"type":15,"tag":16,"props":1064,"children":1065},{},[1066],{"type":20,"value":1067},"Results:",{"type":15,"tag":1069,"props":1070,"children":1071},"table",{},[1072,1091],{"type":15,"tag":1073,"props":1074,"children":1075},"thead",{},[1076],{"type":15,"tag":1077,"props":1078,"children":1079},"tr",{},[1080,1086],{"type":15,"tag":1081,"props":1082,"children":1083},"th",{},[1084],{"type":20,"value":1085},"Thing",{"type":15,"tag":1081,"props":1087,"children":1088},{},[1089],{"type":20,"value":1090},"Cost",{"type":15,"tag":1092,"props":1093,"children":1094},"tbody",{},[1095,1109,1122],{"type":15,"tag":1077,"props":1096,"children":1097},{},[1098,1104],{"type":15,"tag":1099,"props":1100,"children":1101},"td",{},[1102],{"type":20,"value":1103},"oh-my-posh init",{"type":15,"tag":1099,"props":1105,"children":1106},{},[1107],{"type":20,"value":1108},"~377ms",{"type":15,"tag":1077,"props":1110,"children":1111},{},[1112,1117],{"type":15,"tag":1099,"props":1113,"children":1114},{},[1115],{"type":20,"value":1116},"Terminal-Icons import",{"type":15,"tag":1099,"props":1118,"children":1119},{},[1120],{"type":20,"value":1121},"~284ms",{"type":15,"tag":1077,"props":1123,"children":1124},{},[1125,1130],{"type":15,"tag":1099,"props":1126,"children":1127},{},[1128],{"type":20,"value":1129},"fnm env",{"type":15,"tag":1099,"props":1131,"children":1132},{},[1133],{"type":20,"value":1134},"~60ms",{"type":15,"tag":16,"props":1136,"children":1137},{},[1138],{"type":20,"value":1139},"Three culprits. None of them are doing anything wrong, they just have to run every single time a shell opens.",{"type":15,"tag":610,"props":1141,"children":1143},{"id":1142},"fix-1-cache-oh-my-posh-init",[1144],{"type":20,"value":1145},"Fix 1: cache oh-my-posh init",{"type":15,"tag":16,"props":1147,"children":1148},{},[1149,1155],{"type":15,"tag":105,"props":1150,"children":1152},{"className":1151},[],[1153],{"type":20,"value":1154},"oh-my-posh init pwsh",{"type":20,"value":1156}," spawns the executable, parses your config JSON, and prints a PowerShell script that sets up the prompt. That generated script barely changes unless you edit your config or update oh-my-posh. So I cache it:",{"type":15,"tag":114,"props":1158,"children":1160},{"className":631,"code":1159,"language":633,"meta":7,"style":7},"$ompExe = 'C:\\Users\\ibnuh\\AppData\\Local\\Microsoft\\WindowsApps\\oh-my-posh.exe'\n$ompConfig = 'C:\\Users\\ibnuh\\Documents\\PowerShell\\wopian.omp.json'\n$ompCache = Join-Path $env:TEMP 'omp-init-wopian.ps1'\nif (-not (Test-Path $ompCache) -or\n    (Get-Item $ompConfig).LastWriteTime -gt (Get-Item $ompCache).LastWriteTime -or\n    (Get-Item $ompExe).LastWriteTime   -gt (Get-Item $ompCache).LastWriteTime) {\n    & $ompExe init pwsh --config=$ompConfig --print | Out-File -FilePath $ompCache -Encoding utf8\n}\n. $ompCache\n",[1161],{"type":15,"tag":105,"props":1162,"children":1163},{"__ignoreMap":7},[1164,1182,1199,1226,1268,1315,1357,1425,1433],{"type":15,"tag":124,"props":1165,"children":1166},{"class":126,"line":127},[1167,1172,1177],{"type":15,"tag":124,"props":1168,"children":1169},{"style":131},[1170],{"type":20,"value":1171},"$ompExe",{"type":15,"tag":124,"props":1173,"children":1174},{"style":537},[1175],{"type":20,"value":1176}," =",{"type":15,"tag":124,"props":1178,"children":1179},{"style":154},[1180],{"type":20,"value":1181}," 'C:\\Users\\ibnuh\\AppData\\Local\\Microsoft\\WindowsApps\\oh-my-posh.exe'\n",{"type":15,"tag":124,"props":1183,"children":1184},{"class":126,"line":186},[1185,1190,1194],{"type":15,"tag":124,"props":1186,"children":1187},{"style":131},[1188],{"type":20,"value":1189},"$ompConfig",{"type":15,"tag":124,"props":1191,"children":1192},{"style":537},[1193],{"type":20,"value":1176},{"type":15,"tag":124,"props":1195,"children":1196},{"style":154},[1197],{"type":20,"value":1198}," 'C:\\Users\\ibnuh\\Documents\\PowerShell\\wopian.omp.json'\n",{"type":15,"tag":124,"props":1200,"children":1201},{"class":126,"line":234},[1202,1207,1211,1216,1221],{"type":15,"tag":124,"props":1203,"children":1204},{"style":131},[1205],{"type":20,"value":1206},"$ompCache",{"type":15,"tag":124,"props":1208,"children":1209},{"style":537},[1210],{"type":20,"value":1176},{"type":15,"tag":124,"props":1212,"children":1213},{"style":537},[1214],{"type":20,"value":1215}," Join-Path",{"type":15,"tag":124,"props":1217,"children":1218},{"style":131},[1219],{"type":20,"value":1220}," $env:TEMP",{"type":15,"tag":124,"props":1222,"children":1223},{"style":154},[1224],{"type":20,"value":1225}," 'omp-init-wopian.ps1'\n",{"type":15,"tag":124,"props":1227,"children":1228},{"class":126,"line":252},[1229,1234,1239,1244,1248,1253,1258,1263],{"type":15,"tag":124,"props":1230,"children":1231},{"style":175},[1232],{"type":20,"value":1233},"if",{"type":15,"tag":124,"props":1235,"children":1236},{"style":137},[1237],{"type":20,"value":1238}," (",{"type":15,"tag":124,"props":1240,"children":1241},{"style":537},[1242],{"type":20,"value":1243},"-not",{"type":15,"tag":124,"props":1245,"children":1246},{"style":137},[1247],{"type":20,"value":1238},{"type":15,"tag":124,"props":1249,"children":1250},{"style":537},[1251],{"type":20,"value":1252},"Test-Path",{"type":15,"tag":124,"props":1254,"children":1255},{"style":131},[1256],{"type":20,"value":1257}," $ompCache",{"type":15,"tag":124,"props":1259,"children":1260},{"style":137},[1261],{"type":20,"value":1262},") ",{"type":15,"tag":124,"props":1264,"children":1265},{"style":537},[1266],{"type":20,"value":1267},"-or\n",{"type":15,"tag":124,"props":1269,"children":1270},{"class":126,"line":285},[1271,1275,1280,1285,1290,1295,1299,1303,1307,1311],{"type":15,"tag":124,"props":1272,"children":1273},{"style":137},[1274],{"type":20,"value":830},{"type":15,"tag":124,"props":1276,"children":1277},{"style":537},[1278],{"type":20,"value":1279},"Get-Item",{"type":15,"tag":124,"props":1281,"children":1282},{"style":131},[1283],{"type":20,"value":1284}," $ompConfig",{"type":15,"tag":124,"props":1286,"children":1287},{"style":137},[1288],{"type":20,"value":1289},").LastWriteTime ",{"type":15,"tag":124,"props":1291,"children":1292},{"style":537},[1293],{"type":20,"value":1294},"-gt",{"type":15,"tag":124,"props":1296,"children":1297},{"style":137},[1298],{"type":20,"value":1238},{"type":15,"tag":124,"props":1300,"children":1301},{"style":537},[1302],{"type":20,"value":1279},{"type":15,"tag":124,"props":1304,"children":1305},{"style":131},[1306],{"type":20,"value":1257},{"type":15,"tag":124,"props":1308,"children":1309},{"style":137},[1310],{"type":20,"value":1289},{"type":15,"tag":124,"props":1312,"children":1313},{"style":537},[1314],{"type":20,"value":1267},{"type":15,"tag":124,"props":1316,"children":1317},{"class":126,"line":307},[1318,1322,1326,1331,1336,1340,1344,1348,1352],{"type":15,"tag":124,"props":1319,"children":1320},{"style":137},[1321],{"type":20,"value":830},{"type":15,"tag":124,"props":1323,"children":1324},{"style":537},[1325],{"type":20,"value":1279},{"type":15,"tag":124,"props":1327,"children":1328},{"style":131},[1329],{"type":20,"value":1330}," $ompExe",{"type":15,"tag":124,"props":1332,"children":1333},{"style":137},[1334],{"type":20,"value":1335},").LastWriteTime   ",{"type":15,"tag":124,"props":1337,"children":1338},{"style":537},[1339],{"type":20,"value":1294},{"type":15,"tag":124,"props":1341,"children":1342},{"style":137},[1343],{"type":20,"value":1238},{"type":15,"tag":124,"props":1345,"children":1346},{"style":537},[1347],{"type":20,"value":1279},{"type":15,"tag":124,"props":1349,"children":1350},{"style":131},[1351],{"type":20,"value":1257},{"type":15,"tag":124,"props":1353,"children":1354},{"style":137},[1355],{"type":20,"value":1356},").LastWriteTime) {\n",{"type":15,"tag":124,"props":1358,"children":1359},{"class":126,"line":316},[1360,1365,1369,1373,1377,1381,1385,1389,1393,1398,1403,1407,1412,1416,1420],{"type":15,"tag":124,"props":1361,"children":1362},{"style":137},[1363],{"type":20,"value":1364},"    & ",{"type":15,"tag":124,"props":1366,"children":1367},{"style":131},[1368],{"type":20,"value":1171},{"type":15,"tag":124,"props":1370,"children":1371},{"style":137},[1372],{"type":20,"value":850},{"type":15,"tag":124,"props":1374,"children":1375},{"style":537},[1376],{"type":20,"value":855},{"type":15,"tag":124,"props":1378,"children":1379},{"style":137},[1380],{"type":20,"value":860},{"type":15,"tag":124,"props":1382,"children":1383},{"style":537},[1384],{"type":20,"value":865},{"type":15,"tag":124,"props":1386,"children":1387},{"style":131},[1388],{"type":20,"value":1189},{"type":15,"tag":124,"props":1390,"children":1391},{"style":537},[1392],{"type":20,"value":875},{"type":15,"tag":124,"props":1394,"children":1395},{"style":137},[1396],{"type":20,"value":1397},"print | ",{"type":15,"tag":124,"props":1399,"children":1400},{"style":537},[1401],{"type":20,"value":1402},"Out-File",{"type":15,"tag":124,"props":1404,"children":1405},{"style":537},[1406],{"type":20,"value":801},{"type":15,"tag":124,"props":1408,"children":1409},{"style":137},[1410],{"type":20,"value":1411},"FilePath ",{"type":15,"tag":124,"props":1413,"children":1414},{"style":131},[1415],{"type":20,"value":1206},{"type":15,"tag":124,"props":1417,"children":1418},{"style":537},[1419],{"type":20,"value":801},{"type":15,"tag":124,"props":1421,"children":1422},{"style":137},[1423],{"type":20,"value":1424},"Encoding utf8\n",{"type":15,"tag":124,"props":1426,"children":1427},{"class":126,"line":337},[1428],{"type":15,"tag":124,"props":1429,"children":1430},{"style":137},[1431],{"type":20,"value":1432},"}\n",{"type":15,"tag":124,"props":1434,"children":1435},{"class":126,"line":346},[1436,1441],{"type":15,"tag":124,"props":1437,"children":1438},{"style":137},[1439],{"type":20,"value":1440},". ",{"type":15,"tag":124,"props":1442,"children":1443},{"style":131},[1444],{"type":20,"value":1445},"$ompCache\n",{"type":15,"tag":16,"props":1447,"children":1448},{},[1449],{"type":20,"value":1450},"The cache auto-regenerates if either the config or the oh-my-posh binary is newer than the cache file. Dot-sourcing the cached script is much faster than spawning the process again.",{"type":15,"tag":610,"props":1452,"children":1454},{"id":1453},"fix-2-lazy-load-terminal-icons",[1455],{"type":20,"value":1456},"Fix 2: lazy load Terminal-Icons",{"type":15,"tag":16,"props":1458,"children":1459},{},[1460,1462,1468,1470,1475,1477,1482],{"type":20,"value":1461},"Terminal-Icons is a formatter that renders pretty icons next to files in ",{"type":15,"tag":105,"props":1463,"children":1465},{"className":1464},[],[1466],{"type":20,"value":1467},"ls",{"type":20,"value":1469}," output. You only need it when you actually run ",{"type":15,"tag":105,"props":1471,"children":1473},{"className":1472},[],[1474],{"type":20,"value":1467},{"type":20,"value":1476},". So instead of eagerly importing it at shell startup, I defer it to the first time ",{"type":15,"tag":105,"props":1478,"children":1480},{"className":1479},[],[1481],{"type":20,"value":1467},{"type":20,"value":1483}," is called:",{"type":15,"tag":114,"props":1485,"children":1487},{"className":631,"code":1486,"language":633,"meta":7,"style":7},"# Remove the built-in `ls` alias first, since aliases win over functions\nif (Get-Alias -Name ls -ErrorAction SilentlyContinue) {\n    Remove-Item Alias:ls -Force\n}\n$script:__TerminalIconsLoaded = $false\nfunction ls {\n    if (-not $script:__TerminalIconsLoaded) {\n        Import-Module -Name Terminal-Icons\n        $script:__TerminalIconsLoaded = $true\n    }\n    Get-ChildItem @args\n}\n",[1488],{"type":15,"tag":105,"props":1489,"children":1490},{"__ignoreMap":7},[1491,1500,1534,1556,1563,1591,1607,1640,1665,1690,1698,1711],{"type":15,"tag":124,"props":1492,"children":1493},{"class":126,"line":127},[1494],{"type":15,"tag":124,"props":1495,"children":1497},{"style":1496},"--shiki-default:#7F848E;--shiki-default-font-style:italic",[1498],{"type":20,"value":1499},"# Remove the built-in `ls` alias first, since aliases win over functions\n",{"type":15,"tag":124,"props":1501,"children":1502},{"class":126,"line":186},[1503,1507,1511,1516,1520,1525,1529],{"type":15,"tag":124,"props":1504,"children":1505},{"style":175},[1506],{"type":20,"value":1233},{"type":15,"tag":124,"props":1508,"children":1509},{"style":137},[1510],{"type":20,"value":1238},{"type":15,"tag":124,"props":1512,"children":1513},{"style":537},[1514],{"type":20,"value":1515},"Get-Alias",{"type":15,"tag":124,"props":1517,"children":1518},{"style":537},[1519],{"type":20,"value":801},{"type":15,"tag":124,"props":1521,"children":1522},{"style":137},[1523],{"type":20,"value":1524},"Name ls ",{"type":15,"tag":124,"props":1526,"children":1527},{"style":537},[1528],{"type":20,"value":654},{"type":15,"tag":124,"props":1530,"children":1531},{"style":137},[1532],{"type":20,"value":1533},"ErrorAction SilentlyContinue) {\n",{"type":15,"tag":124,"props":1535,"children":1536},{"class":126,"line":234},[1537,1542,1547,1551],{"type":15,"tag":124,"props":1538,"children":1539},{"style":537},[1540],{"type":20,"value":1541},"    Remove-Item",{"type":15,"tag":124,"props":1543,"children":1544},{"style":137},[1545],{"type":20,"value":1546}," Alias:ls ",{"type":15,"tag":124,"props":1548,"children":1549},{"style":537},[1550],{"type":20,"value":654},{"type":15,"tag":124,"props":1552,"children":1553},{"style":137},[1554],{"type":20,"value":1555},"Force\n",{"type":15,"tag":124,"props":1557,"children":1558},{"class":126,"line":252},[1559],{"type":15,"tag":124,"props":1560,"children":1561},{"style":137},[1562],{"type":20,"value":1432},{"type":15,"tag":124,"props":1564,"children":1565},{"class":126,"line":285},[1566,1571,1576,1581,1585],{"type":15,"tag":124,"props":1567,"children":1568},{"style":131},[1569],{"type":20,"value":1570},"$",{"type":15,"tag":124,"props":1572,"children":1573},{"style":175},[1574],{"type":20,"value":1575},"script",{"type":15,"tag":124,"props":1577,"children":1578},{"style":131},[1579],{"type":20,"value":1580},":__TerminalIconsLoaded",{"type":15,"tag":124,"props":1582,"children":1583},{"style":537},[1584],{"type":20,"value":1176},{"type":15,"tag":124,"props":1586,"children":1588},{"style":1587},"--shiki-default:#D19A66",[1589],{"type":20,"value":1590}," $false\n",{"type":15,"tag":124,"props":1592,"children":1593},{"class":126,"line":307},[1594,1598,1603],{"type":15,"tag":124,"props":1595,"children":1596},{"style":175},[1597],{"type":20,"value":178},{"type":15,"tag":124,"props":1599,"children":1600},{"style":143},[1601],{"type":20,"value":1602}," ls",{"type":15,"tag":124,"props":1604,"children":1605},{"style":137},[1606],{"type":20,"value":822},{"type":15,"tag":124,"props":1608,"children":1609},{"class":126,"line":316},[1610,1614,1618,1622,1627,1631,1635],{"type":15,"tag":124,"props":1611,"children":1612},{"style":175},[1613],{"type":20,"value":240},{"type":15,"tag":124,"props":1615,"children":1616},{"style":137},[1617],{"type":20,"value":1238},{"type":15,"tag":124,"props":1619,"children":1620},{"style":537},[1621],{"type":20,"value":1243},{"type":15,"tag":124,"props":1623,"children":1624},{"style":131},[1625],{"type":20,"value":1626}," $",{"type":15,"tag":124,"props":1628,"children":1629},{"style":175},[1630],{"type":20,"value":1575},{"type":15,"tag":124,"props":1632,"children":1633},{"style":131},[1634],{"type":20,"value":1580},{"type":15,"tag":124,"props":1636,"children":1637},{"style":137},[1638],{"type":20,"value":1639},") {\n",{"type":15,"tag":124,"props":1641,"children":1642},{"class":126,"line":337},[1643,1648,1652,1656,1660],{"type":15,"tag":124,"props":1644,"children":1645},{"style":537},[1646],{"type":20,"value":1647},"        Import-Module",{"type":15,"tag":124,"props":1649,"children":1650},{"style":537},[1651],{"type":20,"value":801},{"type":15,"tag":124,"props":1653,"children":1654},{"style":137},[1655],{"type":20,"value":971},{"type":15,"tag":124,"props":1657,"children":1658},{"style":537},[1659],{"type":20,"value":654},{"type":15,"tag":124,"props":1661,"children":1662},{"style":137},[1663],{"type":20,"value":1664},"Icons\n",{"type":15,"tag":124,"props":1666,"children":1667},{"class":126,"line":346},[1668,1673,1677,1681,1685],{"type":15,"tag":124,"props":1669,"children":1670},{"style":131},[1671],{"type":20,"value":1672},"        $",{"type":15,"tag":124,"props":1674,"children":1675},{"style":175},[1676],{"type":20,"value":1575},{"type":15,"tag":124,"props":1678,"children":1679},{"style":131},[1680],{"type":20,"value":1580},{"type":15,"tag":124,"props":1682,"children":1683},{"style":537},[1684],{"type":20,"value":1176},{"type":15,"tag":124,"props":1686,"children":1687},{"style":1587},[1688],{"type":20,"value":1689}," $true\n",{"type":15,"tag":124,"props":1691,"children":1692},{"class":126,"line":355},[1693],{"type":15,"tag":124,"props":1694,"children":1695},{"style":137},[1696],{"type":20,"value":1697},"    }\n",{"type":15,"tag":124,"props":1699,"children":1700},{"class":126,"line":385},[1701,1706],{"type":15,"tag":124,"props":1702,"children":1703},{"style":537},[1704],{"type":20,"value":1705},"    Get-ChildItem",{"type":15,"tag":124,"props":1707,"children":1708},{"style":131},[1709],{"type":20,"value":1710}," @args\n",{"type":15,"tag":124,"props":1712,"children":1713},{"class":126,"line":394},[1714],{"type":15,"tag":124,"props":1715,"children":1716},{"style":137},[1717],{"type":20,"value":1432},{"type":15,"tag":16,"props":1719,"children":1720},{},[1721,1723,1728],{"type":20,"value":1722},"The first ",{"type":15,"tag":105,"props":1724,"children":1726},{"className":1725},[],[1727],{"type":20,"value":1467},{"type":20,"value":1729}," of the session pays the ~280ms import cost. Every subsequent call is instant, and shell startup doesn't pay it at all.",{"type":15,"tag":16,"props":1731,"children":1732},{},[1733,1735,1740,1742,1748],{"type":20,"value":1734},"One thing that bit me: in PowerShell, aliases take precedence over functions during command resolution. ",{"type":15,"tag":105,"props":1736,"children":1738},{"className":1737},[],[1739],{"type":20,"value":1467},{"type":20,"value":1741}," is a built in alias pointing to ",{"type":15,"tag":105,"props":1743,"children":1745},{"className":1744},[],[1746],{"type":20,"value":1747},"Get-ChildItem",{"type":20,"value":1749},", so my function was never called until I removed the alias.",{"type":15,"tag":610,"props":1751,"children":1753},{"id":1752},"fix-3-cache-fnm-env",[1754],{"type":20,"value":1755},"Fix 3: cache fnm env",{"type":15,"tag":16,"props":1757,"children":1758},{},[1759,1761,1766],{"type":20,"value":1760},"Same trick as oh-my-posh. ",{"type":15,"tag":105,"props":1762,"children":1764},{"className":1763},[],[1765],{"type":20,"value":1129},{"type":20,"value":1767}," prints shell code that sets up node version management hooks. Cache it, regenerate only when fnm is updated:",{"type":15,"tag":114,"props":1769,"children":1771},{"className":631,"code":1770,"language":633,"meta":7,"style":7},"$fnmExe = (Get-Command fnm -ErrorAction SilentlyContinue).Source\nif ($fnmExe) {\n    $fnmCache = Join-Path $env:TEMP 'fnm-env.ps1'\n    if (-not (Test-Path $fnmCache) -or\n        (Get-Item $fnmExe).LastWriteTime -gt (Get-Item $fnmCache).LastWriteTime) {\n        fnm env --use-on-cd --shell powershell | Out-File -FilePath $fnmCache -Encoding utf8\n    }\n    . $fnmCache\n}\n",[1772],{"type":15,"tag":105,"props":1773,"children":1774},{"__ignoreMap":7},[1775,1810,1829,1854,1890,1931,1980,1987,2000],{"type":15,"tag":124,"props":1776,"children":1777},{"class":126,"line":127},[1778,1783,1787,1791,1796,1801,1805],{"type":15,"tag":124,"props":1779,"children":1780},{"style":131},[1781],{"type":20,"value":1782},"$fnmExe",{"type":15,"tag":124,"props":1784,"children":1785},{"style":537},[1786],{"type":20,"value":1176},{"type":15,"tag":124,"props":1788,"children":1789},{"style":137},[1790],{"type":20,"value":1238},{"type":15,"tag":124,"props":1792,"children":1793},{"style":537},[1794],{"type":20,"value":1795},"Get-Command",{"type":15,"tag":124,"props":1797,"children":1798},{"style":137},[1799],{"type":20,"value":1800}," fnm ",{"type":15,"tag":124,"props":1802,"children":1803},{"style":537},[1804],{"type":20,"value":654},{"type":15,"tag":124,"props":1806,"children":1807},{"style":137},[1808],{"type":20,"value":1809},"ErrorAction SilentlyContinue).Source\n",{"type":15,"tag":124,"props":1811,"children":1812},{"class":126,"line":186},[1813,1817,1821,1825],{"type":15,"tag":124,"props":1814,"children":1815},{"style":175},[1816],{"type":20,"value":1233},{"type":15,"tag":124,"props":1818,"children":1819},{"style":137},[1820],{"type":20,"value":1238},{"type":15,"tag":124,"props":1822,"children":1823},{"style":131},[1824],{"type":20,"value":1782},{"type":15,"tag":124,"props":1826,"children":1827},{"style":137},[1828],{"type":20,"value":1639},{"type":15,"tag":124,"props":1830,"children":1831},{"class":126,"line":234},[1832,1837,1841,1845,1849],{"type":15,"tag":124,"props":1833,"children":1834},{"style":131},[1835],{"type":20,"value":1836},"    $fnmCache",{"type":15,"tag":124,"props":1838,"children":1839},{"style":537},[1840],{"type":20,"value":1176},{"type":15,"tag":124,"props":1842,"children":1843},{"style":537},[1844],{"type":20,"value":1215},{"type":15,"tag":124,"props":1846,"children":1847},{"style":131},[1848],{"type":20,"value":1220},{"type":15,"tag":124,"props":1850,"children":1851},{"style":154},[1852],{"type":20,"value":1853}," 'fnm-env.ps1'\n",{"type":15,"tag":124,"props":1855,"children":1856},{"class":126,"line":252},[1857,1861,1865,1869,1873,1877,1882,1886],{"type":15,"tag":124,"props":1858,"children":1859},{"style":175},[1860],{"type":20,"value":240},{"type":15,"tag":124,"props":1862,"children":1863},{"style":137},[1864],{"type":20,"value":1238},{"type":15,"tag":124,"props":1866,"children":1867},{"style":537},[1868],{"type":20,"value":1243},{"type":15,"tag":124,"props":1870,"children":1871},{"style":137},[1872],{"type":20,"value":1238},{"type":15,"tag":124,"props":1874,"children":1875},{"style":537},[1876],{"type":20,"value":1252},{"type":15,"tag":124,"props":1878,"children":1879},{"style":131},[1880],{"type":20,"value":1881}," $fnmCache",{"type":15,"tag":124,"props":1883,"children":1884},{"style":137},[1885],{"type":20,"value":1262},{"type":15,"tag":124,"props":1887,"children":1888},{"style":537},[1889],{"type":20,"value":1267},{"type":15,"tag":124,"props":1891,"children":1892},{"class":126,"line":285},[1893,1898,1902,1907,1911,1915,1919,1923,1927],{"type":15,"tag":124,"props":1894,"children":1895},{"style":137},[1896],{"type":20,"value":1897},"        (",{"type":15,"tag":124,"props":1899,"children":1900},{"style":537},[1901],{"type":20,"value":1279},{"type":15,"tag":124,"props":1903,"children":1904},{"style":131},[1905],{"type":20,"value":1906}," $fnmExe",{"type":15,"tag":124,"props":1908,"children":1909},{"style":137},[1910],{"type":20,"value":1289},{"type":15,"tag":124,"props":1912,"children":1913},{"style":537},[1914],{"type":20,"value":1294},{"type":15,"tag":124,"props":1916,"children":1917},{"style":137},[1918],{"type":20,"value":1238},{"type":15,"tag":124,"props":1920,"children":1921},{"style":537},[1922],{"type":20,"value":1279},{"type":15,"tag":124,"props":1924,"children":1925},{"style":131},[1926],{"type":20,"value":1881},{"type":15,"tag":124,"props":1928,"children":1929},{"style":137},[1930],{"type":20,"value":1356},{"type":15,"tag":124,"props":1932,"children":1933},{"class":126,"line":307},[1934,1939,1943,1947,1951,1955,1959,1963,1967,1972,1976],{"type":15,"tag":124,"props":1935,"children":1936},{"style":137},[1937],{"type":20,"value":1938},"        fnm env ",{"type":15,"tag":124,"props":1940,"children":1941},{"style":537},[1942],{"type":20,"value":1028},{"type":15,"tag":124,"props":1944,"children":1945},{"style":137},[1946],{"type":20,"value":1033},{"type":15,"tag":124,"props":1948,"children":1949},{"style":537},[1950],{"type":20,"value":855},{"type":15,"tag":124,"props":1952,"children":1953},{"style":137},[1954],{"type":20,"value":1042},{"type":15,"tag":124,"props":1956,"children":1957},{"style":537},[1958],{"type":20,"value":1402},{"type":15,"tag":124,"props":1960,"children":1961},{"style":537},[1962],{"type":20,"value":801},{"type":15,"tag":124,"props":1964,"children":1965},{"style":137},[1966],{"type":20,"value":1411},{"type":15,"tag":124,"props":1968,"children":1969},{"style":131},[1970],{"type":20,"value":1971},"$fnmCache",{"type":15,"tag":124,"props":1973,"children":1974},{"style":537},[1975],{"type":20,"value":801},{"type":15,"tag":124,"props":1977,"children":1978},{"style":137},[1979],{"type":20,"value":1424},{"type":15,"tag":124,"props":1981,"children":1982},{"class":126,"line":316},[1983],{"type":15,"tag":124,"props":1984,"children":1985},{"style":137},[1986],{"type":20,"value":1697},{"type":15,"tag":124,"props":1988,"children":1989},{"class":126,"line":337},[1990,1995],{"type":15,"tag":124,"props":1991,"children":1992},{"style":137},[1993],{"type":20,"value":1994},"    . ",{"type":15,"tag":124,"props":1996,"children":1997},{"style":131},[1998],{"type":20,"value":1999},"$fnmCache\n",{"type":15,"tag":124,"props":2001,"children":2002},{"class":126,"line":346},[2003],{"type":15,"tag":124,"props":2004,"children":2005},{"style":137},[2006],{"type":20,"value":1432},{"type":15,"tag":16,"props":2008,"children":2009},{},[2010,2012,2018],{"type":20,"value":2011},"This pattern works for any tool that prints shell init code on every launch. Later I added the same thing for ",{"type":15,"tag":105,"props":2013,"children":2015},{"className":2014},[],[2016],{"type":20,"value":2017},"zoxide init powershell",{"type":20,"value":2019},".",{"type":15,"tag":610,"props":2021,"children":2023},{"id":2022},"the-results",[2024],{"type":20,"value":2025},"The results",{"type":15,"tag":1069,"props":2027,"children":2028},{},[2029,2045],{"type":15,"tag":1073,"props":2030,"children":2031},{},[2032],{"type":15,"tag":1077,"props":2033,"children":2034},{},[2035,2040],{"type":15,"tag":1081,"props":2036,"children":2037},{},[2038],{"type":20,"value":2039},"State",{"type":15,"tag":1081,"props":2041,"children":2042},{},[2043],{"type":20,"value":2044},"Startup",{"type":15,"tag":1092,"props":2046,"children":2047},{},[2048,2061],{"type":15,"tag":1077,"props":2049,"children":2050},{},[2051,2056],{"type":15,"tag":1099,"props":2052,"children":2053},{},[2054],{"type":20,"value":2055},"Original profile",{"type":15,"tag":1099,"props":2057,"children":2058},{},[2059],{"type":20,"value":2060},"~905ms",{"type":15,"tag":1077,"props":2062,"children":2063},{},[2064,2069],{"type":15,"tag":1099,"props":2065,"children":2066},{},[2067],{"type":20,"value":2068},"After all three caches",{"type":15,"tag":1099,"props":2070,"children":2071},{},[2072],{"type":20,"value":2073},"~440ms",{"type":15,"tag":16,"props":2075,"children":2076},{},[2077],{"type":20,"value":2078},"Roughly 51% faster. The remaining ~440ms is the unavoidable cost of pwsh itself loading the CLR and PSReadLine, plus parsing the cached oh-my-posh init script (which is still ~250ms even cached, since that generated script is large and sets up the prompt hooks).",{"type":15,"tag":610,"props":2080,"children":2082},{"id":2081},"things-to-know",[2083],{"type":20,"value":2084},"Things to know",{"type":15,"tag":51,"props":2086,"children":2087},{},[2088,2093,2105,2118],{"type":15,"tag":55,"props":2089,"children":2090},{},[2091],{"type":20,"value":2092},"If you edit your oh-my-posh config, the cache regenerates automatically on the next shell launch. Same for fnm and zoxide when their binaries update.",{"type":15,"tag":55,"props":2094,"children":2095},{},[2096,2098,2104],{"type":20,"value":2097},"If you want to force a regeneration, just delete the cache file from ",{"type":15,"tag":105,"props":2099,"children":2101},{"className":2100},[],[2102],{"type":20,"value":2103},"%TEMP%",{"type":20,"value":2019},{"type":15,"tag":55,"props":2106,"children":2107},{},[2108,2110,2116],{"type":20,"value":2109},"Adding ",{"type":15,"tag":105,"props":2111,"children":2113},{"className":2112},[],[2114],{"type":20,"value":2115},"-NoLogo",{"type":20,"value":2117}," to your pwsh command in Windows Terminal shaves off another ~40ms.",{"type":15,"tag":55,"props":2119,"children":2120},{},[2121,2123,2129],{"type":20,"value":2122},"Check your PowerShell version with ",{"type":15,"tag":105,"props":2124,"children":2126},{"className":2125},[],[2127],{"type":20,"value":2128},"$PSVersionTable.PSVersion",{"type":20,"value":2130},". Newer releases tend to have startup improvements, so staying current helps.",{"type":15,"tag":16,"props":2132,"children":2133},{},[2134],{"type":20,"value":2135},"If you want to go even further, you can defer oh-my-posh itself until the first prompt renders, which would get you down to around 240ms. I didn't bother because the tradeoff is that your very first prompt shows plain text for a split second before oh-my-posh swaps in, which felt worse than waiting 200ms.",{"type":15,"tag":580,"props":2137,"children":2138},{},[2139],{"type":20,"value":584},{"title":7,"searchDepth":186,"depth":186,"links":2141},[2142,2143,2144,2145,2146,2147,2148],{"id":612,"depth":234,"text":615},{"id":761,"depth":234,"text":764},{"id":1142,"depth":234,"text":1145},{"id":1453,"depth":234,"text":1456},{"id":1752,"depth":234,"text":1755},{"id":2022,"depth":234,"text":2025},{"id":2081,"depth":234,"text":2084},"content:blog:cutting-powershell-startup-time-in-half.md","blog/cutting-powershell-startup-time-in-half.md","blog/cutting-powershell-startup-time-in-half",{"_path":2153,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":2154,"description":2155,"date":2156,"body":2157,"_type":590,"_id":3437,"_source":592,"_file":3438,"_stem":3439,"_extension":595},"/blog/better-local-development-with-localhost-subdomains","Better local development with .localhost subdomains","How .localhost subdomains can help smooth out cookie, CORS, and HTTPS issues in local development.","2025-06-28T10:21:00+07:00",{"type":12,"children":2158,"toc":3412},[2159,2172,2183,2189,2215,2222,2228,2233,2280,2292,2297,2308,2319,2325,2346,2351,2387,2393,2411,2443,2456,2468,2509,2514,2558,2564,2599,2604,2637,2642,2688,2694,2726,2828,2848,2854,2864,2910,2916,2988,2994,3099,3105,3175,3181,3187,3192,3261,3267,3290,3296,3341,3347,3357,3408],{"type":15,"tag":16,"props":2160,"children":2161},{},[2162,2164,2170],{"type":20,"value":2163},"If you're building web apps with multiple services running locally, like a frontend, an API, maybe an auth service, you're probably using ",{"type":15,"tag":105,"props":2165,"children":2167},{"className":2166},[],[2168],{"type":20,"value":2169},"localhost",{"type":20,"value":2171}," with different ports. It works, but there are some rough edges once cookies, CORS, or HTTPS get involved.",{"type":15,"tag":16,"props":2173,"children":2174},{},[2175,2181],{"type":15,"tag":105,"props":2176,"children":2178},{"className":2177},[],[2179],{"type":20,"value":2180},".localhost",{"type":20,"value":2182}," subdomains can help smooth things out.",{"type":15,"tag":34,"props":2184,"children":2186},{"id":2185},"how-localhost-works",[2187],{"type":20,"value":2188},"How .localhost works",{"type":15,"tag":16,"props":2190,"children":2191},{},[2192,2213],{"type":15,"tag":418,"props":2193,"children":2194},{},[2195,2197,2203,2205,2211],{"type":20,"value":2196},"Any subdomain under ",{"type":15,"tag":105,"props":2198,"children":2200},{"className":2199},[],[2201],{"type":20,"value":2202},"*.localhost",{"type":20,"value":2204}," resolves to ",{"type":15,"tag":105,"props":2206,"children":2208},{"className":2207},[],[2209],{"type":20,"value":2210},"127.0.0.1",{"type":20,"value":2212}," (your local machine)",{"type":20,"value":2214},", on macOS, Linux, and Windows, without any configuration.",{"type":15,"tag":114,"props":2216,"children":2219},{"className":2217},[2218],"mermaid",[2220],{"type":20,"value":2221},"graph TD\n    A[web.localhost:3000] --> B[127.0.0.1:3000]\n    C[anything.you.want.localhost:3001] --> D[127.0.0.1:3001]\n    E[auth.localhost:5000] --> F[127.0.0.1:5000]\n    G[payments.localhost:5002] --> H[127.0.0.1:5002]\n\n    B --> I[Frontend App]\n    D --> J[Backend API]\n    F --> K[Auth Service]\n    H --> L[Webhook Listener]\n\n    classDef domainNode fill:#2d2d2d,stroke:#14b8a6,stroke-width:2px,color:#e4e4e4\n    classDef ipNode fill:#343030,stroke:#f59e0b,stroke-width:2px,color:#e4e4e4\n    classDef serviceNode fill:#1f1f1f,stroke:#f97316,stroke-width:2px,color:#e4e4e4\n\n    class A,C,E,G domainNode\n    class B,D,F,H ipNode\n    class I,J,K,L serviceNode\n",{"type":15,"tag":34,"props":2223,"children":2225},{"id":2224},"clean-multi-service-dev-environments",[2226],{"type":20,"value":2227},"Clean multi-service dev environments",{"type":15,"tag":16,"props":2229,"children":2230},{},[2231],{"type":20,"value":2232},"Instead of remembering which port maps to which service, you can use subdomains:",{"type":15,"tag":51,"props":2234,"children":2235},{},[2236,2247,2258,2269],{"type":15,"tag":55,"props":2237,"children":2238},{},[2239,2245],{"type":15,"tag":105,"props":2240,"children":2242},{"className":2241},[],[2243],{"type":20,"value":2244},"web.localhost:3000",{"type":20,"value":2246}," -> your frontend",{"type":15,"tag":55,"props":2248,"children":2249},{},[2250,2256],{"type":15,"tag":105,"props":2251,"children":2253},{"className":2252},[],[2254],{"type":20,"value":2255},"api.localhost:3001",{"type":20,"value":2257}," -> backend service",{"type":15,"tag":55,"props":2259,"children":2260},{},[2261,2267],{"type":15,"tag":105,"props":2262,"children":2264},{"className":2263},[],[2265],{"type":20,"value":2266},"auth.localhost:5000",{"type":20,"value":2268}," -> auth or mock SSO",{"type":15,"tag":55,"props":2270,"children":2271},{},[2272,2278],{"type":15,"tag":105,"props":2273,"children":2275},{"className":2274},[],[2276],{"type":20,"value":2277},"payments.localhost:5002",{"type":20,"value":2279}," -> webhook listener",{"type":15,"tag":16,"props":2281,"children":2282},{},[2283,2285,2290],{"type":20,"value":2284},"All of these resolve to ",{"type":15,"tag":105,"props":2286,"children":2288},{"className":2287},[],[2289],{"type":20,"value":2210},{"type":20,"value":2291},". You can route them via your dev proxy (Vite, Webpack, Traefik) or just hardcode ports.",{"type":15,"tag":16,"props":2293,"children":2294},{},[2295],{"type":20,"value":2296},"The URLs end up looking closer to production, which makes configuration a bit more straightforward.",{"type":15,"tag":34,"props":2298,"children":2300},{"id":2299},"cookie-behavior-on-localhost",[2301,2303],{"type":20,"value":2302},"Cookie behavior on ",{"type":15,"tag":105,"props":2304,"children":2306},{"className":2305},[],[2307],{"type":20,"value":2169},{"type":15,"tag":16,"props":2309,"children":2310},{},[2311,2313,2318],{"type":20,"value":2312},"There are a few things worth knowing about how cookies work on ",{"type":15,"tag":105,"props":2314,"children":2316},{"className":2315},[],[2317],{"type":20,"value":2169},{"type":20,"value":2019},{"type":15,"tag":610,"props":2320,"children":2322},{"id":2321},"ports-do-not-separate-cookies",[2323],{"type":20,"value":2324},"Ports do not separate cookies",{"type":15,"tag":16,"props":2326,"children":2327},{},[2328,2330,2336,2338,2344],{"type":20,"value":2329},"Browsers don't consider the port when scoping cookies. A cookie set on ",{"type":15,"tag":105,"props":2331,"children":2333},{"className":2332},[],[2334],{"type":20,"value":2335},"localhost:3000",{"type":20,"value":2337}," gets sent to ",{"type":15,"tag":105,"props":2339,"children":2341},{"className":2340},[],[2342],{"type":20,"value":2343},"localhost:3001",{"type":20,"value":2345}," too. In production, your services would be on separate domains or subdomains with their own cookie space. On localhost, they all share one.",{"type":15,"tag":16,"props":2347,"children":2348},{},[2349],{"type":20,"value":2350},"This can lead to:",{"type":15,"tag":51,"props":2352,"children":2353},{},[2354,2359],{"type":15,"tag":55,"props":2355,"children":2356},{},[2357],{"type":20,"value":2358},"Cookies being shared across unrelated services",{"type":15,"tag":55,"props":2360,"children":2361},{},[2362,2364,2370,2371,2377,2379,2385],{"type":20,"value":2363},"Difficulty testing ",{"type":15,"tag":105,"props":2365,"children":2367},{"className":2366},[],[2368],{"type":20,"value":2369},"Domain",{"type":20,"value":172},{"type":15,"tag":105,"props":2372,"children":2374},{"className":2373},[],[2375],{"type":20,"value":2376},"Path",{"type":20,"value":2378},", or ",{"type":15,"tag":105,"props":2380,"children":2382},{"className":2381},[],[2383],{"type":20,"value":2384},"SameSite",{"type":20,"value":2386}," behavior accurately",{"type":15,"tag":114,"props":2388,"children":2390},{"className":2389},[2218],[2391],{"type":20,"value":2392},"graph TB\n    A[localhost:3000] --> B[Cookie Jar]\n    C[localhost:3001] --> B\n    D[localhost:5000] --> B\n    E[localhost:5002] --> B\n\n    classDef portNode fill:#2d2d2d,stroke:#14b8a6,stroke-width:2px,color:#e4e4e4\n    classDef cookieNode fill:#f59e0b,stroke:#f59e0b,stroke-width:2px,color:#1a1a1a\n\n    class A,C,D,E portNode\n    class B cookieNode\n",{"type":15,"tag":610,"props":2394,"children":2396},{"id":2395},"_127001-and-localhost-are-not-treated-the-same",[2397,2402,2404,2409],{"type":15,"tag":105,"props":2398,"children":2400},{"className":2399},[],[2401],{"type":20,"value":2210},{"type":20,"value":2403}," and ",{"type":15,"tag":105,"props":2405,"children":2407},{"className":2406},[],[2408],{"type":20,"value":2169},{"type":20,"value":2410}," are not treated the same",{"type":15,"tag":16,"props":2412,"children":2413},{},[2414,2416,2421,2422,2427,2429,2434,2436,2442],{"type":20,"value":2415},"Although ",{"type":15,"tag":105,"props":2417,"children":2419},{"className":2418},[],[2420],{"type":20,"value":2210},{"type":20,"value":2403},{"type":15,"tag":105,"props":2423,"children":2425},{"className":2424},[],[2426],{"type":20,"value":2169},{"type":20,"value":2428}," both point to your local machine, browsers treat them as different origins. A cookie set on ",{"type":15,"tag":105,"props":2430,"children":2432},{"className":2431},[],[2433],{"type":20,"value":2335},{"type":20,"value":2435}," won't be sent with requests to ",{"type":15,"tag":105,"props":2437,"children":2439},{"className":2438},[],[2440],{"type":20,"value":2441},"127.0.0.1:3001",{"type":20,"value":2019},{"type":15,"tag":610,"props":2444,"children":2446},{"id":2445},"using-localhost-subdomains",[2447,2449,2454],{"type":20,"value":2448},"Using ",{"type":15,"tag":105,"props":2450,"children":2452},{"className":2451},[],[2453],{"type":20,"value":2180},{"type":20,"value":2455}," subdomains",{"type":15,"tag":16,"props":2457,"children":2458},{},[2459,2461,2466],{"type":20,"value":2460},"With ",{"type":15,"tag":105,"props":2462,"children":2464},{"className":2463},[],[2465],{"type":20,"value":2180},{"type":20,"value":2467}," subdomains, each service gets its own domain, which means:",{"type":15,"tag":51,"props":2469,"children":2470},{},[2471,2476,2487,2504],{"type":15,"tag":55,"props":2472,"children":2473},{},[2474],{"type":20,"value":2475},"Domain separation for cookies",{"type":15,"tag":55,"props":2477,"children":2478},{},[2479,2481],{"type":20,"value":2480},"Cookie sharing when you want it, via ",{"type":15,"tag":105,"props":2482,"children":2484},{"className":2483},[],[2485],{"type":20,"value":2486},"Domain=.localhost",{"type":15,"tag":55,"props":2488,"children":2489},{},[2490,2495,2496,2502],{"type":15,"tag":105,"props":2491,"children":2493},{"className":2492},[],[2494],{"type":20,"value":2384},{"type":20,"value":2403},{"type":15,"tag":105,"props":2497,"children":2499},{"className":2498},[],[2500],{"type":20,"value":2501},"Secure",{"type":20,"value":2503}," flags work as expected",{"type":15,"tag":55,"props":2505,"children":2506},{},[2507],{"type":20,"value":2508},"CORS behaves like it would in production",{"type":15,"tag":16,"props":2510,"children":2511},{},[2512],{"type":20,"value":2513},"Some additional benefits:",{"type":15,"tag":51,"props":2515,"children":2516},{},[2517,2535,2553],{"type":15,"tag":55,"props":2518,"children":2519},{},[2520,2526,2527,2533],{"type":15,"tag":105,"props":2521,"children":2523},{"className":2522},[],[2524],{"type":20,"value":2525},"localStorage",{"type":20,"value":2403},{"type":15,"tag":105,"props":2528,"children":2530},{"className":2529},[],[2531],{"type":20,"value":2532},"sessionStorage",{"type":20,"value":2534}," are isolated by subdomain, not by port",{"type":15,"tag":55,"props":2536,"children":2537},{},[2538,2544,2546,2551],{"type":15,"tag":105,"props":2539,"children":2541},{"className":2540},[],[2542],{"type":20,"value":2543},"SameSite=None",{"type":20,"value":2545}," requires HTTPS, which ",{"type":15,"tag":105,"props":2547,"children":2549},{"className":2548},[],[2550],{"type":20,"value":2180},{"type":20,"value":2552}," supports with mkcert",{"type":15,"tag":55,"props":2554,"children":2555},{},[2556],{"type":20,"value":2557},"URLs resemble your production setup",{"type":15,"tag":34,"props":2559,"children":2561},{"id":2560},"reserved-by-spec-supported-everywhere",[2562],{"type":20,"value":2563},"Reserved by spec, supported everywhere",{"type":15,"tag":16,"props":2565,"children":2566},{},[2567,2569,2576,2577,2582,2584,2589,2591,2597],{"type":20,"value":2568},"Per ",{"type":15,"tag":23,"props":2570,"children":2573},{"href":2571,"rel":2572},"https://www.rfc-editor.org/rfc/rfc6761.html",[27],[2574],{"type":20,"value":2575},"RFC 6761",{"type":20,"value":172},{"type":15,"tag":105,"props":2578,"children":2580},{"className":2579},[],[2581],{"type":20,"value":2180},{"type":20,"value":2583}," is a reserved TLD. It resolves to ",{"type":15,"tag":105,"props":2585,"children":2587},{"className":2586},[],[2588],{"type":20,"value":2210},{"type":20,"value":2590}," or ",{"type":15,"tag":105,"props":2592,"children":2594},{"className":2593},[],[2595],{"type":20,"value":2596},"::1",{"type":20,"value":2598}," without hitting external DNS servers.",{"type":15,"tag":16,"props":2600,"children":2601},{},[2602],{"type":20,"value":2603},"This works on all major operating systems:",{"type":15,"tag":51,"props":2605,"children":2606},{},[2607,2617,2627],{"type":15,"tag":55,"props":2608,"children":2609},{},[2610,2615],{"type":15,"tag":418,"props":2611,"children":2612},{},[2613],{"type":20,"value":2614},"macOS",{"type":20,"value":2616},": Built-in DNS resolver",{"type":15,"tag":55,"props":2618,"children":2619},{},[2620,2625],{"type":15,"tag":418,"props":2621,"children":2622},{},[2623],{"type":20,"value":2624},"Linux",{"type":20,"value":2626},": Systemd-resolved and others",{"type":15,"tag":55,"props":2628,"children":2629},{},[2630,2635],{"type":15,"tag":418,"props":2631,"children":2632},{},[2633],{"type":20,"value":2634},"Windows",{"type":20,"value":2636},": Windows DNS resolver",{"type":15,"tag":16,"props":2638,"children":2639},{},[2640],{"type":20,"value":2641},"Some things this gives you:",{"type":15,"tag":51,"props":2643,"children":2644},{},[2645,2662,2672],{"type":15,"tag":55,"props":2646,"children":2647},{},[2648,2653,2655,2660],{"type":15,"tag":418,"props":2649,"children":2650},{},[2651],{"type":20,"value":2652},"No DNS leaks.",{"type":20,"value":2654}," ",{"type":15,"tag":105,"props":2656,"children":2658},{"className":2657},[],[2659],{"type":20,"value":2180},{"type":20,"value":2661}," stays local even if your VPN or DNS config is off.",{"type":15,"tag":55,"props":2663,"children":2664},{},[2665,2670],{"type":15,"tag":418,"props":2666,"children":2667},{},[2668],{"type":20,"value":2669},"No external traffic.",{"type":20,"value":2671}," Requests stay on your machine.",{"type":15,"tag":55,"props":2673,"children":2674},{},[2675,2680,2681,2686],{"type":15,"tag":418,"props":2676,"children":2677},{},[2678],{"type":20,"value":2679},"No conflicts.",{"type":20,"value":2654},{"type":15,"tag":105,"props":2682,"children":2684},{"className":2683},[],[2685],{"type":20,"value":2180},{"type":20,"value":2687}," is unregistrable as a real domain.",{"type":15,"tag":34,"props":2689,"children":2691},{"id":2690},"tls-support-with-mkcert",[2692],{"type":20,"value":2693},"TLS support with mkcert",{"type":15,"tag":16,"props":2695,"children":2696},{},[2697,2699,2704,2706,2717,2719,2724],{"type":20,"value":2698},"Some cookie features (like ",{"type":15,"tag":105,"props":2700,"children":2702},{"className":2701},[],[2703],{"type":20,"value":2543},{"type":20,"value":2705},") require HTTPS. ",{"type":15,"tag":23,"props":2707,"children":2710},{"href":2708,"rel":2709},"https://github.com/FiloSottile/mkcert",[27],[2711],{"type":15,"tag":105,"props":2712,"children":2714},{"className":2713},[],[2715],{"type":20,"value":2716},"mkcert",{"type":20,"value":2718}," makes it easy to generate trusted certificates for ",{"type":15,"tag":105,"props":2720,"children":2722},{"className":2721},[],[2723],{"type":20,"value":2202},{"type":20,"value":2725}," domains:",{"type":15,"tag":114,"props":2727,"children":2731},{"className":2728,"code":2729,"language":2730,"meta":7,"style":7},"language-bash shiki shiki-themes one-dark-pro","# Install mkcert\nbrew install mkcert  # macOS\n# or\nsudo apt install mkcert  # Ubuntu/Debian\n# or\nchoco install mkcert  # Windows (Chocolatey)\n\n# Install the local CA\nmkcert -install\n\n# Generate certificates for your domains\nmkcert web.localhost api.localhost auth.localhost\n","bash",[2732],{"type":15,"tag":105,"props":2733,"children":2734},{"__ignoreMap":7},[2735,2743,2751,2759,2767,2774,2782,2789,2797,2805,2812,2820],{"type":15,"tag":124,"props":2736,"children":2737},{"class":126,"line":127},[2738],{"type":15,"tag":124,"props":2739,"children":2740},{},[2741],{"type":20,"value":2742},"# Install mkcert\n",{"type":15,"tag":124,"props":2744,"children":2745},{"class":126,"line":186},[2746],{"type":15,"tag":124,"props":2747,"children":2748},{},[2749],{"type":20,"value":2750},"brew install mkcert  # macOS\n",{"type":15,"tag":124,"props":2752,"children":2753},{"class":126,"line":234},[2754],{"type":15,"tag":124,"props":2755,"children":2756},{},[2757],{"type":20,"value":2758},"# or\n",{"type":15,"tag":124,"props":2760,"children":2761},{"class":126,"line":252},[2762],{"type":15,"tag":124,"props":2763,"children":2764},{},[2765],{"type":20,"value":2766},"sudo apt install mkcert  # Ubuntu/Debian\n",{"type":15,"tag":124,"props":2768,"children":2769},{"class":126,"line":285},[2770],{"type":15,"tag":124,"props":2771,"children":2772},{},[2773],{"type":20,"value":2758},{"type":15,"tag":124,"props":2775,"children":2776},{"class":126,"line":307},[2777],{"type":15,"tag":124,"props":2778,"children":2779},{},[2780],{"type":20,"value":2781},"choco install mkcert  # Windows (Chocolatey)\n",{"type":15,"tag":124,"props":2783,"children":2784},{"class":126,"line":316},[2785],{"type":15,"tag":124,"props":2786,"children":2787},{"emptyLinePlaceholder":918},[2788],{"type":20,"value":921},{"type":15,"tag":124,"props":2790,"children":2791},{"class":126,"line":337},[2792],{"type":15,"tag":124,"props":2793,"children":2794},{},[2795],{"type":20,"value":2796},"# Install the local CA\n",{"type":15,"tag":124,"props":2798,"children":2799},{"class":126,"line":346},[2800],{"type":15,"tag":124,"props":2801,"children":2802},{},[2803],{"type":20,"value":2804},"mkcert -install\n",{"type":15,"tag":124,"props":2806,"children":2807},{"class":126,"line":355},[2808],{"type":15,"tag":124,"props":2809,"children":2810},{"emptyLinePlaceholder":918},[2811],{"type":20,"value":921},{"type":15,"tag":124,"props":2813,"children":2814},{"class":126,"line":385},[2815],{"type":15,"tag":124,"props":2816,"children":2817},{},[2818],{"type":20,"value":2819},"# Generate certificates for your domains\n",{"type":15,"tag":124,"props":2821,"children":2822},{"class":126,"line":394},[2823],{"type":15,"tag":124,"props":2824,"children":2825},{},[2826],{"type":20,"value":2827},"mkcert web.localhost api.localhost auth.localhost\n",{"type":15,"tag":16,"props":2829,"children":2830},{},[2831,2833,2839,2840,2846],{"type":20,"value":2832},"This gets you ",{"type":15,"tag":105,"props":2834,"children":2836},{"className":2835},[],[2837],{"type":20,"value":2838},"https://web.localhost",{"type":20,"value":172},{"type":15,"tag":105,"props":2841,"children":2843},{"className":2842},[],[2844],{"type":20,"value":2845},"https://api.localhost",{"type":20,"value":2847},", etc. with valid TLS.",{"type":15,"tag":34,"props":2849,"children":2851},{"id":2850},"works-with-proxies-containers-and-dev-tools",[2852],{"type":20,"value":2853},"Works with proxies, containers, and dev tools",{"type":15,"tag":16,"props":2855,"children":2856},{},[2857,2862],{"type":15,"tag":105,"props":2858,"children":2860},{"className":2859},[],[2861],{"type":20,"value":2180},{"type":20,"value":2863}," fits into existing setups:",{"type":15,"tag":51,"props":2865,"children":2866},{},[2867,2888,2893,2898],{"type":15,"tag":55,"props":2868,"children":2869},{},[2870,2872,2878,2880,2886],{"type":20,"value":2871},"Subdomain routing to map ",{"type":15,"tag":105,"props":2873,"children":2875},{"className":2874},[],[2876],{"type":20,"value":2877},"web.localhost",{"type":20,"value":2879}," to one container, ",{"type":15,"tag":105,"props":2881,"children":2883},{"className":2882},[],[2884],{"type":20,"value":2885},"api.localhost",{"type":20,"value":2887}," to another",{"type":15,"tag":55,"props":2889,"children":2890},{},[2891],{"type":20,"value":2892},"Proxies can route based on host headers",{"type":15,"tag":55,"props":2894,"children":2895},{},[2896],{"type":20,"value":2897},"Frontend tools like Vite, Next.js, and Create React App support localhost subdomains",{"type":15,"tag":55,"props":2899,"children":2900},{},[2901,2903,2908],{"type":20,"value":2902},"Test frameworks like Cypress treat ",{"type":15,"tag":105,"props":2904,"children":2906},{"className":2905},[],[2907],{"type":20,"value":2180},{"type":20,"value":2909}," as a trusted domain",{"type":15,"tag":610,"props":2911,"children":2913},{"id":2912},"example-vite-configuration",[2914],{"type":20,"value":2915},"Example Vite configuration",{"type":15,"tag":114,"props":2917,"children":2921},{"className":2918,"code":2919,"language":2920,"meta":7,"style":7},"language-javascript shiki shiki-themes one-dark-pro","// vite.config.js\nexport default {\n  server: {\n    host: 'web.localhost',\n    port: 3000,\n    https: true\n  }\n}\n","javascript",[2922],{"type":15,"tag":105,"props":2923,"children":2924},{"__ignoreMap":7},[2925,2933,2941,2949,2957,2965,2973,2981],{"type":15,"tag":124,"props":2926,"children":2927},{"class":126,"line":127},[2928],{"type":15,"tag":124,"props":2929,"children":2930},{},[2931],{"type":20,"value":2932},"// vite.config.js\n",{"type":15,"tag":124,"props":2934,"children":2935},{"class":126,"line":186},[2936],{"type":15,"tag":124,"props":2937,"children":2938},{},[2939],{"type":20,"value":2940},"export default {\n",{"type":15,"tag":124,"props":2942,"children":2943},{"class":126,"line":234},[2944],{"type":15,"tag":124,"props":2945,"children":2946},{},[2947],{"type":20,"value":2948},"  server: {\n",{"type":15,"tag":124,"props":2950,"children":2951},{"class":126,"line":252},[2952],{"type":15,"tag":124,"props":2953,"children":2954},{},[2955],{"type":20,"value":2956},"    host: 'web.localhost',\n",{"type":15,"tag":124,"props":2958,"children":2959},{"class":126,"line":285},[2960],{"type":15,"tag":124,"props":2961,"children":2962},{},[2963],{"type":20,"value":2964},"    port: 3000,\n",{"type":15,"tag":124,"props":2966,"children":2967},{"class":126,"line":307},[2968],{"type":15,"tag":124,"props":2969,"children":2970},{},[2971],{"type":20,"value":2972},"    https: true\n",{"type":15,"tag":124,"props":2974,"children":2975},{"class":126,"line":316},[2976],{"type":15,"tag":124,"props":2977,"children":2978},{},[2979],{"type":20,"value":2980},"  }\n",{"type":15,"tag":124,"props":2982,"children":2983},{"class":126,"line":337},[2984],{"type":15,"tag":124,"props":2985,"children":2986},{},[2987],{"type":20,"value":1432},{"type":15,"tag":610,"props":2989,"children":2991},{"id":2990},"example-docker-compose-setup",[2992],{"type":20,"value":2993},"Example Docker Compose setup",{"type":15,"tag":114,"props":2995,"children":2999},{"className":2996,"code":2997,"language":2998,"meta":7,"style":7},"language-yaml shiki shiki-themes one-dark-pro","# docker-compose.yml\nversion: '3.8'\nservices:\n  web:\n    image: nginx\n    ports:\n      - \"80:80\"\n    volumes:\n      - ./nginx.conf:/etc/nginx/nginx.conf\n    extra_hosts:\n      - \"web.localhost:127.0.0.1\"\n      - \"api.localhost:127.0.0.1\"\n","yaml",[3000],{"type":15,"tag":105,"props":3001,"children":3002},{"__ignoreMap":7},[3003,3011,3019,3027,3035,3043,3051,3059,3067,3075,3083,3091],{"type":15,"tag":124,"props":3004,"children":3005},{"class":126,"line":127},[3006],{"type":15,"tag":124,"props":3007,"children":3008},{},[3009],{"type":20,"value":3010},"# docker-compose.yml\n",{"type":15,"tag":124,"props":3012,"children":3013},{"class":126,"line":186},[3014],{"type":15,"tag":124,"props":3015,"children":3016},{},[3017],{"type":20,"value":3018},"version: '3.8'\n",{"type":15,"tag":124,"props":3020,"children":3021},{"class":126,"line":234},[3022],{"type":15,"tag":124,"props":3023,"children":3024},{},[3025],{"type":20,"value":3026},"services:\n",{"type":15,"tag":124,"props":3028,"children":3029},{"class":126,"line":252},[3030],{"type":15,"tag":124,"props":3031,"children":3032},{},[3033],{"type":20,"value":3034},"  web:\n",{"type":15,"tag":124,"props":3036,"children":3037},{"class":126,"line":285},[3038],{"type":15,"tag":124,"props":3039,"children":3040},{},[3041],{"type":20,"value":3042},"    image: nginx\n",{"type":15,"tag":124,"props":3044,"children":3045},{"class":126,"line":307},[3046],{"type":15,"tag":124,"props":3047,"children":3048},{},[3049],{"type":20,"value":3050},"    ports:\n",{"type":15,"tag":124,"props":3052,"children":3053},{"class":126,"line":316},[3054],{"type":15,"tag":124,"props":3055,"children":3056},{},[3057],{"type":20,"value":3058},"      - \"80:80\"\n",{"type":15,"tag":124,"props":3060,"children":3061},{"class":126,"line":337},[3062],{"type":15,"tag":124,"props":3063,"children":3064},{},[3065],{"type":20,"value":3066},"    volumes:\n",{"type":15,"tag":124,"props":3068,"children":3069},{"class":126,"line":346},[3070],{"type":15,"tag":124,"props":3071,"children":3072},{},[3073],{"type":20,"value":3074},"      - ./nginx.conf:/etc/nginx/nginx.conf\n",{"type":15,"tag":124,"props":3076,"children":3077},{"class":126,"line":355},[3078],{"type":15,"tag":124,"props":3079,"children":3080},{},[3081],{"type":20,"value":3082},"    extra_hosts:\n",{"type":15,"tag":124,"props":3084,"children":3085},{"class":126,"line":385},[3086],{"type":15,"tag":124,"props":3087,"children":3088},{},[3089],{"type":20,"value":3090},"      - \"web.localhost:127.0.0.1\"\n",{"type":15,"tag":124,"props":3092,"children":3093},{"class":126,"line":394},[3094],{"type":15,"tag":124,"props":3095,"children":3096},{},[3097],{"type":20,"value":3098},"      - \"api.localhost:127.0.0.1\"\n",{"type":15,"tag":610,"props":3100,"children":3102},{"id":3101},"environment-variables",[3103],{"type":20,"value":3104},"Environment variables",{"type":15,"tag":114,"props":3106,"children":3108},{"className":2918,"code":3107,"language":2920,"meta":7,"style":7},"// config.js\nconst isDev = process.env.NODE_ENV === 'development';\n\nexport const config = {\n  apiUrl: isDev ? 'https://api.localhost' : 'https://api.production.com',\n  authUrl: isDev ? 'https://auth.localhost' : 'https://auth.production.com',\n  webUrl: isDev ? 'https://web.localhost' : 'https://app.production.com'\n};\n",[3109],{"type":15,"tag":105,"props":3110,"children":3111},{"__ignoreMap":7},[3112,3120,3128,3135,3143,3151,3159,3167],{"type":15,"tag":124,"props":3113,"children":3114},{"class":126,"line":127},[3115],{"type":15,"tag":124,"props":3116,"children":3117},{},[3118],{"type":20,"value":3119},"// config.js\n",{"type":15,"tag":124,"props":3121,"children":3122},{"class":126,"line":186},[3123],{"type":15,"tag":124,"props":3124,"children":3125},{},[3126],{"type":20,"value":3127},"const isDev = process.env.NODE_ENV === 'development';\n",{"type":15,"tag":124,"props":3129,"children":3130},{"class":126,"line":234},[3131],{"type":15,"tag":124,"props":3132,"children":3133},{"emptyLinePlaceholder":918},[3134],{"type":20,"value":921},{"type":15,"tag":124,"props":3136,"children":3137},{"class":126,"line":252},[3138],{"type":15,"tag":124,"props":3139,"children":3140},{},[3141],{"type":20,"value":3142},"export const config = {\n",{"type":15,"tag":124,"props":3144,"children":3145},{"class":126,"line":285},[3146],{"type":15,"tag":124,"props":3147,"children":3148},{},[3149],{"type":20,"value":3150},"  apiUrl: isDev ? 'https://api.localhost' : 'https://api.production.com',\n",{"type":15,"tag":124,"props":3152,"children":3153},{"class":126,"line":307},[3154],{"type":15,"tag":124,"props":3155,"children":3156},{},[3157],{"type":20,"value":3158},"  authUrl: isDev ? 'https://auth.localhost' : 'https://auth.production.com',\n",{"type":15,"tag":124,"props":3160,"children":3161},{"class":126,"line":316},[3162],{"type":15,"tag":124,"props":3163,"children":3164},{},[3165],{"type":20,"value":3166},"  webUrl: isDev ? 'https://web.localhost' : 'https://app.production.com'\n",{"type":15,"tag":124,"props":3168,"children":3169},{"class":126,"line":337},[3170],{"type":15,"tag":124,"props":3171,"children":3172},{},[3173],{"type":20,"value":3174},"};\n",{"type":15,"tag":34,"props":3176,"children":3178},{"id":3177},"common-gotchas",[3179],{"type":20,"value":3180},"Common gotchas",{"type":15,"tag":610,"props":3182,"children":3184},{"id":3183},"browser-caching",[3185],{"type":20,"value":3186},"Browser caching",{"type":15,"tag":16,"props":3188,"children":3189},{},[3190],{"type":20,"value":3191},"Browsers sometimes cache DNS lookups. If subdomains aren't resolving:",{"type":15,"tag":114,"props":3193,"children":3195},{"className":2728,"code":3194,"language":2730,"meta":7,"style":7},"# macOS\nsudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder\n\n# Linux\nsudo systemctl restart systemd-resolved\n\n# Windows\nipconfig /flushdns\n",[3196],{"type":15,"tag":105,"props":3197,"children":3198},{"__ignoreMap":7},[3199,3207,3215,3222,3230,3238,3245,3253],{"type":15,"tag":124,"props":3200,"children":3201},{"class":126,"line":127},[3202],{"type":15,"tag":124,"props":3203,"children":3204},{},[3205],{"type":20,"value":3206},"# macOS\n",{"type":15,"tag":124,"props":3208,"children":3209},{"class":126,"line":186},[3210],{"type":15,"tag":124,"props":3211,"children":3212},{},[3213],{"type":20,"value":3214},"sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder\n",{"type":15,"tag":124,"props":3216,"children":3217},{"class":126,"line":234},[3218],{"type":15,"tag":124,"props":3219,"children":3220},{"emptyLinePlaceholder":918},[3221],{"type":20,"value":921},{"type":15,"tag":124,"props":3223,"children":3224},{"class":126,"line":252},[3225],{"type":15,"tag":124,"props":3226,"children":3227},{},[3228],{"type":20,"value":3229},"# Linux\n",{"type":15,"tag":124,"props":3231,"children":3232},{"class":126,"line":285},[3233],{"type":15,"tag":124,"props":3234,"children":3235},{},[3236],{"type":20,"value":3237},"sudo systemctl restart systemd-resolved\n",{"type":15,"tag":124,"props":3239,"children":3240},{"class":126,"line":307},[3241],{"type":15,"tag":124,"props":3242,"children":3243},{"emptyLinePlaceholder":918},[3244],{"type":20,"value":921},{"type":15,"tag":124,"props":3246,"children":3247},{"class":126,"line":316},[3248],{"type":15,"tag":124,"props":3249,"children":3250},{},[3251],{"type":20,"value":3252},"# Windows\n",{"type":15,"tag":124,"props":3254,"children":3255},{"class":126,"line":337},[3256],{"type":15,"tag":124,"props":3257,"children":3258},{},[3259],{"type":20,"value":3260},"ipconfig /flushdns\n",{"type":15,"tag":610,"props":3262,"children":3264},{"id":3263},"port-conflicts",[3265],{"type":20,"value":3266},"Port conflicts",{"type":15,"tag":114,"props":3268,"children":3270},{"className":2728,"code":3269,"language":2730,"meta":7,"style":7},"# Check what's running on port 3000\nlsof -i :3000\n",[3271],{"type":15,"tag":105,"props":3272,"children":3273},{"__ignoreMap":7},[3274,3282],{"type":15,"tag":124,"props":3275,"children":3276},{"class":126,"line":127},[3277],{"type":15,"tag":124,"props":3278,"children":3279},{},[3280],{"type":20,"value":3281},"# Check what's running on port 3000\n",{"type":15,"tag":124,"props":3283,"children":3284},{"class":126,"line":186},[3285],{"type":15,"tag":124,"props":3286,"children":3287},{},[3288],{"type":20,"value":3289},"lsof -i :3000\n",{"type":15,"tag":610,"props":3291,"children":3293},{"id":3292},"ssl-certificate-issues",[3294],{"type":20,"value":3295},"SSL certificate issues",{"type":15,"tag":114,"props":3297,"children":3299},{"className":2728,"code":3298,"language":2730,"meta":7,"style":7},"# Reinstall mkcert CA\nmkcert -install\n\n# Regenerate certificates\nmkcert -key-file key.pem -cert-file cert.pem web.localhost api.localhost\n",[3300],{"type":15,"tag":105,"props":3301,"children":3302},{"__ignoreMap":7},[3303,3311,3318,3325,3333],{"type":15,"tag":124,"props":3304,"children":3305},{"class":126,"line":127},[3306],{"type":15,"tag":124,"props":3307,"children":3308},{},[3309],{"type":20,"value":3310},"# Reinstall mkcert CA\n",{"type":15,"tag":124,"props":3312,"children":3313},{"class":126,"line":186},[3314],{"type":15,"tag":124,"props":3315,"children":3316},{},[3317],{"type":20,"value":2804},{"type":15,"tag":124,"props":3319,"children":3320},{"class":126,"line":234},[3321],{"type":15,"tag":124,"props":3322,"children":3323},{"emptyLinePlaceholder":918},[3324],{"type":20,"value":921},{"type":15,"tag":124,"props":3326,"children":3327},{"class":126,"line":252},[3328],{"type":15,"tag":124,"props":3329,"children":3330},{},[3331],{"type":20,"value":3332},"# Regenerate certificates\n",{"type":15,"tag":124,"props":3334,"children":3335},{"class":126,"line":285},[3336],{"type":15,"tag":124,"props":3337,"children":3338},{},[3339],{"type":20,"value":3340},"mkcert -key-file key.pem -cert-file cert.pem web.localhost api.localhost\n",{"type":15,"tag":34,"props":3342,"children":3344},{"id":3343},"final-thoughts",[3345],{"type":20,"value":3346},"Final thoughts",{"type":15,"tag":16,"props":3348,"children":3349},{},[3350,3355],{"type":15,"tag":105,"props":3351,"children":3353},{"className":3352},[],[3354],{"type":20,"value":2180},{"type":20,"value":3356}," is part of the spec for a reason. It gives you:",{"type":15,"tag":51,"props":3358,"children":3359},{},[3360,3370,3380,3388,3398],{"type":15,"tag":55,"props":3361,"children":3362},{},[3363,3368],{"type":15,"tag":418,"props":3364,"children":3365},{},[3366],{"type":20,"value":3367},"Local-only domains",{"type":20,"value":3369}," that are DNS-safe and work on macOS, Linux, and Windows",{"type":15,"tag":55,"props":3371,"children":3372},{},[3373,3378],{"type":15,"tag":418,"props":3374,"children":3375},{},[3376],{"type":20,"value":3377},"Clean subdomain structure",{"type":20,"value":3379}," for microservices or frontends",{"type":15,"tag":55,"props":3381,"children":3382},{},[3383],{"type":15,"tag":418,"props":3384,"children":3385},{},[3386],{"type":20,"value":3387},"Accurate cookie, storage, and CORS behavior",{"type":15,"tag":55,"props":3389,"children":3390},{},[3391,3396],{"type":15,"tag":418,"props":3392,"children":3393},{},[3394],{"type":20,"value":3395},"Easy HTTPS",{"type":20,"value":3397}," with tools like mkcert",{"type":15,"tag":55,"props":3399,"children":3400},{},[3401,3406],{"type":15,"tag":418,"props":3402,"children":3403},{},[3404],{"type":20,"value":3405},"Less config and fewer bugs",{"type":20,"value":3407}," when you go live",{"type":15,"tag":580,"props":3409,"children":3410},{},[3411],{"type":20,"value":584},{"title":7,"searchDepth":186,"depth":186,"links":3413},[3414,3415,3416,3424,3425,3426,3431,3436],{"id":2185,"depth":186,"text":2188},{"id":2224,"depth":186,"text":2227},{"id":2299,"depth":186,"text":3417,"children":3418},"Cookie behavior on localhost",[3419,3420,3422],{"id":2321,"depth":234,"text":2324},{"id":2395,"depth":234,"text":3421},"127.0.0.1 and localhost are not treated the same",{"id":2445,"depth":234,"text":3423},"Using .localhost subdomains",{"id":2560,"depth":186,"text":2563},{"id":2690,"depth":186,"text":2693},{"id":2850,"depth":186,"text":2853,"children":3427},[3428,3429,3430],{"id":2912,"depth":234,"text":2915},{"id":2990,"depth":234,"text":2993},{"id":3101,"depth":234,"text":3104},{"id":3177,"depth":186,"text":3180,"children":3432},[3433,3434,3435],{"id":3183,"depth":234,"text":3186},{"id":3263,"depth":234,"text":3266},{"id":3292,"depth":234,"text":3295},{"id":3343,"depth":186,"text":3346},"content:blog:better-local-development-with-localhost-subdomains.md","blog/better-local-development-with-localhost-subdomains.md","blog/better-local-development-with-localhost-subdomains",1777867099819]