diff --git a/scripts/build.js b/scripts/build.js index 90e5b6023f..d7bfa8b37c 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -157,13 +157,6 @@ const esmBuild = () => 'headless.ts', 'templates/starter/index.ts', 'editor/monaco/monaco.ts', - 'editor/monaco/languages/monaco-lang-astro.ts', - 'editor/monaco/languages/monaco-lang-clio.ts', - 'editor/monaco/languages/monaco-lang-imba.ts', - 'editor/monaco/languages/monaco-lang-minizinc.ts', - 'editor/monaco/languages/monaco-lang-prolog.ts', - // 'editor/monaco/languages/monaco-lang-sql.ts', - 'editor/monaco/languages/monaco-lang-wat.ts', 'editor/codemirror/codemirror.ts', 'editor/codejar/codejar.ts', 'editor/blockly/blockly.ts', diff --git a/src/livecodes/compiler/compile.worker.ts b/src/livecodes/compiler/compile.worker.ts index 34180d33f4..b611303604 100644 --- a/src/livecodes/compiler/compile.worker.ts +++ b/src/livecodes/compiler/compile.worker.ts @@ -1,6 +1,6 @@ import type TS from 'typescript'; import { getCompilerOptions } from '../editor/ts-compiler-options'; -import { languages, processors } from '../languages'; +import { getLanguageSpecs, languages, processors } from '../languages'; import type { CompileOptions, CompileResult, @@ -288,7 +288,10 @@ const initCodemirrorTS = doOnce(async () => { ); const system = createSystem(tsvfsMap); const createTypeScriptEnvironment = (lang: Language) => { - const compilerOpts = getCompilerOptions(lang); + const compilerOpts = { + ...getCompilerOptions(), + ...((getLanguageSpecs(lang)?.editorSupport?.compilerOptions || {}) as TS.CompilerOptions), + }; return createVirtualTypeScriptEnvironment(system, [], worker.ts, compilerOpts); }; const language = codemirrorWorker.language || 'tsx'; diff --git a/src/livecodes/core.ts b/src/livecodes/core.ts index 89cf9a65c6..8c7fbc3f24 100644 --- a/src/livecodes/core.ts +++ b/src/livecodes/core.ts @@ -41,7 +41,6 @@ import { upgradeAndValidate, } from './config'; import { createCustomEditors, createEditor, getFontFamily } from './editor'; -import { hasJsx } from './editor/ts-compiler-options'; import { createEventsManager, createPub } from './events'; import { customEvents } from './events/custom-events'; import { exportJSON } from './export/export-json'; @@ -80,6 +79,7 @@ import { getLanguageExtension, getLanguageSpecs, getLanguageTitle, + hasJsx, languageIsEnabled, languages, mapLanguage, @@ -389,7 +389,7 @@ const loadModuleTypes = async ( ...config.types, ...config.customSettings.types, }; - const reactImport = hasJsx.includes(scriptLanguage) ? `import React from 'react';\n` : ''; + const reactImport = hasJsx(scriptLanguage) ? `import React from 'react';\n` : ''; const libs = await typeLoader.load( reactImport + getConfig().script.content + '\n' + getConfig().markup.content, configTypes, @@ -791,7 +791,7 @@ const configureEditorTools = (language: Language) => { UI.getEditorToolbar().classList.remove('hidden'); const langSpecs = getLanguageSpecs(language); - if (langSpecs?.formatter || langSpecs?.parser) { + if (langSpecs?.formatter) { UI.getFormatButton().classList.remove('disabled'); } else { UI.getFormatButton().classList.add('disabled'); diff --git a/src/livecodes/editor/codejar/codejar.ts b/src/livecodes/editor/codejar/codejar.ts index 400684159a..182496bfec 100644 --- a/src/livecodes/editor/codejar/codejar.ts +++ b/src/livecodes/editor/codejar/codejar.ts @@ -38,8 +38,8 @@ export const createEditor = async (options: EditorOptions): Promise let { value, language } = options; let currentPosition: EditorPosition = { lineNumber: 1 }; - const mapLanguage = options.mapLanguage || ((lang: Language) => lang); - let mappedLanguage = language === 'wat' ? 'wasm' : mapLanguage(language); + const mapLanguage = (lang: Language) => options.mapLanguage?.(lang, 'codejar'); + let mappedLanguage = mapLanguage(language); let editorOptions: ReturnType; const preElement: HTMLElement = document.createElement('pre'); diff --git a/src/livecodes/editor/codemirror/codemirror.ts b/src/livecodes/editor/codemirror/codemirror.ts index 9d48d8a80e..f2d7d0b940 100644 --- a/src/livecodes/editor/codemirror/codemirror.ts +++ b/src/livecodes/editor/codemirror/codemirror.ts @@ -29,6 +29,7 @@ import { colorPicker } from '@replit/codemirror-css-color-picker'; // these are imported normally import { getEditorModeNode } from '../../UI/selectors'; +import { getLanguageSpecs } from '../../languages'; import type { CodeEditor, CodemirrorTheme, @@ -68,14 +69,25 @@ export const createEditor = async (options: EditorOptions): Promise let editorSettings: EditorConfig = { ...options }; if (!container) throw new Error('editor container not found'); - const getLanguageSupport = async (language: Language): Promise => - editorLanguages[language]?.() || (editorLanguages.html?.() as Promise); + const getLanguageSupport = async (lang: Language): Promise => { + const langSupport = getLanguageSpecs(lang)?.editorSupport?.codemirror?.languageSupport; + if (!langSupport) { + return editorLanguages[lang]?.() || editorLanguages.html?.() || []; + } + const loadLanguage: () => Promise = + typeof langSupport === 'string' + ? (await import(langSupport)).default + : typeof langSupport === 'function' + ? langSupport + : async () => []; + return loadLanguage(); + }; const mapLanguage = (lang: Language) => { if (lang.startsWith('vue')) return 'vue'; if (lang.startsWith('svelte')) return 'svelte'; if (lang === 'liquid') return 'liquid'; - return options.mapLanguage?.(lang) || lang; + return options.mapLanguage?.(lang, 'codemirror') || lang; }; const themes: Partial> = { diff --git a/src/livecodes/editor/codemirror/editor-languages.ts b/src/livecodes/editor/codemirror/editor-languages.ts index 9a3c6731f1..b921ceb36b 100644 --- a/src/livecodes/editor/codemirror/editor-languages.ts +++ b/src/livecodes/editor/codemirror/editor-languages.ts @@ -1,97 +1,11 @@ /* eslint-disable import/no-unresolved */ - -// @ts-ignore -import { LanguageSupport, StreamLanguage, type StreamParser } from '@codemirror/language'; -// @ts-ignore -import { html } from '@codemirror/lang-html'; -// @ts-ignore -import { css } from '@codemirror/lang-css'; // @ts-ignore -import { javascript } from '@codemirror/lang-javascript'; +import type { LanguageSupport } from '@codemirror/language'; // @ts-ignore import { json } from '@codemirror/lang-json'; import type { Language } from '../../models'; -import { codeMirrorBaseUrl } from '../../vendors'; - -const legacy = (parser: StreamParser) => - new LanguageSupport(StreamLanguage.define(parser)); - -const getPath = (mod: string) => codeMirrorBaseUrl + mod; - -const moduleUrls = { - vue: getPath('codemirror-lang-vue.js'), - svelte: getPath('codemirror-lang-svelte.js'), - liquid: getPath('codemirror-lang-liquid.js'), - json: getPath('codemirror-lang-json.js'), - markdown: getPath('codemirror-lang-markdown.js'), - python: getPath('codemirror-lang-python.js'), - php: getPath('codemirror-lang-php.js'), - java: getPath('codemirror-lang-java.js'), - clike: getPath('codemirror-lang-clike.js'), - mllike: getPath('codemirror-lang-mllike.js'), - cpp: getPath('codemirror-lang-cpp.js'), - sql: getPath('codemirror-lang-sql.js'), - wast: getPath('codemirror-lang-wast.js'), - scss: getPath('codemirror-lang-scss.js'), - minizinc: getPath('codemirror-lang-minizinc.js'), - prolog: getPath('codemirror-lang-prolog.js'), - coffeescript: getPath('codemirror-lang-coffeescript.js'), - livescript: getPath('codemirror-lang-livescript.js'), - ruby: getPath('codemirror-lang-ruby.js'), - go: getPath('codemirror-lang-go.js'), - perl: getPath('codemirror-lang-perl.js'), - lua: getPath('codemirror-lang-lua.js'), - r: getPath('codemirror-lang-r.js'), - julia: getPath('codemirror-lang-julia.js'), - scheme: getPath('codemirror-lang-scheme.js'), - clojure: getPath('codemirror-lang-clojure.js'), - tcl: getPath('codemirror-lang-tcl.js'), - less: getPath('codemirror-lang-less.js'), - stylus: getPath('codemirror-lang-stylus.js'), - rust: getPath('codemirror-lang-rust.js'), - swift: getPath('codemirror-lang-swift.js'), -}; export const editorLanguages: Partial<{ [key in Language]: () => Promise }> = { - html: async () => html(), - css: async () => css(), - javascript: async () => javascript(), - typescript: async () => javascript({ typescript: true }), - jsx: async () => javascript({ jsx: true }), - tsx: async () => javascript({ jsx: true, typescript: true }), json: async () => json(), - vue: async () => (await import(moduleUrls.vue)).vue(), - svelte: async () => (await import(moduleUrls.svelte)).svelte(), - liquid: async () => (await import(moduleUrls.liquid)).liquid(), - markdown: async () => (await import(moduleUrls.markdown)).markdown(), - python: async () => (await import(moduleUrls.python)).python(), - php: async () => (await import(moduleUrls.php)).php(), - go: async () => (await import(moduleUrls.go)).go(), - java: async () => (await import(moduleUrls.java)).java(), - cpp: async () => (await import(moduleUrls.cpp)).cpp(), - sql: async () => (await import(moduleUrls.sql)).sql(), - wat: async () => (await import(moduleUrls.wast)).wast(), - scss: async () => (await import(moduleUrls.scss)).sass(), - sass: async () => (await import(moduleUrls.scss)).sass({ indented: true }), - minizinc: async () => (await import(moduleUrls.minizinc)).MiniZinc(), - prolog: async () => (await import(moduleUrls.prolog)).prolog(), - coffeescript: async () => legacy((await import(moduleUrls.coffeescript)).coffeeScript), - livescript: async () => legacy((await import(moduleUrls.livescript)).liveScript), - ruby: async () => legacy((await import(moduleUrls.ruby)).ruby), - perl: async () => legacy((await import(moduleUrls.perl)).perl), - lua: async () => legacy((await import(moduleUrls.lua)).lua), - r: async () => legacy((await import(moduleUrls.r)).r), - julia: async () => legacy((await import(moduleUrls.julia)).julia), - scheme: async () => legacy((await import(moduleUrls.scheme)).scheme), - clojure: async () => legacy((await import(moduleUrls.clojure)).clojure), - tcl: async () => legacy((await import(moduleUrls.tcl)).tcl), - less: async () => legacy((await import(moduleUrls.less)).less), - stylus: async () => legacy((await import(moduleUrls.stylus)).stylus), - csharp: async () => legacy((await import(moduleUrls.clike)).csharp), - ocaml: async () => legacy((await import(moduleUrls.mllike)).oCaml), - // fsharp: async () => legacy((await import(moduleUrls.mllike)).fSharp), - // @ts-ignore - rust: async () => legacy((await import(moduleUrls.rust)).rust), - swift: async () => legacy((await import(moduleUrls.swift)).swift), }; diff --git a/src/livecodes/editor/codemirror/utils.ts b/src/livecodes/editor/codemirror/utils.ts new file mode 100644 index 0000000000..a381e7d707 --- /dev/null +++ b/src/livecodes/editor/codemirror/utils.ts @@ -0,0 +1,12 @@ +export const codemirrorLegacy = async (parser: any) => { + const { LanguageSupport, StreamLanguage } = await import(codemirrorImports.language); + return new LanguageSupport(StreamLanguage.define(parser)); +}; + +export const codemirrorImports = { + html: '@codemirror/lang-html', + css: '@codemirror/lang-css', + javascript: '@codemirror/lang-javascript', + json: '@codemirror/lang-json', + language: '@codemirror/language', +}; diff --git a/src/livecodes/editor/monaco/languages/monaco-lang-astro.ts b/src/livecodes/editor/monaco/languages/monaco-lang-astro.ts deleted file mode 100644 index 5f15506a50..0000000000 --- a/src/livecodes/editor/monaco/languages/monaco-lang-astro.ts +++ /dev/null @@ -1,222 +0,0 @@ -export default { - config: { - comments: { - blockComment: [''], - }, - brackets: [ - ['---', '---'], - [''], - ['<', '>'], - ['{', '}'], - ['(', ')'], - ], - autoClosingPairs: [ - { open: '{', close: '}' }, - { open: '[', close: ']' }, - { open: '(', close: ')' }, - { open: "'", close: "'" }, - { open: '"', close: '"' }, - { open: '', notIn: ['comment', 'string'] }, - { open: '/**', close: ' */', notIn: ['string'] }, - { open: '<', close: '>', notIn: ['string'] }, - ], - autoCloseBefore: ';:.,=}])>` \n\t', - surroundingPairs: [ - { open: "'", close: "'" }, - { open: '"', close: '"' }, - { open: '{', close: '}' }, - { open: '[', close: ']' }, - { open: '(', close: ')' }, - { open: '<', close: '>' }, - ], - folding: { - markers: { - start: /^\\s*/, - end: /^\\s*/, - }, - }, - }, - - tokens: { - defaultToken: '', - tokenPostfix: '.astro', - ignoreCase: false, - - // non matched elements - empty: [ - 'area', - 'base', - 'basefont', - 'br', - 'col', - 'frame', - 'hr', - 'img', - 'input', - 'isindex', - 'link', - 'meta', - 'param', - ], - - // The main tokenizer for our languages - tokenizer: { - root: [ - [/)/, ['delimiter', 'tag', '', 'delimiter']], - [/(<)(script)/, ['delimiter', { token: 'tag', next: '@script' }]], - [/(<)(style)/, ['delimiter', { token: 'tag', next: '@style' }]], - [/(<)((?:[\w\-]+:)?[\w\-]+)/, ['delimiter', { token: 'tag', next: '@otherTag' }]], - [/(<\/)((?:[\w\-]+:)?[\w\-]+)/, ['delimiter', { token: 'tag', next: '@otherTag' }]], - [/]+/, 'metatag.content'], - [/>/, 'metatag', '@pop'], - ], - - frontmatter: [ - [/^---/, { token: 'comment', next: '@pop', nextEmbedded: '@pop' }], - [/./, { token: '@rematch', next: '@frontmatterEmbedded', nextEmbedded: 'text/javascript' }], - ], - - frontmatterEmbedded: [ - [/[^-]+|-[^-][^-]+/, { token: '@rematch', next: '@pop' }], - [/^---/, { token: 'comment', next: '@root', nextEmbedded: '@pop' }], - ], - - expression: [ - [ - /[^<{}]/, - { token: '@rematch', next: '@expressionEmbedded', nextEmbedded: 'text/javascript' }, - ], - [//, 'comment', '@pop'], - [/[^-]+/, 'comment.content'], - [/./, 'comment.content'], - ], - - otherTag: [ - [/\/?>/, 'delimiter', '@pop'], - [/"([^"]*)"/, 'attribute.value'], - [/'([^']*)'/, 'attribute.value'], - [/[\w\-]+/, 'attribute.name'], - [/=/, 'delimiter'], - [/[ \t\r\n]+/], // whitespace - ], - - // -- BEGIN