@sanity/cli-build 2.0.1 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_exports/_internal/build.d.ts +136 -86
- package/dist/_exports/_internal/build.js +3 -4
- package/dist/_exports/_internal/build.js.map +1 -1
- package/dist/_exports/_internal/extract.d.ts +1 -1
- package/dist/actions/build/buildApp.js +190 -0
- package/dist/actions/build/buildApp.js.map +1 -0
- package/dist/actions/build/buildDebug.js +1 -1
- package/dist/actions/build/buildDebug.js.map +1 -1
- package/dist/actions/build/buildStaticFiles.js +3 -3
- package/dist/actions/build/buildStaticFiles.js.map +1 -1
- package/dist/actions/build/buildStudio.js +222 -0
- package/dist/actions/build/buildStudio.js.map +1 -0
- package/dist/actions/build/checkRequiredDependencies.js +1 -1
- package/dist/actions/build/checkRequiredDependencies.js.map +1 -1
- package/dist/actions/build/checkStudioDependencyVersions.js +1 -1
- package/dist/actions/build/checkStudioDependencyVersions.js.map +1 -1
- package/dist/actions/build/decorateIndexWithStagingScript.js +1 -1
- package/dist/actions/build/decorateIndexWithStagingScript.js.map +1 -1
- package/dist/actions/build/getViteConfig.js +7 -6
- package/dist/actions/build/getViteConfig.js.map +1 -1
- package/dist/actions/build/handlePrereleaseVersions.js +44 -0
- package/dist/actions/build/handlePrereleaseVersions.js.map +1 -0
- package/dist/actions/build/renderDocument.js +1 -1
- package/dist/actions/build/renderDocument.js.map +1 -1
- package/dist/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.js +62 -1
- package/dist/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.js.map +1 -1
- package/dist/actions/build/renderDocumentWorker/tryLoadDocumentComponent.js +1 -1
- package/dist/actions/build/renderDocumentWorker/tryLoadDocumentComponent.js.map +1 -1
- package/dist/actions/build/resolveVendorBuildConfig.js +1 -1
- package/dist/actions/build/resolveVendorBuildConfig.js.map +1 -1
- package/dist/actions/build/writeFavicons.js +3 -3
- package/dist/actions/build/writeFavicons.js.map +1 -1
- package/dist/actions/build/writeSanityRuntime.js +5 -5
- package/dist/actions/build/writeSanityRuntime.js.map +1 -1
- package/dist/actions/schema/extractSanitySchema.worker.js +1 -1
- package/dist/actions/schema/extractSanitySchema.worker.js.map +1 -1
- package/dist/actions/schema/getExtractOptions.js.map +1 -1
- package/dist/actions/schema/runSchemaExtraction.js +1 -1
- package/dist/actions/schema/runSchemaExtraction.js.map +1 -1
- package/dist/actions/schema/utils/extractValidationFromSchemaError.js +1 -1
- package/dist/actions/schema/utils/extractValidationFromSchemaError.js.map +1 -1
- package/dist/actions/schema/vite/plugin-schema-extraction.js.map +1 -1
- package/dist/util/compareDependencyVersions.js +110 -0
- package/dist/util/compareDependencyVersions.js.map +1 -0
- package/package.json +6 -8
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
import { isInteractive } from '@sanity/cli-core/util';
|
|
3
|
+
import { select } from '@sanity/cli-core/ux';
|
|
4
|
+
/**
|
|
5
|
+
* Handle prerelease versions that cannot be resolved by the auto-updates CDN.
|
|
6
|
+
*
|
|
7
|
+
* In unattended or non-interactive mode, exits with an error. In interactive mode,
|
|
8
|
+
* prompts the user to either disable auto-updates for this build or cancel.
|
|
9
|
+
*
|
|
10
|
+
* Does not return if the build should be cancelled (exits via `output.error`).
|
|
11
|
+
*/ export async function handlePrereleaseVersions({ output, unattendedMode, unresolvedPrerelease }) {
|
|
12
|
+
const prereleaseMessage = `The following packages are using prerelease versions not yet available on the auto-updates CDN:\n\n` + `${unresolvedPrerelease.map((mod)=>` - ${mod.pkg} (${mod.version})`).join('\n')}\n\n` + `Auto-updates cannot be used with prerelease versions. To re-enable auto-updates later, ` + `switch to a non-prerelease version locally and deploy again.`;
|
|
13
|
+
if (unattendedMode || !isInteractive()) {
|
|
14
|
+
output.error(`${prereleaseMessage}\n\n` + `Cannot build with auto-updates in unattended mode when using prerelease versions. ` + `Either switch to a non-prerelease version, or use --no-auto-updates to build without auto-updates.`, {
|
|
15
|
+
exit: 1
|
|
16
|
+
});
|
|
17
|
+
// output.error with exit: 1 throws, but TypeScript doesn't know that
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const choice = await select({
|
|
21
|
+
choices: [
|
|
22
|
+
{
|
|
23
|
+
name: 'Disable auto-updates for this build and continue',
|
|
24
|
+
value: 'disable-auto-updates'
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'Cancel build',
|
|
28
|
+
value: 'cancel'
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
default: 'disable-auto-updates',
|
|
32
|
+
message: styleText('yellow', prereleaseMessage)
|
|
33
|
+
});
|
|
34
|
+
if (choice === 'cancel') {
|
|
35
|
+
output.error('Declined to continue with build', {
|
|
36
|
+
exit: 1
|
|
37
|
+
});
|
|
38
|
+
// output.error with exit: 1 throws, but TypeScript doesn't know that
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
output.warn('Auto-updates disabled for this build');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=handlePrereleaseVersions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/build/handlePrereleaseVersions.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {type Output} from '@sanity/cli-core/types'\nimport {isInteractive} from '@sanity/cli-core/util'\nimport {select} from '@sanity/cli-core/ux'\n\nimport {type UnresolvedPrerelease} from '../../util/compareDependencyVersions.js'\n\n/**\n * Handle prerelease versions that cannot be resolved by the auto-updates CDN.\n *\n * In unattended or non-interactive mode, exits with an error. In interactive mode,\n * prompts the user to either disable auto-updates for this build or cancel.\n *\n * Does not return if the build should be cancelled (exits via `output.error`).\n */\nexport async function handlePrereleaseVersions({\n output,\n unattendedMode,\n unresolvedPrerelease,\n}: {\n output: Output\n unattendedMode: boolean\n unresolvedPrerelease: UnresolvedPrerelease[]\n}): Promise<void> {\n const prereleaseMessage =\n `The following packages are using prerelease versions not yet available on the auto-updates CDN:\\n\\n` +\n `${unresolvedPrerelease.map((mod) => ` - ${mod.pkg} (${mod.version})`).join('\\n')}\\n\\n` +\n `Auto-updates cannot be used with prerelease versions. To re-enable auto-updates later, ` +\n `switch to a non-prerelease version locally and deploy again.`\n\n if (unattendedMode || !isInteractive()) {\n output.error(\n `${prereleaseMessage}\\n\\n` +\n `Cannot build with auto-updates in unattended mode when using prerelease versions. ` +\n `Either switch to a non-prerelease version, or use --no-auto-updates to build without auto-updates.`,\n {exit: 1},\n )\n // output.error with exit: 1 throws, but TypeScript doesn't know that\n return\n }\n\n const choice = await select({\n choices: [\n {\n name: 'Disable auto-updates for this build and continue',\n value: 'disable-auto-updates',\n },\n {name: 'Cancel build', value: 'cancel'},\n ],\n default: 'disable-auto-updates',\n message: styleText('yellow', prereleaseMessage),\n })\n\n if (choice === 'cancel') {\n output.error('Declined to continue with build', {exit: 1})\n // output.error with exit: 1 throws, but TypeScript doesn't know that\n return\n }\n\n output.warn('Auto-updates disabled for this build')\n}\n"],"names":["styleText","isInteractive","select","handlePrereleaseVersions","output","unattendedMode","unresolvedPrerelease","prereleaseMessage","map","mod","pkg","version","join","error","exit","choice","choices","name","value","default","message","warn"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAGnC,SAAQC,aAAa,QAAO,wBAAuB;AACnD,SAAQC,MAAM,QAAO,sBAAqB;AAI1C;;;;;;;CAOC,GACD,OAAO,eAAeC,yBAAyB,EAC7CC,MAAM,EACNC,cAAc,EACdC,oBAAoB,EAKrB;IACC,MAAMC,oBACJ,CAAC,mGAAmG,CAAC,GACrG,GAAGD,qBAAqBE,GAAG,CAAC,CAACC,MAAQ,CAAC,GAAG,EAAEA,IAAIC,GAAG,CAAC,EAAE,EAAED,IAAIE,OAAO,CAAC,CAAC,CAAC,EAAEC,IAAI,CAAC,MAAM,IAAI,CAAC,GACvF,CAAC,uFAAuF,CAAC,GACzF,CAAC,4DAA4D,CAAC;IAEhE,IAAIP,kBAAkB,CAACJ,iBAAiB;QACtCG,OAAOS,KAAK,CACV,GAAGN,kBAAkB,IAAI,CAAC,GACxB,CAAC,kFAAkF,CAAC,GACpF,CAAC,kGAAkG,CAAC,EACtG;YAACO,MAAM;QAAC;QAEV,qEAAqE;QACrE;IACF;IAEA,MAAMC,SAAS,MAAMb,OAAO;QAC1Bc,SAAS;YACP;gBACEC,MAAM;gBACNC,OAAO;YACT;YACA;gBAACD,MAAM;gBAAgBC,OAAO;YAAQ;SACvC;QACDC,SAAS;QACTC,SAASpB,UAAU,UAAUO;IAC/B;IAEA,IAAIQ,WAAW,UAAU;QACvBX,OAAOS,KAAK,CAAC,mCAAmC;YAACC,MAAM;QAAC;QACxD,qEAAqE;QACrE;IACF;IAEAV,OAAOiB,IAAI,CAAC;AACd"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ux } from '@oclif/core';
|
|
2
|
-
import { tsxWorkerTask } from '@sanity/cli-core';
|
|
2
|
+
import { tsxWorkerTask } from '@sanity/cli-core/tasks';
|
|
3
3
|
import { buildDebug } from './buildDebug.js';
|
|
4
4
|
const hasWarnedAbout = new Set();
|
|
5
5
|
export async function renderDocument(options) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/build/renderDocument.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {tsxWorkerTask} from '@sanity/cli-core'\n\nimport {buildDebug} from './buildDebug.js'\n\ninterface DocumentProps {\n basePath: string\n\n css?: string[]\n entryPath?: string\n title?: string\n}\n\ninterface RenderDocumentOptions {\n studioRootPath: string\n\n autoUpdatesCssUrls?: string[]\n importMap?: {\n imports?: Record<string, string>\n }\n isApp?: boolean\n props?: DocumentProps\n}\n\nconst hasWarnedAbout = new Set<string>()\n\ninterface RenderDocumentWorkerResult {\n type: 'error' | 'result' | 'warning'\n\n html?: string\n message?: string | string[]\n warnKey?: string\n}\n\nexport async function renderDocument(options: RenderDocumentOptions): Promise<string> {\n buildDebug('Starting worker thread for %s', import.meta.url)\n try {\n const msg = await tsxWorkerTask<RenderDocumentWorkerResult>(\n new URL(`renderDocument.worker.js`, import.meta.url),\n {\n name: 'renderDocument',\n rootPath: options.studioRootPath,\n workerData: {...options, shouldWarn: true},\n },\n )\n\n if (msg.type === 'warning') {\n if (msg.warnKey && hasWarnedAbout.has(msg.warnKey)) {\n return ''\n }\n\n if (Array.isArray(msg.message)) {\n for (const warning of msg.message) {\n ux.warn(warning)\n }\n } else if (msg.message) {\n ux.warn(msg.message)\n }\n\n if (msg.warnKey) {\n hasWarnedAbout.add(msg.warnKey)\n }\n return ''\n }\n\n if (msg.type === 'error') {\n buildDebug('Error from worker: %s', msg.message || 'Unknown error')\n throw new Error(\n Array.isArray(msg.message)\n ? msg.message.join('\\n')\n : msg.message || 'Document rendering worker stopped with an unknown error',\n )\n }\n\n if (msg.type === 'result') {\n if (!msg.html) {\n throw new Error('Document rendering worker stopped with an unknown error')\n }\n\n buildDebug('Document HTML rendered, %d bytes', msg.html.length)\n return msg.html\n }\n\n throw new Error('Unknown message type')\n } catch (err) {\n buildDebug('Worker errored: %s', err.message)\n throw err\n }\n}\n"],"names":["ux","tsxWorkerTask","buildDebug","hasWarnedAbout","Set","renderDocument","options","url","msg","URL","name","rootPath","studioRootPath","workerData","shouldWarn","type","warnKey","has","Array","isArray","message","warning","warn","add","Error","join","html","length","err"],"mappings":"AAAA,SAAQA,EAAE,QAAO,cAAa;AAC9B,SAAQC,aAAa,QAAO,
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/build/renderDocument.ts"],"sourcesContent":["import {ux} from '@oclif/core'\nimport {tsxWorkerTask} from '@sanity/cli-core/tasks'\n\nimport {buildDebug} from './buildDebug.js'\n\ninterface DocumentProps {\n basePath: string\n\n css?: string[]\n entryPath?: string\n title?: string\n}\n\ninterface RenderDocumentOptions {\n studioRootPath: string\n\n autoUpdatesCssUrls?: string[]\n importMap?: {\n imports?: Record<string, string>\n }\n isApp?: boolean\n props?: DocumentProps\n}\n\nconst hasWarnedAbout = new Set<string>()\n\ninterface RenderDocumentWorkerResult {\n type: 'error' | 'result' | 'warning'\n\n html?: string\n message?: string | string[]\n warnKey?: string\n}\n\nexport async function renderDocument(options: RenderDocumentOptions): Promise<string> {\n buildDebug('Starting worker thread for %s', import.meta.url)\n try {\n const msg = await tsxWorkerTask<RenderDocumentWorkerResult>(\n new URL(`renderDocument.worker.js`, import.meta.url),\n {\n name: 'renderDocument',\n rootPath: options.studioRootPath,\n workerData: {...options, shouldWarn: true},\n },\n )\n\n if (msg.type === 'warning') {\n if (msg.warnKey && hasWarnedAbout.has(msg.warnKey)) {\n return ''\n }\n\n if (Array.isArray(msg.message)) {\n for (const warning of msg.message) {\n ux.warn(warning)\n }\n } else if (msg.message) {\n ux.warn(msg.message)\n }\n\n if (msg.warnKey) {\n hasWarnedAbout.add(msg.warnKey)\n }\n return ''\n }\n\n if (msg.type === 'error') {\n buildDebug('Error from worker: %s', msg.message || 'Unknown error')\n throw new Error(\n Array.isArray(msg.message)\n ? msg.message.join('\\n')\n : msg.message || 'Document rendering worker stopped with an unknown error',\n )\n }\n\n if (msg.type === 'result') {\n if (!msg.html) {\n throw new Error('Document rendering worker stopped with an unknown error')\n }\n\n buildDebug('Document HTML rendered, %d bytes', msg.html.length)\n return msg.html\n }\n\n throw new Error('Unknown message type')\n } catch (err) {\n buildDebug('Worker errored: %s', err.message)\n throw err\n }\n}\n"],"names":["ux","tsxWorkerTask","buildDebug","hasWarnedAbout","Set","renderDocument","options","url","msg","URL","name","rootPath","studioRootPath","workerData","shouldWarn","type","warnKey","has","Array","isArray","message","warning","warn","add","Error","join","html","length","err"],"mappings":"AAAA,SAAQA,EAAE,QAAO,cAAa;AAC9B,SAAQC,aAAa,QAAO,yBAAwB;AAEpD,SAAQC,UAAU,QAAO,kBAAiB;AAqB1C,MAAMC,iBAAiB,IAAIC;AAU3B,OAAO,eAAeC,eAAeC,OAA8B;IACjEJ,WAAW,iCAAiC,YAAYK,GAAG;IAC3D,IAAI;QACF,MAAMC,MAAM,MAAMP,cAChB,IAAIQ,IAAI,CAAC,wBAAwB,CAAC,EAAE,YAAYF,GAAG,GACnD;YACEG,MAAM;YACNC,UAAUL,QAAQM,cAAc;YAChCC,YAAY;gBAAC,GAAGP,OAAO;gBAAEQ,YAAY;YAAI;QAC3C;QAGF,IAAIN,IAAIO,IAAI,KAAK,WAAW;YAC1B,IAAIP,IAAIQ,OAAO,IAAIb,eAAec,GAAG,CAACT,IAAIQ,OAAO,GAAG;gBAClD,OAAO;YACT;YAEA,IAAIE,MAAMC,OAAO,CAACX,IAAIY,OAAO,GAAG;gBAC9B,KAAK,MAAMC,WAAWb,IAAIY,OAAO,CAAE;oBACjCpB,GAAGsB,IAAI,CAACD;gBACV;YACF,OAAO,IAAIb,IAAIY,OAAO,EAAE;gBACtBpB,GAAGsB,IAAI,CAACd,IAAIY,OAAO;YACrB;YAEA,IAAIZ,IAAIQ,OAAO,EAAE;gBACfb,eAAeoB,GAAG,CAACf,IAAIQ,OAAO;YAChC;YACA,OAAO;QACT;QAEA,IAAIR,IAAIO,IAAI,KAAK,SAAS;YACxBb,WAAW,yBAAyBM,IAAIY,OAAO,IAAI;YACnD,MAAM,IAAII,MACRN,MAAMC,OAAO,CAACX,IAAIY,OAAO,IACrBZ,IAAIY,OAAO,CAACK,IAAI,CAAC,QACjBjB,IAAIY,OAAO,IAAI;QAEvB;QAEA,IAAIZ,IAAIO,IAAI,KAAK,UAAU;YACzB,IAAI,CAACP,IAAIkB,IAAI,EAAE;gBACb,MAAM,IAAIF,MAAM;YAClB;YAEAtB,WAAW,oCAAoCM,IAAIkB,IAAI,CAACC,MAAM;YAC9D,OAAOnB,IAAIkB,IAAI;QACjB;QAEA,MAAM,IAAIF,MAAM;IAClB,EAAE,OAAOI,KAAK;QACZ1B,WAAW,sBAAsB0B,IAAIR,OAAO;QAC5C,MAAMQ;IACR;AACF"}
|
|
@@ -20,10 +20,22 @@ import { parse as parseHtml } from 'node-html-parser';
|
|
|
20
20
|
importMapEl.type = 'importmap';
|
|
21
21
|
const newTimestamp = \`/t\${Math.floor(Date.now() / 1000)}\`;
|
|
22
22
|
|
|
23
|
+
function isSanityCdnHost(hostname) {
|
|
24
|
+
return /^sanity-cdn\\.[a-zA-Z]+$/.test(hostname);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isSanityCdnUrl(urlStr) {
|
|
28
|
+
try {
|
|
29
|
+
return isSanityCdnHost(new URL(urlStr).hostname);
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
23
35
|
function replaceTimestamp(urlStr) {
|
|
24
36
|
try {
|
|
25
37
|
const url = new URL(urlStr);
|
|
26
|
-
if (
|
|
38
|
+
if (isSanityCdnHost(url.hostname)) {
|
|
27
39
|
url.pathname = url.pathname.replace(/\\/t\\d+/, newTimestamp);
|
|
28
40
|
}
|
|
29
41
|
return url.toString();
|
|
@@ -47,6 +59,55 @@ import { parse as parseHtml } from 'node-html-parser';
|
|
|
47
59
|
linkEl.href = replaceTimestamp(cssUrl);
|
|
48
60
|
document.head.appendChild(linkEl);
|
|
49
61
|
}
|
|
62
|
+
|
|
63
|
+
// The CDN serves the \`sanity\` module via a cross-origin 302
|
|
64
|
+
// (sanity-cdn.com -> modules.sanity-cdn.com). WebKit mishandles a
|
|
65
|
+
// \`modulepreload\` that follows a cross-origin redirect: it forces the CORS
|
|
66
|
+
// credentials flag true even for \`crossorigin="anonymous"\`, then rejects the
|
|
67
|
+
// redirect because the CDN sends no \`Access-Control-Allow-Credentials\`,
|
|
68
|
+
// which blanks the studio. (This is why these hints were reverted; see
|
|
69
|
+
// PR #1400.)
|
|
70
|
+
//
|
|
71
|
+
// \`preconnect\` only warms a socket; it never follows the redirect or fetches
|
|
72
|
+
// the module, so it is safe in every engine and runs unconditionally.
|
|
73
|
+
const firstCdnImport = Object.values(imports).find(isSanityCdnUrl);
|
|
74
|
+
|
|
75
|
+
if (firstCdnImport) {
|
|
76
|
+
const preconnectEl = document.createElement('link');
|
|
77
|
+
preconnectEl.rel = 'preconnect';
|
|
78
|
+
preconnectEl.href = new URL(firstCdnImport).origin;
|
|
79
|
+
// Module fetches are CORS; without crossorigin the warmed socket is not reused.
|
|
80
|
+
preconnectEl.crossOrigin = 'anonymous';
|
|
81
|
+
document.head.appendChild(preconnectEl);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// \`modulepreload\` is the hint that follows the redirect and can blank the
|
|
85
|
+
// studio in WebKit, so it is gated behind a positive allowlist: it runs only
|
|
86
|
+
// for engines confirmed to handle the cross-origin redirect, i.e. Chromium or
|
|
87
|
+
// Gecko. Every Chromium variant (Edge, Opera, Samsung Internet, ...) carries
|
|
88
|
+
// the \`Chrome\` token, so matching \`Chrom(e|ium)|Firefox\` covers them without
|
|
89
|
+
// enumerating brands. The iOS device-name exclusion is the hard guard for the
|
|
90
|
+
// brick-prone platform: every iOS browser is WebKit regardless of brand, and
|
|
91
|
+
// iOS in-app webviews carry messy UAs, so nothing on an iOS device is ever
|
|
92
|
+
// treated as safe. Anything unrecognised falls through to no preload, costing
|
|
93
|
+
// a missed download rather than bricking the studio.
|
|
94
|
+
const userAgent = typeof navigator === 'undefined' ? '' : navigator.userAgent || '';
|
|
95
|
+
const isKnownSafeEngine =
|
|
96
|
+
!/\\b(iPad|iPhone|iPod)\\b/.test(userAgent) && /Chrom(e|ium)|Firefox/.test(userAgent);
|
|
97
|
+
|
|
98
|
+
// The href reuses replaceTimestamp so it matches the importmap entry exactly;
|
|
99
|
+
// a stale timestamp here would double-fetch the largest chunk.
|
|
100
|
+
const sanityModuleUrl = imports['sanity'];
|
|
101
|
+
if (isKnownSafeEngine && typeof sanityModuleUrl === 'string' && isSanityCdnUrl(sanityModuleUrl)) {
|
|
102
|
+
const preloadEl = document.createElement('link');
|
|
103
|
+
preloadEl.rel = 'modulepreload';
|
|
104
|
+
preloadEl.href = replaceTimestamp(sanityModuleUrl);
|
|
105
|
+
// Must match the preconnect's credentials mode, otherwise the browser
|
|
106
|
+
// treats them as separate connections and the warmed cross-origin socket
|
|
107
|
+
// is not reused for this fetch.
|
|
108
|
+
preloadEl.crossOrigin = 'anonymous';
|
|
109
|
+
document.head.appendChild(preloadEl);
|
|
110
|
+
}
|
|
50
111
|
</script>`;
|
|
51
112
|
/**
|
|
52
113
|
* @internal
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.ts"],"sourcesContent":["import {parse as parseHtml} from 'node-html-parser'\n\n/**\n * This script takes the import map from the `#__imports` script tag,\n * modifies relevant URLs that match the sanity-cdn hostname by replacing\n * the existing timestamp in the sanity-cdn URLs with a new runtime timestamp,\n * and injects the modified import map back into the HTML.\n *\n * It also synchronously creates `<link rel=\"stylesheet\">` tags for each CDN\n * CSS URL with a fresh timestamp.\n *\n * This will be injected into the HTML of the user's bundle.\n *\n * Note that this is in a separate constants file to prevent \"Cannot access\n * before initialization\" errors.\n */\nconst TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT = `<script>\n // auto-generated script to add import map with timestamp\n const importsJson = document.getElementById('__imports')?.textContent;\n const { imports = {}, css = [], ...rest } = importsJson ? JSON.parse(importsJson) : {};\n const importMapEl = document.createElement('script');\n importMapEl.type = 'importmap';\n const newTimestamp = \\`/t\\${Math.floor(Date.now() / 1000)}\\`;\n\n function replaceTimestamp(urlStr) {\n try {\n const url = new URL(urlStr);\n if (
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.ts"],"sourcesContent":["import {parse as parseHtml} from 'node-html-parser'\n\n/**\n * This script takes the import map from the `#__imports` script tag,\n * modifies relevant URLs that match the sanity-cdn hostname by replacing\n * the existing timestamp in the sanity-cdn URLs with a new runtime timestamp,\n * and injects the modified import map back into the HTML.\n *\n * It also synchronously creates `<link rel=\"stylesheet\">` tags for each CDN\n * CSS URL with a fresh timestamp.\n *\n * This will be injected into the HTML of the user's bundle.\n *\n * Note that this is in a separate constants file to prevent \"Cannot access\n * before initialization\" errors.\n */\nconst TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT = `<script>\n // auto-generated script to add import map with timestamp\n const importsJson = document.getElementById('__imports')?.textContent;\n const { imports = {}, css = [], ...rest } = importsJson ? JSON.parse(importsJson) : {};\n const importMapEl = document.createElement('script');\n importMapEl.type = 'importmap';\n const newTimestamp = \\`/t\\${Math.floor(Date.now() / 1000)}\\`;\n\n function isSanityCdnHost(hostname) {\n return /^sanity-cdn\\\\.[a-zA-Z]+$/.test(hostname);\n }\n\n function isSanityCdnUrl(urlStr) {\n try {\n return isSanityCdnHost(new URL(urlStr).hostname);\n } catch {\n return false;\n }\n }\n\n function replaceTimestamp(urlStr) {\n try {\n const url = new URL(urlStr);\n if (isSanityCdnHost(url.hostname)) {\n url.pathname = url.pathname.replace(/\\\\/t\\\\d+/, newTimestamp);\n }\n return url.toString();\n } catch {\n return urlStr;\n }\n }\n\n importMapEl.textContent = JSON.stringify({\n imports: Object.fromEntries(\n Object.entries(imports).map(([specifier, path]) => [specifier, replaceTimestamp(path)])\n ),\n ...rest,\n });\n document.head.appendChild(importMapEl);\n\n // Creates <link rel=\"stylesheet\"> tags with fresh timestamps.\n for (const cssUrl of css) {\n const linkEl = document.createElement('link');\n linkEl.rel = 'stylesheet';\n linkEl.href = replaceTimestamp(cssUrl);\n document.head.appendChild(linkEl);\n }\n\n // The CDN serves the \\`sanity\\` module via a cross-origin 302\n // (sanity-cdn.com -> modules.sanity-cdn.com). WebKit mishandles a\n // \\`modulepreload\\` that follows a cross-origin redirect: it forces the CORS\n // credentials flag true even for \\`crossorigin=\"anonymous\"\\`, then rejects the\n // redirect because the CDN sends no \\`Access-Control-Allow-Credentials\\`,\n // which blanks the studio. (This is why these hints were reverted; see\n // PR #1400.)\n //\n // \\`preconnect\\` only warms a socket; it never follows the redirect or fetches\n // the module, so it is safe in every engine and runs unconditionally.\n const firstCdnImport = Object.values(imports).find(isSanityCdnUrl);\n\n if (firstCdnImport) {\n const preconnectEl = document.createElement('link');\n preconnectEl.rel = 'preconnect';\n preconnectEl.href = new URL(firstCdnImport).origin;\n // Module fetches are CORS; without crossorigin the warmed socket is not reused.\n preconnectEl.crossOrigin = 'anonymous';\n document.head.appendChild(preconnectEl);\n }\n\n // \\`modulepreload\\` is the hint that follows the redirect and can blank the\n // studio in WebKit, so it is gated behind a positive allowlist: it runs only\n // for engines confirmed to handle the cross-origin redirect, i.e. Chromium or\n // Gecko. Every Chromium variant (Edge, Opera, Samsung Internet, ...) carries\n // the \\`Chrome\\` token, so matching \\`Chrom(e|ium)|Firefox\\` covers them without\n // enumerating brands. The iOS device-name exclusion is the hard guard for the\n // brick-prone platform: every iOS browser is WebKit regardless of brand, and\n // iOS in-app webviews carry messy UAs, so nothing on an iOS device is ever\n // treated as safe. Anything unrecognised falls through to no preload, costing\n // a missed download rather than bricking the studio.\n const userAgent = typeof navigator === 'undefined' ? '' : navigator.userAgent || '';\n const isKnownSafeEngine =\n !/\\\\b(iPad|iPhone|iPod)\\\\b/.test(userAgent) && /Chrom(e|ium)|Firefox/.test(userAgent);\n\n // The href reuses replaceTimestamp so it matches the importmap entry exactly;\n // a stale timestamp here would double-fetch the largest chunk.\n const sanityModuleUrl = imports['sanity'];\n if (isKnownSafeEngine && typeof sanityModuleUrl === 'string' && isSanityCdnUrl(sanityModuleUrl)) {\n const preloadEl = document.createElement('link');\n preloadEl.rel = 'modulepreload';\n preloadEl.href = replaceTimestamp(sanityModuleUrl);\n // Must match the preconnect's credentials mode, otherwise the browser\n // treats them as separate connections and the warmed cross-origin socket\n // is not reused for this fetch.\n preloadEl.crossOrigin = 'anonymous';\n document.head.appendChild(preloadEl);\n }\n</script>`\n\n/**\n * @internal\n */\nexport function addTimestampedImportMapScriptToHtml(\n html: string,\n importMap?: {imports?: Record<string, string>},\n autoUpdatesCssUrls?: string[],\n): string {\n if (!importMap) return html\n\n let root = parseHtml(html)\n let htmlEl = root.querySelector('html')\n if (!htmlEl) {\n const oldRoot = root\n root = parseHtml('<html></html>')\n htmlEl = root.querySelector('html')!\n htmlEl.append(oldRoot)\n }\n\n let headEl = htmlEl.querySelector('head')\n\n if (!headEl) {\n htmlEl.insertAdjacentHTML('afterbegin', '<head></head>')\n headEl = root.querySelector('head')!\n }\n\n // Include CSS URLs in the __imports JSON so the runtime script can create\n // <link> tags with fresh timestamps synchronously during head parsing.\n const importMapWithCss =\n autoUpdatesCssUrls && autoUpdatesCssUrls.length > 0\n ? {...importMap, css: autoUpdatesCssUrls}\n : importMap\n\n headEl.insertAdjacentHTML(\n 'beforeend',\n `<script type=\"application/json\" id=\"__imports\">${JSON.stringify(importMapWithCss)}</script>`,\n )\n\n headEl.insertAdjacentHTML('beforeend', TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT)\n return root.outerHTML\n}\n"],"names":["parse","parseHtml","TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT","addTimestampedImportMapScriptToHtml","html","importMap","autoUpdatesCssUrls","root","htmlEl","querySelector","oldRoot","append","headEl","insertAdjacentHTML","importMapWithCss","length","css","JSON","stringify","outerHTML"],"mappings":"AAAA,SAAQA,SAASC,SAAS,QAAO,mBAAkB;AAEnD;;;;;;;;;;;;;CAaC,GACD,MAAMC,wCAAwC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAgGtC,CAAC;AAEV;;CAEC,GACD,OAAO,SAASC,oCACdC,IAAY,EACZC,SAA8C,EAC9CC,kBAA6B;IAE7B,IAAI,CAACD,WAAW,OAAOD;IAEvB,IAAIG,OAAON,UAAUG;IACrB,IAAII,SAASD,KAAKE,aAAa,CAAC;IAChC,IAAI,CAACD,QAAQ;QACX,MAAME,UAAUH;QAChBA,OAAON,UAAU;QACjBO,SAASD,KAAKE,aAAa,CAAC;QAC5BD,OAAOG,MAAM,CAACD;IAChB;IAEA,IAAIE,SAASJ,OAAOC,aAAa,CAAC;IAElC,IAAI,CAACG,QAAQ;QACXJ,OAAOK,kBAAkB,CAAC,cAAc;QACxCD,SAASL,KAAKE,aAAa,CAAC;IAC9B;IAEA,0EAA0E;IAC1E,uEAAuE;IACvE,MAAMK,mBACJR,sBAAsBA,mBAAmBS,MAAM,GAAG,IAC9C;QAAC,GAAGV,SAAS;QAAEW,KAAKV;IAAkB,IACtCD;IAENO,OAAOC,kBAAkB,CACvB,aACA,CAAC,+CAA+C,EAAEI,KAAKC,SAAS,CAACJ,kBAAkB,SAAS,CAAC;IAG/FF,OAAOC,kBAAkB,CAAC,aAAaX;IACvC,OAAOK,KAAKY,SAAS;AACvB"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import { doImport } from '@sanity/cli-core';
|
|
2
|
+
import { doImport } from '@sanity/cli-core/util';
|
|
3
3
|
import { buildDebug } from '../buildDebug.js';
|
|
4
4
|
import { getPossibleDocumentComponentLocations } from '../getPossibleDocumentComponentLocations.js';
|
|
5
5
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/build/renderDocumentWorker/tryLoadDocumentComponent.ts"],"sourcesContent":["import fs from 'node:fs'\n\nimport {doImport} from '@sanity/cli-core'\n\nimport {buildDebug} from '../buildDebug.js'\nimport {getPossibleDocumentComponentLocations} from '../getPossibleDocumentComponentLocations.js'\n\n/**\n * @internal\n */\nexport async function tryLoadDocumentComponent(studioRootPath: string) {\n const locations = getPossibleDocumentComponentLocations(studioRootPath)\n\n for (const componentPath of locations) {\n buildDebug('Trying to load document component from %s', componentPath)\n try {\n const component = await doImport(componentPath)\n\n return {\n component,\n modified: Math.floor(fs.statSync(componentPath)?.mtimeMs),\n path: componentPath,\n }\n } catch (err) {\n // Allow \"not found\" errors\n if (err.code !== 'ERR_MODULE_NOT_FOUND') {\n buildDebug('Failed to load document component: %s', err.message)\n throw err\n }\n\n buildDebug('Document component not found at %s', componentPath)\n }\n }\n\n return null\n}\n"],"names":["fs","doImport","buildDebug","getPossibleDocumentComponentLocations","tryLoadDocumentComponent","studioRootPath","locations","componentPath","component","modified","Math","floor","statSync","mtimeMs","path","err","code","message"],"mappings":"AAAA,OAAOA,QAAQ,UAAS;AAExB,SAAQC,QAAQ,QAAO,
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/build/renderDocumentWorker/tryLoadDocumentComponent.ts"],"sourcesContent":["import fs from 'node:fs'\n\nimport {doImport} from '@sanity/cli-core/util'\n\nimport {buildDebug} from '../buildDebug.js'\nimport {getPossibleDocumentComponentLocations} from '../getPossibleDocumentComponentLocations.js'\n\n/**\n * @internal\n */\nexport async function tryLoadDocumentComponent(studioRootPath: string) {\n const locations = getPossibleDocumentComponentLocations(studioRootPath)\n\n for (const componentPath of locations) {\n buildDebug('Trying to load document component from %s', componentPath)\n try {\n const component = await doImport(componentPath)\n\n return {\n component,\n modified: Math.floor(fs.statSync(componentPath)?.mtimeMs),\n path: componentPath,\n }\n } catch (err) {\n // Allow \"not found\" errors\n if (err.code !== 'ERR_MODULE_NOT_FOUND') {\n buildDebug('Failed to load document component: %s', err.message)\n throw err\n }\n\n buildDebug('Document component not found at %s', componentPath)\n }\n }\n\n return null\n}\n"],"names":["fs","doImport","buildDebug","getPossibleDocumentComponentLocations","tryLoadDocumentComponent","studioRootPath","locations","componentPath","component","modified","Math","floor","statSync","mtimeMs","path","err","code","message"],"mappings":"AAAA,OAAOA,QAAQ,UAAS;AAExB,SAAQC,QAAQ,QAAO,wBAAuB;AAE9C,SAAQC,UAAU,QAAO,mBAAkB;AAC3C,SAAQC,qCAAqC,QAAO,8CAA6C;AAEjG;;CAEC,GACD,OAAO,eAAeC,yBAAyBC,cAAsB;IACnE,MAAMC,YAAYH,sCAAsCE;IAExD,KAAK,MAAME,iBAAiBD,UAAW;QACrCJ,WAAW,6CAA6CK;QACxD,IAAI;YACF,MAAMC,YAAY,MAAMP,SAASM;YAEjC,OAAO;gBACLC;gBACAC,UAAUC,KAAKC,KAAK,CAACX,GAAGY,QAAQ,CAACL,gBAAgBM;gBACjDC,MAAMP;YACR;QACF,EAAE,OAAOQ,KAAK;YACZ,2BAA2B;YAC3B,IAAIA,IAAIC,IAAI,KAAK,wBAAwB;gBACvCd,WAAW,yCAAyCa,IAAIE,OAAO;gBAC/D,MAAMF;YACR;YAEAb,WAAW,sCAAsCK;QACnD;IACF;IAEA,OAAO;AACT"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { getLocalPackageDir, getLocalPackageVersion } from '@sanity/cli-core';
|
|
3
|
+
import { getLocalPackageDir, getLocalPackageVersion } from '@sanity/cli-core/package-manager';
|
|
4
4
|
import { gt, minVersion, rcompare, satisfies } from 'semver';
|
|
5
5
|
import { getCjsNamedExports } from './getCjsNamedExports.js';
|
|
6
6
|
// Define the vendor packages and their corresponding versions and entry points
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/build/resolveVendorBuildConfig.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {getLocalPackageDir, getLocalPackageVersion} from '@sanity/cli-core'\nimport {gt, minVersion, rcompare, satisfies} from 'semver'\n\nimport {getCjsNamedExports} from './getCjsNamedExports.js'\n\n/**\n * A type representing the imports of vendor packages, defining specific entry\n * points for various versions and subpaths of the packages.\n */\ntype VendorImports = {\n [packageName: string]: {\n [versionRange: string]: {\n [subpath: string]: string\n }\n }\n}\n\n// Define the vendor packages and their corresponding versions and entry points\nconst VENDOR_IMPORTS: VendorImports = {\n react: {\n '^19.2.0': {\n '.': './cjs/react.production.js',\n './compiler-runtime': './cjs/react-compiler-runtime.production.js',\n './jsx-dev-runtime': './cjs/react-jsx-dev-runtime.production.js',\n './jsx-runtime': './cjs/react-jsx-runtime.production.js',\n './package.json': './package.json',\n },\n },\n 'react-dom': {\n '^19.2.0': {\n '.': './cjs/react-dom.production.js',\n './client': './cjs/react-dom-client.production.js',\n './package.json': './package.json',\n './server': './cjs/react-dom-server-legacy.browser.production.js',\n './server.browser': './cjs/react-dom-server-legacy.browser.production.js',\n './static': './cjs/react-dom-server.browser.production.js',\n './static.browser': './cjs/react-dom-server.browser.production.js',\n },\n },\n}\n\nconst STYLED_COMPONENTS_IMPORTS = {\n 'styled-components': {\n '^6.1.0': {\n '.': './dist/styled-components.browser.esm.js',\n './package.json': './package.json',\n },\n },\n}\n\nexport interface VendorBuildConfig {\n /** Rolldown entry name -\\> absolute path to the package entry file. */\n entries: Record<string, string>\n /** Named exports each CommonJS entry must re-expose as ESM, keyed by chunk name. */\n namesByChunkName: Record<string, readonly string[]>\n /** Rolldown entry chunk name -\\> bare import specifier (e.g. `react`, `react-dom/client`). */\n specifiersByChunkName: Record<string, string>\n}\n\ninterface ResolveVendorBuildConfigOptions {\n cwd: string\n isApp: boolean\n}\n\n/**\n * Resolves vendor package entry points and metadata for a combined studio/app build.\n * Does not run a build — callers add `entries` to the main Vite/Rolldown input and\n * derive the import map from emitted vendor chunks after the single `vite.build`.\n *\n * @internal\n */\nexport async function resolveVendorBuildConfig({\n cwd,\n isApp,\n}: ResolveVendorBuildConfigOptions): Promise<VendorBuildConfig> {\n const entries: Record<string, string> = {}\n const namesByChunkName: Record<string, readonly string[]> = {}\n const specifiersByChunkName: Record<string, string> = {}\n\n const vendorImports = isApp ? VENDOR_IMPORTS : {...VENDOR_IMPORTS, ...STYLED_COMPONENTS_IMPORTS}\n\n for (const [packageName, ranges] of Object.entries(vendorImports)) {\n const version = await getLocalPackageVersion(packageName, cwd)\n if (!version) {\n throw new Error(`Could not get version for '${packageName}'`)\n }\n\n const sortedRanges = Object.keys(ranges).toSorted((range1, range2) => {\n const min1 = minVersion(range1)\n const min2 = minVersion(range2)\n\n if (!min1) throw new Error(`Could not parse range '${range1}'`)\n if (!min2) throw new Error(`Could not parse range '${range2}'`)\n\n return rcompare(min1.version, min2.version)\n })\n\n const matchedRange = sortedRanges.find((range) => satisfies(version, range))\n\n if (!matchedRange) {\n const min = minVersion(sortedRanges.at(-1)!)\n if (!min) {\n throw new Error(`Could not find a minimum version for package '${packageName}'`)\n }\n\n if (gt(min.version, version)) {\n throw new Error(`Package '${packageName}' requires at least ${min.version}.`)\n }\n\n throw new Error(`Version '${version}' of package '${packageName}' is not supported yet.`)\n }\n\n const subpaths = ranges[matchedRange]\n const packageDir = getLocalPackageDir(packageName, cwd)\n\n for (const [subpath, relativeEntryPoint] of Object.entries(subpaths)) {\n const specifier = path.posix.join(packageName, subpath)\n const chunkName = path.posix.join(\n packageName,\n path.relative(packageName, specifier) || 'index',\n )\n\n const entryPath = path.join(packageDir, relativeEntryPoint)\n entries[chunkName] = entryPath\n specifiersByChunkName[chunkName] = specifier\n\n if (packageName in VENDOR_IMPORTS && subpath !== './package.json') {\n const source = await readFile(entryPath, 'utf8')\n namesByChunkName[chunkName] = await getCjsNamedExports(source, chunkName)\n }\n }\n }\n\n return {entries, namesByChunkName, specifiersByChunkName}\n}\n"],"names":["readFile","path","getLocalPackageDir","getLocalPackageVersion","gt","minVersion","rcompare","satisfies","getCjsNamedExports","VENDOR_IMPORTS","react","STYLED_COMPONENTS_IMPORTS","resolveVendorBuildConfig","cwd","isApp","entries","namesByChunkName","specifiersByChunkName","vendorImports","packageName","ranges","Object","version","Error","sortedRanges","keys","toSorted","range1","range2","min1","min2","matchedRange","find","range","min","at","subpaths","packageDir","subpath","relativeEntryPoint","specifier","posix","join","chunkName","relative","entryPath","source"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AACzC,OAAOC,UAAU,YAAW;AAE5B,SAAQC,kBAAkB,EAAEC,sBAAsB,QAAO,
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/build/resolveVendorBuildConfig.ts"],"sourcesContent":["import {readFile} from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {getLocalPackageDir, getLocalPackageVersion} from '@sanity/cli-core/package-manager'\nimport {gt, minVersion, rcompare, satisfies} from 'semver'\n\nimport {getCjsNamedExports} from './getCjsNamedExports.js'\n\n/**\n * A type representing the imports of vendor packages, defining specific entry\n * points for various versions and subpaths of the packages.\n */\ntype VendorImports = {\n [packageName: string]: {\n [versionRange: string]: {\n [subpath: string]: string\n }\n }\n}\n\n// Define the vendor packages and their corresponding versions and entry points\nconst VENDOR_IMPORTS: VendorImports = {\n react: {\n '^19.2.0': {\n '.': './cjs/react.production.js',\n './compiler-runtime': './cjs/react-compiler-runtime.production.js',\n './jsx-dev-runtime': './cjs/react-jsx-dev-runtime.production.js',\n './jsx-runtime': './cjs/react-jsx-runtime.production.js',\n './package.json': './package.json',\n },\n },\n 'react-dom': {\n '^19.2.0': {\n '.': './cjs/react-dom.production.js',\n './client': './cjs/react-dom-client.production.js',\n './package.json': './package.json',\n './server': './cjs/react-dom-server-legacy.browser.production.js',\n './server.browser': './cjs/react-dom-server-legacy.browser.production.js',\n './static': './cjs/react-dom-server.browser.production.js',\n './static.browser': './cjs/react-dom-server.browser.production.js',\n },\n },\n}\n\nconst STYLED_COMPONENTS_IMPORTS = {\n 'styled-components': {\n '^6.1.0': {\n '.': './dist/styled-components.browser.esm.js',\n './package.json': './package.json',\n },\n },\n}\n\nexport interface VendorBuildConfig {\n /** Rolldown entry name -\\> absolute path to the package entry file. */\n entries: Record<string, string>\n /** Named exports each CommonJS entry must re-expose as ESM, keyed by chunk name. */\n namesByChunkName: Record<string, readonly string[]>\n /** Rolldown entry chunk name -\\> bare import specifier (e.g. `react`, `react-dom/client`). */\n specifiersByChunkName: Record<string, string>\n}\n\ninterface ResolveVendorBuildConfigOptions {\n cwd: string\n isApp: boolean\n}\n\n/**\n * Resolves vendor package entry points and metadata for a combined studio/app build.\n * Does not run a build — callers add `entries` to the main Vite/Rolldown input and\n * derive the import map from emitted vendor chunks after the single `vite.build`.\n *\n * @internal\n */\nexport async function resolveVendorBuildConfig({\n cwd,\n isApp,\n}: ResolveVendorBuildConfigOptions): Promise<VendorBuildConfig> {\n const entries: Record<string, string> = {}\n const namesByChunkName: Record<string, readonly string[]> = {}\n const specifiersByChunkName: Record<string, string> = {}\n\n const vendorImports = isApp ? VENDOR_IMPORTS : {...VENDOR_IMPORTS, ...STYLED_COMPONENTS_IMPORTS}\n\n for (const [packageName, ranges] of Object.entries(vendorImports)) {\n const version = await getLocalPackageVersion(packageName, cwd)\n if (!version) {\n throw new Error(`Could not get version for '${packageName}'`)\n }\n\n const sortedRanges = Object.keys(ranges).toSorted((range1, range2) => {\n const min1 = minVersion(range1)\n const min2 = minVersion(range2)\n\n if (!min1) throw new Error(`Could not parse range '${range1}'`)\n if (!min2) throw new Error(`Could not parse range '${range2}'`)\n\n return rcompare(min1.version, min2.version)\n })\n\n const matchedRange = sortedRanges.find((range) => satisfies(version, range))\n\n if (!matchedRange) {\n const min = minVersion(sortedRanges.at(-1)!)\n if (!min) {\n throw new Error(`Could not find a minimum version for package '${packageName}'`)\n }\n\n if (gt(min.version, version)) {\n throw new Error(`Package '${packageName}' requires at least ${min.version}.`)\n }\n\n throw new Error(`Version '${version}' of package '${packageName}' is not supported yet.`)\n }\n\n const subpaths = ranges[matchedRange]\n const packageDir = getLocalPackageDir(packageName, cwd)\n\n for (const [subpath, relativeEntryPoint] of Object.entries(subpaths)) {\n const specifier = path.posix.join(packageName, subpath)\n const chunkName = path.posix.join(\n packageName,\n path.relative(packageName, specifier) || 'index',\n )\n\n const entryPath = path.join(packageDir, relativeEntryPoint)\n entries[chunkName] = entryPath\n specifiersByChunkName[chunkName] = specifier\n\n if (packageName in VENDOR_IMPORTS && subpath !== './package.json') {\n const source = await readFile(entryPath, 'utf8')\n namesByChunkName[chunkName] = await getCjsNamedExports(source, chunkName)\n }\n }\n }\n\n return {entries, namesByChunkName, specifiersByChunkName}\n}\n"],"names":["readFile","path","getLocalPackageDir","getLocalPackageVersion","gt","minVersion","rcompare","satisfies","getCjsNamedExports","VENDOR_IMPORTS","react","STYLED_COMPONENTS_IMPORTS","resolveVendorBuildConfig","cwd","isApp","entries","namesByChunkName","specifiersByChunkName","vendorImports","packageName","ranges","Object","version","Error","sortedRanges","keys","toSorted","range1","range2","min1","min2","matchedRange","find","range","min","at","subpaths","packageDir","subpath","relativeEntryPoint","specifier","posix","join","chunkName","relative","entryPath","source"],"mappings":"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"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import {
|
|
3
|
+
import { up as packageUp } from 'empathic/package';
|
|
4
4
|
import { copyDir } from '../../util/copyDir.js';
|
|
5
5
|
import { writeWebManifest } from './writeWebManifest.js';
|
|
6
6
|
/**
|
|
7
7
|
* @internal
|
|
8
8
|
*/ export async function getDefaultFaviconsPath() {
|
|
9
|
-
const sanityCliPkgPath = (
|
|
9
|
+
const sanityCliPkgPath = packageUp({
|
|
10
10
|
cwd: import.meta.dirname
|
|
11
|
-
})
|
|
11
|
+
});
|
|
12
12
|
if (!sanityCliPkgPath) {
|
|
13
13
|
throw new Error('Unable to resolve `@sanity/cli-build` module root');
|
|
14
14
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/build/writeFavicons.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/build/writeFavicons.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {up as packageUp} from 'empathic/package'\n\nimport {copyDir} from '../../util/copyDir.js'\nimport {writeWebManifest} from './writeWebManifest.js'\n\n/**\n * @internal\n */\nexport async function getDefaultFaviconsPath(): Promise<string> {\n const sanityCliPkgPath = packageUp({cwd: import.meta.dirname})\n if (!sanityCliPkgPath) {\n throw new Error('Unable to resolve `@sanity/cli-build` module root')\n }\n\n return path.join(path.dirname(sanityCliPkgPath), 'static', 'favicons')\n}\n\n/**\n * @internal\n */\nexport async function writeFavicons(basePath: string, destDir: string): Promise<void> {\n const faviconsPath = await getDefaultFaviconsPath()\n\n await fs.mkdir(destDir, {recursive: true})\n await copyDir(faviconsPath, destDir, true)\n await writeWebManifest(basePath, destDir)\n\n // Copy the /static/favicon.ico to /favicon.ico as well, because some tools/browsers\n // blindly expects it to be there before requesting the HTML containing the actual path\n await fs.copyFile(path.join(destDir, 'favicon.ico'), path.join(destDir, '..', 'favicon.ico'))\n}\n"],"names":["fs","path","up","packageUp","copyDir","writeWebManifest","getDefaultFaviconsPath","sanityCliPkgPath","cwd","dirname","Error","join","writeFavicons","basePath","destDir","faviconsPath","mkdir","recursive","copyFile"],"mappings":"AAAA,OAAOA,QAAQ,mBAAkB;AACjC,OAAOC,UAAU,YAAW;AAE5B,SAAQC,MAAMC,SAAS,QAAO,mBAAkB;AAEhD,SAAQC,OAAO,QAAO,wBAAuB;AAC7C,SAAQC,gBAAgB,QAAO,wBAAuB;AAEtD;;CAEC,GACD,OAAO,eAAeC;IACpB,MAAMC,mBAAmBJ,UAAU;QAACK,KAAK,YAAYC,OAAO;IAAA;IAC5D,IAAI,CAACF,kBAAkB;QACrB,MAAM,IAAIG,MAAM;IAClB;IAEA,OAAOT,KAAKU,IAAI,CAACV,KAAKQ,OAAO,CAACF,mBAAmB,UAAU;AAC7D;AAEA;;CAEC,GACD,OAAO,eAAeK,cAAcC,QAAgB,EAAEC,OAAe;IACnE,MAAMC,eAAe,MAAMT;IAE3B,MAAMN,GAAGgB,KAAK,CAACF,SAAS;QAACG,WAAW;IAAI;IACxC,MAAMb,QAAQW,cAAcD,SAAS;IACrC,MAAMT,iBAAiBQ,UAAUC;IAEjC,oFAAoF;IACpF,uFAAuF;IACvF,MAAMd,GAAGkB,QAAQ,CAACjB,KAAKU,IAAI,CAACG,SAAS,gBAAgBb,KAAKU,IAAI,CAACG,SAAS,MAAM;AAChF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { tryFindStudioConfigPath } from '@sanity/cli-core';
|
|
3
|
+
import { tryFindStudioConfigPath } from '@sanity/cli-core/config';
|
|
4
4
|
import { watch as chokidarWatch } from 'chokidar';
|
|
5
5
|
import { buildDebug } from './buildDebug.js';
|
|
6
6
|
import { decorateIndexWithAutoGeneratedWarning } from './decorateIndexWithAutoGeneratedWarning.js';
|
|
@@ -20,7 +20,7 @@ import { renderDocument } from './renderDocument.js';
|
|
|
20
20
|
const { appTitle, basePath, cwd, entry, isApp, isWorkbenchApp, reactStrictMode, watch } = options;
|
|
21
21
|
const runtimeDir = path.join(cwd, '.sanity', 'runtime');
|
|
22
22
|
buildDebug('Making runtime directory');
|
|
23
|
-
await
|
|
23
|
+
await mkdir(runtimeDir, {
|
|
24
24
|
recursive: true
|
|
25
25
|
});
|
|
26
26
|
async function renderAndWriteDocument() {
|
|
@@ -35,7 +35,7 @@ import { renderDocument } from './renderDocument.js';
|
|
|
35
35
|
studioRootPath: cwd
|
|
36
36
|
}))));
|
|
37
37
|
buildDebug('Writing index.html to runtime directory');
|
|
38
|
-
await
|
|
38
|
+
await writeFile(path.join(runtimeDir, 'index.html'), indexHtml);
|
|
39
39
|
}
|
|
40
40
|
let watcher;
|
|
41
41
|
if (watch) {
|
|
@@ -60,7 +60,7 @@ import { renderDocument } from './renderDocument.js';
|
|
|
60
60
|
reactStrictMode,
|
|
61
61
|
relativeConfigLocation
|
|
62
62
|
});
|
|
63
|
-
await
|
|
63
|
+
await writeFile(path.join(runtimeDir, 'app.js'), appJsContent);
|
|
64
64
|
return {
|
|
65
65
|
entries: {
|
|
66
66
|
relativeConfigLocation,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/build/writeSanityRuntime.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/build/writeSanityRuntime.ts"],"sourcesContent":["import {mkdir, writeFile} from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {tryFindStudioConfigPath} from '@sanity/cli-core/config'\nimport {watch as chokidarWatch, type FSWatcher} from 'chokidar'\n\nimport {buildDebug} from './buildDebug.js'\nimport {decorateIndexWithAutoGeneratedWarning} from './decorateIndexWithAutoGeneratedWarning.js'\nimport {decorateIndexWithBridgeScript} from './decorateIndexWithBridgeScript.js'\nimport {decorateIndexWithStagingScript} from './decorateIndexWithStagingScript.js'\nimport {getEntryModule} from './getEntryModule.js'\nimport {getPossibleDocumentComponentLocations} from './getPossibleDocumentComponentLocations.js'\nimport {renderDocument} from './renderDocument.js'\n\ninterface RuntimeOptions {\n cwd: string\n reactStrictMode: boolean | undefined\n watch: boolean\n\n appTitle?: string\n basePath?: string\n entry?: string\n isApp?: boolean\n isWorkbenchApp?: boolean\n}\n\n/**\n * Generates the `.sanity/runtime` directory, and optionally watches for custom\n * document files, rebuilding when they change\n *\n * @param options - Current working directory (Sanity root dir), and whether or not to watch\n * @returns A watcher instance if watch is enabled, undefined otherwise\n * @internal\n */\nexport async function writeSanityRuntime(options: RuntimeOptions): Promise<{\n entries: {relativeConfigLocation: string | null; relativeEntry: string | null}\n watcher: FSWatcher | undefined\n}> {\n const {appTitle, basePath, cwd, entry, isApp, isWorkbenchApp, reactStrictMode, watch} = options\n const runtimeDir = path.join(cwd, '.sanity', 'runtime')\n\n buildDebug('Making runtime directory')\n await mkdir(runtimeDir, {recursive: true})\n\n async function renderAndWriteDocument() {\n buildDebug('Rendering document template')\n const indexHtml = decorateIndexWithStagingScript(\n decorateIndexWithBridgeScript(\n decorateIndexWithAutoGeneratedWarning(\n await renderDocument({\n isApp,\n props: {\n basePath: basePath || '/',\n entryPath: `/${toForwardSlashes(path.relative(cwd, path.join(runtimeDir, 'app.js')))}`,\n title: appTitle,\n },\n studioRootPath: cwd,\n }),\n ),\n ),\n )\n\n buildDebug('Writing index.html to runtime directory')\n await writeFile(path.join(runtimeDir, 'index.html'), indexHtml)\n }\n\n let watcher: FSWatcher | undefined\n\n if (watch) {\n // Skip the initial scan; the explicit renderAndWriteDocument() below handles first generation.\n watcher = chokidarWatch(getPossibleDocumentComponentLocations(cwd), {ignoreInitial: true}).on(\n 'all',\n () => renderAndWriteDocument(),\n )\n }\n\n await renderAndWriteDocument()\n\n buildDebug('Writing app.js to runtime directory')\n const {relativeConfigLocation, relativeEntry} = await resolveEntries({\n cwd,\n entry,\n isApp,\n isWorkbenchApp,\n runtimeDir,\n })\n const appJsContent = getEntryModule({\n basePath,\n entry: relativeEntry ?? undefined,\n isApp,\n reactStrictMode,\n relativeConfigLocation,\n })\n await writeFile(path.join(runtimeDir, 'app.js'), appJsContent)\n\n return {\n entries: {\n relativeConfigLocation,\n relativeEntry,\n },\n watcher,\n }\n}\n\n/**\n * Resolves the relative entry paths for a Sanity project without writing any\n * runtime files to disk. Used by federation builds that skip the full runtime\n * generation but still need entry metadata for the vite plugin.\n */\nexport async function resolveEntries(options: {\n cwd: string\n entry?: string\n isApp?: boolean\n isWorkbenchApp?: boolean\n runtimeDir?: string\n}): Promise<{relativeConfigLocation: string | null; relativeEntry: string | null}> {\n const {cwd, entry, isApp, isWorkbenchApp} = options\n const runtimeDir = options.runtimeDir ?? path.join(cwd, '.sanity', 'runtime')\n\n let relativeConfigLocation: string | null = null\n if (!isApp) {\n const studioConfigPath = await tryFindStudioConfigPath(cwd)\n relativeConfigLocation = studioConfigPath\n ? toForwardSlashes(path.relative(runtimeDir, studioConfigPath))\n : null\n }\n\n // Only a *branded* app (`unstable_defineApp`) that declares no `entry` has no\n // navigable app view (sanity-io/workbench spec 002-workbench-extension-api,\n // US5): `null` entry tells the runtime/federation to skip the `./App` render\n // path. Non-branded (legacy SDK) apps keep the historical `./src/App` default,\n // and studios ignore `relativeEntry` — so gating on `isApp` here would regress\n // non-opted-in apps to the dock-only stub.\n const relativeEntry =\n isWorkbenchApp && !entry\n ? null\n : toForwardSlashes(path.relative(runtimeDir, path.resolve(cwd, entry || './src/App')))\n\n return {relativeConfigLocation, relativeEntry}\n}\n\nfunction toForwardSlashes(filePath: string): string {\n return filePath.replaceAll('\\\\', '/')\n}\n"],"names":["mkdir","writeFile","path","tryFindStudioConfigPath","watch","chokidarWatch","buildDebug","decorateIndexWithAutoGeneratedWarning","decorateIndexWithBridgeScript","decorateIndexWithStagingScript","getEntryModule","getPossibleDocumentComponentLocations","renderDocument","writeSanityRuntime","options","appTitle","basePath","cwd","entry","isApp","isWorkbenchApp","reactStrictMode","runtimeDir","join","recursive","renderAndWriteDocument","indexHtml","props","entryPath","toForwardSlashes","relative","title","studioRootPath","watcher","ignoreInitial","on","relativeConfigLocation","relativeEntry","resolveEntries","appJsContent","undefined","entries","studioConfigPath","resolve","filePath","replaceAll"],"mappings":"AAAA,SAAQA,KAAK,EAAEC,SAAS,QAAO,mBAAkB;AACjD,OAAOC,UAAU,YAAW;AAE5B,SAAQC,uBAAuB,QAAO,0BAAyB;AAC/D,SAAQC,SAASC,aAAa,QAAuB,WAAU;AAE/D,SAAQC,UAAU,QAAO,kBAAiB;AAC1C,SAAQC,qCAAqC,QAAO,6CAA4C;AAChG,SAAQC,6BAA6B,QAAO,qCAAoC;AAChF,SAAQC,8BAA8B,QAAO,sCAAqC;AAClF,SAAQC,cAAc,QAAO,sBAAqB;AAClD,SAAQC,qCAAqC,QAAO,6CAA4C;AAChG,SAAQC,cAAc,QAAO,sBAAqB;AAclD;;;;;;;CAOC,GACD,OAAO,eAAeC,mBAAmBC,OAAuB;IAI9D,MAAM,EAACC,QAAQ,EAAEC,QAAQ,EAAEC,GAAG,EAAEC,KAAK,EAAEC,KAAK,EAAEC,cAAc,EAAEC,eAAe,EAAEjB,KAAK,EAAC,GAAGU;IACxF,MAAMQ,aAAapB,KAAKqB,IAAI,CAACN,KAAK,WAAW;IAE7CX,WAAW;IACX,MAAMN,MAAMsB,YAAY;QAACE,WAAW;IAAI;IAExC,eAAeC;QACbnB,WAAW;QACX,MAAMoB,YAAYjB,+BAChBD,8BACED,sCACE,MAAMK,eAAe;YACnBO;YACAQ,OAAO;gBACLX,UAAUA,YAAY;gBACtBY,WAAW,CAAC,CAAC,EAAEC,iBAAiB3B,KAAK4B,QAAQ,CAACb,KAAKf,KAAKqB,IAAI,CAACD,YAAY,aAAa;gBACtFS,OAAOhB;YACT;YACAiB,gBAAgBf;QAClB;QAKNX,WAAW;QACX,MAAML,UAAUC,KAAKqB,IAAI,CAACD,YAAY,eAAeI;IACvD;IAEA,IAAIO;IAEJ,IAAI7B,OAAO;QACT,+FAA+F;QAC/F6B,UAAU5B,cAAcM,sCAAsCM,MAAM;YAACiB,eAAe;QAAI,GAAGC,EAAE,CAC3F,OACA,IAAMV;IAEV;IAEA,MAAMA;IAENnB,WAAW;IACX,MAAM,EAAC8B,sBAAsB,EAAEC,aAAa,EAAC,GAAG,MAAMC,eAAe;QACnErB;QACAC;QACAC;QACAC;QACAE;IACF;IACA,MAAMiB,eAAe7B,eAAe;QAClCM;QACAE,OAAOmB,iBAAiBG;QACxBrB;QACAE;QACAe;IACF;IACA,MAAMnC,UAAUC,KAAKqB,IAAI,CAACD,YAAY,WAAWiB;IAEjD,OAAO;QACLE,SAAS;YACPL;YACAC;QACF;QACAJ;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,eAAeK,eAAexB,OAMpC;IACC,MAAM,EAACG,GAAG,EAAEC,KAAK,EAAEC,KAAK,EAAEC,cAAc,EAAC,GAAGN;IAC5C,MAAMQ,aAAaR,QAAQQ,UAAU,IAAIpB,KAAKqB,IAAI,CAACN,KAAK,WAAW;IAEnE,IAAImB,yBAAwC;IAC5C,IAAI,CAACjB,OAAO;QACV,MAAMuB,mBAAmB,MAAMvC,wBAAwBc;QACvDmB,yBAAyBM,mBACrBb,iBAAiB3B,KAAK4B,QAAQ,CAACR,YAAYoB,qBAC3C;IACN;IAEA,8EAA8E;IAC9E,4EAA4E;IAC5E,6EAA6E;IAC7E,+EAA+E;IAC/E,+EAA+E;IAC/E,2CAA2C;IAC3C,MAAML,gBACJjB,kBAAkB,CAACF,QACf,OACAW,iBAAiB3B,KAAK4B,QAAQ,CAACR,YAAYpB,KAAKyC,OAAO,CAAC1B,KAAKC,SAAS;IAE5E,OAAO;QAACkB;QAAwBC;IAAa;AAC/C;AAEA,SAASR,iBAAiBe,QAAgB;IACxC,OAAOA,SAASC,UAAU,CAAC,MAAM;AACnC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isMainThread, parentPort, workerData } from 'node:worker_threads';
|
|
2
|
-
import { getStudioWorkspaces, getWorkspace } from '@sanity/cli-core';
|
|
2
|
+
import { getStudioWorkspaces, getWorkspace } from '@sanity/cli-core/config';
|
|
3
3
|
import { extractSchema } from '@sanity/schema/_internal';
|
|
4
4
|
import { extractSchemaWorkerData } from './types.js';
|
|
5
5
|
import { extractValidationFromSchemaError } from './utils/extractValidationFromSchemaError.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/schema/extractSanitySchema.worker.ts"],"sourcesContent":["import {isMainThread, parentPort, workerData} from 'node:worker_threads'\n\nimport {getStudioWorkspaces, getWorkspace} from '@sanity/cli-core'\nimport {extractSchema} from '@sanity/schema/_internal'\n\nimport {extractSchemaWorkerData} from './types.js'\nimport {extractValidationFromSchemaError} from './utils/extractValidationFromSchemaError.js'\n\nif (isMainThread || !parentPort) {\n throw new Error('Should only be run in a worker!')\n}\n\nconst {configPath, enforceRequiredFields, workDir, workspaceName} =\n extractSchemaWorkerData.parse(workerData)\n\ntry {\n const workspaces = await getStudioWorkspaces(configPath)\n if (workspaces.length === 0) {\n throw new Error('Failed to resolve configuration')\n }\n\n const workspace = getWorkspace(workspaces, workspaceName)\n const schema = extractSchema(workspace.schema, {\n enforceRequiredFields,\n })\n\n parentPort.postMessage({\n schema,\n type: 'success',\n })\n} catch (error) {\n const validation = await extractValidationFromSchemaError(error, workDir)\n parentPort.postMessage({\n error: error instanceof Error ? error.message : String(error),\n type: 'error',\n validation,\n })\n}\n"],"names":["isMainThread","parentPort","workerData","getStudioWorkspaces","getWorkspace","extractSchema","extractSchemaWorkerData","extractValidationFromSchemaError","Error","configPath","enforceRequiredFields","workDir","workspaceName","parse","workspaces","length","workspace","schema","postMessage","type","error","validation","message","String"],"mappings":"AAAA,SAAQA,YAAY,EAAEC,UAAU,EAAEC,UAAU,QAAO,sBAAqB;AAExE,SAAQC,mBAAmB,EAAEC,YAAY,QAAO,
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/schema/extractSanitySchema.worker.ts"],"sourcesContent":["import {isMainThread, parentPort, workerData} from 'node:worker_threads'\n\nimport {getStudioWorkspaces, getWorkspace} from '@sanity/cli-core/config'\nimport {extractSchema} from '@sanity/schema/_internal'\n\nimport {extractSchemaWorkerData} from './types.js'\nimport {extractValidationFromSchemaError} from './utils/extractValidationFromSchemaError.js'\n\nif (isMainThread || !parentPort) {\n throw new Error('Should only be run in a worker!')\n}\n\nconst {configPath, enforceRequiredFields, workDir, workspaceName} =\n extractSchemaWorkerData.parse(workerData)\n\ntry {\n const workspaces = await getStudioWorkspaces(configPath)\n if (workspaces.length === 0) {\n throw new Error('Failed to resolve configuration')\n }\n\n const workspace = getWorkspace(workspaces, workspaceName)\n const schema = extractSchema(workspace.schema, {\n enforceRequiredFields,\n })\n\n parentPort.postMessage({\n schema,\n type: 'success',\n })\n} catch (error) {\n const validation = await extractValidationFromSchemaError(error, workDir)\n parentPort.postMessage({\n error: error instanceof Error ? error.message : String(error),\n type: 'error',\n validation,\n })\n}\n"],"names":["isMainThread","parentPort","workerData","getStudioWorkspaces","getWorkspace","extractSchema","extractSchemaWorkerData","extractValidationFromSchemaError","Error","configPath","enforceRequiredFields","workDir","workspaceName","parse","workspaces","length","workspace","schema","postMessage","type","error","validation","message","String"],"mappings":"AAAA,SAAQA,YAAY,EAAEC,UAAU,EAAEC,UAAU,QAAO,sBAAqB;AAExE,SAAQC,mBAAmB,EAAEC,YAAY,QAAO,0BAAyB;AACzE,SAAQC,aAAa,QAAO,2BAA0B;AAEtD,SAAQC,uBAAuB,QAAO,aAAY;AAClD,SAAQC,gCAAgC,QAAO,8CAA6C;AAE5F,IAAIP,gBAAgB,CAACC,YAAY;IAC/B,MAAM,IAAIO,MAAM;AAClB;AAEA,MAAM,EAACC,UAAU,EAAEC,qBAAqB,EAAEC,OAAO,EAAEC,aAAa,EAAC,GAC/DN,wBAAwBO,KAAK,CAACX;AAEhC,IAAI;IACF,MAAMY,aAAa,MAAMX,oBAAoBM;IAC7C,IAAIK,WAAWC,MAAM,KAAK,GAAG;QAC3B,MAAM,IAAIP,MAAM;IAClB;IAEA,MAAMQ,YAAYZ,aAAaU,YAAYF;IAC3C,MAAMK,SAASZ,cAAcW,UAAUC,MAAM,EAAE;QAC7CP;IACF;IAEAT,WAAWiB,WAAW,CAAC;QACrBD;QACAE,MAAM;IACR;AACF,EAAE,OAAOC,OAAO;IACd,MAAMC,aAAa,MAAMd,iCAAiCa,OAAOT;IACjEV,WAAWiB,WAAW,CAAC;QACrBE,OAAOA,iBAAiBZ,QAAQY,MAAME,OAAO,GAAGC,OAAOH;QACvDD,MAAM;QACNE;IACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/schema/getExtractOptions.ts"],"sourcesContent":["import {existsSync, statSync} from 'node:fs'\nimport {extname, join, resolve} from 'node:path'\n\nimport {type ProjectRootResult} from '@sanity/cli-core'\n\nexport interface ExtractOptions {\n configPath: string\n enforceRequiredFields: boolean\n format: string\n outputPath: string\n watchPatterns: string[]\n workspace: string | undefined\n}\n\ninterface GetExtractOptions {\n enforceRequiredFields: boolean | undefined\n format: string | undefined\n path: string | undefined\n projectRoot: ProjectRootResult\n watchPatterns: string[] | undefined\n workspace: string | undefined\n}\n\nexport function getExtractOptions(options: GetExtractOptions): ExtractOptions {\n const {\n enforceRequiredFields,\n format,\n path: pathFlag,\n projectRoot,\n watchPatterns,\n workspace,\n } = options\n let outputPath: string\n if (pathFlag) {\n const resolved = resolve(join(projectRoot.directory, pathFlag))\n const isExistingDirectory = existsSync(resolved) && statSync(resolved).isDirectory()\n\n outputPath =\n isExistingDirectory || !extname(resolved) ? join(resolved, 'schema.json') : resolved\n } else {\n outputPath = resolve(join(projectRoot.directory, 'schema.json'))\n }\n\n return {\n configPath: projectRoot.path,\n enforceRequiredFields: enforceRequiredFields ?? false,\n format: format ?? 'groq-type-nodes',\n outputPath,\n watchPatterns: watchPatterns ?? [],\n workspace,\n }\n}\n"],"names":["existsSync","statSync","extname","join","resolve","getExtractOptions","options","enforceRequiredFields","format","path","pathFlag","projectRoot","watchPatterns","workspace","outputPath","resolved","directory","isExistingDirectory","isDirectory","configPath"],"mappings":"AAAA,SAAQA,UAAU,EAAEC,QAAQ,QAAO,UAAS;AAC5C,SAAQC,OAAO,EAAEC,IAAI,EAAEC,OAAO,QAAO,YAAW;AAsBhD,OAAO,SAASC,kBAAkBC,OAA0B;IAC1D,MAAM,EACJC,qBAAqB,EACrBC,MAAM,EACNC,MAAMC,QAAQ,EACdC,WAAW,EACXC,aAAa,EACbC,SAAS,EACV,GAAGP;IACJ,IAAIQ;IACJ,IAAIJ,UAAU;QACZ,MAAMK,WAAWX,QAAQD,KAAKQ,YAAYK,SAAS,EAAEN;QACrD,MAAMO,sBAAsBjB,WAAWe,aAAad,SAASc,UAAUG,WAAW;QAElFJ,aACEG,uBAAuB,CAACf,QAAQa,YAAYZ,KAAKY,UAAU,iBAAiBA;IAChF,OAAO;QACLD,aAAaV,QAAQD,KAAKQ,YAAYK,SAAS,EAAE;IACnD;IAEA,OAAO;QACLG,YAAYR,YAAYF,IAAI;QAC5BF,uBAAuBA,yBAAyB;QAChDC,QAAQA,UAAU;QAClBM;QACAF,eAAeA,iBAAiB,EAAE;QAClCC;IACF;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/schema/getExtractOptions.ts"],"sourcesContent":["import {existsSync, statSync} from 'node:fs'\nimport {extname, join, resolve} from 'node:path'\n\nimport {type ProjectRootResult} from '@sanity/cli-core/types'\n\nexport interface ExtractOptions {\n configPath: string\n enforceRequiredFields: boolean\n format: string\n outputPath: string\n watchPatterns: string[]\n workspace: string | undefined\n}\n\ninterface GetExtractOptions {\n enforceRequiredFields: boolean | undefined\n format: string | undefined\n path: string | undefined\n projectRoot: ProjectRootResult\n watchPatterns: string[] | undefined\n workspace: string | undefined\n}\n\nexport function getExtractOptions(options: GetExtractOptions): ExtractOptions {\n const {\n enforceRequiredFields,\n format,\n path: pathFlag,\n projectRoot,\n watchPatterns,\n workspace,\n } = options\n let outputPath: string\n if (pathFlag) {\n const resolved = resolve(join(projectRoot.directory, pathFlag))\n const isExistingDirectory = existsSync(resolved) && statSync(resolved).isDirectory()\n\n outputPath =\n isExistingDirectory || !extname(resolved) ? join(resolved, 'schema.json') : resolved\n } else {\n outputPath = resolve(join(projectRoot.directory, 'schema.json'))\n }\n\n return {\n configPath: projectRoot.path,\n enforceRequiredFields: enforceRequiredFields ?? false,\n format: format ?? 'groq-type-nodes',\n outputPath,\n watchPatterns: watchPatterns ?? [],\n workspace,\n }\n}\n"],"names":["existsSync","statSync","extname","join","resolve","getExtractOptions","options","enforceRequiredFields","format","path","pathFlag","projectRoot","watchPatterns","workspace","outputPath","resolved","directory","isExistingDirectory","isDirectory","configPath"],"mappings":"AAAA,SAAQA,UAAU,EAAEC,QAAQ,QAAO,UAAS;AAC5C,SAAQC,OAAO,EAAEC,IAAI,EAAEC,OAAO,QAAO,YAAW;AAsBhD,OAAO,SAASC,kBAAkBC,OAA0B;IAC1D,MAAM,EACJC,qBAAqB,EACrBC,MAAM,EACNC,MAAMC,QAAQ,EACdC,WAAW,EACXC,aAAa,EACbC,SAAS,EACV,GAAGP;IACJ,IAAIQ;IACJ,IAAIJ,UAAU;QACZ,MAAMK,WAAWX,QAAQD,KAAKQ,YAAYK,SAAS,EAAEN;QACrD,MAAMO,sBAAsBjB,WAAWe,aAAad,SAASc,UAAUG,WAAW;QAElFJ,aACEG,uBAAuB,CAACf,QAAQa,YAAYZ,KAAKY,UAAU,iBAAiBA;IAChF,OAAO;QACLD,aAAaV,QAAQD,KAAKQ,YAAYK,SAAS,EAAE;IACnD;IAEA,OAAO;QACLG,YAAYR,YAAYF,IAAI;QAC5BF,uBAAuBA,yBAAyB;QAChDC,QAAQA,UAAU;QAClBM;QACAF,eAAeA,iBAAiB,EAAE;QAClCC;IACF;AACF"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
2
2
|
import { dirname } from 'node:path';
|
|
3
|
-
import { studioWorkerTask } from '@sanity/cli-core';
|
|
3
|
+
import { studioWorkerTask } from '@sanity/cli-core/tasks';
|
|
4
4
|
import { SchemaExtractionError } from './utils/SchemaExtractionError.js';
|
|
5
5
|
/**
|
|
6
6
|
* Core schema extraction logic.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/actions/schema/runSchemaExtraction.ts"],"sourcesContent":["import {mkdir, writeFile} from 'node:fs/promises'\nimport {dirname} from 'node:path'\n\nimport {studioWorkerTask} from '@sanity/cli-core'\nimport {type extractSchema as extractSchemaInternal} from '@sanity/schema/_internal'\n\nimport {type ExtractOptions} from './getExtractOptions.js'\nimport {type ExtractSchemaWorkerData, type ExtractSchemaWorkerError} from './types.js'\nimport {SchemaExtractionError} from './utils/SchemaExtractionError.js'\n\ninterface ExtractSchemaWorkerResult {\n schema: ReturnType<typeof extractSchemaInternal>\n type: 'success'\n}\n\ntype ExtractSchemaWorkerMessage = ExtractSchemaWorkerError | ExtractSchemaWorkerResult\n\n/**\n * Core schema extraction logic.\n * Performs the extraction via worker and writes to file.\n * Throws SchemaExtractionError on failure.\n */\nexport async function runSchemaExtraction(\n extractOptions: Omit<ExtractOptions, 'watchPatterns'>,\n): Promise<ReturnType<typeof extractSchemaInternal>> {\n const {configPath, enforceRequiredFields, format, outputPath, workspace} = extractOptions\n\n if (format !== 'groq-type-nodes') {\n throw new Error(`Unsupported format: \"${format}\"`)\n }\n\n const workDir = dirname(configPath)\n const outputDir = dirname(outputPath)\n\n const result = await studioWorkerTask<ExtractSchemaWorkerMessage>(\n new URL('extractSanitySchema.worker.js', import.meta.url),\n {\n name: 'extractSanitySchema',\n studioRootPath: workDir,\n workerData: {\n configPath,\n enforceRequiredFields,\n workDir,\n workspaceName: workspace,\n } satisfies ExtractSchemaWorkerData,\n },\n )\n\n if (result.type === 'error') {\n throw new SchemaExtractionError(result.error, result.validation)\n }\n\n const schema = result.schema\n\n // Ensure output directory exists\n await mkdir(outputDir, {recursive: true})\n\n // Write schema to file\n await writeFile(outputPath, `${JSON.stringify(schema, null, 2)}\\n`)\n\n return schema\n}\n"],"names":["mkdir","writeFile","dirname","studioWorkerTask","SchemaExtractionError","runSchemaExtraction","extractOptions","configPath","enforceRequiredFields","format","outputPath","workspace","Error","workDir","outputDir","result","URL","url","name","studioRootPath","workerData","workspaceName","type","error","validation","schema","recursive","JSON","stringify"],"mappings":"AAAA,SAAQA,KAAK,EAAEC,SAAS,QAAO,mBAAkB;AACjD,SAAQC,OAAO,QAAO,YAAW;AAEjC,SAAQC,gBAAgB,QAAO,
|
|
1
|
+
{"version":3,"sources":["../../../src/actions/schema/runSchemaExtraction.ts"],"sourcesContent":["import {mkdir, writeFile} from 'node:fs/promises'\nimport {dirname} from 'node:path'\n\nimport {studioWorkerTask} from '@sanity/cli-core/tasks'\nimport {type extractSchema as extractSchemaInternal} from '@sanity/schema/_internal'\n\nimport {type ExtractOptions} from './getExtractOptions.js'\nimport {type ExtractSchemaWorkerData, type ExtractSchemaWorkerError} from './types.js'\nimport {SchemaExtractionError} from './utils/SchemaExtractionError.js'\n\ninterface ExtractSchemaWorkerResult {\n schema: ReturnType<typeof extractSchemaInternal>\n type: 'success'\n}\n\ntype ExtractSchemaWorkerMessage = ExtractSchemaWorkerError | ExtractSchemaWorkerResult\n\n/**\n * Core schema extraction logic.\n * Performs the extraction via worker and writes to file.\n * Throws SchemaExtractionError on failure.\n */\nexport async function runSchemaExtraction(\n extractOptions: Omit<ExtractOptions, 'watchPatterns'>,\n): Promise<ReturnType<typeof extractSchemaInternal>> {\n const {configPath, enforceRequiredFields, format, outputPath, workspace} = extractOptions\n\n if (format !== 'groq-type-nodes') {\n throw new Error(`Unsupported format: \"${format}\"`)\n }\n\n const workDir = dirname(configPath)\n const outputDir = dirname(outputPath)\n\n const result = await studioWorkerTask<ExtractSchemaWorkerMessage>(\n new URL('extractSanitySchema.worker.js', import.meta.url),\n {\n name: 'extractSanitySchema',\n studioRootPath: workDir,\n workerData: {\n configPath,\n enforceRequiredFields,\n workDir,\n workspaceName: workspace,\n } satisfies ExtractSchemaWorkerData,\n },\n )\n\n if (result.type === 'error') {\n throw new SchemaExtractionError(result.error, result.validation)\n }\n\n const schema = result.schema\n\n // Ensure output directory exists\n await mkdir(outputDir, {recursive: true})\n\n // Write schema to file\n await writeFile(outputPath, `${JSON.stringify(schema, null, 2)}\\n`)\n\n return schema\n}\n"],"names":["mkdir","writeFile","dirname","studioWorkerTask","SchemaExtractionError","runSchemaExtraction","extractOptions","configPath","enforceRequiredFields","format","outputPath","workspace","Error","workDir","outputDir","result","URL","url","name","studioRootPath","workerData","workspaceName","type","error","validation","schema","recursive","JSON","stringify"],"mappings":"AAAA,SAAQA,KAAK,EAAEC,SAAS,QAAO,mBAAkB;AACjD,SAAQC,OAAO,QAAO,YAAW;AAEjC,SAAQC,gBAAgB,QAAO,yBAAwB;AAKvD,SAAQC,qBAAqB,QAAO,mCAAkC;AAStE;;;;CAIC,GACD,OAAO,eAAeC,oBACpBC,cAAqD;IAErD,MAAM,EAACC,UAAU,EAAEC,qBAAqB,EAAEC,MAAM,EAAEC,UAAU,EAAEC,SAAS,EAAC,GAAGL;IAE3E,IAAIG,WAAW,mBAAmB;QAChC,MAAM,IAAIG,MAAM,CAAC,qBAAqB,EAAEH,OAAO,CAAC,CAAC;IACnD;IAEA,MAAMI,UAAUX,QAAQK;IACxB,MAAMO,YAAYZ,QAAQQ;IAE1B,MAAMK,SAAS,MAAMZ,iBACnB,IAAIa,IAAI,iCAAiC,YAAYC,GAAG,GACxD;QACEC,MAAM;QACNC,gBAAgBN;QAChBO,YAAY;YACVb;YACAC;YACAK;YACAQ,eAAeV;QACjB;IACF;IAGF,IAAII,OAAOO,IAAI,KAAK,SAAS;QAC3B,MAAM,IAAIlB,sBAAsBW,OAAOQ,KAAK,EAAER,OAAOS,UAAU;IACjE;IAEA,MAAMC,SAASV,OAAOU,MAAM;IAE5B,iCAAiC;IACjC,MAAMzB,MAAMc,WAAW;QAACY,WAAW;IAAI;IAEvC,uBAAuB;IACvB,MAAMzB,UAAUS,YAAY,GAAGiB,KAAKC,SAAS,CAACH,QAAQ,MAAM,GAAG,EAAE,CAAC;IAElE,OAAOA;AACT"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveLocalPackage } from '@sanity/cli-core';
|
|
1
|
+
import { resolveLocalPackage } from '@sanity/cli-core/package-manager';
|
|
2
2
|
/**
|
|
3
3
|
* Extracts validation problem groups from a SchemaError.
|
|
4
4
|
*/ export async function extractValidationFromSchemaError(error, workDir) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/schema/utils/extractValidationFromSchemaError.ts"],"sourcesContent":["import {resolveLocalPackage} from '@sanity/cli-core'\nimport {type SchemaValidationProblemGroup} from '@sanity/types'\n\n/**\n * Extracts validation problem groups from a SchemaError.\n */\nexport async function extractValidationFromSchemaError(\n error: unknown,\n workDir: string,\n): Promise<SchemaValidationProblemGroup[] | undefined> {\n const {SchemaError} = await resolveLocalPackage<typeof import('sanity')>('sanity', workDir)\n\n if (error instanceof SchemaError) {\n return error.schema._validation\n }\n\n return undefined\n}\n"],"names":["resolveLocalPackage","extractValidationFromSchemaError","error","workDir","SchemaError","schema","_validation","undefined"],"mappings":"AAAA,SAAQA,mBAAmB,QAAO,
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/schema/utils/extractValidationFromSchemaError.ts"],"sourcesContent":["import {resolveLocalPackage} from '@sanity/cli-core/package-manager'\nimport {type SchemaValidationProblemGroup} from '@sanity/types'\n\n/**\n * Extracts validation problem groups from a SchemaError.\n */\nexport async function extractValidationFromSchemaError(\n error: unknown,\n workDir: string,\n): Promise<SchemaValidationProblemGroup[] | undefined> {\n const {SchemaError} = await resolveLocalPackage<typeof import('sanity')>('sanity', workDir)\n\n if (error instanceof SchemaError) {\n return error.schema._validation\n }\n\n return undefined\n}\n"],"names":["resolveLocalPackage","extractValidationFromSchemaError","error","workDir","SchemaError","schema","_validation","undefined"],"mappings":"AAAA,SAAQA,mBAAmB,QAAO,mCAAkC;AAGpE;;CAEC,GACD,OAAO,eAAeC,iCACpBC,KAAc,EACdC,OAAe;IAEf,MAAM,EAACC,WAAW,EAAC,GAAG,MAAMJ,oBAA6C,UAAUG;IAEnF,IAAID,iBAAiBE,aAAa;QAChC,OAAOF,MAAMG,MAAM,CAACC,WAAW;IACjC;IAEA,OAAOC;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/actions/schema/vite/plugin-schema-extraction.ts"],"sourcesContent":["import path, {isAbsolute} from 'node:path'\n\nimport {type CLITelemetryStore} from '@sanity/cli-core'\nimport {logSymbols} from '@sanity/cli-core/ux'\nimport debounce from 'lodash-es/debounce.js'\nimport mean from 'lodash-es/mean.js'\nimport once from 'lodash-es/once.js'\nimport {type Plugin} from 'vite'\n\nimport {\n SchemaExtractedTrace,\n SchemaExtractionWatchModeTrace,\n} from '../../../telemetry/extractSchema.telemetry.js'\nimport {formatSchemaValidation} from '../formatSchemaValidation.js'\nimport {createSchemaPatternMatcher} from '../matchSchemaPattern.js'\nimport {runSchemaExtraction} from '../runSchemaExtraction.js'\nimport {SchemaExtractionError} from '../utils/SchemaExtractionError.js'\n\n/**\n * Default glob patterns to watch for schema changes.\n * Covers the sanity config file and common schema directory naming conventions.\n */\nconst DEFAULT_SCHEMA_PATTERNS = [\n 'sanity.config.{js,jsx,ts,tsx,mjs}',\n 'schema*/**/*.{js,jsx,ts,tsx,mjs}',\n]\n\n/** Default debounce delay in milliseconds */\nconst DEFAULT_DEBOUNCE_MS = 1000\n\n/**\n * Delay before initial extraction to allow Vite to finish startup\n * and avoid race conditions with module resolution.\n */\nconst INITIAL_EXTRACTION_DELAY_MS = 1000\n\n/**\n * Options for the Sanity schema extraction Vite plugin.\n *\n * @public\n */\ninterface SchemaExtractionPluginOptions {\n /**\n * Name of sanity config file\n * @example sanity.config.ts\n */\n configPath: string\n\n /**\n * Additional glob patterns to watch for schema changes.\n * These are merged with the default patterns.\n * @example `['lib/custom-types/**\\/*.ts', 'shared/schemas/**\\/*.ts']`\n */\n additionalPatterns?: string[]\n\n /**\n * Debounce delay in milliseconds before triggering extraction\n * after a file change. Helps prevent excessive extractions\n * during rapid file saves.\n * @defaultValue 1000\n */\n debounceMs?: number\n\n /**\n * When true, marks all fields as required in the extracted schema\n * unless they are explicitly marked as optional.\n * @defaultValue false\n */\n enforceRequiredFields?: boolean\n\n /**\n * Format of schema export. groq-type-nodes is the only avilable format at the moment\n */\n format?: string\n\n /**\n * Logger for output messages. Must implement `log`, `info`, and `error` methods.\n * @defaultValue `console`\n */\n output?: Pick<Console, 'error' | 'info' | 'log'>\n\n /**\n * Path where the extracted schema JSON will be written.\n * Can be absolute or relative to the working directory.\n * @defaultValue `path.join(workDir, 'schema.json')`\n */\n outputPath?: string\n\n /**\n * Telemetry logger for the Sanity CLI tooling. If no logger is provided no telemetry\n * is sent. Also, no telemetry will be sent if telemetry is disabled in the sanity CLI.\n */\n telemetryLogger?: CLITelemetryStore\n\n /**\n * Working directory containing the Sanity configuration.\n * This should be the root of your Sanity Studio project where\n * `sanity.config.ts` is located.\n * @defaultValue Vite's project root (`config.root`)\n */\n workDir?: string\n\n /**\n * Workspace name for multi-workspace Sanity configurations.\n * Required when your `sanity.config.ts` exports multiple workspaces\n * and you want to extract schema from a specific one.\n */\n workspaceName?: string\n}\n\n/**\n * Creates a Vite plugin that automatically extracts Sanity schema during development and build.\n *\n * **During development:**\n * The plugin performs an initial extraction when the dev server starts, then watches\n * for file changes and re-extracts the schema when relevant files are modified.\n *\n * **During build:**\n * The plugin extracts the schema once at the end of the build process, ensuring\n * the schema is always up-to-date when deploying.\n *\n * **How it works in dev mode:**\n * 1. Registers watch patterns with Vite's built-in file watcher\n * 2. Performs initial schema extraction when the server starts\n * 3. On file changes matching the patterns, triggers a debounced extraction\n * 4. Uses concurrency control to prevent overlapping extractions\n *\n * @param options - Configuration options for the plugin\n * @returns A Vite plugin configured for schema extraction\n *\n * @internal\n */\nexport function sanitySchemaExtractionPlugin(options: SchemaExtractionPluginOptions) {\n const {\n additionalPatterns = [],\n configPath,\n debounceMs = DEFAULT_DEBOUNCE_MS,\n enforceRequiredFields = false,\n format = 'groq-type-nodes',\n output = console,\n outputPath: outputPathOption = 'schema.json',\n telemetryLogger,\n workDir: workDirOption,\n workspaceName,\n } = options\n\n const watchPatterns = [...DEFAULT_SCHEMA_PATTERNS, ...additionalPatterns]\n\n // Resolved after Vite config is available\n let resolvedWorkDir: string\n let resolvedOutputPath: string\n\n // State for concurrency control\n let isExtracting = false\n let pendingExtraction = false\n\n // Stats for telemetry\n const startTime = Date.now()\n const stats: {failedCount: number; successfulDurations: number[]} = {\n failedCount: 0,\n successfulDurations: [],\n }\n\n const extractSchema = () =>\n runSchemaExtraction({\n configPath,\n enforceRequiredFields,\n format,\n outputPath: resolvedOutputPath,\n workspace: workspaceName,\n })\n\n /**\n * Runs extraction with concurrency control.\n * If extraction is already running, queues one more extraction to run after completion.\n */\n async function runExtraction(isBuilding = false): Promise<void> {\n if (isExtracting) {\n pendingExtraction = true\n return\n }\n\n isExtracting = true\n pendingExtraction = false\n\n const extractionStartTime = Date.now()\n try {\n await extractSchema()\n if (isBuilding) {\n // TODO: Remove when we have better control over progress reporting in build\n output.log('')\n }\n output.log(logSymbols.success, `Extracted schema to ${outputPathOption}`)\n\n // add stats for the successful extraction run to use later for telemetry\n stats.successfulDurations.push(Date.now() - extractionStartTime)\n } catch (err) {\n output.error(\n logSymbols.error,\n `Extraction failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.error(logSymbols.error, formatSchemaValidation(err.validation))\n }\n\n // track the failed extraction\n stats.failedCount++\n } finally {\n isExtracting = false\n\n // If a change came in during extraction, run again\n if (pendingExtraction) {\n pendingExtraction = false\n await runExtraction()\n }\n }\n }\n\n const debouncedExtract = debounce(() => {\n void runExtraction()\n }, debounceMs)\n\n const {isMatch} = createSchemaPatternMatcher(watchPatterns)\n\n const handleChange = (filePath: string) => {\n if (isMatch(filePath, resolvedWorkDir)) {\n debouncedExtract()\n }\n }\n\n return {\n name: 'sanity/schema-extraction',\n\n configResolved(config) {\n // Resolve workDir from option or Vite's project root\n resolvedWorkDir = workDirOption ?? config.root\n\n resolvedOutputPath = isAbsolute(outputPathOption)\n ? outputPathOption\n : path.join(resolvedWorkDir, outputPathOption)\n },\n\n configureServer(server) {\n const trace = telemetryLogger?.trace(SchemaExtractionWatchModeTrace)\n trace?.start()\n\n trace?.log({enforceRequiredFields, schemaFormat: format, step: 'started'})\n\n // Add schema patterns to Vite's watcher\n const absolutePatterns = watchPatterns.map((pattern) => path.join(resolvedWorkDir, pattern))\n server.watcher.add(absolutePatterns)\n\n // Prepare function to log \"stopped\" event to trace and complete the trace\n const onClose = once(() => {\n // Cancel any pending debounced extractions\n debouncedExtract.cancel()\n\n // Log telemetry if available\n if (trace) {\n trace.log({\n averageExtractionDuration: mean(stats.successfulDurations) || 0,\n extractionFailedCount: stats.failedCount,\n extractionSuccessfulCount: stats.successfulDurations.length,\n step: 'stopped',\n watcherDuration: Date.now() - startTime,\n })\n trace.complete()\n }\n\n // Clean up process listeners (must always run, not just when trace exists)\n process.off('SIGTERM', onClose)\n process.off('SIGINT', onClose)\n })\n\n server.watcher.on('change', handleChange)\n server.watcher.on('add', handleChange)\n server.watcher.on('unlink', handleChange)\n\n // call the watcherClosed method when watcher is closed or when process is stopped/killed\n server.watcher.on('close', onClose)\n process.on('SIGTERM', onClose)\n process.on('SIGINT', onClose)\n\n // Run initial extraction after server is ready\n const startExtraction = () => {\n setTimeout(() => {\n // Notify about schema extraction enabled\n output.info(logSymbols.info, 'Schema extraction enabled. Watching:')\n for (const pattern of watchPatterns) {\n output.info(` - ${pattern}`)\n }\n\n // Perform first extraction\n void runExtraction()\n }, INITIAL_EXTRACTION_DELAY_MS)\n }\n\n if (server.httpServer) {\n server.httpServer.once('listening', startExtraction)\n } else {\n // Middleware mode - no HTTP server, run extraction immediately\n startExtraction()\n }\n },\n\n async buildEnd() {\n const trace = telemetryLogger?.trace(SchemaExtractedTrace)\n trace?.start()\n\n try {\n const start = Date.now()\n const schema = await extractSchema()\n output.log(\n logSymbols.success,\n `Extracted schema to ${outputPathOption} (${Date.now() - start}ms)`,\n )\n\n trace?.log({\n enforceRequiredFields,\n schemaAllTypesCount: schema.length,\n schemaDocumentTypesCount: schema.filter((type) => type.type === 'document').length,\n schemaFormat: format,\n schemaTypesCount: schema.filter((type) => type.type === 'type').length,\n })\n } catch (err) {\n trace?.error(err)\n throw err\n } finally {\n trace?.complete()\n }\n },\n } satisfies Plugin\n}\n"],"names":["path","isAbsolute","logSymbols","debounce","mean","once","SchemaExtractedTrace","SchemaExtractionWatchModeTrace","formatSchemaValidation","createSchemaPatternMatcher","runSchemaExtraction","SchemaExtractionError","DEFAULT_SCHEMA_PATTERNS","DEFAULT_DEBOUNCE_MS","INITIAL_EXTRACTION_DELAY_MS","sanitySchemaExtractionPlugin","options","additionalPatterns","configPath","debounceMs","enforceRequiredFields","format","output","console","outputPath","outputPathOption","telemetryLogger","workDir","workDirOption","workspaceName","watchPatterns","resolvedWorkDir","resolvedOutputPath","isExtracting","pendingExtraction","startTime","Date","now","stats","failedCount","successfulDurations","extractSchema","workspace","runExtraction","isBuilding","extractionStartTime","log","success","push","err","error","Error","message","String","validation","length","debouncedExtract","isMatch","handleChange","filePath","name","configResolved","config","root","join","configureServer","server","trace","start","schemaFormat","step","absolutePatterns","map","pattern","watcher","add","onClose","cancel","averageExtractionDuration","extractionFailedCount","extractionSuccessfulCount","watcherDuration","complete","process","off","on","startExtraction","setTimeout","info","httpServer","buildEnd","schema","schemaAllTypesCount","schemaDocumentTypesCount","filter","type","schemaTypesCount"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/actions/schema/vite/plugin-schema-extraction.ts"],"sourcesContent":["import path, {isAbsolute} from 'node:path'\n\nimport {type CLITelemetryStore} from '@sanity/cli-core/types'\nimport {logSymbols} from '@sanity/cli-core/ux'\nimport debounce from 'lodash-es/debounce.js'\nimport mean from 'lodash-es/mean.js'\nimport once from 'lodash-es/once.js'\nimport {type Plugin} from 'vite'\n\nimport {\n SchemaExtractedTrace,\n SchemaExtractionWatchModeTrace,\n} from '../../../telemetry/extractSchema.telemetry.js'\nimport {formatSchemaValidation} from '../formatSchemaValidation.js'\nimport {createSchemaPatternMatcher} from '../matchSchemaPattern.js'\nimport {runSchemaExtraction} from '../runSchemaExtraction.js'\nimport {SchemaExtractionError} from '../utils/SchemaExtractionError.js'\n\n/**\n * Default glob patterns to watch for schema changes.\n * Covers the sanity config file and common schema directory naming conventions.\n */\nconst DEFAULT_SCHEMA_PATTERNS = [\n 'sanity.config.{js,jsx,ts,tsx,mjs}',\n 'schema*/**/*.{js,jsx,ts,tsx,mjs}',\n]\n\n/** Default debounce delay in milliseconds */\nconst DEFAULT_DEBOUNCE_MS = 1000\n\n/**\n * Delay before initial extraction to allow Vite to finish startup\n * and avoid race conditions with module resolution.\n */\nconst INITIAL_EXTRACTION_DELAY_MS = 1000\n\n/**\n * Options for the Sanity schema extraction Vite plugin.\n *\n * @public\n */\ninterface SchemaExtractionPluginOptions {\n /**\n * Name of sanity config file\n * @example sanity.config.ts\n */\n configPath: string\n\n /**\n * Additional glob patterns to watch for schema changes.\n * These are merged with the default patterns.\n * @example `['lib/custom-types/**\\/*.ts', 'shared/schemas/**\\/*.ts']`\n */\n additionalPatterns?: string[]\n\n /**\n * Debounce delay in milliseconds before triggering extraction\n * after a file change. Helps prevent excessive extractions\n * during rapid file saves.\n * @defaultValue 1000\n */\n debounceMs?: number\n\n /**\n * When true, marks all fields as required in the extracted schema\n * unless they are explicitly marked as optional.\n * @defaultValue false\n */\n enforceRequiredFields?: boolean\n\n /**\n * Format of schema export. groq-type-nodes is the only avilable format at the moment\n */\n format?: string\n\n /**\n * Logger for output messages. Must implement `log`, `info`, and `error` methods.\n * @defaultValue `console`\n */\n output?: Pick<Console, 'error' | 'info' | 'log'>\n\n /**\n * Path where the extracted schema JSON will be written.\n * Can be absolute or relative to the working directory.\n * @defaultValue `path.join(workDir, 'schema.json')`\n */\n outputPath?: string\n\n /**\n * Telemetry logger for the Sanity CLI tooling. If no logger is provided no telemetry\n * is sent. Also, no telemetry will be sent if telemetry is disabled in the sanity CLI.\n */\n telemetryLogger?: CLITelemetryStore\n\n /**\n * Working directory containing the Sanity configuration.\n * This should be the root of your Sanity Studio project where\n * `sanity.config.ts` is located.\n * @defaultValue Vite's project root (`config.root`)\n */\n workDir?: string\n\n /**\n * Workspace name for multi-workspace Sanity configurations.\n * Required when your `sanity.config.ts` exports multiple workspaces\n * and you want to extract schema from a specific one.\n */\n workspaceName?: string\n}\n\n/**\n * Creates a Vite plugin that automatically extracts Sanity schema during development and build.\n *\n * **During development:**\n * The plugin performs an initial extraction when the dev server starts, then watches\n * for file changes and re-extracts the schema when relevant files are modified.\n *\n * **During build:**\n * The plugin extracts the schema once at the end of the build process, ensuring\n * the schema is always up-to-date when deploying.\n *\n * **How it works in dev mode:**\n * 1. Registers watch patterns with Vite's built-in file watcher\n * 2. Performs initial schema extraction when the server starts\n * 3. On file changes matching the patterns, triggers a debounced extraction\n * 4. Uses concurrency control to prevent overlapping extractions\n *\n * @param options - Configuration options for the plugin\n * @returns A Vite plugin configured for schema extraction\n *\n * @internal\n */\nexport function sanitySchemaExtractionPlugin(options: SchemaExtractionPluginOptions) {\n const {\n additionalPatterns = [],\n configPath,\n debounceMs = DEFAULT_DEBOUNCE_MS,\n enforceRequiredFields = false,\n format = 'groq-type-nodes',\n output = console,\n outputPath: outputPathOption = 'schema.json',\n telemetryLogger,\n workDir: workDirOption,\n workspaceName,\n } = options\n\n const watchPatterns = [...DEFAULT_SCHEMA_PATTERNS, ...additionalPatterns]\n\n // Resolved after Vite config is available\n let resolvedWorkDir: string\n let resolvedOutputPath: string\n\n // State for concurrency control\n let isExtracting = false\n let pendingExtraction = false\n\n // Stats for telemetry\n const startTime = Date.now()\n const stats: {failedCount: number; successfulDurations: number[]} = {\n failedCount: 0,\n successfulDurations: [],\n }\n\n const extractSchema = () =>\n runSchemaExtraction({\n configPath,\n enforceRequiredFields,\n format,\n outputPath: resolvedOutputPath,\n workspace: workspaceName,\n })\n\n /**\n * Runs extraction with concurrency control.\n * If extraction is already running, queues one more extraction to run after completion.\n */\n async function runExtraction(isBuilding = false): Promise<void> {\n if (isExtracting) {\n pendingExtraction = true\n return\n }\n\n isExtracting = true\n pendingExtraction = false\n\n const extractionStartTime = Date.now()\n try {\n await extractSchema()\n if (isBuilding) {\n // TODO: Remove when we have better control over progress reporting in build\n output.log('')\n }\n output.log(logSymbols.success, `Extracted schema to ${outputPathOption}`)\n\n // add stats for the successful extraction run to use later for telemetry\n stats.successfulDurations.push(Date.now() - extractionStartTime)\n } catch (err) {\n output.error(\n logSymbols.error,\n `Extraction failed: ${err instanceof Error ? err.message : String(err)}`,\n )\n if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {\n output.error(logSymbols.error, formatSchemaValidation(err.validation))\n }\n\n // track the failed extraction\n stats.failedCount++\n } finally {\n isExtracting = false\n\n // If a change came in during extraction, run again\n if (pendingExtraction) {\n pendingExtraction = false\n await runExtraction()\n }\n }\n }\n\n const debouncedExtract = debounce(() => {\n void runExtraction()\n }, debounceMs)\n\n const {isMatch} = createSchemaPatternMatcher(watchPatterns)\n\n const handleChange = (filePath: string) => {\n if (isMatch(filePath, resolvedWorkDir)) {\n debouncedExtract()\n }\n }\n\n return {\n name: 'sanity/schema-extraction',\n\n configResolved(config) {\n // Resolve workDir from option or Vite's project root\n resolvedWorkDir = workDirOption ?? config.root\n\n resolvedOutputPath = isAbsolute(outputPathOption)\n ? outputPathOption\n : path.join(resolvedWorkDir, outputPathOption)\n },\n\n configureServer(server) {\n const trace = telemetryLogger?.trace(SchemaExtractionWatchModeTrace)\n trace?.start()\n\n trace?.log({enforceRequiredFields, schemaFormat: format, step: 'started'})\n\n // Add schema patterns to Vite's watcher\n const absolutePatterns = watchPatterns.map((pattern) => path.join(resolvedWorkDir, pattern))\n server.watcher.add(absolutePatterns)\n\n // Prepare function to log \"stopped\" event to trace and complete the trace\n const onClose = once(() => {\n // Cancel any pending debounced extractions\n debouncedExtract.cancel()\n\n // Log telemetry if available\n if (trace) {\n trace.log({\n averageExtractionDuration: mean(stats.successfulDurations) || 0,\n extractionFailedCount: stats.failedCount,\n extractionSuccessfulCount: stats.successfulDurations.length,\n step: 'stopped',\n watcherDuration: Date.now() - startTime,\n })\n trace.complete()\n }\n\n // Clean up process listeners (must always run, not just when trace exists)\n process.off('SIGTERM', onClose)\n process.off('SIGINT', onClose)\n })\n\n server.watcher.on('change', handleChange)\n server.watcher.on('add', handleChange)\n server.watcher.on('unlink', handleChange)\n\n // call the watcherClosed method when watcher is closed or when process is stopped/killed\n server.watcher.on('close', onClose)\n process.on('SIGTERM', onClose)\n process.on('SIGINT', onClose)\n\n // Run initial extraction after server is ready\n const startExtraction = () => {\n setTimeout(() => {\n // Notify about schema extraction enabled\n output.info(logSymbols.info, 'Schema extraction enabled. Watching:')\n for (const pattern of watchPatterns) {\n output.info(` - ${pattern}`)\n }\n\n // Perform first extraction\n void runExtraction()\n }, INITIAL_EXTRACTION_DELAY_MS)\n }\n\n if (server.httpServer) {\n server.httpServer.once('listening', startExtraction)\n } else {\n // Middleware mode - no HTTP server, run extraction immediately\n startExtraction()\n }\n },\n\n async buildEnd() {\n const trace = telemetryLogger?.trace(SchemaExtractedTrace)\n trace?.start()\n\n try {\n const start = Date.now()\n const schema = await extractSchema()\n output.log(\n logSymbols.success,\n `Extracted schema to ${outputPathOption} (${Date.now() - start}ms)`,\n )\n\n trace?.log({\n enforceRequiredFields,\n schemaAllTypesCount: schema.length,\n schemaDocumentTypesCount: schema.filter((type) => type.type === 'document').length,\n schemaFormat: format,\n schemaTypesCount: schema.filter((type) => type.type === 'type').length,\n })\n } catch (err) {\n trace?.error(err)\n throw err\n } finally {\n trace?.complete()\n }\n },\n } satisfies Plugin\n}\n"],"names":["path","isAbsolute","logSymbols","debounce","mean","once","SchemaExtractedTrace","SchemaExtractionWatchModeTrace","formatSchemaValidation","createSchemaPatternMatcher","runSchemaExtraction","SchemaExtractionError","DEFAULT_SCHEMA_PATTERNS","DEFAULT_DEBOUNCE_MS","INITIAL_EXTRACTION_DELAY_MS","sanitySchemaExtractionPlugin","options","additionalPatterns","configPath","debounceMs","enforceRequiredFields","format","output","console","outputPath","outputPathOption","telemetryLogger","workDir","workDirOption","workspaceName","watchPatterns","resolvedWorkDir","resolvedOutputPath","isExtracting","pendingExtraction","startTime","Date","now","stats","failedCount","successfulDurations","extractSchema","workspace","runExtraction","isBuilding","extractionStartTime","log","success","push","err","error","Error","message","String","validation","length","debouncedExtract","isMatch","handleChange","filePath","name","configResolved","config","root","join","configureServer","server","trace","start","schemaFormat","step","absolutePatterns","map","pattern","watcher","add","onClose","cancel","averageExtractionDuration","extractionFailedCount","extractionSuccessfulCount","watcherDuration","complete","process","off","on","startExtraction","setTimeout","info","httpServer","buildEnd","schema","schemaAllTypesCount","schemaDocumentTypesCount","filter","type","schemaTypesCount"],"mappings":"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"}
|