#!/usr/bin/env node
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const node_child_process_1 = require("node:child_process");
const node_crypto_1 = require("node:crypto");
const node_fs_1 = require("node:fs");
const node_path_1 = require("node:path");
const promises_1 = require("node:timers/promises");
const env_1 = require("../env");
const common_1 = require("./common");
const dev_state_1 = require("./dev-state");
env_1.Env.APP_ENV ||= 'development';
const projectDir = (0, common_1.findProjectRoot)();
const { stateFile: devStateFile, buildLockFile: devLockFile } = (0, dev_state_1.getDevStatePaths)(projectDir);
async function main(args = process.argv.slice(2)) {
    const [cmd, ...rest] = args;
    switch (cmd) {
        case 'clean':
            (0, common_1.cleanDist)(projectDir);
            return 0;
        case 'build':
            return await cmdBuild(rest);
        case 'run':
            return cmdRun(rest);
        case 'repl':
            return cmdRepl(rest);
        case 'test':
            return cmdTest(rest);
        case 'migrate':
            return cmdMigrate('run', rest);
        case 'migrate:create':
            return cmdMigrate('create', rest);
        case 'migrate:reset':
            return cmdMigrate('reset', rest);
        case 'migrate:charset':
            return cmdMigrate('charset', rest);
        case 'openapi:generate':
            return cmdOpenApiGenerate(rest);
        default:
            printUsage();
            return 1;
    }
}
function printUsage() {
    console.error(`Usage: tsf-dev <command> [options]

Commands:
  clean                         Remove dist/
  build [--watch] [-p file]     Clean and compile TypeScript
  run [--debug] [script] -- server:start
                                Ensure a watch build and run node --watch
  repl [--debug] [script]       Build and start a fresh application REPL
  test [--debug] [-p file]      Build tests and run tsf-test
  migrate [-p file]             Build and run compiled migrations
  migrate:create [-p file]      Build and create a raw SQL migration
  migrate:reset [-p file]       Build and create a base migration
  migrate:charset [-p file]     Build and standardize MySQL charset/collation
  openapi:generate [-p file]    Build and write openapi.yaml

Common options:
  -p, --tsconfig <file>         TypeScript config file`);
}
async function cmdBuild(args) {
    const watch = takeFlag(args, '--watch');
    const tsconfig = (0, common_1.extractTsconfigArg)(args) ?? 'tsconfig.json';
    const status = getBuildStatus(tsconfig);
    if (watch) {
        if (!isDevRunning() && !status.fresh)
            (0, common_1.cleanDist)(projectDir);
        const compiler = startTscWatch(tsconfig);
        const stop = () => compiler.kill('SIGTERM');
        process.on('SIGINT', stop);
        process.on('SIGTERM', stop);
        return await compiler.closed;
    }
    return runTscIfNeeded(tsconfig, status);
}
async function cmdRun(args) {
    const separator = args.indexOf('--');
    const ownArgs = separator >= 0 ? args.slice(0, separator) : args;
    const childArgs = separator >= 0 ? args.slice(separator + 1) : [];
    const debug = takeFlag(ownArgs, '--debug');
    const tsconfig = (0, common_1.extractTsconfigArg)(ownArgs) ?? 'tsconfig.json';
    const script = ownArgs.find(arg => !arg.startsWith('-')) ?? '.';
    const tscChild = await ensureDevBuild(tsconfig);
    const runId = (0, node_crypto_1.randomUUID)();
    const now = Date.now();
    (0, dev_state_1.registerDevRun)(devStateFile, {
        runId,
        runnerPid: process.pid,
        script,
        command: childArgs,
        startedAt: now,
        updatedAt: now
    });
    let registered = true;
    const unregister = () => {
        if (!registered)
            return;
        registered = false;
        try {
            (0, dev_state_1.unregisterDevRun)(devStateFile, runId, process.pid);
        }
        catch {
            // Stale state is pruned by PID on the next command if shutdown races with another writer.
        }
    };
    process.on('exit', unregister);
    const inspectFlag = debug ? '--inspect-brk' : '--inspect';
    const inspectArg = env_1.Env.PORT ? `${inspectFlag}=${Number(env_1.Env.PORT) + 1000}` : inspectFlag;
    const child = (0, node_child_process_1.spawn)(process.execPath, ['--enable-source-maps', '--watch', '--watch-preserve-output', inspectArg, script, ...childArgs], {
        cwd: projectDir,
        env: {
            ...process.env,
            [dev_state_1.TSF_DEV_STATE_FILE_ENV]: devStateFile,
            [dev_state_1.TSF_DEV_RUN_ID_ENV]: runId,
            [dev_state_1.TSF_DEV_RUNNER_PID_ENV]: String(process.pid)
        },
        stdio: 'inherit'
    });
    return await new Promise(resolve => {
        child.on('close', code => {
            unregister();
            tscChild?.kill();
            resolve(code ?? 0);
        });
        process.on('SIGTERM', () => {
            child.kill('SIGTERM');
            tscChild?.kill('SIGTERM');
        });
    });
}
function cmdRepl(args) {
    if (args.includes('--help') || args.includes('-h')) {
        printReplUsage();
        return 0;
    }
    const debug = takeFlag(args, '--debug');
    const tsconfig = (0, common_1.extractTsconfigArg)(args) ?? 'tsconfig.json';
    let script = '.';
    let scriptSelected = false;
    const entrypointArgs = [];
    for (let index = 0; index < args.length; index++) {
        const arg = args[index];
        if (arg === '--eval' || arg === '-e') {
            const value = args[index + 1];
            if (value === undefined)
                throw new Error(`${arg} requires JavaScript source`);
            entrypointArgs.push('--eval', value);
            index++;
            continue;
        }
        if (arg.startsWith('--eval=')) {
            entrypointArgs.push('--eval', arg.slice('--eval='.length));
            continue;
        }
        if (arg === '--script') {
            const value = args[index + 1];
            if (value === undefined)
                throw new Error('--script requires an entrypoint path');
            script = value;
            scriptSelected = true;
            index++;
            continue;
        }
        if (arg.startsWith('--script=')) {
            script = arg.slice('--script='.length);
            scriptSelected = true;
            continue;
        }
        if (!arg.startsWith('-') && !scriptSelected) {
            script = arg;
            scriptSelected = true;
            continue;
        }
        throw new Error(`Unknown repl option: ${arg}`);
    }
    const tscStatus = runTscIfNeeded(tsconfig);
    if (tscStatus !== 0)
        return tscStatus;
    const debugArgs = debug ? [env_1.Env.PORT ? `--inspect-brk=${Number(env_1.Env.PORT) + 1000}` : '--inspect-brk'] : [];
    return (0, common_1.runNode)([...debugArgs, '--enable-source-maps', script, 'repl', ...entrypointArgs], projectDir, {
        APP_ENV: env_1.Env.APP_ENV ?? 'development',
        TSF_TSCONFIG: tsconfig
    }).status;
}
function printReplUsage() {
    console.log(`Usage: tsf-dev repl [options] [script]\n\nBuild and start a fresh application REPL without starting HTTP listeners or workers.\n\nOptions:\n  --debug                   Start the application with the Node inspector paused\n  --eval <code>, -e <code>  Evaluate JavaScript once and exit\n  --script <path>           Application entrypoint (default: .)\n  -p, --tsconfig <file>     TypeScript config file (default: tsconfig.json)\n  --help, -h                Show this help\n`);
}
function cmdTest(args) {
    const debug = takeFlag(args, '--debug');
    const tsconfig = (0, common_1.extractTsconfigArg)(args) ?? ((0, node_fs_1.existsSync)((0, node_path_1.join)(projectDir, 'tsconfig.test.json')) ? 'tsconfig.test.json' : 'tsconfig.json');
    const tscStatus = runTscIfNeeded(tsconfig);
    if (tscStatus !== 0)
        return tscStatus;
    const testCli = (0, node_path_1.join)(__dirname, 'tsf-test.js');
    const testArgs = debug ? ['--inspect-brk=9268', ...args] : args;
    const testRunTs = env_1.Env.TEST_RUN_TS ?? String(Math.floor(Date.now() / 1000));
    return (0, common_1.runNode)([testCli, ...testArgs], projectDir, {
        APP_ENV: 'test',
        TZ: 'UTC',
        TEST_RUN_TS: testRunTs,
        TSF_TSCONFIG: tsconfig
    }).status;
}
function cmdMigrate(command, args) {
    const debug = takeFlag(args, '--debug');
    const tsconfig = (0, common_1.extractTsconfigArg)(args) ?? 'tsconfig.json';
    const tscStatus = runTscIfNeeded(tsconfig);
    if (tscStatus !== 0)
        return tscStatus;
    return (0, common_1.runNode)([...(debug ? ['--inspect-brk=9226'] : []), '--enable-source-maps', '.', `migrate:${command}`, ...args], projectDir, {
        TSF_TSCONFIG: tsconfig
    }).status;
}
function cmdOpenApiGenerate(args) {
    const tsconfig = (0, common_1.extractTsconfigArg)(args) ?? 'tsconfig.json';
    const tscStatus = runTscIfNeeded(tsconfig);
    if (tscStatus !== 0)
        return tscStatus;
    return (0, common_1.runNode)(['--enable-source-maps', '.', 'openapi:generate', ...args], projectDir, {
        APP_ENV: env_1.Env.APP_ENV ?? 'development'
    }).status;
}
function runTsc(tsconfig) {
    return (0, common_1.runNode)([getTscPath(), '-p', tsconfig], projectDir).status;
}
function runTscIfNeeded(tsconfig, existingStatus = getBuildStatus(tsconfig)) {
    if (existingStatus.fresh) {
        console.log(`tsf-dev: build is up to date for ${tsconfig}`);
        return 0;
    }
    if (!isDevRunning())
        (0, common_1.cleanDist)(projectDir);
    const status = runTsc(tsconfig);
    if (status === 0)
        writeBuildState(tsconfig);
    return status;
}
function takeFlag(args, flag) {
    const index = args.indexOf(flag);
    if (index === -1)
        return false;
    args.splice(index, 1);
    return true;
}
function isDevRunning() {
    const state = (0, dev_state_1.pruneDevState)(devStateFile);
    return !!state?.ready && state.pids.some(dev_state_1.isPidAlive);
}
function setDevBuildReady(ready) {
    (0, dev_state_1.updateDevState)(devStateFile, state => {
        state.ready = ready;
        state.pids = state.pids.filter(dev_state_1.isPidAlive);
    });
}
function tryAcquireBuildLock() {
    try {
        (0, node_fs_1.writeFileSync)(devLockFile, String(process.pid), { flag: 'wx' });
        return true;
    }
    catch (error) {
        if (error.code !== 'EEXIST')
            throw error;
        try {
            const pid = Number((0, node_fs_1.readFileSync)(devLockFile, 'utf8'));
            if (!(0, dev_state_1.isPidAlive)(pid)) {
                (0, node_fs_1.unlinkSync)(devLockFile);
                return tryAcquireBuildLock();
            }
        }
        catch {
            return tryAcquireBuildLock();
        }
        return false;
    }
}
function releaseBuildLock() {
    try {
        (0, node_fs_1.unlinkSync)(devLockFile);
    }
    catch {
        // ignore
    }
}
async function ensureDevBuild(tsconfig) {
    if (isDevRunning())
        return undefined;
    if (!tryAcquireBuildLock()) {
        while (true) {
            if ((0, dev_state_1.readDevState)(devStateFile)?.ready)
                return undefined;
            await (0, promises_1.setTimeout)(200);
            try {
                const pid = Number((0, node_fs_1.readFileSync)(devLockFile, 'utf8'));
                if (!(0, dev_state_1.isPidAlive)(pid) && tryAcquireBuildLock())
                    break;
            }
            catch {
                if (tryAcquireBuildLock())
                    break;
            }
        }
    }
    const status = getBuildStatus(tsconfig);
    if (status.fresh) {
        setDevBuildReady(true);
        releaseBuildLock();
        return watchBuildInputsUntilChanged(tsconfig, collectCompilerWatchInputs(tsconfig));
    }
    (0, common_1.cleanDist)(projectDir);
    setDevBuildReady(false);
    const compiler = startTscWatch(tsconfig);
    try {
        await compiler.ready;
    }
    catch (error) {
        releaseBuildLock();
        throw error;
    }
    setDevBuildReady(true);
    releaseBuildLock();
    return compiler;
}
function startTscWatch(tsconfig) {
    let stopped = false;
    let running = false;
    let rerun = false;
    let watcher;
    let pollTimer;
    let debounceTimer;
    let baseline = collectCompilerWatchInputs(tsconfig);
    let resolveReady;
    let resolveClosed;
    let readyResolved = false;
    const ready = new Promise(resolve => {
        resolveReady = resolve;
    });
    const closed = new Promise(resolve => {
        resolveClosed = resolve;
    });
    const markReady = () => {
        if (readyResolved)
            return;
        readyResolved = true;
        resolveReady();
    };
    const close = (code = 0) => {
        stopped = true;
        watcher?.close();
        if (pollTimer)
            clearInterval(pollTimer);
        if (debounceTimer)
            clearTimeout(debounceTimer);
        resolveClosed(code);
    };
    const rebuild = () => {
        if (stopped || running) {
            rerun = true;
            return;
        }
        running = true;
        baseline = collectCompilerWatchInputs(tsconfig);
        process.stdout.write(`[ttsc] rebuilding at ${new Date().toLocaleTimeString()}\n`);
        const status = runTsc(tsconfig);
        if (status === 0) {
            writeBuildState(tsconfig);
            process.stdout.write('[ttsc] watch build complete\n');
            markReady();
        }
        else {
            process.stdout.write('[ttsc] watch build failed\n');
        }
        running = false;
        if (rerun) {
            rerun = false;
            scheduleCheck();
        }
    };
    const checkForChanges = () => {
        if (stopped)
            return;
        const current = collectCompilerWatchInputs(tsconfig);
        if (current.fingerprint === baseline.fingerprint && current.fileCount === baseline.fileCount)
            return;
        baseline = current;
        rebuild();
    };
    const scheduleCheck = () => {
        if (debounceTimer)
            clearTimeout(debounceTimer);
        debounceTimer = setTimeout(checkForChanges, 100);
    };
    process.stdout.write(`[ttsc] watching ${(0, node_path_1.relative)(projectDir, (0, node_path_1.dirname)((0, node_path_1.resolve)(projectDir, tsconfig))) || '.'}\n`);
    try {
        watcher = (0, node_fs_1.watch)(projectDir, { recursive: true }, (_event, filename) => {
            if (isPotentialCompilerWatchInputPath(filename))
                scheduleCheck();
        });
    }
    catch {
        pollTimer = setInterval(checkForChanges, 1000);
    }
    rebuild();
    return {
        ready,
        closed,
        kill(signal) {
            close(typeof signal === 'number' ? signal : 0);
        }
    };
}
function watchBuildInputsUntilChanged(tsconfig, baseline) {
    let stopped = false;
    let compiler;
    let watcher;
    let pollTimer;
    let debounceTimer;
    const startCompiler = () => {
        if (stopped || compiler)
            return;
        watcher?.close();
        watcher = undefined;
        if (pollTimer)
            clearInterval(pollTimer);
        pollTimer = undefined;
        compiler = startTscWatch(tsconfig);
    };
    const checkForChanges = () => {
        if (stopped || compiler)
            return;
        const current = collectCompilerWatchInputs(tsconfig);
        if (current.fingerprint !== baseline.fingerprint || current.fileCount !== baseline.fileCount)
            startCompiler();
    };
    const scheduleCheck = () => {
        if (debounceTimer)
            clearTimeout(debounceTimer);
        debounceTimer = setTimeout(checkForChanges, 100);
    };
    try {
        watcher = (0, node_fs_1.watch)(projectDir, { recursive: true }, (_event, filename) => {
            if (isPotentialCompilerWatchInputPath(filename))
                scheduleCheck();
        });
    }
    catch {
        pollTimer = setInterval(checkForChanges, 1000);
    }
    return {
        kill(signal) {
            stopped = true;
            watcher?.close();
            if (pollTimer)
                clearInterval(pollTimer);
            if (debounceTimer)
                clearTimeout(debounceTimer);
            compiler?.kill(signal);
        }
    };
}
function getBuildStatus(tsconfig) {
    const inputs = collectBuildInputs(tsconfig);
    const outputs = collectBuildOutputs();
    const state = readBuildState(tsconfig);
    return {
        fresh: !!state &&
            state.version === 2 &&
            state.fingerprint === inputs.fingerprint &&
            state.inputCount === inputs.fileCount &&
            state.outputFingerprint === outputs.fingerprint &&
            state.outputCount === outputs.fileCount,
        inputs,
        outputs
    };
}
function readBuildState(tsconfig) {
    try {
        return JSON.parse((0, node_fs_1.readFileSync)(getBuildStateFile(tsconfig), 'utf8'));
    }
    catch {
        return undefined;
    }
}
function writeBuildState(tsconfig) {
    const inputs = collectBuildInputs(tsconfig);
    const outputs = collectBuildOutputs();
    const state = {
        version: 2,
        tsconfig: normalizeBuildPath(tsconfig),
        fingerprint: inputs.fingerprint,
        inputCount: inputs.fileCount,
        outputFingerprint: outputs.fingerprint,
        outputCount: outputs.fileCount,
        builtAt: new Date().toISOString()
    };
    (0, node_fs_1.mkdirSync)((0, node_path_1.join)(projectDir, 'dist'), { recursive: true });
    (0, node_fs_1.writeFileSync)(getBuildStateFile(tsconfig), JSON.stringify(state, null, 2));
}
function getBuildStateFile(tsconfig) {
    const hash = (0, node_crypto_1.createHash)('md5').update(normalizeBuildPath(tsconfig)).digest('hex').slice(0, 12);
    return (0, node_path_1.join)(projectDir, 'dist', `.tsf-dev-build-${hash}.json`);
}
function collectBuildInputs(tsconfig) {
    const files = new Set();
    collectBuildInputFiles(projectDir, files);
    const tsconfigPath = (0, node_path_1.resolve)(projectDir, tsconfig);
    if ((0, node_fs_1.existsSync)(tsconfigPath))
        files.add(tsconfigPath);
    return fingerprintFiles(files);
}
function collectCompilerWatchInputs(tsconfig) {
    const files = new Set();
    const tsconfigDir = (0, node_path_1.dirname)((0, node_path_1.resolve)(projectDir, tsconfig));
    const config = readTsconfig(tsconfig);
    const rawIncludes = config.files ?? config.include;
    const includes = normalizeTsconfigPatterns(rawIncludes ?? ['**/*']);
    const excludes = normalizeTsconfigPatterns([
        ...(typeof config.compilerOptions?.outDir === 'string' ? [config.compilerOptions.outDir] : []),
        ...(config.exclude ?? [])
    ]);
    for (const file of rawIncludes === undefined ? collectAllCompilerWatchFiles(tsconfigDir) : collectTsconfigPatternFiles(tsconfigDir, includes)) {
        const relativePath = (0, node_path_1.relative)(tsconfigDir, file).replace(/\\/g, '/');
        if (isIgnoredCompilerWatchPath(relativePath) || matchesTsconfigPattern(relativePath, excludes))
            continue;
        if (isCompilerWatchInputFile(relativePath))
            files.add(file);
    }
    return fingerprintFiles(files);
}
function readTsconfig(tsconfig) {
    try {
        return JSON.parse(stripJsonComments((0, node_fs_1.readFileSync)((0, node_path_1.resolve)(projectDir, tsconfig), 'utf8')));
    }
    catch {
        return {};
    }
}
function collectAllCompilerWatchFiles(dir) {
    const files = new Set();
    collectCompilerWatchFiles(dir, dir, files);
    return files;
}
function collectCompilerWatchFiles(rootDir, dir, files) {
    for (const entry of (0, node_fs_1.readdirSync)(dir, { withFileTypes: true })) {
        const path = (0, node_path_1.join)(dir, entry.name);
        const relativePath = (0, node_path_1.relative)(rootDir, path).replace(/\\/g, '/');
        if (isIgnoredCompilerWatchPath(relativePath))
            continue;
        if (entry.isDirectory()) {
            collectCompilerWatchFiles(rootDir, path, files);
            continue;
        }
        if (entry.isFile() && isCompilerWatchInputFile(entry.name))
            files.add(path);
    }
}
function collectTsconfigPatternFiles(baseDir, patterns) {
    const files = new Set();
    for (const pattern of patterns) {
        for (const entry of (0, node_fs_1.globSync)(pattern, { cwd: baseDir })) {
            const path = (0, node_path_1.resolve)(baseDir, entry);
            if (isFile(path))
                files.add(path);
        }
    }
    return files;
}
function normalizeTsconfigPatterns(patterns) {
    return patterns
        .filter(pattern => typeof pattern === 'string' && pattern.trim() !== '')
        .map(pattern => {
        const normalized = pattern.replace(/\\/g, '/').replace(/^\.\//, '').replace(/\/+$/, '');
        if (hasGlobMagic(normalized) || isCompilerWatchInputFile(normalized))
            return normalized;
        return `${normalized}/**/*`;
    });
}
function matchesTsconfigPattern(path, patterns) {
    return patterns.some(pattern => globToRegExp(pattern).test(path));
}
function globToRegExp(pattern) {
    let regex = '^';
    for (let index = 0; index < pattern.length; index++) {
        const char = pattern[index];
        const next = pattern[index + 1];
        if (char === '*') {
            if (next === '*') {
                const after = pattern[index + 2];
                if (after === '/') {
                    regex += '(?:.*/)?';
                    index += 2;
                }
                else {
                    regex += '.*';
                    index += 1;
                }
            }
            else {
                regex += '[^/]*';
            }
        }
        else if (char === '?') {
            regex += '[^/]';
        }
        else {
            regex += escapeRegExp(char);
        }
    }
    return new RegExp(`${regex}$`);
}
function hasGlobMagic(pattern) {
    return /[*?[\]{}]/.test(pattern);
}
function isFile(path) {
    try {
        return (0, node_fs_1.statSync)(path).isFile();
    }
    catch {
        return false;
    }
}
function stripJsonComments(text) {
    let output = '';
    let inString = false;
    let escaped = false;
    let lineComment = false;
    let blockComment = false;
    for (let index = 0; index < text.length; index++) {
        const char = text[index];
        const next = text[index + 1];
        if (lineComment) {
            if (char === '\n' || char === '\r') {
                lineComment = false;
                output += char;
            }
            continue;
        }
        if (blockComment) {
            if (char === '*' && next === '/') {
                blockComment = false;
                index++;
            }
            continue;
        }
        if (inString) {
            output += char;
            if (escaped) {
                escaped = false;
            }
            else if (char === '\\') {
                escaped = true;
            }
            else if (char === '"') {
                inString = false;
            }
            continue;
        }
        if (char === '"') {
            inString = true;
            output += char;
            continue;
        }
        if (char === '/' && next === '/') {
            lineComment = true;
            index++;
            continue;
        }
        if (char === '/' && next === '*') {
            blockComment = true;
            index++;
            continue;
        }
        output += char;
    }
    return output.replace(/,\s*([}\]])/g, '$1');
}
function collectBuildOutputs() {
    const files = new Set();
    collectBuildOutputFiles((0, node_path_1.join)(projectDir, 'dist'), files);
    return fingerprintFiles(files);
}
function fingerprintFiles(files) {
    const hash = (0, node_crypto_1.createHash)('sha256');
    const sorted = [...files].sort();
    for (const file of sorted) {
        hash.update(normalizeBuildPath(file));
        hash.update('\0');
        hash.update((0, node_fs_1.readFileSync)(file));
        hash.update('\n');
    }
    return {
        fingerprint: hash.digest('hex'),
        fileCount: sorted.length
    };
}
function collectBuildInputFiles(dir, files) {
    for (const entry of (0, node_fs_1.readdirSync)(dir, { withFileTypes: true })) {
        if (entry.name === 'node_modules' || entry.name === 'dist' || entry.name === '.git' || entry.name === '.yarn')
            continue;
        const path = (0, node_path_1.join)(dir, entry.name);
        if (entry.isDirectory()) {
            collectBuildInputFiles(path, files);
            continue;
        }
        if (!entry.isFile())
            continue;
        if (isBuildInputFile(entry.name))
            files.add(path);
    }
}
function collectBuildOutputFiles(dir, files) {
    if (!(0, node_fs_1.existsSync)(dir))
        return;
    for (const entry of (0, node_fs_1.readdirSync)(dir, { withFileTypes: true })) {
        const path = (0, node_path_1.join)(dir, entry.name);
        if (entry.isDirectory()) {
            collectBuildOutputFiles(path, files);
            continue;
        }
        if (!entry.isFile() || isBuildStateFile(entry.name))
            continue;
        files.add(path);
    }
}
function isBuildInputFile(name) {
    return (name.endsWith('.ts') ||
        name.endsWith('.tsx') ||
        name.endsWith('.mts') ||
        name.endsWith('.cts') ||
        name.endsWith('.js') ||
        name.endsWith('.jsx') ||
        name.endsWith('.mjs') ||
        name.endsWith('.cjs') ||
        name.endsWith('.json') ||
        name === 'yarn.lock');
}
function isPotentialCompilerWatchInputPath(filename) {
    if (!filename)
        return true;
    const normalized = filename.toString().replace(/\\/g, '/');
    if (isIgnoredCompilerWatchPath(normalized))
        return false;
    const segments = normalized.split('/');
    return isCompilerWatchInputFile(segments.at(-1) ?? normalized);
}
function isCompilerWatchInputFile(name) {
    return (name.endsWith('.ts') ||
        name.endsWith('.tsx') ||
        name.endsWith('.mts') ||
        name.endsWith('.cts') ||
        name.endsWith('.js') ||
        name.endsWith('.jsx') ||
        name.endsWith('.mjs') ||
        name.endsWith('.cjs'));
}
function isIgnoredCompilerWatchPath(path) {
    const segments = path.split('/');
    return segments.includes('node_modules') || segments.includes('dist') || segments.includes('.git') || segments.includes('.yarn');
}
function isBuildStateFile(name) {
    return /^\.tsf-dev-build-[a-f0-9]+\.json$/.test(name);
}
function normalizeBuildPath(path) {
    const absolute = (0, node_path_1.resolve)(projectDir, path);
    return (0, node_path_1.relative)(projectDir, absolute).replace(/\\/g, '/');
}
function escapeRegExp(value) {
    return value.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
}
function getTscPath() {
    return (0, node_path_1.join)((0, node_path_1.dirname)((0, common_1.resolveFromProject)(projectDir, 'ttsc/package.json')), 'lib/launcher/ttsc.js');
}
if (require.main === module) {
    main()
        .then(code => process.exit(code))
        .catch(error => {
        console.error(error);
        process.exit(1);
    });
}
//# sourceMappingURL=tsf-dev.js.map