1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20import * as parse5 from 'parse5' 21import templater from './templater' 22import styler from './styler' 23import scripter from './scripter' 24import { checkTagName } from './templater/component_validator' 25 26import { 27 FUNC_START_REG, 28 FUNC_END_REG, 29 stringifyFunction 30} from './util' 31 32function getAttribute (node, name) { 33 if (node.attrs) { 34 let i = node.attrs.length 35 let attr 36 while (i--) { 37 attr = node.attrs[i] 38 if (attr.name === name) { 39 return attr.value 40 } 41 } 42 } 43} 44 45function extractDependencies (node, deps) { 46 if (node.childNodes) { 47 node.childNodes.forEach(child => { 48 checkTagName(child, { 49 result: {}, 50 deps, 51 log: [] 52 }) 53 extractDependencies(child, deps) 54 }) 55 } 56} 57 58export function parseFragment (source) { 59 const fragment = parse5.parseFragment(source, { 60 locationInfo: true 61 }) 62 63 const output = { 64 deps: [], 65 element: [], 66 template: [], 67 style: [], 68 script: [], 69 data: [], 70 config: [] 71 } 72 73 fragment.childNodes.forEach(node => { 74 let type 75 76 if (node.tagName === 'script') { 77 type = getAttribute(node, 'type') 78 if (type !== 'data' && type !== 'config') { 79 type = 'script' 80 } 81 } 82 else { 83 type = node.tagName 84 } 85 if (type === 'we-element') { 86 console.warn(`<we-element name="${getAttribute(node, 'name')}"> is deprecated, please use <element> instead.`) 87 type = 'element' 88 } 89 90 if (!output[type]) { 91 return 92 } 93 94 const name = getAttribute(node, 'name') 95 const src = getAttribute(node, 'src') 96 const lang = getAttribute(node, 'lang') 97 98 output[type].push({ 99 name, 100 src, 101 lang, 102 node 103 }) 104 105 if (type === 'template') { 106 const deps = [] 107 extractDependencies(node.content, deps) 108 output.deps = deps 109 } 110 }) 111 112 return output 113} 114 115export function parseTemplate (source, resourcePath) { 116 return new Promise((resolve, reject) => { 117 templater.parse(source, (err, obj) => { 118 if (err) { 119 reject(err) 120 } 121 else { 122 // parse json to string and treat function specially 123 let parsed = JSON.stringify(obj.jsonTemplate, stringifyFunction, ' ') 124 parsed = parsed.replace(FUNC_START_REG, '').replace(FUNC_END_REG, '') 125 resolve({ parsed, log: obj.log }) 126 } 127 }, resourcePath) 128 }) 129} 130 131export function parseStyle (source, resourcePath) { 132 return new Promise((resolve, reject) => { 133 styler.parse(source, (err, obj) => { 134 if (err) { 135 reject(err) 136 } 137 else { 138 const parsed = JSON.stringify(obj.jsonStyle, null, 2) 139 resolve({ parsed, log: obj.log }) 140 } 141 }, resourcePath) 142 }) 143} 144 145export function parseScript (source) { 146 return new Promise((resolve, reject) => { 147 const parsed = scripter.fix(source) 148 resolve({ parsed }) 149 }) 150} 151