Skip to content

Commit

Permalink
feat: setFiles & getFiles
Browse files Browse the repository at this point in the history
  • Loading branch information
sxzz committed Jan 12, 2024
1 parent 65e3aa2 commit e043ad1
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 70 deletions.
20 changes: 14 additions & 6 deletions src/import-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@ import { computed, version as currentVersion, ref } from 'vue'

export function useVueImportMap(
defaults: {
runtimeDev?: string
runtimeProd?: string
serverRenderer?: string
runtimeDev?: string | (() => string)
runtimeProd?: string | (() => string)
serverRenderer?: string | (() => string)
} = {},
) {
function normalizeDefaults(defaults?: string | (() => string)) {
if (!defaults) return
return typeof defaults === 'string' ? defaults : defaults()
}

const productionMode = ref(false)
const vueVersion = ref<string | undefined>()
const importMap = computed<ImportMap>(() => {
const vue =
(!vueVersion.value &&
(productionMode.value ? defaults.runtimeProd : defaults.runtimeDev)) ||
normalizeDefaults(
productionMode.value ? defaults.runtimeProd : defaults.runtimeDev,
)) ||
`https://cdn.jsdelivr.net/npm/@vue/runtime-dom@${
vueVersion.value || currentVersion
}/dist/runtime-dom.esm-browser${productionMode.value ? `.prod` : ``}.js`

const serverRenderer =
(!vueVersion.value && defaults.serverRenderer) ||
(!vueVersion.value && normalizeDefaults(defaults.serverRenderer)) ||
`https://cdn.jsdelivr.net/npm/@vue/server-renderer@${
vueVersion.value || currentVersion
}/dist/server-renderer.esm-browser.js`
Expand All @@ -32,8 +39,9 @@ export function useVueImportMap(

return {
productionMode,
vueVersion,
importMap,
vueVersion,
defaultVersion: currentVersion,
}
}

Expand Down
3 changes: 0 additions & 3 deletions src/output/Preview.vue
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,6 @@ watch(
},
)
// reset sandbox when version changes
// watch(() => store.resetFlip, createSandbox)
// reset theme
watch(
() => theme.value,
Expand Down
143 changes: 93 additions & 50 deletions src/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ export function useStore(
}: Partial<StoreState> = {},
serializedState?: string,
): ReplStore {
function setFile(filename: string, content: string) {
const normalized = addSrcPrefix(filename)
files.value[normalized] = new File(normalized, content)
function applyBuiltinImportMap() {
const importMap = mergeImportMap(builtinImportMap.value, getImportMap())
setImportMap(importMap)
}

const init = () => {
function init() {
watchEffect(() =>
compileFile(store, activeFile.value).then(
(errs) => (errors.value = errs),
Expand Down Expand Up @@ -86,9 +86,7 @@ export function useStore(
}
})

// init import map
const importMap = mergeImportMap(builtinImportMap.value, getImportMap())
setImportMap(importMap)
applyBuiltinImportMap()
watch(
builtinImportMap,
() => {
Expand All @@ -113,6 +111,20 @@ export function useStore(
}
}
}

function setImportMap(map: ImportMap) {
if (map.imports)
for (const [key, value] of Object.entries(map.imports)) {
if (value) {
map.imports![key] = fixURL(value)
}
}
files.value[importMapFile] = new File(
importMapFile,
JSON.stringify(map, undefined, 2),
)
}

const setActive: Store['setActive'] = (filename) => {
activeFile.value = files.value[filename]
}
Expand Down Expand Up @@ -191,14 +203,6 @@ export function useStore(
return {}
}
}
const getFiles = () => {
const exported: Record<string, string> = {}
for (const [filename, file] of Object.entries(files.value)) {
const normalized = stripSrcPrefix(filename)
exported[normalized] = file.code
}
return exported
}
const serialize: ReplStore['serialize'] = () => {
const files = getFiles()
const importMap = files[importMapFile]
Expand Down Expand Up @@ -227,37 +231,63 @@ export function useStore(
}
return '#' + utoa(JSON.stringify(files))
}
const deserialize = (serializedState: string) => {
const deserialize: ReplStore['deserialize'] = (serializedState: string) => {
if (serializedState.startsWith('#'))
serializedState = serializedState.slice(1)
const saved = JSON.parse(atou(serializedState))
for (const filename in saved) {
setFile(filename, saved[filename])
setFile(files.value, filename, saved[filename])
}
}
const setImportMap = (map: ImportMap) => {
if (map.imports)
for (const [key, value] of Object.entries(map.imports)) {
if (value) {
map.imports![key] = fixURL(value)
}
}
files.value[importMapFile] = new File(
importMapFile,
JSON.stringify(map, undefined, 2),
)
const getFiles: ReplStore['getFiles'] = () => {
const exported: Record<string, string> = {}
for (const [filename, file] of Object.entries(files.value)) {
const normalized = stripSrcPrefix(filename)
exported[normalized] = file.code
}
return exported
}
const setFiles: ReplStore['setFiles'] = async (
newFiles,
mainFile = store.mainFile,
) => {
const files: Record<string, File> = Object.create(null)

mainFile = addSrcPrefix(mainFile)
if (!newFiles[mainFile]) {
setFile(files, mainFile, template.value.welcomeSFC || welcomeSFCCode)
}
for (const [filename, file] of Object.entries(newFiles)) {
setFile(files, filename, file)
}

const errors = []
for (const file of Object.values(files)) {
errors.push(...(await compileFile(store, file)))
}

store.mainFile = mainFile
store.files = files
store.errors = errors
applyBuiltinImportMap()
setActive(store.mainFile)
}

if (serializedState) {
deserialize(serializedState)
} else {
setFile(mainFile.value, template.value.welcomeSFC || welcomeSFCCode)
setFile(
files.value,
mainFile.value,
template.value.welcomeSFC || welcomeSFCCode,
)
}
activeFile ||= ref(
files.value[mainFile.value] || Object.values(files.value)[0],
)
if (!files.value[mainFile.value]) {
mainFile.value = Object.keys(files.value)[0]
}
activeFile ||= ref(files.value[mainFile.value])

const store = reactive({
const store: ReplStore = reactive({
files,
activeFile,
mainFile,
Expand Down Expand Up @@ -285,6 +315,8 @@ export function useStore(
getTsConfig,
serialize,
deserialize,
getFiles,
setFiles,
})
return store
}
Expand Down Expand Up @@ -313,14 +345,21 @@ export interface SFCOptions {
export type StoreState = ToRefs<{
files: Record<string, File>
activeFile: File
errors: (string | Error)[]
mainFile: string
template: {
welcomeSFC?: string
newSFC?: string
}
builtinImportMap: ImportMap

// output
errors: (string | Error)[]
showOutput: boolean
outputMode: OutputModes
sfcOptions: SFCOptions
/** `@vue/compiler-sfc` */
compiler: typeof defaultCompiler
/* only apply for compiler-sfc */
vueVersion: string | undefined

// volar-related
Expand All @@ -329,31 +368,27 @@ export type StoreState = ToRefs<{
/** \{ dependencyName: version \} */
dependencyVersion: Record<string, string>
reloadLanguageTools?: (() => void) | undefined

mainFile: string
template: {
welcomeSFC?: string
newSFC?: string
}

builtinImportMap: ImportMap
}>

export interface ReplStore extends UnwrapRef<StoreState> {
init: () => void
setActive: (filename: string) => void
addFile: (filename: string | File) => void
deleteFile: (filename: string) => void
renameFile: (oldFilename: string, newFilename: string) => void
getImportMap: () => ImportMap
getTsConfig?: () => Record<string, any>
init(): void
setActive(filename: string): void
addFile(filename: string | File): void
deleteFile(filename: string): void
renameFile(oldFilename: string, newFilename: string): void
getImportMap(): ImportMap
getTsConfig(): Record<string, any>
serialize(): string
deserialize(serializedState: string): void
getFiles(): Record<string, string>
setFiles(newFiles: Record<string, string>, mainFile?: string): Promise<void>
}

export type Store = Pick<
ReplStore,
| 'files'
| 'activeFile'
| 'mainFile'
| 'errors'
| 'showOutput'
| 'outputMode'
Expand All @@ -364,7 +399,6 @@ export type Store = Pick<
| 'typescriptVersion'
| 'dependencyVersion'
| 'reloadLanguageTools'
| 'mainFile'
| 'init'
| 'setActive'
| 'addFile'
Expand Down Expand Up @@ -420,3 +454,12 @@ export function stripSrcPrefix(file: string) {
function fixURL(url: string) {
return url.replace('https://sfc.vuejs', 'https://play.vuejs')
}

function setFile(
files: Record<string, File>,
filename: string,
content: string,
) {
const normalized = addSrcPrefix(filename)
files[normalized] = new File(normalized, content)
}
22 changes: 11 additions & 11 deletions test/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,17 @@ const App = {
watchEffect(() => history.replaceState({}, '', store.serialize()))

// setTimeout(() => {
// store.setFiles(
// {
// 'index.html': '<h1>yo</h1>',
// 'main.js': 'document.body.innerHTML = "<h1>hello</h1>"',
// 'foo.js': 'document.body.innerHTML = "<h1>hello</h1>"',
// 'bar.js': 'document.body.innerHTML = "<h1>hello</h1>"',
// 'baz.js': 'document.body.innerHTML = "<h1>hello</h1>"'
// },
// 'index.html'
// )
// }, 1000);
// store.setFiles(
// {
// 'src/index.html': '<h1>yo</h1>',
// 'src/main.js': 'document.body.innerHTML = "<h1>hello</h1>"',
// 'src/foo.js': 'document.body.innerHTML = "<h1>hello</h1>"',
// 'src/bar.js': 'document.body.innerHTML = "<h1>hello</h1>"',
// 'src/baz.js': 'document.body.innerHTML = "<h1>hello</h1>"',
// },
// 'src/index.html',
// )
// }, 1000)

store.vueVersion = '3.4.1'

Expand Down

0 comments on commit e043ad1

Please sign in to comment.