blob: 8635af0a8e64c8f09680d2765e9bbcc856b64071 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import loaderUtils from 'loader-utils'
import path from 'path'
import fs from 'fs'
import md5 from 'md5'
import * as config from './config'
import * as legacy from './legacy'
import {
parseFragment
}
from './parser'
import {
getNameByPath,
getRequireString,
stringifyLoaders
}
from './util'
import vueLoader from 'weex-vue-loader'
const loaderPath = __dirname
const defaultLoaders = {
none: '',
main: path.resolve(loaderPath, 'loader.js'),
extract: path.resolve(loaderPath, 'extract.js'),
template: path.resolve(loaderPath, 'template.js'),
style: path.resolve(loaderPath, 'style.js'),
script: path.resolve(loaderPath, 'script.js'),
json: path.resolve(loaderPath, 'json.js'),
babel: loadBabelModule('babel-loader')
}
function loadBabelModule (moduleName) {
try {
const filePath = require.resolve(moduleName)
return filePath.slice(0, filePath.lastIndexOf(moduleName) + moduleName.length)
}
catch (e) {
return moduleName
}
}
function getLoaderString (type, config) {
config = config || {}
let customLoader
let loaders
if (config.lang && config.customLang[config.lang]) {
customLoader = config.customLang[config.lang]
}
if (type === 'main') {
loaders = [{
name: defaultLoaders.main
}]
return stringifyLoaders(loaders)
}
if (type === 'element') {
loaders = [{
name: defaultLoaders.main,
query: {
element: config.source ? undefined : true
}
}]
if (!config.source) {
loaders.push({
name: defaultLoaders.extract,
query: {
index: config.name,
type: 'elements'
}
})
}
return stringifyLoaders(loaders)
}
if (type === 'template') {
loaders = [{
name: defaultLoaders.json
}, {
name: defaultLoaders.template
}]
if (customLoader) {
loaders = loaders.concat(customLoader)
}
if (!config.source) {
loaders.push({
name: defaultLoaders.extract,
query: {
type: 'template'
}
})
}
if (config.element) {
loaders.push({
name: defaultLoaders.extract,
query: {
index: config.elementName,
type: 'elements'
}
})
}
return stringifyLoaders(loaders)
}
if (type === 'style') {
loaders = [{
name: defaultLoaders.json
}, {
name: defaultLoaders.style
}]
if (customLoader) {
loaders = loaders.concat(customLoader)
}
if (!config.source) {
loaders.push({
name: defaultLoaders.extract,
query: {
index: 0,
type: 'styles'
}
})
}
if (config.element) {
loaders.push({
name: defaultLoaders.extract,
query: {
index: config.elementName,
type: 'elements'
}
})
}
return stringifyLoaders(loaders)
}
if (type === 'script') {
loaders = [{
name: defaultLoaders.script
}]
if (customLoader) {
loaders = loaders.concat(customLoader)
}
else {
loaders.push({
name: defaultLoaders.babel,
query: {
presets: [loadBabelModule('babel-preset-es2015')],
plugins: [loadBabelModule('babel-plugin-transform-runtime')],
comments: 'false'
}
})
}
if (!config.source) {
loaders.push({
name: defaultLoaders.extract,
query: {
index: 0,
type: 'scripts'
}
})
}
if (config.element) {
loaders.push({
name: defaultLoaders.extract,
query: {
index: config.elementName,
type: 'elements'
}
})
}
return stringifyLoaders(loaders)
}
if (type === 'config') {
loaders = [{
name: defaultLoaders.json
}]
if (!config.source) {
loaders.push({
name: defaultLoaders.extract,
query: {
type: 'config'
}
})
}
return stringifyLoaders(loaders)
}
if (type === 'data') {
loaders = [{
name: defaultLoaders.json
}]
if (!config.source) {
loaders.push({
name: defaultLoaders.extract,
query: {
type: 'data'
}
})
}
return stringifyLoaders(loaders)
}
}
function loader (source) {
this.cacheable && this.cacheable()
// Support *.vue files.
// If file extname is vue then go to `weex-vue-loader`.
if (path.extname(this.resourcePath).match(/\.vue/)) {
return vueLoader.call(this, source)
}
const options = this._compiler.options.weex || {}
const customLang = options.lang || {}
const loaderQuery = loaderUtils.getOptions(this) || {}
const resourceQuery = this.resourceQuery && loaderUtils.parseQuery(this.resourceQuery) || {}
const resourcePath = this.resourcePath
const isElement = loaderQuery.element
const isEntry = resourceQuery.entry
const filename = path.relative('.', resourcePath)
const name = isEntry ? md5(fs.readFileSync(filename)) : (resourceQuery.name || getNameByPath(resourcePath))
let output = ''
const frag = parseFragment(source)
const elementNames = []
if (frag.element.length) {
for (let i = 0; i < frag.element.length; i++) {
const element = frag.element[i]
if (!element.name) {
this.emitError('Element block need a name attribute')
return ''
}
elementNames.push(element.name)
let src = resourcePath
if (element.src) {
src = element.src
}
output += getRequireString(this, getLoaderString('element', {
customLang,
name: element.name,
source: element.src
}), `${src}?name=${element.name}`)
}
}
if (frag.deps.length) {
for (const dep of frag.deps) {
const filepath = path.resolve(path.dirname(resourcePath), `${dep}.we`)
if (elementNames.indexOf(dep) < 0 && fs.existsSync(filepath)) {
output += getRequireString(this, getLoaderString('none'), `./${dep}.we`)
}
}
}
if (!frag.template.length) {
this.emitError('Template block is required')
return ''
}
else {
const template = frag.template[0]
let src = resourcePath
if (template.src) {
src = template.src
}
output += 'var __weex_template__ = ' + getRequireString(this, getLoaderString('template', {
customLang,
lang: template.lang,
element: isElement,
elementName: isElement ? name : undefined,
source: template.src
}), src)
}
if (frag.style.length) {
const style = frag.style[0]
let src = resourcePath
if (style.src) {
src = style.src
}
output += 'var __weex_style__ = ' + getRequireString(this, getLoaderString('style', {
customLang,
lang: style.lang,
element: isElement,
elementName: isElement ? name : undefined,
source: style.src
}), src)
}
if (frag.script.length) {
const script = frag.script[0]
let src = resourcePath
if (script.src) {
src = script.src
}
output += 'var __weex_script__ = ' + getRequireString(this, getLoaderString('script', {
customLang,
lang: script.lang,
element: isElement,
elementName: isElement ? name : undefined,
source: script.src
}), src)
}
if (isEntry && frag.data.length) {
const data = frag.data[0]
let src = resourcePath
if (data.src) {
src = data.src
}
output += 'var __weex_data__ = ' + getRequireString(this, getLoaderString('data', {
source: data.src
}), src)
}
if (isEntry && frag.config.length) {
const config = frag.config[0]
let src = resourcePath
if (config.src) {
src = config.src
}
output += 'var __weex_config__ = ' + getRequireString(this, getLoaderString('config', {
source: config.src
}), src)
}
output += `
__weex_define__('@weex-component/${name}', [], function(__weex_require__, __weex_exports__, __weex_module__) {
` + (frag.script.length > 0 ? `
__weex_script__(__weex_module__, __weex_exports__, __weex_require__)
if (__weex_exports__.__esModule && __weex_exports__.default) {
__weex_module__.exports = __weex_exports__.default
}
` : '') + `
__weex_module__.exports.template = __weex_template__
` + (frag.style.length > 0 ? `
__weex_module__.exports.style = __weex_style__
` : '') + `
})
`
if (isEntry) {
output += `
__weex_bootstrap__('@weex-component/${name}'` + (frag.config.length > 0 ? `,__weex_config__` : ',undefined') + (frag.data.length > 0 ? `,__weex_data__` : ',undefined') + `)`
}
return output
}
loader.setLogLevel = level => {
config.logLevel = level
}
for (const key in legacy) {
loader[key] = legacy[key]
}
module.exports = loader