Optimize Transform

This commit is contained in:
2024-01-10 23:00:30 +08:00
parent 71052009ae
commit 309d5892c7
7 changed files with 111 additions and 107 deletions

View File

@@ -37,7 +37,7 @@ const Editor: React.FC<EditorProps> = ({
dispose: () => undefined
})
const { total, finished, onWatch } = useTypesProgress()
const file = files[selectedFileName] ?? { name: 'Untitled' }
const file = files[selectedFileName] || { name: 'Untitled' }
const handleOnEditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => {
editorRef.current = editor

View File

@@ -46,7 +46,7 @@ const CodeEditor: React.FC<CodeEditorProps> = ({
(item) => ![IMPORT_MAP_FILE_NAME, ENTRY_FILE_NAME].includes(item) && !files[item].hidden
)
const propsSelectedFileName =
props.selectedFileName ?? (filteredFilesName.length ? filteredFilesName[0] : '')
props.selectedFileName || (filteredFilesName.length ? filteredFilesName[0] : '')
const [selectedFileName, setSelectedFileName] = useState(propsSelectedFileName)
const [errorMsg, setErrorMsg] = useState('')

View File

@@ -1,78 +0,0 @@
import React, { useRef, useState } from 'react'
import { IFiles } from '@/components/Playground/shared.ts'
import { useUpdatedEffect } from '@/util/hooks.tsx'
import Compiler from '@/components/Playground/compiler.ts'
import { Loader } from 'esbuild-wasm'
import { cssToJs, jsonToJs } from '@/components/Playground/files.ts'
interface OutputProps {
files: IFiles
}
const Preview: React.FC<OutputProps> = ({ files }) => {
const compiler = useRef<Compiler>()
const [compileCode, setCompileCode] = useState('')
const [fileName, setFileName] = useState('main.tsx')
const handleOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setFileName(e.target.value)
}
useUpdatedEffect(() => {
if (!compiler.current) {
compiler.current = new Compiler()
}
}, [])
useUpdatedEffect(() => {
if (files[fileName]) {
try {
const file = files[fileName]
let loader: Loader
let code = file.value
switch (file.language) {
case 'typescript':
loader = 'tsx'
break
case 'javascript':
loader = 'jsx'
break
case 'css':
code = cssToJs(file)
loader = 'js'
break
case 'json':
code = jsonToJs(file)
loader = 'js'
break
case 'xml':
loader = 'default'
}
compiler.current
?.transform(code, loader)
.then((value) => {
setCompileCode(value.code)
})
.catch((e) => {
console.error('编译失败', e)
})
} catch (e) {
setCompileCode('')
}
} else {
setCompileCode('')
}
}, [fileName, files])
return (
<>
<AntdInput.TextArea value={compileCode} />
<AntdInput value={fileName} onChange={handleOnChange} />
</>
)
}
export default Preview

View File

@@ -0,0 +1,77 @@
import React from 'react'
import MonacoEditor from '@monaco-editor/react'
import { Loader } from 'esbuild-wasm'
import { useUpdatedEffect } from '@/util/hooks'
import { IFile, ITheme } from '@/components/Playground/shared'
import Compiler from '@/components/Playground/compiler'
import { cssToJs, jsonToJs } from '@/components/Playground/files'
import { MonacoEditorConfig } from '@/components/Playground/CodeEditor/Editor/monacoConfig'
interface OutputProps {
file: IFile
theme?: ITheme
}
const Preview: React.FC<OutputProps> = ({ file, theme }) => {
const compiler = useRef<Compiler>()
const [compileCode, setCompileCode] = useState('')
useUpdatedEffect(() => {
if (!compiler.current) {
compiler.current = new Compiler()
}
}, [])
const compile = (code: string, loader: Loader) => {
compiler.current
?.transform(code, loader)
.then((value) => {
setCompileCode(value.code)
})
.catch((e) => {
console.error('编译失败', e)
})
}
useUpdatedEffect(() => {
if (file) {
try {
const code = file.value
switch (file.language) {
case 'typescript':
compile(code, 'tsx')
break
case 'javascript':
compile(code, 'jsx')
break
case 'css':
setCompileCode(cssToJs(file))
break
case 'json':
setCompileCode(jsonToJs(file))
break
case 'xml':
setCompileCode(code)
}
} catch (e) {
setCompileCode('')
}
} else {
setCompileCode('')
}
}, [file])
return (
<>
<MonacoEditor
theme={theme}
language={'javascript'}
value={compileCode}
options={{ ...MonacoEditorConfig, readOnly: false }}
/>
</>
)
}
export default Preview

View File

@@ -9,9 +9,7 @@ class Compiler {
void esbuild.initialize({ worker: true, wasmURL: wasm }).then(() => {
this.init = true
})
} catch (e) {
throw e
}
} catch (e) {}
}
transform = (code: string, loader: Loader) =>

