Compare commits
	
		
			18 Commits
		
	
	
		
			main
			...
			feat/ts-su
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					513db7bf09 | ||
| 
						 | 
					969fd0f63c | ||
| 
						 | 
					bd1a0c9836 | ||
| 
						 | 
					ba95eb5be2 | ||
| 
						 | 
					6911bcc0f3 | ||
| 
						 | 
					a6055feb1f | ||
| 
						 | 
					31a185f49f | ||
| 
						 | 
					d693458421 | ||
| 
						 | 
					692bbeb9b2 | ||
| 
						 | 
					a29368d633 | ||
| 
						 | 
					0f32fcb71c | ||
| 
						 | 
					90db6b1f12 | ||
| 
						 | 
					dde0317544 | ||
| 
						 | 
					a6ad39f6e8 | ||
| 
						 | 
					0536e48cfe | ||
| 
						 | 
					a08823a38b | ||
| 
						 | 
					653e28583d | ||
| 
						 | 
					e75443c8e2 | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -23,6 +23,7 @@
 | 
			
		||||
npm-debug.log*
 | 
			
		||||
yarn-debug.log*
 | 
			
		||||
yarn-error.log*
 | 
			
		||||
.yarn
 | 
			
		||||
 | 
			
		||||
# local env files
 | 
			
		||||
