<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jerry Hogan</title>
    <description>The latest articles on DEV Community by Jerry Hogan (@hdjerry).</description>
    <link>https://dev.to/hdjerry</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F267092%2F59aec1e1-108d-4408-b108-509bba250c5e.png</url>
      <title>DEV Community: Jerry Hogan</title>
      <link>https://dev.to/hdjerry</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hdjerry"/>
    <language>en</language>
    <item>
      <title>How I Built and Published a Vue 3 Starter Kit CLI</title>
      <dc:creator>Jerry Hogan</dc:creator>
      <pubDate>Fri, 25 Sep 2026 13:19:01 +0000</pubDate>
      <link>https://dev.to/hdjerry/how-i-built-and-published-a-vue-3-starter-kit-cli-28gc</link>
      <guid>https://dev.to/hdjerry/how-i-built-and-published-a-vue-3-starter-kit-cli-28gc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A step-by-step breakdown of building &lt;a href="https://github.com/hdjerry/vue-starter" rel="noopener noreferrer"&gt;&lt;code&gt;create-vue-starter-with-test&lt;/code&gt;&lt;/a&gt; — from bootstrapping a Vue app to publishing a CLI tool on npm.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/hdjerry/vue-starter" rel="noopener noreferrer"&gt;&lt;code&gt;create-vue-starter-with-test&lt;/code&gt;&lt;/a&gt; is a CLI that scaffolds a fully configured Vue 3 project in seconds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-vue-starter-with-test my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated project ships with &lt;a href="https://vuejs.org" rel="noopener noreferrer"&gt;Vue 3&lt;/a&gt;, &lt;a href="https://vite.dev" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;, &lt;a href="https://vitest.dev" rel="noopener noreferrer"&gt;Vitest&lt;/a&gt;, &lt;a href="https://router.vuejs.org" rel="noopener noreferrer"&gt;Vue Router&lt;/a&gt;, &lt;a href="https://pinia.vuejs.org" rel="noopener noreferrer"&gt;Pinia&lt;/a&gt;, &lt;a href="https://mswjs.io" rel="noopener noreferrer"&gt;MSW (Mock Service Worker)&lt;/a&gt;, &lt;a href="https://sass-lang.com" rel="noopener noreferrer"&gt;Sass&lt;/a&gt;, &lt;a href="https://eslint.org" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt;, and &lt;a href="https://prettier.io" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; — all wired up and ready to go. This write-up walks through exactly how it was built, PR by PR.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 1 — Bootstrap the Template App
&lt;/h2&gt;

