Initial commit 2

mistress
Yuki 10 months ago
parent d4bfc0347a
commit 58413f8034

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

47
dist/index.html vendored

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri + Svelte</title>
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
}
.window {
min-height: 100vh;
min-width: 100vw;
max-height: 100vh;
max-width: 100vw;
}
.window-body {
min-height: calc(100vh - 48px);
max-height: calc(100vh - 48px);
overflow: auto;
}
</style>
<script type="module" crossorigin src="/assets/index-b80e8e17.js"></script>
<link rel="stylesheet" href="/assets/index-9e689485.css">
</head>
<body class="window glass active">
<div data-tauri-drag-region class="title-bar">
<div data-tauri-drag-region class="title-bar-text">Lindows Mive Wessenger</div>
<div class="title-bar-controls">
<button aria-label="Minimize"></button>
<button aria-label="Maximize"></button>
<button aria-label="Close"></button>
</div>
</div>
<div id="app" class="window-body has-space"></div>
</body>
</html>

1
dist/svelte.svg vendored

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="26.6" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 308"><path fill="#FF3E00" d="M239.682 40.707C211.113-.182 154.69-12.301 113.895 13.69L42.247 59.356a82.198 82.198 0 0 0-37.135 55.056a86.566 86.566 0 0 0 8.536 55.576a82.425 82.425 0 0 0-12.296 30.719a87.596 87.596 0 0 0 14.964 66.244c28.574 40.893 84.997 53.007 125.787 27.016l71.648-45.664a82.182 82.182 0 0 0 37.135-55.057a86.601 86.601 0 0 0-8.53-55.577a82.409 82.409 0 0 0 12.29-30.718a87.573 87.573 0 0 0-14.963-66.244"></path><path fill="#FFF" d="M106.889 270.841c-23.102 6.007-47.497-3.036-61.103-22.648a52.685 52.685 0 0 1-9.003-39.85a49.978 49.978 0 0 1 1.713-6.693l1.35-4.115l3.671 2.697a92.447 92.447 0 0 0 28.036 14.007l2.663.808l-.245 2.659a16.067 16.067 0 0 0 2.89 10.656a17.143 17.143 0 0 0 18.397 6.828a15.786 15.786 0 0 0 4.403-1.935l71.67-45.672a14.922 14.922 0 0 0 6.734-9.977a15.923 15.923 0 0 0-2.713-12.011a17.156 17.156 0 0 0-18.404-6.832a15.78 15.78 0 0 0-4.396 1.933l-27.35 17.434a52.298 52.298 0 0 1-14.553 6.391c-23.101 6.007-47.497-3.036-61.101-22.649a52.681 52.681 0 0 1-9.004-39.849a49.428 49.428 0 0 1 22.34-33.114l71.664-45.677a52.218 52.218 0 0 1 14.563-6.398c23.101-6.007 47.497 3.036 61.101 22.648a52.685 52.685 0 0 1 9.004 39.85a50.559 50.559 0 0 1-1.713 6.692l-1.35 4.116l-3.67-2.693a92.373 92.373 0 0 0-28.037-14.013l-2.664-.809l.246-2.658a16.099 16.099 0 0 0-2.89-10.656a17.143 17.143 0 0 0-18.398-6.828a15.786 15.786 0 0 0-4.402 1.935l-71.67 45.674a14.898 14.898 0 0 0-6.73 9.975a15.9 15.9 0 0 0 2.709 12.012a17.156 17.156 0 0 0 18.404 6.832a15.841 15.841 0 0 0 4.402-1.935l27.345-17.427a52.147 52.147 0 0 1 14.552-6.397c23.101-6.006 47.497 3.037 61.102 22.65a52.681 52.681 0 0 1 9.003 39.848a49.453 49.453 0 0 1-22.34 33.12l-71.664 45.673a52.218 52.218 0 0 1-14.563 6.398"></path></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

6
dist/tauri.svg vendored

@ -0,0 +1,6 @@
<svg width="206" height="231" viewBox="0 0 206 231" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M143.143 84C143.143 96.1503 133.293 106 121.143 106C108.992 106 99.1426 96.1503 99.1426 84C99.1426 71.8497 108.992 62 121.143 62C133.293 62 143.143 71.8497 143.143 84Z" fill="#FFC131"/>
<ellipse cx="84.1426" cy="147" rx="22" ry="22" transform="rotate(180 84.1426 147)" fill="#24C8DB"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M166.738 154.548C157.86 160.286 148.023 164.269 137.757 166.341C139.858 160.282 141 153.774 141 147C141 144.543 140.85 142.121 140.558 139.743C144.975 138.204 149.215 136.139 153.183 133.575C162.73 127.404 170.292 118.608 174.961 108.244C179.63 97.8797 181.207 86.3876 179.502 75.1487C177.798 63.9098 172.884 53.4021 165.352 44.8883C157.82 36.3744 147.99 30.2165 137.042 27.1546C126.095 24.0926 114.496 24.2568 103.64 27.6274C92.7839 30.998 83.1319 37.4317 75.8437 46.1553C74.9102 47.2727 74.0206 48.4216 73.176 49.5993C61.9292 50.8488 51.0363 54.0318 40.9629 58.9556C44.2417 48.4586 49.5653 38.6591 56.679 30.1442C67.0505 17.7298 80.7861 8.57426 96.2354 3.77762C111.685 -1.01901 128.19 -1.25267 143.769 3.10474C159.348 7.46215 173.337 16.2252 184.056 28.3411C194.775 40.457 201.767 55.4101 204.193 71.404C206.619 87.3978 204.374 103.752 197.73 118.501C191.086 133.25 180.324 145.767 166.738 154.548ZM41.9631 74.275L62.5557 76.8042C63.0459 72.813 63.9401 68.9018 65.2138 65.1274C57.0465 67.0016 49.2088 70.087 41.9631 74.275Z" fill="#FFC131"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.4045 76.4519C47.3493 70.6709 57.2677 66.6712 67.6171 64.6132C65.2774 70.9669 64 77.8343 64 85.0001C64 87.1434 64.1143 89.26 64.3371 91.3442C60.0093 92.8732 55.8533 94.9092 51.9599 97.4256C42.4128 103.596 34.8505 112.392 30.1816 122.756C25.5126 133.12 23.9357 144.612 25.6403 155.851C27.3449 167.09 32.2584 177.598 39.7906 186.112C47.3227 194.626 57.153 200.784 68.1003 203.846C79.0476 206.907 90.6462 206.743 101.502 203.373C112.359 200.002 122.011 193.568 129.299 184.845C130.237 183.722 131.131 182.567 131.979 181.383C143.235 180.114 154.132 176.91 164.205 171.962C160.929 182.49 155.596 192.319 148.464 200.856C138.092 213.27 124.357 222.426 108.907 227.222C93.458 232.019 76.9524 232.253 61.3736 227.895C45.7948 223.538 31.8055 214.775 21.0867 202.659C10.3679 190.543 3.37557 175.59 0.949823 159.596C-1.47592 143.602 0.768139 127.248 7.41237 112.499C14.0566 97.7497 24.8183 85.2327 38.4045 76.4519ZM163.062 156.711L163.062 156.711C162.954 156.773 162.846 156.835 162.738 156.897C162.846 156.835 162.954 156.773 163.062 156.711Z" fill="#24C8DB"/>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

1
dist/vite.svg vendored

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,45 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Tauri + Svelte</title>
<link rel="stylesheet" href="node_modules/7.css">
<style>
body {
margin: 0;
padding: 0;
overflow: hidden;
}
.window {
min-height: 100vh;
min-width: 100vw;
max-height: 100vh;
max-width: 100vw;
}
.window-body {
min-height: calc(100vh - 48px);
max-height: calc(100vh - 48px);
overflow: auto;
}
</style>
</head>
<body class="window glass active">
<div data-tauri-drag-region class="title-bar">
<div data-tauri-drag-region class="title-bar-text">Lindows Mive Wessenger</div>
<div class="title-bar-controls">
<button aria-label="Minimize"></button>
<button aria-label="Maximize"></button>
<button aria-label="Close"></button>
</div>
</div>
<div id="app" class="window-body has-space"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

@ -0,0 +1,29 @@
{
"compilerOptions": {
"moduleResolution": "Node",
"target": "ESNext",
"module": "ESNext",
"verbatimModuleSyntax": false,
"isolatedModules": true,
"resolveJsonModule": true,
/**
* To have warnings / errors of the Svelte compiler at the
* correct position, enable source maps by default.
*/
"sourceMap": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": ".",
/**
* Typecheck JS in `.svelte` and `.js` files by default.
* Disable this if you'd like to use dynamic types.
*/
"checkJs": true
},
/**
* Use global.d.ts instead of compilerOptions.types
* to avoid limiting type declarations.
*/
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
}

17
node_modules/.bin/tauri generated vendored

@ -0,0 +1,17 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -z "$NODE_PATH" ]; then
export NODE_PATH="/home/yukil/repos/msn/node_modules/.pnpm/@tauri-apps+cli@1.3.1/node_modules/@tauri-apps/cli/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/@tauri-apps+cli@1.3.1/node_modules/@tauri-apps/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/@tauri-apps+cli@1.3.1/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/node_modules"
else
export NODE_PATH="/home/yukil/repos/msn/node_modules/.pnpm/@tauri-apps+cli@1.3.1/node_modules/@tauri-apps/cli/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/@tauri-apps+cli@1.3.1/node_modules/@tauri-apps/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/@tauri-apps+cli@1.3.1/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/node_modules:$NODE_PATH"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../@tauri-apps/cli/tauri.js" "$@"
else
exec node "$basedir/../@tauri-apps/cli/tauri.js" "$@"
fi

17
node_modules/.bin/vite generated vendored

@ -0,0 +1,17 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -z "$NODE_PATH" ]; then
export NODE_PATH="/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules/vite/bin/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules/vite/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/node_modules"
else
export NODE_PATH="/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules/vite/bin/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules/vite/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/node_modules:$NODE_PATH"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../vite/bin/vite.js" "$@"
else
exec node "$basedir/../vite/bin/vite.js" "$@"
fi

142
node_modules/.modules.yaml generated vendored

