sanity 4.22.0-next.9 → 4.22.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.
- package/bin/sanity +152 -38
- package/lib/_chunks-es/_internal.js +9 -1
- package/lib/_chunks-es/_internal.js.map +1 -1
- package/lib/_chunks-es/index3.js +70 -38
- package/lib/_chunks-es/index3.js.map +1 -1
- package/lib/_chunks-es/package.js +1 -1
- package/lib/_chunks-es/pane.js +53 -65
- package/lib/_chunks-es/pane.js.map +1 -1
- package/lib/_chunks-es/version.js +1 -1
- package/lib/_internal.d.ts +1 -1
- package/lib/_singletons.d.ts +13 -1
- package/lib/desk.d.ts +12 -0
- package/lib/index.d.ts +29 -21
- package/lib/index.js +302 -237
- package/lib/index.js.map +1 -1
- package/lib/presentation.d.ts +7 -1
- package/lib/structure.d.ts +7 -1
- package/package.json +23 -23
package/bin/sanity
CHANGED
|
@@ -1,57 +1,171 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* `npx sanity`: in order for this to work, the `sanity` module should be the one
|
|
4
|
+
* exposing a binary. However, the `@sanity/cli` module is the one who _actually_ ship
|
|
5
|
+
* the CLI binary.
|
|
6
|
+
*
|
|
7
|
+
* To solve this:
|
|
8
|
+
* 1. `@sanity/cli` is a dependency of the `sanity` module
|
|
9
|
+
* 2. The path to this file is configured as `bin.sanity` in the `sanity` module
|
|
10
|
+
* 3. This script resolves the `@sanity/cli` package, finds the path to the `sanity`
|
|
11
|
+
* binary from the `@sanity/cli` package declaration (`bin.sanity`)
|
|
12
|
+
* 4. Either imports the resolved path directly (if it's a Node.js script), or spawns it
|
|
13
|
+
* (if the path specified does not point to a Node.js script)
|
|
14
|
+
*
|
|
15
|
+
* See `runSanityCli()` for more details on the dual approach of importing vs spawning.
|
|
16
|
+
*/
|
|
3
17
|
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import readPkgUp from 'read-pkg-up'
|
|
18
|
+
/* eslint-disable import/no-dynamic-require, no-process-exit */
|
|
19
|
+
import {readFile, open} from 'node:fs/promises'
|
|
7
20
|
import {createRequire} from 'node:module'
|
|
8
|
-
import {
|
|
21
|
+
import {dirname, resolve} from 'node:path'
|
|
22
|
+
import execa from 'execa'
|
|
9
23
|
|
|
10
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
|
-
const expectedPath = path.join(__dirname, '..', 'node_modules', '.bin', 'sanity')
|
|
12
24
|
const require = createRequire(import.meta.url)
|
|
13
25
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
26
|
+
runSanityCli().catch((err) => {
|
|
27
|
+
// Use setImmediate/setTimeout to throw outside the promise chain
|
|
28
|
+
setImmediate(() => {
|
|
29
|
+
throw err
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolves the Sanity CLI binary from `@sanity/cli` and executes it in the best
|
|
35
|
+
* possible way available to us, based on the module type:
|
|
36
|
+
*
|
|
37
|
+
* - If the binary is a Node.js script, we either `import()` or `require()` it based on
|
|
38
|
+
* the extension (cjs/mjs) or the package `type` field. This is prefered as it does
|
|
39
|
+
* not require spawning a new process.
|
|
40
|
+
* - If the binary is not a Node.js script, we spawn it using `execa`, which handles
|
|
41
|
+
* things correctly across platforms (windows doesn't enjoy shebangs etc). stdio is
|
|
42
|
+
* inherited from the parent process.
|
|
43
|
+
*
|
|
44
|
+
* Note:
|
|
45
|
+
* The "new" CLI is an ESM module, while the "old" CLI is CommonJS.
|
|
46
|
+
* In theory, Node.js now has support for transparently mixing CommonJS and ESM, but in
|
|
47
|
+
* practice we've seen some issues with this approach for the CLI specifically. Thus, we
|
|
48
|
+
* explicitly check the package type and use either `import()` or `require()` accordingly.
|
|
49
|
+
*/
|
|
50
|
+
async function runSanityCli() {
|
|
51
|
+
const {bin, type} = await getSanityCliBin()
|
|
52
|
+
|
|
53
|
+
// The optimal case: the binary is a Node.js script - import/require it directly to
|
|
54
|
+
// prevent needing to spawn a new process
|
|
55
|
+
if (await isNodeScript(bin)) {
|
|
56
|
+
if (type === 'module') {
|
|
57
|
+
await import(bin)
|
|
18
58
|
} else {
|
|
19
|
-
|
|
59
|
+
require(bin)
|
|
20
60
|
}
|
|
61
|
+
return
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Fallback: spawn the binary as a child process
|
|
65
|
+
const child = execa(bin, process.argv.slice(2), {
|
|
66
|
+
stdio: 'inherit',
|
|
67
|
+
encoding: null,
|
|
68
|
+
buffer: false,
|
|
69
|
+
reject: false,
|
|
21
70
|
})
|
|
22
|
-
} else {
|
|
23
|
-
runFromCli()
|
|
24
|
-
}
|
|
25
71
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
72
|
+
// Forward signals to the child process
|
|
73
|
+
const signals = ['SIGINT', 'SIGTERM', 'SIGHUP']
|
|
74
|
+
const signalHandlers = signals.map((signal) => {
|
|
75
|
+
const handler = () => child.kill(signal)
|
|
76
|
+
process.on(signal, handler)
|
|
77
|
+
return {signal, handler}
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const {exitCode} = await child
|
|
82
|
+
process.exitCode = exitCode
|
|
83
|
+
} finally {
|
|
84
|
+
// Clean up signal handlers
|
|
85
|
+
for (const {signal, handler} of signalHandlers) {
|
|
86
|
+
process.off(signal, handler)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
33
89
|
}
|
|
34
90
|
|
|
35
|
-
async function
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
91
|
+
async function getSanityCliBin() {
|
|
92
|
+
// Resolve package.json from `@sanity/cli` instead of the main export to allow
|
|
93
|
+
// resolving before the package is built. Note that this might fail if `@sanity/cli`
|
|
94
|
+
// does not explicitly list `./package.json` in the exports field, but since control
|
|
95
|
+
// this module, this feels like a safe assumption.
|
|
96
|
+
const pkgPath = require.resolve('@sanity/cli/package.json')
|
|
97
|
+
|
|
98
|
+
let pkg
|
|
99
|
+
try {
|
|
100
|
+
pkg = JSON.parse(await readFile(pkgPath, 'utf8'))
|
|
101
|
+
} catch (err) {
|
|
102
|
+
throw new Error(`Failed to read @sanity/cli package.json: ${err.message}`)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (!pkg.bin?.sanity) {
|
|
106
|
+
throw new Error('Failed to find `bin.sanity` field in @sanity/cli package.json')
|
|
44
107
|
}
|
|
45
108
|
|
|
46
|
-
|
|
109
|
+
// Assuming `package.json` is located at the root of the package - anything else would
|
|
110
|
+
// be… wild, unconventional, unorthodox… I'd even say… heretical.
|
|
111
|
+
const cliDir = dirname(pkgPath)
|
|
112
|
+
|
|
113
|
+
// Note that npm normalizes bin paths: `./bin/sanity` -> `bin/sanity`
|
|
114
|
+
const bin = resolve(cliDir, pkg.bin.sanity)
|
|
115
|
+
const type = determineModuleType(bin, pkg)
|
|
116
|
+
return {bin, type}
|
|
47
117
|
}
|
|
48
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Determines if the file at `scriptPath` is a Node.js script by checking its shebang.
|
|
121
|
+
* Returns true if:
|
|
122
|
+
* - The shebang interpreter is `node` (e.g., `#!/usr/bin/node`, `#!/usr/bin/env node`)
|
|
123
|
+
* - The file has a `.js`, `.mjs`, or `.cjs` extension (fallback when no shebang)
|
|
124
|
+
*/
|
|
49
125
|
async function isNodeScript(scriptPath) {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
126
|
+
let file
|
|
127
|
+
try {
|
|
128
|
+
file = await open(scriptPath)
|
|
129
|
+
} catch (err) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`Failed to read CLI binary at ${scriptPath}: ${err instanceof Error ? err.message : err}`,
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const {buffer, bytesRead} = await file.read({length: 256})
|
|
137
|
+
const content = buffer.toString('utf8', 0, bytesRead)
|
|
138
|
+
|
|
139
|
+
if (!content.startsWith('#!')) {
|
|
140
|
+
// No shebang - fall back to extension check (.js, .mjs, .cjs)
|
|
141
|
+
// This is not perfect - but it's better than nothing
|
|
142
|
+
return /\.(m?js|cjs)$/.test(scriptPath)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Extract the interpreter, handling both direct paths and `env` invocations:
|
|
146
|
+
// `#!/usr/bin/node` => `node`
|
|
147
|
+
// `#!/usr/bin/env node` => `node`
|
|
148
|
+
// `#!/usr/bin/env -S node --flags` => `node`
|
|
149
|
+
const firstLine = content.slice(2, content.indexOf('\n')).trim()
|
|
150
|
+
const parts = firstLine.split(/\s+/)
|
|
151
|
+
const interpreter = parts[0].endsWith('/env')
|
|
152
|
+
? parts.find((part, i) => i > 0 && !part.startsWith('-'))
|
|
153
|
+
: parts[0]
|
|
154
|
+
|
|
155
|
+
return interpreter && (interpreter.endsWith('node') || interpreter.endsWith('node.exe'))
|
|
156
|
+
} finally {
|
|
157
|
+
await file.close()
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function determineModuleType(binPath, pkgJson) {
|
|
162
|
+
if (binPath.endsWith('.cjs')) {
|
|
163
|
+
return 'commonjs'
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (binPath.endsWith('.mjs')) {
|
|
167
|
+
return 'module'
|
|
168
|
+
}
|
|
53
169
|
|
|
54
|
-
|
|
55
|
-
const firstLine = content.trimStart().slice(0, content.indexOf('\n'))
|
|
56
|
-
return firstLine.endsWith('node')
|
|
170
|
+
return pkgJson.type === 'module' ? 'module' : 'commonjs'
|
|
57
171
|
}
|
|
@@ -1682,7 +1682,7 @@ const MINIMUM_API_VERSION = "2025-02-19", importDatasetCommand = {
|
|
|
1682
1682
|
skipCrossDatasetReferences,
|
|
1683
1683
|
allowSystemDocuments,
|
|
1684
1684
|
replaceAssets
|
|
1685
|
-
} = flags, operation = getMutationOperation(args.extOptions), client = apiClient(), [file, target] = args.argsWithoutOptions;
|
|
1685
|
+
} = flags, operation = getMutationOperation(args.extOptions), releasesOperation = getReleasesOperation(flags), client = apiClient(), [file, target] = args.argsWithoutOptions;
|
|
1686
1686
|
if (!file)
|
|
1687
1687
|
throw new Error(`Source file name and target dataset must be specified ("sanity dataset import ${chalk.bold("[file]")} [dataset]")`);
|
|
1688
1688
|
const targetDataset = await determineTargetDataset(target, context);
|
|
@@ -1742,6 +1742,7 @@ const MINIMUM_API_VERSION = "2025-02-19", importDatasetCommand = {
|
|
|
1742
1742
|
client: importClient,
|
|
1743
1743
|
assetsBase,
|
|
1744
1744
|
operation,
|
|
1745
|
+
releasesOperation,
|
|
1745
1746
|
onProgress,
|
|
1746
1747
|
allowFailingAssets,
|
|
1747
1748
|
allowAssetsInDifferentDataset,
|
|
@@ -1805,6 +1806,13 @@ function getMutationOperation(flags) {
|
|
|
1805
1806
|
throw new Error("Cannot use both --replace and --missing");
|
|
1806
1807
|
return flags.replace ? "createOrReplace" : flags.missing ? "createIfNotExists" : "create";
|
|
1807
1808
|
}
|
|
1809
|
+
function getReleasesOperation(flags) {
|
|
1810
|
+
const {
|
|
1811
|
+
replace,
|
|
1812
|
+
missing
|
|
1813
|
+
} = flags;
|
|
1814
|
+
return replace ? "replace" : missing ? "ignore" : "fail";
|
|
1815
|
+
}
|
|
1808
1816
|
function getPercentage(opts) {
|
|
1809
1817
|
if (!opts.total || typeof opts.current > "u")
|
|
1810
1818
|
return "";
|