utils.js 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // @ts-check
  2. import fs from 'node:fs'
  3. import pico from 'picocolors'
  4. import { createRequire } from 'node:module'
  5. import { spawn } from 'node:child_process'
  6. import path from 'node:path'
  7. const require = createRequire(import.meta.url)
  8. const packagesPath = path.resolve(import.meta.dirname, '../packages')
  9. export const targets = fs.readdirSync(packagesPath).filter(f => {
  10. const folder = path.resolve(packagesPath, f)
  11. if (
  12. !fs.statSync(folder).isDirectory() ||
  13. !fs.existsSync(`${folder}/package.json`)
  14. ) {
  15. return false
  16. }
  17. const pkg = require(`${folder}/package.json`)
  18. if (pkg.private && !pkg.buildOptions) {
  19. return false
  20. }
  21. return true
  22. })
  23. /**
  24. *
  25. * @param {ReadonlyArray<string>} partialTargets
  26. * @param {boolean | undefined} includeAllMatching
  27. */
  28. export function fuzzyMatchTarget(partialTargets, includeAllMatching) {
  29. /** @type {Array<string>} */
  30. const matched = []
  31. partialTargets.forEach(partialTarget => {
  32. if (!includeAllMatching && targets.includes(partialTarget)) {
  33. matched.push(partialTarget)
  34. return
  35. }
  36. for (const target of targets) {
  37. if (target.match(partialTarget)) {
  38. matched.push(target)
  39. if (!includeAllMatching) {
  40. break
  41. }
  42. }
  43. }
  44. })
  45. if (matched.length) {
  46. return matched
  47. } else {
  48. console.log()
  49. console.error(
  50. ` ${pico.white(pico.bgRed(' ERROR '))} ${pico.red(
  51. `Target ${pico.underline(partialTargets.toString())} not found!`,
  52. )}`,
  53. )
  54. console.log()
  55. process.exit(1)
  56. }
  57. }
  58. /**
  59. * @param {string} command
  60. * @param {ReadonlyArray<string>} args
  61. * @param {object} [options]
  62. * @returns {Promise<{ ok: boolean, code: number | null, stderr: string, stdout: string }>}
  63. */
  64. export async function exec(command, args, options) {
  65. return new Promise((resolve, reject) => {
  66. const _process = spawn(command, args, {
  67. stdio: [
  68. 'ignore', // stdin
  69. 'pipe', // stdout
  70. 'pipe', // stderr
  71. ],
  72. ...options,
  73. shell: process.platform === 'win32',
  74. })
  75. /**
  76. * @type {Buffer[]}
  77. */
  78. const stderrChunks = []
  79. /**
  80. * @type {Buffer[]}
  81. */
  82. const stdoutChunks = []
  83. _process.stderr?.on('data', chunk => {
  84. stderrChunks.push(chunk)
  85. })
  86. _process.stdout?.on('data', chunk => {
  87. stdoutChunks.push(chunk)
  88. })
  89. _process.on('error', error => {
  90. reject(error)
  91. })
  92. _process.on('exit', code => {
  93. const ok = code === 0
  94. const stderr = Buffer.concat(stderrChunks).toString().trim()
  95. const stdout = Buffer.concat(stdoutChunks).toString().trim()
  96. const result = { ok, code, stderr, stdout }
  97. resolve(result)
  98. })
  99. })
  100. }
  101. /**
  102. * @param {boolean=} short
  103. */
  104. export async function getSha(short) {
  105. return (
  106. await exec('git', ['rev-parse', ...(short ? ['--short'] : []), 'HEAD'])
  107. ).stdout
  108. }