├── .gitattributes ├── .github ├── FUNDING.yml ├── stale.yml └── workflows │ ├── release.yml │ └── screenshot.yml ├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── docs ├── .gitignore ├── babel.config.js ├── docs │ ├── async-screenshots.mdx │ ├── config │ │ ├── _category_.yml │ │ ├── alias.md │ │ ├── browsers.md │ │ ├── css.md │ │ ├── overview.md │ │ ├── static-assets.md │ │ ├── svgr.md │ │ ├── vite.md │ │ └── wrapper.mdx │ ├── getting-started.mdx │ ├── how-it-works.md │ ├── managing-screenshots.md │ ├── troubleshooting.md │ └── why.md ├── docusaurus.config.js ├── package.json ├── sidebars.ts ├── src │ ├── css │ │ └── custom.css │ ├── index.d.ts │ └── pages │ │ ├── index.module.css │ │ └── index.tsx ├── static │ ├── .nojekyll │ ├── img │ │ ├── logo-min.png │ │ ├── logo-min.svg │ │ ├── logo.png │ │ ├── logo.svg │ │ ├── og-image.png │ │ ├── undraw_Outer_space_drqu.svg │ │ ├── undraw_elements_cipa-3.svg │ │ └── undraw_happy_feeling_slmw.svg │ └── videos │ │ └── viteshot.mp4 ├── tsconfig.json ├── vercel.json └── yarn.lock ├── examples ├── preact │ ├── .gitignore │ ├── README.md │ ├── __reactpreview__ │ │ ├── Wrapper.module.css │ │ └── Wrapper.tsx │ ├── package.json │ ├── src │ │ ├── .babelrc │ │ ├── App.screenshot.tsx │ │ ├── __screenshots__ │ │ │ ├── darwin │ │ │ │ ├── laptop │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ │ └── pixel2 │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ ├── linux │ │ │ │ ├── laptop │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ │ └── pixel2 │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ └── win32 │ │ │ │ ├── laptop │ │ │ │ ├── App-App.png │ │ │ │ ├── App-Clicked.png │ │ │ │ └── App-HelloWorld.png │ │ │ │ └── pixel2 │ │ │ │ ├── App-App.png │ │ │ │ ├── App-Clicked.png │ │ │ │ └── App-HelloWorld.png │ │ ├── assets │ │ │ ├── favicon.ico │ │ │ └── icons │ │ │ │ ├── android-chrome-192x192.png │ │ │ │ ├── android-chrome-512x512.png │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── favicon-16x16.png │ │ │ │ ├── favicon-32x32.png │ │ │ │ └── mstile-150x150.png │ │ ├── components │ │ │ ├── app.tsx │ │ │ └── header │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ ├── declaration.d.ts │ │ ├── index.ts │ │ ├── manifest.json │ │ ├── routes │ │ │ ├── home │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ ├── notfound │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ │ └── profile │ │ │ │ ├── index.tsx │ │ │ │ └── style.module.css │ │ ├── style │ │ │ └── index.css │ │ ├── sw.js │ │ └── template.html │ ├── tests │ │ ├── __mocks__ │ │ │ ├── browserMocks.ts │ │ │ ├── fileMocks.ts │ │ │ └── setupTests.ts │ │ ├── declarations.d.ts │ │ └── header.test.tsx │ ├── tsconfig.json │ ├── viteshot.config.js │ ├── viteshot.config.percy.js │ └── yarn.lock ├── react-js │ ├── .gitignore │ ├── README.md │ ├── __reactpreview__ │ │ ├── Wrapper.js │ │ └── Wrapper.module.css │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.js │ │ ├── App.screenshot.js │ │ ├── App.test.js │ │ ├── __screenshots__ │ │ │ ├── darwin │ │ │ │ ├── laptop │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ │ └── pixel2 │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ ├── linux │ │ │ │ ├── laptop │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ │ └── pixel2 │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ └── win32 │ │ │ │ ├── laptop │ │ │ │ ├── App-App.png │ │ │ │ ├── App-Clicked.png │ │ │ │ └── App-HelloWorld.png │ │ │ │ └── pixel2 │ │ │ │ ├── App-App.png │ │ │ │ ├── App-Clicked.png │ │ │ │ └── App-HelloWorld.png │ │ ├── index.css │ │ ├── index.js │ │ ├── logo.svg │ │ ├── reportWebVitals.js │ │ └── setupTests.js │ ├── viteshot.config.js │ └── yarn.lock ├── react-tsx │ ├── README.md │ ├── __reactpreview__ │ │ ├── Wrapper.module.css │ │ └── Wrapper.tsx │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ ├── src │ │ ├── App.css │ │ ├── App.screenshot.tsx │ │ ├── App.test.tsx │ │ ├── App.tsx │ │ ├── __screenshots__ │ │ │ ├── darwin │ │ │ │ ├── laptop │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ │ └── pixel2 │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ ├── linux │ │ │ │ ├── laptop │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ │ └── pixel2 │ │ │ │ │ ├── App-App.png │ │ │ │ │ ├── App-Clicked.png │ │ │ │ │ └── App-HelloWorld.png │ │ │ └── win32 │ │ │ │ ├── laptop │ │ │ │ ├── App-App.png │ │ │ │ ├── App-Clicked.png │ │ │ │ └── App-HelloWorld.png │ │ │ │ └── pixel2 │ │ │ │ ├── App-App.png │ │ │ │ ├── App-Clicked.png │ │ │ │ └── App-HelloWorld.png │ │ ├── index.css │ │ ├── index.tsx │ │ ├── logo.svg │ │ ├── react-app-env.d.ts │ │ ├── reportWebVitals.ts │ │ └── setupTests.ts │ ├── tsconfig.json │ ├── viteshot.config.js │ └── yarn.lock ├── solid │ ├── .gitignore │ ├── README.md │ ├── __reactpreview__ │ │ ├── Wrapper.module.css │ │ └── Wrapper.tsx │ ├── declaration.d.ts │ ├── index.html │ ├── package.json │ ├── src │ │ ├── App.module.css │ │ ├── App.screenshot.tsx │ │ ├── App.tsx │ │ ├── __screenshots__ │ │ │ ├── darwin │ │ │ │ ├── App-App.png │ │ │ │ ├── App-Clicked.png │ │ │ │ └── App-HelloWorld.png │ │ │ ├── linux │ │ │ │ ├── App-App.png │ │ │ │ ├── App-Clicked.png │ │ │ │ └── App-HelloWorld.png │ │ │ └── win32 │ │ │ │ ├── App-App.png │ │ │ │ ├── App-Clicked.png │ │ │ │ └── App-HelloWorld.png │ │ ├── assets │ │ │ └── favicon.ico │ │ ├── index.css │ │ ├── index.tsx │ │ └── logo.svg │ ├── tsconfig.json │ ├── vite.config.ts │ ├── viteshot.config.js │ └── yarn.lock ├── svelte │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── App.svelte │ │ ├── assets │ │ │ └── svelte.png │ │ ├── lib │ │ │ ├── Counter.screenshot.svelte │ │ │ ├── Counter.svelte │ │ │ └── __screenshots__ │ │ │ │ ├── darwin │ │ │ │ └── Counter.png │ │ │ │ ├── linux │ │ │ │ └── Counter.png │ │ │ │ └── win32 │ │ │ │ └── Counter.png │ │ ├── main.ts │ │ └── vite-env.d.ts │ ├── svelte.config.js │ ├── tsconfig.json │ ├── vite.config.js │ ├── viteshot.config.cjs │ └── yarn.lock └── vue │ ├── .gitignore │ ├── README.md │ ├── index.html │ ├── package.json │ ├── public │ └── favicon.ico │ ├── src │ ├── App.vue │ ├── assets │ │ └── logo.png │ ├── components │ │ ├── HelloWorld.screenshot.vue │ │ ├── HelloWorld.vue │ │ └── __screenshots__ │ │ │ ├── darwin │ │ │ └── HelloWorld.png │ │ │ ├── linux │ │ │ └── HelloWorld.png │ │ │ └── win32 │ │ │ └── HelloWorld.png │ ├── main.ts │ ├── shims-vue.d.ts │ └── vite-env.d.ts │ ├── tsconfig.json │ ├── vite.config.ts │ ├── viteshot.config.js │ └── yarn.lock ├── logo.png ├── logo.svg ├── package.json ├── renderers ├── main.ts ├── preact.ts ├── react.ts ├── solid.ts ├── svelte.ts └── vue.ts ├── rollup.config.js ├── shooters ├── percy.ts └── playwright.ts ├── src ├── cli.ts ├── commands │ ├── debug.ts │ ├── init.ts │ └── shoot.ts ├── config.ts ├── frameworks │ ├── config.ts │ ├── preact.ts │ ├── react.ts │ ├── solid.ts │ ├── svelte.ts │ └── vue.ts ├── helpers │ ├── fail.ts │ ├── find-file.ts │ ├── print.ts │ └── read-config.ts ├── index.ts ├── plugins │ └── svgr-plugin.ts └── renderer.ts ├── tsconfig.json ├── viteshot.gif ├── viteshot.mp4 └── yarn.lock /.gitattributes: -------------------------------------------------------------------------------- 1 | **/__screenshots__/*.* binary 2 | **/__screenshots__/*.* filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: fwouts 2 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 30 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: stale 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions. 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: 6 | - created 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | node-version: [14.x] 14 | steps: 15 | - uses: actions/checkout@v2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v2 18 | with: 19 | always-auth: true 20 | node-version: ${{ matrix.node-version }} 21 | - run: echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc 22 | - name: Install dependencies 23 | run: yarn --frozen-lockfile 24 | - name: Get the version 25 | run: echo "RELEASE_VERSION=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV 26 | - name: Update the version 27 | run: sed -i 's/0.0.0-dev/${{ env.RELEASE_VERSION }}/g' package.json 28 | - name: Publish 29 | run: yarn release 30 | -------------------------------------------------------------------------------- /.github/workflows/screenshot.yml: -------------------------------------------------------------------------------- 1 | name: Screenshot 2 | 3 | concurrency: 4 | group: screenshot-${{ github.ref }} 5 | cancel-in-progress: true 6 | 7 | on: push 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | node-version: [14.x] 15 | os: [macos-latest, ubuntu-latest, windows-latest] 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | lfs: true 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v2 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - name: Cache Node.js modules 25 | uses: actions/cache@v2 26 | with: 27 | path: | 28 | **/node_modules 29 | ~/.cache/ms-playwright 30 | ~/Caches/ms-playwright 31 | key: ${{ runner.OS }}-v3-yarn-${{ hashFiles('**/yarn.lock') }} 32 | restore-keys: | 33 | ${{ runner.OS }}-v3-yarn- 34 | - run: yarn --frozen-lockfile 35 | - run: yarn build 36 | - run: yarn playwright install 37 | - run: cd dist && npm link 38 | - run: | 39 | cd examples/preact 40 | yarn --frozen-lockfile 41 | npm link viteshot 42 | yarn viteshot -p 43 | yarn rimraf node_modules/viteshot 44 | - run: | 45 | cd examples/react-js 46 | yarn --frozen-lockfile 47 | npm link viteshot 48 | yarn viteshot -p 49 | yarn rimraf node_modules/viteshot 50 | - run: | 51 | cd examples/react-tsx 52 | yarn --frozen-lockfile 53 | npm link viteshot 54 | yarn viteshot -p 55 | yarn rimraf node_modules/viteshot 56 | - run: | 57 | cd examples/solid 58 | yarn --frozen-lockfile 59 | npm link viteshot 60 | yarn viteshot -p 61 | yarn rimraf node_modules/viteshot 62 | - run: | 63 | cd examples/svelte 64 | yarn --frozen-lockfile 65 | npm link viteshot 66 | yarn viteshot -p 67 | yarn rimraf node_modules/viteshot 68 | - run: | 69 | cd examples/vue 70 | yarn --frozen-lockfile 71 | npm link viteshot 72 | yarn viteshot -p 73 | yarn rimraf node_modules/viteshot 74 | - name: Erase Vite caches 75 | run: yarn rimraf "**/node_modules/.vite" 76 | - uses: actions/upload-artifact@v2 77 | if: failure() 78 | with: 79 | name: screenshots 80 | path: "**/__screenshots__/" 81 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | package-lock.json 8 | 9 | # misc 10 | .DS_Store 11 | .env.local 12 | .env.development.local 13 | .env.test.local 14 | .env.production.local 15 | 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | 20 | # outputs 21 | dist 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Zenc Labs Pty Ltd 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > **Warning** 2 | > 3 | > This package is no longer actively maintained. 4 | > 5 | > Check out [@previewjs/screenshot](https://github.com/fwouts/previewjs/tree/main/screenshot) for an alternative. 6 | 7 |

8 | logo 9 |

10 |
11 |

12 | 13 | npm 14 | 15 | 16 | license 17 | 18 |