.env.local
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,7 @@ const DeployEditor = () => {
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      {`You haven't compiled any files yet, compile files on `}
 | 
			
		||||
      <NextLink shallow href={`/develop/${router.query.slug}`} passHref>
 | 
			
		||||
      <NextLink legacyBehavior shallow href={`/develop/${router.query.slug}`} passHref>
 | 
			
		||||
        <Link as="a">develop view</Link>
 | 
			
		||||
      </NextLink>
 | 
			
		||||
    </Text>
 | 
			
		||||
 
 | 
			
		||||
@@ -155,9 +155,11 @@ const EditorNavigation = ({ renderNav }: { renderNav?: () => ReactNode }) => {
 | 
			
		||||
                >
 | 
			
		||||
                  <Image
 | 
			
		||||
                    src={session?.user?.image || ''}
 | 
			
		||||
                    width="30px"
 | 
			
		||||
                    height="30px"
 | 
			
		||||
                    objectFit="cover"
 | 
			
		||||
                    width="30"
 | 
			
		||||
                    height="30"
 | 
			
		||||
                    style={{
 | 
			
		||||
                      objectFit: 'cover'
 | 
			
		||||
                    }}
 | 
			
		||||
                    alt="User avatar"
 | 
			
		||||
                  />
 | 
			
		||||
                </Box>
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,6 @@ const EnrichLog: FC<EnrichLogProps> = ({ str }) => {
 | 
			
		||||
      if (match.startsWith('HookSet')) {
 | 
			
		||||
        const code = match.match(/^HookSet\((\d+)\)/)?.[1]
 | 
			
		||||
        const val = hookSetCodes.find(v => code && v.code === +code)
 | 
			
		||||
        console.log({ code, val })
 | 
			
		||||
        if (!val) return match
 | 
			
		||||
 | 
			
		||||
        const content = capitalize(val.description) || 'No hint available!'
 | 
			
		||||
 
 | 
			
		||||
@@ -94,6 +94,14 @@ const setMarkers = (monacoE: typeof monaco) => {
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const langWarnings: Record<string, { shown: boolean; message: string }> = {
 | 
			
		||||
  ts: {
 | 
			
		||||
    shown: false,
 | 
			
		||||
    message:
 | 
			
		||||
      'Typescript suppport for hooks is still in early planning stage, write actual hooks in C only for now!'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const HooksEditor = () => {
 | 
			
		||||
  const editorRef = useRef<monaco.editor.IStandaloneCodeEditor>()
 | 
			
		||||
  const monacoRef = useRef<typeof monaco>()
 | 
			
		||||
@@ -125,6 +133,14 @@ const HooksEditor = () => {
 | 
			
		||||
 | 
			
		||||
  const file = snap.files[snap.active]
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    let warning = langWarnings[file?.language || '']
 | 
			
		||||
    if (warning && !warning.shown) {
 | 
			
		||||
      alert(warning.message) // TODO Custom dialog.
 | 
			
		||||
      warning.shown = true
 | 
			
		||||
    }
 | 
			
		||||
  }, [file])
 | 
			
		||||
 | 
			
		||||
  const renderNav = () => (
 | 
			
		||||
    <Tabs
 | 
			
		||||
      label="File"
 | 
			
		||||
@@ -221,7 +237,7 @@ const HooksEditor = () => {
 | 
			
		||||
                monaco.languages.register({
 | 
			
		||||
                  id: 'text',
 | 
			
		||||
                  extensions: ['.txt'],
 | 
			
		||||
                  mimetypes: ['text/plain'],
 | 
			
		||||
                  mimetypes: ['text/plain']
 | 
			
		||||
                })
 | 
			
		||||
                MonacoServices.install(monaco)
 | 
			
		||||
                const webSocket = createWebSocket(
 | 
			
		||||
@@ -240,7 +256,7 @@ const HooksEditor = () => {
 | 
			
		||||
                      try {
 | 
			
		||||
                        disposable.dispose()
 | 
			
		||||
                      } catch (err) {
 | 
			
		||||
                        console.log('err', err)
 | 
			
		||||
                        console.error('err', err)
 | 
			
		||||
                      }
 | 
			
		||||
                    })
 | 
			
		||||
                  }
 | 
			
		||||
 
 | 
			
		||||
@@ -160,7 +160,7 @@ export const Log: FC<ILog> = ({
 | 
			
		||||
        )}
 | 
			
		||||
        <Pre>{message}</Pre>
 | 
			
		||||
        {link && (
 | 
			
		||||
          <NextLink href={link} shallow passHref>
 | 
			
		||||
          <NextLink legacyBehavior href={link} shallow passHref>
 | 
			
		||||
            <Link as="a">{linkText}</Link>
 | 
			
		||||
          </NextLink>
 | 
			
		||||
        )}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import React from 'react'
 | 
			
		||||
import Link from 'next/link'
 | 
			
		||||
import NextLink from 'next/link'
 | 
			
		||||
 | 
			
		||||
import { useSnapshot } from 'valtio'
 | 
			
		||||
import { useRouter } from 'next/router'
 | 
			
		||||
@@ -78,7 +78,7 @@ const Navigation = () => {
 | 
			
		||||
            pr: '$4'
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          <Link href={gistId ? `/develop/${gistId}` : '/develop'} passHref>
 | 
			
		||||
          <NextLink legacyBehavior href={gistId ? `/develop/${gistId}` : '/develop'} passHref>
 | 
			
		||||
            <Box
 | 
			
		||||
              as="a"
 | 
			
		||||
              css={{
 | 
			
		||||
@@ -89,7 +89,7 @@ const Navigation = () => {
 | 
			
		||||
            >
 | 
			
		||||
              <Logo width="32px" height="32px" />
 | 
			
		||||
            </Box>
 | 
			
		||||
          </Link>
 | 
			
		||||
          </NextLink>
 | 
			
		||||
          <Flex
 | 
			
		||||
            css={{
 | 
			
		||||
              ml: '$5',
 | 
			
		||||
@@ -105,7 +105,8 @@ const Navigation = () => {
 | 
			
		||||
                <Text css={{ fontSize: '$xs', color: '$mauve10', lineHeight: 1 }}>
 | 
			
		||||
                  {snap.files.length > 0 ? 'Gist: ' : 'Builder'}
 | 
			
		||||
                  {snap.files.length > 0 && (
 | 
			
		||||
                    <Link
 | 
			
		||||
                    <NextLink
 | 
			
		||||
                      legacyBehavior
 | 
			
		||||
                      href={`https://gist.github.com/${snap.gistOwner || ''}/${snap.gistId || ''}`}
 | 
			
		||||
                      passHref
 | 
			
		||||
                    >
 | 
			
		||||
@@ -117,7 +118,7 @@ const Navigation = () => {
 | 
			
		||||
                      >
 | 
			
		||||
                        {`${snap.gistOwner || '-'}/${truncate(snap.gistId || '')}`}
 | 
			
		||||
                      </Text>
 | 
			
		||||
                    </Link>
 | 
			
		||||
                    </NextLink>
 | 
			
		||||
                  )}
 | 
			
		||||
                </Text>
 | 
			
		||||
              </>
 | 
			
		||||
@@ -336,29 +337,39 @@ const Navigation = () => {
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            <ButtonGroup>
 | 
			
		||||
              <Link href={gistId ? `/develop/${gistId}` : '/develop'} passHref shallow>
 | 
			
		||||
              <NextLink
 | 
			
		||||
                legacyBehavior
 | 
			
		||||
                href={gistId ? `/develop/${gistId}` : '/develop'}
 | 
			
		||||
                passHref
 | 
			
		||||
                shallow
 | 
			
		||||
              >
 | 
			
		||||
                <Button as="a" outline={!router.pathname.includes('/develop')} uppercase>
 | 
			
		||||
                  Develop
 | 
			
		||||
                </Button>
 | 
			
		||||
              </Link>
 | 
			
		||||
              <Link href={gistId ? `/deploy/${gistId}` : '/deploy'} passHref shallow>
 | 
			
		||||
              </NextLink>
 | 
			
		||||
              <NextLink
 | 
			
		||||
                legacyBehavior
 | 
			
		||||
                href={gistId ? `/deploy/${gistId}` : '/deploy'}
 | 
			
		||||
                passHref
 | 
			
		||||
                shallow
 | 
			
		||||
              >
 | 
			
		||||
                <Button as="a" outline={!router.pathname.includes('/deploy')} uppercase>
 | 
			
		||||
                  Deploy
 | 
			
		||||
                </Button>
 | 
			
		||||
              </Link>
 | 
			
		||||
              <Link href={gistId ? `/test/${gistId}` : '/test'} passHref shallow>
 | 
			
		||||
              </NextLink>
 | 
			
		||||
              <NextLink legacyBehavior href={gistId ? `/test/${gistId}` : '/test'} passHref shallow>
 | 
			
		||||
                <Button as="a" outline={!router.pathname.includes('/test')} uppercase>
 | 
			
		||||
                  Test
 | 
			
		||||
                </Button>
 | 
			
		||||
              </Link>
 | 
			
		||||
              </NextLink>
 | 
			
		||||
            </ButtonGroup>
 | 
			
		||||
            <Link href="https://xrpl-hooks.readme.io/v2.0" passHref>
 | 
			
		||||
            <NextLink legacyBehavior href="https://xrpl-hooks.readme.io/v2.0" passHref>
 | 
			
		||||
              <a target="_blank" rel="noreferrer noopener">
 | 
			
		||||
                <Button outline>
 | 
			
		||||
                  <BookOpen size="15px" />
 | 
			
		||||
                </Button>
 | 
			
		||||
              </a>
 | 
			
		||||
            </Link>
 | 
			
		||||
            </NextLink>
 | 
			
		||||
          </Stack>
 | 
			
		||||
        </Flex>
 | 
			
		||||
      </Container>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import type { NextRequest, NextFetchEvent } from 'next/server'
 | 
			
		||||
import type { NextRequest } from 'next/server'
 | 
			
		||||
import { NextResponse as Response } from 'next/server'
 | 
			
		||||
 | 
			
		||||
export default function middleware(req: NextRequest, ev: NextFetchEvent) {
 | 
			
		||||
export default function middleware(req: NextRequest) {
 | 
			
		||||
  if (req.nextUrl.pathname === '/') {
 | 
			
		||||
    const url = req.nextUrl.clone()
 | 
			
		||||
    url.pathname = '/develop'
 | 
			
		||||
@@ -8,8 +8,16 @@ module.exports = {
 | 
			
		||||
    config.resolve.alias['vscode'] = require.resolve(
 | 
			
		||||
      '@codingame/monaco-languageclient/lib/vscode-compatibility'
 | 
			
		||||
    )
 | 
			
		||||
    config.experiments = {
 | 
			
		||||
      topLevelAwait: true,
 | 
			
		||||
      layers: true
 | 
			
		||||
    }
 | 
			
		||||
    if (!isServer) {
 | 
			
		||||
      config.resolve.fallback.fs = false
 | 
			
		||||
      config.resolve.fallback = {
 | 
			
		||||
        ...config.resolve.fallback,
 | 
			
		||||
        fs: false,
 | 
			
		||||
        module: false
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    config.module.rules.push({
 | 
			
		||||
      test: [/\.md$/, /hook-bundle\.js$/],
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								package.json
									
									
									
									
									
								
							@@ -14,6 +14,7 @@
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
    "@codingame/monaco-jsonrpc": "^0.3.1",
 | 
			
		||||
    "@codingame/monaco-languageclient": "^0.17.0",
 | 
			
		||||
    "@eqlabs/assemblyscript": "^0.0.0-alpha.1680097351",
 | 
			
		||||
    "@monaco-editor/react": "^4.4.5",
 | 
			
		||||
    "@octokit/core": "^3.5.1",
 | 
			
		||||
    "@radix-ui/colors": "^0.1.7",
 | 
			
		||||
@@ -37,7 +38,7 @@
 | 
			
		||||
    "lodash.uniqby": "^4.7.0",
 | 
			
		||||
    "lodash.xor": "^4.5.0",
 | 
			
		||||
    "monaco-editor": "^0.33.0",
 | 
			
		||||
    "next": "^12.0.4",
 | 
			
		||||
    "next": "^13.1.1",
 | 
			
		||||
    "next-auth": "^4.10.3",
 | 
			
		||||
    "next-plausible": "^3.2.0",
 | 
			
		||||
    "next-themes": "^0.1.1",
 | 
			
		||||
@@ -49,8 +50,8 @@
 | 
			
		||||
    "postinstall-postinstall": "^2.1.0",
 | 
			
		||||
    "prettier": "^2.7.1",
 | 
			
		||||
    "re-resizable": "^6.9.1",
 | 
			
		||||
    "react": "17.0.2",
 | 
			
		||||
    "react-dom": "17.0.2",
 | 
			
		||||
    "react": "^18.2.0",
 | 
			
		||||
    "react-dom": "^18.2.0",
 | 
			
		||||
    "react-hook-form": "^7.28.0",
 | 
			
		||||
    "react-hot-keys": "^2.7.1",
 | 
			
		||||
    "react-hot-toast": "^2.1.1",
 | 
			
		||||
@@ -65,7 +66,7 @@
 | 
			
		||||
    "valtio": "^1.2.5",
 | 
			
		||||
    "vscode-languageserver": "^7.0.0",
 | 
			
		||||
    "vscode-uri": "^3.0.2",
 | 
			
		||||
    "wabt": "^1.0.30",
 | 
			
		||||
    "wabt": "^1.0.32",
 | 
			
		||||
    "xrpl-accountlib": "^1.6.1",
 | 
			
		||||
    "xrpl-client": "^2.0.2"
 | 
			
		||||
  },
 | 
			
		||||
@@ -78,7 +79,7 @@
 | 
			
		||||
    "@types/react": "17.0.31",
 | 
			
		||||
    "browserify": "^17.0.0",
 | 
			
		||||
    "eslint": "7.32.0",
 | 
			
		||||
    "eslint-config-next": "11.1.2",
 | 
			
		||||
    "eslint-config-next": "^13.1.1",
 | 
			
		||||
    "raw-loader": "^4.0.2",
 | 
			
		||||
    "typescript": "4.4.4"
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ import { ChatCircleText } from 'phosphor-react'
 | 
			
		||||
TimeAgo.setDefaultLocale(en.locale)
 | 
			
		||||
TimeAgo.addLocale(en)
 | 
			
		||||
 | 
			
		||||
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
 | 
			
		||||
function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps<{ session?: any }>) {
 | 
			
		||||
  const router = useRouter()
 | 
			
		||||
  const slug = router.query?.slug
 | 
			
		||||
  const gistId = (Array.isArray(slug) && slug[0]) ?? null
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ export default async function handler(
 | 
			
		||||
    }
 | 
			
		||||
    return res.status(200).json(json)
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.log(err)
 | 
			
		||||
    console.error(err)
 | 
			
		||||
    return res.status(500).json({ error: 'Server error' })
 | 
			
		||||
  }
 | 
			
		||||
  return res.status(500).json({ error: 'Not able to create faucet, try again' })
 | 
			
		||||
 
 | 
			
		||||
@@ -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,49 @@
 | 
			
		||||
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'
 | 
			
		||||
import asc from "@eqlabs/assemblyscript/dist/asc"
 | 
			
		||||
import { getFileExtention } from '../../utils/helpers'
 | 
			
		||||
 | 
			
		||||
type CompilationResult = Pick<IFile, "compiledContent" | "compiledWatContent">
 | 
			
		||||
 | 
			
		||||
/* 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.
 | 
			
		||||
 */
 | 
			
		||||
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
 | 
			
		||||
    else if (file.language === "ts") {
 | 
			
		||||
      result = await compileTs(file);
 | 
			
		||||
    }
 | 
			
		||||
    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!')
 | 
			
		||||
    else if (navigator?.onLine === false) {
 | 
			
		||||
      throw Error('You seem offline, check you internet connection and try again!')
 | 
			
		||||
    }
 | 
			
		||||
    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 +52,8 @@ export const compileCode = async (activeId: number) => {
 | 
			
		||||
      linkText: 'Go to deploy'
 | 
			
		||||
    })
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.log(err)
 | 
			
		||||
    console.error(err)
 | 
			
		||||
    let message: string;
 | 
			
		||||
 | 
			
		||||
    if (err instanceof Array && typeof err[0] === 'string') {
 | 
			
		||||
      err.forEach(message => {
 | 
			
		||||
@@ -109,63 +62,157 @@ export const compileCode = async (activeId: number) => {
 | 
			
		||||
          message
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
      message = "Compilation errors occurred, see logs for more info."
 | 
			
		||||
    } else if (err instanceof Error) {
 | 
			
		||||
      state.logs.push({
 | 
			
		||||
        type: 'error',
 | 
			
		||||
        message: err.message
 | 
			
		||||
      })
 | 
			
		||||
      message = err.message
 | 
			
		||||
    } 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' })
 | 
			
		||||
    toast.error(message, { position: 'bottom-center' })
 | 
			
		||||
    file.containsErrors = true
 | 
			
		||||
  }
 | 
			
		||||
  state.compiling = state.compiling.filter(id => id !== activeId);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* 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
 | 
			
		||||
        }
 | 
			
		||||
      ]
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
  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)
 | 
			
		||||
        }
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
    throw errors
 | 
			
		||||
  }
 | 
			
		||||
  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 mod = wabt.parseWat(file.name, file.content);
 | 
			
		||||
  mod.resolveNames();
 | 
			
		||||
  mod.validate();
 | 
			
		||||
  const { buffer } = mod.toBinary({
 | 
			
		||||
    log: false,
 | 
			
		||||
    write_debug_names: true,
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    compiledContent: ref(buffer),
 | 
			
		||||
    compiledWatContent: file.content,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
export const compileTs = async (file: IFile): Promise<CompilationResult> => {
 | 
			
		||||
  return new Promise(async (resolve, reject) => {
 | 
			
		||||
    let result: Partial<CompilationResult> = {}
 | 
			
		||||
    const { error, stdout, stderr } = await asc.main([
 | 
			
		||||
      // Command line options
 | 
			
		||||
      file.name,
 | 
			
		||||
      "--outFile", `${file.name}.wasm`,
 | 
			
		||||
      "--textFile", `${file.name}.wat`,
 | 
			
		||||
      "--runtime", "stub",
 | 
			
		||||
      "--initialMemory", "1",
 | 
			
		||||
      "--maximumMemory", "1",
 | 
			
		||||
      "--noExportMemory",
 | 
			
		||||
      "--optimize",
 | 
			
		||||
      "--topLevelToHook"
 | 
			
		||||
    ], {
 | 
			
		||||
      readFile: (name, baseDir) => {
 | 
			
		||||
        const file = state.files.find(file => file.name === name)
 | 
			
		||||
        if (file) {
 | 
			
		||||
          return file.content
 | 
			
		||||
        }
 | 
			
		||||
        return null
 | 
			
		||||
      },
 | 
			
		||||
      writeFile: async (name, data: ArrayBuffer | string, baseDir) => {
 | 
			
		||||
        const ext = getFileExtention(name);
 | 
			
		||||
        if (ext === 'wasm') {
 | 
			
		||||
          result.compiledContent = data as ArrayBuffer;
 | 
			
		||||
        }
 | 
			
		||||
        else if (ext === 'wat') {
 | 
			
		||||
          result.compiledWatContent = data as string;
 | 
			
		||||
        }
 | 
			
		||||
        if (result.compiledContent && result.compiledWatContent) {
 | 
			
		||||
          resolve({ ...result });
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      listFiles: (dirname, baseDir) => {
 | 
			
		||||
        return state.files.map(file => file.name)
 | 
			
		||||
      },
 | 
			
		||||
      // reportDiagnostic?: ...,
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    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'
 | 
			
		||||
    })
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.log(err)
 | 
			
		||||
    let message = "Error compiling WAT file!"
 | 
			
		||||
    if (err instanceof Error) {
 | 
			
		||||
      message = err.message
 | 
			
		||||
    let logMsg = stdout.toString()
 | 
			
		||||
    let errMsg = stderr.toString()
 | 
			
		||||
    if (logMsg) {
 | 
			
		||||
      state.logs.push({
 | 
			
		||||
        type: "log",
 | 
			
		||||
        message: logMsg
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
    state.logs.push({
 | 
			
		||||
      type: 'error',
 | 
			
		||||
      message
 | 
			
		||||
    })
 | 
			
		||||
    toast.error(`Error occurred while compiling!`, { position: 'bottom-center' })
 | 
			
		||||
    file.containsErrors = true
 | 
			
		||||
  }
 | 
			
		||||
  state.compiling = false
 | 
			
		||||
    if (errMsg) {
 | 
			
		||||
      state.logs.push({
 | 
			
		||||
        type: "error",
 | 
			
		||||
        message: errMsg
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (error) return reject(error)
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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',
 | 
			
		||||
 
 | 
			
		||||
@@ -247,7 +247,7 @@ export const deleteHook = async (account: IAccount & { name?: string }) => {
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.log(err)
 | 
			
		||||
    console.error(err)
 | 
			
		||||
    toast.error('Error occurred while deleting hook', { id: toastId })
 | 
			
		||||
    state.deployLogs.push({
 | 
			
		||||
      type: 'error',
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import { Octokit } from '@octokit/core'
 | 
			
		||||
import state, { IFile } from '../index'
 | 
			
		||||
import state, { IFile, ILang } from '../index'
 | 
			
		||||
import { templateFileIds } from '../constants'
 | 
			
		||||
 | 
			
		||||
const octokit = new Octokit()
 | 
			
		||||
@@ -48,7 +48,7 @@ export const fetchFiles = async (gistId: string) => {
 | 
			
		||||
 | 
			
		||||
    const files: IFile[] = Object.keys(res.data.files).map(filename => ({
 | 
			
		||||
      name: res.data.files?.[filename]?.filename || 'untitled.c',
 | 
			
		||||
      language: res.data.files?.[filename]?.language?.toLowerCase() || '',
 | 
			
		||||
      language: res.data.files?.[filename]?.language?.toLowerCase() as ILang | undefined,
 | 
			
		||||
      content: res.data.files?.[filename]?.content || ''
 | 
			
		||||
    }))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,7 @@ export const syncToGist = async (session?: Session | null, createNewGist?: boole
 | 
			
		||||
        return toast.success('Updated to gist successfully!', { id: toastId })
 | 
			
		||||
      })
 | 
			
		||||
      .catch(err => {
 | 
			
		||||
        console.log(err)
 | 
			
		||||
        console.error(err)
 | 
			
		||||
        state.gistLoading = false
 | 
			
		||||
        return toast.error(`Could not update Gist, try again later!`, {
 | 
			
		||||
          id: toastId
 | 
			
		||||
@@ -85,7 +85,7 @@ export const syncToGist = async (session?: Session | null, createNewGist?: boole
 | 
			
		||||
        return toast.success('Created new gist successfully!', { id: toastId })
 | 
			
		||||
      })
 | 
			
		||||
      .catch(err => {
 | 
			
		||||
        console.log(err)
 | 
			
		||||
        console.error(err)
 | 
			
		||||
        state.gistLoading = false
 | 
			
		||||
        return toast.error(`Could not create Gist, try again later!`, {
 | 
			
		||||
          id: toastId
 | 
			
		||||
 
 | 
			
		||||
@@ -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: [],
 | 
			
		||||
@@ -135,7 +136,7 @@ if (typeof window !== 'undefined') {
 | 
			
		||||
  try {
 | 
			
		||||
    localStorageAccounts = localStorage.getItem('hooksIdeAccounts')
 | 
			
		||||
  } catch (err) {
 | 
			
		||||
    console.log(`localStorage state broken`)
 | 
			
		||||
    console.error(`localStorage state broken`)
 | 
			
		||||
    localStorage.removeItem('hooksIdeAccounts')
 | 
			
		||||
  }
 | 
			
		||||
  if (localStorageAccounts) {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,12 @@ export const getFileExtention = (filename?: string): string | undefined => {
 | 
			
		||||
  return ext
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const getFileNamePart = (filename?: string): string | undefined => {
 | 
			
		||||
  if (!filename) return
 | 
			
		||||
  const name = (filename.includes('.') && filename.split('.').slice(0, -1).join(".")) || filename
 | 
			
		||||
  return name
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Type = "array" | "undefined" | "object" | "string" | "number" | "bigint" | "boolean" | "symbol" | "function"
 | 
			
		||||
type obj = Record<string | number | symbol, unknown>
 | 
			
		||||
type arr = unknown[]
 | 
			
		||||
@@ -32,4 +38,4 @@ export const typeIs = <T extends Type>(arg: any, t: T | T[]): arg is unknown & (
 | 
			
		||||
export const typeOf = (arg: any): Type => {
 | 
			
		||||
  const type = arg instanceof Array ? 'array' : arg === null ? 'undefined' : typeof arg
 | 
			
		||||
  return type;
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user