@sanity/pkg-utils
Version:
Simple utilities for modern npm packages.
550 lines (549 loc) • 25.5 kB
JavaScript
import { createRequire } from "node:module";
import path, { resolve } from "node:path";
import { register } from "esbuild-register/dist/node";
import pkgUp from "pkg-up";
import findConfig from "find-config";
import { fileExists, isRecord } from "./isRecord.js";
import chalk from "chalk";
import { z, ZodError } from "zod";
import fs from "node:fs/promises";
import browserslist from "browserslist";
import config from "@sanity/browserslist-config";
import { existsSync } from "node:fs";
import ts from "typescript";
import { errorMap } from "zod-validation-error";
const CONFIG_FILE_NAMES = [
"package.config.ts",
"package.config.js",
"package.config.cjs",
"package.config.mjs"
];
function findConfigFile(cwd) {
const pkgJsonPath = findConfig("package.json", { cwd });
if (!pkgJsonPath) return;
const pkgPath = path.dirname(pkgJsonPath);
for (const fileName of CONFIG_FILE_NAMES) {
const configPath = path.resolve(pkgPath, fileName);
if (fileExists(configPath))
return configPath;
}
}
const require2 = createRequire(import.meta.url);
async function loadConfig(options) {
const { cwd } = options, pkgPath = await pkgUp({ cwd });
if (!pkgPath) return;
const root = path.dirname(pkgPath), configFile = await findConfigFile(root);
if (!configFile || !configFile.startsWith(cwd))
return;
const esbuildOptions = { extensions: [".js", ".mjs", ".ts"] }, { unregister } = globalThis.__DEV__ ? { unregister: () => {
} } : register(esbuildOptions), mod = require2(configFile);
return unregister(), mod?.default || mod || void 0;
}
function assertLast(a, arr) {
const aIdx = arr.indexOf(a);
return aIdx === -1 ? !0 : aIdx === arr.length - 1;
}
function assertOrder(a, b, arr) {
const aIdx = arr.indexOf(a), bIdx = arr.indexOf(b);
return aIdx === -1 || bIdx === -1 ? !0 : aIdx < bIdx;
}
const pkgSchema = z.object({
type: z.optional(z.enum(["commonjs", "module"])),
name: z.string(),
version: z.string(),
license: z.string(),
bin: z.optional(z.record(z.string())),
dependencies: z.optional(z.record(z.string())),
devDependencies: z.optional(z.record(z.string())),
peerDependencies: z.optional(z.record(z.string())),
source: z.optional(z.string()),
main: z.optional(z.string()),
browser: z.optional(z.record(z.string())),
module: z.optional(z.string()),
types: z.optional(z.string()),
exports: z.optional(
z.record(
z.union([
z.custom((val) => /^\.\/.*\.json$/.test(val)),
z.custom((val) => /^\.\/.*\.css$/.test(val)),
z.object({
types: z.optional(z.string()),
source: z.optional(z.string()),
browser: z.optional(
z.object({
source: z.string(),
import: z.optional(z.string()),
require: z.optional(z.string())
})
),
node: z.optional(
z.object({
source: z.optional(z.string()),
import: z.optional(z.string()),
require: z.optional(z.string())
})
),
import: z.optional(z.string()),
require: z.optional(z.string()),
default: z.string()
}),
z.object({
types: z.optional(z.string()),
svelte: z.string(),
default: z.optional(z.string())
})
])
)
),
browserslist: z.optional(z.union([z.string(), z.array(z.string())])),
sideEffects: z.optional(z.union([z.boolean(), z.array(z.string())])),
// @TODO type this properly
typesVersions: z.optional(z.any())
}), typoMap = /* @__PURE__ */ new Map();
for (const key of pkgSchema.keyof()._def.values)
typoMap.set(key.toUpperCase(), key);
function validatePkg(input) {
const pkg = pkgSchema.parse(input), invalidKey = Object.keys(input).find((key) => {
const needle = key.toUpperCase();
return typoMap.has(needle) ? typoMap.get(needle) !== key : !1;
});
if (invalidKey)
throw new TypeError(
`
- package.json: "${invalidKey}" is not a valid key. Did you mean "${typoMap.get(invalidKey.toUpperCase())}"?`
);
return pkg;
}
async function loadPkg(options) {
const { cwd } = options, pkgPath = await pkgUp({ cwd });
if (!pkgPath) throw new Error("no package.json found");
const buf = await fs.readFile(pkgPath), raw = JSON.parse(buf.toString());
return validatePkg(raw), raw;
}
async function loadPkgWithReporting(options) {
const { cwd, logger, strict } = options;
try {
const pkg = await loadPkg({ cwd });
let shouldError = !1;
if (strict && (pkg.type || (shouldError = !0, logger.error(
'the `type` field in `./package.json` must be either "module" or "commonjs")'
))), pkg.exports) {
const _exports = Object.entries(pkg.exports);
for (const [expPath, exp] of _exports) {
if (typeof exp == "string" || "svelte" in exp)
continue;
const keys = Object.keys(exp);
exp.types && (shouldError = !0, logger.error(
`exports["${expPath}"]: the \`types\` condition shouldn't be used as dts files are generated in such a way that both CJS and ESM is supported`
)), exp.module && (shouldError = !0, logger.error(
`exports["${expPath}"]: the \`module\` condition shouldn't be used as it's not well supported in all bundlers.`
)), exp.node ? (exp.import && exp.node.import && !assertOrder("node", "import", keys) && (shouldError = !0, logger.error(
`exports["${expPath}"]: the \`node\` property should come before the \`import\` property`
)), exp.node.module && (shouldError = !0, logger.error(
`exports["${expPath}"]: the \`node.module\` condition shouldn't be used as it's not well supported in all bundlers. A better strategy is to refactor the codebase to no longer be vulnerable to the "dual package hazard"`
)), !exp.node.source && exp.node.import && (exp.node.require || exp.require) && (exp.node.import.endsWith(".cjs.js") || exp.node.import.endsWith(".cjs.mjs")) && (shouldError = !0, logger.error(
`exports["${expPath}"]: the \`node.import\` re-export pattern shouldn't be used as it's not well supported in all bundlers. A better strategy is to refactor the codebase to no longer be vulnerable to the "dual package hazard"`
)), exp.require && exp.node.require && exp.require === exp.node.require ? (shouldError = !0, logger.error(
`exports["${expPath}"]: the \`node.require\` property isn't necessary as it's identical to \`require\``
)) : exp.require && exp.node.require && !assertOrder("node", "require", keys) && (shouldError = !0, logger.error(
`exports["${expPath}"]: the \`node\` property should come before the \`require\` property`
))) : assertOrder("import", "require", keys) || logger.warn(
`exports["${expPath}"]: the \`import\` property should come before the \`require\` property`
), assertLast("default", keys) || (shouldError = !0, logger.error(
`exports["${expPath}"]: the \`default\` property should be the last property`
));
}
}
return shouldError && process.exit(1), pkg;
} catch (err) {
if (err instanceof ZodError)
for (const issue of err.issues) {
if (issue.code === "invalid_type") {
logger.error(
[
`\`${formatPath(issue.path)}\` `,
`in \`./package.json\` must be of type ${chalk.magenta(issue.expected)} `,
`(received ${chalk.magenta(issue.received)})`
].join("")
);
continue;
}
logger.error(issue);
}
else
logger.error(err);
process.exit(1);
}
}
function formatPath(segments) {
return segments.map((s, idx) => idx === 0 ? s : typeof s == "number" ? `[${s}]` : s.startsWith(".") ? `["${s}"]` : `.${s}`).join("");
}
function browserslistToEsbuild(browserslistConfig, options = {}) {
if (!browserslistConfig) {
const path2 = process.cwd();
browserslistConfig = browserslist.loadConfig({ path: path2, ...options });
}
const SUPPORTED_ESBUILD_TARGETS = [
"es",
"chrome",
"edge",
"firefox",
"ios",
"node",
"safari",
"opera",
"ie"
], UNSUPPORTED = ["android 4"], replaces = {
ios_saf: "ios",
android: "chrome"
}, separator = " ";
return browserslist(browserslistConfig, options).filter((b) => !UNSUPPORTED.some((u) => b.startsWith(u))).map((b) => b === "safari TP" ? browserslist("last 1 safari version")[0] : b).map((b) => b.split(separator)).map((b) => (replaces[b[0]] && (b[0] = replaces[b[0]]), b)).map((b) => (b[1].includes("-") && (b[1] = b[1].slice(0, b[1].indexOf("-"))), b)).map((b) => (b[1].endsWith(".0") && (b[1] = b[1].slice(0, -2)), b)).filter((b) => /^\d+(\.\d+)*$/.test(b[1])).filter((b) => SUPPORTED_ESBUILD_TARGETS.includes(b[0])).reduce((acc, b) => {
const existingIndex = acc.findIndex((br) => br[0] === b[0]);
return existingIndex !== -1 ? acc[existingIndex][1] = b[1] : acc.push(b), acc;
}, []).map((b) => b.join(""));
}
function resolveConfigProperty(prop, initialValue) {
return prop ? typeof prop == "function" ? prop(initialValue) : prop : initialValue;
}
const DEFAULT_BROWSERSLIST_QUERY = config;
function pathContains(containerPath, itemPath) {
return !path.relative(containerPath, itemPath).startsWith("..");
}
function findCommonDirPath(filePaths) {
let ret;
for (const filePath of filePaths) {
let dirPath = path.dirname(filePath);
if (!ret) {
ret = dirPath;
continue;
}
for (; dirPath !== ret && (dirPath = path.dirname(dirPath), dirPath !== ret); ) {
if (pathContains(dirPath, ret)) {
ret = dirPath;
break;
}
if (dirPath === ".") return;
}
}
return ret;
}
const fileEnding = /\.[mc]?js$/, dtsEnding = ".d.ts", defaultEnding = ".js", mjsEnding = ".mjs", cjsEnding = ".cjs", mtsEnding = ".d.mts", ctsEnding = ".d.cts";
function getTargetPaths(_type, expOrBundle) {
const type = _type === "module" ? "module" : "commonjs", set = /* @__PURE__ */ new Set();
return expOrBundle?.import && set.add(expOrBundle.import.replace(fileEnding, type === "module" ? dtsEnding : mtsEnding)), expOrBundle?.require && set.add(expOrBundle.require.replace(fileEnding, type === "commonjs" ? dtsEnding : ctsEnding)), isPkgExport$1(expOrBundle) && (expOrBundle.browser?.source || (expOrBundle.browser?.import && set.add(
expOrBundle.browser.import.replace(fileEnding, type === "module" ? dtsEnding : mtsEnding)
), expOrBundle.browser?.require && set.add(
expOrBundle.browser.require.replace(
fileEnding,
type === "commonjs" ? dtsEnding : ctsEnding
)
)), expOrBundle.node?.source || (expOrBundle.node?.import && set.add(
expOrBundle.node.import.replace(fileEnding, type === "module" ? dtsEnding : mtsEnding)
), expOrBundle.node?.require && set.add(
expOrBundle.node.require.replace(fileEnding, type === "commonjs" ? dtsEnding : ctsEnding)
))), Array.from(set);
}
function isPkgExport$1(exp) {
return exp?.browser || exp?.node || exp?.default;
}
const pkgExtMap = {
// pkg.type: "commonjs"
commonjs: {
commonjs: defaultEnding,
esm: mjsEnding
},
// pkg.type: "module"
module: {
commonjs: cjsEnding,
esm: defaultEnding
}
};
function validateExports(_exports, options) {
const { pkg } = options, type = pkg.type || "commonjs", ext = pkgExtMap[type], errors = [];
for (const exp of _exports)
exp.require && !exp.require.endsWith(ext.commonjs) && errors.push(
`package.json with \`type: "${type}"\` - \`exports["${exp._path}"].require\` must end with "${ext.commonjs}"`
), exp.import && !exp.import.endsWith(ext.esm) && errors.push(
`package.json with \`type: "${type}"\` - \`exports["${exp._path}"].import\` must end with "${ext.esm}"`
);
return errors;
}
function parseExports(options) {
const { cwd, pkg, strict, strictOptions: strictOptions2, logger } = options, type = pkg.type || "commonjs", errors = [], report = (kind, message) => {
kind === "warn" ? logger.warn(message) : errors.push(message);
};
if (!pkg.main && strict && strictOptions2.alwaysPackageJsonMain !== "off" && report(strictOptions2.alwaysPackageJsonMain, "package.json: `main` must be declared"), !Array.isArray(pkg.files) && strict && strictOptions2.alwaysPackageJsonFiles !== "off" && report(
strictOptions2.alwaysPackageJsonFiles,
"package.json: `files` should be used over `.npmignore`"
), pkg.source) {
if (strict && pkg.exports?.["."] && typeof pkg.exports["."] == "object" && "source" in pkg.exports["."] && pkg.exports["."].source === pkg.source)
errors.push(
'package.json: the "source" property can be removed, as it is equal to exports["."].source.'
);
else if (!pkg.exports && pkg.main) {
const extMap = pkgExtMap[type], importExport = pkg.main.replace(fileEnding, extMap.esm), requireExport = pkg.main.replace(fileEnding, extMap.commonjs), defaultExport = pkg.main.replace(fileEnding, defaultEnding), maybeBrowserCondition = [];
if (pkg.browser) {
const browserConditions = [];
pkg.module && pkg.browser?.[pkg.module] ? browserConditions.push(
` "import": ${JSON.stringify(pkg.browser[pkg.module].replace(fileEnding, extMap.esm))}`
) : pkg.browser?.[pkg.main] && browserConditions.push(
` "import": ${JSON.stringify(pkg.browser[pkg.main].replace(fileEnding, extMap.esm))}`
), pkg.browser?.[pkg.main] && browserConditions.push(
` "require": ${JSON.stringify(pkg.browser[pkg.main].replace(fileEnding, extMap.commonjs))}`
), browserConditions.length && maybeBrowserCondition.push(
' "browser": {',
` "source": ${JSON.stringify(pkg.browser?.[pkg.source] || pkg.source)},`,
...browserConditions,
" }"
);
}
errors.push(
...[
"package.json: `exports` are missing, it should be:",
'"exports": {',
' ".": {',
` "source": ${JSON.stringify(pkg.source)},`,
// If browser conditions are detected then add them to the suggestion
...maybeBrowserCondition.length > 0 ? maybeBrowserCondition : [],
type === "commonjs" && ` "import": ${JSON.stringify(importExport)},`,
type === "module" && ` "require": ${JSON.stringify(requireExport)},`,
` "default": ${JSON.stringify(defaultExport)}`,
" },",
' "./package.json": "./package.json"',
"}"
].filter(Boolean)
);
}
}
if (errors.length)
throw new Error(`
- ` + errors.join(`
- `));
if (!pkg.exports)
throw new Error(
`
- ` + [
"package.json: `exports` are missing, please set a minimal configuration, for example:",
'"exports": {',
' ".": {',
' "source": "./src/index.js",',
' "default": "./dist/index.js"',
" },",
' "./package.json": "./package.json"',
"}"
].join(`
- `)
);
const _exports = [];
strict && strictOptions2.noPackageJsonTypings !== "off" && "typings" in pkg && report(strictOptions2.noPackageJsonTypings, "package.json: `typings` should be `types`"), strict && strictOptions2.alwaysPackageJsonTypes !== "off" && !pkg.types && typeof pkg.exports?.["."] == "object" && "source" in pkg.exports["."] && pkg.exports["."].source?.endsWith(".ts") && report(
strictOptions2.alwaysPackageJsonTypes,
"package.json: `types` must be declared for the npm listing to show as a TypeScript module."
), strict && !pkg.exports["./package.json"] && errors.push('package.json: `exports["./package.json"] must be declared.');
for (const [exportPath, exportEntry] of Object.entries(pkg.exports))
if (exportPath.endsWith(".json") || typeof exportEntry == "string" && exportEntry.endsWith(".json"))
exportPath === "./package.json" && exportEntry !== "./package.json" && errors.push('package.json: `exports["./package.json"]` must be "./package.json".');
else if (exportPath.endsWith(".css"))
typeof exportEntry == "string" && !existsSync(resolve(cwd, exportEntry)) ? errors.push(
`package.json: \`exports[${JSON.stringify(exportPath)}]\`: file does not exist.`
) : typeof exportEntry != "string" && errors.push(
`package.json: \`exports[${JSON.stringify(exportPath)}]\`: export conditions not supported for CSS files.`
);
else if (!(isRecord(exportEntry) && "svelte" in exportEntry))
if (isPkgExport(exportEntry)) {
const exp = {
_exported: !0,
_path: exportPath,
...exportEntry
};
if (!exp.default) {
const fallback = type === "module" ? exp.import : exp.require;
fallback && (exp.default = fallback);
}
!exp.require && type === "commonjs" && exp.default && (exp.require = exp.default), !exp.import && type === "module" && exp.default && (exp.import = exp.default), exportPath === "." && (exportEntry.require && pkg.main && exportEntry.require !== pkg.main && errors.push(
'package.json: mismatch between "main" and "exports.require". These must be equal.'
), exportEntry.import && pkg.module && exportEntry.import !== pkg.module && errors.push(
'package.json: mismatch between "module" and "exports.import" These must be equal.'
)), _exports.push(exp);
} else isRecord(exportEntry) || errors.push("package.json: exports must be an object");
if (errors.push(...validateExports(_exports, { pkg })), errors.length)
throw new Error(`
- ` + errors.join(`
- `));
return _exports;
}
function isPkgExport(value) {
return isRecord(value) && "source" in value && typeof value.source == "string";
}
async function loadTSConfig(options) {
const { cwd, tsconfigPath } = options, configPath = ts.findConfigFile(cwd, ts.sys.fileExists, tsconfigPath);
if (!configPath)
return;
const configFile = ts.readConfigFile(configPath, ts.sys.readFile);
return ts.parseJsonConfigFileContent(configFile.config, ts.sys, cwd);
}
function resolveBrowserTarget(versions) {
const target = versions.filter(
(version) => version.startsWith("chrome") || version.startsWith("edge") || version.startsWith("firefox") || version.startsWith("ios") || version.startsWith("safari") || version.startsWith("opera")
);
if (target.length !== 0)
return target;
}
function resolveNodeTarget(versions) {
const target = versions.filter((version) => version.startsWith("node"));
if (target.length !== 0)
return target;
}
const toggle = z.union([z.literal("error"), z.literal("warn"), z.literal("off")]), strictOptions = z.object({
noPackageJsonTypings: toggle.default("error"),
noImplicitSideEffects: toggle.default("warn"),
noImplicitBrowsersList: toggle.default("warn"),
alwaysPackageJsonTypes: toggle.default("error"),
alwaysPackageJsonMain: toggle.default("error"),
alwaysPackageJsonFiles: toggle.default("error"),
noCheckTypes: toggle.default("warn")
}).strict(), validationSchema = z.object({
strictOptions: strictOptions.default({})
});
function parseStrictOptions(input) {
return validationSchema.parse({ strictOptions: input }, { errorMap }).strictOptions;
}
async function resolveBuildContext(options) {
const {
config: config2,
cwd,
emitDeclarationOnly = !1,
logger,
pkg,
strict,
tsconfig: tsconfigPath
} = options, tsconfig = await loadTSConfig({ cwd, tsconfigPath }), strictOptions2 = parseStrictOptions(config2?.strictOptions ?? {});
if (strictOptions2.noCheckTypes !== "off" && tsconfig?.options && config2?.dts !== "rolldown" && tsconfig.options.noCheck !== !1 && !tsconfig.options.noCheck) {
if (strictOptions2.noCheckTypes === "error")
throw new Error(
"`noCheck` is not set to `true` in the tsconfig.json file used by `package.config.ts`. This makes generating dts files slower than it needs to be, as it will perform type checking on the dts files while at it."
);
logger.warn(
"`noCheck` is not set to `true` in the tsconfig.json file used by `package.config.ts`. This makes generating dts files slower than it needs to be, as it will perform type checking on the dts files while at it."
);
}
let browserslist2 = pkg.browserslist;
if (!browserslist2) {
if (strict && strictOptions2.noImplicitBrowsersList !== "off") {
if (strictOptions2.noImplicitBrowsersList === "error")
throw new Error(
'\n- package.json: "browserslist" is missing, set it to `"browserslist": "extends @sanity/browserslist-config"`'
);
logger.warn(
'Could not detect a `browserslist` property in `package.json`, using default configuration. Add `"browserslist": "extends @sanity/browserslist-config"` to silence this warning.'
);
}
browserslist2 = DEFAULT_BROWSERSLIST_QUERY;
}
const targetVersions = browserslistToEsbuild(browserslist2);
if (strict && strictOptions2.noImplicitSideEffects !== "off" && typeof pkg.sideEffects > "u") {
const msg = "package.json: `sideEffects` is missing, see https://webpack.js.org/guides/tree-shaking/#clarifying-tree-shaking-and-sideeffects for how to define `sideEffects`";
if (strictOptions2.noImplicitSideEffects === "error")
throw new Error(msg);
logger.warn(msg);
}
const nodeTarget = resolveNodeTarget(targetVersions), webTarget = resolveBrowserTarget(targetVersions);
if (!nodeTarget)
throw new Error("no matching `node` target");
if (!webTarget)
throw new Error("no matching `web` target");
const target = {
"*": webTarget.concat(nodeTarget),
browser: webTarget,
node: nodeTarget
}, parsedExports = parseExports({
cwd,
pkg,
strict,
strictOptions: strictOptions2,
logger
}).reduce(
(acc, { _path: exportPath, ...exportEntry }) => Object.assign(acc, { [exportPath]: exportEntry }),
{}
), exports = resolveConfigProperty(config2?.exports, parsedExports), parsedExternal = [
...pkg.dependencies ? Object.keys(pkg.dependencies) : [],
...pkg.peerDependencies ? Object.keys(pkg.peerDependencies) : []
], external = config2 && Array.isArray(config2.external) ? [...parsedExternal, ...config2.external] : resolveConfigProperty(config2?.external, parsedExternal), externalWithTypes = /* @__PURE__ */ new Set([pkg.name, ...external, ...external.map(transformPackageName)]), bundledDependencies = (pkg.devDependencies ? Object.keys(pkg.devDependencies) : []).filter(
// Do not bundle anything that is marked as external
(_) => !externalWithTypes.has(_)
), bundledPackages = config2 && Array.isArray(config2.extract?.bundledPackages) ? [...bundledDependencies, ...config2.extract.bundledPackages] : resolveConfigProperty(config2?.extract?.bundledPackages, bundledDependencies), outputPaths = Object.values(exports).flatMap((exportEntry) => [
exportEntry.import,
exportEntry.require,
exportEntry.browser?.import,
exportEntry.browser?.require,
exportEntry.node?.source && exportEntry.node.import,
exportEntry.node?.source && exportEntry.node.require
].filter(Boolean)).map((p) => path.resolve(cwd, p)), commonDistPath = findCommonDirPath(outputPaths);
if (commonDistPath === cwd)
throw new Error(
"all output files must share a common parent directory which is not the root package directory"
);
if (commonDistPath && !pathContains(cwd, commonDistPath))
throw new Error("all output files must be located within the package");
const configDistPath = config2?.dist ? path.resolve(cwd, config2.dist) : void 0;
if (configDistPath && commonDistPath && configDistPath !== commonDistPath && !pathContains(configDistPath, commonDistPath))
throw logger.log(`did you mean to configure \`dist: './${path.relative(cwd, commonDistPath)}'\`?`), new Error("all output files must be located with the configured `dist` path");
const distPath = configDistPath || commonDistPath;
if (!distPath)
throw new Error("could not detect `dist` path");
return {
config: config2,
cwd,
distPath,
emitDeclarationOnly,
exports,
external,
bundledPackages,
files: [],
logger,
pkg,
runtime: config2?.runtime ?? "*",
strict,
target,
ts: {
config: tsconfig,
configPath: tsconfigPath
},
dts: config2?.dts === "rolldown" ? "rolldown" : "api-extractor"
};
}
function transformPackageName(packageName) {
if (packageName.startsWith("@types/"))
return packageName;
if (packageName.startsWith("@")) {
const [scope, name] = packageName.split("/");
return `@types/${scope?.slice(1)}__${name}`;
} else
return `@types/${packageName}`;
}
function createConsoleSpy(options) {
const { onRestored } = options || {}, original = {
log: console.log,
warn: console.warn,
error: console.error
}, messages = [];
return console.log = (...args) => messages.push({ type: "log", args }), console.warn = (...args) => messages.push({ type: "warn", args }), console.error = (...args) => messages.push({ type: "error", args }), {
messages,
restore: () => {
console.log = original.log, console.warn = original.warn, console.error = original.error, onRestored?.(messages);
}
};
}
export {
DEFAULT_BROWSERSLIST_QUERY,
createConsoleSpy,
getTargetPaths,
loadConfig,
loadPkg,
loadPkgWithReporting,
parseExports,
parseStrictOptions,
pkgExtMap,
resolveBuildContext,
resolveConfigProperty
};
//# sourceMappingURL=consoleSpy.js.map