"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildEsbuildPlugin = exports.esbuildPlugin = exports.transformWithEsbuild = exports.stopService = exports.ensureService = void 0;
const path_1 = __importDefault(require("path"));
const chalk_1 = __importDefault(require("chalk"));
const utils_1 = require("../utils");
const merge_source_map_1 = __importDefault(require("merge-source-map"));
const pluginutils_1 = require("@rollup/pluginutils");
const debug = utils_1.createDebugger('vite:esbuild');
// lazy start the service
let _servicePromise;
async function ensureService() {
    if (!_servicePromise) {
        _servicePromise = require('esbuild').startService();
    }
    return _servicePromise;
}
exports.ensureService = ensureService;
async function stopService() {
    if (_servicePromise) {
        const service = await _servicePromise;
        service.stop();
        _servicePromise = undefined;
    }
}
exports.stopService = stopService;
async function transformWithEsbuild(code, filename, options, inMap) {
    const service = await ensureService();
    const resolvedOptions = {
        loader: path_1.default.extname(filename).slice(1),
        sourcemap: true,
        // ensure source file name contains full query
        sourcefile: filename,
        ...options
    };
    delete resolvedOptions.include;
    delete resolvedOptions.exclude;
    delete resolvedOptions.jsxInject;
    try {
        const result = await service.transform(code, resolvedOptions);
        if (inMap) {
            const nextMap = JSON.parse(result.map);
            // merge-source-map will overwrite original sources if newMap also has
            // sourcesContent
            nextMap.sourcesContent = [];
            return {
                ...result,
                map: merge_source_map_1.default(inMap, nextMap)
            };
        }
        else {
            return {
                ...result,
                map: JSON.parse(result.map)
            };
        }
    }
    catch (e) {
        debug(`esbuild error with options used: `, resolvedOptions);
        // patch error information
        if (e.errors) {
            e.frame = '';
            e.errors.forEach((m) => {
                e.frame += `\n` + prettifyMessage(m, code);
            });
            e.loc = e.errors[0].location;
        }
        throw e;
    }
}
exports.transformWithEsbuild = transformWithEsbuild;
function esbuildPlugin(options = {}) {
    const filter = pluginutils_1.createFilter(options.include || /\.(tsx?|jsx)($|\?)/, options.exclude);
    return {
        name: 'vite:esbuild',
        async transform(code, id) {
            if (filter(id)) {
                const result = await transformWithEsbuild(code, id, options);
                if (result.warnings.length) {
                    result.warnings.forEach((m) => {
                        this.warn(prettifyMessage(m, code));
                    });
                }
                if (options.jsxInject) {
                    result.code = options.jsxInject + ';' + result.code;
                }
                return {
                    code: result.code,
                    map: result.map
                };
            }
        },
        async closeBundle() {
            await stopService();
        }
    };
}
exports.esbuildPlugin = esbuildPlugin;
exports.buildEsbuildPlugin = (config) => {
    return {
        name: 'vite:esbuild-transpile',
        async renderChunk(code, chunk) {
            const target = config.build.target;
            const minify = config.build.minify === 'esbuild';
            if (!target && !minify) {
                return null;
            }
            return transformWithEsbuild(code, chunk.fileName, {
                target: target || undefined,
                minify
            });
        },
        async closeBundle() {
            await stopService();
        }
    };
};
function prettifyMessage(m, code) {
    let res = chalk_1.default.yellow(m.text);
    if (m.location) {
        const lines = code.split(/\r?\n/g);
        const line = Number(m.location.line);
        const column = Number(m.location.column);
        const offset = lines
            .slice(0, line - 1)
            .map((l) => l.length)
            .reduce((total, l) => total + l + 1, 0) + column;
        res += `\n` + utils_1.generateCodeFrame(code, offset, offset + 1);
    }
    return res + `\n`;
}
//# sourceMappingURL=esbuild.js.map