Detect ts files.

This commit is contained in:
muzam1l
2023-01-06 19:58:03 +05:30
parent 48daf1c5c8
commit e75443c8e2
5 changed files with 137 additions and 126 deletions

View File

@@ -125,6 +125,16 @@ const HooksEditor = () => {
const file = snap.files[snap.active]
const tsLangWarningRef = useRef(false)
useEffect(() => {
if (file?.language === 'ts' && !tsLangWarningRef.current) {
alert(
'Typescript suppport for hooks is still in early planning stage, write actual hooks in C only for now!'
)
tsLangWarningRef.current = true
}
}, [file])
const renderNav = () => (
<Tabs
label="File"
@@ -221,7 +231,7 @@ const HooksEditor = () => {
monaco.languages.register({
id: 'text',
extensions: ['.txt'],
mimetypes: ['text/plain'],
mimetypes: ['text/plain']
})
MonacoServices.install(monaco)
const webSocket = createWebSocket(

View File

@@ -150,7 +150,9 @@ const Home: NextPage = () => {
const activeFile = snap.files[snap.active] as IFile | undefined
const activeFileExt = getFileExtention(activeFile?.name)
const canCompile = activeFileExt === 'c' || activeFileExt === 'wat'
const canCompile = activeFileExt === 'c' || activeFileExt === 'wat' || activeFileExt === 'ts'
const isCompiling = snap.compiling.includes(snap.active);
return (
<Split
direction="vertical"
@@ -166,7 +168,9 @@ const Home: NextPage = () => {
{canCompile && (
<Hotkeys
keyName="command+b,ctrl+b"
onKeyDown={() => !snap.compiling && snap.files.length && compileCode(snap.active)}
onKeyDown={() =>
snap.compiling === undefined && snap.files.length && compileCode(snap.active)
}
>
<Flex
css={{
@@ -183,7 +187,7 @@ const Home: NextPage = () => {
variant="primary"
uppercase
disabled={!snap.files.length}
isLoading={snap.compiling}
isLoading={isCompiling}
onClick={() => compileCode(snap.active)}
>
<Play weight="bold" size="16px" />
@@ -200,7 +204,9 @@ const Home: NextPage = () => {
{activeFileExt === 'js' && (
<Hotkeys
keyName="command+b,ctrl+b"
onKeyDown={() => !snap.compiling && snap.files.length && compileCode(snap.active)}
onKeyDown={() =>
!isCompiling && snap.files.length && compileCode(snap.active)
}
>
<Flex
css={{

View File

@@ -1,97 +1,47 @@
import toast from 'react-hot-toast'
import Router from 'next/router'
import state from '../index'
import state, { IFile } from '../index'
import { saveFile } from './saveFile'
import { decodeBinary } from '../../utils/decodeBinary'
import { ref } from 'valtio'
/* compileCode sends the code of the active file to compile endpoint
* If all goes well you will get base64 encoded wasm file back with
* some extra logging information if we can provide it. This function
* also decodes the returned wasm and creates human readable WAT file
* out of it and store both in global state.
*/
type CompilationResult = Pick<IFile, "compiledContent" | "compiledWatContent">
export const compileCode = async (activeId: number) => {
// Save the file to global state
saveFile(false, activeId)
const file = state.files[activeId]
if (file.name.endsWith('.wat')) {
return compileWat(activeId)
}
if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
throw Error('Missing env!')
}
// Bail out if we're already compiling
if (state.compiling) {
// if compiling is ongoing return // TODO Inform user about it.
// Bail out if we're already compiling the file.
if (!file || state.compiling.includes(activeId)) {
return
}
// Set loading state to true
state.compiling = true
state.logs = []
try {
state.compiling.push(activeId)
state.logs = []
file.containsErrors = false
let res: Response
try {
res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
output: 'wasm',
compress: true,
strip: state.compileOptions.strip,
files: [
{
type: 'c',
options: state.compileOptions.optimizationLevel || '-O2',
name: file.name,
src: file.content
}
]
})
})
} catch (error) {
throw Error('Something went wrong, check your network connection and try again!')
let result: CompilationResult;
if (file.name.endsWith('.wat')) {
result = await compileWat(file);
}
const json = await res.json()
state.compiling = false
if (!json.success) {
const errors = [json.message]
if (json.tasks && json.tasks.length > 0) {
json.tasks.forEach((task: any) => {
if (!task.success) {
errors.push(task?.console)
}
})
}
throw errors
if (navigator?.onLine === false) {
throw Error('You seem offline, check you internet connection and try again!')
}
try {
// Decode base64 encoded wasm that is coming back from the endpoint
const bufferData = await decodeBinary(json.output)
// Import wabt from and create human readable version of wasm file and
// put it into state
const ww = await (await import('wabt')).default()
const myModule = ww.readWasm(new Uint8Array(bufferData), {
readDebugNames: true
})
myModule.applyNames()
const wast = myModule.toText({ foldExprs: false, inlineExport: false })
file.compiledContent = ref(bufferData)
file.lastCompiled = new Date()
file.compiledValueSnapshot = file.content
file.compiledWatContent = wast
} catch (error) {
throw Error('Invalid compilation result produced, check your code for errors and try again!')
if (file.language === "ts") {
result = await compileTs(file);
}
else if (file.language === 'c') {
result = await compileC(file)
}
else throw Error("Unknown file type.")
file.lastCompiled = new Date();
file.compiledValueSnapshot = file.content;
file.compiledContent = result.compiledContent;
file.compiledWatContent = result.compiledWatContent;
toast.success('Compiled successfully!', { position: 'bottom-center' })
state.logs.push({
type: 'success',
@@ -100,7 +50,7 @@ export const compileCode = async (activeId: number) => {
linkText: 'Go to deploy'
})
} catch (err) {
console.log(err)
console.error(err)
if (err instanceof Array && typeof err[0] === 'string') {
err.forEach(message => {
@@ -117,55 +67,99 @@ export const compileCode = async (activeId: number) => {
} else {
state.logs.push({
type: 'error',
message: 'Something went wrong, come back later!'
message: 'Something went wrong, try again later!'
})
}
state.compiling = false
toast.error(`Error occurred while compiling!`, { position: 'bottom-center' })
file.containsErrors = true
}
state.compiling = state.compiling.filter(id => id !== activeId);
}
export const compileWat = async (activeId: number) => {
if (state.compiling) return;
const file = state.files[activeId]
state.compiling = true
state.logs = []
try {
const wabt = await (await import('wabt')).default()
const module = wabt.parseWat(file.name, file.content);
module.resolveNames();
module.validate();
const { buffer } = module.toBinary({
log: false,
write_debug_names: true,
});
file.compiledContent = ref(buffer)
file.lastCompiled = new Date()
file.compiledValueSnapshot = file.content
file.compiledWatContent = file.content
toast.success('Compiled successfully!', { position: 'bottom-center' })
state.logs.push({
type: 'success',
message: `File ${state.files?.[activeId]?.name} compiled successfully. Ready to deploy.`,
link: Router.asPath.replace('develop', 'deploy'),
linkText: 'Go to deploy'
/* compileC sends the code of the active file to compile endpoint
* If all goes well you will get base64 encoded wasm file back with
* some extra logging information if we can provide it. This function
* also decodes the returned wasm and creates human readable WAT file
* out of it and store both in global state.
*/
export const compileC = async (file: IFile): Promise<CompilationResult> => {
if (!process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT) {
throw Error('Missing C compile endpoint!')
}
let res: Response
res = await fetch(process.env.NEXT_PUBLIC_COMPILE_API_ENDPOINT, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
output: 'wasm',
compress: true,
strip: state.compileOptions.strip,
files: [
{
type: 'c',
options: state.compileOptions.optimizationLevel || '-O2',
name: file.name,
src: file.content
}
]
})
} catch (err) {
console.log(err)
let message = "Error compiling WAT file!"
if (err instanceof Error) {
message = err.message
})
const json = await res.json()
if (!json.success) {
const errors = [json.message]
if (json.tasks && json.tasks.length > 0) {
json.tasks.forEach((task: any) => {
if (!task.success) {
errors.push(task?.console)
}
})
}
state.logs.push({
type: 'error',
message
})
toast.error(`Error occurred while compiling!`, { position: 'bottom-center' })
file.containsErrors = true
throw errors
}
state.compiling = false
try {
// Decode base64 encoded wasm that is coming back from the endpoint
const bufferData = await decodeBinary(json.output)
// Import wabt from and create human readable version of wasm file and
// put it into state
const ww = await (await import('wabt')).default()
const myModule = ww.readWasm(new Uint8Array(bufferData), {
readDebugNames: true
})
myModule.applyNames()
const wast = myModule.toText({ foldExprs: false, inlineExport: false })
return {
compiledContent: ref(bufferData),
compiledWatContent: wast
}
} catch (error) {
throw Error('Invalid compilation result produced, check your code for errors and try again!')
}
}
export const compileWat = async (file: IFile): Promise<CompilationResult> => {
const wabt = await (await import('wabt')).default()
const module = wabt.parseWat(file.name, file.content);
module.resolveNames();
module.validate();
const { buffer } = module.toBinary({
log: false,
write_debug_names: true,
});
return {
compiledContent: ref(buffer),
compiledWatContent: file.content,
}
}
export const compileTs = async (file: IFile): Promise<CompilationResult> => {
throw Error("TBD")
}

View File

@@ -1,8 +1,8 @@
import { getFileExtention } from '../../utils/helpers'
import state, { IFile } from '../index'
import state, { IFile, ILang } from '../index'
const languageMapping: Record<string, string | undefined> = {
ts: 'typescript',
const languageMapping: Record<string, ILang | undefined> = {
ts: 'ts',
js: 'javascript',
md: 'markdown',
c: 'c',

View File

@@ -9,9 +9,10 @@ declare module 'valtio' {
function snapshot<T extends object>(p: T): T
}
export type ILang = "ts" | "javascript" | "markdown" | "c" | "text"
export interface IFile {
name: string
language: string
language: ILang | undefined
content: string
compiledValueSnapshot?: string
compiledContent?: ArrayBuffer | null
@@ -66,7 +67,7 @@ export interface IState {
loading: boolean
gistLoading: boolean
zipLoading: boolean
compiling: boolean
compiling: /* file id */ number[]
logs: ILog[]
deployLogs: ILog[]
transactionLogs: ILog[]
@@ -98,7 +99,7 @@ let initialState: IState = {
// Active file index on the Deploy page editor
activeWat: 0,
loading: false,
compiling: false,
compiling: [],
logs: [],
deployLogs: [],
transactionLogs: [],