Commit 8badc35f authored by Evan You's avatar Evan You

init

parents
{
"presets": [
["env", { "modules": false }]
]
}
.DS_Store
test
node_modules
*.log
module.exports = function assemble (sourcePath, descriptor) {
const templateImport = descriptor.template
? `import { render, staticRenderFns } from '${sourcePath}?template'`
: ``
const scriptImport = descriptor.script
? `import script from '${sourcePath}?script'`
: `const script = {}`
const styleImports = descriptor.styles.map((_, i) => {
return `import style${i} from '${sourcePath}?style&index=${i}'`
}).join('\n')
return `
${templateImport}
${scriptImport}
${styleImports}
script.render = render
script.staticRenderFns = staticRenderFns
export default script
`.trim()
}
const path = require('path')
const hash = require('hash-sum')
const qs = require('querystring')
const parse = require('./parse')
const assemble = require('./assemble')
const loaderUtils = require('loader-utils')
const compileTemplate = require('./template-compiler')
module.exports = function (source) {
const { resourcePath, resourceQuery, target, minimize, sourceMap } = this
const isServer = target === 'node'
const isProduction = minimize || process.env.NODE_ENV === 'production'
const options = loaderUtils.getOptions(this) || {}
const fileName = path.basename(resourcePath)
const context = this.rootContext || process.cwd()
const sourceRoot = path.dirname(path.relative(context, resourcePath))
const shortFilePath = path.relative(context, resourcePath).replace(/^(\.\.[\\\/])+/, '')
const moduleId = 'data-v-' + hash(isProduction ? (shortFilePath + '\n' + content) : shortFilePath)
const needCssSourceMap = !isProduction && sourceMap && options.cssSourceMap !== false
const descriptor = parse(
source,
fileName,
sourceRoot,
sourceMap,
needCssSourceMap
)
if (resourceQuery) {
const query = qs.parse(resourceQuery.slice(1))
// template
if (query.template != null) {
return compileTemplate(descriptor, this, moduleId)
}
// script
if (query.script != null) {
this.callback(
null,
descriptor.script.content,
descriptor.script.map
)
return
}
// styles
if (query.style != null && query.index != null) {
return descriptor.styles[query.index].content
}
}
// assemble
return assemble(resourcePath, descriptor)
}
const hash = require('hash-sum')
const cache = require('lru-cache')(100)
const compiler = require('vue-template-compiler')
const SourceMapGenerator = require('source-map').SourceMapGenerator
const splitRE = /\r?\n/g
const emptyRE = /^(?:\/\/)?\s*$/
module.exports = (content, filename, sourceRoot, needMap, needCSSMap) => {
const cacheKey = hash(filename + content)
let output = cache.get(cacheKey)
if (output) return output
output = compiler.parseComponent(content, { pad: 'line' })
if (needMap) {
if (output.script && !output.script.src) {
output.script.map = generateSourceMap(
filename,
content,
output.script.content,
sourceRoot
)
}
if (needCSSMap && output.styles) {
output.styles.forEach(style => {
if (!style.src) {
style.map = generateSourceMap(
filename,
content,
style.content,
sourceRoot
)
}
})
}
}
cache.set(cacheKey, output)
return output
}
function generateSourceMap (filename, source, generated, sourceRoot) {
const map = new SourceMapGenerator({ sourceRoot })
map.setSourceContent(filename, source)
generated.split(splitRE).forEach((line, index) => {
if (!emptyRE.test(line)) {
map.addMapping({
source: filename,
original: {
line: index + 1,
column: 0
},
generated: {
line: index + 1,
column: 0
}
})
}
})
return map.toJSON()
}
const prettier = require('prettier')
const loaderUtils = require('loader-utils')
const compiler = require('vue-template-compiler')
const transpile = require('vue-template-es2015-compiler')
const transformRequire = require('./modules/transform-require')
const transformSrcset = require('./modules/transform-srcset')
const hotReloadAPIPath = require.resolve('vue-hot-reload-api')
module.exports = function compileTemplate (descriptor, loaderContext, id) {
const sourceTemplate = descriptor.template.content
const isServer = loaderContext.target === 'node'
const isProduction = loaderContext.minimize || process.env.NODE_ENV === 'production'
const options = loaderUtils.getOptions(loaderContext) || {}
const needsHotReload = !isServer && !isProduction && options.hotReload !== false
const defaultModules = [transformRequire(options.transformToRequire), transformSrcset()]
const hasScoped = descriptor.styles.some(({ scoped }) => scoped)
const templateAttrs = descriptor.template.attrs || {}
const hasComment = !!templateAttrs.comments
const hasFunctionalTemplate = !!templateAttrs.functional
const compilerOptions = {
preserveWhitespace: options.preserveWhitespace,
modules: defaultModules.concat(options.compilerModules || []),
directives: options.compilerDirectives || {},
scopeId: hasScoped ? id : null,
comments: hasComment
}
const compile =
isServer && compiler.ssrCompile && options.optimizeSSR !== false
? compiler.ssrCompile
: compiler.compile
const compiled = compile(sourceTemplate, compilerOptions)
// tips
if (compiled.tips && compiled.tips.length) {
compiled.tips.forEach(tip => {
loaderContext.emitWarning(tip)
})
}
let code
if (compiled.errors && compiled.errors.length) {
loaderContext.emitError(
`\n Error compiling template:\n${pad(sourceTemplate)}\n` +
compiled.errors.map(e => ` - ${e}`).join('\n') +
'\n'
)
code =
`export var render = function () {}\n` +
`export var staticRenderFns = []`
} else {
const bubleOptions = options.buble || {
transforms: {
stripWithFunctional: hasFunctionalTemplate
}
}
const staticRenderFns = compiled.staticRenderFns.map(fn =>
toFunction(fn, hasFunctionalTemplate)
)
code =
transpile(
'var render = ' +
toFunction(compiled.render, hasFunctionalTemplate) +
'\n' +
'var staticRenderFns = [' +
staticRenderFns.join(',') +
']',
bubleOptions
) + '\n'
// prettify render fn
if (!isProduction) {
code = prettier.format(code, { semi: false })
}
// mark with stripped (this enables Vue to use correct runtime proxy detection)
if (!isProduction && bubleOptions.transforms.stripWith !== false) {
code += `render._withStripped = true\n`
}
code += `export { render, staticRenderFns }`
}
// hot-reload
if (needsHotReload) {
code +=
'\nif (module.hot) {\n' +
' module.hot.accept()\n' +
' if (module.hot.data) {\n' +
' require("' + hotReloadAPIPath + '")' +
' .rerender("' + options.id + '", { render: render, staticRenderFns: staticRenderFns })\n' +
' }\n' +
'}'
}
return code
}
function toFunction (code, hasFunctionalTemplate) {
return (
'function (' + (hasFunctionalTemplate ? '_h,_vm' : '') + ') {' + code + '}'
)
}
function pad (sourceTemplate) {
return sourceTemplate
.split(/\r?\n/)
.map(line => ` ${line}`)
.join('\n')
}
// vue compiler module for transforming `<tag>:<attribute>` to `require`
const urlToRequire = require('../url-to-require')
const defaultOptions = {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
module.exports = userOptions => {
const options = userOptions
? Object.assign({}, defaultOptions, userOptions)
: defaultOptions
return {
postTransformNode: node => {
transform(node, options)
}
}
}
function transform (node, options) {
for (const tag in options) {
if ((tag === '*' || node.tag === tag) && node.attrs) {
const attributes = options[tag]
if (typeof attributes === 'string') {
node.attrs.some(attr => rewrite(attr, attributes))
} else if (Array.isArray(attributes)) {
attributes.forEach(item => node.attrs.some(attr => rewrite(attr, item)))
}
}
}
}
function rewrite (attr, name) {
if (attr.name === name) {
const value = attr.value
// only transform static URLs
if (value.charAt(0) === '"' && value.charAt(value.length - 1) === '"') {
attr.value = urlToRequire(value.slice(1, -1))
return true
}
}
}
// vue compiler module for transforming `img:srcset` to a number of `require`s
const urlToRequire = require('../url-to-require')
module.exports = () => ({
postTransformNode: node => {
transform(node)
}
})
function transform (node) {
const tags = ['img', 'source']
if (tags.indexOf(node.tag) !== -1 && node.attrs) {
node.attrs.forEach(attr => {
if (attr.name === 'srcset') {
// same logic as in transform-require.js
const value = attr.value
const isStatic = value.charAt(0) === '"' && value.charAt(value.length - 1) === '"'
if (!isStatic) {
return
}
// http://w3c.github.io/html/semantics-embedded-content.html#ref-for-image-candidate-string-5
const escapedSpaceCharacters = /( |\\t|\\n|\\f|\\r)+/g
const imageCandidates = value.substr(1, value.length - 2).split(',').map(s => {
// The attribute value arrives here with all whitespace, except normal spaces, represented by escape sequences
const [url, descriptor] = s.replace(escapedSpaceCharacters, ' ').trim().split(' ', 2)
return { require: urlToRequire(url), descriptor: descriptor }
})
// "require(url1)"
// "require(url1) 1x"
// "require(url1), require(url2)"
// "require(url1), require(url2) 2x"
// "require(url1) 1x, require(url2)"
// "require(url1) 1x, require(url2) 2x"
const code = imageCandidates.map(
({ require, descriptor }) => `${require} + "${descriptor ? ' ' + descriptor : ''}, " + `
).join('').slice(0, -6).concat('"').replace(/ \+ ""$/, '')
attr.value = code
}
})
}
}
module.exports = function urlToRequire (url) {
// same logic as in transform-require.js
const firstChar = url.charAt(0)
if (firstChar === '.' || firstChar === '~' || firstChar === '@') {
if (firstChar === '~') {
const secondChar = url.charAt(1)
url = url.slice(secondChar === '/' ? 2 : 1)
}
return `require("${url}")`
} else {
return `"${url}"`
}
}
{
"name": "vue-loader-next",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "webpack --config test/webpack.config.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.6.1",
"css-loader": "^0.28.11",
"vue": "^2.5.16",
"vue-template-compiler": "^2.5.16",
"webpack": "^4.1.0",
"webpack-cli": "^2.0.10"
},
"dependencies": {
"hash-sum": "^1.0.2",
"loader-utils": "^1.1.0",
"lru-cache": "^4.1.2",
"prettier": "^1.11.1",
"source-map": "^0.7.2",
"vue-component-compiler": "vuejs/vue-component-compiler#master",
"vue-hot-reload-api": "^2.3.0",
"vue-style-loader": "^4.0.2",
"vue-template-es2015-compiler": "^1.6.0"
}
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment