diff --git a/README.md b/README.md index 53a44d7..5fcb811 100644 --- a/README.md +++ b/README.md @@ -20,3 +20,5 @@ There are some aspects of the REPL that can be configured from the URL (more tha - `sfcOptions` or `sfc-options` with a JSON stringified object will be used as [`sfcOptions` for @vue/repl](https://github.com/search?q=repo%3Avuejs%2Frepl%20sfcOptions&type=code) - `previewOptions` or `preview-options` with a JSON stringified object will be used as [`previewOptions` for @vue/repl](https://github.com/search?q=repo%3Avuejs%2Frepl+previewOptions&type=code) - `prod` with no value or with a lower case converted value of `''`, `'true'`, `'t'`, or `'1'` will start the REPL with Vue in PROD mode +- `ssr` with no value or with a lower case converted value of `''`, `'true'`, `'t'`, or `'1'` will start the REPL with Vue in SSR mode +- `file` will set a specific file active diff --git a/package.json b/package.json index b5ae759..d27b09f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pdanpdan/quasar-play", - "version": "1.1.1", + "version": "1.1.2", "description": "Playground for Quasar Framework", "productName": "Quasar Play", "author": { diff --git a/src/App.vue b/src/App.vue index c4e8534..cd4a7ab 100644 --- a/src/App.vue +++ b/src/App.vue @@ -65,6 +65,7 @@ const repl = await useRepl({ outputMode: (urlSearch.get('previewMode') || urlSearch.get('preview-mode') || 'preview').toLowerCase(), productionMode: [ '', 'true', 't', '1' ].includes(String(urlSearch.get('prod')).toLowerCase()), ssr: [ '', 'true', 't', '1' ].includes(String(urlSearch.get('ssr')).toLowerCase()), + activeFile: String(urlSearch.get('file')), versions, }); diff --git a/src/components/TopBar.vue b/src/components/TopBar.vue index d34361b..62d9e1d 100644 --- a/src/components/TopBar.vue +++ b/src/components/TopBar.vue @@ -181,6 +181,7 @@ import { onBeforeUnmount, ref, reactive, + watch, type PropType, } from 'vue'; import { Dialog } from 'quasar'; @@ -389,6 +390,12 @@ function onToggleSSR() { history.replaceState({}, '', String(url)); } +watch(props.store.activeFile, (fileName: string) => { + const url = new URL(location.href); + url.searchParams.set('file', fileName); + history.replaceState({}, '', String(url)); +}); + async function onFormatFiles() { const files = props.store.replStore.state.files; diff --git a/src/store.ts b/src/store.ts index e66884f..9c06f5d 100644 --- a/src/store.ts +++ b/src/store.ts @@ -29,7 +29,7 @@ const importMaps = { '@quasar/extras/roboto-font/roboto-font.css': [ '@quasar/extras', 'roboto-font/roboto-font.css' ], '@quasar/extras/material-icons/material-icons.css': [ '@quasar/extras', 'material-icons/material-icons.css' ], -} as Record; +} as Record; function buildImports(currentImportMap: Record>, versions: Record = {}) { const imports: Record = currentImportMap.imports || {}; @@ -46,6 +46,18 @@ function buildImports(currentImportMap: Record>, }; } +function patchTsConfig(code: string, versions: Record = {}) { + try { + const tsConfig = JSON.parse(code); + const moduleResolution = parseInt(versions.typescript.split('.')[ 0 ], 10) < 5 ? 'Node' : 'Bundler'; + tsConfig.compilerOptions.moduleResolution = moduleResolution; + return JSON.stringify(tsConfig, null, 2); + } catch (e) { + // caught + } + return code; +} + const templateFiles = [ { name: 'src/boot.ts', code: BOOT_CODE }, { name: APP_FILE, code: APP_CODE }, @@ -59,6 +71,7 @@ const templateFiles = [ type ReplOptionsType = StoreOptions & { versions?: Record; ssr?: boolean; + activeFile?: string; }; export async function useRepl(options: ReplOptionsType = {}) { @@ -84,6 +97,8 @@ export async function useRepl(options: ReplOptionsType = {}) { const quasarCSSUrl = computed(() => getCdnUrl('quasar', 'dist/quasar.rtl.prod.css', versions[ 'quasar' ])); const replStore = new ReplStore(options); + replStore.addFile(new File(TS_FILE, patchTsConfig(replStore.state.files[ TS_FILE ].code, versions))); + replStore.setTypeScriptVersion(versions.typescript); replStore.setVueVersion(versions.vue); const addAllFiles = options.serializedState!.length === 0; @@ -105,10 +120,17 @@ export async function useRepl(options: ReplOptionsType = {}) { templateFiles.filter((file) => file.internal === true).forEach((file) => { replStore.state.files[ file.name ].hidden = true; }); - replStore.setActive(APP_FILE); + + const activeFile = typeof options.activeFile === 'string' && options.activeFile.trim().length > 0 ? options.activeFile.trim() : APP_FILE; + const fileNames = Object.keys(replStore.state.files); + replStore.setActive(fileNames.includes(activeFile) === true ? activeFile : APP_FILE); watch(() => versions.quasar, () => { + const { activeFile } = replStore.state; replStore.addFile(new File(IMPORT_FILE, JSON.stringify(buildImports(replStore.getImportMap(), versions), null, 2))); + if (activeFile) { + replStore.setActive(activeFile.filename); + } }); watch(() => versions.vue, () => { @@ -116,16 +138,13 @@ export async function useRepl(options: ReplOptionsType = {}) { }); watch(() => versions.typescript, () => { - try { - const tsConfig = JSON.parse(TS_CODE); - const moduleResolution = parseInt(versions.typescript.split('.')[ 0 ], 10) < 5 ? 'Node' : 'Bundler'; - tsConfig.compilerOptions.moduleResolution = moduleResolution; - replStore.addFile(new File(TS_FILE, JSON.stringify(tsConfig, null, 2))); - } catch (e) { - // caught - } + const { activeFile } = replStore.state; + replStore.addFile(new File(TS_FILE, patchTsConfig(replStore.state.files[ TS_FILE ].code, versions))); replStore.setTypeScriptVersion(versions.typescript); - }, { immediate: true }); + if (activeFile) { + replStore.setActive(activeFile.filename); + } + }); watch(productionMode, () => { if (replStore.productionMode !== productionMode.value) { @@ -163,6 +182,7 @@ export async function useRepl(options: ReplOptionsType = {}) { productionMode, quasarCSSUrl, versions, + activeFile: computed(() => (replStore.state.activeFile.filename)), setVersions(newVersions: Record) { Object.assign(versions, newVersions);