Skip to content

Commit

Permalink
feat: transformer
Browse files Browse the repository at this point in the history
close #16
  • Loading branch information
sxzz committed Dec 12, 2022
1 parent 4af73cb commit 52610a5
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 47 deletions.
6 changes: 3 additions & 3 deletions src/editor/FileSelector.vue
Expand Up @@ -48,9 +48,9 @@ function doneAddFile() {
if (!pending.value) return
const filename = pendingFilename.value
if (!/\.(vue|js|ts|css)$/.test(filename)) {
if (!store.supportedLanguages.some((ext) => filename.endsWith(ext))) {
store.state.errors = [
`Playground only supports *.vue, *.js, *.ts, *.css files.`
`Playground only supports ${store.supportedLanguages.join(', ')} files.`,
]
return
}
Expand All @@ -73,7 +73,7 @@ function horizontalScroll(e: WheelEvent) {
Math.abs(e.deltaX) >= Math.abs(e.deltaY) ? e.deltaX : e.deltaY
const distance = 30 * (direction > 0 ? 1 : -1)
el.scrollTo({
left: el.scrollLeft + distance
left: el.scrollLeft + distance,
})
}
</script>
Expand Down
32 changes: 24 additions & 8 deletions src/store.ts
Expand Up @@ -5,7 +5,7 @@ import { utoa, atou } from './utils'
import {
SFCScriptCompileOptions,
SFCAsyncStyleCompileOptions,
SFCTemplateCompileOptions
SFCTemplateCompileOptions,
} from 'vue/compiler-sfc'
import { OutputModes } from './output/types'

Expand All @@ -31,7 +31,7 @@ export class File {
compiled = {
js: '',
css: '',
ssr: ''
ssr: '',
}

constructor(filename: string, code = '', hidden = false) {
Expand All @@ -58,6 +58,14 @@ export interface SFCOptions {
template?: SFCTemplateCompileOptions
}

export interface TransformerOptions {
code: string
filename: string
file: File
stage: 'pre' | 'post'
ssr: boolean
}

export interface Store {
state: StoreState
options?: SFCOptions
Expand All @@ -70,6 +78,8 @@ export interface Store {
getImportMap: () => any
initialShowOutput: boolean
initialOutputMode: OutputModes
supportedLanguages: string[]
transformer?(options: TransformerOptions): undefined | string
}

export interface StoreOptions {
Expand All @@ -88,6 +98,7 @@ export class ReplStore implements Store {
options?: SFCOptions
initialShowOutput: boolean
initialOutputMode: OutputModes
supportedLanguages = ['vue', 'js', 'ts', 'css', 'pug']

private defaultVueRuntimeURL: string
private defaultVueServerRendererURL: string
Expand All @@ -98,7 +109,7 @@ export class ReplStore implements Store {
defaultVueRuntimeURL = `https://unpkg.com/@vue/runtime-dom@${version}/dist/runtime-dom.esm-browser.js`,
defaultVueServerRendererURL = `https://unpkg.com/@vue/server-renderer@${version}/dist/server-renderer.esm-browser.js`,
showOutput = false,
outputMode = 'preview'
outputMode = 'preview',
}: StoreOptions = {}) {
let files: StoreState['files'] = {}

Expand All @@ -109,7 +120,7 @@ export class ReplStore implements Store {
}
} else {
files = {
[defaultMainFile]: new File(defaultMainFile, welcomeCode)
[defaultMainFile]: new File(defaultMainFile, welcomeCode),
}
}

Expand All @@ -129,7 +140,7 @@ export class ReplStore implements Store {
errors: [],
vueRuntimeURL: this.defaultVueRuntimeURL,
vueServerRendererURL: this.defaultVueServerRendererURL,
resetFlip: true
resetFlip: true,
})

this.initImportMap()
Expand Down Expand Up @@ -209,8 +220,8 @@ export class ReplStore implements Store {
JSON.stringify(
{
imports: {
vue: this.defaultVueRuntimeURL
}
vue: this.defaultVueRuntimeURL,
},
},
null,
2
Expand All @@ -236,7 +247,7 @@ export class ReplStore implements Store {
return JSON.parse(this.state.files['import-map.json'].code)
} catch (e) {
this.state.errors = [
`Syntax error in import-map.json: ${(e as Error).message}`
`Syntax error in import-map.json: ${(e as Error).message}`,
]
return {}
}
Expand Down Expand Up @@ -281,4 +292,9 @@ export class ReplStore implements Store {
this.forceSandboxReset()
console.info(`[@vue/repl] Now using default Vue version`)
}

transformer(options: TransformerOptions) {
console.log(options)
return undefined
}
}
123 changes: 87 additions & 36 deletions src/transform.ts
Expand Up @@ -4,7 +4,7 @@ import {
BindingMetadata,
shouldTransformRef,
transformRef,
CompilerOptions
CompilerOptions,
} from 'vue/compiler-sfc'
import { transform } from 'sucrase'
// @ts-ignore
Expand All @@ -14,22 +14,38 @@ export const COMP_IDENTIFIER = `__sfc__`

async function transformTS(src: string) {
return transform(src, {
transforms: ['typescript']
transforms: ['typescript'],
}).code
}

export async function compileFile(
store: Store,
{ filename, code, compiled }: File
) {
export async function compileFile(store: Store, file: File) {
function withTransformer(
code: string,
filename: string,
stage: 'pre' | 'post',
ssr = false
) {
if (!store.transformer) return code
try {
return store.transformer({ code, filename, file, stage, ssr }) || code
} catch (err: any) {
store.state.errors.push(err)
}
return code
}

store.state.errors = []

let { code } = file
const { filename, compiled } = file
if (!code.trim()) {
store.state.errors = []
return
}

code = withTransformer(code, filename, 'pre')

if (filename.endsWith('.css')) {
compiled.css = code
store.state.errors = []
compiled.css = withTransformer(code, filename, 'post')
return
}

Expand All @@ -40,34 +56,44 @@ export async function compileFile(
if (filename.endsWith('.ts')) {
code = await transformTS(code)
}
compiled.js = compiled.ssr = code
store.state.errors = []
compiled.js = withTransformer(code, filename, 'post')
compiled.ssr = withTransformer(code, filename, 'post', true)
return
}

if (!filename.endsWith('.vue')) {
store.state.errors = []
compiled.js = withTransformer(code, filename, 'post')
return
}

const id = hashId(filename)
const { errors, descriptor } = store.compiler.parse(code, {
filename,
sourceMap: true
sourceMap: true,
})
if (errors.length) {
store.state.errors = errors
return
}

const unsupportedStyle = descriptor.styles.find(
(s) => s.lang && !store.supportedLanguages.includes(s.lang)
)
if (unsupportedStyle) {
store.state.errors.push(
`lang="${unsupportedStyle.lang}" pre-processors for <style> are not supported.`
)
return
}

if (
descriptor.styles.some((s) => s.lang) ||
(descriptor.template && descriptor.template.lang)
descriptor.template &&
descriptor.template.lang &&
!store.supportedLanguages.includes(descriptor.template.lang)
) {
store.state.errors = [
`lang="x" pre-processors for <template> or <style> are currently not ` +
`supported.`
]
store.state.errors.push(
`lang="${descriptor.template.lang}" pre-processors for <template> are not supported.`
)
return
}

Expand All @@ -76,7 +102,7 @@ export async function compileFile(
(descriptor.scriptSetup && descriptor.scriptSetup.lang)
const isTS = scriptLang === 'ts'
if (scriptLang && !isTS) {
store.state.errors = [`Only lang="ts" is supported for <script> blocks.`]
store.state.errors.push(`Only lang="ts" is supported for <script> blocks.`)
return
}

Expand Down Expand Up @@ -128,27 +154,48 @@ export async function compileFile(
descriptor.template &&
(!descriptor.scriptSetup || store.options?.script?.inlineTemplate === false)
) {
const clientTemplateResult = await doCompileTemplate(
const templateFilename = `${filename}.template.${
descriptor.template.lang || 'html'
}`
const source = withTransformer(
descriptor.template!.content,
templateFilename,
'pre'
)
let clientTemplateResult = await doCompileTemplate(
store,
descriptor,
source,
id,
bindings,
false,
isTS
)
clientTemplateResult = withTransformer(
clientTemplateResult || '',
templateFilename,
'post'
)
if (!clientTemplateResult) {
return
}
clientCode += clientTemplateResult

const ssrTemplateResult = await doCompileTemplate(
let ssrTemplateResult = await doCompileTemplate(
store,
descriptor,
source,
id,
bindings,
true,
isTS
)
ssrTemplateResult = withTransformer(
ssrTemplateResult || '',
templateFilename,
'post',
true
)
if (ssrTemplateResult) {
// ssr compile failure is fine
ssrCode += ssrTemplateResult
Expand All @@ -174,21 +221,23 @@ export async function compileFile(

// styles
let css = ''
for (const style of descriptor.styles) {
for (const [i, style] of descriptor.styles.entries()) {
if (style.module) {
store.state.errors = [
store.state.errors.push(
`<style module> is not supported in the playground.`
]
)
return
}

const styleFilename = `${filename}.${i}.${style.lang || 'css'}`
const source = withTransformer(style.content, styleFilename, 'pre')
const styleResult = await store.compiler.compileStyleAsync({
...store.options?.style,
source: style.content,
source,
filename,
id,
scoped: style.scoped,
modules: !!style.module
modules: !!style.module,
})
if (styleResult.errors.length) {
// postcss uses pathToFileURL which isn't polyfilled in the browser
Expand All @@ -198,7 +247,7 @@ export async function compileFile(
}
// proceed even if css compile errors
} else {
css += styleResult.code + '\n'
css += withTransformer(styleResult.code + '\n', styleFilename, 'post')
}
}
if (css) {
Expand All @@ -207,8 +256,9 @@ export async function compileFile(
compiled.css = '/* No <style> tags present */'
}

// clear errors
store.state.errors = []
compiled.js = withTransformer(compiled.js, filename, 'post')
compiled.ssr = withTransformer(compiled.ssr, filename, 'post', true)
compiled.css = withTransformer(compiled.css, filename + '.css', 'post')
}

async function doCompileScript(
Expand All @@ -233,9 +283,9 @@ async function doCompileScript(
ssrCssVars: descriptor.cssVars,
compilerOptions: {
...store.options?.template?.compilerOptions,
expressionPlugins
}
}
expressionPlugins,
},
},
})
let code = ''
if (compiledScript.bindings) {
Expand Down Expand Up @@ -270,14 +320,15 @@ async function doCompileScript(
async function doCompileTemplate(
store: Store,
descriptor: SFCDescriptor,
source: string,
id: string,
bindingMetadata: BindingMetadata | undefined,
ssr: boolean,
isTS: boolean
) {
const templateResult = store.compiler.compileTemplate({
...store.options?.template,
source: descriptor.template!.content,
source,
filename: descriptor.filename,
id,
scoped: descriptor.styles.some((s) => s.scoped),
Expand All @@ -288,8 +339,8 @@ async function doCompileTemplate(
compilerOptions: {
...store.options?.template?.compilerOptions,
bindingMetadata,
expressionPlugins: isTS ? ['typescript'] : undefined
}
expressionPlugins: isTS ? ['typescript'] : undefined,
},
})
if (templateResult.errors.length) {
store.state.errors = templateResult.errors
Expand Down

0 comments on commit 52610a5

Please sign in to comment.