View File

@@ -3,18 +3,19 @@ import importMap from '@/components/Playground/template/import-map.json?raw'
import AppCss from '@/components/Playground/template/src/App.css?raw'
import App from '@/components/Playground/template/src/App.tsx?raw'
import main from '@/components/Playground/template/src/main.tsx?raw'
import { IFile, IFiles } from '@/components/Playground/shared'
import { IFile, IFiles, ILanguage } from '@/components/Playground/shared'
export const MAIN_FILE_NAME = 'App.tsx'
export const IMPORT_MAP_FILE_NAME = 'import-map.json'
export const ENTRY_FILE_NAME = 'main.tsx'
export const fileNameToLanguage = (name: string) => {
export const fileNameToLanguage = (name: string): ILanguage => {
const suffix = name.split('.').pop() || ''
if (['js', 'jsx'].includes(suffix)) return 'javascript'
if (['ts', 'tsx'].includes(suffix)) return 'typescript'
if (['json'].includes(suffix)) return 'json'
if (['css'].includes(suffix)) return 'css'
if (['svg'].includes(suffix)) return 'xml'
return 'javascript'
}
@@ -62,27 +63,30 @@ export const getModuleFile = (files: IFiles, moduleName: string) => {
return files[_moduleName]
}
export const jsToBlob = (code: string) => {
return URL.createObjectURL(new Blob([code], { type: 'application/javascript' }))
}
export const jsonToJs = (file: IFile) => {
const js = `export default ${file.value}`
return URL.createObjectURL(new Blob([js], { type: 'application/javascript' }))
return `export default ${file.value}`
}
export const cssToJs = (file: IFile) => {
const randomId = new Date().getTime()
const js = `
(() => {
let stylesheet = document.getElementById('style_${randomId}_${file.name}');
if (!stylesheet) {
stylesheet = document.createElement('style')
stylesheet.setAttribute('id', 'style_${randomId}_${file.name}')
document.head.appendChild(stylesheet)
}
const styles = document.createTextNode(\`${file.value}\`)
stylesheet.innerHTML = ''
stylesheet.appendChild(styles)
})()
`
return URL.createObjectURL(new Blob([js], { type: 'application/javascript' }))
return `(() => {
let stylesheet = document.getElementById('style_${randomId}_${file.name}');
if (!stylesheet) {
stylesheet = document.createElement('style')
stylesheet.setAttribute('id', 'style_${randomId}_${file.name}')
document.head.appendChild(stylesheet)
}
const styles = document.createTextNode(
\`${file.value}\`
)
stylesheet.innerHTML = ''
stylesheet.appendChild(styles)
})()
`
}
export const initFiles: IFiles = getFilesFromUrl() || {

View File

@@ -1,13 +1,14 @@
import React from 'react'
import React, { useState } from 'react'
import CodeEditor from '@/components/Playground/CodeEditor'
import { initFiles } from '@/components/Playground/files'
import { initFiles, MAIN_FILE_NAME } from '@/components/Playground/files'
import { IFiles } from '@/components/Playground/shared'
import FitFullscreen from '@/components/common/FitFullscreen.tsx'
import FlexBox from '@/components/common/FlexBox.tsx'
import Preview from '@/components/Playground/Preview'
import FitFullscreen from '@/components/common/FitFullscreen'
import FlexBox from '@/components/common/FlexBox'
import Transform from '@/components/Playground/Transform'
const OnlineEditor: React.FC = () => {
const [files, setFiles] = useState<IFiles>(initFiles)
const [selectedFileName, setSelectedFileName] = useState(MAIN_FILE_NAME)
return (
<>
@@ -15,12 +16,14 @@ const OnlineEditor: React.FC = () => {
<FlexBox style={{ width: '100%', height: '100%' }} direction={'horizontal'}>
<CodeEditor
files={files}
onSelectedFileChange={setSelectedFileName}
selectedFileName={selectedFileName}
onAddFile={(_, files) => setFiles(files)}
onRemoveFile={(_, files) => setFiles(files)}
onRenameFile={(_, __, files) => setFiles(files)}
onChangeFileContent={(_, __, files) => setFiles(files)}
/>
<Preview files={files} />
<Transform file={files[selectedFileName]} />
</FlexBox>
</FitFullscreen>
</>