@sanity/cli-build 0.1.1 → 0.2.1

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.
Files changed (90) hide show
  1. package/dist/_exports/_internal/build.d.ts +2418 -0
  2. package/dist/_exports/_internal/build.js +10 -0
  3. package/dist/_exports/_internal/build.js.map +1 -0
  4. package/dist/_exports/_internal/extract.d.ts +120 -0
  5. package/dist/_exports/_internal/extract.js +9 -0
  6. package/dist/_exports/_internal/extract.js.map +1 -0
  7. package/dist/actions/build/buildDebug.js +4 -0
  8. package/dist/actions/build/buildDebug.js.map +1 -0
  9. package/dist/actions/build/buildVendorDependencies.js +149 -0
  10. package/dist/actions/build/buildVendorDependencies.js.map +1 -0
  11. package/dist/actions/build/checkStudioDependencyVersions.js +155 -0
  12. package/dist/actions/build/checkStudioDependencyVersions.js.map +1 -0
  13. package/dist/actions/build/createExternalFromImportMap.js +11 -0
  14. package/dist/actions/build/createExternalFromImportMap.js.map +1 -0
  15. package/dist/actions/build/decorateIndexWithAutoGeneratedWarning.js +13 -0
  16. package/dist/actions/build/decorateIndexWithAutoGeneratedWarning.js.map +1 -0
  17. package/dist/actions/build/decorateIndexWithBridgeScript.js +17 -0
  18. package/dist/actions/build/decorateIndexWithBridgeScript.js.map +1 -0
  19. package/dist/actions/build/decorateIndexWithStagingScript.js +16 -0
  20. package/dist/actions/build/decorateIndexWithStagingScript.js.map +1 -0
  21. package/dist/actions/build/getEntryModule.js +46 -0
  22. package/dist/actions/build/getEntryModule.js.map +1 -0
  23. package/dist/actions/build/getPossibleDocumentComponentLocations.js +11 -0
  24. package/dist/actions/build/getPossibleDocumentComponentLocations.js.map +1 -0
  25. package/dist/actions/build/getViteConfig.js +204 -0
  26. package/dist/actions/build/getViteConfig.js.map +1 -0
  27. package/dist/actions/build/normalizeBasePath.js +9 -0
  28. package/dist/actions/build/normalizeBasePath.js.map +1 -0
  29. package/dist/actions/build/renderDocument.js +50 -0
  30. package/dist/actions/build/renderDocument.js.map +1 -0
  31. package/dist/actions/build/renderDocument.worker.js +9 -0
  32. package/dist/actions/build/renderDocument.worker.js.map +1 -0
  33. package/dist/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.js +79 -0
  34. package/dist/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.js.map +1 -0
  35. package/dist/actions/build/renderDocumentWorker/components/BasicDocument.js +61 -0
  36. package/dist/actions/build/renderDocumentWorker/components/BasicDocument.js.map +1 -0
  37. package/dist/actions/build/renderDocumentWorker/components/DefaultDocument.js +165 -0
  38. package/dist/actions/build/renderDocumentWorker/components/DefaultDocument.js.map +1 -0
  39. package/dist/actions/build/renderDocumentWorker/components/Favicons.js +28 -0
  40. package/dist/actions/build/renderDocumentWorker/components/Favicons.js.map +1 -0
  41. package/dist/actions/build/renderDocumentWorker/components/GlobalErrorHandler.js +178 -0
  42. package/dist/actions/build/renderDocumentWorker/components/GlobalErrorHandler.js.map +1 -0
  43. package/dist/actions/build/renderDocumentWorker/components/NoJavascript.js +51 -0
  44. package/dist/actions/build/renderDocumentWorker/components/NoJavascript.js.map +1 -0
  45. package/dist/actions/build/renderDocumentWorker/getDocumentComponent.js +41 -0
  46. package/dist/actions/build/renderDocumentWorker/getDocumentComponent.js.map +1 -0
  47. package/dist/actions/build/renderDocumentWorker/getDocumentHtml.js +55 -0
  48. package/dist/actions/build/renderDocumentWorker/getDocumentHtml.js.map +1 -0
  49. package/dist/actions/build/renderDocumentWorker/renderDocumentWorker.js +31 -0
  50. package/dist/actions/build/renderDocumentWorker/renderDocumentWorker.js.map +1 -0
  51. package/dist/actions/build/renderDocumentWorker/tryLoadDocumentComponent.js +30 -0
  52. package/dist/actions/build/renderDocumentWorker/tryLoadDocumentComponent.js.map +1 -0
  53. package/dist/actions/build/renderDocumentWorker/types.js +5 -0
  54. package/dist/actions/build/renderDocumentWorker/types.js.map +1 -0
  55. package/dist/actions/build/vite/plugin-sanity-build-entries.js +67 -0
  56. package/dist/actions/build/vite/plugin-sanity-build-entries.js.map +1 -0
  57. package/dist/actions/build/vite/plugin-sanity-favicons.js +72 -0
  58. package/dist/actions/build/vite/plugin-sanity-favicons.js.map +1 -0
  59. package/dist/actions/build/vite/plugin-sanity-runtime-rewrite.js +18 -0
  60. package/dist/actions/build/vite/plugin-sanity-runtime-rewrite.js.map +1 -0
  61. package/dist/actions/build/writeSanityRuntime.js +66 -0
  62. package/dist/actions/build/writeSanityRuntime.js.map +1 -0
  63. package/dist/actions/schema/extractSanitySchema.worker.js +32 -0
  64. package/dist/actions/schema/extractSanitySchema.worker.js.map +1 -0
  65. package/dist/actions/schema/formatSchemaValidation.js +78 -0
  66. package/dist/actions/schema/formatSchemaValidation.js.map +1 -0
  67. package/dist/actions/schema/getExtractOptions.js +23 -0
  68. package/dist/actions/schema/getExtractOptions.js.map +1 -0
  69. package/dist/actions/schema/matchSchemaPattern.js +21 -0
  70. package/dist/actions/schema/matchSchemaPattern.js.map +1 -0
  71. package/dist/actions/schema/runSchemaExtraction.js +39 -0
  72. package/dist/actions/schema/runSchemaExtraction.js.map +1 -0
  73. package/dist/actions/schema/types.js +9 -0
  74. package/dist/actions/schema/types.js.map +1 -0
  75. package/dist/actions/schema/utils/SchemaExtractionError.js +10 -0
  76. package/dist/actions/schema/utils/SchemaExtractionError.js.map +1 -0
  77. package/dist/actions/schema/utils/extractValidationFromSchemaError.js +12 -0
  78. package/dist/actions/schema/utils/extractValidationFromSchemaError.js.map +1 -0
  79. package/dist/actions/schema/vite/plugin-schema-extraction.js +201 -0
  80. package/dist/actions/schema/vite/plugin-schema-extraction.js.map +1 -0
  81. package/dist/constants.js +8 -0
  82. package/dist/constants.js.map +1 -0
  83. package/dist/telemetry/build.telemetry.js +13 -0
  84. package/dist/telemetry/build.telemetry.js.map +1 -0
  85. package/dist/telemetry/extractSchema.telemetry.js +18 -0
  86. package/dist/telemetry/extractSchema.telemetry.js.map +1 -0
  87. package/package.json +38 -9
  88. package/dist/_exports/_internal.d.ts +0 -45
  89. package/dist/_exports/_internal.js +0 -5
  90. package/dist/_exports/_internal.js.map +0 -1
