• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 fs from 'fs'
22import loaderUtils from 'loader-utils'
23const crypto = require("crypto")
24import {
25  SourceMapGenerator,
26  SourceMapConsumer
27} from 'source-map'
28
29const { systemModules } =  require('../main.product')
30const { DEVICE_LEVEL } = require('./lite/lite-enum')
31export const useOSFiles = new Set();
32export const elements = {};
33
34export function getNameByPath (resourcePath) {
35  return path.basename(resourcePath).replace(/\..*$/, '')
36}
37
38export function getFileNameWithHash (resourcePath, content) {
39  const filename = path.relative('.', resourcePath)
40  const hash = crypto.createHash('sha256')
41  hash.update((filename + content).toString())
42  const cacheKey = hash.digest('hex')
43  return `./${filename}?${cacheKey}`
44}
45
46export function getFilenameByPath (filepath) {
47  return path.relative('.', filepath)
48}
49
50export const FUNC_START = '#####FUN_S#####'
51export const FUNC_START_REG = new RegExp('["\']' + FUNC_START, 'g')
52export const FUNC_END = '#####FUN_E#####'
53export const FUNC_END_REG = new RegExp(FUNC_END + '["\']', 'g')
54
55export function stringifyFunction (key, value) {
56  if (typeof value === 'function') {
57    return FUNC_START + value.toString() + FUNC_END
58  }
59  return value
60}
61
62export function logWarn (loader, logs) {
63  // add flag to determine if there is an error log
64  let flag = false
65  if (process.env.logLevel > 0) {
66    if (logs && logs.length) {
67      logs.forEach(log => {
68        if (log.reason.startsWith('NOTE') && parseInt(process.env.logLevel) <= 1) {
69          if (log.line && log.column) {
70            loader.emitWarning('noteStartNOTE File:' + loader.resourcePath + ':' +
71              log.line + ':' + log.column + '\n ' + log.reason.replace('NOTE: ', '') + 'noteEnd')
72          } else {
73            loader.emitWarning('noteStartNOTE File:' + loader.resourcePath +
74              '\n ' + log.reason.replace('NOTE: ', '') + 'noteEnd')
75          }
76        } else if (log.reason.startsWith('WARN') && parseInt(process.env.logLevel) <= 2) {
77          if (log.line && log.column) {
78            loader.emitWarning('warnStartWARNING File:' + loader.resourcePath + ':' +
79              log.line + ':' + log.column + '\n ' + log.reason.replace('WARNING: ', '') + 'warnEnd')
80          } else {
81            loader.emitWarning('warnStartWARNING File:' + loader.resourcePath +
82              '\n ' + log.reason.replace('WARNING: ', '') + 'warnEnd')
83          }
84        } else if (log.reason.startsWith('ERROR') && parseInt(process.env.logLevel) <= 3) {
85          flag = true
86          if (log.line && log.column) {
87            loader.emitError('errorStartERROR File:' + loader.resourcePath + ':' +
88              log.line + ':' + log.column + '\n ' + log.reason.replace('ERROR: ', '') + 'errorEnd')
89          } else {
90            loader.emitError('errorStartERROR File:' + loader.resourcePath +
91              '\n ' + log.reason.replace('ERROR: ', '') + 'errorEnd')
92          }
93        }
94      })
95    }
96  }
97  return flag
98}
99
100export function getRequireString (loaderContext, loader, filepath) {
101  return 'require(' +
102                loaderUtils.stringifyRequest(
103                  loaderContext,
104                  loader ?
105                    `!!${loader}!${filepath}` :
106                    `${filepath}`
107                ) +
108           ')\n'
109}
110
111export function stringifyLoaders (loaders) {
112  return loaders.map(loader => {
113    if (typeof loader === 'string') {
114      return loader
115    }
116    else {
117      const name = loader.name
118      const query = []
119      if (loader.query) {
120        for (const k in loader.query) {
121          const v = loader.query[k]
122          if (v != null) {
123            if (v === true) {
124              query.push(k)
125            }
126            else if (v instanceof Array) {
127              query.push(`${k}[]=${v.join(',')}`)
128            }
129            else {
130              query.push(`${k}=${v}`)
131            }
132          }
133        }
134      }
135      return `${name}${query.length ? ('?' + query.join('&')) : ''}`
136    }
137  }).join('!')
138}
139
140export function generateMap (loader, source, iterator) {
141  const filePath = loader.resourcePath
142
143  const fileNameWithHash = getFileNameWithHash(filePath)
144  const sourceRoot = path.resolve('.')
145
146  const map = new SourceMapGenerator({
147    sourceRoot,
148    skipValidation: true
149  })
150  map.setSourceContent(fileNameWithHash, source)
151
152  for (const { original, generated } of iterator) {
153    map.addMapping({
154      source: fileNameWithHash,
155      original,
156      generated
157    })
158  }
159
160  return map
161}
162
163export function consumeMap (loader, target, map) {
164  const smc = new SourceMapConsumer(map)
165  let source
166  const original = []
167  const generated = []
168  const mapping = {}
169
170  splitSourceLine(target)
171    .forEach((input, line) => {
172      const column = 0
173      line = line + 1
174
175      const pos = smc.originalPositionFor({
176        line,
177        column
178      })
179
180      if (pos.source) {
181        source = pos.source
182        original.push({
183          line: pos.line,
184          column: pos.column
185        })
186        generated.push({
187          line,
188          column
189        })
190        mapping[`line-${line}-column-${column}`] = {
191          line: pos.line,
192          column: pos.column
193        }
194      }
195    })
196
197  return {
198    source,
199    original,
200    generated,
201    mapping,
202    sourcesContent: smc.sourcesContent
203  }
204}
205
206const LINE_REG = /\r?\n/g
207export function splitSourceLine (source) {
208  return source.split(LINE_REG)
209}
210
211export function printSourceWithLine (source) {
212  console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>')
213  source = splitSourceLine(source)
214    .map((input, line) => {
215      console.log(line + 1 + ':', input)
216    })
217  console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<')
218}
219
220export function loadBabelModule (moduleName) {
221  try {
222    const filePath = require.resolve(moduleName)
223    return filePath.slice(0, filePath.indexOf(moduleName.replace(/\//g, path.sep)) + moduleName.length)
224  }
225  catch (e) {
226    return moduleName
227  }
228}
229
230const methodForLite =
231`
232function requireModule(moduleName) {
233  return requireNative(moduleName.slice(1));
234}
235`
236const methodForOthers =
237`
238function requireModule(moduleName) {
239  const systemList = ['system.router', 'system.app', 'system.prompt', 'system.configuration',
240  'system.image', 'system.device', 'system.mediaquery', 'ohos.animator', 'system.grid', 'system.resource']
241  var target = ''
242  if (systemList.includes(moduleName.replace('@', ''))) {
243    target = $app_require$('@app-module/' + moduleName.substring(1));
244    return target;
245  }
246  var shortName = moduleName.replace(/@[^.]+\.([^.]+)/, '$1');
247  target = requireNapi(shortName);
248  if (typeof target !== 'undefined' && /@ohos/.test(moduleName)) {
249    return target;
250  }
251  if (typeof ohosplugin !== 'undefined' && /@ohos/.test(moduleName)) {
252    target = ohosplugin;
253    for (let key of shortName.split('.')) {
254      target = target[key];
255      if(!target) {
256        break;
257      }
258    }
259    if (typeof target !== 'undefined') {
260      return target;
261    }
262  }
263  if (typeof systemplugin !== 'undefined') {
264    target = systemplugin;
265    for (let key of shortName.split('.')) {
266      target = target[key];
267      if(!target) {
268        break;
269      }
270    }
271    if (typeof target !== 'undefined') {
272      return target;
273    }
274  }
275  return target;
276}
277`
278export function parseRequireModule (source, resourcePath) {
279  const requireMethod = process.env.DEVICE_LEVEL === DEVICE_LEVEL.LITE ? methodForLite : methodForOthers
280  source = `${source}\n${requireMethod}`
281  const requireReg = /require\(['"]([^()]+)['"]\)/g
282  const libReg = /^lib(.+)\.so$/
283  const REG_SYSTEM = /@(system|ohos)\.(\S+)/g;
284  let requireStatements = source.match(requireReg)
285  if (requireStatements && requireStatements.length) {
286    for (let requireStatement of requireStatements) {
287      const requireStatementExec = /\((\"|\')(.+)(\"|\')\)/.exec(requireStatement);
288      if (requireStatement.match(REG_SYSTEM) && requireStatementExec && requireStatementExec.length > 3) {
289        if (systemModules.length == 0 || systemModules.includes(requireStatementExec[2] + '.d.ts') ||
290          process.env.DEVICE_LEVEL === 'lite') {
291          source = source.replace(requireStatement, requireStatement.replace('require', 'requireModule'));
292        }
293      }
294    }
295  }
296  source = source.replace(requireReg, (item, item1) => {
297    if (libReg.test(item1)) {
298      item = `requireNapi("${item1.replace(libReg, '$1')}", true)`
299      if (resourcePath) {
300        useOSFiles.add(resourcePath);
301      }
302    }
303    return item
304  })
305  return source
306}
307
308export function jsonLoaders (type, customLoader, isVisual, queryType) {
309  let loaders = []
310
311  switch (type) {
312    case "template":
313      loaders = [{
314        name: path.resolve(__dirname, 'json.js')
315      }, {
316        name: path.resolve(__dirname, 'template.js')
317      }]
318      break
319    case "style":
320      loaders = [{
321        name: path.resolve(__dirname, 'json.js')
322      }, {
323        name: path.resolve(__dirname, 'style.js')
324      }]
325      break
326    case "json":
327      loaders = [{
328        name: path.resolve(__dirname, 'json.js')
329      }]
330      break
331    default:
332      break
333  }
334
335  if (customLoader) {
336    loaders.push({
337      name: path.resolve(__dirname, `../node_modules/${customLoader}`)
338    })
339  }
340
341  if (isVisual) {
342    loaders.push({
343      name: path.resolve(__dirname, 'extgen.js'),
344      query: {
345        type: queryType
346      }
347    })
348  }
349
350  return stringifyLoaders(loaders)
351}
352
353export function circularFile(inputPath, outputPath) {
354  if ((!inputPath) || (!outputPath)) {
355    return;
356  }
357  fs.readdir(inputPath,function(err, files){
358    if (!files) {
359      return;
360    }
361    files.forEach(file => {
362      const inputFile = path.resolve(inputPath, file);
363      const outputFile = path.resolve(outputPath, file);
364      const fileStat = fs.statSync(inputFile);
365      if (fileStat.isFile()) {
366        copyFile(inputFile, outputFile);
367      } else {
368        circularFile(inputFile, outputFile);
369      }
370    });
371  })
372}
373
374function copyFile(inputFile, outputFile) {
375  try {
376    const parent = path.join(outputFile, '..');
377    if (!(fs.existsSync(parent) && fs.statSync(parent).isDirectory())) {
378      mkDir(parent);
379    }
380    if (path.parse(parent).name === 'i18n' && path.parse(inputFile).ext === '.json' &&
381      fs.existsSync(outputFile)) {
382        copyJsonFile(inputFile, outputFile);
383    } else if (!fs.existsSync(outputFile)){
384      fs.writeFileSync(outputFile, fs.readFileSync(inputFile));
385    }
386  } catch (err) {
387    throw err;
388  }
389}
390
391function copyJsonFile(inputFile, outputFile) {
392  try {
393    const contentInput = JSON.parse(fs.readFileSync(inputFile, 'utf-8'));
394    const contentOutput = JSON.parse(fs.readFileSync(outputFile, 'utf-8'));
395    Object.keys(contentInput).forEach(function (key) {
396      const contentElementMerge = mergeJson(contentInput[key], contentOutput[key]);
397      contentOutput[key] = contentElementMerge;
398    });
399    fs.writeFileSync(outputFile, JSON.stringify(contentOutput, null, '\t'));
400  } catch (err) {
401    throw err;
402  }
403}
404
405function mergeJson(inputValue, outputValue) {
406  if (outputValue === null || outputValue === undefined) {
407    return inputValue;
408  }
409  const typeInput = typeof inputValue;
410  if (typeInput === typeof outputValue && typeInput === 'object') {
411    Object.keys(inputValue).forEach(function (key) {
412      const contentElementMerge = mergeJson(inputValue[key], outputValue[key]);
413      outputValue[key] = contentElementMerge;
414    })
415  }
416  return outputValue;
417}
418
419export function mkDir(path_) {
420  const parent = path.join(path_, '..');
421  if (!(fs.existsSync(parent) && !fs.statSync(parent).isFile())) {
422    mkDir(parent);
423  }
424  fs.mkdirSync(path_);
425}