"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SvelteFragmentMapper = exports.FallbackTranspiledSvelteDocument = exports.TranspiledSvelteDocument = exports.SvelteDocument = exports.TranspileErrorSource = void 0;
const trace_mapping_1 = require("@jridgewell/trace-mapping");
const documents_1 = require("../../lib/documents");
const utils_1 = require("../../utils");
var TranspileErrorSource;
(function (TranspileErrorSource) {
    TranspileErrorSource["Script"] = "Script";
    TranspileErrorSource["Style"] = "Style";
})(TranspileErrorSource || (exports.TranspileErrorSource = TranspileErrorSource = {}));
/**
 * Represents a text document that contains a svelte component.
 */
class SvelteDocument {
    get config() {
        return this.parent.configPromise;
    }
    constructor(parent) {
        this.parent = parent;
        this.languageId = 'svelte';
        this.version = 0;
        this.uri = this.parent.uri;
        this.script = this.parent.scriptInfo;
        this.moduleScript = this.parent.moduleScriptInfo;
        this.style = this.parent.styleInfo;
        this.version = this.parent.version;
    }
    getText() {
        return this.parent.getText();
    }
    getFilePath() {
        return this.parent.getFilePath() || '';
    }
    offsetAt(position) {
        return this.parent.offsetAt(position);
    }
    async getTranspiled() {
        if (!this.transpiledDoc) {
            const [major, minor] = this.parent.getSvelteVersion();
            if (major > 3 || (major === 3 && minor >= 32)) {
                this.transpiledDoc = TranspiledSvelteDocument.create(this.parent, await this.config);
            }
            else {
                this.transpiledDoc = FallbackTranspiledSvelteDocument.create(this.parent, (await this.config)?.preprocess);
            }
        }
        return this.transpiledDoc;
    }
    async getCompiled() {
        if (!this.compileResult) {
            this.compileResult = this.getCompiledWith((await this.config)?.compilerOptions);
        }
        return this.compileResult;
    }
    async getCompiledWith(options = {}) {
        return this.parent.compiler.compile((await this.getTranspiled()).getText(), {
            ...options,
            filename: this.getFilePath()
        });
    }
}
exports.SvelteDocument = SvelteDocument;
class TranspiledSvelteDocument {
    static async create(document, config) {
        if (!config?.preprocess) {
            return new TranspiledSvelteDocument(document.getText());
        }
        const filename = document.getFilePath() || '';
        const preprocessed = await document.compiler.preprocess(document.getText(), wrapPreprocessors(config?.preprocess), {
            filename
        });
        if (preprocessed.code === document.getText()) {
            return new TranspiledSvelteDocument(document.getText());
        }
        return new TranspiledSvelteDocument(preprocessed.code, preprocessed.map
            ? new documents_1.SourceMapDocumentMapper(createTraceMap(preprocessed.map), 
            // The "sources" array only contains the Svelte filename, not its path.
            // For getting generated positions, the sourcemap consumer wants an exact match
            // of the source filepath. Therefore only pass in the filename here.
            (0, utils_1.getLastPartOfPath)(filename))
            : undefined);
    }
    constructor(code, mapper) {
        this.code = code;
        this.mapper = mapper;
    }
    getOriginalPosition(generatedPosition) {
        return this.mapper?.getOriginalPosition(generatedPosition) || generatedPosition;
    }
    getText() {
        return this.code;
    }
    getGeneratedPosition(originalPosition) {
        return this.mapper?.getGeneratedPosition(originalPosition) || originalPosition;
    }
}
exports.TranspiledSvelteDocument = TranspiledSvelteDocument;
/**
 * Only used when the user has an old Svelte version installed where source map support
 * for preprocessors is not built in yet.
 * This fallback version does not map correctly when there's both a module and instance script.
 * It isn't worth fixing these cases though now that Svelte ships a preprocessor with source maps.
 */
