Skip to content
This repository has been archived by the owner on Aug 16, 2022. It is now read-only.

Commit

Permalink
feat: add ts typings
Browse files Browse the repository at this point in the history
  • Loading branch information
znck committed Oct 7, 2017
1 parent 19be9bf commit 8cc4f5f
Show file tree
Hide file tree
Showing 6 changed files with 247 additions and 58 deletions.
154 changes: 154 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
declare module VueComponentCompiler {
/**
* Parse SFC file into block descriptors.
*
* @param content Contents of the SFC.
* @param filename Filepath (used for cache key & in generated source maps)
*/
export function parse(content: string, filename: string, config: ParserConfig): SFCDescriptor

/**
* Compile styles for SFC
*
* @param styles List of styles to process.
* @param filename SFC file path
*/
export function compileStyles(styles: Array<StyleCompilerSource>, filename: string, config: StyleCompilerConfig): Promise<Array<StyleCompilerOutput>>

/**
* Compile template to render functions
*
* @param template Template to compile
* @param filename SFC file path
*/
export function compileTemplate(template: TemplateCompilerSource, filename: string, config: TemplateCompilerConfig): Promise<TemplateCompilerOutput>

export function assemble(source: AssemblerSource, filename: string, config: AssemblerConfig): string

type ParserConfig = {
needMap: boolean
}

type SFCDescriptor = {
script: ScriptDescriptor
styles: Array<StyleDescriptor>
template: TemplateDescriptor
customBlocks: Array<BlockDescriptor>
}

type BlockDescriptor = {
type: string // tag
content: string
start: number
end: number
attrs: Array<{ name: string, value: string | boolean}>
}

type StyleDescriptor = BlockDescriptor & {
scoped?: boolean
module?: string | boolean
lang?: string
src?: string
}

type ScriptDescriptor = BlockDescriptor & {
lang?: string
src?: string
}

type TemplateDescriptor = BlockDescriptor & {
lang?: string
src?: string
}

type CompilerSource = {
code: string
map?: object // prev source map
}

type StyleCompilerSource = CompilerSource & {
descriptor: StyleDescriptor
}

type StyleCompilerConfig = {
scopeId: string // used for scoped styles.
needMap?: boolean
plugins?: Array<object> // postcss plugins
options?: object // postcss options
onWarn?: MessageHandler
}

type MessageHandler = (message: Message) => void

type Message = {
type: string
text?: string
}

type CompilerOutput = {
code: string,
map?: object
}

type StyleCompilerOutput = CompilerOutput & {}

type TemplateCompilerSource = CompilerSource & {
descriptor: TemplateDescriptor
}

type TemplateCompilerConfig = {
isHot?: boolean // false
isServer?: boolean // false
isProduction?: boolean // true
optimizeSSR?: boolean // true
buble: object // see https://github.com/vuejs/vue-template-es2015-compiler/blob/master/index.js#L6
options?: {
preserveWhitspace?: boolean // true
}
transformToRequire?: object
plugins?: Array<Function>
}

type TemplateCompilerOutput = CompilerOutput & {
errors: Array<object>
tips: Array<object>
}

type AssemblerSource = {
script: {
id: string,
descriptor: ScriptDescriptor
}
styles: Array<{
id: string
hotPath: string
descriptor: StyleDescriptor
}>
render: {
id: string
descriptor: TemplateDescriptor
}
customBlocks: Array<{
id: string
descriptor: BlockDescriptor
}>
}

type AssemblerConfig = {
hashKey?: string
esModule?: boolean // true
shortFilePath?: string // = filename
require?: {
vueHotReloadAPI?: string // vue-hot-reload-api
normalizeComponent?: string // vue-component-compiler/src/normalize-component.js
}
moduleId: string // same as scopeId of style compiler.
moduleIdentifier?: string // autogenerated
isHot?: boolean // false
isServer?: boolean // false
isProduction?: boolean // true
isInjectable?: boolean // false
hasStyleInjectFn?: boolean // false
onWarn?: MessageHandler // console.warn
}
}
51 changes: 26 additions & 25 deletions src/assemble.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,12 @@
const defaultsDeep = require('lodash.defaultsdeep')
const defaults = require('lodash.defaultsdeep')
const hash = require('hash-sum')

const genId = require('./gen-id')

const DISPOSED = 'disposed'
const INJECT_STYLE_FN = 'injectStyle'
const CSS_MODULES = 'cssModules'

const defaultConfig = {
esModule: true,
filename: null,
shortFilePath: null,
require: {
vueHotReloadAPI: 'vue-hot-reload-api',
normalizeComponent: 'vue-component-compiler/src/normalize-component.js'
},
moduleId: null, // css scope id.
moduleIdentifier: null, // require for server.
isHot: false,
isServer: false,
isProduction: true,
isInjectable: false,
hasStyleInjectFn: false,
onWarn () {}
}

function _s (any) {
return JSON.stringify(any)
}
Expand All @@ -41,13 +26,29 @@ function __vue_type__ (type, id, esModule, addPrefix = true) {
return output
}

