sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
345 lines (342 loc) • 16.4 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf, __hasOwnProp = Object.prototype.hasOwnProperty;
var __copyProps = (to, from, except, desc) => {
if (from && typeof from == "object" || typeof from == "function")
for (let key of __getOwnPropNames(from))
!__hasOwnProp.call(to, key) && key !== except && __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: !0 }) : target,
mod
));
var path = require("node:path"), chalk = require("chalk"), logSymbols = require("log-symbols"), semver = require("semver"), telemetry = require("@sanity/telemetry"), rimraf = require("rimraf"), previewServer = require("./previewServer.js"), runtime = require("./runtime.js"), checkRequiredDependencies = require("./checkRequiredDependencies.js"), timing = require("./timing.js"), fs = require("node:fs"), resolveFrom = require("resolve-from"), _internal = require("./_internal.js");
function _interopDefaultCompat(e) {
return e && typeof e == "object" && "default" in e ? e : { default: e };
}
var path__default = /* @__PURE__ */ _interopDefaultCompat(path), chalk__default = /* @__PURE__ */ _interopDefaultCompat(chalk), semver__default = /* @__PURE__ */ _interopDefaultCompat(semver), fs__default = /* @__PURE__ */ _interopDefaultCompat(fs), resolveFrom__default = /* @__PURE__ */ _interopDefaultCompat(resolveFrom);
const BuildTrace = telemetry.defineTrace({
name: "Studio Build Completed",
version: 0,
description: "A Studio build completed"
}), VENDOR_DIR = "vendor", VENDOR_IMPORTS = {
react: {
"^19.0.0": {
".": "./cjs/react.production.js",
"./jsx-runtime": "./cjs/react-jsx-runtime.production.js",
"./jsx-dev-runtime": "./cjs/react-jsx-dev-runtime.production.js",
"./compiler-runtime": "./cjs/react-compiler-runtime.production.js",
"./package.json": "./package.json"
},
"^18.0.0": {
".": "./cjs/react.production.min.js",
"./jsx-runtime": "./cjs/react-jsx-runtime.production.min.js",
"./jsx-dev-runtime": "./cjs/react-jsx-dev-runtime.production.min.js",
"./package.json": "./package.json"
}
},
"react-dom": {
"^19.0.0": {
".": "./cjs/react-dom.production.js",
"./client": "./cjs/react-dom-client.production.js",
"./server": "./cjs/react-dom-server-legacy.browser.production.js",
"./server.browser": "./cjs/react-dom-server-legacy.browser.production.js",
"./static": "./cjs/react-dom-server.browser.production.js",
"./static.browser": "./cjs/react-dom-server.browser.production.js",
"./package.json": "./package.json"
},
"^18.0.0": {
".": "./cjs/react-dom.production.min.js",
"./client": "./cjs/react-dom.production.min.js",
"./server": "./cjs/react-dom-server-legacy.browser.production.min.js",
"./server.browser": "./cjs/react-dom-server-legacy.browser.production.min.js",
"./package.json": "./package.json"
}
},
"styled-components": {
"^6.1.0": {
".": "./dist/styled-components.esm.js",
"./package.json": "./package.json"
}
}
};
async function buildVendorDependencies({
cwd,
outputDir,
basePath
}) {
const dir = path__default.default.relative(process.cwd(), path__default.default.resolve(cwd)), entry = {}, imports = {};
for (const [packageName, ranges] of Object.entries(VENDOR_IMPORTS)) {
const packageJsonPath = resolveFrom__default.default.silent(cwd, path__default.default.join(packageName, "package.json"));
if (!packageJsonPath)
throw new Error(`Could not find package.json for package '${packageName}' from directory '${dir}'. Is it installed?`);
let packageJson;
try {
packageJson = JSON.parse(await fs__default.default.promises.readFile(packageJsonPath, "utf-8"));
} catch (e) {
const message = `Could not read package.json for package '${packageName}' from directory '${dir}'`;
throw typeof e?.message == "string" ? (e.message = `${message}: ${e.message}`, e) : new Error(message, {
cause: e
});
}
const version = semver__default.default.coerce(packageJson.version)?.version;
if (!version)
throw new Error(`Could not parse version '${packageJson.version}' from '${packageName}'`);
const sortedRanges = Object.keys(ranges).sort((range1, range2) => {
const min1 = semver__default.default.minVersion(range1), min2 = semver__default.default.minVersion(range2);
if (!min1) throw new Error(`Could not parse range '${range1}'`);
if (!min2) throw new Error(`Could not parse range '${range2}'`);
return semver__default.default.rcompare(min1.version, min2.version);
}), matchedRange = sortedRanges.find((range) => semver__default.default.satisfies(version, range));
if (!matchedRange) {
const min = semver__default.default.minVersion(sortedRanges[sortedRanges.length - 1]);
throw min ? semver__default.default.gt(min.version, version) ? new Error(`Package '${packageName}' requires at least ${min.version}.`) : new Error(`Version '${version}' of package '${packageName}' is not supported yet.`) : new Error(`Could not find a minimum version for package '${packageName}'`);
}
const subpaths = ranges[matchedRange];
for (const [subpath, relativeEntryPoint] of Object.entries(subpaths)) {
const packagePath = path__default.default.dirname(packageJsonPath), entryPoint = resolveFrom__default.default.silent(packagePath, relativeEntryPoint);
if (!entryPoint)
throw new Error(`Failed to resolve entry point '${path__default.default.join(packageName, relativeEntryPoint)}'. `);
const specifier = path__default.default.posix.join(packageName, subpath), chunkName = path__default.default.posix.join(packageName, path__default.default.relative(packageName, specifier) || "index");
entry[chunkName] = entryPoint, imports[specifier] = path__default.default.posix.join("/", basePath, VENDOR_DIR, `${chunkName}.mjs`);
}
}
const {
build
} = await import("vite");
let buildResult = await build({
// Define a custom cache directory so that sanity's vite cache
// does not conflict with any potential local vite projects
cacheDir: "node_modules/.sanity/vite-vendor",
root: cwd,
configFile: !1,
logLevel: "silent",
appType: "custom",
mode: "production",
define: {
"process.env.NODE_ENV": JSON.stringify("production")
},
build: {
commonjsOptions: {
strictRequires: "auto"
},
minify: !0,
emptyOutDir: !1,
// Rely on CLI to do this
outDir: path__default.default.join(outputDir, VENDOR_DIR),
lib: {
entry,
formats: ["es"]
},
rollupOptions: {
external: runtime.createExternalFromImportMap({
imports
}),
output: {
entryFileNames: "[name]-[hash].mjs",
chunkFileNames: "[name]-[hash].mjs",
exports: "named",
format: "es"
},
treeshake: {
preset: "recommended"
}
}
}
});
buildResult = Array.isArray(buildResult) ? buildResult : [buildResult];
const hashedImports = {}, output = buildResult.flatMap((i) => i.output);
for (const chunk of output)
if (chunk.type !== "asset")
for (const [specifier, originalPath] of Object.entries(imports))
originalPath.endsWith(`${chunk.name}.mjs`) && (hashedImports[specifier] = path__default.default.posix.join("/", basePath, VENDOR_DIR, chunk.fileName));
return hashedImports;
}
async function getRemoteResolvedVersion(fetchFn, url) {
try {
return (await fetchFn(url, {
method: "HEAD",
redirect: "manual"
})).headers.get("x-resolved-version");
} catch (err) {
throw new Error(`Failed to fetch remote version for ${url}: ${err.message}`);
}
}
async function compareStudioDependencyVersions(autoUpdatesImports, workDir, fetchFn = globalThis.fetch) {
const manifest = checkRequiredDependencies.readPackageJson(path__default.default.join(workDir, "package.json")), dependencies = {
...manifest.dependencies,
...manifest.devDependencies
}, failedDependencies = [], filteredAutoUpdatesImports = Object.entries(autoUpdatesImports).filter(([pkg]) => !pkg.endsWith("/"));
for (const [pkg, value] of filteredAutoUpdatesImports) {
const resolvedVersion = await getRemoteResolvedVersion(fetchFn, value);
if (!resolvedVersion)
throw new Error(`Failed to fetch remote version for ${value}`);
const dependency = dependencies[pkg], manifestPath = resolveFrom__default.default.silent(workDir, path__default.default.join(pkg, "package.json")), installed = semver__default.default.coerce(manifestPath ? checkRequiredDependencies.readPackageJson(manifestPath).version : dependency);
if (!installed)
throw new Error(`Failed to parse installed version for ${pkg}`);
semver__default.default.eq(resolvedVersion, installed.version) || failedDependencies.push({
pkg,
installed: installed.version,
remote: resolvedVersion
});
}
return failedDependencies;
}
const MODULES_HOST = process.env.SANITY_INTERNAL_ENV === "staging" ? "https://sanity-cdn.work" : "https://sanity-cdn.com";
function getAutoUpdateImportMap(version) {
const timestamp = `t${Math.floor(Date.now() / 1e3)}`;
return {
sanity: `${MODULES_HOST}/v1/modules/sanity/default/${version}/${timestamp}`,
"sanity/": `${MODULES_HOST}/v1/modules/sanity/default/${version}/${timestamp}/`,
"@sanity/vision": `${MODULES_HOST}/v1/modules/@sanity__vision/default/${version}/${timestamp}`,
"@sanity/vision/": `${MODULES_HOST}/v1/modules/@sanity__vision/default/${version}/${timestamp}/`
};
}
function shouldAutoUpdate({
flags,
cliConfig
}) {
return "auto-updates" in flags ? !!flags["auto-updates"] : cliConfig && "autoUpdates" in cliConfig ? !!cliConfig.autoUpdates : !1;
}
async function buildSanityStudio(args, context, overrides) {
const timer = timing.getTimer(), {
output,
prompt,
workDir,
cliConfig,
telemetry: telemetry$1 = telemetry.noopLogger
} = context, flags = {
minify: !0,
stats: !1,
"source-maps": !1,
...args.extOptions
}, unattendedMode = !!(flags.yes || flags.y), defaultOutputDir = path__default.default.resolve(path__default.default.join(workDir, "dist")), outputDir = path__default.default.resolve(args.argsWithoutOptions[0] || defaultOutputDir), isApp = _internal.determineIsApp(cliConfig);
await checkRequiredDependencies.checkStudioDependencyVersions(workDir);
const {
didInstall,
installedSanityVersion
} = await checkRequiredDependencies.checkRequiredDependencies(context);
if (didInstall)
return {
didCompile: !1
};
const autoUpdatesEnabled = shouldAutoUpdate({
flags,
cliConfig
}), coercedSanityVersion = semver__default.default.coerce(installedSanityVersion)?.version;
if (autoUpdatesEnabled && !coercedSanityVersion)
throw new Error(`Failed to parse installed Sanity version: ${installedSanityVersion}`);
const version = encodeURIComponent(`^${coercedSanityVersion}`), autoUpdatesImports = getAutoUpdateImportMap(version);
if (autoUpdatesEnabled) {
output.print(`${logSymbols.info} Building with auto-updates enabled`);
try {
const result = await compareStudioDependencyVersions(autoUpdatesImports, workDir);
if (result?.length && !unattendedMode && !await prompt.single({
type: "confirm",
message: chalk__default.default.yellow(`The following local package versions are different from the versions currently served at runtime.
When using auto updates, we recommend that you test locally with the same versions before deploying.
${result.map((mod) => ` - ${mod.pkg} (local version: ${mod.installed}, runtime version: ${mod.remote})`).join(`
`)}
Continue anyway?`),
default: !1
}))
return process.exit(0);
} catch (err) {
throw err;
}
}
const envVarKeys = getSanityEnvVars();
envVarKeys.length > 0 && (output.print(`
Including the following environment variables as part of the JavaScript bundle:`), envVarKeys.forEach((key) => output.print(`- ${key}`)), output.print(""));
let shouldClean = !0;
outputDir !== defaultOutputDir && !unattendedMode && (shouldClean = await prompt.single({
type: "confirm",
message: `Do you want to delete the existing directory (${outputDir}) first?`,
default: !0
}));
let basePath = "/";
const envBasePath = process.env.SANITY_STUDIO_BASEPATH, configBasePath = cliConfig?.project?.basePath;
overrides?.basePath ? basePath = overrides.basePath : envBasePath ? basePath = envBasePath : configBasePath && (basePath = configBasePath), envBasePath && configBasePath && output.warn(`Overriding configured base path (${configBasePath}) with value from environment variable (${envBasePath})`);
let spin;
if (shouldClean) {
timer.start("cleanOutputFolder"), spin = output.spinner("Clean output folder").start(), await rimraf.rimraf(outputDir);
const cleanDuration = timer.end("cleanOutputFolder");
spin.text = `Clean output folder (${cleanDuration.toFixed()}ms)`, spin.succeed();
}
spin = output.spinner(`Build Sanity ${isApp ? "application" : "Studio"}`).start();
const trace = telemetry$1.trace(BuildTrace);
trace.start();
let importMap;
autoUpdatesEnabled && (importMap = {
imports: {
...await buildVendorDependencies({
cwd: workDir,
outputDir,
basePath
}),
...autoUpdatesImports
}
});
try {
timer.start("bundleStudio");
const bundle = await previewServer.buildStaticFiles({
cwd: workDir,
outputDir,
basePath,
sourceMap: !!flags["source-maps"],
minify: !!flags.minify,
vite: cliConfig && "vite" in cliConfig ? cliConfig.vite : void 0,
importMap,
reactCompiler: cliConfig && "reactCompiler" in cliConfig ? cliConfig.reactCompiler : void 0,
entry: cliConfig && "app" in cliConfig ? cliConfig.app?.entry : void 0,
isApp
});
trace.log({
outputSize: bundle.chunks.flatMap((chunk) => chunk.modules.flatMap((mod) => mod.renderedLength)).reduce((sum, n) => sum + n, 0)
});
const buildDuration = timer.end("bundleStudio");
spin.text = `Build Sanity ${isApp ? "application" : "Studio"} (${buildDuration.toFixed()}ms)`, spin.succeed(), trace.complete(), flags.stats && (output.print(`
Largest module files:`), output.print(formatModuleSizes(sortModulesBySize(bundle.chunks).slice(0, 15))));
} catch (err) {
throw spin.fail(), trace.error(err), err;
}
return {
didCompile: !0
};
}
function getSanityEnvVars(env = process.env) {
return Object.keys(env).filter((key) => key.toUpperCase().startsWith("SANITY_STUDIO_"));
}
function sortModulesBySize(chunks) {
return chunks.flatMap((chunk) => chunk.modules).sort((modA, modB) => modB.renderedLength - modA.renderedLength);
}
function formatModuleSizes(modules) {
const lines = [];
for (const mod of modules)
lines.push(` - ${formatModuleName(mod.name)} (${formatSize(mod.renderedLength)})`);
return lines.join(`
`);
}
function formatModuleName(modName) {
const delimiter = "/node_modules/", nodeIndex = modName.lastIndexOf(delimiter);
return nodeIndex === -1 ? modName : modName.slice(nodeIndex + delimiter.length);
}
function formatSize(bytes) {
return chalk__default.default.cyan(`${(bytes / 1024).toFixed()} kB`);
}
var buildAction = /* @__PURE__ */ Object.freeze({
__proto__: null,
default: buildSanityStudio
});
exports.buildAction = buildAction;
exports.buildSanityStudio = buildSanityStudio;
exports.shouldAutoUpdate = shouldAutoUpdate;
//# sourceMappingURL=buildAction.js.map