Detect ts files.
This commit is contained in:
@@ -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(
|
||||
|
||||
@@ -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={{
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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: [],
|
||||
|
||||
Reference in New Issue
Block a user