@@ -0,0 +1,201 @@
1
+ import path, { isAbsolute } from 'node:path';
2
+ import { logSymbols } from '@sanity/cli-core/ux';
3
+ import debounce from 'lodash-es/debounce.js';
4
+ import mean from 'lodash-es/mean.js';
5
+ import once from 'lodash-es/once.js';
6
+ import { SchemaExtractedTrace, SchemaExtractionWatchModeTrace } from '../../../telemetry/extractSchema.telemetry.js';
7
+ import { formatSchemaValidation } from '../formatSchemaValidation.js';
8
+ import { createSchemaPatternMatcher } from '../matchSchemaPattern.js';
9
+ import { runSchemaExtraction } from '../runSchemaExtraction.js';
10
+ import { SchemaExtractionError } from '../utils/SchemaExtractionError.js';
11
+ /**
12
+ * Default glob patterns to watch for schema changes.
13
+ * Covers the sanity config file and common schema directory naming conventions.
14
+ */ const DEFAULT_SCHEMA_PATTERNS = [
15
+ 'sanity.config.{js,jsx,ts,tsx,mjs}',
16
+ 'schema*/**/*.{js,jsx,ts,tsx,mjs}'
17
+ ];
18
+ /** Default debounce delay in milliseconds */ const DEFAULT_DEBOUNCE_MS = 1000;
19
+ /**
20
+ * Delay before initial extraction to allow Vite to finish startup
21
+ * and avoid race conditions with module resolution.
22
+ */ const INITIAL_EXTRACTION_DELAY_MS = 1000;
23
+ /**
24
+ * Creates a Vite plugin that automatically extracts Sanity schema during development and build.
25
+ *
26
+ * **During development:**
27
+ * The plugin performs an initial extraction when the dev server starts, then watches
28
+ * for file changes and re-extracts the schema when relevant files are modified.
29
+ *
30
+ * **During build:**
31
+ * The plugin extracts the schema once at the end of the build process, ensuring
32
+ * the schema is always up-to-date when deploying.
33
+ *
34
+ * **How it works in dev mode:**
35
+ * 1. Registers watch patterns with Vite's built-in file watcher
36
+ * 2. Performs initial schema extraction when the server starts
37
+ * 3. On file changes matching the patterns, triggers a debounced extraction
38
+ * 4. Uses concurrency control to prevent overlapping extractions
39
+ *
40
+ * @param options - Configuration options for the plugin
41
+ * @returns A Vite plugin configured for schema extraction
42
+ *
43
+ * @internal
44
+ */ export function sanitySchemaExtractionPlugin(options) {
45
+ const { additionalPatterns = [], configPath, debounceMs = DEFAULT_DEBOUNCE_MS, enforceRequiredFields = false, format = 'groq-type-nodes', output = console, outputPath: outputPathOption = 'schema.json', telemetryLogger, workDir: workDirOption, workspaceName } = options;
46
+ const watchPatterns = [
47
+ ...DEFAULT_SCHEMA_PATTERNS,
48
+ ...additionalPatterns
49
+ ];
50
+ // Resolved after Vite config is available
51
+ let resolvedWorkDir;
52
+ let resolvedOutputPath;
53
+ // State for concurrency control
54
+ let isExtracting = false;
55
+ let pendingExtraction = false;
56
+ // Stats for telemetry
57
+ const startTime = Date.now();
58
+ const stats = {
59
+ failedCount: 0,
60
+ successfulDurations: []
61
+ };
62
+ const extractSchema = ()=>runSchemaExtraction({
63
+ configPath,
64
+ enforceRequiredFields,
65
+ format,
66
+ outputPath: resolvedOutputPath,
67
+ workspace: workspaceName
68
+ });
69
+ /**
70
+ * Runs extraction with concurrency control.
71
+ * If extraction is already running, queues one more extraction to run after completion.
72
+ */ async function runExtraction(isBuilding = false) {
73
+ if (isExtracting) {
74
+ pendingExtraction = true;
75
+ return;
76
+ }
77
+ isExtracting = true;
78
+ pendingExtraction = false;
79
+ const extractionStartTime = Date.now();
80
+ try {
81
+ await extractSchema();
82
+ if (isBuilding) {
83
+ // TODO: Remove when we have better control over progress reporting in build
84
+ output.log('');
85
+ }
86
+ output.log(logSymbols.success, `Extracted schema to ${outputPathOption}`);
87
+ // add stats for the successful extraction run to use later for telemetry
88
+ stats.successfulDurations.push(Date.now() - extractionStartTime);
89
+ } catch (err) {
90
+ output.error(logSymbols.error, `Extraction failed: ${err instanceof Error ? err.message : String(err)}`);
91
+ if (err instanceof SchemaExtractionError && err.validation && err.validation.length > 0) {
92
+ output.error(logSymbols.error, formatSchemaValidation(err.validation));
93
+ }
94
+ // track the failed extraction
95
+ stats.failedCount++;
96
+ } finally{
97
+ isExtracting = false;
98
+ // If a change came in during extraction, run again
99
+ if (pendingExtraction) {
100
+ pendingExtraction = false;
101
+ await runExtraction();
102
+ }
103
+ }
104
+ }
105
+ const debouncedExtract = debounce(()=>{
106
+ void runExtraction();
107
+ }, debounceMs);
108
+ const { isMatch } = createSchemaPatternMatcher(watchPatterns);
109
+ const handleChange = (filePath)=>{
110
+ if (isMatch(filePath, resolvedWorkDir)) {
111
+ debouncedExtract();
112
+ }
113
+ };
114
+ return {
115
+ name: 'sanity/schema-extraction',
116
+ configResolved (config) {
117
+ // Resolve workDir from option or Vite's project root
118
+ resolvedWorkDir = workDirOption ?? config.root;
119
+ resolvedOutputPath = isAbsolute(outputPathOption) ? outputPathOption : path.join(resolvedWorkDir, outputPathOption);
120
+ },
121
+ configureServer (server) {
122
+ const trace = telemetryLogger?.trace(SchemaExtractionWatchModeTrace);
123
+ trace?.start();
124
+ trace?.log({
125
+ enforceRequiredFields,
126
+ schemaFormat: format,
127
+ step: 'started'
128
+ });
129
+ // Add schema patterns to Vite's watcher
130
+ const absolutePatterns = watchPatterns.map((pattern)=>path.join(resolvedWorkDir, pattern));
131
+ server.watcher.add(absolutePatterns);
132
+ // Prepare function to log "stopped" event to trace and complete the trace
133
+ const onClose = once(()=>{
134
+ // Cancel any pending debounced extractions
135
+ debouncedExtract.cancel();
136
+ // Log telemetry if available
137
+ if (trace) {
138
+ trace.log({
139
+ averageExtractionDuration: mean(stats.successfulDurations) || 0,
140
+ extractionFailedCount: stats.failedCount,
141
+ extractionSuccessfulCount: stats.successfulDurations.length,
142
+ step: 'stopped',
143
+ watcherDuration: Date.now() - startTime
144
+ });
145
+ trace.complete();
146
+ }
147
+ // Clean up process listeners (must always run, not just when trace exists)
148
+ process.off('SIGTERM', onClose);
149
+ process.off('SIGINT', onClose);
150
+ });
151
+ server.watcher.on('change', handleChange);
152
+ server.watcher.on('add', handleChange);
153
+ server.watcher.on('unlink', handleChange);
154
+ // call the watcherClosed method when watcher is closed or when process is stopped/killed
155
+ server.watcher.on('close', onClose);
156
+ process.on('SIGTERM', onClose);
157
+ process.on('SIGINT', onClose);
158
+ // Run initial extraction after server is ready
159
+ const startExtraction = ()=>{
160
+ setTimeout(()=>{
161
+ // Notify about schema extraction enabled
162
+ output.info(logSymbols.info, 'Schema extraction enabled. Watching:');
163
+ for (const pattern of watchPatterns){
164
+ output.info(` - ${pattern}`);
165
+ }
166
+ // Perform first extraction
167
+ void runExtraction();
168
+ }, INITIAL_EXTRACTION_DELAY_MS);
169
+ };
170
+ if (server.httpServer) {
171
+ server.httpServer.once('listening', startExtraction);
172
+ } else {
173
+ // Middleware mode - no HTTP server, run extraction immediately
174
+ startExtraction();
175
+ }
176
+ },
177
+ async buildEnd () {
178
+ const trace = telemetryLogger?.trace(SchemaExtractedTrace);
179
+ trace?.start();
180
+ try {
181
+ const start = Date.now();
182
+ const schema = await extractSchema();
183
+ output.log(logSymbols.success, `Extracted schema to ${outputPathOption} (${Date.now() - start}ms)`);
184
+ trace?.log({
185
+ enforceRequiredFields,
186
+ schemaAllTypesCount: schema.length,
187
+ schemaDocumentTypesCount: schema.filter((type)=>type.type === 'document').length,
188
+ schemaFormat: format,
189
+ schemaTypesCount: schema.filter((type)=>type.type === 'type').length
190
+ });
191
+ } catch (err) {
192
+ trace?.error(err);
193
+ throw err;
194
+ } finally{
195
+ trace?.complete();
196
+ }
197
+ }
198
+ };
199
+ }
200
+
201
+ //# sourceMappingURL=plugin-schema-extraction.js.map
@@ -0,0 +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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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Root directory (relative to the project) used by Sanity tooling for
3
+ * build-time artifacts and caches — Vite's `cacheDir` and the dev-time
4
+ * manifest output. Lives under `node_modules/` so it's out of `dist` and
5
+ * ignored by default in typical `.gitignore` files.
6
+ */ export const SANITY_CACHE_DIR = 'node_modules/.sanity';
7
+
8
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/constants.ts"],"sourcesContent":["/**\n * Root directory (relative to the project) used by Sanity tooling for\n * build-time artifacts and caches — Vite's `cacheDir` and the dev-time\n * manifest output. Lives under `node_modules/` so it's out of `dist` and\n * ignored by default in typical `.gitignore` files.\n */\nexport const SANITY_CACHE_DIR = 'node_modules/.sanity'\n"],"names":["SANITY_CACHE_DIR"],"mappings":"AAAA;;;;;CAKC,GACD,OAAO,MAAMA,mBAAmB,uBAAsB"}
@@ -0,0 +1,13 @@
1
+ import { defineTrace } from '@sanity/telemetry';
2
+ export const StudioBuildTrace = defineTrace({
3
+ description: 'A Studio build completed',
4
+ name: 'Studio Build Completed',
5
+ version: 0
6
+ });
7
+ export const AppBuildTrace = defineTrace({
8
+ description: 'An App build completed',
9
+ name: 'App Build Completed',
10
+ version: 0
11
+ });
12
+
13
+ //# sourceMappingURL=build.telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/build.telemetry.ts"],"sourcesContent":["import {defineTrace} from '@sanity/telemetry'\n\nexport const StudioBuildTrace = defineTrace<{outputSize: number}>({\n description: 'A Studio build completed',\n name: 'Studio Build Completed',\n version: 0,\n})\n\nexport const AppBuildTrace = defineTrace<{outputSize: number}>({\n description: 'An App build completed',\n name: 'App Build Completed',\n version: 0,\n})\n"],"names":["defineTrace","StudioBuildTrace","description","name","version","AppBuildTrace"],"mappings":"AAAA,SAAQA,WAAW,QAAO,oBAAmB;AAE7C,OAAO,MAAMC,mBAAmBD,YAAkC;IAChEE,aAAa;IACbC,MAAM;IACNC,SAAS;AACX,GAAE;AAEF,OAAO,MAAMC,gBAAgBL,YAAkC;IAC7DE,aAAa;IACbC,MAAM;IACNC,SAAS;AACX,GAAE"}
@@ -0,0 +1,18 @@
1
+ import { defineTrace } from '@sanity/telemetry';
2
+ export const SchemaExtractedTrace = defineTrace({
3
+ description: 'Trace emitted when extracting schema',
4
+ name: 'Schema Extracted',
5
+ version: 0
6
+ });
7
+ export const SchemaExtractionWatchModeTrace = defineTrace({
8
+ description: 'Trace emitted when schema extraction watch mode is run',
9
+ name: 'Schema Extraction Watch Mode',
10
+ version: 0
11
+ });
12
+ export const SchemaDeploy = defineTrace({
13
+ description: 'Schema deploy action was executed, either via sanity schema deploy or as sanity deploy',
14
+ name: 'Schema deploy action executed',
15
+ version: 1
16
+ });
17
+
18
+ //# sourceMappingURL=extractSchema.telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/telemetry/extractSchema.telemetry.ts"],"sourcesContent":["import {defineTrace} from '@sanity/telemetry'\n\ninterface SchemaExtractedTraceAttributes {\n enforceRequiredFields: boolean\n schemaAllTypesCount: number\n schemaDocumentTypesCount: number\n\n schemaFormat: string\n schemaTypesCount: number\n}\n\ntype SchemaExtractionWatchModeAttributes =\n | {\n averageExtractionDuration: number\n extractionFailedCount: number\n extractionSuccessfulCount: number\n step: 'stopped'\n watcherDuration: number\n }\n | {\n enforceRequiredFields: boolean\n schemaFormat: string\n step: 'started'\n }\n\ninterface SchemaDeployTraceData {\n manifestDir: string\n schemaRequired: boolean\n\n extractManifest?: boolean\n}\n\nexport const SchemaExtractedTrace = defineTrace<SchemaExtractedTraceAttributes>({\n description: 'Trace emitted when extracting schema',\n name: 'Schema Extracted',\n version: 0,\n})\n\nexport const SchemaExtractionWatchModeTrace = defineTrace<SchemaExtractionWatchModeAttributes>({\n description: 'Trace emitted when schema extraction watch mode is run',\n name: 'Schema Extraction Watch Mode',\n version: 0,\n})\n\nexport const SchemaDeploy = defineTrace<SchemaDeployTraceData>({\n description:\n 'Schema deploy action was executed, either via sanity schema deploy or as sanity deploy',\n name: 'Schema deploy action executed',\n version: 1,\n})\n"],"names":["defineTrace","SchemaExtractedTrace","description","name","version","SchemaExtractionWatchModeTrace","SchemaDeploy"],"mappings":"AAAA,SAAQA,WAAW,QAAO,oBAAmB;AAgC7C,OAAO,MAAMC,uBAAuBD,YAA4C;IAC9EE,aAAa;IACbC,MAAM;IACNC,SAAS;AACX,GAAE;AAEF,OAAO,MAAMC,iCAAiCL,YAAiD;IAC7FE,aAAa;IACbC,MAAM;IACNC,SAAS;AACX,GAAE;AAEF,OAAO,MAAME,eAAeN,YAAmC;IAC7DE,aACE;IACFC,MAAM;IACNC,SAAS;AACX,GAAE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/cli-build",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Internal Sanity package for building studios and apps",
5
5
  "keywords": [
6
6
  "cli",
@@ -27,12 +27,14 @@
27
27
  ],
