mirror of
https://github.com/YFGaia/dify-plus.git
synced 2026-06-04 10:14:00 +08:00
feat(web): migrate PWA to Serwist (#30808)
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
'use client'
|
||||||
|
|
||||||
|
export { SerwistProvider } from '@serwist/turbopack/react'
|
||||||
+33
-27
@@ -12,6 +12,7 @@ import { ToastProvider } from './components/base/toast'
|
|||||||
import BrowserInitializer from './components/browser-initializer'
|
import BrowserInitializer from './components/browser-initializer'
|
||||||
import { ReactScanLoader } from './components/devtools/react-scan/loader'
|
import { ReactScanLoader } from './components/devtools/react-scan/loader'
|
||||||
import { I18nServerProvider } from './components/provider/i18n-server'
|
import { I18nServerProvider } from './components/provider/i18n-server'
|
||||||
|
import { SerwistProvider } from './components/provider/serwist'
|
||||||
import SentryInitializer from './components/sentry-initializer'
|
import SentryInitializer from './components/sentry-initializer'
|
||||||
import RoutePrefixHandle from './routePrefixHandle'
|
import RoutePrefixHandle from './routePrefixHandle'
|
||||||
import './styles/globals.css'
|
import './styles/globals.css'
|
||||||
@@ -39,6 +40,9 @@ const LocaleLayout = async ({
|
|||||||
}) => {
|
}) => {
|
||||||
const locale = await getLocaleOnServer()
|
const locale = await getLocaleOnServer()
|
||||||
|
|
||||||
|
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''
|
||||||
|
const swUrl = `${basePath}/serwist/sw.js`
|
||||||
|
|
||||||
const datasetMap: Record<DatasetAttr, string | undefined> = {
|
const datasetMap: Record<DatasetAttr, string | undefined> = {
|
||||||
[DatasetAttr.DATA_API_PREFIX]: process.env.NEXT_PUBLIC_API_PREFIX,
|
[DatasetAttr.DATA_API_PREFIX]: process.env.NEXT_PUBLIC_API_PREFIX,
|
||||||
[DatasetAttr.DATA_PUBLIC_API_PREFIX]: process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX,
|
[DatasetAttr.DATA_PUBLIC_API_PREFIX]: process.env.NEXT_PUBLIC_PUBLIC_API_PREFIX,
|
||||||
@@ -92,33 +96,35 @@ const LocaleLayout = async ({
|
|||||||
className="color-scheme h-full select-auto"
|
className="color-scheme h-full select-auto"
|
||||||
{...datasetMap}
|
{...datasetMap}
|
||||||
>
|
>
|
||||||
<ReactScanLoader />
|
<SerwistProvider swUrl={swUrl}>
|
||||||
<JotaiProvider>
|
<ReactScanLoader />
|
||||||
<ThemeProvider
|
<JotaiProvider>
|
||||||
attribute="data-theme"
|
<ThemeProvider
|
||||||
defaultTheme="system"
|
attribute="data-theme"
|
||||||
enableSystem
|
defaultTheme="system"
|
||||||
disableTransitionOnChange
|
enableSystem
|
||||||
enableColorScheme={false}
|
disableTransitionOnChange
|
||||||
>
|
enableColorScheme={false}
|
||||||
<NuqsAdapter>
|
>
|
||||||
<BrowserInitializer>
|
<NuqsAdapter>
|
||||||
<SentryInitializer>
|
<BrowserInitializer>
|
||||||
<TanstackQueryInitializer>
|
<SentryInitializer>
|
||||||
<I18nServerProvider>
|
<TanstackQueryInitializer>
|
||||||
<ToastProvider>
|
<I18nServerProvider>
|
||||||
<GlobalPublicStoreProvider>
|
<ToastProvider>
|
||||||
{children}
|
<GlobalPublicStoreProvider>
|
||||||
</GlobalPublicStoreProvider>
|
{children}
|
||||||
</ToastProvider>
|
</GlobalPublicStoreProvider>
|
||||||
</I18nServerProvider>
|
</ToastProvider>
|
||||||
</TanstackQueryInitializer>
|
</I18nServerProvider>
|
||||||
</SentryInitializer>
|
</TanstackQueryInitializer>
|
||||||
</BrowserInitializer>
|
</SentryInitializer>
|
||||||
</NuqsAdapter>
|
</BrowserInitializer>
|
||||||
</ThemeProvider>
|
</NuqsAdapter>
|
||||||
</JotaiProvider>
|
</ThemeProvider>
|
||||||
<RoutePrefixHandle />
|
</JotaiProvider>
|
||||||
|
<RoutePrefixHandle />
|
||||||
|
</SerwistProvider>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import { spawnSync } from 'node:child_process'
|
||||||
|
import { randomUUID } from 'node:crypto'
|
||||||
|
import { createSerwistRoute } from '@serwist/turbopack'
|
||||||
|
|
||||||
|
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || ''
|
||||||
|
const revision = spawnSync('git', ['rev-parse', 'HEAD'], { encoding: 'utf-8' }).stdout?.trim() || randomUUID()
|
||||||
|
|
||||||
|
export const { dynamic, dynamicParams, revalidate, generateStaticParams, GET } = createSerwistRoute({
|
||||||
|
additionalPrecacheEntries: [{ url: `${basePath}/_offline.html`, revision }],
|
||||||
|
swSrc: 'app/sw.ts',
|
||||||
|
nextConfig: {
|
||||||
|
basePath,
|
||||||
|
},
|
||||||
|
})
|
||||||
+104
@@ -0,0 +1,104 @@
|
|||||||
|
/// <reference no-default-lib="true" />
|
||||||
|
/// <reference lib="esnext" />
|
||||||
|
/// <reference lib="webworker" />
|
||||||
|
|
||||||
|
import type { PrecacheEntry, SerwistGlobalConfig } from 'serwist'
|
||||||
|
import { CacheableResponsePlugin, CacheFirst, ExpirationPlugin, NetworkFirst, Serwist, StaleWhileRevalidate } from 'serwist'
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
// eslint-disable-next-line ts/consistent-type-definitions
|
||||||
|
interface WorkerGlobalScope extends SerwistGlobalConfig {
|
||||||
|
__SW_MANIFEST: (PrecacheEntry | string)[] | undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const self: ServiceWorkerGlobalScope
|
||||||
|
|
||||||
|
const scopePathname = new URL(self.registration.scope).pathname
|
||||||
|
const basePath = scopePathname.replace(/\/serwist\/$/, '').replace(/\/$/, '')
|
||||||
|
const offlineUrl = `${basePath}/_offline.html`
|
||||||
|
|
||||||
|
const serwist = new Serwist({
|
||||||
|
precacheEntries: self.__SW_MANIFEST,
|
||||||
|
skipWaiting: true,
|
||||||
|
clientsClaim: true,
|
||||||
|
navigationPreload: true,
|
||||||
|
runtimeCaching: [
|
||||||
|
{
|
||||||
|
matcher: ({ url }) => url.origin === 'https://fonts.googleapis.com',
|
||||||
|
handler: new CacheFirst({
|
||||||
|
cacheName: 'google-fonts',
|
||||||
|
plugins: [
|
||||||
|
new CacheableResponsePlugin({ statuses: [0, 200] }),
|
||||||
|
new ExpirationPlugin({
|
||||||
|
maxEntries: 4,
|
||||||
|
maxAgeSeconds: 365 * 24 * 60 * 60,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matcher: ({ url }) => url.origin === 'https://fonts.gstatic.com',
|
||||||
|
handler: new CacheFirst({
|
||||||
|
cacheName: 'google-fonts-webfonts',
|
||||||
|
plugins: [
|
||||||
|
new CacheableResponsePlugin({ statuses: [0, 200] }),
|
||||||
|
new ExpirationPlugin({
|
||||||
|
maxEntries: 4,
|
||||||
|
maxAgeSeconds: 365 * 24 * 60 * 60,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matcher: ({ request }) => request.destination === 'image',
|
||||||
|
handler: new CacheFirst({
|
||||||
|
cacheName: 'images',
|
||||||
|
plugins: [
|
||||||
|
new CacheableResponsePlugin({ statuses: [0, 200] }),
|
||||||
|
new ExpirationPlugin({
|
||||||
|
maxEntries: 64,
|
||||||
|
maxAgeSeconds: 30 * 24 * 60 * 60,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matcher: ({ request }) => request.destination === 'script' || request.destination === 'style',
|
||||||
|
handler: new StaleWhileRevalidate({
|
||||||
|
cacheName: 'static-resources',
|
||||||
|
plugins: [
|
||||||
|
new ExpirationPlugin({
|
||||||
|
maxEntries: 32,
|
||||||
|
maxAgeSeconds: 24 * 60 * 60,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
matcher: ({ url, sameOrigin }) => sameOrigin && url.pathname.startsWith('/api/'),
|
||||||
|
handler: new NetworkFirst({
|
||||||
|
cacheName: 'api-cache',
|
||||||
|
networkTimeoutSeconds: 10,
|
||||||
|
plugins: [
|
||||||
|
new ExpirationPlugin({
|
||||||
|
maxEntries: 16,
|
||||||
|
maxAgeSeconds: 60 * 60,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fallbacks: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
url: offlineUrl,
|
||||||
|
matcher({ request }) {
|
||||||
|
return request.destination === 'document'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
serwist.addEventListeners()
|
||||||
+1
-4
@@ -15,10 +15,7 @@ const config: KnipConfig = {
|
|||||||
ignoreBinaries: [
|
ignoreBinaries: [
|
||||||
'only-allow',
|
'only-allow',
|
||||||
],
|
],
|
||||||
ignoreDependencies: [
|
ignoreDependencies: [],
|
||||||
// required by next-pwa
|
|
||||||
'babel-loader',
|
|
||||||
],
|
|
||||||
rules: {
|
rules: {
|
||||||
files: 'warn',
|
files: 'warn',
|
||||||
dependencies: 'warn',
|
dependencies: 'warn',
|
||||||
|
|||||||
+2
-70
@@ -1,77 +1,8 @@
|
|||||||
import withBundleAnalyzerInit from '@next/bundle-analyzer'
|
import withBundleAnalyzerInit from '@next/bundle-analyzer'
|
||||||
import createMDX from '@next/mdx'
|
import createMDX from '@next/mdx'
|
||||||
import { codeInspectorPlugin } from 'code-inspector-plugin'
|
import { codeInspectorPlugin } from 'code-inspector-plugin'
|
||||||
import withPWAInit from 'next-pwa'
|
|
||||||
|
|
||||||
const isDev = process.env.NODE_ENV === 'development'
|
const isDev = process.env.NODE_ENV === 'development'
|
||||||
|
|
||||||
const withPWA = withPWAInit({
|
|
||||||
dest: 'public',
|
|
||||||
register: true,
|
|
||||||
skipWaiting: true,
|
|
||||||
disable: process.env.NODE_ENV === 'development',
|
|
||||||
fallbacks: {
|
|
||||||
document: '/_offline.html',
|
|
||||||
},
|
|
||||||
runtimeCaching: [
|
|
||||||
{
|
|
||||||
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
|
|
||||||
handler: 'CacheFirst',
|
|
||||||
options: {
|
|
||||||
cacheName: 'google-fonts',
|
|
||||||
expiration: {
|
|
||||||
maxEntries: 4,
|
|
||||||
maxAgeSeconds: 365 * 24 * 60 * 60, // 1 year
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
urlPattern: /^https:\/\/fonts\.gstatic\.com\/.*/i,
|
|
||||||
handler: 'CacheFirst',
|
|
||||||
options: {
|
|
||||||
cacheName: 'google-fonts-webfonts',
|
|
||||||
expiration: {
|
|
||||||
maxEntries: 4,
|
|
||||||
maxAgeSeconds: 365 * 24 * 60 * 60, // 1 year
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
urlPattern: /\.(?:png|jpg|jpeg|svg|gif|webp|avif)$/i,
|
|
||||||
handler: 'CacheFirst',
|
|
||||||
options: {
|
|
||||||
cacheName: 'images',
|
|
||||||
expiration: {
|
|
||||||
maxEntries: 64,
|
|
||||||
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
urlPattern: /\.(?:js|css)$/i,
|
|
||||||
handler: 'StaleWhileRevalidate',
|
|
||||||
options: {
|
|
||||||
cacheName: 'static-resources',
|
|
||||||
expiration: {
|
|
||||||
maxEntries: 32,
|
|
||||||
maxAgeSeconds: 24 * 60 * 60, // 1 day
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
urlPattern: /^\/api\/.*/i,
|
|
||||||
handler: 'NetworkFirst',
|
|
||||||
options: {
|
|
||||||
cacheName: 'api-cache',
|
|
||||||
networkTimeoutSeconds: 10,
|
|
||||||
expiration: {
|
|
||||||
maxEntries: 16,
|
|
||||||
maxAgeSeconds: 60 * 60, // 1 hour
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
const withMDX = createMDX({
|
const withMDX = createMDX({
|
||||||
extension: /\.mdx?$/,
|
extension: /\.mdx?$/,
|
||||||
options: {
|
options: {
|
||||||
@@ -97,6 +28,7 @@ const remoteImageURLs = [hasSetWebPrefix ? new URL(`${process.env.NEXT_PUBLIC_WE
|
|||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const nextConfig = {
|
const nextConfig = {
|
||||||
basePath: process.env.NEXT_PUBLIC_BASE_PATH || '',
|
basePath: process.env.NEXT_PUBLIC_BASE_PATH || '',
|
||||||
|
serverExternalPackages: ['esbuild-wasm'],
|
||||||
transpilePackages: ['echarts', 'zrender'],
|
transpilePackages: ['echarts', 'zrender'],
|
||||||
turbopack: {
|
turbopack: {
|
||||||
rules: codeInspectorPlugin({
|
rules: codeInspectorPlugin({
|
||||||
@@ -148,4 +80,4 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default withPWA(withBundleAnalyzer(withMDX(nextConfig)))
|
export default withBundleAnalyzer(withMDX(nextConfig))
|
||||||
|
|||||||
+3
-3
@@ -111,7 +111,6 @@
|
|||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"negotiator": "^1.0.0",
|
"negotiator": "^1.0.0",
|
||||||
"next": "~15.5.9",
|
"next": "~15.5.9",
|
||||||
"next-pwa": "^5.6.0",
|
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"nuqs": "^2.8.6",
|
"nuqs": "^2.8.6",
|
||||||
"pinyin-pro": "^3.27.0",
|
"pinyin-pro": "^3.27.0",
|
||||||
@@ -153,7 +152,6 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^6.7.3",
|
"@antfu/eslint-config": "^6.7.3",
|
||||||
"@babel/core": "^7.28.4",
|
|
||||||
"@chromatic-com/storybook": "^4.1.1",
|
"@chromatic-com/storybook": "^4.1.1",
|
||||||
"@eslint-react/eslint-plugin": "^2.3.13",
|
"@eslint-react/eslint-plugin": "^2.3.13",
|
||||||
"@mdx-js/loader": "^3.1.1",
|
"@mdx-js/loader": "^3.1.1",
|
||||||
@@ -162,6 +160,7 @@
|
|||||||
"@next/eslint-plugin-next": "15.5.9",
|
"@next/eslint-plugin-next": "15.5.9",
|
||||||
"@next/mdx": "15.5.9",
|
"@next/mdx": "15.5.9",
|
||||||
"@rgrove/parse-xml": "^4.2.0",
|
"@rgrove/parse-xml": "^4.2.0",
|
||||||
|
"@serwist/turbopack": "^9.5.0",
|
||||||
"@storybook/addon-docs": "9.1.13",
|
"@storybook/addon-docs": "9.1.13",
|
||||||
"@storybook/addon-links": "9.1.13",
|
"@storybook/addon-links": "9.1.13",
|
||||||
"@storybook/addon-onboarding": "9.1.13",
|
"@storybook/addon-onboarding": "9.1.13",
|
||||||
@@ -194,9 +193,9 @@
|
|||||||
"@vitejs/plugin-react": "^5.1.2",
|
"@vitejs/plugin-react": "^5.1.2",
|
||||||
"@vitest/coverage-v8": "4.0.16",
|
"@vitest/coverage-v8": "4.0.16",
|
||||||
"autoprefixer": "^10.4.21",
|
"autoprefixer": "^10.4.21",
|
||||||
"babel-loader": "^10.0.0",
|
|
||||||
"code-inspector-plugin": "1.2.9",
|
"code-inspector-plugin": "1.2.9",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
|
"esbuild-wasm": "^0.27.2",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-plugin-react-hooks": "^7.0.1",
|
"eslint-plugin-react-hooks": "^7.0.1",
|
||||||
"eslint-plugin-react-refresh": "^0.4.26",
|
"eslint-plugin-react-refresh": "^0.4.26",
|
||||||
@@ -212,6 +211,7 @@
|
|||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"react-scan": "^0.4.3",
|
"react-scan": "^0.4.3",
|
||||||
"sass": "^1.93.2",
|
"sass": "^1.93.2",
|
||||||
|
"serwist": "^9.5.0",
|
||||||
"storybook": "9.1.17",
|
"storybook": "9.1.17",
|
||||||
"tailwindcss": "^3.4.18",
|
"tailwindcss": "^3.4.18",
|
||||||
"tsx": "^4.21.0",
|
"tsx": "^4.21.0",
|
||||||
|
|||||||
Generated
+345
-779
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user