module.exports = function assemble (script, render, styles, customBlocks, config) {
module.exports = function assemble (source, filename, config) {
config = defaults(config, {
esModule: true,
shortFilePath: filename,
require: {
vueHotReloadAPI: 'vue-hot-reload-api',
normalizeComponent: 'vue-component-compiler/src/normalize-component.js'
},
moduleId: null,
moduleIdentifier: config.moduleIdentifier || hash(_s({ filename, config })), // require for server. TODO: verify this is correct.
isHot: false,
isServer: false,
isProduction: true,
isInjectable: false,
hasStyleInjectFn: false,
onWarn: message => console.warn(message)
})

let output = ''
const { script, render, styles, customBlocks } = source
const needsHotReload = !config.isProduction && config.isHot
const hasScoped = styles.some(style => style.descriptor.scoped)

config = defaultsDeep({}, config, defaultConfig)

if (config.isInjectable) config.esModule = false

if (needsHotReload) output += `var ${DISPOSED} = false\n`
Expand Down Expand Up @@ -96,7 +97,7 @@ module.exports = function assemble (script, render, styles, customBlocks, config
` var oldLocals = ${CSS_MODULES}[${MODULE_KEY}]\n` +
` if (!oldLocals) return\n` +
// 2. re-import (side effect: updates the <style>)
` var newLocals = ${style.code}\n` +
` var newLocals = ${requireString}\n` +
// 3. compare new and old locals to see if selectors changed
` if (JSON.stringify(newLocals) === JSON.stringify(oldLocals)) return\n` +
// 4. locals changed. Update and force re-render.
Expand Down Expand Up @@ -166,11 +167,11 @@ module.exports = function assemble (script, render, styles, customBlocks, config
'console.error("named exports are not supported in *.vue files.")' +
'}\n'
// check functional components used with templates
if (render.code) {
if (render.id) {
output +=
'if (Component.options.functional) {' +
'console.error("' +
'[vue-loader] ' + config.filename + ': functional components are not ' +
'[vue-loader] ' + filename + ': functional components are not ' +
'supported with templates, they should use render functions.' +
'")}\n'
}
Expand Down
13 changes: 13 additions & 0 deletions src/gen-id.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// utility for generating a uid for each component file
// used in scoped CSS rewriting
var path = require('path')
var hash = require('hash-sum')
var cache = Object.create(null)
var sepRE = new RegExp(path.sep.replace('\\', '\\\\'), 'g')

module.exports = function genId (file, context, key) {
var contextPath = context.split(path.sep)
var rootId = contextPath[contextPath.length - 1]
file = rootId + '/' + path.relative(context, file).replace(sepRE, '/') + (key || '')
return cache[file] || (cache[file] = hash(file))
}
7 changes: 5 additions & 2 deletions src/parser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const compiler = require('vue-template-compiler')
const defaults = require('lodash.defaultsdeep')
const LRU = require('lru-cache')
const hash = require('hash-sum')
const { SourceMapGenerator } = require('source-map')
Expand All @@ -7,14 +8,16 @@ const cache = LRU(100)
const splitRE = /\r?\n/g
const emptyRE = /^(?:\/\/)?\s*$/

module.exports = function (content, filename, needMap) {
module.exports = function (content, filename, config) {
config = defaults(config, { needMap: true })

const cacheKey = hash(filename + content)
const filenameWithHash = filename + '?' + cacheKey // source-map cache busting for hot-reloadded modules

if (cache.has(cacheKey)) return cache.get(cacheKey)

const output = compiler.parseComponent(content, { pad: 'line' })
if (needMap) {
if (config.needMap) {
if (output.script && !output.script.src) {
output.script.map = generateSourceMap(
filenameWithHash,
Expand Down
43 changes: 21 additions & 22 deletions src/style-compiler/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
const postcss = require('postcss')
const defaults = require('lodash.defaultsdeep')

const trim = require('./plugins/trim')
const scopeId = require('./plugins/scope-id')

/**
* Compile SFC style.
*
* @param {CompilerSource} style
* @param {string} filename
* @param {{plugins: object, options: object, needMap: boolean}} config
*/
function compileStyle (style, filename, config = {}) {
const plugins = [trim].concat(config.plugins || [])
function compileStyle (style, filename, config) {
const plugins = [trim].concat(config.plugins)
const options = Object.assign({
to: filename,
from: filename,
map: false
from: filename
}, config.options)

// source map
if (config.needMap && !options.map) {
if (config.needMap && !style.map) {
if (!style.map) {
throw Error('Previous source map is missing.')
}
Expand All @@ -43,17 +36,23 @@ function compileStyle (style, filename, config = {}) {
plugins.push(scopeId({ id: config.scopeId }))
}

return postcss(plugins).process(style.code, options)
return postcss(plugins).process(style.code, options).then(result => {
const output = { code: result.css }

if (config.needMap) { output.map = result.map }
result.warnings().forEach(warning => config.onWarn(warning))

return output
})
}

module.exports =
/**
* Compile SFC styles.
*
* @param {CompilerSource[]} styles
* @param {string} filename
* @param {{plugins: object, options: object, needMap: boolean}} config
*/
function compileStyles (styles, filename, config) {
module.exports = function compileStyles (styles, filename, config) {
config = defaults(config, {
needMap: true,
plugins: [],
options: {},
onWarn: message => console.warn(message)
})

return Promise.all(styles.map(style => compileStyle(style, filename, config)))
}
Loading

0 comments on commit 8cc4f5f

Please sign in to comment.