class FallbackTranspiledSvelteDocument {
    static async create(document, preprocessors = []) {
        const { transpiled, processedScripts, processedStyles } = await transpile(document, preprocessors);
        const scriptMapper = SvelteFragmentMapper.createScript(document, transpiled, processedScripts);
        const styleMapper = SvelteFragmentMapper.createStyle(document, transpiled, processedStyles);
        return new FallbackTranspiledSvelteDocument(document, transpiled, scriptMapper, styleMapper);
    }
    constructor(parent, transpiled, scriptMapper, styleMapper) {
        this.parent = parent;
        this.transpiled = transpiled;
        this.scriptMapper = scriptMapper;
        this.styleMapper = styleMapper;
        this.fragmentInfos = [this.scriptMapper?.fragmentInfo, this.styleMapper?.fragmentInfo]
            .filter(utils_1.isNotNullOrUndefined)
            .sort((i1, i2) => i1.end - i2.end);
    }
    getOriginalPosition(generatedPosition) {
        if (this.scriptMapper?.isInTranspiledFragment(generatedPosition)) {
            return this.scriptMapper.getOriginalPosition(generatedPosition);
        }
        if (this.styleMapper?.isInTranspiledFragment(generatedPosition)) {
            return this.styleMapper.getOriginalPosition(generatedPosition);
        }
        // Position is not in fragments, but we still need to account for
        // the length differences of the fragments before the position.
        let offset = (0, documents_1.offsetAt)(generatedPosition, this.transpiled);
        for (const fragmentInfo of this.fragmentInfos) {
            if (offset > fragmentInfo.end) {
                offset += fragmentInfo.diff;
            }
        }
        return this.parent.positionAt(offset);
    }
    getURL() {
        return this.parent.getURL();
    }
    getText() {
        return this.transpiled;
    }
    getGeneratedPosition(originalPosition) {
        const { styleInfo, scriptInfo } = this.parent;
        if ((0, documents_1.isInTag)(originalPosition, scriptInfo) && this.scriptMapper) {
            return this.scriptMapper.getGeneratedPosition(originalPosition);
        }
        if ((0, documents_1.isInTag)(originalPosition, styleInfo) && this.styleMapper) {
            return this.styleMapper.getGeneratedPosition(originalPosition);
        }
        // Add length difference of each fragment
        let offset = (0, documents_1.offsetAt)(originalPosition, this.parent.getText());
        for (const fragmentInfo of this.fragmentInfos) {
            if (offset > fragmentInfo.end) {
                offset -= fragmentInfo.diff;
            }
        }
        return (0, documents_1.positionAt)(offset, this.getText());
    }
}
exports.FallbackTranspiledSvelteDocument = FallbackTranspiledSvelteDocument;
class SvelteFragmentMapper {
    static createStyle(originalDoc, transpiled, processed) {
        return SvelteFragmentMapper.create(originalDoc, transpiled, originalDoc.styleInfo, (0, documents_1.extractStyleTag)(transpiled), processed);
    }
    static createScript(originalDoc, transpiled, processed) {
        const scriptInfo = originalDoc.scriptInfo || originalDoc.moduleScriptInfo;
        const maybeScriptTag = (0, documents_1.extractScriptTags)(transpiled);
        const maybeScriptTagInfo = maybeScriptTag && (maybeScriptTag.script || maybeScriptTag.moduleScript);
        return SvelteFragmentMapper.create(originalDoc, transpiled, scriptInfo, maybeScriptTagInfo || null, processed);
    }
    static create(originalDoc, transpiled, originalTagInfo, transpiledTagInfo, processed) {
        const sourceMapper = processed.length > 0
            ? SvelteFragmentMapper.createSourceMapper(processed, originalDoc)
            : new documents_1.IdentityMapper(originalDoc.uri);
        if (originalTagInfo && transpiledTagInfo) {
            const sourceLength = originalTagInfo.container.end - originalTagInfo.container.start;
            const transpiledLength = transpiledTagInfo.container.end - transpiledTagInfo.container.start;
            const diff = sourceLength - transpiledLength;
            return new SvelteFragmentMapper({ end: transpiledTagInfo.container.end, diff }, new documents_1.FragmentMapper(originalDoc.getText(), originalTagInfo, originalDoc.uri), new documents_1.FragmentMapper(transpiled, transpiledTagInfo, originalDoc.uri), sourceMapper);
        }
        return null;
    }
    static createSourceMapper(processed, originalDoc) {
        return processed.reduce((parent, processedSingle) => processedSingle?.map
            ? new documents_1.SourceMapDocumentMapper(createTraceMap(processedSingle.map), originalDoc.uri, parent)
            : new documents_1.IdentityMapper(originalDoc.uri, parent), undefined);
    }
    constructor(
    /**
     * End offset + length difference to original
     */
    fragmentInfo, 
    /**
     * Maps between full original source and fragment within that original.
     */
    originalFragmentMapper, 
    /**
     * Maps between full transpiled source and fragment within that transpiled.
     */
    transpiledFragmentMapper, 
    /**
     * Maps between original and transpiled, within fragment.
     */
    sourceMapper) {
        this.fragmentInfo = fragmentInfo;
        this.originalFragmentMapper = originalFragmentMapper;
        this.transpiledFragmentMapper = transpiledFragmentMapper;
        this.sourceMapper = sourceMapper;
    }
    isInTranspiledFragment(generatedPosition) {
        return this.transpiledFragmentMapper.isInGenerated(generatedPosition);
    }
    getOriginalPosition(generatedPosition) {
        // Map the position to be relative to the transpiled fragment
        const positionInTranspiledFragment = this.transpiledFragmentMapper.getGeneratedPosition(generatedPosition);
        // Map the position, using the sourcemap, to the original position in the source fragment
        const positionInOriginalFragment = this.sourceMapper.getOriginalPosition(positionInTranspiledFragment);
        // Map the position to be in the original fragment's parent
        return this.originalFragmentMapper.getOriginalPosition(positionInOriginalFragment);
    }
    /**
     * Reversing `getOriginalPosition`
     */
    getGeneratedPosition(originalPosition) {
        const positionInOriginalFragment = this.originalFragmentMapper.getGeneratedPosition(originalPosition);
        const positionInTranspiledFragment = this.sourceMapper.getGeneratedPosition(positionInOriginalFragment);
        return this.transpiledFragmentMapper.getOriginalPosition(positionInTranspiledFragment);
    }
}
exports.SvelteFragmentMapper = SvelteFragmentMapper;
/**
 * Wrap preprocessors and rethrow on errors with more info on where the error came from.
 */
