@sanity/cli-test 0.0.2-alpha.5 → 0.0.2-alpha.7

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 (66) hide show
  1. package/README.md +228 -0
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +3 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/test/constants.d.ts +2 -0
  6. package/dist/test/constants.js +8 -0
  7. package/dist/test/constants.js.map +1 -0
  8. package/dist/test/setupExamples.d.ts +60 -0
  9. package/dist/test/setupExamples.js +129 -0
  10. package/dist/test/setupExamples.js.map +1 -0
  11. package/dist/test/testExample.d.ts +46 -0
  12. package/dist/test/testExample.js +93 -0
  13. package/dist/test/testExample.js.map +1 -0
  14. package/dist/utils/fileExists.d.ts +9 -0
  15. package/dist/utils/fileExists.js +13 -0
  16. package/dist/utils/fileExists.js.map +1 -0
  17. package/dist/utils/paths.d.ts +22 -0
  18. package/dist/utils/paths.js +30 -0
  19. package/dist/utils/paths.js.map +1 -0
  20. package/dist/vitest.d.ts +20 -0
  21. package/dist/vitest.js +21 -0
  22. package/dist/vitest.js.map +1 -0
  23. package/dist/vitestWorker.d.ts +23 -0
  24. package/dist/vitestWorker.js +131 -0
  25. package/dist/vitestWorker.js.map +1 -0
  26. package/examples/basic-app/package.json +27 -0
  27. package/examples/basic-app/sanity.cli.ts +12 -0
  28. package/examples/basic-app/src/App.css +20 -0
  29. package/examples/basic-app/src/App.tsx +26 -0
  30. package/examples/basic-app/src/ExampleComponent.css +84 -0
  31. package/examples/basic-app/src/ExampleComponent.tsx +38 -0
  32. package/examples/basic-app/tsconfig.json +17 -0
  33. package/examples/basic-studio/package.json +29 -0
  34. package/examples/basic-studio/sanity.cli.ts +11 -0
  35. package/examples/basic-studio/sanity.config.ts +18 -0
  36. package/examples/basic-studio/schemaTypes/author.ts +52 -0
  37. package/examples/basic-studio/schemaTypes/blockContent.ts +71 -0
  38. package/examples/basic-studio/schemaTypes/category.ts +20 -0
  39. package/examples/basic-studio/schemaTypes/index.ts +6 -0
  40. package/examples/basic-studio/schemaTypes/post.ts +67 -0
  41. package/examples/basic-studio/tsconfig.json +17 -0
  42. package/examples/multi-workspace-studio/package.json +29 -0
  43. package/examples/multi-workspace-studio/sanity.cli.ts +11 -0
  44. package/examples/multi-workspace-studio/sanity.config.ts +37 -0
  45. package/examples/multi-workspace-studio/schemaTypes/author.ts +52 -0
  46. package/examples/multi-workspace-studio/schemaTypes/blockContent.ts +70 -0
  47. package/examples/multi-workspace-studio/schemaTypes/category.ts +20 -0
  48. package/examples/multi-workspace-studio/schemaTypes/index.ts +6 -0
  49. package/examples/multi-workspace-studio/schemaTypes/post.ts +67 -0
  50. package/examples/multi-workspace-studio/tsconfig.json +17 -0
  51. package/examples/worst-case-studio/README.md +21 -0
  52. package/examples/worst-case-studio/package.json +33 -0
  53. package/examples/worst-case-studio/sanity.cli.ts +16 -0
  54. package/examples/worst-case-studio/sanity.config.tsx +48 -0
  55. package/examples/worst-case-studio/src/defines.ts +8 -0
  56. package/examples/worst-case-studio/src/descriptionIcon.svg +7 -0
  57. package/examples/worst-case-studio/src/descriptionInput.module.css +13 -0
  58. package/examples/worst-case-studio/src/descriptionInput.tsx +55 -0
  59. package/examples/worst-case-studio/src/schemaTypes/author.ts +52 -0
  60. package/examples/worst-case-studio/src/schemaTypes/blockContent.ts +70 -0
  61. package/examples/worst-case-studio/src/schemaTypes/category.ts +20 -0
  62. package/examples/worst-case-studio/src/schemaTypes/index.ts +6 -0
  63. package/examples/worst-case-studio/src/schemaTypes/post.ts +71 -0
  64. package/examples/worst-case-studio/src/typings.d.ts +37 -0
  65. package/examples/worst-case-studio/tsconfig.json +22 -0
  66. package/package.json +27 -12
