Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
151 lines
4.8 KiB
TypeScript
151 lines
4.8 KiB
TypeScript
import { execFile, execFileSync } from 'child_process'
|
|
import path, { join } from 'path'
|
|
import { REPO_ROOT, readJsonIfExists } from './lib/file'
|
|
import { getAllWorkspacePackages } from './lib/workspace'
|
|
|
|
async function main() {
|
|
const allWorkspaces = await getAllWorkspacePackages()
|
|
const tsconfigFiles: string[] = []
|
|
for (const workspace of allWorkspaces) {
|
|
const tsconfigFile = path.join(workspace.path, 'tsconfig.json')
|
|
const tsconfigExists = await readJsonIfExists(tsconfigFile)
|
|
if (tsconfigExists) tsconfigFiles.push(tsconfigFile)
|
|
}
|
|
|
|
const args = ['--build']
|
|
const isWatchMode = process.argv.includes('--watch')
|
|
if (process.argv.includes('--force')) args.push('--force')
|
|
if (isWatchMode) args.push('--watch')
|
|
if (process.argv.includes('--preserveWatchOutput')) args.push('--preserveWatchOutput')
|
|
if (process.argv.includes('--extendedDiagnostics')) args.push('--extendedDiagnostics')
|
|
|
|
const compilerPath = join(REPO_ROOT, 'node_modules/.bin/tsc')
|
|
|
|
// tsc currently struggles to resolve some workspace package imports on a fresh checkout
|
|
// when all projects are passed in a single --build invocation. Building package
|
|
// workspaces first in topological order (leaves before dependents) produces the
|
|
// declaration outputs needed for the full graph.
|
|
const packageTsconfigFiles = tsconfigFiles.filter((file) =>
|
|
file.includes(`${path.sep}packages${path.sep}`)
|
|
)
|
|
if (packageTsconfigFiles.length > 0) {
|
|
const sortedPackageTsconfigFiles = await topoSortTsconfigs(packageTsconfigFiles)
|
|
const bootstrapArgs = ['--build']
|
|
if (process.argv.includes('--force')) bootstrapArgs.push('--force')
|
|
for (const tsconfigFile of sortedPackageTsconfigFiles) {
|
|
execFileSync(compilerPath, [...bootstrapArgs, tsconfigFile], { stdio: 'inherit' })
|
|
}
|
|
}
|
|
|
|
// In watch mode, use execFileSync with inherited stdio - it handles everything
|
|
if (isWatchMode) {
|
|
execFileSync(compilerPath, [...args, ...tsconfigFiles], { stdio: 'inherit' })
|
|
return
|
|
}
|
|
|
|
// In non-watch mode, capture output so we can show a clear error summary
|
|
const output: string[] = []
|
|
const errors: string[] = []
|
|
|
|
return new Promise<void>((resolve) => {
|
|
const childProcess = execFile(
|
|
compilerPath,
|
|
[...args, ...tsconfigFiles],
|
|
{ cwd: REPO_ROOT },
|
|
(err) => {
|
|
if (err) {
|
|
// Extract TypeScript errors from the output
|
|
const allOutput = output.join('') + errors.join('')
|
|
const tsErrors = allOutput.split('\n').filter((line) => {
|
|
// Match TypeScript error lines like: "file.ts(123,4): error TS2304: ..."
|
|
return /\.(ts|tsx|js|jsx)\(\d+,\d+\):\s+error\s+TS\d+:/i.test(line)
|
|
})
|
|
|
|
// Display a clear error summary at the end
|
|
console.error('\n' + '='.repeat(80))
|
|
console.error('❌ TypeScript type checking failed')
|
|
console.error('='.repeat(80) + '\n')
|
|
|
|
if (tsErrors.length > 0) {
|
|
console.error('TypeScript errors:')
|
|
tsErrors.forEach((error) => console.error(error))
|
|
console.error('')
|
|
} else if (errors.length > 0) {
|
|
// Fallback: show stderr if we couldn't extract specific errors
|
|
console.error('Error output:')
|
|
console.error(errors.join(''))
|
|
console.error('')
|
|
}
|
|
|
|
console.error('Please fix the errors above and try again.')
|
|
console.error('='.repeat(80) + '\n')
|
|
process.exit(err.code || 1)
|
|
} else {
|
|
resolve()
|
|
}
|
|
}
|
|
)
|
|
|
|
// Capture and display stdout in real-time
|
|
childProcess.stdout?.on('data', (chunk) => {
|
|
const text = chunk.toString()
|
|
output.push(text)
|
|
process.stdout.write(text)
|
|
})
|
|
|
|
// Capture and display stderr in real-time
|
|
childProcess.stderr?.on('data', (chunk) => {
|
|
const text = chunk.toString()
|
|
errors.push(text)
|
|
process.stderr.write(text)
|
|
})
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Sort tsconfig files in topological order based on their `references` fields,
|
|
* so that leaf packages (no dependencies) come first and dependents come after
|
|
* all their dependencies. This is needed because tsc --build doesn't always
|
|
* resolve the reference graph correctly on a fresh checkout.
|
|
*/
|
|
async function topoSortTsconfigs(tsconfigFiles: string[]): Promise<string[]> {
|
|
const dirToFile = new Map<string, string>()
|
|
const deps = new Map<string, string[]>()
|
|
|
|
for (const file of tsconfigFiles) {
|
|
const dir = path.dirname(file)
|
|
dirToFile.set(dir, file)
|
|
const tsconfig = await readJsonIfExists(file)
|
|
const refs: string[] = []
|
|
if (tsconfig?.references) {
|
|
for (const ref of tsconfig.references) {
|
|
refs.push(path.resolve(dir, ref.path))
|
|
}
|
|
}
|
|
deps.set(dir, refs)
|
|
}
|
|
|
|
const visited = new Set<string>()
|
|
const sorted: string[] = []
|
|
|
|
function visit(dir: string) {
|
|
if (visited.has(dir)) return
|
|
visited.add(dir)
|
|
for (const dep of deps.get(dir) ?? []) {
|
|
if (dirToFile.has(dep)) visit(dep)
|
|
}
|
|
sorted.push(dirToFile.get(dir)!)
|
|
}
|
|
|
|
for (const dir of dirToFile.keys()) {
|
|
visit(dir)
|
|
}
|
|
|
|
return sorted
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error('Unexpected error:', err)
|
|
process.exit(1)
|
|
})
|