Skip to content

Commit b6a0b3b

Browse files
committed
refactor: move non import-map files under src for cleaner path
auto completion
1 parent d7cdf70 commit b6a0b3b

File tree

4 files changed

+48
-17
lines changed

4 files changed

+48
-17
lines changed

src/editor/FileSelector.vue

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,16 @@ import { computed, inject, ref, VNode, Ref } from 'vue'
44
55
const store = inject('store') as Store
66
7+
/**
8+
* When `true`: indicates adding a new file
9+
* When `string`: indicates renaming a file, and holds the old filename in case
10+
* of cancel.
11+
*/
712
const pending = ref<boolean | string>(false)
13+
/**
14+
* Text shown in the input box when editing a file's name
15+
* This is a display name so it should always strip off the `src/` prefix.
16+
*/
817
const pendingFilename = ref('Comp.vue')
918
const showImportMap = inject('import-map') as Ref<boolean>
1019
const files = computed(() =>
@@ -45,7 +54,8 @@ function focus({ el }: VNode) {
4554
4655
function doneNameFile() {
4756
if (!pending.value) return
48-
const filename = pendingFilename.value
57+
// add back the src prefix
58+
const filename = 'src/' + pendingFilename.value
4959
const oldFilename = pending.value === true ? '' : pending.value
5060
5161
if (!/\.(vue|js|ts|css|json)$/.test(filename)) {
@@ -75,10 +85,14 @@ function doneNameFile() {
7585
}
7686
7787
function editFileName(file: string) {
78-
pendingFilename.value = file
88+
pendingFilename.value = stripSrcPrefix(file)
7989
pending.value = file
8090
}
8191
92+
function stripSrcPrefix(file: string) {
93+
return file.replace(/^src\//, '')
94+
}
95+
8296
const fileSel = ref(null)
8397
function horizontalScroll(e: WheelEvent) {
8498
e.preventDefault()
@@ -108,7 +122,7 @@ function horizontalScroll(e: WheelEvent) {
108122
@dblclick="i > 0 && editFileName(file)"
109123
>
110124
<span class="label">{{
111-
file === importMapFile ? 'Import Map' : file
125+
file === importMapFile ? 'Import Map' : stripSrcPrefix(file)
112126
}}</span>
113127
<span v-if="i > 0" class="remove" @click.stop="store.deleteFile(file)">
114128
<svg class="icon" width="12" height="12" viewBox="0 0 24 24">
@@ -117,7 +131,10 @@ function horizontalScroll(e: WheelEvent) {
117131
</svg>
118132
</span>
119133
</div>
120-
<div v-if="(pending === true && i === files.length - 1) || (pending === file)" class="file pending">
134+
<div
135+
v-if="(pending === true && i === files.length - 1) || pending === file"
136+
class="file pending"
137+
>
121138
<input
122139
v-model="pendingFilename"
123140
spellcheck="false"

src/output/moduleCompiler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ function processModule(store: Store, src: string, filename: string) {
132132
}
133133

134134
function defineImport(node: Node, source: string) {
135-
const filename = resolveImport(source.replace(/^\.\/+/, ''))
135+
const filename = resolveImport(source.replace(/^\.\/+/, 'src/'))
136136
if (!filename) {
137-
throw new Error(`File "${filename}" does not exist.`)
137+
throw new Error(`File "${source}" does not exist.`)
138138
}
139139
if (importedFiles.has(filename)) {
140140
return importToIdMap.get(filename)!

src/store.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,17 +119,15 @@ export class ReplStore implements Store {
119119
showOutput = false,
120120
outputMode = 'preview'
121121
}: StoreOptions = {}) {
122-
let files: StoreState['files'] = {}
122+
const files: StoreState['files'] = {}
123123

124124
if (serializedState) {
125125
const saved = JSON.parse(atou(serializedState))
126126
for (const filename in saved) {
127-
files[filename] = new File(filename, saved[filename])
127+
setFile(files, filename, saved[filename])
128128
}
129129
} else {
130-
files = {
131-
[defaultMainFile]: new File(defaultMainFile, welcomeCode)
132-
}
130+
setFile(files, defaultMainFile, welcomeCode)
133131
}
134132

135133
this.defaultVueRuntimeURL = defaultVueRuntimeURL
@@ -245,18 +243,20 @@ export class ReplStore implements Store {
245243
getFiles() {
246244
const exported: Record<string, string> = {}
247245
for (const filename in this.state.files) {
248-
exported[filename] = this.state.files[filename].code
246+
const normalized =
247+
filename === importMapFile ? filename : filename.replace(/^src\//, '')
248+
exported[normalized] = this.state.files[filename].code
249249
}
250250
return exported
251251
}
252252

253253
async setFiles(newFiles: Record<string, string>, mainFile = defaultMainFile) {
254254
const files: Record<string, File> = {}
255255
if (mainFile === defaultMainFile && !newFiles[mainFile]) {
256-
files[mainFile] = new File(mainFile, welcomeCode)
256+
setFile(files, mainFile, welcomeCode)
257257
}
258258
for (const filename in newFiles) {
259-
files[filename] = new File(filename, newFiles[filename])
259+
setFile(files, filename, newFiles[filename])
260260
}
261261
for (const file in files) {
262262
await compileFile(this, files[file])
@@ -360,6 +360,20 @@ export class ReplStore implements Store {
360360
}
361361
}
362362

363+
function setFile(
364+
files: Record<string, File>,
365+
filename: string,
366+
content: string
367+
) {
368+
// prefix user files with src/
369+
// for cleaner Volar path completion when using Monaco editor
370+
const normalized =
371+
filename !== importMapFile && !filename.startsWith('src/')
372+
? `src/${filename}`
373+
: filename
374+
files[normalized] = new File(normalized, content)
375+
}
376+
363377
function fixURL(url: string) {
364378
return url.replace('https://sfc.vuejs', 'https://play.vuejs')
365379
}

test/main.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ import { createApp, h, watchEffect } from 'vue'
22
import { Repl, ReplStore } from '../src'
33
import MonacoEditor from '../src/editor/MonacoEditor.vue'
44
// import CodeMirrorEditor from '../src/editor/CodeMirrorEditor.vue'
5-
65
;(window as any).process = { env: {} }
76

87
const App = {
98
setup() {
109
const query = new URLSearchParams(location.search)
11-
const store = new ReplStore({
10+
const store = ((window as any).store = new ReplStore({
1211
serializedState: location.hash.slice(1),
1312
showOutput: query.has('so'),
1413
outputMode: query.get('om') || 'preview',
@@ -18,7 +17,7 @@ const App = {
1817
defaultVueServerRendererURL: import.meta.env.PROD
1918
? undefined
2019
: `${location.origin}/src/vue-server-renderer-dev-proxy`
21-
})
20+
}))
2221

2322
watchEffect(() => history.replaceState({}, '', store.serialize()))
2423

@@ -38,6 +37,7 @@ const App = {
3837
// store.setVueVersion('3.2.8')
3938

4039
return () =>
40+
//@ts-ignore
4141
h(Repl, {
4242
store,
4343
editor: MonacoEditor,

0 commit comments

Comments
 (0)