| @ -1 +1,15 @@ | |||||
| .stack-work/ | .stack-work/ | ||||
| dist-newstyle | |||||
| *.hi-boot | |||||
| *.o | |||||
| *.o-boot | |||||
| *.hi | |||||
| src/Presyntax/Lexer.hs | |||||
| src/Presyntax/Parser.hs | |||||
| *.js | |||||
| !web/interaction.js | |||||
| *.wasm | |||||
| cabal.project.local | |||||
| @ -0,0 +1,28 @@ | |||||
| FUNCTIONS := $(shell grep -R "foreign" src/ | cut -d' ' -f4) | |||||
| HS_FILES := $(shell find src -type f -name '*.hs' -or -name '*.hs-boot') | |||||
| CABAL_OPTL := $(foreach function,$(FUNCTIONS),--ghc-option=-optl--export-function=$(function)) -f asterius | |||||
| AHCD_OPTL := $(foreach function,$(FUNCTIONS),--export-function=$(function)) | |||||
| CABAL := ahc-cabal | |||||
| AHCD := ahc-dist | |||||
| web/dist/cubical.wasm: web/dist/cubical.js | |||||
| cp dist-newstyle/cubical.wasm $@ | |||||
| web/dist/cubical.js: dist-newstyle/cubical.js | |||||
| cp dist-newstyle/cubical.js $@ | |||||
| dist-newstyle/cubical.js: dist-newstyle/cubical src/wrapper.mjs | |||||
| mkdir -p dist-newstyle/ahcd-spam | |||||
| $(AHCD) $(AHCD_OPTL) --input-exe $< --browser --bundle --input-mjs src/wrapper.mjs | |||||
| dist-newstyle/cubical: src/Presyntax/Lexer.hs src/Presyntax/Parser.hs $(HS_FILES) | |||||
| $(CABAL) v2-install $(CABAL_OPTL) --installdir dist-newstyle exe:cubical --overwrite-policy=always | |||||
| src/Presyntax/Lexer.hs: src/Presyntax/Lexer.x | |||||
| alex $< | |||||
| src/Presyntax/Parser.hs: src/Presyntax/Parser.y | |||||
| happy $< | |||||
| @ -10,19 +10,25 @@ maintainer: [email protected] | |||||
| copyright: 2021 Abigail Magalhães | copyright: 2021 Abigail Magalhães | ||||
| category: Web | category: Web | ||||
| build-type: Simple | build-type: Simple | ||||
| cabal-version: >=2.0 | |||||
| cabal-version: 2.0 | |||||
| extra-source-files: README.md | extra-source-files: README.md | ||||
| flag asterius | |||||
| description: Is this build for the web? | |||||
| manual: True | |||||
| default: False | |||||
| executable cubical | executable cubical | ||||
| hs-source-dirs: src | hs-source-dirs: src | ||||
| main-is: Main.hs | main-is: Main.hs | ||||
| default-language: Haskell2010 | default-language: Haskell2010 | ||||
| build-depends: base ^>= 4.14 | |||||
| build-depends: base >= 4.13 | |||||
| , mtl ^>= 2.2 | , mtl ^>= 2.2 | ||||
| , syb ^>= 0.7 | , syb ^>= 0.7 | ||||
| , text ^>= 1.2 | , text ^>= 1.2 | ||||
| , array ^>= 0.5 | , array ^>= 0.5 | ||||
| , aeson >= 1.4 | |||||
| , containers ^>= 0.6 | , containers ^>= 0.6 | ||||
| , bytestring ^>= 0.10 | , bytestring ^>= 0.10 | ||||
| @ -46,8 +52,14 @@ executable cubical | |||||
| , Elab.WiredIn | , Elab.WiredIn | ||||
| , Elab.Eval.Formula | , Elab.Eval.Formula | ||||
| build-tool-depends: alex:alex >= 3.2.4 && < 4.0 | |||||
| , happy:happy >= 1.19.12 && < 2.0 | |||||
| -- Asterius wrapper | |||||
| , Web | |||||
| ghc-options: -Wall -Wextra -Wno-name-shadowing | |||||
| if !flag(asterius) | |||||
| build-tool-depends: alex:alex >= 3.2.4 && < 4.0 | |||||
| , happy:happy >= 1.19.12 && < 2.0 | |||||
| if flag(asterius) | |||||
| build-depends: asterius-prelude == 0.0.1 | |||||
| ghc-options: -Wall -Wextra -Wno-name-shadowing | |||||
| @ -0,0 +1,135 @@ | |||||
| {-# LANGUAGE CPP #-} | |||||
| module Web where | |||||
| import Control.Exception.Base | |||||
| import Control.Monad.Reader | |||||
| import qualified Data.ByteString.Lazy as Bsl | |||||
| import qualified Data.Text.Encoding as T | |||||
| import qualified Data.Map.Strict as Map | |||||
| import qualified Data.Text as T | |||||
| import Elab.Eval (zonkIO, force) | |||||
| import Elab.Monad | |||||
| import Elab | |||||
| import Foreign | |||||
| import Presyntax.Presyntax | |||||
| import Presyntax.Parser | |||||
| import Presyntax.Lexer | |||||
| import Syntax.Pretty | |||||
| import Syntax | |||||
| #ifdef ASTERIUS | |||||
| import Asterius.Types | |||||
| import Asterius.Aeson | |||||
| #endif | |||||
| parseFromString :: String -> IO (StablePtr [Statement]) | |||||
| parseFromString str = do | |||||
| case runAlex (Bsl.fromStrict (T.encodeUtf8 inp)) parseProg of | |||||
| Right e -> newStablePtr e | |||||
| Left e -> throwIO (ErrorCall e) | |||||
| where | |||||
| inp = T.pack str | |||||
| newEnvironment :: IO (StablePtr ElabEnv) | |||||
| newEnvironment = newStablePtr =<< emptyEnv | |||||
| typeCheckProgram :: StablePtr ElabEnv -> StablePtr [Statement] -> IO (StablePtr ElabEnv) | |||||
| typeCheckProgram envP prog = | |||||
| do | |||||
| prog <- deRefStablePtr prog | |||||
| env <- runElab (go prog) =<< deRefStablePtr envP | |||||
| newStablePtr env | |||||
| where | |||||
| go prog = checkProgram prog ask | |||||
| getTypeByName :: String -> StablePtr ElabEnv -> IO (StablePtr Value) | |||||
| getTypeByName str env = do | |||||
| env <- deRefStablePtr env | |||||
| case Map.lookup (T.pack str) (nameMap env) of | |||||
| Just nm -> newStablePtr (fst (getEnv env Map.! nm)) | |||||
| Nothing -> throwIO (NotInScope (Bound (T.pack str) 0)) | |||||
| getValueByName :: String -> StablePtr ElabEnv -> IO (StablePtr Value) | |||||
| getValueByName str env = do | |||||
| env <- deRefStablePtr env | |||||
| case Map.lookup (T.pack str) (nameMap env) of | |||||
| Just nm -> newStablePtr (snd (getEnv env Map.! nm)) | |||||
| Nothing -> throwIO (NotInScope (Bound (T.pack str) 0)) | |||||
| classifyValue :: StablePtr Value -> IO String | |||||
| classifyValue val = do | |||||
| t <- deRefStablePtr val | |||||
| pure $ case force t of | |||||
| VNe (HData pathcs _) _ | |||||
| | pathcs -> "data.higher" | |||||
| | otherwise -> "data" | |||||
| VNe (HCon _ _) _ -> "constr" | |||||
| _ -> "other" | |||||
| classifyValueByName :: String -> StablePtr ElabEnv -> IO String | |||||
| classifyValueByName str env = do | |||||
| env <- deRefStablePtr env | |||||
| case Map.lookup (T.pack str) (nameMap env) of | |||||
| Just nm -> | |||||
| pure $ case force (snd (getEnv env Map.! nm)) of | |||||
| VNe (HData pathcs _) _ | |||||
| | pathcs -> "constr.data.higher" | |||||
| | otherwise -> "constr.data" | |||||
| VNe HPCon{} _ -> "constr.higher" | |||||
| VNe HCon{} _ -> "constr" | |||||
| _ -> "other" | |||||
| Nothing -> throwIO $ NotInScope (Bound (T.pack str) 0) | |||||
| findDefinition :: String -> StablePtr ElabEnv -> IO (Maybe (Posn, Posn)) | |||||
| findDefinition str env = do | |||||
| env <- deRefStablePtr env | |||||
| case Map.lookup (T.pack str) (nameMap env) of | |||||
| Just nm -> pure $ Map.lookup nm (whereBound env) | |||||
| Nothing -> throwIO $ NotInScope (Bound (T.pack str) 0) | |||||
| zonkAndShowType :: StablePtr Value -> IO String | |||||
| zonkAndShowType val = do | |||||
| val <- deRefStablePtr val | |||||
| val <- zonkIO val | |||||
| let str = show . prettyTm . quote $ val | |||||
| pure str | |||||
| freeHaskellValue :: StablePtr a -> IO () | |||||
| freeHaskellValue = freeStablePtr | |||||
| #ifdef ASTERIUS | |||||
| parseFromStringJs :: JSString -> IO (StablePtr [Statement]) | |||||
| parseFromStringJs = parseFromString . fromJSString | |||||
| getTypeByNameJs :: JSString -> StablePtr ElabEnv -> IO (StablePtr Value) | |||||
| getTypeByNameJs str env = getTypeByName (fromJSString str) env | |||||
| getValueByNameJs :: JSString -> StablePtr ElabEnv -> IO (StablePtr Value) | |||||
| getValueByNameJs str env = getValueByName (fromJSString str) env | |||||
| zonkAndShowTypeJs :: StablePtr Value -> IO JSString | |||||
| zonkAndShowTypeJs = fmap toJSString . zonkAndShowType | |||||
| classifyValueByNameJs :: JSString -> StablePtr ElabEnv -> IO JSString | |||||
| classifyValueByNameJs str env = toJSString <$> classifyValueByName (fromJSString str) env | |||||
| findDefinitionJs :: JSString -> StablePtr ElabEnv -> IO JSVal | |||||
| findDefinitionJs str env = jsonToJSVal <$> findDefinition (fromJSString str) env | |||||
| foreign export javascript parseFromStringJs :: JSString -> IO (StablePtr [Statement]) | |||||
| foreign export javascript getTypeByNameJs :: JSString -> StablePtr ElabEnv -> IO (StablePtr Value) | |||||
| foreign export javascript getValueByNameJs :: JSString -> StablePtr ElabEnv -> IO (StablePtr Value) | |||||
| foreign export javascript classifyValueByNameJs :: JSString -> StablePtr ElabEnv -> IO JSString | |||||
| foreign export javascript newEnvironment :: IO (StablePtr ElabEnv) | |||||
| foreign export javascript typeCheckProgram :: StablePtr ElabEnv -> StablePtr [Statement] -> IO (StablePtr ElabEnv) | |||||
| foreign export javascript zonkAndShowTypeJs :: StablePtr Value -> IO JSString | |||||
| foreign export javascript freeHaskellValue :: StablePtr Value -> IO () | |||||
| foreign export javascript findDefinitionJs :: JSString -> StablePtr ElabEnv -> IO JSVal | |||||
| #endif | |||||
| @ -0,0 +1,8 @@ | |||||
| import * as rts from "./rts.mjs"; | |||||
| import module from "./cubical.wasm.mjs"; | |||||
| import req from "./cubical.req.mjs"; | |||||
| document.addEventListener('DOMContentLoaded', async () => { | |||||
| window.cubical = await module.then(m => rts.newAsteriusInstance(Object.assign(req, {module: m}))) | |||||
| document.dispatchEvent(new Event('cubicalLoaded')); | |||||
| }); | |||||
| @ -0,0 +1,7 @@ | |||||
| /node_modules | |||||
| /native_modules | |||||
| /dist | |||||
| *.tsbuildinfo | |||||
| !/webpack.config.js | |||||
| @ -0,0 +1,13 @@ | |||||
| #!/usr/bin/env bash | |||||
| if which ahc-cabal &>/dev/null; then | |||||
| pushd .. | |||||
| make | |||||
| popd | |||||
| fi | |||||
| cp ../intro.tt dist/ -rv | |||||
| cp html/* dist/ -rv | |||||
| cp styles/* dist/ -rv | |||||
| rsync dist/ ${SYNC_SERVER}:/var/www/demo.inductive.properties -avx | |||||
| @ -0,0 +1,17 @@ | |||||
| <!doctype html> | |||||
| <html lang="en"> | |||||
| <head> | |||||
| <title>cubical</title> | |||||
| <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |||||
| <link rel="stylesheet" href="/main.css" /> | |||||
| <script type="text/javascript" src="/cubical.js"></script> | |||||
| <script type="text/javascript" src="/bundle.js"></script> | |||||
| </head> | |||||
| <body> | |||||
| <pre class="editor-container" style="height: 100vh;" src="/intro.tt"></pre> | |||||
| </body> | |||||
| </html> | |||||
| @ -0,0 +1,27 @@ | |||||
| { | |||||
| "name": "cubical-frontend", | |||||
| "version": "1.0.0", | |||||
| "description": "Web frontend for cubical", | |||||
| "private": true, | |||||
| "scripts": { | |||||
| "test": "echo \"Error: no test specified\" && exit 1" | |||||
| }, | |||||
| "repository": { | |||||
| "type": "git", | |||||
| "url": "https://git.abby.how/abby/cubical" | |||||
| }, | |||||
| "author": "Abigail", | |||||
| "license": "MIT", | |||||
| "dev-dependencies": { | |||||
| "typescript": "^4.2.3" | |||||
| }, | |||||
| "devDependencies": { | |||||
| "css-loader": "^5.1.3", | |||||
| "file-loader": "^6.2.0", | |||||
| "monaco-editor-webpack-plugin": "^3.0.1", | |||||
| "style-loader": "^2.0.0", | |||||
| "ts-loader": "^8.0.18", | |||||
| "typescript": "^4.2.3", | |||||
| "webpack-cli": "^4.5.0" | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,71 @@ | |||||
| import * as monaco from 'monaco-editor'; | |||||
| import { CubicalT, Environment, Value } from '../typings/cubical'; | |||||
| import * as haskell from '../typings/cubical'; | |||||
| let editorNum: number = 0; | |||||
| let cubical: CubicalT; | |||||
| export const MODEL_ENVS: Record<string, Environment> = {} | |||||
| async function reloadModelEnv(model: monaco.editor.ITextModel, environ: Environment): Promise<void> { | |||||
| console.log(`reloading environment for model ${model.id}`) | |||||
| let code, typed; | |||||
| try { | |||||
| code = await cubical.exports.parseFromStringJs(model.getValue()); | |||||
| typed = await cubical.exports.typeCheckProgram(environ, code); | |||||
| MODEL_ENVS[model.id] = typed; | |||||
| } catch (e) { | |||||
| if (typeof (e) === 'string') | |||||
| console.log(e); | |||||
| } | |||||
| } | |||||
| export class CubicalEditor { | |||||
| private model: monaco.editor.ITextModel; | |||||
| private editor: monaco.editor.IStandaloneCodeEditor; | |||||
| private environment: Environment | undefined; | |||||
| private uri: monaco.Uri; | |||||
| private element: HTMLElement; | |||||
| private didChangeLocked: boolean; | |||||
| constructor(el: HTMLElement, code: string, uri: monaco.Uri) { | |||||
| this.element = el; | |||||
| this.uri = uri; | |||||
| this.model = monaco.editor.createModel(code, 'cubical', this.uri); | |||||
| this.editor = monaco.editor.create(el, { | |||||
| model: this.model, | |||||
| tabSize: 2, | |||||
| insertSpaces: true, | |||||
| }) | |||||
| this.didChangeLocked = false; | |||||
| } | |||||
| async load() { | |||||
| cubical = await haskell.waitForLoad; | |||||
| this.environment = await cubical.exports.newEnvironment(); | |||||
| this.model.onDidChangeContent(async (e) => { | |||||
| if (this.didChangeLocked) { | |||||
| console.log("going too fast"); | |||||
| return; | |||||
| } | |||||
| this.didChangeLocked = true; | |||||
| console.log('doing the work') | |||||
| // reloadModelEnv(this.model!, this.environment!); | |||||
| setTimeout(() => { | |||||
| this.didChangeLocked = false; | |||||
| }, 1000); | |||||
| }); | |||||
| this.editor.layout(); | |||||
| reloadModelEnv(this.model, this.environment); | |||||
| window.addEventListener('resize', () => this.editor.layout()); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,98 @@ | |||||
| import * as monaco from 'monaco-editor'; | |||||
| import { CubicalT, Environment, Value } from '../typings/cubical'; | |||||
| import * as haskell from '../typings/cubical'; | |||||
| import language from './language'; | |||||
| import toast from './toast'; | |||||
| import { CubicalEditor, MODEL_ENVS } from './editor'; | |||||
| let initCode: string[] = [ | |||||
| "{-# PRIMITIVE Type #-}", | |||||
| "", | |||||
| "data Nat : Type where", | |||||
| " zero : Nat", | |||||
| " succ : Nat -> Nat", | |||||
| "", | |||||
| "test : Nat", | |||||
| "test = succ zero" | |||||
| ] | |||||
| declare const cubical: CubicalT; | |||||
| const LANGUAGE: string = "cubical"; | |||||
| monaco.languages.register({ | |||||
| id: LANGUAGE | |||||
| }); | |||||
| monaco.languages.registerHoverProvider(LANGUAGE, { | |||||
| provideHover: async (model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken) => { | |||||
| const word = model.getWordAtPosition(position); | |||||
| if (!word) return; | |||||
| try { | |||||
| const env: Environment | null = MODEL_ENVS[model.id]; | |||||
| if (!env) return null; | |||||
| const ty: Value = await cubical.exports.getTypeByNameJs(word.word, env); | |||||
| return { | |||||
| contents: [{ | |||||
| value: await cubical.exports.zonkAndShowTypeJs(ty) | |||||
| }] | |||||
| } | |||||
| } catch (e) { | |||||
| return null; | |||||
| } | |||||
| } | |||||
| }); | |||||
| monaco.languages.registerDefinitionProvider(LANGUAGE, { | |||||
| provideDefinition: async (model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken) => { | |||||
| const word = model.getWordAtPosition(position); | |||||
| if (!word) return []; | |||||
| try { | |||||
| const env: Environment | null = MODEL_ENVS[model.id]; | |||||
| if (!env) return []; | |||||
| const range: haskell.Range | null = await cubical.exports.findDefinitionJs(word.word, env); | |||||
| if (range) { | |||||
| return [{ | |||||
| range: { | |||||
| startColumn: range[0].posnColm, | |||||
| endColumn: range[1].posnColm, | |||||
| startLineNumber: range[0].posnLine, | |||||
| endLineNumber: range[1].posnLine, | |||||
| }, | |||||
| uri: model.uri, | |||||
| }]; | |||||
| } else { | |||||
| return []; | |||||
| } | |||||
| } catch (e) { | |||||
| console.log(e); | |||||
| return []; | |||||
| } | |||||
| } | |||||
| }) | |||||
| monaco.languages.setMonarchTokensProvider(LANGUAGE, language); | |||||
| document.addEventListener('DOMContentLoaded', async () => { | |||||
| let editors: CubicalEditor[] = []; | |||||
| let n = 0; | |||||
| document.querySelectorAll("pre.editor-container").forEach(async (theEl) => { | |||||
| let el = theEl as HTMLPreElement; | |||||
| let attr = el.getAttribute("src"); | |||||
| let text : string; | |||||
| if (attr) { | |||||
| text = await fetch(attr).then(x => x.text()); | |||||
| } else { | |||||
| text = el.innerText; | |||||
| } | |||||
| el.innerText = ''; | |||||
| let editor = new CubicalEditor(el, text, monaco.Uri.parse(attr ?? `editor://${n++}`)); | |||||
| await editor.load(); | |||||
| }) | |||||
| }); | |||||
| @ -0,0 +1,30 @@ | |||||
| import * as monaco from 'monaco-editor'; | |||||
| const language: monaco.languages.IMonarchLanguage = { | |||||
| ignoreCase: false, | |||||
| brackets: [ | |||||
| { open: '(', close: ')', token: 'delimiter.parens' }, | |||||
| { open: '[', close: ']', token: 'delimiter.square' }, | |||||
| { open: '{', close: '}', token: 'delimiter.curly' }, | |||||
| ], | |||||
| tokenizer: { | |||||
| "normal": [ | |||||
| [/\{\-#/, 'comment', '@pragma'], | |||||
| [/\[/, 'delimiter.square', '@system'], | |||||
| [/\-\-.*$/, 'comment'], | |||||
| [/\b(data|where|case|as|in|postulate|let|where)\b/, 'keyword'], | |||||
| [/(=|:|\-\>|\\)/, 'keyword'] | |||||
| ], | |||||
| "pragma": [ | |||||
| [/PRIMITIVE/, 'keyword'], | |||||
| [/#\-\}/, 'comment', '@pop'] | |||||
| ], | |||||
| "system": [ | |||||
| [/\b(i0|i1)/, 'keyword'], | |||||
| [/\]/, 'delimiter.square', '@pop'], | |||||
| { include: "normal" } | |||||
| ] | |||||
| } | |||||
| }; | |||||
| export default language; | |||||
| @ -0,0 +1,15 @@ | |||||
| export default function toast(duration: number, message: HTMLElement | string): void { | |||||
| let element = document.createElement('div'); | |||||
| element.classList.add("toast"); | |||||
| if (typeof(message) == 'string') { | |||||
| element.innerText = message; | |||||
| } else { | |||||
| element.appendChild(message); | |||||
| } | |||||
| document.body.appendChild(element); | |||||
| setTimeout(() => { | |||||
| element.remove(); | |||||
| }, duration * 1000); | |||||
| } | |||||
| @ -0,0 +1,14 @@ | |||||
| html { | |||||
| padding: 0; | |||||
| margin: 0; | |||||
| height: 100vh; | |||||
| width: 100vw; | |||||
| } | |||||
| body { | |||||
| height: 100%; | |||||
| width: 70%; | |||||
| margin: auto; | |||||
| overflow-x: hidden; | |||||
| } | |||||
| @ -0,0 +1,33 @@ | |||||
| { | |||||
| "compilerOptions": { | |||||
| "target": "es6", | |||||
| "lib": [ | |||||
| "dom", | |||||
| "dom.iterable", | |||||
| "esnext" | |||||
| ], | |||||
| "outDir": "./dist/", | |||||
| "allowJs": true, | |||||
| "skipLibCheck": true, | |||||
| "esModuleInterop": true, | |||||
| "allowSyntheticDefaultImports": true, | |||||
| "strict": true, | |||||
| "forceConsistentCasingInFileNames": true, | |||||
| "noFallthroughCasesInSwitch": true, | |||||
| "downlevelIteration": true, | |||||
| "module": "esnext", | |||||
| "moduleResolution": "node", | |||||
| "resolveJsonModule": true, | |||||
| "isolatedModules": true, | |||||
| "noImplicitAny": true, | |||||
| "jsx": "react-jsx", | |||||
| "typeRoots": [ | |||||
| "./node_modules/@types", | |||||
| "./typings" | |||||
| ] | |||||
| }, | |||||
| "include": [ | |||||
| "src", | |||||
| "typings" | |||||
| ] | |||||
| } | |||||
| @ -0,0 +1,31 @@ | |||||
| export class Program { }; | |||||
| export class Environment { }; | |||||
| export class Value { }; | |||||
| export type Posn = { | |||||
| posnLine: number, | |||||
| posnColm: number | |||||
| }; | |||||
| export type Range = [Posn, Posn]; | |||||
| export type CubicalT = { | |||||
| exports: { | |||||
| parseFromStringJs(s: string): Promise<Program>; | |||||
| newEnvironment(): Promise<Environment>; | |||||
| typeCheckProgram(p: Environment, e: Program): Promise<Environment>; | |||||
| zonkAndShowTypeJs(val: Value): Promise<string>; | |||||
| getTypeByNameJs(name: string, e: Environment): Promise<Value>; | |||||
| getValueByNameJs(name: string, e: Environment): Promise<Value>; | |||||
| classifyValueByNameJs(name: string, e: Environment): Promise<string>; | |||||
| findDefinitionJs(name: string, e: Environment): Promise<Range | null>; | |||||
| } | |||||
| }; | |||||
| export const waitForLoad : Promise<CubicalT> = new Promise(resolve => | |||||
| document.addEventListener('cubicalLoaded', () => { | |||||
| resolve((window as unknown as { cubical: CubicalT}).cubical) | |||||
| })); | |||||
| @ -0,0 +1,36 @@ | |||||
| const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin'); | |||||
| const path = require('path'); | |||||
| module.exports = { | |||||
| mode: 'development', | |||||
| entry: './src/index.ts', | |||||
| module: { | |||||
| rules: [ | |||||
| { | |||||
| test: /\.tsx?$/, | |||||
| use: 'ts-loader', | |||||
| exclude: /node_modules/, | |||||
| }, | |||||
| { | |||||
| test: /\.css$/, | |||||
| use: ['style-loader', 'css-loader'] | |||||
| }, | |||||
| { | |||||
| test: /\.ttf$/, | |||||
| use: ['file-loader'] | |||||
| } | |||||
| ], | |||||
| }, | |||||
| resolve: { | |||||
| extensions: ['.tsx', '.ts', '.js'], | |||||
| }, | |||||
| output: { | |||||
| filename: 'bundle.js', | |||||
| path: path.resolve(__dirname, 'dist'), | |||||
| }, | |||||
| plugins: [ | |||||
| new MonacoWebpackPlugin({ | |||||
| languages: [], | |||||
| }) | |||||
| ] | |||||
| }; | |||||