├── .editorconfig
├── .eslintrc
├── .gitignore
├── .npmrc
├── .vscode
├── extensions.json
└── settings.json
├── LICENSE
├── README.md
├── cypress
└── support
│ ├── component-index.html
│ └── component.shared.ts
├── package.json
├── packages
├── docs
│ ├── .gitignore
│ ├── .vitepress
│ │ └── config.ts
│ ├── README.md
│ ├── guide.md
│ ├── index.md
│ └── package.json
├── lib-vue2
│ ├── .eslintrc
│ ├── README.md
│ ├── components.d.ts
│ ├── cypress.config.ts
│ ├── cypress
│ │ └── support
│ │ │ └── component.ts
│ ├── index.html
│ ├── package.json
│ ├── src
│ ├── tsconfig.json
│ └── vite.config.ts
└── lib-vue3
│ ├── components.d.ts
│ ├── cypress.config.ts
│ ├── cypress
│ └── support
│ │ ├── commands.ts
│ │ └── component.ts
│ ├── package.json
│ ├── src
│ ├── components
│ │ └── Counter
│ │ │ ├── Counter.cy.tsx
│ │ │ ├── Counter.test.ts
│ │ │ └── Counter.vue
│ ├── main.ts
│ └── styles
│ │ └── main.scss
│ ├── tsconfig.json
│ └── vite.config.ts
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── scripts
└── symlink.ts
├── tsconfig.cy.json
├── tsconfig.json
├── tsconfig.vitest.json
├── tsconfig.web.json
└── vite.config.shared.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["@antfu"]
3 | }
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .vite-ssg-dist
3 | .vite-ssg-temp
4 | *.local
5 | dist
6 | dist-ssr
7 | node_modules
8 | .idea/
9 | *.log
10 | *.tsbuildinfo
11 | .eslintcache
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | shamefully-hoist-peer-dependencies=true
2 | strict-peer-dependencies=false
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "rvest.vs-code-prettier-eslint",
4 | "antfu.iconify",
5 | "antfu.unocss",
6 | "antfu.goto-alias",
7 | "csstools.postcss",
8 | "vue.volar",
9 | "lokalise.i18n-ally",
10 | "streetsidesoftware.code-spell-checker"
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "cSpell.words": [
3 | "antfu",
4 | "demi",
5 | "esno",
6 | "iconify",
7 | "intlify",
8 | "jsdelivr",
9 | "pinia",
10 | "pnpm",
11 | "unocss",
12 | "unplugin",
13 | "Vite",
14 | "vitejs",
15 | "Vitepress",
16 | "Vitesse",
17 | "vitest",
18 | "vueuse"
19 | ],
20 | "i18n-ally.sourceLanguage": "en",
21 | "i18n-ally.keystyle": "nested",
22 | "i18n-ally.localesPaths": "locales",
23 | "i18n-ally.sortKeys": true,
24 | "editor.codeActionsOnSave": {
25 | "source.fixAll.eslint": true
26 | },
27 | "editor.defaultFormatter": "rvest.vs-code-prettier-eslint",
28 | "editor.formatOnPaste": true, // required
29 | "editor.formatOnType": false, // required
30 | "editor.formatOnSave": true, // optional
31 | "editor.formatOnSaveMode": "file", // required to format on save
32 | "files.autoSave": "onFocusChange", // optional but recommended
33 | "files.associations": {
34 | "*.css": "postcss"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020-2021 Jessica Sachs
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 🚧 Under Construction 🚧
2 |
3 | # Petite ✌️
4 |
5 | Petite is an opinionated GitHub template built for Vue component authors. It sets up the tooling required to develop, document, and test **Universal SFC Components** that are backwards-compatible with the Vue 2.7 runtime.
6 |
7 | This is accomplished with some runtime helpers and a very opinionated monorepo structure.
8 |
9 | Petite sets up Vite, Volar, Linting, Vitepress, TypeScript, and Testing so that you can choose to write Vue 3-style code while easily maintaining backwards compatibility for your Vue 2.x users. 👏👏
10 |
11 | ## Features
12 |
13 | **Build + Compile**
14 |
15 | - [x] Vue 2.7 + Vue 3 w/ Script Setup
16 | - [x] Volar support for Vue 2.7 and Vue 3
17 | - [x] TypeScript
18 | - [x] Ship d.ts files for each source file
19 | - [x] Ship global types via `dist/components.d.ts` for your users (via `unplugin-vue-components`)
20 | - [x] pnpm workspaces + monorepo
21 | - [x] Vite
22 |
23 | **Lint**
24 |
25 | - [x] Eslint and prettier
26 | - [x] Lint pre-commit and on save
27 |
28 | **Document**
29 |
30 | - [x] Documentation Site via Vitepress
31 | - [ ] netlify.toml
32 | - [ ] Type-gen documentation (see [Faker's Vitepress config](https://github.com/faker-js/faker/tree/main/docs/.vitepress))
33 |
34 | **Test and Run**
35 |
36 | - [x] Component Testing via Cypress for headed components
37 | - [ ] Fix HMR for Vue 2 (Cypress isn't watching symlinks?)
38 | - [x] Unit Testing via Vitest for headless components and logic
39 | - [ ] Stories via Histoire
40 | - [ ] netlify.toml
41 | - [ ] Cypress End-to-end example
42 |
43 | **Tooling + README**
44 |
45 | - [ ] GitHub integration
46 | - [ ] GitHub actions
47 | - [ ] Issue templates
48 | - [ ] Fancy README Badges
49 |
50 | **Style**
51 |
52 | - [x] SCSS
53 | - [ ] UnoCSS
54 | - [ ] CSS-only Icons via Iconify
55 |
56 | ## Why Petite?
57 |
58 | It is possible to write universal Vue SFC components that work for Vue 2 and Vue 3; however, setting up the tooling required to develop, lint, compile, test, and deploy these libraries can be painful and there are often conflicts between versions.
59 |
60 | Most Vue library authors:
61 |
62 | 1. Use vue-demi for headless components
63 | 2. Only choose Vue 3 when writing renderable components
64 | 3. Painstakingly backport Vue 3 features to Vue 2 user
65 |
66 | Petite gives you a GitHub template that works out-of-the-box with all of the modern tooling you'd like to use, and asks you to make few concessions on how you write your code.
67 |
68 | ## Getting Started
69 |
70 | 1. Grab Petite, either via GitHub's "Use Template" button or `degit`.
71 |
72 | ```sh
73 | npx degit JessicaSachs/petite your-project-name
74 | ```
75 |
76 | 2. Install pnpm if you don't have it.
77 | 3. `pnpm install`
78 | 4. Change all references to `@petite/lib-vue2` and `@petite/lib-vue3` to your library's name.
79 | 5. Optionally rename the folders, as well.
80 |
81 | ## Development
82 |
83 | ```sh
84 | pnpm dev:3
85 | ```
86 |
87 | Vite suggests that library authors re-use their `index.html` as a sort of playground and ship that as a website. This can be helpful for getting started, but it's sometimes not enough.
88 |
89 | Petite has a couple of tools that it ships with that can help library authors develop quickly and deliver well-tested libraries.
90 |
91 | 1. [Component-driven development](https://on.cypress.io/component) with Cypress.
92 | 1. Docs-driven development with Vitepress.
93 | 1. Vite's own `index.html`, which can then be [End-to-end](#end-to-end-testing) tested.
94 |
95 | ### Reactivity Caveats
96 |
97 | When writing Universal SFC Components, any reactivity caveats to objects and arrays will still apply. Please ensure that you abide by the rules in the [Reactivity section](https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats) of the Vue 2 docs.
98 |
99 | ## Linting
100 |
101 | Petite ships with Antfu's eslint config (no-semis, trailing commas) and prettier.
102 |
103 | - Lints on save (VSCode)
104 | - Lints markdown, tsx, vue, ts, and more.
105 | - Lints before commit using `lint-staged` and `simple-git-hooks`
106 |
107 | ## Package Deployment
108 |
109 | Rather than break on a major version to support Vue 2 or Vue 3, you will ship two versions of your package on npm.
110 |
111 | The downside of this is that your users will need to install a new version when they upgrade and change their imports. The upside is that it is much easier for you to write backwards compatible code and provide regular upgrades for your users. Additionally, you are able to split out your Vue 2-only and Vue 3-only dependencies.
112 |
113 | If you use `lodash` in your universal code, you'll want to
114 |
115 | After you run `pnpm build` in the workspace root, each package (`lib-vue3`, `lib-vue2`) should be deployed independently.
116 |
117 | ## Styles
118 |
119 | Petite supports both Atomic CSS frameworks such as UnoCSS, Tailwind, and WindiCSS. It comes with UnoCSS and a style reset.
120 |
121 | ### Vanilla CSS
122 |
123 | Petite comes with `sass` installed and will export your styles, minified, to `dist/styles.css` for your users to manually install at the top of their `main.ts`.
124 |
125 | ### Atomic CSS
126 |
127 | If you prefer an atomic style framework, such as [UnoCSS](https://github.com/unocss/unocss) or [Tailwind](https://tailwindcss.com/docs/guides/vite)... you're in luck!
128 |
129 | Petite supports these as normal Vite plugins. Just add their relevant configurations to `vite.config.shared.ts` at the root of the workspace using `pnpm install unocss -D -w` (or the Tailwind equivalent) and then follow the setup instructions.
130 |
131 | #### Style Limitations
132 |
133 | Petite provides no ability to extend the themes of your Atomic CSS libraries. This is out-of-scope for Petite, whose purpose is to give you a build configuration that will give you an environment to build + test + publish Vue 2 + Vue 3 compatible libraries.
134 |
135 | If you would like to provide your users with the ability to extend your CSS either at runtime or build time, consider using vanilla CSS variables or other runtime solutions.
136 |
137 | ## Testing
138 |
139 | Petite ships with Unit Testing via Vitest and Component Testing via Cypress. Both of them re-use your Vite config and Vite dev server.
140 |
141 | ### Unit Testing
142 |
143 | In any of the libraries, or the workspace root you are able to run a test command. Simply append `--watch` if you'd like to re-run your tests on change.
144 |
145 | ```sh
146 | pnpm test:unit
147 | ```
148 |
149 | Unit testing support is provided by [Vitest](https://vitest.dev) and, like Cypress, will re-use each application's build configuration. This means that if you do something that would break Vue 2 or Vue 3 only, your tests will catch it.
150 |
151 | ### Component Testing
152 |
153 | Petite's development ethos is centered around the belief that library code should be developed in the environment that users will consume it in. For components that render, Petite uses Cypress Component Testing.
154 |
155 | Cypress is a thorough way to accomplish component testing and isolated component development in one tool.
156 |
157 | **TODO:** Petite aspires to use [Histoire](https://histoire.dev) which you could use as an isolated sandbox and then point an end-to-end testing tool at. Histoire is a Vite-based Storybook replacement and is developed by Vue Core Team Member, [Akyrum](https://github.com/akryum).
158 |
159 | ### End-to-end Testing
160 |
161 | This is not implemented. If your library requires a lot of app-level dependencies or setup before users can use your library, you may consider writing End-to-end tests that test the integration of your user's JS, any HTML, and any styles you expect them to write before loading and using your library code.
162 |
163 | Cypress is already installed and has linting and types already set up, however you'll have to start and run an example playground app yourself.
164 |
165 | Here are some approaches to end-to-end testing your library code.
166 |
167 | #### Quick: Sanity check one version of Vue
168 |
169 | 1. Run `pnpm build`
170 | 1. Use Vite's `index.html` and import the files directly using module syntax
171 | 1. Run Cypress to test the example usage
172 |
173 | ```html
174 |
175 |
176 |
177 |
178 |
179 |
190 |
191 | ```
192 |
193 | #### Robust: Scaffold user applications and test against them
194 |
195 | A more complex integration testing architecture allows you to test that any integrated Vite changes are working as expected. E.g. if you're writing a Vite plugin with virtual modules, etc... this is a viable strategy.
196 |
197 | **Create the test applications**
198 |
199 | 1. Create test workspaces with their own Vite config files
200 | 1. Create fully-formed applications within them
201 | 1. Use [pnpm's `workspace` notation](https://pnpm.io/workspaces) to import the package's dist'd files.
202 |
203 | **Run your E2E tests**
204 | At the workspace root...
205 |
206 | 1. Run `pnpm build` at the workspace root
207 | 1. Start up each workspace's Vite server (`vite preview` or `vite dev`)
208 | 1. Once the server is started, begin executing your End-to-end tests.
209 |
210 | ## Docs
211 |
212 | ```sh
213 | pnpm run docs
214 | ```
215 |
216 | All good libraries have documentation. Petite uses Vitepress for its documentation.
217 |
218 | ## Thanks
219 |
220 | Inspired by the following repositories:
221 |
222 | - [Vitesse](https://github.com/antfu/vitesse)
223 | - [VueUse](https://github.com/vueuse/vueuse)
224 | - [Vue Bridge](https://github.com/vue-bridge/vue-bridge)
225 |
226 | Big thanks to Anthony Fu ([@antfu](https://github.com/antfu)) for the templates and resources and Thorsten Lünborg ([@LinusBorg](https://github.com/linusborg)) for answering questions and walking me through [Vue Bridge](https://github.com/vue-bridge/vue-bridge).
227 |
--------------------------------------------------------------------------------
/cypress/support/component-index.html:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | Component Tests
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/cypress/support/component.shared.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Add Vue-agnostic support code here.
3 | * For example: UnoCSS, Percy, Cypress browser-side plugins.
4 | */
5 |
6 | console.log('hello world')
7 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "petite",
3 | "private": true,
4 | "packageManager": "pnpm@7.8.0",
5 | "version": "1.0.0",
6 | "description": "An opinionated Vue template for libraries that want to support delivering both Vue 2 and Vue 3 components. TypeScript-first, with infrastructure to test, develop, and document your components against multiple versions of Vue.",
7 | "repository": "https://github.com/JessicaSachs/petite",
8 | "keywords": [
9 | "vue",
10 | "vue3",
11 | "vue2",
12 | "vue library",
13 | "library template",
14 | "monorepo template"
15 | ],
16 | "type": "module",
17 | "author": "Jessica Sachs",
18 | "license": "MIT",
19 | "scripts": {
20 | "clean": "pnpm --filter @petite/* clean",
21 | "lint": "pnpm lint:code",
22 | "lint:code": "pnpm eslint --cache --fix packages/lib-*",
23 | "dev:3": "pnpm --filter @petite/lib-vue3 dev",
24 | "dev:2": "pnpm --filter @petite/lib-vue2 dev",
25 | "dev:docs": "pnpm --filter docs dev",
26 | "build": "pnpm --filter @petite/lib-* build",
27 | "build:docs": "pnpm --filter @petite/docs build",
28 | "postinstall": "esno scripts/symlink packages/lib-vue3/src packages/lib-vue2/src"
29 | },
30 | "devDependencies": {
31 | "@antfu/eslint-config": "0.25.2",
32 | "@typescript-eslint/parser": "5.36.2",
33 | "@vue-bridge/eslint-config": "0.2.0",
34 | "@testing-library/cypress": "8.0.3",
35 | "@vue/tsconfig": "0.1.3",
36 | "cpy-cli": "4.2.0",
37 | "cypress": "10.8.0",
38 | "eslint": "8.23.0",
39 | "esno": "0.16.3",
40 | "lint-staged": "13.0.3",
41 | "npm-run-all": "4.1.5",
42 | "pkg-types": "^0.3.5",
43 | "pnpm": "7.9.3",
44 | "prettier": "2.7.1",
45 | "rimraf": "3.0.2",
46 | "sass": "^1.55.0",
47 | "typescript": "4.7.4",
48 | "unplugin-vue-components": "0.22.7",
49 | "vite": "^3.1.4",
50 | "vite-plugin-dts": "^1.5.0",
51 | "vitest": "0.23.2",
52 | "vue-eslint-parser": "9.1.0",
53 | "vue-tsc": "0.40.13"
54 | },
55 | "peerDependenciesMeta": {
56 | "vue": {
57 | "optional": true
58 | }
59 | },
60 | "simple-git-hooks": {
61 | "pre-commit": "run-s lint-staged build"
62 | },
63 | "lint-staged": {
64 | "*.{js,ts,tsx,md,vue}": "pnpm lint",
65 | "*.{js,css,md,vue}": "prettier --write"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/packages/docs/.gitignore:
--------------------------------------------------------------------------------
1 | components/*.md
2 | composables/*.md
--------------------------------------------------------------------------------
/packages/docs/.vitepress/config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vitepress'
2 | import { readPackageJSON } from 'pkg-types'
3 |
4 | const libraryPath = '../../package.json'
5 | const packageJson = await readPackageJSON(libraryPath)
6 | const githubLink = (typeof packageJson.repository === 'string'
7 | ? packageJson.repository
8 | : packageJson.repository?.url)
9 | ?? 'https://github.com/JessicaSachs/petite'
10 |
11 | // Define your App-level Config
12 | // Read more about this on the official Vitepress docs.
13 | export default async () => defineConfig({
14 | title: 'Petite Docs',
15 | description: packageJson.description,
16 | themeConfig: {
17 | nav: [
18 | { text: 'Home', link: '/' },
19 | { text: 'Guide', link: '/guide' },
20 | { text: 'GitHub', link: githubLink },
21 | ],
22 | },
23 | })
24 |
--------------------------------------------------------------------------------
/packages/docs/README.md:
--------------------------------------------------------------------------------
1 | # Documentation Development
2 |
3 | Your documentation site is written in Vitepress, which has support for writing **Vue 3** components in-line.
4 |
5 | You are still able to preview your Vue 2-only code within your Vitepress site, but you have to set that up.
6 |
7 | If you're new to Vitepress, check out their [documentation](https://vitepress.vuejs.org/) but in general, there are a few concepts:
8 |
9 | 1. The Theme - Installing, modifying, or otherwise playing with the runtime of your application.
10 | 2. The Build - Configure your site's smart defaults (Title, Description, Colors)
11 | 3. The Content - Markdown files.
12 |
--------------------------------------------------------------------------------
/packages/docs/guide.md:
--------------------------------------------------------------------------------
1 | # Petite ✌️
2 |
3 | Petite is an opinionated GitHub template built for Vue component authors. It sets up the tooling required to develop, document, and test **Universal SFC Components** that are backwards-compatible with the Vue 2.7 runtime.
4 |
5 | This is accomplished with some runtime helpers and a very opinionated monorepo structure.
6 |
7 | Petite sets up [Vite](https://vitejs.dev), Volar, Linting, Vitepress, TypeScript, and Testing so that you can choose to write Vue 3-style code while easily maintaining backwards compatibility for your Vue 2.x users. 👏👏
8 |
9 | ## Why Petite?
10 |
11 | It is possible to write universal Vue SFC components that work for Vue 2 and Vue 3; however, setting up the tooling required to develop, lint, compile, test, and deploy these libraries can be painful and there are often conflicts between versions.
12 |
13 | Most Vue library authors:
14 |
15 | 1. Use vue-demi for headless components
16 | 2. Only choose Vue 3 when writing renderable components
17 | 3. Painstakingly backport Vue 3 features to Vue 2 user
18 |
19 | Petite gives you a GitHub template that works out-of-the-box with all of the modern tooling you'd like to use, and asks you to make few concessions on how you write your code.
20 |
21 | ## Getting Started
22 |
23 | 1. Grab Petite, either via GitHub's "Use Template" button or `degit`.
24 |
25 | ```sh
26 | npx degit JessicaSachs/petite your-project-name
27 | ```
28 |
29 | 2. Install pnpm if you don't have it.
30 | 3. `pnpm install`
31 | 4. Change all references to `@petite/lib-vue2` and `@petite/lib-vue3` to your library's name.
32 | 5. Optionally rename the folders, as well.
33 |
34 | ## Development
35 |
36 | I recommend you develop in Vue 3, because HMR is less likely to have issues. Periodically use Volar, linting, or Cypress to check that you're not breaking Vue 2.
37 |
38 | ```sh
39 | pnpm dev:3
40 | ```
41 |
42 | Vite suggests that library authors re-use their `index.html` as a sort of playground and ship that as a website. This can be helpful for getting started, but it's sometimes not enough.
43 |
44 | Petite has a couple of tools that it ships with that can help library authors develop quickly and deliver well-tested libraries.
45 |
46 | 1. [Component-driven development](https://on.cypress.io/component) with Cypress.
47 | 1. Docs-driven development with Vitepress.
48 | 1. Vite's own `index.html`, which can then be [End-to-end](#end-to-end-testing) tested.
49 |
50 | ### Reactivity Caveats
51 |
52 | When writing Universal SFC Components, any reactivity caveats to objects and arrays will still apply. Please ensure that you abide by the rules in the [Reactivity section](https://v2.vuejs.org/v2/guide/reactivity.html#Change-Detection-Caveats) of the Vue 2 docs.
53 |
54 | ## Linting
55 |
56 | Petite ships with Antfu's eslint config (no-semis, trailing commas) and prettier.
57 |
58 | - Lints on save (VSCode)
59 | - Lints markdown, tsx, vue, ts, and more.
60 | - Lints before commit using `lint-staged` and `simple-git-hooks`
61 |
62 | ## Package Deployment
63 |
64 | Rather than break on a major version to support Vue 2 or Vue 3, you will ship two versions of your package on npm.
65 |
66 | The downside of this is that your users will need to install a new version when they upgrade and change their imports. The upside is that it is much easier for you to write backwards compatible code and provide regular upgrades for your users. Additionally, you are able to split out your Vue 2-only and Vue 3-only dependencies.
67 |
68 | If you use `lodash` in your universal code, you'll want to
69 |
70 | After you run `pnpm build` in the workspace root, each package (`lib-vue3`, `lib-vue2`) should be deployed independently.
71 |
72 | ## Styles
73 |
74 | Petite supports both Atomic CSS frameworks such as UnoCSS, Tailwind, and WindiCSS. It comes with UnoCSS and a style reset.
75 |
76 | ### Vanilla CSS
77 |
78 | Petite comes with `sass` installed and will export your styles, minified, to `dist/styles.css` for your users to manually install at the top of their `main.ts`.
79 |
80 | ### Atomic CSS
81 |
82 | If you prefer an atomic style framework, such as [UnoCSS](https://github.com/unocss/unocss) or [Tailwind](https://tailwindcss.com/docs/guides/vite)... you're in luck!
83 |
84 | Petite supports these as normal Vite plugins. Just add their relevant configurations to `vite.config.shared.ts` at the root of the workspace using `pnpm install unocss -D -w` (or the Tailwind equivalent) and then follow the setup instructions.
85 |
86 | #### Style Limitations
87 |
88 | Petite provides no ability to extend the themes of your Atomic CSS libraries. This is out-of-scope for Petite, whose purpose is to give you a build configuration that will give you an environment to build + test + publish Vue 2 + Vue 3 compatible libraries.
89 |
90 | If you would like to provide your users with the ability to extend your CSS either at runtime or build time, consider using vanilla CSS variables or other runtime solutions.
91 |
92 | ## Testing
93 |
94 | Petite ships with Unit Testing via Vitest and Component Testing via Cypress. Both of them re-use your Vite config and Vite dev server.
95 |
96 | ### Unit Testing
97 |
98 | In any of the libraries, or the workspace root you are able to run a test command. Simply append `--watch` if you'd like to re-run your tests on change.
99 |
100 | ```sh
101 | pnpm test:unit
102 | ```
103 |
104 | Unit testing support is provided by [Vitest](https://vitest.dev) and, like Cypress, will re-use each application's build configuration. This means that if you do something that would break Vue 2 or Vue 3 only, your tests will catch it.
105 |
106 | ### Component Testing
107 |
108 | Petite's development ethos is centered around the belief that library code should be developed in the environment that users will consume it in. For components that render, Petite uses Cypress Component Testing.
109 |
110 | Cypress is a thorough way to accomplish component testing and isolated component development in one tool.
111 |
112 | **TODO:** Petite aspires to use [Histoire](https://histoire.dev) which you could use as an isolated sandbox and then point an end-to-end testing tool at. Histoire is a Vite-based Storybook replacement and is developed by Vue Core Team Member, [Akyrum](https://github.com/akryum).
113 |
114 | ### End-to-end Testing
115 |
116 | This is not implemented. If your library requires a lot of app-level dependencies or setup before users can use your library, you may consider writing End-to-end tests that test the integration of your user's JS, any HTML, and any styles you expect them to write before loading and using your library code.
117 |
118 | Cypress is already installed and has linting and types already set up, however you'll have to start and run an example playground app yourself.
119 |
120 | Here are some approaches to end-to-end testing your library code.
121 |
122 | #### Quick: Sanity check one version of Vue
123 |
124 | 1. Run `pnpm build`
125 | 1. Use Vite's `index.html` and import the files directly using module syntax
126 | 1. Run Cypress to test the example usage
127 |
128 | ```html
129 |
130 |
131 |
132 |
133 |
134 |
145 |
146 | ```
147 |
148 | #### Robust: Scaffold user applications and test against them
149 |
150 | A more complex integration testing architecture allows you to test that any integrated Vite changes are working as expected. E.g. if you're writing a Vite plugin with virtual modules, etc... this is a viable strategy.
151 |
152 | **Create the test applications**
153 |
154 | 1. Create test workspaces with their own Vite config files
155 | 1. Create fully-formed applications within them
156 | 1. Use [pnpm's `workspace` notation](https://pnpm.io/workspaces) to import the package's dist'd files.
157 |
158 | **Run your E2E tests**
159 | At the workspace root...
160 |
161 | 1. Run `pnpm build` at the workspace root
162 | 1. Start up each workspace's Vite server (`vite preview` or `vite dev`)
163 | 1. Once the server is started, begin executing your End-to-end tests.
164 |
165 | ## Docs
166 |
167 | Petite uses [Vitepress](https://vitepress.vuejs.org/) for its documentation. All good libraries have documentation.
168 |
169 | ```sh
170 | pnpm dev:docs
171 | ```
172 |
173 | ## Thanks
174 |
175 | Inspired by the following repositories:
176 |
177 | - [Vitesse](https://github.com/antfu/vitesse)
178 | - [VueUse](https://github.com/vueuse/vueuse)
179 | - [Vue Bridge](https://github.com/vue-bridge/vue-bridge)
180 |
181 | Big thanks to Anthony Fu ([@antfu](https://github.com/antfu)) for the templates and resources and Thorsten Lünborg ([@LinusBorg](https://github.com/linusborg)) for answering questions and walking me through [Vue Bridge](https://github.com/vue-bridge/vue-bridge).
182 |
--------------------------------------------------------------------------------
/packages/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: home
3 |
4 | hero:
5 | name: Petite
6 | text: A tiny template for Vue Library Authors
7 | tagline: Easily ship universal Vue 2 and Vue 3 components.
8 | actions:
9 | - theme: brand
10 | text: Get Started
11 | link: /guide
12 | - theme: alt
13 | text: View on GitHub
14 | link: https://github.com/JessicaSachs/petite
15 | features:
16 | - icon: ⚡️
17 | title: Vite-first, always.
18 | details: With Vite, Vitest, and Cypress Tests - all your tooling re-uses the same compilation config.
19 | - icon: 🖖
20 | title: Get Vue 2.7 + Vue 3 support at the same time, with the same source code
21 | details: Target Vue versions without re-writing or backporting fixes.
22 | - icon: 🛠️
23 | title: All the tools you'd expect, working in harmony
24 | details: Volar, Eslint, Prettier, and TypeScript, all configured correctly.
25 | ---
26 |
--------------------------------------------------------------------------------
/packages/docs/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@petite/docs",
3 | "private": true,
4 | "type": "module",
5 | "packageManager": "pnpm@7.8.0",
6 | "version": "0.0.0",
7 | "description": "Documentation for both the Vue 2 and Vue 3 versions of your components.",
8 | "author": "Jessica Sachs",
9 | "license": "MIT",
10 | "scripts": {
11 | "dev": "vitepress dev .",
12 | "build": "vitepress build .",
13 | "clean": "rimraf .vitepress/dist"
14 | },
15 | "dependencies": {
16 | "vue": "3.2.39"
17 | },
18 | "devDependencies": {
19 | "vitepress": "1.0.0-alpha.13"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/packages/lib-vue2/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["../../.eslintrc", "@vue-bridge/eslint-config"]
3 | }
4 |
--------------------------------------------------------------------------------
/packages/lib-vue2/README.md:
--------------------------------------------------------------------------------
1 | # Vue 2 Compat Build Package
2 |
3 | This package is the Vue 2 version of your library. It can
4 |
5 | - test
6 | - build
7 | - publish
8 |
9 | the code symlinked from `/lib-vue3/src` as a Vue 2 plugin library.
10 |
11 | ## Things to be aware of.
12 |
13 | - The src folder is just a symlink to `/lib-vue3/src`. So both packages share the same source.
14 | - You need to synchronize the dependencies between both packages. if you add a new dependency to `/lib-vue3`, you likely also want to install it here. pnpm can help doing that in a single command: running i.e. `pnpm add lodash-es --filter="example-*"` in the project root will add the package `lodash-es` as a dependencies to both `/lib-vue2` and `/lib-vue3` packages, as their package names both start with `example-*`.
15 |
16 | ## Alternative Publishing Strategy
17 |
18 | This template is preconfigured to publish the Vue 2 and Vue 3 versions in individual packages. This is generally the recommended approach so you don't have to mix dependencies for Vue 2 and Vue 3 in package.json.
19 |
20 | However, you can alternatively decide to publish just one package, including both the Vue 2 and Vue 3 bundles. Just be aware that you might encounter trouble if you need different versions of a dependency for each version.
21 |
22 | Here's what you would have to change for that:
23 |
24 | ### `/lib-vue2/package.json`
25 |
26 | - give a distinct name
27 | - make it private so it won't be published.
28 | - Optionally remove module exports & entry points
29 |
30 | ```diff
31 | {
32 | "name": "some-name-different-from-lib-vue3",
33 | + "private": "false",
34 | "type": "module",
35 | - "main": "dist/index.cjs.cjs",
36 | - "module": "dist/index.es.js",
37 | - "typings": "index.vue2.d.ts",
38 | - "exports": {
39 | - ".": {
40 | -- "script": "dist/index.iife.js",
41 | - "import": "dist/index.es.js",
42 | - "require": "dist/index.cjs.cjs"
43 | - },
44 | - },
45 | - "files": [
46 | - "dist",
47 | - "src",
48 | - "index.vue2.d.ts",
49 | - "README.md",
50 | - ]
51 | }
52 | ```
53 |
54 | ### `/lib-vue2/vite.config.ts`
55 |
56 | change build output to `../lib-vue3/dist`:
57 |
58 | ```diff
59 | // ...
60 | build: buildConfig({
61 | - outDir: "./dist",
62 | + outDir: "../lib-vue3/dist-vue2",
63 | }),
64 | //...
65 | ```
66 |
67 | ### Setup src folder
68 |
69 | Right now, `/lib-vue2` is a symlink to `/lib-vue3/src`. For types, we need to publish the src folder with out package, not just a symlink. so instead, we need an actual copy of the source folder
70 |
71 | 1. Remove symlink - just delete the `/lib-vue2/src` folder.
72 | 2. install `rimraf` && `cpy-cli`
73 |
74 | ```bash
75 | # in /lib-vue2
76 | pnpm add -D cpy-cli
77 | ```
78 |
79 | 3. Adjust these scripts in package.json - we don't need the copy script anymore:
80 |
81 | ```diff
82 | "script": {
83 | "dev": "vite build --watch",
84 | - "build": "pnpm prepare-src && copy-src",
85 | + "build": "vite build",
86 | - "copy-src: "cpy ../lib-vue3/src/* ./dist/src/"
87 | # ....
88 | }
89 | ```
90 |
91 | ### `index.vue2.d.ts`
92 |
93 | Move `/lib-vue2/index.d.ts` to `/lib-vue3/index.vue2.d.ts` and make it export the src directory there.
94 |
95 | ## module exports in `/lib-vue3`
96 |
97 | ```json
98 | {
99 | "main": "dist/index.cjs.cjs",
100 | "module": "dist/index.es.js",
101 | "jsdelivr": "dist/index.iife.js",
102 | "unpkg": "dist/index.iife.js",
103 | "typings": "./typings/main.d.ts",
104 | "exports": {
105 | ".": {
106 | "types": "./typings/main.d.ts",
107 | "script": "./dist/index.iife.js",
108 | "import": "./dist/index.es.js",
109 | "require": "./dist/index.cjs.cjs",
110 | "default": "./dist/index.es.js"
111 | },
112 | "./vue3": {
113 | "types": "./typings/main.d.ts",
114 | "script": "./dist/index.iife.js",
115 | "import": "./dist/index.es.js",
116 | "require": "./dist/index.cjs.cjs",
117 | "default": "./dist/index.es.js"
118 | },
119 | "./vue2": {
120 | "types": "./index.vue2.d.ts",
121 | "script": "./dist-vue2/index.iife.js",
122 | "import": "./dist-vue2/index.es.js",
123 | "require": "./dist-vue2/index.cjs.cjs",
124 | "default": "./dist-vue2/index.es.js"
125 | },
126 | "./style.css": "./dist/style.css",
127 | "./src/": "./src/",
128 | "./package.json": "./package.json"
129 | }
130 | }
131 | ```
132 |
133 | ### That's it!
134 |
135 | Now you can run `pnpm release` and pnpm will test, build and publish both version in one package!
136 |
--------------------------------------------------------------------------------
/packages/lib-vue2/components.d.ts:
--------------------------------------------------------------------------------
1 | // generated by unplugin-vue-components
2 | // We suggest you to commit this file into source control
3 | // Read more: https://github.com/vuejs/core/pull/3399
4 | import '@vue/runtime-core'
5 |
6 | export {}
7 |
8 | declare module '@vue/runtime-core' {
9 | export interface GlobalComponents {
10 | Counter: typeof import('./src/components/Counter/Counter.vue')['default']
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/lib-vue2/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import url from 'node:url'
2 | import path from 'node:path'
3 | import { defineConfig } from 'cypress'
4 |
5 | const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
6 |
7 | export default defineConfig({
8 | component: {
9 | indexHtmlFile: path.resolve(__dirname, '../../cypress/support/component-index.html'),
10 | supportFile: path.resolve(__dirname, './cypress/support/component.ts'),
11 | devServer: {
12 | bundler: 'vite',
13 | framework: 'vue',
14 | },
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/packages/lib-vue2/cypress/support/component.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Set up any app code that should run once per file for the
3 | * Vue 2 app. For example, the mount command for Cypress is different
4 | * because it imports a Vue 2-only version of Vue Test Utils.
5 | *
6 | * Common code should be imported from the root of the project.
7 | */
8 |
9 | import { mount } from 'cypress/vue2'
10 | import '../../../../cypress/support/component.shared'
11 |
12 | declare global {
13 | namespace Cypress {
14 | interface Chainable {
15 | mount: (renderFn: () => JSX.Element) => ReturnType
16 | }
17 | }
18 | }
19 |
20 | /**
21 | * Example usage of cy.mount can be found at https://on.cypress.io/component
22 | * We prefer to use JSX syntax which looks like this:
23 | *
24 | * cy.mount(() => )
25 | */
26 |
27 | Cypress.Commands.add('mount', (render) => {
28 | return mount({
29 | render,
30 | })
31 | })
32 |
--------------------------------------------------------------------------------
/packages/lib-vue2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Help
8 |
9 |
10 |
11 | Hey yallllll
12 |
13 |
14 |
--------------------------------------------------------------------------------
/packages/lib-vue2/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@petite/lib-vue2",
3 | "type": "module",
4 | "version": "0.0.0",
5 | "exports": {
6 | ".": {
7 | "types": "./typings/main.d.ts",
8 | "script": "./dist/index.umd.js",
9 | "require": "./dist/index.cjs.cjs",
10 | "import": "./dist/index.es.mjs",
11 | "default": "./dist/index.umd.js"
12 | },
13 | "./style.css": "./dist/style.css",
14 | "./package.json": "./package.json",
15 | "./src/": "./src/"
16 | },
17 | "main": "dist/index.umd.cjs",
18 | "module": "dist/index.es.mjs",
19 | "unpkg": "dist/index.umd.js",
20 | "jsdelivr": "dist/index.umd.js",
21 | "files": [
22 | "dist",
23 | "src",
24 | "README.md"
25 | ],
26 | "scripts": {
27 | "dev": "pnpm cypress open --component --browser=chrome",
28 | "test:ci": "run-s test:unit test:component",
29 | "test:unit": "pnpm vitest",
30 | "test:component": "pnpm cypress run --component --browser=chrome",
31 | "build": "vite build",
32 | "build:watch": "vite build --watch",
33 | "clean": "rimraf dist"
34 | },
35 | "browserslist": [
36 | "IE 11, >3%, last 2 versions"
37 | ],
38 | "peerDependencies": {
39 | "vue": "^2.7"
40 | },
41 | "peerDependenciesMeta": {
42 | "vue": {
43 | "optional": true
44 | }
45 | },
46 | "dependencies": {
47 | "@swc/helpers": "^0.4.11",
48 | "@vue-bridge/runtime": "^0.1.1",
49 | "core-js": "^3.25.0",
50 | "regenerator-runtime": "^0.13.9"
51 | },
52 | "devDependencies": {
53 | "@vitejs/plugin-vue2": "^2.0.0",
54 | "@vitejs/plugin-vue2-jsx": "1.0.3",
55 | "@vue-bridge/testing": "0.2.1",
56 | "@vue-bridge/vite-plugin": "^0.2.0",
57 | "browserslist": "^4.21.3",
58 | "vite": "^3.1.4",
59 | "vue": "2.7.10"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/packages/lib-vue2/src:
--------------------------------------------------------------------------------
1 | /Users/jess.sachs/petite/packages/lib-vue3/src
--------------------------------------------------------------------------------
/packages/lib-vue2/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../tsconfig.json",
3 | "compilerOptions": {
4 | "jsx": "preserve"
5 | },
6 | "vueCompilerOptions": { "target": 2.7 }
7 | }
8 |
--------------------------------------------------------------------------------
/packages/lib-vue2/vite.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue2'
3 | import vueJsx from '@vitejs/plugin-vue2-jsx'
4 | import { vueBridge } from '@vue-bridge/vite-plugin'
5 | import { buildConfig, pluginsConfig, sharedConfig } from '../../vite.config.shared'
6 |
7 | // This is the name of the global you library is accessible in the iife build (for CDN use)
8 | // (window.PetiteLegacy)
9 | const libraryGlobalName = 'PetiteLegacy'
10 |
11 | // https://vitejs.dev/config/
12 | export default defineConfig({
13 | plugins: pluginsConfig([
14 | vue(),
15 | vueJsx(),
16 | // @ts-expect-error Vue Bridge doesn't have a name value.
17 | vueBridge({
18 | vueVersion: '2',
19 | localizeDeps: true,
20 | useSwc: true,
21 | swcOptions: {
22 | env: {
23 | mode: 'usage',
24 | },
25 | jsc: {
26 | parser: {
27 | syntax: 'typescript',
28 | tsx: false,
29 | },
30 | loose: true,
31 | },
32 | },
33 | }),
34 | ]),
35 | resolve: {
36 | alias: {
37 | '@vue-bridge/runtime': '@vue-bridge/runtime/vue2',
38 | },
39 | },
40 | build: buildConfig({
41 | name: libraryGlobalName,
42 | }),
43 | ...sharedConfig(),
44 | })
45 |
46 |
--------------------------------------------------------------------------------
/packages/lib-vue3/components.d.ts:
--------------------------------------------------------------------------------
1 | // generated by unplugin-vue-components
2 | // We suggest you to commit this file into source control
3 | // Read more: https://github.com/vuejs/core/pull/3399
4 | import '@vue/runtime-core'
5 |
6 | export {}
7 |
8 | declare module '@vue/runtime-core' {
9 | export interface GlobalComponents {
10 | Counter: typeof import('./src/components/Counter/Counter.vue')['default']
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/packages/lib-vue3/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import url from 'node:url'
2 | import path from 'node:path'
3 | import { defineConfig } from 'cypress'
4 |
5 | const __dirname = url.fileURLToPath(new URL('.', import.meta.url))
6 |
7 | export default defineConfig({
8 | component: {
9 | indexHtmlFile: path.resolve(__dirname, '../../cypress/support/component-index.html'),
10 | supportFile: path.resolve(__dirname, './cypress/support/component.ts'),
11 | devServer: {
12 | bundler: 'vite',
13 | framework: 'vue',
14 | },
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/packages/lib-vue3/cypress/support/commands.ts:
--------------------------------------------------------------------------------
1 | ///
2 | // ***********************************************
3 | // This example commands.ts shows you how to
4 | // create various custom commands and overwrite
5 | // existing commands.
6 | //
7 | // For more comprehensive examples of custom
8 | // commands please read more here:
9 | // https://on.cypress.io/custom-commands
10 | // ***********************************************
11 | //
12 | //
13 | // -- This is a parent command --
14 | // Cypress.Commands.add('login', (email, password) => { ... })
15 | //
16 | //
17 | // -- This is a child command --
18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
19 | //
20 | //
21 | // -- This is a dual command --
22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
23 | //
24 | //
25 | // -- This will overwrite an existing command --
26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
27 | //
28 | // declare global {
29 | // namespace Cypress {
30 | // interface Chainable {
31 | // login(email: string, password: string): Chainable
32 | // drag(subject: string, options?: Partial): Chainable
33 | // dismiss(subject: string, options?: Partial): Chainable
34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable
35 | // }
36 | // }
37 | // }
38 |
--------------------------------------------------------------------------------
/packages/lib-vue3/cypress/support/component.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Set up any app code that should run once per file for the
3 | * Vue 3 app. For example, the mount command for Cypress is different
4 | * because it imports a Vue 3-only version of Vue Test Utils.
5 | *
6 | * Common code should be imported from the root of the project.
7 | */
8 |
9 | import { mount } from 'cypress/vue'
10 | import '../../../../cypress/support/component.shared'
11 |
12 | declare global {
13 | namespace Cypress {
14 | interface Chainable {
15 | mount: (renderFn: () => JSX.Element) => Cypress.Chainable
16 | }
17 | }
18 | }
19 |
20 | /**
21 | * Example usage of cy.mount can be found at https://on.cypress.io/component
22 | * We prefer to use JSX syntax which looks like this:
23 | *
24 | * cy.mount(() => )
25 | */
26 |
27 | Cypress.Commands.add('mount', (renderFn) => {
28 | return mount(renderFn)
29 | })
30 |
--------------------------------------------------------------------------------
/packages/lib-vue3/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@petite/lib-vue3",
3 | "type": "module",
4 | "version": "0.0.0",
5 | "exports": {
6 | ".": {
7 | "types": "./dist/main.d.ts",
8 | "script": "./dist/index.umd.js",
9 | "require": "./dist/index.cjs.cjs",
10 | "import": "./dist/index.es.mjs",
11 | "default": "./dist/index.umd.js"
12 | },
13 | "./style.css": "./dist/style.css",
14 | "./package.json": "./package.json",
15 | "./src/": "./src/"
16 | },
17 | "main": "dist/index.umd.js",
18 | "module": "dist/index.es.mjs",
19 | "unpkg": "dist/index.umd.js",
20 | "jsdelivr": "dist/index.umd.js",
21 | "files": [
22 | "dist",
23 | "src",
24 | "README.md"
25 | ],
26 | "scripts": {
27 | "dev": "pnpm cypress open --component --browser=chrome",
28 | "test:ci": "run-s test:unit test:component",
29 | "test:unit": "pnpm vitest",
30 | "test:component": "pnpm cypress run --component --browser=chrome",
31 | "build": "vite build",
32 | "build:watch": "vite build --watch",
33 | "clean": "rimraf dist",
34 | "playground": "pnpm vite"
35 | },
36 | "dependencies": {
37 | "@vue-bridge/runtime": "0.1.1"
38 | },
39 | "devDependencies": {
40 | "@vitejs/plugin-vue": "^3.0.3",
41 | "@vitejs/plugin-vue-jsx": "2.0.1",
42 | "@vue-bridge/vite-plugin": "^0.2.0",
43 | "vite": "^3.1.4",
44 | "vue": "^3.2.37"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/packages/lib-vue3/src/components/Counter/Counter.cy.tsx:
--------------------------------------------------------------------------------
1 | import Counter from './Counter.vue'
2 |
3 | describe('', () => {
4 | it('renders', () => {
5 | cy.mount(() => )
6 | .get('button')
7 | .first()
8 | .click()
9 | .click()
10 |
11 | // Assertions
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/packages/lib-vue3/src/components/Counter/Counter.test.ts:
--------------------------------------------------------------------------------
1 | import { it } from 'vitest'
2 | import Counter from './Counter.vue'
3 |
4 | it('works', () => {
5 | Counter
6 | })
7 |
--------------------------------------------------------------------------------
/packages/lib-vue3/src/components/Counter/Counter.vue:
--------------------------------------------------------------------------------
1 |
30 |
31 |
32 |