From a02af4f90c2f8052dd22255c8a12cef4800d546c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Sat, 22 Jun 2024 14:36:30 +0200 Subject: [PATCH] add localisation and theming for playground --- astro.config.mjs | 182 ++++++++++++--------- package.json | 1 + pnpm-lock.yaml | 62 ++++++- src/components/Playground.tsx | 92 +++++++---- src/components/override/ContentPanel.astro | 14 +- src/components/override/PageTitle.astro | 3 +- src/components/playground/Editor.tsx | 27 +-- src/components/playground/FileView.tsx | 6 +- src/components/playground/Header.tsx | 13 +- src/pages/de/playground.astro | 30 ++++ src/pages/playground.astro | 30 +++- src/styles/playground.scss | 22 ++- src/utils/playground.ts | 33 ++++ src/wasm/webcompiler/src/fs.rs | 18 +- src/wasm/webcompiler/src/lib.rs | 6 +- 15 files changed, 403 insertions(+), 136 deletions(-) create mode 100644 src/pages/de/playground.astro create mode 100644 src/utils/playground.ts diff --git a/astro.config.mjs b/astro.config.mjs index 6a445d9..1ef7897 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -1,78 +1,114 @@ -import { defineConfig } from 'astro/config'; -import starlight from '@astrojs/starlight'; +import { defineConfig } from "astro/config"; +import starlight from "@astrojs/starlight"; import react from "@astrojs/react"; import starlightLinksValidator from "starlight-links-validator"; -import shikiConfig from './src/utils/shiki'; +import starlightUtils from "@lorenzo_lewis/starlight-utils"; +import shikiConfig from "./src/utils/shiki"; + +const playgroundSidebarEntry = { + label: "Playground", + link: "/playground", + translations: { de: "Spielplatz" }, +}; + +const navLinks = [playgroundSidebarEntry]; // https://astro.build/config export default defineConfig({ - integrations: [react(), starlight({ - title: 'ShulkerScript', - logo: { - src: './src/assets/logo.webp', - alt: 'ShulkerScript Logo' - }, - favicon: '/favicon.ico', - description: 'A simple and powerful scripting language for Minecraft datapacks.', - social: { - github: 'https://github.com/moritz-hoelting/shulkerscript-cli' - }, - tableOfContents: { - minHeadingLevel: 1, - maxHeadingLevel: 3 - }, - defaultLocale: 'root', - locales: { - root: { - label: 'English', - lang: 'en' - }, - de: { - label: 'Deutsch', - lang: 'de' - } - }, - editLink: { - baseUrl: 'https://github.com/moritz-hoelting/shulkerscript-webpage/edit/main' - }, - customCss: ['./src/styles/style.css'], - plugins: [starlightLinksValidator({ - errorOnFallbackPages: false - })], - expressiveCode: { - shiki: shikiConfig - }, - components: { - PageTitle: './src/components/override/PageTitle.astro', - ContentPanel: './src/components/override/ContentPanel.astro', - }, - sidebar: [{ - label: 'Guides', - autogenerate: { - directory: 'guides' - }, - translations: { - de: 'Anleitungen' - } - }, { - label: 'Roadmap', - link: '/roadmap', - translations: { - de: 'Zukunftspläne' - } - }, { - label: 'Reference', - autogenerate: { - directory: 'reference' - }, - collapsed: true, - translations: { - de: 'Referenz' - }, - badge: { - text: 'WIP', - variant: 'caution' - } - }] - })] -}); \ No newline at end of file + integrations: [ + react(), + starlight({ + title: "ShulkerScript", + logo: { + src: "./src/assets/logo.webp", + alt: "ShulkerScript Logo", + }, + favicon: "/favicon.ico", + description: + "A simple and powerful scripting language for Minecraft datapacks.", + social: { + github: "https://github.com/moritz-hoelting/shulkerscript-cli", + }, + tableOfContents: { + minHeadingLevel: 1, + maxHeadingLevel: 3, + }, + defaultLocale: "root", + locales: { + root: { + label: "English", + lang: "en", + }, + de: { + label: "Deutsch", + lang: "de", + }, + }, + editLink: { + baseUrl: + "https://github.com/moritz-hoelting/shulkerscript-webpage/edit/main", + }, + customCss: ["./src/styles/style.css"], + plugins: [ + starlightUtils({ + navLinks: { + leading: { + useSidebarLabelled: "leadingNavLinks", + }, + }, + }), + starlightLinksValidator({ + errorOnFallbackPages: false, + }), + ], + expressiveCode: { + shiki: shikiConfig, + }, + components: { + PageTitle: "./src/components/override/PageTitle.astro", + ContentPanel: "./src/components/override/ContentPanel.astro", + }, + sidebar: [ + { + label: "leadingNavLinks", + items: navLinks, + }, + { + label: "Guides", + autogenerate: { + directory: "guides", + }, + translations: { + de: "Anleitungen", + }, + }, + { + label: "More", + translations: { + de: "Mehr", + }, + items: [ + { + label: "Roadmap", + link: "/roadmap", + translations: { + de: "Zukunftspläne", + }, + }, + playgroundSidebarEntry + ], + }, + { + label: "Reference", + autogenerate: { + directory: "reference", + }, + collapsed: true, + translations: { + de: "Referenz", + }, + }, + ], + }), + ], +}); diff --git a/package.json b/package.json index 5c7fc82..5bc2931 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "@astrojs/starlight": "^0.24.2", "@emotion/react": "^11.11.4", "@emotion/styled": "^11.11.5", + "@lorenzo_lewis/starlight-utils": "^0.1.1", "@monaco-editor/react": "^4.6.0", "@mui/icons-material": "^5.15.20", "@mui/material": "^5.15.20", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8770354..14042dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ dependencies: '@emotion/styled': specifier: ^11.11.5 version: 11.11.5(@emotion/react@11.11.4)(@types/react@18.3.3)(react@18.3.1) + '@lorenzo_lewis/starlight-utils': + specifier: ^0.1.1 + version: 0.1.1(@astrojs/starlight@0.24.2)(astro@4.10.2) '@monaco-editor/react': specifier: ^4.6.0 version: 4.6.0(monaco-editor@0.49.0)(react-dom@18.3.1)(react@18.3.1) @@ -1410,6 +1413,19 @@ packages: '@jridgewell/sourcemap-codec': 1.4.15 dev: false + /@lorenzo_lewis/starlight-utils@0.1.1(@astrojs/starlight@0.24.2)(astro@4.10.2): + resolution: {integrity: sha512-WBbZ9tnLxRsiiNVBzyNrANbl098/wMt7gVT09XCJtHZiiOlYQFBBYywgk/vAwzgicUe3vb27MBFc6jDOqvmu5w==} + peerDependencies: + '@astrojs/starlight': '>=0.16.0' + astro: '>=4.0.0' + dependencies: + '@astrojs/starlight': 0.24.2(astro@4.10.2) + astro: 4.10.2(sass@1.77.6)(typescript@5.4.5) + astro-integration-kit: 0.13.3(astro@4.10.2) + transitivePeerDependencies: + - '@astrojs/db' + dev: false + /@mdx-js/mdx@3.0.1: resolution: {integrity: sha512-eIQ4QTrOWyL3LWEe/bu6Taqzq2HQvHcyTMaOrI95P2/LmJE7AsfPfgJGuFLPVqBUE1BC1rik3VIhU+s9u72arA==} dependencies: @@ -2161,6 +2177,13 @@ packages: resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} dev: false + /ast-types@0.16.1: + resolution: {integrity: sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==} + engines: {node: '>=4'} + dependencies: + tslib: 2.6.3 + dev: false + /astring@1.8.6: resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==} hasBin: true @@ -2175,6 +2198,20 @@ packages: rehype-expressive-code: 0.35.3 dev: false + /astro-integration-kit@0.13.3(astro@4.10.2): + resolution: {integrity: sha512-hUEQMnZ7z+7ySPCX6mXnIr0BFZU1+49eQQBg4aHjKGz1o2oZ5tvuB9Tlyj/orRH9ubd+Gkd0SSoldz0BTNe4Rg==} + peerDependencies: + '@astrojs/db': ^0.9 || ^0.10 || ^0.11 + astro: ^4.4.1 + peerDependenciesMeta: + '@astrojs/db': + optional: true + dependencies: + astro: 4.10.2(sass@1.77.6)(typescript@5.4.5) + pathe: 1.1.2 + recast: 0.23.9 + dev: false + /astro@4.10.2(sass@1.77.6)(typescript@5.4.5): resolution: {integrity: sha512-SBdkoOanPsxKlKVU4uu/XG0G7NYAFoqmfBtq9SPMJ34B7Hr1MxVdEugERs8IwYN6UaxdDVcqA++9PvH6Onq2cg==} engines: {node: ^18.17.1 || ^20.3.0 || >=21.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} @@ -4437,6 +4474,10 @@ packages: engines: {node: '>=8'} dev: false + /pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + dev: false + /periscopic@3.1.0: resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} dependencies: @@ -4639,6 +4680,17 @@ packages: dependencies: picomatch: 2.3.1 + /recast@0.23.9: + resolution: {integrity: sha512-Hx/BGIbwj+Des3+xy5uAtAbdCyqK9y9wbBcDFDYanLS9JnMqf7OeF87HQwUimE87OEc72mr6tkKUKMBBL+hF9Q==} + engines: {node: '>= 4'} + dependencies: + ast-types: 0.16.1 + esprima: 4.0.1 + source-map: 0.6.1 + tiny-invariant: 1.3.3 + tslib: 2.6.3 + dev: false + /regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} dev: false @@ -5079,6 +5131,11 @@ packages: engines: {node: '>=0.10.0'} dev: false + /source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + dev: false + /source-map@0.7.4: resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} engines: {node: '>= 8'} @@ -5279,6 +5336,10 @@ packages: b4a: 1.6.6 dev: false + /tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + dev: false + /tm-themes@1.4.3: resolution: {integrity: sha512-nsUGwktLaWFMyKw2e/hNyDmcTIO+ue6Q2Mvb8L7WQkKSCflOm2TjGSspcJw6q7hi4QxQQNTuGICyvQN1UqtunQ==} dev: false @@ -5319,7 +5380,6 @@ packages: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} requiresBuild: true dev: false - optional: true /tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} diff --git a/src/components/Playground.tsx b/src/components/Playground.tsx index b034b74..c7b55b5 100644 --- a/src/components/Playground.tsx +++ b/src/components/Playground.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useMonaco, type Monaco } from "@monaco-editor/react"; import { useImmer, type Updater } from "use-immer"; @@ -14,16 +14,7 @@ import initWasm, { compile, compileZip, } from "@wasm/webcompiler/pkg/webcompiler"; - -export type File = { - language?: string; - content: string; -}; -export type Directory = { - dirs?: { [key: string]: Directory }; - files?: { [key: string]: File }; -}; -export type SetState = React.Dispatch>; +import type { Directory, File, PlaygroundLang } from "@utils/playground"; const FILE_STORAGE_KEY = "playground-files"; const DEFAULT_FILES = { @@ -46,7 +37,7 @@ const DEFAULT_FILES = { }, }; -export default function Playground() { +export default function Playground({ lang }: { lang: PlaygroundLang }) { initWasm().catch((err) => { console.error(err); }); @@ -55,32 +46,40 @@ export default function Playground() { getStorageOrDefault(FILE_STORAGE_KEY, DEFAULT_FILES) as Directory ); + const [theme, setTheme] = useState<"light" | "dark">("dark"); const [fileName, setFileName] = useState("src/main.shu"); const file = getFile(rootDir, fileName); const onBuild = () => { if (monaco) { - const dist = JSON.parse( - JSON.stringify(compile(getFiles(monaco)), jsonReplacer) - ); - const withRoot = { - dirs: { - dist: dist, - }, - } as Directory; - loadFiles(monaco, updateRootDir, withRoot); + const compiled = compile(getFiles(monaco)); + if (compiled) { + const dist = JSON.parse(JSON.stringify(compiled, jsonReplacer)); + const withRoot = { + dirs: { + dist: dist, + }, + } as Directory; + loadFiles(monaco, updateRootDir, withRoot); + } else { + alert("Compilation failed"); + } } else { console.error("monaco has not loaded"); } }; const onZip = () => { if (monaco) { - const data = - "data:application/zip;base64," + compileZip(getFiles(monaco)); - const a = document.createElement("a"); - a.href = data; - a.download = "shulkerscript-pack.zip"; - a.click(); + const zipped = compileZip(getFiles(monaco)); + if (zipped) { + const data = "data:application/zip;base64," + zipped; + const a = document.createElement("a"); + a.href = data; + a.download = "shulkerscript-pack.zip"; + a.click(); + } else { + alert("Compilation failed"); + } } else { console.error("monaco has not loaded"); } @@ -125,6 +124,37 @@ export default function Playground() { } }, [monaco]); + useEffect(() => { + if (monaco) { + let isReadOnly = fileName.startsWith("dist/"); + monaco.editor.getEditors().forEach((e) => + e.updateOptions({ + readOnly: isReadOnly, + readOnlyMessage: { + value: "Generated files are read-only", + }, + }) + ); + } + }, [fileName]); + + useEffect(() => { + const root = document.querySelector(":root") as HTMLElement; + if (root) { + function reactToThemeChange() { + const selectedTheme = root.getAttribute("data-theme"); + if (selectedTheme !== theme && selectedTheme !== null) { + setTheme(selectedTheme as "light" | "dark"); + } + } + reactToThemeChange(); + + root.onchange = () => { + reactToThemeChange(); + }; + } + }); + return ( <>
+ -
); diff --git a/src/components/override/ContentPanel.astro b/src/components/override/ContentPanel.astro index 1c21944..2b95fd7 100644 --- a/src/components/override/ContentPanel.astro +++ b/src/components/override/ContentPanel.astro @@ -1,9 +1,17 @@ --- import type { Props } from "@astrojs/starlight/props"; import Default from "@astrojs/starlight/components/ContentPanel.astro"; +import { isPlaygroundPage } from '@utils/playground'; -const isPlayground = Astro.props.slug === 'playground'; +const isPlayground = isPlaygroundPage(Astro.props.slug, Astro.currentLocale); --- - -{isPlayground ? : } +{ + isPlayground ? ( + + ) : ( + + + + ) +} diff --git a/src/components/override/PageTitle.astro b/src/components/override/PageTitle.astro index 089c702..82d1a3c 100644 --- a/src/components/override/PageTitle.astro +++ b/src/components/override/PageTitle.astro @@ -1,8 +1,9 @@ --- import type { Props } from "@astrojs/starlight/props"; import Default from "@astrojs/starlight/components/PageTitle.astro"; +import { isPlaygroundPage } from '@utils/playground'; -const isPlayground = Astro.props.slug === 'playground'; +const isPlayground = isPlaygroundPage(Astro.props.slug, Astro.currentLocale); --- diff --git a/src/components/playground/Editor.tsx b/src/components/playground/Editor.tsx index 2a1ceaf..cdc5640 100644 --- a/src/components/playground/Editor.tsx +++ b/src/components/playground/Editor.tsx @@ -1,17 +1,20 @@ -import type { File } from "@components/Playground"; +import type { File } from "@utils/playground"; import MonacoEditor, { useMonaco } from "@monaco-editor/react"; import { getHighlighter, type Highlighter } from "shiki"; import { shikiToMonaco } from "@shikijs/monaco"; import { useEffect, useState } from "react"; import darkPlus from "tm-themes/themes/dark-plus.json"; +import lightPlus from "tm-themes/themes/light-plus.json"; import { shulkerscriptGrammar } from "@utils/shulkerscript-grammar"; import { mcfunctionGrammar } from "@utils/mcfunction-grammar"; export default function Editor({ + theme, fileName, file, }: { + theme: "light" | "dark"; fileName: string; file?: File; }) { @@ -22,9 +25,12 @@ export default function Editor({ if (monaco) { if (highlighter == null) { getHighlighter({ - themes: [darkPlus as any], + themes: [darkPlus as any, lightPlus], langs: ["toml", shulkerscriptGrammar, mcfunctionGrammar], }).then((highlighter) => { + highlighter.setTheme( + theme === "dark" ? "dark-plus" : "light-plus" + ); setHighlighter(highlighter); }); } else { @@ -43,14 +49,13 @@ export default function Editor({ }, [highlighter]); return ( -
- -
+ ); } diff --git a/src/components/playground/FileView.tsx b/src/components/playground/FileView.tsx index 1a36556..3e763a4 100644 --- a/src/components/playground/FileView.tsx +++ b/src/components/playground/FileView.tsx @@ -1,4 +1,4 @@ -import type { Directory, SetState } from "@components/Playground"; +import type { Directory, PlaygroundExplorerLang, SetState } from "@utils/playground"; import React, { useState } from "react"; import { GoChevronDown as ChevDown, @@ -6,11 +6,13 @@ import { } from "react-icons/go"; export default function FileView({ + lang, root, fileName, setSelectedFileName, className, }: { + lang: PlaygroundExplorerLang; root: Directory; fileName: string; setSelectedFileName: SetState; @@ -18,7 +20,7 @@ export default function FileView({ }) { return (
-

Explorer

+

{lang.title}

{Object.entries(root.dirs ?? {}).map(([name, dir]) => { return ( diff --git a/src/components/playground/Header.tsx b/src/components/playground/Header.tsx index 4db4102..e084744 100644 --- a/src/components/playground/Header.tsx +++ b/src/components/playground/Header.tsx @@ -1,11 +1,14 @@ +import type { PlaygroundHeaderLang } from "@utils/playground"; import DropdownButton from "./DropdownButton"; export default function Header({ + lang, onSave, onReset, onBuild, onZip, }: { + lang: PlaygroundHeaderLang; onSave: () => void; onReset: () => void; onBuild: () => void; @@ -19,17 +22,17 @@ export default function Header({ marginBottom: "0.5cm", }} > -

Playground

+

{lang.title}

diff --git a/src/pages/de/playground.astro b/src/pages/de/playground.astro new file mode 100644 index 0000000..e8ed2a3 --- /dev/null +++ b/src/pages/de/playground.astro @@ -0,0 +1,30 @@ +--- +import PlaygroundComponent from "@components/Playground"; +import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro"; +import type { PlaygroundLang } from "@utils/playground"; + +const lang: PlaygroundLang = { + header: { + title: "Spielplatz", + buttons: { + build: "Bauen", + zip: "Als Zip herunterladen", + save: "Speichern", + reset: "Zurücksetzen", + }, + }, + explorer: { + title: "Dateien", + }, +}; +--- + + + + + + \ No newline at end of file diff --git a/src/pages/playground.astro b/src/pages/playground.astro index aabf177..5ab4ec2 100644 --- a/src/pages/playground.astro +++ b/src/pages/playground.astro @@ -1,8 +1,30 @@ --- -import PlaygroundComponent from '@components/Playground'; -import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro'; +import PlaygroundComponent from "@components/Playground"; +import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro"; +import type { PlaygroundLang } from "@utils/playground"; + +const lang: PlaygroundLang = { + header: { + title: "Playground", + buttons: { + build: "Build", + zip: "Download zip", + save: "Save", + reset: "Reset", + }, + }, + explorer: { + title: "Explorer", + }, +}; --- - - + + + + \ No newline at end of file diff --git a/src/styles/playground.scss b/src/styles/playground.scss index 5fef050..3a05268 100644 --- a/src/styles/playground.scss +++ b/src/styles/playground.scss @@ -13,6 +13,8 @@ display: flex; flex-direction: column; margin-right: 0.5cm; + overflow-x: hidden; + overflow-y: auto; .entries { button { @@ -22,15 +24,15 @@ background-color: transparent; cursor: pointer; + &:hover { + background-color: #444444; + } + &:disabled { cursor: default; color: var(--sl-color-text); background-color: #333; } - - &:hover { - background-color: #444444; - } } } } @@ -38,3 +40,15 @@ grid-area: editor; } } + +:root[data-theme='light'] { + .playground > .file-view .entries button { + &:hover { + background-color: var(--sl-color-gray-5); + } + + &:disabled { + background-color: var(--sl-color-gray-6); + } + } +} \ No newline at end of file diff --git a/src/utils/playground.ts b/src/utils/playground.ts new file mode 100644 index 0000000..06fec04 --- /dev/null +++ b/src/utils/playground.ts @@ -0,0 +1,33 @@ +export type PlaygroundLang = { + header: PlaygroundHeaderLang; + explorer: PlaygroundExplorerLang; +}; + +export type PlaygroundHeaderLang = { + title: string; + buttons: { + save: string; + reset: string; + build: string; + zip: string; + }; +}; +export type PlaygroundExplorerLang = { + title: string; +}; + +export type File = { + language?: string; + content: string; +}; +export type Directory = { + dirs?: { [key: string]: Directory }; + files?: { [key: string]: File }; +}; +export type SetState = React.Dispatch>; + +export function isPlaygroundPage(slug: string, lang?: string): boolean { + return ( + slug === (!lang || lang == "en" ? "playground" : `${lang}/playground`) + ); +} diff --git a/src/wasm/webcompiler/src/fs.rs b/src/wasm/webcompiler/src/fs.rs index 9ec6fe1..197f1c0 100644 --- a/src/wasm/webcompiler/src/fs.rs +++ b/src/wasm/webcompiler/src/fs.rs @@ -62,7 +62,10 @@ impl From for Directory { } for (name, item) in value.get_files() { - files.insert(name.to_string(), item.clone().into()); + files.insert( + name.to_string(), + File::from(item.clone()).correct_lang(name), + ); } Self { @@ -84,3 +87,16 @@ impl From for File { } } } + +impl File { + pub fn correct_lang(self, name: &str) -> Self { + let language = match name.split('.').last() { + Some("shu") => Some("shulkerscript".to_string()), + Some("mcfunction") => Some("mcfunction".to_string()), + Some("json" | "mcmeta") => Some("json".to_string()), + _ => None, + }; + + Self { language, ..self } + } +} diff --git a/src/wasm/webcompiler/src/lib.rs b/src/wasm/webcompiler/src/lib.rs index 8f83edc..18aa9eb 100644 --- a/src/wasm/webcompiler/src/lib.rs +++ b/src/wasm/webcompiler/src/lib.rs @@ -42,10 +42,10 @@ pub fn compile(root_dir: JsValue) -> JsValue { /// Returns a base64 encoded zip file containing the compiled datapack. #[wasm_bindgen(js_name = compileZip)] -pub fn compile_zip(root_dir: JsValue) -> String { +pub fn compile_zip(root_dir: JsValue) -> Option { let root_dir = VFolder::from(serde_wasm_bindgen::from_value::(root_dir).unwrap()); - let datapack = _compile(&root_dir).unwrap(); + let datapack = _compile(&root_dir).ok()?; let mut buffer = Cursor::new(Vec::new()); let mut writer = ZipWriter::new(&mut buffer); @@ -70,7 +70,7 @@ pub fn compile_zip(root_dir: JsValue) -> String { writer.finish().unwrap(); - BASE64_STANDARD.encode(buffer.into_inner()) + Some(BASE64_STANDARD.encode(buffer.into_inner())) } fn _compile(root_dir: &VFolder) -> Result {