19 |
20 | 21 | # ViteShot 📸 22 | 23 | ViteShot is a fast and simple component screenshot tool based on [Vite](https://vitejs.dev). 24 | 25 | It supports Preact, React, Solid, Svelte and Vue 3. 26 | 27 | ![Gif preview](viteshot.gif) 28 | 29 | ## Installation 30 | 31 | ```sh 32 | 33 | # Install ViteShot. 34 | npm install --save-dev viteshot # NPM 35 | yarn add -D viteshot # Yarn 36 | pnpm add -D viteshot # PNPM 37 | 38 | # Set up ViteShot configuration in your repository. 39 | viteshot init 40 | ``` 41 | 42 | ## Getting Started 43 | 44 | Please refer to the [documentation](https://viteshot.com/docs/getting-started) for more information. 45 | 46 | ## Examples 47 | 48 | All you need is to export UI components from files with the `.screenshot.jsx/tsx/vue/svelte` extension. 49 | 50 | See examples: 51 | 52 | - [Preact](https://github.com/zenclabs/viteshot/blob/main/examples/preact/src/App.screenshot.tsx) 53 | - [React](https://github.com/zenclabs/viteshot/blob/main/examples/react-tsx/src/App.screenshot.tsx) 54 | - [Solid](https://github.com/zenclabs/viteshot/blob/main/examples/solid/src/App.screenshot.tsx) 55 | - [Svelte](https://github.com/zenclabs/viteshot/blob/main/examples/svelte/src/lib/Counter.screenshot.svelte) 56 | - [Vue](https://github.com/zenclabs/viteshot/blob/main/examples/vue/src/components/HelloWorld.screenshot.vue) 57 | 58 | Then, generate screenshots with: 59 | 60 | ```sh 61 | # Take screenshots. 62 | viteshot 63 | > Capturing: src/__screenshots__/darwin/pixel2/App-App.png 64 | > Capturing: src/__screenshots__/darwin/laptop/App-App.png 65 | > Capturing: src/__screenshots__/darwin/pixel2/App-Clicked.png 66 | > Capturing: src/__screenshots__/darwin/laptop/App-Clicked.png 67 | > Capturing: src/__screenshots__/darwin/pixel2/App-Greet.png 68 | > Capturing: src/__screenshots__/darwin/laptop/App-Greet.png 69 | > Capturing: src/__screenshots__/darwin/laptop/App-HelloWorld.png 70 | > Capturing: src/__screenshots__/darwin/pixel2/App-HelloWorld.png 71 | > All done. 72 | ``` 73 | 74 | ## License 75 | 76 | MIT 77 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | dist 11 | 12 | # Misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /docs/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve('@docusaurus/core/lib/babel/preset')], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/docs/async-screenshots.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Async Screenshots 6 | 7 | ViteShot lets you specify a `beforeScreenshot` hook, allowing you to wait for a specific state or interact with the component before taking the screenshot. 8 | 9 | import Tabs from "@theme/Tabs"; 10 | import TabItem from "@theme/TabItem"; 11 | 12 | 22 | 23 | 24 | ```jsx title="/src/components/example.screenshot.jsx" 25 | import React from "react"; 26 | 27 | export const Clicked = () => { 28 | const [clicked, setClicked] = useState(false); 29 | 30 | return ( 31 |
32 | 35 |
36 | ); 37 | }; 38 | Clicked.beforeScreenshot = async (element: HTMLElement) => { 39 | element.querySelector("#button")!.click(); 40 | }; 41 | 42 | ``` 43 | 44 |
45 | 46 | 47 | ```jsx title="/src/components/example.screenshot.jsx" 48 | import { h } from "preact"; 49 | import { useState } from "preact/hooks"; 50 | 51 | export const Clicked = () => { 52 | const [clicked, setClicked] = useState(false); 53 | 54 | return ( 55 |
56 | 59 |
60 | ); 61 | }; 62 | Clicked.beforeScreenshot = async (element: HTMLElement) => { 63 | element.querySelector("#button")!.click(); 64 | }; 65 | 66 | ``` 67 | 68 |
69 | 70 | 71 | ```jsx title="/src/components/example.screenshot.jsx" 72 | import { createSignal } from "solid-js"; 73 | 74 | export const Clicked = () => { 75 | const [clicked, setClicked] = createSignal(false); 76 | 77 | return ( 78 |
79 | 82 |
83 | ); 84 | }; 85 | Clicked.beforeScreenshot = async (element: HTMLElement) => { 86 | element.querySelector("#button")!.click(); 87 | }; 88 | ``` 89 | 90 |
91 | 92 | 93 | ```svelte title="/src/components/example.screenshot.svelte" 94 | 104 | 105 |
106 | 109 |
110 | ``` 111 | 112 |
113 | 114 | 115 | ```vue title="/src/components/example.screenshot.vue" 116 | 123 | 124 | 144 | ``` 145 | 146 | 147 |
148 | -------------------------------------------------------------------------------- /docs/docs/config/_category_.yml: -------------------------------------------------------------------------------- 1 | label: Configuration 2 | position: 3 3 | -------------------------------------------------------------------------------- /docs/docs/config/alias.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Custom Aliases 6 | 7 | If you use custom aliases, you may need to help ViteShot resolve imports. 8 | 9 | For example if you use Webpack, you would need the following configuration: 10 | 11 | ```js title="/viteshot.config.js" 12 | const webpackConfig = require("./webpack.aliases"); 13 | const vite = require("vite"); 14 | 15 | module.exports = { 16 | // ... 17 | vite: vite.defineConfig({ 18 | resolve: { 19 | alias: webpackConfig.resolve.alias, 20 | }, 21 | }), 22 | }; 23 | ``` 24 | 25 | For more information, see [Vite's documentation](https://vitejs.dev/config/#resolve-alias). 26 | 27 | ## TypeScript support 28 | 29 | If you use TypeScript, you're in luck. 30 | 31 | ViteShot uses [vite-tsconfig-paths](https://github.com/aleclarson/vite-tsconfig-paths), which automatically detects aliases from `tsconfig.json`. As a result, you may not need to explicitly provide aliases. 32 | -------------------------------------------------------------------------------- /docs/docs/config/browsers.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 8 3 | --- 4 | 5 | # Browsers and Viewports 6 | 7 | ## Playwright shooter 8 | 9 | By default, screenshots are taken with the [Playwright](https://playwright.dev) shooter on Chromium with a viewport size of 1280x720. 10 | 11 | ### Viewports 12 | 13 | You can customise the viewport easily, as well as take screenshots on multiple viewports automatically. 14 | 15 | For example, here is a configuration to take each screenshot with a simulated laptop viewport (1366x768) as well as a mobile Pixel 2 viewport (411x731 with 2.625 device scale factor). 16 | 17 | ```js title="/viteshot.config.js" 18 | const playwrightShooter = require("viteshot/shooters/playwright"); 19 | const playwright = require("playwright"); 20 | 21 | module.exports = { 22 | // ... 23 | shooter: playwrightShooter(playwright.chromium, { 24 | contexts: { 25 | laptop: { 26 | viewport: { 27 | width: 1366, 28 | height: 768, 29 | }, 30 | }, 31 | pixel2: playwright.devices["Pixel 2"], 32 | }, 33 | }), 34 | }; 35 | ``` 36 | 37 | ### Screenshot paths 38 | 39 | By default, screenshots are stored in a `__screenshots__` subdirectory next to the `.screenshot.*` file, within a subdirectory corresponding to `process.platform` (e.g. `darwin` for MacOS). 40 | 41 | You can customise this by specifying a prefix path, suffix path or both. 42 | 43 | For example if you'd like all screenshots to be stored in a top-level `__screenshots__` directory: 44 | 45 | ```js title="/viteshot.config.js" 46 | const playwrightShooter = require("viteshot/shooters/playwright"); 47 | const playwright = require("playwright"); 48 | 49 | module.exports = { 50 | // ... 51 | shooter: playwrightShooter(playwright.chromium, { 52 | output: { 53 | prefixPath: `__screenshots__/${process.platform}`, 54 | suffixPath: "", 55 | }, 56 | }), 57 | }; 58 | ``` 59 | 60 | ### Browsers 61 | 62 | If you use the default Playwright shooter, you can also choose to take screenshots with a different browser. Simply replace `playwright.chromium` with another such as `firefox` or `webkit`. See [Playweight documentation](https://playwright.dev/docs/browsers) for more details. 63 | 64 | :::info Taking screenshots with multiple browsers 65 | 66 | If you want to take screenshots with multiple browsers, you can create multiple variants of `viteshot.config.js`, such as `viteshot.chromium.js` and `viteshot.webkit.js`. 67 | 68 | Use the `-c` flag when running `viteshot` to generate screenshots with a specific browser: 69 | 70 | ```sh 71 | $ viteshot -c viteshot.chromium.js 72 | ``` 73 | 74 | ::: 75 | 76 | ## Alternative: Percy shooter 77 | 78 | While Playwright generates screenshots on the machine that runs `viteshot`, an alternative is to use a service such as [Percy](https://percy.io), which uploads HTML/CSS snapshots to the cloud before taking screenshots with a suite of browsers. 79 | 80 | This means that you won't have to manage screenshots yourself, but it comes at a cost (literally). 81 | 82 | To use Percy, first install `@percy/puppeteer` and `puppeteer`. 83 | 84 | Then, update `viteshot.config.js`: 85 | 86 | ```js title="/viteshot.config.js" 87 | const percyShooter = require("viteshot/shooters/percy"); 88 | 89 | module.exports = { 90 | // .... 91 | shooter: percyShooter(), 92 | }; 93 | ``` 94 | 95 | You can then upload screenshots to Percy by running: 96 | 97 | ```sh 98 | $ percy exec -- viteshot 99 | ``` 100 | 101 | :::note Do you work at Percy / BrowserStack? 102 | 103 | Percy shooter is currently not automatically tested because Percy no longer grants free licenses to open-source projects. 104 | 105 | If you work at Percy and would like to help improve the Percy shooter or better yet sponsor ViteShot, please get in touch! 106 | 107 | ::: 108 | -------------------------------------------------------------------------------- /docs/docs/config/css.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # CSS 6 | 7 | Thanks to a strong foundation provided by [Vite](https://vitejs.dev), most CSS-in-JS libraries will work out of the box, as well as plain CSS imports and CSS modules. 8 | 9 | ## Global CSS 10 | 11 | Since ViteShot renders components in isolation, it doesn't run any top-level entry points that you may have set up in your app. This means that you will need to explicitly import any global CSS from your screenshots. 12 | 13 | For example in React: 14 | 15 | ```jsx title="/src/components/example.screenshot.jsx" 16 | import "../index.css"; // Global CSS import 17 | 18 | export const MyScreenshot = () =>
Hello, World!
; 19 | ``` 20 | 21 | ## PostCSS / Tailwind 22 | 23 | If your CSS relies on PostCSS, it will be picked up as long as your project has a `postcss.config.js` file. 24 | 25 | For example for Tailwind, here is the required configuration: 26 | 27 | ```js title="/postcss.config.js" 28 | module.exports = { 29 | plugins: { 30 | tailwindcss: {}, 31 | autoprefixer: {}, 32 | }, 33 | }; 34 | ``` 35 | 36 | You should also make sure that you import the global CSS as explained above. 37 | -------------------------------------------------------------------------------- /docs/docs/config/overview.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Config Overview 6 | 7 | ViteShot may not work out of the box for complex use cases, but fear not! You may only be one line of config away from your goal. 8 | 9 | Here is an overview of supported options: 10 | 11 | ```js title="/viteshot.config.js" 12 | const playwrightShooter = require("viteshot/shooters/playwright"); 13 | const playwright = require("playwright"); 14 | 15 | module.exports = { 16 | /** 17 | * Configure the framework used by your project. 18 | */ 19 | framework: { 20 | type: "preact" | "react" | "solid" | "svelte" | "vue" 21 | }, 22 | 23 | /** 24 | * Configure your screenshot file paths. 25 | */ 26 | filePathPattern: "**/*.screenshot.*", 27 | 28 | /** 29 | * Set up a custom wrapper component (not supported in Vue or Svelte). 30 | */ 31 | wrapper: { 32 | path: "__viteshot__/ScreenshotWrapper.tsx", 33 | componentName: "Wrapper" 34 | }, 35 | 36 | /** 37 | * Choose a shooter. 38 | * 39 | * See https://github.com/zenclabs/viteshot/tree/main/shooters for other choices. 40 | */ 41 | shooter: playwrightShooter(playwright.chromium, { 42 | // Take each screenshot in two different viewports. 43 | contexts: { 44 | laptop: { 45 | viewport: { 46 | width: 1366, 47 | height: 768, 48 | }, 49 | }, 50 | pixel2: playwright.devices["Pixel 2"], 51 | }, 52 | output: { 53 | // Put all screenshots in a top-level __screenshots__ directory. 54 | prefixPath: `__screenshots__/${process.platform}`, 55 | // Alternatively, put all screenshots next to the component definition. 56 | suffixPath: `__screenshots__/${process.platform}` 57 | } 58 | }), 59 | 60 | /** 61 | * Specify a custom Vite configuration. 62 | */ 63 | vite: vite.UserConfig; 64 | } 65 | ``` 66 | 67 | Read on to learn about: 68 | 69 | - setting up CSS rules 70 | - wrapping components (including providing context) 71 | - configuring module aliases 72 | - SVGR support (React only) 73 | - specifying screenshot viewports 74 | - using multiple browsers 75 | - customising generated screenshot file paths 76 | -------------------------------------------------------------------------------- /docs/docs/config/static-assets.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Static Assets 6 | 7 | Your components may depend on static assets such as images. ViteShot automatically lets you import any asset with an `import` statement. 8 | 9 | If you prefer to use explicit paths instead, you may need to configure a public directory for ViteShot. 10 | 11 | This is done via the `vite.public` property in `viteshot.config.js`: 12 | 13 | ```js title="/viteshot.config.js" 14 | const vite = require("vite"); 15 | 16 | module.exports = { 17 | // ... 18 | vite: vite.defineConfig({ 19 | public: "static", 20 | }), 21 | }; 22 | ``` 23 | 24 | For more information, see [Vite's documentation](https://vitejs.dev/guide/assets.html#the-public-directory). 25 | -------------------------------------------------------------------------------- /docs/docs/config/svgr.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # SVGR 6 | 7 | :::caution Supported frameworks 8 | 9 | As of `v0.1.8`, this feature is only supported in **React**. 10 | 11 | Contributions are welcome! 12 | 13 | ::: 14 | 15 | SVGR is a useful feature that allows you to import a `.svg` file as a component directly into your React code. 16 | 17 | By default, you can import `ReactComponent` for any SVG file: 18 | 19 | ```jsx 20 | import { ReactComponent as Logo } from "./logo.svg"; 21 | 22 | function App() { 23 | return ( 24 |
25 | 26 | ... 27 |
28 | ); 29 | } 30 | ``` 31 | 32 | You can customise the exported component name. For example, if you'd like to use a default import, use the following configuration: 33 | 34 | ```js title="/viteshot.config.js" 35 | module.exports = { 36 | framework: { 37 | type: "react", 38 | svgr: { 39 | componentName: "default", 40 | }, 41 | }, 42 | // ... 43 | }; 44 | ``` 45 | 46 | The above example then becomes: 47 | 48 | ```jsx 49 | import Logo from "./logo.svg"; 50 | 51 | function App() { 52 | return ( 53 |
54 | 55 | ... 56 |
57 | ); 58 | } 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/docs/config/vite.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Vite Config 6 | 7 | As we've seen in [Custom Aliases](/docs/config/alias) and [Static Assets](/docs/config/static-assets), you can pass any custom Vite config to ViteShot in the `vite` property of `viteshot.config.js`. 8 | 9 | ```js title="/viteshot.config.js" 10 | const vite = require("vite"); 11 | 12 | module.exports = { 13 | // ... 14 | vite: vite.defineConfig({ 15 | // ... 16 | }), 17 | }; 18 | ``` 19 | 20 | For more information on what options you can pass, see [Vite's documentation](https://vitejs.dev/config). 21 | -------------------------------------------------------------------------------- /docs/docs/config/wrapper.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Wrapper component 6 | 7 | :::caution Supported frameworks 8 | 9 | As of `v0.1.8`, this feature is not supported in **Svelte** and **Vue**. 10 | 11 | Contributions are welcome! 12 | 13 | ::: 14 | 15 | You may wish to wrap your components into another component, especially if they rely on context providers. 16 | 17 | You can automatically wrap each component by creating a wrapper component, which will receive the screenshot component in the `children` property: 18 | 19 | import Tabs from "@theme/Tabs"; 20 | import TabItem from "@theme/TabItem"; 21 | 22 | 30 | 31 | 32 | ```jsx title="/__viteshot__/ScreenshotWrapper.tsx" 33 | import React from "react"; 34 | import styles from "./Wrapper.module.css"; 35 | 36 | export const Wrapper: React.FC = (props) => ( 37 |
{props.children}
38 | ); 39 | ``` 40 | 41 |
42 | 43 | 44 | ```jsx title="/__viteshot__/ScreenshotWrapper.tsx" 45 | import * as Preact from "preact"; 46 | import styles from "./Wrapper.module.css"; 47 | 48 | export const Wrapper: Preact.FunctionalComponent = (props) => ( 49 |
{props.children}
50 | ); 51 | ``` 52 | 53 |
54 | 55 | 56 | ```jsx title="/__viteshot__/ScreenshotWrapper.tsx" 57 | import type { Component } from "solid-js"; 58 | import styles from "./Wrapper.module.css"; 59 | 60 | export const Wrapper: Component = (props) => ( 61 |
{props.children}
62 | ); 63 | ``` 64 | 65 |
66 |
67 | 68 | Then, update `viteshot.config.js` to point to the wrapper component: 69 | 70 | ```js title="/viteshot.config.js" 71 | module.exports = { 72 | // ... 73 | wrapper: { 74 | path: "__viteshot__/ScreenshotWrapper.tsx", 75 | componentName: "Wrapper", 76 | }, 77 | }; 78 | ``` 79 | 80 | :::tip Do you use React Preview? 81 | 82 | You can use the same wrapper component for both ViteShot and [React Preview](https://reactpreview.com) by copy-pasting the `wrapper` config in `reactpreview.config.js`. 83 | 84 | ::: 85 | -------------------------------------------------------------------------------- /docs/docs/getting-started.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Getting Started 6 | 7 | ## Overview 8 | 9 | ViteShot (named after [Vite](https://vitejs.dev), pronounced `/vit/`) is a command-line tool that renders screenshots from components. 10 | 11 | You can learn more about the rationale behind ViteShot in the [Why ViteShot](/docs/why) section. 12 | 13 | ## Installation 14 | 15 | With NPM: 16 | 17 | ```sh 18 | $ npm install viteshot playwright 19 | ``` 20 | 21 | With Yarn: 22 | 23 | ```sh 24 | $ yarn add viteshot playwright 25 | ``` 26 | 27 | With PNPM: 28 | 29 | ```sh 30 | $ pnpm add viteshot playwright 31 | ``` 32 | 33 | ## First Time Setup 34 | 35 | To set up ViteShot in your project, run: 36 | 37 | ```sh 38 | $ viteshot init 39 | ``` 40 | 41 | ViteShot will automatically detect which framework you're using and set up the appropriate defaults for you. 42 | 43 | ## Create your first screenshot 44 | 45 | Select your framework below for an example. 46 | 47 | import Tabs from "@theme/Tabs"; 48 | import TabItem from "@theme/TabItem"; 49 | 50 | 60 | 61 | 62 | ```jsx title="/src/components/example.screenshot.jsx" 63 | import React from "react"; 64 | 65 | export const MyScreenshot = () =>
Hello, World!
; 66 | ``` 67 | 68 |
69 | 70 | 71 | ```jsx title="/src/components/example.screenshot.jsx" 72 | import { h } from "preact"; 73 | 74 | export const MyScreenshot = () =>
Hello, World!
; 75 | ``` 76 | 77 |
78 | 79 | 80 | ```jsx title="/src/components/example.screenshot.jsx" 81 | export const MyScreenshot = () =>
Hello, World!
; 82 | ``` 83 | 84 |
85 | 86 | 87 | ```svelte title="/src/components/example.screenshot.svelte" 88 |
Hello, World!
89 | ``` 90 | 91 |
92 | 93 | 94 | ```vue title="/src/components/example.screenshot.vue" 95 | 98 | 99 | 106 | ``` 107 | 108 | 109 |
110 | 111 | ## Generate screenshots 112 | 113 | To generate screenshots, run: 114 | 115 | ```sh 116 | $ viteshot 117 | ``` 118 | 119 | You should see an output similar to: 120 | 121 | ```txt 122 | Capturing: src/components/__screenshots__/darwin/example-MyScreenshot.png 123 | All done. 124 | ``` 125 | 126 | That's it! 127 | -------------------------------------------------------------------------------- /docs/docs/how-it-works.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # How ViteShot Works 6 | 7 | In order to take screenshots as fast as possible, ViteShot generates a virtual app that imports every screenshot file in your codebase and spins it up with [Vite](https://vitejs.dev). 8 | 9 | It then loads the page and iterates through all screenshot scenarios in a loop, taking a screenshot for each. 10 | 11 | This means that all screenshots are taken in a single page load, ensuring that no resources are wasted on additional code parsing and execution, page reloads and so on. 12 | -------------------------------------------------------------------------------- /docs/docs/managing-screenshots.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Managing Screenshots 6 | 7 | ViteShot helps you generate screenshots, but that's not the entire story. To achieve visual testing, you need a way to store screenshots and easily compare them across commits. 8 | 9 | You can choose between several approaches: 10 | 11 | - storing screenshots in Git (or Git LFS) 12 | - uploading them to S3 and comparing them with [reg-suit](https://github.com/reg-viz/reg-suit) 13 | - using a third-party such as [Percy](https://percy.io) 14 | 15 | ## Option 1: Storing screenshots in Git 16 | 17 | The simplest approach is to store screenshots in Git. 18 | 19 | We recommend using [Git LFS](https://git-lfs.github.com) to store screenshots. This will help prevent your Git repository from becoming bloated over time, as each updated screenshot would otherwise increase the repository's size. 20 | 21 | If you're unfamiliar with Git LFS, you can learn about it with [this short video (2 min)](https://www.youtube.com/watch?v=uLR1RNqJ1Mw) and/or by going through [the official tutorial](https://github.com/git-lfs/git-lfs/wiki/Tutorial). 22 | 23 | To set up Git LFS, [install the Git extension](https://git-lfs.github.com/) and add the following to `.gitattributes` in your repository ([source](https://github.com/americanexpress/jest-image-snapshot/issues/92#issuecomment-493582776)): 24 | 25 | ``` 26 | **/__screenshots__/*.* binary 27 | **/__screenshots__/*.* filter=lfs diff=lfs merge=lfs -text 28 | ``` 29 | 30 | ### Reviewing screenshots 31 | 32 | When you store screenshots in Git, updated screenshots will appear alongside updated code in your usual pull request review interface. 33 | 34 | ### Screenshot consistency 35 | 36 | To ensure that screenshots are always identical, it's recommended that you generate screenshots on CI. 37 | 38 | You can run `viteshot -p` to automatically push new commits that update screenshots once they've been updated. This will fail on `main` and `master` branches. 39 | 40 | This command works out of the box with GitHub Actions, however you may need to set up Git credentials on other CI platforms. 41 | 42 | ## Option 2: Storing screenshots in S3 43 | 44 | If you prefer to store screenshots independently from your Git repository, one of the best options is [`reg-suit`](https://github.com/reg-viz/reg-suit). Once configured, it will automatically upload generated screenshots to your own S3 bucket. 45 | 46 | reg-suit also includes GitHub and GitLab plugins that will automatically add a comment on each pull request, pointing out any updated screenshots. 47 | 48 | ## Option 3: Using Percy 49 | 50 | When you use [Percy](https://percy.io), your screenshots are generated and stored in the cloud. You also get a separate screenshot review and approval interface, which is particularly convenient when working in large teams. 51 | 52 | If you'd like to use Percy, please refer to the [browsers and viewports](/docs/config/browsers#alternative-percy-shooter) documentation. 53 | 54 | ## Option 4: Upcoming `screendiff` platform 55 | 56 | The author of `viteshot` is designing a screenshot upload and comparison platform that gives you simplicity and peace of mind at an affordable cost. 57 | 58 | If you'd like to participate in early trials, please get in touch at screendiff@zenc.io. 59 | -------------------------------------------------------------------------------- /docs/docs/troubleshooting.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 7 3 | --- 4 | 5 | # Troubleshooting 6 | 7 | If you run into problems, do not hesitate to open a new issue on [GitHub](https://github.com/zenclabs/viteshot/issues). 8 | 9 | Contributions are also welcome! 10 | -------------------------------------------------------------------------------- /docs/docs/why.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Why ViteShot 6 | 7 | Web development is a predominantly visual endeavour. Aside from being functional, a website has to look right. 8 | 9 | Testing has become a must-have, especially in teams of developers. While unit testing of components is ubiquitous, it only covers the functional aspects. For example, Jest snapshot tests only tell you that the generated HTML structure is correct. They tell you nothing about a component's appearance. 10 | 11 | Visual testing fills this gap, but it can be a complex endeavour. You can either spin up your app server and take screen-level screenshots with Cypress, Puppeteer or Playwright, or you can use Storybook in combination with Storycap or Chromatic to generate screenshots of individual components and ensure they don't change unexpectedly. These approaches require significant effort to setup. If you use Storybook, you'll also notice increasing build times as your project gets larger. 12 | 13 | This is where ViteShot comes in. Based on [Vite](https://vitejs.dev), a super-fast webpack alternative, it can take hundreds of screenshots of individual components in a few seconds, even in a large codebase. It also requires minimal setup, so you can get on with your work. 14 | -------------------------------------------------------------------------------- /docs/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | const lightCodeTheme = require("prism-react-renderer/themes/github"); 2 | const darkCodeTheme = require("prism-react-renderer/themes/dracula"); 3 | 4 | /** @type {import('@docusaurus/types').DocusaurusConfig} */ 5 | module.exports = { 6 | title: "ViteShot", 7 | tagline: "Generate screenshots of UI components within seconds", 8 | url: "https://viteshot.com", 9 | baseUrl: "/", 10 | onBrokenLinks: "throw", 11 | onBrokenMarkdownLinks: "throw", 12 | favicon: "img/logo.png", 13 | organizationName: "zenclabs", 14 | projectName: "viteshot", 15 | scripts: [ 16 | { 17 | src: "https://viteshot.com/js/script.js", 18 | async: true, 19 | defer: true, 20 | "data-domain": "viteshot.com", 21 | }, 22 | ], 23 | themeConfig: { 24 | algolia: { 25 | apiKey: "3d87a08a4e159b4a65c997b29782699a", 26 | indexName: "viteshot", 27 | }, 28 | image: "img/og-image.png", 29 | navbar: { 30 | title: "ViteShot", 31 | logo: { 32 | alt: "ViteShot Logo", 33 | src: "img/logo.svg", 34 | }, 35 | items: [ 36 | { 37 | type: "doc", 38 | docId: "getting-started", 39 | position: "right", 40 | label: "Guide", 41 | }, 42 | { 43 | type: "doc", 44 | docId: "config/overview", 45 | position: "right", 46 | label: "Config", 47 | }, 48 | { 49 | href: "https://github.com/zenclabs/viteshot", 50 | label: "GitHub", 51 | position: "right", 52 | }, 53 | ], 54 | }, 55 | footer: { 56 | copyright: `MIT Licensed | Copyright © ${new Date().getFullYear()} Zenc Labs Pty Ltd, Inc. Built with Docusaurus + unDraw.`, 57 | }, 58 | prism: { 59 | theme: lightCodeTheme, 60 | darkTheme: darkCodeTheme, 61 | }, 62 | }, 63 | presets: [ 64 | [ 65 | "@docusaurus/preset-classic", 66 | { 67 | docs: { 68 | sidebarPath: require.resolve("./sidebars.ts"), 69 | }, 70 | theme: { 71 | customCss: require.resolve("./src/css/custom.css"), 72 | }, 73 | }, 74 | ], 75 | ], 76 | }; 77 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@amplitude/node": "^1.8.0", 18 | "@docusaurus/core": "2.0.0-beta.4", 19 | "@docusaurus/preset-classic": "2.0.0-beta.4", 20 | "@docusaurus/theme-search-algolia": "^2.0.0-beta.6", 21 | "@emotion/react": "^11.4.0", 22 | "@emotion/styled": "^11.3.0", 23 | "@mdx-js/react": "^1.6.22", 24 | "@svgr/webpack": "^5.5.0", 25 | "assert-never": "^1.2.1", 26 | "clsx": "^1.1.1", 27 | "cors": "^2.8.5", 28 | "file-loader": "^6.2.0", 29 | "prism-react-renderer": "^1.2.1", 30 | "react": "^17.0.2", 31 | "react-dom": "^17.0.2", 32 | "url-loader": "^4.1.1" 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.5%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | }, 46 | "devDependencies": { 47 | "@docusaurus/module-type-aliases": "^2.0.0-beta.4", 48 | "@tsconfig/docusaurus": "^1.0.2", 49 | "@types/express": "^4.17.13", 50 | "@types/react": "^17.0.15", 51 | "@types/react-helmet": "^6.1.2", 52 | "@types/react-router-dom": "^5.1.8", 53 | "typescript": "^4.3.5" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /docs/sidebars.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Creating a sidebar enables you to: 3 | - create an ordered group of docs 4 | - render a sidebar for each doc of that group 5 | - provide next/previous navigation 6 | 7 | The sidebars can be generated from the filesystem, or explicitly defined here. 8 | 9 | Create as many sidebars as you want. 10 | */ 11 | 12 | module.exports = { 13 | // By default, Docusaurus generates a sidebar from the docs folder structure 14 | tutorialSidebar: [{type: 'autogenerated', dirName: '.'}], 15 | 16 | // But you can create a sidebar manually 17 | /* 18 | tutorialSidebar: [ 19 | { 20 | type: 'category', 21 | label: 'Tutorial', 22 | items: ['hello'], 23 | }, 24 | ], 25 | */ 26 | }; 27 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | /** 3 | * Any CSS included here will be global. The classic template 4 | * bundles Infima by default. Infima is a CSS framework designed to 5 | * work well for content-centric websites. 6 | */ 7 | 8 | /* You can override the default Infima variables here. */ 9 | :root { 10 | --ifm-color-primary: hsl(213, 80%, 70%); 11 | --ifm-color-primary-dark: hsl(213, 85%, 60%); 12 | --ifm-color-primary-darker: hsl(213, 90%, 50%); 13 | --ifm-color-primary-darkest: hsl(213, 95%, 40%); 14 | --ifm-color-primary-light: hsl(213, 70%, 80%); 15 | --ifm-color-primary-lighter: hsl(213, 60%, 85%); 16 | --ifm-color-primary-lightest: hsl(213, 50%, 90%); 17 | --ifm-code-font-size: 95%; 18 | } 19 | 20 | .docusaurus-highlight-code-line { 21 | background-color: rgba(0, 0, 0, 0.1); 22 | display: block; 23 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 24 | padding: 0 var(--ifm-pre-padding); 25 | } 26 | 27 | .card__image img { 28 | height: 300px; 29 | width: 100%; 30 | padding: 30px; 31 | object-fit: contain; 32 | } 33 | 34 | html[data-theme="dark"] .docusaurus-highlight-code-line { 35 | background-color: rgba(0, 0, 0, 0.3); 36 | } 37 | -------------------------------------------------------------------------------- /docs/src/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg" { 2 | const value: React.FunctionComponent>; 3 | export default value; 4 | } 5 | -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /* stylelint-disable docusaurus/copyright-header */ 2 | 3 | /** 4 | * CSS files with the .module.css suffix will be treated as CSS modules 5 | * and scoped locally. 6 | */ 7 | 8 | .heroBanner { 9 | padding: 4rem 0; 10 | text-align: center; 11 | position: relative; 12 | overflow: hidden; 13 | } 14 | 15 | .heroLogo { 16 | max-width: 400px; 17 | } 18 | 19 | @media screen and (max-width: 966px) { 20 | .heroBanner { 21 | padding: 2rem 0rem; 22 | } 23 | 24 | .heroLogo { 25 | max-width: 80%; 26 | } 27 | } 28 | 29 | .video { 30 | box-sizing: border-box; 31 | width: 95%; 32 | box-shadow: 0 4px 8px 0 hsl(213, 50%, 30%); 33 | border-radius: 8px; 34 | aspect-ratio: 16/10; 35 | } 36 | -------------------------------------------------------------------------------- /docs/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Link from "@docusaurus/Link"; 2 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 3 | import Layout from "@theme/Layout"; 4 | import clsx from "clsx"; 5 | import React from "react"; 6 | import styles from "./index.module.css"; 7 | 8 | function HomepageHeader() { 9 | const { siteConfig } = useDocusaurusContext(); 10 | return ( 11 |
12 |
13 |

{siteConfig.title}

14 | 15 |

{siteConfig.tagline}

16 |
26 |
27 | ); 28 | } 29 | 30 | export default function Home() { 31 | const { siteConfig } = useDocusaurusContext(); 32 | return ( 33 | 37 | 38 |
39 |
40 |
41 | 45 | 🚀   Get Started 46 | 47 |
48 |
49 | 53 | ℹ️   Learn more 54 | 55 |
56 |
57 |
58 |
59 |
60 |
61 |

Fast

62 |
63 |
64 | 65 |
66 |
67 |

68 | ViteShot is built on top of{" "} 69 | 70 | Vite 71 | 72 | , a super-fast webpack alternative. 73 |

74 |

75 | It only takes a few seconds to regenerate all screenshots in a 76 | large project. 77 |

78 |
79 |
80 |
81 |
82 |
83 |
84 |

Easy

85 |
86 |
87 | 88 |
89 |
90 |

91 | ViteShot works out of the box for React, Preact, Solid, Svelte 92 | and Vue 3 projects. 93 |

94 |

95 | All you need is a simple config file, which ViteShot can 96 | generate for you. 97 |

98 |
99 |
100 |
101 |
102 |
103 |
104 |

Flexible

105 |
106 |
107 | 108 |
109 |
110 |

111 | ViteShot can handle complex scenarios easily thanks to 112 | pre-screenshot hooks. 113 |

114 |

115 | It also supports CSS Modules, PostCSS and so on out of the 116 | box. 117 |

118 |
119 |
120 |
121 |
122 |
123 |
124 | ); 125 | } 126 | -------------------------------------------------------------------------------- /docs/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/docs/static/.nojekyll -------------------------------------------------------------------------------- /docs/static/img/logo-min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/docs/static/img/logo-min.png -------------------------------------------------------------------------------- /docs/static/img/logo-min.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | logo-min 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 27 | 28 | -------------------------------------------------------------------------------- /docs/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/docs/static/img/logo.png -------------------------------------------------------------------------------- /docs/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | logo 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 28 | -------------------------------------------------------------------------------- /docs/static/img/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/docs/static/img/og-image.png -------------------------------------------------------------------------------- /docs/static/img/undraw_elements_cipa-3.svg: -------------------------------------------------------------------------------- 1 | elements -------------------------------------------------------------------------------- /docs/static/videos/viteshot.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/docs/static/videos/viteshot.mp4 -------------------------------------------------------------------------------- /docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/docusaurus/tsconfig.json", 3 | "include": ["api/", "src/"], 4 | "compilerOptions": { 5 | "outDir": "dist", 6 | "noEmit": false 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /docs/vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "github": { 3 | "silent": true 4 | }, 5 | "rewrites": [ 6 | { 7 | "source": "/js/script.js", 8 | "destination": "https://plausible.io/js/plausible.js" 9 | }, 10 | { 11 | "source": "/api/event", 12 | "destination": "https://plausible.io/api/event" 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /examples/preact/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /build 3 | /*.log 4 | -------------------------------------------------------------------------------- /examples/preact/README.md: -------------------------------------------------------------------------------- 1 | # preact-example 2 | 3 | ## CLI Commands 4 | * `npm install`: Installs dependencies 5 | 6 | * `npm run dev`: Run a development, HMR server 7 | 8 | * `npm run serve`: Run a production-like server 9 | 10 | * `npm run build`: Production-ready build 11 | 12 | * `npm run lint`: Pass TypeScript files using ESLint 13 | 14 | * `npm run test`: Run Jest and Enzyme with 15 | [`enzyme-adapter-preact-pure`](https://github.com/preactjs/enzyme-adapter-preact-pure) for 16 | your tests 17 | 18 | 19 | For detailed explanation on how things work, checkout the [CLI Readme](https://github.com/developit/preact-cli/blob/master/README.md). 20 | -------------------------------------------------------------------------------- /examples/preact/__reactpreview__/Wrapper.module.css: -------------------------------------------------------------------------------- 1 | .Wrapper { 2 | padding: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /examples/preact/__reactpreview__/Wrapper.tsx: -------------------------------------------------------------------------------- 1 | import * as Preact from "preact"; 2 | import styles from "./Wrapper.module.css"; 3 | 4 | export const Wrapper: Preact.FunctionalComponent = (props) => ( 5 |
{props.children}
6 | ); 7 | -------------------------------------------------------------------------------- /examples/preact/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "preact-example", 4 | "version": "0.0.0", 5 | "license": "MIT", 6 | "scripts": { 7 | "build": "preact build", 8 | "serve": "sirv build --port 8080 --cors --single", 9 | "dev": "preact watch", 10 | "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'", 11 | "test": "jest ./tests" 12 | }, 13 | "eslintConfig": { 14 | "parser": "@typescript-eslint/parser", 15 | "extends": [ 16 | "preact", 17 | "plugin:@typescript-eslint/recommended" 18 | ], 19 | "ignorePatterns": [ 20 | "build/" 21 | ] 22 | }, 23 | "dependencies": { 24 | "preact": "^10.3.1", 25 | "preact-render-to-string": "^5.1.4", 26 | "preact-router": "^3.2.1" 27 | }, 28 | "devDependencies": { 29 | "@types/enzyme": "^3.10.5", 30 | "@types/jest": "^26.0.8", 31 | "@typescript-eslint/eslint-plugin": "^2.25.0", 32 | "@typescript-eslint/parser": "^2.25.0", 33 | "enzyme": "^3.11.0", 34 | "enzyme-adapter-preact-pure": "^2.2.0", 35 | "eslint": "^6.8.0", 36 | "eslint-config-preact": "^1.1.1", 37 | "jest": "^26.2.2", 38 | "jest-preset-preact": "^4.0.2", 39 | "preact-cli": "^3.0.0", 40 | "rimraf": "^3.0.2", 41 | "sirv-cli": "^1.0.0-next.3", 42 | "typescript": "^3.7.5" 43 | }, 44 | "jest": { 45 | "preset": "jest-preset-preact", 46 | "setupFiles": [ 47 | "/tests/__mocks__/browserMocks.ts", 48 | "/tests/__mocks__/setupTests.ts" 49 | ] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/preact/src/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "preact-cli/babel" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /examples/preact/src/App.screenshot.tsx: -------------------------------------------------------------------------------- 1 | import { h } from "preact"; 2 | import { useState } from "preact/hooks"; 3 | import Home from "./routes/home"; 4 | 5 | export const HelloWorld = ({ label }: { label: string }) =>
{label}
; 6 | HelloWorld.args = { 7 | label: "Hello, World!", 8 | }; 9 | 10 | export const App = () => ; 11 | 12 | export const Clicked = () => { 13 | const [clicked, setClicked] = useState(false); 14 | 15 | return ( 16 |
17 | 20 |
21 | {clicked ? "clicked" : "not clicked"} 22 |
23 | ); 24 | }; 25 | Clicked.beforeScreenshot = async (element: HTMLElement) => { 26 | element.querySelector("#button")!.click(); 27 | }; 28 | -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/darwin/laptop/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/darwin/laptop/App-App.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/darwin/laptop/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/darwin/laptop/App-Clicked.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/darwin/laptop/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/darwin/laptop/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/darwin/pixel2/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/darwin/pixel2/App-App.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/darwin/pixel2/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/darwin/pixel2/App-Clicked.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/darwin/pixel2/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/darwin/pixel2/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/linux/laptop/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/linux/laptop/App-App.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/linux/laptop/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/linux/laptop/App-Clicked.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/linux/laptop/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/linux/laptop/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/linux/pixel2/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/linux/pixel2/App-App.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/linux/pixel2/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/linux/pixel2/App-Clicked.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/linux/pixel2/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/linux/pixel2/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/win32/laptop/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/win32/laptop/App-App.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/win32/laptop/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/win32/laptop/App-Clicked.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/win32/laptop/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/win32/laptop/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/win32/pixel2/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/win32/pixel2/App-App.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/win32/pixel2/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/win32/pixel2/App-Clicked.png -------------------------------------------------------------------------------- /examples/preact/src/__screenshots__/win32/pixel2/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/__screenshots__/win32/pixel2/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/preact/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/assets/favicon.ico -------------------------------------------------------------------------------- /examples/preact/src/assets/icons/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/assets/icons/android-chrome-192x192.png -------------------------------------------------------------------------------- /examples/preact/src/assets/icons/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/assets/icons/android-chrome-512x512.png -------------------------------------------------------------------------------- /examples/preact/src/assets/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/assets/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /examples/preact/src/assets/icons/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/assets/icons/favicon-16x16.png -------------------------------------------------------------------------------- /examples/preact/src/assets/icons/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/assets/icons/favicon-32x32.png -------------------------------------------------------------------------------- /examples/preact/src/assets/icons/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/preact/src/assets/icons/mstile-150x150.png -------------------------------------------------------------------------------- /examples/preact/src/components/app.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionalComponent, h } from "preact"; 2 | import { Route, Router } from "preact-router"; 3 | import Home from "../routes/home"; 4 | import NotFoundPage from "../routes/notfound"; 5 | import Profile from "../routes/profile"; 6 | import Header from "./header"; 7 | 8 | const App: FunctionalComponent = () => { 9 | return ( 10 |
11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | ); 20 | }; 21 | 22 | export default App; 23 | -------------------------------------------------------------------------------- /examples/preact/src/components/header/index.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionalComponent, h } from "preact"; 2 | import { Link } from "preact-router/match"; 3 | import style from "./style.module.css"; 4 | 5 | const Header: FunctionalComponent = () => { 6 | return ( 7 |
8 |

Preact App

9 | 20 |
21 | ); 22 | }; 23 | 24 | export default Header; 25 | -------------------------------------------------------------------------------- /examples/preact/src/components/header/style.module.css: -------------------------------------------------------------------------------- 1 | .header { 2 | position: fixed; 3 | left: 0; 4 | top: 0; 5 | width: 100%; 6 | height: 56px; 7 | padding: 0; 8 | background: #673ab7; 9 | box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); 10 | z-index: 50; 11 | } 12 | 13 | .header h1 { 14 | float: left; 15 | margin: 0; 16 | padding: 0 15px; 17 | font-size: 24px; 18 | line-height: 56px; 19 | font-weight: 400; 20 | color: #fff; 21 | } 22 | 23 | .header nav { 24 | float: right; 25 | font-size: 100%; 26 | } 27 | 28 | .header nav a { 29 | display: inline-block; 30 | height: 56px; 31 | line-height: 56px; 32 | padding: 0 15px; 33 | min-width: 50px; 34 | text-align: center; 35 | background: rgba(255, 255, 255, 0); 36 | text-decoration: none; 37 | color: #fff; 38 | will-change: background-color; 39 | } 40 | 41 | .header nav a:hover, 42 | .header nav a:active { 43 | background: rgba(0, 0, 0, 0.2); 44 | } 45 | 46 | .header nav a.active { 47 | background: rgba(0, 0, 0, 0.4); 48 | } 49 | -------------------------------------------------------------------------------- /examples/preact/src/declaration.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.css" { 2 | const mapping: Record; 3 | export default mapping; 4 | } 5 | -------------------------------------------------------------------------------- /examples/preact/src/index.ts: -------------------------------------------------------------------------------- 1 | import App from "./components/app"; 2 | import "./style/index.css"; 3 | 4 | export default App; 5 | -------------------------------------------------------------------------------- /examples/preact/src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "preact-example", 3 | "short_name": "preact-example", 4 | "start_url": "/", 5 | "display": "standalone", 6 | "orientation": "portrait", 7 | "background_color": "#fff", 8 | "theme_color": "#673ab8", 9 | "icons": [ 10 | { 11 | "src": "/assets/icons/android-chrome-192x192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "/assets/icons/android-chrome-512x512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /examples/preact/src/routes/home/index.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionalComponent, h } from "preact"; 2 | import style from "./style.module.css"; 3 | 4 | const Home: FunctionalComponent = () => { 5 | return ( 6 |
7 |

Home

8 |

This is the Home component.

9 |
10 | ); 11 | }; 12 | 13 | export default Home; 14 | -------------------------------------------------------------------------------- /examples/preact/src/routes/home/style.module.css: -------------------------------------------------------------------------------- 1 | .home { 2 | padding: 56px 20px; 3 | min-height: 100%; 4 | width: 100%; 5 | } 6 | -------------------------------------------------------------------------------- /examples/preact/src/routes/notfound/index.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionalComponent, h } from "preact"; 2 | import { Link } from "preact-router/match"; 3 | import style from "./style.module.css"; 4 | 5 | const Notfound: FunctionalComponent = () => { 6 | return ( 7 |
8 |

Error 404

9 |

That page doesn't exist.

10 | 11 |

Back to Home

12 | 13 |
14 | ); 15 | }; 16 | 17 | export default Notfound; 18 | -------------------------------------------------------------------------------- /examples/preact/src/routes/notfound/style.module.css: -------------------------------------------------------------------------------- 1 | .notfound { 2 | padding: 0 5%; 3 | margin: 100px 0; 4 | } 5 | -------------------------------------------------------------------------------- /examples/preact/src/routes/profile/index.tsx: -------------------------------------------------------------------------------- 1 | import { FunctionalComponent, h } from "preact"; 2 | import { useEffect, useState } from "preact/hooks"; 3 | import style from "./style.module.css"; 4 | 5 | interface Props { 6 | user: string; 7 | } 8 | 9 | const Profile: FunctionalComponent = (props: Props) => { 10 | const { user } = props; 11 | const [time, setTime] = useState(Date.now()); 12 | const [count, setCount] = useState(0); 13 | 14 | // gets called when this route is navigated to 15 | useEffect(() => { 16 | const timer = window.setInterval(() => setTime(Date.now()), 1000); 17 | 18 | // gets called just before navigating away from the route 19 | return (): void => { 20 | clearInterval(timer); 21 | }; 22 | }, []); 23 | 24 | // update the current time 25 | const increment = (): void => { 26 | setCount(count + 1); 27 | }; 28 | 29 | return ( 30 |
31 |

Profile: {user}

32 |

This is the user profile for a user named {user}.

33 | 34 |
Current time: {new Date(time).toLocaleString()}
35 | 36 |

37 | Clicked {count} times. 38 |

39 |
40 | ); 41 | }; 42 | 43 | export default Profile; 44 | -------------------------------------------------------------------------------- /examples/preact/src/routes/profile/style.module.css: -------------------------------------------------------------------------------- 1 | .profile { 2 | padding: 56px 20px; 3 | min-height: 100%; 4 | width: 100%; 5 | } 6 | -------------------------------------------------------------------------------- /examples/preact/src/style/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | width: 100%; 5 | padding: 0; 6 | margin: 0; 7 | background: #fafafa; 8 | font-family: "Helvetica Neue", arial, sans-serif; 9 | font-weight: 400; 10 | color: #444; 11 | -webkit-font-smoothing: antialiased; 12 | -moz-osx-font-smoothing: grayscale; 13 | } 14 | 15 | * { 16 | box-sizing: border-box; 17 | } 18 | 19 | #app { 20 | height: 100%; 21 | } 22 | -------------------------------------------------------------------------------- /examples/preact/src/sw.js: -------------------------------------------------------------------------------- 1 | import { getFiles, setupPrecaching, setupRouting } from "preact-cli/sw/"; 2 | 3 | setupRouting(); 4 | setupPrecaching(getFiles()); 5 | -------------------------------------------------------------------------------- /examples/preact/src/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <% preact.title %> 6 | 7 | 8 | 9 | 10 | <% preact.headEnd %> 11 | 12 | 13 | <% preact.bodyEnd %> 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/preact/tests/__mocks__/browserMocks.ts: -------------------------------------------------------------------------------- 1 | // Mock Browser API's which are not supported by JSDOM, e.g. ServiceWorker, LocalStorage 2 | /** 3 | * An example how to mock localStorage is given below 👇 4 | */ 5 | 6 | /* 7 | // Mocks localStorage 8 | const localStorageMock = (function() { 9 | let store = {}; 10 | 11 | return { 12 | getItem: (key) => store[key] || null, 13 | setItem: (key, value) => store[key] = value.toString(), 14 | clear: () => store = {} 15 | }; 16 | 17 | })(); 18 | 19 | Object.defineProperty(window, 'localStorage', { 20 | value: localStorageMock 21 | }); */ 22 | -------------------------------------------------------------------------------- /examples/preact/tests/__mocks__/fileMocks.ts: -------------------------------------------------------------------------------- 1 | // This fixed an error related to the CSS and loading gif breaking my Jest test 2 | // See https://facebook.github.io/jest/docs/en/webpack.html#handling-static-assets 3 | export default 'test-file-stub'; 4 | -------------------------------------------------------------------------------- /examples/preact/tests/__mocks__/setupTests.ts: -------------------------------------------------------------------------------- 1 | import { configure } from 'enzyme'; 2 | import Adapter from 'enzyme-adapter-preact-pure'; 3 | 4 | configure({ 5 | adapter: new Adapter() 6 | }); 7 | -------------------------------------------------------------------------------- /examples/preact/tests/declarations.d.ts: -------------------------------------------------------------------------------- 1 | // Enable enzyme adapter's integration with TypeScript 2 | // See: https://github.com/preactjs/enzyme-adapter-preact-pure#usage-with-typescript 3 | /// 4 | -------------------------------------------------------------------------------- /examples/preact/tests/header.test.tsx: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import Header from '../src/components/header'; 3 | // See: https://github.com/preactjs/enzyme-adapter-preact-pure 4 | import { shallow } from 'enzyme'; 5 | 6 | describe('Initial Test of the Header', () => { 7 | test('Header renders 3 nav items', () => { 8 | const context = shallow(
); 9 | expect(context.find('h1').text()).toBe('Preact App'); 10 | expect(context.find('Link').length).toBe(3); 11 | }); 12 | }); 13 | -------------------------------------------------------------------------------- /examples/preact/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | "target": "ES5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 5 | "module": "ESNext", /* Specify module code generation: 'none', commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 6 | // "lib": [], /* Specify library files to be included in the compilation: */ 7 | "allowJs": true, /* Allow javascript files to be compiled. */ 8 | // "checkJs": true, /* Report errors in .js files. */ 9 | "jsx": "react", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 10 | "jsxFactory": "h", /* Specify the JSX factory function to use when targeting react JSX emit, e.g. React.createElement or h. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 13 | // "outFile": "./", /* Concatenate and emit output to single file. */ 14 | // "outDir": "./", /* Redirect output structure to the directory. */ 15 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 16 | // "removeComments": true, /* Do not emit comments to output. */ 17 | "noEmit": true, /* Do not emit outputs. */ 18 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 19 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 20 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 21 | 22 | /* Strict Type-Checking Options */ 23 | "strict": true, /* Enable all strict type-checking options. */ 24 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 25 | // "strictNullChecks": true, /* Enable strict null checks. */ 26 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 27 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 28 | 29 | /* Additional Checks */ 30 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 31 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 32 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 33 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 34 | 35 | /* Module Resolution Options */ 36 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 37 | "esModuleInterop": true, /* */ 38 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 39 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 40 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 41 | // "typeRoots": [], /* List of folders to include type definitions from. */ 42 | // "types": [], /* Type declaration files to be included in compilation. */ 43 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 44 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 45 | 46 | /* Source Map Options */ 47 | // "sourceRoot": "./", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 48 | // "mapRoot": "./", /* Specify the location where debugger should locate map files instead of generated locations. */ 49 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 50 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 51 | 52 | /* Experimental Options */ 53 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 54 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 55 | 56 | /* Advanced Options */ 57 | "skipLibCheck": true /* Skip type checking of declaration files. */ 58 | }, 59 | "include": ["src/**/*", "tests/**/*"] 60 | } 61 | -------------------------------------------------------------------------------- /examples/preact/viteshot.config.js: -------------------------------------------------------------------------------- 1 | const playwrightShooter = require("viteshot/shooters/playwright"); 2 | const playwright = require("playwright"); 3 | 4 | /** 5 | * @type {import('viteshot').UserConfig} 6 | */ 7 | module.exports = { 8 | framework: { 9 | type: "preact", 10 | }, 11 | shooter: playwrightShooter(playwright.chromium, { 12 | contexts: { 13 | laptop: { 14 | viewport: { 15 | width: 1366, 16 | height: 768, 17 | }, 18 | }, 19 | pixel2: playwright.devices["Pixel 2"], 20 | }, 21 | }), 22 | filePathPattern: "**/*.screenshot.@(jsx|tsx)", 23 | wrapper: { 24 | path: "__reactpreview__/Wrapper", 25 | componentName: "Wrapper", 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /examples/preact/viteshot.config.percy.js: -------------------------------------------------------------------------------- 1 | const percyShooter = require("viteshot/shooters/percy"); 2 | 3 | /** 4 | * @type {import('viteshot').UserConfig} 5 | */ 6 | module.exports = { 7 | framework: { 8 | type: "preact", 9 | }, 10 | shooter: percyShooter(), 11 | wrapper: { 12 | path: "__reactpreview__/Wrapper", 13 | componentName: "Wrapper", 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /examples/react-js/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /examples/react-js/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `yarn start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `yarn test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `yarn build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `yarn eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `yarn build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /examples/react-js/__reactpreview__/Wrapper.js: -------------------------------------------------------------------------------- 1 | import styles from "./Wrapper.module.css"; 2 | 3 | export const Wrapper = (props) => ( 4 |
{props.children}
5 | ); 6 | -------------------------------------------------------------------------------- /examples/react-js/__reactpreview__/Wrapper.module.css: -------------------------------------------------------------------------------- 1 | .Wrapper { 2 | padding: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /examples/react-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-app", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "4.0.3", 12 | "web-vitals": "^1.0.1" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/react-js/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/public/favicon.ico -------------------------------------------------------------------------------- /examples/react-js/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/react-js/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/public/logo192.png -------------------------------------------------------------------------------- /examples/react-js/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/public/logo512.png -------------------------------------------------------------------------------- /examples/react-js/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /examples/react-js/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/react-js/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/react-js/src/App.js: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import { ReactComponent as Logo } from "./logo.svg"; 3 | 4 | function App() { 5 | return ( 6 |
7 |
8 | 9 |

10 | Edit src/App.js and save to reload. 11 |

12 | 18 | Learn React 19 | 20 |
21 |
22 | ); 23 | } 24 | 25 | export default App; 26 | -------------------------------------------------------------------------------- /examples/react-js/src/App.screenshot.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import AppComponent from "./App"; 3 | 4 | export const HelloWorld = ({ label }) =>
{label}
; 5 | HelloWorld.args = { 6 | label: "Hello, World!", 7 | }; 8 | 9 | export const App = () => ; 10 | 11 | export const Clicked = () => { 12 | const [clicked, setClicked] = useState(false); 13 | 14 | return ( 15 |
16 | 19 |
20 | {clicked ? "clicked" : "not clicked"} 21 |
22 | ); 23 | }; 24 | Clicked.beforeScreenshot = async (element) => { 25 | element.querySelector("#button").click(); 26 | }; 27 | -------------------------------------------------------------------------------- /examples/react-js/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/darwin/laptop/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/darwin/laptop/App-App.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/darwin/laptop/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/darwin/laptop/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/darwin/laptop/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/darwin/laptop/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/darwin/pixel2/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/darwin/pixel2/App-App.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/darwin/pixel2/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/darwin/pixel2/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/darwin/pixel2/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/darwin/pixel2/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/linux/laptop/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/linux/laptop/App-App.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/linux/laptop/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/linux/laptop/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/linux/laptop/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/linux/laptop/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/linux/pixel2/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/linux/pixel2/App-App.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/linux/pixel2/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/linux/pixel2/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/linux/pixel2/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/linux/pixel2/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/win32/laptop/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/win32/laptop/App-App.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/win32/laptop/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/win32/laptop/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/win32/laptop/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/win32/laptop/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/win32/pixel2/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/win32/pixel2/App-App.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/win32/pixel2/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/win32/pixel2/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-js/src/__screenshots__/win32/pixel2/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-js/src/__screenshots__/win32/pixel2/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-js/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /examples/react-js/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /examples/react-js/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/react-js/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /examples/react-js/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /examples/react-js/viteshot.config.js: -------------------------------------------------------------------------------- 1 | const playwrightShooter = require("viteshot/shooters/playwright"); 2 | const playwright = require("playwright"); 3 | 4 | /** 5 | * @type {import('viteshot').UserConfig} 6 | */ 7 | module.exports = { 8 | framework: { 9 | type: "react", 10 | }, 11 | shooter: playwrightShooter(playwright.chromium, { 12 | contexts: { 13 | laptop: { 14 | viewport: { 15 | width: 1366, 16 | height: 768, 17 | }, 18 | }, 19 | pixel2: playwright.devices["Pixel 2"], 20 | }, 21 | }), 22 | filePathPattern: "**/*.screenshot.@(js|jsx|tsx)", 23 | wrapper: { 24 | path: "__reactpreview__/Wrapper", 25 | componentName: "Wrapper", 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /examples/react-tsx/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `yarn start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `yarn test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `yarn build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `yarn eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /examples/react-tsx/__reactpreview__/Wrapper.module.css: -------------------------------------------------------------------------------- 1 | .Wrapper { 2 | padding: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /examples/react-tsx/__reactpreview__/Wrapper.tsx: -------------------------------------------------------------------------------- 1 | import styles from "./Wrapper.module.css"; 2 | 3 | export const Wrapper: React.FC = (props) => ( 4 |
{props.children}
5 | ); 6 | -------------------------------------------------------------------------------- /examples/react-tsx/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "@types/jest": "^26.0.15", 10 | "@types/node": "^12.0.0", 11 | "@types/react": "^17.0.0", 12 | "@types/react-dom": "^17.0.0", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2", 15 | "react-scripts": "4.0.3", 16 | "typescript": "^4.1.2", 17 | "web-vitals": "^1.0.1" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": [ 27 | "react-app", 28 | "react-app/jest" 29 | ] 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | }, 43 | "devDependencies": { 44 | "rimraf": "^3.0.2" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /examples/react-tsx/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/public/favicon.ico -------------------------------------------------------------------------------- /examples/react-tsx/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /examples/react-tsx/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/public/logo192.png -------------------------------------------------------------------------------- /examples/react-tsx/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/public/logo512.png -------------------------------------------------------------------------------- /examples/react-tsx/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /examples/react-tsx/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/react-tsx/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /examples/react-tsx/src/App.screenshot.tsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import AppComponent from "./App"; 3 | 4 | export const HelloWorld = ({ label }: { label: string }) =>
{label}
; 5 | HelloWorld.args = { 6 | label: "Hello, World!", 7 | }; 8 | 9 | export const App = () => ; 10 | 11 | export const Clicked = () => { 12 | const [clicked, setClicked] = useState(false); 13 | 14 | return ( 15 |
16 | 19 |
20 | {clicked ? "clicked" : "not clicked"} 21 |
22 | ); 23 | }; 24 | Clicked.beforeScreenshot = async (element: HTMLElement) => { 25 | element.querySelector("#button")!.click(); 26 | }; 27 | -------------------------------------------------------------------------------- /examples/react-tsx/src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render, screen } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | render(); 7 | const linkElement = screen.getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /examples/react-tsx/src/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./App.css"; 3 | import { ReactComponent as Logo } from "./logo.svg"; 4 | 5 | function App() { 6 | return ( 7 |
8 |
9 | 10 |

11 | Edit src/App.tsx and save to reload. 12 |

13 | 19 | Learn React 20 | 21 |
22 |
23 | ); 24 | } 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/darwin/laptop/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/darwin/laptop/App-App.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/darwin/laptop/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/darwin/laptop/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/darwin/laptop/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/darwin/laptop/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/darwin/pixel2/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/darwin/pixel2/App-App.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/darwin/pixel2/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/darwin/pixel2/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/darwin/pixel2/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/darwin/pixel2/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/linux/laptop/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/linux/laptop/App-App.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/linux/laptop/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/linux/laptop/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/linux/laptop/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/linux/laptop/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/linux/pixel2/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/linux/pixel2/App-App.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/linux/pixel2/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/linux/pixel2/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/linux/pixel2/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/linux/pixel2/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/win32/laptop/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/win32/laptop/App-App.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/win32/laptop/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/win32/laptop/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/win32/laptop/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/win32/laptop/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/win32/pixel2/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/win32/pixel2/App-App.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/win32/pixel2/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/win32/pixel2/App-Clicked.png -------------------------------------------------------------------------------- /examples/react-tsx/src/__screenshots__/win32/pixel2/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/react-tsx/src/__screenshots__/win32/pixel2/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/react-tsx/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /examples/react-tsx/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /examples/react-tsx/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/react-tsx/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/react-tsx/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /examples/react-tsx/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /examples/react-tsx/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "forceConsistentCasingInFileNames": true, 15 | "noFallthroughCasesInSwitch": true, 16 | "module": "esnext", 17 | "moduleResolution": "node", 18 | "resolveJsonModule": true, 19 | "isolatedModules": true, 20 | "noEmit": true, 21 | "jsx": "react-jsx" 22 | }, 23 | "include": [ 24 | "src" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /examples/react-tsx/viteshot.config.js: -------------------------------------------------------------------------------- 1 | const playwrightShooter = require("viteshot/shooters/playwright"); 2 | const playwright = require("playwright"); 3 | 4 | /** 5 | * @type {import('viteshot').UserConfig} 6 | */ 7 | module.exports = { 8 | framework: { 9 | type: "react", 10 | }, 11 | shooter: playwrightShooter(playwright.chromium, { 12 | contexts: { 13 | laptop: { 14 | viewport: { 15 | width: 1366, 16 | height: 768, 17 | }, 18 | }, 19 | pixel2: playwright.devices["Pixel 2"], 20 | }, 21 | }), 22 | filePathPattern: "**/*.screenshot.@(jsx|tsx)", 23 | wrapper: { 24 | path: "__reactpreview__/Wrapper", 25 | componentName: "Wrapper", 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /examples/solid/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /examples/solid/README.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | Those templates dependencies are maintained via [pnpm](https://pnpm.js.org/) via `pnpm up -Lri`. 4 | 5 | This is the reason you see a `pnpm-lock.yaml`. That being said, any package manager will work. This file can be safely be removed once you clone a template. 6 | 7 | ```bash 8 | $ npm install # or pnpm install or yarn install 9 | ``` 10 | ### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs) 11 | 12 | ## Available Scripts 13 | 14 | In the project directory, you can run: 15 | 16 | ### `npm dev` or `npm start` 17 | 18 | Runs the app in the development mode.
19 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 20 | 21 | The page will reload if you make edits.
22 | 23 | ### `npm run build` 24 | 25 | Builds the app for production to the `dist` folder.
26 | It correctly bundles Solid in production mode and optimizes the build for the best performance. 27 | 28 | The build is minified and the filenames include the hashes.
29 | Your app is ready to be deployed! 30 | 31 | ## Deployment 32 | 33 | You can deploy the `dist` folder to any static host provider (netlify, surge, now, etc.) 34 | -------------------------------------------------------------------------------- /examples/solid/__reactpreview__/Wrapper.module.css: -------------------------------------------------------------------------------- 1 | .Wrapper { 2 | padding: 16px; 3 | } 4 | -------------------------------------------------------------------------------- /examples/solid/__reactpreview__/Wrapper.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from "solid-js"; 2 | import styles from "./Wrapper.module.css"; 3 | 4 | export const Wrapper: Component = (props) => ( 5 |
{props.children}
6 | ); 7 | -------------------------------------------------------------------------------- /examples/solid/declaration.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.svg"; 2 | declare module "*.png"; 3 | declare module "*.jpg"; 4 | declare module "*.jpeg"; 5 | declare module "*.gif"; 6 | -------------------------------------------------------------------------------- /examples/solid/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Solid App 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/solid/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vite-template-solid", 3 | "version": "0.0.0", 4 | "description": "", 5 | "scripts": { 6 | "start": "vite", 7 | "dev": "vite", 8 | "build": "vite build" 9 | }, 10 | "license": "MIT", 11 | "devDependencies": { 12 | "rimraf": "^3.0.2", 13 | "vite": "^2.3.8", 14 | "vite-plugin-solid": "^2.0.0" 15 | }, 16 | "dependencies": { 17 | "solid-js": "^1.0.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/solid/src/App.module.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .logo { 6 | animation: logo-spin infinite 20s linear; 7 | height: 40vmin; 8 | pointer-events: none; 9 | } 10 | 11 | .header { 12 | background-color: #282c34; 13 | min-height: 100vh; 14 | display: flex; 15 | flex-direction: column; 16 | align-items: center; 17 | justify-content: center; 18 | font-size: calc(10px + 2vmin); 19 | color: white; 20 | } 21 | 22 | .link { 23 | color: #b318f0; 24 | } 25 | 26 | @keyframes logo-spin { 27 | from { 28 | transform: rotate(0deg); 29 | } 30 | to { 31 | transform: rotate(360deg); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/solid/src/App.screenshot.tsx: -------------------------------------------------------------------------------- 1 | import { createSignal } from "solid-js"; 2 | import AppComponent from "./App"; 3 | 4 | export const HelloWorld = ({ label }: { label: string }) =>
{label}
; 5 | HelloWorld.args = { 6 | label: "Hello, World!", 7 | }; 8 | 9 | export const App = () => ; 10 | 11 | export const Clicked = () => { 12 | const [clicked, setClicked] = createSignal(false); 13 | 14 | return ( 15 |
16 | 19 |
20 | {clicked() ? "clicked" : "not clicked"} 21 |
22 | ); 23 | }; 24 | Clicked.beforeScreenshot = async (element: HTMLElement) => { 25 | element.querySelector("#button")!.click(); 26 | }; 27 | -------------------------------------------------------------------------------- /examples/solid/src/App.tsx: -------------------------------------------------------------------------------- 1 | import type { Component } from "solid-js"; 2 | import styles from "./App.module.css"; 3 | import logo from "./logo.svg"; 4 | 5 | const App: Component = () => { 6 | return ( 7 |
8 |
9 | logo 10 |

11 | Edit src/App.tsx and save to reload. 12 |

13 | 19 | Learn Solid 20 | 21 |
22 |
23 | ); 24 | }; 25 | 26 | export default App; 27 | -------------------------------------------------------------------------------- /examples/solid/src/__screenshots__/darwin/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/solid/src/__screenshots__/darwin/App-App.png -------------------------------------------------------------------------------- /examples/solid/src/__screenshots__/darwin/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/solid/src/__screenshots__/darwin/App-Clicked.png -------------------------------------------------------------------------------- /examples/solid/src/__screenshots__/darwin/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/solid/src/__screenshots__/darwin/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/solid/src/__screenshots__/linux/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/solid/src/__screenshots__/linux/App-App.png -------------------------------------------------------------------------------- /examples/solid/src/__screenshots__/linux/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/solid/src/__screenshots__/linux/App-Clicked.png -------------------------------------------------------------------------------- /examples/solid/src/__screenshots__/linux/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/solid/src/__screenshots__/linux/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/solid/src/__screenshots__/win32/App-App.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/solid/src/__screenshots__/win32/App-App.png -------------------------------------------------------------------------------- /examples/solid/src/__screenshots__/win32/App-Clicked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/solid/src/__screenshots__/win32/App-Clicked.png -------------------------------------------------------------------------------- /examples/solid/src/__screenshots__/win32/App-HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/solid/src/__screenshots__/win32/App-HelloWorld.png -------------------------------------------------------------------------------- /examples/solid/src/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/solid/src/assets/favicon.ico -------------------------------------------------------------------------------- /examples/solid/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /examples/solid/src/index.tsx: -------------------------------------------------------------------------------- 1 | import { render } from "solid-js/web"; 2 | 3 | import "./index.css"; 4 | import App from "./App"; 5 | 6 | render(() => , document.getElementById("root")); 7 | -------------------------------------------------------------------------------- /examples/solid/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /examples/solid/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "allowSyntheticDefaultImports": true, 7 | "esModuleInterop": true, 8 | "jsx": "preserve", 9 | "jsxImportSource": "solid-js", 10 | "types": ["vite/client"] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /examples/solid/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import solidPlugin from "vite-plugin-solid"; 3 | 4 | export default defineConfig({ 5 | plugins: [solidPlugin()], 6 | build: { 7 | target: "esnext", 8 | polyfillDynamicImport: false, 9 | }, 10 | }); 11 | -------------------------------------------------------------------------------- /examples/solid/viteshot.config.js: -------------------------------------------------------------------------------- 1 | const playwrightShooter = require("viteshot/shooters/playwright"); 2 | const playwright = require("playwright"); 3 | 4 | /** 5 | * @type {import('viteshot').UserConfig} 6 | */ 7 | module.exports = { 8 | framework: { 9 | type: "solid", 10 | }, 11 | shooter: playwrightShooter(playwright.firefox), 12 | filePathPattern: "**/*.screenshot.@(jsx|tsx)", 13 | wrapper: { 14 | path: "__reactpreview__/Wrapper", 15 | componentName: "Wrapper", 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /examples/svelte/.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | /dist/ 3 | /.vscode/ 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /examples/svelte/README.md: -------------------------------------------------------------------------------- 1 | # Svelte + TS + Vite 2 | 3 | This template should help get you started developing with Svelte and TypeScript in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VSCode](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode). 8 | 9 | ## Need an official Svelte framework? 10 | 11 | Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more. 12 | 13 | ## Technical considerations 14 | 15 | **Why use this over SvelteKit?** 16 | 17 | - It brings its own routing solution which might not be preferable for some users. 18 | - It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app. 19 | `vite dev` and `vite build` wouldn't work in a SvelteKit environment, for example. 20 | 21 | This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-app` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project. 22 | 23 | Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate. 24 | 25 | **Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?** 26 | 27 | Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information. 28 | 29 | **Why include `.vscode/extensions.json`?** 30 | 31 | Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project. 32 | 33 | **Why enable `allowJs` in the TS template?** 34 | 35 | While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant. 36 | 37 | **Why is HMR not preserving my local component state?** 38 | 39 | HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr). 40 | 41 | If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR. 42 | 43 | ```ts 44 | // store.ts 45 | // An extremely simple external store 46 | import { writable } from 'svelte/store' 47 | export default writable(0) 48 | ``` 49 | -------------------------------------------------------------------------------- /examples/svelte/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Svelte + TS + Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/svelte/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.0", 3 | "type": "module", 4 | "scripts": { 5 | "dev": "vite", 6 | "build": "vite build", 7 | "serve": "vite preview", 8 | "check": "svelte-check --tsconfig ./tsconfig.json" 9 | }, 10 | "devDependencies": { 11 | "@sveltejs/vite-plugin-svelte": "^1.0.0-next.11", 12 | "@tsconfig/svelte": "^2.0.1", 13 | "rimraf": "^3.0.2", 14 | "svelte": "^3.37.0", 15 | "svelte-check": "^2.1.0", 16 | "svelte-preprocess": "^4.7.2", 17 | "tslib": "^2.2.0", 18 | "typescript": "^4.3.2", 19 | "vite": "^2.3.8" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/svelte/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/svelte/public/favicon.ico -------------------------------------------------------------------------------- /examples/svelte/src/App.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 |
7 | Svelte Logo 8 |

Hello Typescript!

9 | 10 | 11 | 12 |

13 | Visit svelte.dev to learn how to build Svelte 14 | apps. 15 |

16 | 17 |

18 | Check out SvelteKit for 19 | the officially supported framework, also powered by Vite! 20 |

21 |
22 | 23 | 66 | -------------------------------------------------------------------------------- /examples/svelte/src/assets/svelte.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/svelte/src/assets/svelte.png -------------------------------------------------------------------------------- /examples/svelte/src/lib/Counter.screenshot.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 |
10 |

Hi, World!

11 | 12 |
13 | -------------------------------------------------------------------------------- /examples/svelte/src/lib/Counter.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 11 | 12 | 35 | -------------------------------------------------------------------------------- /examples/svelte/src/lib/__screenshots__/darwin/Counter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/svelte/src/lib/__screenshots__/darwin/Counter.png -------------------------------------------------------------------------------- /examples/svelte/src/lib/__screenshots__/linux/Counter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/svelte/src/lib/__screenshots__/linux/Counter.png -------------------------------------------------------------------------------- /examples/svelte/src/lib/__screenshots__/win32/Counter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/svelte/src/lib/__screenshots__/win32/Counter.png -------------------------------------------------------------------------------- /examples/svelte/src/main.ts: -------------------------------------------------------------------------------- 1 | import App from './App.svelte' 2 | 3 | const app = new App({ 4 | target: document.getElementById('app') 5 | }) 6 | 7 | export default app 8 | -------------------------------------------------------------------------------- /examples/svelte/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /examples/svelte/svelte.config.js: -------------------------------------------------------------------------------- 1 | import sveltePreprocess from 'svelte-preprocess' 2 | 3 | export default { 4 | // Consult https://github.com/sveltejs/svelte-preprocess 5 | // for more information about preprocessors 6 | preprocess: sveltePreprocess() 7 | } 8 | -------------------------------------------------------------------------------- /examples/svelte/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | "compilerOptions": { 4 | "target": "esnext", 5 | "module": "esnext", 6 | "resolveJsonModule": true, 7 | "baseUrl": ".", 8 | /** 9 | * Typecheck JS in `.svelte` and `.js` files by default. 10 | * Disable checkJs if you'd like to use dynamic types in JS. 11 | * Note that setting allowJs false does not prevent the use 12 | * of JS in `.svelte` files. 13 | */ 14 | "allowJs": true, 15 | "checkJs": true 16 | }, 17 | "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"] 18 | } 19 | -------------------------------------------------------------------------------- /examples/svelte/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import { svelte } from '@sveltejs/vite-plugin-svelte' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [svelte()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/svelte/viteshot.config.cjs: -------------------------------------------------------------------------------- 1 | const playwrightShooter = require("viteshot/shooters/playwright"); 2 | const playwright = require("playwright"); 3 | 4 | /** 5 | * @type {import('viteshot').UserConfig} 6 | */ 7 | module.exports = { 8 | framework: { 9 | type: "svelte", 10 | }, 11 | shooter: playwrightShooter(playwright.firefox), 12 | filePathPattern: "**/*.screenshot.svelte", 13 | }; 14 | -------------------------------------------------------------------------------- /examples/vue/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | dist 4 | dist-ssr 5 | *.local 6 | -------------------------------------------------------------------------------- /examples/vue/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + Typescript + Vite 2 | 3 | This template should help get you started developing with Vue 3 and Typescript in Vite. 4 | 5 | ## Recommended IDE Setup 6 | 7 | [VSCode](https://code.visualstudio.com/) + [Vetur](https://marketplace.visualstudio.com/items?itemName=octref.vetur). Make sure to enable `vetur.experimental.templateInterpolationService` in settings! 8 | 9 | ### If Using ` 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.0.0", 3 | "scripts": { 4 | "dev": "vite", 5 | "build": "vue-tsc --noEmit && vite build", 6 | "serve": "vite preview" 7 | }, 8 | "dependencies": { 9 | "vue": "^3.0.5" 10 | }, 11 | "devDependencies": { 12 | "@vitejs/plugin-vue": "^1.2.3", 13 | "@vue/compiler-sfc": "^3.0.5", 14 | "rimraf": "^3.0.2", 15 | "typescript": "^4.3.2", 16 | "vite": "^2.3.8", 17 | "vue-tsc": "^0.0.24" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/vue/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/vue/public/favicon.ico -------------------------------------------------------------------------------- /examples/vue/src/App.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 17 | 18 | 28 | -------------------------------------------------------------------------------- /examples/vue/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/vue/src/assets/logo.png -------------------------------------------------------------------------------- /examples/vue/src/components/HelloWorld.screenshot.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 21 | 22 | -------------------------------------------------------------------------------- /examples/vue/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 53 | 54 | 71 | -------------------------------------------------------------------------------- /examples/vue/src/components/__screenshots__/darwin/HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/vue/src/components/__screenshots__/darwin/HelloWorld.png -------------------------------------------------------------------------------- /examples/vue/src/components/__screenshots__/linux/HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/vue/src/components/__screenshots__/linux/HelloWorld.png -------------------------------------------------------------------------------- /examples/vue/src/components/__screenshots__/win32/HelloWorld.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/examples/vue/src/components/__screenshots__/win32/HelloWorld.png -------------------------------------------------------------------------------- /examples/vue/src/main.ts: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /examples/vue/src/shims-vue.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue' 3 | const component: DefineComponent<{}, {}, any> 4 | export default component 5 | } 6 | -------------------------------------------------------------------------------- /examples/vue/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /examples/vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "strict": true, 7 | "jsx": "preserve", 8 | "sourceMap": true, 9 | "resolveJsonModule": true, 10 | "esModuleInterop": true, 11 | "lib": ["esnext", "dom"] 12 | }, 13 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"] 14 | } 15 | -------------------------------------------------------------------------------- /examples/vue/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [vue()] 7 | }) 8 | -------------------------------------------------------------------------------- /examples/vue/viteshot.config.js: -------------------------------------------------------------------------------- 1 | const playwrightShooter = require("viteshot/shooters/playwright"); 2 | const playwright = require("playwright"); 3 | 4 | /** 5 | * @type {import('viteshot').UserConfig} 6 | */ 7 | module.exports = { 8 | framework: { 9 | type: "vue", 10 | }, 11 | shooter: playwrightShooter(playwright.firefox), 12 | filePathPattern: "**/*.screenshot.vue", 13 | }; 14 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/logo.png -------------------------------------------------------------------------------- /logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | logo 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "viteshot", 3 | "version": "0.0.0-dev", 4 | "author": "François Wouts ", 5 | "license": "MIT", 6 | "types": "src/index.d.ts", 7 | "bin": { 8 | "viteshot": "lib/cli.js" 9 | }, 10 | "scripts": { 11 | "build": "rimraf dist && rollup -c && chmod +x dist/lib/cli.js && cp package.json dist/ && cp README.md dist/", 12 | "dev": "ts-node src/cli.ts", 13 | "dev:watch": "ts-node-dev --respawn src/cli.ts", 14 | "release": "yarn build && cd dist && yarn publish --non-interactive" 15 | }, 16 | "dependencies": { 17 | "@svgr/core": "^5.5.0", 18 | "assert-never": "^1.2.1", 19 | "chalk": "^4.1.2", 20 | "connect": "^3.7.0", 21 | "fs-extra": "^10.0.0", 22 | "get-port": "^5.1.1", 23 | "glob": "^7.1.7", 24 | "rollup-plugin-friendly-type-imports": "^1.0.1", 25 | "simple-git": "^2.44.0", 26 | "vite": "^2.5.1", 27 | "vite-tsconfig-paths": "^3.3.13", 28 | "yargs": "^17.1.1" 29 | }, 30 | "devDependencies": { 31 | "@percy/cli": "^1.0.0-beta.67", 32 | "@percy/puppeteer": "^2.0.0", 33 | "@rollup/plugin-replace": "^3.0.0", 34 | "@types/connect": "^3.4.35", 35 | "@types/fs-extra": "^9.0.12", 36 | "@types/glob": "^7.1.4", 37 | "@types/node": "^16.7.2", 38 | "@types/react": "^17.0.19", 39 | "@types/react-dom": "^17.0.9", 40 | "@types/yargs": "^17.0.2", 41 | "playwright": "^1.14.1", 42 | "preact": "^10.5.14", 43 | "puppeteer": "^10.2.0", 44 | "rimraf": "^3.0.2", 45 | "rollup": "^2.56.3", 46 | "rollup-plugin-preserve-shebangs": "^0.2.0", 47 | "rollup-plugin-typescript2": "^0.30.0", 48 | "solid-js": "^1.1.1", 49 | "svelte": "^3.42.3", 50 | "ts-node": "^10.2.1", 51 | "ts-node-dev": "^1.1.8", 52 | "typescript": "^4.4.2", 53 | "vue": "^3", 54 | "webpack": "^5.51.1", 55 | "webpack-cli": "^4.8.0" 56 | }, 57 | "peerDependencies": { 58 | "@percy/puppeteer": "^2.0.0", 59 | "@sveltejs/vite-plugin-svelte": "^1.0.0", 60 | "@vitejs/plugin-vue": "^1.2.3", 61 | "puppeteer": "^10.2.0", 62 | "vite-plugin-solid": "^2.0.0" 63 | }, 64 | "peerDependenciesMeta": { 65 | "@percy/puppeteer": { 66 | "optional": true 67 | }, 68 | "@sveltejs/vite-plugin-svelte": { 69 | "optional": true 70 | }, 71 | "@vitejs/plugin-vue": { 72 | "optional": true 73 | }, 74 | "puppeteer": { 75 | "optional": true 76 | }, 77 | "vite-plugin-solid": { 78 | "optional": true 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /renderers/main.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare global { 4 | interface Window { 5 | __takeScreenshot__(name: string): Promise; 6 | __done__(error?: string): Promise; 7 | } 8 | } 9 | 10 | // Default implementation of functions normally injected by the browser. 11 | // 12 | // This is especially useful when running the `debug` command. 13 | if (!window.__takeScreenshot__) { 14 | window.__takeScreenshot__ = (name) => { 15 | console.log(`Simulating screenshot: ${name}`); 16 | return new Promise((resolve) => setTimeout(resolve, 1000)); 17 | }; 18 | window.__done__ = async (errorMessage) => { 19 | if (errorMessage) { 20 | console.error(`Done with error: ${errorMessage}`); 21 | } else { 22 | console.log(`Done without errors.`); 23 | } 24 | }; 25 | } 26 | 27 | // Useful polyfills. 28 | const w = window as any; 29 | w.global ||= window; 30 | w.process ||= { 31 | env: {}, 32 | }; 33 | 34 | // Catch runtime errors and stop immediately. 35 | window.onerror = (event, source, lineno, colno, error) => { 36 | window.__done__((error && (error.stack || error.message)) || "Unknown error"); 37 | }; 38 | 39 | // Catch Vite errors and also stop immediately. 40 | // @ts-ignore Fixing this error would break ts-node-dev. 41 | import.meta.hot?.on("vite:error", (payload) => { 42 | window.__done__(payload.err.message); 43 | }); 44 | -------------------------------------------------------------------------------- /renderers/preact.ts: -------------------------------------------------------------------------------- 1 | import * as Preact from "preact"; 2 | 3 | export async function renderScreenshots( 4 | components: Array< 5 | [ 6 | string, 7 | Preact.ComponentType<{}> & { 8 | beforeScreenshot?: (element: HTMLElement) => Promise; 9 | } 10 | ] 11 | >, 12 | Wrapper: Preact.ComponentType<{}> | null 13 | ) { 14 | Wrapper ||= Preact.Fragment; 15 | const root = document.getElementById("root")!; 16 | for (const [name, Component] of components) { 17 | if (typeof Component !== "function") { 18 | // This is not a component. 19 | continue; 20 | } 21 | root.innerHTML = ""; 22 | try { 23 | Preact.render( 24 | Preact.createElement( 25 | Wrapper, 26 | {}, 27 | Preact.createElement(Component, (Component as any).args || {}) 28 | ), 29 | root 30 | ); 31 | const beforeScreenshot = (Component as any).beforeScreenshot; 32 | if (beforeScreenshot) { 33 | await beforeScreenshot(root); 34 | } 35 | } catch (e) { 36 | root.innerHTML = `
${
37 |         (e as any).stack || e
38 |       }
`; 39 | } 40 | await window.__takeScreenshot__(name); 41 | Preact.render(null, root); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /renderers/react.ts: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | 4 | export async function renderScreenshots( 5 | components: Array< 6 | [ 7 | string, 8 | React.ComponentType<{}> & { 9 | beforeScreenshot?: (element: HTMLElement) => Promise; 10 | } 11 | ] 12 | >, 13 | Wrapper: React.ComponentType<{}> | null 14 | ) { 15 | Wrapper ||= React.Fragment; 16 | const root = document.getElementById("root")!; 17 | for (const [name, Component] of components) { 18 | if (typeof Component !== "function") { 19 | // This is not a component. 20 | continue; 21 | } 22 | root.innerHTML = ""; 23 | try { 24 | ReactDOM.render( 25 | React.createElement( 26 | Wrapper, 27 | {}, 28 | React.createElement(Component, (Component as any).args || {}) 29 | ), 30 | root 31 | ); 32 | const beforeScreenshot = (Component as any).beforeScreenshot; 33 | if (beforeScreenshot) { 34 | await beforeScreenshot(root); 35 | } 36 | } catch (e) { 37 | root.innerHTML = `
${
38 |         (e as any).stack || e
39 |       }
`; 40 | } 41 | await window.__takeScreenshot__(name); 42 | ReactDOM.unmountComponentAtNode(root); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /renderers/solid.ts: -------------------------------------------------------------------------------- 1 | import { JSX } from "solid-js/jsx-runtime"; 2 | import { createComponent, render } from "solid-js/web"; 3 | 4 | export async function renderScreenshots( 5 | components: Array< 6 | [ 7 | string, 8 | ((props: {}) => JSX.Element) & { 9 | beforeScreenshot?: (element: HTMLElement) => Promise; 10 | } 11 | ] 12 | >, 13 | Wrapper: ((props: { children: JSX.Element }) => JSX.Element) | null 14 | ) { 15 | Wrapper ||= (props) => props.children; 16 | const root = document.getElementById("root")!; 17 | for (const [name, Component] of components) { 18 | if (typeof Component !== "function") { 19 | // This is not a component. 20 | continue; 21 | } 22 | root.innerHTML = ""; 23 | let detach: () => void = () => {}; 24 | try { 25 | detach = render( 26 | () => 27 | createComponent(Wrapper!, { 28 | children: createComponent(Component, (Component as any).args || {}), 29 | }), 30 | root 31 | ); 32 | const beforeScreenshot = (Component as any).beforeScreenshot; 33 | if (beforeScreenshot) { 34 | await beforeScreenshot(root); 35 | } 36 | } catch (e) { 37 | root.innerHTML = `
${e}\n${
38 |         (e as any).stack
39 |       }
`; 40 | } 41 | await window.__takeScreenshot__(name); 42 | detach(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /renderers/svelte.ts: -------------------------------------------------------------------------------- 1 | import { SvelteComponent } from "svelte"; 2 | 3 | export async function renderScreenshots( 4 | components: Array< 5 | [ 6 | string, 7 | { 8 | new (options: { 9 | target: HTMLElement | null; 10 | props: any; 11 | }): SvelteComponent & { 12 | beforeScreenshot?: (element: HTMLElement) => Promise; 13 | }; 14 | } 15 | ] 16 | > 17 | ) { 18 | // TODO: Support Wrapper in Svelte. 19 | const root = document.getElementById("root")!; 20 | for (const [name, Component] of components) { 21 | if (typeof Component !== "function") { 22 | // This is not a component. 23 | continue; 24 | } 25 | root.innerHTML = ""; 26 | try { 27 | const component = new Component({ 28 | target: root, 29 | props: {}, 30 | }); 31 | if (component.beforeScreenshot) { 32 | await component.beforeScreenshot(root); 33 | } 34 | } catch (e) { 35 | root.innerHTML = `
${e}\n${
36 |         (e as any).stack
37 |       }
`; 38 | } 39 | await window.__takeScreenshot__(name); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /renderers/vue.ts: -------------------------------------------------------------------------------- 1 | import { App, Component, createApp } from "vue"; 2 | 3 | export async function renderScreenshots( 4 | components: Array< 5 | [ 6 | string, 7 | Component & { beforeScreenshot?: (element: HTMLElement) => Promise } 8 | ] 9 | > 10 | ) { 11 | // TODO: Support Wrapper in Vue. 12 | const root = document.getElementById("root")!; 13 | for (const [name, component] of components) { 14 | root.innerHTML = ""; 15 | let app: App | null = null; 16 | try { 17 | // TODO: Support auto-generated fake props. 18 | app = createApp(component); 19 | app.mount("#root"); 20 | if (component.beforeScreenshot) { 21 | await component.beforeScreenshot(root); 22 | } 23 | } catch (e) { 24 | root.innerHTML = `
${e}\n${
25 |         (e as any).stack
26 |       }
`; 27 | } 28 | await window.__takeScreenshot__(name); 29 | if (app) { 30 | app.unmount(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import { preserveShebangs } from "rollup-plugin-preserve-shebangs"; 4 | import typescript from "rollup-plugin-typescript2"; 5 | 6 | const typescriptPlugin = typescript({ 7 | useTsconfigDeclarationDir: true, 8 | tsconfigOverride: { 9 | compilerOptions: { 10 | module: "esnext", 11 | declarationDir: "dist", 12 | }, 13 | }, 14 | }); 15 | 16 | export default [ 17 | { 18 | preserveModules: true, 19 | input: ["src/cli.ts"], 20 | output: [{ dir: "dist/lib", format: "cjs" }], 21 | external: ["@svgr/core"], 22 | plugins: [typescriptPlugin, preserveShebangs()], 23 | }, 24 | { 25 | preserveModules: true, 26 | input: allFiles("shooters"), 27 | output: [{ dir: "dist/shooters", format: "cjs" }], 28 | plugins: [typescriptPlugin], 29 | }, 30 | { 31 | preserveModules: true, 32 | input: allFiles("renderers"), 33 | output: [{ dir: "dist/renderers", format: "esm" }], 34 | plugins: [typescriptPlugin], 35 | }, 36 | ]; 37 | 38 | function allFiles(dirPath) { 39 | return fs.readdirSync(dirPath).flatMap((f) => { 40 | const filePath = path.join(dirPath, f); 41 | const stat = fs.statSync(filePath); 42 | if (stat.isFile()) { 43 | return [filePath]; 44 | } else if (stat.isDirectory()) { 45 | return [...allFiles(filePath)]; 46 | } else { 47 | return []; 48 | } 49 | }); 50 | } 51 | -------------------------------------------------------------------------------- /shooters/percy.ts: -------------------------------------------------------------------------------- 1 | import percySnapshot from "@percy/puppeteer"; 2 | import puppeteer from "puppeteer"; 3 | import { Shooter } from "../src/config"; 4 | 5 | export default (): Shooter => ({ 6 | shoot: async (url) => { 7 | let browser!: puppeteer.Browser; 8 | try { 9 | browser = await puppeteer.launch(); 10 | const page = await browser.newPage(); 11 | let errorMessage: string | null = null; 12 | let done!: (errorMessage?: string) => void; 13 | const donePromise = new Promise((resolve) => { 14 | done = (receivedErrorMessage) => { 15 | if (receivedErrorMessage) { 16 | errorMessage = receivedErrorMessage; 17 | } 18 | resolve(); 19 | }; 20 | }); 21 | await page.exposeFunction("__takeScreenshot__", async (name: string) => { 22 | console.log(`Capturing: ${name}`); 23 | await percySnapshot(page, name); 24 | }); 25 | await page.exposeFunction("__done__", done); 26 | await page.goto(url); 27 | await donePromise; 28 | if (errorMessage) { 29 | throw new Error(errorMessage); 30 | } 31 | return []; 32 | } finally { 33 | if (browser) { 34 | await browser.close(); 35 | } 36 | } 37 | }, 38 | }); 39 | -------------------------------------------------------------------------------- /shooters/playwright.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | import playwright from "playwright"; 4 | import { Shooter } from "../src/config"; 5 | 6 | const DEFAULT_TIMEOUT_MILLIS = 2 * 60 * 1000; 7 | 8 | export default ( 9 | browserType: playwright.BrowserType<{}>, 10 | options: { 11 | contexts?: Record; 12 | output?: { 13 | prefixPath?: string; 14 | suffixPath?: string; 15 | }; 16 | } = {} 17 | ): Shooter => { 18 | let prefixPath: string; 19 | let suffixPath: string; 20 | if (options.output) { 21 | prefixPath = options.output.prefixPath || ""; 22 | suffixPath = options.output.suffixPath || ""; 23 | } else { 24 | prefixPath = ""; 25 | suffixPath = `__screenshots__/${process.platform}`; 26 | } 27 | return { 28 | shoot: async (url) => { 29 | const screenshotPaths = new Set(); 30 | let browser!: playwright.Browser; 31 | 32 | async function takeScreenshots( 33 | contextName: string, 34 | contextOptions: playwright.BrowserContextOptions 35 | ): Promise { 36 | const context = await browser.newContext(contextOptions); 37 | const page = await context.newPage(); 38 | page.setDefaultTimeout(DEFAULT_TIMEOUT_MILLIS); 39 | await page.exposeFunction( 40 | "__takeScreenshot__", 41 | async (name: string) => { 42 | // Ensure all images are loaded. 43 | // Source: https://stackoverflow.com/a/49233383 44 | await page.evaluate(async () => { 45 | const selectors = Array.from(document.querySelectorAll("img")); 46 | await Promise.all( 47 | selectors.map((img) => { 48 | if (img.complete) { 49 | return; 50 | } 51 | return new Promise((resolve) => { 52 | img.addEventListener("load", resolve); 53 | // If an image fails to load, ignore it. 54 | img.addEventListener("error", resolve); 55 | }); 56 | }) 57 | ); 58 | }); 59 | const dirPath = path.dirname(name); 60 | const baseName = path.basename(name); 61 | const screenshotPath = path.resolve( 62 | prefixPath, 63 | dirPath, 64 | suffixPath, 65 | contextName, 66 | `${baseName}.png` 67 | ); 68 | const screenshotDirPath = path.dirname(screenshotPath); 69 | if (suffixPath && !screenshotPaths.has(screenshotDirPath)) { 70 | // Ensure the directory is clean (delete old screenshots). 71 | screenshotPaths.add(screenshotDirPath); 72 | await fs.promises.rm(screenshotDirPath, { 73 | recursive: true, 74 | force: true, 75 | }); 76 | } 77 | console.log( 78 | `Capturing: ${path.relative(process.cwd(), screenshotPath)}` 79 | ); 80 | await page.screenshot({ 81 | fullPage: true, 82 | path: screenshotPath, 83 | }); 84 | } 85 | ); 86 | let errorMessage: string | null = null; 87 | let done!: (errorMessage?: string) => void; 88 | const donePromise = new Promise((resolve) => { 89 | done = (receivedErrorMessage) => { 90 | if (receivedErrorMessage) { 91 | errorMessage = receivedErrorMessage; 92 | } 93 | resolve(); 94 | }; 95 | }); 96 | await page.exposeFunction("__done__", done); 97 | await page.goto(url); 98 | await donePromise; 99 | if (errorMessage) { 100 | throw new Error(errorMessage); 101 | } 102 | } 103 | 104 | // Delete all old screenshots if they live in a top-level directory. 105 | if (prefixPath) { 106 | await fs.promises.rm(prefixPath, { 107 | recursive: true, 108 | force: true, 109 | }); 110 | } 111 | 112 | try { 113 | browser = await browserType.launch(); 114 | const pendingScreenshots: Array> = []; 115 | for (const [contextName, contextOptions] of Object.entries( 116 | options.contexts || { "": {} } 117 | )) { 118 | pendingScreenshots.push(takeScreenshots(contextName, contextOptions)); 119 | } 120 | await Promise.all(pendingScreenshots); 121 | } finally { 122 | if (browser) { 123 | await browser.close(); 124 | } 125 | } 126 | return [...screenshotPaths]; 127 | }, 128 | }; 129 | }; 130 | -------------------------------------------------------------------------------- /src/cli.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import yargs from "yargs"; 4 | import { hideBin } from "yargs/helpers"; 5 | import { debugCommand } from "./commands/debug"; 6 | import { initCommand } from "./commands/init"; 7 | import { shootCommand } from "./commands/shoot"; 8 | 9 | yargs(hideBin(process.argv)) 10 | .command("init", "set up an initial config", {}, async (args) => { 11 | await initCommand(); 12 | }) 13 | .command( 14 | ["*", "shoot"], 15 | "captures screenshots", 16 | (yargs) => { 17 | return yargs 18 | .option("config", { 19 | alias: "c", 20 | describe: "Path of a config file", 21 | type: "string", 22 | }) 23 | .option("push", { 24 | alias: "p", 25 | describe: 26 | "Automatically create a commit with updated screenshots and push it", 27 | type: "boolean", 28 | }); 29 | }, 30 | async (args) => { 31 | await shootCommand(args); 32 | } 33 | ) 34 | .command( 35 | ["debug"], 36 | "starts the component server for debugging purposes", 37 | (yargs) => { 38 | return yargs 39 | .option("config", { 40 | alias: "c", 41 | describe: "Path of a config file", 42 | type: "string", 43 | }) 44 | .option("port", { 45 | alias: "p", 46 | describe: "Port on which to run the server", 47 | type: "number", 48 | }); 49 | }, 50 | async (args) => { 51 | await debugCommand(args); 52 | } 53 | ) 54 | .demandCommand().argv; 55 | -------------------------------------------------------------------------------- /src/commands/debug.ts: -------------------------------------------------------------------------------- 1 | import { info } from "../helpers/print"; 2 | import { readConfig } from "../helpers/read-config"; 3 | import { startRenderer } from "../renderer"; 4 | 5 | export async function debugCommand(options: { 6 | config?: string; 7 | port?: number; 8 | }) { 9 | const config = await readConfig(options.config); 10 | const port = options.port || 3130; 11 | await startRenderer({ 12 | ...config, 13 | port, 14 | }); 15 | info(`Debug server running at http://localhost:${port}`); 16 | } 17 | -------------------------------------------------------------------------------- /src/commands/init.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs-extra"; 2 | import path from "path"; 3 | import { Framework } from "../config"; 4 | import { fail } from "../helpers/fail"; 5 | import { findFileUpwards } from "../helpers/find-file"; 6 | import { success, warn } from "../helpers/print"; 7 | 8 | export async function initCommand(): Promise { 9 | const packageJsonFilePath = await findFileUpwards("package.json"); 10 | if (!packageJsonFilePath) { 11 | return fail( 12 | `Unable to find package.json. Are you in the correct directory?` 13 | ); 14 | } 15 | const packageInfo = JSON.parse( 16 | await fs.readFile(packageJsonFilePath, "utf8") 17 | ); 18 | const configFileName = 19 | packageInfo.type === "module" 20 | ? "viteshot.config.cjs" 21 | : "viteshot.config.js"; 22 | const configFilePath = path.join( 23 | path.dirname(packageJsonFilePath), 24 | configFileName 25 | ); 26 | if (await fs.pathExists(configFilePath)) { 27 | return fail(`${configFileName} already exists. Exiting early.`); 28 | } 29 | const dependencies = packageInfo.dependencies || {}; 30 | let framework: Framework; 31 | if ("preact" in dependencies) { 32 | framework = "preact"; 33 | } else if ("react" in dependencies) { 34 | framework = "react"; 35 | } else if ("solid-js" in dependencies) { 36 | framework = "solid"; 37 | } else if ("svelte" in dependencies) { 38 | framework = "svelte"; 39 | } else if ("vue" in dependencies) { 40 | framework = "vue"; 41 | } else { 42 | warn( 43 | `Unable to detect which framework is used in this project. Defaulting to react.` 44 | ); 45 | framework = "react"; 46 | } 47 | await fs.writeFile( 48 | configFilePath, 49 | `const playwrightShooter = require("viteshot/shooters/playwright"); 50 | const playwright = require("playwright"); 51 | 52 | module.exports = { 53 | framework: { 54 | type: "${framework}", 55 | }, 56 | shooter: playwrightShooter(playwright.chromium), 57 | filePathPattern: "**/*.screenshot.@(js|jsx|tsx|vue|svelte)", 58 | }; 59 | `, 60 | "utf8" 61 | ); 62 | success(`${configFileName} was created successfully.`); 63 | } 64 | -------------------------------------------------------------------------------- /src/commands/shoot.ts: -------------------------------------------------------------------------------- 1 | import getPort from "get-port"; 2 | import simpleGit from "simple-git"; 3 | import { fail } from "../helpers/fail"; 4 | import { info } from "../helpers/print"; 5 | import { readConfig } from "../helpers/read-config"; 6 | import { startRenderer } from "../renderer"; 7 | 8 | const MAIN_BRANCHES = new Set(["main", "master"]); 9 | 10 | export async function shootCommand(options: { 11 | config?: string; 12 | push?: boolean; 13 | }) { 14 | const config = await readConfig(options.config); 15 | const port = await getPort(); 16 | let stopRenderer = async () => {}; 17 | let screenshotPaths: string[]; 18 | try { 19 | stopRenderer = await startRenderer({ 20 | ...config, 21 | port, 22 | }); 23 | screenshotPaths = await config.shooter.shoot(`http://localhost:${port}`); 24 | } catch (e) { 25 | return fail(`${e}`); 26 | } finally { 27 | await stopRenderer(); 28 | } 29 | if (options.push) { 30 | const git = simpleGit(); 31 | await git.addConfig("user.name", "🤖 ViteShot"); 32 | await git.addConfig("user.email", "viteshot-bot@zenc.io"); 33 | await git.add(screenshotPaths); 34 | const status = await git.status(); 35 | const branch = await git.branch(); 36 | if (status.files.length > 0) { 37 | if (MAIN_BRANCHES.has(branch.current)) { 38 | return fail(`🚨 Screenshots have changed on ${branch.current}!`); 39 | } 40 | await git.stash(); 41 | await git.pull(); 42 | await git.stash(["pop"]); 43 | await git.add(screenshotPaths); 44 | await git.commit("📸 Updated screenshots"); 45 | await git.push(); 46 | info("⚠️ Screenshots have been updated."); 47 | } else { 48 | info("✅ Screenshots have not changed."); 49 | } 50 | } 51 | info("All done."); 52 | return process.exit(0); 53 | } 54 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import * as vite from "vite"; 2 | 3 | export interface UserConfig { 4 | framework: FrameworkOptions; 5 | filePathPattern: string; 6 | projectPath: string; 7 | wrapper?: WrapperConfig; 8 | shooter: Shooter; 9 | vite?: vite.UserConfig; 10 | } 11 | 12 | export type FrameworkOptions = 13 | | { 14 | type: "react"; 15 | svgr?: { 16 | componentName: string; 17 | }; 18 | } 19 | | { 20 | type: "preact" | "solid" | "svelte" | "vue"; 21 | }; 22 | 23 | export interface WrapperConfig { 24 | path: string; 25 | componentName: string; 26 | } 27 | export interface Shooter { 28 | shoot(url: string): Promise; 29 | } 30 | 31 | export type Framework = "preact" | "react" | "solid" | "svelte" | "vue"; 32 | -------------------------------------------------------------------------------- /src/frameworks/config.ts: -------------------------------------------------------------------------------- 1 | import { Plugin } from "vite"; 2 | 3 | export interface FrameworkConfiguration { 4 | packages: string[]; 5 | defaultImports: boolean; 6 | plugins: Plugin[]; 7 | } 8 | -------------------------------------------------------------------------------- /src/frameworks/preact.ts: -------------------------------------------------------------------------------- 1 | import { FrameworkConfiguration } from "./config"; 2 | 3 | export function preactConfiguration(): FrameworkConfiguration { 4 | return { 5 | packages: ["preact"], 6 | defaultImports: false, 7 | plugins: [ 8 | { 9 | name: "preact", 10 | config() { 11 | return { 12 | esbuild: { 13 | jsxFactory: "h", 14 | jsxFragment: "Fragment", 15 | }, 16 | resolve: { 17 | alias: { 18 | "react-dom": "preact/compat", 19 | react: "preact/compat", 20 | }, 21 | }, 22 | }; 23 | }, 24 | transform(code: string, id: string) { 25 | if (id.endsWith("sx") && !code.includes(`from "preact"`)) { 26 | return `import { h } from 'preact';\n${code}`; 27 | } 28 | return null; 29 | }, 30 | }, 31 | ], 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /src/frameworks/react.ts: -------------------------------------------------------------------------------- 1 | import * as vite from "vite"; 2 | import { svgrPlugin } from "../plugins/svgr-plugin"; 3 | import { FrameworkConfiguration } from "./config"; 4 | 5 | export function reactConfiguration( 6 | config: { 7 | type: "react"; 8 | svgr?: { 9 | componentName: string; 10 | }; 11 | }, 12 | viteConfig?: vite.UserConfig 13 | ): FrameworkConfiguration { 14 | const alias = viteConfig?.resolve?.alias; 15 | return { 16 | packages: ["react", "react-dom"], 17 | defaultImports: false, 18 | plugins: [ 19 | { 20 | name: "react", 21 | transform(code: string, id: string) { 22 | // Since React 17, importing React is optional when building with webpack. 23 | // We do need the import with Vite, however. 24 | const reactImportRegExp = /import (\* as )?React[ ,]/; 25 | if ( 26 | (id.endsWith(".js") || id.endsWith("sx")) && 27 | !reactImportRegExp.test(code) 28 | ) { 29 | return `import React from "react";${code}`; 30 | } 31 | return null; 32 | }, 33 | }, 34 | svgrPlugin( 35 | // Only record aliases are supported, not arrays. 36 | alias && !Array.isArray(alias) ? (alias as Record) : {}, 37 | config.svgr?.componentName 38 | ), 39 | ], 40 | }; 41 | } 42 | -------------------------------------------------------------------------------- /src/frameworks/solid.ts: -------------------------------------------------------------------------------- 1 | import { FrameworkConfiguration } from "./config"; 2 | 3 | export function solidConfiguration( 4 | projectPath: string 5 | ): FrameworkConfiguration { 6 | // Note: This package is an optional peer dependency. 7 | const solidPlugin = require(require.resolve("vite-plugin-solid", { 8 | paths: [projectPath], 9 | })); 10 | return { 11 | packages: ["solid-js"], 12 | defaultImports: false, 13 | plugins: [solidPlugin()], 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/frameworks/svelte.ts: -------------------------------------------------------------------------------- 1 | import { FrameworkConfiguration } from "./config"; 2 | 3 | export function svelteConfiguration( 4 | projectPath: string 5 | ): FrameworkConfiguration { 6 | // Note: This package is an optional peer dependency. 7 | const { svelte } = require(require.resolve("@sveltejs/vite-plugin-svelte", { 8 | paths: [projectPath], 9 | })); 10 | return { 11 | packages: ["svelte"], 12 | defaultImports: true, 13 | plugins: [svelte()], 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/frameworks/vue.ts: -------------------------------------------------------------------------------- 1 | import { FrameworkConfiguration } from "./config"; 2 | 3 | export function vueConfiguration(projectPath: string): FrameworkConfiguration { 4 | // Note: This package is an optional peer dependency. 5 | const vue = require(require.resolve("@vitejs/plugin-vue", { 6 | paths: [projectPath], 7 | })); 8 | return { 9 | packages: ["vue"], 10 | defaultImports: true, 11 | plugins: [vue()], 12 | }; 13 | } 14 | -------------------------------------------------------------------------------- /src/helpers/fail.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | 3 | export function fail(message: string) { 4 | console.error(chalk.red(message)); 5 | return process.exit(1); 6 | } 7 | -------------------------------------------------------------------------------- /src/helpers/find-file.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs-extra"; 2 | import path from "path"; 3 | 4 | export async function findFileUpwards( 5 | ...possibleNames: string[] 6 | ): Promise { 7 | let dirPath = process.cwd(); 8 | while (dirPath !== path.dirname(dirPath)) { 9 | for (const name of possibleNames) { 10 | const filePath = path.join(dirPath, name); 11 | if (await fs.pathExists(filePath)) { 12 | return filePath; 13 | } 14 | } 15 | dirPath = path.dirname(dirPath); 16 | } 17 | return null; 18 | } 19 | -------------------------------------------------------------------------------- /src/helpers/print.ts: -------------------------------------------------------------------------------- 1 | import chalk from "chalk"; 2 | 3 | export function info(message: string) { 4 | console.log(message); 5 | } 6 | 7 | export function success(message: string) { 8 | console.log(chalk.green(message)); 9 | } 10 | 11 | export function warn(message: string) { 12 | console.warn(chalk.yellow(message)); 13 | } 14 | -------------------------------------------------------------------------------- /src/helpers/read-config.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs-extra"; 2 | import path from "path"; 3 | import { UserConfig } from "../config"; 4 | import { fail } from "./fail"; 5 | import { findFileUpwards } from "./find-file"; 6 | 7 | const CONFIG_FILE_NAMES = ["viteshot.config.js", "viteshot.config.cjs"]; 8 | 9 | export async function readConfig( 10 | customConfigFilePath?: string 11 | ): Promise { 12 | const configFilePath = customConfigFilePath 13 | ? path.resolve(customConfigFilePath) 14 | : await findFileUpwards(...CONFIG_FILE_NAMES); 15 | if (!configFilePath) { 16 | return fail( 17 | `Could not find viteshot config file. Please run \`viteshot init\` to set it up.` 18 | ); 19 | } 20 | try { 21 | const stat = await fs.stat(configFilePath); 22 | if (!stat.isFile()) { 23 | return fail(`Expected to find a valid config file: ${configFilePath}`); 24 | } 25 | } catch { 26 | return fail(`Unable to access config file: ${configFilePath}`); 27 | } 28 | const config = require(configFilePath) as Partial; 29 | const configFileName = path.basename(configFilePath); 30 | if (!config.framework) { 31 | return fail(`Please specify \`framework\` in ${configFileName}`); 32 | } 33 | if (!config.shooter) { 34 | return fail(`Please specify \`shooter\` in ${configFileName}`); 35 | } 36 | if (!config.filePathPattern) { 37 | return fail(`Please specify \`filePathPattern\` in ${configFileName}`); 38 | } 39 | return { 40 | framework: config.framework, 41 | shooter: config.shooter, 42 | projectPath: config.projectPath || path.dirname(configFilePath), 43 | filePathPattern: config.filePathPattern, 44 | vite: config.vite, 45 | wrapper: config.wrapper, 46 | }; 47 | } 48 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export type { UserConfig } from "./config"; 2 | -------------------------------------------------------------------------------- /src/plugins/svgr-plugin.ts: -------------------------------------------------------------------------------- 1 | // @ts-ignore untyped (pending https://github.com/gregberge/svgr/pull/555) 2 | import svgr from "@svgr/core"; 3 | import * as esbuild from "esbuild"; 4 | import fs from "fs-extra"; 5 | import path from "path"; 6 | import * as vite from "vite"; 7 | 8 | export function svgrPlugin( 9 | alias: Record, 10 | componentName?: string 11 | ): vite.Plugin { 12 | componentName = componentName || "ReactComponent"; 13 | return { 14 | name: "vite:svgr", 15 | async transform(code, id) { 16 | if (!id.endsWith(".svg")) { 17 | return; 18 | } 19 | let filePath = id; 20 | for (const [mapFrom, mapTo] of Object.entries(alias)) { 21 | const matchStart = path.join(path.sep, mapFrom); 22 | if (id.startsWith(matchStart)) { 23 | filePath = path.join(mapTo, path.relative(matchStart, id)); 24 | } 25 | } 26 | if (!(await fs.pathExists(filePath))) { 27 | console.warn(`Unable to resolve SVG file: ${id}`); 28 | return; 29 | } 30 | const svg = await fs.readFile(filePath, "utf8"); 31 | const generatedSvgrCode: string = await svgr( 32 | svg, 33 | {}, 34 | { componentName: "ReactComponent" } 35 | ); 36 | const componentCode = generatedSvgrCode.replace( 37 | "export default ReactComponent", 38 | `export { ReactComponent as ${componentName} }` 39 | ); 40 | const res = await esbuild.transform( 41 | (componentName !== "default" ? code : "") + "\n" + componentCode, 42 | { 43 | loader: "jsx", 44 | } 45 | ); 46 | return { 47 | code: res.code, 48 | }; 49 | }, 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Basic Options */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | "target": "es2019" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */, 8 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 9 | // "lib": [], /* Specify library files to be included in the compilation. */ 10 | // "allowJs": true, /* Allow javascript files to be compiled. */ 11 | // "checkJs": true, /* Report errors in .js files. */ 12 | "jsx": "preserve" /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */, 13 | "declaration": true /* Generates corresponding '.d.ts' file. */, 14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 15 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 16 | // "outFile": "./", /* Concatenate and emit output to single file. */ 17 | "outDir": "dist/" /* Redirect output structure to the directory. */, 18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 19 | // "composite": true, /* Enable project compilation */ 20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 21 | // "removeComments": true, /* Do not emit comments to output. */ 22 | // "noEmit": true, /* Do not emit outputs. */ 23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 25 | "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */, 26 | 27 | /* Strict Type-Checking Options */ 28 | "strict": true /* Enable all strict type-checking options. */, 29 | "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */, 30 | "strictNullChecks": true /* Enable strict null checks. */, 31 | "strictFunctionTypes": true /* Enable strict checking of function types. */, 32 | "strictBindCallApply": true /* Enable strict 'bind', 'call', and 'apply' methods on functions. */, 33 | "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */, 34 | "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */, 35 | "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */, 36 | 37 | /* Additional Checks */ 38 | // "noUnusedLocals": true /* Report errors on unused locals. */, 39 | // "noUnusedParameters": true /* Report errors on unused parameters. */, 40 | "noImplicitReturns": true /* Report error when not all code paths in function return a value. */, 41 | "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */, 42 | "noUncheckedIndexedAccess": true /* Include 'undefined' in index signature results */, 43 | "noImplicitOverride": true /* Ensure overriding members in derived classes are marked with an 'override' modifier. */, 44 | "noPropertyAccessFromIndexSignature": true /* Require undeclared properties from index signatures to use element accesses. */, 45 | 46 | /* Module Resolution Options */ 47 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, 48 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 49 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 50 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 51 | // "typeRoots": [], /* List of folders to include type definitions from. */ 52 | // "types": [], /* Type declaration files to be included in compilation. */ 53 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 54 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 55 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 56 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 57 | 58 | /* Source Map Options */ 59 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 60 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 61 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 62 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 63 | 64 | /* Experimental Options */ 65 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 66 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 67 | 68 | /* Advanced Options */ 69 | "skipLibCheck": true /* Skip type checking of declaration files. */, 70 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 71 | }, 72 | "include": ["src", "shooters", "renderers"] 73 | } 74 | -------------------------------------------------------------------------------- /viteshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/viteshot.gif -------------------------------------------------------------------------------- /viteshot.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fwouts/viteshot/1ec2591175d7c23a5a407dad626cd3ce243d21bd/viteshot.mp4 --------------------------------------------------------------------------------