🔒 SSR UPDATE

This commit is contained in:
NEO 2024-04-21 02:55:19 +08:00
parent 23f82d956a
commit cb44d3db8b
31 changed files with 991 additions and 210 deletions

View file

@ -96,6 +96,7 @@
"toReactive": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"tryOnBeforeMount": true,
"tryOnBeforeUnmount": true,
@ -285,7 +286,6 @@
"watchThrottled": true,
"watchTriggerable": true,
"watchWithFilter": true,
"whenever": true,
"toValue": true
"whenever": true
}
}

29
components.d.ts vendored
View file

@ -89,17 +89,28 @@ declare module '@vue/runtime-core' {
HttpStatusCodes: typeof import('./src/tools/http-status-codes/http-status-codes.vue')['default']
IbanValidatorAndParser: typeof import('./src/tools/iban-validator-and-parser/iban-validator-and-parser.vue')['default']
'IconMdi:brushVariant': typeof import('~icons/mdi/brush-variant')['default']
'IconMdi:contentCopy': typeof import('~icons/mdi/content-copy')['default']
'IconMdi:kettleSteamOutline': typeof import('~icons/mdi/kettle-steam-outline')['default']
IconMdiArrowDown: typeof import('~icons/mdi/arrow-down')['default']
IconMdiArrowRightBottom: typeof import('~icons/mdi/arrow-right-bottom')['default']
IconMdiCamera: typeof import('~icons/mdi/camera')['default']
IconMdiChevronDown: typeof import('~icons/mdi/chevron-down')['default']
IconMdiChevronRight: typeof import('~icons/mdi/chevron-right')['default']
IconMdiClose: typeof import('~icons/mdi/close')['default']
IconMdiContentCopy: typeof import('~icons/mdi/content-copy')['default']
IconMdiDeleteOutline: typeof import('~icons/mdi/delete-outline')['default']
IconMdiDownload: typeof import('~icons/mdi/download')['default']
IconMdiEye: typeof import('~icons/mdi/eye')['default']
IconMdiEyeOff: typeof import('~icons/mdi/eye-off')['default']
IconMdiHeart: typeof import('~icons/mdi/heart')['default']
IconMdiPause: typeof import('~icons/mdi/pause')['default']
IconMdiPlay: typeof import('~icons/mdi/play')['default']
IconMdiRecord: typeof import('~icons/mdi/record')['default']
IconMdiRefresh: typeof import('~icons/mdi/refresh')['default']
IconMdiSearch: typeof import('~icons/mdi/search')['default']
IconMdiTranslate: typeof import('~icons/mdi/translate')['default']
IconMdiTriangleDown: typeof import('~icons/mdi/triangle-down')['default']
IconMdiVideo: typeof import('~icons/mdi/video')['default']
InputCopyable: typeof import('./src/components/InputCopyable.vue')['default']
IntegerBaseConverter: typeof import('./src/tools/integer-base-converter/integer-base-converter.vue')['default']
Ipv4AddressConverter: typeof import('./src/tools/ipv4-address-converter/ipv4-address-converter.vue')['default']
@ -126,25 +137,40 @@ declare module '@vue/runtime-core' {
MenuLayout: typeof import('./src/components/MenuLayout.vue')['default']
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
NAlert: typeof import('naive-ui')['NAlert']
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
NCheckbox: typeof import('naive-ui')['NCheckbox']
NCode: typeof import('naive-ui')['NCode']
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
NColorPicker: typeof import('naive-ui')['NColorPicker']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
NDatePicker: typeof import('naive-ui')['NDatePicker']
NDivider: typeof import('naive-ui')['NDivider']
NDynamicInput: typeof import('naive-ui')['NDynamicInput']
NEllipsis: typeof import('naive-ui')['NEllipsis']
NForm: typeof import('naive-ui')['NForm']
NFormItem: typeof import('naive-ui')['NFormItem']
NGi: typeof import('naive-ui')['NGi']
NGrid: typeof import('naive-ui')['NGrid']
NH1: typeof import('naive-ui')['NH1']
NH2: typeof import('naive-ui')['NH2']
NH3: typeof import('naive-ui')['NH3']
NIcon: typeof import('naive-ui')['NIcon']
NImage: typeof import('naive-ui')['NImage']
NInputGroup: typeof import('naive-ui')['NInputGroup']
NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
NInputNumber: typeof import('naive-ui')['NInputNumber']
NLabel: typeof import('naive-ui')['NLabel']
NLayout: typeof import('naive-ui')['NLayout']
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
NMenu: typeof import('naive-ui')['NMenu']
NProgress: typeof import('naive-ui')['NProgress']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSlider: typeof import('naive-ui')['NSlider']
NSpin: typeof import('naive-ui')['NSpin']
NStatistic: typeof import('naive-ui')['NStatistic']
NSwitch: typeof import('naive-ui')['NSwitch']
NTable: typeof import('naive-ui')['NTable']
NTag: typeof import('naive-ui')['NTag']
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
@ -159,6 +185,7 @@ declare module '@vue/runtime-core' {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default']
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']

View file

@ -4,8 +4,8 @@
<meta charset="UTF-8" />
<link rel="icon" href="favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>IT Tools - Handy online tools for developers</title>
<meta itemprop="name" content="IT Tools - Handy online tools for developers" />
<title>IT Tools</title>
<meta itemprop="name" content="IT Tools" />
<meta
name="description"
content="Collection of handy online tools for developers, with great UX. IT Tools is a free and open-source collection of handy online tools for developers & people working in IT."
@ -26,7 +26,7 @@
<meta property="og:url" content="https://it-tools.tech/" />
<meta property="og:type" content="website" />
<meta property="og:title" content="IT Tools - Handy online tools for developers" />
<meta property="og:title" content="IT Tools" />
<meta
property="og:description"
content="Collection of handy online tools for developers, with great UX. IT Tools is a free and open-source collection of handy online tools for developers & people working in IT."
@ -37,16 +37,17 @@
<meta name="twitter:site" content="@ittoolsdottech" />
<meta name="twitter:creator" content="@cthmsst" />
<meta name="twitter:title" content="IT Tools - Handy online tools for developers" />
<meta name="twitter:title" content="IT Tools" />
<meta
name="twitter:description"
content="Collection of handy online tools for developers, with great UX. IT Tools is a free and open-source collection of handy online tools for developers & people working in IT."
/>
<meta name="twitter:image" content="https://it-tools.tech/banner.png?v=2" />
<meta name="twitter:image:alt" content="IT Tools - Handy online tools for developers" />
<meta name="twitter:image:alt" content="IT Tools" />
<style><!--app-css--></style>
</head>
<body>
<div id="app"></div>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

View file

@ -1,6 +1,7 @@
{
"name": "it-tools",
"version": "2023.12.21-5ed3693",
"type": "module",
"description": "Collection of handy online tools for developers, with great UX. ",
"keywords": [
"productivity",
@ -22,6 +23,7 @@
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && NODE_OPTIONS=--max_old_space_size=4096 vite build",
"build:ssr": "node server.js",
"preview": "vite preview --port 5050",
"test": "npm run test:unit",
"test:unit": "vitest --environment jsdom",
@ -35,6 +37,7 @@
"release": "node ./scripts/release.mjs"
},
"dependencies": {
"@css-render/vue3-ssr": "^0.15.12",
"@it-tools/bip39": "^0.0.4",
"@it-tools/oggen": "^1.3.0",
"@sindresorhus/slugify": "^2.2.1",
@ -44,6 +47,7 @@
"@types/figlet": "^1.5.8",
"@vicons/material": "^0.12.0",
"@vicons/tabler": "^0.12.0",
"@vue/server-renderer": "^3.4.23",
"@vueuse/core": "^10.3.0",
"@vueuse/head": "^1.0.0",
"@vueuse/router": "^10.0.0",
@ -58,6 +62,7 @@
"date-fns": "^2.29.3",
"dompurify": "^3.0.6",
"emojilib": "^3.0.10",
"express": "^4.19.2",
"figlet": "^1.7.0",
"figue": "^1.2.0",
"fuse.js": "^6.6.2",
@ -86,9 +91,11 @@
"unicode-emoji-json": "^0.4.0",
"unplugin-auto-import": "^0.16.4",
"uuid": "^9.0.0",
"vite-plugin-ssr": "^0.4.142",
"vue": "^3.3.4",
"vue-i18n": "^9.9.1",
"vue-router": "^4.1.6",
"vue-server-renderer": "^2.7.16",
"vue-tsc": "^1.8.1",
"xml-formatter": "^3.3.2",
"yaml": "^2.2.1"

828
pnpm-lock.yaml generated

File diff suppressed because it is too large Load diff

63
server.js Normal file
View file

@ -0,0 +1,63 @@
// server.js
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import express from 'express';
import { createServer as createViteServer } from 'vite';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
async function startServer() {
const app = express();
// 创建Vite服务器实例
const vite = await createViteServer({
server: { middlewareMode: true },
});
// 使用Vite的中间件
app.use(vite.middlewares);
app.use('*', async (req, res) => {
try {
// 读取index.html文件
const indexProd = fs.readFileSync(path.resolve(__dirname, 'index.html'), 'utf-8');
// 应用Vite HTML转换。重要在生产环境中这将由Vite自己处理。
const indexHtml = await vite.transformIndexHtml(req.originalUrl, indexProd);
// 加载服务器入口。vite.ssrLoadModule会自动转换ECS模块为CommonJS可直接被Node.js导入
const { render } = await vite.ssrLoadModule('/src/entry-server.ts');
const {
app, router, pinia, i18n,
cssHtml,
appHtml,
} = await render(req, res);
// 设置服务器端路由位置
router.push(req.originalUrl);
// 等待路由准备就绪
await router.isReady();
// 注入应用内容到模板中
const html = indexHtml
.replace('<!--app-html-->', appHtml)
.replace('<!--app-css-->', cssHtml);
// 发送渲染后的HTML回客户端
res.status(200).set({ 'Content-Type': 'text/html' }).end(html);
}
catch (e) {
// 如果捕获到错误则使用Vite修复堆栈追踪并发送500错误
vite.ssrFixStacktrace(e);
console.error(e);
res.status(500).end(e.message);
}
});
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
}
startServer();

34
src/entry-client.ts Normal file
View file

@ -0,0 +1,34 @@
// // entry-client.js
// import { createApp } from './main';
//
// const { app, router } = createApp();
//
// router.isReady().then(() => {
// app.mount('#app', true);
// });
// import 'uno.css'; // 确保这是你的 UnoCSS 生成的样式文件
import { createHead } from '@vueuse/head';
// src/entry-client.js
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';
import router from '@/router';
import { plausible } from '@/plugins/plausible.plugin';
import { naive } from '@/plugins/naive.plugin';
import { i18nPlugin } from '@/plugins/i18n.plugin';
const app = createApp(App);
const pinia = createPinia();
app.use(pinia);
app.use(createHead());
app.use(i18nPlugin);
app.use(router);
app.use(plausible);
app.use(naive);
router.isReady().then(() => {
app.mount('#app', true);
});

22
src/entry-server.ts Normal file
View file

@ -0,0 +1,22 @@
import { renderToString } from '@vue/server-renderer';
import { setup } from '@css-render/vue3-ssr';
import { createApp } from './main';
// import 'virtual:uno.css';
/**
* Render page with naive ui
*/
export async function render() {
const { app, router, pinia, i18n } = createApp();
const { collect } = setup(app);
const appHtml = await renderToString(app);
const cssHtml = collect();
return {
cssHtml,
appHtml,
app,
router,
pinia,
i18n,
};
}

View file

@ -1,8 +1,8 @@
import { createApp } from 'vue';
import { createSSRApp } from 'vue';
import { createPinia } from 'pinia';
import { createHead } from '@vueuse/head';
import { registerSW } from 'virtual:pwa-register';
// import { registerSW } from 'virtual:pwa-register';
import { plausible } from './plugins/plausible.plugin';
import 'virtual:uno.css';
@ -13,15 +13,28 @@ import App from './App.vue';
import router from './router';
import { i18nPlugin } from './plugins/i18n.plugin';
registerSW();
// registerSW();
const app = createApp(App);
// const app = createApp(App);
app.use(createPinia());
app.use(createHead());
app.use(i18nPlugin);
app.use(router);
app.use(naive);
app.use(plausible);
// app.use(createPinia());
// app.use(createHead());
// app.use(i18nPlugin);
// app.use(router);
// app.use(naive);
// // app.use(plausible);
//
// app.mount('#app');
export function createApp() {
const app = createSSRApp(App);
const pinia = createPinia();
app.use(pinia);
app.use(createHead());
app.use(i18nPlugin);
app.use(router);
app.use(plausible);
app.use(naive);
// 其他插件...
app.mount('#app');
return { app, router, pinia, i18n: i18nPlugin };
}

View file

@ -7,7 +7,7 @@ import type { PaletteOption } from './command-palette.types';
const isModalOpen = ref(false);
const inputRef = ref();
const router = useRouter();
const isMac = computed(() => window.navigator.userAgent.toLowerCase().includes('mac'));
const isMac = computed(() => true);
const commandPaletteStore = useCommandPaletteStore();
const { searchPrompt, filteredSearchResult } = storeToRefs(commandPaletteStore);

View file

@ -1,4 +1,6 @@
<script setup lang="ts">
import CSelect from '@/ui/c-select/c-select.vue';
const { availableLocales, locale } = useI18n();
const localesLong: Record<string, string> = {
@ -21,7 +23,7 @@ const localeOptions = computed(() =>
</script>
<template>
<c-select
<CSelect
v-model:value="locale"
:options="localeOptions"
placeholder="Select a language"

View file

@ -1,5 +1,5 @@
import _ from 'lodash';
import type Plausible from 'plausible-tracker';
// import type Plausible from 'plausible-tracker';
import { inject } from 'vue';
export { createTrackerService, useTracker };
@ -7,21 +7,21 @@ export { createTrackerService, useTracker };
function createTrackerService({ plausible }: { plausible: ReturnType<typeof Plausible> }) {
return {
trackEvent({ eventName }: { eventName: string }) {
plausible.trackEvent(eventName);
// plausible.trackEvent(eventName);
},
};
}
function useTracker() {
const plausible: ReturnType<typeof Plausible> | undefined = inject('plausible');
// const plausible: ReturnType<any> | undefined = inject('plausible');
//
// if (_.isNil(plausible)) {
// throw new TypeError('Plausible must be instantiated');
// }
if (_.isNil(plausible)) {
throw new TypeError('Plausible must be instantiated');
}
const tracker = createTrackerService({ plausible });
// const tracker = createTrackerService({ plausible });
return {
tracker,
tracker: () => {},
};
}

View file

@ -3,18 +3,29 @@ import { get } from '@vueuse/core';
import type { Plugin } from 'vue';
import { createI18n } from 'vue-i18n';
const i18n = createI18n({
legacy: false,
locale: 'en',
messages,
});
// const i18n = createI18n({
// legacy: false,
// locale: 'en',
// messages,
// });
export const i18nPlugin: Plugin = {
install: (app) => {
const i18n = createI18n({
legacy: false,
locale: 'zh',
messages,
});
app.use(i18n);
},
};
export const i18n = createI18n({
legacy: false,
locale: 'zh',
messages,
});
export const translate = function (localeKey: string) {
const hasKey = i18n.global.te(localeKey, get(i18n.global.locale));
return hasKey ? i18n.global.t(localeKey) : localeKey;

View file

@ -1,10 +1,10 @@
import { noop } from 'lodash';
import Plausible from 'plausible-tracker';
// import Plausible from 'plausible-tracker';
import type { App } from 'vue';
import { config } from '@/config';
function createFakePlausibleInstance(): Pick<ReturnType<typeof Plausible>, 'trackEvent' | 'enableAutoPageviews'> {
function createFakePlausibleInstance(): Pick<ReturnType<any>, 'trackEvent' | 'enableAutoPageviews'> {
return {
trackEvent: noop,
enableAutoPageviews: () => noop,
@ -22,7 +22,7 @@ function createPlausibleInstance({
}
}) {
if (config.isTrackerEnabled) {
return Plausible(config);
return config;
}
return createFakePlausibleInstance();

View file

@ -1,4 +1,4 @@
import { createRouter, createWebHistory } from 'vue-router';
import { createMemoryHistory, createRouter } from 'vue-router';
import { layouts } from './layouts/index';
import HomePage from './pages/Home.page.vue';
import NotFound from './pages/404.page.vue';
@ -19,7 +19,7 @@ const toolsRedirectRoutes = tools
);
const router = createRouter({
history: createWebHistory(config.app.baseUrl),
history: createMemoryHistory(config.app.baseUrl),
routes: [
{
path: '/',

View file

@ -12,7 +12,7 @@ import {
sentenceCase,
snakeCase,
} from 'change-case';
import InputCopyable from '../../components/InputCopyable.vue';
import InputCopyable from '@/components/InputCopyable.vue';
const baseConfig = {
stripRegexp: /[^A-Za-zÀ-ÖØ-öø-ÿ]+/gi,

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { useThemeVars } from 'naive-ui';
import InputCopyable from '../../components/InputCopyable.vue';
import InputCopyable from '@/components/InputCopyable.vue';
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation } from './chmod-calculator.service';
import type { Group, Scope } from './chmod-calculator.types';

View file

@ -2,7 +2,7 @@
import type { lib } from 'crypto-js';
import { MD5, RIPEMD160, SHA1, SHA224, SHA256, SHA3, SHA384, SHA512, enc } from 'crypto-js';
import InputCopyable from '../../components/InputCopyable.vue';
import InputCopyable from '@/components/InputCopyable.vue';
import { convertHexToBin } from './hash-text.service';
import { useQueryParam } from '@/composable/queryParams';

View file

@ -1,4 +1,5 @@
<script setup lang="ts">
import CInputText from "@/ui/c-input-text/c-input-text.vue";
import { extractIBAN, friendlyFormatIBAN, isQRIBAN, validateIBAN } from 'ibantools';
import { getFriendlyErrors } from './iban-validator-and-parser.service';
import type { CKeyValueListItems } from '@/ui/c-key-value-list/c-key-value-list.types';

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import InputCopyable from '../../components/InputCopyable.vue';
import InputCopyable from '@/components/InputCopyable.vue';
import { convertBase } from './integer-base-converter.model';
import { getErrorMessageIfThrows } from '@/utils/error';

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { stringify as stringifyToml } from 'iarna-toml-esm';
import JSON5 from 'json5';
import { withDefaultOnError } from '../../utils/defaults';
import { withDefaultOnError } from '@/utils/defaults';
import type { UseValidationRule } from '@/composable/validation';
const convertJsonToToml = (value: string) => [stringifyToml(JSON5.parse(value))].flat().join('\n').trim();

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { useEventListener } from '@vueuse/core';
import InputCopyable from '../../components/InputCopyable.vue';
import InputCopyable from '@/components/InputCopyable.vue';
const event = ref<KeyboardEvent>();

View file

@ -1,6 +1,6 @@
<script setup lang="ts">
import { parse as parseToml } from 'iarna-toml-esm';
import { withDefaultOnError } from '../../utils/defaults';
import { withDefaultOnError } from '@/utils/defaults';
import { isValidToml } from './toml.services';
import type { UseValidationRule } from '@/composable/validation';

View file

@ -1,5 +1,5 @@
import { parse as parseToml } from 'iarna-toml-esm';
import { isNotThrowing } from '../../utils/boolean';
import { isNotThrowing } from '@/utils/boolean';
export { isValidToml };

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { parse as parseToml } from 'iarna-toml-esm';
import { stringify as stringifyToYaml } from 'yaml';
import { withDefaultOnError } from '../../utils/defaults';
import { withDefaultOnError } from '@/utils/defaults';
import { isValidToml } from '../toml-to-json/toml.services';
import type { UseValidationRule } from '@/composable/validation';

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import InputCopyable from '../../components/InputCopyable.vue';
import InputCopyable from '@/components/InputCopyable.vue';
import { isNotThrowing } from '@/utils/boolean';
import { withDefaultOnError } from '@/utils/defaults';

View file

@ -1,7 +1,7 @@
<script setup lang="ts">
import { stringify as stringifyToml } from 'iarna-toml-esm';
import { parse as parseYaml } from 'yaml';
import { withDefaultOnError } from '../../utils/defaults';
import { withDefaultOnError } from '@/utils/defaults';
import type { UseValidationRule } from '@/composable/validation';
const convertYamlToToml = (value: string) => [stringifyToml(parseYaml(value))].flat().join('\n').trim();

View file

@ -3,6 +3,7 @@ import { useAppTheme } from '../theme/themes';
import type { CLabelProps } from '../c-label/c-label.types';
import type { CSelectOption } from './c-select.types';
import { useTheme } from './c-select.theme';
import CLabel from '@/ui/c-label/c-label.vue';
import { clamp } from '@/modules/shared/number.models';
import { useFuzzySearch } from '@/composable/fuzzySearch';
@ -138,7 +139,7 @@ function onSearchInput() {
</script>
<template>
<c-label v-bind="props">
<CLabel v-bind="props">
<div ref="elementRef" relative class="c-select" w-full>
<div
flex flex-nowrap cursor-pointer items-center
@ -190,7 +191,7 @@ function onSearchInput() {
</div>
</transition>
</div>
</c-label>
</CLabel>
</template>
<style lang="less" scoped>

View file

@ -14,8 +14,7 @@ export default defineConfig({
transformers: [transformerDirectives(), transformerVariantGroup()],
theme: {
colors: {
primary: '#1ea54c',
primary: '#9c1ea5',
},
},
shortcuts: {

View file

@ -11,10 +11,11 @@ import Icons from 'unplugin-icons/vite';
import { NaiveUiResolver } from 'unplugin-vue-components/resolvers';
import Components from 'unplugin-vue-components/vite';
import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
// import { VitePWA } from 'vite-plugin-pwa';
import markdown from 'vite-plugin-vue-markdown';
import svgLoader from 'vite-svg-loader';
import { configDefaults } from 'vitest/config';
import ssr from 'vite-plugin-ssr/plugin';
const baseUrl = process.env.BASE_URL ?? '/';
@ -53,43 +54,43 @@ export default defineConfig({
vueJsx(),
markdown(),
svgLoader(),
VitePWA({
registerType: 'autoUpdate',
strategies: 'generateSW',
manifest: {
name: 'IT Tools',
description: 'Aggregated set of useful tools for developers.',
display: 'standalone',
lang: 'fr-FR',
start_url: `${baseUrl}?utm_source=pwa&utm_medium=pwa`,
orientation: 'any',
theme_color: '#18a058',
background_color: '#f1f5f9',
icons: [
{
src: '/favicon-16x16.png',
type: 'image/png',
sizes: '16x16',
},
{
src: '/favicon-32x32.png',
type: 'image/png',
sizes: '32x32',
},
{
src: '/android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any maskable',
},
],
},
}),
// VitePWA({
// registerType: 'autoUpdate',
// strategies: 'generateSW',
// manifest: {
// name: 'IT Tools',
// description: 'Aggregated set of useful tools for developers.',
// display: 'standalone',
// lang: 'fr-FR',
// start_url: `${baseUrl}?utm_source=pwa&utm_medium=pwa`,
// orientation: 'any',
// theme_color: '#18a058',
// background_color: '#f1f5f9',
// icons: [
// {
// src: '/favicon-16x16.png',
// type: 'image/png',
// sizes: '16x16',
// },
// {
// src: '/favicon-32x32.png',
// type: 'image/png',
// sizes: '32x32',
// },
// {
// src: '/android-chrome-192x192.png',
// sizes: '192x192',
// type: 'image/png',
// },
// {
// src: '/android-chrome-512x512.png',
// sizes: '512x512',
// type: 'image/png',
// purpose: 'any maskable',
// },
// ],
// },
// }),
Components({
dirs: ['src/'],
extensions: ['vue', 'md'],
@ -97,6 +98,7 @@ export default defineConfig({
resolvers: [NaiveUiResolver(), IconsResolver({ prefix: 'icon' })],
}),
Unocss(),
ssr(),
],
base: baseUrl,
resolve: {