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 path from 'path' 21import loaderUtils from 'loader-utils' 22import hash from 'hash-sum' 23import { 24 SourceMapGenerator, 25 SourceMapConsumer 26} from 'source-map' 27 28const { DEVICE_LEVEL } = require('./lite/lite-enum') 29 30export function getNameByPath (resourcePath) { 31 return path.basename(resourcePath).replace(/\..*$/, '') 32} 33 34export function getFileNameWithHash (resourcePath, content) { 35 const filename = path.relative('.', resourcePath) 36 const cacheKey = hash(filename + content) 37 return `./${filename}?${cacheKey}` 38} 39 40export function getFilenameByPath (filepath) { 41 return path.relative('.', filepath) 42} 43 44export const FUNC_START = '#####FUN_S#####' 45export const FUNC_START_REG = new RegExp('["\']' + FUNC_START, 'g') 46export const FUNC_END = '#####FUN_E#####' 47export const FUNC_END_REG = new RegExp(FUNC_END + '["\']', 'g') 48 49export function stringifyFunction (key, value) { 50 if (typeof value === 'function') { 51 return FUNC_START + value.toString() + FUNC_END 52 } 53 return value 54} 55 56export function logWarn (loader, logs) { 57 // add flag to determine if there is an error log 58 let flag = false 59 if (process.env.logLevel > 0) { 60 if (logs && logs.length) { 61 logs.forEach(log => { 62 if (log.reason.startsWith('NOTE') && parseInt(process.env.logLevel) <= 1) { 63 if (log.line && log.column) { 64 loader.emitWarning('noteStartNOTE File:' + loader.resourcePath + ':' + 65 log.line + ':' + log.column + '\n ' + log.reason.replace('NOTE: ', '') + 'noteEnd') 66 } else { 67 loader.emitWarning('noteStartNOTE File:' + loader.resourcePath + 68 '\n ' + log.reason.replace('NOTE: ', '') + 'noteEnd') 69 } 70 } else if (log.reason.startsWith('WARN') && parseInt(process.env.logLevel) <= 2) { 71 if (log.line && log.column) { 72 loader.emitWarning('warnStartWARNING File:' + loader.resourcePath + ':' + 73 log.line + ':' + log.column + '\n ' + log.reason.replace('WARNING: ', '') + 'warnEnd') 74 } else { 75 loader.emitWarning('warnStartWARNING File:' + loader.resourcePath + 76 '\n ' + log.reason.replace('WARNING: ', '') + 'warnEnd') 77 } 78 } else if (log.reason.startsWith('ERROR') && parseInt(process.env.logLevel) <= 3) { 79 flag = true 80 if (log.line && log.column) { 81 loader.emitError('errorStartERROR File:' + loader.resourcePath + ':' + 82 log.line + ':' + log.column + '\n ' + log.reason.replace('ERROR: ', '') + 'errorEnd') 83 } else { 84 loader.emitError('errorStartERROR File:' + loader.resourcePath + 85 '\n ' + log.reason.replace('ERROR: ', '') + 'errorEnd') 86 } 87 } 88 }) 89 } 90 } 91 return flag 92} 93 94export function getRequireString (loaderContext, loader, filepath) { 95 return 'require(' + 96 loaderUtils.stringifyRequest( 97 loaderContext, 98 loader ? 99 `!!${loader}!${filepath}` : 100 `${filepath}` 101 ) + 102 ')\n' 103} 104 105export function stringifyLoaders (loaders) { 106 return loaders.map(loader => { 107 if (typeof loader === 'string') { 108 return loader 109 } 110 else { 111 const name = loader.name 112 const query = [] 113 if (loader.query) { 114 for (const k in loader.query) { 115 const v = loader.query[k] 116 if (v != null) { 117 if (v === true) { 118 query.push(k) 119 } 120 else if (v instanceof Array) { 121 query.push(`${k}[]=${v.join(',')}`) 122 } 123 else { 124 query.push(`${k}=${v}`) 125 } 126 } 127 } 128 } 129 return `${name}${query.length ? ('?' + query.join('&')) : ''}` 130 } 131 }).join('!') 132} 133 134export function generateMap (loader, source, iterator) { 135 const filePath = loader.resourcePath 136 137 const fileNameWithHash = getFileNameWithHash(filePath) 138 const sourceRoot = path.resolve('.') 139 140 const map = new SourceMapGenerator({ 141 sourceRoot, 142 skipValidation: true 143 }) 144 map.setSourceContent(fileNameWithHash, source) 145 146 for (const { original, generated } of iterator) { 147 map.addMapping({ 148 source: fileNameWithHash, 149 original, 150 generated 151 }) 152 } 153 154 return map 155} 156 157export function consumeMap (loader, target, map) { 158 const smc = new SourceMapConsumer(map) 159 let source 160 const original = [] 161 const generated = [] 162 const mapping = {} 163 164 splitSourceLine(target) 165 .forEach((input, line) => { 166 const column = 0 167 line = line + 1 168 169 const pos = smc.originalPositionFor({ 170 line, 171 column 172 }) 173 174 if (pos.source) { 175 source = pos.source 176 original.push({ 177 line: pos.line, 178 column: pos.column 179 }) 180 generated.push({ 181 line, 182 column 183 }) 184 mapping[`line-${line}-column-${column}`] = { 185 line: pos.line, 186 column: pos.column 187 } 188 } 189 }) 190 191 return { 192 source, 193 original, 194 generated, 195 mapping, 196 sourcesContent: smc.sourcesContent 197 } 198} 199 200const LINE_REG = /\r?\n/g 201export function splitSourceLine (source) { 202 return source.split(LINE_REG) 203} 204 205export function printSourceWithLine (source) { 206 console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>') 207 source = splitSourceLine(source) 208 .map((input, line) => { 209 console.log(line + 1 + ':', input) 210 }) 211 console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<') 212} 213 214export function loadBabelModule (moduleName) { 215 try { 216 const filePath = require.resolve(moduleName) 217 return filePath.slice(0, filePath.indexOf(moduleName.replace(/\//g, path.sep)) + moduleName.length) 218 } 219 catch (e) { 220 return moduleName 221 } 222} 223 224const methodForLite = 225` 226function requireModule(moduleName) { 227 return requireNative(moduleName.slice(1)); 228} 229` 230const methodForOthers = 231` 232function requireModule(moduleName) { 233 const systemList = ['system.router', 'system.app', 'system.prompt', 'system.configuration', 234 'system.image', 'system.device', 'system.mediaquery', 'ohos.animator', 'system.grid', 'system.resource'] 235 var target = '' 236 if (systemList.includes(moduleName.replace('@', ''))) { 237 target = $app_require$('@app-module/' + moduleName.substring(1)); 238 return target; 239 } 240 var shortName = moduleName.replace(/@[^.]+\.([^.]+)/, '$1'); 241 target = requireNapi(shortName); 242 if (target !== 'undefined' && /@ohos/.test(moduleName)) { 243 return target; 244 } 245 if (typeof ohosplugin !== 'undefined' && /@ohos/.test(moduleName)) { 246 target = ohosplugin; 247 for (let key of shortName.split('.')) { 248 target = target[key]; 249 if(!target) { 250 break; 251 } 252 } 253 if (typeof target !== 'undefined') { 254 return target; 255 } 256 } 257 if (typeof systemplugin !== 'undefined') { 258 target = systemplugin; 259 for (let key of shortName.split('.')) { 260 target = target[key]; 261 if(!target) { 262 break; 263 } 264 } 265 if (typeof target !== 'undefined') { 266 return target; 267 } 268 } 269 return target; 270} 271` 272export function parseRequireModule (source) { 273 const requireMethod = process.env.DEVICE_LEVEL === DEVICE_LEVEL.LITE ? methodForLite : methodForOthers 274 source = `${source}\n${requireMethod}` 275 const requireReg = /require\(['"]([^()]+)['"]\)/g 276 const libReg = /^lib(.+)\.so$/ 277 let requireStatements = source.match(requireReg) 278 if (requireStatements && requireStatements.length) { 279 for (let requireStatement of requireStatements) { 280 if (requireStatement.indexOf('@system') > 0 || requireStatement.indexOf('@ohos') > 0) { 281 source = source.replace(requireStatement, requireStatement.replace('require', 'requireModule')) 282 } 283 } 284 } 285 source = source.replace(requireReg, (item, item1) => { 286 if (libReg.test(item1)) { 287 item = `requireNapi("${item1.replace(libReg, '$1')}", true)` 288 } 289 return item 290 }) 291 return source 292} 293 294export function jsonLoaders (type, customLoader, isVisual, queryType) { 295 let loaders = [] 296 297 switch (type) { 298 case "template": 299 loaders = [{ 300 name: path.resolve(__dirname, 'json.js') 301 }, { 302 name: path.resolve(__dirname, 'template.js') 303 }] 304 break 305 case "style": 306 loaders = [{ 307 name: path.resolve(__dirname, 'json.js') 308 }, { 309 name: path.resolve(__dirname, 'style.js') 310 }] 311 break 312 case "json": 313 loaders = [{ 314 name: path.resolve(__dirname, 'json.js') 315 }] 316 break 317 default: 318 break 319 } 320 321 if (customLoader) { 322 loaders.push({ 323 name: path.resolve(__dirname, `../node_modules/${customLoader}`) 324 }) 325 } 326 327 if (isVisual) { 328 loaders.push({ 329 name: path.resolve(__dirname, 'extgen.js'), 330 query: { 331 type: queryType 332 } 333 }) 334 } 335 336 return stringifyLoaders(loaders) 337} 338