"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const configLoader_1 = require("../../../src/lib/documents/configLoader");
const logger_1 = require("../../../src/logger");
const path_1 = __importDefault(require("path"));
const url_1 = require("url");
const assert_1 = __importDefault(require("assert"));
const sinon_1 = require("sinon");
describe('ConfigLoader', () => {
    function configFrom(path, configSource = 'svelte') {
        return {
            compilerOptions: {
                dev: true,
                generate: false
            },
            preprocess: (0, url_1.pathToFileURL)(path).toString(),
            configSource
        };
    }
    function viteConfig() {
        return {
            compilerOptions: {
                dev: true,
                generate: false
            },
            preprocess: { name: 'vite-preprocess' },
            configSource: 'vite'
        };
    }
    function normalizePath(filePath) {
        return path_1.default.join(...filePath.split('/'));
    }
    function mockFdir(results) {
        return class {
            withPathSeparator() {
                return this;
            }
            exclude() {
                return this;
            }
            filter() {
                return this;
            }
            withRelativePaths() {
                return this;
            }
            crawl() {
                return this;
            }
            sync() {
                return typeof results === 'function' ? results() : results;
            }
        };
    }
    function createConfigLoader(globSync, fs, moduleLoader, processFeatures, loadFromVite) {
        return new configLoader_1.ConfigLoader(globSync, fs, path_1.default, processFeatures, async (dirOrFile) => {
            if (isConfigFilePath(dirOrFile)) {
                if (/[/\\]svelte\.config\./.test(dirOrFile)) {
                    return loadConfigFile(dirOrFile);
                }
                if (loadFromVite) {
                    const config = await loadFromVite(path_1.default.dirname(dirOrFile));
                    if (config) {
                        return { config, configFilePath: dirOrFile, configSource: 'vite' };
                    }
                }
                return loadConfigFile(dirOrFile);
            }
            const viteConfigPath = findConfig(dirOrFile, 'vite.config', [
                'js',
                'mjs',
                'ts',
                'cjs',
                'mts',
                'cts'
            ]);
            if (viteConfigPath && loadFromVite) {
                const config = await loadFromVite(dirOrFile);
                if (config) {
                    return { config, configFilePath: viteConfigPath, configSource: 'vite' };
                }
            }
            const svelteConfigPath = findConfig(dirOrFile, 'svelte.config', processFeatures && 'typescript' in processFeatures && processFeatures.typescript
                ? ['js', 'cjs', 'mjs', 'ts', 'mts']
                : ['js', 'cjs', 'mjs']);
            if (!svelteConfigPath) {
                return undefined;
            }
            return loadConfigFile(svelteConfigPath);
            function isConfigFilePath(filePath) {
                return /\.(js|cjs|mjs|ts|mts|cts)$/.test(path_1.default.basename(filePath));
            }
            async function loadConfigFile(configFilePath) {
                try {
                    const config = (await moduleLoader((0, url_1.pathToFileURL)(configFilePath)))?.default;
                    if (!config) {
                        throw new Error('Missing exports in the config.');
                    }
                    return { config, configFilePath, configSource: 'svelte' };
                }
                catch (error) {
                    return {
                        error,
                        configFilePath,
                        configSource: 'svelte'
                    };
                }
            }
            function findConfig(directory, basename, extensions) {
                for (const extension of extensions) {
                    const configPath = path_1.default.join(directory, `${basename}.${extension}`);
                    if (fs.existsSync(configPath)) {
                        return configPath;
                    }
                }
            }
        });
    }
    async function assertFindsConfig(configLoader, filePath, configPath) {
        filePath = normalizePath(filePath);
        configPath = normalizePath(configPath);
        assert_1.default.deepStrictEqual(configLoader.getConfig(filePath), configFrom(configPath));
        assert_1.default.deepStrictEqual(await configLoader.awaitConfig(filePath), configFrom(configPath));
    }
    it('should load all config files below and the one inside/above given directory', async () => {
        const configLoader = createConfigLoader(mockFdir(['svelte.config.js', 'below/svelte.config.js']), { existsSync: () => true }, (module) => Promise.resolve({ default: { preprocess: module.toString() } }), process.features);
        await configLoader.loadConfigs(normalizePath('/some/path'));
        await assertFindsConfig(configLoader, '/some/path/comp.svelte', '/some/path/svelte.config.js');
        await assertFindsConfig(configLoader, '/some/path/aside/comp.svelte', '/some/path/svelte.config.js');
        await assertFindsConfig(configLoader, '/some/path/below/comp.svelte', '/some/path/below/svelte.config.js');
        await assertFindsConfig(configLoader, '/some/path/below/further/comp.svelte', '/some/path/below/svelte.config.js');
    });
    it('finds first above if none found inside/below directory', async () => {
        const configLoader = createConfigLoader(mockFdir([]), {
            existsSync: (p) => typeof p === 'string' && p.endsWith(path_1.default.join('some', 'svelte.config.js'))
        }, (module) => Promise.resolve({ default: { preprocess: module.toString() } }), process.features);
        await configLoader.loadConfigs(normalizePath('/some/path'));
        await assertFindsConfig(configLoader, '/some/path/comp.svelte', '/some/svelte.config.js');
    });
    it('adds fallback if no config found', async () => {
        const configLoader = createConfigLoader(mockFdir([]), { existsSync: () => false }, (module) => Promise.resolve({ default: { preprocess: module.toString() } }), process.features);
        await configLoader.loadConfigs(normalizePath('/some/path'));
        assert_1.default.deepStrictEqual(
        // Can't do the equal-check directly, instead check if it's the expected object props
        Object.keys(configLoader.getConfig(normalizePath('/some/path/comp.svelte'))?.preprocess || {}).sort(), ['name', 'script'].sort());
    });
    it('will not load config multiple times if config loading started in parallel', async () => {
        let firstGlobCall = true;
        let nrImportCalls = 0;
        const configLoader = createConfigLoader(mockFdir(() => {
            if (firstGlobCall) {
                firstGlobCall = false;
                return ['svelte.config.js'];
            }
            else {
                return [];
            }
        }), {
            existsSync: (p) => typeof p === 'string' &&
                p.endsWith(path_1.default.join('some', 'path', 'svelte.config.js'))
        }, (module) => {
            nrImportCalls++;
            return new Promise((resolve) => {
                setTimeout(() => resolve({ default: { preprocess: module.toString() } }), 500);
            });
        }, process.features);
        await Promise.all([
            configLoader.loadConfigs(normalizePath('/some/path')),
            configLoader.loadConfigs(normalizePath('/some/path/sub')),
            configLoader.awaitConfig(normalizePath('/some/path/file.svelte'))
        ]);
        await assertFindsConfig(configLoader, '/some/path/comp.svelte', '/some/path/svelte.config.js');
        await assertFindsConfig(configLoader, '/some/path/sub/comp.svelte', '/some/path/svelte.config.js');
        assert_1.default.deepStrictEqual(nrImportCalls, 1);
    });
    it('can deal with missing config', () => {
        const configLoader = createConfigLoader(mockFdir([]), { existsSync: () => false }, () => Promise.resolve('unimportant'), process.features);
        assert_1.default.deepStrictEqual(configLoader.getConfig(normalizePath('/some/file.svelte')), undefined);
    });
    it('should await config', async () => {
        const configLoader = createConfigLoader(mockFdir([]), { existsSync: () => true }, (module) => Promise.resolve({ default: { preprocess: module.toString() } }), process.features);
        assert_1.default.deepStrictEqual(await configLoader.awaitConfig(normalizePath('some/file.svelte')), configFrom(normalizePath('some/svelte.config.js')));
    });
    it('should not load config when disabled', async () => {
        const moduleLoader = (0, sinon_1.spy)();
        const configLoader = createConfigLoader(mockFdir([]), { existsSync: () => true }, moduleLoader, process.features);
        configLoader.setDisabled(true);
        await configLoader.awaitConfig(normalizePath('some/file.svelte'));
        assert_1.default.deepStrictEqual(moduleLoader.notCalled, true);
    });
    it('loads config from vite.config when no svelte.config found', async () => {
        const viteConfigDir = normalizePath('/some/path');
        const viteConfigPath = path_1.default.join(viteConfigDir, 'vite.config.js');
        const configLoader = createConfigLoader(mockFdir([]), {
            existsSync: (p) => typeof p === 'string' && p.endsWith(viteConfigPath)
        }, () => Promise.resolve({ default: {} }), process.features, async (root) => {
            assert_1.default.equal(root, viteConfigDir);
            return viteConfig();
        });
        await configLoader.loadConfigs(viteConfigDir);
        assert_1.default.deepStrictEqual(configLoader.getConfig(normalizePath('/some/path/comp.svelte')), viteConfig());
    });
    it('prefers vite.config over svelte.config when both exist', async () => {
        const viteConfigPath = normalizePath('/some/path/vite.config.js');
        const loadFromVite = (0, sinon_1.spy)(async () => viteConfig());
        const configLoader = createConfigLoader(mockFdir(['svelte.config.js']), {
            existsSync: (p) => typeof p === 'string' &&
                (p.endsWith(normalizePath('/some/path/svelte.config.js')) ||
                    p.endsWith(viteConfigPath))
        }, (module) => Promise.resolve({ default: { preprocess: module.toString() } }), process.features, loadFromVite);
        await configLoader.loadConfigs(normalizePath('/some/path'));
        assert_1.default.deepStrictEqual(configLoader.getConfig(normalizePath('/some/path/comp.svelte')), viteConfig());
        assert_1.default.deepStrictEqual(loadFromVite.calledOnce, true);
    });
    it('falls back to preprocessors when vite config has no svelte plugin options', async () => {
        const viteConfigPath = normalizePath('/some/path/vite.config.ts');
        const configLoader = createConfigLoader(mockFdir([]), {
            existsSync: (p) => typeof p === 'string' && p.endsWith(viteConfigPath)
        }, () => Promise.resolve({ default: {} }), process.features, async () => undefined);
        await configLoader.loadConfigs(normalizePath('/some/path'));
        assert_1.default.deepStrictEqual(Object.keys(configLoader.getConfig(normalizePath('/some/path/comp.svelte'))?.preprocess || {}).sort(), ['name', 'script'].sort());
    });
    it('does not log an error when vite config has no svelte plugin', async () => {
        const errorSpy = (0, sinon_1.spy)(logger_1.Logger, 'error');
        try {
            const viteConfigPath = normalizePath('/some/path/vite.config.ts');
            const configLoader = createConfigLoader(mockFdir([]), {
                existsSync: (p) => typeof p === 'string' && p.endsWith(viteConfigPath)
            }, () => Promise.resolve({ default: {} }), process.features, async () => undefined);
            await configLoader.loadConfigs(normalizePath('/some/path'));
            assert_1.default.deepStrictEqual(errorSpy.called, false);
        }
        finally {
            errorSpy.restore();
        }
    });
    it('logs an error when the vite config itself fails to load', async () => {
        const errorSpy = (0, sinon_1.spy)(logger_1.Logger, 'error');
        try {
            const viteConfigPath = normalizePath('/some/path/vite.config.ts');
            // Built directly instead of through createConfigLoader, which always wraps the
            // result in `{ config }` and so cannot express a failed load.
            const configLoader = new configLoader_1.ConfigLoader(mockFdir([]), {
                existsSync: (p) => typeof p === 'string' && p.endsWith(viteConfigPath)
            }, path_1.default, process.features, async () => ({
                error: new Error('kaboom'),
                configFilePath: viteConfigPath,
                configSource: 'vite'
            }));
            await configLoader.loadConfigs(normalizePath('/some/path'));
            assert_1.default.deepStrictEqual(errorSpy.called, true);
        }
        finally {
            errorSpy.restore();
        }
    });
    it('can scan svelte.config.ts', async () => {
        const configLoader = createConfigLoader(mockFdir(['svelte.config.ts']), {
            existsSync: (p) => typeof p === 'string' &&
                p.endsWith(path_1.default.join('some', 'path', 'svelte.config.ts'))
        }, (module) => Promise.resolve({ default: { preprocess: module.toString() } }), { ...process.features, typescript: 'transform' });
        await configLoader.loadConfigs(normalizePath('/some/path'));
        await assertFindsConfig(configLoader, '/some/path/comp.svelte', '/some/path/svelte.config.ts');
    });
    it('can skips svelte.config.ts loading', async () => {
        const configLoader = createConfigLoader(mockFdir(['svelte.config.ts', 'svelte.config.cjs']), {
            existsSync: (p) => typeof p === 'string' &&
                (p.endsWith(path_1.default.join('some', 'path', 'svelte.config.ts')) ||
                    p.endsWith(path_1.default.join('some', 'path', 'svelte.config.cjs')))
        }, (module) => Promise.resolve({ default: { preprocess: module.toString() } }), { ...process.features, typescript: false });
        await configLoader.loadConfigs(normalizePath('/some/path'));
        await assertFindsConfig(configLoader, '/some/path/comp.svelte', '/some/path/svelte.config.cjs');
    });
    it('uses explicit config only within the scoped root directory', async () => {
        const appRoot = normalizePath('/monorepo/packages/app');
        const libRoot = normalizePath('/monorepo/packages/lib');
        const explicitConfigPath = path_1.default.join(appRoot, 'vite.custom.config.js');
        const libConfigPath = path_1.default.join(libRoot, 'svelte.config.js');
        const configLoader = createConfigLoader(mockFdir(['svelte.config.js']), {
            existsSync: (p) => typeof p === 'string' &&
                (p.endsWith(explicitConfigPath) ||
                    p.endsWith(libConfigPath) ||
                    p.endsWith(path_1.default.join(libRoot, 'vite.config.js')))
        }, (module) => Promise.resolve({ default: { preprocess: module.toString() } }), process.features, async (root) => {
            if (root === appRoot) {
                return viteConfig();
            }
            return undefined;
        });
        configLoader.setExplicitConfigScope({
            configPath: explicitConfigPath,
            rootDirectory: appRoot
        });
        await configLoader.loadConfigs(appRoot);
        await configLoader.loadConfigs(libRoot);
        assert_1.default.deepStrictEqual(configLoader.getConfig(normalizePath('/monorepo/packages/app/comp.svelte')), viteConfig());
        await assertFindsConfig(configLoader, '/monorepo/packages/lib/comp.svelte', libConfigPath);
    });
});
//# sourceMappingURL=configLoader.test.js.map