28
28
  "type": "module",
29
29
  "sideEffects": false,
30
- "main": "./dist/_exports/_internal.js",
31
- "types": "./dist/_exports/_internal.d.ts",
32
30
  "exports": {
33
- "./_internal": {
34
- "source": "./src/_exports/_internal.ts",
35
- "default": "./dist/_exports/_internal.js"
31
+ "./_internal/build": {
32
+ "source": "./src/_exports/_internal/build.ts",
33
+ "default": "./dist/_exports/_internal/build.js"
34
+ },
35
+ "./_internal/extract": {
36
+ "source": "./src/_exports/_internal/extract.ts",
37
+ "default": "./dist/_exports/_internal/extract.js"
36
38
  },
37
39
  "./package.json": "./package.json"
38
40
  },
@@ -40,22 +42,49 @@
40
42
  "access": "public"
41
43
  },
42
44
  "dependencies": {
43
- "read-package-up": "^12.0.0"
45
+ "@oclif/core": "^4.11.0",
46
+ "@sanity/generate-help-url": "^4.0.0",
47
+ "@sanity/schema": "^5.26.0",
48
+ "@sanity/telemetry": "^0.9.0",
49
+ "@sanity/types": "^5.26.0",
50
+ "@vitejs/plugin-react": "^5.2.0",
51
+ "chokidar": "^5.0.0",
52
+ "debug": "^4.4.3",
53
+ "lodash-es": "^4.18.1",
54
+ "node-html-parser": "^7.1.0",
55
+ "picomatch": "^4.0.4",
56
+ "react": "^19.2.4",
57
+ "react-dom": "^19.2.4",
58
+ "read-package-up": "^12.0.0",
59
+ "semver": "^7.7.4",
60
+ "vite": "^7.3.3",
61
+ "zod": "^4.3.6",
62
+ "@sanity/cli-core": "^1.3.3"
44
63
  },