@@ -0,0 +1,13 @@
1
+ import { access } from 'node:fs/promises';
2
+ /**
3
+ * Checks if a file exists and can be "accessed".
4
+ * Prone to race conditions, but good enough for our use cases.
5
+ *
6
+ * @param filePath - The path to the file to check
7
+ * @returns A promise that resolves to true if the file exists, false otherwise
8
+ * @internal
9
+ */ export function fileExists(filePath) {
10
+ return access(filePath).then(()=>true, ()=>false);
11
+ }
12
+
13
+ //# sourceMappingURL=fileExists.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/fileExists.ts"],"sourcesContent":["import {access} from 'node:fs/promises'\n\n/**\n * Checks if a file exists and can be \"accessed\".\n * Prone to race conditions, but good enough for our use cases.\n *\n * @param filePath - The path to the file to check\n * @returns A promise that resolves to true if the file exists, false otherwise\n * @internal\n */\nexport function fileExists(filePath: string): Promise<boolean> {\n return access(filePath).then(\n () => true,\n () => false,\n )\n}\n"],"names":["access","fileExists","filePath","then"],"mappings":"AAAA,SAAQA,MAAM,QAAO,mBAAkB;AAEvC;;;;;;;CAOC,GACD,OAAO,SAASC,WAAWC,QAAgB;IACzC,OAAOF,OAAOE,UAAUC,IAAI,CAC1B,IAAM,MACN,IAAM;AAEV"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Gets the path to the examples directory bundled with this package.
3
+ *
4
+ * The examples are copied during build and bundled with the published package.
5
+ * This function works the same whether the package is used in a monorepo
6
+ * or installed from npm.
7
+ *
8
+ * @returns Absolute path to the examples directory
9
+ * @internal
10
+ */
11
+ export declare function getExamplesPath(): string;
12
+ /**
13
+ * Gets the path to the temporary directory for test examples.
14
+ *
15
+ * Uses the initial working directory captured when this module was first loaded,
16
+ * not process.cwd() which may change during test execution.
17
+ *
18
+ * @param customTempDir - Optional custom temp directory path
19
+ * @returns Absolute path to temp directory (default: initial cwd/tmp)
20
+ * @internal
21
+ */
22
+ export declare function getTempPath(customTempDir?: string): string;
@@ -0,0 +1,30 @@
1
+ import { resolve } from 'node:path';
2
+ // Capture the initial working directory before any tests change it
3
+ const INITIAL_CWD = process.cwd();
4
+ /**
5
+ * Gets the path to the examples directory bundled with this package.
6
+ *
7
+ * The examples are copied during build and bundled with the published package.
8
+ * This function works the same whether the package is used in a monorepo
9
+ * or installed from npm.
10
+ *
11
+ * @returns Absolute path to the examples directory
12
+ * @internal
13
+ */ export function getExamplesPath() {
14
+ // From dist/utils/paths.js -> ../../examples
15
+ return resolve(import.meta.dirname, '../../examples');
16
+ }
17
+ /**
18
+ * Gets the path to the temporary directory for test examples.
19
+ *
20
+ * Uses the initial working directory captured when this module was first loaded,
21
+ * not process.cwd() which may change during test execution.
22
+ *
23
+ * @param customTempDir - Optional custom temp directory path
24
+ * @returns Absolute path to temp directory (default: initial cwd/tmp)
25
+ * @internal
26
+ */ export function getTempPath(customTempDir) {
27
+ return customTempDir || resolve(INITIAL_CWD, 'tmp');
28
+ }
29
+
30
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/paths.ts"],"sourcesContent":["import {resolve} from 'node:path'\n\n// Capture the initial working directory before any tests change it\nconst INITIAL_CWD = process.cwd()\n\n/**\n * Gets the path to the examples directory bundled with this package.\n *\n * The examples are copied during build and bundled with the published package.\n * This function works the same whether the package is used in a monorepo\n * or installed from npm.\n *\n * @returns Absolute path to the examples directory\n * @internal\n */\nexport function getExamplesPath(): string {\n // From dist/utils/paths.js -> ../../examples\n return resolve(import.meta.dirname, '../../examples')\n}\n\n/**\n * Gets the path to the temporary directory for test examples.\n *\n * Uses the initial working directory captured when this module was first loaded,\n * not process.cwd() which may change during test execution.\n *\n * @param customTempDir - Optional custom temp directory path\n * @returns Absolute path to temp directory (default: initial cwd/tmp)\n * @internal\n */\nexport function getTempPath(customTempDir?: string): string {\n return customTempDir || resolve(INITIAL_CWD, 'tmp')\n}\n"],"names":["resolve","INITIAL_CWD","process","cwd","getExamplesPath","dirname","getTempPath","customTempDir"],"mappings":"AAAA,SAAQA,OAAO,QAAO,YAAW;AAEjC,mEAAmE;AACnE,MAAMC,cAAcC,QAAQC,GAAG;AAE/B;;;;;;;;;CASC,GACD,OAAO,SAASC;IACd,6CAA6C;IAC7C,OAAOJ,QAAQ,YAAYK,OAAO,EAAE;AACtC;AAEA;;;;;;;;;CASC,GACD,OAAO,SAASC,YAAYC,aAAsB;IAChD,OAAOA,iBAAiBP,QAAQC,aAAa;AAC/C"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Vitest global setup entry point.
3
+ *
4
+ * Import this in your vitest.config.ts globalSetup array for automatic
5
+ * test example setup and teardown.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // vitest.config.ts
10
+ * import {defineConfig} from 'vitest/config'
11
+ *
12
+ * export default defineConfig({
13
+ * test: {
14
+ * globalSetup: ['@sanity/cli-test/vitest']
15
+ * }
16
+ * })
17
+ * ```
18
+ */
19
+ export { setup, teardown } from './test/setupExamples.js';
20
+ export { setupWorkerBuild, teardownWorkerBuild } from './vitestWorker.js';
package/dist/vitest.js ADDED
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Vitest global setup entry point.
3
+ *
4
+ * Import this in your vitest.config.ts globalSetup array for automatic
5
+ * test example setup and teardown.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // vitest.config.ts
10
+ * import {defineConfig} from 'vitest/config'
11
+ *
12
+ * export default defineConfig({
13
+ * test: {
14
+ * globalSetup: ['@sanity/cli-test/vitest']
15
+ * }
16
+ * })
17
+ * ```
18
+ */ export { setup, teardown } from './test/setupExamples.js';
19
+ export { setupWorkerBuild, teardownWorkerBuild } from './vitestWorker.js';
20
+
21
+ //# sourceMappingURL=vitest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vitest.ts"],"sourcesContent":["/**\n * Vitest global setup entry point.\n *\n * Import this in your vitest.config.ts globalSetup array for automatic\n * test example setup and teardown.\n *\n * @example\n * ```typescript\n * // vitest.config.ts\n * import {defineConfig} from 'vitest/config'\n *\n * export default defineConfig({\n * test: {\n * globalSetup: ['@sanity/cli-test/vitest']\n * }\n * })\n * ```\n */\nexport {setup, teardown} from './test/setupExamples.js'\nexport {setupWorkerBuild, teardownWorkerBuild} from './vitestWorker.js'\n"],"names":["setup","teardown","setupWorkerBuild","teardownWorkerBuild"],"mappings":"AAAA;;;;;;;;;;;;;;;;;CAiBC,GACD,SAAQA,KAAK,EAAEC,QAAQ,QAAO,0BAAyB;AACvD,SAAQC,gBAAgB,EAAEC,mBAAmB,QAAO,oBAAmB"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Setup function to build the worker files with esbuild.
3
+ *
4
+ * Bundles the worker files with esbuild and sets up watch mode if in watch mode.
5
+ * All npm packages are automatically marked as external (loaded from node_modules at runtime).
6
+ * Only internal project code is bundled inline.
7
+ *
8
+ * @param filePaths - The paths to the worker files to build
9
+ * @returns A promise that resolves when the worker build is setup
10
+ * @throws If the worker files cannot be bundled
11
+ * @throws If the watcher cannot be set up
12
+ */
13
+ export declare function setupWorkerBuild(filePaths: string[]): Promise<void>;
14
+ /**
15
+ * Teardown function to clean up the worker build.
16
+ *
17
+ * Closes all build contexts and deletes the compiled JavaScript files.
18
+ *
19
+ * @returns A promise that resolves when the worker build is teared down
20
+ * @throws If the build contexts cannot be disposed
21
+ * @throws If the compiled JavaScript files cannot be deleted
22
+ */
23
+ export declare function teardownWorkerBuild(): Promise<void>;
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Vitest worker setup for the Sanity CLI.
3
+ *
4
+ * This builds the worker TS files into JS files using esbuild bundling.
5
+ * All internal dependencies are bundled inline, while npm packages remain external.
6
+ */ import { unlink } from 'node:fs/promises';
7
+ import { build, context } from 'esbuild';
8
+ const compiledFiles = new Set();
9
+ let buildContexts = [];
10
+ /**
11
+ * Generate esbuild configuration for bundling worker files.
12
+ *
13
+ * @param filePath - The worker file path to bundle
14
+ * @param outputFile - The output file path for the bundled worker
15
+ * @returns esbuild configuration object
16
+ */ function esbuildOptions(filePath, outputFile) {
17
+ return {
18
+ bundle: true,
19
+ conditions: [
20
+ 'node',
21
+ 'import'
22
+ ],
23
+ entryPoints: [
24
+ filePath
25
+ ],
26
+ // Marks cli-core as external to avoid bundling it inline
27
+ // this is mostly necessary for the monorepo.
28
+ external: [
29
+ '@sanity/cli-core'
30
+ ],
31
+ format: 'esm',
32
+ loader: {
33
+ '.json': 'json'
34
+ },
35
+ logLevel: 'warning',
36
+ mainFields: [
37
+ 'module',
38
+ 'main'
39
+ ],
40
+ outfile: outputFile,
41
+ packages: 'external',
42
+ platform: 'node',
43
+ sourcemap: false,
44
+ target: 'node20'
45
+ };
46
+ }
47
+ /**
48
+ * Bundle a single worker file with esbuild.
49
+ *
50
+ * @param filePath - The worker file path to bundle
51
+ * @param external - Array of package names to keep external
52
+ * @returns The output file path
53
+ */ async function bundleWorkerFile(filePath) {
54
+ const outputFile = filePath.replace(/\.ts$/, '.js');
55
+ await build(esbuildOptions(filePath, outputFile));
56
+ compiledFiles.add(outputFile);
57
+ return outputFile;
58
+ }
59
+ /**
60
+ * Bundle all worker files with esbuild (non-watch mode).
61
+ *
62
+ * @param filePaths - Array of worker file paths to bundle
63
+ */ async function setupBundling(filePaths) {
64
+ console.log(`Found ${filePaths.length} worker files to bundle`);
65
+ for (const workerFile of filePaths){
66
+ try {
67
+ await bundleWorkerFile(workerFile);
68
+ console.log(`✓ Bundled ${workerFile}`);
69
+ } catch (error) {
70
+ console.error(`✗ Failed to bundle ${workerFile}:`, error);
71
+ throw error;
72
+ }
73
+ }
74
+ }
75
+ /**
76
+ * Set up watch mode for worker files using esbuild's native watch API.
77
+ *
78
+ * @param files - Array of worker file paths to watch
79
+ */ async function setupWatchMode(files) {
80
+ for (const filePath of files){
81
+ const outputFile = filePath.replace(/\.ts$/, '.js');
82
+ const ctx = await context(esbuildOptions(filePath, outputFile));
83
+ await ctx.watch();
84
+ buildContexts.push(ctx);
85
+ compiledFiles.add(outputFile);
86
+ console.log(`👀 Watching ${filePath}`);
87
+ }
88
+ }
89
+ /**
90
+ * Setup function to build the worker files with esbuild.
91
+ *
92
+ * Bundles the worker files with esbuild and sets up watch mode if in watch mode.
93
+ * All npm packages are automatically marked as external (loaded from node_modules at runtime).
94
+ * Only internal project code is bundled inline.
95
+ *
96
+ * @param filePaths - The paths to the worker files to build
97
+ * @returns A promise that resolves when the worker build is setup
98
+ * @throws If the worker files cannot be bundled
99
+ * @throws If the watcher cannot be set up
100
+ */ export async function setupWorkerBuild(filePaths) {
101
+ const isWatchMode = (process.env.VITEST_WATCH === 'true' || !process.argv.includes('run')) && process.env.CI !== 'true';
102
+ await (isWatchMode ? setupWatchMode(filePaths) : setupBundling(filePaths));
103
+ }
104
+ /**
105
+ * Teardown function to clean up the worker build.
106
+ *
107
+ * Closes all build contexts and deletes the compiled JavaScript files.
108
+ *
109
+ * @returns A promise that resolves when the worker build is teared down
110
+ * @throws If the build contexts cannot be disposed
111
+ * @throws If the compiled JavaScript files cannot be deleted
112
+ */ export async function teardownWorkerBuild() {
113
+ // Dispose all build contexts (for watch mode)
114
+ for (const ctx of buildContexts){
115
+ await ctx.dispose();
116
+ }
117
+ buildContexts = [];
118
+ // Clean up compiled JavaScript files
119
+ console.log('Cleaning up compiled JavaScript files...');
120
+ for (const filePath of compiledFiles){
121
+ try {
122
+ await unlink(filePath);
123
+ console.log(`✓ Deleted ${filePath}`);
124
+ } catch (error) {
125
+ console.error(`Failed to delete ${filePath}:`, error);
126
+ }
127
+ }
128
+ compiledFiles.clear();
129
+ }
130
+
131
+ //# sourceMappingURL=vitestWorker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/vitestWorker.ts"],"sourcesContent":["/**\n * Vitest worker setup for the Sanity CLI.\n *\n * This builds the worker TS files into JS files using esbuild bundling.\n * All internal dependencies are bundled inline, while npm packages remain external.\n */\nimport {unlink} from 'node:fs/promises'\n\nimport {build, type BuildContext, type BuildOptions, context} from 'esbuild'\n\nconst compiledFiles: Set<string> = new Set()\nlet buildContexts: BuildContext[] = []\n\n/**\n * Generate esbuild configuration for bundling worker files.\n *\n * @param filePath - The worker file path to bundle\n * @param outputFile - The output file path for the bundled worker\n * @returns esbuild configuration object\n */\nfunction esbuildOptions(filePath: string, outputFile: string): BuildOptions {\n return {\n bundle: true,\n conditions: ['node', 'import'],\n entryPoints: [filePath],\n // Marks cli-core as external to avoid bundling it inline\n // this is mostly necessary for the monorepo.\n external: ['@sanity/cli-core'],\n format: 'esm',\n loader: {'.json': 'json'},\n logLevel: 'warning',\n mainFields: ['module', 'main'],\n outfile: outputFile,\n packages: 'external',\n platform: 'node',\n sourcemap: false,\n target: 'node20',\n }\n}\n\n/**\n * Bundle a single worker file with esbuild.\n *\n * @param filePath - The worker file path to bundle\n * @param external - Array of package names to keep external\n * @returns The output file path\n */\nasync function bundleWorkerFile(filePath: string): Promise<string> {\n const outputFile = filePath.replace(/\\.ts$/, '.js')\n\n await build(esbuildOptions(filePath, outputFile))\n\n compiledFiles.add(outputFile)\n return outputFile\n}\n\n/**\n * Bundle all worker files with esbuild (non-watch mode).\n *\n * @param filePaths - Array of worker file paths to bundle\n */\nasync function setupBundling(filePaths: string[]) {\n console.log(`Found ${filePaths.length} worker files to bundle`)\n\n for (const workerFile of filePaths) {\n try {\n await bundleWorkerFile(workerFile)\n console.log(`✓ Bundled ${workerFile}`)\n } catch (error) {\n console.error(`✗ Failed to bundle ${workerFile}:`, error)\n throw error\n }\n }\n}\n\n/**\n * Set up watch mode for worker files using esbuild's native watch API.\n *\n * @param files - Array of worker file paths to watch\n */\nasync function setupWatchMode(files: string[]) {\n for (const filePath of files) {\n const outputFile = filePath.replace(/\\.ts$/, '.js')\n\n const ctx = await context(esbuildOptions(filePath, outputFile))\n\n await ctx.watch()\n buildContexts.push(ctx)\n compiledFiles.add(outputFile)\n\n console.log(`👀 Watching ${filePath}`)\n }\n}\n\n/**\n * Setup function to build the worker files with esbuild.\n *\n * Bundles the worker files with esbuild and sets up watch mode if in watch mode.\n * All npm packages are automatically marked as external (loaded from node_modules at runtime).\n * Only internal project code is bundled inline.\n *\n * @param filePaths - The paths to the worker files to build\n * @returns A promise that resolves when the worker build is setup\n * @throws If the worker files cannot be bundled\n * @throws If the watcher cannot be set up\n */\nexport async function setupWorkerBuild(filePaths: string[]) {\n const isWatchMode =\n (process.env.VITEST_WATCH === 'true' || !process.argv.includes('run')) &&\n process.env.CI !== 'true'\n\n await (isWatchMode ? setupWatchMode(filePaths) : setupBundling(filePaths))\n}\n\n/**\n * Teardown function to clean up the worker build.\n *\n * Closes all build contexts and deletes the compiled JavaScript files.\n *\n * @returns A promise that resolves when the worker build is teared down\n * @throws If the build contexts cannot be disposed\n * @throws If the compiled JavaScript files cannot be deleted\n */\nexport async function teardownWorkerBuild(): Promise<void> {\n // Dispose all build contexts (for watch mode)\n for (const ctx of buildContexts) {\n await ctx.dispose()\n }\n buildContexts = []\n\n // Clean up compiled JavaScript files\n console.log('Cleaning up compiled JavaScript files...')\n for (const filePath of compiledFiles) {\n try {\n await unlink(filePath)\n console.log(`✓ Deleted ${filePath}`)\n } catch (error) {\n console.error(`Failed to delete ${filePath}:`, error)\n }\n }\n compiledFiles.clear()\n}\n"],"names":["unlink","build","context","compiledFiles","Set","buildContexts","esbuildOptions","filePath","outputFile","bundle","conditions","entryPoints","external","format","loader","logLevel","mainFields","outfile","packages","platform","sourcemap","target","bundleWorkerFile","replace","add","setupBundling","filePaths","console","log","length","workerFile","error","setupWatchMode","files","ctx","watch","push","setupWorkerBuild","isWatchMode","process","env","VITEST_WATCH","argv","includes","CI","teardownWorkerBuild","dispose","clear"],"mappings":"AAAA;;;;;CAKC,GACD,SAAQA,MAAM,QAAO,mBAAkB;AAEvC,SAAQC,KAAK,EAAwCC,OAAO,QAAO,UAAS;AAE5E,MAAMC,gBAA6B,IAAIC;AACvC,IAAIC,gBAAgC,EAAE;AAEtC;;;;;;CAMC,GACD,SAASC,eAAeC,QAAgB,EAAEC,UAAkB;IAC1D,OAAO;QACLC,QAAQ;QACRC,YAAY;YAAC;YAAQ;SAAS;QAC9BC,aAAa;YAACJ;SAAS;QACvB,yDAAyD;QACzD,6CAA6C;QAC7CK,UAAU;YAAC;SAAmB;QAC9BC,QAAQ;QACRC,QAAQ;YAAC,SAAS;QAAM;QACxBC,UAAU;QACVC,YAAY;YAAC;YAAU;SAAO;QAC9BC,SAAST;QACTU,UAAU;QACVC,UAAU;QACVC,WAAW;QACXC,QAAQ;IACV;AACF;AAEA;;;;;;CAMC,GACD,eAAeC,iBAAiBf,QAAgB;IAC9C,MAAMC,aAAaD,SAASgB,OAAO,CAAC,SAAS;IAE7C,MAAMtB,MAAMK,eAAeC,UAAUC;IAErCL,cAAcqB,GAAG,CAAChB;IAClB,OAAOA;AACT;AAEA;;;;CAIC,GACD,eAAeiB,cAAcC,SAAmB;IAC9CC,QAAQC,GAAG,CAAC,CAAC,MAAM,EAAEF,UAAUG,MAAM,CAAC,uBAAuB,CAAC;IAE9D,KAAK,MAAMC,cAAcJ,UAAW;QAClC,IAAI;YACF,MAAMJ,iBAAiBQ;YACvBH,QAAQC,GAAG,CAAC,CAAC,UAAU,EAAEE,YAAY;QACvC,EAAE,OAAOC,OAAO;YACdJ,QAAQI,KAAK,CAAC,CAAC,mBAAmB,EAAED,WAAW,CAAC,CAAC,EAAEC;YACnD,MAAMA;QACR;IACF;AACF;AAEA;;;;CAIC,GACD,eAAeC,eAAeC,KAAe;IAC3C,KAAK,MAAM1B,YAAY0B,MAAO;QAC5B,MAAMzB,aAAaD,SAASgB,OAAO,CAAC,SAAS;QAE7C,MAAMW,MAAM,MAAMhC,QAAQI,eAAeC,UAAUC;QAEnD,MAAM0B,IAAIC,KAAK;QACf9B,cAAc+B,IAAI,CAACF;QACnB/B,cAAcqB,GAAG,CAAChB;QAElBmB,QAAQC,GAAG,CAAC,CAAC,YAAY,EAAErB,UAAU;IACvC;AACF;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,eAAe8B,iBAAiBX,SAAmB;IACxD,MAAMY,cACJ,AAACC,CAAAA,QAAQC,GAAG,CAACC,YAAY,KAAK,UAAU,CAACF,QAAQG,IAAI,CAACC,QAAQ,CAAC,MAAK,KACpEJ,QAAQC,GAAG,CAACI,EAAE,KAAK;IAErB,MAAON,CAAAA,cAAcN,eAAeN,aAAaD,cAAcC,UAAS;AAC1E;AAEA;;;;;;;;CAQC,GACD,OAAO,eAAemB;IACpB,8CAA8C;IAC9C,KAAK,MAAMX,OAAO7B,cAAe;QAC/B,MAAM6B,IAAIY,OAAO;IACnB;IACAzC,gBAAgB,EAAE;IAElB,qCAAqC;IACrCsB,QAAQC,GAAG,CAAC;IACZ,KAAK,MAAMrB,YAAYJ,cAAe;QACpC,IAAI;YACF,MAAMH,OAAOO;YACboB,QAAQC,GAAG,CAAC,CAAC,UAAU,EAAErB,UAAU;QACrC,EAAE,OAAOwB,OAAO;YACdJ,QAAQI,KAAK,CAAC,CAAC,iBAAiB,EAAExB,SAAS,CAAC,CAAC,EAAEwB;QACjD;IACF;IACA5B,cAAc4C,KAAK;AACrB"}
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "basic-app",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "keywords": [
6
+ "sanity"
7
+ ],
8
+ "license": "UNLICENSED",
9
+ "main": "package.json",
10
+ "scripts": {
11
+ "build": "sanity build",
12
+ "dev": "sanity dev",
13
+ "start": "sanity start"
14
+ },
15
+ "dependencies": {
16
+ "@sanity/sdk": "^2.5.0",
17
+ "@sanity/sdk-react": "^2.5.0",
18
+ "react": "^19.2.3",
19
+ "react-dom": "^19.2.3",
20
+ "sanity": "^5.6.0"
21
+ },
22
+ "devDependencies": {
23
+ "@types/react": "^19.2.8",
24
+ "sanity": "^5.6.0",
25
+ "typescript": "^5.9.3"
26
+ }
27
+ }
@@ -0,0 +1,12 @@
1
+ import {defineCliConfig} from 'sanity/cli'
2
+
3
+ export default defineCliConfig({
4
+ app: {
5
+ entry: './src/App.tsx',
6
+ organizationId: 'org-id',
7
+ },
8
+ deployment: {
9
+ appId: 'app-id',
10
+ autoUpdates: true,
11
+ },
12
+ })
@@ -0,0 +1,20 @@
1
+ /* Container styling for the app */
2
+ .app-container {
3
+ max-width: 1200px;
4
+ margin: 0 auto;
5
+ padding: 2rem;
6
+ font-family:
7
+ -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans',
8
+ 'Helvetica Neue', sans-serif;
9
+ }
10
+
11
+ /* Basic reset */
12
+ * {
13
+ box-sizing: border-box;
14
+ }
15
+
16
+ body {
17
+ margin: 0;
18
+ padding: 0;
19
+ background-color: #f9f9f9;
20
+ }
@@ -0,0 +1,26 @@
1
+ import {type SanityConfig} from '@sanity/sdk'
2
+ import {SanityApp} from '@sanity/sdk-react'
3
+
4
+ import {ExampleComponent} from './ExampleComponent'
5
+ import './App.css'
6
+
7
+ function App() {
8
+ // apps can access many different projects or other sources of data
9
+ const sanityConfigs: SanityConfig[] = [
10
+ {
11
+ dataset: 'dataset-name',
12
+ projectId: 'project-id',
13
+ },
14
+ ]
15
+
16
+ return (
17
+ <div className="app-container">
18
+ <SanityApp config={sanityConfigs} fallback={<div>Loading...</div>}>
19
+ {/* add your own components here! */}
20
+ <ExampleComponent />
21
+ </SanityApp>
22
+ </div>
23
+ )
24
+ }
25
+
26
+ export default App
@@ -0,0 +1,84 @@
1
+ .example-container {
2
+ background-color: white;
3
+ border-radius: 8px;
4
+ padding: 2rem;
5
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
6
+ }
7
+
8
+ .example-avatar-container {
9
+ position: relative;
10
+ margin-block-end: 1rem;
11
+ }
12
+
13
+ @keyframes wave {
14
+ 0% {
15
+ rotate: 0deg;
16
+ }
17
+ 2% {
18
+ rotate: 20deg;
19
+ filter: blur(1px);
20
+ }
21
+ 4% {
22
+ rotate: 0deg;
23
+ }
24
+ 6% {
25
+ rotate: -20deg;
26
+ }
27
+ 8% {
28
+ rotate: 0deg;
29
+ }
30
+ 10% {
31
+ rotate: 20deg;
32
+ }
33
+ 12% {
34
+ rotate: 0deg;
35
+ }
36
+ 14% {
37
+ rotate: -20deg;
38
+ }
39
+ 16% {
40
+ rotate: 0deg;
41
+ filter: blur(0);
42
+ }
43
+ }
44
+
45
+ .example-avatar-container:after {
46
+ content: '👋';
47
+ position: absolute;
48
+ font-size: 1.5rem;
49
+ display: flex;
50
+ align-items: center;
51
+ justify-container: center;
52
+ inset-inline-start: 0;
53
+ inset-block-start: 0;
54
+ translate: -25% -25%;
55
+ transform-origin: 100% 100%;
56
+ animation: wave 4s infinite linear;
57
+ }
58
+
59
+ .example-avatar {
60
+ inline-size: 4rem;
61
+ aspect-ratio: 1;
62
+ border-radius: 50%;
63
+ }
64
+
65
+ .example-heading {
66
+ color: #333;
67
+ margin-top: 0;
68
+ font-size: 2rem;
69
+ }
70
+
71
+ p {
72
+ color: #666;
73
+ line-height: 1.6;
74
+ }
75
+
76
+ .code-hint {
77
+ background-color: #f5f5f5;
78
+ padding: 1rem;
79
+ border-radius: 4px;
80
+ font-family: monospace;
81
+ margin-top: 1.5rem;
82
+ border-left: 3px solid #e0e0e0;
83
+ line-height: 1.5;
84
+ }
@@ -0,0 +1,38 @@
1
+ import {type CurrentUser, useCurrentUser} from '@sanity/sdk-react'
2
+
3
+ import './ExampleComponent.css'
4
+
5
+ export function ExampleComponent() {
6
+ const user: CurrentUser | null = useCurrentUser()
7
+
8
+ return (
9
+ <div className="example-container">
10
+ {user?.profileImage ? (
11
+ <div className="example-avatar-container">
12
+ <img alt="" className="example-avatar" src={user.profileImage} />
13
+ </div>
14
+ ) : (
15
+ ''
16
+ )}
17
+ <h1 className="example-heading">
18
+ Welcome to your Sanity App{user?.name ? `, ${user.name}` : ''}!
19
+ </h1>
20
+ <p className="example-text">
21
+ This is an example component. You can replace this with your own content by creating a new
22
+ component and importing it in App.tsx.
23
+ </p>
24
+ <div className="code-hint">
25
+ <p>
26
+ Quick tip: Create new components in separate files and import them like this in App.tsx /
27
+ App.jsx:
28
+ </p>
29
+ <pre>{`import {YourComponent} from './YourComponent'
30
+
31
+ // Then use it in your JSX
32
+ <SanityApp config={sanityConfigs}>
33
+ <YourComponent />
34
+ </SanityApp>`}</pre>
35
+ </div>
36
+ </div>
37
+ )
38
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2017",
4
+ "lib": ["dom", "dom.iterable", "esnext"],
5
+ "allowJs": true,
6
+ "skipLibCheck": true,
7
+ "strict": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "module": "Preserve",
10
+ "moduleDetection": "force",
11
+ "isolatedModules": true,
12
+ "jsx": "preserve",
13
+ "incremental": true
14
+ },
15
+ "include": ["**/*.ts", "**/*.tsx"],
16
+ "exclude": ["node_modules"]
17
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@sanity/basic-studio",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "keywords": [
6
+ "sanity"
7
+ ],
8
+ "license": "MIT",
9
+ "type": "module",
10
+ "main": "package.json",
11
+ "scripts": {
12
+ "build": "sanity build",
13
+ "deploy": "sanity deploy",
14
+ "deploy-graphql": "sanity graphql deploy",
15
+ "dev": "sanity dev",
16
+ "start": "sanity start"
17
+ },
18
+ "dependencies": {
19
+ "@sanity/vision": "^5.6.0",
20
+ "react": "^19.2.3",
21
+ "react-dom": "^19.2.3",
22
+ "sanity": "^5.6.0",
23
+ "styled-components": "^6.3.5"
24
+ },
25
+ "devDependencies": {
26
+ "@types/react": "^19.2.8",
27
+ "typescript": "^5.9.3"
28
+ }
29
+ }
@@ -0,0 +1,11 @@
1
+ import {defineCliConfig} from 'sanity/cli'
2
+
3
+ export default defineCliConfig({
4
+ api: {
5
+ dataset: 'test',
6
+ projectId: 'ppsg7ml5',
7
+ },
8
+ deployment: {
9
+ autoUpdates: true,
10
+ },
11
+ })
@@ -0,0 +1,18 @@
1
+ import {visionTool} from '@sanity/vision'
2
+ import {defineConfig} from 'sanity'
3
+ import {structureTool} from 'sanity/structure'
4
+
5
+ import {schemaTypes} from './schemaTypes'
6
+
7
+ export default defineConfig({
8
+ title: 'Basic Studio',
9
+
10
+ dataset: 'test',
11
+ projectId: 'ppsg7ml5',
12
+
13
+ plugins: [structureTool(), visionTool()],
14
+
15
+ schema: {
16
+ types: schemaTypes,
17
+ },
18
+ })
@@ -0,0 +1,52 @@
1
+ import {defineField, defineType} from 'sanity'
2
+
3
+ export default defineType({
4
+ name: 'author',
5
+ title: 'Author',
6
+ type: 'document',
7
+
8
+ fields: [
9
+ defineField({
10
+ name: 'name',
11
+ title: 'Name',
12
+ type: 'string',
13
+ }),
14
+ defineField({
15
+ name: 'slug',
16
+ options: {
17
+ maxLength: 96,
18
+ source: 'name',
19
+ },
20
+ title: 'Slug',
21
+ type: 'slug',
22
+ }),
23
+ defineField({
24
+ name: 'image',
25
+ options: {
26
+ hotspot: true,
27
+ },
28
+ title: 'Image',
29
+ type: 'image',
30
+ }),
31
+ defineField({
32
+ name: 'bio',
33
+ of: [
34
+ {
35
+ lists: [],
36
+ styles: [{title: 'Normal', value: 'normal'}],
37
+ title: 'Block',
38
+ type: 'block',
39
+ },
40
+ ],
41
+ title: 'Bio',
42
+ type: 'array',
43
+ }),
44
+ ],
45
+
46
+ preview: {
47
+ select: {
48
+ media: 'image',
49
+ title: 'name',
50
+ },
51
+ },
52
+ })