mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 01:18:05 +08:00
chore: use react-grab to replace code-inspector-plugin (#33078)
This commit is contained in:
80
web/plugins/vite/custom-i18n-hmr.ts
Normal file
80
web/plugins/vite/custom-i18n-hmr.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import type { Plugin } from 'vite'
|
||||
import fs from 'node:fs'
|
||||
import { injectClientSnippet, normalizeViteModuleId } from './utils'
|
||||
|
||||
type CustomI18nHmrPluginOptions = {
|
||||
injectTarget: string
|
||||
}
|
||||
|
||||
export const customI18nHmrPlugin = ({ injectTarget }: CustomI18nHmrPluginOptions): Plugin => {
|
||||
const i18nHmrClientMarker = 'custom-i18n-hmr-client'
|
||||
const i18nHmrClientSnippet = `/* ${i18nHmrClientMarker} */
|
||||
if (import.meta.hot) {
|
||||
const getI18nUpdateTarget = (file) => {
|
||||
const match = file.match(/[/\\\\]i18n[/\\\\]([^/\\\\]+)[/\\\\]([^/\\\\]+)\\.json$/)
|
||||
if (!match)
|
||||
return null
|
||||
const [, locale, namespaceFile] = match
|
||||
return { locale, namespaceFile }
|
||||
}
|
||||
|
||||
import.meta.hot.on('i18n-update', async ({ file, content }) => {
|
||||
const target = getI18nUpdateTarget(file)
|
||||
if (!target)
|
||||
return
|
||||
|
||||
const [{ getI18n }, { camelCase }] = await Promise.all([
|
||||
import('react-i18next'),
|
||||
import('es-toolkit/string'),
|
||||
])
|
||||
|
||||
const i18n = getI18n()
|
||||
if (!i18n)
|
||||
return
|
||||
if (target.locale !== i18n.language)
|
||||
return
|
||||
|
||||
let resources
|
||||
try {
|
||||
resources = JSON.parse(content)
|
||||
}
|
||||
catch {
|
||||
return
|
||||
}
|
||||
|
||||
const namespace = camelCase(target.namespaceFile)
|
||||
i18n.addResourceBundle(target.locale, namespace, resources, true, true)
|
||||
i18n.emit('languageChanged', i18n.language)
|
||||
})
|
||||
}
|
||||
`
|
||||
|
||||
return {
|
||||
name: 'custom-i18n-hmr',
|
||||
apply: 'serve',
|
||||
handleHotUpdate({ file, server }) {
|
||||
if (file.endsWith('.json') && file.includes('/i18n/')) {
|
||||
server.ws.send({
|
||||
type: 'custom',
|
||||
event: 'i18n-update',
|
||||
data: {
|
||||
file,
|
||||
content: fs.readFileSync(file, 'utf-8'),
|
||||
},
|
||||
})
|
||||
|
||||
return []
|
||||
}
|
||||
},
|
||||
transform(code, id) {
|
||||
const cleanId = normalizeViteModuleId(id)
|
||||
if (cleanId !== injectTarget)
|
||||
return null
|
||||
|
||||
const nextCode = injectClientSnippet(code, i18nHmrClientMarker, i18nHmrClientSnippet)
|
||||
if (nextCode === code)
|
||||
return null
|
||||
return { code: nextCode, map: null }
|
||||
},
|
||||
}
|
||||
}
|
||||
92
web/plugins/vite/react-grab-open-file.ts
Normal file
92
web/plugins/vite/react-grab-open-file.ts
Normal file
@ -0,0 +1,92 @@
|
||||
import type { Plugin } from 'vite'
|
||||
import { injectClientSnippet, normalizeViteModuleId } from './utils'
|
||||
|
||||
type ReactGrabOpenFilePluginOptions = {
|
||||
injectTarget: string
|
||||
projectRoot: string
|
||||
}
|
||||
|
||||
export const reactGrabOpenFilePlugin = ({
|
||||
injectTarget,
|
||||
projectRoot,
|
||||
}: ReactGrabOpenFilePluginOptions): Plugin => {
|
||||
const reactGrabOpenFileClientMarker = 'react-grab-open-file-client'
|
||||
const reactGrabOpenFileClientSnippet = `/* ${reactGrabOpenFileClientMarker} */
|
||||
if (typeof window !== 'undefined') {
|
||||
const projectRoot = ${JSON.stringify(projectRoot)};
|
||||
const pluginName = 'dify-vite-open-file';
|
||||
const rootRelativeSourcePathPattern = /^\\/(?!@|node_modules)(?:.+)\\.(?:[cm]?[jt]sx?|mdx?)$/;
|
||||
|
||||
const normalizeProjectRoot = (input) => {
|
||||
return input.endsWith('/') ? input.slice(0, -1) : input;
|
||||
};
|
||||
|
||||
const resolveFilePath = (filePath) => {
|
||||
if (filePath.startsWith('/@fs/')) {
|
||||
return filePath.slice('/@fs'.length);
|
||||
}
|
||||
|
||||
if (!rootRelativeSourcePathPattern.test(filePath)) {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
const normalizedProjectRoot = normalizeProjectRoot(projectRoot);
|
||||
if (filePath.startsWith(normalizedProjectRoot)) {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
return \`\${normalizedProjectRoot}\${filePath}\`;
|
||||
};
|
||||
|
||||
const registerPlugin = () => {
|
||||
if (window.__DIFY_REACT_GRAB_OPEN_FILE_PLUGIN_REGISTERED__) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reactGrab = window.__REACT_GRAB__;
|
||||
if (!reactGrab) {
|
||||
return;
|
||||
}
|
||||
|
||||
reactGrab.registerPlugin({
|
||||
name: pluginName,
|
||||
hooks: {
|
||||
onOpenFile(filePath, lineNumber) {
|
||||
const params = new URLSearchParams({
|
||||
file: resolveFilePath(filePath),
|
||||
column: '1',
|
||||
});
|
||||
|
||||
if (lineNumber) {
|
||||
params.set('line', String(lineNumber));
|
||||
}
|
||||
|
||||
void fetch(\`/__open-in-editor?\${params.toString()}\`);
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
window.__DIFY_REACT_GRAB_OPEN_FILE_PLUGIN_REGISTERED__ = true;
|
||||
};
|
||||
|
||||
registerPlugin();
|
||||
window.addEventListener('react-grab:init', registerPlugin);
|
||||
}
|
||||
`
|
||||
|
||||
return {
|
||||
name: 'react-grab-open-file',
|
||||
apply: 'serve',
|
||||
transform(code, id) {
|
||||
const cleanId = normalizeViteModuleId(id)
|
||||
if (cleanId !== injectTarget)
|
||||
return null
|
||||
|
||||
const nextCode = injectClientSnippet(code, reactGrabOpenFileClientMarker, reactGrabOpenFileClientSnippet)
|
||||
if (nextCode === code)
|
||||
return null
|
||||
return { code: nextCode, map: null }
|
||||
},
|
||||
}
|
||||
}
|
||||
20
web/plugins/vite/utils.ts
Normal file
20
web/plugins/vite/utils.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export const normalizeViteModuleId = (id: string): string => {
|
||||
const withoutQuery = id.split('?', 1)[0]
|
||||
|
||||
if (withoutQuery.startsWith('/@fs/'))
|
||||
return withoutQuery.slice('/@fs'.length)
|
||||
|
||||
return withoutQuery
|
||||
}
|
||||
|
||||
export const injectClientSnippet = (code: string, marker: string, snippet: string): string => {
|
||||
if (code.includes(marker))
|
||||
return code
|
||||
|
||||
const useClientMatch = code.match(/(['"])use client\1;?\s*\n/)
|
||||
if (!useClientMatch)
|
||||
return `${snippet}\n${code}`
|
||||
|
||||
const insertAt = (useClientMatch.index ?? 0) + useClientMatch[0].length
|
||||
return `${code.slice(0, insertAt)}\n${snippet}\n${code.slice(insertAt)}`
|
||||
}
|
||||
Reference in New Issue
Block a user