@ -0,0 +1,142 @@
hoistPattern:
- '*'
hoistedDependencies:
/@esbuild/android-arm/0.17.19:
'@esbuild/android-arm': private
/@esbuild/android-arm64/0.17.19:
'@esbuild/android-arm64': private
/@esbuild/android-x64/0.17.19:
'@esbuild/android-x64': private
/@esbuild/darwin-arm64/0.17.19:
'@esbuild/darwin-arm64': private
/@esbuild/darwin-x64/0.17.19:
'@esbuild/darwin-x64': private
/@esbuild/freebsd-arm64/0.17.19:
'@esbuild/freebsd-arm64': private
/@esbuild/freebsd-x64/0.17.19:
'@esbuild/freebsd-x64': private
/@esbuild/linux-arm/0.17.19:
'@esbuild/linux-arm': private
/@esbuild/linux-arm64/0.17.19:
'@esbuild/linux-arm64': private
/@esbuild/linux-ia32/0.17.19:
'@esbuild/linux-ia32': private
/@esbuild/linux-loong64/0.17.19:
'@esbuild/linux-loong64': private
/@esbuild/linux-mips64el/0.17.19:
'@esbuild/linux-mips64el': private
/@esbuild/linux-ppc64/0.17.19:
'@esbuild/linux-ppc64': private
/@esbuild/linux-riscv64/0.17.19:
'@esbuild/linux-riscv64': private
/@esbuild/linux-s390x/0.17.19:
'@esbuild/linux-s390x': private
/@esbuild/linux-x64/0.17.19:
'@esbuild/linux-x64': private
/@esbuild/netbsd-x64/0.17.19:
'@esbuild/netbsd-x64': private
/@esbuild/openbsd-x64/0.17.19:
'@esbuild/openbsd-x64': private
/@esbuild/sunos-x64/0.17.19:
'@esbuild/sunos-x64': private
/@esbuild/win32-arm64/0.17.19:
'@esbuild/win32-arm64': private
/@esbuild/win32-ia32/0.17.19:
'@esbuild/win32-ia32': private
/@esbuild/win32-x64/0.17.19:
'@esbuild/win32-x64': private
/@jridgewell/sourcemap-codec/1.4.15:
'@jridgewell/sourcemap-codec': private
/@tauri-apps/cli-darwin-arm64/1.3.1:
'@tauri-apps/cli-darwin-arm64': private
/@tauri-apps/cli-darwin-x64/1.3.1:
'@tauri-apps/cli-darwin-x64': private
/@tauri-apps/cli-linux-arm-gnueabihf/1.3.1:
'@tauri-apps/cli-linux-arm-gnueabihf': private
/@tauri-apps/cli-linux-arm64-gnu/1.3.1:
'@tauri-apps/cli-linux-arm64-gnu': private
/@tauri-apps/cli-linux-arm64-musl/1.3.1:
'@tauri-apps/cli-linux-arm64-musl': private
/@tauri-apps/cli-linux-x64-gnu/1.3.1:
'@tauri-apps/cli-linux-x64-gnu': private
/@tauri-apps/cli-linux-x64-musl/1.3.1:
'@tauri-apps/cli-linux-x64-musl': private
/@tauri-apps/cli-win32-ia32-msvc/1.3.1:
'@tauri-apps/cli-win32-ia32-msvc': private
/@tauri-apps/cli-win32-x64-msvc/1.3.1:
'@tauri-apps/cli-win32-x64-msvc': private
/debug/4.3.4:
debug: private
/deepmerge/4.3.1:
deepmerge: private
/esbuild/0.17.19:
esbuild: private
/fsevents/2.3.2:
fsevents: private
/kleur/4.1.5:
kleur: private
/magic-string/0.30.0:
magic-string: private
/ms/2.1.2:
ms: private
/nanoid/3.3.6:
nanoid: private
/picocolors/1.0.0:
picocolors: private
/postcss/8.4.23:
postcss: private
/rollup/3.22.0:
rollup: private
/source-map-js/1.0.2:
source-map-js: private
/svelte-hmr/0.15.1(svelte@3.59.1):
svelte-hmr: private
/vitefu/0.2.4(vite@4.3.8):
vitefu: private
included:
dependencies: true
devDependencies: true
optionalDependencies: true
injectedDeps: {}
layoutVersion: 5
nodeLinker: isolated
packageManager: pnpm@8.5.0
pendingBuilds: []
prunedAt: Sat, 20 May 2023 13:52:24 GMT
publicHoistPattern:
- '*eslint*'
- '*prettier*'
registries:
default: https://registry.npmjs.org/
skipped:
- /@esbuild/android-arm/0.17.19
- /@esbuild/android-arm64/0.17.19
- /@esbuild/android-x64/0.17.19
- /@esbuild/darwin-arm64/0.17.19
- /@esbuild/darwin-x64/0.17.19
- /@esbuild/freebsd-arm64/0.17.19
- /@esbuild/freebsd-x64/0.17.19
- /@esbuild/linux-arm/0.17.19
- /@esbuild/linux-arm64/0.17.19
- /@esbuild/linux-ia32/0.17.19
- /@esbuild/linux-loong64/0.17.19
- /@esbuild/linux-mips64el/0.17.19
- /@esbuild/linux-ppc64/0.17.19
- /@esbuild/linux-riscv64/0.17.19
- /@esbuild/linux-s390x/0.17.19
- /@esbuild/netbsd-x64/0.17.19
- /@esbuild/openbsd-x64/0.17.19
- /@esbuild/sunos-x64/0.17.19
- /@esbuild/win32-arm64/0.17.19
- /@esbuild/win32-ia32/0.17.19
- /@esbuild/win32-x64/0.17.19
- /@tauri-apps/cli-darwin-arm64/1.3.1
- /@tauri-apps/cli-darwin-x64/1.3.1
- /@tauri-apps/cli-linux-arm-gnueabihf/1.3.1
- /@tauri-apps/cli-linux-arm64-gnu/1.3.1
- /@tauri-apps/cli-linux-arm64-musl/1.3.1
- /@tauri-apps/cli-win32-ia32-msvc/1.3.1
- /@tauri-apps/cli-win32-x64-msvc/1.3.1
- /fsevents/2.3.2
storeDir: /home/yukil/.local/share/pnpm/store/v3
virtualStoreDir: .pnpm

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Khang Nguyen Duy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,71 @@
# 7.css
[![npm](https://img.shields.io/npm/v/7.css)](http://npm.im/7.css)
[![gzip size](https://img.shields.io/bundlephobia/minzip/7.css)](https://unpkg.com/7.css)
![A screenshot of a window with the title 'My First Program' and two buttons OK and Cancel, styled like a Windows 7 dialog](/docs/window.png)
**7.css** is a CSS framework that takes semantic HTML and styles them to the Windows 7 design.
It is built on top of [XP.css](https://github.com/botoxparty/XP.css), which is an extension of [98.CSS](https://github.com/jdan/98.css).
It does not ship with any JavaScript, so it is compatible with your frontend framework of choice.
## 📦 Installation / Usage
Directly via [unpkg](https://unpkg.com/):
```html
<!DOCTYPE html>
<html>
<head>
<title>7.css example</title>
<meta charset="UTF-8" />
<link rel="stylesheet" href="https://unpkg.com/7.css" />
</head>
<body>
<div class="window" style="margin: 32px; width: 250px">
<div class="title-bar">
<div class="title-bar-text">My First Program</div>
</div>
<div class="window-body">
<p>Hello, world!</p>
</div>
</div>
</body>
</html>
```
Via [npm](https://www.npmjs.com/package/7.css):
```sh
npm install 7.css
```
Then import it as below:
```javascript
import "7.css/dist/7.css";
```
## 📚 Documentation / Demo
Refer to the [documentation page](https://khang-nd.github.io/7.css/) for specific instructions on this framework's components.
## 🛠 Developing
Clone the repo and run `npm install`.
The core styles are managed in [`gui`](https://github.com/khang-nd/7.css/tree/main/gui).
You can use `npm start` to start a development environment that will watch for file changes and rebuild the files, reloading your browser in the process.
You can run a build manually with `npm run build`. This will write to the `dist/` directory.
## 📝 Issues, Contributing, etc.
You are so welcome to report issues, help out with contributions or whatever you could think of to improve this lovely UI framework.
## 📜 Changelog
Refer to [Releases](https://github.com/khang-nd/7.css/releases).

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1,2 @@
[role=tooltip]{background:linear-gradient(180deg,#fff,#ddd);border:1px solid rgba(0,0,0,.4);border-radius:3px;box-shadow:5px 5px 3px -3px rgba(0,0,0,.4);padding:1em 1em 1em 2.5em;position:relative}[role=tooltip]:before{background:url("data:image/svg+xml;charset=utf-8,%3Csvg width='20' height='20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M.5 1.5v18h18l-18-18Z' fill='%23fff' stroke='%23fff'/%3E%3Cpath d='M1 19.5H.5v-18l18 18H18' stroke='%23939393'/%3E%3C/svg%3E");content:"";height:18px;left:1em;position:absolute;top:-18px;width:18px}[role=tooltip][id]{position:absolute}[role=tooltip].is-top:before{background:url("data:image/svg+xml;charset=utf-8,%3Csvg width='20' height='20' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M.5 1.5v18h18l-18-18Z' fill='%23ddd' stroke='%23ddd'/%3E%3Cpath d='M1 19.5H.5v-18l18 18H18' stroke='%23939393'/%3E%3C/svg%3E");bottom:-18px;top:unset;transform:scale(-1)}[role=tooltip].is-top.is-right:before{transform:scaleY(-1)}[role=tooltip].is-left:before{left:unset;right:1em}[role=tooltip].is-left.is-bottom:before{transform:scaleX(-1)}
/*# sourceMappingURL=balloon.css.map */

@ -0,0 +1,2 @@
[role=button],button{background:linear-gradient(180deg,#eee 45%,#ddd 0,#bbb);border:1px solid #8e8f8f;border-radius:3px;box-shadow:inset 0 -1px 1px hsla(0,0%,100%,.8),inset 0 1px 1px #fff;box-sizing:border-box;color:#222;font:9pt Segoe UI,sans-serif;min-height:23px;min-width:75px;padding:0 12px;text-align:center}[role=button]:disabled,button:disabled{background:#f4f4f4;border-color:#aeb2b5;color:#838383}[role=button]:not(:disabled):hover,button:not(:disabled):hover{background:linear-gradient(180deg,#e5f4fd 45%,#b3e0f9 0);border-color:#3c7fb1}[role=button]:not(:disabled).active,[role=button]:not(:disabled):active,button:not(:disabled).active,button:not(:disabled):active{background:linear-gradient(180deg,#cee9f8 45%,#86c6e8 0);border-color:#6d91ab;box-shadow:none}[role=button].focused,[role=button]:focus,button.focused,button:focus{box-shadow:inset 0 0 0 2px #86c6e8;outline:1px dotted #000;outline-offset:-4px}[role=button].default,button.default{background:linear-gradient(180deg,#eee 45%,#c3dcea 0);border-color:#5586a3;box-shadow:inset 0 0 0 1px #34deff}
/*# sourceMappingURL=button.css.map */

@ -0,0 +1,2 @@
input[type=checkbox]{appearance:none;-webkit-appearance:none;-moz-appearance:none;background:none;border:none;font:9pt Segoe UI,sans-serif;margin:0;opacity:0}input[type=checkbox]+label{align-items:center;display:inline-flex;font:9pt Segoe UI,sans-serif;margin-left:0;position:relative}input[type=checkbox]+label:before{background:#f6f6f6;border:1px solid #8e8f8f;box-shadow:inset 0 0 0 1px #f4f4f4,inset 1px 1px 0 1px #aeaeae,inset -1px -1px 0 1px #ddd,inset 3px 3px 6px #ccc;box-sizing:border-box;content:"";display:inline-block;height:14px;margin-right:6px;transition:.4s;width:14px}input[type=checkbox]+label:hover:before{background:#e9f7fe;border-color:#3c7fb1;box-shadow:inset 0 0 0 1px #def9fa,inset 1px 1px 0 1px #79c6f9,inset -1px -1px 0 1px #c6e9fc,inset 3px 3px 6px #b1dffd}input[type=checkbox]:focus+label{outline:1px dotted #000}input[type=checkbox]:checked+label:after{color:#4a5f97;content:"\2714";display:block;font-weight:700;left:2px;position:absolute;top:0}input[type=checkbox]:disabled+label{opacity:.6}
/*# sourceMappingURL=checkbox.css.map */

@ -0,0 +1,2 @@
details{margin-top:0}details>summary{cursor:pointer;display:inline;margin-bottom:0;position:relative}details>summary:before{border:5px solid transparent;border-left-color:#000;border-radius:3px;content:"";position:absolute;right:100%;top:calc(50% - 5px)}details[open]>summary:before{top:calc(50% - 2.5px);transform:rotate(45deg)}
/*# sourceMappingURL=collapse.css.map */

@ -0,0 +1,2 @@
.combobox{display:inline-block;position:relative}.combobox input[type=text]{padding-right:20px;width:100%}.combobox button{border-bottom-left-radius:0;border-top-left-radius:0;min-width:16px;padding:0;position:absolute;right:0}.combobox button:before{border:4px solid transparent;border-radius:2px;border-top-color:#000;content:"";left:calc(50% - 4px);position:absolute;top:calc(50% - 1px)}.combobox button:focus{box-shadow:none;outline:none}
/*# sourceMappingURL=combobox.css.map */

@ -0,0 +1,2 @@
select:not([multiple]){-webkit-appearance:none;-moz-appearance:none;appearance:none;background:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTciIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3R5bGU9InN0b3AtY29sb3I6IzMzMztzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6I2FhYTtzdG9wLW9wYWNpdHk6MSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxwYXRoIGQ9Ik0xMSA2SDR2MWgxdjFoMXYxaDF2MWgxVjloMVY4aDFWN2gxVjZaIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+"),linear-gradient(180deg,#eee 45%,#ddd 0,#bbb);background-position:100%;background-repeat:no-repeat;border:1px solid #8e8f8f;border-radius:3px;box-shadow:inset 0 -1px 1px hsla(0,0%,100%,.8),inset 0 1px 1px #fff;box-sizing:border-box;color:#222;font:9pt Segoe UI,sans-serif;padding:2px 30px 2px 3px;position:relative}select:not([multiple]):hover{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTciIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3R5bGU9InN0b3AtY29sb3I6IzMzMztzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6I2FhYTtzdG9wLW9wYWNpdHk6MSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxwYXRoIGQ9Ik0xMSA2SDR2MWgxdjFoMXYxaDF2MWgxVjloMVY4aDFWN2gxVjZaIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+"),linear-gradient(180deg,#e5f4fd 45%,#b3e0f9 0);border-color:#3c7fb1}select:not([multiple]):focus{background-image:url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTciIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJhIiB4MT0iMCUiIHkxPSIwJSIgeDI9IjEwMCUiIHkyPSIwJSI+PHN0b3Agb2Zmc2V0PSIwJSIgc3R5bGU9InN0b3AtY29sb3I6IzMzMztzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6I2FhYTtzdG9wLW9wYWNpdHk6MSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxwYXRoIGQ9Ik0xMSA2SDR2MWgxdjFoMXYxaDF2MWgxVjloMVY4aDFWN2gxVjZaIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+"),linear-gradient(180deg,#cee9f8 45%,#86c6e8 0);border-color:#6d91ab;box-shadow:unset;outline:none}
/*# sourceMappingURL=dropdown.css.map */

File diff suppressed because one or more lines are too long

@ -0,0 +1,2 @@
fieldset{border:1px solid #cdd7db;border-radius:3px;box-shadow:inset 0 0 0 1px #fff;margin:0;padding:10px;-webkit-padding-before:8px;padding-block-start:8px}fieldset legend{font:9pt Segoe UI,sans-serif}.field-row{align-items:center;display:flex}.field-row>*+*{margin-left:6px}.field-row-stacked{display:flex;flex-direction:column}.field-row-stacked *+*,[class^=field-row]+[class^=field-row]{margin-top:6px}
/*# sourceMappingURL=groupbox.css.map */

@ -0,0 +1,2 @@
[role=listbox],select[multiple]{background:#fff;border:1px solid #c0c1cd;display:block;font:9pt Segoe UI,sans-serif;overflow-y:scroll}[role=listbox].has-shadow,select[multiple].has-shadow{box-shadow:4px 4px 3px -2px #999}[role=listbox].has-hover li:hover,select[multiple].has-hover li:hover{background-color:#2a90ff;color:#fff}[role=listbox]:focus,select[multiple]:focus{outline:none}[role=listbox] [role=option],[role=listbox] option,select[multiple] [role=option],select[multiple] option{padding:2px}[role=listbox] [role=option]:focus,[role=listbox] [role=option][aria-selected],[role=listbox] option:focus,[role=listbox] option[aria-selected],select[multiple] [role=option]:focus,select[multiple] [role=option][aria-selected],select[multiple] option:focus,select[multiple] option[aria-selected]{background-color:#2a90ff;color:#fff}
/*# sourceMappingURL=listbox.css.map */

@ -0,0 +1,2 @@
ul[role]{cursor:default;list-style:none;margin:0;padding:0}ul[role=menubar]{background:linear-gradient(#fff 20%,#f1f4fa 25%,#f1f4fa 43%,#d4dbee 48%,#e6eaf6);display:flex}ul[role=menubar]>[role=menuitem]{padding:6px 10px;position:relative}ul[role=menubar]>[role=menuitem]:focus,ul[role=menubar]>[role=menuitem]:focus-within,ul[role=menubar]>[role=menuitem]:hover{background:#39f;color:#fff;outline:none}ul[role=menu]{background:#f0f0f0;border:1px solid rgba(0,0,0,.4);box-shadow:4px 4px 3px -2px rgba(0,0,0,.5);color:initial;min-width:150px;padding:2px;position:relative}ul[role=menu]:before{box-shadow:inset 1px 0 rgba(0,0,0,.15),inset -1px 0 #fff;content:"";height:calc(100% - 4px);left:30px;pointer-events:none;position:absolute;width:2px}[role=menuitem] ul[role=menu]{display:none;left:0;position:absolute;top:100%;z-index:99}ul[role=menu] [role=menuitem]>[role=menu]{left:100%;top:-4px}ul[role=menu]>[role=menuitem]>a,ul[role=menu]>[role=menuitem]>button,ul[role=menu]>[role=menuitem]>label,ul[role=menu]>[role=menuitem][aria-haspopup=true]{all:unset;border:1px solid transparent;border-radius:3px;box-sizing:border-box;display:flex;justify-content:space-between;padding:4px 10px 4px 32px;position:relative;white-space:nowrap;width:100%}ul[role=menu]>[role=menuitem]>a:focus,ul[role=menu]>[role=menuitem]>a:hover,ul[role=menu]>[role=menuitem]>button:focus,ul[role=menu]>[role=menuitem]>button:hover,ul[role=menu]>[role=menuitem]>label:focus,ul[role=menu]>[role=menuitem]>label:hover,ul[role=menu]>[role=menuitem][aria-haspopup=true]:focus,ul[role=menu]>[role=menuitem][aria-haspopup=true]:hover{background:linear-gradient(180deg,hsla(0,0%,100%,.6),rgba(230,236,245,.8) 90%,hsla(0,0%,100%,.8));border-color:#b8d6fb}ul[role=menu]>[role=menuitem][aria-haspopup=true]:after{border:4px solid transparent;border-left-color:currentcolor;content:"";position:absolute;right:2px;top:50%;transform:translateY(-50%)}ul [role=menuitem]{position:relative}ul [role=menuitem]>input[type]{display:none}ul [role=menuitem]>input[type]+label{display:block;position:relative}ul [role=menuitem]>input[type]+label:before{all:unset;background:linear-gradient(180deg,hsla(0,0%,100%,.6),rgba(230,236,245,.8) 90%,hsla(0,0%,100%,.8));border-radius:inherit;box-shadow:0 0 0 1px #b3d3f9;box-sizing:border-box;height:22px;left:0;position:absolute;top:0;width:22px}ul [role=menuitem]>input[type]:checked+label:before{content:""}ul [role=menuitem]>input[type][type=radio]:checked+label:after{background:radial-gradient(circle at 75% 25%,#d5d4ea,#333583);border:1px solid #1a1490;box-shadow:none;left:8px;top:50%;transform:translateY(-50%)}ul [role=menuitem]>input[type][type=checkbox]:checked+label:after{color:#0c12a1;font-size:10pt;left:6px;top:50%;transform:translateY(-52%)}ul [role=menuitem]:focus-within>[role=menu],ul [role=menuitem]:focus>[role=menu]{display:block}ul [role=menuitem][aria-disabled]{opacity:.5;pointer-events:none}ul [role=menuitem].has-divider:after{box-shadow:inset 0 1px rgba(0,0,0,.15),inset 0 -1px #fff;content:"";display:block;height:2px;margin:3px 0 2px 30px;pointer-events:none}ul [role=menuitem] img{left:2px;pointer-events:none;position:absolute;top:50%;transform:translateY(-50%);z-index:1}ul [role=menuitem] span{margin-left:32px}ul.can-hover [role=menuitem]:hover>[role=menu]{display:block}
/*# sourceMappingURL=menu.css.map */

@ -0,0 +1,2 @@
[role=progressbar]{background:linear-gradient(90deg,rgba(0,0,0,.1),transparent 20%,transparent 80%,rgba(0,0,0,.1)),linear-gradient(180deg,hsla(0,0%,100%,.6) 25%,rgba(0,0,0,.05) 35%,rgba(0,0,0,.05) 90%,hsla(0,0%,100%,.2) 95%),#ddd;border:1px solid #8e8f8f;border-radius:3px;box-shadow:inset 0 0 2px #fff,0 0 2px #aaa;height:15px;margin:2px 0;overflow:hidden}[role=progressbar]>div{background-color:#0bd82c;background-image:linear-gradient(90deg,rgba(0,0,0,.2),transparent 20%,transparent 80%,rgba(0,0,0,.2)),linear-gradient(180deg,hsla(0,0%,100%,.6) 30%,rgba(0,0,0,.05) 0,rgba(0,0,0,.05) 90%,hsla(0,0%,100%,.2) 95%);box-shadow:inset 0 0 1px #fff;height:100%;overflow:hidden}[role=progressbar].paused>div{background-color:#e6df1b}[role=progressbar].error>div{background-color:#ef0000}[role=progressbar].animate>div:before,[role=progressbar].marquee:before{animation:progressbar 3s linear infinite;background:linear-gradient(90deg,transparent,hsla(0,0%,100%,.5),transparent 40%);content:"";display:block;height:100%}[role=progressbar].marquee:before{background:linear-gradient(90deg,transparent,#0bd82c,transparent 35%);opacity:.5}@keyframes progressbar{0%{transform:translateX(-40%)}60%{transform:translateX(100%)}to{transform:translateX(100%)}}
/*# sourceMappingURL=progressbar.css.map */

@ -0,0 +1,2 @@
input[type=radio]{appearance:none;-webkit-appearance:none;-moz-appearance:none;background:0;border:none;margin:0;opacity:0;position:fixed}input[type=radio]+label{align-items:center;display:inline-flex;font:9pt Segoe UI,sans-serif;margin-left:20px;position:relative}input[type=radio]+label:before{background:#f6f6f6;border:1px solid #8e8f8f;border-radius:50%;box-shadow:inset 0 0 0 1.5px #f4f4f4,inset 1px 1px 0 1.5px #aeaeae,inset -1px 0 0 1.5px #ddd,inset 3px 3px 6px #ccc;box-sizing:border-box;content:"";display:inline-block;height:14px;left:-20px;margin-right:6px;position:absolute;top:0;transition:.4s;width:14px}input[type=radio]+label:hover:before{border-color:#3c7fb1;box-shadow:inset 0 0 0 1.5px #def9fa,inset 1px 1px 0 1.5px #79c6f9,inset -1px -1px 0 1.5px #c6e9fc,inset 3px 3px 6px #b1dffd}input[type=radio]:checked+label:after{background:#7cd3eb;border:1.5px solid #27506d;border-radius:50%;box-shadow:inset -1px -1px 0 .5px #16638f,inset -1px -1px 0 1px #1985c0;box-sizing:border-box;content:"";display:block;height:8px;left:-17px;position:absolute;top:3px;width:8px}input[type=radio]:focus+label{outline:1px dotted #000}input[type=radio]:disabled+label{opacity:.6}
/*# sourceMappingURL=radiobutton.css.map */

@ -0,0 +1,2 @@
[type=search]{background-color:#fff;border:1px solid transparent;border-radius:2px;box-shadow:inset 1px 1px 0 #8e8f8f,inset -1px -1px 0 #ccc;box-sizing:border-box;font:9pt Segoe UI,sans-serif;height:24px;min-width:187px;padding:3px 6px}[type=search]:-moz-placeholder-shown{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggc3Ryb2tlPSIjMjA3MGI5IiBkPSJNMTAuNSAxQzguMDIgMSA2IDMuMDIgNiA1LjVhNC40NSA0LjQ1IDAgMCAwIDEgMi43OTNMMi4wMjMgMTMuMjdsLjcwNC43MUw3LjcwNyA5Yy43Ny42MTcgMS43MzQgMSAyLjc5MyAxIDIuNDggMCA0LjUtMi4wMiA0LjUtNC41UzEyLjk4IDEgMTAuNSAxWm0wIDFDMTIuNDM4IDIgMTQgMy41NjMgMTQgNS41IDE0IDcuNDM4IDEyLjQzNyA5IDEwLjUgOUEzLjQ5NCAzLjQ5NCAwIDAgMSA3IDUuNUM3IDMuNTYyIDguNTYzIDIgMTAuNSAyWiIvPjwvc3ZnPg==");background-position:calc(100% - 8px);background-repeat:no-repeat;background-size:14px}[type=search]:placeholder-shown{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggc3Ryb2tlPSIjMjA3MGI5IiBkPSJNMTAuNSAxQzguMDIgMSA2IDMuMDIgNiA1LjVhNC40NSA0LjQ1IDAgMCAwIDEgMi43OTNMMi4wMjMgMTMuMjdsLjcwNC43MUw3LjcwNyA5Yy43Ny42MTcgMS43MzQgMSAyLjc5MyAxIDIuNDggMCA0LjUtMi4wMiA0LjUtNC41UzEyLjk4IDEgMTAuNSAxWm0wIDFDMTIuNDM4IDIgMTQgMy41NjMgMTQgNS41IDE0IDcuNDM4IDEyLjQzNyA5IDEwLjUgOUEzLjQ5NCAzLjQ5NCAwIDAgMSA3IDUuNUM3IDMuNTYyIDguNTYzIDIgMTAuNSAyWiIvPjwvc3ZnPg==");background-position:calc(100% - 8px);background-repeat:no-repeat;background-size:14px}[type=search]:focus{outline:none}[type=search]::-moz-placeholder{font-style:italic}[type=search]::placeholder{font-style:italic}.searchbox [type=search]{padding-right:26px}.searchbox [type=search]+[aria-label=search]{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggc3Ryb2tlPSIjMjA3MGI5IiBkPSJNMTAuNSAxQzguMDIgMSA2IDMuMDIgNiA1LjVhNC40NSA0LjQ1IDAgMCAwIDEgMi43OTNMMi4wMjMgMTMuMjdsLjcwNC43MUw3LjcwNyA5Yy43Ny42MTcgMS43MzQgMSAyLjc5MyAxIDIuNDggMCA0LjUtMi4wMiA0LjUtNC41UzEyLjk4IDEgMTAuNSAxWm0wIDFDMTIuNDM4IDIgMTQgMy41NjMgMTQgNS41IDE0IDcuNDM4IDEyLjQzNyA5IDEwLjUgOUEzLjQ5NCAzLjQ5NCAwIDAgMSA3IDUuNUM3IDMuNTYyIDguNTYzIDIgMTAuNSAyWiIvPjwvc3ZnPg==") no-repeat 50%,linear-gradient(180deg,#eee 45%,#ddd 0,#bbb);background-size:14px;border-radius:0;min-height:22px;min-width:26px;padding:0;position:absolute;right:1px;top:1px}.searchbox [type=search]+[aria-label=search]:hover{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggc3Ryb2tlPSIjMjA3MGI5IiBkPSJNMTAuNSAxQzguMDIgMSA2IDMuMDIgNiA1LjVhNC40NSA0LjQ1IDAgMCAwIDEgMi43OTNMMi4wMjMgMTMuMjdsLjcwNC43MUw3LjcwNyA5Yy43Ny42MTcgMS43MzQgMSAyLjc5MyAxIDIuNDggMCA0LjUtMi4wMiA0LjUtNC41UzEyLjk4IDEgMTAuNSAxWm0wIDFDMTIuNDM4IDIgMTQgMy41NjMgMTQgNS41IDE0IDcuNDM4IDEyLjQzNyA5IDEwLjUgOUEzLjQ5NCAzLjQ5NCAwIDAgMSA3IDUuNUM3IDMuNTYyIDguNTYzIDIgMTAuNSAyWiIvPjwvc3ZnPg==") no-repeat 50%,linear-gradient(180deg,#e5f4fd 45%,#b3e0f9 0);background-size:14px}.searchbox [type=search]+[aria-label=search]:active{background:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiI+PHBhdGggc3Ryb2tlPSIjMjA3MGI5IiBkPSJNMTAuNSAxQzguMDIgMSA2IDMuMDIgNiA1LjVhNC40NSA0LjQ1IDAgMCAwIDEgMi43OTNMMi4wMjMgMTMuMjdsLjcwNC43MUw3LjcwNyA5Yy43Ny42MTcgMS43MzQgMSAyLjc5MyAxIDIuNDggMCA0LjUtMi4wMiA0LjUtNC41UzEyLjk4IDEgMTAuNSAxWm0wIDFDMTIuNDM4IDIgMTQgMy41NjMgMTQgNS41IDE0IDcuNDM4IDEyLjQzNyA5IDEwLjUgOUEzLjQ5NCAzLjQ5NCAwIDAgMSA3IDUuNUM3IDMuNTYyIDguNTYzIDIgMTAuNSAyWiIvPjwvc3ZnPg==") no-repeat 50%,linear-gradient(180deg,#cee9f8 45%,#86c6e8 0);background-size:14px;box-shadow:inset 1px 1px 2px #37698f}.searchbox{display:inline-block;position:relative}
/*# sourceMappingURL=searchbox.css.map */

@ -0,0 +1,2 @@
input[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;background:transparent;width:100%}input[type=range]:focus{outline:none}input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAASCAYAAABit09LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAC7SURBVHgBlZLBCYQwEEVj9CDYgRfbswF7sRAbsArPdhBICDmEzPoDE9CdXeKH0eHP48fINOrSPM+k/mhd16YDtCyLmqZJhM7zxIs6PAF570WQAzJIRLkksd89DUl939eB1Ym3b0wpiQBmIYSXiTFGZYwRgWEY6o8uIPQLZGlu2rYtP54L3g3c912N45gHSEahh4dZERZj2zZyztFxHLnQw/vaLIattbmeULkMdg6XxLFaa3WB7MlCirTIHxVUkxicbwSEAAAAAElFTkSuQmCC");height:18px;transform:translateY(-7px);width:10px}input[type=range]::-webkit-slider-thumb:active{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAASCAYAAABit09LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACsSURBVHgB3ZKxCoMwEIYvJWOn0r10LHTuVDp1KIU+RN+gT9FZ6AP0LYqDY+nQWXAScXMQdRYh+ouRqEF0cPGHHJfcl7sbfkaFroYlqEefx5lxQK/blu6rjRZ6R34RLcFxAWSkmRZEbUkeLWigZgVymZhuoid264lGjwMvTkhmtYsqvKEGMQQY43jYNwoy//7t0j31b8DPXyBOcVYe5Kr9mDpKdoY6ndoCrDNyDnRZRNbxQWFyAAAAAElFTkSuQmCC")}input[type=range]::-moz-range-thumb{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAASCAYAAABit09LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAC7SURBVHgBlZLBCYQwEEVj9CDYgRfbswF7sRAbsArPdhBICDmEzPoDE9CdXeKH0eHP48fINOrSPM+k/mhd16YDtCyLmqZJhM7zxIs6PAF570WQAzJIRLkksd89DUl939eB1Ym3b0wpiQBmIYSXiTFGZYwRgWEY6o8uIPQLZGlu2rYtP54L3g3c912N45gHSEahh4dZERZj2zZyztFxHLnQw/vaLIattbmeULkMdg6XxLFaa3WB7MlCirTIHxVUkxicbwSEAAAAAElFTkSuQmCC");border:0;border-radius:0;height:18px;width:10px}input[type=range]::-moz-range-thumb:active{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAASCAYAAABit09LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACsSURBVHgB3ZKxCoMwEIYvJWOn0r10LHTuVDp1KIU+RN+gT9FZ6AP0LYqDY+nQWXAScXMQdRYh+ouRqEF0cPGHHJfcl7sbfkaFroYlqEefx5lxQK/blu6rjRZ6R34RLcFxAWSkmRZEbUkeLWigZgVymZhuoid264lGjwMvTkhmtYsqvKEGMQQY43jYNwoy//7t0j31b8DPXyBOcVYe5Kr9mDpKdoY6ndoCrDNyDnRZRNbxQWFyAAAAAElFTkSuQmCC")}input[type=range]::-webkit-slider-runnable-track{background:#f0f0f0;box-shadow:inset 1px 1px 1px #999,inset -1px 0 #999,0 1px #fff;box-sizing:border-box;height:3px;width:100%}input[type=range]::-moz-range-track{background:#f0f0f0;box-shadow:inset 1px 1px 1px #999,inset -1px 0 #999,0 1px #fff;box-sizing:border-box;height:3px;width:100%}input[type=range].has-box-indicator::-webkit-slider-thumb{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAASCAYAAABit09LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAABrSURBVHgB7dLNCcAgDAXgWASjm9TJ3Egns8N4SGv6c2gbyQA+kED40IPPwJGUEsEgpRRjO8o5i6i11gfZe7HVCojIJ4QA6D3vnXM8F1DmBUkL1TdOOIRGC2X7hcIvPu1ZY/wFV83OhzTF3QGRrxiDB3GCSQAAAABJRU5ErkJggg==")}input[type=range].has-box-indicator::-webkit-slider-thumb:active{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAASCAYAAABit09LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAABmSURBVHgB7dKxCYAwEAXQfyEKYhrBThdwCwsLyRKukFGcRiyyh0tY2kcT0fJIn/zijg+v/IQnerUOTDYzkfToWkbOQcM6+ZX5OMNvVIVeFehqEbopXyIQmQzTgf969qFlIfkTM9wbGH4SS8YdBX4AAAAASUVORK5CYII=")}input[type=range].has-box-indicator::-moz-range-thumb{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAASCAYAAABit09LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAABrSURBVHgB7dLNCcAgDAXgWASjm9TJ3Egns8N4SGv6c2gbyQA+kED40IPPwJGUEsEgpRRjO8o5i6i11gfZe7HVCojIJ4QA6D3vnXM8F1DmBUkL1TdOOIRGC2X7hcIvPu1ZY/wFV83OhzTF3QGRrxiDB3GCSQAAAABJRU5ErkJggg==")}input[type=range].has-box-indicator::-moz-range-thumb:active{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAASCAYAAABit09LAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAABmSURBVHgB7dKxCYAwEAXQfyEKYhrBThdwCwsLyRKukFGcRiyyh0tY2kcT0fJIn/zijg+v/IQnerUOTDYzkfToWkbOQcM6+ZX5OMNvVIVeFehqEbopXyIQmQzTgf969qFlIfkTM9wbGH4SS8YdBX4AAAAASUVORK5CYII=")}.is-vertical{display:inline-block;height:150px;transform:translateY(50%);width:4px}.is-vertical>input[type=range]{height:4px;margin:undefined;transform:rotate(270deg) translateX(calc(-50% + 8px));transform-origin:left;width:150px}.is-vertical>input[type=range]::-webkit-slider-thumb{transform:translateY(-8px) scaleX(-1)}.is-vertical>input[type=range]::-moz-range-thumb{transform:translateY(2px) scaleX(-1)}.is-vertical>input[type=range].has-box-indicator::-webkit-slider-thumb{transform:translateY(-9px) scaleX(-1)}.is-vertical>input[type=range].has-box-indicator::-moz-range-thumb{transform:translateY(0) scaleX(-1)}
/*# sourceMappingURL=slider.css.map */

File diff suppressed because one or more lines are too long

@ -0,0 +1,2 @@
menu[role=tablist]{display:flex;list-style-type:none;margin:0 0 -2px;padding-left:3px;position:relative;text-indent:0}menu[role=tablist] button{border-radius:0;color:#222;display:block;min-width:unset;padding:2px 6px;text-decoration:none;z-index:1}menu[role=tablist] button[aria-selected=true]{background:#fff;border-bottom:0;box-shadow:none;margin:-2px 0 1px -3px;padding-bottom:4px;position:relative;z-index:8}menu[role=tablist] button:focus{outline:1px dotted #222;outline-offset:-4px}menu[role=tablist] button:disabled{opacity:.6}menu[role=tablist].justified button{flex-grow:1;text-align:center}[role=tabpanel]{background:#fff;border:1px solid #888;clear:both;margin-bottom:9px;padding:14px;position:relative;z-index:2}
/*# sourceMappingURL=tabs.css.map */

@ -0,0 +1,2 @@
input[type=email],input[type=number],input[type=password],input[type=text]{height:23px}input[type=email],input[type=number],input[type=password],input[type=text],textarea{background-color:#fff;border:1px solid #ccc;border-radius:2px;border-top-color:#8e8f8f;box-sizing:border-box;font:9pt Segoe UI,sans-serif;padding:3px 4px 5px}input[type=email]:focus,input[type=number]:focus,input[type=password]:focus,input[type=text]:focus,textarea:focus{border-color:#86c6e8 #b3e0f9 #b3e0f9;outline:none}
/*# sourceMappingURL=textbox.css.map */

@ -0,0 +1,2 @@
ul.tree-view{display:block;font:9pt Segoe UI,sans-serif;margin:0;padding:6px 6px 6px 20px}ul.tree-view li{list-style-type:none;margin-top:4px;position:relative}ul.tree-view a{color:#000;text-decoration:none}ul.tree-view ul{margin-top:4px;padding-left:20px}ul.tree-view.has-container{background:#fff;border:1px solid #8e8f8f}ul.tree-view.has-collapse-button details>summary:before{background:linear-gradient(180deg,#eee 45%,#ddd);border:1px solid #919191;border-radius:1px;color:#4b63a7;content:"\002b";font-size:8pt;font-weight:700;height:8px;left:-16px;line-height:.5;margin:0;right:unset;text-align:center;top:calc(50% - 4px);width:8px}ul.tree-view.has-collapse-button details[open]>summary:before{content:"\2013";transform:none}ul.tree-view.has-connector ul{position:relative}ul.tree-view.has-connector ul:before{border-left:1px dotted #000;content:"";height:calc(100% - 8px);left:8px;position:absolute;top:0}ul.tree-view.has-connector ul li:before{border-bottom:1px dotted #000;content:"";position:absolute;right:calc(100% + 2px);top:8px;width:10px}
/*# sourceMappingURL=treeview.css.map */

@ -0,0 +1,2 @@
a{color:#06c;text-decoration:none}a:focus{outline:1px dotted #06c}a:hover{color:#39f;text-decoration:underline}.instruction{color:#000;font:9pt Segoe UI,sans-serif;font-weight:400;margin:0 0 20px}.instruction-primary{color:#039;font-size:12pt}.header{font:9pt Segoe UI,sans-serif;font-weight:400}.header-document{color:#000;font-family:Calibri;font-size:17pt}.header-group{color:#039;font-size:11pt}
/*# sourceMappingURL=typography.css.map */

File diff suppressed because one or more lines are too long

@ -0,0 +1,52 @@
{
"name": "7.css",
"version": "0.13.0",
"description": "A design system for building faithful recreations of the Windows 7 UI.",
"main": "dist/7.css",
"files": [
"dist/gui",
"dist/7*"
],
"scripts": {
"build": "node build.js",
"deploy": "npm run build && gh-pages -d dist",
"release": "npm run build && npm publish"
},
"repository": {
"type": "git",
"url": "git+https://github.com/khang-nd/7.css.git"
},
"keywords": [
"css",
"scss",
"windows",
"windows7",
"gui",
"ui-design"
],
"author": "khang-nd",
"license": "MIT",
"bugs": {
"url": "https://github.com/khang-nd/7.css/issues"
},
"homepage": "https://khang-nd.github.io/7.css",
"devDependencies": {
"autoprefixer": "^10.4.8",
"chokidar": "^3.3.1",
"cssnano": "^5.1.7",
"dedent": "^0.7.0",
"ejs": "^3.0.2",
"gh-pages": "^2.2.0",
"glob": "^7.1.6",
"highlight.js": ">=10.4.1",
"live-server": "^1.1.0",
"mkdirp": "^1.0.4",
"postcss": "^8.3.5",
"postcss-base64": "^0.7.1",
"postcss-calc": "^7.0.2",
"postcss-css-variables": "^0.14.0",
"postcss-import": "^12.0.1",
"postcss-nested": "^4.2.1",
"postcss-prefix-selector": "^1.15.0"
}
}

@ -0,0 +1,3 @@
# esbuild
This is the Linux 64-bit binary for esbuild, a JavaScript bundler and minifier. See https://github.com/evanw/esbuild for details.

@ -0,0 +1,17 @@
{
"name": "@esbuild/linux-x64",
"version": "0.17.19",
"description": "The Linux 64-bit binary for esbuild, a JavaScript bundler.",
"repository": "https://github.com/evanw/esbuild",
"license": "MIT",
"preferUnplugged": true,
"engines": {
"node": ">=12"
},
"os": [
"linux"
],
"cpu": [
"x64"
]
}

@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2015 Rich Harris
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

@ -0,0 +1,200 @@
# @jridgewell/sourcemap-codec
Encode/decode the `mappings` property of a [sourcemap](https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit).
## Why?
Sourcemaps are difficult to generate and manipulate, because the `mappings` property the part that actually links the generated code back to the original source is encoded using an obscure method called [Variable-length quantity](https://en.wikipedia.org/wiki/Variable-length_quantity). On top of that, each segment in the mapping contains offsets rather than absolute indices, which means that you can't look at a segment in isolation you have to understand the whole sourcemap.
This package makes the process slightly easier.
## Installation
```bash
npm install @jridgewell/sourcemap-codec
```
## Usage
```js
import { encode, decode } from '@jridgewell/sourcemap-codec';
var decoded = decode( ';EAEEA,EAAE,EAAC,CAAE;ECQY,UACC' );
assert.deepEqual( decoded, [
// the first line (of the generated code) has no mappings,
// as shown by the starting semi-colon (which separates lines)
[],
// the second line contains four (comma-separated) segments
[
// segments are encoded as you'd expect:
// [ generatedCodeColumn, sourceIndex, sourceCodeLine, sourceCodeColumn, nameIndex ]
// i.e. the first segment begins at column 2, and maps back to the second column
// of the second line (both zero-based) of the 0th source, and uses the 0th
// name in the `map.names` array
[ 2, 0, 2, 2, 0 ],
// the remaining segments are 4-length rather than 5-length,
// because they don't map a name
[ 4, 0, 2, 4 ],
[ 6, 0, 2, 5 ],
[ 7, 0, 2, 7 ]
],
// the final line contains two segments
[
[ 2, 1, 10, 19 ],
[ 12, 1, 11, 20 ]
]
]);
var encoded = encode( decoded );
assert.equal( encoded, ';EAEEA,EAAE,EAAC,CAAE;ECQY,UACC' );
```
## Benchmarks
```
node v18.0.0
amp.js.map - 45120 segments
Decode Memory Usage:
@jridgewell/sourcemap-codec 5479160 bytes
sourcemap-codec 5659336 bytes
source-map-0.6.1 17144440 bytes
source-map-0.8.0 6867424 bytes
Smallest memory usage is @jridgewell/sourcemap-codec
Decode speed:
decode: @jridgewell/sourcemap-codec x 502 ops/sec ±1.03% (90 runs sampled)
decode: sourcemap-codec x 445 ops/sec ±0.97% (92 runs sampled)
decode: source-map-0.6.1 x 36.01 ops/sec ±1.64% (49 runs sampled)
decode: source-map-0.8.0 x 367 ops/sec ±0.04% (95 runs sampled)
Fastest is decode: @jridgewell/sourcemap-codec
Encode Memory Usage:
@jridgewell/sourcemap-codec 1261620 bytes
sourcemap-codec 9119248 bytes
source-map-0.6.1 8968560 bytes
source-map-0.8.0 8952952 bytes
Smallest memory usage is @jridgewell/sourcemap-codec
Encode speed:
encode: @jridgewell/sourcemap-codec x 738 ops/sec ±0.42% (98 runs sampled)
encode: sourcemap-codec x 238 ops/sec ±0.73% (88 runs sampled)
encode: source-map-0.6.1 x 162 ops/sec ±0.43% (84 runs sampled)
encode: source-map-0.8.0 x 191 ops/sec ±0.34% (90 runs sampled)
Fastest is encode: @jridgewell/sourcemap-codec
***
babel.min.js.map - 347793 segments
Decode Memory Usage:
@jridgewell/sourcemap-codec 35338184 bytes
sourcemap-codec 35922736 bytes
source-map-0.6.1 62366360 bytes
source-map-0.8.0 44337416 bytes
Smallest memory usage is @jridgewell/sourcemap-codec
Decode speed:
decode: @jridgewell/sourcemap-codec x 40.35 ops/sec ±4.47% (54 runs sampled)
decode: sourcemap-codec x 36.76 ops/sec ±3.67% (51 runs sampled)
decode: source-map-0.6.1 x 4.44 ops/sec ±2.15% (16 runs sampled)
decode: source-map-0.8.0 x 59.35 ops/sec ±0.05% (78 runs sampled)
Fastest is decode: source-map-0.8.0
Encode Memory Usage:
@jridgewell/sourcemap-codec 7212604 bytes
sourcemap-codec 21421456 bytes
source-map-0.6.1 25286888 bytes
source-map-0.8.0 25498744 bytes
Smallest memory usage is @jridgewell/sourcemap-codec
Encode speed:
encode: @jridgewell/sourcemap-codec x 112 ops/sec ±0.13% (84 runs sampled)
encode: sourcemap-codec x 30.23 ops/sec ±2.76% (53 runs sampled)
encode: source-map-0.6.1 x 19.43 ops/sec ±3.70% (37 runs sampled)
encode: source-map-0.8.0 x 19.40 ops/sec ±3.26% (37 runs sampled)
Fastest is encode: @jridgewell/sourcemap-codec
***
preact.js.map - 1992 segments
Decode Memory Usage:
@jridgewell/sourcemap-codec 500272 bytes
sourcemap-codec 516864 bytes
source-map-0.6.1 1596672 bytes
source-map-0.8.0 517272 bytes
Smallest memory usage is @jridgewell/sourcemap-codec
Decode speed:
decode: @jridgewell/sourcemap-codec x 16,137 ops/sec ±0.17% (99 runs sampled)
decode: sourcemap-codec x 12,139 ops/sec ±0.13% (99 runs sampled)
decode: source-map-0.6.1 x 1,264 ops/sec ±0.12% (100 runs sampled)
decode: source-map-0.8.0 x 9,894 ops/sec ±0.08% (101 runs sampled)
Fastest is decode: @jridgewell/sourcemap-codec
Encode Memory Usage:
@jridgewell/sourcemap-codec 321026 bytes
sourcemap-codec 830832 bytes
source-map-0.6.1 586608 bytes
source-map-0.8.0 586680 bytes
Smallest memory usage is @jridgewell/sourcemap-codec
Encode speed:
encode: @jridgewell/sourcemap-codec x 19,876 ops/sec ±0.78% (95 runs sampled)
encode: sourcemap-codec x 6,983 ops/sec ±0.15% (100 runs sampled)
encode: source-map-0.6.1 x 5,070 ops/sec ±0.12% (102 runs sampled)
encode: source-map-0.8.0 x 5,641 ops/sec ±0.17% (100 runs sampled)
Fastest is encode: @jridgewell/sourcemap-codec
***
react.js.map - 5726 segments
Decode Memory Usage:
@jridgewell/sourcemap-codec 734848 bytes
sourcemap-codec 954200 bytes
source-map-0.6.1 2276432 bytes
source-map-0.8.0 955488 bytes
Smallest memory usage is @jridgewell/sourcemap-codec
Decode speed:
decode: @jridgewell/sourcemap-codec x 5,723 ops/sec ±0.12% (98 runs sampled)
decode: sourcemap-codec x 4,555 ops/sec ±0.09% (101 runs sampled)
decode: source-map-0.6.1 x 437 ops/sec ±0.11% (93 runs sampled)
decode: source-map-0.8.0 x 3,441 ops/sec ±0.15% (100 runs sampled)
Fastest is decode: @jridgewell/sourcemap-codec
Encode Memory Usage:
@jridgewell/sourcemap-codec 638672 bytes
sourcemap-codec 1109840 bytes
source-map-0.6.1 1321224 bytes
source-map-0.8.0 1324448 bytes
Smallest memory usage is @jridgewell/sourcemap-codec
Encode speed:
encode: @jridgewell/sourcemap-codec x 6,801 ops/sec ±0.48% (98 runs sampled)
encode: sourcemap-codec x 2,533 ops/sec ±0.13% (101 runs sampled)
encode: source-map-0.6.1 x 2,248 ops/sec ±0.08% (100 runs sampled)
encode: source-map-0.8.0 x 2,303 ops/sec ±0.15% (100 runs sampled)
Fastest is encode: @jridgewell/sourcemap-codec
```
# License
MIT

@ -0,0 +1,164 @@
const comma = ','.charCodeAt(0);
const semicolon = ';'.charCodeAt(0);
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const intToChar = new Uint8Array(64); // 64 possible chars.
const charToInt = new Uint8Array(128); // z is 122 in ASCII
for (let i = 0; i < chars.length; i++) {
const c = chars.charCodeAt(i);
intToChar[i] = c;
charToInt[c] = i;
}
// Provide a fallback for older environments.
const td = typeof TextDecoder !== 'undefined'
? /* #__PURE__ */ new TextDecoder()
: typeof Buffer !== 'undefined'
? {
decode(buf) {
const out = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);
return out.toString();
},
}
: {
decode(buf) {
let out = '';
for (let i = 0; i < buf.length; i++) {
out += String.fromCharCode(buf[i]);
}
return out;
},
};
function decode(mappings) {
const state = new Int32Array(5);
const decoded = [];
let index = 0;
do {
const semi = indexOf(mappings, index);
const line = [];
let sorted = true;
let lastCol = 0;
state[0] = 0;
for (let i = index; i < semi; i++) {
let seg;
i = decodeInteger(mappings, i, state, 0); // genColumn
const col = state[0];
if (col < lastCol)
sorted = false;
lastCol = col;
if (hasMoreVlq(mappings, i, semi)) {
i = decodeInteger(mappings, i, state, 1); // sourcesIndex
i = decodeInteger(mappings, i, state, 2); // sourceLine
i = decodeInteger(mappings, i, state, 3); // sourceColumn
if (hasMoreVlq(mappings, i, semi)) {
i = decodeInteger(mappings, i, state, 4); // namesIndex
seg = [col, state[1], state[2], state[3], state[4]];
}
else {
seg = [col, state[1], state[2], state[3]];
}
}
else {
seg = [col];
}
line.push(seg);
}
if (!sorted)
sort(line);
decoded.push(line);
index = semi + 1;
} while (index <= mappings.length);
return decoded;
}
function indexOf(mappings, index) {
const idx = mappings.indexOf(';', index);
return idx === -1 ? mappings.length : idx;
}
function decodeInteger(mappings, pos, state, j) {
let value = 0;
let shift = 0;
let integer = 0;
do {
const c = mappings.charCodeAt(pos++);
integer = charToInt[c];
value |= (integer & 31) << shift;
shift += 5;
} while (integer & 32);
const shouldNegate = value & 1;
value >>>= 1;
if (shouldNegate) {
value = -0x80000000 | -value;
}
state[j] += value;
return pos;
}
function hasMoreVlq(mappings, i, length) {
if (i >= length)
return false;
return mappings.charCodeAt(i) !== comma;
}
function sort(line) {
line.sort(sortComparator);
}
function sortComparator(a, b) {
return a[0] - b[0];
}
function encode(decoded) {
const state = new Int32Array(5);
const bufLength = 1024 * 16;
const subLength = bufLength - 36;
const buf = new Uint8Array(bufLength);
const sub = buf.subarray(0, subLength);
let pos = 0;
let out = '';
for (let i = 0; i < decoded.length; i++) {
const line = decoded[i];
if (i > 0) {
if (pos === bufLength) {
out += td.decode(buf);
pos = 0;
}
buf[pos++] = semicolon;
}
if (line.length === 0)
continue;
state[0] = 0;
for (let j = 0; j < line.length; j++) {
const segment = line[j];
// We can push up to 5 ints, each int can take at most 7 chars, and we
// may push a comma.
if (pos > subLength) {
out += td.decode(sub);
buf.copyWithin(0, subLength, pos);
pos -= subLength;
}
if (j > 0)
buf[pos++] = comma;
pos = encodeInteger(buf, pos, state, segment, 0); // genColumn
if (segment.length === 1)
continue;
pos = encodeInteger(buf, pos, state, segment, 1); // sourcesIndex
pos = encodeInteger(buf, pos, state, segment, 2); // sourceLine
pos = encodeInteger(buf, pos, state, segment, 3); // sourceColumn
if (segment.length === 4)
continue;
pos = encodeInteger(buf, pos, state, segment, 4); // namesIndex
}
}
return out + td.decode(buf.subarray(0, pos));
}
function encodeInteger(buf, pos, state, segment, j) {
const next = segment[j];
let num = next - state[j];
state[j] = next;
num = num < 0 ? (-num << 1) | 1 : num << 1;
do {
let clamped = num & 0b011111;
num >>>= 5;
if (num > 0)
clamped |= 0b100000;
buf[pos++] = intToChar[clamped];
} while (num > 0);
return pos;
}
export { decode, encode };
//# sourceMappingURL=sourcemap-codec.mjs.map

@ -0,0 +1,175 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
typeof define === 'function' && define.amd ? define(['exports'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.sourcemapCodec = {}));
})(this, (function (exports) { 'use strict';
const comma = ','.charCodeAt(0);
const semicolon = ';'.charCodeAt(0);
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
const intToChar = new Uint8Array(64); // 64 possible chars.
const charToInt = new Uint8Array(128); // z is 122 in ASCII
for (let i = 0; i < chars.length; i++) {
const c = chars.charCodeAt(i);
intToChar[i] = c;
charToInt[c] = i;
}
// Provide a fallback for older environments.
const td = typeof TextDecoder !== 'undefined'
? /* #__PURE__ */ new TextDecoder()
: typeof Buffer !== 'undefined'
? {
decode(buf) {
const out = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength);
return out.toString();
},
}
: {
decode(buf) {
let out = '';
for (let i = 0; i < buf.length; i++) {
out += String.fromCharCode(buf[i]);
}
return out;
},
};
function decode(mappings) {
const state = new Int32Array(5);
const decoded = [];
let index = 0;
do {
const semi = indexOf(mappings, index);
const line = [];
let sorted = true;
let lastCol = 0;
state[0] = 0;
for (let i = index; i < semi; i++) {
let seg;
i = decodeInteger(mappings, i, state, 0); // genColumn
const col = state[0];
if (col < lastCol)
sorted = false;
lastCol = col;
if (hasMoreVlq(mappings, i, semi)) {
i = decodeInteger(mappings, i, state, 1); // sourcesIndex
i = decodeInteger(mappings, i, state, 2); // sourceLine
i = decodeInteger(mappings, i, state, 3); // sourceColumn
if (hasMoreVlq(mappings, i, semi)) {
i = decodeInteger(mappings, i, state, 4); // namesIndex
seg = [col, state[1], state[2], state[3], state[4]];
}
else {
seg = [col, state[1], state[2], state[3]];
}
}
else {
seg = [col];
}
line.push(seg);
}
if (!sorted)
sort(line);
decoded.push(line);
index = semi + 1;
} while (index <= mappings.length);
return decoded;
}
function indexOf(mappings, index) {
const idx = mappings.indexOf(';', index);
return idx === -1 ? mappings.length : idx;
}
function decodeInteger(mappings, pos, state, j) {
let value = 0;
let shift = 0;
let integer = 0;
do {
const c = mappings.charCodeAt(pos++);
integer = charToInt[c];
value |= (integer & 31) << shift;
shift += 5;
} while (integer & 32);
const shouldNegate = value & 1;
value >>>= 1;
if (shouldNegate) {
value = -0x80000000 | -value;
}
state[j] += value;
return pos;
}
function hasMoreVlq(mappings, i, length) {
if (i >= length)
return false;
return mappings.charCodeAt(i) !== comma;
}
function sort(line) {
line.sort(sortComparator);
}
function sortComparator(a, b) {
return a[0] - b[0];
}
function encode(decoded) {
const state = new Int32Array(5);
const bufLength = 1024 * 16;
const subLength = bufLength - 36;
const buf = new Uint8Array(bufLength);
const sub = buf.subarray(0, subLength);
let pos = 0;
let out = '';
for (let i = 0; i < decoded.length; i++) {
const line = decoded[i];
if (i > 0) {
if (pos === bufLength) {
out += td.decode(buf);
pos = 0;
}
buf[pos++] = semicolon;
}
if (line.length === 0)
continue;
state[0] = 0;
for (let j = 0; j < line.length; j++) {
const segment = line[j];
// We can push up to 5 ints, each int can take at most 7 chars, and we
// may push a comma.
if (pos > subLength) {
out += td.decode(sub);
buf.copyWithin(0, subLength, pos);
pos -= subLength;
}
if (j > 0)
buf[pos++] = comma;
pos = encodeInteger(buf, pos, state, segment, 0); // genColumn
if (segment.length === 1)
continue;
pos = encodeInteger(buf, pos, state, segment, 1); // sourcesIndex
pos = encodeInteger(buf, pos, state, segment, 2); // sourceLine
pos = encodeInteger(buf, pos, state, segment, 3); // sourceColumn
if (segment.length === 4)
continue;
pos = encodeInteger(buf, pos, state, segment, 4); // namesIndex
}
}
return out + td.decode(buf.subarray(0, pos));
}
function encodeInteger(buf, pos, state, segment, j) {
const next = segment[j];
let num = next - state[j];
state[j] = next;
num = num < 0 ? (-num << 1) | 1 : num << 1;
do {
let clamped = num & 0b011111;
num >>>= 5;
if (num > 0)
clamped |= 0b100000;
buf[pos++] = intToChar[clamped];
} while (num > 0);
return pos;
}
exports.decode = decode;
exports.encode = encode;
Object.defineProperty(exports, '__esModule', { value: true });
}));
//# sourceMappingURL=sourcemap-codec.umd.js.map

@ -0,0 +1,6 @@
export declare type SourceMapSegment = [number] | [number, number, number, number] | [number, number, number, number, number];
export declare type SourceMapLine = SourceMapSegment[];
export declare type SourceMapMappings = SourceMapLine[];
export declare function decode(mappings: string): SourceMapMappings;
export declare function encode(decoded: SourceMapMappings): string;
export declare function encode(decoded: Readonly<SourceMapMappings>): string;

@ -0,0 +1,74 @@
{
"name": "@jridgewell/sourcemap-codec",
"version": "1.4.15",
"description": "Encode/decode sourcemap mappings",
"keywords": [
"sourcemap",
"vlq"
],
"main": "dist/sourcemap-codec.umd.js",
"module": "dist/sourcemap-codec.mjs",
"types": "dist/types/sourcemap-codec.d.ts",
"files": [
"dist"
],
"exports": {
".": [
{
"types": "./dist/types/sourcemap-codec.d.ts",
"browser": "./dist/sourcemap-codec.umd.js",
"require": "./dist/sourcemap-codec.umd.js",
"import": "./dist/sourcemap-codec.mjs"
},
"./dist/sourcemap-codec.umd.js"
],
"./package.json": "./package.json"
},
"scripts": {
"benchmark": "run-s build:rollup benchmark:*",
"benchmark:install": "cd benchmark && npm install",
"benchmark:only": "node --expose-gc benchmark/index.js",
"build": "run-s -n build:*",
"build:rollup": "rollup -c rollup.config.js",
"build:ts": "tsc --project tsconfig.build.json",
"lint": "run-s -n lint:*",
"lint:prettier": "npm run test:lint:prettier -- --write",
"lint:ts": "npm run test:lint:ts -- --fix",
"prebuild": "rm -rf dist",
"prepublishOnly": "npm run preversion",
"preversion": "run-s test build",
"pretest": "run-s build:rollup",
"test": "run-s -n test:lint test:only",
"test:debug": "mocha --inspect-brk",
"test:lint": "run-s -n test:lint:*",
"test:lint:prettier": "prettier --check '{src,test}/**/*.ts'",
"test:lint:ts": "eslint '{src,test}/**/*.ts'",
"test:only": "mocha",
"test:coverage": "c8 mocha",
"test:watch": "mocha --watch"
},
"repository": {
"type": "git",
"url": "git+https://github.com/jridgewell/sourcemap-codec.git"
},
"author": "Rich Harris",
"license": "MIT",
"devDependencies": {
"@rollup/plugin-typescript": "8.3.0",
"@types/node": "17.0.15",
"@typescript-eslint/eslint-plugin": "5.10.0",
"@typescript-eslint/parser": "5.10.0",
"benchmark": "2.1.4",
"c8": "7.11.2",
"eslint": "8.7.0",
"eslint-config-prettier": "8.3.0",
"mocha": "9.2.0",
"npm-run-all": "4.1.5",
"prettier": "2.5.1",
"rollup": "2.64.0",
"source-map": "0.6.1",
"source-map-js": "1.0.2",
"sourcemap-codec": "1.4.8",
"typescript": "4.5.4"
}
}

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 [these people](https://github.com/sveltejs/vite-plugin-svelte/graphs/contributors)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,28 @@
# @sveltejs/vite-plugin-svelte
The official [Svelte](https://svelte.dev) plugin for [Vite](https://vitejs.dev).
## Usage
```js
// vite.config.js
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
export default defineConfig({
plugins: [
svelte({
/* plugin options */
})
]
});
```
## Documentation
- [Plugin options](../../docs/config.md)
- [FAQ](../../docs/faq.md)
## License
[MIT](./LICENSE)

@ -0,0 +1,259 @@
import { InlineConfig, ResolvedConfig, UserConfig, Plugin } from 'vite';
import { CompileOptions, Warning } from 'svelte/types/compiler/interfaces';
export { CompileOptions, Warning } from 'svelte/types/compiler/interfaces';
import { PreprocessorGroup } from 'svelte/types/compiler/preprocess';
export { MarkupPreprocessor, Preprocessor, PreprocessorGroup, Processed } from 'svelte/types/compiler/preprocess';
interface InspectorOptions {
/**
* define a key combo to toggle inspector,
* @default 'meta-shift' on mac, 'control-shift' on other os
*
* any number of modifiers `control` `shift` `alt` `meta` followed by zero or one regular key, separated by -
* examples: control-shift, control-o, control-alt-s meta-x control-meta
* Some keys have native behavior (e.g. alt-s opens history menu on firefox).
* To avoid conflicts or accidentally typing into inputs, modifier only combinations are recommended.
*/
toggleKeyCombo?: string;
/**
* define keys to select elements with via keyboard
* @default {parent: 'ArrowUp', child: 'ArrowDown', next: 'ArrowRight', prev: 'ArrowLeft' }
*
* improves accessibility and also helps when you want to select elements that do not have a hoverable surface area
* due to tight wrapping
*
* A note for users of screen-readers:
* If you are using arrow keys to navigate the page itself, change the navKeys to avoid conflicts.
* e.g. navKeys: {parent: 'w', prev: 'a', child: 's', next: 'd'}
*
*
* parent: select closest parent
* child: select first child (or grandchild)
* next: next sibling (or parent if no next sibling exists)
* prev: previous sibling (or parent if no prev sibling exists)
*/
navKeys?: {
parent: string;
child: string;
next: string;
prev: string;
};
/**
* define key to open the editor for the currently selected dom node
*
* @default 'Enter'
*/
openKey?: string;
/**
* inspector is automatically disabled when releasing toggleKeyCombo after holding it for a longpress
* @default true
*/
holdMode?: boolean;
/**
* when to show the toggle button
* @default 'active'
*/
showToggleButton?: 'always' | 'active' | 'never';
/**
* where to display the toggle button
* @default top-right
*/
toggleButtonPos?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
/**
* inject custom styles when inspector is active
*/
customStyles?: boolean;
/**
* internal options that are automatically set, not to be set or used by users
*/
__internal?: {
base: string;
};
}
type Options = Omit<SvelteOptions, 'vitePlugin'> & PluginOptionsInline;
interface PluginOptionsInline extends PluginOptions {
/**
* Path to a svelte config file, either absolute or relative to Vite root
*
* set to `false` to ignore the svelte config file
*
* @see https://vitejs.dev/config/#root
*/
configFile?: string | false;
}
interface PluginOptions {
/**
* A `picomatch` pattern, or array of patterns, which specifies the files the plugin should
* operate on. By default, all svelte files are included.
*
* @see https://github.com/micromatch/picomatch
*/
include?: Arrayable<string>;
/**
* A `picomatch` pattern, or array of patterns, which specifies the files to be ignored by the
* plugin. By default, no files are ignored.
*
* @see https://github.com/micromatch/picomatch
*/
exclude?: Arrayable<string>;
/**
* Emit Svelte styles as virtual CSS files for Vite and other plugins to process
*
* @default true
*/
emitCss?: boolean;
/**
* Enable or disable Hot Module Replacement.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* DO NOT CUSTOMIZE SVELTE-HMR OPTIONS UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING
*
* YOU HAVE BEEN WARNED
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* Set an object to pass custom options to svelte-hmr
*
* @see https://github.com/rixo/svelte-hmr#options
* @default true for development, always false for production
*/
hot?: boolean | {
injectCss?: boolean;
partialAccept?: boolean;
[key: string]: any;
};
/**
* Some Vite plugins can contribute additional preprocessors by defining `api.sveltePreprocess`.
* If you don't want to use them, set this to true to ignore them all or use an array of strings
* with plugin names to specify which.
*
* @default false
*/
ignorePluginPreprocessors?: boolean | string[];
/**
* vite-plugin-svelte automatically handles excluding svelte libraries and reinclusion of their dependencies
* in vite.optimizeDeps.
*
* `disableDependencyReinclusion: true` disables all reinclusions
* `disableDependencyReinclusion: ['foo','bar']` disables reinclusions for dependencies of foo and bar
*
* This should be used for hybrid packages that contain both node and browser dependencies, eg Routify
*
* @default false
*/
disableDependencyReinclusion?: boolean | string[];
/**
* Enable support for Vite's dependency optimization to prebundle Svelte libraries.
*
* To disable prebundling for a specific library, add it to `optimizeDeps.exclude`.
*
* @default true for dev, false for build
*/
prebundleSvelteLibraries?: boolean;
/**
* toggle/configure Svelte Inspector
*
* @default true
*/
inspector?: InspectorOptions | boolean;
/**
* These options are considered experimental and breaking changes to them can occur in any release
*/
experimental?: ExperimentalOptions;
}
interface SvelteOptions {
/**
* A list of file extensions to be compiled by Svelte
*
* @default ['.svelte']
*/
extensions?: string[];
/**
* An array of preprocessors to transform the Svelte source code before compilation
*
* @see https://svelte.dev/docs#svelte_preprocess
*/
preprocess?: Arrayable<PreprocessorGroup>;
/**
* The options to be passed to the Svelte compiler. A few options are set by default,
* including `dev` and `css`. However, some options are non-configurable, like
* `filename`, `format`, `generate`, and `cssHash` (in dev).
*
* @see https://svelte.dev/docs#svelte_compile
*/
compilerOptions?: Omit<CompileOptions, 'filename' | 'format' | 'generate'>;
/**
* Handles warning emitted from the Svelte compiler
*/
onwarn?: (warning: Warning, defaultHandler?: (warning: Warning) => void) => void;
/**
* Options for vite-plugin-svelte
*/
vitePlugin?: PluginOptions;
}
/**
* These options are considered experimental and breaking changes to them can occur in any release
*/
interface ExperimentalOptions {
/**
* A function to update `compilerOptions` before compilation
*
* `data.filename` - The file to be compiled
* `data.code` - The preprocessed Svelte code
* `data.compileOptions` - The current compiler options
*
* To change part of the compiler options, return an object with the changes you need.
*
* @example
* ```
* ({ filename, compileOptions }) => {
* // Dynamically set hydration per Svelte file
* if (compileWithHydratable(filename) && !compileOptions.hydratable) {
* return { hydratable: true };
* }
* }
* ```
*/
dynamicCompileOptions?: (data: {
filename: string;
code: string;
compileOptions: Partial<CompileOptions>;
}) => Promise<Partial<CompileOptions> | void> | Partial<CompileOptions> | void;
/**
* send a websocket message with svelte compiler warnings during dev
*
*/
sendWarningsToBrowser?: boolean;
/**
* disable svelte field resolve warnings
*
* @default false
*/
disableSvelteResolveWarnings?: boolean;
}
type ModuleFormat = NonNullable<CompileOptions['format']>;
type CssHashGetter = NonNullable<CompileOptions['cssHash']>;
type Arrayable<T> = T | T[];
declare function vitePreprocess(opts?: {
script?: boolean;
style?: boolean | InlineConfig | ResolvedConfig;
}): PreprocessorGroup;
declare function loadSvelteConfig(viteConfig?: UserConfig, inlineOptions?: Partial<Options>): Promise<Partial<SvelteOptions> | undefined>;
type SvelteWarningsMessage = {
id: string;
filename: string;
normalizedFilename: string;
timestamp: number;
warnings: Warning[];
allWarnings: Warning[];
rawWarnings: Warning[];
};
declare function svelte(inlineOptions?: Partial<Options>): Plugin[];
export { Arrayable, CssHashGetter, ModuleFormat, Options, PluginOptions, SvelteOptions, SvelteWarningsMessage, loadSvelteConfig, svelte, vitePreprocess };

@ -0,0 +1,17 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -z "$NODE_PATH" ]; then
export NODE_PATH="/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules/vite/bin/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules/vite/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/node_modules"
else
export NODE_PATH="/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules/vite/bin/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules/vite/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/vite@4.3.8/node_modules:/home/yukil/repos/msn/node_modules/.pnpm/node_modules:$NODE_PATH"
fi
if [ -x "$basedir/node" ]; then
exec "$basedir/node" "$basedir/../../../../../../vite@4.3.8/node_modules/vite/bin/vite.js" "$@"
else
exec node "$basedir/../../../../../../vite@4.3.8/node_modules/vite/bin/vite.js" "$@"
fi

@ -0,0 +1,64 @@
{
"name": "@sveltejs/vite-plugin-svelte",
"version": "2.2.0",
"license": "MIT",
"author": "dominikg",
"files": [
"dist",
"src",
"*.d.ts"
],
"type": "module",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js"
},
"./package.json": "./package.json",
"./src/ui/*": "./src/ui/*"
},
"engines": {
"node": "^14.18.0 || >= 16"
},
"repository": {
"type": "git",
"url": "git+https://github.com/sveltejs/vite-plugin-svelte.git",
"directory": "packages/vite-plugin-svelte"
},
"keywords": [
"vite-plugin",
"vite plugin",
"vite",
"svelte"
],
"bugs": {
"url": "https://github.com/sveltejs/vite-plugin-svelte/issues"
},
"homepage": "https://github.com/sveltejs/vite-plugin-svelte#readme",
"dependencies": {
"debug": "^4.3.4",
"deepmerge": "^4.3.1",
"kleur": "^4.1.5",
"magic-string": "^0.30.0",
"svelte-hmr": "^0.15.1",
"vitefu": "^0.2.4"
},
"peerDependencies": {
"svelte": "^3.54.0",
"vite": "^4.0.0"
},
"devDependencies": {
"@types/debug": "^4.1.7",
"esbuild": "^0.17.18",
"rollup": "^3.21.2",
"svelte": "^3.58.0",
"tsup": "^6.7.0",
"vite": "^4.3.3"
},
"scripts": {
"dev": "pnpm build:ci --sourcemap --watch src",
"build:ci": "rimraf dist && tsup-node src/index.ts --format esm",
"build": "pnpm build:ci --dts --sourcemap"
}
}

@ -0,0 +1,51 @@
import { describe, it, expect } from 'vitest';
import { vitePreprocess } from '../preprocess';
import path from 'path';
import { normalizePath } from 'vite';
import { fileURLToPath } from 'url';
const fixtureDir = normalizePath(
path.join(path.dirname(fileURLToPath(import.meta.url)), 'fixtures', 'preprocess')
);
describe('vitePreprocess', () => {
it('returns function', () => {
const preprocessorGroup = vitePreprocess({ script: true, style: true });
expect(typeof preprocessorGroup).toBe('object');
expect(typeof preprocessorGroup.script).toBe('function');
expect(typeof preprocessorGroup.style).toBe('function');
});
describe('style', async () => {
it('produces sourcemap with relative filename', async () => {
const { style } = vitePreprocess({ style: { css: { devSourcemap: true } } });
const scss = `
@import './foo';
.foo {
&.bar {
color: red;
}
}`.replace(/\t/g, '');
const processed = await style({
content: scss,
attributes: {
lang: 'scss'
},
markup: '', // not read by vitePreprocess
filename: `${fixtureDir}/File.svelte`
});
expect(processed).toBeDefined();
// @ts-ignore
const { code, map, dependencies } = processed;
expect(code).toBe('.foo {\n color: green;\n}\n\n.foo.bar {\n color: red;\n}');
expect(map.file).toBe('File.svelte');
expect(map.sources.length).toBe(2);
expect(map.sources[0]).toBe('foo.scss');
expect(map.sources[1]).toBe('File.svelte');
expect(dependencies).toBeDefined();
expect(dependencies[0]).toBe(path.resolve(fixtureDir, 'foo.scss'));
expect(dependencies.length).toBe(1);
});
});
});

@ -0,0 +1,118 @@
import { ModuleNode, HmrContext } from 'vite';
import { Code, CompileData } from './utils/compile';
import { log, logCompilerWarnings } from './utils/log';
import { SvelteRequest } from './utils/id';
import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache';
import { ResolvedOptions } from './utils/options';
import { toRollupError } from './utils/error';
/**
* Vite-specific HMR handling
*/
export async function handleHotUpdate(
compileSvelte: Function,
ctx: HmrContext,
svelteRequest: SvelteRequest,
cache: VitePluginSvelteCache,
options: ResolvedOptions
): Promise<ModuleNode[] | void> {
if (!cache.has(svelteRequest)) {
// file hasn't been requested yet (e.g. async component)
log.debug(`handleHotUpdate called before initial transform for ${svelteRequest.id}`);
return;
}
const { read, server, modules } = ctx;
const cachedJS = cache.getJS(svelteRequest);
const cachedCss = cache.getCSS(svelteRequest);
const content = await read();
let compileData: CompileData;
try {
compileData = await compileSvelte(svelteRequest, content, options);
cache.update(compileData);
} catch (e) {
cache.setError(svelteRequest, e);
throw toRollupError(e, options);
}
const affectedModules = [...modules];
const cssIdx = modules.findIndex((m) => m.id === svelteRequest.cssId);
if (cssIdx > -1) {
const cssUpdated = cssChanged(cachedCss, compileData.compiled.css);
if (!cssUpdated) {
log.debug(`skipping unchanged css for ${svelteRequest.cssId}`);
affectedModules.splice(cssIdx, 1);
}
}
const jsIdx = modules.findIndex((m) => m.id === svelteRequest.id);
if (jsIdx > -1) {
const jsUpdated = jsChanged(cachedJS, compileData.compiled.js, svelteRequest.filename);
if (!jsUpdated) {
log.debug(`skipping unchanged js for ${svelteRequest.id}`);
affectedModules.splice(jsIdx, 1);
// transform won't be called, log warnings here
logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options);
}
}
// TODO is this enough? see also: https://github.com/vitejs/vite/issues/2274
const ssrModulesToInvalidate = affectedModules.filter((m) => !!m.ssrTransformResult);
if (ssrModulesToInvalidate.length > 0) {
log.debug(`invalidating modules ${ssrModulesToInvalidate.map((m) => m.id).join(', ')}`);
ssrModulesToInvalidate.forEach((moduleNode) => server.moduleGraph.invalidateModule(moduleNode));
}
if (affectedModules.length > 0) {
log.debug(
`handleHotUpdate for ${svelteRequest.id} result: ${affectedModules
.map((m) => m.id)
.join(', ')}`
);
}
return affectedModules;
}
function cssChanged(prev?: Code, next?: Code): boolean {
return !isCodeEqual(prev?.code, next?.code);
}
function jsChanged(prev?: Code, next?: Code, filename?: string): boolean {
const prevJs = prev?.code;
const nextJs = next?.code;
const isStrictEqual = isCodeEqual(prevJs, nextJs);
if (isStrictEqual) {
return false;
}
const isLooseEqual = isCodeEqual(normalizeJsCode(prevJs), normalizeJsCode(nextJs));
if (!isStrictEqual && isLooseEqual) {
log.warn(
`ignoring compiler output js change for ${filename} as it is equal to previous output after normalization`
);
}
return !isLooseEqual;
}
function isCodeEqual(prev?: string, next?: string): boolean {
if (!prev && !next) {
return true;
}
if ((!prev && next) || (prev && !next)) {
return false;
}
return prev === next;
}
/**
* remove code that only changes metadata and does not require a js update for the component to keep working
*
* 1) add_location() calls. These add location metadata to elements, only used by some dev tools
* 2) ... maybe more (or less) in the future
* @param code
*/
function normalizeJsCode(code?: string): string | undefined {
if (!code) {
return code;
}
return code.replace(/\s*\badd_location\s*\([^)]*\)\s*;?/g, '');
}

@ -0,0 +1,300 @@
import fs from 'fs';
import { VERSION as svelteVersion } from 'svelte/compiler';
import {
HmrContext,
ModuleNode,
Plugin,
ResolvedConfig,
UserConfig,
version as viteVersion
} from 'vite';
// eslint-disable-next-line node/no-missing-import
import { isDepExcluded } from 'vitefu';
import { handleHotUpdate } from './handle-hot-update';
import { log, logCompilerWarnings } from './utils/log';
import { type CompileSvelte, createCompileSvelte } from './utils/compile';
import { buildIdParser, IdParser } from './utils/id';
import {
buildExtraViteConfig,
validateInlineOptions,
Options,
ResolvedOptions,
resolveOptions,
patchResolvedViteConfig,
preResolveOptions
} from './utils/options';
import { ensureWatchedFile, setupWatchers } from './utils/watch';
import { resolveViaPackageJsonSvelte } from './utils/resolve';
import { PartialResolvedId } from 'rollup';
import { toRollupError } from './utils/error';
import { saveSvelteMetadata } from './utils/optimizer';
import { svelteInspector } from './ui/inspector/plugin';
import { VitePluginSvelteCache } from './utils/vite-plugin-svelte-cache';
import { loadRaw } from './utils/load-raw';
import { FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE } from './utils/constants';
interface PluginAPI {
/**
* must not be modified, should not be used outside of vite-plugin-svelte repo
* @internal
* @experimental
*/
options?: ResolvedOptions;
// TODO expose compile cache here so other utility plugins can use it
}
const isVite4_0 = viteVersion.startsWith('4.0');
const isSvelte3 = svelteVersion.startsWith('3');
export function svelte(inlineOptions?: Partial<Options>): Plugin[] {
if (process.env.DEBUG != null) {
log.setLevel('debug');
}
validateInlineOptions(inlineOptions);
const cache = new VitePluginSvelteCache();
// updated in configResolved hook
let requestParser: IdParser;
let options: ResolvedOptions;
let viteConfig: ResolvedConfig;
/* eslint-disable no-unused-vars */
let compileSvelte: CompileSvelte;
/* eslint-enable no-unused-vars */
let resolvedSvelteSSR: Promise<PartialResolvedId | null>;
let packagesWithResolveWarnings: Set<string>;
const api: PluginAPI = {};
const plugins: Plugin[] = [
{
name: 'vite-plugin-svelte',
// make sure our resolver runs before vite internal resolver to resolve svelte field correctly
enforce: 'pre',
api,
async config(config, configEnv): Promise<Partial<UserConfig>> {
// setup logger
if (process.env.DEBUG) {
log.setLevel('debug');
} else if (config.logLevel) {
log.setLevel(config.logLevel);
}
// @ts-expect-error temporarily lend the options variable until fixed in configResolved
options = await preResolveOptions(inlineOptions, config, configEnv);
// extra vite config
const extraViteConfig = await buildExtraViteConfig(options, config);
log.debug('additional vite config', extraViteConfig);
return extraViteConfig;
},
async configResolved(config) {
options = resolveOptions(options, config, cache);
patchResolvedViteConfig(config, options);
requestParser = buildIdParser(options);
compileSvelte = createCompileSvelte(options);
viteConfig = config;
// TODO deep clone to avoid mutability from outside?
api.options = options;
log.debug('resolved options', options);
},
async buildStart() {
packagesWithResolveWarnings = new Set<string>();
if (!options.prebundleSvelteLibraries) return;
const isSvelteMetadataChanged = await saveSvelteMetadata(viteConfig.cacheDir, options);
if (isSvelteMetadataChanged) {
// Force Vite to optimize again. Although we mutate the config here, it works because
// Vite's optimizer runs after `buildStart()`.
viteConfig.optimizeDeps.force = true;
}
},
configureServer(server) {
// eslint-disable-next-line no-unused-vars
options.server = server;
setupWatchers(options, cache, requestParser);
},
async load(id, opts) {
const ssr = !!opts?.ssr;
const svelteRequest = requestParser(id, !!ssr);
if (svelteRequest) {
const { filename, query, raw } = svelteRequest;
if (raw) {
return loadRaw(svelteRequest, compileSvelte, options);
} else {
if (query.svelte && query.type === 'style') {
const css = cache.getCSS(svelteRequest);
if (css) {
log.debug(`load returns css for ${filename}`);
return css;
}
}
// prevent vite asset plugin from loading files as url that should be compiled in transform
if (viteConfig.assetsInclude(filename)) {
log.debug(`load returns raw content for ${filename}`);
return fs.readFileSync(filename, 'utf-8');
}
}
}
},
async resolveId(importee, importer, opts) {
const ssr = !!opts?.ssr;
const svelteRequest = requestParser(importee, ssr);
if (svelteRequest?.query.svelte) {
if (svelteRequest.query.type === 'style' && !svelteRequest.raw) {
// return cssId with root prefix so postcss pipeline of vite finds the directory correctly
// see https://github.com/sveltejs/vite-plugin-svelte/issues/14
log.debug(`resolveId resolved virtual css module ${svelteRequest.cssId}`);
return svelteRequest.cssId;
}
}
// TODO: remove this after bumping peerDep on Vite to 4.1+ or Svelte to 4.0+
if (isVite4_0 && isSvelte3 && ssr && importee === 'svelte') {
if (!resolvedSvelteSSR) {
resolvedSvelteSSR = this.resolve('svelte/ssr', undefined, { skipSelf: true }).then(
(svelteSSR) => {
log.debug('resolved svelte to svelte/ssr');
return svelteSSR;
},
(err) => {
log.debug(
'failed to resolve svelte to svelte/ssr. Update svelte to a version that exports it',
err
);
return null; // returning null here leads to svelte getting resolved regularly
}
);
}
return resolvedSvelteSSR;
}
//@ts-expect-error scan
const scan = !!opts?.scan; // scanner phase of optimizeDeps
const isPrebundled =
options.prebundleSvelteLibraries &&
viteConfig.optimizeDeps?.disabled !== true &&
viteConfig.optimizeDeps?.disabled !== (options.isBuild ? 'build' : 'dev') &&
!isDepExcluded(importee, viteConfig.optimizeDeps?.exclude ?? []);
// for prebundled libraries we let vite resolve the prebundling result
// for ssr, during scanning and non-prebundled, we do it
if (ssr || scan || !isPrebundled) {
try {
const isFirstResolve = !cache.hasResolvedSvelteField(importee, importer);
const resolved = await resolveViaPackageJsonSvelte(importee, importer, cache);
if (isFirstResolve && resolved) {
const packageInfo = await cache.getPackageInfo(resolved);
const packageVersion = `${packageInfo.name}@${packageInfo.version}`;
log.debug.once(
`resolveId resolved ${importee} to ${resolved} via package.json svelte field of ${packageVersion}`
);
try {
const viteResolved = (
await this.resolve(importee, importer, { ...opts, skipSelf: true })
)?.id;
if (resolved !== viteResolved) {
packagesWithResolveWarnings.add(packageVersion);
log.debug.enabled &&
log.debug.once(
`resolve difference for ${packageVersion} ${importee} - svelte: "${resolved}", vite: "${viteResolved}"`
);
}
} catch (e) {
packagesWithResolveWarnings.add(packageVersion);
log.debug.enabled &&
log.debug.once(
`resolve error for ${packageVersion} ${importee} - svelte: "${resolved}", vite: ERROR`,
e
);
}
}
return resolved;
} catch (e) {
log.debug.once(
`error trying to resolve ${importee} from ${importer} via package.json svelte field `,
e
);
// this error most likely happens due to non-svelte related importee/importers so swallow it here
// in case it really way a svelte library, users will notice anyway. (lib not working due to failed resolve)
}
}
},
async transform(code, id, opts) {
const ssr = !!opts?.ssr;
const svelteRequest = requestParser(id, ssr);
if (!svelteRequest || svelteRequest.query.type === 'style' || svelteRequest.raw) {
return;
}
let compileData;
try {
compileData = await compileSvelte(svelteRequest, code, options);
} catch (e) {
cache.setError(svelteRequest, e);
throw toRollupError(e, options);
}
logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options);
cache.update(compileData);
if (compileData.dependencies?.length && options.server) {
compileData.dependencies.forEach((d) => {
ensureWatchedFile(options.server!.watcher, d, options.root);
});
}
log.debug(`transform returns compiled js for ${svelteRequest.filename}`);
return {
...compileData.compiled.js,
meta: {
vite: {
lang: compileData.lang
}
}
};
},
handleHotUpdate(ctx: HmrContext): void | Promise<Array<ModuleNode> | void> {
if (!options.hot || !options.emitCss) {
return;
}
const svelteRequest = requestParser(ctx.file, false, ctx.timestamp);
if (svelteRequest) {
return handleHotUpdate(compileSvelte, ctx, svelteRequest, cache, options);
}
},
async buildEnd() {
await options.stats?.finishAll();
if (
!options.experimental?.disableSvelteResolveWarnings &&
packagesWithResolveWarnings?.size > 0
) {
log.warn(
`WARNING: The following packages use a svelte resolve configuration in package.json that has conflicting results and is going to cause problems future.\n\n${[
...packagesWithResolveWarnings
].join('\n')}\n\nPlease see ${FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE} for details.`
);
}
}
},
svelteInspector()
];
return plugins;
}
export { vitePreprocess } from './preprocess';
export { loadSvelteConfig } from './utils/load-svelte-config';
export {
Options,
PluginOptions,
SvelteOptions,
Preprocessor,
PreprocessorGroup,
CompileOptions,
CssHashGetter,
Arrayable,
MarkupPreprocessor,
ModuleFormat,
Processed,
Warning
} from './utils/options';
export { SvelteWarningsMessage } from './utils/log';

@ -0,0 +1,109 @@
import { preprocessCSS, resolveConfig, transformWithEsbuild } from 'vite';
import type { ESBuildOptions, InlineConfig, ResolvedConfig } from 'vite';
// eslint-disable-next-line node/no-missing-import
import type { Preprocessor, PreprocessorGroup } from 'svelte/types/compiler/preprocess';
import { mapToRelative, removeLangSuffix } from './utils/sourcemaps';
const supportedStyleLangs = ['css', 'less', 'sass', 'scss', 'styl', 'stylus', 'postcss', 'sss'];
const supportedScriptLangs = ['ts'];
export const lang_sep = '.vite-preprocess.';
export function vitePreprocess(opts?: {
script?: boolean;
style?: boolean | InlineConfig | ResolvedConfig;
}) {
const preprocessor: PreprocessorGroup = {};
if (opts?.script !== false) {
preprocessor.script = viteScript().script;
}
if (opts?.style !== false) {
const styleOpts = typeof opts?.style == 'object' ? opts?.style : undefined;
preprocessor.style = viteStyle(styleOpts).style;
}
return preprocessor;
}
function viteScript(): { script: Preprocessor } {
return {
async script({ attributes, content, filename = '' }) {
const lang = attributes.lang as string;
if (!supportedScriptLangs.includes(lang)) return;
const { code, map } = await transformWithEsbuild(content, filename, {
loader: lang as ESBuildOptions['loader'],
target: 'esnext',
tsconfigRaw: {
compilerOptions: {
// svelte typescript needs this flag to work with type imports
importsNotUsedAsValues: 'preserve',
preserveValueImports: true
}
}
});
mapToRelative(map, filename);
return {
code,
map
};
}
};
}
function viteStyle(config: InlineConfig | ResolvedConfig = {}): {
style: Preprocessor;
} {
let transform: CssTransform;
const style: Preprocessor = async ({ attributes, content, filename = '' }) => {
const lang = attributes.lang as string;
if (!supportedStyleLangs.includes(lang)) return;
if (!transform) {
let resolvedConfig: ResolvedConfig;
// @ts-expect-error special prop added if running in v-p-s
if (style.__resolvedConfig) {
// @ts-expect-error
resolvedConfig = style.__resolvedConfig;
} else if (isResolvedConfig(config)) {
resolvedConfig = config;
} else {
resolvedConfig = await resolveConfig(
config,
process.env.NODE_ENV === 'production' ? 'build' : 'serve'
);
}
transform = getCssTransformFn(resolvedConfig);
}
const suffix = `${lang_sep}${lang}`;
const moduleId = `${filename}${suffix}`;
const { code, map, deps } = await transform(content, moduleId);
removeLangSuffix(map, suffix);
mapToRelative(map, filename);
const dependencies = deps ? Array.from(deps).filter((d) => !d.endsWith(suffix)) : undefined;
return {
code,
map: map ?? undefined,
dependencies
};
};
// @ts-expect-error tag so can be found by v-p-s
style.__resolvedConfig = null;
return { style };
}
type CssTransform = (
// eslint-disable-next-line no-unused-vars
code: string,
// eslint-disable-next-line no-unused-vars
filename: string
) => Promise<{ code: string; map?: any; deps?: Set<string> }>;
function getCssTransformFn(config: ResolvedConfig): CssTransform {
return async (code, filename) => {
return preprocessCSS(code, filename, config);
};
}
function isResolvedConfig(config: any): config is ResolvedConfig {
return !!config.inlineConfig;
}

@ -0,0 +1,384 @@
<script>
// do not use TS here so that this component works in non-ts projects too
import { onMount } from 'svelte';
// eslint-disable-next-line node/no-missing-import
import options from 'virtual:svelte-inspector-options';
const toggle_combo = options.toggleKeyCombo?.toLowerCase().split('-');
const nav_keys = Object.values(options.navKeys).map((k) => k.toLowerCase());
let enabled = false;
let hasOpened = false;
const icon = `data:image/svg+xml;base64,${btoa(
`
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="25" viewBox="0 0 107 128">
<title>svelte-inspector-logo</title>
<path d="M94.1566,22.8189c-10.4-14.8851-30.94-19.2971-45.7914-9.8348L22.2825,29.6078A29.9234,29.9234,0,0,0,8.7639,49.6506a31.5136,31.5136,0,0,0,3.1076,20.2318A30.0061,30.0061,0,0,0,7.3953,81.0653a31.8886,31.8886,0,0,0,5.4473,24.1157c10.4022,14.8865,30.9423,19.2966,45.7914,9.8348L84.7167,98.3921A29.9177,29.9177,0,0,0,98.2353,78.3493,31.5263,31.5263,0,0,0,95.13,58.117a30,30,0,0,0,4.4743-11.1824,31.88,31.88,0,0,0-5.4473-24.1157" style="fill:#ff3e00"/><path d="M45.8171,106.5815A20.7182,20.7182,0,0,1,23.58,98.3389a19.1739,19.1739,0,0,1-3.2766-14.5025,18.1886,18.1886,0,0,1,.6233-2.4357l.4912-1.4978,1.3363.9815a33.6443,33.6443,0,0,0,10.203,5.0978l.9694.2941-.0893.9675a5.8474,5.8474,0,0,0,1.052,3.8781,6.2389,6.2389,0,0,0,6.6952,2.485,5.7449,5.7449,0,0,0,1.6021-.7041L69.27,76.281a5.4306,5.4306,0,0,0,2.4506-3.631,5.7948,5.7948,0,0,0-.9875-4.3712,6.2436,6.2436,0,0,0-6.6978-2.4864,5.7427,5.7427,0,0,0-1.6.7036l-9.9532,6.3449a19.0329,19.0329,0,0,1-5.2965,2.3259,20.7181,20.7181,0,0,1-22.2368-8.2427,19.1725,19.1725,0,0,1-3.2766-14.5024,17.9885,17.9885,0,0,1,8.13-12.0513L55.8833,23.7472a19.0038,19.0038,0,0,1,5.3-2.3287A20.7182,20.7182,0,0,1,83.42,29.6611a19.1739,19.1739,0,0,1,3.2766,14.5025,18.4,18.4,0,0,1-.6233,2.4357l-.4912,1.4978-1.3356-.98a33.6175,33.6175,0,0,0-10.2037-5.1l-.9694-.2942.0893-.9675a5.8588,5.8588,0,0,0-1.052-3.878,6.2389,6.2389,0,0,0-6.6952-2.485,5.7449,5.7449,0,0,0-1.6021.7041L37.73,51.719a5.4218,5.4218,0,0,0-2.4487,3.63,5.7862,5.7862,0,0,0,.9856,4.3717,6.2437,6.2437,0,0,0,6.6978,2.4864,5.7652,5.7652,0,0,0,1.602-.7041l9.9519-6.3425a18.978,18.978,0,0,1,5.2959-2.3278,20.7181,20.7181,0,0,1,22.2368,8.2427,19.1725,19.1725,0,0,1,3.2766,14.5024,17.9977,17.9977,0,0,1-8.13,12.0532L51.1167,104.2528a19.0038,19.0038,0,0,1-5.3,2.3287" style="fill:#fff"/>
<polygon points="0,0 15,40 40,20" stroke="#ff3e00" fill="#ff3e00"></polygon>
</svg>
`
.replace(/[\n\r\t\s]+/g, ' ')
.trim()
)}`;
// location of code in file
let file_loc;
// cursor pos and width for file_loc overlay positioning
let x, y, w;
let active_el;
let enabled_ts;
$: show_toggle =
options.showToggleButton === 'always' || (options.showToggleButton === 'active' && enabled);
function mousemove(event) {
x = event.x;
y = event.y;
}
function find_selectable_parent(el, include_self = false) {
if (!include_self) {
el = el.parentNode;
}
while (el) {
if (is_selectable(el)) {
return el;
}
el = el.parentNode;
}
}
function find_selectable_child(el) {
return [...el.querySelectorAll('*')].find(is_selectable);
}
function find_selectable_sibling(el, prev = false) {
do {
el = prev ? el.previousElementSibling : el.nextElementSibling;
if (is_selectable(el)) {
return el;
}
} while (el);
}
function find_selectable_for_nav(key) {
const el = active_el;
if (!el) {
return find_selectable_child(document?.body);
}
switch (key) {
case options.navKeys.parent:
return find_selectable_parent(el);
case options.navKeys.child:
return find_selectable_child(el);
case options.navKeys.next:
return find_selectable_sibling(el) || find_selectable_parent(el);
case options.navKeys.prev:
return find_selectable_sibling(el, true) || find_selectable_parent(el);
default:
return;
}
}
function is_selectable(el) {
const file = el?.__svelte_meta?.loc?.file;
if (!file || file.includes('node_modules/')) {
return false; // no file or 3rd party
}
const id = el.getAttribute('id');
if (id === 'svelte-announcer' || id?.startsWith('svelte-inspector-')) {
return false; // ignore some elements by id that would be selectable from keyboard nav otherwise
}
return true;
}
function mouseover({ target }) {
const el = find_selectable_parent(target, true);
activate(el, false);
}
function activate(el, set_bubble_pos = true) {
if (options.customStyles && el !== active_el) {
if (active_el) {
active_el.classList.remove('svelte-inspector-active-target');
}
if (el) {
el.classList.add('svelte-inspector-active-target');
}
}
if (el) {
const { file, line, column } = el.__svelte_meta.loc;
file_loc = `${file}:${line + 1}:${column + 1}`;
} else {
file_loc = null;
}
active_el = el;
if (set_bubble_pos) {
const pos = el.getBoundingClientRect();
x = Math.ceil(pos.left);
y = Math.ceil(pos.bottom - 20);
}
}
function open_editor(event) {
if (file_loc) {
stop(event);
fetch(`${options.__internal.base}/__open-in-editor?file=${encodeURIComponent(file_loc)}`);
hasOpened = true;
if (options.holdMode && is_holding()) {
disable();
}
}
}
function is_key_active(key, event) {
switch (key) {
case 'shift':
case 'control':
case 'alt':
case 'meta':
return event.getModifierState(key.charAt(0).toUpperCase() + key.slice(1));
default:
return key === event.key.toLowerCase();
}
}
function is_combo(event) {
return toggle_combo?.every((key) => is_key_active(key, event));
}
function is_nav(event) {
return nav_keys?.some((key) => is_key_active(key, event));
}
function is_open(event) {
return options.openKey && options.openKey.toLowerCase() === event.key.toLowerCase();
}
function is_holding() {
return enabled_ts && Date.now() - enabled_ts > 250;
}
function stop(event) {
event.preventDefault();
event.stopPropagation();
event.stopImmediatePropagation();
}
function keydown(event) {
if (event.repeat || event.key == null) {
return;
}
if (is_combo(event)) {
toggle();
if (options.holdMode && enabled) {
enabled_ts = Date.now();
}
} else if (enabled) {
if (is_nav(event)) {
const el = find_selectable_for_nav(event.key);
if (el) {
activate(el);
stop(event);
}
} else if (is_open(event)) {
open_editor(event);
}
}
}
function keyup(event) {
if (event.repeat || event.key == null) {
return;
}
const k = event.key.toLowerCase();
if (enabled && is_holding() && toggle_combo.includes(k)) {
disable();
} else {
enabled_ts = null;
}
}
function toggle() {
enabled ? disable() : enable();
}
function listeners(body, enabled) {
const l = enabled ? body.addEventListener : body.removeEventListener;
l('mousemove', mousemove);
l('mouseover', mouseover);
l('click', open_editor, true);
}
function enable() {
enabled = true;
const b = document.body;
if (options.customStyles) {
b.classList.add('svelte-inspector-enabled');
}
listeners(b, enabled);
activate_initial_el();
}
function activate_initial_el() {
const hov = innermost_hover_el();
let el = find_selectable_parent(hov, true);
if (!el) {
const act = document.activeElement;
el = find_selectable_parent(act, true);
}
if (!el) {
el = find_selectable_child(document.body);
}
if (el) {
activate(el);
}
}
function innermost_hover_el() {
let e = document.body.querySelector(':hover');
let result;
while (e) {
result = e;
e = e.querySelector(':hover');
}
return result;
}
function disable() {
enabled = false;
hasOpened = false;
enabled_ts = null;
const b = document.body;
listeners(b, enabled);
if (options.customStyles) {
b.classList.remove('svelte-inspector-enabled');
active_el?.classList.remove('svelte-inspector-active-target');
}
active_el = null;
}
function visibilityChange() {
if (document.visibilityState === 'hidden') {
onLeave();
}
}
function onLeave() {
if (hasOpened) {
disable();
}
}
onMount(() => {
const s = document.createElement('style');
s.setAttribute('type', 'text/css');
s.setAttribute('id', 'svelte-inspector-style');
s.textContent = `:root { --svelte-inspector-icon: url(${icon})};`;
document.head.append(s);
if (toggle_combo) {
document.body.addEventListener('keydown', keydown);
if (options.holdMode) {
document.body.addEventListener('keyup', keyup);
}
}
document.addEventListener('visibilitychange', visibilityChange);
document.documentElement.addEventListener('mouseleave', onLeave);
return () => {
// make sure we get rid of everything
disable();
const s = document.head.querySelector('#svelte-inspector-style');
if (s) {
document.head.removeChild(s);
}
if (toggle_combo) {
document.body.removeEventListener('keydown', keydown);
if (options.holdMode) {
document.body.removeEventListener('keyup', keyup);
}
}
document.removeEventListener('visibilitychange', visibilityChange);
document.documentElement.removeEventListener('mouseleave', onLeave);
};
});
</script>
{#if show_toggle}
<button
id="svelte-inspector-toggle"
class:enabled
style={`background-image: var(--svelte-inspector-icon);${options.toggleButtonPos
.split('-')
.map((p) => `${p}: 8px;`)
.join('')}`}
on:click={() => toggle()}
aria-label={`${enabled ? 'disable' : 'enable'} svelte-inspector`}
/>
{/if}
{#if enabled && active_el && file_loc}
{@const loc = active_el.__svelte_meta.loc}
<div
id="svelte-inspector-overlay"
style:left="{Math.min(x + 3, document.documentElement.clientWidth - w - 10)}px"
style:top="{document.documentElement.clientHeight < y + 50 ? y - 30 : y + 30}px"
bind:offsetWidth={w}
>
&lt;{active_el.tagName.toLowerCase()}&gt;&nbsp;{file_loc}
</div>
<div id="svelte-inspector-announcer" aria-live="assertive" aria-atomic="true">
{active_el.tagName.toLowerCase()} in file {loc.file} on line {loc.line} column {loc.column}
</div>
{/if}
<style>
:global(body.svelte-inspector-enabled *) {
cursor: var(--svelte-inspector-icon), crosshair !important;
}
:global(.svelte-inspector-active-target) {
outline: 2px dashed #ff3e00 !important;
}
#svelte-inspector-overlay {
position: fixed;
background-color: rgba(0, 0, 0, 0.8);
color: #fff;
padding: 2px 4px;
border-radius: 5px;
z-index: 999999;
pointer-events: none;
}
#svelte-inspector-toggle {
all: unset;
border: 1px solid #ff3e00;
border-radius: 8px;
position: fixed;
height: 32px;
width: 32px;
background-color: white;
background-position: center;
background-repeat: no-repeat;
cursor: pointer;
}
#svelte-inspector-announcer {
position: absolute;
left: 0px;
top: 0px;
clip: rect(0px, 0px, 0px, 0px);
clip-path: inset(50%);
overflow: hidden;
white-space: nowrap;
width: 1px;
height: 1px;
}
#svelte-inspector-toggle:not(.enabled) {
filter: grayscale(1);
}
#svelte-inspector-toggle:hover {
background-color: #facece;
}
</style>

@ -0,0 +1,15 @@
// eslint-disable-next-line node/no-missing-import
import Inspector from 'virtual:svelte-inspector-path:Inspector.svelte';
function create_inspector_host() {
const id = 'svelte-inspector-host';
if (document.getElementById(id) != null) {
throw new Error('svelte-inspector-host element already exists');
}
const el = document.createElement('div');
el.setAttribute('id', id);
document.documentElement.appendChild(el);
return el;
}
new Inspector({ target: create_inspector_host() });

@ -0,0 +1,131 @@
import * as process from 'process';
import { log } from '../../utils/log';
import { loadEnv, ResolvedConfig } from 'vite';
export const defaultInspectorOptions: InspectorOptions = {
toggleKeyCombo: process.platform === 'darwin' ? 'meta-shift' : 'control-shift',
navKeys: { parent: 'ArrowUp', child: 'ArrowDown', next: 'ArrowRight', prev: 'ArrowLeft' },
openKey: 'Enter',
holdMode: true,
showToggleButton: 'active',
toggleButtonPos: 'top-right',
customStyles: true
};
export function parseEnvironmentOptions(
config: ResolvedConfig
): Partial<InspectorOptions> | boolean | void {
const env = loadEnv(config.mode, config.envDir ?? process.cwd(), 'SVELTE_INSPECTOR');
const options = env?.SVELTE_INSPECTOR_OPTIONS;
const toggle = env?.SVELTE_INSPECTOR_TOGGLE;
if (options) {
try {
const parsed = JSON.parse(options);
const parsedType = typeof parsed;
if (parsedType === 'boolean') {
return parsed;
} else if (parsedType === 'object') {
if (Array.isArray(parsed)) {
throw new Error('invalid type, expected object map but got array');
}
const parsedKeys = Object.keys(parsed);
const defaultKeys = Object.keys(defaultInspectorOptions);
const unknownKeys = parsedKeys.filter((k) => !defaultKeys.includes(k));
if (unknownKeys.length) {
log.warn(
`ignoring unknown options in environment SVELTE_INSPECTOR_OPTIONS: ${unknownKeys.join(
', '
)}.`,
undefined,
'inspector'
);
for (const key of unknownKeys) {
delete parsed[key];
}
}
log.debug('loaded environment config', parsed, 'inspector');
return parsed;
}
} catch (e) {
log.error(
`failed to parse inspector options from environment SVELTE_INSPECTOR_OPTIONS="${options}"`,
e,
'inspector'
);
}
} else if (toggle) {
const keyConfig = {
toggleKeyCombo: toggle
};
log.debug('loaded environment config', keyConfig, 'inspector');
return keyConfig;
}
}
export interface InspectorOptions {
/**
* define a key combo to toggle inspector,
* @default 'meta-shift' on mac, 'control-shift' on other os
*
* any number of modifiers `control` `shift` `alt` `meta` followed by zero or one regular key, separated by -
* examples: control-shift, control-o, control-alt-s meta-x control-meta
* Some keys have native behavior (e.g. alt-s opens history menu on firefox).
* To avoid conflicts or accidentally typing into inputs, modifier only combinations are recommended.
*/
toggleKeyCombo?: string;
/**
* define keys to select elements with via keyboard
* @default {parent: 'ArrowUp', child: 'ArrowDown', next: 'ArrowRight', prev: 'ArrowLeft' }
*
* improves accessibility and also helps when you want to select elements that do not have a hoverable surface area
* due to tight wrapping
*
* A note for users of screen-readers:
* If you are using arrow keys to navigate the page itself, change the navKeys to avoid conflicts.
* e.g. navKeys: {parent: 'w', prev: 'a', child: 's', next: 'd'}
*
*
* parent: select closest parent
* child: select first child (or grandchild)
* next: next sibling (or parent if no next sibling exists)
* prev: previous sibling (or parent if no prev sibling exists)
*/
navKeys?: { parent: string; child: string; next: string; prev: string };
/**
* define key to open the editor for the currently selected dom node
*
* @default 'Enter'
*/
openKey?: string;
/**
* inspector is automatically disabled when releasing toggleKeyCombo after holding it for a longpress
* @default true
*/
holdMode?: boolean;
/**
* when to show the toggle button
* @default 'active'
*/
showToggleButton?: 'always' | 'active' | 'never';
/**
* where to display the toggle button
* @default top-right
*/
toggleButtonPos?: 'top-right' | 'top-left' | 'bottom-right' | 'bottom-left';
/**
* inject custom styles when inspector is active
*/
customStyles?: boolean;
/**
* internal options that are automatically set, not to be set or used by users
*/
__internal?: {
// vite base url
base: string;
};
}

@ -0,0 +1,97 @@
import { Plugin, normalizePath } from 'vite';
import { log } from '../../utils/log';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { idToFile } from './utils';
import { defaultInspectorOptions, type InspectorOptions, parseEnvironmentOptions } from './options';
function getInspectorPath() {
const pluginPath = normalizePath(path.dirname(fileURLToPath(import.meta.url)));
return pluginPath.replace(/\/vite-plugin-svelte\/dist$/, '/vite-plugin-svelte/src/ui/inspector/');
}
export function svelteInspector(): Plugin {
const inspectorPath = getInspectorPath();
log.debug.enabled && log.debug(`svelte inspector path: ${inspectorPath}`);
let inspectorOptions: InspectorOptions;
let disabled = false;
return {
name: 'vite-plugin-svelte:inspector',
apply: 'serve',
enforce: 'pre',
configResolved(config) {
const vps = config.plugins.find((p) => p.name === 'vite-plugin-svelte');
if (!vps) {
log.warn('vite-plugin-svelte is missing, inspector disabled', undefined, 'inspector');
disabled = true;
return;
}
const configFileOptions = vps?.api?.options?.inspector;
const environmentOptions = parseEnvironmentOptions(config);
if (!configFileOptions && !environmentOptions) {
log.debug('no options found, inspector disabled', undefined, 'inspector');
disabled = true;
return;
}
if (environmentOptions === true) {
inspectorOptions = defaultInspectorOptions;
} else {
inspectorOptions = {
...defaultInspectorOptions,
...configFileOptions,
...(environmentOptions || {})
};
}
inspectorOptions.__internal = {
base: config.base?.replace(/\/$/, '') || ''
};
},
async resolveId(importee: string, importer, options) {
if (options?.ssr || disabled) {
return;
}
if (importee.startsWith('virtual:svelte-inspector-options')) {
return importee;
} else if (importee.startsWith('virtual:svelte-inspector-path:')) {
const resolved = importee.replace('virtual:svelte-inspector-path:', inspectorPath);
log.debug.enabled &&
log.debug(`resolved ${importee} with ${resolved}`, undefined, 'inspector');
return resolved;
}
},
async load(id, options) {
if (options?.ssr || disabled) {
return;
}
if (id === 'virtual:svelte-inspector-options') {
return `export default ${JSON.stringify(inspectorOptions ?? {})}`;
} else if (id.startsWith(inspectorPath)) {
// read file ourselves to avoid getting shut out by vites fs.allow check
const file = idToFile(id);
if (fs.existsSync(file)) {
return await fs.promises.readFile(file, 'utf-8');
} else {
log.error(
`failed to find file for svelte-inspector: ${file}, referenced by id ${id}.`,
undefined,
'inspector'
);
}
}
},
transform(code, id, options) {
if (options?.ssr || disabled) {
return;
}
if (id.includes('vite/dist/client/client.mjs')) {
return { code: `${code}\nimport('virtual:svelte-inspector-path:load-inspector.js')` };
}
}
};
}

@ -0,0 +1,13 @@
const FS_PREFIX = `/@fs/`;
const IS_WINDOWS = process.platform === 'win32';
const queryRE = /\?.*$/s;
const hashRE = /#.*$/s;
export function idToFile(id: string): string {
// strip /@fs/ but keep leading / on non-windows
if (id.startsWith(FS_PREFIX)) {
id = id = id.slice(IS_WINDOWS ? FS_PREFIX.length : FS_PREFIX.length - 1);
}
// strip query and hash
return id.replace(hashRE, '').replace(queryRE, '');
}

@ -0,0 +1,49 @@
import { describe, it, expect } from 'vitest';
import { createCompileSvelte } from '../compile';
import { ResolvedOptions } from '../options';
const options: ResolvedOptions = {
compilerOptions: {
dev: false,
format: 'esm',
css: 'external'
},
isBuild: false,
isDebug: false,
isProduction: false,
isServe: false,
root: process.cwd()
};
describe('createCompileSvelte', () => {
it('returns function', () => {
const compileSvelte = createCompileSvelte(options);
expect(typeof compileSvelte).toBe('function');
});
describe('compileSvelte', async () => {
it('removes dangling pure annotations', async () => {
const code = `<script>
const x=1;
console.log('something',/* @__PURE__ */ new Date());
console.log('something else');
</script>
<div>{x}</div>`;
const compileSvelte = createCompileSvelte(options);
const output = await compileSvelte(
{
cssId: 'svelte-xxxxx',
query: undefined,
raw: false,
ssr: false,
timestamp: Date.now(),
id: 'id',
filename: '/some/File.svelte',
normalizedFilename: 'some/File.svelte'
},
code,
{}
);
expect(output.compiled.js.code).not.toContain('/* @__PURE__ */\n');
});
});
});

@ -0,0 +1,79 @@
import { describe, it, expect } from 'vitest';
import { removeLangSuffix, mapToRelative } from '../sourcemaps';
import { lang_sep } from '../../preprocess';
import { normalizePath } from 'vite';
import path from 'path';
import { fileURLToPath, pathToFileURL } from 'url';
const fixtureDir = normalizePath(
path.join(path.dirname(fileURLToPath(import.meta.url)), 'fixtures', 'preprocess')
);
const filename = 'File.svelte';
describe('removeLangSuffix', () => {
it('removes suffix', () => {
const suffix = `${lang_sep}scss`;
const map = {
file: `${fixtureDir}/${filename}${suffix}`,
sources: ['foo.scss', `${fixtureDir}/${filename}${suffix}`],
sourceRoot: fixtureDir
};
removeLangSuffix(map, suffix);
expect(map.file).toBe(`${fixtureDir}/${filename}`);
expect(map.sourceRoot).toBe(fixtureDir);
expect(map.sources[0]).toBe('foo.scss');
expect(map.sources[1]).toBe(`${fixtureDir}/${filename}`);
});
});
describe('mapToRelative', () => {
it('converts absolute to relative', () => {
const file = `${fixtureDir}/File.svelte`;
const map = {
file,
sources: [`${fixtureDir}/foo.scss`, file]
};
mapToRelative(map, file);
expect(map.file).toBe('File.svelte');
expect(map.sources[0]).toBe('foo.scss');
expect(map.sources[1]).toBe('File.svelte');
});
it('accounts for sourceRoot', () => {
const file = `${fixtureDir}/File.svelte`;
const sourceRoot = normalizePath(path.resolve(fixtureDir, '..'));
const rootedBase = fixtureDir.replace(sourceRoot, '');
const map = {
file,
sourceRoot,
sources: [
`${rootedBase}/foo.scss`,
`${rootedBase}/File.svelte`,
`${pathToFileURL(`${fixtureDir}/bar.scss`)}`
]
};
mapToRelative(map, file);
expect(map.file).toBe('File.svelte');
expect(map.sources[0]).toBe('foo.scss');
expect(map.sources[1]).toBe('File.svelte');
expect(map.sources[2]).toBe('bar.scss');
expect(map.sources.length).toBe(3);
expect(map.sourceRoot).not.toBeDefined();
});
it('accounts for relative sourceRoot', () => {
const file = `${fixtureDir}/File.svelte`;
const map = {
file,
sourceRoot: './some-path/..',
sources: [`foo.scss`, `File.svelte`, `${pathToFileURL(`${fixtureDir}/bar.scss`)}`]
};
mapToRelative(map, file);
expect(map.file).toBe('File.svelte');
expect(map.sources[0]).toBe('./some-path/../foo.scss');
expect(map.sources[1]).toBe('./some-path/../File.svelte');
expect(map.sources[2]).toBe('bar.scss');
expect(map.sources.length).toBe(3);
expect(map.sourceRoot).not.toBeDefined();
});
});

@ -0,0 +1,102 @@
import { describe, it, expect } from 'vitest';
import { compareToSvelte, atLeastSvelte, parseVersion } from '../svelte-version';
import { VERSION } from 'svelte/compiler';
const svelteVersion = parseVersion(VERSION);
describe('svelte-version', () => {
describe('parseVersion', () => {
it('should fill major,minor,patch', () => {
expect(parseVersion('3')).toEqual([3, 0, 0]);
expect(parseVersion('3.1')).toEqual([3, 1, 0]);
});
it('should ignore additional segments', () => {
expect(parseVersion('1.2.3.4')).toEqual([1, 2, 3]);
});
});
describe('compareToSvelte', () => {
it('should return 0 for current', async () => {
expect(compareToSvelte(VERSION)).toBe(0);
});
it('should return 1 for patch bump', async () => {
const patch = svelteVersion.concat();
patch[2] += 1;
const patchBump = patch.join('.');
expect(compareToSvelte(patchBump)).toBe(1);
});
it('should return 1 for minor bump', async () => {
const minor = svelteVersion.concat();
minor[1] += 1;
const minorBump = minor.join('.');
expect(compareToSvelte(minorBump)).toBe(1);
});
it('should return 1 for major bump', async () => {
const major = svelteVersion.concat();
major[0] += 1;
const majorBump = major.join('.');
expect(compareToSvelte(majorBump)).toBe(1);
});
it('should return -1 for lower patch', async () => {
const patch = svelteVersion.concat();
patch[2] -= 1;
const lowerPatch = patch.join('.');
expect(compareToSvelte(lowerPatch)).toBe(-1);
});
it('should return -1 for lower minor', async () => {
const minor = svelteVersion.concat();
minor[1] -= 1;
const lowerMinor = minor.join('.');
expect(compareToSvelte(lowerMinor)).toBe(-1);
});
it('should return -1 for lower major', async () => {
const major = svelteVersion.concat();
major[0] -= 1;
const lowerMajor = major.join('.');
expect(compareToSvelte(lowerMajor)).toBe(-1);
});
});
describe('atLeastSvelte', () => {
it('should return true for current', async () => {
expect(atLeastSvelte(VERSION)).toBe(true);
});
it('should return false for higher patch', async () => {
const patch = svelteVersion.concat();
patch[2] += 1;
const patchBump = patch.join('.');
expect(atLeastSvelte(patchBump)).toBe(false);
});
it('should return false for higher minor', async () => {
const minor = svelteVersion.concat();
minor[1] += 1;
const minorBump = minor.join('.');
expect(atLeastSvelte(minorBump)).toBe(false);
});
it('should return false for higher major', async () => {
const major = svelteVersion.concat();
major[0] += 1;
const majorBump = major.join('.');
expect(atLeastSvelte(majorBump)).toBe(false);
});
it('should return true for lower patch', async () => {
const patch = svelteVersion.concat();
patch[2] -= 1;
const lowerPatch = patch.join('.');
expect(atLeastSvelte(lowerPatch)).toBe(true);
});
it('should return true for lower minor', async () => {
const minor = svelteVersion.concat();
minor[1] -= 1;
const lowerMinor = minor.join('.');
expect(atLeastSvelte(lowerMinor)).toBe(true);
});
it('should return true for lower major', async () => {
const major = svelteVersion.concat();
major[0] -= 1;
const lowerMajor = major.join('.');
expect(atLeastSvelte(lowerMajor)).toBe(true);
});
});
});

@ -0,0 +1,232 @@
import { CompileOptions, ResolvedOptions } from './options';
import { compile, preprocess, walk } from 'svelte/compiler';
// @ts-ignore
import { createMakeHot } from 'svelte-hmr';
import { SvelteRequest } from './id';
import { safeBase64Hash } from './hash';
import { log } from './log';
import { StatCollection } from './vite-plugin-svelte-stats';
//eslint-disable-next-line node/no-missing-import
import type { Processed } from 'svelte/types/compiler/preprocess';
import { createInjectScopeEverythingRulePreprocessorGroup } from './preprocess';
import { mapToRelative } from './sourcemaps';
const scriptLangRE = /<script [^>]*lang=["']?([^"' >]+)["']?[^>]*>/;
export type CompileSvelte = ReturnType<typeof _createCompileSvelte>;
const _createCompileSvelte = (makeHot: Function) => {
let stats: StatCollection | undefined;
const devStylePreprocessor = createInjectScopeEverythingRulePreprocessorGroup();
return async function compileSvelte(
svelteRequest: SvelteRequest,
code: string,
options: Partial<ResolvedOptions>
): Promise<CompileData> {
const { filename, normalizedFilename, cssId, ssr, raw } = svelteRequest;
const { emitCss = true } = options;
const dependencies = [];
if (options.stats) {
if (options.isBuild) {
if (!stats) {
// build is either completely ssr or csr, create stats collector on first compile
// it is then finished in the buildEnd hook.
stats = options.stats.startCollection(`${ssr ? 'ssr' : 'dom'} compile`, {
logInProgress: () => false
});
}
} else {
// dev time ssr, it's a ssr request and there are no stats, assume new page load and start collecting
if (ssr && !stats) {
stats = options.stats.startCollection('ssr compile');
}
// stats are being collected but this isn't an ssr request, assume page loaded and stop collecting
if (!ssr && stats) {
stats.finish();
stats = undefined;
}
// TODO find a way to trace dom compile during dev
// problem: we need to call finish at some point but have no way to tell if page load finished
// also they for hmr updates too
}
}
const compileOptions: CompileOptions = {
...options.compilerOptions,
filename: normalizedFilename, // use normalized here to avoid bleeding absolute fs path
generate: ssr ? 'ssr' : 'dom',
format: 'esm'
};
if (options.hot && options.emitCss) {
const hash = `s-${safeBase64Hash(normalizedFilename)}`;
log.debug(`setting cssHash ${hash} for ${normalizedFilename}`);
compileOptions.cssHash = () => hash;
}
if (ssr && compileOptions.enableSourcemap !== false) {
if (typeof compileOptions.enableSourcemap === 'object') {
compileOptions.enableSourcemap.css = false;
} else {
compileOptions.enableSourcemap = { js: true, css: false };
}
}
let preprocessed;
let preprocessors = options.preprocess;
if (!options.isBuild && options.emitCss && options.hot) {
// inject preprocessor that ensures css hmr works better
if (!Array.isArray(preprocessors)) {
preprocessors = preprocessors
? [preprocessors, devStylePreprocessor]
: [devStylePreprocessor];
} else {
preprocessors = preprocessors.concat(devStylePreprocessor);
}
}
if (preprocessors) {
try {
preprocessed = await preprocess(code, preprocessors, { filename }); // full filename here so postcss works
} catch (e) {
e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
throw e;
}
if (preprocessed.dependencies) dependencies.push(...preprocessed.dependencies);
if (preprocessed.map) compileOptions.sourcemap = preprocessed.map;
}
if (typeof preprocessed?.map === 'object') {
mapToRelative(preprocessed?.map, filename);
}
if (raw && svelteRequest.query.type === 'preprocessed') {
// shortcut
return { preprocessed: preprocessed ?? { code } } as CompileData;
}
const finalCode = preprocessed ? preprocessed.code : code;
const dynamicCompileOptions = await options.experimental?.dynamicCompileOptions?.({
filename,
code: finalCode,
compileOptions
});
if (dynamicCompileOptions && log.debug.enabled) {
log.debug(
`dynamic compile options for ${filename}: ${JSON.stringify(dynamicCompileOptions)}`
);
}
const finalCompileOptions = dynamicCompileOptions
? {
...compileOptions,
...dynamicCompileOptions
}
: compileOptions;
const endStat = stats?.start(filename);
const compiled = compile(finalCode, finalCompileOptions);
// prevent dangling pure comments
// see https://github.com/sveltejs/kit/issues/9492#issuecomment-1487704985
// uses regex replace with whitespace to keep sourcemap/character count unmodified
compiled.js.code = compiled.js.code.replace(
/\/\* [@#]__PURE__ \*\/(\s*)$/gm,
' $1'
);
if (endStat) {
endStat();
}
mapToRelative(compiled.js?.map, filename);
mapToRelative(compiled.css?.map, filename);
if (!raw) {
// wire css import and code for hmr
const hasCss = compiled.css?.code?.trim().length > 0;
// compiler might not emit css with mode none or it may be empty
if (emitCss && hasCss) {
// TODO properly update sourcemap?
compiled.js.code += `\nimport ${JSON.stringify(cssId)};\n`;
}
// only apply hmr when not in ssr context and hot options are set
if (!ssr && makeHot) {
compiled.js.code = makeHot({
id: filename,
compiledCode: compiled.js.code,
//@ts-expect-error hot isn't a boolean at this point
hotOptions: { ...options.hot, injectCss: options.hot?.injectCss === true && hasCss },
compiled,
originalCode: code,
compileOptions: finalCompileOptions
});
}
}
compiled.js.dependencies = dependencies;
return {
filename,
normalizedFilename,
lang: code.match(scriptLangRE)?.[1] || 'js',
// @ts-ignore
compiled,
ssr,
dependencies,
preprocessed: preprocessed ?? { code }
};
};
};
function buildMakeHot(options: ResolvedOptions) {
const needsMakeHot = options.hot !== false && options.isServe && !options.isProduction;
if (needsMakeHot) {
// @ts-ignore
const hotApi = options?.hot?.hotApi;
// @ts-ignore
const adapter = options?.hot?.adapter;
return createMakeHot({
walk,
hotApi,
adapter,
hotOptions: { noOverlay: true, ...(options.hot as object) }
});
}
}
export function createCompileSvelte(options: ResolvedOptions) {
const makeHot = buildMakeHot(options);
return _createCompileSvelte(makeHot);
}
export interface Code {
code: string;
map?: any;
dependencies?: any[];
}
export interface Compiled {
js: Code;
css: Code;
ast: any; // TODO type
warnings: any[]; // TODO type
vars: {
name: string;
export_name: string;
injected: boolean;
module: boolean;
mutated: boolean;
reassigned: boolean;
referenced: boolean;
writable: boolean;
referenced_from_script: boolean;
}[];
stats: {
timings: {
total: number;
};
};
}
export interface CompileData {
filename: string;
normalizedFilename: string;
lang: string;
compiled: Compiled;
ssr: boolean | undefined;
dependencies: string[];
preprocessed: Processed;
}

@ -0,0 +1,25 @@
export const VITE_RESOLVE_MAIN_FIELDS = ['module', 'jsnext:main', 'jsnext'];
export const SVELTE_RESOLVE_MAIN_FIELDS = ['svelte'];
export const SVELTE_IMPORTS = [
'svelte/animate',
'svelte/easing',
'svelte/internal',
'svelte/motion',
'svelte/ssr',
'svelte/store',
'svelte/transition',
'svelte'
];
export const SVELTE_HMR_IMPORTS = [
'svelte-hmr/runtime/hot-api-esm.js',
'svelte-hmr/runtime/proxy-adapter-dom.js',
'svelte-hmr'
];
export const SVELTE_EXPORT_CONDITIONS = ['svelte'];
export const FAQ_LINK_CONFLICTS_IN_SVELTE_RESOLVE =
'https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#conflicts-in-svelte-resolve';

@ -0,0 +1,85 @@
import path from 'path';
import fs from 'fs/promises';
// eslint-disable-next-line node/no-missing-import
import { findDepPkgJsonPath } from 'vitefu';
interface DependencyData {
dir: string;
pkg: Record<string, any>;
}
export async function resolveDependencyData(
dep: string,
parent: string
): Promise<DependencyData | undefined> {
const depDataPath = await findDepPkgJsonPath(dep, parent);
if (!depDataPath) return undefined;
try {
return {
dir: path.dirname(depDataPath),
pkg: JSON.parse(await fs.readFile(depDataPath, 'utf-8'))
};
} catch {
return undefined;
}
}
const COMMON_DEPENDENCIES_WITHOUT_SVELTE_FIELD = [
'@lukeed/uuid',
'@playwright/test',
'@sveltejs/vite-plugin-svelte',
'@sveltejs/kit',
'autoprefixer',
'cookie',
'dotenv',
'esbuild',
'eslint',
'jest',
'mdsvex',
'playwright',
'postcss',
'prettier',
'svelte',
'svelte-check',
'svelte-hmr',
'svelte-preprocess',
'tslib',
'typescript',
'vite',
'vitest',
'__vite-browser-external' // see https://github.com/sveltejs/vite-plugin-svelte/issues/362
];
const COMMON_PREFIXES_WITHOUT_SVELTE_FIELD = [
'@fontsource/',
'@postcss-plugins/',
'@rollup/',
'@sveltejs/adapter-',
'@types/',
'@typescript-eslint/',
'eslint-',
'jest-',
'postcss-plugin-',
'prettier-plugin-',
'rollup-plugin-',
'vite-plugin-'
];
/**
* Test for common dependency names that tell us it is not a package including a svelte field, eg. eslint + plugins.
*
* This speeds up the find process as we don't have to try and require the package.json for all of them
*
* @param dependency {string}
* @returns {boolean} true if it is a dependency without a svelte field
*/
export function isCommonDepWithoutSvelteField(dependency: string): boolean {
return (
COMMON_DEPENDENCIES_WITHOUT_SVELTE_FIELD.includes(dependency) ||
COMMON_PREFIXES_WITHOUT_SVELTE_FIELD.some(
(prefix) =>
prefix.startsWith('@')
? dependency.startsWith(prefix)
: dependency.substring(dependency.lastIndexOf('/') + 1).startsWith(prefix) // check prefix omitting @scope/
)
);
}

@ -0,0 +1,95 @@
import { RollupError } from 'rollup';
import { ResolvedOptions, Warning } from './options';
import { buildExtendedLogMessage } from './log';
import { PartialMessage } from 'esbuild';
/**
* convert an error thrown by svelte.compile to a RollupError so that vite displays it in a user friendly way
* @param error a svelte compiler error, which is a mix of Warning and an error
* @returns {RollupError} the converted error
*/
export function toRollupError(error: Warning & Error, options: ResolvedOptions): RollupError {
const { filename, frame, start, code, name, stack } = error;
const rollupError: RollupError = {
name, // needed otherwise sveltekit coalesce_to_error turns it into a string
id: filename,
message: buildExtendedLogMessage(error), // include filename:line:column so that it's clickable
frame: formatFrameForVite(frame),
code,
stack: options.isBuild || options.isDebug || !frame ? stack : ''
};
if (start) {
rollupError.loc = {
line: start.line,
column: start.column,
file: filename
};
}
return rollupError;
}
/**
* convert an error thrown by svelte.compile to an esbuild PartialMessage
* @param error a svelte compiler error, which is a mix of Warning and an error
* @returns {PartialMessage} the converted error
*/
export function toESBuildError(error: Warning & Error, options: ResolvedOptions): PartialMessage {
const { filename, frame, start, stack } = error;
const partialMessage: PartialMessage = {
text: buildExtendedLogMessage(error)
};
if (start) {
partialMessage.location = {
line: start.line,
column: start.column,
file: filename,
lineText: lineFromFrame(start.line, frame) // needed to get a meaningful error message on cli
};
}
if (options.isBuild || options.isDebug || !frame) {
partialMessage.detail = stack;
}
return partialMessage;
}
/**
* extract line with number from codeframe
*/
function lineFromFrame(lineNo: number, frame?: string): string {
if (!frame) {
return '';
}
const lines = frame.split('\n');
const errorLine = lines.find((line) => line.trimStart().startsWith(`${lineNo}: `));
return errorLine ? errorLine.substring(errorLine.indexOf(': ') + 3) : '';
}
/**
* vite error overlay expects a specific format to show frames
* this reformats svelte frame (colon separated, less whitespace)
* to one that vite displays on overlay ( pipe separated, more whitespace)
* e.g.
* ```
* 1: foo
* 2: bar;
* ^
* 3: baz
* ```
* to
* ```
* 1 | foo
* 2 | bar;
* ^
* 3 | baz
* ```
* @see https://github.com/vitejs/vite/blob/96591bf9989529de839ba89958755eafe4c445ae/packages/vite/src/client/overlay.ts#L116
*/
function formatFrameForVite(frame?: string): string {
if (!frame) {
return '';
}
return frame
.split('\n')
.map((line) => (line.match(/^\s+\^/) ? ' ' + line : ' ' + line.replace(':', ' | ')))
.join('\n');
}

@ -0,0 +1,101 @@
import { readFileSync } from 'fs';
import { compile, preprocess } from 'svelte/compiler';
import { DepOptimizationOptions } from 'vite';
import { Compiled } from './compile';
import { log } from './log';
import { CompileOptions, ResolvedOptions } from './options';
import { toESBuildError } from './error';
import { StatCollection } from './vite-plugin-svelte-stats';
type EsbuildOptions = NonNullable<DepOptimizationOptions['esbuildOptions']>;
type EsbuildPlugin = NonNullable<EsbuildOptions['plugins']>[number];
export const facadeEsbuildSveltePluginName = 'vite-plugin-svelte:facade';
export function esbuildSveltePlugin(options: ResolvedOptions): EsbuildPlugin {
return {
name: 'vite-plugin-svelte:optimize-svelte',
setup(build) {
// Skip in scanning phase as Vite already handles scanning Svelte files.
// Otherwise this would heavily slow down the scanning phase.
if (build.initialOptions.plugins?.some((v) => v.name === 'vite:dep-scan')) return;
const svelteExtensions = (options.extensions ?? ['.svelte']).map((ext) => ext.slice(1));
const svelteFilter = new RegExp(`\\.(` + svelteExtensions.join('|') + `)(\\?.*)?$`);
let statsCollection: StatCollection | undefined;
build.onStart(() => {
statsCollection = options.stats?.startCollection('prebundle libraries', {
logResult: (c) => c.stats.length > 1
});
});
build.onLoad({ filter: svelteFilter }, async ({ path: filename }) => {
const code = readFileSync(filename, 'utf8');
try {
const contents = await compileSvelte(options, { filename, code }, statsCollection);
return { contents };
} catch (e) {
return { errors: [toESBuildError(e, options)] };
}
});
build.onEnd(() => {
statsCollection?.finish();
});
}
};
}
async function compileSvelte(
options: ResolvedOptions,
{ filename, code }: { filename: string; code: string },
statsCollection?: StatCollection
): Promise<string> {
let css = options.compilerOptions.css;
if (css !== 'none') {
// TODO ideally we'd be able to externalize prebundled styles too, but for now always put them in the js
css = 'injected';
}
const compileOptions: CompileOptions = {
...options.compilerOptions,
css,
filename,
format: 'esm',
generate: 'dom'
};
let preprocessed;
if (options.preprocess) {
try {
preprocessed = await preprocess(code, options.preprocess, { filename });
} catch (e) {
e.message = `Error while preprocessing ${filename}${e.message ? ` - ${e.message}` : ''}`;
throw e;
}
if (preprocessed.map) compileOptions.sourcemap = preprocessed.map;
}
const finalCode = preprocessed ? preprocessed.code : code;
const dynamicCompileOptions = await options.experimental?.dynamicCompileOptions?.({
filename,
code: finalCode,
compileOptions
});
if (dynamicCompileOptions && log.debug.enabled) {
log.debug(`dynamic compile options for ${filename}: ${JSON.stringify(dynamicCompileOptions)}`);
}
const finalCompileOptions = dynamicCompileOptions
? {
...compileOptions,
...dynamicCompileOptions
}
: compileOptions;
const endStat = statsCollection?.start(filename);
const compiled = compile(finalCode, finalCompileOptions) as Compiled;
if (endStat) {
endStat();
}
return compiled.js.code + '//# sourceMappingURL=' + compiled.js.map.toUrl();
}

@ -0,0 +1,32 @@
import * as crypto from 'crypto';
const hashes = Object.create(null);
//TODO shorter?
const hash_length = 12;
export function safeBase64Hash(input: string) {
if (hashes[input]) {
return hashes[input];
}
//TODO if performance really matters, use a faster one like xx-hash etc.
// should be evenly distributed because short input length and similarities in paths could cause collisions otherwise
// OR DON'T USE A HASH AT ALL, what about a simple counter?
const md5 = crypto.createHash('md5');
md5.update(input);
const hash = toSafe(md5.digest('base64')).slice(0, hash_length);
hashes[input] = hash;
return hash;
}
const replacements: { [key: string]: string } = {
'+': '-',
'/': '_',
'=': ''
};
const replaceRE = new RegExp(`[${Object.keys(replacements).join('')}]`, 'g');
function toSafe(base64: string) {
return base64.replace(replaceRE, (x) => replacements[x]);
}

@ -0,0 +1,187 @@
/* eslint-disable no-unused-vars */
import { createFilter } from 'vite';
import { Arrayable, ResolvedOptions } from './options';
import { normalizePath } from 'vite';
import * as fs from 'fs';
//eslint-disable-next-line node/no-missing-import
import { CompileOptions } from 'svelte/types/compiler/interfaces';
import { log } from './log';
const VITE_FS_PREFIX = '/@fs/';
const IS_WINDOWS = process.platform === 'win32';
const SUPPORTED_COMPILER_OPTIONS = [
'generate',
'dev',
'css',
'hydratable',
'customElement',
'immutable',
'enableSourcemap'
];
const TYPES_WITH_COMPILER_OPTIONS = ['style', 'script', 'all'];
export type SvelteQueryTypes = 'style' | 'script' | 'preprocessed' | 'all';
export interface RequestQuery {
// our own
svelte?: boolean;
type?: SvelteQueryTypes;
sourcemap?: boolean;
compilerOptions?: Pick<
CompileOptions,
'generate' | 'dev' | 'css' | 'hydratable' | 'customElement' | 'immutable' | 'enableSourcemap'
>;
// vite specific
url?: boolean;
raw?: boolean;
direct?: boolean;
}
export interface SvelteRequest {
id: string;
cssId: string;
filename: string;
normalizedFilename: string;
query: RequestQuery;
timestamp: number;
ssr: boolean;
raw: boolean;
}
function splitId(id: string) {
const parts = id.split(`?`, 2);
const filename = parts[0];
const rawQuery = parts[1];
return { filename, rawQuery };
}
function parseToSvelteRequest(
id: string,
filename: string,
rawQuery: string,
root: string,
timestamp: number,
ssr: boolean
): SvelteRequest | undefined {
const query = parseRequestQuery(rawQuery);
const rawOrDirect = !!(query.raw || query.direct);
if (query.url || (!query.svelte && rawOrDirect)) {
// skip requests with special vite tags
return;
}
const raw = rawOrDirect;
const normalizedFilename = normalize(filename, root);
const cssId = createVirtualImportId(filename, root, 'style');
return {
id,
filename,
normalizedFilename,
cssId,
query,
timestamp,
ssr,
raw
};
}
function createVirtualImportId(filename: string, root: string, type: SvelteQueryTypes) {
const parts = ['svelte', `type=${type}`];
if (type === 'style') {
parts.push('lang.css');
}
if (existsInRoot(filename, root)) {
filename = root + filename;
} else if (filename.startsWith(VITE_FS_PREFIX)) {
filename = IS_WINDOWS
? filename.slice(VITE_FS_PREFIX.length) // remove /@fs/ from /@fs/C:/...
: filename.slice(VITE_FS_PREFIX.length - 1); // remove /@fs from /@fs/home/user
}
// return same virtual id format as vite-plugin-vue eg ...App.svelte?svelte&type=style&lang.css
return `${filename}?${parts.join('&')}`;
}
function parseRequestQuery(rawQuery: string): RequestQuery {
const query = Object.fromEntries(new URLSearchParams(rawQuery));
for (const key in query) {
if (query[key] === '') {
// @ts-ignore
query[key] = true;
}
}
const compilerOptions = query.compilerOptions;
if (compilerOptions) {
if (!((query.raw || query.direct) && TYPES_WITH_COMPILER_OPTIONS.includes(query.type))) {
throw new Error(
`Invalid compilerOptions in query ${rawQuery}. CompilerOptions are only supported for raw or direct queries with type in "${TYPES_WITH_COMPILER_OPTIONS.join(
', '
)}" e.g. '?svelte&raw&type=script&compilerOptions={"generate":"ssr","dev":false}`
);
}
try {
const parsed = JSON.parse(compilerOptions);
const invalid = Object.keys(parsed).filter(
(key) => !SUPPORTED_COMPILER_OPTIONS.includes(key)
);
if (invalid.length) {
throw new Error(
`Invalid compilerOptions in query ${rawQuery}: ${invalid.join(
', '
)}. Supported: ${SUPPORTED_COMPILER_OPTIONS.join(', ')}`
);
}
query.compilerOptions = parsed;
} catch (e) {
log.error('failed to parse request query compilerOptions', e);
throw e;
}
}
return query as RequestQuery;
}
/**
* posixify and remove root at start
*
* @param filename
* @param normalizedRoot
*/
function normalize(filename: string, normalizedRoot: string) {
return stripRoot(normalizePath(filename), normalizedRoot);
}
function existsInRoot(filename: string, root: string) {
if (filename.startsWith(VITE_FS_PREFIX)) {
return false; // vite already tagged it as out of root
}
return fs.existsSync(root + filename);
}
function stripRoot(normalizedFilename: string, normalizedRoot: string) {
return normalizedFilename.startsWith(normalizedRoot + '/')
? normalizedFilename.slice(normalizedRoot.length)
: normalizedFilename;
}
function buildFilter(
include: Arrayable<string> | undefined,
exclude: Arrayable<string> | undefined,
extensions: string[]
): (filename: string) => boolean {
const rollupFilter = createFilter(include, exclude);
return (filename) => rollupFilter(filename) && extensions.some((ext) => filename.endsWith(ext));
}
export type IdParser = (id: string, ssr: boolean, timestamp?: number) => SvelteRequest | undefined;
export function buildIdParser(options: ResolvedOptions): IdParser {
const { include, exclude, extensions, root } = options;
const normalizedRoot = normalizePath(root);
const filter = buildFilter(include, exclude, extensions!);
return (id, ssr, timestamp = Date.now()) => {
const { filename, rawQuery } = splitId(id);
if (filter(filename)) {
return parseToSvelteRequest(id, filename, rawQuery, normalizedRoot, timestamp, ssr);
}
};
}

@ -0,0 +1,132 @@
import { ResolvedOptions } from './options';
import fs from 'fs';
import { toRollupError } from './error';
import { log } from './log';
import type { SvelteRequest } from './id';
import { type CompileData, CompileSvelte } from './compile';
/**
* utility function to compile ?raw and ?direct requests in load hook
*/
export async function loadRaw(
svelteRequest: SvelteRequest,
compileSvelte: CompileSvelte,
options: ResolvedOptions
) {
const { id, filename, query } = svelteRequest;
// raw svelte subrequest, compile on the fly and return requested subpart
let compileData;
const source = fs.readFileSync(filename, 'utf-8');
try {
//avoid compileSvelte doing extra ssr stuff unless requested
svelteRequest.ssr = query.compilerOptions?.generate === 'ssr';
const type = query.type;
compileData = await compileSvelte(svelteRequest, source, {
...options,
// don't use dynamic vite-plugin-svelte defaults here to ensure stable result between ssr,dev and build
compilerOptions: {
dev: false,
css: false,
hydratable: false,
enableSourcemap: query.sourcemap
? {
js: type === 'script' || type === 'all',
css: type === 'style' || type === 'all'
}
: false,
...svelteRequest.query.compilerOptions
},
hot: false,
emitCss: true
});
} catch (e) {
throw toRollupError(e, options);
}
let result;
if (query.type === 'style') {
result = compileData.compiled.css;
} else if (query.type === 'script') {
result = compileData.compiled.js;
} else if (query.type === 'preprocessed') {
result = compileData.preprocessed;
} else if (query.type === 'all' && query.raw) {
return allToRawExports(compileData, source);
} else {
throw new Error(
`invalid "type=${query.type}" in ${id}. supported are script, style, preprocessed, all`
);
}
if (query.direct) {
const supportedDirectTypes = ['script', 'style'];
if (!supportedDirectTypes.includes(query.type)) {
throw new Error(
`invalid "type=${
query.type
}" combined with direct in ${id}. supported are: ${supportedDirectTypes.join(', ')}`
);
}
log.debug(`load returns direct result for ${id}`);
let directOutput = result.code;
if (query.sourcemap && result.map?.toUrl) {
const map = `sourceMappingURL=${result.map.toUrl()}`;
if (query.type === 'style') {
directOutput += `\n\n/*# ${map} */\n`;
} else if (query.type === 'script') {
directOutput += `\n\n//# ${map}\n`;
}
}
return directOutput;
} else if (query.raw) {
log.debug(`load returns raw result for ${id}`);
return toRawExports(result);
} else {
throw new Error(`invalid raw mode in ${id}, supported are raw, direct`);
}
}
/**
* turn compileData and source into a flat list of raw exports
*
* @param compileData
* @param source
*/
function allToRawExports(compileData: CompileData, source: string) {
// flatten CompileData
const exports: Partial<CompileData & { source: string }> = {
...compileData,
...compileData.compiled,
source
};
delete exports.compiled;
delete exports.filename; // absolute path, remove to avoid it in output
return toRawExports(exports);
}
/**
* turn object into raw exports.
*
* every prop is returned as a const export, and if prop 'code' exists it is additionally added as default export
*
* eg {'foo':'bar','code':'baz'} results in
*
* ```js
* export const code='baz'
* export const foo='bar'
* export default code
* ```
* @param object
*/
function toRawExports(object: object) {
let exports =
Object.entries(object)
//eslint-disable-next-line no-unused-vars
.filter(([key, value]) => typeof value !== 'function') // preprocess output has a toString function that's enumerable
.sort(([a], [b]) => (a < b ? -1 : a === b ? 0 : 1))
.map(([key, value]) => `export const ${key}=${JSON.stringify(value)}`)
.join('\n') + '\n';
if (Object.prototype.hasOwnProperty.call(object, 'code')) {
exports += `export default code\n`;
}
return exports;
}

@ -0,0 +1,115 @@
import { createRequire } from 'module';
import path from 'path';
import fs from 'fs';
import { pathToFileURL } from 'url';
import { log } from './log';
import { Options, SvelteOptions } from './options';
import { UserConfig } from 'vite';
// used to require cjs config in esm.
// NOTE dynamic import() cjs technically works, but timestamp query cache bust
// have no effect, likely because it has another internal cache?
let esmRequire: NodeRequire;
export const knownSvelteConfigNames = [
'svelte.config.js',
'svelte.config.cjs',
'svelte.config.mjs'
];
// hide dynamic import from ts transform to prevent it turning into a require
// see https://github.com/microsoft/TypeScript/issues/43329#issuecomment-811606238
// also use timestamp query to avoid caching on reload
const dynamicImportDefault = new Function(
'path',
'timestamp',
'return import(path + "?t=" + timestamp).then(m => m.default)'
);
export async function loadSvelteConfig(
viteConfig?: UserConfig,
inlineOptions?: Partial<Options>
): Promise<Partial<SvelteOptions> | undefined> {
if (inlineOptions?.configFile === false) {
return;
}
const configFile = findConfigToLoad(viteConfig, inlineOptions);
if (configFile) {
let err;
// try to use dynamic import for svelte.config.js first
if (configFile.endsWith('.js') || configFile.endsWith('.mjs')) {
try {
const result = await dynamicImportDefault(
pathToFileURL(configFile).href,
fs.statSync(configFile).mtimeMs
);
if (result != null) {
return {
...result,
configFile
};
} else {
throw new Error(`invalid export in ${configFile}`);
}
} catch (e) {
log.error(`failed to import config ${configFile}`, e);
err = e;
}
}
// cjs or error with dynamic import
if (!configFile.endsWith('.mjs')) {
try {
// identify which require function to use (esm and cjs mode)
const _require = import.meta.url
? (esmRequire ??= createRequire(import.meta.url))
: require;
// avoid loading cached version on reload
delete _require.cache[_require.resolve(configFile)];
const result = _require(configFile);
if (result != null) {
return {
...result,
configFile
};
} else {
throw new Error(`invalid export in ${configFile}`);
}
} catch (e) {
log.error(`failed to require config ${configFile}`, e);
if (!err) {
err = e;
}
}
}
// failed to load existing config file
throw err;
}
}
function findConfigToLoad(viteConfig?: UserConfig, inlineOptions?: Partial<Options>) {
const root = viteConfig?.root || process.cwd();
if (inlineOptions?.configFile) {
const abolutePath = path.isAbsolute(inlineOptions.configFile)
? inlineOptions.configFile
: path.resolve(root, inlineOptions.configFile);
if (!fs.existsSync(abolutePath)) {
throw new Error(`failed to find svelte config file ${abolutePath}.`);
}
return abolutePath;
} else {
const existingKnownConfigFiles = knownSvelteConfigNames
.map((candidate) => path.resolve(root, candidate))
.filter((file) => fs.existsSync(file));
if (existingKnownConfigFiles.length === 0) {
log.debug(`no svelte config found at ${root}`);
return;
} else if (existingKnownConfigFiles.length > 1) {
log.warn(
`found more than one svelte config file, using ${existingKnownConfigFiles[0]}. you should only have one!`,
existingKnownConfigFiles
);
}
return existingKnownConfigFiles[0];
}
}

@ -0,0 +1,221 @@
/* eslint-disable no-unused-vars,no-console */
import { cyan, yellow, red } from 'kleur/colors';
import debug from 'debug';
import { ResolvedOptions, Warning } from './options';
import { SvelteRequest } from './id';
const levels: string[] = ['debug', 'info', 'warn', 'error', 'silent'];
const prefix = 'vite-plugin-svelte';
const loggers: { [key: string]: any } = {
debug: {
log: debug(`vite:${prefix}`),
enabled: false,
isDebug: true
},
info: {
color: cyan,
log: console.log,
enabled: true
},
warn: {
color: yellow,
log: console.warn,
enabled: true
},
error: {
color: red,
log: console.error,
enabled: true
},
silent: {
enabled: false
}
};
let _level: string = 'info';
function setLevel(level: string) {
if (level === _level) {
return;
}
const levelIndex = levels.indexOf(level);
if (levelIndex > -1) {
_level = level;
for (let i = 0; i < levels.length; i++) {
loggers[levels[i]].enabled = i >= levelIndex;
}
} else {
_log(loggers.error, `invalid log level: ${level} `);
}
}
function _log(logger: any, message: string, payload?: any, namespace?: string) {
if (!logger.enabled) {
return;
}
if (logger.isDebug) {
const log = namespace ? logger.log.extend(namespace) : logger.log;
payload !== undefined ? log(message, payload) : log(message);
} else {
logger.log(
logger.color(
`${new Date().toLocaleTimeString()} [${prefix}${
namespace ? `:${namespace}` : ''
}] ${message}`
)
);
if (payload) {
logger.log(payload);
}
}
}
export interface LogFn {
(message: string, payload?: any, namespace?: string): void;
enabled: boolean;
once: (message: string, payload?: any, namespace?: string) => void;
}
function createLogger(level: string): LogFn {
const logger = loggers[level];
const logFn: LogFn = _log.bind(null, logger) as LogFn;
const logged = new Set<String>();
const once = function (message: string, payload?: any, namespace?: string) {
if (!logger.enabled || logged.has(message)) {
return;
}
logged.add(message);
logFn.apply(null, [message, payload, namespace]);
};
Object.defineProperty(logFn, 'enabled', {
get() {
return logger.enabled;
}
});
Object.defineProperty(logFn, 'once', {
get() {
return once;
}
});
return logFn;
}
export const log = {
debug: createLogger('debug'),
info: createLogger('info'),
warn: createLogger('warn'),
error: createLogger('error'),
setLevel
};
export type SvelteWarningsMessage = {
id: string;
filename: string;
normalizedFilename: string;
timestamp: number;
warnings: Warning[]; // allWarnings filtered by warnings where onwarn did not call the default handler
allWarnings: Warning[]; // includes warnings filtered by onwarn and our extra vite plugin svelte warnings
rawWarnings: Warning[]; // raw compiler output
};
export function logCompilerWarnings(
svelteRequest: SvelteRequest,
warnings: Warning[],
options: ResolvedOptions
) {
const { emitCss, onwarn, isBuild } = options;
const sendViaWS = !isBuild && options.experimental?.sendWarningsToBrowser;
let warn = isBuild ? warnBuild : warnDev;
const handledByDefaultWarn: Warning[] = [];
const notIgnored = warnings?.filter((w) => !ignoreCompilerWarning(w, isBuild, emitCss));
const extra = buildExtraWarnings(warnings, isBuild);
const allWarnings = [...notIgnored, ...extra];
if (sendViaWS) {
const _warn = warn;
warn = (w: Warning) => {
handledByDefaultWarn.push(w);
_warn(w);
};
}
allWarnings.forEach((warning) => {
if (onwarn) {
onwarn(warning, warn);
} else {
warn(warning);
}
});
if (sendViaWS) {
const message: SvelteWarningsMessage = {
id: svelteRequest.id,
filename: svelteRequest.filename,
normalizedFilename: svelteRequest.normalizedFilename,
timestamp: svelteRequest.timestamp,
warnings: handledByDefaultWarn, // allWarnings filtered by warnings where onwarn did not call the default handler
allWarnings, // includes warnings filtered by onwarn and our extra vite plugin svelte warnings
rawWarnings: warnings // raw compiler output
};
log.debug(`sending svelte:warnings message for ${svelteRequest.normalizedFilename}`);
options.server?.ws?.send('svelte:warnings', message);
}
}
function ignoreCompilerWarning(
warning: Warning,
isBuild: boolean,
emitCss: boolean | undefined
): boolean {
return (
(!emitCss && warning.code === 'css-unused-selector') || // same as rollup-plugin-svelte
(!isBuild && isNoScopableElementWarning(warning))
);
}
function isNoScopableElementWarning(warning: Warning) {
// see https://github.com/sveltejs/vite-plugin-svelte/issues/153
return warning.code === 'css-unused-selector' && warning.message.includes('"*"');
}
function buildExtraWarnings(warnings: Warning[], isBuild: boolean): Warning[] {
const extraWarnings = [];
if (!isBuild) {
const noScopableElementWarnings = warnings.filter((w) => isNoScopableElementWarning(w));
if (noScopableElementWarnings.length > 0) {
// in case there are multiple, use last one as that is the one caused by our *{} rule
const noScopableElementWarning =
noScopableElementWarnings[noScopableElementWarnings.length - 1];
extraWarnings.push({
...noScopableElementWarning,
code: 'vite-plugin-svelte-css-no-scopable-elements',
message: `No scopable elements found in template. If you're using global styles in the style tag, you should move it into an external stylesheet file and import it in JS. See https://github.com/sveltejs/vite-plugin-svelte/blob/main/docs/faq.md#where-should-i-put-my-global-styles.`
});
}
}
return extraWarnings;
}
function warnDev(w: Warning) {
log.info.enabled && log.info(buildExtendedLogMessage(w));
}
function warnBuild(w: Warning) {
log.warn.enabled && log.warn(buildExtendedLogMessage(w), w.frame);
}
export function buildExtendedLogMessage(w: Warning) {
const parts = [];
if (w.filename) {
parts.push(w.filename);
}
if (w.start) {
parts.push(':', w.start.line, ':', w.start.column);
}
if (w.message) {
if (parts.length > 0) {
parts.push(' ');
}
parts.push(w.message);
}
return parts.join('');
}
export function isDebugNamespaceEnabled(namespace: string) {
return debug.enabled(`vite:${prefix}:${namespace}`);
}

@ -0,0 +1,45 @@
import { promises as fs } from 'fs';
import path from 'path';
import { ResolvedOptions } from './options';
// List of options that changes the prebundling result
const PREBUNDLE_SENSITIVE_OPTIONS: (keyof ResolvedOptions)[] = [
'compilerOptions',
'configFile',
'experimental',
'extensions',
'ignorePluginPreprocessors',
'preprocess'
];
/**
* @returns Whether the Svelte metadata has changed
*/
export async function saveSvelteMetadata(cacheDir: string, options: ResolvedOptions) {
const svelteMetadata = generateSvelteMetadata(options);
const svelteMetadataPath = path.resolve(cacheDir, '_svelte_metadata.json');
const currentSvelteMetadata = JSON.stringify(svelteMetadata, (_, value) => {
// Handle preprocessors
return typeof value === 'function' ? value.toString() : value;
});
let existingSvelteMetadata: string | undefined;
try {
existingSvelteMetadata = await fs.readFile(svelteMetadataPath, 'utf8');
} catch {
// ignore
}
await fs.mkdir(cacheDir, { recursive: true });
await fs.writeFile(svelteMetadataPath, currentSvelteMetadata);
return currentSvelteMetadata !== existingSvelteMetadata;
}
function generateSvelteMetadata(options: ResolvedOptions) {
const metadata: Record<string, any> = {};
for (const key of PREBUNDLE_SENSITIVE_OPTIONS) {
metadata[key] = options[key];
}
return metadata;
}

@ -0,0 +1,761 @@
/* eslint-disable no-unused-vars */
import { ConfigEnv, normalizePath, ResolvedConfig, UserConfig, ViteDevServer } from 'vite';
import { isDebugNamespaceEnabled, log } from './log';
import { loadSvelteConfig } from './load-svelte-config';
import {
SVELTE_EXPORT_CONDITIONS,
SVELTE_HMR_IMPORTS,
SVELTE_IMPORTS,
SVELTE_RESOLVE_MAIN_FIELDS,
VITE_RESOLVE_MAIN_FIELDS
} from './constants';
// eslint-disable-next-line node/no-missing-import
import type { CompileOptions, Warning } from 'svelte/types/compiler/interfaces';
import type {
MarkupPreprocessor,
Preprocessor,
PreprocessorGroup,
Processed
// eslint-disable-next-line node/no-missing-import
} from 'svelte/types/compiler/preprocess';
import path from 'path';
import { esbuildSveltePlugin, facadeEsbuildSveltePluginName } from './esbuild';
import { addExtraPreprocessors } from './preprocess';
import deepmerge from 'deepmerge';
import {
crawlFrameworkPkgs,
isDepExcluded,
isDepExternaled,
isDepIncluded,
isDepNoExternaled
// eslint-disable-next-line node/no-missing-import
} from 'vitefu';
import { isCommonDepWithoutSvelteField } from './dependencies';
import { VitePluginSvelteStats } from './vite-plugin-svelte-stats';
import { VitePluginSvelteCache } from './vite-plugin-svelte-cache';
import type { InspectorOptions } from '../ui/inspector/options';
const allowedPluginOptions = new Set([
'include',
'exclude',
'emitCss',
'hot',
'ignorePluginPreprocessors',
'disableDependencyReinclusion',
'prebundleSvelteLibraries',
'inspector',
'experimental'
]);
const knownRootOptions = new Set(['extensions', 'compilerOptions', 'preprocess', 'onwarn']);
const allowedInlineOptions = new Set(['configFile', ...allowedPluginOptions, ...knownRootOptions]);
export function validateInlineOptions(inlineOptions?: Partial<Options>) {
const invalidKeys = Object.keys(inlineOptions || {}).filter(
(key) => !allowedInlineOptions.has(key)
);
if (invalidKeys.length) {
log.warn(`invalid plugin options "${invalidKeys.join(', ')}" in inline config`, inlineOptions);
}
}
function convertPluginOptions(config?: Partial<SvelteOptions>): Partial<Options> | undefined {
if (!config) {
return;
}
const invalidRootOptions = Object.keys(config).filter((key) => allowedPluginOptions.has(key));
if (invalidRootOptions.length > 0) {
throw new Error(
`Invalid options in svelte config. Move the following options into 'vitePlugin:{...}': ${invalidRootOptions.join(
', '
)}`
);
}
if (!config.vitePlugin) {
return config;
}
const pluginOptions = config.vitePlugin;
const pluginOptionKeys = Object.keys(pluginOptions);
const rootOptionsInPluginOptions = pluginOptionKeys.filter((key) => knownRootOptions.has(key));
if (rootOptionsInPluginOptions.length > 0) {
throw new Error(
`Invalid options in svelte config under vitePlugin:{...}', move them to the config root : ${rootOptionsInPluginOptions.join(
', '
)}`
);
}
const duplicateOptions = pluginOptionKeys.filter((key) =>
Object.prototype.hasOwnProperty.call(config, key)
);
if (duplicateOptions.length > 0) {
throw new Error(
`Invalid duplicate options in svelte config under vitePlugin:{...}', they are defined in root too and must only exist once: ${duplicateOptions.join(
', '
)}`
);
}
const unknownPluginOptions = pluginOptionKeys.filter((key) => !allowedPluginOptions.has(key));
if (unknownPluginOptions.length > 0) {
log.warn(
`ignoring unknown plugin options in svelte config under vitePlugin:{...}: ${unknownPluginOptions.join(
', '
)}`
);
unknownPluginOptions.forEach((unkownOption) => {
// @ts-ignore
delete pluginOptions[unkownOption];
});
}
const result: Options = {
...config,
...pluginOptions
};
// @ts-expect-error it exists
delete result.vitePlugin;
return result;
}
// used in config phase, merges the default options, svelte config, and inline options
export async function preResolveOptions(
inlineOptions: Partial<Options> = {},
viteUserConfig: UserConfig,
viteEnv: ConfigEnv
): Promise<PreResolvedOptions> {
const viteConfigWithResolvedRoot: UserConfig = {
...viteUserConfig,
root: resolveViteRoot(viteUserConfig)
};
const isBuild = viteEnv.command === 'build';
const defaultOptions: Partial<Options> = {
extensions: ['.svelte'],
emitCss: true,
prebundleSvelteLibraries: !isBuild
};
const svelteConfig = convertPluginOptions(
await loadSvelteConfig(viteConfigWithResolvedRoot, inlineOptions)
);
const extraOptions: Partial<PreResolvedOptions> = {
root: viteConfigWithResolvedRoot.root!,
isBuild,
isServe: viteEnv.command === 'serve',
isDebug: process.env.DEBUG != null
};
const merged = mergeConfigs<PreResolvedOptions>(
defaultOptions,
svelteConfig,
inlineOptions,
extraOptions
);
// configFile of svelteConfig contains the absolute path it was loaded from,
// prefer it over the possibly relative inline path
if (svelteConfig?.configFile) {
merged.configFile = svelteConfig.configFile;
}
return merged;
}
function mergeConfigs<T>(...configs: (Partial<T> | undefined)[]): T {
let result: Partial<T> = {};
for (const config of configs.filter((x) => x != null)) {
result = deepmerge<T>(result, config!, {
// replace arrays
arrayMerge: (target: any[], source: any[]) => source ?? target
});
}
return result as T;
}
// used in configResolved phase, merges a contextual default config, pre-resolved options, and some preprocessors.
// also validates the final config.
export function resolveOptions(
preResolveOptions: PreResolvedOptions,
viteConfig: ResolvedConfig,
cache: VitePluginSvelteCache
): ResolvedOptions {
const css = preResolveOptions.emitCss ? 'external' : 'injected';
const defaultOptions: Partial<Options> = {
hot: viteConfig.isProduction
? false
: {
injectCss: css === 'injected',
partialAccept: !!viteConfig.experimental?.hmrPartialAccept
},
compilerOptions: {
css,
dev: !viteConfig.isProduction
}
};
const extraOptions: Partial<ResolvedOptions> = {
root: viteConfig.root,
isProduction: viteConfig.isProduction
};
const merged = mergeConfigs<ResolvedOptions>(defaultOptions, preResolveOptions, extraOptions);
removeIgnoredOptions(merged);
handleDeprecatedOptions(merged);
addExtraPreprocessors(merged, viteConfig);
enforceOptionsForHmr(merged);
enforceOptionsForProduction(merged);
// mergeConfigs would mangle functions on the stats class, so do this afterwards
if (log.debug.enabled && isDebugNamespaceEnabled('stats')) {
merged.stats = new VitePluginSvelteStats(cache);
}
return merged;
}
function enforceOptionsForHmr(options: ResolvedOptions) {
if (options.hot) {
if (!options.compilerOptions.dev) {
log.warn('hmr is enabled but compilerOptions.dev is false, forcing it to true');
options.compilerOptions.dev = true;
}
if (options.emitCss) {
if (options.hot !== true && options.hot.injectCss) {
log.warn('hmr and emitCss are enabled but hot.injectCss is true, forcing it to false');
options.hot.injectCss = false;
}
const css = options.compilerOptions.css;
if (css === true || css === 'injected') {
const forcedCss = 'external';
log.warn(
`hmr and emitCss are enabled but compilerOptions.css is ${css}, forcing it to ${forcedCss}`
);
options.compilerOptions.css = forcedCss;
}
} else {
if (options.hot === true || !options.hot.injectCss) {
log.warn(
'hmr with emitCss disabled requires option hot.injectCss to be enabled, forcing it to true'
);
if (options.hot === true) {
options.hot = { injectCss: true };
} else {
options.hot.injectCss = true;
}
}
const css = options.compilerOptions.css;
if (!(css === true || css === 'injected')) {
const forcedCss = 'injected';
log.warn(
`hmr with emitCss disabled requires compilerOptions.css to be enabled, forcing it to ${forcedCss}`
);
options.compilerOptions.css = forcedCss;
}
}
}
}
function enforceOptionsForProduction(options: ResolvedOptions) {
if (options.isProduction) {
if (options.hot) {
log.warn('options.hot is enabled but does not work on production build, forcing it to false');
options.hot = false;
}
if (options.compilerOptions.dev) {
log.warn(
'you are building for production but compilerOptions.dev is true, forcing it to false'
);
options.compilerOptions.dev = false;
}
}
}
function removeIgnoredOptions(options: ResolvedOptions) {
const ignoredCompilerOptions = ['generate', 'format', 'filename'];
if (options.hot && options.emitCss) {
ignoredCompilerOptions.push('cssHash');
}
const passedCompilerOptions = Object.keys(options.compilerOptions || {});
const passedIgnored = passedCompilerOptions.filter((o) => ignoredCompilerOptions.includes(o));
if (passedIgnored.length) {
log.warn(
`The following Svelte compilerOptions are controlled by vite-plugin-svelte and essential to its functionality. User-specified values are ignored. Please remove them from your configuration: ${passedIgnored.join(
', '
)}`
);
passedIgnored.forEach((ignored) => {
// @ts-expect-error string access
delete options.compilerOptions[ignored];
});
}
}
function handleDeprecatedOptions(options: ResolvedOptions) {
const experimental = options.experimental as any;
if (experimental) {
for (const promoted of ['prebundleSvelteLibraries', 'inspector']) {
if (experimental[promoted]) {
//@ts-expect-error untyped assign
options[promoted] = experimental[promoted];
delete experimental[promoted];
log.warn(
`Option "vitePlugin.experimental.${promoted}" is no longer experimental and has moved to "vitePlugin.${promoted}". Please update your svelte config.`
);
}
}
if (experimental.generateMissingPreprocessorSourcemaps) {
log.warn('experimental.generateMissingPreprocessorSourcemaps has been removed.');
}
}
}
// vite passes unresolved `root`option to config hook but we need the resolved value, so do it here
// https://github.com/sveltejs/vite-plugin-svelte/issues/113
// https://github.com/vitejs/vite/blob/43c957de8a99bb326afd732c962f42127b0a4d1e/packages/vite/src/node/config.ts#L293
function resolveViteRoot(viteConfig: UserConfig): string | undefined {
return normalizePath(viteConfig.root ? path.resolve(viteConfig.root) : process.cwd());
}
export async function buildExtraViteConfig(
options: PreResolvedOptions,
config: UserConfig
): Promise<Partial<UserConfig>> {
// make sure we only readd vite default mainFields when no other plugin has changed the config already
// see https://github.com/sveltejs/vite-plugin-svelte/issues/581
if (!config.resolve) {
config.resolve = {};
}
config.resolve.mainFields = [
...SVELTE_RESOLVE_MAIN_FIELDS,
...(config.resolve.mainFields ?? VITE_RESOLVE_MAIN_FIELDS)
];
const extraViteConfig: Partial<UserConfig> = {
resolve: {
dedupe: [...SVELTE_IMPORTS, ...SVELTE_HMR_IMPORTS],
conditions: [...SVELTE_EXPORT_CONDITIONS]
}
// this option is still awaiting a PR in vite to be supported
// see https://github.com/sveltejs/vite-plugin-svelte/issues/60
// @ts-ignore
// knownJsSrcExtensions: options.extensions
};
const extraSvelteConfig = buildExtraConfigForSvelte(config);
const extraDepsConfig = await buildExtraConfigForDependencies(options, config);
// merge extra svelte and deps config, but make sure dep values are not contradicting svelte
extraViteConfig.optimizeDeps = {
include: [
...extraSvelteConfig.optimizeDeps.include,
...extraDepsConfig.optimizeDeps.include.filter(
(dep) => !isDepExcluded(dep, extraSvelteConfig.optimizeDeps.exclude)
)
],
exclude: [
...extraSvelteConfig.optimizeDeps.exclude,
...extraDepsConfig.optimizeDeps.exclude.filter(
(dep) => !isDepIncluded(dep, extraSvelteConfig.optimizeDeps.include)
)
]
};
extraViteConfig.ssr = {
external: [
...extraSvelteConfig.ssr.external,
...extraDepsConfig.ssr.external.filter(
(dep) => !isDepNoExternaled(dep, extraSvelteConfig.ssr.noExternal)
)
],
noExternal: [
...extraSvelteConfig.ssr.noExternal,
...extraDepsConfig.ssr.noExternal.filter(
(dep) => !isDepExternaled(dep, extraSvelteConfig.ssr.external)
)
]
};
// handle prebundling for svelte files
if (options.prebundleSvelteLibraries) {
extraViteConfig.optimizeDeps = {
...extraViteConfig.optimizeDeps,
// Experimental Vite API to allow these extensions to be scanned and prebundled
// @ts-ignore
extensions: options.extensions ?? ['.svelte'],
// Add esbuild plugin to prebundle Svelte files.
// Currently a placeholder as more information is needed after Vite config is resolved,
// the real Svelte plugin is added in `patchResolvedViteConfig()`
esbuildOptions: {
plugins: [{ name: facadeEsbuildSveltePluginName, setup: () => {} }]
}
};
}
// enable hmrPartialAccept if not explicitly disabled
if (
(options.hot == null ||
options.hot === true ||
(options.hot && options.hot.partialAccept !== false)) && // deviate from svelte-hmr, default to true
config.experimental?.hmrPartialAccept !== false
) {
log.debug('enabling "experimental.hmrPartialAccept" in vite config');
extraViteConfig.experimental = { hmrPartialAccept: true };
}
validateViteConfig(extraViteConfig, config, options);
return extraViteConfig;
}
function validateViteConfig(
extraViteConfig: Partial<UserConfig>,
config: UserConfig,
options: PreResolvedOptions
) {
const { prebundleSvelteLibraries, isBuild } = options;
if (prebundleSvelteLibraries) {
const isEnabled = (option: 'dev' | 'build' | boolean) =>
option !== true && option !== (isBuild ? 'build' : 'dev');
const logWarning = (name: string, value: 'dev' | 'build' | boolean, recommendation: string) =>
log.warn.once(
`Incompatible options: \`prebundleSvelteLibraries: true\` and vite \`${name}: ${JSON.stringify(
value
)}\` ${isBuild ? 'during build.' : '.'} ${recommendation}`
);
const viteOptimizeDepsDisabled = config.optimizeDeps?.disabled ?? 'build'; // fall back to vite default
const isOptimizeDepsEnabled = isEnabled(viteOptimizeDepsDisabled);
if (!isBuild && !isOptimizeDepsEnabled) {
logWarning(
'optimizeDeps.disabled',
viteOptimizeDepsDisabled,
'Forcing `optimizeDeps.disabled: "build"`. Disable prebundleSvelteLibraries or update your vite config to enable optimizeDeps during dev.'
);
extraViteConfig.optimizeDeps!.disabled = 'build';
} else if (isBuild && isOptimizeDepsEnabled) {
logWarning(
'optimizeDeps.disabled',
viteOptimizeDepsDisabled,
'Disable optimizeDeps or prebundleSvelteLibraries for build if you experience errors.'
);
}
}
}
async function buildExtraConfigForDependencies(options: PreResolvedOptions, config: UserConfig) {
// extra handling for svelte dependencies in the project
const depsConfig = await crawlFrameworkPkgs({
root: options.root,
isBuild: options.isBuild,
viteUserConfig: config,
isFrameworkPkgByJson(pkgJson) {
let hasSvelteCondition = false;
if (typeof pkgJson.exports === 'object') {
// use replacer as a simple way to iterate over nested keys
JSON.stringify(pkgJson.exports, (key, value) => {
if (SVELTE_EXPORT_CONDITIONS.includes(key)) {
hasSvelteCondition = true;
}
return value;
});
}
return hasSvelteCondition || !!pkgJson.svelte;
},
isSemiFrameworkPkgByJson(pkgJson) {
return !!pkgJson.dependencies?.svelte || !!pkgJson.peerDependencies?.svelte;
},
isFrameworkPkgByName(pkgName) {
const isNotSveltePackage = isCommonDepWithoutSvelteField(pkgName);
if (isNotSveltePackage) {
return false;
} else {
return undefined;
}
}
});
log.debug('extra config for dependencies generated by vitefu', depsConfig);
if (options.prebundleSvelteLibraries) {
// prebundling enabled, so we don't need extra dependency excludes
depsConfig.optimizeDeps.exclude = [];
// but keep dependency reinclusions of explicit user excludes
const userExclude = config.optimizeDeps?.exclude;
depsConfig.optimizeDeps.include = !userExclude
? []
: depsConfig.optimizeDeps.include.filter((dep: string) => {
// reincludes look like this: foo > bar > baz
// in case foo or bar are excluded, we have to retain the reinclude even with prebundling
return (
dep.includes('>') &&
dep
.split('>')
.slice(0, -1)
.some((d) => isDepExcluded(d.trim(), userExclude))
);
});
}
if (options.disableDependencyReinclusion === true) {
depsConfig.optimizeDeps.include = depsConfig.optimizeDeps.include.filter(
(dep) => !dep.includes('>')
);
} else if (Array.isArray(options.disableDependencyReinclusion)) {
const disabledDeps = options.disableDependencyReinclusion;
depsConfig.optimizeDeps.include = depsConfig.optimizeDeps.include.filter((dep) => {
if (!dep.includes('>')) return true;
const trimDep = dep.replace(/\s+/g, '');
return disabledDeps.some((disabled) => trimDep.includes(`${disabled}>`));
});
}
log.debug('post-processed extra config for dependencies', depsConfig);
return depsConfig;
}
function buildExtraConfigForSvelte(config: UserConfig) {
// include svelte imports for optimization unless explicitly excluded
const include: string[] = [];
const exclude: string[] = ['svelte-hmr'];
if (!isDepExcluded('svelte', config.optimizeDeps?.exclude ?? [])) {
const svelteImportsToInclude = SVELTE_IMPORTS.filter((x) => x !== 'svelte/ssr'); // not used on clientside
log.debug(
`adding bare svelte packages to optimizeDeps.include: ${svelteImportsToInclude.join(', ')} `
);
include.push(...svelteImportsToInclude);
} else {
log.debug('"svelte" is excluded in optimizeDeps.exclude, skipped adding it to include.');
}
const noExternal: (string | RegExp)[] = [];
const external: string[] = [];
// add svelte to ssr.noExternal unless it is present in ssr.external
// so we can resolve it with svelte/ssr
if (!isDepExternaled('svelte', config.ssr?.external ?? [])) {
noExternal.push('svelte', /^svelte\//);
}
return { optimizeDeps: { include, exclude }, ssr: { noExternal, external } };
}
export function patchResolvedViteConfig(viteConfig: ResolvedConfig, options: ResolvedOptions) {
if (options.preprocess) {
for (const preprocessor of arraify(options.preprocess)) {
if (preprocessor.style && '__resolvedConfig' in preprocessor.style) {
preprocessor.style.__resolvedConfig = viteConfig;
}
}
}
// replace facade esbuild plugin with a real one
const facadeEsbuildSveltePlugin = viteConfig.optimizeDeps.esbuildOptions?.plugins?.find(
(plugin) => plugin.name === facadeEsbuildSveltePluginName
);
if (facadeEsbuildSveltePlugin) {
Object.assign(facadeEsbuildSveltePlugin, esbuildSveltePlugin(options));
}
}
function arraify<T>(value: T | T[]): T[] {
return Array.isArray(value) ? value : [value];
}
export type Options = Omit<SvelteOptions, 'vitePlugin'> & PluginOptionsInline;
interface PluginOptionsInline extends PluginOptions {
/**
* Path to a svelte config file, either absolute or relative to Vite root
*
* set to `false` to ignore the svelte config file
*
* @see https://vitejs.dev/config/#root
*/
configFile?: string | false;
}
export interface PluginOptions {
/**
* A `picomatch` pattern, or array of patterns, which specifies the files the plugin should
* operate on. By default, all svelte files are included.
*
* @see https://github.com/micromatch/picomatch
*/
include?: Arrayable<string>;
/**
* A `picomatch` pattern, or array of patterns, which specifies the files to be ignored by the
* plugin. By default, no files are ignored.
*
* @see https://github.com/micromatch/picomatch
*/
exclude?: Arrayable<string>;
/**
* Emit Svelte styles as virtual CSS files for Vite and other plugins to process
*
* @default true
*/
emitCss?: boolean;
/**
* Enable or disable Hot Module Replacement.
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* DO NOT CUSTOMIZE SVELTE-HMR OPTIONS UNLESS YOU KNOW EXACTLY WHAT YOU ARE DOING
*
* YOU HAVE BEEN WARNED
*
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*
* Set an object to pass custom options to svelte-hmr
*
* @see https://github.com/rixo/svelte-hmr#options
* @default true for development, always false for production
*/
hot?: boolean | { injectCss?: boolean; partialAccept?: boolean; [key: string]: any };
/**
* Some Vite plugins can contribute additional preprocessors by defining `api.sveltePreprocess`.
* If you don't want to use them, set this to true to ignore them all or use an array of strings
* with plugin names to specify which.
*
* @default false
*/
ignorePluginPreprocessors?: boolean | string[];
/**
* vite-plugin-svelte automatically handles excluding svelte libraries and reinclusion of their dependencies
* in vite.optimizeDeps.
*
* `disableDependencyReinclusion: true` disables all reinclusions
* `disableDependencyReinclusion: ['foo','bar']` disables reinclusions for dependencies of foo and bar
*
* This should be used for hybrid packages that contain both node and browser dependencies, eg Routify
*
* @default false
*/
disableDependencyReinclusion?: boolean | string[];
/**
* Enable support for Vite's dependency optimization to prebundle Svelte libraries.
*
* To disable prebundling for a specific library, add it to `optimizeDeps.exclude`.
*
* @default true for dev, false for build
*/
prebundleSvelteLibraries?: boolean;
/**
* toggle/configure Svelte Inspector
*
* @default true
*/
inspector?: InspectorOptions | boolean;
/**
* These options are considered experimental and breaking changes to them can occur in any release
*/
experimental?: ExperimentalOptions;
}
export interface SvelteOptions {
/**
* A list of file extensions to be compiled by Svelte
*
* @default ['.svelte']
*/
extensions?: string[];
/**
* An array of preprocessors to transform the Svelte source code before compilation
*
* @see https://svelte.dev/docs#svelte_preprocess
*/
preprocess?: Arrayable<PreprocessorGroup>;
/**
* The options to be passed to the Svelte compiler. A few options are set by default,
* including `dev` and `css`. However, some options are non-configurable, like
* `filename`, `format`, `generate`, and `cssHash` (in dev).
*
* @see https://svelte.dev/docs#svelte_compile
*/
compilerOptions?: Omit<CompileOptions, 'filename' | 'format' | 'generate'>;
/**
* Handles warning emitted from the Svelte compiler
*/
onwarn?: (warning: Warning, defaultHandler?: (warning: Warning) => void) => void;
/**
* Options for vite-plugin-svelte
*/
vitePlugin?: PluginOptions;
}
/**
* These options are considered experimental and breaking changes to them can occur in any release
*/
export interface ExperimentalOptions {
/**
* A function to update `compilerOptions` before compilation
*
* `data.filename` - The file to be compiled
* `data.code` - The preprocessed Svelte code
* `data.compileOptions` - The current compiler options
*
* To change part of the compiler options, return an object with the changes you need.
*
* @example
* ```
* ({ filename, compileOptions }) => {
* // Dynamically set hydration per Svelte file
* if (compileWithHydratable(filename) && !compileOptions.hydratable) {
* return { hydratable: true };
* }
* }
* ```
*/
dynamicCompileOptions?: (data: {
filename: string;
code: string;
compileOptions: Partial<CompileOptions>;
}) => Promise<Partial<CompileOptions> | void> | Partial<CompileOptions> | void;
/**
* send a websocket message with svelte compiler warnings during dev
*
*/
sendWarningsToBrowser?: boolean;
/**
* disable svelte field resolve warnings
*
* @default false
*/
disableSvelteResolveWarnings?: boolean;
}
export interface PreResolvedOptions extends Options {
// these options are non-nullable after resolve
compilerOptions: CompileOptions;
experimental?: ExperimentalOptions;
// extra options
root: string;
isBuild: boolean;
isServe: boolean;
isDebug: boolean;
}
export interface ResolvedOptions extends PreResolvedOptions {
isProduction: boolean;
server?: ViteDevServer;
stats?: VitePluginSvelteStats;
}
export type {
CompileOptions,
Processed,
MarkupPreprocessor,
Preprocessor,
PreprocessorGroup,
Warning
};
export type ModuleFormat = NonNullable<CompileOptions['format']>;
export type CssHashGetter = NonNullable<CompileOptions['cssHash']>;
export type Arrayable<T> = T | T[];

@ -0,0 +1,102 @@
import type { ResolvedConfig, Plugin } from 'vite';
import MagicString from 'magic-string';
import { PreprocessorGroup, ResolvedOptions } from './options';
import { log } from './log';
import path from 'path';
/**
* this appends a *{} rule to component styles to force the svelte compiler to add style classes to all nodes
* That means adding/removing class rules from <style> node won't trigger js updates as the scope classes are not changed
*
* only used during dev with enabled css hmr
*/
export function createInjectScopeEverythingRulePreprocessorGroup(): PreprocessorGroup {
return {
style({ content, filename }) {
const s = new MagicString(content);
s.append(' *{}');
return {
code: s.toString(),
map: s.generateDecodedMap({
source: filename ? path.basename(filename) : undefined,
hires: true
})
};
}
};
}
function buildExtraPreprocessors(options: ResolvedOptions, config: ResolvedConfig) {
const prependPreprocessors: PreprocessorGroup[] = [];
const appendPreprocessors: PreprocessorGroup[] = [];
// @ts-ignore
const pluginsWithPreprocessorsDeprecated = config.plugins.filter((p) => p?.sveltePreprocess);
if (pluginsWithPreprocessorsDeprecated.length > 0) {
log.warn(
`The following plugins use the deprecated 'plugin.sveltePreprocess' field. Please contact their maintainers and ask them to move it to 'plugin.api.sveltePreprocess': ${pluginsWithPreprocessorsDeprecated
.map((p) => p.name)
.join(', ')}`
);
// patch plugin to avoid breaking
pluginsWithPreprocessorsDeprecated.forEach((p) => {
if (!p.api) {
p.api = {};
}
if (p.api.sveltePreprocess === undefined) {
// @ts-ignore
p.api.sveltePreprocess = p.sveltePreprocess;
} else {
log.error(
`ignoring plugin.sveltePreprocess of ${p.name} because it already defined plugin.api.sveltePreprocess.`
);
}
});
}
const pluginsWithPreprocessors: Plugin[] = config.plugins.filter((p) => p?.api?.sveltePreprocess);
const ignored: Plugin[] = [],
included: Plugin[] = [];
for (const p of pluginsWithPreprocessors) {
if (
options.ignorePluginPreprocessors === true ||
(Array.isArray(options.ignorePluginPreprocessors) &&
options.ignorePluginPreprocessors?.includes(p.name))
) {
ignored.push(p);
} else {
included.push(p);
}
}
if (ignored.length > 0) {
log.debug(
`Ignoring svelte preprocessors defined by these vite plugins: ${ignored
.map((p) => p.name)
.join(', ')}`
);
}
if (included.length > 0) {
log.debug(
`Adding svelte preprocessors defined by these vite plugins: ${included
.map((p) => p.name)
.join(', ')}`
);
appendPreprocessors.push(...pluginsWithPreprocessors.map((p) => p.api.sveltePreprocess));
}
return { prependPreprocessors, appendPreprocessors };
}
export function addExtraPreprocessors(options: ResolvedOptions, config: ResolvedConfig) {
const { prependPreprocessors, appendPreprocessors } = buildExtraPreprocessors(options, config);
if (prependPreprocessors.length > 0 || appendPreprocessors.length > 0) {
if (!options.preprocess) {
options.preprocess = [...prependPreprocessors, ...appendPreprocessors];
} else if (Array.isArray(options.preprocess)) {
options.preprocess.unshift(...prependPreprocessors);
options.preprocess.push(...appendPreprocessors);
} else {
options.preprocess = [...prependPreprocessors, options.preprocess, ...appendPreprocessors];
}
}
}

@ -0,0 +1,57 @@
import path from 'path';
import { builtinModules } from 'module';
import { resolveDependencyData, isCommonDepWithoutSvelteField } from './dependencies';
import { VitePluginSvelteCache } from './vite-plugin-svelte-cache';
import { normalizePath } from 'vite';
export async function resolveViaPackageJsonSvelte(
importee: string,
importer: string | undefined,
cache: VitePluginSvelteCache
): Promise<string | void> {
if (
importer &&
isBareImport(importee) &&
!isNodeInternal(importee) &&
!isCommonDepWithoutSvelteField(importee)
) {
const cached = cache.getResolvedSvelteField(importee, importer);
if (cached) {
return cached;
}
const pkgData = await resolveDependencyData(importee, importer);
if (pkgData) {
const { pkg, dir } = pkgData;
if (pkg.svelte) {
const result = normalizePath(path.resolve(dir, pkg.svelte));
cache.setResolvedSvelteField(importee, importer, result);
return result;
}
}
}
}
function isNodeInternal(importee: string) {
return importee.startsWith('node:') || builtinModules.includes(importee);
}
function isBareImport(importee: string): boolean {
if (
!importee ||
importee[0] === '.' ||
importee[0] === '\0' ||
importee.includes(':') ||
path.isAbsolute(importee)
) {
return false;
}
const parts = importee.split('/');
switch (parts.length) {
case 1:
return true;
case 2:
return parts[0].startsWith('@');
default:
return false;
}
}

@ -0,0 +1,73 @@
import path from 'path';
const IS_WINDOWS = process.platform === 'win32';
interface SourceMapFileRefs {
file?: string;
sources?: string[];
sourceRoot?: string;
}
/**
* convert absolute paths in sourcemap file refs to their relative equivalents to avoid leaking fs info
*
* map is modified in place.
*
* @param map sourcemap
* @param filename absolute path to file the sourcemap is for
*/
export function mapToRelative(map: SourceMapFileRefs | undefined, filename: string) {
if (!map) {
return;
}
const sourceRoot = map.sourceRoot;
const dirname = path.dirname(filename);
const toRelative = (s: string) => {
if (!s) {
return s;
}
let sourcePath: string;
if (s.startsWith('file:///')) {
// windows has file:///C:/foo and posix has file:///foo, so we have to remove one extra on windows
sourcePath = s.slice(IS_WINDOWS ? 8 : 7);
} else if (sourceRoot) {
const sep = sourceRoot[sourceRoot.length - 1] === '/' || s[0] === '/' ? '' : '/';
sourcePath = `${sourceRoot}${sep}${s}`;
} else {
sourcePath = s;
}
return path.isAbsolute(sourcePath) ? path.relative(dirname, sourcePath) : sourcePath;
};
if (map.file) {
map.file = path.basename(filename);
}
if (map.sources) {
map.sources = map.sources.map(toRelative);
}
if (map.sourceRoot) {
// we have prepended sourceRoot and computed relative paths from it
// remove it here to avoid downstream processing prepending it again
delete map.sourceRoot;
}
}
/**
* vitePreprocess uses an extra lang extension to tell vite about the type of preprocessor to use
* This function removes it afterwards to get back working file refs
*
* map is modified in place.
*
* @param map the output sourcemap
* @param suffix the suffix to remove
*/
export function removeLangSuffix(map: SourceMapFileRefs | undefined, suffix: string) {
if (!map) {
return;
}
const removeSuffix = (s: string) => (s?.endsWith(suffix) ? s.slice(0, -1 * suffix.length) : s);
if (map.file) {
map.file = removeSuffix(map.file);
}
if (map.sources) {
map.sources = map.sources.map(removeSuffix);
}
}

@ -0,0 +1,37 @@
import { VERSION } from 'svelte/compiler';
const svelteVersion = parseVersion(VERSION);
export function parseVersion(version: string): number[] {
const segments = version.split('.', 3).map((s) => parseInt(s, 10));
while (segments.length < 3) {
segments.push(0);
}
return segments;
}
/**
* compare version with current svelte, only takes major.minor.patch into account.
* If you don't pass all three, values will be filled with 0, ie `3` is equal to `3.0.0`
* @param version
* @returns 1 if passed version is larger than current, 0 if it is equal and -1 if it is lower
*/
export function compareToSvelte(version: string): 1 | 0 | -1 {
const parsedVersion = parseVersion(version);
for (let i = 0; i < svelteVersion.length; i++) {
const a = parsedVersion[i];
const b = svelteVersion[i];
if (a === b) {
continue;
} else if (a > b) {
return 1;
} else {
return -1;
}
}
return 0;
}
export function atLeastSvelte(version: string) {
const result = compareToSvelte(version) <= 0;
return result;
}

@ -0,0 +1,182 @@
import { SvelteRequest } from './id';
import { Code, CompileData } from './compile';
import { readFileSync } from 'fs';
import { dirname } from 'path';
//eslint-disable-next-line node/no-missing-import
import { findClosestPkgJsonPath } from 'vitefu';
import { normalizePath } from 'vite';
interface PackageInfo {
name: string;
version: string;
svelte?: string;
path: string;
}
export class VitePluginSvelteCache {
private _css = new Map<string, Code>();
private _js = new Map<string, Code>();
private _dependencies = new Map<string, string[]>();
private _dependants = new Map<string, Set<string>>();
private _resolvedSvelteFields = new Map<string, string>();
private _errors = new Map<string, any>();
private _packageInfos: PackageInfo[] = [];
public update(compileData: CompileData) {
this._errors.delete(compileData.normalizedFilename);
this.updateCSS(compileData);
this.updateJS(compileData);
this.updateDependencies(compileData);
}
public has(svelteRequest: SvelteRequest) {
const id = svelteRequest.normalizedFilename;
return this._errors.has(id) || this._js.has(id) || this._css.has(id);
}
public setError(svelteRequest: SvelteRequest, error: any) {
// keep dependency info, otherwise errors in dependants would not trigger an update after fixing
// because they are no longer watched
this.remove(svelteRequest, true);
this._errors.set(svelteRequest.normalizedFilename, error);
}
private updateCSS(compileData: CompileData) {
this._css.set(compileData.normalizedFilename, compileData.compiled.css);
}
private updateJS(compileData: CompileData) {
if (!compileData.ssr) {
// do not cache SSR js
this._js.set(compileData.normalizedFilename, compileData.compiled.js);
}
}
private updateDependencies(compileData: CompileData) {
const id = compileData.normalizedFilename;
const prevDependencies = this._dependencies.get(id) || [];
const dependencies = compileData.dependencies;
this._dependencies.set(id, dependencies);
const removed = prevDependencies.filter((d) => !dependencies.includes(d));
const added = dependencies.filter((d) => !prevDependencies.includes(d));
added.forEach((d) => {
if (!this._dependants.has(d)) {
this._dependants.set(d, new Set<string>());
}
this._dependants.get(d)!.add(compileData.filename);
});
removed.forEach((d) => {
this._dependants.get(d)!.delete(compileData.filename);
});
}
public remove(svelteRequest: SvelteRequest, keepDependencies: boolean = false): boolean {
const id = svelteRequest.normalizedFilename;
let removed = false;
if (this._errors.delete(id)) {
removed = true;
}
if (this._js.delete(id)) {
removed = true;
}
if (this._css.delete(id)) {
removed = true;
}
if (!keepDependencies) {
const dependencies = this._dependencies.get(id);
if (dependencies) {
removed = true;
dependencies.forEach((d) => {
const dependants = this._dependants.get(d);
if (dependants && dependants.has(svelteRequest.filename)) {
dependants.delete(svelteRequest.filename);
}
});
this._dependencies.delete(id);
}
}
return removed;
}
public getCSS(svelteRequest: SvelteRequest) {
return this._css.get(svelteRequest.normalizedFilename);
}
public getJS(svelteRequest: SvelteRequest) {
if (!svelteRequest.ssr) {
// SSR js isn't cached
return this._js.get(svelteRequest.normalizedFilename);
}
}
public getError(svelteRequest: SvelteRequest) {
return this._errors.get(svelteRequest.normalizedFilename);
}
public getDependants(path: string): string[] {
const dependants = this._dependants.get(path);
return dependants ? [...dependants] : [];
}
public getResolvedSvelteField(name: string, importer?: string): string | void {
return this._resolvedSvelteFields.get(this._getResolvedSvelteFieldKey(name, importer));
}
public hasResolvedSvelteField(name: string, importer?: string) {
return this._resolvedSvelteFields.has(this._getResolvedSvelteFieldKey(name, importer));
}
public setResolvedSvelteField(
importee: string,
importer: string | undefined = undefined,
resolvedSvelte: string
) {
this._resolvedSvelteFields.set(
this._getResolvedSvelteFieldKey(importee, importer),
resolvedSvelte
);
}
private _getResolvedSvelteFieldKey(importee: string, importer?: string): string {
return importer ? `${importer} > ${importee}` : importee;
}
public async getPackageInfo(file: string): Promise<PackageInfo> {
let info = this._packageInfos.find((pi) => file.startsWith(pi.path));
if (!info) {
info = await findPackageInfo(file);
this._packageInfos.push(info);
}
return info;
}
}
/**
* utility to get some info from the closest package.json with a "name" set
*
* @param {string} file to find info for
* @returns {PackageInfo}
*/
async function findPackageInfo(file: string): Promise<PackageInfo> {
const info: PackageInfo = {
name: '$unknown',
version: '0.0.0-unknown',
path: '$unknown'
};
let path = await findClosestPkgJsonPath(file, (pkgPath) => {
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
if (pkg.name != null) {
info.name = pkg.name;
if (pkg.version != null) {
info.version = pkg.version;
}
info.svelte = pkg.svelte;
return true;
}
return false;
});
// return normalized path with appended '/' so .startsWith works for future file checks
path = normalizePath(dirname(path ?? file)) + '/';
info.path = path;
return info;
}

@ -0,0 +1,195 @@
import { log } from './log';
import { performance } from 'perf_hooks';
import { normalizePath } from 'vite';
import { VitePluginSvelteCache } from './vite-plugin-svelte-cache';
interface Stat {
file: string;
pkg?: string;
start: number;
end: number;
}
export interface StatCollection {
name: string;
options: CollectionOptions;
//eslint-disable-next-line no-unused-vars
start: (file: string) => () => void;
stats: Stat[];
packageStats?: PackageStats[];
collectionStart: number;
duration?: number;
finish: () => Promise<void> | void;
finished: boolean;
}
interface PackageStats {
pkg: string;
files: number;
duration: number;
}
export interface CollectionOptions {
//eslint-disable-next-line no-unused-vars
logInProgress: (collection: StatCollection, now: number) => boolean;
//eslint-disable-next-line no-unused-vars
logResult: (collection: StatCollection) => boolean;
}
const defaultCollectionOptions: CollectionOptions = {
// log after 500ms and more than one file processed
logInProgress: (c, now) => now - c.collectionStart > 500 && c.stats.length > 1,
// always log results
logResult: () => true
};
function humanDuration(n: number) {
// 99.9ms 0.10s
return n < 100 ? `${n.toFixed(1)}ms` : `${(n / 1000).toFixed(2)}s`;
}
function formatPackageStats(pkgStats: PackageStats[]) {
const statLines = pkgStats.map((pkgStat) => {
const duration = pkgStat.duration;
const avg = duration / pkgStat.files;
return [pkgStat.pkg, `${pkgStat.files}`, humanDuration(duration), humanDuration(avg)];
});
statLines.unshift(['package', 'files', 'time', 'avg']);
const columnWidths = statLines.reduce(
(widths: number[], row) => {
for (let i = 0; i < row.length; i++) {
const cell = row[i];
if (widths[i] < cell.length) {
widths[i] = cell.length;
}
}
return widths;
},
statLines[0].map(() => 0)
);
const table = statLines
.map((row: string[]) =>
row
.map((cell: string, i: number) => {
if (i === 0) {
return cell.padEnd(columnWidths[i], ' ');
} else {
return cell.padStart(columnWidths[i], ' ');
}
})
.join('\t')
)
.join('\n');
return table;
}
export class VitePluginSvelteStats {
// package directory -> package name
private _cache: VitePluginSvelteCache;
private _collections: StatCollection[] = [];
constructor(cache: VitePluginSvelteCache) {
this._cache = cache;
}
startCollection(name: string, opts?: Partial<CollectionOptions>) {
const options = {
...defaultCollectionOptions,
...opts
};
const stats: Stat[] = [];
const collectionStart = performance.now();
const _this = this;
let hasLoggedProgress = false;
const collection: StatCollection = {
name,
options,
stats,
collectionStart,
finished: false,
start(file) {
if (collection.finished) {
throw new Error('called after finish() has been used');
}
file = normalizePath(file);
const start = performance.now();
const stat: Stat = { file, start, end: start };
return () => {
const now = performance.now();
stat.end = now;
stats.push(stat);
if (!hasLoggedProgress && options.logInProgress(collection, now)) {
hasLoggedProgress = true;
log.debug(`${name} in progress ...`, undefined, 'stats');
}
};
},
async finish() {
await _this._finish(collection);
}
};
_this._collections.push(collection);
return collection;
}
public async finishAll() {
await Promise.all(this._collections.map((c) => c.finish()));
}
private async _finish(collection: StatCollection) {
try {
collection.finished = true;
const now = performance.now();
collection.duration = now - collection.collectionStart;
const logResult = collection.options.logResult(collection);
if (logResult) {
await this._aggregateStatsResult(collection);
log.debug(
`${collection.name} done.\n${formatPackageStats(collection.packageStats!)}`,
undefined,
'stats'
);
}
// cut some ties to free it for garbage collection
const index = this._collections.indexOf(collection);
this._collections.splice(index, 1);
collection.stats.length = 0;
collection.stats = [];
if (collection.packageStats) {
collection.packageStats.length = 0;
collection.packageStats = [];
}
collection.start = () => () => {};
collection.finish = () => {};
} catch (e) {
// this should not happen, but stats taking also should not break the process
log.debug.once(`failed to finish stats for ${collection.name}\n`, e, 'stats');
}
}
private async _aggregateStatsResult(collection: StatCollection) {
const stats = collection.stats;
for (const stat of stats) {
stat.pkg = (await this._cache.getPackageInfo(stat.file)).name;
}
// group stats
const grouped: { [key: string]: PackageStats } = {};
stats.forEach((stat) => {
const pkg = stat.pkg!;
let group = grouped[pkg];
if (!group) {
group = grouped[pkg] = {
files: 0,
duration: 0,
pkg
};
}
group.files += 1;
group.duration += stat.end - stat.start;
});
const groups = Object.values(grouped);
groups.sort((a, b) => b.duration - a.duration);
collection.packageStats = groups;
}
}

@ -0,0 +1,110 @@
import { VitePluginSvelteCache } from './vite-plugin-svelte-cache';
import fs from 'fs';
import { log } from './log';
import { IdParser } from './id';
import { ResolvedOptions } from './options';
import { knownSvelteConfigNames } from './load-svelte-config';
import path from 'path';
import { FSWatcher } from 'vite';
export function setupWatchers(
options: ResolvedOptions,
cache: VitePluginSvelteCache,
requestParser: IdParser
) {
const { server, configFile: svelteConfigFile } = options;
if (!server) {
return;
}
const { watcher, ws } = server;
const { root, server: serverConfig } = server.config;
const emitChangeEventOnDependants = (filename: string) => {
const dependants = cache.getDependants(filename);
dependants.forEach((dependant) => {
if (fs.existsSync(dependant)) {
log.debug(
`emitting virtual change event for "${dependant}" because depdendency "${filename}" changed`
);
watcher.emit('change', dependant);
}
});
};
const removeUnlinkedFromCache = (filename: string) => {
const svelteRequest = requestParser(filename, false);
if (svelteRequest) {
const removedFromCache = cache.remove(svelteRequest);
if (removedFromCache) {
log.debug(`cleared VitePluginSvelteCache for deleted file ${filename}`);
}
}
};
const triggerViteRestart = (filename: string) => {
if (serverConfig.middlewareMode) {
// in middlewareMode we can't restart the server automatically
// show the user an overlay instead
const message =
'Svelte config change detected, restart your dev process to apply the changes.';
log.info(message, filename);
ws.send({
type: 'error',
err: { message, stack: '', plugin: 'vite-plugin-svelte', id: filename }
});
} else {
log.info(`svelte config changed: restarting vite server. - file: ${filename}`);
server.restart();
}
};
// collection of watcher listeners by event
const listenerCollection = {
add: [] as Array<Function>,
change: [emitChangeEventOnDependants],
unlink: [removeUnlinkedFromCache, emitChangeEventOnDependants]
};
if (svelteConfigFile !== false) {
// configFile false means we ignore the file and external process is responsible
const possibleSvelteConfigs = knownSvelteConfigNames.map((cfg) => path.join(root, cfg));
const restartOnConfigAdd = (filename: string) => {
if (possibleSvelteConfigs.includes(filename)) {
triggerViteRestart(filename);
}
};
const restartOnConfigChange = (filename: string) => {
if (filename === svelteConfigFile) {
triggerViteRestart(filename);
}
};
if (svelteConfigFile) {
listenerCollection.change.push(restartOnConfigChange);
listenerCollection.unlink.push(restartOnConfigChange);
} else {
listenerCollection.add.push(restartOnConfigAdd);
}
}
Object.entries(listenerCollection).forEach(([evt, listeners]) => {
if (listeners.length > 0) {
watcher.on(evt, (filename) => listeners.forEach((listener) => listener(filename)));
}
});
}
// taken from vite utils
export function ensureWatchedFile(watcher: FSWatcher, file: string | null, root: string): void {
if (
file &&
// only need to watch if out of root
!file.startsWith(root + '/') &&
// some rollup plugins use null bytes for private resolved Ids
!file.includes('\0') &&
fs.existsSync(file)
) {
// resolve file to normalized system path
watcher.add(path.resolve(file));
}
}

@ -0,0 +1 @@
../../magic-string@0.30.0/node_modules/magic-string

@ -0,0 +1 @@
../../svelte-hmr@0.15.1_svelte@3.59.1/node_modules/svelte-hmr

@ -0,0 +1 @@
../../vitefu@0.2.4_vite@4.3.8/node_modules/vitefu

@ -0,0 +1,374 @@
# Changelog
## \[1.3.0]
- Return correct type for ` event.payload ` in `onResized` and `onMoved` window event handlers.
- [0b46637e](https://www.github.com/tauri-apps/tauri/commit/0b46637ebaba54403afa32a1cb466f09df2db999) fix(api): construct correct object for onResized and onMoved, closes [#6507](https://www.github.com/tauri-apps/tauri/pull/6507) ([#6509](https://www.github.com/tauri-apps/tauri/pull/6509)) on 2023-04-03
- Added the `WindowOptions::contentProtected` option and `WebviewWindow#setContentProtected` to change it at runtime.
- [4ab5545b](https://www.github.com/tauri-apps/tauri/commit/4ab5545b7a831c549f3c65e74de487ede3ab7ce5) feat: add content protection api, closes [#5132](https://www.github.com/tauri-apps/tauri/pull/5132) ([#5513](https://www.github.com/tauri-apps/tauri/pull/5513)) on 2022-12-13
- Allow setting the text of the dialog buttons.
- [00e1efaa](https://www.github.com/tauri-apps/tauri/commit/00e1efaa9b33876d41dd360624b69971e70d3856) feat: customize button texts of message dialog ([#4383](https://www.github.com/tauri-apps/tauri/pull/4383)) on 2022-12-28
- Add `is_minimized()` window method.
- [62144ef3](https://www.github.com/tauri-apps/tauri/commit/62144ef3be63b237869e511826edfb938e2c7174) feat: add is_minimized (fix [#3878](https://www.github.com/tauri-apps/tauri/pull/3878)) ([#5618](https://www.github.com/tauri-apps/tauri/pull/5618)) on 2022-12-13
- Add `title` getter on window.
- [233e43b0](https://www.github.com/tauri-apps/tauri/commit/233e43b0c34fada1ca025378533a0b76931a6540) feat: add `title` getter on window, closes [#5023](https://www.github.com/tauri-apps/tauri/pull/5023) ([#5515](https://www.github.com/tauri-apps/tauri/pull/5515)) on 2022-12-13
## \[1.2.0]
- Added the `acceptFirstMouse` window option.
- [95f467ad](https://www.github.com/tauri-apps/tauri/commit/95f467add51448319983c54e2f382c7c09fb72d6) feat(core): add window `accept_first_mouse` option, closes [#5347](https://www.github.com/tauri-apps/tauri/pull/5347) ([#5374](https://www.github.com/tauri-apps/tauri/pull/5374)) on 2022-10-17
- Fix incorrect return type on `fs/exists`
- [ca3cd8b3](https://www.github.com/tauri-apps/tauri/commit/ca3cd8b3d11beb9b6102da40b7d27f6dbe6cd2d0) fix(api): fs/exists return type previously set to void when it should be boolean ([#5252](https://www.github.com/tauri-apps/tauri/pull/5252)) on 2022-09-29
- Initialize `Monitor` instances with the correct classes for `position` and `size` fields instead of plain object.
- [6f41a271](https://www.github.com/tauri-apps/tauri/commit/6f41a2712445ac41a5ed84bbcd40af3b76c8b1d8) fix(api.js): fix `Monitor` initialization, closes [#4672](https://www.github.com/tauri-apps/tauri/pull/4672) ([#5314](https://www.github.com/tauri-apps/tauri/pull/5314)) on 2022-09-30
- **Breaking change:** Node.js v12 is no longer supported.
- [1129f4f5](https://www.github.com/tauri-apps/tauri/commit/1129f4f575dd02f746abe8e66472c88c8f9fe63d) refactor: simplify api.js bundling ([#4277](https://www.github.com/tauri-apps/tauri/pull/4277)) on 2022-10-04
- Add new app-specific `BaseDirectory` enum variants `AppConfig`, `AppData`, `AppLocalData`, `AppCache` and `AppLog` along with equivalent functions in `path` module and deprecated ambiguous variants `Log` and `App` along with their equivalent functions in `path` module.
- [5d89905e](https://www.github.com/tauri-apps/tauri/commit/5d89905e39ce0e6eaaec50a693679335449edb32) feat(api): add app-specific directory APIs, closes [#5263](https://www.github.com/tauri-apps/tauri/pull/5263) ([#5272](https://www.github.com/tauri-apps/tauri/pull/5272)) on 2022-09-28
- Fix `dialog.save` return type
- [8357ce5b](https://www.github.com/tauri-apps/tauri/commit/8357ce5b2efdd6f92c7944822542e48ba0e303ce) Fix dialog.save return type ([#5373](https://www.github.com/tauri-apps/tauri/pull/5373)) on 2022-10-08
- Added support to `FormData` on the `Body.form` function.
- [aa119f28](https://www.github.com/tauri-apps/tauri/commit/aa119f28364f8ffbc64c6bcdfc77483613076a20) feat(api): add FormData support on Body.form, closes [#5545](https://www.github.com/tauri-apps/tauri/pull/5545) ([#5546](https://www.github.com/tauri-apps/tauri/pull/5546)) on 2022-11-04
- Added `show` and `hide` methods on the `app` module.
- [39bf895b](https://www.github.com/tauri-apps/tauri/commit/39bf895b73ec6b53f5758815396ba85dda6b9c67) feat(macOS): Add application `show` and `hide` methods ([#3689](https://www.github.com/tauri-apps/tauri/pull/3689)) on 2022-10-03
- Added `tabbingIdentifier` window option for macOS.
- [4137ab44](https://www.github.com/tauri-apps/tauri/commit/4137ab44a81d739556cbc7583485887e78952bf1) feat(macos): add `tabbing_identifier` option, closes [#2804](https://www.github.com/tauri-apps/tauri/pull/2804), [#3912](https://www.github.com/tauri-apps/tauri/pull/3912) ([#5399](https://www.github.com/tauri-apps/tauri/pull/5399)) on 2022-10-19
- Added `tabbing_identifier` to the window builder on macOS.
- [4137ab44](https://www.github.com/tauri-apps/tauri/commit/4137ab44a81d739556cbc7583485887e78952bf1) feat(macos): add `tabbing_identifier` option, closes [#2804](https://www.github.com/tauri-apps/tauri/pull/2804), [#3912](https://www.github.com/tauri-apps/tauri/pull/3912) ([#5399](https://www.github.com/tauri-apps/tauri/pull/5399)) on 2022-10-19
- Added the `user_agent` option when creating a window.
- [a6c94119](https://www.github.com/tauri-apps/tauri/commit/a6c94119d8545d509723b147c273ca5edfe3729f) feat(core): expose user_agent to window config ([#5317](https://www.github.com/tauri-apps/tauri/pull/5317)) on 2022-10-02
## \[1.1.0]
- Update `mockIPC()` handler signature to allow async handler functions.
- [4fa968dc](https://www.github.com/tauri-apps/tauri/commit/4fa968dc0e74b5206bfcd54e704d180c16b67b08) fix(api): add async `mockIPC()` handler signature ([#5056](https://www.github.com/tauri-apps/tauri/pull/5056)) on 2022-08-26
- Improve shell's `Command`, `Command.stdout` and `Command.stderr` events with new `once`, `off`, `listenerCount`, `prependListener`, `prependOnceListener` and `removeAllListeners` functions.
- [aa9f1243](https://www.github.com/tauri-apps/tauri/commit/aa9f1243e6c1629972a82e469f20c8399741740e) Improved EventEmitter for tauri api shell ([#4697](https://www.github.com/tauri-apps/tauri/pull/4697)) on 2022-07-26
- Added the `encoding` option to the `Command` options.
- [d8cf9f9f](https://www.github.com/tauri-apps/tauri/commit/d8cf9f9fcd617ac24fa418952fd4a32c08804f5c) Command support for specified character encoding, closes [#4644](https://www.github.com/tauri-apps/tauri/pull/4644) ([#4772](https://www.github.com/tauri-apps/tauri/pull/4772)) on 2022-07-28
- Add `exists` function to the fs module.
- [3c62dbc9](https://www.github.com/tauri-apps/tauri/commit/3c62dbc902c904d35a7472ce72a969084c95fbbe) feat(api): Add `exists` function to the fs module. ([#5060](https://www.github.com/tauri-apps/tauri/pull/5060)) on 2022-09-15
## \[1.0.2]
- Added helper functions to listen to updater and window events.
- [b02fc90f](https://www.github.com/tauri-apps/tauri/commit/b02fc90f450ff9e9d8a35ee55dc1beced4957869) feat(api): add abstractions to updater and window event listeners ([#4569](https://www.github.com/tauri-apps/tauri/pull/4569)) on 2022-07-05
- Add support to `ArrayBuffer` in `Body.bytes` and `writeBinaryFile`.
- [92aca55a](https://www.github.com/tauri-apps/tauri/commit/92aca55a6f1f899d5c0c3a6aae9ac9cb0a7e9a86) feat(api): add support to ArrayBuffer ([#4579](https://www.github.com/tauri-apps/tauri/pull/4579)) on 2022-07-05
- Use `toString()` on message/confirm/ask dialogs title and message values.
- [b8cd2a79](https://www.github.com/tauri-apps/tauri/commit/b8cd2a7993cd2aa5b71b30c545b3307245d254bf) feat(api): call `toString()` on dialog title and message, closes [#4583](https://www.github.com/tauri-apps/tauri/pull/4583) ([#4588](https://www.github.com/tauri-apps/tauri/pull/4588)) on 2022-07-04
- Remove the `type-fest` dependency, changing the OS types to the specific enum instead of allowing any string.
- [d5e910eb](https://www.github.com/tauri-apps/tauri/commit/d5e910ebcc6c8d7f055ab0691286722b140ffcd4) chore(api): remove `type-fest` ([#4605](https://www.github.com/tauri-apps/tauri/pull/4605)) on 2022-07-06
## \[1.0.1]
- Fixes the `writeBinaryFile` sending an empty file contents when only the first argument is passed.
- [ea43cf52](https://www.github.com/tauri-apps/tauri/commit/ea43cf52db8541d20a6397ef3ecd40f0f2bd6113) fix(api): `writeBinaryFile` sends an empty contents with only one arg ([#4368](https://www.github.com/tauri-apps/tauri/pull/4368)) on 2022-06-16
## \[1.0.0]
- Allow choosing multiple folders in `dialog.open`.
- [4e51dce6](https://www.github.com/tauri-apps/tauri/commit/4e51dce6ca21c7664de779bc78a04be1051371f7) fix: dialog open supports multiple dirs, fixes [#4091](https://www.github.com/tauri-apps/tauri/pull/4091) ([#4354](https://www.github.com/tauri-apps/tauri/pull/4354)) on 2022-06-15
- Upgrade to `stable`!
- [f4bb30cc](https://www.github.com/tauri-apps/tauri/commit/f4bb30cc73d6ba9b9ef19ef004dc5e8e6bb901d3) feat(covector): prepare for v1 ([#4351](https://www.github.com/tauri-apps/tauri/pull/4351)) on 2022-06-15
## \[1.0.0-rc.7]
- Fix `FilePart` usage in `http.Body.form` by renaming the `value` property to `file`.
- [55f89d5f](https://www.github.com/tauri-apps/tauri/commit/55f89d5f9d429252ad3fd557b1d6233b256495e0) fix(api): Rename FormPart `value` to `file` to match docs and endpoint ([#4307](https://www.github.com/tauri-apps/tauri/pull/4307)) on 2022-06-09
- Fixes a memory leak in the command system.
- [f72cace3](https://www.github.com/tauri-apps/tauri/commit/f72cace36821dc675a6d25268ae85a21bdbd6296) fix: never remove ipc callback & mem never be released ([#4274](https://www.github.com/tauri-apps/tauri/pull/4274)) on 2022-06-05
- The notification's `isPermissionGranted` function now returns `boolean` instead of `boolean | null`. The response is never `null` because we won't check the permission for now, always returning `true` instead.
- [f482b094](https://www.github.com/tauri-apps/tauri/commit/f482b0942276e9402ab3725957535039bacb4fef) fix: remove notification permission prompt ([#4302](https://www.github.com/tauri-apps/tauri/pull/4302)) on 2022-06-09
- Added the `resolveResource` API to the path module.
- [7bba8db8](https://www.github.com/tauri-apps/tauri/commit/7bba8db83ead92e9bd9c4be7863742e71ac47513) feat(api): add `resolveResource` API to the path module ([#4234](https://www.github.com/tauri-apps/tauri/pull/4234)) on 2022-05-29
- Renamed `writeFile` to `writeTextFile` but kept the original function for backwards compatibility.
- [3f998ca2](https://www.github.com/tauri-apps/tauri/commit/3f998ca29445a349489078a74dd068e157a4d68e) feat(api): add `writeTextFile` and `(path, contents, options)` overload ([#4228](https://www.github.com/tauri-apps/tauri/pull/4228)) on 2022-05-29
- Added `(path, contents[, options])` overload to the `writeTextFile` and `writeBinaryFile` APIs.
- [3f998ca2](https://www.github.com/tauri-apps/tauri/commit/3f998ca29445a349489078a74dd068e157a4d68e) feat(api): add `writeTextFile` and `(path, contents, options)` overload ([#4228](https://www.github.com/tauri-apps/tauri/pull/4228)) on 2022-05-29
## \[1.0.0-rc.6]
- Expose option to set the dialog type.
- [f46175d5](https://www.github.com/tauri-apps/tauri/commit/f46175d5d46fa3eae66ad2415a0eb1efb7d31da2) feat(core): expose option to set dialog type, closes [#4183](https://www.github.com/tauri-apps/tauri/pull/4183) ([#4187](https://www.github.com/tauri-apps/tauri/pull/4187)) on 2022-05-21
- Expose `title` option in the message dialog API.
- [ae99f991](https://www.github.com/tauri-apps/tauri/commit/ae99f991674d77c322a2240d10ed4b78ed2f4d4b) feat(core): expose message dialog's title option, ref [#4183](https://www.github.com/tauri-apps/tauri/pull/4183) ([#4186](https://www.github.com/tauri-apps/tauri/pull/4186)) on 2022-05-21
## \[1.0.0-rc.5]
- Fixes the type of `http > connectTimeout`.
- [f3c5ca89](https://www.github.com/tauri-apps/tauri/commit/f3c5ca89e79d429183c4e15a9e7cebada2b493a0) fix(core): http api `connect_timeout` deserialization, closes [#4004](https://www.github.com/tauri-apps/tauri/pull/4004) ([#4006](https://www.github.com/tauri-apps/tauri/pull/4006)) on 2022-04-29
## \[1.0.0-rc.4]
- Encode the file path in the `convertFileSrc` function.
- [42e8d9cf](https://www.github.com/tauri-apps/tauri/commit/42e8d9cf925089e9ad591198ee04b0cc0a0eed48) fix(api): encode file path in `convertFileSrc` function, closes [#3841](https://www.github.com/tauri-apps/tauri/pull/3841) ([#3846](https://www.github.com/tauri-apps/tauri/pull/3846)) on 2022-04-02
- Added `theme` getter to `WebviewWindow`.
- [4cebcf6d](https://www.github.com/tauri-apps/tauri/commit/4cebcf6da7cad1953e0f01b426afac3b5ef1f81e) feat: expose theme APIs, closes [#3903](https://www.github.com/tauri-apps/tauri/pull/3903) ([#3937](https://www.github.com/tauri-apps/tauri/pull/3937)) on 2022-04-21
- Added `theme` field to `WindowOptions`.
- [4cebcf6d](https://www.github.com/tauri-apps/tauri/commit/4cebcf6da7cad1953e0f01b426afac3b5ef1f81e) feat: expose theme APIs, closes [#3903](https://www.github.com/tauri-apps/tauri/pull/3903) ([#3937](https://www.github.com/tauri-apps/tauri/pull/3937)) on 2022-04-21
- Added the `setCursorGrab`, `setCursorVisible`, `setCursorIcon` and `setCursorPosition` methods to the `WebviewWindow` class.
- [c54ddfe9](https://www.github.com/tauri-apps/tauri/commit/c54ddfe9338e7eb90b4d5b02dfde687d432d5bc1) feat: expose window cursor APIs, closes [#3888](https://www.github.com/tauri-apps/tauri/pull/3888) [#3890](https://www.github.com/tauri-apps/tauri/pull/3890) ([#3935](https://www.github.com/tauri-apps/tauri/pull/3935)) on 2022-04-21
- **Breaking change:** The process Command API stdio lines now includes the trailing `\r`.
- [b5622882](https://www.github.com/tauri-apps/tauri/commit/b5622882cf3748e1e5a90915f415c0cd922aaaf8) fix(cli): exit on non-compilation Cargo errors, closes [#3930](https://www.github.com/tauri-apps/tauri/pull/3930) ([#3942](https://www.github.com/tauri-apps/tauri/pull/3942)) on 2022-04-22
- Added the `tauri://theme-changed` event.
- [4cebcf6d](https://www.github.com/tauri-apps/tauri/commit/4cebcf6da7cad1953e0f01b426afac3b5ef1f81e) feat: expose theme APIs, closes [#3903](https://www.github.com/tauri-apps/tauri/pull/3903) ([#3937](https://www.github.com/tauri-apps/tauri/pull/3937)) on 2022-04-21
## \[1.0.0-rc.3]
- Properly define the `appWindow` type.
- [1deeb03e](https://www.github.com/tauri-apps/tauri/commit/1deeb03ef6c7cbea8cf585864424a3d66f184a02) fix(api.js): appWindow shown as type `any`, fixes [#3747](https://www.github.com/tauri-apps/tauri/pull/3747) ([#3772](https://www.github.com/tauri-apps/tauri/pull/3772)) on 2022-03-24
- Added `Temp` to the `BaseDirectory` enum.
- [266156a0](https://www.github.com/tauri-apps/tauri/commit/266156a0b08150b21140dd552c8bc252fe413cdd) feat(core): add `BaseDirectory::Temp` and `$TEMP` variable ([#3763](https://www.github.com/tauri-apps/tauri/pull/3763)) on 2022-03-24
## \[1.0.0-rc.2]
- Do not crash if `__TAURI_METADATA__` is not set, log an error instead.
- [9cb1059a](https://www.github.com/tauri-apps/tauri/commit/9cb1059aa3f81521ccc6da655243acfe0327cd98) fix(api): do not throw an exception if **TAURI_METADATA** is not set, fixes [#3554](https://www.github.com/tauri-apps/tauri/pull/3554) ([#3572](https://www.github.com/tauri-apps/tauri/pull/3572)) on 2022-03-03
- Reimplement endpoint to read file as string for performance.
- [834ccc51](https://www.github.com/tauri-apps/tauri/commit/834ccc51539401d36a7dfa1c0982623c9c446a4c) feat(core): reimplement `readTextFile` for performance ([#3631](https://www.github.com/tauri-apps/tauri/pull/3631)) on 2022-03-07
- Fixes a regression on the `unlisten` command.
- [76c791bd](https://www.github.com/tauri-apps/tauri/commit/76c791bd2b836d2055410e37e71716172a3f81ef) fix(core): regression on the unlisten function ([#3623](https://www.github.com/tauri-apps/tauri/pull/3623)) on 2022-03-06
## \[1.0.0-rc.1]
- Provide functions to mock IPC calls during testing and static site generation.
- [7e04c072](https://www.github.com/tauri-apps/tauri/commit/7e04c072c4ee2278c648f44575c6c4710ac047f3) feat: add mock functions for testing and SSG ([#3437](https://www.github.com/tauri-apps/tauri/pull/3437)) on 2022-02-14
- [6f5ed2e6](https://www.github.com/tauri-apps/tauri/commit/6f5ed2e69cb7ffa0d5c8eb5a744fbf94ed6010d4) fix: change file on 2022-02-14
## \[1.0.0-rc.0]
- Add `fileDropEnabled` property to `WindowOptions` so you can now disable it when creating windows from js.
- [1bfc32a3](https://www.github.com/tauri-apps/tauri/commit/1bfc32a3b2f31b962ce8a5c611b60cb008360923) fix(api.js): add `fileDropEnabled` to `WindowOptions`, closes [#2968](https://www.github.com/tauri-apps/tauri/pull/2968) ([#2989](https://www.github.com/tauri-apps/tauri/pull/2989)) on 2021-12-09
- Add `logDir` function to the `path` module to access the suggested log directory.
Add `BaseDirectory.Log` to the `fs` module.
- [acbb3ae7](https://www.github.com/tauri-apps/tauri/commit/acbb3ae7bb0165846b9456aea103269f027fc548) feat: add Log directory ([#2736](https://www.github.com/tauri-apps/tauri/pull/2736)) on 2021-10-16
- [62c7a8ad](https://www.github.com/tauri-apps/tauri/commit/62c7a8ad30fd3031b8679960590e5ef3eef8e4da) chore(covector): prepare for `rc` release ([#3376](https://www.github.com/tauri-apps/tauri/pull/3376)) on 2022-02-10
- Expose `ask`, `message` and `confirm` APIs on the dialog module.
- [e98c1af4](https://www.github.com/tauri-apps/tauri/commit/e98c1af44279a5ff6c8a6f0a506ecc219c9f77af) feat(core): expose message dialog APIs, fix window.confirm, implement HasRawWindowHandle for Window, closes [#2535](https://www.github.com/tauri-apps/tauri/pull/2535) ([#2700](https://www.github.com/tauri-apps/tauri/pull/2700)) on 2021-10-02
- Event `emit` now automatically serialize non-string types.
- [06000996](https://www.github.com/tauri-apps/tauri/commit/060009969627890fa9018e2f1105bad13299394c) feat(api): support unknown types for event emit payload, closes [#2929](https://www.github.com/tauri-apps/tauri/pull/2929) ([#2964](https://www.github.com/tauri-apps/tauri/pull/2964)) on 2022-01-07
- Fix `http.fetch` throwing error if the response is successful but the body is empty.
- [50c63900](https://www.github.com/tauri-apps/tauri/commit/50c63900c7313064037e2ceb798a6432fcd1bcda) fix(api.js): fix `http.fetch` throwing error if response body is empty, closes [#2831](https://www.github.com/tauri-apps/tauri/pull/2831) ([#3008](https://www.github.com/tauri-apps/tauri/pull/3008)) on 2021-12-09
- Add `title` option to file open/save dialogs.
- [e1d6a6e6](https://www.github.com/tauri-apps/tauri/commit/e1d6a6e6445637723e2331ca799a662e720e15a8) Create api-file-dialog-title.md ([#3235](https://www.github.com/tauri-apps/tauri/pull/3235)) on 2022-01-16
- [62c7a8ad](https://www.github.com/tauri-apps/tauri/commit/62c7a8ad30fd3031b8679960590e5ef3eef8e4da) chore(covector): prepare for `rc` release ([#3376](https://www.github.com/tauri-apps/tauri/pull/3376)) on 2022-02-10
- Fix `os.platform` returning `macos` and `windows` instead of `darwin` and `win32`.
- [3924c3d8](https://www.github.com/tauri-apps/tauri/commit/3924c3d85365df30b376a1ec6c2d933460d66af0) fix(api.js): fix `os.platform` return on macos and windows, closes [#2698](https://www.github.com/tauri-apps/tauri/pull/2698) ([#2699](https://www.github.com/tauri-apps/tauri/pull/2699)) on 2021-10-02
- The `formatCallback` helper function now returns a number instead of a string.
- [a48b8b18](https://www.github.com/tauri-apps/tauri/commit/a48b8b18d428bcc404d489daa690bbefe1f57311) feat(core): validate callbacks and event names \[TRI-038] \[TRI-020] ([#21](https://www.github.com/tauri-apps/tauri/pull/21)) on 2022-01-09
- Added `rawHeaders` to `http > Response`.
- [b7a2345b](https://www.github.com/tauri-apps/tauri/commit/b7a2345b06ca0306988b4ba3d3deadd449e65af9) feat(core): add raw headers to HTTP API, closes [#2695](https://www.github.com/tauri-apps/tauri/pull/2695) ([#3053](https://www.github.com/tauri-apps/tauri/pull/3053)) on 2022-01-07
- Removed the `currentDir` API from the `path` module.
- [a08509c6](https://www.github.com/tauri-apps/tauri/commit/a08509c641f43695e25944a2dd47697b18cd83e2) fix(api): remove `currentDir` API from the `path` module on 2022-02-04
- Remove `.ts` files on the published package.
- [0f321ac0](https://www.github.com/tauri-apps/tauri/commit/0f321ac08d56412edd5bc9d166201fbc95d887d8) fix(api): do not ship TS files, closes [#2598](https://www.github.com/tauri-apps/tauri/pull/2598) ([#2645](https://www.github.com/tauri-apps/tauri/pull/2645)) on 2021-09-23
- **Breaking change:** Replaces all usages of `number[]` with `Uint8Array` to be closer aligned with the wider JS ecosystem.
- [9b19a805](https://www.github.com/tauri-apps/tauri/commit/9b19a805aa8efa64b22f2dfef193a144b8e0cee3) fix(api.js) Replace `number[]`with `Uint8Array`. fixes [#3306](https://www.github.com/tauri-apps/tauri/pull/3306) ([#3305](https://www.github.com/tauri-apps/tauri/pull/3305)) on 2022-02-05
- `WindowManager` methods `innerPosition` `outerPosition` now correctly return instance of `PhysicalPosition`.
`WindowManager` methods `innerSize` `outerSize` now correctly return instance of `PhysicalSize`.
- [cc8b1468](https://www.github.com/tauri-apps/tauri/commit/cc8b1468c821df53ceb771061c919409a9c80978) Fix(api): Window size and position returning wrong class (fix: [#2599](https://www.github.com/tauri-apps/tauri/pull/2599)) ([#2621](https://www.github.com/tauri-apps/tauri/pull/2621)) on 2021-09-22
- Change the `event` field of the `Event` interface to type `EventName` instead of `string`.
- [b5d9bcb4](https://www.github.com/tauri-apps/tauri/commit/b5d9bcb402380abc86ae1fa1a77c629af2275f9d) Consistent event name usage ([#3228](https://www.github.com/tauri-apps/tauri/pull/3228)) on 2022-01-15
- [62c7a8ad](https://www.github.com/tauri-apps/tauri/commit/62c7a8ad30fd3031b8679960590e5ef3eef8e4da) chore(covector): prepare for `rc` release ([#3376](https://www.github.com/tauri-apps/tauri/pull/3376)) on 2022-02-10
- Now `resolve()`, `join()` and `normalize()` from the `path` module, won't throw errors if the path doesn't exist, which matches NodeJS behavior.
- [fe381a0b](https://www.github.com/tauri-apps/tauri/commit/fe381a0bde86ebf4014007f6e21af4c1a9e58cef) fix: `join` no longer cares if path doesn't exist, closes [#2499](https://www.github.com/tauri-apps/tauri/pull/2499) ([#2548](https://www.github.com/tauri-apps/tauri/pull/2548)) on 2021-09-21
- Fixes the dialog `defaultPath` usage on Linux.
- [2212bd5d](https://www.github.com/tauri-apps/tauri/commit/2212bd5d75146f5a2df27cc2157a057642f626da) fix: dialog default path on Linux, closes [#3091](https://www.github.com/tauri-apps/tauri/pull/3091) ([#3123](https://www.github.com/tauri-apps/tauri/pull/3123)) on 2021-12-27
- Fixes `window.label` property returning null instead of the actual label.
- [f5109e0c](https://www.github.com/tauri-apps/tauri/commit/f5109e0c962e3d25404995194968bade1be33b16) fix(api): window label null instead of actual value, closes [#3295](https://www.github.com/tauri-apps/tauri/pull/3295) ([#3332](https://www.github.com/tauri-apps/tauri/pull/3332)) on 2022-02-04
- Remove the `BaseDirectory::Current` enum variant for security reasons.
- [696dca58](https://www.github.com/tauri-apps/tauri/commit/696dca58a9f8ee127a1cf857eb848e09f5845d18) refactor(core): remove `BaseDirectory::Current` variant on 2022-01-26
- Change `WindowLabel` type to `string`.
- [f68603ae](https://www.github.com/tauri-apps/tauri/commit/f68603aee4e16500dff9e385b217f5dd8b1b39e8) chore(docs): simplify event system documentation on 2021-09-27
- When building Universal macOS Binaries through the virtual target `universal-apple-darwin`:
- Expect a universal binary to be created by the user
- Ensure that binary is bundled and accessed correctly at runtime
- [3035e458](https://www.github.com/tauri-apps/tauri/commit/3035e4581c161ec7f0bd6d9b42e9015cf1dd1d77) Remove target triple from sidecar bin paths, closes [#3355](https://www.github.com/tauri-apps/tauri/pull/3355) ([#3356](https://www.github.com/tauri-apps/tauri/pull/3356)) on 2022-02-07
## \[1.0.0-beta.8]
- Revert target back to ES5.
- [657c7dac](https://www.github.com/tauri-apps/tauri/commit/657c7dac734661956b87d021ff531ba530dd92a3) fix(api): revert ES2021 target on 2021-08-23
## \[1.0.0-beta.7]
- Fix missing asset protocol path.Now the protocol is `https://asset.localhost/path/to/file` on Windows. Linux and macOS
is still `asset://path/to/file`.
- [994b5325](https://www.github.com/tauri-apps/tauri/commit/994b5325dd385f564b37fe1530c5d798dc925fff) fix: missing asset protocol path ([#2484](https://www.github.com/tauri-apps/tauri/pull/2484)) on 2021-08-23
## \[1.0.0-beta.6]
- `bundle` now exports `clipboard` module so you can `import { clipboard } from "@tauri-apps/api"`.
- [4f88c3fb](https://www.github.com/tauri-apps/tauri/commit/4f88c3fb94286f3daafb906e3513c9210ecfa76b) fix(api.js): `bundle` now exports `clipboard` mod, closes [#2243](https://www.github.com/tauri-apps/tauri/pull/2243) ([#2244](https://www.github.com/tauri-apps/tauri/pull/2244)) on 2021-07-19
- Fix double window creation
- [9fbcc024](https://www.github.com/tauri-apps/tauri/commit/9fbcc024542d87f71afd364acdcf2302cf82912c) fix(api.js): fix double window creation, closes [#2284](https://www.github.com/tauri-apps/tauri/pull/2284) ([#2285](https://www.github.com/tauri-apps/tauri/pull/2285)) on 2021-07-23
- Add `os` module which exports `EOL`, `platform()`, `version()`, `type()`, `arch()`, `tempdir()`
- [05e679a6](https://www.github.com/tauri-apps/tauri/commit/05e679a6d2aca5642c780052bcf1384c49a462de) feat(api.js): add `os` module ([#2299](https://www.github.com/tauri-apps/tauri/pull/2299)) on 2021-07-28
- - Add new nodejs-inspired functions which are `join`, `resolve`, `normalize`, `dirname`, `basename` and `extname`.
- Add `sep` and `delimiter` constants.
- Removed `resolvePath` API, use `resolve` instead.
- [05b9d81e](https://www.github.com/tauri-apps/tauri/commit/05b9d81ee6bcc920defca76cff00178b301fffe8) feat(api.js): add nodejs-inspired functions in `path` module ([#2310](https://www.github.com/tauri-apps/tauri/pull/2310)) on 2021-08-02
- Change target to ES2021.
- [97bc52ee](https://www.github.com/tauri-apps/tauri/commit/97bc52ee03dec0b67cc1cced23305a4c53e9eb62) Tooling: \[API] Changed target in tsconfig to es6 ([#2362](https://www.github.com/tauri-apps/tauri/pull/2362)) on 2021-08-09
- Add `toggleMaximize()` function to the `WebviewWindow` class.
- [1a510066](https://www.github.com/tauri-apps/tauri/commit/1a510066732d5f61c88c0ceed1c5f5cc559faf7d) fix(core): `data-tauri-drag-region` didn't respect resizable, closes [#2314](https://www.github.com/tauri-apps/tauri/pull/2314) ([#2316](https://www.github.com/tauri-apps/tauri/pull/2316)) on 2021-08-02
- Fix `@ts-expect` error usage
- [dd52e738](https://www.github.com/tauri-apps/tauri/commit/dd52e738f1fd323bd8d185d6e650f412eb031200) fix(api.js): fix `@ts-expect-error` usage, closes [#2249](https://www.github.com/tauri-apps/tauri/pull/2249) ([#2250](https://www.github.com/tauri-apps/tauri/pull/2250)) on 2021-07-20
- Fixes file drop events being swapped (`file-drop-hover` on drop and `file-drop` on hover).
- [c2b0fe1c](https://www.github.com/tauri-apps/tauri/commit/c2b0fe1ce58e54dbcfdb63162ad17d7e6d8774d9) fix(core): fix wrong file drop events ([#2300](https://www.github.com/tauri-apps/tauri/pull/2300)) on 2021-07-31
- Fixes the global bundle UMD code.
- [268450b1](https://www.github.com/tauri-apps/tauri/commit/268450b1329a4b55f2043890c565a8563f890c3a) fix(api): global bundle broken code, closes [#2289](https://www.github.com/tauri-apps/tauri/pull/2289) ([#2297](https://www.github.com/tauri-apps/tauri/pull/2297)) on 2021-07-26
- - Fixes monitor api not working.
- Fixes window.print() not working on macOS.
- [0f63f5e7](https://www.github.com/tauri-apps/tauri/commit/0f63f5e757873f1787a1ae07ca531340d0d45ec3) fix(api): Fix monitor functions, closes [#2294](https://www.github.com/tauri-apps/tauri/pull/2294) ([#2301](https://www.github.com/tauri-apps/tauri/pull/2301)) on 2021-07-29
- Improve `EventName` type using `type-fest`'s `LiteralUnion`.
- [8e480297](https://www.github.com/tauri-apps/tauri/commit/8e48029790857b38988da4d291aa7458f51bb265) feat(api): improve `EventName` type definition ([#2379](https://www.github.com/tauri-apps/tauri/pull/2379)) on 2021-08-10
- Update protocol url path with wry 0.12.1 on Windows.
- [88382fe1](https://www.github.com/tauri-apps/tauri/commit/88382fe147ebcb3f59308cc529e5562a04970876) chore(api): update protocol url path with wry 0.12.1 on Windows ([#2409](https://www.github.com/tauri-apps/tauri/pull/2409)) on 2021-08-13
## \[1.0.0-beta.5]
- Adds `convertFileSrc` helper to the `tauri` module, simplifying the process of using file paths as webview source (`img`, `video`, etc).
- [51a5cfe4](https://www.github.com/tauri-apps/tauri/commit/51a5cfe4b5e9890fb6f639c9c929657fd747a595) feat(api): add `convertFileSrc` helper ([#2138](https://www.github.com/tauri-apps/tauri/pull/2138)) on 2021-07-02
- You can now use `emit`, `listen` and `once` using the `appWindow` exported by the window module.
- [5d7626f8](https://www.github.com/tauri-apps/tauri/commit/5d7626f89781a6ebccceb9ab3b2e8335aa7a0392) feat(api): WindowManager extends WebviewWindowHandle, add events docs ([#2146](https://www.github.com/tauri-apps/tauri/pull/2146)) on 2021-07-03
- Allow manipulating a spawned window directly using `WebviewWindow`, which now extends `WindowManager`.
- [d69b1cf6](https://www.github.com/tauri-apps/tauri/commit/d69b1cf6d7c13297073073d753e30fe1a22a09cb) feat(api): allow managing windows created on JS ([#2154](https://www.github.com/tauri-apps/tauri/pull/2154)) on 2021-07-05
## \[1.0.0-beta.4]
- Add asset custom protocol to access local file system.
- [ee60e424](https://www.github.com/tauri-apps/tauri/commit/ee60e424221559d3d725716b0003c5566ef2b5cd) feat: asset custom protocol to access local file system ([#2104](https://www.github.com/tauri-apps/tauri/pull/2104)) on 2021-06-28
## \[1.0.0-beta.3]
- Export `Response` and `ResponseType` as value instead of type.
- [394b6e05](https://www.github.com/tauri-apps/tauri/commit/394b6e0572e7a0a92e103e462a7f603f7d569319) fix(api): http `ResponseType` export type error ([#2065](https://www.github.com/tauri-apps/tauri/pull/2065)) on 2021-06-24
## \[1.0.0-beta.2]
- Export `BaseDirectory` in `path` module
- [277f5ca5](https://www.github.com/tauri-apps/tauri/commit/277f5ca5a8ae227bbdccee1ad52bdd88b4a5b11b) feat(api): export `BaseDirectory` in `path` module ([#1885](https://www.github.com/tauri-apps/tauri/pull/1885)) on 2021-05-30
- Use `export type` to export TS types, enums and interfaces.
- [9a662d26](https://www.github.com/tauri-apps/tauri/commit/9a662d2601b01d712c6bd205f8db1b674f56dfa7) fix: Monitor if --isolatedModules is enabled ([#1825](https://www.github.com/tauri-apps/tauri/pull/1825)) on 2021-05-13
- [612cd8ec](https://www.github.com/tauri-apps/tauri/commit/612cd8ecb8e02954f3696b9e138cbc7d2c228fad) feat(api): finalize `export type` usage ([#1847](https://www.github.com/tauri-apps/tauri/pull/1847)) on 2021-05-17
- Adds `focus?: boolean` to the WindowOptions interface.
- [5f351622](https://www.github.com/tauri-apps/tauri/commit/5f351622c7812ad1bb56ddb37364ccaa4124c24b) feat(core): add focus API to the WindowBuilder and WindowOptions, [#1737](https://www.github.com/tauri-apps/tauri/pull/1737) on 2021-05-30
- Adds `isDecorated` getter on the window API.
- [f58a2114](https://www.github.com/tauri-apps/tauri/commit/f58a2114fbfd5307c349f05c88f2e08fd8baa8aa) feat(core): add `is_decorated` Window getter on 2021-05-30
- Adds `isResizable` getter on the window API.
- [1e8af280](https://www.github.com/tauri-apps/tauri/commit/1e8af280c27f381828d6209722b10e889082fa00) feat(core): add `is_resizable` Window getter on 2021-05-30
- Adds `isVisible` getter on the window API.
- [36506c96](https://www.github.com/tauri-apps/tauri/commit/36506c967de82bc7ff453d11e6104ecf66d7a588) feat(core): add `is_visible` API on 2021-05-30
- Adds `requestUserAttention` API to the `window` module.
- [7dcca6e9](https://www.github.com/tauri-apps/tauri/commit/7dcca6e9281182b11ad3d4a79871f09b30b9b419) feat(core): add `request_user_attention` API, closes [#2023](https://www.github.com/tauri-apps/tauri/pull/2023) ([#2026](https://www.github.com/tauri-apps/tauri/pull/2026)) on 2021-06-20
- Adds `setFocus` to the window API.
- [bb6992f8](https://www.github.com/tauri-apps/tauri/commit/bb6992f888196ca7c87bb2fe74ad2bd8bf393e05) feat(core): add `set_focus` window API, fixes [#1737](https://www.github.com/tauri-apps/tauri/pull/1737) on 2021-05-30
- Adds `setSkipTaskbar` to the window API.
- [e06aa277](https://www.github.com/tauri-apps/tauri/commit/e06aa277384450cfef617c0e57b0d5d403bb1e7f) feat(core): add `set_skip_taskbar` API on 2021-05-30
- Adds `skipTaskbar?: boolean` to the WindowOptions interface.
- [5525b03a](https://www.github.com/tauri-apps/tauri/commit/5525b03a78a2232c650043fbd9894ce1553cad41) feat(core): add `skip_taskbar` API to the WindowBuilder/WindowOptions on 2021-05-30
- Adds `center?: boolean` to `WindowOptions` and `center()` API to the `appWindow`.
- [5cba6eb4](https://www.github.com/tauri-apps/tauri/commit/5cba6eb4d28d53f06855d60d4d0eae6b95233ccf) feat(core): add window `center` API, closes [#1822](https://www.github.com/tauri-apps/tauri/pull/1822) ([#1954](https://www.github.com/tauri-apps/tauri/pull/1954)) on 2021-06-05
- Adds `clipboard` APIs (write and read text).
- [285bf64b](https://www.github.com/tauri-apps/tauri/commit/285bf64bf9569efb2df904c69c6df405ff0d62e2) feat(core): add clipboard writeText and readText APIs ([#2035](https://www.github.com/tauri-apps/tauri/pull/2035)) on 2021-06-21
- [dee71ad5](https://www.github.com/tauri-apps/tauri/commit/dee71ad58349f699995cc9077b79032bacc6afcb) fix(workflows): update docs workflow syntax ([#2054](https://www.github.com/tauri-apps/tauri/pull/2054)) on 2021-06-23
- The `http` APIs now resolve the returned promise when the API call finishes with an error status code.
- [47f75584](https://www.github.com/tauri-apps/tauri/commit/47f7558417cc654bdb1d018127e8900bc4eac622) fix(core): resolve HTTP API on non-ok status code, fix binary response, closes [#2046](https://www.github.com/tauri-apps/tauri/pull/2046) ([#2053](https://www.github.com/tauri-apps/tauri/pull/2053)) on 2021-06-23
- Improve RPC security by requiring a numeric code to invoke commands. The codes are generated by the Rust side and injected into the app's code using a closure, so external scripts can't access the backend. This change doesn't protect `withGlobalTauri` (`window.__TAURI__`) usage.
- [160fb052](https://www.github.com/tauri-apps/tauri/commit/160fb0529fd31d755574ae30fbdf01fa221a2acb) feat(core): improve RPC security, closes [#814](https://www.github.com/tauri-apps/tauri/pull/814) ([#2047](https://www.github.com/tauri-apps/tauri/pull/2047)) on 2021-06-22
- Mark the `WebviewWindow` constructor as public.
- [4aeb936e](https://www.github.com/tauri-apps/tauri/commit/4aeb936e9b60b895d383597dc698ee5d638436f9) fix(api): `WebviewWindow` constructor is public ([#1888](https://www.github.com/tauri-apps/tauri/pull/1888)) on 2021-05-21
- Validate arguments on the window `setLocation`, `setSize`, `setMinSize` and `setMaxSize` API.
- [7616e6cc](https://www.github.com/tauri-apps/tauri/commit/7616e6cc7bcd49f688b0d00fdc33c94b7b93713d) feat(api): validate window API `size` and `location` arguments ([#1846](https://www.github.com/tauri-apps/tauri/pull/1846)) on 2021-05-17
## \[1.0.0-beta.1]
- Adds `package.json` to the `exports` object.
- [ab1ea96](https://www.github.com/tauri-apps/tauri/commit/ab1ea964786e1781c922582b059c555b6072f1a0) chore(api): add `package.json` to the `exports` field ([#1807](https://www.github.com/tauri-apps/tauri/pull/1807)) on 2021-05-12
## \[1.0.0-beta.0]
- CommonJS chunks are now properly exported with `.cjs` extension
- [ddcd923](https://www.github.com/tauri-apps/tauri/commit/ddcd9233bd6f499aa7f22484d6c151b01778bc1b) fix(api): export commonjs chunks with `.cjs` extension, fix [#1625](https://www.github.com/tauri-apps/tauri/pull/1625) ([#1627](https://www.github.com/tauri-apps/tauri/pull/1627)) on 2021-04-26
- Adds `transparent?: boolean` to the `WindowOptions` interface.
- [08c1c5c](https://www.github.com/tauri-apps/tauri/commit/08c1c5ca5c0ebe17ea98689a5fe3b7e47a98e955) fix(api): missing `transparent` flag on `WindowOptions` ([#1764](https://www.github.com/tauri-apps/tauri/pull/1764)) on 2021-05-10
- Adds `options` argument to the shell command API (`env` and `cwd` configuration).
- [721e98f](https://www.github.com/tauri-apps/tauri/commit/721e98f175567b360c86f30565ab1b9d08e7cf85) feat(core): add env, cwd to the command API, closes [#1634](https://www.github.com/tauri-apps/tauri/pull/1634) ([#1635](https://www.github.com/tauri-apps/tauri/pull/1635)) on 2021-04-28
- Adds `startDragging` API on the window module.
- [c31f097](https://www.github.com/tauri-apps/tauri/commit/c31f0978c535f794fffb75a121e69a323e70b06e) refactor: update to wry 0.9 ([#1630](https://www.github.com/tauri-apps/tauri/pull/1630)) on 2021-04-28
- Move `exit` and `relaunch` APIs from `app` to `process` module.
- [b0bb796](https://www.github.com/tauri-apps/tauri/commit/b0bb796a42e2560233aea47ce6ced54ac238eb53) refactor: rename `command` mod to `process`, move restart_application ([#1667](https://www.github.com/tauri-apps/tauri/pull/1667)) on 2021-04-30
- The window management API was refactored: removed `setX`, `setY`, `setWidth`, `setHeight` APIs, renamed `resize` to `setSize` and the size and position APIs now allow defining both logical and physical values.
- [6bfac86](https://www.github.com/tauri-apps/tauri/commit/6bfac866a703f1499a64237fb29b2625703f4e22) refactor(core): add window getters, physical & logical sizes/positions ([#1723](https://www.github.com/tauri-apps/tauri/pull/1723)) on 2021-05-05
- Adds window getters.
- [6bfac86](https://www.github.com/tauri-apps/tauri/commit/6bfac866a703f1499a64237fb29b2625703f4e22) refactor(core): add window getters, physical & logical sizes/positions ([#1723](https://www.github.com/tauri-apps/tauri/pull/1723)) on 2021-05-05
## \[1.0.0-beta-rc.3]
- Fixes distribution of the `@tauri-apps/api` package for older bundlers.
- [7f998d0](https://www.github.com/tauri-apps/tauri/commit/7f998d08e3ab8823c99190fa283bdfa2c4f2749b) fix(api): distribution ([#1582](https://www.github.com/tauri-apps/tauri/pull/1582)) on 2021-04-22
- Update minimum Node.js version to v12.13.0
- [1f089fb](https://www.github.com/tauri-apps/tauri/commit/1f089fb4f964c673dcab5784bdf1da2833487a7c) chore: update minimum nodejs version to 12.13.0 ([#1562](https://www.github.com/tauri-apps/tauri/pull/1562)) on 2021-04-21
## \[1.0.0-beta-rc.2]
- TS was wrongly re-exporting the module.
- [fcb3b48](https://www.github.com/tauri-apps/tauri/commit/fcb3b4857efa17d2a3717f32457e88b24520cc9b) fix: [#1512](https://www.github.com/tauri-apps/tauri/pull/1512) ([#1517](https://www.github.com/tauri-apps/tauri/pull/1517)) on 2021-04-19
- [ae14a3f](https://www.github.com/tauri-apps/tauri/commit/ae14a3ff51a742b6ab6f76bbfc21f385310f1dc6) fix: [#1517](https://www.github.com/tauri-apps/tauri/pull/1517) had the wrong package reference in the changefile ([#1538](https://www.github.com/tauri-apps/tauri/pull/1538)) on 2021-04-19
## \[1.0.0-beta-rc.1]
- Missing the `files` property in the package.json which mean that the `dist` directory was not published and used.
- [b2569a7](https://www.github.com/tauri-apps/tauri/commit/b2569a729a3caa88bdba62abc31f0665e1323aaa) fix(js-api): dist ([#1498](https://www.github.com/tauri-apps/tauri/pull/1498)) on 2021-04-15
## \[1.0.0-beta-rc.0]
- Add current working directory to the path api module.
- [52c2baf](https://www.github.com/tauri-apps/tauri/commit/52c2baf940773cf7c51647fb6f20d0f7df126115) feat: add current working directory to path api module ([#1375](https://www.github.com/tauri-apps/tauri/pull/1375)) on 2021-03-23
- [a6def70](https://www.github.com/tauri-apps/tauri/commit/a6def7066eec19c889b0f14cc1e475bf209a332e) Refactor(tauri): move tauri-api and tauri-updater to tauri ([#1455](https://www.github.com/tauri-apps/tauri/pull/1455)) on 2021-04-11
- The shell process spawning API was rewritten and now includes stream access.
- [3713066](https://www.github.com/tauri-apps/tauri/commit/3713066e451bd30d0cc6f57bb437f08276f4c4ad) refactor(core): rewrite shell execute API, closes [#1229](https://www.github.com/tauri-apps/tauri/pull/1229) ([#1408](https://www.github.com/tauri-apps/tauri/pull/1408)) on 2021-03-31
- The file dialog API now uses [rfd](https://github.com/PolyMeilex/rfd). The filter option is now an array of `{ name: string, extensions: string[] }`.
- [2326bcd](https://www.github.com/tauri-apps/tauri/commit/2326bcd399411f7f0eabdb7ade910be473adadae) refactor(core): use `nfd` for file dialogs, closes [#1251](https://www.github.com/tauri-apps/tauri/pull/1251) ([#1257](https://www.github.com/tauri-apps/tauri/pull/1257)) on 2021-02-18
- [a6def70](https://www.github.com/tauri-apps/tauri/commit/a6def7066eec19c889b0f14cc1e475bf209a332e) Refactor(tauri): move tauri-api and tauri-updater to tauri ([#1455](https://www.github.com/tauri-apps/tauri/pull/1455)) on 2021-04-11
- The HTTP API was improved with client caching and better payload and response types.
- [a7bc472](https://www.github.com/tauri-apps/tauri/commit/a7bc472e994730071f960d09a12ac85296a080ae) refactor(core): improve HTTP API, closes [#1098](https://www.github.com/tauri-apps/tauri/pull/1098) ([#1237](https://www.github.com/tauri-apps/tauri/pull/1237)) on 2021-02-15
- [a6def70](https://www.github.com/tauri-apps/tauri/commit/a6def7066eec19c889b0f14cc1e475bf209a332e) Refactor(tauri): move tauri-api and tauri-updater to tauri ([#1455](https://www.github.com/tauri-apps/tauri/pull/1455)) on 2021-04-11
- Update all code files to have our license header.
- [bf82136](https://www.github.com/tauri-apps/tauri/commit/bf8213646689175f8a158b956911f3a43e360690) feat(license): SPDX Headers ([#1449](https://www.github.com/tauri-apps/tauri/pull/1449)) on 2021-04-11
- [a6def70](https://www.github.com/tauri-apps/tauri/commit/a6def7066eec19c889b0f14cc1e475bf209a332e) Refactor(tauri): move tauri-api and tauri-updater to tauri ([#1455](https://www.github.com/tauri-apps/tauri/pull/1455)) on 2021-04-11
- [aea6145](https://www.github.com/tauri-apps/tauri/commit/aea614587bddab930d552512b54e18624fbf573e) refactor(repo): add /tooling folder ([#1457](https://www.github.com/tauri-apps/tauri/pull/1457)) on 2021-04-12
- Use secure RNG on callback function names.
- [c8992bb](https://www.github.com/tauri-apps/tauri/commit/c8992bb0bfb8eaeae8ebed444719f9c9372d39d4) refactor(api): use secure RNG, closes [#1356](https://www.github.com/tauri-apps/tauri/pull/1356) ([#1398](https://www.github.com/tauri-apps/tauri/pull/1398)) on 2021-03-30
- The invoke function can now be called with the cmd as the first parameter and the args as the second.
- [427d170](https://www.github.com/tauri-apps/tauri/commit/427d170930ab711fd0ca82f7a73b524d6fdc222f) feat(api/invoke): separate cmd arg ([#1321](https://www.github.com/tauri-apps/tauri/pull/1321)) on 2021-03-04
- Adds a global shortcut API.
- [855effa](https://www.github.com/tauri-apps/tauri/commit/855effadd9ebfb6bc1a3555ac7fc733f6f766b7a) feat(core): globalShortcut API ([#1232](https://www.github.com/tauri-apps/tauri/pull/1232)) on 2021-02-14
- [a6def70](https://www.github.com/tauri-apps/tauri/commit/a6def7066eec19c889b0f14cc1e475bf209a332e) Refactor(tauri): move tauri-api and tauri-updater to tauri ([#1455](https://www.github.com/tauri-apps/tauri/pull/1455)) on 2021-04-11
- Added window management and window creation APIs.
- [a3d6dff](https://www.github.com/tauri-apps/tauri/commit/a3d6dff2163c7a45842253edd81dbc62248dc65d) feat(core): window API ([#1225](https://www.github.com/tauri-apps/tauri/pull/1225)) on 2021-02-13
- [641374b](https://www.github.com/tauri-apps/tauri/commit/641374b15343518cd835bd5ada811941c65dcf2e) feat(core): window creation at runtime ([#1249](https://www.github.com/tauri-apps/tauri/pull/1249)) on 2021-02-17

@ -0,0 +1,177 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 - Present Tauri Apps Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save