[{"data":1,"prerenderedAt":2168},["ShallowReactive",2],{"post-\u002Fposts\u002Fthe-simplest-method-to-create-a-vue-js-component-library":3,"post-link-en-55d2cdcf-ba27-4bfe-9bc1-492e866ff3b1":2163},{"id":4,"title":5,"body":6,"contentId":2156,"date":2157,"description":2158,"extension":2159,"meta":2160,"navigation":163,"path":2161,"rawbody":2162,"robots":2163,"schemaOrg":2163,"seo":2164,"sitemap":2165,"stem":2166,"__hash__":2167},"posts\u002Fposts\u002Fthe-simplest-method-to-create-a-vue-js-component-library.md","The Simplest Method to Create a Vue.js Component Library",{"type":7,"value":8,"toc":2145},"minimark",[9,22,28,57,2141],[10,11,12,13],"p",{},"TL;DR: ",[14,15,17,18],"git-hub-link",{"repo":16},"barbapapazes\u002Fvue-library"," ",[19,20,21],"em",{},"Continue reading to grasp the process behind it and understand the reasons for my choices.",[10,23,24,25],{},"A component library is a collection of reusable pieces that can be employed in various projects. It facilitates sharing resources among different projects and teams. These components may be low-level and generic like buttons, inputs, and modals, or more business-specific modules. ",[19,26,27],{},"Ultimately, it's a method to share code across projects, a crucial skill that can significantly save time.",[10,29,30,31,38,39,44,45,50,51,56],{},"However, building a component library with ",[32,33,37],"a",{"href":34,"rel":35},"https:\u002F\u002Fvuejs.org",[36],"nofollow","Vue.js"," is harder than it seems because of the ",[32,40,43],{"href":41,"rel":42},"https:\u002F\u002Fvuejs.org\u002Fguide\u002Fscaling-up\u002Fsfc.html",[36],"Single File Components"," (SFC). In a standard TypeScript project, you usually transpile to JavaScript and bundle your files with a tool like ",[32,46,49],{"href":47,"rel":48},"https:\u002F\u002Ftsup.egoist.dev\u002F",[36],"tsup"," or ",[32,52,55],{"href":53,"rel":54},"https:\u002F\u002Fvitejs.dev\u002F",[36],"Vite",".",[58,59,61,79,82,85,261,264,372,378,381,389,396,457,467,619,624,631,638,644,673,680,692,706,710,729,732,735,750,753,764,769,775,798,805,951,961,965,982,1000,1009,1178,1185,1189,1195,1246,1252,1258,1261,1272,1276,1279,1306,1309,1322,1348,1352,1355,1365,1447,1480,1491,1497,1648,1654,1660,1673,1677,1684,1687,1690,1698,1705,1735,1742,1763,1766,1779,1790,1796,1807,1888,1891,1912,1921,1925,1928,1935,1947,1956,1974,1979,2046,2057,2068,2071,2086,2089,2093,2096,2117,2126,2134,2139],"callout-read-more",{"id":60},"55d2cdcf-ba27-4bfe-9bc1-492e866ff3b1",[62,63,64],"blockquote",{},[10,65,66,70,71,75,76],{},[67,68,69],"span",{},"!NOTE","\nRemember to ",[72,73,74],"strong",{},"deliver package code as native as possible"," and let userland tools handle transpiling and optimization. It's a principle I always keep in mind when crafting libraries, streamlining the process significantly. ",[19,77,78],{},"Each case is unique, but this is a solid guideline to follow.",[10,80,81],{},"The use of Vue.js SFC complicates this process. Let's examine why by exploring the challenges you'll encounter when constructing a Vue.js component library.",[10,83,84],{},"Consider the following component:",[86,87,92],"pre",{"className":88,"code":89,"language":90,"meta":91,"style":91},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Cscript lang=\"ts\" setup>\nimport { useUser } from '..\u002Fcomposables'\n\nconst { user } = useUser()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv v-if=\"user\">\n    {{ user.name }}\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n","vue","",[93,94,95,129,158,165,188,198,203,213,236,242,252],"code",{"__ignoreMap":91},[67,96,99,103,107,111,114,117,121,123,126],{"class":97,"line":98},"line",1,[67,100,102],{"class":101},"sMK4o","\u003C",[67,104,106],{"class":105},"swJcz","script",[67,108,110],{"class":109},"spNyl"," lang",[67,112,113],{"class":101},"=",[67,115,116],{"class":101},"\"",[67,118,120],{"class":119},"sfazB","ts",[67,122,116],{"class":101},[67,124,125],{"class":109}," setup",[67,127,128],{"class":101},">\n",[67,130,132,136,139,143,146,149,152,155],{"class":97,"line":131},2,[67,133,135],{"class":134},"s7zQu","import",[67,137,138],{"class":101}," {",[67,140,142],{"class":141},"sTEyZ"," useUser",[67,144,145],{"class":101}," }",[67,147,148],{"class":134}," from",[67,150,151],{"class":101}," '",[67,153,154],{"class":119},"..\u002Fcomposables",[67,156,157],{"class":101},"'\n",[67,159,161],{"class":97,"line":160},3,[67,162,164],{"emptyLinePlaceholder":163},true,"\n",[67,166,168,171,173,176,179,182,185],{"class":97,"line":167},4,[67,169,170],{"class":109},"const",[67,172,138],{"class":101},[67,174,175],{"class":141}," user ",[67,177,178],{"class":101},"}",[67,180,181],{"class":101}," =",[67,183,142],{"class":184},"s2Zo4",[67,186,187],{"class":141},"()\n",[67,189,191,194,196],{"class":97,"line":190},5,[67,192,193],{"class":101},"\u003C\u002F",[67,195,106],{"class":105},[67,197,128],{"class":101},[67,199,201],{"class":97,"line":200},6,[67,202,164],{"emptyLinePlaceholder":163},[67,204,206,208,211],{"class":97,"line":205},7,[67,207,102],{"class":101},[67,209,210],{"class":105},"template",[67,212,128],{"class":101},[67,214,216,219,222,225,227,229,232,234],{"class":97,"line":215},8,[67,217,218],{"class":101},"  \u003C",[67,220,221],{"class":105},"div",[67,223,224],{"class":109}," v-if",[67,226,113],{"class":101},[67,228,116],{"class":101},[67,230,231],{"class":119},"user",[67,233,116],{"class":101},[67,235,128],{"class":101},[67,237,239],{"class":97,"line":238},9,[67,240,241],{"class":141},"    {{ user.name }}\n",[67,243,245,248,250],{"class":97,"line":244},10,[67,246,247],{"class":101},"  \u003C\u002F",[67,249,221],{"class":105},[67,251,128],{"class":101},[67,253,255,257,259],{"class":97,"line":254},11,[67,256,193],{"class":101},[67,258,210],{"class":105},[67,260,128],{"class":101},[10,262,263],{},"The composable is as follows:",[86,265,268],{"className":266,"code":267,"language":120,"meta":91,"style":91},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { ref } from 'vue'\n\nexport function useUser() {\n  const user = ref({ name: 'John Doe' })\n\n  return {\n    user\n  }\n}\n",[93,269,270,289,293,309,346,350,357,362,367],{"__ignoreMap":91},[67,271,272,274,276,279,281,283,285,287],{"class":97,"line":98},[67,273,135],{"class":134},[67,275,138],{"class":101},[67,277,278],{"class":141}," ref",[67,280,145],{"class":101},[67,282,148],{"class":134},[67,284,151],{"class":101},[67,286,90],{"class":119},[67,288,157],{"class":101},[67,290,291],{"class":97,"line":131},[67,292,164],{"emptyLinePlaceholder":163},[67,294,295,298,301,303,306],{"class":97,"line":160},[67,296,297],{"class":134},"export",[67,299,300],{"class":109}," function",[67,302,142],{"class":184},[67,304,305],{"class":101},"()",[67,307,308],{"class":101}," {\n",[67,310,311,314,317,319,321,324,327,330,333,335,338,341,343],{"class":97,"line":167},[67,312,313],{"class":109},"  const",[67,315,316],{"class":141}," user",[67,318,181],{"class":101},[67,320,278],{"class":184},[67,322,323],{"class":105},"(",[67,325,326],{"class":101},"{",[67,328,329],{"class":105}," name",[67,331,332],{"class":101},":",[67,334,151],{"class":101},[67,336,337],{"class":119},"John Doe",[67,339,340],{"class":101},"'",[67,342,145],{"class":101},[67,344,345],{"class":105},")\n",[67,347,348],{"class":97,"line":190},[67,349,164],{"emptyLinePlaceholder":163},[67,351,352,355],{"class":97,"line":200},[67,353,354],{"class":134},"  return",[67,356,308],{"class":101},[67,358,359],{"class":97,"line":205},[67,360,361],{"class":141},"    user\n",[67,363,364],{"class":97,"line":215},[67,365,366],{"class":101},"  }\n",[67,368,369],{"class":97,"line":238},[67,370,371],{"class":101},"}\n",[10,373,374,375],{},"The component employs a composable imported from another file. ",[19,376,377],{},"It may seem trivial, but this poses a significant issue.",[10,379,380],{},"All of this follows the architecture below:",[86,382,387],{"className":383,"code":385,"language":386},[384],"language-text","src\u002F\n  components\u002F\n    User.vue\n  composables\u002F\n    useUser.ts\n  index.ts\n","text",[93,388,385],{"__ignoreMap":91},[10,390,391,392,395],{},"The ",[93,393,394],{},"index.ts"," file acts as the library's entry point and it looks like this:",[86,397,399],{"className":266,"code":398,"language":120,"meta":91,"style":91},"import User from '.\u002Fcomponents\u002FUser.vue'\n\nexport { User }\n\nexport { useUser } from '.\u002Fcomposables\u002FuseUser'\n",[93,400,401,418,422,434,438],{"__ignoreMap":91},[67,402,403,405,408,411,413,416],{"class":97,"line":98},[67,404,135],{"class":134},[67,406,407],{"class":141}," User ",[67,409,410],{"class":134},"from",[67,412,151],{"class":101},[67,414,415],{"class":119},".\u002Fcomponents\u002FUser.vue",[67,417,157],{"class":101},[67,419,420],{"class":97,"line":131},[67,421,164],{"emptyLinePlaceholder":163},[67,423,424,426,428,431],{"class":97,"line":160},[67,425,297],{"class":134},[67,427,138],{"class":101},[67,429,430],{"class":141}," User",[67,432,433],{"class":101}," }\n",[67,435,436],{"class":97,"line":167},[67,437,164],{"emptyLinePlaceholder":163},[67,439,440,442,444,446,448,450,452,455],{"class":97,"line":190},[67,441,297],{"class":134},[67,443,138],{"class":101},[67,445,142],{"class":141},[67,447,145],{"class":101},[67,449,148],{"class":134},[67,451,151],{"class":101},[67,453,454],{"class":119},".\u002Fcomposables\u002FuseUser",[67,456,157],{"class":101},[10,458,459,460,463,464,332],{},"Plus, the ",[93,461,462],{},"package.json"," includes some ",[93,465,466],{},"exports",[86,468,472],{"className":469,"code":470,"language":471,"meta":91,"style":91},"language-json shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","{\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \".\u002Fdist\u002Findex.d.ts\",\n      \"import\": \".\u002Fdist\u002Findex.mjs\"\n    }\n  },\n  \"main\": \"dist\u002Findex.mjs\",\n  \"types\": \"dist\u002Findex.d.ts\"\n}\n","json",[93,473,474,479,502,514,528,550,568,573,578,598,615],{"__ignoreMap":91},[67,475,476],{"class":97,"line":98},[67,477,478],{"class":101},"{\n",[67,480,481,484,487,489,491,494,497,499],{"class":97,"line":131},[67,482,483],{"class":101},"  \"",[67,485,486],{"class":109},"type",[67,488,116],{"class":101},[67,490,332],{"class":101},[67,492,493],{"class":101}," \"",[67,495,496],{"class":119},"module",[67,498,116],{"class":101},[67,500,501],{"class":101},",\n",[67,503,504,506,508,510,512],{"class":97,"line":160},[67,505,483],{"class":101},[67,507,466],{"class":109},[67,509,116],{"class":101},[67,511,332],{"class":101},[67,513,308],{"class":101},[67,515,516,519,522,524,526],{"class":97,"line":167},[67,517,518],{"class":101},"    \"",[67,520,56],{"class":521},"sBMFI",[67,523,116],{"class":101},[67,525,332],{"class":101},[67,527,308],{"class":101},[67,529,530,533,537,539,541,543,546,548],{"class":97,"line":190},[67,531,532],{"class":101},"      \"",[67,534,536],{"class":535},"sbssI","types",[67,538,116],{"class":101},[67,540,332],{"class":101},[67,542,493],{"class":101},[67,544,545],{"class":119},".\u002Fdist\u002Findex.d.ts",[67,547,116],{"class":101},[67,549,501],{"class":101},[67,551,552,554,556,558,560,562,565],{"class":97,"line":200},[67,553,532],{"class":101},[67,555,135],{"class":535},[67,557,116],{"class":101},[67,559,332],{"class":101},[67,561,493],{"class":101},[67,563,564],{"class":119},".\u002Fdist\u002Findex.mjs",[67,566,567],{"class":101},"\"\n",[67,569,570],{"class":97,"line":205},[67,571,572],{"class":101},"    }\n",[67,574,575],{"class":97,"line":215},[67,576,577],{"class":101},"  },\n",[67,579,580,582,585,587,589,591,594,596],{"class":97,"line":238},[67,581,483],{"class":101},[67,583,584],{"class":109},"main",[67,586,116],{"class":101},[67,588,332],{"class":101},[67,590,493],{"class":101},[67,592,593],{"class":119},"dist\u002Findex.mjs",[67,595,116],{"class":101},[67,597,501],{"class":101},[67,599,600,602,604,606,608,610,613],{"class":97,"line":244},[67,601,483],{"class":101},[67,603,536],{"class":109},[67,605,116],{"class":101},[67,607,332],{"class":101},[67,609,493],{"class":101},[67,611,612],{"class":119},"dist\u002Findex.d.ts",[67,614,567],{"class":101},[67,616,617],{"class":97,"line":254},[67,618,371],{"class":101},[620,621,623],"h2",{"id":622},"similar-to-a-standard-typescript-project","Similar to a standard TypeScript project",[62,625,626],{},[10,627,628,630],{},[67,629,69],{},"\nA bundle is a single file encapsulating all your project's code. It optimizes project loading by minimizing the number of files to load. This bundle isn't transpiled (except TypeScript to JavaScript) or minified when creating an npm package.",[10,632,633,634,637],{},"Now, if we attempt to bundle this project, our ",[93,635,636],{},"dist"," folder will appear like this:",[86,639,642],{"className":640,"code":641,"language":386},[384],"dist\u002F\n  index.cjs\n",[93,643,641],{"__ignoreMap":91},[10,645,646,647,650,651,654,655],{},"Upon inspecting ",[93,648,649],{},"index.cjs",", you'll find a surprise: the ",[93,652,653],{},"useUser"," composable is inlined, but the component is entirely ignored. ",[19,656,657,658,661,662,50,667,672],{},"If it functions, the bundler might throw an error like ",[93,659,660],{},"No loader is configured for \".vue\" files",". Essentially, tools like ",[32,663,666],{"href":664,"rel":665},"https:\u002F\u002Fesbuild.github.io",[36],"esbuild",[32,668,671],{"href":669,"rel":670},"https:\u002F\u002Fwww.typescriptlang.org\u002Fdocs\u002Fhandbook\u002Fcompiler-options.html",[36],"tsc"," don't know how to handle Vue files. It's logical since Vue files aren't JavaScript files.",[10,674,675,676,679],{},"The problem becomes evident. We need some configuration to handle Vue files. Let's quickly browse the internet for something like ",[93,677,678],{},"vue loader",", as we're utilizing Vue files and need to transpile them to JavaScript.",[10,681,682,683,688,689],{},"The top result is ",[32,684,687],{"href":685,"rel":686},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fvue-loader",[36],"Vue Loader",", which is a Webpack loader. ",[19,690,691],{},"No. I refuse to use Webpack. Vite is now a standard, and there's no justification for employing Webpack in this context.",[10,693,694,695,17,697,705],{},"But before delving further, let's revisit our mantra: ",[72,696,74],{},[19,698,699,700],{},"and ",[32,701,704],{"href":702,"rel":703},"https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FKISS_principle",[36],"keep it simple, stupid!"," Transpiling Vue SFC files using Webpack seems contrary to this principle.",[620,707,709],{"id":708},"ignoring-vue-files","Ignoring Vue files",[10,711,712,713,716,717,722,723,728],{},"To deliver ",[93,714,715],{},".vue"," files without transpiling them, we instruct our bundler to disregard them. This is achieved using a tool called ",[32,718,721],{"href":719,"rel":720},"https:\u002F\u002Funbuild.unjs.io",[36],"unbuild"," from the ",[32,724,727],{"href":725,"rel":726},"https:\u002F\u002Funjs.io",[36],"UnJS"," ecosystem.",[10,730,731],{},"Unbuild is a straightforward tool yet highly configurable since it's constructed atop rollup.",[10,733,734],{},"For our small project, we can directly experiment with it:",[86,736,740],{"className":737,"code":738,"language":739,"meta":91,"style":91},"language-sh shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","npx unbuild\n","sh",[93,741,742],{"__ignoreMap":91},[67,743,744,747],{"class":97,"line":98},[67,745,746],{"class":521},"npx",[67,748,749],{"class":119}," unbuild\n",[10,751,752],{},"Unfortunately, it fails for the same reasons as before:",[86,754,758],{"className":755,"code":756,"language":757,"meta":91,"style":91},"language-txt shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","src\u002Fcomponents\u002FShowGitHubUser.vue (1:0): Expression expected (Note that you need plugins to import files that are not JavaScript)\n","txt",[93,759,760],{"__ignoreMap":91},[67,761,762],{"class":97,"line":98},[67,763,756],{},[10,765,766],{},[19,767,768],{},"But unbuild has the potential for much more.",[10,770,771,772,774],{},"To tackle this problem, let's attempt the obvious solution. In the ",[93,773,394],{}," file, remove the component export and solely export TypeScript files:",[86,776,778],{"className":266,"code":777,"language":120,"meta":91,"style":91},"export { useUser } from '.\u002Fcomposables\u002FuseUser'\n",[93,779,780],{"__ignoreMap":91},[67,781,782,784,786,788,790,792,794,796],{"class":97,"line":98},[67,783,297],{"class":134},[67,785,138],{"class":101},[67,787,142],{"class":141},[67,789,145],{"class":101},[67,791,148],{"class":134},[67,793,151],{"class":101},[67,795,454],{"class":119},[67,797,157],{"class":101},[10,799,800,801,804],{},"Now, the command ",[93,802,803],{},"npx unbuild"," functions properly.",[86,806,808],{"className":737,"code":807,"language":739,"meta":91,"style":91},"➜ npx unbuild\nℹ Automatically detected entries: src\u002Findex [esm] [dts]\nℹ Building vue-library\nℹ Cleaning dist directory: .\u002Fdist\n✔ Build succeeded for vue-library\n  dist\u002Findex.mjs (total size: 139 B, chunk size: 139 B, exports: useUser)\n\nΣ Total dist size (byte size): 531 B\n",[93,809,810,820,849,859,875,891,925,929],{"__ignoreMap":91},[67,811,812,815,818],{"class":97,"line":98},[67,813,814],{"class":521},"➜",[67,816,817],{"class":119}," npx",[67,819,749],{"class":119},[67,821,822,825,828,831,834,837,840,843,846],{"class":97,"line":131},[67,823,824],{"class":521},"ℹ",[67,826,827],{"class":119}," Automatically",[67,829,830],{"class":119}," detected",[67,832,833],{"class":119}," entries:",[67,835,836],{"class":119}," src\u002Findex",[67,838,839],{"class":141}," [esm] ",[67,841,842],{"class":101},"[",[67,844,845],{"class":141},"dts",[67,847,848],{"class":101},"]\n",[67,850,851,853,856],{"class":97,"line":160},[67,852,824],{"class":521},[67,854,855],{"class":119}," Building",[67,857,858],{"class":119}," vue-library\n",[67,860,861,863,866,869,872],{"class":97,"line":167},[67,862,824],{"class":521},[67,864,865],{"class":119}," Cleaning",[67,867,868],{"class":119}," dist",[67,870,871],{"class":119}," directory:",[67,873,874],{"class":119}," .\u002Fdist\n",[67,876,877,880,883,886,889],{"class":97,"line":190},[67,878,879],{"class":521},"✔",[67,881,882],{"class":119}," Build",[67,884,885],{"class":119}," succeeded",[67,887,888],{"class":119}," for",[67,890,858],{"class":119},[67,892,893,896,899,902,905,908,911,914,916,918,921,923],{"class":97,"line":200},[67,894,895],{"class":521},"  dist\u002Findex.mjs",[67,897,898],{"class":141}," (total ",[67,900,901],{"class":119},"size:",[67,903,904],{"class":535}," 139",[67,906,907],{"class":119}," B,",[67,909,910],{"class":119}," chunk",[67,912,913],{"class":119}," size:",[67,915,904],{"class":535},[67,917,907],{"class":119},[67,919,920],{"class":119}," exports:",[67,922,142],{"class":119},[67,924,345],{"class":141},[67,926,927],{"class":97,"line":205},[67,928,164],{"emptyLinePlaceholder":163},[67,930,931,934,937,939,942,945,948],{"class":97,"line":215},[67,932,933],{"class":521},"Σ",[67,935,936],{"class":119}," Total",[67,938,868],{"class":119},[67,940,941],{"class":119}," size",[67,943,944],{"class":141}," (byte ",[67,946,947],{"class":119},"size",[67,949,950],{"class":141},"): 531 B\n",[10,952,953,954,957,958,960],{},"This is a good start, but our components remain in the ",[93,955,956],{},"src"," folder and no matter how diligently you search, they're missing from the ",[93,959,636],{}," folder.",[620,962,964],{"id":963},"copying-vue-files","Copying Vue files",[10,966,967,968,971,972,974,975,977,978,981],{},"Now that ",[93,969,970],{},".ts"," files are processed correctly, we can attempt to copy the ",[93,973,715],{}," files to the ",[93,976,636],{}," folder using a simple ",[93,979,980],{},"cp"," command.",[86,983,985],{"className":737,"code":984,"language":739,"meta":91,"style":91},"cp -r src\u002Fcomponents\u002F dist\u002Fcomponents\u002F\n",[93,986,987],{"__ignoreMap":91},[67,988,989,991,994,997],{"class":97,"line":98},[67,990,980],{"class":521},[67,992,993],{"class":119}," -r",[67,995,996],{"class":119}," src\u002Fcomponents\u002F",[67,998,999],{"class":119}," dist\u002Fcomponents\u002F\n",[10,1001,1002,1003,1005,1006,1008],{},"Subsequently, we add an ",[93,1004,466],{}," field in the ",[93,1007,462],{}," to inform the user where to locate the components:",[86,1010,1012],{"className":469,"code":1011,"language":471,"meta":91,"style":91},"{\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \".\u002Fdist\u002Findex.d.ts\",\n      \"import\": \".\u002Fdist\u002Findex.mjs\"\n    },\n    \".\u002Fcomponents\u002F*\": {\n      \"import\": \".\u002Fdist\u002Fcomponents\u002F*.vue\"\n    }\n  },\n  \"main\": \"dist\u002Findex.mjs\",\n  \"types\": \"dist\u002Findex.d.ts\"\n}\n",[93,1013,1014,1018,1036,1048,1060,1078,1094,1099,1112,1129,1133,1137,1156,1173],{"__ignoreMap":91},[67,1015,1016],{"class":97,"line":98},[67,1017,478],{"class":101},[67,1019,1020,1022,1024,1026,1028,1030,1032,1034],{"class":97,"line":131},[67,1021,483],{"class":101},[67,1023,486],{"class":109},[67,1025,116],{"class":101},[67,1027,332],{"class":101},[67,1029,493],{"class":101},[67,1031,496],{"class":119},[67,1033,116],{"class":101},[67,1035,501],{"class":101},[67,1037,1038,1040,1042,1044,1046],{"class":97,"line":160},[67,1039,483],{"class":101},[67,1041,466],{"class":109},[67,1043,116],{"class":101},[67,1045,332],{"class":101},[67,1047,308],{"class":101},[67,1049,1050,1052,1054,1056,1058],{"class":97,"line":167},[67,1051,518],{"class":101},[67,1053,56],{"class":521},[67,1055,116],{"class":101},[67,1057,332],{"class":101},[67,1059,308],{"class":101},[67,1061,1062,1064,1066,1068,1070,1072,1074,1076],{"class":97,"line":190},[67,1063,532],{"class":101},[67,1065,536],{"class":535},[67,1067,116],{"class":101},[67,1069,332],{"class":101},[67,1071,493],{"class":101},[67,1073,545],{"class":119},[67,1075,116],{"class":101},[67,1077,501],{"class":101},[67,1079,1080,1082,1084,1086,1088,1090,1092],{"class":97,"line":200},[67,1081,532],{"class":101},[67,1083,135],{"class":535},[67,1085,116],{"class":101},[67,1087,332],{"class":101},[67,1089,493],{"class":101},[67,1091,564],{"class":119},[67,1093,567],{"class":101},[67,1095,1096],{"class":97,"line":205},[67,1097,1098],{"class":101},"    },\n",[67,1100,1101,1103,1106,1108,1110],{"class":97,"line":215},[67,1102,518],{"class":101},[67,1104,1105],{"class":521},".\u002Fcomponents\u002F*",[67,1107,116],{"class":101},[67,1109,332],{"class":101},[67,1111,308],{"class":101},[67,1113,1114,1116,1118,1120,1122,1124,1127],{"class":97,"line":238},[67,1115,532],{"class":101},[67,1117,135],{"class":535},[67,1119,116],{"class":101},[67,1121,332],{"class":101},[67,1123,493],{"class":101},[67,1125,1126],{"class":119},".\u002Fdist\u002Fcomponents\u002F*.vue",[67,1128,567],{"class":101},[67,1130,1131],{"class":97,"line":244},[67,1132,572],{"class":101},[67,1134,1135],{"class":97,"line":254},[67,1136,577],{"class":101},[67,1138,1140,1142,1144,1146,1148,1150,1152,1154],{"class":97,"line":1139},12,[67,1141,483],{"class":101},[67,1143,584],{"class":109},[67,1145,116],{"class":101},[67,1147,332],{"class":101},[67,1149,493],{"class":101},[67,1151,593],{"class":119},[67,1153,116],{"class":101},[67,1155,501],{"class":101},[67,1157,1159,1161,1163,1165,1167,1169,1171],{"class":97,"line":1158},13,[67,1160,483],{"class":101},[67,1162,536],{"class":109},[67,1164,116],{"class":101},[67,1166,332],{"class":101},[67,1168,493],{"class":101},[67,1170,612],{"class":119},[67,1172,567],{"class":101},[67,1174,1176],{"class":97,"line":1175},14,[67,1177,371],{"class":101},[10,1179,1180,1181,1184],{},"Looks promising, doesn't it? ",[19,1182,1183],{},"But it isn't."," 😔",[620,1186,1188],{"id":1187},"the-import-path-dilemma","The import path dilemma",[10,1190,1191,1192,1194],{},"In our Vue component, the ",[93,1193,653],{}," composable is imported like this:",[86,1196,1198],{"className":88,"code":1197,"language":90,"meta":91,"style":91},"\u003Cscript lang=\"ts\" setup>\nimport { useUser } from '..\u002Fcomposables'\n\u003C\u002Fscript>\n",[93,1199,1200,1220,1238],{"__ignoreMap":91},[67,1201,1202,1204,1206,1208,1210,1212,1214,1216,1218],{"class":97,"line":98},[67,1203,102],{"class":101},[67,1205,106],{"class":105},[67,1207,110],{"class":109},[67,1209,113],{"class":101},[67,1211,116],{"class":101},[67,1213,120],{"class":119},[67,1215,116],{"class":101},[67,1217,125],{"class":109},[67,1219,128],{"class":101},[67,1221,1222,1224,1226,1228,1230,1232,1234,1236],{"class":97,"line":131},[67,1223,135],{"class":134},[67,1225,138],{"class":101},[67,1227,142],{"class":141},[67,1229,145],{"class":101},[67,1231,148],{"class":134},[67,1233,151],{"class":101},[67,1235,154],{"class":119},[67,1237,157],{"class":101},[67,1239,1240,1242,1244],{"class":97,"line":160},[67,1241,193],{"class":101},[67,1243,106],{"class":105},[67,1245,128],{"class":101},[10,1247,1248,1249,1251],{},"Meanwhile, our ",[93,1250,636],{}," folder is structured as follows:",[86,1253,1256],{"className":1254,"code":1255,"language":386},[384],"dist\u002F\n  components\u002F\n    User.vue\n  index.d.mts\n  index.d.ts\n  index.mjs\n",[93,1257,1255],{"__ignoreMap":91},[10,1259,1260],{},"Can you identify the problem? 👀",[10,1262,1263,1264,1267,1268,1271],{},"The component is unusable and will produce an error like ",[93,1265,1266],{},"Cannot find module '..\u002Fcomposables' or its corresponding type declarations",", because the path leads nowhere. We've bundled all of our TypeScript files into a single file ",[93,1269,1270],{},"index.mjs",", losing the project's structure entirely.",[620,1273,1275],{"id":1274},"to-bundle-or-not-to-bundle","To bundle or not to bundle?",[10,1277,1278],{},"From here, we have two choices:",[1280,1281,1282,1294],"ol",{},[1283,1284,1285,1288,1289,1293],"li",{},[72,1286,1287],{},"Bundling the Vue files"," using ",[32,1290,55],{"href":1291,"rel":1292},"https:\u002F\u002Fvitejs.dev",[36],", plugins, and extensive configuration.",[1283,1295,1296,1299,1300,1305],{},[72,1297,1298],{},"Maintaining the directory structure"," and using a tool like ",[32,1301,1304],{"href":1302,"rel":1303},"https:\u002F\u002Fgithub.com\u002Funjs\u002Fmkdist",[36],"mkdist"," for file-to-file transpiling (bundleless build) on TypeScript files.",[10,1307,1308],{},"The decision largely hinges on your specific needs, but I'm convinced that simplicity is the best approach. So, let's explore the second option.",[10,1310,1311,1312,1314,1315,1317,1318,1321],{},"The goal is to transform the TypeScript files into JavaScript files, preserving the original structure of the ",[93,1313,956],{}," folder in the ",[93,1316,636],{}," folder and ",[19,1319,1320],{},"ignoring"," the Vue files.",[62,1323,1324],{},[10,1325,1326,1328,1329,1332,1333,1336,1337,1342,1343,56],{},[67,1327,69],{},"\nUsing a ",[93,1330,1331],{},"\u003Cscript setup lang=\"ts\">"," block, mkdist will ignore Vue files to enable the Vue compiler to generate runtime props. If not, it will generate a ",[93,1334,1335],{},".vue.d.ts"," file to offer types for the Vue component. Remember that the ",[32,1338,1341],{"href":1339,"rel":1340},"https:\u002F\u002Fgithub.com\u002Fvitejs\u002Fvite-plugin-vue\u002Ftree\u002Fmain\u002Fpackages\u002Fplugin-vue",[36],"@vitejs\u002Fplugin-vue"," and Vite inherently understand TypeScript script blocks. For more details, refer to ",[32,1344,1347],{"href":1345,"rel":1346},"https:\u002F\u002Fgithub.com\u002Funjs\u002Fmkdist\u002Fissues\u002F14",[36],"issue mkdist#14",[620,1349,1351],{"id":1350},"configuring-unbuild","Configuring Unbuild",[10,1353,1354],{},"Yes, we will configure unbuild since it incorporates mkdist, making it exceptionally user-friendly.",[10,1356,1357,1358,1361,1362,1364],{},"First, create a ",[93,1359,1360],{},"build.config.ts"," file at the project's root. Unbuild will read this configuration file to understand how to process the project. It assumes defaults and infers many aspects from the ",[93,1363,462],{},", but we need to tell it to utilize mkdist.",[86,1366,1368],{"className":266,"code":1367,"language":120,"meta":91,"style":91},"import { defineBuildConfig } from 'unbuild'\n\nexport default defineBuildConfig({\n  entries: ['.\u002Fsrc\u002F'],\n  declaration: true,\n})\n",[93,1369,1370,1389,1393,1406,1428,1441],{"__ignoreMap":91},[67,1371,1372,1374,1376,1379,1381,1383,1385,1387],{"class":97,"line":98},[67,1373,135],{"class":134},[67,1375,138],{"class":101},[67,1377,1378],{"class":141}," defineBuildConfig",[67,1380,145],{"class":101},[67,1382,148],{"class":134},[67,1384,151],{"class":101},[67,1386,721],{"class":119},[67,1388,157],{"class":101},[67,1390,1391],{"class":97,"line":131},[67,1392,164],{"emptyLinePlaceholder":163},[67,1394,1395,1397,1400,1402,1404],{"class":97,"line":160},[67,1396,297],{"class":134},[67,1398,1399],{"class":134}," default",[67,1401,1378],{"class":184},[67,1403,323],{"class":141},[67,1405,478],{"class":101},[67,1407,1408,1411,1413,1416,1418,1421,1423,1426],{"class":97,"line":167},[67,1409,1410],{"class":105},"  entries",[67,1412,332],{"class":101},[67,1414,1415],{"class":141}," [",[67,1417,340],{"class":101},[67,1419,1420],{"class":119},".\u002Fsrc\u002F",[67,1422,340],{"class":101},[67,1424,1425],{"class":141},"]",[67,1427,501],{"class":101},[67,1429,1430,1433,1435,1439],{"class":97,"line":190},[67,1431,1432],{"class":105},"  declaration",[67,1434,332],{"class":101},[67,1436,1438],{"class":1437},"sfNiH"," true",[67,1440,501],{"class":101},[67,1442,1443,1445],{"class":97,"line":200},[67,1444,178],{"class":101},[67,1446,345],{"class":141},[10,1448,1449,1450,1453,1454,1457,1458,1460,1461,1464,1465,1468,1469,1471,1472,1475,1476,1479],{},"It's a very straightforward configuration file. ",[19,1451,1452],{},"And I adore it."," The ",[93,1455,1456],{},"entries"," key informs unbuild of the initial file to begin transpiling. However, ",[93,1459,1420],{}," is a directory, indicated by the ",[93,1462,1463],{},"\u002F"," at the end. With this hint, unbuild bypasses the default bundler ",[93,1466,1467],{},"rollup"," in favor of ",[93,1470,1304],{},". The ",[93,1473,1474],{},"declaration"," key prompts unbuild to create TypeScript declaration files (",[93,1477,1478],{},".d.ts",").",[10,1481,1482,1483,1005,1485,1487,1488,1490],{},"We can also remove the ",[93,1484,466],{},[93,1486,462],{}," since we won't be bundling files anymore and restore the component export in the ",[93,1489,394],{}," file.",[10,1492,1493,1494,1496],{},"Let's rerun the ",[93,1495,803],{}," command and witness the magic.",[86,1498,1500],{"className":737,"code":1499,"language":739,"meta":91,"style":91},"➜ npx unbuild\nℹ Building vue-library\nℹ Cleaning dist directory: .\u002Fdist\n✔ Build succeeded for vue-library\n  dist (total size: 600 B)\n  └─ dist\u002Findex.d.ts (108 B)\n  └─ dist\u002Findex.mjs (112 B)\n  └─ dist\u002Fcomposables\u002FuseUser.d.ts (79 B)\n  └─ dist\u002Fcomposables\u002FuseUser.mjs (124 B)\n  └─ dist\u002Fcomponents\u002FUser.vue (177 B)\nΣ Total dist size (byte size): 600 B\n",[93,1501,1502,1510,1518,1530,1542,1559,1575,1589,1603,1617,1631],{"__ignoreMap":91},[67,1503,1504,1506,1508],{"class":97,"line":98},[67,1505,814],{"class":521},[67,1507,817],{"class":119},[67,1509,749],{"class":119},[67,1511,1512,1514,1516],{"class":97,"line":131},[67,1513,824],{"class":521},[67,1515,855],{"class":119},[67,1517,858],{"class":119},[67,1519,1520,1522,1524,1526,1528],{"class":97,"line":160},[67,1521,824],{"class":521},[67,1523,865],{"class":119},[67,1525,868],{"class":119},[67,1527,871],{"class":119},[67,1529,874],{"class":119},[67,1531,1532,1534,1536,1538,1540],{"class":97,"line":167},[67,1533,879],{"class":521},[67,1535,882],{"class":119},[67,1537,885],{"class":119},[67,1539,888],{"class":119},[67,1541,858],{"class":119},[67,1543,1544,1547,1549,1551,1554,1557],{"class":97,"line":190},[67,1545,1546],{"class":521},"  dist",[67,1548,898],{"class":141},[67,1550,901],{"class":119},[67,1552,1553],{"class":535}," 600",[67,1555,1556],{"class":119}," B",[67,1558,345],{"class":141},[67,1560,1561,1564,1567,1570,1573],{"class":97,"line":200},[67,1562,1563],{"class":521},"  └─",[67,1565,1566],{"class":119}," dist\u002Findex.d.ts",[67,1568,1569],{"class":141}," (108 ",[67,1571,1572],{"class":119},"B",[67,1574,345],{"class":141},[67,1576,1577,1579,1582,1585,1587],{"class":97,"line":205},[67,1578,1563],{"class":521},[67,1580,1581],{"class":119}," dist\u002Findex.mjs",[67,1583,1584],{"class":141}," (112 ",[67,1586,1572],{"class":119},[67,1588,345],{"class":141},[67,1590,1591,1593,1596,1599,1601],{"class":97,"line":215},[67,1592,1563],{"class":521},[67,1594,1595],{"class":119}," dist\u002Fcomposables\u002FuseUser.d.ts",[67,1597,1598],{"class":141}," (79 ",[67,1600,1572],{"class":119},[67,1602,345],{"class":141},[67,1604,1605,1607,1610,1613,1615],{"class":97,"line":238},[67,1606,1563],{"class":521},[67,1608,1609],{"class":119}," dist\u002Fcomposables\u002FuseUser.mjs",[67,1611,1612],{"class":141}," (124 ",[67,1614,1572],{"class":119},[67,1616,345],{"class":141},[67,1618,1619,1621,1624,1627,1629],{"class":97,"line":244},[67,1620,1563],{"class":521},[67,1622,1623],{"class":119}," dist\u002Fcomponents\u002FUser.vue",[67,1625,1626],{"class":141}," (177 ",[67,1628,1572],{"class":119},[67,1630,345],{"class":141},[67,1632,1633,1635,1637,1639,1641,1643,1645],{"class":97,"line":254},[67,1634,933],{"class":521},[67,1636,936],{"class":119},[67,1638,868],{"class":119},[67,1640,941],{"class":119},[67,1642,944],{"class":141},[67,1644,947],{"class":119},[67,1646,1647],{"class":141},"): 600 B\n",[10,1649,1650,1651,1653],{},"Now, the ",[93,1652,636],{}," folder appears like this:",[86,1655,1658],{"className":1656,"code":1657,"language":386},[384],"dist\u002F\n  components\u002F\n    User.vue\n  composables\u002F\n    useUser.d.ts\n    useUser.mjs\n  index.d.ts\n  index.mjs\n",[93,1659,1657],{"__ignoreMap":91},[10,1661,1662,1663,1665,1666,1669,1670,1672],{},"This mirrors the structure of the ",[93,1664,956],{}," folder, and the ",[93,1667,1668],{},"User.vue"," component is now usable in the ",[93,1671,636],{}," folder. The relative import path remains intact, and the component can be utilized in any Vue project. :partying_face:",[620,1674,1676],{"id":1675},"local-development","Local development",[10,1678,1679,1680,1683],{},"Many tutorials halt here, suggesting you're ready to publish your package to npm. But how can you create a complex library if you can't ",[72,1681,1682],{},"see the outcome of your work","? If you can't test it while building it?",[10,1685,1686],{},"Basically, you can't.",[10,1688,1689],{},"Let's examine how to use the library in a local project, within the same repository for simplicity.",[10,1691,1692,1693,56],{},"I'll demonstrate the simplest method using a ",[32,1694,1697],{"href":1695,"rel":1696},"https:\u002F\u002Fpnpm.io\u002F",[36],"pnpm workspace",[10,1699,1700,1701,1704],{},"Start by creating a ",[93,1702,1703],{},"pnpm-workspace.yaml"," file at the project's root:",[86,1706,1710],{"className":1707,"code":1708,"language":1709,"meta":91,"style":91},"language-yaml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","packages:\n  - .\n  - playground\n","yaml",[93,1711,1712,1720,1728],{"__ignoreMap":91},[67,1713,1714,1717],{"class":97,"line":98},[67,1715,1716],{"class":105},"packages",[67,1718,1719],{"class":101},":\n",[67,1721,1722,1725],{"class":97,"line":131},[67,1723,1724],{"class":101},"  -",[67,1726,1727],{"class":535}," .\n",[67,1729,1730,1732],{"class":97,"line":160},[67,1731,1724],{"class":101},[67,1733,1734],{"class":119}," playground\n",[10,1736,1737,1738,1741],{},"Then, create a new Vite project in a ",[93,1739,1740],{},"playground"," folder:",[86,1743,1745],{"className":737,"code":1744,"language":739,"meta":91,"style":91},"npx create-vite playground --template vue-ts\n",[93,1746,1747],{"__ignoreMap":91},[67,1748,1749,1751,1754,1757,1760],{"class":97,"line":98},[67,1750,746],{"class":521},[67,1752,1753],{"class":119}," create-vite",[67,1755,1756],{"class":119}," playground",[67,1758,1759],{"class":119}," --template",[67,1761,1762],{"class":119}," vue-ts\n",[10,1764,1765],{},"Since we're using a pnpm workspace, install the dependencies from the project's root:",[86,1767,1769],{"className":737,"code":1768,"language":739,"meta":91,"style":91},"pnpm install\n",[93,1770,1771],{"__ignoreMap":91},[67,1772,1773,1776],{"class":97,"line":98},[67,1774,1775],{"class":521},"pnpm",[67,1777,1778],{"class":119}," install\n",[62,1780,1781],{},[10,1782,1783,1785,1786,1789],{},[67,1784,69],{},"\nThis step isn't mandatory. You can create a new Vite project and install dependencies within it. Pnpm simplifies this by installing dependencies for all workspaces in the root. A simple ",[93,1787,1788],{},"pnpm install"," installs dependencies from all workspaces.",[10,1791,1792,1793,1795],{},"Now, we can use this ",[93,1794,1740],{}," library within a \"real\" project to test our library.",[10,1797,1798,1799,1802,1803,1806],{},"For instance, open the ",[93,1800,1801],{},"src\u002FApp.vue"," file and import the ",[93,1804,1805],{},"User"," component:",[86,1808,1810],{"className":88,"code":1809,"language":90,"meta":91,"style":91},"\u003Cscript setup lang=\"ts\">\nimport { User } from '..\u002F..\u002Fsrc'\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CUser \u002F>\n\u003C\u002Ftemplate>\n",[93,1811,1812,1832,1851,1859,1863,1871,1880],{"__ignoreMap":91},[67,1813,1814,1816,1818,1820,1822,1824,1826,1828,1830],{"class":97,"line":98},[67,1815,102],{"class":101},[67,1817,106],{"class":105},[67,1819,125],{"class":109},[67,1821,110],{"class":109},[67,1823,113],{"class":101},[67,1825,116],{"class":101},[67,1827,120],{"class":119},[67,1829,116],{"class":101},[67,1831,128],{"class":101},[67,1833,1834,1836,1838,1840,1842,1844,1846,1849],{"class":97,"line":131},[67,1835,135],{"class":134},[67,1837,138],{"class":101},[67,1839,430],{"class":141},[67,1841,145],{"class":101},[67,1843,148],{"class":134},[67,1845,151],{"class":101},[67,1847,1848],{"class":119},"..\u002F..\u002Fsrc",[67,1850,157],{"class":101},[67,1852,1853,1855,1857],{"class":97,"line":160},[67,1854,193],{"class":101},[67,1856,106],{"class":105},[67,1858,128],{"class":101},[67,1860,1861],{"class":97,"line":167},[67,1862,164],{"emptyLinePlaceholder":163},[67,1864,1865,1867,1869],{"class":97,"line":190},[67,1866,102],{"class":101},[67,1868,210],{"class":105},[67,1870,128],{"class":101},[67,1872,1873,1875,1877],{"class":97,"line":200},[67,1874,218],{"class":101},[67,1876,1805],{"class":105},[67,1878,1879],{"class":101}," \u002F>\n",[67,1881,1882,1884,1886],{"class":97,"line":205},[67,1883,193],{"class":101},[67,1885,210],{"class":105},[67,1887,128],{"class":101},[10,1889,1890],{},"And run the project:",[86,1892,1894],{"className":737,"code":1893,"language":739,"meta":91,"style":91},"cd playground && pnpm dev\n",[93,1895,1896],{"__ignoreMap":91},[67,1897,1898,1901,1903,1906,1909],{"class":97,"line":98},[67,1899,1900],{"class":184},"cd",[67,1902,1756],{"class":119},[67,1904,1905],{"class":101}," &&",[67,1907,1908],{"class":521}," pnpm",[67,1910,1911],{"class":119}," dev\n",[10,1913,1914,1915,1917,1918],{},"You'll see the ",[93,1916,1805],{}," component displayed in the browser. 🎉 So simple, yet so powerful. ",[19,1919,1920],{},"I use this method for virtually every library I build, and it works brilliantly.",[620,1922,1924],{"id":1923},"publishing-to-npm","Publishing to npm",[10,1926,1927],{},"This part is straightforward once you grasp the workflow.",[10,1929,1930,1931,1934],{},"Begin by naming your library. I'll use ",[93,1932,1933],{},"@barbapapazes\u002Fvue-library"," for this example.",[10,1936,1937,1938,1943,1944,981],{},"Next, create an account on ",[32,1939,1942],{"href":1940,"rel":1941},"https:\u002F\u002Fwww.npmjs.com",[36],"npm"," and log in using the ",[93,1945,1946],{},"npm login",[10,1948,1949,1950,1955],{},"Then, install ",[32,1951,1954],{"href":1952,"rel":1953},"https:\u002F\u002Fchangelogen.unjs.io",[36],"changelogen"," to generate a changelog, update the version number following semantic versioning and commit convention, and to pre-fill the GitHub release.",[86,1957,1959],{"className":737,"code":1958,"language":739,"meta":91,"style":91},"pnpm i -D changelogen\n",[93,1960,1961],{"__ignoreMap":91},[67,1962,1963,1965,1968,1971],{"class":97,"line":98},[67,1964,1775],{"class":521},[67,1966,1967],{"class":119}," i",[67,1969,1970],{"class":119}," -D",[67,1972,1973],{"class":119}," changelogen\n",[10,1975,1976,1977,332],{},"Next, add two scripts to your ",[93,1978,462],{},[86,1980,1982],{"className":469,"code":1981,"language":471,"meta":91,"style":91},"{\n  \"scripts\": {\n    \"prepack\": \"unbuild\",\n    \"release\": \"changelogen --release && npm publish --access public && git push --follow-tags\"\n  }\n}\n",[93,1983,1984,1988,2001,2020,2038,2042],{"__ignoreMap":91},[67,1985,1986],{"class":97,"line":98},[67,1987,478],{"class":101},[67,1989,1990,1992,1995,1997,1999],{"class":97,"line":131},[67,1991,483],{"class":101},[67,1993,1994],{"class":109},"scripts",[67,1996,116],{"class":101},[67,1998,332],{"class":101},[67,2000,308],{"class":101},[67,2002,2003,2005,2008,2010,2012,2014,2016,2018],{"class":97,"line":160},[67,2004,518],{"class":101},[67,2006,2007],{"class":521},"prepack",[67,2009,116],{"class":101},[67,2011,332],{"class":101},[67,2013,493],{"class":101},[67,2015,721],{"class":119},[67,2017,116],{"class":101},[67,2019,501],{"class":101},[67,2021,2022,2024,2027,2029,2031,2033,2036],{"class":97,"line":167},[67,2023,518],{"class":101},[67,2025,2026],{"class":521},"release",[67,2028,116],{"class":101},[67,2030,332],{"class":101},[67,2032,493],{"class":101},[67,2034,2035],{"class":119},"changelogen --release && npm publish --access public && git push --follow-tags",[67,2037,567],{"class":101},[67,2039,2040],{"class":97,"line":190},[67,2041,366],{"class":101},[67,2043,2044],{"class":97,"line":200},[67,2045,371],{"class":101},[10,2047,391,2048,2050,2051,2053,2054,2056],{},[93,2049,2007],{}," script runs the ",[93,2052,721],{}," command before publishing the package to npm. The ",[93,2055,2026],{}," script generates a changelog, publishes the package to npm, and pushes the tags to GitHub.",[62,2058,2059],{},[10,2060,2061,2063,2064,2067],{},[67,2062,69],{},"\nYour project should be on GitHub for the ",[93,2065,2066],{},"--release"," option to work. If not, omit it and create the release manually on your repository.",[10,2069,2070],{},"Now, publish your package to npm using the following command:",[86,2072,2074],{"className":737,"code":2073,"language":739,"meta":91,"style":91},"npm run release\n",[93,2075,2076],{"__ignoreMap":91},[67,2077,2078,2080,2083],{"class":97,"line":98},[67,2079,1942],{"class":521},[67,2081,2082],{"class":119}," run",[67,2084,2085],{"class":119}," release\n",[10,2087,2088],{},"And that's it! You've just published your first Vue.js component library to npm. 🚀",[620,2090,2092],{"id":2091},"everything-is-ready","Everything is ready",[10,2094,2095],{},"We're done for today. We've covered numerous topics:",[2097,2098,2099,2102,2105,2108,2111,2114],"ul",{},[1283,2100,2101],{},"How to build a Vue.js component library with TypeScript.",[1283,2103,2104],{},"How to manage Vue files in a TypeScript project.",[1283,2106,2107],{},"How to use unbuild to transpile TypeScript files without bundling Vue files.",[1283,2109,2110],{},"How to leverage mkdist to maintain the project structure.",[1283,2112,2113],{},"How to utilize a pnpm workspace to test the library in a local project.",[1283,2115,2116],{},"How to publish the library to npm.",[10,2118,2119,2120],{},"For a more detailed and complex example, check GitHub: ",[14,2121,2122,2123,56],{"repo":16}," but everything explained here stems from ",[72,2124,2125],{},"my real-world experience",[10,2127,2128,2129,56],{},"I hope you enjoyed this tutorial and that it helps you build your own Vue.js component library. If you have questions, feel free to reach out to me on ",[32,2130,2133],{"href":2131,"rel":2132},"https:\u002F\u002Fx.com\u002Fsoubiran_",[36],"X (Twitter)",[10,2135,2136],{},[19,2137,2138],{},"You can go even further by using Vite to build your component library:",[58,2140],{"id":60},[2142,2143,2144],"style",{},"html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":91,"searchDepth":131,"depth":131,"links":2146},[2147,2148,2149,2150,2151,2152,2153,2154,2155],{"id":622,"depth":131,"text":623},{"id":708,"depth":131,"text":709},{"id":963,"depth":131,"text":964},{"id":1187,"depth":131,"text":1188},{"id":1274,"depth":131,"text":1275},{"id":1350,"depth":131,"text":1351},{"id":1675,"depth":131,"text":1676},{"id":1923,"depth":131,"text":1924},{"id":2091,"depth":131,"text":2092},"f9754148-40bd-4df2-a144-f686e894b200","2024-09-24","Manage the complexities of Vue SFCs with powerful tools like unbuild and mkdist to create a component library, saving you time and effort.","md",{},"\u002Fposts\u002Fthe-simplest-method-to-create-a-vue-js-component-library","---\ncontentId: f9754148-40bd-4df2-a144-f686e894b200\ntitle: The Simplest Method to Create a Vue.js Component Library\ndescription: Manage the complexities of Vue SFCs with powerful tools like unbuild and mkdist to create a component library, saving you time and effort.\ndate: 2024-09-24\n---\n\nTL;DR: \u003CGitHubLink repo=\"barbapapazes\u002Fvue-library\" \u002F> _Continue reading to grasp the process behind it and understand the reasons for my choices._\n\nA component library is a collection of reusable pieces that can be employed in various projects. It facilitates sharing resources among different projects and teams. These components may be low-level and generic like buttons, inputs, and modals, or more business-specific modules. _Ultimately, it's a method to share code across projects, a crucial skill that can significantly save time._\n\nHowever, building a component library with [Vue.js](https:\u002F\u002Fvuejs.org) is harder than it seems because of the [Single File Components](https:\u002F\u002Fvuejs.org\u002Fguide\u002Fscaling-up\u002Fsfc.html) (SFC). In a standard TypeScript project, you usually transpile to JavaScript and bundle your files with a tool like [tsup](https:\u002F\u002Ftsup.egoist.dev\u002F) or [Vite](https:\u002F\u002Fvitejs.dev\u002F).\n\n\u003CCalloutReadMore id=\"55d2cdcf-ba27-4bfe-9bc1-492e866ff3b1\" \u002F>\n\n> [!NOTE]\n> Remember to **deliver package code as native as possible** and let userland tools handle transpiling and optimization. It's a principle I always keep in mind when crafting libraries, streamlining the process significantly. _Each case is unique, but this is a solid guideline to follow._\n\nThe use of Vue.js SFC complicates this process. Let's examine why by exploring the challenges you'll encounter when constructing a Vue.js component library.\n\nConsider the following component:\n\n```vue\n\u003Cscript lang=\"ts\" setup>\nimport { useUser } from '..\u002Fcomposables'\n\nconst { user } = useUser()\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv v-if=\"user\">\n    {{ user.name }}\n  \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n```\n\nThe composable is as follows:\n\n```ts\nimport { ref } from 'vue'\n\nexport function useUser() {\n  const user = ref({ name: 'John Doe' })\n\n  return {\n    user\n  }\n}\n```\n\nThe component employs a composable imported from another file. _It may seem trivial, but this poses a significant issue._\n\nAll of this follows the architecture below:\n\n```\nsrc\u002F\n  components\u002F\n    User.vue\n  composables\u002F\n    useUser.ts\n  index.ts\n```\n\nThe `index.ts` file acts as the library's entry point and it looks like this:\n\n```ts\nimport User from '.\u002Fcomponents\u002FUser.vue'\n\nexport { User }\n\nexport { useUser } from '.\u002Fcomposables\u002FuseUser'\n```\n\nPlus, the `package.json` includes some `exports`:\n\n```json\n{\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \".\u002Fdist\u002Findex.d.ts\",\n      \"import\": \".\u002Fdist\u002Findex.mjs\"\n    }\n  },\n  \"main\": \"dist\u002Findex.mjs\",\n  \"types\": \"dist\u002Findex.d.ts\"\n}\n```\n\n## Similar to a standard TypeScript project\n\n> [!NOTE]\n> A bundle is a single file encapsulating all your project's code. It optimizes project loading by minimizing the number of files to load. This bundle isn't transpiled (except TypeScript to JavaScript) or minified when creating an npm package.\n\nNow, if we attempt to bundle this project, our `dist` folder will appear like this:\n\n```\ndist\u002F\n  index.cjs\n```\n\nUpon inspecting `index.cjs`, you'll find a surprise: the `useUser` composable is inlined, but the component is entirely ignored. _If it functions, the bundler might throw an error like `No loader is configured for \".vue\" files`. Essentially, tools like [esbuild](https:\u002F\u002Fesbuild.github.io) or [tsc](https:\u002F\u002Fwww.typescriptlang.org\u002Fdocs\u002Fhandbook\u002Fcompiler-options.html) don't know how to handle Vue files. It's logical since Vue files aren't JavaScript files._\n\nThe problem becomes evident. We need some configuration to handle Vue files. Let's quickly browse the internet for something like `vue loader`, as we're utilizing Vue files and need to transpile them to JavaScript.\n\nThe top result is [Vue Loader](https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Fvue-loader), which is a Webpack loader. _No. I refuse to use Webpack. Vite is now a standard, and there's no justification for employing Webpack in this context._\n\nBut before delving further, let's revisit our mantra: **deliver package code as native as possible** _and [keep it simple, stupid!](https:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FKISS_principle)_ Transpiling Vue SFC files using Webpack seems contrary to this principle.\n\n## Ignoring Vue files\n\nTo deliver `.vue` files without transpiling them, we instruct our bundler to disregard them. This is achieved using a tool called [unbuild](https:\u002F\u002Funbuild.unjs.io) from the [UnJS](https:\u002F\u002Funjs.io) ecosystem.\n\nUnbuild is a straightforward tool yet highly configurable since it's constructed atop rollup.\n\nFor our small project, we can directly experiment with it:\n\n```sh\nnpx unbuild\n```\n\nUnfortunately, it fails for the same reasons as before:\n\n```txt\nsrc\u002Fcomponents\u002FShowGitHubUser.vue (1:0): Expression expected (Note that you need plugins to import files that are not JavaScript)\n```\n\n_But unbuild has the potential for much more._\n\nTo tackle this problem, let's attempt the obvious solution. In the `index.ts` file, remove the component export and solely export TypeScript files:\n\n```ts\nexport { useUser } from '.\u002Fcomposables\u002FuseUser'\n```\n\nNow, the command `npx unbuild` functions properly.\n\n```sh\n➜ npx unbuild\nℹ Automatically detected entries: src\u002Findex [esm] [dts]\nℹ Building vue-library\nℹ Cleaning dist directory: .\u002Fdist\n✔ Build succeeded for vue-library\n  dist\u002Findex.mjs (total size: 139 B, chunk size: 139 B, exports: useUser)\n\nΣ Total dist size (byte size): 531 B\n```\n\nThis is a good start, but our components remain in the `src` folder and no matter how diligently you search, they're missing from the `dist` folder.\n\n## Copying Vue files\n\nNow that `.ts` files are processed correctly, we can attempt to copy the `.vue` files to the `dist` folder using a simple `cp` command.\n\n```sh\ncp -r src\u002Fcomponents\u002F dist\u002Fcomponents\u002F\n```\n\nSubsequently, we add an `exports` field in the `package.json` to inform the user where to locate the components:\n\n```json\n{\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \".\u002Fdist\u002Findex.d.ts\",\n      \"import\": \".\u002Fdist\u002Findex.mjs\"\n    },\n    \".\u002Fcomponents\u002F*\": {\n      \"import\": \".\u002Fdist\u002Fcomponents\u002F*.vue\"\n    }\n  },\n  \"main\": \"dist\u002Findex.mjs\",\n  \"types\": \"dist\u002Findex.d.ts\"\n}\n```\n\nLooks promising, doesn't it? _But it isn't._ :pensive:\n\n## The import path dilemma\n\nIn our Vue component, the `useUser` composable is imported like this:\n\n```vue\n\u003Cscript lang=\"ts\" setup>\nimport { useUser } from '..\u002Fcomposables'\n\u003C\u002Fscript>\n```\n\nMeanwhile, our `dist` folder is structured as follows:\n\n```\ndist\u002F\n  components\u002F\n    User.vue\n  index.d.mts\n  index.d.ts\n  index.mjs\n```\n\nCan you identify the problem? :eyes:\n\nThe component is unusable and will produce an error like `Cannot find module '..\u002Fcomposables' or its corresponding type declarations`, because the path leads nowhere. We've bundled all of our TypeScript files into a single file `index.mjs`, losing the project's structure entirely.\n\n## To bundle or not to bundle?\n\nFrom here, we have two choices:\n\n1. **Bundling the Vue files** using [Vite](https:\u002F\u002Fvitejs.dev), plugins, and extensive configuration.\n2. **Maintaining the directory structure** and using a tool like [mkdist](https:\u002F\u002Fgithub.com\u002Funjs\u002Fmkdist) for file-to-file transpiling (bundleless build) on TypeScript files.\n\nThe decision largely hinges on your specific needs, but I'm convinced that simplicity is the best approach. So, let's explore the second option.\n\nThe goal is to transform the TypeScript files into JavaScript files, preserving the original structure of the `src` folder in the `dist` folder and _ignoring_ the Vue files.\n\n> [!NOTE]\n> Using a `\u003Cscript setup lang=\"ts\">` block, mkdist will ignore Vue files to enable the Vue compiler to generate runtime props. If not, it will generate a `.vue.d.ts` file to offer types for the Vue component. Remember that the [@vitejs\u002Fplugin-vue](https:\u002F\u002Fgithub.com\u002Fvitejs\u002Fvite-plugin-vue\u002Ftree\u002Fmain\u002Fpackages\u002Fplugin-vue) and Vite inherently understand TypeScript script blocks. For more details, refer to [issue mkdist#14](https:\u002F\u002Fgithub.com\u002Funjs\u002Fmkdist\u002Fissues\u002F14).\n\n## Configuring Unbuild\n\nYes, we will configure unbuild since it incorporates mkdist, making it exceptionally user-friendly.\n\nFirst, create a `build.config.ts` file at the project's root. Unbuild will read this configuration file to understand how to process the project. It assumes defaults and infers many aspects from the `package.json`, but we need to tell it to utilize mkdist.\n\n```ts\nimport { defineBuildConfig } from 'unbuild'\n\nexport default defineBuildConfig({\n  entries: ['.\u002Fsrc\u002F'],\n  declaration: true,\n})\n```\n\nIt's a very straightforward configuration file. _And I adore it._ The `entries` key informs unbuild of the initial file to begin transpiling. However, `.\u002Fsrc\u002F` is a directory, indicated by the `\u002F` at the end. With this hint, unbuild bypasses the default bundler `rollup` in favor of `mkdist`. The `declaration` key prompts unbuild to create TypeScript declaration files (`.d.ts`).\n\nWe can also remove the `exports` field in the `package.json` since we won't be bundling files anymore and restore the component export in the `index.ts` file.\n\nLet's rerun the `npx unbuild` command and witness the magic.\n\n```sh\n➜ npx unbuild\nℹ Building vue-library\nℹ Cleaning dist directory: .\u002Fdist\n✔ Build succeeded for vue-library\n  dist (total size: 600 B)\n  └─ dist\u002Findex.d.ts (108 B)\n  └─ dist\u002Findex.mjs (112 B)\n  └─ dist\u002Fcomposables\u002FuseUser.d.ts (79 B)\n  └─ dist\u002Fcomposables\u002FuseUser.mjs (124 B)\n  └─ dist\u002Fcomponents\u002FUser.vue (177 B)\nΣ Total dist size (byte size): 600 B\n```\n\nNow, the `dist` folder appears like this:\n\n```\ndist\u002F\n  components\u002F\n    User.vue\n  composables\u002F\n    useUser.d.ts\n    useUser.mjs\n  index.d.ts\n  index.mjs\n```\n\nThis mirrors the structure of the `src` folder, and the `User.vue` component is now usable in the `dist` folder. The relative import path remains intact, and the component can be utilized in any Vue project. :partying_face:\n\n## Local development\n\nMany tutorials halt here, suggesting you're ready to publish your package to npm. But how can you create a complex library if you can't **see the outcome of your work**? If you can't test it while building it?\n\nBasically, you can't.\n\nLet's examine how to use the library in a local project, within the same repository for simplicity.\n\nI'll demonstrate the simplest method using a [pnpm workspace](https:\u002F\u002Fpnpm.io\u002F).\n\nStart by creating a `pnpm-workspace.yaml` file at the project's root:\n\n```yaml\npackages:\n  - .\n  - playground\n```\n\nThen, create a new Vite project in a `playground` folder:\n\n```sh\nnpx create-vite playground --template vue-ts\n```\n\nSince we're using a pnpm workspace, install the dependencies from the project's root:\n\n```sh\npnpm install\n```\n\n> [!NOTE]\n> This step isn't mandatory. You can create a new Vite project and install dependencies within it. Pnpm simplifies this by installing dependencies for all workspaces in the root. A simple `pnpm install` installs dependencies from all workspaces.\n\nNow, we can use this `playground` library within a \"real\" project to test our library.\n\nFor instance, open the `src\u002FApp.vue` file and import the `User` component:\n\n```vue\n\u003Cscript setup lang=\"ts\">\nimport { User } from '..\u002F..\u002Fsrc'\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003CUser \u002F>\n\u003C\u002Ftemplate>\n```\n\nAnd run the project:\n\n```sh\ncd playground && pnpm dev\n```\n\nYou'll see the `User` component displayed in the browser. :tada: So simple, yet so powerful. _I use this method for virtually every library I build, and it works brilliantly._\n\n## Publishing to npm\n\nThis part is straightforward once you grasp the workflow.\n\nBegin by naming your library. I'll use `@barbapapazes\u002Fvue-library` for this example.\n\nNext, create an account on [npm](https:\u002F\u002Fwww.npmjs.com) and log in using the `npm login` command.\n\nThen, install [changelogen](https:\u002F\u002Fchangelogen.unjs.io) to generate a changelog, update the version number following semantic versioning and commit convention, and to pre-fill the GitHub release.\n\n```sh\npnpm i -D changelogen\n```\n\nNext, add two scripts to your `package.json`:\n\n```json\n{\n  \"scripts\": {\n    \"prepack\": \"unbuild\",\n    \"release\": \"changelogen --release && npm publish --access public && git push --follow-tags\"\n  }\n}\n```\n\nThe `prepack` script runs the `unbuild` command before publishing the package to npm. The `release` script generates a changelog, publishes the package to npm, and pushes the tags to GitHub.\n\n> [!NOTE]\n> Your project should be on GitHub for the `--release` option to work. If not, omit it and create the release manually on your repository.\n\nNow, publish your package to npm using the following command:\n\n```sh\nnpm run release\n```\n\nAnd that's it! You've just published your first Vue.js component library to npm. :rocket:\n\n## Everything is ready\n\nWe're done for today. We've covered numerous topics:\n\n- How to build a Vue.js component library with TypeScript.\n- How to manage Vue files in a TypeScript project.\n- How to use unbuild to transpile TypeScript files without bundling Vue files.\n- How to leverage mkdist to maintain the project structure.\n- How to utilize a pnpm workspace to test the library in a local project.\n- How to publish the library to npm.\n\nFor a more detailed and complex example, check GitHub: \u003CGitHubLink repo=\"barbapapazes\u002Fvue-library\" \u002F> but everything explained here stems from **my real-world experience**.\n\nI hope you enjoyed this tutorial and that it helps you build your own Vue.js component library. If you have questions, feel free to reach out to me on [X (Twitter)](https:\u002F\u002Fx.com\u002Fsoubiran_).\n\n_You can go even further by using Vite to build your component library:_\n\n\u003CCalloutReadMore id=\"55d2cdcf-ba27-4bfe-9bc1-492e866ff3b1\" \u002F>\n",null,{"title":5,"description":2158},{"loc":2161},"posts\u002Fthe-simplest-method-to-create-a-vue-js-component-library","54sSthPedhwWu7jxhjUQZlFD1TvUKk6X4GktWQA3ikA",1790086901574]