Optimize Transform
This commit is contained in:
@@ -37,7 +37,7 @@ const Editor: React.FC<EditorProps> = ({
|
|||||||
dispose: () => undefined
|
dispose: () => undefined
|
||||||
})
|
})
|
||||||
const { total, finished, onWatch } = useTypesProgress()
|
const { total, finished, onWatch } = useTypesProgress()
|
||||||
const file = files[selectedFileName] ?? { name: 'Untitled' }
|
const file = files[selectedFileName] || { name: 'Untitled' }
|
||||||
|
|
||||||
const handleOnEditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => {
|
const handleOnEditorDidMount = (editor: editor.IStandaloneCodeEditor, monaco: Monaco) => {
|
||||||
editorRef.current = editor
|
editorRef.current = editor
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ const CodeEditor: React.FC<CodeEditorProps> = ({
|
|||||||
(item) => ![IMPORT_MAP_FILE_NAME, ENTRY_FILE_NAME].includes(item) && !files[item].hidden
|
(item) => ![IMPORT_MAP_FILE_NAME, ENTRY_FILE_NAME].includes(item) && !files[item].hidden
|
||||||
)
|
)
|
||||||
const propsSelectedFileName =
|
const propsSelectedFileName =
|
||||||
props.selectedFileName ?? (filteredFilesName.length ? filteredFilesName[0] : '')
|
props.selectedFileName || (filteredFilesName.length ? filteredFilesName[0] : '')
|
||||||
const [selectedFileName, setSelectedFileName] = useState(propsSelectedFileName)
|
const [selectedFileName, setSelectedFileName] = useState(propsSelectedFileName)
|
||||||
const [errorMsg, setErrorMsg] = useState('')
|
const [errorMsg, setErrorMsg] = useState('')
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
77
src/components/Playground/Transform/index.tsx
Normal file
77
src/components/Playground/Transform/index.tsx
Normal 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
|
||||||
@@ -9,9 +9,7 @@ class Compiler {
|
|||||||
void esbuild.initialize({ worker: true, wasmURL: wasm }).then(() => {
|
void esbuild.initialize({ worker: true, wasmURL: wasm }).then(() => {
|
||||||
this.init = true
|
this.init = true
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {}
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
transform = (code: string, loader: Loader) =>
|
transform = (code: string, loader: Loader) =>
|
||||||
|
|||||||
@@ -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 AppCss from '@/components/Playground/template/src/App.css?raw'
|
||||||
import App from '@/components/Playground/template/src/App.tsx?raw'
|
import App from '@/components/Playground/template/src/App.tsx?raw'
|
||||||
import main from '@/components/Playground/template/src/main.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 MAIN_FILE_NAME = 'App.tsx'
|
||||||
export const IMPORT_MAP_FILE_NAME = 'import-map.json'
|
export const IMPORT_MAP_FILE_NAME = 'import-map.json'
|
||||||
export const ENTRY_FILE_NAME = 'main.tsx'
|
export const ENTRY_FILE_NAME = 'main.tsx'
|
||||||
|
|
||||||
export const fileNameToLanguage = (name: string) => {
|
export const fileNameToLanguage = (name: string): ILanguage => {
|
||||||
const suffix = name.split('.').pop() || ''
|
const suffix = name.split('.').pop() || ''
|
||||||
if (['js', 'jsx'].includes(suffix)) return 'javascript'
|
if (['js', 'jsx'].includes(suffix)) return 'javascript'
|
||||||
if (['ts', 'tsx'].includes(suffix)) return 'typescript'
|
if (['ts', 'tsx'].includes(suffix)) return 'typescript'
|
||||||
if (['json'].includes(suffix)) return 'json'
|
if (['json'].includes(suffix)) return 'json'
|
||||||
if (['css'].includes(suffix)) return 'css'
|
if (['css'].includes(suffix)) return 'css'
|
||||||
|
if (['svg'].includes(suffix)) return 'xml'
|
||||||
return 'javascript'
|
return 'javascript'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,27 +63,30 @@ export const getModuleFile = (files: IFiles, moduleName: string) => {
|
|||||||
return files[_moduleName]
|
return files[_moduleName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const jsToBlob = (code: string) => {
|
||||||
|
return URL.createObjectURL(new Blob([code], { type: 'application/javascript' }))
|
||||||
|
}
|
||||||
|
|
||||||
export const jsonToJs = (file: IFile) => {
|
export const jsonToJs = (file: IFile) => {
|
||||||
const js = `export default ${file.value}`
|
return `export default ${file.value}`
|
||||||
return URL.createObjectURL(new Blob([js], { type: 'application/javascript' }))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const cssToJs = (file: IFile) => {
|
export const cssToJs = (file: IFile) => {
|
||||||
const randomId = new Date().getTime()
|
const randomId = new Date().getTime()
|
||||||
const js = `
|
return `(() => {
|
||||||
(() => {
|
let stylesheet = document.getElementById('style_${randomId}_${file.name}');
|
||||||
let stylesheet = document.getElementById('style_${randomId}_${file.name}');
|
if (!stylesheet) {
|
||||||
if (!stylesheet) {
|
stylesheet = document.createElement('style')
|
||||||
stylesheet = document.createElement('style')
|
stylesheet.setAttribute('id', 'style_${randomId}_${file.name}')
|
||||||
stylesheet.setAttribute('id', 'style_${randomId}_${file.name}')
|
document.head.appendChild(stylesheet)
|
||||||
document.head.appendChild(stylesheet)
|
}
|
||||||
}
|
const styles = document.createTextNode(
|
||||||
const styles = document.createTextNode(\`${file.value}\`)
|
\`${file.value}\`
|
||||||
stylesheet.innerHTML = ''
|
)
|
||||||
stylesheet.appendChild(styles)
|
stylesheet.innerHTML = ''
|
||||||
})()
|
stylesheet.appendChild(styles)
|
||||||
`
|
})()
|
||||||
return URL.createObjectURL(new Blob([js], { type: 'application/javascript' }))
|
`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initFiles: IFiles = getFilesFromUrl() || {
|
export const initFiles: IFiles = getFilesFromUrl() || {
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
import React from 'react'
|
import React, { useState } from 'react'
|
||||||
import CodeEditor from '@/components/Playground/CodeEditor'
|
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 { IFiles } from '@/components/Playground/shared'
|
||||||
import FitFullscreen from '@/components/common/FitFullscreen.tsx'
|
import FitFullscreen from '@/components/common/FitFullscreen'
|
||||||
import FlexBox from '@/components/common/FlexBox.tsx'
|
import FlexBox from '@/components/common/FlexBox'
|
||||||
import Preview from '@/components/Playground/Preview'
|
import Transform from '@/components/Playground/Transform'
|
||||||
|
|
||||||
const OnlineEditor: React.FC = () => {
|
const OnlineEditor: React.FC = () => {
|
||||||
const [files, setFiles] = useState<IFiles>(initFiles)
|
const [files, setFiles] = useState<IFiles>(initFiles)
|
||||||
|
const [selectedFileName, setSelectedFileName] = useState(MAIN_FILE_NAME)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -15,12 +16,14 @@ const OnlineEditor: React.FC = () => {
|
|||||||
<FlexBox style={{ width: '100%', height: '100%' }} direction={'horizontal'}>
|
<FlexBox style={{ width: '100%', height: '100%' }} direction={'horizontal'}>
|
||||||
<CodeEditor
|
<CodeEditor
|
||||||
files={files}
|
files={files}
|
||||||
|
onSelectedFileChange={setSelectedFileName}
|
||||||
|
selectedFileName={selectedFileName}
|
||||||
onAddFile={(_, files) => setFiles(files)}
|
onAddFile={(_, files) => setFiles(files)}
|
||||||
onRemoveFile={(_, files) => setFiles(files)}
|
onRemoveFile={(_, files) => setFiles(files)}
|
||||||
onRenameFile={(_, __, files) => setFiles(files)}
|
onRenameFile={(_, __, files) => setFiles(files)}
|
||||||
onChangeFileContent={(_, __, files) => setFiles(files)}
|
onChangeFileContent={(_, __, files) => setFiles(files)}
|
||||||
/>
|
/>
|
||||||
<Preview files={files} />
|
<Transform file={files[selectedFileName]} />
|
||||||
</FlexBox>
|
</FlexBox>
|
||||||
</FitFullscreen>
|
</FitFullscreen>
|
||||||
</>
|
</>
|
||||||
|
|||||||
Reference in New Issue
Block a user