/*--------------------------------------------------------- * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ import { existsSync, promises as fs } from 'fs'; import { dirname, isAbsolute, join } from 'path'; import { pathToFileURL } from 'url'; import { CliExpectedError } from './error.mjs'; import { mustResolve } from './resolver.mjs'; import { ensureArray } from './util.mjs'; const configFileRules = { json: (path) => fs.readFile(path, 'utf8').then(JSON.parse), js: (path) => import(pathToFileURL(path).toString()), cjs: (path) => import(pathToFileURL(path).toString()), mjs: (path) => import(pathToFileURL(path).toString()), }; /** Loads the default config based on the process working directory. */ export async function loadDefaultConfigFile() { const base = '.vscode-test'; let dir = process.cwd(); while (true) { for (const ext of Object.keys(configFileRules)) { const candidate = join(dir, `${base}.${ext}`); if (existsSync(candidate)) { return tryLoadConfigFile(candidate); } } const next = dirname(dir); if (next === dir) { break; } dir = next; } throw new CliExpectedError(`Could not find a ${base} file in this directory or any parent. You can specify one with the --config option.`); } /** Loads a specific config file by the path, throwing if loading fails. */ export async function tryLoadConfigFile(path) { const ext = path.split('.').pop(); if (!configFileRules.hasOwnProperty(ext)) { throw new CliExpectedError(`I don't know how to load the extension '${ext}'. We can load: ${Object.keys(configFileRules).join(', ')}`); } try { let loaded = await configFileRules[ext](path); if ('default' in loaded) { // handle default es module exports loaded = loaded.default; } // allow returned promises to resolve: loaded = await loaded; if (typeof loaded === 'object' && 'tests' in loaded) { return await ResolvedTestConfiguration.load(loaded, path); } return await ResolvedTestConfiguration.load({ tests: ensureArray(loaded) }, path); } catch (e) { throw new CliExpectedError(`Could not read config file ${path}: ${e.stack || e}`); } } export class ResolvedTestConfiguration { path; tests; coverage; /** Directory name the configuration file resides in. */ dir; static async load(config, path) { // Resolve all mocha `require` locations relative to the configuration file, // since these are otherwise relative to the runner which is opaque to the user. const dir = dirname(path); for (const test of config.tests) { if (test.mocha?.require) { test.mocha.require = await Promise.all(ensureArray(test.mocha.require).map((f) => mustResolve(dir, f))); } } return new ResolvedTestConfiguration(config, path); } constructor(config, path) { this.path = path; this.coverage = config.coverage; this.tests = config.tests; this.dir = dirname(path); } /** * Gets the resolved extension development path for the test configuration. */ extensionDevelopmentPath(test) { return ensureArray(test.extensionDevelopmentPath?.slice() || this.dir).map((p) => isAbsolute(p) ? p : join(this.dir, p)); } }