tar.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. const tar = require('tar')
  2. const ssri = require('ssri')
  3. const npmlog = require('npmlog')
  4. const formatBytes = require('./format-bytes.js')
  5. const columnify = require('columnify')
  6. const localeCompare = require('@isaacs/string-locale-compare')('en', {
  7. sensitivity: 'case',
  8. numeric: true,
  9. })
  10. const logTar = (tarball, opts = {}) => {
  11. const { unicode = false, log = npmlog } = opts
  12. log.notice('')
  13. log.notice('', `${unicode ? '📦 ' : 'package:'} ${tarball.name}@${tarball.version}`)
  14. log.notice('=== Tarball Contents ===')
  15. if (tarball.files.length) {
  16. log.notice('', columnify(tarball.files.map((f) => {
  17. const bytes = formatBytes(f.size, false)
  18. return (/^node_modules\//.test(f.path)) ? null
  19. : { path: f.path, size: `${bytes}` }
  20. }).filter(f => f), {
  21. include: ['size', 'path'],
  22. showHeaders: false,
  23. }))
  24. }
  25. if (tarball.bundled.length) {
  26. log.notice('=== Bundled Dependencies ===')
  27. tarball.bundled.forEach((name) => log.notice('', name))
  28. }
  29. log.notice('=== Tarball Details ===')
  30. log.notice('', columnify([
  31. { name: 'name:', value: tarball.name },
  32. { name: 'version:', value: tarball.version },
  33. tarball.filename && { name: 'filename:', value: tarball.filename },
  34. { name: 'package size:', value: formatBytes(tarball.size) },
  35. { name: 'unpacked size:', value: formatBytes(tarball.unpackedSize) },
  36. { name: 'shasum:', value: tarball.shasum },
  37. {
  38. name: 'integrity:',
  39. value: tarball.integrity.toString().substr(0, 20) + '[...]' + tarball.integrity.toString().substr(80),
  40. },
  41. tarball.bundled.length && { name: 'bundled deps:', value: tarball.bundled.length },
  42. tarball.bundled.length && { name: 'bundled files:', value: tarball.entryCount - tarball.files.length },
  43. tarball.bundled.length && { name: 'own files:', value: tarball.files.length },
  44. { name: 'total files:', value: tarball.entryCount },
  45. ].filter((x) => x), {
  46. include: ['name', 'value'],
  47. showHeaders: false,
  48. }))
  49. log.notice('', '')
  50. }
  51. const getContents = async (manifest, tarball) => {
  52. const files = []
  53. const bundled = new Set()
  54. let totalEntries = 0
  55. let totalEntrySize = 0
  56. // reads contents of tarball
  57. const stream = tar.t({
  58. onentry (entry) {
  59. totalEntries++
  60. totalEntrySize += entry.size
  61. const p = entry.path
  62. if (p.startsWith('package/node_modules/')) {
  63. const name = p.match(/^package\/node_modules\/((?:@[^/]+\/)?[^/]+)/)[1]
  64. bundled.add(name)
  65. }
  66. files.push({
  67. path: entry.path.replace(/^package\//, ''),
  68. size: entry.size,
  69. mode: entry.mode,
  70. })
  71. },
  72. })
  73. stream.end(tarball)
  74. const integrity = await ssri.fromData(tarball, {
  75. algorithms: ['sha1', 'sha512'],
  76. })
  77. const comparator = ({ path: a }, { path: b }) => localeCompare(a, b)
  78. const isUpper = (str) => {
  79. const ch = str.charAt(0)
  80. return ch === ch.toUpperCase()
  81. }
  82. const uppers = files.filter(file => isUpper(file.path))
  83. const others = files.filter(file => !isUpper(file.path))
  84. uppers.sort(comparator)
  85. others.sort(comparator)
  86. const shasum = integrity.sha1[0].hexDigest()
  87. return {
  88. id: manifest._id || `${manifest.name}@${manifest.version}`,
  89. name: manifest.name,
  90. version: manifest.version,
  91. size: tarball.length,
  92. unpackedSize: totalEntrySize,
  93. shasum,
  94. integrity: ssri.parse(integrity.sha512[0]),
  95. filename: `${manifest.name}-${manifest.version}.tgz`,
  96. files: uppers.concat(others),
  97. entryCount: totalEntries,
  98. bundled: Array.from(bundled),
  99. }
  100. }
  101. module.exports = { logTar, getContents }