Skip to content

Commit daa0013

Browse files
authored
fix: schema extract path always appends schema.json (#686)
1 parent 15c6b7d commit daa0013

File tree

3 files changed

+126
-7
lines changed

3 files changed

+126
-7
lines changed

packages/@sanity/cli/src/actions/schema/__tests__/getExtractOptions.test.ts

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {mkdtempSync, writeFileSync} from 'node:fs'
2+
import {tmpdir} from 'node:os'
13
import {join, resolve} from 'node:path'
24

35
import {describe, expect, test} from 'vitest'
@@ -69,6 +71,101 @@ describe('getExtractOptions', () => {
6971
})
7072
})
7173

74+
test('should append schema.json when path points to an existing directory', () => {
75+
const tempDir = mkdtempSync(join(tmpdir(), 'schema-test-'))
76+
const projectRoot = {
77+
directory: tempDir,
78+
path: join(tempDir, 'sanity.config.ts'),
79+
type: 'studio' as const,
80+
}
81+
82+
const result = getExtractOptions({
83+
flags: {
84+
format: 'groq-type-nodes',
85+
path: '.', // current dir - definitely exists
86+
watch: false,
87+
'watch-patterns': undefined,
88+
workspace: undefined,
89+
} as ExtractSchemaCommand['flags'],
90+
projectRoot,
91+
schemaExtraction: undefined,
92+
})
93+
94+
expect(result.outputPath).toEqual(join(tempDir, 'schema.json'))
95+
})
96+
97+
test('should treat path with .json extension as a file path', () => {
98+
const result = getExtractOptions({
99+
flags: {
100+
format: 'groq-type-nodes',
101+
path: './schema.json',
102+
watch: false,
103+
'watch-patterns': undefined,
104+
workspace: undefined,
105+
} as ExtractSchemaCommand['flags'],
106+
projectRoot: mockProjectRoot,
107+
schemaExtraction: undefined,
108+
})
109+
110+
expect(result.outputPath).toEqual(resolve(join('/test/project', 'schema.json')))
111+
})
112+
113+
test('should treat path with .json extension in subdirectory as a file path', () => {
114+
const result = getExtractOptions({
115+
flags: {
116+
format: 'groq-type-nodes',
117+
path: 'output/my-schema.json',
118+
watch: false,
119+
'watch-patterns': undefined,
120+
workspace: undefined,
121+
} as ExtractSchemaCommand['flags'],
122+
projectRoot: mockProjectRoot,
123+
schemaExtraction: undefined,
124+
})
125+
126+
expect(result.outputPath).toEqual(resolve(join('/test/project', 'output', 'my-schema.json')))
127+
})
128+
129+
test('should treat path with non-.json extension as a file path', () => {
130+
const result = getExtractOptions({
131+
flags: {
132+
format: 'groq-type-nodes',
133+
path: 'output/my-schema.yaml',
134+
watch: false,
135+
'watch-patterns': undefined,
136+
workspace: undefined,
137+
} as ExtractSchemaCommand['flags'],
138+
projectRoot: mockProjectRoot,
139+
schemaExtraction: undefined,
140+
})
141+
142+
expect(result.outputPath).toEqual(resolve(join('/test/project', 'output', 'my-schema.yaml')))
143+
})
144+
145+
test('should respect .json file path when file exists on disk', () => {
146+
const tempDir = mkdtempSync(join(tmpdir(), 'schema-test-'))
147+
writeFileSync(join(tempDir, 'schema.json'), '{}')
148+
const projectRoot = {
149+
directory: tempDir,
150+
path: join(tempDir, 'sanity.config.ts'),
151+
type: 'studio' as const,
152+
}
153+
154+
const result = getExtractOptions({
155+
flags: {
156+
format: 'groq-type-nodes',
157+
path: 'schema.json',
158+
watch: false,
159+
'watch-patterns': undefined,
160+
workspace: undefined,
161+
} as ExtractSchemaCommand['flags'],
162+
projectRoot,
163+
schemaExtraction: undefined,
164+
})
165+
166+
expect(result.outputPath).toEqual(join(tempDir, 'schema.json'))
167+
})
168+
72169
test('should use default values when neither flags nor CLI config are provided', () => {
73170
const result = getExtractOptions({
74171
flags: {
@@ -86,7 +183,7 @@ describe('getExtractOptions', () => {
86183
configPath: '/test/project/sanity.config.ts',
87184
enforceRequiredFields: false,
88185
format: 'groq-type-nodes',
89-
outputPath: join('/test/project', 'schema.json'),
186+
outputPath: resolve(join('/test/project', 'schema.json')),
90187
watchPatterns: [],
91188
workspace: undefined,
92189
})

packages/@sanity/cli/src/actions/schema/getExtractOptions.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import {join, resolve} from 'node:path'
1+
import {existsSync, statSync} from 'node:fs'
2+
import {extname, join, resolve} from 'node:path'
23

34
import {type CliConfig, ProjectRootResult} from '@sanity/cli-core'
45

@@ -25,10 +26,19 @@ export function getExtractOptions({
2526
schemaExtraction,
2627
}: GetExtractionOptions): ExtractOptions {
2728
const pathFlag = flags.path ?? schemaExtraction?.path
28-
const outputDir = pathFlag
29-
? resolve(join(projectRoot.directory, pathFlag))
30-
: projectRoot.directory
31-
const outputPath = join(outputDir, 'schema.json')
29+
let outputPath: string
30+
if (pathFlag) {
31+
const resolved = resolve(join(projectRoot.directory, pathFlag))
32+
const isExistingDirectory = existsSync(resolved) && statSync(resolved).isDirectory()
33+
34+
if (isExistingDirectory || !extname(resolved)) {
35+
outputPath = join(resolved, 'schema.json')
36+
} else {
37+
outputPath = resolved
38+
}
39+
} else {
40+
outputPath = resolve(join(projectRoot.directory, 'schema.json'))
41+
}
3242

3343
return {
3444
configPath: projectRoot.path,

packages/@sanity/cli/src/commands/schema/__tests__/extract.test.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ describe('#schema:extract', {timeout: 60 * 1000}, () => {
105105
expect(existsSync(resolve(cwd, 'schema.json'))).toBe(true)
106106
})
107107

108-
test('should extract schema with path flag', async () => {
108+
test('should extract schema with path flag (directory)', async () => {
109109
const cwd = await testFixture('basic-studio')
110110
process.chdir(cwd)
111111

@@ -117,6 +117,18 @@ describe('#schema:extract', {timeout: 60 * 1000}, () => {
117117
expect(existsSync(resolve(cwd, 'custom-output', 'schema.json'))).toBe(true)
118118
})
119119

120+
test('should extract schema with path flag (file path with .json extension)', async () => {
121+
const cwd = await testFixture('basic-studio')
122+
process.chdir(cwd)
123+
124+
const {error, stderr} = await testCommand(ExtractSchemaCommand, ['--path', './my-schema.json'])
125+
126+
if (error) throw error
127+
expect(stderr).toContain('Extracting schema')
128+
expect(stderr).toContain('Extracted schema')
129+
expect(existsSync(resolve(cwd, 'my-schema.json'))).toBe(true)
130+
})
131+
120132
test('throws an error if format flag is not groq-type-nodes', async () => {
121133
const cwd = await testFixture('basic-studio')
122134
process.chdir(cwd)

0 commit comments

Comments
 (0)