Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled
179 lines
4.5 KiB
TypeScript
179 lines
4.5 KiB
TypeScript
import {
|
|
execSync,
|
|
execFileSync,
|
|
spawn,
|
|
ExecSyncOptionsWithStringEncoding,
|
|
} from 'child_process'
|
|
import { existsSync } from 'fs'
|
|
import globOrig from 'glob'
|
|
import { join } from 'path'
|
|
import { promisify } from 'util'
|
|
|
|
export const glob = promisify(globOrig)
|
|
|
|
export const NEXT_DIR = join(__dirname, '..')
|
|
|
|
/**
|
|
* @param {string} title
|
|
* @param {string | string[]} command
|
|
* @param {ExecSyncOptions} [opts]
|
|
* @returns {string}
|
|
*/
|
|
export function exec(title, command, opts?: ExecSyncOptionsWithStringEncoding) {
|
|
if (Array.isArray(command)) {
|
|
logCommand(title, command)
|
|
return execFileSync(command[0], command.slice(1), {
|
|
stdio: 'inherit',
|
|
cwd: NEXT_DIR,
|
|
...opts,
|
|
})
|
|
} else {
|
|
logCommand(title, command)
|
|
return execSync(command, {
|
|
stdio: 'inherit',
|
|
cwd: NEXT_DIR,
|
|
...opts,
|
|
})
|
|
}
|
|
}
|
|
|
|
class ExecError extends Error {
|
|
code: number | null
|
|
stdout: Buffer
|
|
stderr: Buffer
|
|
}
|
|
|
|
type ExecOutput = {
|
|
stdout: Buffer
|
|
stderr: Buffer
|
|
}
|
|
|
|
/**
|
|
* @param {string} title
|
|
* @param {string | string[]} command
|
|
* @param {SpawnOptions} [opts]
|
|
*/
|
|
export function execAsyncWithOutput(
|
|
title,
|
|
command,
|
|
opts?: Partial<ExecSyncOptionsWithStringEncoding>
|
|
): Promise<ExecOutput> {
|
|
logCommand(title, command)
|
|
const proc = spawn(command[0], command.slice(1), {
|
|
encoding: 'utf8',
|
|
stdio: ['inherit', 'pipe', 'pipe'],
|
|
cwd: NEXT_DIR,
|
|
...opts,
|
|
})
|
|
|
|
if (!proc || !proc.stdout || !proc.stderr) {
|
|
throw new Error(`Failed to spawn: ${title}`)
|
|
}
|
|
|
|
const stdout: Buffer[] = []
|
|
proc.stdout.on('data', (data) => {
|
|
process.stdout.write(data)
|
|
stdout.push(data)
|
|
})
|
|
const stderr: Buffer[] = []
|
|
proc.stderr.on('data', (data) => {
|
|
process.stderr.write(data)
|
|
stderr.push(data)
|
|
})
|
|
return new Promise((resolve, reject) => {
|
|
proc.on('exit', (code) => {
|
|
if (code === 0) {
|
|
return resolve({
|
|
stdout: Buffer.concat(stdout),
|
|
stderr: Buffer.concat(stderr),
|
|
})
|
|
}
|
|
const err = new ExecError(
|
|
`Command failed with exit code ${code}: ${prettyCommand(command)}`
|
|
)
|
|
err.code = code
|
|
err.stdout = Buffer.concat(stdout)
|
|
err.stderr = Buffer.concat(stderr)
|
|
reject(err)
|
|
})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* @template T
|
|
* @param {string} title
|
|
* @param {() => T} fn
|
|
* @returns {T}
|
|
*/
|
|
export function execFn<T>(title: string, fn: () => T): T {
|
|
logCommand(title, fn.toString())
|
|
return fn()
|
|
}
|
|
|
|
/**
|
|
* @param {string | string[]} command
|
|
*/
|
|
function prettyCommand(command: string | string[]): string {
|
|
if (Array.isArray(command)) command = command.join(' ')
|
|
return command.replace(/ -- .*/, ' -- …')
|
|
}
|
|
|
|
/**
|
|
* @param {string} title
|
|
* @param {string | string[]} [command]
|
|
*/
|
|
export function logCommand(title: string, command: string | string[]) {
|
|
if (command) {
|
|
const pretty = prettyCommand(command)
|
|
console.log(`\n\x1b[1;4m${title}\x1b[0m\n> \x1b[1m${pretty}\x1b[0m\n`)
|
|
} else {
|
|
console.log(`\n\x1b[1;4m${title}\x1b[0m\n`)
|
|
}
|
|
}
|
|
|
|
const DEFAULT_GLOBS = ['**', '!target', '!node_modules', '!crates', '!.turbo']
|
|
const FORCED_GLOBS = ['package.json', 'README*', 'LICENSE*', 'LICENCE*']
|
|
|
|
/**
|
|
* @param {string} path
|
|
* @returns {Promise<string[]>}
|
|
*/
|
|
export async function packageFiles(path: string): Promise<string[]> {
|
|
const { files = DEFAULT_GLOBS, main, bin } = require(`${path}/package.json`)
|
|
|
|
const allFiles: string[] = files.concat(
|
|
FORCED_GLOBS,
|
|
main ?? [],
|
|
Object.values(bin ?? {})
|
|
)
|
|
const isGlob = (f) => f.includes('*') || f.startsWith('!')
|
|
const simpleFiles = allFiles
|
|
.filter((f) => !isGlob(f) && existsSync(join(path, f)))
|
|
.map((f) => f.replace(/^\.\//, ''))
|
|
const globFiles = allFiles.filter(isGlob)
|
|
const globbedFiles = await glob(
|
|
`+(${globFiles.filter((f) => !f.startsWith('!')).join('|')})`,
|
|
{
|
|
cwd: path,
|
|
ignore: `+(${globFiles
|
|
.filter((f) => f.startsWith('!'))
|
|
.map((f) => f.slice(1))
|
|
.join('|')})`,
|
|
}
|
|
)
|
|
const packageFiles = [...globbedFiles, ...simpleFiles].sort()
|
|
const set = new Set()
|
|
return packageFiles.filter((f) => {
|
|
if (set.has(f)) return false
|
|
// We add the full path, but check for parent directories too.
|
|
// This catches the case where the whole directory is added and then a single file from the directory.
|
|
// The sorting before ensures that the directory comes before the files inside of the directory.
|
|
while (f.includes('/')) {
|
|
f = f.replace(/\/[^/]*$/, '')
|
|
if (set.has(f)) return false
|
|
}
|
|
set.add(f)
|
|
return true
|
|
})
|
|
}
|