utils.js 3.0 KB

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