├── .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 | 42 | -------------------------------------------------------------------------------- /packages/lib-vue3/src/main.ts: -------------------------------------------------------------------------------- 1 | import './styles/main.scss' 2 | 3 | export * as Counter from './components/Counter/Counter.vue' 4 | -------------------------------------------------------------------------------- /packages/lib-vue3/src/styles/main.scss: -------------------------------------------------------------------------------- 1 | .btn { 2 | margin: 0.5rem 1rem; 3 | background: red; 4 | } 5 | -------------------------------------------------------------------------------- /packages/lib-vue3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "jsx": "preserve" 5 | }, 6 | "vueCompilerOptions": { "target": 3.0 } 7 | } 8 | -------------------------------------------------------------------------------- /packages/lib-vue3/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import vueJsx from '@vitejs/plugin-vue-jsx' 4 | import { vueBridge } from '@vue-bridge/vite-plugin' 5 | 6 | import { buildConfig, pluginsConfig, sharedConfig } from '../../vite.config.shared' 7 | 8 | // This is the name of the global you library is accessible in the iife build (for CDN use) 9 | // (window.Petite) 10 | const libraryGlobalName = 'Petite' 11 | 12 | // https://vitejs.dev/config/ 13 | export default defineConfig({ 14 | plugins: pluginsConfig([ 15 | vue(), 16 | vueJsx(), 17 | // @ts-expect-error Vue Bridge doesn't have a name value. 18 | vueBridge({ 19 | vueVersion: '3', 20 | localizeDeps: true, 21 | useSwc: true, 22 | swcOptions: { 23 | env: { 24 | mode: 'usage', 25 | }, 26 | jsc: { 27 | parser: { 28 | syntax: 'typescript', 29 | tsx: false, 30 | }, 31 | loose: true, 32 | }, 33 | }, 34 | }), 35 | ]), 36 | resolve: { 37 | alias: { 38 | '@vue-bridge/runtime': '@vue-bridge/runtime/vue3', 39 | }, 40 | }, 41 | build: buildConfig({ 42 | name: libraryGlobalName, 43 | }), 44 | ...sharedConfig(), 45 | }) 46 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - packages/* -------------------------------------------------------------------------------- /scripts/symlink.ts: -------------------------------------------------------------------------------- 1 | import { existsSync } from 'fs' 2 | import { symlink } from 'node:fs/promises' 3 | import { argv } from 'process' 4 | import { resolve } from 'path' 5 | 6 | const from = argv[2] 7 | const to = argv[3] 8 | 9 | const fromPath = resolve(process.cwd(), from) 10 | const toPath = resolve(process.cwd(), to) 11 | 12 | if (!existsSync(from)) { 13 | console.error(`⚠️ ERROR: path not found: ${from} relative to ${process.cwd()}`) 14 | process.exit(1) 15 | } 16 | 17 | if (!existsSync(toPath)) 18 | await symlink(fromPath, toPath, 'junction') 19 | 20 | else 21 | console.info(`Symlink in '${toPath}' already exists.`) 22 | -------------------------------------------------------------------------------- /tsconfig.cy.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["cypress"], 5 | "jsx": "preserve", 6 | "jsxFactory": "h", 7 | "jsxFragmentFactory": "h" 8 | }, 9 | "files": ["**/*.cy.tsx", "**/*.cy.ts", "**/*.cy.js", "**/*.cy.jsx", "cypress"] 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "ESNext", 5 | "target": "ESNext", 6 | "lib": ["DOM", "ESNext"], 7 | "strict": true, 8 | "isolatedModules": false, 9 | "esModuleInterop": true, 10 | "jsx": "preserve", 11 | "jsxFactory": "h", 12 | "jsxFragmentFactory": "h", 13 | "skipLibCheck": true, 14 | "moduleResolution": "node", 15 | "resolveJsonModule": true, 16 | "noUnusedLocals": true, 17 | "strictNullChecks": true, 18 | "allowJs": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "types": ["vite/client"], 21 | "paths": { 22 | "~/*": ["src/*"] 23 | } 24 | }, 25 | "vueCompilerOptions": { "target": 2.7 }, 26 | "exclude": ["dist", "node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /tsconfig.vitest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "types": [ 5 | "vitest" 6 | ] 7 | }, 8 | "files": ["*.test.*", "*.spec.*"] 9 | } -------------------------------------------------------------------------------- /tsconfig.web.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.web.json", 3 | "exclude": ["**/*.cy.*", "**/*.test.*"], 4 | "compilerOptions": { 5 | "composite": true, 6 | "isolatedModules": false, 7 | "jsx": "preserve" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /vite.config.shared.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin, UserConfig } from 'vite' 2 | import Components from 'unplugin-vue-components/vite' 3 | import dts from 'vite-plugin-dts' 4 | 5 | export interface VueBridgeBuildOptions { 6 | name: string 7 | outDir?: string 8 | } 9 | 10 | const defaults: Partial = { 11 | outDir: 'dist', 12 | } 13 | 14 | const fileExtensionMap = { 15 | es: 'mjs', 16 | cjs: 'cjs', 17 | iife: 'js', 18 | umd: 'js', 19 | } 20 | 21 | export const pluginsConfig = (plugins: Plugin[] = []): UserConfig['plugins'] => ([ 22 | Components({ 23 | dirs: ['src/components'], 24 | dts: true, 25 | }), 26 | dts(), 27 | ...plugins, 28 | ]) 29 | 30 | export const sharedConfig = (_options = {}): UserConfig => ({ 31 | server: { 32 | watch: { 33 | followSymlinks: true, 34 | }, 35 | fs: { 36 | strict: false, 37 | }, 38 | }, 39 | test: { 40 | environment: 'jsdom', 41 | deps: { 42 | inline: true, 43 | }, 44 | }, 45 | ..._options, 46 | }) 47 | 48 | export const buildConfig = (_options: VueBridgeBuildOptions): UserConfig['build'] => { 49 | const options = Object.assign({}, defaults, _options) 50 | 51 | return { 52 | outDir: options.outDir, 53 | 54 | lib: { 55 | entry: 'src/main.ts', 56 | formats: ['es', 'cjs', 'iife'], 57 | name: options.name, // global variable name for IIFE build 58 | 59 | // @ts-expect-error Not sure why this is invalid. 60 | fileName: (format: keyof typeof fileExtensionMap) => { 61 | return `index.${format}.${fileExtensionMap[format]}` 62 | }, 63 | }, 64 | 65 | rollupOptions: { 66 | output: { 67 | // this means your main.ts file should only have named exports, and no default export! 68 | exports: 'named', 69 | 70 | // Add global names for externalized dependencies here. 71 | // IIFE needs to now how to access external deps like: `window.Vue` 72 | globals: { 73 | 'vue': 'Vue', 74 | '@vue-bridge/runtime': 'VueBridge', 75 | }, 76 | }, 77 | // add any 3rd party packages that you do no want to have bundled in your library 78 | // this *must* contain 'vue' 79 | external: ['vue', '@vue-bridge/runtime'], 80 | }, 81 | } 82 | } 83 | --------------------------------------------------------------------------------