&lt;p&gt;The foundation starts with &lt;a href="https://github.com/vuejs/create-vue" rel="noopener noreferrer"&gt;&lt;code&gt;create-vue&lt;/code&gt;&lt;/a&gt;, the official Vue scaffolding tool. Run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create vue@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This launches an interactive prompt. Here's what was selected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ Project name: vue-starter
✔ Add TypeScript? → Yes
✔ Add JSX Support? → No
✔ Add Vue Router for Single Page Application development? → Yes
✔ Add Pinia for state management? → Yes
✔ Add Vitest for Unit Testing? → Yes
✔ Add an End-to-End Testing Solution? → No
✔ Add ESLint for code quality? → Yes
✔ Add Prettier for code formatting? → Yes
✔ Add Vue DevTools 7 extension for browser debugging? → Yes   (https://devtools.vuejs.org)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then install dependencies and start the dev server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;vue-starter
yarn &lt;span class="nb"&gt;install
&lt;/span&gt;yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point you have a working Vue 3 + Vite app with TypeScript, Vue Router, Pinia, Vitest, ESLint, and Prettier all wired up by the scaffolder. The next step is layering in the extra tooling that &lt;code&gt;create-vue&lt;/code&gt; doesn't include by default — MSW, Sass, and the test configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Additional packages installed manually:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add &lt;span class="nt"&gt;-D&lt;/span&gt; msw sass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Stack chosen:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://vuejs.org" rel="noopener noreferrer"&gt;Vue 3&lt;/a&gt;&lt;/strong&gt; with the Composition API and &lt;code&gt;&amp;lt;script setup&amp;gt;&lt;/code&gt; syntax&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://vite.dev" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;&lt;/strong&gt; as the build tool and dev server&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://www.typescriptlang.org" rel="noopener noreferrer"&gt;TypeScript&lt;/a&gt;&lt;/strong&gt;  throughout&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://vitest.dev" rel="noopener noreferrer"&gt;Vitest&lt;/a&gt;&lt;/strong&gt; for unit testing (configured to merge with the Vite config)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://router.vuejs.org" rel="noopener noreferrer"&gt;Vue Router&lt;/a&gt;&lt;/strong&gt; for client-side routing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://pinia.vuejs.org" rel="noopener noreferrer"&gt;Pinia&lt;/a&gt;&lt;/strong&gt; for state management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://mswjs.io" rel="noopener noreferrer"&gt;MSW (Mock Service Worker)&lt;/a&gt;&lt;/strong&gt; for API mocking in tests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://sass-lang.com" rel="noopener noreferrer"&gt;Sass&lt;/a&gt;&lt;/strong&gt; for component styling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Configuration files set up:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;vite.config.ts&lt;/code&gt; — configures the Vue plugin and &lt;code&gt;@&lt;/code&gt; path alias&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vitest.config.ts&lt;/code&gt; — merges with the Vite config, sets &lt;a href="https://github.com/jsdom/jsdom" rel="noopener noreferrer"&gt;&lt;code&gt;jsdom&lt;/code&gt;&lt;/a&gt; as the test environment, and supports both &lt;code&gt;__tests__/&lt;/code&gt; and &lt;code&gt;*.test.ts&lt;/code&gt; file patterns&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tsconfig.json&lt;/code&gt;, &lt;code&gt;tsconfig.app.json&lt;/code&gt;, &lt;code&gt;tsconfig.node.json&lt;/code&gt;, &lt;code&gt;tsconfig.vitest.json&lt;/code&gt; — TypeScript project references for clean separation between app, node, and test code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;eslint.config.ts&lt;/code&gt; — &lt;a href="https://eslint.org" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt; with Vue and TypeScript rules&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.prettierrc.json&lt;/code&gt; — &lt;a href="https://prettier.io" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; for consistent code formatting&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.editorconfig&lt;/code&gt; — consistent editor settings across IDEs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The MSW Setup
&lt;/h3&gt;

&lt;p&gt;MSW is configured for the Node environment (used in Vitest) via &lt;code&gt;src/mock/serverSetup.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;setupServer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;msw/node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;handlers&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./handlers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setupServer&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Handlers are organized by resource. The post handler intercepts &lt;code&gt;GET /posts&lt;/code&gt; and returns fixture data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HttpResponse&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;msw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;postHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://jsonplaceholder.typicode.com/posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;HttpResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;body a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;postHandler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means tests never hit the real network — they're fast, deterministic, and don't depend on external services.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 2 — Build a Real Feature (Posts View)
&lt;/h2&gt;

&lt;p&gt;With the skeleton in place, the first real feature was a Posts view: a page that fetches posts from an API, shows a loading state while fetching, renders cards when data arrives, and shows an empty state when there's nothing to display.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PR #1 — Empty state for posts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PostView.vue&lt;/code&gt; handles three states using Vue's &lt;code&gt;v-if&lt;/code&gt; / &lt;code&gt;v-else-if&lt;/code&gt; / &lt;code&gt;v-else&lt;/code&gt; directives:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"isLoadingPosts"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"loading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Loading...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"cards-wrapper"&lt;/span&gt; &lt;span class="na"&gt;v-else-if=&lt;/span&gt;&lt;span class="s"&gt;"hasPosts"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt; &lt;span class="na"&gt;v-for=&lt;/span&gt;&lt;span class="s"&gt;"(post, index) in posts"&lt;/span&gt; &lt;span class="na"&gt;:key=&lt;/span&gt;&lt;span class="s"&gt;"index"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"empty-state"&lt;/span&gt; &lt;span class="na"&gt;v-else&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Oops! Nothing to see here&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;hasPosts&lt;/code&gt; computed property combines loading and data state cleanly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasPosts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLoadingPosts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The test for this view&lt;/strong&gt; (&lt;code&gt;src/tests/pages/PostView.test.ts&lt;/code&gt;) uses MSW to mock the API and &lt;a href="https://test-utils.vuejs.org" rel="noopener noreferrer"&gt;&lt;code&gt;@vue/test-utils&lt;/code&gt;&lt;/a&gt; to mount the component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;beforeAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nf"&gt;afterAll&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nf"&gt;afterEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resetHandlers&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should render&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;shallowMount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PostView&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.loading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// loading state&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;flushPromises&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// wait for fetch + DOM update&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.cards-wrapper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.card&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// one card from mock handler&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://test-utils.vuejs.org/api/#flushpromises" rel="noopener noreferrer"&gt;&lt;code&gt;flushPromises()&lt;/code&gt;&lt;/a&gt; is the key here — it drains all async queues so the component fully resolves before assertions run.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 3 — Prepare for Publishing
&lt;/h2&gt;

&lt;p&gt;PRs #2 and #3 covered documentation and licensing — a &lt;code&gt;README.md&lt;/code&gt; with usage instructions and a &lt;code&gt;LICENSE&lt;/code&gt; file with MIT terms. These are non-negotiable for an open-source npm package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PR #4 — The CLI itself&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where the project transforms from a starter template into a distributable CLI. Three things were added:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;package.json&lt;/code&gt; configured for npm
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"create-vue-starter-with-test"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"private"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"create-vue-starter-with-test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/index.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;bin&lt;/code&gt; field tells npm what to run when someone calls &lt;code&gt;npx create-vue-starter-with-test&lt;/code&gt;. The &lt;code&gt;files&lt;/code&gt; field limits what gets published to just &lt;code&gt;dist/&lt;/code&gt; (compiled CLI) and &lt;code&gt;template/&lt;/code&gt; (the project scaffold).&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;code&gt;tsconfig.cli.json&lt;/code&gt; — separate TypeScript config for the CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ES2022"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NodeNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NodeNext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"include"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"src"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This compiles only &lt;code&gt;src/index.ts&lt;/code&gt; into &lt;code&gt;dist/index.js&lt;/code&gt; — completely separate from the template app's TypeScript config.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;code&gt;src/index.ts&lt;/code&gt; — the CLI script
&lt;/h3&gt;

&lt;p&gt;The CLI is a Node.js script that does four things when run:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Validates input&lt;/strong&gt; — exits with a helpful message if no project name is given&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copies the template&lt;/strong&gt; — recursively copies everything from &lt;code&gt;template/&lt;/code&gt; into the target directory&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cleans up&lt;/strong&gt; — removes any &lt;code&gt;.git&lt;/code&gt; folder from the copy&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Installs dependencies&lt;/strong&gt; — runs &lt;code&gt;yarn install&lt;/code&gt; in the new project
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cp"&gt;#!/usr/bin/env node
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;execSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;chalk&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chalk&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// https://github.com/chalk/chalk&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;copyDir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mkdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;recursive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;srcPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;destPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;statSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isDirectory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;copyDir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;destPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copyFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;srcPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;destPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;projectName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;targetDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;existsSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;targetDir&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;templateDir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;copyDir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;templateDir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetDir&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;execSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`cd &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;projectName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; yarn install`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stdio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;inherit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;__dirname&lt;/code&gt; trick (via &lt;code&gt;fileURLToPath&lt;/code&gt;) resolves the template path relative to the compiled CLI file — this is important because when the package is installed globally via npx, &lt;code&gt;process.cwd()&lt;/code&gt; is the user's directory, not the package directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PR #5 — &lt;code&gt;.npmignore&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without this file, npm would publish everything including &lt;code&gt;node_modules&lt;/code&gt;, source files, and dev config. &lt;code&gt;.npmignore&lt;/code&gt; explicitly excludes everything except what's already listed in &lt;code&gt;files&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;. It's redundant but acts as a safety net.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PR #6 — Move template into &lt;code&gt;template/&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was the structural pivot. Up to this point the Vue app and the CLI tool lived in the same flat structure. PR #6 moved all Vue app files into &lt;code&gt;template/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before:                     After:
src/                        src/
  index.ts                    index.ts  ← CLI only
  App.vue                   template/
  router/                     App.vue
  views/                      router/
  ...                         views/
                              ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI's &lt;code&gt;copyDir&lt;/code&gt; call was updated to point at &lt;code&gt;../template&lt;/code&gt; relative to the compiled output in &lt;code&gt;dist/&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Publishing to npm (and making &lt;code&gt;npx&lt;/code&gt; work)
&lt;/h2&gt;

&lt;p&gt;Once the CLI and template are in place, publishing follows three steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Build the CLI
&lt;/h3&gt;

&lt;p&gt;The CLI is written in TypeScript, so it must be compiled before publishing. The &lt;code&gt;build&lt;/code&gt; script in &lt;code&gt;package.json&lt;/code&gt; runs &lt;code&gt;tsc&lt;/code&gt; using the CLI-specific tsconfig:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn build
&lt;span class="c"&gt;# compiles src/index.ts → dist/index.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Always run this before publishing. The &lt;code&gt;dist/&lt;/code&gt; folder is what actually ships — if you publish without building, users get nothing.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Log in to npm and publish
&lt;/h3&gt;

&lt;p&gt;See the &lt;a href="https://docs.npmjs.com/cli/v10/commands/npm-publish" rel="noopener noreferrer"&gt;npm publish docs&lt;/a&gt; for full options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm login        &lt;span class="c"&gt;# one-time: authenticates with your npm account&lt;/span&gt;
npm publish      &lt;span class="c"&gt;# publishes the package&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;npm reads &lt;code&gt;package.json&lt;/code&gt; to determine the package name (&lt;code&gt;create-vue-starter-with-test&lt;/code&gt;), version, and what files to include (&lt;code&gt;dist/&lt;/code&gt; and &lt;code&gt;template/&lt;/code&gt; via the &lt;code&gt;files&lt;/code&gt; field). Because &lt;code&gt;private&lt;/code&gt; is set to &lt;code&gt;false&lt;/code&gt;, npm allows the publish.&lt;/p&gt;

&lt;p&gt;For subsequent releases, bump the version first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm version patch   &lt;span class="c"&gt;# 1.0.6 → 1.0.7&lt;/span&gt;
yarn build
npm publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. How &lt;code&gt;npx&lt;/code&gt; resolves it
&lt;/h3&gt;

&lt;p&gt;When a user runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-vue-starter-with-test my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;npx&lt;/code&gt; looks up the package name on the npm registry, downloads it temporarily (or uses a cached version), then reads the &lt;code&gt;bin&lt;/code&gt; field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"create-vue-starter-with-test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist/index.js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It executes &lt;code&gt;dist/index.js&lt;/code&gt; with Node, passing &lt;code&gt;my-app&lt;/code&gt; as &lt;code&gt;process.argv[2]&lt;/code&gt;. The &lt;code&gt;#!/usr/bin/env node&lt;/code&gt; shebang at the top of the file tells the OS to use Node to run it.&lt;/p&gt;

&lt;p&gt;The naming convention &lt;code&gt;create-*&lt;/code&gt; is intentional — it follows the same pattern as &lt;code&gt;create-react-app&lt;/code&gt;, &lt;code&gt;create-vite&lt;/code&gt;, and &lt;code&gt;create-vue&lt;/code&gt;, which makes the package discoverable and the command feel natural.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 4 — Component Architecture
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PR #7 — Atomic Design folder structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The final major feature was introducing an &lt;a href="https://atomicdesign.bradfrost.com/chapter-2/" rel="noopener noreferrer"&gt;atomic design&lt;/a&gt; component hierarchy. Instead of dumping all components in one flat folder, they're organized by complexity:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/components/
  atoms/          ← smallest building blocks
    BaseButton.vue
    BaseInput.vue
    icons/
  molecules/      ← groups of atoms
    FormField.vue
  organisms/      ← complex UI sections
    Card.vue
  templates/      ← page-level layout wrappers
    PageTemplate.vue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives any team using the starter kit a clear mental model for where new components go and how they compose. A &lt;code&gt;FormField&lt;/code&gt; (molecule) is built from a &lt;code&gt;BaseInput&lt;/code&gt; (atom). A &lt;code&gt;Card&lt;/code&gt; (organism) uses the styling primitives but operates at a higher abstraction level. &lt;code&gt;PageTemplate&lt;/code&gt; defines layout structure independently of content.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It All Fits Together
&lt;/h2&gt;

&lt;p&gt;The repository has two distinct roles:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Path&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CLI tool&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;src/index.ts&lt;/code&gt; → &lt;code&gt;dist/index.js&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;What runs when users call &lt;code&gt;npx create-vue-starter-with-test&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Template app&lt;/td&gt;
&lt;td&gt;&lt;code&gt;template/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The Vue project that gets copied into the user's directory&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When you run &lt;code&gt;npm publish&lt;/code&gt;, npm packages &lt;code&gt;dist/&lt;/code&gt; and &lt;code&gt;template/&lt;/code&gt; per the &lt;code&gt;files&lt;/code&gt; field. When a user runs &lt;code&gt;npx create-vue-starter-with-test my-app&lt;/code&gt;, npm downloads the package and executes &lt;code&gt;dist/index.js&lt;/code&gt;, which copies &lt;code&gt;template/&lt;/code&gt; to &lt;code&gt;./my-app&lt;/code&gt; and installs dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  Phase 5 — GitHub Actions (CI)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PR #8 — Workflow setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The final piece is automating quality checks on every push and pull request. A &lt;a href="https://docs.github.com/en/actions" rel="noopener noreferrer"&gt;GitHub Actions&lt;/a&gt; workflow ensures the CLI builds and tests pass before anything merges.&lt;/p&gt;

&lt;p&gt;The workflow lives at &lt;a href="https://github.com/hdjerry/vue-starter/tree/main/.github/workflows" rel="noopener noreferrer"&gt;&lt;code&gt;.github/workflows/&lt;/code&gt;&lt;/a&gt; and runs on push to &lt;code&gt;main&lt;/code&gt; and on pull requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-and-test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn install&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn build&lt;/span&gt;       &lt;span class="c1"&gt;# compile the CLI&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn test:run&lt;/span&gt;    &lt;span class="c1"&gt;# run Vitest once (no watch mode)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a green/red signal on every PR — if the CLI fails to compile or a test breaks, the merge is blocked. It also means contributors can't accidentally ship a broken template.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Separate concerns early.&lt;/strong&gt; The CLI and the template app are different things. Keeping them in separate directories (&lt;code&gt;src/&lt;/code&gt; vs &lt;code&gt;template/&lt;/code&gt;) avoids confusion and keeps the published package clean.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use &lt;code&gt;__dirname&lt;/code&gt; relative paths in CLI tools.&lt;/strong&gt; &lt;code&gt;process.cwd()&lt;/code&gt; is the user's working directory. Template paths must be resolved relative to the installed package location.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test with MSW, not mocks.&lt;/strong&gt; MSW intercepts at the network level, so tests exercise real &lt;code&gt;fetch&lt;/code&gt; calls without hitting the network. This is closer to production behavior than &lt;code&gt;vi.mock()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;vitest.config.ts&lt;/code&gt; should extend &lt;code&gt;vite.config.ts&lt;/code&gt;.&lt;/strong&gt; Using &lt;code&gt;mergeConfig&lt;/code&gt; keeps the two in sync — especially for path aliases (&lt;code&gt;@&lt;/code&gt;), which are needed in both build and test contexts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Atomic design scales.&lt;/strong&gt; Starting the template with &lt;code&gt;atoms/molecules/organisms/templates&lt;/code&gt; gives consumers a naming convention and mental model from day one, rather than a &lt;code&gt;components/&lt;/code&gt; folder that grows chaotically.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;.npmignore&lt;/code&gt; + &lt;code&gt;files&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;.&lt;/strong&gt; Use both. &lt;code&gt;files&lt;/code&gt; is the whitelist of what to include; &lt;code&gt;.npmignore&lt;/code&gt; is the blacklist. Together they ensure the published package is lean.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>vue</category>
      <category>opensource</category>
      <category>frontend</category>
      <category>typescript</category>
    </item>
    <item>
      <title>I Built My Own Vue 3 Project Starter</title>
      <dc:creator>Jerry Hogan</dc:creator>
      <pubDate>Thu, 24 Sep 2026 10:29:09 +0000</pubDate>
      <link>https://dev.to/hdjerry/i-built-my-own-vue-3-project-starter-590k</link>
      <guid>https://dev.to/hdjerry/i-built-my-own-vue-3-project-starter-590k</guid>
      <description>&lt;p&gt;I created a Vue 3 starter template to give my projects a consistent foundation for development, testing, API mocking, and component organization.&lt;/p&gt;

&lt;p&gt;This project is available on GitHub:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/hdJerry/vue-starter" rel="noopener noreferrer"&gt;https://github.com/hdJerry/vue-starter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It can be used to scaffold a new project with &lt;code&gt;npx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-vue-starter-with-test my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
yarn dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The endgame is simple: start a Vue project with some of the configuration and structure I normally set up manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack
&lt;/h2&gt;

&lt;p&gt;The starter is built with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vue 3&lt;/li&gt;
&lt;li&gt;Vite&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;li&gt;Unit testing&lt;/li&gt;
&lt;li&gt;API mocking&lt;/li&gt;
&lt;li&gt;Prettier&lt;/li&gt;
&lt;li&gt;Atomic Design-inspired component organization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives a new project a basic development and testing environment without having to configure everything from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Component architecture
&lt;/h2&gt;

&lt;p&gt;One of the main architectural decisions was to organize components using the concepts of Atomic Design.&lt;/p&gt;

&lt;p&gt;The structure is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
└── components/
    ├── atoms/
    ├── molecules/
    ├── organisms/
    └── templates/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The idea is to separate components based on their level of composition.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Atoms&lt;/strong&gt; are small reusable UI elements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Molecules&lt;/strong&gt; combine atoms into more specific UI components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Organisms&lt;/strong&gt; combine multiple components into larger sections of an interface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Templates&lt;/strong&gt; define page-level layouts and composition.&lt;/p&gt;

&lt;p&gt;This provides a predictable structure as the application grows and makes reusable components easier to identify.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Testing is included in the starter rather than added later.&lt;/p&gt;

&lt;p&gt;The project provides the initial setup required to write unit tests for the application.&lt;/p&gt;

&lt;p&gt;For me, having the testing infrastructure available from the beginning makes it easier to treat testing as part of the development workflow rather than a separate task that needs to be introduced later.&lt;/p&gt;

&lt;h2&gt;
  
  
  API mocking
&lt;/h2&gt;

&lt;p&gt;The starter also includes API mocking.&lt;/p&gt;

&lt;p&gt;This allows frontend development to continue without requiring a live backend for every development scenario.&lt;/p&gt;

&lt;p&gt;Mocked responses can be used while building components and testing application behavior, which is particularly useful when frontend and backend development are happening in parallel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;npx&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;I wanted the project to behave more like a development tool than a repository that needs to be cloned and manually configured.&lt;/p&gt;

&lt;p&gt;The intended workflow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-vue-starter-with-test my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The developer gets a new project from the starter and can immediately begin working on the application.&lt;/p&gt;

&lt;p&gt;This also means improvements to the starter can be distributed through future versions rather than requiring developers to manually copy changes from the repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building the component structure
&lt;/h2&gt;

&lt;p&gt;The Atomic Design structure was introduced through &lt;a href="https://github.com/hdJerry/vue-starter/pull/7" rel="noopener noreferrer"&gt;PR #7&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The change established the initial separation between atoms, molecules, organisms, and templates.&lt;/p&gt;

&lt;p&gt;I wanted the architecture to encourage composition and reuse rather than having every new feature introduce another isolated component.&lt;/p&gt;

&lt;p&gt;The structure is intentionally simple. It can be adapted as the application develops instead of trying to solve every architectural problem at the scaffolding stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;Building the starter made me think more carefully about what should be part of a project foundation.&lt;/p&gt;

&lt;p&gt;A starter should provide useful defaults without becoming so opinionated that developers spend more time working around it than using it.&lt;/p&gt;

&lt;p&gt;That means some things belong in the foundation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;development tooling&lt;/li&gt;
&lt;li&gt;testing&lt;/li&gt;
&lt;li&gt;formatting&lt;/li&gt;
&lt;li&gt;API mocking&lt;/li&gt;
&lt;li&gt;basic component organization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other decisions are better left to the application itself.&lt;/p&gt;

&lt;p&gt;That balance is probably the most interesting part of building a reusable starter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;The project is available on GitHub and I plan to continue improving it as I use it and receive feedback.&lt;/p&gt;

&lt;p&gt;The repository is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/hdJerry/vue-starter" rel="noopener noreferrer"&gt;https://github.com/hdJerry/vue-starter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you work with Vue, I'd be interested in hearing what you normally include in your own project starter and what you would add to this one.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>npx</category>
      <category>opensource</category>
      <category>frontend</category>
    </item>
  </channel>
</rss>