function wrapPreprocessors(preprocessors = []) {
    preprocessors = Array.isArray(preprocessors) ? preprocessors : [preprocessors];
    return preprocessors.map((preprocessor) => {
        const wrappedPreprocessor = { markup: preprocessor.markup };
        if (preprocessor.script) {
            wrappedPreprocessor.script = async (args) => {
                try {
                    return await preprocessor.script(args);
                }
                catch (e) {
                    e.__source = TranspileErrorSource.Script;
                    throw e;
                }
            };
        }
        if (preprocessor.style) {
            wrappedPreprocessor.style = async (args) => {
                try {
                    return await preprocessor.style(args);
                }
                catch (e) {
                    e.__source = TranspileErrorSource.Style;
                    throw e;
                }
            };
        }
        return wrappedPreprocessor;
    });
}
async function transpile(document, preprocessors = []) {
    preprocessors = Array.isArray(preprocessors) ? preprocessors : [preprocessors];
    const processedScripts = [];
    const processedStyles = [];
    const wrappedPreprocessors = preprocessors.map((preprocessor) => {
        const wrappedPreprocessor = { markup: preprocessor.markup };
        if (preprocessor.script) {
            wrappedPreprocessor.script = async (args) => {
                try {
                    const res = await preprocessor.script(args);
                    if (res && res.map) {
                        processedScripts.push(res);
                    }
                    return res;
                }
                catch (e) {
                    e.__source = TranspileErrorSource.Script;
                    throw e;
                }
            };
        }
        if (preprocessor.style) {
            wrappedPreprocessor.style = async (args) => {
                try {
                    const res = await preprocessor.style(args);
                    if (res && res.map) {
                        processedStyles.push(res);
                    }
                    return res;
                }
                catch (e) {
                    e.__source = TranspileErrorSource.Style;
                    throw e;
                }
            };
        }
        return wrappedPreprocessor;
    });
    const result = await document.compiler.preprocess(document.getText(), wrappedPreprocessors, {
        filename: document.getFilePath() || ''
    });
    const transpiled = result.code || result.toString?.() || '';
    return { transpiled, processedScripts, processedStyles };
}
function createTraceMap(map) {
    return new trace_mapping_1.TraceMap(normalizeMap(map));
    function normalizeMap(map) {
        // We don't know what we get, could be a stringified sourcemap,
        // or a class which has the required properties on it, or a class
        // which we need to call toString() on to get the correct format.
        if (typeof map === 'string' || map.version) {
            return map;
        }
        return map.toString();
    }
}
//# sourceMappingURL=SvelteDocument.js.map