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