@sanity/cli-build 1.1.1 → 2.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.
@@ -28,7 +28,7 @@ export declare const AppBuildTrace: DefinedTelemetryTrace<
28
28
  *
29
29
  * @internal
30
30
  */
31
- export declare interface AutoUpdatesBuildConfig {
31
+ declare interface AutoUpdatesBuildConfig {
32
32
  /** Import map entries for the packages served from Sanity's module CDN. */
33
33
  imports: Record<string, string>;
34
34
  /** Vendor packages to emit as hashed browser-loadable ESM chunks. */
@@ -39,6 +39,15 @@ export declare interface AutoUpdatesBuildConfig {
39
39
 
40
40
  export declare const buildDebug: Debugger;
41
41
 
42
+ /**
43
+ * Builds static files
44
+ *
45
+ * @internal
46
+ */
47
+ export declare function buildStaticFiles(options: StaticBuildOptions): Promise<{
48
+ chunks: ChunkStats[];
49
+ }>;
50
+
42
51
  /**
43
52
  * Checks that the studio has declared and installed the required dependencies
44
53
  * needed by the Sanity modules. While we generally use regular, explicit
@@ -74,20 +83,15 @@ export declare function checkStudioDependencyVersions(
74
83
  },
75
84
  ): Promise<void>;
76
85
 
77
- /**
78
- * Copies a directory from one location to another
79
- *
80
- * @internal
81
- *
82
- * @param srcDir - Source directory
83
- * @param destDir - Destination directory
84
- * @param skipExisting - Skip existing files
85
- */
86
- export declare function copyDir(
87
- srcDir: string,
88
- destDir: string,
89
- skipExisting?: boolean,
90
- ): Promise<void>;
86
+ declare interface ChunkModule {
87
+ name: string;
88
+ renderedLength: number;
89
+ }
90
+
91
+ declare interface ChunkStats {
92
+ modules: ChunkModule[];
93
+ name: string;
94
+ }
91
95
 
92
96
  /**
93
97
  * Merge user-provided Vite configuration object or function
@@ -103,29 +107,7 @@ export declare function extendViteConfigWithUserConfig(
103
107
  userConfig: UserViteConfig,
104
108
  ): Promise<InlineConfig>;
105
109
 
106
- /**
107
- * Re-asserts the critical parts of the default config after a userland vite
108
- * config (`vite` in `sanity.cli.ts`) has been applied.
109
- *
110
- * Everything `getViteConfig` sets under `build.rolldownOptions` is load-bearing:
111
- * the `input` entries (the studio entry plus, for auto-updating studios/apps,
112
- * the vendor entries), `preserveEntrySignatures`, the `experimental` flags the
113
- * vendor plugins rely on, and the `output` chunk naming. A userland config that
114
- * returns a brand-new object for any of these would silently break the build
115
- * (e.g. vendor chunks never emitted while the bundle still treats them as
116
- * external), so the default `rolldownOptions` are deep-merged back over the
117
- * userland config: userland additions survive, replacements of critical
118
- * options are healed.
119
- *
120
- * @param config - User-modified configuration
121
- * @param defaultConfig - The configuration produced by `getViteConfig`, before the userland config was applied
122
- * @returns Merged configuration
123
- * @internal
124
- */
125
- export declare function finalizeViteConfig(
126
- config: InlineConfig,
127
- defaultConfig: InlineConfig,
128
- ): Promise<InlineConfig>;
110
+ export declare function formatModuleSizes(modules: ChunkModule[]): string;
129
111
 
130
112
  /**
131
113
  * Generate CDN CSS URLs for auto-updated packages.
@@ -185,22 +167,6 @@ declare type PackageWithCss = Package & {
185
167
  cssFile?: string;
186
168
  };
187
169
 
188
- /**
189
- * Resolves the relative entry paths for a Sanity project without writing any
190
- * runtime files to disk. Used by federation builds that skip the full runtime
191
- * generation but still need entry metadata for the vite plugin.
192
- */
193
- export declare function resolveEntries(options: {
194
- cwd: string;
195
- entry?: string;
196
- isApp?: boolean;
197
- isWorkbenchApp?: boolean;
198
- runtimeDir?: string;
199
- }): Promise<{
200
- relativeConfigLocation: string | null;
201
- relativeEntry: string | null;
202
- }>;
203
-
204
170
  /**
205
171
  * Resolves vendor package entry points and metadata for a combined studio/app build.
206
172
  * Does not run a build — callers add `entries` to the main Vite/Rolldown input and
@@ -237,6 +203,28 @@ declare interface RuntimeOptions {
237
203
  */
238
204
  export declare const SANITY_CACHE_DIR = "node_modules/.sanity";
239
205
 
206
+ export declare function sortModulesBySize(chunks: ChunkStats[]): ChunkModule[];
207
+
208
+ declare interface StaticBuildOptions {
209
+ basePath: string;
210
+ cwd: string;
211
+ outputDir: string;
212
+ appTitle?: string;
213
+ autoUpdates?: AutoUpdatesBuildConfig;
214
+ entry?: string;
215
+ isApp?: boolean;
216
+ /** Workbench app (opted in via `unstable_defineApp`) — drives the federation build. */
217
+ isWorkbenchApp?: boolean;
218
+ minify?: boolean;
219
+ profile?: boolean;
220
+ reactCompiler?: PluginOptions;
221
+ schemaExtraction?: CliConfig["schemaExtraction"];
222
+ services?: DefineAppInput["services"];
223
+ sourceMap?: boolean;
224
+ views?: DefineAppInput["views"];
225
+ vite?: UserViteConfig;
226
+ }
227
+
240
228
  export declare const StudioBuildTrace: DefinedTelemetryTrace<
241
229
  {
242
230
  outputSize: number;
@@ -250,7 +238,7 @@ declare interface TrackedPackage {
250
238
  supported: string[];
251
239
  }
252
240
 
253
- export declare interface VendorBuildConfig {
241
+ declare interface VendorBuildConfig {
254
242
  /** Rolldown entry name -\> absolute path to the package entry file. */
255
243
  entries: Record<string, string>;
256
244
  /** Named exports each CommonJS entry must re-expose as ESM, keyed by chunk name. */
@@ -333,14 +321,6 @@ declare interface ViteOptions {
333
321
  views?: DefineAppInput["views"];
334
322
  }
335
323
 
336
- /**
337
- * @internal
338
- */
339
- export declare function writeFavicons(
340
- basePath: string,
341
- destDir: string,
342
- ): Promise<void>;
343
-
344
324
  /**
345
325
  * Generates the `.sanity/runtime` directory, and optionally watches for custom
346
326
  * document files, rebuilding when they change
@@ -1,13 +1,13 @@
1
1
  export { buildDebug } from '../../actions/build/buildDebug.js';
2
+ export { buildStaticFiles } from '../../actions/build/buildStaticFiles.js';
2
3
  export { checkRequiredDependencies } from '../../actions/build/checkRequiredDependencies.js';
3
4
  export { checkStudioDependencyVersions } from '../../actions/build/checkStudioDependencyVersions.js';
4
5
  export { getAutoUpdatesCssUrls, getAutoUpdatesImportMap, getModuleUrl } from '../../actions/build/getAutoUpdatesImportMap.js';
5
- export { extendViteConfigWithUserConfig, finalizeViteConfig, getViteConfig } from '../../actions/build/getViteConfig.js';
6
+ export { extendViteConfigWithUserConfig, getViteConfig } from '../../actions/build/getViteConfig.js';
6
7
  export { resolveVendorBuildConfig } from '../../actions/build/resolveVendorBuildConfig.js';
7
- export { writeFavicons } from '../../actions/build/writeFavicons.js';
8
- export { resolveEntries, writeSanityRuntime } from '../../actions/build/writeSanityRuntime.js';
8
+ export { writeSanityRuntime } from '../../actions/build/writeSanityRuntime.js';
9
9
  export { SANITY_CACHE_DIR } from '../../constants.js';
10
10
  export { AppBuildTrace, StudioBuildTrace } from '../../telemetry/build.telemetry.js';
11
- export { copyDir } from '../../util/copyDir.js';
11
+ export { formatModuleSizes, sortModulesBySize } from '../../util/moduleFormatUtils.js';
12
12
 
13
13
  //# sourceMappingURL=build.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/_exports/_internal/build.ts"],"sourcesContent":["export {type AutoUpdatesBuildConfig} from '../../actions/build/autoUpdates.js'\nexport {buildDebug} from '../../actions/build/buildDebug.js'\nexport {checkRequiredDependencies} from '../../actions/build/checkRequiredDependencies.js'\nexport {checkStudioDependencyVersions} from '../../actions/build/checkStudioDependencyVersions.js'\nexport {\n getAutoUpdatesCssUrls,\n getAutoUpdatesImportMap,\n getModuleUrl,\n} from '../../actions/build/getAutoUpdatesImportMap.js'\nexport {\n extendViteConfigWithUserConfig,\n finalizeViteConfig,\n getViteConfig,\n} from '../../actions/build/getViteConfig.js'\nexport {\n resolveVendorBuildConfig,\n type VendorBuildConfig,\n} from '../../actions/build/resolveVendorBuildConfig.js'\nexport {writeFavicons} from '../../actions/build/writeFavicons.js'\nexport {resolveEntries, writeSanityRuntime} from '../../actions/build/writeSanityRuntime.js'\nexport {SANITY_CACHE_DIR} from '../../constants.js'\nexport {AppBuildTrace, StudioBuildTrace} from '../../telemetry/build.telemetry.js'\nexport {copyDir} from '../../util/copyDir.js'\n"],"names":["buildDebug","checkRequiredDependencies","checkStudioDependencyVersions","getAutoUpdatesCssUrls","getAutoUpdatesImportMap","getModuleUrl","extendViteConfigWithUserConfig","finalizeViteConfig","getViteConfig","resolveVendorBuildConfig","writeFavicons","resolveEntries","writeSanityRuntime","SANITY_CACHE_DIR","AppBuildTrace","StudioBuildTrace","copyDir"],"mappings":"AACA,SAAQA,UAAU,QAAO,oCAAmC;AAC5D,SAAQC,yBAAyB,QAAO,mDAAkD;AAC1F,SAAQC,6BAA6B,QAAO,uDAAsD;AAClG,SACEC,qBAAqB,EACrBC,uBAAuB,EACvBC,YAAY,QACP,iDAAgD;AACvD,SACEC,8BAA8B,EAC9BC,kBAAkB,EAClBC,aAAa,QACR,uCAAsC;AAC7C,SACEC,wBAAwB,QAEnB,kDAAiD;AACxD,SAAQC,aAAa,QAAO,uCAAsC;AAClE,SAAQC,cAAc,EAAEC,kBAAkB,QAAO,4CAA2C;AAC5F,SAAQC,gBAAgB,QAAO,qBAAoB;AACnD,SAAQC,aAAa,EAAEC,gBAAgB,QAAO,qCAAoC;AAClF,SAAQC,OAAO,QAAO,wBAAuB"}
1
+ {"version":3,"sources":["../../../src/_exports/_internal/build.ts"],"sourcesContent":["export {buildDebug} from '../../actions/build/buildDebug.js'\nexport {buildStaticFiles} from '../../actions/build/buildStaticFiles.js'\nexport {checkRequiredDependencies} from '../../actions/build/checkRequiredDependencies.js'\nexport {checkStudioDependencyVersions} from '../../actions/build/checkStudioDependencyVersions.js'\nexport {\n getAutoUpdatesCssUrls,\n getAutoUpdatesImportMap,\n getModuleUrl,\n} from '../../actions/build/getAutoUpdatesImportMap.js'\nexport {extendViteConfigWithUserConfig, getViteConfig} from '../../actions/build/getViteConfig.js'\nexport {resolveVendorBuildConfig} from '../../actions/build/resolveVendorBuildConfig.js'\nexport {writeSanityRuntime} from '../../actions/build/writeSanityRuntime.js'\nexport {SANITY_CACHE_DIR} from '../../constants.js'\nexport {AppBuildTrace, StudioBuildTrace} from '../../telemetry/build.telemetry.js'\nexport {formatModuleSizes, sortModulesBySize} from '../../util/moduleFormatUtils.js'\n"],"names":["buildDebug","buildStaticFiles","checkRequiredDependencies","checkStudioDependencyVersions","getAutoUpdatesCssUrls","getAutoUpdatesImportMap","getModuleUrl","extendViteConfigWithUserConfig","getViteConfig","resolveVendorBuildConfig","writeSanityRuntime","SANITY_CACHE_DIR","AppBuildTrace","StudioBuildTrace","formatModuleSizes","sortModulesBySize"],"mappings":"AAAA,SAAQA,UAAU,QAAO,oCAAmC;AAC5D,SAAQC,gBAAgB,QAAO,0CAAyC;AACxE,SAAQC,yBAAyB,QAAO,mDAAkD;AAC1F,SAAQC,6BAA6B,QAAO,uDAAsD;AAClG,SACEC,qBAAqB,EACrBC,uBAAuB,EACvBC,YAAY,QACP,iDAAgD;AACvD,SAAQC,8BAA8B,EAAEC,aAAa,QAAO,uCAAsC;AAClG,SAAQC,wBAAwB,QAAO,kDAAiD;AACxF,SAAQC,kBAAkB,QAAO,4CAA2C;AAC5E,SAAQC,gBAAgB,QAAO,qBAAoB;AACnD,SAAQC,aAAa,EAAEC,gBAAgB,QAAO,qCAAoC;AAClF,SAAQC,iBAAiB,EAAEC,iBAAiB,QAAO,kCAAiC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Get environment variables prefixed with SANITY_APP_, as an object.
3
+ *
4
+ * @param options - Options for the environment variable loading
5
+ * {@link StudioEnvVariablesOptions}
6
+ * @returns Object of app environment variables
7
+ *
8
+ * @internal
9
+ */
10
+ export declare function getAppEnvironmentVariables(
11
+ options?: StudioEnvVariablesOptions,
12
+ ): Record<string, string>;
13
+
14
+ /**
15
+ * Get environment variables prefixed with SANITY_STUDIO_, as an object.
16
+ *
17
+ * @param options - Options for the environment variable loading
18
+ * {@link StudioEnvVariablesOptions}
19
+ * @returns Object of studio environment variables
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * getStudioEnvironmentVariables({prefix: 'process.env.', jsonEncode: true})
24
+ * ```
25
+ *
26
+ * @public
27
+ */
28
+ export declare function getStudioEnvironmentVariables(
29
+ options?: StudioEnvVariablesOptions,
30
+ ): Record<string, string>;
31
+
32
+ /**
33
+ * The params for the `getStudioEnvironmentVariables` and `getAppEnvironmentVariables` function
34
+ * that gets Studio/App-focused environment variables.
35
+ *
36
+ * @public
37
+ */
38
+ export declare interface StudioEnvVariablesOptions {
39
+ /**
40
+ * When specified includes environment variables from dotenv files (`.env`), in the same way the studio does.
41
+ * A `mode` must be specified, usually `development`
42
+ * or `production`, which will load the corresponding `.env.development` or `.env.production`.
43
+ * To specify where to look for the dotenv files, specify `options.envFile.envDir`.
44
+ */
45
+ envFile?:
46
+ | false
47
+ | {
48
+ envDir?: string;
49
+ mode: string;
50
+ };
51
+ /**
52
+ * When specified, JSON-encodes the values, which is handy if you want to pass
53
+ * this to a bundlers hardcoded defines, such as Vite's `define` or Webpack's `DefinePlugin`.
54
+ */
55
+ jsonEncode?: boolean;
56
+ /**
57
+ * When specified adds a prefix to the environment variable keys,
58
+ * eg: `getStudioEnvironmentVariables({prefix: 'process.env.'})`
59
+ */
60
+ prefix?: string;
61
+ }
62
+
63
+ export {};
@@ -0,0 +1,3 @@
1
+ export { getAppEnvironmentVariables, getStudioEnvironmentVariables } from '../../actions/build/getEnvironmentVariables.js';
2
+
3
+ //# sourceMappingURL=env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/_exports/_internal/env.ts"],"sourcesContent":["export {\n getAppEnvironmentVariables,\n getStudioEnvironmentVariables,\n type StudioEnvVariablesOptions,\n} from '../../actions/build/getEnvironmentVariables.js'\n"],"names":["getAppEnvironmentVariables","getStudioEnvironmentVariables"],"mappings":"AAAA,SACEA,0BAA0B,EAC1BC,6BAA6B,QAExB,iDAAgD"}
@@ -0,0 +1,148 @@
1
+ import path from 'node:path';
2
+ import { build, createBuilder } from 'vite';
3
+ import { copyDir } from '../../util/copyDir.js';
4
+ import { buildDebug } from './buildDebug.js';
5
+ import { getAppEnvironmentVariables, getStudioEnvironmentVariables } from './getEnvironmentVariables.js';
6
+ import { extendViteConfigWithUserConfig, finalizeViteConfig, getViteConfig } from './getViteConfig.js';
7
+ import { writeFavicons } from './writeFavicons.js';
8
+ import { resolveEntries, writeSanityRuntime } from './writeSanityRuntime.js';
9
+ /**
10
+ * Builds static files
11
+ *
12
+ * @internal
13
+ */ export async function buildStaticFiles(options) {
14
+ const { appTitle, autoUpdates, basePath, cwd, entry, isApp, isWorkbenchApp, minify = true, outputDir, reactCompiler, schemaExtraction, services, sourceMap = false, views, vite: extendViteConfig } = options;
15
+ const mode = 'production';
16
+ /* Federation builds only produce the federation environment
17
+ * (remote-entry, mf-manifest) — skip client-specific steps like
18
+ * runtime generation, static file copies, and favicons.
19
+ */ if (isWorkbenchApp) {
20
+ buildDebug('Resolving entries for federation build');
21
+ const entries = await resolveEntries({
22
+ cwd,
23
+ entry,
24
+ isApp,
25
+ isWorkbenchApp
26
+ });
27
+ buildDebug('Resolving vite config (federation)');
28
+ let viteConfig = await getViteConfig({
29
+ basePath,
30
+ cwd,
31
+ entries,
32
+ getEnvironmentVariables,
33
+ isApp,
34
+ isWorkbenchApp,
35
+ minify,
36
+ mode,
37
+ outputDir,
38
+ reactCompiler,
39
+ // Schema extraction is a build-time artifact, not a client-specific step,
40
+ // so a federated studio extracts its schema like the legacy studio build.
41
+ schemaExtraction,
42
+ services,
43
+ sourceMap,
44
+ views
45
+ });
46
+ // Apply the user's Vite config so plugins like `@vanilla-extract/vite-plugin`
47
+ // transform source files before the federation environment is bundled.
48
+ // `finalizeViteConfig` is intentionally skipped: the federation environment
49
+ // has its own entry and does not use `.sanity/runtime/app.js`.
50
+ if (extendViteConfig) {
51
+ viteConfig = await extendViteConfigWithUserConfig({
52
+ command: 'build',
53
+ mode
54
+ }, viteConfig, extendViteConfig);
55
+ }
56
+ buildDebug('Bundling federation environment');
57
+ const builder = await createBuilder(viteConfig);
58
+ await builder.buildApp();
59
+ buildDebug('Bundling complete');
60
+ // TODO: add stats here
61
+ return {
62
+ chunks: []
63
+ };
64
+ }
65
+ buildDebug('Writing Sanity runtime files');
66
+ const { entries } = await writeSanityRuntime({
67
+ appTitle,
68
+ basePath,
69
+ cwd,
70
+ entry,
71
+ isApp,
72
+ isWorkbenchApp,
73
+ reactStrictMode: false,
74
+ watch: false
75
+ });
76
+ function getEnvironmentVariables() {
77
+ return isApp ? getAppEnvironmentVariables({
78
+ jsonEncode: true,
79
+ prefix: 'process.env.'
80
+ }) : getStudioEnvironmentVariables({
81
+ jsonEncode: true,
82
+ prefix: 'process.env.'
83
+ });
84
+ }
85
+ buildDebug('Resolving vite config');
86
+ let viteConfig = await getViteConfig({
87
+ autoUpdates,
88
+ basePath,
89
+ cwd,
90
+ entries,
91
+ getEnvironmentVariables,
92
+ isApp,
93
+ isWorkbenchApp,
94
+ minify,
95
+ mode,
96
+ outputDir,
97
+ reactCompiler,
98
+ schemaExtraction,
99
+ sourceMap
100
+ });
101
+ if (extendViteConfig) {
102
+ const defaultViteConfig = viteConfig;
103
+ viteConfig = await extendViteConfigWithUserConfig({
104
+ command: 'build',
105
+ mode
106
+ }, viteConfig, extendViteConfig);
107
+ viteConfig = await finalizeViteConfig(viteConfig, defaultViteConfig);
108
+ }
109
+ const fromPath = path.join(cwd, 'static');
110
+ // Copy files placed in /static to the built /static
111
+ buildDebug(`Copying static files from ${fromPath} to output dir`);
112
+ const staticPath = path.join(outputDir, 'static');
113
+ await copyDir(fromPath, staticPath);
114
+ // Write favicons, not overwriting ones that already exist, to static folder
115
+ buildDebug('Writing favicons to output dir');
116
+ const faviconBasePath = `${basePath.replace(/\/+$/, '')}/static`;
117
+ await writeFavicons(faviconBasePath, staticPath);
118
+ buildDebug('Bundling using vite');
119
+ const bundle = await build(viteConfig);
120
+ buildDebug('Bundling complete');
121
+ // For typescript only - this shouldn't ever be the case given we're not watching
122
+ if (Array.isArray(bundle) || !('output' in bundle)) {
123
+ return {
124
+ chunks: []
125
+ };
126
+ }
127
+ const stats = [];
128
+ for (const chunk of bundle.output){
129
+ if (chunk.type !== 'chunk') {
130
+ continue;
131
+ }
132
+ stats.push({
133
+ modules: Object.entries(chunk.modules).map(([rawFilePath, chunkModule])=>{
134
+ const filePath = rawFilePath.startsWith('\u0000') ? rawFilePath.slice('\u0000'.length) : rawFilePath;
135
+ return {
136
+ name: path.isAbsolute(filePath) ? path.relative(cwd, filePath) : filePath,
137
+ renderedLength: chunkModule.renderedLength
138
+ };
139
+ }),
140
+ name: chunk.name
141
+ });
142
+ }
143
+ return {
144
+ chunks: stats
145
+ };
146
+ }
147
+
148
+ //# sourceMappingURL=buildStaticFiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/build/buildStaticFiles.ts"],"sourcesContent":["import path from 'node:path'\n\nimport {type CliConfig, type UserViteConfig} from '@sanity/cli-core'\nimport {type DefineAppInput} from '@sanity/workbench-cli'\nimport {type PluginOptions as ReactCompilerConfig} from 'babel-plugin-react-compiler'\nimport {build, createBuilder} from 'vite'\n\nimport {copyDir} from '../../util/copyDir.js'\nimport {type AutoUpdatesBuildConfig} from './autoUpdates.js'\nimport {buildDebug} from './buildDebug.js'\nimport {\n getAppEnvironmentVariables,\n getStudioEnvironmentVariables,\n} from './getEnvironmentVariables.js'\nimport {extendViteConfigWithUserConfig, finalizeViteConfig, getViteConfig} from './getViteConfig.js'\nimport {writeFavicons} from './writeFavicons.js'\nimport {resolveEntries, writeSanityRuntime} from './writeSanityRuntime.js'\n\nexport interface ChunkModule {\n name: string\n renderedLength: number\n}\n\nexport interface ChunkStats {\n modules: ChunkModule[]\n name: string\n}\n\ninterface StaticBuildOptions {\n basePath: string\n cwd: string\n outputDir: string\n\n appTitle?: string\n autoUpdates?: AutoUpdatesBuildConfig\n entry?: string\n isApp?: boolean\n /** Workbench app (opted in via `unstable_defineApp`) — drives the federation build. */\n isWorkbenchApp?: boolean\n minify?: boolean\n profile?: boolean\n reactCompiler?: ReactCompilerConfig\n schemaExtraction?: CliConfig['schemaExtraction']\n services?: DefineAppInput['services']\n sourceMap?: boolean\n views?: DefineAppInput['views']\n vite?: UserViteConfig\n}\n\n/**\n * Builds static files\n *\n * @internal\n */\nexport async function buildStaticFiles(\n options: StaticBuildOptions,\n): Promise<{chunks: ChunkStats[]}> {\n const {\n appTitle,\n autoUpdates,\n basePath,\n cwd,\n entry,\n isApp,\n isWorkbenchApp,\n minify = true,\n outputDir,\n reactCompiler,\n schemaExtraction,\n services,\n sourceMap = false,\n views,\n vite: extendViteConfig,\n } = options\n\n const mode = 'production'\n\n /* Federation builds only produce the federation environment\n * (remote-entry, mf-manifest) — skip client-specific steps like\n * runtime generation, static file copies, and favicons.\n */\n if (isWorkbenchApp) {\n buildDebug('Resolving entries for federation build')\n const entries = await resolveEntries({cwd, entry, isApp, isWorkbenchApp})\n\n buildDebug('Resolving vite config (federation)')\n let viteConfig = await getViteConfig({\n basePath,\n cwd,\n entries,\n getEnvironmentVariables,\n isApp,\n isWorkbenchApp,\n minify,\n mode,\n outputDir,\n reactCompiler,\n // Schema extraction is a build-time artifact, not a client-specific step,\n // so a federated studio extracts its schema like the legacy studio build.\n schemaExtraction,\n services,\n sourceMap,\n views,\n })\n\n // Apply the user's Vite config so plugins like `@vanilla-extract/vite-plugin`\n // transform source files before the federation environment is bundled.\n // `finalizeViteConfig` is intentionally skipped: the federation environment\n // has its own entry and does not use `.sanity/runtime/app.js`.\n if (extendViteConfig) {\n viteConfig = await extendViteConfigWithUserConfig(\n {command: 'build', mode},\n viteConfig,\n extendViteConfig,\n )\n }\n\n buildDebug('Bundling federation environment')\n const builder = await createBuilder(viteConfig)\n await builder.buildApp()\n buildDebug('Bundling complete')\n // TODO: add stats here\n return {chunks: []}\n }\n\n buildDebug('Writing Sanity runtime files')\n const {entries} = await writeSanityRuntime({\n appTitle,\n basePath,\n cwd,\n entry,\n isApp,\n isWorkbenchApp,\n reactStrictMode: false,\n watch: false,\n })\n\n function getEnvironmentVariables() {\n return isApp\n ? getAppEnvironmentVariables({jsonEncode: true, prefix: 'process.env.'})\n : getStudioEnvironmentVariables({jsonEncode: true, prefix: 'process.env.'})\n }\n\n buildDebug('Resolving vite config')\n let viteConfig = await getViteConfig({\n autoUpdates,\n basePath,\n cwd,\n entries,\n getEnvironmentVariables,\n isApp,\n isWorkbenchApp,\n minify,\n mode,\n outputDir,\n reactCompiler,\n schemaExtraction,\n sourceMap,\n })\n\n if (extendViteConfig) {\n const defaultViteConfig = viteConfig\n viteConfig = await extendViteConfigWithUserConfig(\n {command: 'build', mode},\n viteConfig,\n extendViteConfig,\n )\n viteConfig = await finalizeViteConfig(viteConfig, defaultViteConfig)\n }\n\n const fromPath = path.join(cwd, 'static')\n // Copy files placed in /static to the built /static\n buildDebug(`Copying static files from ${fromPath} to output dir`)\n const staticPath = path.join(outputDir, 'static')\n await copyDir(fromPath, staticPath)\n\n // Write favicons, not overwriting ones that already exist, to static folder\n buildDebug('Writing favicons to output dir')\n const faviconBasePath = `${basePath.replace(/\\/+$/, '')}/static`\n await writeFavicons(faviconBasePath, staticPath)\n\n buildDebug('Bundling using vite')\n const bundle = await build(viteConfig)\n buildDebug('Bundling complete')\n\n // For typescript only - this shouldn't ever be the case given we're not watching\n if (Array.isArray(bundle) || !('output' in bundle)) {\n return {chunks: []}\n }\n\n const stats: ChunkStats[] = []\n for (const chunk of bundle.output) {\n if (chunk.type !== 'chunk') {\n continue\n }\n\n stats.push({\n modules: Object.entries(chunk.modules).map(([rawFilePath, chunkModule]) => {\n const filePath = rawFilePath.startsWith('\\u0000')\n ? rawFilePath.slice('\\u0000'.length)\n : rawFilePath\n\n return {\n name: path.isAbsolute(filePath) ? path.relative(cwd, filePath) : filePath,\n renderedLength: chunkModule.renderedLength,\n }\n }),\n name: chunk.name,\n })\n }\n\n return {chunks: stats}\n}\n"],"names":["path","build","createBuilder","copyDir","buildDebug","getAppEnvironmentVariables","getStudioEnvironmentVariables","extendViteConfigWithUserConfig","finalizeViteConfig","getViteConfig","writeFavicons","resolveEntries","writeSanityRuntime","buildStaticFiles","options","appTitle","autoUpdates","basePath","cwd","entry","isApp","isWorkbenchApp","minify","outputDir","reactCompiler","schemaExtraction","services","sourceMap","views","vite","extendViteConfig","mode","entries","viteConfig","getEnvironmentVariables","command","builder","buildApp","chunks","reactStrictMode","watch","jsonEncode","prefix","defaultViteConfig","fromPath","join","staticPath","faviconBasePath","replace","bundle","Array","isArray","stats","chunk","output","type","push","modules","Object","map","rawFilePath","chunkModule","filePath","startsWith","slice","length","name","isAbsolute","relative","renderedLength"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAK5B,SAAQC,KAAK,EAAEC,aAAa,QAAO,OAAM;AAEzC,SAAQC,OAAO,QAAO,wBAAuB;AAE7C,SAAQC,UAAU,QAAO,kBAAiB;AAC1C,SACEC,0BAA0B,EAC1BC,6BAA6B,QACxB,+BAA8B;AACrC,SAAQC,8BAA8B,EAAEC,kBAAkB,EAAEC,aAAa,QAAO,qBAAoB;AACpG,SAAQC,aAAa,QAAO,qBAAoB;AAChD,SAAQC,cAAc,EAAEC,kBAAkB,QAAO,0BAAyB;AAiC1E;;;;CAIC,GACD,OAAO,eAAeC,iBACpBC,OAA2B;IAE3B,MAAM,EACJC,QAAQ,EACRC,WAAW,EACXC,QAAQ,EACRC,GAAG,EACHC,KAAK,EACLC,KAAK,EACLC,cAAc,EACdC,SAAS,IAAI,EACbC,SAAS,EACTC,aAAa,EACbC,gBAAgB,EAChBC,QAAQ,EACRC,YAAY,KAAK,EACjBC,KAAK,EACLC,MAAMC,gBAAgB,EACvB,GAAGhB;IAEJ,MAAMiB,OAAO;IAEb;;;GAGC,GACD,IAAIV,gBAAgB;QAClBjB,WAAW;QACX,MAAM4B,UAAU,MAAMrB,eAAe;YAACO;YAAKC;YAAOC;YAAOC;QAAc;QAEvEjB,WAAW;QACX,IAAI6B,aAAa,MAAMxB,cAAc;YACnCQ;YACAC;YACAc;YACAE;YACAd;YACAC;YACAC;YACAS;YACAR;YACAC;YACA,0EAA0E;YAC1E,0EAA0E;YAC1EC;YACAC;YACAC;YACAC;QACF;QAEA,8EAA8E;QAC9E,uEAAuE;QACvE,4EAA4E;QAC5E,+DAA+D;QAC/D,IAAIE,kBAAkB;YACpBG,aAAa,MAAM1B,+BACjB;gBAAC4B,SAAS;gBAASJ;YAAI,GACvBE,YACAH;QAEJ;QAEA1B,WAAW;QACX,MAAMgC,UAAU,MAAMlC,cAAc+B;QACpC,MAAMG,QAAQC,QAAQ;QACtBjC,WAAW;QACX,uBAAuB;QACvB,OAAO;YAACkC,QAAQ,EAAE;QAAA;IACpB;IAEAlC,WAAW;IACX,MAAM,EAAC4B,OAAO,EAAC,GAAG,MAAMpB,mBAAmB;QACzCG;QACAE;QACAC;QACAC;QACAC;QACAC;QACAkB,iBAAiB;QACjBC,OAAO;IACT;IAEA,SAASN;QACP,OAAOd,QACHf,2BAA2B;YAACoC,YAAY;YAAMC,QAAQ;QAAc,KACpEpC,8BAA8B;YAACmC,YAAY;YAAMC,QAAQ;QAAc;IAC7E;IAEAtC,WAAW;IACX,IAAI6B,aAAa,MAAMxB,cAAc;QACnCO;QACAC;QACAC;QACAc;QACAE;QACAd;QACAC;QACAC;QACAS;QACAR;QACAC;QACAC;QACAE;IACF;IAEA,IAAIG,kBAAkB;QACpB,MAAMa,oBAAoBV;QAC1BA,aAAa,MAAM1B,+BACjB;YAAC4B,SAAS;YAASJ;QAAI,GACvBE,YACAH;QAEFG,aAAa,MAAMzB,mBAAmByB,YAAYU;IACpD;IAEA,MAAMC,WAAW5C,KAAK6C,IAAI,CAAC3B,KAAK;IAChC,oDAAoD;IACpDd,WAAW,CAAC,0BAA0B,EAAEwC,SAAS,cAAc,CAAC;IAChE,MAAME,aAAa9C,KAAK6C,IAAI,CAACtB,WAAW;IACxC,MAAMpB,QAAQyC,UAAUE;IAExB,4EAA4E;IAC5E1C,WAAW;IACX,MAAM2C,kBAAkB,GAAG9B,SAAS+B,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC;IAChE,MAAMtC,cAAcqC,iBAAiBD;IAErC1C,WAAW;IACX,MAAM6C,SAAS,MAAMhD,MAAMgC;IAC3B7B,WAAW;IAEX,iFAAiF;IACjF,IAAI8C,MAAMC,OAAO,CAACF,WAAW,CAAE,CAAA,YAAYA,MAAK,GAAI;QAClD,OAAO;YAACX,QAAQ,EAAE;QAAA;IACpB;IAEA,MAAMc,QAAsB,EAAE;IAC9B,KAAK,MAAMC,SAASJ,OAAOK,MAAM,CAAE;QACjC,IAAID,MAAME,IAAI,KAAK,SAAS;YAC1B;QACF;QAEAH,MAAMI,IAAI,CAAC;YACTC,SAASC,OAAO1B,OAAO,CAACqB,MAAMI,OAAO,EAAEE,GAAG,CAAC,CAAC,CAACC,aAAaC,YAAY;gBACpE,MAAMC,WAAWF,YAAYG,UAAU,CAAC,YACpCH,YAAYI,KAAK,CAAC,SAASC,MAAM,IACjCL;gBAEJ,OAAO;oBACLM,MAAMlE,KAAKmE,UAAU,CAACL,YAAY9D,KAAKoE,QAAQ,CAAClD,KAAK4C,YAAYA;oBACjEO,gBAAgBR,YAAYQ,cAAc;gBAC5C;YACF;YACAH,MAAMb,MAAMa,IAAI;QAClB;IACF;IAEA,OAAO;QAAC5B,QAAQc;IAAK;AACvB"}
@@ -0,0 +1,54 @@
1
+ import { loadEnv } from 'vite';
2
+ const appEnvPrefix = 'SANITY_APP_';
3
+ const studioEnvPrefix = 'SANITY_STUDIO_';
4
+ /**
5
+ * Get environment variables prefixed with SANITY_STUDIO_, as an object.
6
+ *
7
+ * @param options - Options for the environment variable loading
8
+ * {@link StudioEnvVariablesOptions}
9
+ * @returns Object of studio environment variables
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * getStudioEnvironmentVariables({prefix: 'process.env.', jsonEncode: true})
14
+ * ```
15
+ *
16
+ * @public
17
+ */ export function getStudioEnvironmentVariables(options = {}) {
18
+ return getEnvironmentVariables({
19
+ ...options,
20
+ varTypePrefix: studioEnvPrefix
21
+ });
22
+ }
23
+ /**
24
+ * Get environment variables prefixed with SANITY_APP_, as an object.
25
+ *
26
+ * @param options - Options for the environment variable loading
27
+ * {@link StudioEnvVariablesOptions}
28
+ * @returns Object of app environment variables
29
+ *
30
+ * @internal
31
+ */ export function getAppEnvironmentVariables(options = {}) {
32
+ return getEnvironmentVariables({
33
+ ...options,
34
+ varTypePrefix: appEnvPrefix
35
+ });
36
+ }
37
+ function getEnvironmentVariables(options) {
38
+ const { envFile = false, jsonEncode = false, prefix = '', varTypePrefix } = options;
39
+ const fullEnv = envFile ? {
40
+ ...process.env,
41
+ ...loadEnv(envFile.mode, envFile.envDir || process.cwd(), [
42
+ varTypePrefix
43
+ ])
44
+ } : process.env;
45
+ const appEnv = {};
46
+ for(const key in fullEnv){
47
+ if (key.startsWith(varTypePrefix)) {
48
+ appEnv[`${prefix}${key}`] = jsonEncode ? JSON.stringify(fullEnv[key] || '') : fullEnv[key] || '';
49
+ }
50
+ }
51
+ return appEnv;
52
+ }
53
+
54
+ //# sourceMappingURL=getEnvironmentVariables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/build/getEnvironmentVariables.ts"],"sourcesContent":["import {loadEnv} from 'vite'\n\nconst appEnvPrefix = 'SANITY_APP_'\nconst studioEnvPrefix = 'SANITY_STUDIO_'\n\n/**\n * The params for the `getStudioEnvironmentVariables` and `getAppEnvironmentVariables` function\n * that gets Studio/App-focused environment variables.\n *\n * @public\n */\nexport interface StudioEnvVariablesOptions {\n /**\n * When specified includes environment variables from dotenv files (`.env`), in the same way the studio does.\n * A `mode` must be specified, usually `development`\n * or `production`, which will load the corresponding `.env.development` or `.env.production`.\n * To specify where to look for the dotenv files, specify `options.envFile.envDir`.\n */\n envFile?: false | {envDir?: string; mode: string}\n /**\n * When specified, JSON-encodes the values, which is handy if you want to pass\n * this to a bundlers hardcoded defines, such as Vite's `define` or Webpack's `DefinePlugin`.\n */\n jsonEncode?: boolean\n /**\n * When specified adds a prefix to the environment variable keys,\n * eg: `getStudioEnvironmentVariables({prefix: 'process.env.'})`\n */\n prefix?: string\n}\n\n/**\n * Get environment variables prefixed with SANITY_STUDIO_, as an object.\n *\n * @param options - Options for the environment variable loading\n * {@link StudioEnvVariablesOptions}\n * @returns Object of studio environment variables\n *\n * @example\n * ```tsx\n * getStudioEnvironmentVariables({prefix: 'process.env.', jsonEncode: true})\n * ```\n *\n * @public\n */\nexport function getStudioEnvironmentVariables(\n options: StudioEnvVariablesOptions = {},\n): Record<string, string> {\n return getEnvironmentVariables({...options, varTypePrefix: studioEnvPrefix})\n}\n\n/**\n * Get environment variables prefixed with SANITY_APP_, as an object.\n *\n * @param options - Options for the environment variable loading\n * {@link StudioEnvVariablesOptions}\n * @returns Object of app environment variables\n *\n * @internal\n */\nexport function getAppEnvironmentVariables(\n options: StudioEnvVariablesOptions = {},\n): Record<string, string> {\n return getEnvironmentVariables({...options, varTypePrefix: appEnvPrefix})\n}\n\nfunction getEnvironmentVariables(\n options: StudioEnvVariablesOptions & {varTypePrefix: string},\n): Record<string, string> {\n const {envFile = false, jsonEncode = false, prefix = '', varTypePrefix} = options\n const fullEnv = envFile\n ? {...process.env, ...loadEnv(envFile.mode, envFile.envDir || process.cwd(), [varTypePrefix])}\n : process.env\n\n const appEnv: Record<string, string> = {}\n for (const key in fullEnv) {\n if (key.startsWith(varTypePrefix)) {\n appEnv[`${prefix}${key}`] = jsonEncode\n ? JSON.stringify(fullEnv[key] || '')\n : fullEnv[key] || ''\n }\n }\n return appEnv\n}\n"],"names":["loadEnv","appEnvPrefix","studioEnvPrefix","getStudioEnvironmentVariables","options","getEnvironmentVariables","varTypePrefix","getAppEnvironmentVariables","envFile","jsonEncode","prefix","fullEnv","process","env","mode","envDir","cwd","appEnv","key","startsWith","JSON","stringify"],"mappings":"AAAA,SAAQA,OAAO,QAAO,OAAM;AAE5B,MAAMC,eAAe;AACrB,MAAMC,kBAAkB;AA4BxB;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASC,8BACdC,UAAqC,CAAC,CAAC;IAEvC,OAAOC,wBAAwB;QAAC,GAAGD,OAAO;QAAEE,eAAeJ;IAAe;AAC5E;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASK,2BACdH,UAAqC,CAAC,CAAC;IAEvC,OAAOC,wBAAwB;QAAC,GAAGD,OAAO;QAAEE,eAAeL;IAAY;AACzE;AAEA,SAASI,wBACPD,OAA4D;IAE5D,MAAM,EAACI,UAAU,KAAK,EAAEC,aAAa,KAAK,EAAEC,SAAS,EAAE,EAAEJ,aAAa,EAAC,GAAGF;IAC1E,MAAMO,UAAUH,UACZ;QAAC,GAAGI,QAAQC,GAAG;QAAE,GAAGb,QAAQQ,QAAQM,IAAI,EAAEN,QAAQO,MAAM,IAAIH,QAAQI,GAAG,IAAI;YAACV;SAAc,CAAC;IAAA,IAC3FM,QAAQC,GAAG;IAEf,MAAMI,SAAiC,CAAC;IACxC,IAAK,MAAMC,OAAOP,QAAS;QACzB,IAAIO,IAAIC,UAAU,CAACb,gBAAgB;YACjCW,MAAM,CAAC,GAAGP,SAASQ,KAAK,CAAC,GAAGT,aACxBW,KAAKC,SAAS,CAACV,OAAO,CAACO,IAAI,IAAI,MAC/BP,OAAO,CAACO,IAAI,IAAI;QACtB;IACF;IACA,OAAOD;AACT"}
@@ -23,7 +23,7 @@ import { parse as parseHtml } from 'node-html-parser';
23
23
  function replaceTimestamp(urlStr) {
24
24
  try {
25
25
  const url = new URL(urlStr);
26
- if (isSanityCdnUrl(urlStr)) {
26
+ if (/^sanity-cdn\\.[a-zA-Z]+$/.test(url.hostname)) {
27
27
  url.pathname = url.pathname.replace(/\\/t\\d+/, newTimestamp);
28
28
  }
29
29
  return url.toString();
@@ -32,14 +32,6 @@ import { parse as parseHtml } from 'node-html-parser';
32
32
  }
33
33
  }
34
34
 
35
- function isSanityCdnUrl(urlStr) {
36
- try {
37
- return /^sanity-cdn\\.[a-zA-Z]+$/.test(new URL(urlStr).hostname);
38
- } catch {
39
- return false;
40
- }
41
- }
42
-
43
35
  importMapEl.textContent = JSON.stringify({
44
36
  imports: Object.fromEntries(
45
37
  Object.entries(imports).map(([specifier, path]) => [specifier, replaceTimestamp(path)])
@@ -55,34 +47,6 @@ import { parse as parseHtml } from 'node-html-parser';
55
47
  linkEl.href = replaceTimestamp(cssUrl);
56
48
  document.head.appendChild(linkEl);
57
49
  }
58
-
59
- // Warm the CDN module connection during head parse so the cross-origin
60
- // \`sanity\` fetch can start before the entry script discovers the import.
61
- const firstCdnImport = Object.values(imports).find(isSanityCdnUrl);
62
-
63
- if (firstCdnImport) {
64
- const preconnectEl = document.createElement('link');
65
- preconnectEl.rel = 'preconnect';
66
- preconnectEl.href = new URL(firstCdnImport).origin;
67
- // Module fetches are CORS; without crossorigin the warmed socket is not reused.
68
- preconnectEl.crossOrigin = 'anonymous';
69
- document.head.appendChild(preconnectEl);
70
- }
71
-
72
- // Start downloading the timestamped \`sanity\` module during head parse. The
73
- // href reuses replaceTimestamp so it matches the importmap entry exactly —
74
- // a stale timestamp here would double-fetch the largest chunk.
75
- const sanityModuleUrl = imports['sanity'];
76
- if (typeof sanityModuleUrl === 'string' && isSanityCdnUrl(sanityModuleUrl)) {
77
- const preloadEl = document.createElement('link');
78
- preloadEl.rel = 'modulepreload';
79
- preloadEl.href = replaceTimestamp(sanityModuleUrl);
80
- // Must match the preconnect's credentials mode, otherwise the browser
81
- // treats them as separate connections and the warmed cross-origin socket
82
- // is not reused for this fetch.
83
- preloadEl.crossOrigin = 'anonymous';
84
- document.head.appendChild(preloadEl);
85
- }
86
50
  </script>`;
87
51
  /**
88
52
  * @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 (isSanityCdnUrl(urlStr)) {\n url.pathname = url.pathname.replace(/\\\\/t\\\\d+/, newTimestamp);\n }\n return url.toString();\n } catch {\n return urlStr;\n }\n }\n\n function isSanityCdnUrl(urlStr) {\n try {\n return /^sanity-cdn\\\\.[a-zA-Z]+$/.test(new URL(urlStr).hostname);\n } catch {\n return false;\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 // Warm the CDN module connection during head parse so the cross-origin\n // \\`sanity\\` fetch can start before the entry script discovers the import.\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 // Start downloading the timestamped \\`sanity\\` module during head parse. The\n // 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 (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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAuEtC,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
+ {"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 (/^sanity-cdn\\\\.[a-zA-Z]+$/.test(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</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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;SAmCtC,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"}
@@ -39,7 +39,10 @@ import { renderDocument } from './renderDocument.js';
39
39
  }
40
40
  let watcher;
41
41
  if (watch) {
42
- watcher = chokidarWatch(getPossibleDocumentComponentLocations(cwd)).on('all', ()=>renderAndWriteDocument());
42
+ // Skip the initial scan; the explicit renderAndWriteDocument() below handles first generation.
43
+ watcher = chokidarWatch(getPossibleDocumentComponentLocations(cwd), {
44
+ ignoreInitial: true
45
+ }).on('all', ()=>renderAndWriteDocument());
43
46
  }
44
47
  await renderAndWriteDocument();
45
48
  buildDebug('Writing app.js to runtime directory');
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/build/writeSanityRuntime.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {tryFindStudioConfigPath} from '@sanity/cli-core'\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 fs.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 fs.writeFile(path.join(runtimeDir, 'index.html'), indexHtml)\n }\n\n let watcher: FSWatcher | undefined\n\n if (watch) {\n watcher = chokidarWatch(getPossibleDocumentComponentLocations(cwd)).on('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 fs.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":["fs","path","tryFindStudioConfigPath","watch","chokidarWatch","buildDebug","decorateIndexWithAutoGeneratedWarning","decorateIndexWithBridgeScript","decorateIndexWithStagingScript","getEntryModule","getPossibleDocumentComponentLocations","renderDocument","writeSanityRuntime","options","appTitle","basePath","cwd","entry","isApp","isWorkbenchApp","reactStrictMode","runtimeDir","join","mkdir","recursive","renderAndWriteDocument","indexHtml","props","entryPath","toForwardSlashes","relative","title","studioRootPath","writeFile","watcher","on","relativeConfigLocation","relativeEntry","resolveEntries","appJsContent","undefined","entries","studioConfigPath","resolve","filePath","replaceAll"],"mappings":"AAAA,OAAOA,QAAQ,mBAAkB;AACjC,OAAOC,UAAU,YAAW;AAE5B,SAAQC,uBAAuB,QAAO,mBAAkB;AACxD,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,MAAML,GAAGuB,KAAK,CAACF,YAAY;QAACG,WAAW;IAAI;IAE3C,eAAeC;QACbpB,WAAW;QACX,MAAMqB,YAAYlB,+BAChBD,8BACED,sCACE,MAAMK,eAAe;YACnBO;YACAS,OAAO;gBACLZ,UAAUA,YAAY;gBACtBa,WAAW,CAAC,CAAC,EAAEC,iBAAiB5B,KAAK6B,QAAQ,CAACd,KAAKf,KAAKqB,IAAI,CAACD,YAAY,aAAa;gBACtFU,OAAOjB;YACT;YACAkB,gBAAgBhB;QAClB;QAKNX,WAAW;QACX,MAAML,GAAGiC,SAAS,CAAChC,KAAKqB,IAAI,CAACD,YAAY,eAAeK;IAC1D;IAEA,IAAIQ;IAEJ,IAAI/B,OAAO;QACT+B,UAAU9B,cAAcM,sCAAsCM,MAAMmB,EAAE,CAAC,OAAO,IAC5EV;IAEJ;IAEA,MAAMA;IAENpB,WAAW;IACX,MAAM,EAAC+B,sBAAsB,EAAEC,aAAa,EAAC,GAAG,MAAMC,eAAe;QACnEtB;QACAC;QACAC;QACAC;QACAE;IACF;IACA,MAAMkB,eAAe9B,eAAe;QAClCM;QACAE,OAAOoB,iBAAiBG;QACxBtB;QACAE;QACAgB;IACF;IACA,MAAMpC,GAAGiC,SAAS,CAAChC,KAAKqB,IAAI,CAACD,YAAY,WAAWkB;IAEpD,OAAO;QACLE,SAAS;YACPL;YACAC;QACF;QACAH;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,eAAeI,eAAezB,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,IAAIoB,yBAAwC;IAC5C,IAAI,CAAClB,OAAO;QACV,MAAMwB,mBAAmB,MAAMxC,wBAAwBc;QACvDoB,yBAAyBM,mBACrBb,iBAAiB5B,KAAK6B,QAAQ,CAACT,YAAYqB,qBAC3C;IACN;IAEA,8EAA8E;IAC9E,4EAA4E;IAC5E,6EAA6E;IAC7E,+EAA+E;IAC/E,+EAA+E;IAC/E,2CAA2C;IAC3C,MAAML,gBACJlB,kBAAkB,CAACF,QACf,OACAY,iBAAiB5B,KAAK6B,QAAQ,CAACT,YAAYpB,KAAK0C,OAAO,CAAC3B,KAAKC,SAAS;IAE5E,OAAO;QAACmB;QAAwBC;IAAa;AAC/C;AAEA,SAASR,iBAAiBe,QAAgB;IACxC,OAAOA,SAASC,UAAU,CAAC,MAAM;AACnC"}
1
+ {"version":3,"sources":["../../../src/actions/build/writeSanityRuntime.ts"],"sourcesContent":["import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nimport {tryFindStudioConfigPath} from '@sanity/cli-core'\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 fs.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 fs.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 fs.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":["fs","path","tryFindStudioConfigPath","watch","chokidarWatch","buildDebug","decorateIndexWithAutoGeneratedWarning","decorateIndexWithBridgeScript","decorateIndexWithStagingScript","getEntryModule","getPossibleDocumentComponentLocations","renderDocument","writeSanityRuntime","options","appTitle","basePath","cwd","entry","isApp","isWorkbenchApp","reactStrictMode","runtimeDir","join","mkdir","recursive","renderAndWriteDocument","indexHtml","props","entryPath","toForwardSlashes","relative","title","studioRootPath","writeFile","watcher","ignoreInitial","on","relativeConfigLocation","relativeEntry","resolveEntries","appJsContent","undefined","entries","studioConfigPath","resolve","filePath","replaceAll"],"mappings":"AAAA,OAAOA,QAAQ,mBAAkB;AACjC,OAAOC,UAAU,YAAW;AAE5B,SAAQC,uBAAuB,QAAO,mBAAkB;AACxD,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,MAAML,GAAGuB,KAAK,CAACF,YAAY;QAACG,WAAW;IAAI;IAE3C,eAAeC;QACbpB,WAAW;QACX,MAAMqB,YAAYlB,+BAChBD,8BACED,sCACE,MAAMK,eAAe;YACnBO;YACAS,OAAO;gBACLZ,UAAUA,YAAY;gBACtBa,WAAW,CAAC,CAAC,EAAEC,iBAAiB5B,KAAK6B,QAAQ,CAACd,KAAKf,KAAKqB,IAAI,CAACD,YAAY,aAAa;gBACtFU,OAAOjB;YACT;YACAkB,gBAAgBhB;QAClB;QAKNX,WAAW;QACX,MAAML,GAAGiC,SAAS,CAAChC,KAAKqB,IAAI,CAACD,YAAY,eAAeK;IAC1D;IAEA,IAAIQ;IAEJ,IAAI/B,OAAO;QACT,+FAA+F;QAC/F+B,UAAU9B,cAAcM,sCAAsCM,MAAM;YAACmB,eAAe;QAAI,GAAGC,EAAE,CAC3F,OACA,IAAMX;IAEV;IAEA,MAAMA;IAENpB,WAAW;IACX,MAAM,EAACgC,sBAAsB,EAAEC,aAAa,EAAC,GAAG,MAAMC,eAAe;QACnEvB;QACAC;QACAC;QACAC;QACAE;IACF;IACA,MAAMmB,eAAe/B,eAAe;QAClCM;QACAE,OAAOqB,iBAAiBG;QACxBvB;QACAE;QACAiB;IACF;IACA,MAAMrC,GAAGiC,SAAS,CAAChC,KAAKqB,IAAI,CAACD,YAAY,WAAWmB;IAEpD,OAAO;QACLE,SAAS;YACPL;YACAC;QACF;QACAJ;IACF;AACF;AAEA;;;;CAIC,GACD,OAAO,eAAeK,eAAe1B,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,IAAIqB,yBAAwC;IAC5C,IAAI,CAACnB,OAAO;QACV,MAAMyB,mBAAmB,MAAMzC,wBAAwBc;QACvDqB,yBAAyBM,mBACrBd,iBAAiB5B,KAAK6B,QAAQ,CAACT,YAAYsB,qBAC3C;IACN;IAEA,8EAA8E;IAC9E,4EAA4E;IAC5E,6EAA6E;IAC7E,+EAA+E;IAC/E,+EAA+E;IAC/E,2CAA2C;IAC3C,MAAML,gBACJnB,kBAAkB,CAACF,QACf,OACAY,iBAAiB5B,KAAK6B,QAAQ,CAACT,YAAYpB,KAAK2C,OAAO,CAAC5B,KAAKC,SAAS;IAE5E,OAAO;QAACoB;QAAwBC;IAAa;AAC/C;AAEA,SAAST,iBAAiBgB,QAAgB;IACxC,OAAOA,SAASC,UAAU,CAAC,MAAM;AACnC"}
@@ -0,0 +1,10 @@
1
+ import { styleText } from 'node:util';
2
+ /**
3
+ * Formats bytes to kB
4
+ *
5
+ * @internal
6
+ */ export function formatSize(bytes) {
7
+ return styleText('cyan', `${(bytes / 1024).toFixed(0)} kB`);
8
+ }
9
+
10
+ //# sourceMappingURL=formatSize.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/formatSize.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\n/**\n * Formats bytes to kB\n *\n * @internal\n */\nexport function formatSize(bytes: number): string {\n return styleText('cyan', `${(bytes / 1024).toFixed(0)} kB`)\n}\n"],"names":["styleText","formatSize","bytes","toFixed"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC;;;;CAIC,GACD,OAAO,SAASC,WAAWC,KAAa;IACtC,OAAOF,UAAU,QAAQ,GAAG,AAACE,CAAAA,QAAQ,IAAG,EAAGC,OAAO,CAAC,GAAG,GAAG,CAAC;AAC5D"}
@@ -0,0 +1,18 @@
1
+ import { formatSize } from './formatSize.js';
2
+ export function formatModuleSizes(modules) {
3
+ const lines = [];
4
+ for (const mod of modules){
5
+ lines.push(` - ${formatModuleName(mod.name)} (${formatSize(mod.renderedLength)})`);
6
+ }
7
+ return lines.join('\n');
8
+ }
9
+ function formatModuleName(modName) {
10
+ const delimiter = '/node_modules/';
11
+ const nodeIndex = modName.lastIndexOf(delimiter);
12
+ return nodeIndex === -1 ? modName : modName.slice(nodeIndex + delimiter.length);
13
+ }
14
+ export function sortModulesBySize(chunks) {
15
+ return chunks.flatMap((chunk)=>chunk.modules).toSorted((modA, modB)=>modB.renderedLength - modA.renderedLength);
16
+ }
17
+
18
+ //# sourceMappingURL=moduleFormatUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/moduleFormatUtils.ts"],"sourcesContent":["import {type ChunkModule, type ChunkStats} from '../actions/build/buildStaticFiles.js'\nimport {formatSize} from './formatSize.js'\n\nexport function formatModuleSizes(modules: ChunkModule[]): string {\n const lines: string[] = []\n for (const mod of modules) {\n lines.push(` - ${formatModuleName(mod.name)} (${formatSize(mod.renderedLength)})`)\n }\n\n return lines.join('\\n')\n}\n\nfunction formatModuleName(modName: string): string {\n const delimiter = '/node_modules/'\n const nodeIndex = modName.lastIndexOf(delimiter)\n return nodeIndex === -1 ? modName : modName.slice(nodeIndex + delimiter.length)\n}\n\nexport function sortModulesBySize(chunks: ChunkStats[]): ChunkModule[] {\n return chunks\n .flatMap((chunk) => chunk.modules)\n .toSorted((modA, modB) => modB.renderedLength - modA.renderedLength)\n}\n"],"names":["formatSize","formatModuleSizes","modules","lines","mod","push","formatModuleName","name","renderedLength","join","modName","delimiter","nodeIndex","lastIndexOf","slice","length","sortModulesBySize","chunks","flatMap","chunk","toSorted","modA","modB"],"mappings":"AACA,SAAQA,UAAU,QAAO,kBAAiB;AAE1C,OAAO,SAASC,kBAAkBC,OAAsB;IACtD,MAAMC,QAAkB,EAAE;IAC1B,KAAK,MAAMC,OAAOF,QAAS;QACzBC,MAAME,IAAI,CAAC,CAAC,GAAG,EAAEC,iBAAiBF,IAAIG,IAAI,EAAE,EAAE,EAAEP,WAAWI,IAAII,cAAc,EAAE,CAAC,CAAC;IACnF;IAEA,OAAOL,MAAMM,IAAI,CAAC;AACpB;AAEA,SAASH,iBAAiBI,OAAe;IACvC,MAAMC,YAAY;IAClB,MAAMC,YAAYF,QAAQG,WAAW,CAACF;IACtC,OAAOC,cAAc,CAAC,IAAIF,UAAUA,QAAQI,KAAK,CAACF,YAAYD,UAAUI,MAAM;AAChF;AAEA,OAAO,SAASC,kBAAkBC,MAAoB;IACpD,OAAOA,OACJC,OAAO,CAAC,CAACC,QAAUA,MAAMjB,OAAO,EAChCkB,QAAQ,CAAC,CAACC,MAAMC,OAASA,KAAKd,cAAc,GAAGa,KAAKb,cAAc;AACvE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/cli-build",
3
- "version": "1.1.1",
3
+ "version": "2.0.0",
4
4
  "description": "Internal Sanity package for building studios and apps",
5
5
  "keywords": [
6
6
  "cli",
@@ -32,6 +32,10 @@
32
32
  "source": "./src/_exports/_internal/build.ts",
33
33
  "default": "./dist/_exports/_internal/build.js"
34
34
  },
35
+ "./_internal/env": {
36
+ "source": "./src/_exports/_internal/env.ts",
37
+ "default": "./dist/_exports/_internal/env.js"
38
+ },
35
39
  "./_internal/extract": {
36
40
  "source": "./src/_exports/_internal/extract.ts",
37
41
  "default": "./dist/_exports/_internal/extract.js"
@@ -60,10 +64,10 @@
60
64
  "react-dom": "^19.2.7",
61
65
  "read-package-up": "^12.0.0",
62
66
  "semver": "^7.8.1",
63
- "vite": "^8.1.0",
67
+ "vite": "8.1.0",
64
68
  "zod": "^4.4.3",
65
- "@sanity/cli-core": "^2.1.1",
66
- "@sanity/workbench-cli": "^1.1.0"
69
+ "@sanity/cli-core": "^2.1.2",
70
+ "@sanity/workbench-cli": "^1.1.2"
67
71
  },
68
72
  "devDependencies": {
69
73
  "@eslint/compat": "^2.1.0",
@@ -90,7 +94,7 @@
90
94
  "vitest": "^4.1.9",
91
95
  "@repo/package.config": "0.0.1",
92
96
  "@repo/tsconfig": "3.70.0",
93
- "@sanity/cli-test": "2.0.1",
97
+ "@sanity/cli-test": "2.0.2",
94
98
  "@sanity/eslint-config-cli": "^1.1.2"
95
99
  },
96
100
  "peerDependencies": {