Files
supabase/apps/studio/lib/eszip-parser.ts
Lakshan Perera a66278b811 Use relative paths in File editor and support existing entrypoint, import map settings (#34553)
* fix: add jsr:@std/path module

* fix: use relative paths for files in editor

* Smol fix

---------

Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
2025-03-31 15:23:12 +08:00

132 lines
3.4 KiB
TypeScript

import { Parser } from '@deno/eszip'
import path from 'path'
function url2path(url: string) {
try {
// Parse the URL
return new URL(url).pathname
} catch (error) {
// If URL parsing fails, fallback to extracting just the filename
console.warn('Failed to parse URL:', url)
try {
// Try to extract just the filename part
const parts = url.split('/').filter(Boolean)
if (parts.length > 0) {
return parts[parts.length - 1] // Return just the filename
}
} catch (e) {
// Last resort: use the original path joining
console.error('Failed to extract filename:', e)
}
return path.join(...new URL(url).pathname.split('/').filter(Boolean))
}
}
// Initialize parser outside of request handler
let parserPromise: Promise<any> | null = null
async function getParser() {
if (!parserPromise) {
parserPromise = Parser.createInstance().catch((err) => {
console.error('Failed to create parser instance:', err)
parserPromise = null
throw err
})
}
return parserPromise
}
export async function parseEszip(bytes: Uint8Array) {
try {
const parser = await getParser()
if (!parser) {
throw new Error('Failed to initialize parser')
}
// Parse bytes in a try-catch block
let specifiers: string[] = []
try {
specifiers = await parser.parseBytes(bytes)
} catch (parseError) {
console.error('Error parsing bytes:', parseError)
// Reset parser on parse error
parserPromise = null
throw parseError
}
// Load in a separate try-catch
try {
await parser.load()
} catch (loadError) {
console.error('Error loading parser:', loadError)
parserPromise = null
throw loadError
}
// Extract files from the eszip
const files = await extractEszip(parser, specifiers)
// Convert files to the expected format
const responseFiles = await Promise.all(
files.map(async (file) => {
const content = await file.text()
return {
name: file.name,
content: content,
}
})
)
return responseFiles
} catch (error) {
console.error('Error in parseEszip:', error)
throw error
}
}
async function extractEszip(parser: any, specifiers: string[]) {
const files = []
// First, filter out the specifiers we want to keep
const filteredSpecifiers = specifiers.filter((specifier) => {
const shouldSkip =
specifier.startsWith('---') ||
specifier.startsWith('npm:') ||
specifier.startsWith('static:') ||
specifier.startsWith('vfs:') ||
specifier.startsWith('https:') ||
specifier.startsWith('jsr:')
if (shouldSkip) {
console.log('Skipping specifier:', specifier)
} else {
console.log('Keeping specifier:', specifier)
}
return !shouldSkip
})
console.log('Filtered specifiers count:', filteredSpecifiers.length)
console.log('Filtered specifiers:', JSON.stringify(filteredSpecifiers))
// Then process each one
for (const specifier of filteredSpecifiers) {
try {
// Try to get the module source
const moduleSource = await parser.getModuleSource(specifier)
// Get the file path
const filePath = url2path(specifier)
// Create a file object
const file = new File([moduleSource], filePath)
files.push(file)
} catch (error) {
console.error('Error processing specifier:', specifier, error)
}
}
return files
}