[
  {
    "path": ".eslintignore",
    "content": "node_modules\n.next/\nout_publish/\nout_function/"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"env\": {\n    \"browser\": true,\n    \"es6\": true,\n    \"node\": true\n  },\n  \"extends\": [\n    \"react-app\",\n    \"plugin:react/recommended\",\n    \"plugin:react-hooks/recommended\",\n    \"prettier-standard\"\n  ],\n  \"parserOptions\": {\n    \"project\": \"./tsconfig.json\"\n  },\n  \"plugins\": [\n    \"react\",\n    \"@typescript-eslint\",\n    \"react-hooks\",\n    \"prettier\",\n    \"simple-import-sort\"\n  ],\n  \"rules\": {\n    \"react/jsx-no-undef\": [\"error\", { \"allowGlobals\": true }],\n    \"no-use-before-define\": \"off\",\n    \"react/no-children-prop\": \"off\",\n    \"prettier/prettier\": [\n      \"error\",\n      {\n        \"endOfLine\": \"auto\"\n      }\n    ],\n    \"jsx-a11y/anchor-is-valid\": \"off\",\n    \"simple-import-sort/imports\": \"error\",\n    \"simple-import-sort/exports\": \"error\"\n  }\n}\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# vercel\n.vercel\n/out_publish/\n/out_functions/\n\n# Local Netlify folder\n.netlify\n\n.vscode/"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"goscript\"]\n\tpath = goscript\n\turl = https://github.com/zackradisic/goscript\n\tbranch = \"wasm-experiment\"\n"
  },
  {
    "path": ".prettierignore",
    "content": "node_modules\n.next/\nout/\nout_publish/\nout_functions/\n.netlify/\n.vercel/"
  },
  {
    "path": ".prettierrc",
    "content": "\"prettier-config-standard\"\n"
  },
  {
    "path": "README.md",
    "content": "# Go Playground WASM\n\nThis is an experiment in trying to make a version of play.golang.org that runs completely in the browser by \ncompiling [goscript](https://github.com/oxfeeefeee/goscript) (by [oxfeeefeee](https://github.com/oxfeeefeee)) to WASM.\n\nMost of Go's language features are supported, notably channels/goroutines/select.\n\n## Building and running\nThe wasm binary is pre-compiled and available in the `public/` directory so you should be \ngood to go to if you just want to run the Next.js server. You will need to install node and yarn of course.\n```\n# Run in development mode\nyarn dev\n\n# Build and serve optimized production build\nyarn build\nyarn start\n```\n\nIf you want to play around with modifying the wasm build, `cd` into `goscript/wasm` and\nyou can mess around with the code. When you want to build the wasm binary run:\n```\n# Build wasm\ncargo build --release --target=wasm32-wasi\n\n# Copy into Next.js's public folder\ncp target/wasm32-wasi/release/wasm.wasm ../public\n```\n\n\n### Disclaimer\ngoscript only makes guarantees that the syntax will be identical to Go's, there are implementation details that will cause discrepancies from running actual Go code with the actual Go compiler.\n\n"
  },
  {
    "path": "lib/RunGo.ts",
    "content": "import { WasmAlloc, WasmDealloc, WasmRunFn } from '@type/types'\nimport { WasmFs } from '@wasmer/wasmfs'\n\nconst SCRIPT_PATH = '/script.gos'\n\nexport class RunGo {\n  memory: WebAssembly.Memory\n  wasmFs: WasmFs\n\n  runFn: WasmRunFn\n  alloc: WasmAlloc\n  dealloc: WasmDealloc\n\n  scriptPathPtr: number\n\n  constructor(\n    memory: WebAssembly.Memory,\n    wasmFs: WasmFs,\n    runFn: WasmRunFn,\n    alloc: WasmAlloc,\n    dealloc: WasmDealloc\n  ) {\n    this.memory = memory\n    this.wasmFs = wasmFs\n\n    this.runFn = runFn\n    this.alloc = alloc\n    this.dealloc = dealloc\n\n    // Write script path now since it's constant\n    const strBuf = new TextEncoder().encode(SCRIPT_PATH)\n    const ptr = this.alloc(strBuf.length)\n    const memBuf = new Uint8Array(this.memory.buffer, ptr, strBuf.length)\n    memBuf.set(strBuf)\n    this.scriptPathPtr = ptr\n  }\n\n  run(code: string) {\n    this.wasmFs.fs.writeFileSync(SCRIPT_PATH, new TextEncoder().encode(code))\n\n    console.log('Running')\n    return this.runFn(this.scriptPathPtr)\n  }\n}\n"
  },
  {
    "path": "lib/fibonacci.ts",
    "content": "export const fibonnaci = `package main\n\nimport \"fmt\"\n\nfunc fibonacci(c, quit chan int) {\n  x, y := 0, 1\n  for {\n    select {\n    case c <- x:\n      x, y = y, x+y\n    case <-quit:\n      fmt.Println(\"quit\")\n      return\n    }\n  }\n}\n\nfunc main() {\n  c := make(chan int)\n  quit := make(chan int)\n  go func() {\n    for i := 0; i < 12; i++ {\n      fmt.Println(<-c)\n    }\n    quit <- 0\n  }()\n  \n  fibonacci(c, quit)\n}\n`\n"
  },
  {
    "path": "lib/go_syntax.ts",
    "content": "import { languages } from 'prismjs'\nexport const goSyntax = languages.extend('clike', {\n  string: {\n    pattern: /([\"'`])(?:\\\\[\\s\\S]|(?!\\1)[^\\\\])*\\1/,\n    greedy: true\n  },\n  keyword:\n    /\\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\\b/,\n  boolean: /\\b(?:_|iota|nil|true|false)\\b/,\n  number: /(?:\\b0x[a-f\\d]+|(?:\\b\\d+(?:\\.\\d*)?|\\B\\.\\d+)(?:e[-+]?\\d+)?)i?/i,\n  operator:\n    /[*/%^!=]=?|\\+[=+]?|-[=-]?|\\|[=|]?|&(?:=|&|\\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\\.\\.\\./,\n  builtin:\n    /\\b(?:bool|byte|complex(?:64|128)|error|float(?:32|64)|rune|string|u?int(?:8|16|32|64)?|uintptr|append|cap|close|complex|copy|delete|imag|len|make|new|panic|print(?:ln)?|real|recover)\\b/\n})\n"
  },
  {
    "path": "lib/load_wasm.ts",
    "content": "/* eslint-disable camelcase */\nimport { WasmAlloc, WasmDealloc, WasmRunFn } from '@type/types'\nimport { WASI } from '@wasmer/wasi'\nimport { lowerI64Imports } from '@wasmer/wasm-transformer'\nimport { WasmFs } from '@wasmer/wasmfs'\nimport * as fflate from 'fflate'\nimport * as path from 'isomorphic-path'\n\nimport { RunGo } from './RunGo'\n\nconst instantiateWasm = async (wasi: WASI) => {\n  const binary = await (await fetch('/wasm.wasm')).arrayBuffer()\n  const loweredBinary = await lowerI64Imports(new Uint8Array(binary))\n  const module = await WebAssembly.compile(loweredBinary)\n  const imports = wasi.getImports(module)\n  const instance = await WebAssembly.instantiate(module, {\n    ...imports\n  })\n  wasi.start(instance)\n\n  return instance.exports\n}\n\nconst setupFs = async (\n  fs: WasmFs,\n  stdout: (input: string) => void,\n  stderr: (input: string) => void\n) => {\n  // Fetch and write stdlib to FS\n  const zipped = await (await fetch('/stdlib.zip')).arrayBuffer()\n  const unzipped = fflate.unzipSync(new Uint8Array(zipped))\n  for (const [key, val] of Object.entries(unzipped)) {\n    if (val.length !== 0) {\n      console.log(key)\n      const dir = path.dirname(key)\n      if (!fs.fs.existsSync(dir)) {\n        fs.fs.mkdirpSync(dir)\n      }\n      fs.fs.writeFileSync(key, val)\n    }\n  }\n\n  // Intercept stdout and stderr\n  const oldWrite = fs.fs.writeSync\n  const textDecoder = new TextDecoder()\n  // @ts-ignore\n  fs.fs.writeSync = (fd, buffer, offset, length, position) => {\n    if (fd === 1) {\n      stdout(textDecoder.decode(buffer).replace('\\n', '<br />'))\n      return buffer.length\n    }\n    if (fd === 2) {\n      stderr(textDecoder.decode(buffer).replace('\\n', '<br />'))\n      return buffer.length\n    }\n    return oldWrite(fd, buffer, offset, length, position)\n  }\n}\n\nexport const initWasm = async (\n  stdin: (input: string) => void,\n  stdout: (inupt: string) => void\n) => {\n  const wasmFs = new WasmFs()\n  await setupFs(wasmFs, stdin, stdout)\n\n  const wasi = new WASI({\n    args: [],\n    env: {},\n    bindings: {\n      ...WASI.defaultBindings,\n      fs: wasmFs.fs,\n      // https://github.com/wasmerio/wasmer-js/issues/253\n      path: WASI.defaultBindings.path.default\n    },\n    preopens: {\n      '/': '/',\n      '.': '.'\n    }\n  })\n\n  const exports = await instantiateWasm(wasi)\n  const memory = exports.memory as WebAssembly.Memory\n  const { run_go, alloc, dealloc } = exports as {\n    run_go: WasmRunFn\n    alloc: WasmAlloc\n    dealloc: WasmDealloc\n  }\n  return new RunGo(memory, wasmFs, run_go, alloc, dealloc)\n}\n"
  },
  {
    "path": "netlify.toml",
    "content": "[build]\n  command   = \"yarn build\""
  },
  {
    "path": "next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/types/global\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/basic-features/typescript for more information.\n"
  },
  {
    "path": "next.config.js",
    "content": "\nmodule.exports = {\n  // Target must be serverless\n  target: 'serverless',\n  async headers() {\n    // return []\n    return [\n      {\n        source: '/',\n        headers: [\n          {\n            key: 'Cross-Origin-Opener-Policy',\n            value: 'unsafe-none'\n          },\n          {\n            key: 'Cross-Origin-Embedder-Policy',\n            value: 'unsafe-none'\n          },\n          {\n          key: 'Cross-Origin-Resource-Policy',\n          value: 'cross-origin'\n          }\n        ]\n      }\n    ]\n  },\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"go-playground-wasm\",\n  \"author\": \"zackradisic\",\n  \"version\": \"1.0.0\",\n  \"scripts\": {\n    \"dev\": \"next\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"type-check\": \"tsc\",\n    \"lint\": \"eslint . --ext .ts,.tsx\",\n    \"lint:fix\": \"yarn lint --fix\",\n    \"prettier\": \"prettier --check **/*.ts*\",\n    \"prettier:fix\": \"prettier --write **/*.ts*\",\n    \"verify\": \"yarn prettier && yarn lint\",\n    \"verify:fix\": \"yarn prettier:fix && yarn lint:fix\",\n    \"add-article\": \"node scripts/addArticle.js\"\n  },\n  \"dependencies\": {\n    \"@headlessui/react\": \"^1.4.1\",\n    \"@heroicons/react\": \"^1.0.1\",\n    \"@tailwindcss/aspect-ratio\": \"^0.2.1\",\n    \"@wasmer/wasi\": \"^0.12.0\",\n    \"@wasmer/wasm-terminal\": \"^0.12.0\",\n    \"@wasmer/wasm-transformer\": \"^0.12.0\",\n    \"@wasmer/wasmfs\": \"^0.12.0\",\n    \"autoprefixer\": \"^10.2.4\",\n    \"classnames\": \"^2.3.1\",\n    \"fflate\": \"^0.7.1\",\n    \"isomorphic-path\": \"^2.0.1\",\n    \"next\": \"latest\",\n    \"postcss\": \"^8.2.4\",\n    \"prismjs\": \"^1.25.0\",\n    \"react\": \"^17.0.1\",\n    \"react-dom\": \"^16.12.0\",\n    \"react-markdown\": \"^7.0.0\",\n    \"react-simple-code-editor\": \"^0.11.0\",\n    \"tailwindcss\": \"^2.2.9\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^12.12.21\",\n    \"@types/prismjs\": \"^1.16.6\",\n    \"@types/react\": \"^16.9.16\",\n    \"@types/react-dom\": \"^16.9.4\",\n    \"@typescript-eslint/eslint-plugin\": \"^4.14.2\",\n    \"@typescript-eslint/parser\": \"^4.14.2\",\n    \"babel-eslint\": \"^10.1.0\",\n    \"eslint\": \"^7.19.0\",\n    \"eslint-config-prettier\": \"^7.2.0\",\n    \"eslint-config-prettier-standard\": \"^3.0.1\",\n    \"eslint-config-react-app\": \"^6.0.0\",\n    \"eslint-config-standard\": \"^16.0.2\",\n    \"eslint-plugin-flowtype\": \"^5.2.0\",\n    \"eslint-plugin-import\": \"^2.22.1\",\n    \"eslint-plugin-jsx-a11y\": \"^6.4.1\",\n    \"eslint-plugin-node\": \"^11.1.0\",\n    \"eslint-plugin-prettier\": \"^3.3.1\",\n    \"eslint-plugin-promise\": \"^4.2.1\",\n    \"eslint-plugin-react\": \"^7.22.0\",\n    \"eslint-plugin-react-hooks\": \"^4.2.0\",\n    \"eslint-plugin-simple-import-sort\": \"^7.0.0\",\n    \"eslint-plugin-standard\": \"^5.0.0\",\n    \"husky\": \"^4.3.8\",\n    \"prettier\": \"^2.2.1\",\n    \"prettier-config-standard\": \"^1.0.1\",\n    \"slugify\": \"^1.6.0\",\n    \"typescript\": \"~4.3.5\"\n  },\n  \"license\": \"MIT\",\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"yarn prettier\",\n      \"pre-push\": \"yarn verify\"\n    }\n  }\n}\n"
  },
  {
    "path": "pages/_app.ts",
    "content": "import '@styles/global.css'\n\nimport App from 'next/app'\n\nexport default App\n"
  },
  {
    "path": "pages/index.tsx",
    "content": "// eslint-disable-next-line simple-import-sort/imports\nimport { fibonnaci } from 'lib/fibonacci'\nimport { goSyntax } from 'lib/go_syntax'\nimport { initWasm } from 'lib/load_wasm'\nimport { RunGo } from 'lib/RunGo'\nimport { highlight } from 'prismjs'\nimport 'prismjs/components/prism-clike'\nimport 'prismjs/themes/prism-dark.css' // Example style, you can use another\n\nimport React, { useEffect, useRef, useState } from 'react'\nimport Editor from 'react-simple-code-editor'\n\nconst Index = () => {\n  const [code, setCode] = useState<string>(fibonnaci)\n  const [terminalText, setTerminalText] = useState<string>(\n    \"Click 'Run' to get started! \"\n  )\n  const [loaded, setLoaded] = useState<boolean>(false)\n  const runGoRef = useRef<RunGo | undefined>(undefined)\n\n  useEffect(() => {\n    const run = async () => {\n      const runGo = await initWasm(\n        input => setTerminalText(text => text + input),\n        input => setTerminalText(text => text + input)\n      )\n      runGoRef.current = runGo\n      setLoaded(true)\n    }\n    run()\n  }, [])\n\n  return (\n    <main className=\"bg-blueGray-900 h-full flex flex-col\">\n      <div className=\"bg-blueGray-700 bg-opacity-50 p-4 rounded flex flex-row justify-between items-center\">\n        <h1 className=\"text-2xl text-coolGray-50\">Go playground WASM</h1>\n        <button className=\"bg-blue-600 text-gray-50 px-4 rounded hover:bg-blue-700 transition-all\">\n          <a href=\"https://github.com/zackradisic/go-playground-wasm/\">\n            View source\n          </a>\n        </button>\n      </div>\n      <div className=\"bg-blueGray-800 bg-opacity-50 text-coolGray-300\">\n        <Editor\n          value={code}\n          onValueChange={code => setCode(code)}\n          highlight={code => highlight(code, goSyntax, 'go')}\n          padding={10}\n          style={{\n            fontFamily: '\"Fira code\", \"Fira Mono\", monospace',\n            fontSize: 12\n          }}\n        />\n      </div>\n      <div className=\"bg-blueGray-900 bg-opacity-50 p-4 text-blueGray-50 overflow-auto h-full\">\n        <div\n          className=\"overflow-auto\"\n          dangerouslySetInnerHTML={{ __html: terminalText }}></div>\n        <div className=\"absolute bottom-0 right-0 pb-4 pr-4 flex flex-row\">\n          <button\n            onClick={() => {\n              if (!loaded) return\n              runGoRef.current!.run(code)\n            }}\n            className=\"bg-blue-600 px-4 py-1 rounded hover:bg-blue-700\">\n            {!loaded ? 'Loading...' : 'Run'}\n          </button>\n          <button\n            onClick={() => {\n              setTerminalText('')\n            }}\n            className=\"ml-4 bg-gray-600 px-4 py-1 rounded hover:bg-blue-700\">\n            Clear\n          </button>\n        </div>\n      </div>\n    </main>\n  )\n}\n\nexport default Index\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {}\n  }\n}\n"
  },
  {
    "path": "styles/global.css",
    "content": "/* ./styles/globals.css */\n@tailwind base;\n@tailwind components;\n@tailwind utilities;\nhtml,\nbody,\nbody > div:first-child,\ndiv#__next,\ndiv#__next > div {\n\theight: 100%;\n}\n/* \n@font-face {\n\tfont-family: 'Fira Code';\n\tsrc: url('woff2/FiraCode-Light.woff2') format('woff2'),\n\t\turl(\"woff/FiraCode-Light.woff\") format(\"woff\");\n\tfont-weight: 300;\n\tfont-style: normal;\n} */\n\n@font-face {\n\tfont-family: 'Fira Code';\n\tsrc: url('/FiraCode-Regular.woff2') format('woff2'),\n\t\turl(\"/FiraCode-Regular.woff\") format(\"woff\");\n\tfont-weight: 400;\n\tfont-style: normal;\n}\n/* \n@font-face {\n\tfont-family: 'Fira Code';\n\tsrc: url('woff2/FiraCode-Medium.woff2') format('woff2'),\n\t\turl(\"woff/FiraCode-Medium.woff\") format(\"woff\");\n\tfont-weight: 500;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'Fira Code';\n\tsrc: url('woff2/FiraCode-SemiBold.woff2') format('woff2'),\n\t\turl(\"woff/FiraCode-SemiBold.woff\") format(\"woff\");\n\tfont-weight: 600;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'Fira Code';\n\tsrc: url('woff2/FiraCode-Bold.woff2') format('woff2'),\n\t\turl(\"woff/FiraCode-Bold.woff\") format(\"woff\");\n\tfont-weight: 700;\n\tfont-style: normal;\n}\n\n@font-face {\n\tfont-family: 'Fira Code VF';\n\tsrc: url('woff2/FiraCode-VF.woff2') format('woff2-variations'),\n\t\t\turl('woff/FiraCode-VF.woff') format('woff-variations');\n\tfont-weight: 300 700;\n\tfont-style: normal;\n} */\n"
  },
  {
    "path": "tailwind.config.js",
    "content": "const colors = require('tailwindcss/colors')\n\nmodule.exports = {\n  purge: ['**/*.tsx'],\n  mode: 'jit',\n  darkMode: false, // or 'media' or 'class'\n  theme: {\n    extend: {\n      colors: {\n        orange: colors.orange,\n        coolGray: colors.coolGray,\n        blueGray: colors.blueGray\n      }\n    },\n    fontFamily: {\n      sans: ['Fira code', 'Fira Mono', 'monospace']\n    }\n  },\n  variants: {\n    extend: {}\n  },\n  plugins: [require('@tailwindcss/aspect-ratio')]\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"alwaysStrict\": true,\n    \"esModuleInterop\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"lib\": [\"dom\", \"es2017\"],\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"noEmit\": true,\n    \"noFallthroughCasesInSwitch\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true,\n    \"resolveJsonModule\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"target\": \"esnext\",\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@styles*\": [\"styles*\"],\n      \"@components*\": [\"components*\"],\n      \"@type*\": [\"type*\"],\n      \"@utils*\": [\"utils*\"]\n    }\n  },\n  \"exclude\": [\"node_modules\", \"goscript/\"],\n  \"include\": [\"**/*.ts\", \"**/*.tsx\"]\n}\n"
  },
  {
    "path": "type/types.ts",
    "content": "export type WasmRunFn = (pathPtr: number) => number\nexport type WasmAlloc = (size: number) => number\nexport type WasmDealloc = (ptr: number) => number\n"
  }
]