swap out monaco and babel-walk with codemirror and @babel/traverse
This commit is contained in:
parent
ec05b76078
commit
9da49ee124
24 changed files with 478 additions and 291 deletions
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"semi": false,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
|
|
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
2
bunfig.toml
Normal file
2
bunfig.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[install.scopes]
|
||||
"@jsr" = "https://npm.jsr.io"
|
11
package.json
11
package.json
|
@ -15,20 +15,25 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@babel/parser": "^7.26.5",
|
||||
"@babel/traverse": "^7.26.5",
|
||||
"@codemirror/lang-javascript": "^6.2.2",
|
||||
"@std/async": "npm:@jsr/std__async",
|
||||
"@sveltejs/adapter-static": "^3.0.6",
|
||||
"@sveltejs/kit": "^2.0.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
||||
"@tailwindcss/postcss": "^4.0.0-beta.9",
|
||||
"babel-walk": "^3.0.1",
|
||||
"constrained-editor-plugin": "^1.3.0",
|
||||
"@types/babel__traverse": "^7.20.6",
|
||||
"@typescript/vfs": "^1.6.0",
|
||||
"@valtown/codemirror-ts": "^2.3.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"daisyui": "^5.0.0-beta.1",
|
||||
"esbuild-wasm": "^0.24.2",
|
||||
"monaco-editor": "^0.52.2",
|
||||
"prettier": "^3.3.2",
|
||||
"prettier-plugin-svelte": "^3.2.6",
|
||||
"sprig": "^1.1.3",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"svelte-codemirror-editor": "^1.4.1",
|
||||
"tailwindcss": "^4.0.0-beta.9",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^5.4.11",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export default {
|
||||
plugins: {
|
||||
'@tailwindcss/postcss': {},
|
||||
'@tailwindcss/postcss': {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
2
src/app.d.ts
vendored
2
src/app.d.ts
vendored
|
@ -10,4 +10,4 @@ declare global {
|
|||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
export {}
|
||||
|
|
|
@ -1,153 +1,39 @@
|
|||
<script lang=ts>
|
||||
import { onMount } from 'svelte';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
|
||||
import {} from 'sprig/web';
|
||||
import { constrainedEditor } from 'constrained-editor-plugin';
|
||||
import { parse } from '@babel/parser';
|
||||
import { buildProject } from './build/esbuild';
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import { buildProject } from './build/esbuild'
|
||||
import checkBabel, { setupBabel } from './text-editor/babel'
|
||||
|
||||
let editorContainer: HTMLDivElement;
|
||||
let editor: import('monaco-editor').editor.IStandaloneCodeEditor;
|
||||
// const monaco = await import ('monaco-editor')
|
||||
// Initial code inside the function
|
||||
function debounce<A, T = void>(func: (this: T, ...args: A[]) => any, timeout = 300) {
|
||||
let timer: number;
|
||||
return function (this: T, ...args: A[]) {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
func.apply(this, args);
|
||||
}, timeout);
|
||||
};
|
||||
}
|
||||
let { code = $bindable() }: { code: string } = $props();
|
||||
onMount(async () => {
|
||||
window.build = () => buildProject(code, true);
|
||||
globalThis.process = {};
|
||||
globalThis.process.env = {};
|
||||
const { recursive } = await import('babel-walk');
|
||||
await addGlobalTypes();
|
||||
setupEditor();
|
||||
self.MonacoEnvironment = {
|
||||
getWorker: function (_: any, label: string) {
|
||||
return new tsWorker();
|
||||
}
|
||||
};
|
||||
import { basicSetup, EditorView } from 'codemirror'
|
||||
|
||||
async function addGlobalTypes() {
|
||||
const sprigTypeDef = (await import('./editorcontext/sprig.d.ts?raw')).default;
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
sprigTypeDef,
|
||||
'context/sprig.d.ts'
|
||||
);
|
||||
|
||||
// IMPLEMENT ONCE v2 IS DONE!
|
||||
// const sprigsyTypeDef = (await import('./editorcontext/sprigsy.d.ts?raw')).default;
|
||||
// monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
// sprigsyTypeDef,
|
||||
// 'context/sprigsy.d.ts'
|
||||
// );
|
||||
}
|
||||
const disallowedIdentifiers = ['window', 'globalThis'];
|
||||
type ASTState = {
|
||||
disallowedWarnings: { identifier: string; loc: [number, number, number, number] }[];
|
||||
errors: { reason: string; loc: [number, number, number, number] }[];
|
||||
};
|
||||
const ASTWalker = recursive<ASTState>({
|
||||
Identifier(node, state, c) {
|
||||
if (disallowedIdentifiers.includes(node.name)) {
|
||||
state.disallowedWarnings.push({
|
||||
identifier: node.name,
|
||||
loc: [
|
||||
node.loc?.start?.line!,
|
||||
node.loc?.start?.column!,
|
||||
node.loc?.end?.line!,
|
||||
node.loc?.end?.column!
|
||||
]
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
MemberExpression(node, state, c) {
|
||||
if (node.optional) {
|
||||
state.errors.push({
|
||||
reason: 'optional chaining (?.) will work in the editor but it will not work on the sprig console',
|
||||
loc: [
|
||||
node.loc?.start.line!,
|
||||
node.loc?.start.column!,
|
||||
node.loc?.end.line!,
|
||||
node.loc?.end.column!
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const checkBabel = debounce(() => {
|
||||
let ast;
|
||||
try {
|
||||
ast = parse(editor.getValue(), {
|
||||
plugins: ['typescript']
|
||||
});
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
const state: ASTState = { disallowedWarnings: [], errors: [] };
|
||||
ASTWalker(ast, state);
|
||||
state.disallowedWarnings.forEach((v) => {
|
||||
monaco.editor.setModelMarkers(editor.getModel()!, editor.getId(), [
|
||||
{
|
||||
message: `'${v.identifier}' is not allowed.`,
|
||||
severity: monaco.MarkerSeverity.Error,
|
||||
startLineNumber: v.loc[0],
|
||||
startColumn: v.loc[1] + 1,
|
||||
endLineNumber: v.loc[2],
|
||||
endColumn: v.loc[3] + 1
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
function setupEditor() {
|
||||
editor = monaco.editor.create(editorContainer, {
|
||||
value: `async function game(api: sprig.FullSprigAPI): void {
|
||||
import CodeMirror from 'svelte-codemirror-editor'
|
||||
import { env, typescriptExtensions } from './text-editor/extensions/typescript'
|
||||
let { code = $bindable() }: { code: string } = $props()
|
||||
let editor = $state<EditorView>();
|
||||
let value = $state<string>(
|
||||
`/// <reference types="lib.sprig.d.ts" />
|
||||
async function game(api: sprig.FullSprigAPI): void {
|
||||
// Code here
|
||||
}`,
|
||||
language: 'typescript',
|
||||
automaticLayout: true,
|
||||
scrollBeyondLastLine: false,
|
||||
theme: 'vs-dark'
|
||||
});
|
||||
}`)
|
||||
|
||||
const model = editor.getModel()!;
|
||||
const constrainedInstance = constrainedEditor(monaco);
|
||||
constrainedInstance.initializeIn(editor);
|
||||
constrainedInstance.addRestrictionsTo(model, [
|
||||
{
|
||||
range: [2, 1, 2, 14], // Range of Function definition
|
||||
allowMultiline: true,
|
||||
label: 'funcDefinition'
|
||||
}
|
||||
]);
|
||||
model.updateValueInEditableRanges({
|
||||
funcDefinition: code
|
||||
});
|
||||
editor.onDidChangeModelContent((event) => {
|
||||
code = model.getValueInEditableRanges().funcDefinition;
|
||||
checkBabel();
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
if (editor) {
|
||||
editor.dispose();
|
||||
}
|
||||
};
|
||||
});
|
||||
const extensions = [basicSetup, ...typescriptExtensions]
|
||||
|
||||
onMount(async () => {
|
||||
// @ts-expect-error: @babel/types uses process.env without `?.` (ironic that babel is only implemented to warn against using `?.` )
|
||||
window.process = { env: {} }
|
||||
// @ts-expect-error
|
||||
window.env = env
|
||||
|
||||
await setupBabel()
|
||||
})
|
||||
</script>
|
||||
|
||||
<div bind:this={editorContainer} class="editor-container"></div>
|
||||
<!-- <div bind:this={editorContainer} class="editor-container"></div> -->
|
||||
<CodeMirror on:ready={(e) => editor = e.detail} on:change={(e) => {
|
||||
console.log(e)
|
||||
}} bind:value {extensions} class="editor-container" />
|
||||
|
||||
<style lang=postcss>
|
||||
.editor-container {
|
||||
<style lang="postcss">
|
||||
:global(.editor-container) {
|
||||
@apply h-screen w-full;
|
||||
}
|
||||
</style>
|
||||
|
|
147
src/lib/CodeEditorOld.svelte
Normal file
147
src/lib/CodeEditorOld.svelte
Normal file
|
@ -0,0 +1,147 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte'
|
||||
import { parse } from '@babel/parser'
|
||||
import { buildProject } from './build/esbuild'
|
||||
|
||||
let editorContainer: HTMLDivElement
|
||||
let editor: import('monaco-editor').editor.IStandaloneCodeEditor
|
||||
// const monaco = await import ('monaco-editor')
|
||||
// Initial code inside the function
|
||||
function debounce<A, T = void>(func: (this: T, ...args: A[]) => any, timeout = 300) {
|
||||
let timer: number
|
||||
return function (this: T, ...args: A[]) {
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(() => {
|
||||
func.apply(this, args)
|
||||
}, timeout)
|
||||
}
|
||||
}
|
||||
let { code = $bindable() }: { code: string } = $props()
|
||||
onMount(async () => {
|
||||
window.build = () => buildProject(code, true)
|
||||
globalThis.process = {}
|
||||
globalThis.process.env = {}
|
||||
const { recursive } = await import('babel-walk')
|
||||
await addGlobalTypes()
|
||||
setupEditor()
|
||||
self.MonacoEnvironment = {
|
||||
getWorker: function (_: any, label: string) {
|
||||
return new tsWorker()
|
||||
}
|
||||
}
|
||||
|
||||
async function addGlobalTypes() {
|
||||
const sprigTypeDef = (await import('./editorcontext/sprig.d.ts?raw')).default
|
||||
monaco.languages.typescript.typescriptDefaults.addExtraLib(sprigTypeDef, 'context/sprig.d.ts')
|
||||
|
||||
// IMPLEMENT ONCE v2 IS DONE!
|
||||
// const sprigsyTypeDef = (await import('./editorcontext/sprigsy.d.ts?raw')).default;
|
||||
// monaco.languages.typescript.typescriptDefaults.addExtraLib(
|
||||
// sprigsyTypeDef,
|
||||
// 'context/sprigsy.d.ts'
|
||||
// );
|
||||
}
|
||||
const disallowedIdentifiers = ['window', 'globalThis']
|
||||
type ASTState = {
|
||||
disallowedWarnings: { identifier: string; loc: [number, number, number, number] }[]
|
||||
errors: { reason: string; loc: [number, number, number, number] }[]
|
||||
}
|
||||
const ASTWalker = recursive<ASTState>({
|
||||
Identifier(node, state, c) {
|
||||
if (disallowedIdentifiers.includes(node.name)) {
|
||||
state.disallowedWarnings.push({
|
||||
identifier: node.name,
|
||||
loc: [
|
||||
node.loc?.start?.line!,
|
||||
node.loc?.start?.column!,
|
||||
node.loc?.end?.line!,
|
||||
node.loc?.end?.column!
|
||||
]
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
MemberExpression(node, state, c) {
|
||||
if (node.optional) {
|
||||
state.errors.push({
|
||||
reason:
|
||||
'optional chaining (?.) will work in the editor but it will not work on the sprig console',
|
||||
loc: [
|
||||
node.loc?.start.line!,
|
||||
node.loc?.start.column!,
|
||||
node.loc?.end.line!,
|
||||
node.loc?.end.column!
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
const checkBabel = debounce(() => {
|
||||
let ast
|
||||
try {
|
||||
ast = parse(editor.getValue(), {
|
||||
plugins: ['typescript']
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
const state: ASTState = { disallowedWarnings: [], errors: [] }
|
||||
ASTWalker(ast, state)
|
||||
state.disallowedWarnings.forEach((v) => {
|
||||
monaco.editor.setModelMarkers(editor.getModel()!, editor.getId(), [
|
||||
{
|
||||
message: `'${v.identifier}' is not allowed.`,
|
||||
severity: monaco.MarkerSeverity.Error,
|
||||
startLineNumber: v.loc[0],
|
||||
startColumn: v.loc[1] + 1,
|
||||
endLineNumber: v.loc[2],
|
||||
endColumn: v.loc[3] + 1
|
||||
}
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
function setupEditor() {
|
||||
editor = monaco.editor.create(editorContainer, {
|
||||
value: `async function game(api: sprig.FullSprigAPI): void {
|
||||
// Code here
|
||||
}`,
|
||||
language: 'typescript',
|
||||
automaticLayout: true,
|
||||
scrollBeyondLastLine: false,
|
||||
theme: 'vs-dark'
|
||||
})
|
||||
|
||||
const model = editor.getModel()!
|
||||
const constrainedInstance = constrainedEditor(monaco)
|
||||
constrainedInstance.initializeIn(editor)
|
||||
constrainedInstance.addRestrictionsTo(model, [
|
||||
{
|
||||
range: [2, 1, 2, 14], // Range of Function definition
|
||||
allowMultiline: true,
|
||||
label: 'funcDefinition'
|
||||
}
|
||||
])
|
||||
model.updateValueInEditableRanges({
|
||||
funcDefinition: code
|
||||
})
|
||||
editor.onDidChangeModelContent((event) => {
|
||||
code = model.getValueInEditableRanges().funcDefinition
|
||||
checkBabel()
|
||||
})
|
||||
}
|
||||
return () => {
|
||||
if (editor) {
|
||||
editor.dispose()
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div bind:this={editorContainer} class="editor-container"></div>
|
||||
|
||||
<style lang="postcss">
|
||||
.editor-container {
|
||||
@apply h-screen w-full;
|
||||
}
|
||||
</style>
|
|
@ -1,23 +1,23 @@
|
|||
import { transform, initialize } from 'esbuild-wasm';
|
||||
import { transform, initialize } from 'esbuild-wasm'
|
||||
|
||||
await initialize({
|
||||
wasmURL: (await import('esbuild-wasm/esbuild.wasm?url')).default
|
||||
});
|
||||
})
|
||||
|
||||
export async function buildProject(code: string, vanilla: boolean) {
|
||||
let template = '';
|
||||
let template = ''
|
||||
if (vanilla) {
|
||||
template += `async function game(api) {
|
||||
${code}
|
||||
}
|
||||
console.log("Made with Sprigsy")
|
||||
game({ addSprite, addText, afterInput, bitmap, clearInterval, clearText, clearTile, clearTimeout, color, getAll, getFirst, getGrid, getTile, height, map, onInput, playTune, setBackground, setInterval, setLegend, setMap, setPushables, setSolids, setTimeout, tilesWith, tune, width })
|
||||
`;
|
||||
`
|
||||
} else {
|
||||
throw new Error('not implemented');
|
||||
throw new Error('not implemented')
|
||||
}
|
||||
|
||||
return await transform(template, {
|
||||
loader: 'ts',
|
||||
});
|
||||
loader: 'ts'
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script lang="ts">
|
||||
import type { Snippet } from 'svelte';
|
||||
import type { Snippet } from 'svelte'
|
||||
|
||||
let {
|
||||
tab = $bindable(),
|
||||
|
@ -7,17 +7,17 @@
|
|||
map,
|
||||
...props
|
||||
}: {
|
||||
tab: string | undefined;
|
||||
disabled?: boolean;
|
||||
map?: Record<string, string>;
|
||||
class?: string;
|
||||
[x: `_${string}`]: Snippet<[]>;
|
||||
} = $props();
|
||||
tab: string | undefined
|
||||
disabled?: boolean
|
||||
map?: Record<string, string>
|
||||
class?: string
|
||||
[x: `_${string}`]: Snippet<[]>
|
||||
} = $props()
|
||||
const tabs = Object.fromEntries(
|
||||
Object.entries(props)
|
||||
.filter((v) => v[0].startsWith('_'))
|
||||
.map(([name, snippet]): [string, Snippet<[]>] => [name.replace('_', ''), snippet])
|
||||
);
|
||||
)
|
||||
</script>
|
||||
|
||||
<div role="tablist" class="tabs tabs-boxed {props.class}">
|
||||
|
|
79
src/lib/editorcontext/sprig.d.ts
vendored
79
src/lib/editorcontext/sprig.d.ts
vendored
|
@ -1,79 +0,0 @@
|
|||
/**
|
||||
* normal sprig api
|
||||
* this is provided because your project type is
|
||||
*/
|
||||
declare namespace sprig {
|
||||
export declare const VALID_INPUTS: readonly ['w', 's', 'a', 'd', 'i', 'j', 'k', 'l'];
|
||||
export type InputKey = (typeof VALID_INPUTS)[number];
|
||||
export interface AddTextOptions {
|
||||
x?: number;
|
||||
y?: number;
|
||||
color?: string;
|
||||
}
|
||||
export declare class SpriteType {
|
||||
type: string;
|
||||
x: number;
|
||||
y: number;
|
||||
readonly dx: number;
|
||||
readonly dy: number;
|
||||
remove(): void;
|
||||
}
|
||||
export type Rgba = [number, number, number, number];
|
||||
export interface TextElement {
|
||||
x: number;
|
||||
y: number;
|
||||
color: Rgba;
|
||||
content: string;
|
||||
}
|
||||
export interface GameState {
|
||||
legend: [string, string][];
|
||||
texts: TextElement[];
|
||||
dimensions: {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
sprites: SpriteType[];
|
||||
solids: string[];
|
||||
pushable: Record<string, string[]>;
|
||||
background: string | null;
|
||||
}
|
||||
export interface PlayTuneRes {
|
||||
end(): void;
|
||||
isPlaying(): boolean;
|
||||
}
|
||||
// export declare const tones: Record<string, number>;
|
||||
// export declare const instruments: readonly ["sine", "triangle", "square", "sawtooth"];
|
||||
export type InstrumentType = (typeof instruments)[number];
|
||||
// export declare const instrumentKey: Record<string, InstrumentType>;
|
||||
// export declare const reverseInstrumentKey: Record<"sine" | "triangle" | "square" | "sawtooth", string>;
|
||||
export type Tune = [number, ...(InstrumentType | number | string)[]][];
|
||||
export interface FullSprigAPI {
|
||||
map(template: TemplateStringsArray, ...params: string[]): string;
|
||||
bitmap(template: TemplateStringsArray, ...params: string[]): string;
|
||||
color(template: TemplateStringsArray, ...params: string[]): string;
|
||||
tune(template: TemplateStringsArray, ...params: string[]): string;
|
||||
setMap(string: string): void;
|
||||
addText(str: string, opts?: AddTextOptions): void;
|
||||
clearText(): void;
|
||||
addSprite(x: number, y: number, type: string): void;
|
||||
getGrid(): SpriteType[][];
|
||||
getTile(x: number, y: number): SpriteType[];
|
||||
tilesWith(...matchingTypes: string[]): SpriteType[][];
|
||||
clearTile(x: number, y: number): void;
|
||||
setSolids(types: string[]): void;
|
||||
setPushables(map: Record<string, string[]>): void;
|
||||
setBackground(type: string): void;
|
||||
getFirst(type: string): SpriteType | undefined;
|
||||
getAll(type?: string): SpriteType[];
|
||||
width(): number;
|
||||
height(): number;
|
||||
setLegend(...bitmaps: [string, string][]): void;
|
||||
onInput(key: InputKey, fn: () => void): void;
|
||||
afterInput(fn: () => void): void;
|
||||
playTune(text: string, n?: number): PlayTuneRes;
|
||||
setTimeout(fn: TimerHandler, ms: number): number;
|
||||
setInterval(fn: TimerHandler, ms: number): number;
|
||||
clearTimeout(id: number): void;
|
||||
clearInterval(id: number): void;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { buildProject } from "$lib/build/esbuild"
|
||||
import { buildProject } from '$lib/build/esbuild'
|
||||
|
||||
enum Capability {
|
||||
BuildToVite,
|
||||
|
@ -6,11 +6,7 @@ enum Capability {
|
|||
}
|
||||
|
||||
export default abstract class Provider {
|
||||
constructor(
|
||||
|
||||
) {
|
||||
|
||||
}
|
||||
constructor() {}
|
||||
|
||||
abstract capabilities(): Capability[]
|
||||
|
||||
|
@ -18,7 +14,7 @@ export default abstract class Provider {
|
|||
* Builds HTML project
|
||||
*/
|
||||
async buildHTML(input: string): Promise<string> {
|
||||
throw new TypeError("BuildToVite capability is not available")
|
||||
throw new TypeError('BuildToVite capability is not available')
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
6
src/lib/text-editor/Tooltip.svelte
Normal file
6
src/lib/text-editor/Tooltip.svelte
Normal file
|
@ -0,0 +1,6 @@
|
|||
<script lang=ts>
|
||||
import type { HoverInfo } from "@valtown/codemirror-ts"
|
||||
|
||||
let { info }: { info: HoverInfo } = $props();
|
||||
console.log(info)
|
||||
</script>
|
71
src/lib/text-editor/babel.ts
Normal file
71
src/lib/text-editor/babel.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import { debounce } from '@std/async'
|
||||
import { parse } from '@babel/parser'
|
||||
|
||||
let traverse: typeof import("@babel/traverse")['default']
|
||||
|
||||
export async function setupBabel() {
|
||||
traverse = (await import("@babel/traverse")).default
|
||||
}
|
||||
|
||||
const disallowedIdentifiers = ['window', 'globalThis']
|
||||
type ASTState = {
|
||||
disallowedWarnings: { identifier: string; loc: [number, number, number, number] }[]
|
||||
errors: { reason: string; loc: [number, number, number, number] }[]
|
||||
}
|
||||
|
||||
export default debounce((code: string) => {
|
||||
let ast
|
||||
try {
|
||||
ast = parse(code, {
|
||||
plugins: ['typescript']
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
const state: ASTState = { disallowedWarnings: [], errors: [] }
|
||||
traverse(ast, {
|
||||
MemberExpression: {
|
||||
enter: (path, state) => {
|
||||
if (path.node.optional) {
|
||||
state.errors.push({
|
||||
reason:
|
||||
'optional chaining (?.) will work in the editor but it will not work on the sprig console',
|
||||
loc: [
|
||||
path.node.loc?.start.line!,
|
||||
path.node.loc?.start.column!,
|
||||
path.node.loc?.end.line!,
|
||||
path.node.loc?.end.column!
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
Identifier: {
|
||||
enter: (path, state) => {
|
||||
if (disallowedIdentifiers.includes(path.node.name)) {
|
||||
state.disallowedWarnings.push({
|
||||
identifier: path.node.name,
|
||||
loc: [
|
||||
path.node.loc?.start?.line!,
|
||||
path.node.loc?.start?.column!,
|
||||
path.node.loc?.end?.line!,
|
||||
path.node.loc?.end?.column!
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
}, undefined, state)
|
||||
state.disallowedWarnings.forEach((v) => {
|
||||
monaco.editor.setModelMarkers(editor.getModel()!, editor.getId(), [
|
||||
{
|
||||
message: `'${v.identifier}' is not allowed.`,
|
||||
severity: monaco.MarkerSeverity.Error,
|
||||
startLineNumber: v.loc[0],
|
||||
startColumn: v.loc[1] + 1,
|
||||
endLineNumber: v.loc[2],
|
||||
endColumn: v.loc[3] + 1
|
||||
}
|
||||
])
|
||||
})
|
||||
}, 300)
|
84
src/lib/text-editor/context/sprig.d.ts
vendored
Normal file
84
src/lib/text-editor/context/sprig.d.ts
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* basic sprig API
|
||||
* this is provided because your project type is base
|
||||
*/
|
||||
declare global {
|
||||
declare namespace sprig {
|
||||
export declare const VALID_INPUTS: readonly ['w', 's', 'a', 'd', 'i', 'j', 'k', 'l']
|
||||
export type InputKey = (typeof VALID_INPUTS)[number]
|
||||
export interface AddTextOptions {
|
||||
x?: number
|
||||
y?: number
|
||||
color?: string
|
||||
}
|
||||
export declare class SpriteType {
|
||||
type: string
|
||||
x: number
|
||||
y: number
|
||||
readonly dx: number
|
||||
readonly dy: number
|
||||
remove(): void
|
||||
}
|
||||
export type Rgba = [number, number, number, number]
|
||||
export interface TextElement {
|
||||
x: number
|
||||
y: number
|
||||
color: Rgba
|
||||
content: string
|
||||
}
|
||||
export interface GameState {
|
||||
legend: [string, string][]
|
||||
texts: TextElement[]
|
||||
dimensions: {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
sprites: SpriteType[]
|
||||
solids: string[]
|
||||
pushable: Record<string, string[]>
|
||||
background: string | null
|
||||
}
|
||||
export interface PlayTuneRes {
|
||||
end(): void
|
||||
isPlaying(): boolean
|
||||
}
|
||||
// export declare const tones: Record<string, number>;
|
||||
// export declare const instruments: readonly ["sine", "triangle", "square", "sawtooth"];
|
||||
export type InstrumentType = (typeof instruments)[number]
|
||||
// export declare const instrumentKey: Record<string, InstrumentType>;
|
||||
// export declare const reverseInstrumentKey: Record<"sine" | "triangle" | "square" | "sawtooth", string>;
|
||||
export type Tune = [number, ...(InstrumentType | number | string)[]][]
|
||||
export interface FullSprigAPI {
|
||||
map(template: TemplateStringsArray, ...params: string[]): string
|
||||
bitmap(template: TemplateStringsArray, ...params: string[]): string
|
||||
color(template: TemplateStringsArray, ...params: string[]): string
|
||||
tune(template: TemplateStringsArray, ...params: string[]): string
|
||||
setMap(string: string): void
|
||||
addText(str: string, opts?: AddTextOptions): void
|
||||
clearText(): void
|
||||
addSprite(x: number, y: number, type: string): void
|
||||
getGrid(): SpriteType[][]
|
||||
getTile(x: number, y: number): SpriteType[]
|
||||
tilesWith(...matchingTypes: string[]): SpriteType[][]
|
||||
clearTile(x: number, y: number): void
|
||||
setSolids(types: string[]): void
|
||||
setPushables(map: Record<string, string[]>): void
|
||||
setBackground(type: string): void
|
||||
getFirst(type: string): SpriteType | undefined
|
||||
getAll(type?: string): SpriteType[]
|
||||
width(): number
|
||||
height(): number
|
||||
setLegend(...bitmaps: [string, string][]): void
|
||||
onInput(key: InputKey, fn: () => void): void
|
||||
afterInput(fn: () => void): void
|
||||
playTune(text: string, n?: number): PlayTuneRes
|
||||
setTimeout(fn: TimerHandler, ms: number): number
|
||||
setInterval(fn: TimerHandler, ms: number): number
|
||||
clearTimeout(id: number): void
|
||||
clearInterval(id: number): void
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export {};
|
|
@ -1,4 +1,5 @@
|
|||
/**
|
||||
* THIS SHOULD NOT BE HERE
|
||||
* API for sprigsy
|
||||
* this is provided because your project has sprigsy v2 enabeled
|
||||
*/
|
||||
|
@ -8,6 +9,6 @@ declare namespace sprigsy {
|
|||
* Retrieve a sprite key for the sprite name
|
||||
* @param name Sprite name
|
||||
*/
|
||||
spriteKeyFor(name: string): string;
|
||||
spriteKeyFor(name: string): string
|
||||
}
|
||||
}
|
0
src/lib/text-editor/extensions/babel.ts
Normal file
0
src/lib/text-editor/extensions/babel.ts
Normal file
62
src/lib/text-editor/extensions/typescript.ts
Normal file
62
src/lib/text-editor/extensions/typescript.ts
Normal file
|
@ -0,0 +1,62 @@
|
|||
import { javascript } from '@codemirror/lang-javascript'
|
||||
import { autocompletion } from '@codemirror/autocomplete'
|
||||
import Tooltip from '../Tooltip.svelte'
|
||||
import { mount, unmount } from 'svelte'
|
||||
import {
|
||||
createDefaultMapFromCDN,
|
||||
createSystem,
|
||||
createVirtualTypeScriptEnvironment
|
||||
} from '@typescript/vfs'
|
||||
import ts from 'typescript'
|
||||
import { tsFacet, tsLinter, tsHover, tsAutocomplete, tsSync } from '@valtown/codemirror-ts'
|
||||
|
||||
import sprigDeclarations from '$lib/text-editor/context/sprig.d.ts?raw'
|
||||
|
||||
const fsMap = await createDefaultMapFromCDN({ target: ts.ScriptTarget.ES2022 }, '5.7.3', true, ts)
|
||||
fsMap.set('lib.sprig.d.ts', sprigDeclarations)
|
||||
const system = createSystem(fsMap)
|
||||
const compilerOpts = {}
|
||||
const env = createVirtualTypeScriptEnvironment(system, ['lib.sprig.d.ts'], ts, compilerOpts)
|
||||
|
||||
console.log(fsMap.entries().toArray())
|
||||
const path = 'index.ts'
|
||||
|
||||
export const typescriptExtensions = [
|
||||
javascript({
|
||||
jsx: false,
|
||||
typescript: true
|
||||
}),
|
||||
tsFacet.of({ env, path }),
|
||||
tsSync(),
|
||||
tsLinter(),
|
||||
autocompletion({
|
||||
override: [tsAutocomplete()],
|
||||
|
||||
}),
|
||||
tsHover({
|
||||
renderTooltip: (info) => {
|
||||
console.log("rendering tooltip")
|
||||
let tooltip: Tooltip;
|
||||
const div = document.createElement('div')
|
||||
return {
|
||||
dom: div,
|
||||
mount() {
|
||||
tooltip = mount(Tooltip, {
|
||||
target: div,
|
||||
intro: true,
|
||||
props: {
|
||||
info
|
||||
}
|
||||
})
|
||||
},
|
||||
destroy() {
|
||||
unmount(tooltip, { outro: true })
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
export {
|
||||
env
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
<script lang="ts">
|
||||
import '../app.css';
|
||||
const { children } = $props();
|
||||
import '../app.css'
|
||||
const { children } = $props()
|
||||
</script>
|
||||
|
||||
{@render children()}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
export const csr = true;
|
||||
export const prerender = true;
|
||||
export const csr = true
|
||||
export const prerender = true
|
||||
export const ssr = false
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
<script>
|
||||
import CodeEditor from '$lib/CodeEditor.svelte';
|
||||
import Tab from '$lib/components/Tab.svelte';
|
||||
let tab = 'code';
|
||||
let code = '';
|
||||
import CodeEditor from '$lib/CodeEditor.svelte'
|
||||
import Tab from '$lib/components/Tab.svelte'
|
||||
let tab = 'code'
|
||||
let code = ''
|
||||
function keypress() {
|
||||
debugger;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex h-full! w-full select-none flex-col bg-base-100">
|
||||
|
@ -56,3 +59,5 @@
|
|||
This will have the game screen
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<svelte:window on:keypress={keypress}></svelte:window>
|
|
@ -1,18 +1,17 @@
|
|||
import adapter from '@sveltejs/adapter-static';
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
|
||||
import adapter from '@sveltejs/adapter-static'
|
||||
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
|
||||
|
||||
/** @type {import('@sveltejs/kit').Config} */
|
||||
const config = {
|
||||
// Consult https://svelte.dev/docs/kit/integrations
|
||||
// for more information about preprocessors
|
||||
preprocess: vitePreprocess(),
|
||||
|
||||
kit: {
|
||||
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
||||
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
|
||||
adapter: adapter()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default config;
|
||||
export default config
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { sveltekit } from '@sveltejs/kit/vite';
|
||||
import { defineConfig } from 'vite';
|
||||
import { sveltekit } from '@sveltejs/kit/vite'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [sveltekit()],
|
||||
build: {
|
||||
target: ['es2022']
|
||||
}
|
||||
});
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue