diff --git a/README.md b/README.md
index f13e8bd..1ea0e21 100644
--- a/README.md
+++ b/README.md
@@ -58,6 +58,7 @@ interface DescriptorCompileResult {
 
 interface CompileResult {
   code: string
+  lang?: string
   map?: any
 }
 
diff --git a/src/assembler.ts b/src/assembler.ts
index 00e62c6..8996c4f 100644
--- a/src/assembler.ts
+++ b/src/assembler.ts
@@ -7,7 +7,7 @@ import * as path from 'path'
 
 export interface AssembleSource {
   filename: string
-  script?: { source: string; map?: any }
+  script?: { source: string; map?: any, lang?: string }
   template?: { source: string; functional?: boolean }
   styles: Array<{
     source: string
@@ -221,14 +221,14 @@ export function assembleFromSource(
       var styleElement = document.createElement('style')
       styleElement.type = 'text/css'
       shadowRoot.appendChild(styleElement)
-    
+
       return styleElement
-    }    
+    }
 
     return function addStyle(id, css) {
       const styleElement = createStyleElement(shadowRoot)
       if (css.media) styleElement.setAttribute('media', css.media)
-      
+
       let code = css.source
 
       if (${e(compiler.template.isProduction)} && css.map) {
@@ -241,7 +241,7 @@ export function assembleFromSource(
           btoa(unescape(encodeURIComponent(JSON.stringify(css.map)))) +
           ' */'
       }
-      
+
       if ('styleSheet' in styleElement) {
         styleElement.styleSheet.cssText = code
       } else {
@@ -306,7 +306,7 @@ export function assembleFromSource(
         component._ssrRegister = hook
       }
       else if (style) {
-        hook = shadowMode 
+        hook = shadowMode
           ? function(context) {
               style.call(this, createInjectorShadow(context, this.$root.$options.shadowRoot))
             }
diff --git a/src/compiler.ts b/src/compiler.ts
index ff14650..d34c253 100644
--- a/src/compiler.ts
+++ b/src/compiler.ts
@@ -47,6 +47,7 @@ export interface ScriptOptions {
 
 export interface CompileResult {
   code: string
+  lang?: string
   map?: any
 }
 export type StyleCompileResult = StyleCompileResults & {
@@ -113,7 +114,8 @@ export class SFCCompiler {
           code: rawScript.src
             ? this.read(rawScript.src, filename)
             : rawScript.content,
-          map: rawScript.map
+          map: rawScript.map,
+          lang: rawScript.lang
         }
       : undefined
 
@@ -159,7 +161,8 @@ export class SFCCompiler {
           code: rawScript.src
             ? this.read(rawScript.src, filename)
             : rawScript.content,
-          map: rawScript.map
+          map: rawScript.map,
+          lang: rawScript.lang
         }
       : undefined
 
diff --git a/test/__snapshots__/compile.spec.ts.snap b/test/__snapshots__/compile.spec.ts.snap
index 2bda97a..19a6f17 100644
--- a/test/__snapshots__/compile.spec.ts.snap
+++ b/test/__snapshots__/compile.spec.ts.snap
@@ -17,6 +17,7 @@ export default {
   }
 }
 ",
+    "lang": undefined,
     "map": Object {
       "file": "foo.vue",
       "mappings": ";;;;;;AAMA;AACA;AACA;AACA;AACA",
@@ -132,6 +133,7 @@ export default {
   }
 }
 ",
+    "lang": undefined,
     "map": Object {
       "file": "foo.vue",
       "mappings": ";;;;;;AAMA;AACA;AACA;AACA;AACA",
diff --git a/test/baseline.spec.ts b/test/baseline.spec.ts
index 32b0d1a..676a053 100644
--- a/test/baseline.spec.ts
+++ b/test/baseline.spec.ts
@@ -4,17 +4,25 @@ const { join, resolve } = require('path')
 import { build, open } from './setup/utils'
 
 let browser = null
+let finished = false
 const fixtures = readdirSync(join(__dirname, 'fixtures'))
   .filter(it => it.endsWith('.vue'))
   .map(it => it.replace(/\.vue$/i, ''))
 
-beforeAll(async () => {
+beforeAll(async function setup () {
+  if (finished) {
+    return
+  }
   browser = await puppeteer.launch({
     args: ['--no-sandbox', '--disable-setuid-sandbox'],
     headless: Boolean(process.env.CI)
   })
+  browser.on('disconnected', setup);
+})
+afterAll(async () => {
+  finished = true
+  browser && (await browser.close())
 })
-afterAll(async () => browser && (await browser.close()))
 
 fixtures.forEach(it =>
   test(it, async () => {
diff --git a/test/compile.spec.ts b/test/compile.spec.ts
index d6a6b85..3492824 100644
--- a/test/compile.spec.ts
+++ b/test/compile.spec.ts
@@ -61,6 +61,34 @@ function removeRawResult(result: DescriptorCompileResult): DescriptorCompileResu
   return result
 }
 
+
+test('detect script lang attribute', () => {
+  const source = `
+    <template>
+      <h1 id="test">Hello {{ name }}!</h1>
+    </template>
+
+    <script lang="ts">
+    export default {
+      data () {
+        return { name: 'John Doe' }
+      }
+    }
+    </script>
+
+    <style>
+    .title {
+      color: red;
+    }
+    </style>
+    `
+
+  const compiler = createDefaultCompiler()
+  const result = compiler.compileToDescriptor('foo.vue', source)
+
+  expect(result.script.lang).toBe('ts')
+})
+
 describe('when source contains css module', () => {
   const componentSource = `
     <template>
diff --git a/test/fixtures/with-langs.vue b/test/fixtures/with-langs.vue
index 40097e9..7fed54c 100644
--- a/test/fixtures/with-langs.vue
+++ b/test/fixtures/with-langs.vue
@@ -2,7 +2,7 @@
   h1#test.title Hello {{ name }}!
 </template>
 
-<script>
+<script lang="ts">
 export default {
   data () {
     return { name: 'John Doe' }