UNPKG

vite-test-utils-edge

Version:
392 lines (386 loc) 13.3 kB
'use strict'; const node_fs = require('node:fs'); const node_path = require('node:path'); const createDebug = require('debug'); const pc = require('picocolors'); const vite = require('vite'); const defu = require('defu'); const node_module = require('node:module'); const node_os = require('node:os'); const node_url = require('node:url'); const shared = require('@intlify/shared'); const esbuild = require('esbuild'); let currentContext; function createTestContext(options = {}) { const cwd = process.cwd(); const browser = !!options.browser; const _options = defu.defu(options, { rootDir: cwd, mode: "dev", server: browser ? true : !!options.server, browser, browserOptions: { type: "chromium" } }); return setTestContext({ options: _options }); } function setTestContext(ctx) { currentContext = ctx; return currentContext; } function useTestContext() { if (!currentContext) { throw new Error("No context is available. (Forgot calling setup?)"); } return currentContext; } const DEBUG$1 = createDebug("vite-test-utils:utils"); async function isExists(path) { try { await node_fs.promises.access(path, node_fs.constants.F_OK); return true; } catch { return false; } } async function mkTmpDir(prefix) { const p = node_path.join(node_os.tmpdir(), prefix ? `vite-test-utils-${prefix}-` : "vite-test-utils-"); const dir = await node_fs.promises.mkdtemp(p); DEBUG$1("mkTmpDir", dir); return dir; } async function dynamicImport(module) { try { return import(String(module)); } catch { throw new Error(` The dependency '${module}' not found. Please run 'npm install --save-dev ${module}' or 'yarn add --dev ${module}' or 'pnpm add --save-dev ${module}' `); } } const _require = node_module.createRequire((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('shared/vite-test-utils-edge.21a779c8.cjs', document.baseURI).href))); async function dynamicImportCompatibility(fileUrl, isESM) { return isESM ? import(fileUrl) : new Function("file", "return import(file)")(fileUrl); } async function loadConfig(configEnv, { configFile = "vite.config.ts", configRoot = process.cwd(), configFilePath = void 0 } = {}) { DEBUG$1("loadConfig: configFile ->", configFile); DEBUG$1("loadConfig: configRoot ->", configRoot); DEBUG$1("loadConfig: configFilePath ->", configFilePath); let resolvedPath = configFilePath; if (resolvedPath == null) { resolvedPath = node_path.resolve(configRoot, configFile); } let isESM = false; if (/\.m[jt]s$/.test(resolvedPath)) { isESM = true; } else if (/\.c[jt]s$/.test(resolvedPath)) { isESM = false; } else { try { const pkg = await lookupFile(configRoot, ["package.json"]); isESM = !!pkg && JSON.parse(pkg).type === "module"; } catch (e) { } } try { const bundled = await bundleConfigFile(resolvedPath, isESM); const userConfig = await loadConfigFromBundledFile(resolvedPath, bundled.code, isESM); DEBUG$1(`bundled config file loaded`); const config = await (typeof userConfig === "function" ? userConfig(configEnv) : userConfig); if (!shared.isObject(config)) { throw new Error(`config must export or return an object.`); } return { path: vite.normalizePath(resolvedPath), config, dependencies: bundled.dependencies }; } catch (e) { console.error(`failed to load config from ${resolvedPath}: ${e.message}`); throw e; } } async function lookupFile(dir, formats, options) { for (const format of formats) { const fullPath = node_path.join(dir, format); if (await isExists(fullPath) && (await node_fs.promises.stat(fullPath)).isFile()) { return options?.pathOnly ? fullPath : await node_fs.promises.readFile(fullPath, "utf-8"); } } const parentDir = node_path.dirname(dir); if (parentDir !== dir && (!options?.rootDir || parentDir.startsWith(options?.rootDir))) { return lookupFile(parentDir, formats, options); } } async function bundleConfigFile(fileName, isESM) { const dirnameVarName = "__vite_injected_original_dirname"; const filenameVarName = "__vite_injected_original_filename"; const importMetaUrlVarName = "__vite_injected_original_import_meta_url"; const result = await esbuild.build({ absWorkingDir: process.cwd(), entryPoints: [fileName], outfile: "out.js", write: false, target: ["node14.18", "node16"], platform: "node", bundle: true, format: isESM ? "esm" : "cjs", sourcemap: "inline", metafile: true, define: { __dirname: dirnameVarName, __filename: filenameVarName, "import.meta.url": importMetaUrlVarName }, plugins: [ { name: "externalize-deps", setup(build2) { build2.onResolve({ filter: /.*/ }, async ({ path: id, importer }) => { if (id[0] !== "." && !node_path.isAbsolute(id)) { return { external: true }; } const idFsPath = node_path.resolve(node_path.dirname(importer), id); const idPkgPath = await lookupFile(idFsPath, [`package.json`], { pathOnly: true }); if (idPkgPath) { const idPkgDir = node_path.dirname(idPkgPath); if (node_path.relative(idPkgDir, fileName).startsWith("..")) { return { path: node_url.pathToFileURL(idFsPath).href, external: true }; } } }); } }, { name: "inject-file-scope-variables", setup(build2) { build2.onLoad({ filter: /\.[cm]?[jt]s$/ }, async (args) => { const contents = await node_fs.promises.readFile(args.path, "utf8"); const injectValues = `const ${dirnameVarName} = ${JSON.stringify(node_path.dirname(args.path))};const ${filenameVarName} = ${JSON.stringify(args.path)};const ${importMetaUrlVarName} = ${JSON.stringify(node_url.pathToFileURL(args.path).href)};`; return { loader: args.path.endsWith("ts") ? "ts" : "js", contents: injectValues + contents }; }); } } ] }); const { text } = result.outputFiles[0]; return { code: text, dependencies: result.metafile ? Object.keys(result.metafile.inputs) : [] }; } async function loadConfigFromBundledFile(fileName, bundledCode, isESM) { DEBUG$1("loadConfigFromBundledFile: ", fileName, isESM); if (isESM) { const fileBase = `${fileName}.timestamp-${Date.now()}`; const fileNameTmp = `${fileBase}.mjs`; const fileUrl = `${node_url.pathToFileURL(fileBase)}.mjs`; await node_fs.promises.writeFile(fileNameTmp, bundledCode); try { return await dynamicImportCompatibility(fileUrl, isESM).then((mod) => mod.default || mod); } finally { await node_fs.promises.unlink(fileNameTmp); } } else { const extension = node_path.extname(fileName); const realFileName = await node_fs.promises.realpath(fileName); const loaderExt = extension in _require.extensions ? extension : ".js"; const defaultLoader = _require.extensions[loaderExt]; _require.extensions[loaderExt] = (module, filename) => { if (filename === realFileName) { module._compile(bundledCode, filename); } else { defaultLoader(module, filename); } }; delete _require.cache[_require.resolve(fileName)]; const raw = _require(fileName); _require.extensions[loaderExt] = defaultLoader; return raw.__esModule ? raw.default : raw; } } function stringifyObj(obj) { return `Object({${Object.entries(obj).map(([key, value]) => `${JSON.stringify(key)}:${toCode(value)}`).join(`,`)}})`; } function toCode(code) { if (code === null) { return `null`; } if (code === void 0) { return `undefined`; } if (shared.isString(code)) { return JSON.stringify(code); } if (shared.isRegExp(code) && code.toString) { return code.toString(); } if (shared.isFunction(code) && code.toString) { return `(${code.toString()})`; } if (shared.isArray(code)) { return `[${code.map((c) => toCode(c)).join(`,`)}]`; } if (shared.isObject(code)) { return stringifyObj(code); } return code + ``; } const DEBUG = createDebug("vite-test-utils:vite"); function getFixtureContextFrom(env) { const options = {}; options.port = env.__VTU_PORT != null ? parseInt(env.__VTU_PORT) : 3e3; options.mode = env.__VTU_MODE != null && ["dev", "preview"].includes(env.__VTU_MODE) ? env.__VTU_MODE : "dev"; options.root = env.__VTU_FIXTURE_ROOT ?? process.cwd(); options.buildDir = env.__VTU_FIXTURE_BUILD_DIR; options.configFile = env.__VTU_FIXTURE_CONFIG_FILE; options.viteConfig = env.__VTU_FIXTURE_VITE_CONFIG; options.viteConfigFile = env.__VTU_FIXTURE_VITE_CONFIG_FILE; return options; } const DEFAULT_CONFIG_FILES = [ "vite.config.mjs", "vite.config.mts", "vite.config.cjs", "vite.config.cts", "vite.config.ts", "vite.config.js" ]; async function resolveViteConfig(dir, { config = "vite.config.ts", viteDefaultConfigCheck = true } = {}) { DEBUG("resolveViteConfig: ", dir, config, viteDefaultConfigCheck); if (!await isExists(dir)) { DEBUG("resolveViteConfig: dir not exists", dir); return [false, null]; } const configFiles = viteDefaultConfigCheck ? DEFAULT_CONFIG_FILES : [config]; DEBUG("resolveViteConfig: configFiles -> ", configFiles); let found = false; let resolveViteConfig2 = null; for (const config2 of configFiles) { const target = node_path.resolve(dir, config2); DEBUG("resolveViteConfig: target -> ", target); if (await isExists(target)) { found = true; resolveViteConfig2 = target; break; } } DEBUG("resolveViteConfig: final -> ", found, resolveViteConfig2); return [found, resolveViteConfig2]; } async function resolveFixture(fixtureDir, config) { if (config) { const [found2] = await resolveViteConfig(fixtureDir, { config, viteDefaultConfigCheck: false }); if (found2) { return true; } } let found = false; const dirs = [fixtureDir, process.cwd()]; for (const dir of dirs) { const [_found, resolvedViteConfig] = await resolveViteConfig(dir); DEBUG("resolveFixture:", dir, found, resolvedViteConfig); if (_found) { found = true; break; } } DEBUG("resolvedViteConfig: final", found); return found; } function getViteCommand(mode) { return mode === "dev" ? "serve" : "build"; } function getViteMode(mode) { return mode === "dev" ? "development" : "production"; } async function loadViteConfig(configRoot, configFile, optionName, mode) { DEBUG("loadViteConfig:", configRoot, configFile, optionName, mode); const result = await loadConfig( { command: getViteCommand(mode), mode: getViteMode(mode) }, { configFilePath: configFile, configRoot } ); if (result == null) { console.warn(pc.yellow(pc.bold(`Cannot load from '${optionName}' option`))); return {}; } else { return result.config; } } async function writeViteConfigOptions(options) { const tmp = await mkTmpDir("vite-config-inline"); const configTmp = node_path.resolve(tmp, "vite.config.mjs"); DEBUG("writeViteConfigOptions: configTmp -> ", configTmp); await node_fs.promises.writeFile(configTmp, `export default ${toCode(options)}`, "utf-8"); return configTmp; } async function mkBuildDir() { return await mkTmpDir(Math.random().toString(36).slice(2, 8)); } async function prepareFixture() { const ctx = useTestContext(); if (ctx.options.viteConfig) { ctx.viteConfigInline = await writeViteConfigOptions(ctx.options.viteConfig); } if (ctx.options.mode === "preview") { ctx.buildDir = await mkBuildDir(); } } async function loadFixture(env) { const ctx = getFixtureContextFrom(env || process.env); const { mode, root, configFile, viteConfig, viteConfigFile } = ctx; if (!await resolveFixture(root, configFile)) { console.warn(pc.yellow(pc.bold(`vite config has been not found in ${root}`))); console.warn( pc.yellow( pc.bold( "The fixture that will work from now on will follow the vite defaults or you have specified options that are `viteConfig` or `viteConfigFile` options." ) ) ); } ctx.vite = viteConfigFile ? await loadViteConfig(root, node_path.resolve(root, viteConfigFile), "viteConfigFile", mode) : viteConfig ? await loadViteConfig(root, viteConfig, "viteConfig", mode) : {}; ctx.vite = vite.mergeConfig(ctx.vite, { root, logLevel: mode === "preview" ? "silent" : "info", build: { outDir: mode === "preview" ? ctx.buildDir : void 0 } }); return ctx; } async function buildFixture(ctx) { await vite.build(ctx.vite); } exports.buildFixture = buildFixture; exports.createTestContext = createTestContext; exports.dynamicImport = dynamicImport; exports.loadFixture = loadFixture; exports.prepareFixture = prepareFixture; exports.setTestContext = setTestContext; exports.useTestContext = useTestContext;