45
64
  "devDependencies": {
46
65
  "@eslint/compat": "^2.0.5",
47
66
  "@sanity/pkg-utils": "^10.4.18",
48
67
  "@swc/cli": "^0.8.1",
49
68
  "@swc/core": "^1.15.33",
50
- "@types/node": "^20.19.39",
69
+ "@types/debug": "^4.1.13",
70
+ "@types/jsdom": "^28.0.3",
71
+ "@types/lodash-es": "^4.17.12",
72
+ "@types/node": "^20.19.41",
73
+ "@types/picomatch": "^4.0.3",
74
+ "@types/react": "^19.2.14",
75
+ "@types/react-dom": "^19.2.3",
76
+ "@types/semver": "^7.7.1",
51
77
  "@vitest/coverage-istanbul": "^4.1.5",
78
+ "babel-plugin-react-compiler": "^1.0.0",
52
79
  "eslint": "^10.2.1",
80
+ "jsdom": "^29.1.1",
53
81
  "publint": "^0.3.18",
82
+ "sanity": "^5.26.0",
54
83
  "typescript": "^5.9.3",
55
84
  "vitest": "^4.1.5",
56
85
  "@repo/package.config": "0.0.1",
57
86
  "@repo/tsconfig": "3.70.0",
58
- "@sanity/cli-test": "0.3.3",
87
+ "@sanity/cli-test": "0.3.4",
59
88
  "@sanity/eslint-config-cli": "^1.1.1"
60
89
  },
61
90
  "engines": {
@@ -1,45 +0,0 @@
1
- /**
2
- * Copies a directory from one location to another
3
- *
4
- * @internal
5
- *
6
- * @param srcDir - Source directory
7
- * @param destDir - Destination directory
8
- * @param skipExisting - Skip existing files
9
- */
10
- export declare function copyDir(
11
- srcDir: string,
12
- destDir: string,
13
- skipExisting?: boolean,
14
- ): Promise<void>;
15
-
16
- /**
17
- * @internal
18
- */
19
- export declare function generateWebManifest(basePath: string): WebManifest;
20
-
21
- /**
22
- * @internal
23
- */
24
- export declare function getDefaultFaviconsPath(): Promise<string>;
25
-
26
- /**
27
- * @internal
28
- */
29
- declare interface WebManifest {
30
- icons: {
31
- sizes: string;
32
- src: string;
33
- type: string;
34
- }[];
35
- }
36
-
37
- /**
38
- * @internal
39
- */
40
- export declare function writeFavicons(
41
- basePath: string,
42
- destDir: string,
43
- ): Promise<void>;
44
-
45
- export {};
@@ -1,5 +0,0 @@
1
- export * from '../actions/build/generateWebManifest.js';
2
- export * from '../actions/build/writeFavicons.js';
3
- export { copyDir } from '../util/copyDir.js';
4
-
5
- //# sourceMappingURL=_internal.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/_exports/_internal.ts"],"sourcesContent":["export * from '../actions/build/generateWebManifest.js'\nexport * from '../actions/build/writeFavicons.js'\nexport {copyDir} from '../util/copyDir.js'\n"],"names":["copyDir"],"mappings":"AAAA,cAAc,0CAAyC;AACvD,cAAc,oCAAmC;AACjD,SAAQA,OAAO,QAAO,qBAAoB"}