Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546
  1. 'use strict'
  2. /* global __coverage__ */
  3. const cachingTransform = require('caching-transform')
  4. const findCacheDir = require('find-cache-dir')
  5. const fs = require('./lib/fs-promises')
  6. const os = require('os')
  7. const { debuglog, promisify } = require('util')
  8. const glob = promisify(require('glob'))
  9. const Hash = require('./lib/hash')
  10. const libCoverage = require('istanbul-lib-coverage')
  11. const libHook = require('istanbul-lib-hook')
  12. const { ProcessInfo, ProcessDB } = require('istanbul-lib-processinfo')
  13. const mkdirp = require('make-dir')
  14. const Module = require('module')
  15. const onExit = require('signal-exit')
  16. const path = require('path')
  17. const rimraf = promisify(require('rimraf'))
  18. const SourceMaps = require('./lib/source-maps')
  19. const TestExclude = require('test-exclude')
  20. const pMap = require('p-map')
  21. const getPackageType = require('get-package-type')
  22. const debugLog = debuglog('nyc')
  23. let selfCoverageHelper
  24. /* istanbul ignore next */
  25. if (/self-coverage/.test(__dirname)) {
  26. selfCoverageHelper = require('../self-coverage-helper')
  27. } else {
  28. // Avoid additional conditional code
  29. selfCoverageHelper = {
  30. onExit () {}
  31. }
  32. }
  33. function coverageFinder () {
  34. var coverage = global.__coverage__
  35. if (typeof __coverage__ === 'object') coverage = __coverage__
  36. if (!coverage) coverage = global.__coverage__ = {}
  37. return coverage
  38. }
  39. class NYC {
  40. constructor (config) {
  41. this.config = { ...config }
  42. this.subprocessBin = config.subprocessBin || path.resolve(__dirname, './bin/nyc.js')
  43. this._tempDirectory = config.tempDirectory || config.tempDir || './.nyc_output'
  44. this._instrumenterLib = require(config.instrumenter || './lib/instrumenters/istanbul')
  45. this._reportDir = config.reportDir || 'coverage'
  46. this._sourceMap = typeof config.sourceMap === 'boolean' ? config.sourceMap : true
  47. this._showProcessTree = config.showProcessTree || false
  48. this._eagerInstantiation = config.eager || false
  49. this.cwd = config.cwd || process.cwd()
  50. this.reporter = [].concat(config.reporter || 'text')
  51. this.cacheDirectory = (config.cacheDir && path.resolve(config.cacheDir)) || findCacheDir({ name: 'nyc', cwd: this.cwd })
  52. this.cache = Boolean(this.cacheDirectory && config.cache)
  53. this.extensions = [].concat(config.extension || [])
  54. .concat('.js')
  55. .map(ext => ext.toLowerCase())
  56. .filter((item, pos, arr) => arr.indexOf(item) === pos)
  57. this.exclude = new TestExclude({
  58. cwd: this.cwd,
  59. include: config.include,
  60. exclude: config.exclude,
  61. excludeNodeModules: config.excludeNodeModules !== false,
  62. extension: this.extensions
  63. })
  64. this.sourceMaps = new SourceMaps({
  65. cache: this.cache,
  66. cacheDirectory: this.cacheDirectory
  67. })
  68. // require extensions can be provided as config in package.json.
  69. this.require = [].concat(config.require || [])
  70. this.transforms = this.extensions.reduce((transforms, ext) => {
  71. transforms[ext] = this._createTransform(ext)
  72. return transforms
  73. }, {})
  74. this.hookRequire = config.hookRequire
  75. this.hookRunInContext = config.hookRunInContext
  76. this.hookRunInThisContext = config.hookRunInThisContext
  77. this.fakeRequire = null
  78. this.processInfo = new ProcessInfo(Object.assign({}, config._processInfo, {
  79. directory: path.resolve(this.tempDirectory(), 'processinfo')
  80. }))
  81. this.hashCache = {}
  82. }
  83. _createTransform (ext) {
  84. const opts = {
  85. salt: Hash.salt(this.config),
  86. hashData: (input, metadata) => [metadata.filename],
  87. filenamePrefix: metadata => path.parse(metadata.filename).name + '-',
  88. onHash: (input, metadata, hash) => {
  89. this.hashCache[metadata.filename] = hash
  90. },
  91. cacheDir: this.cacheDirectory,
  92. // when running --all we should not load source-file from
  93. // cache, we want to instead return the fake source.
  94. disableCache: this._disableCachingTransform(),
  95. ext: ext
  96. }
  97. if (this._eagerInstantiation) {
  98. opts.transform = this._transformFactory(this.cacheDirectory)
  99. } else {
  100. opts.factory = this._transformFactory.bind(this)
  101. }
  102. return cachingTransform(opts)
  103. }
  104. _disableCachingTransform () {
  105. return !(this.cache && this.config.isChildProcess)
  106. }
  107. _loadAdditionalModules () {
  108. if (!this.config.useSpawnWrap || this.require.length === 0) {
  109. return
  110. }
  111. const resolveFrom = require('resolve-from')
  112. this.require.forEach(requireModule => {
  113. // Attempt to require the module relative to the directory being instrumented.
  114. // Then try other locations, e.g. the nyc node_modules folder.
  115. require(resolveFrom.silent(this.cwd, requireModule) || requireModule)
  116. })
  117. }
  118. instrumenter () {
  119. return this._instrumenter || (this._instrumenter = this._createInstrumenter())
  120. }
  121. _createInstrumenter () {
  122. return this._instrumenterLib({
  123. ignoreClassMethods: [].concat(this.config.ignoreClassMethod).filter(a => a),
  124. produceSourceMap: this.config.produceSourceMap,
  125. compact: this.config.compact,
  126. preserveComments: this.config.preserveComments,
  127. esModules: this.config.esModules,
  128. parserPlugins: this.config.parserPlugins
  129. })
  130. }
  131. addFile (filename) {
  132. const source = this._readTranspiledSource(filename)
  133. this._maybeInstrumentSource(source, filename)
  134. }
  135. _readTranspiledSource (filePath) {
  136. var source = null
  137. var ext = path.extname(filePath)
  138. if (typeof Module._extensions[ext] === 'undefined') {
  139. ext = '.js'
  140. }
  141. Module._extensions[ext]({
  142. _compile: function (content, filename) {
  143. source = content
  144. }
  145. }, filePath)
  146. return source
  147. }
  148. _getSourceMap (code, filename, hash) {
  149. const sourceMap = {}
  150. if (this._sourceMap) {
  151. sourceMap.sourceMap = this.sourceMaps.extract(code, filename)
  152. sourceMap.registerMap = () => this.sourceMaps.registerMap(filename, hash, sourceMap.sourceMap)
  153. } else {
  154. sourceMap.registerMap = () => {}
  155. }
  156. return sourceMap
  157. }
  158. async addAllFiles () {
  159. this._loadAdditionalModules()
  160. this.fakeRequire = true
  161. const files = await this.exclude.glob(this.cwd)
  162. for (const relFile of files) {
  163. const filename = path.resolve(this.cwd, relFile)
  164. const ext = path.extname(filename)
  165. if (ext === '.mjs' || (ext === '.js' && await getPackageType(filename) === 'module')) {
  166. const source = await fs.readFile(filename, 'utf8')
  167. this.instrumenter().instrumentSync(
  168. source,
  169. filename,
  170. this._getSourceMap(source, filename)
  171. )
  172. } else {
  173. this.addFile(filename)
  174. }
  175. const coverage = coverageFinder()
  176. const lastCoverage = this.instrumenter().lastFileCoverage()
  177. if (lastCoverage) {
  178. coverage[lastCoverage.path] = {
  179. ...lastCoverage,
  180. // Only use this data if we don't have it without `all: true`
  181. all: true
  182. }
  183. }
  184. }
  185. this.fakeRequire = false
  186. this.writeCoverageFile()
  187. }
  188. async instrumentAllFiles (input, output) {
  189. let inputDir = '.' + path.sep
  190. const visitor = async relFile => {
  191. const inFile = path.resolve(inputDir, relFile)
  192. const inCode = await fs.readFile(inFile, 'utf-8')
  193. const outCode = this._transform(inCode, inFile) || inCode
  194. if (output) {
  195. const { mode } = await fs.stat(inFile)
  196. const outFile = path.resolve(output, relFile)
  197. await mkdirp(path.dirname(outFile))
  198. await fs.writeFile(outFile, outCode)
  199. await fs.chmod(outFile, mode)
  200. } else {
  201. console.log(outCode)
  202. }
  203. }
  204. this._loadAdditionalModules()
  205. const stats = await fs.lstat(input)
  206. if (stats.isDirectory()) {
  207. inputDir = input
  208. const filesToInstrument = await this.exclude.glob(input)
  209. const concurrency = output ? os.cpus().length : 1
  210. if (this.config.completeCopy && output) {
  211. const files = await glob(path.resolve(input, '**'), {
  212. dot: true,
  213. nodir: true,
  214. ignore: ['**/.git', '**/.git/**', path.join(output, '**')]
  215. })
  216. const destDirs = new Set(
  217. files.map(src => path.dirname(path.join(output, path.relative(input, src))))
  218. )
  219. await pMap(
  220. destDirs,
  221. dir => mkdirp(dir),
  222. { concurrency }
  223. )
  224. await pMap(
  225. files,
  226. src => fs.copyFile(src, path.join(output, path.relative(input, src))),
  227. { concurrency }
  228. )
  229. }
  230. await pMap(filesToInstrument, visitor, { concurrency })
  231. } else {
  232. await visitor(input)
  233. }
  234. }
  235. _transform (code, filename) {
  236. const extname = path.extname(filename).toLowerCase()
  237. const transform = this.transforms[extname] || (() => null)
  238. return transform(code, { filename })
  239. }
  240. _maybeInstrumentSource (code, filename) {
  241. if (!this.exclude.shouldInstrument(filename)) {
  242. return null
  243. }
  244. return this._transform(code, filename)
  245. }
  246. maybePurgeSourceMapCache () {
  247. if (!this.cache) {
  248. this.sourceMaps.purgeCache()
  249. }
  250. }
  251. _transformFactory (cacheDir) {
  252. const instrumenter = this.instrumenter()
  253. let instrumented
  254. return (code, metadata, hash) => {
  255. const filename = metadata.filename
  256. const sourceMap = this._getSourceMap(code, filename, hash)
  257. try {
  258. instrumented = instrumenter.instrumentSync(code, filename, sourceMap)
  259. } catch (e) {
  260. debugLog('failed to instrument ' + filename + ' with error: ' + e.stack)
  261. if (this.config.exitOnError) {
  262. console.error('Failed to instrument ' + filename)
  263. process.exit(1)
  264. } else {
  265. instrumented = code
  266. }
  267. }
  268. if (this.fakeRequire) {
  269. return 'function x () {}'
  270. } else {
  271. return instrumented
  272. }
  273. }
  274. }
  275. _handleJs (code, options) {
  276. // ensure the path has correct casing (see istanbuljs/nyc#269 and nodejs/node#6624)
  277. const filename = path.resolve(this.cwd, options.filename)
  278. return this._maybeInstrumentSource(code, filename) || code
  279. }
  280. _addHook (type) {
  281. const handleJs = this._handleJs.bind(this)
  282. const dummyMatcher = () => true // we do all processing in transformer
  283. libHook['hook' + type](dummyMatcher, handleJs, { extensions: this.extensions })
  284. }
  285. _addRequireHooks () {
  286. if (this.hookRequire) {
  287. this._addHook('Require')
  288. }
  289. if (this.hookRunInContext) {
  290. this._addHook('RunInContext')
  291. }
  292. if (this.hookRunInThisContext) {
  293. this._addHook('RunInThisContext')
  294. }
  295. }
  296. async createTempDirectory () {
  297. await mkdirp(this.tempDirectory())
  298. if (this.cache) {
  299. await mkdirp(this.cacheDirectory)
  300. }
  301. await mkdirp(this.processInfo.directory)
  302. }
  303. async reset () {
  304. if (!process.env.NYC_CWD) {
  305. await rimraf(this.tempDirectory())
  306. }
  307. await this.createTempDirectory()
  308. }
  309. _wrapExit () {
  310. selfCoverageHelper.registered = true
  311. // we always want to write coverage
  312. // regardless of how the process exits.
  313. onExit(
  314. () => {
  315. this.writeCoverageFile()
  316. selfCoverageHelper.onExit()
  317. },
  318. { alwaysLast: true }
  319. )
  320. }
  321. wrap (bin) {
  322. process.env.NYC_PROCESS_ID = this.processInfo.uuid
  323. // This is a bug with the spawn-wrap method where
  324. // we cannot force propagation of NYC_PROCESS_ID.
  325. if (!this.config.useSpawnWrap) {
  326. const updateVariable = require('./lib/register-env.js')
  327. updateVariable('NYC_PROCESS_ID')
  328. }
  329. this._addRequireHooks()
  330. this._wrapExit()
  331. this._loadAdditionalModules()
  332. return this
  333. }
  334. writeCoverageFile () {
  335. var coverage = coverageFinder()
  336. // Remove any files that should be excluded but snuck into the coverage
  337. Object.keys(coverage).forEach(function (absFile) {
  338. if (!this.exclude.shouldInstrument(absFile)) {
  339. delete coverage[absFile]
  340. }
  341. }, this)
  342. if (this.cache) {
  343. Object.keys(coverage).forEach(function (absFile) {
  344. if (this.hashCache[absFile] && coverage[absFile]) {
  345. coverage[absFile].contentHash = this.hashCache[absFile]
  346. }
  347. }, this)
  348. }
  349. var id = this.processInfo.uuid
  350. var coverageFilename = path.resolve(this.tempDirectory(), id + '.json')
  351. fs.writeFileSync(
  352. coverageFilename,
  353. JSON.stringify(coverage),
  354. 'utf-8'
  355. )
  356. this.processInfo.coverageFilename = coverageFilename
  357. this.processInfo.files = Object.keys(coverage)
  358. this.processInfo.saveSync()
  359. }
  360. async getCoverageMapFromAllCoverageFiles (baseDirectory) {
  361. const map = libCoverage.createCoverageMap({})
  362. const files = await this.coverageFiles(baseDirectory)
  363. await pMap(
  364. files,
  365. async f => {
  366. const report = await this.coverageFileLoad(f, baseDirectory)
  367. map.merge(report)
  368. },
  369. { concurrency: os.cpus().length }
  370. )
  371. map.data = await this.sourceMaps.remapCoverage(map.data)
  372. // depending on whether source-code is pre-instrumented
  373. // or instrumented using a JIT plugin like @babel/require
  374. // you may opt to exclude files after applying
  375. // source-map remapping logic.
  376. if (this.config.excludeAfterRemap) {
  377. map.filter(filename => this.exclude.shouldInstrument(filename))
  378. }
  379. return map
  380. }
  381. async report () {
  382. const libReport = require('istanbul-lib-report')
  383. const reports = require('istanbul-reports')
  384. const context = libReport.createContext({
  385. dir: this.reportDirectory(),
  386. watermarks: this.config.watermarks,
  387. coverageMap: await this.getCoverageMapFromAllCoverageFiles()
  388. })
  389. this.reporter.forEach((_reporter) => {
  390. reports.create(_reporter, {
  391. skipEmpty: this.config.skipEmpty,
  392. skipFull: this.config.skipFull,
  393. projectRoot: this.cwd,
  394. maxCols: process.stdout.columns || 100
  395. }).execute(context)
  396. })
  397. if (this._showProcessTree) {
  398. await this.showProcessTree()
  399. }
  400. }
  401. async writeProcessIndex () {
  402. const db = new ProcessDB(this.processInfo.directory)
  403. await db.writeIndex()
  404. }
  405. async showProcessTree () {
  406. const db = new ProcessDB(this.processInfo.directory)
  407. console.log(await db.renderTree(this))
  408. }
  409. async checkCoverage (thresholds, perFile) {
  410. const map = await this.getCoverageMapFromAllCoverageFiles()
  411. if (perFile) {
  412. map.files().forEach(file => {
  413. // ERROR: Coverage for lines (90.12%) does not meet threshold (120%) for index.js
  414. this._checkCoverage(map.fileCoverageFor(file).toSummary(), thresholds, file)
  415. })
  416. } else {
  417. // ERROR: Coverage for lines (90.12%) does not meet global threshold (120%)
  418. this._checkCoverage(map.getCoverageSummary(), thresholds)
  419. }
  420. }
  421. _checkCoverage (summary, thresholds, file) {
  422. Object.keys(thresholds).forEach(function (key) {
  423. var coverage = summary[key].pct
  424. if (coverage < thresholds[key]) {
  425. process.exitCode = 1
  426. if (file) {
  427. console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet threshold (' + thresholds[key] + '%) for ' + file)
  428. } else {
  429. console.error('ERROR: Coverage for ' + key + ' (' + coverage + '%) does not meet global threshold (' + thresholds[key] + '%)')
  430. }
  431. }
  432. })
  433. }
  434. coverageFiles (baseDirectory = this.tempDirectory()) {
  435. return fs.readdir(baseDirectory)
  436. }
  437. async coverageFileLoad (filename, baseDirectory = this.tempDirectory()) {
  438. try {
  439. const report = JSON.parse(await fs.readFile(path.resolve(baseDirectory, filename)), 'utf8')
  440. await this.sourceMaps.reloadCachedSourceMaps(report)
  441. return report
  442. } catch (error) {
  443. return {}
  444. }
  445. }
  446. // TODO: Remove from nyc v16
  447. async coverageData (baseDirectory) {
  448. const files = await this.coverageFiles(baseDirectory)
  449. return pMap(
  450. files,
  451. f => this.coverageFileLoad(f, baseDirectory),
  452. { concurrency: os.cpus().length }
  453. )
  454. }
  455. tempDirectory () {
  456. return path.resolve(this.cwd, this._tempDirectory)
  457. }
  458. reportDirectory () {
  459. return path.resolve(this.cwd, this._reportDir)
  460. }
  461. }
  462. module.exports = NYC