@sanity/plugin-kit 5.0.2 → 6.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.
- package/README.md +26 -17
- package/dist/_chunks-es/init.js +2 -2
- package/dist/_chunks-es/init2.js +711 -12
- package/dist/_chunks-es/init2.js.map +1 -1
- package/dist/_chunks-es/inject.js +2 -2
- package/dist/_chunks-es/package.js +391 -1084
- package/dist/_chunks-es/package.js.map +1 -1
- package/dist/_chunks-es/package2.js +1 -1
- package/dist/_chunks-es/{ts.js → verify-common.js} +75 -75
- package/dist/_chunks-es/verify-common.js.map +1 -0
- package/dist/_chunks-es/verify-package.js +7 -4
- package/dist/_chunks-es/verify-package.js.map +1 -1
- package/dist/_chunks-es/verify-studio.js +1 -1
- package/dist/_chunks-es/verify-studio.js.map +1 -1
- package/package.json +5 -3
- package/assets/inject/sanity.json +0 -8
- package/assets/inject/ui-workshop/src/CustomField.tsx +0 -15
- package/assets/inject/ui-workshop/src/__workshop__/index.tsx +0 -14
- package/assets/inject/ui-workshop/src/__workshop__/props.tsx +0 -21
- package/assets/inject/ui-workshop/workshop.config.ts +0 -5
- package/assets/inject/v2-incompatible.js.template +0 -11
- package/dist/_chunks-es/ts.js.map +0 -1
|
@@ -3,444 +3,43 @@ import path from "path";
|
|
|
3
3
|
import util from "util";
|
|
4
4
|
import githubUrlToObject from "github-url-to-object";
|
|
5
5
|
import validateNpmPackageName from "validate-npm-package-name";
|
|
6
|
-
import { URL, fileURLToPath } from "url";
|
|
7
|
-
import licenses from "@rexxars/choosealicense-list";
|
|
8
|
-
import gitRemoteOriginUrl from "git-remote-origin-url";
|
|
9
|
-
import { log, urls, minPkgUtilsMajor, requiredNodeEngine, incompatiblePluginPackage, cliName } from "./index.js";
|
|
10
6
|
import { createRequire } from "node:module";
|
|
11
7
|
import chalk from "chalk";
|
|
12
|
-
import outdent
|
|
8
|
+
import outdent from "outdent";
|
|
9
|
+
import { log, urls, minPkgUtilsMajor, requiredNodeEngine, incompatiblePluginPackage, cliName } from "./index.js";
|
|
13
10
|
import crypto from "crypto";
|
|
14
11
|
import json5 from "json5";
|
|
15
12
|
import pAny from "p-any";
|
|
16
|
-
import {
|
|
13
|
+
import { URL } from "url";
|
|
17
14
|
import inquirer from "inquirer";
|
|
18
|
-
import
|
|
15
|
+
import { pkg } from "./package2.js";
|
|
16
|
+
import { getLatestVersion } from "get-latest-version";
|
|
19
17
|
import pProps from "p-props";
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
value: JSON.stringify(eslintConfig, null, 2)
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function eslintignoreTemplate(options) {
|
|
49
|
-
const { flags, outDir } = options, patterns = [
|
|
50
|
-
".eslintrc.js",
|
|
51
|
-
"commitlint.config.js",
|
|
52
|
-
outDir,
|
|
53
|
-
"lint-staged.config.js",
|
|
54
|
-
"package.config.ts",
|
|
55
|
-
flags.typescript ? "*.js" : ""
|
|
56
|
-
].filter(Boolean);
|
|
57
|
-
return patterns.sort(), {
|
|
58
|
-
type: "template",
|
|
59
|
-
force: flags.force,
|
|
60
|
-
to: ".eslintignore",
|
|
61
|
-
value: patterns.join(`
|
|
62
|
-
`)
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
function gitignoreTemplate() {
|
|
66
|
-
return {
|
|
67
|
-
type: "template",
|
|
68
|
-
to: ".gitignore",
|
|
69
|
-
value: outdent`
|
|
70
|
-
# Logs
|
|
71
|
-
logs
|
|
72
|
-
*.log
|
|
73
|
-
npm-debug.log*
|
|
74
|
-
|
|
75
|
-
# Runtime data
|
|
76
|
-
pids
|
|
77
|
-
*.pid
|
|
78
|
-
*.seed
|
|
79
|
-
|
|
80
|
-
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
81
|
-
lib-cov
|
|
82
|
-
|
|
83
|
-
# Coverage directory used by tools like istanbul
|
|
84
|
-
coverage
|
|
85
|
-
|
|
86
|
-
# nyc test coverage
|
|
87
|
-
.nyc_output
|
|
88
|
-
|
|
89
|
-
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
|
90
|
-
.grunt
|
|
91
|
-
|
|
92
|
-
# node-waf configuration
|
|
93
|
-
.lock-wscript
|
|
94
|
-
|
|
95
|
-
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
|
96
|
-
build/Release
|
|
97
|
-
|
|
98
|
-
# Dependency directories
|
|
99
|
-
node_modules
|
|
100
|
-
jspm_packages
|
|
101
|
-
|
|
102
|
-
# Optional npm cache directory
|
|
103
|
-
.npm
|
|
104
|
-
|
|
105
|
-
# Optional REPL history
|
|
106
|
-
.node_repl_history
|
|
107
|
-
|
|
108
|
-
# macOS finder cache file
|
|
109
|
-
.DS_Store
|
|
110
|
-
|
|
111
|
-
# VS Code settings
|
|
112
|
-
.vscode
|
|
113
|
-
|
|
114
|
-
# IntelliJ
|
|
115
|
-
.idea
|
|
116
|
-
*.iml
|
|
117
|
-
|
|
118
|
-
# Cache
|
|
119
|
-
.cache
|
|
120
|
-
|
|
121
|
-
# Yalc
|
|
122
|
-
.yalc
|
|
123
|
-
yalc.lock
|
|
124
|
-
|
|
125
|
-
# npm package zips
|
|
126
|
-
*.tgz
|
|
127
|
-
`
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
function pkgConfigTemplate(options) {
|
|
131
|
-
const { flags, outDir } = options;
|
|
132
|
-
return {
|
|
133
|
-
type: "template",
|
|
134
|
-
force: flags.force,
|
|
135
|
-
// Always a `.ts` config: plugins are ESM (`"type": "module"`), so `@sanity/pkg-utils`
|
|
136
|
-
// loads it without needing a `.mts`/`.mjs` extension to force ESM interpretation.
|
|
137
|
-
to: "package.config.ts",
|
|
138
|
-
value: outdent`
|
|
139
|
-
import {defineConfig} from '@sanity/pkg-utils'
|
|
140
|
-
|
|
141
|
-
export default defineConfig({
|
|
142
|
-
dist: '${outDir}',
|
|
143
|
-
tsconfig: 'tsconfig.${outDir}.json',
|
|
144
|
-
|
|
145
|
-
// Remove this block to enable strict export validation
|
|
146
|
-
extract: {
|
|
147
|
-
rules: {
|
|
148
|
-
'ae-incompatible-release-tags': 'off',
|
|
149
|
-
'ae-internal-missing-underscore': 'off',
|
|
150
|
-
'ae-missing-release-tag': 'off',
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
})
|
|
154
|
-
`
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
function prettierignoreTemplate(options) {
|
|
158
|
-
const { outDir } = options;
|
|
159
|
-
return {
|
|
160
|
-
type: "template",
|
|
161
|
-
to: ".prettierignore",
|
|
162
|
-
value: [outDir, "pnpm-lock.yaml", "yarn.lock", "package-lock.json"].join(`
|
|
163
|
-
`)
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
function tsconfigTemplate(options) {
|
|
167
|
-
const { flags } = options;
|
|
168
|
-
return {
|
|
169
|
-
type: "template",
|
|
170
|
-
force: flags.force,
|
|
171
|
-
to: "tsconfig.json",
|
|
172
|
-
value: outdent`
|
|
173
|
-
{
|
|
174
|
-
"extends": "./tsconfig.settings",
|
|
175
|
-
"include": ["./src", "./package.config.ts"]
|
|
176
|
-
}
|
|
177
|
-
`
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
function tsconfigTemplateDist(options) {
|
|
181
|
-
const { flags, outDir } = options;
|
|
182
|
-
return {
|
|
183
|
-
type: "template",
|
|
184
|
-
force: flags.force,
|
|
185
|
-
to: `tsconfig.${outDir}.json`,
|
|
186
|
-
value: outdent`
|
|
187
|
-
{
|
|
188
|
-
"extends": "./tsconfig.settings",
|
|
189
|
-
"include": ["./src"],
|
|
190
|
-
"exclude": [
|
|
191
|
-
"./src/**/__fixtures__",
|
|
192
|
-
"./src/**/__mocks__",
|
|
193
|
-
"./src/**/*.test.ts",
|
|
194
|
-
"./src/**/*.test.tsx"
|
|
195
|
-
]
|
|
196
|
-
}
|
|
197
|
-
`
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
function tsconfigTemplateSettings(options) {
|
|
201
|
-
const { flags, outDir } = options;
|
|
202
|
-
return {
|
|
203
|
-
type: "template",
|
|
204
|
-
force: flags.force,
|
|
205
|
-
to: "tsconfig.settings.json",
|
|
206
|
-
value: outdent`
|
|
207
|
-
{
|
|
208
|
-
"compilerOptions": {
|
|
209
|
-
"rootDir": ".",
|
|
210
|
-
"outDir": "./${outDir}",
|
|
211
|
-
|
|
212
|
-
"target": "esnext",
|
|
213
|
-
"jsx": "preserve",
|
|
214
|
-
"module": "preserve",
|
|
215
|
-
"moduleResolution": "bundler",
|
|
216
|
-
"esModuleInterop": true,
|
|
217
|
-
"resolveJsonModule": true,
|
|
218
|
-
"moduleDetection": "force",
|
|
219
|
-
"strict": true,
|
|
220
|
-
"allowSyntheticDefaultImports": true,
|
|
221
|
-
"skipLibCheck": true,
|
|
222
|
-
"forceConsistentCasingInFileNames": true,
|
|
223
|
-
"isolatedModules": true,
|
|
224
|
-
|
|
225
|
-
// Don't emit by default, pkg-utils will ignore this when generating .d.ts files
|
|
226
|
-
"noEmit": true
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
`
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
const renovatePreset = {
|
|
233
|
-
name: "renovatebot",
|
|
234
|
-
description: "Files to enable renovatebot.",
|
|
235
|
-
apply: applyPreset$3
|
|
236
|
-
};
|
|
237
|
-
async function applyPreset$3(options) {
|
|
238
|
-
await writeAssets(
|
|
239
|
-
[
|
|
240
|
-
{
|
|
241
|
-
type: "copy",
|
|
242
|
-
from: ["renovatebot", "renovate.json"],
|
|
243
|
-
to: "renovate.json"
|
|
244
|
-
}
|
|
245
|
-
],
|
|
246
|
-
options
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
const lockedDependencies = {
|
|
250
|
-
"styled-components": "^6.1",
|
|
251
|
-
eslint: "^8.57.0"
|
|
252
|
-
};
|
|
253
|
-
function resolveLatestVersions(packages) {
|
|
254
|
-
const versions = {};
|
|
255
|
-
for (const pkgName of packages)
|
|
256
|
-
versions[pkgName] = pkgName in lockedDependencies ? lockedDependencies[pkgName] : "latest";
|
|
257
|
-
return pProps(
|
|
258
|
-
versions,
|
|
259
|
-
async (range, pkgName) => {
|
|
260
|
-
const version = await getLatestVersion(pkgName, { range });
|
|
261
|
-
if (!version)
|
|
262
|
-
throw new Error(`Found no version for ${pkgName}`);
|
|
263
|
-
return rangeify(version);
|
|
264
|
-
},
|
|
265
|
-
{ concurrency: 8 }
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
function rangeify(version) {
|
|
269
|
-
return `^${version}`;
|
|
270
|
-
}
|
|
271
|
-
function errorToUndefined(err) {
|
|
272
|
-
if (err instanceof TypeError)
|
|
273
|
-
throw err;
|
|
274
|
-
}
|
|
275
|
-
const buildExtensions = [".js", ".jsx", ".es6", ".es", ".mjs", ".ts", ".tsx"], stat$1 = util.promisify(fs.stat), readFile$2 = util.promisify(fs.readFile), allowedPartProps = ["name", "implements", "path", "description"], disallowedPluginProps = ["api", "project", "plugins", "env"];
|
|
276
|
-
async function getPaths(options) {
|
|
277
|
-
const { basePath } = options, manifest = await readManifest(options);
|
|
278
|
-
return manifest.paths ? absolutifyPaths(manifest.paths, basePath) : null;
|
|
279
|
-
}
|
|
280
|
-
function absolutifyPaths(paths, basePath) {
|
|
281
|
-
const getPath = (relative) => relative ? path.resolve(path.join(basePath, relative)) : void 0;
|
|
282
|
-
return paths ? {
|
|
283
|
-
basePath,
|
|
284
|
-
compiled: getPath(paths.compiled),
|
|
285
|
-
source: getPath(paths.source)
|
|
286
|
-
} : { basePath };
|
|
287
|
-
}
|
|
288
|
-
async function readManifest(options) {
|
|
289
|
-
const { basePath, validate: validate2 = !0 } = options, manifestPath = path.normalize(path.join(basePath, "sanity.json"));
|
|
290
|
-
let content;
|
|
291
|
-
try {
|
|
292
|
-
content = await readFile$2(manifestPath, "utf8");
|
|
293
|
-
} catch (err) {
|
|
294
|
-
throw err.code === "ENOENT" ? new Error(
|
|
295
|
-
`No sanity.json found. sanity.json is required for plugins to function. Use \`${pkg.binname} init\` for a new plugin, or create an empty \`sanity.json\` with an empty object (\`{}\`) for existing ones.`
|
|
296
|
-
) : new Error(`Failed to read "${manifestPath}": ${err.message}`);
|
|
297
|
-
}
|
|
298
|
-
let parsed;
|
|
299
|
-
try {
|
|
300
|
-
parsed = JSON.parse(content);
|
|
301
|
-
} catch (err) {
|
|
302
|
-
throw new Error(`Error parsing "${manifestPath}": ${err.message}`);
|
|
303
|
-
}
|
|
304
|
-
return validate2 && await validateManifest(parsed, options), parsed;
|
|
305
|
-
}
|
|
306
|
-
async function validateManifest(manifest, opts) {
|
|
307
|
-
const options = { isPlugin: !0, ...opts };
|
|
308
|
-
if (!isObject$1(manifest))
|
|
309
|
-
throw new Error("Invalid sanity.json: Root must be an object");
|
|
310
|
-
if (options.isPlugin ? await validatePluginManifest(manifest, options) : validateProjectManifest(manifest), "root" in manifest && typeof manifest.root != "boolean")
|
|
311
|
-
throw new Error('Invalid sanity.json: "root" property must be a boolean if declared');
|
|
312
|
-
await validateParts(manifest, {
|
|
313
|
-
...options,
|
|
314
|
-
paths: absolutifyPaths(manifest.paths, options.basePath)
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
function validateProjectManifest(manifest) {
|
|
318
|
-
if ("paths" in manifest)
|
|
319
|
-
throw new Error('Invalid sanity.json: "paths" property has no meaning in a project manifest');
|
|
320
|
-
}
|
|
321
|
-
async function validatePluginManifest(manifest, options) {
|
|
322
|
-
const disallowed = Object.keys(manifest).filter((key) => disallowedPluginProps.includes(key)).map((key) => `"${key}"`);
|
|
323
|
-
if (disallowed.length > 0) {
|
|
324
|
-
const plural = disallowed.length > 1 ? "s" : "", joined = disallowed.join(", ");
|
|
325
|
-
throw new Error(
|
|
326
|
-
`Invalid sanity.json: Key${plural} ${joined} ${plural ? "are" : "is"} not allowed in a plugin manifest`
|
|
327
|
-
);
|
|
328
|
-
}
|
|
329
|
-
if (manifest.root)
|
|
330
|
-
throw new Error('Invalid sanity.json: "root" cannot be truthy in a plugin manifest');
|
|
331
|
-
await validatePaths$1(manifest, options);
|
|
332
|
-
}
|
|
333
|
-
async function validatePaths$1(manifest, options) {
|
|
334
|
-
if (!("paths" in manifest))
|
|
335
|
-
return;
|
|
336
|
-
if (!isObject$1(manifest.paths))
|
|
337
|
-
throw new Error('Invalid sanity.json: "paths" must be an object if declared');
|
|
338
|
-
if (typeof manifest.paths.compiled != "string")
|
|
339
|
-
throw new Error(
|
|
340
|
-
'Invalid sanity.json: "paths" must have a (string) "compiled" property if declared'
|
|
341
|
-
);
|
|
342
|
-
if (typeof manifest.paths.source != "string")
|
|
343
|
-
throw new Error(
|
|
344
|
-
'Invalid sanity.json: "paths" must have a (string) "source" property if declared'
|
|
345
|
-
);
|
|
346
|
-
const sourcePath = path.resolve(options.basePath, manifest.paths.source);
|
|
347
|
-
let srcStats;
|
|
348
|
-
try {
|
|
349
|
-
srcStats = await stat$1(sourcePath);
|
|
350
|
-
} catch (err) {
|
|
351
|
-
if (err.code === "ENOENT")
|
|
352
|
-
throw new Error(`sanity.json references "source" path which does not exist: "${sourcePath}"`);
|
|
353
|
-
}
|
|
354
|
-
if (!srcStats?.isDirectory())
|
|
355
|
-
throw new Error(
|
|
356
|
-
`sanity.json references "source" path which is not a directory: "${sourcePath}"`
|
|
357
|
-
);
|
|
358
|
-
}
|
|
359
|
-
async function validateParts(manifest, options) {
|
|
360
|
-
if (!("parts" in manifest))
|
|
361
|
-
return;
|
|
362
|
-
if (!Array.isArray(manifest.parts))
|
|
363
|
-
throw new Error('Invalid sanity.json: "parts" must be an array if declared');
|
|
364
|
-
let i = 0;
|
|
365
|
-
for (const part of manifest.parts)
|
|
366
|
-
await validatePart(part, i, options), i++;
|
|
367
|
-
}
|
|
368
|
-
async function validatePart(part, index, options) {
|
|
369
|
-
if (!isObject$1(part))
|
|
370
|
-
throw new Error(`Invalid sanity.json: "parts[${index}]" must be an object`);
|
|
371
|
-
validateAllowedPartKeys(part, index), validatePartStringValues(part, index), validatePartNames(part, index, options), await validatePartFiles(part, index, options);
|
|
372
|
-
}
|
|
373
|
-
async function validatePartFiles(part, index, options) {
|
|
374
|
-
const { verifyCompiledParts, verifySourceParts, paths } = options;
|
|
375
|
-
if (!part?.path)
|
|
376
|
-
return;
|
|
377
|
-
const ext = path.extname(part.path);
|
|
378
|
-
if (paths?.source && ext && ext !== ".js" && buildExtensions.includes(ext))
|
|
379
|
-
throw new Error(
|
|
380
|
-
`Invalid sanity.json: Part path has extension which is not applicable after compiling. ${ext} becomes .js after compiling. Specify filename without extension (${path.basename(
|
|
381
|
-
part.path
|
|
382
|
-
)}) (parts[${index}])`
|
|
383
|
-
);
|
|
384
|
-
if (!verifySourceParts && !verifyCompiledParts)
|
|
385
|
-
return;
|
|
386
|
-
const [srcExists, outDirExists] = await Promise.all([
|
|
387
|
-
hasSourceFile(part.path, paths),
|
|
388
|
-
verifyCompiledParts && hasCompiledFile(part.path, paths)
|
|
389
|
-
]);
|
|
390
|
-
if (!srcExists)
|
|
391
|
-
throw new Error(
|
|
392
|
-
`Invalid sanity.json: Part path references file that does not exist in source directory (${paths?.source || paths?.basePath}) (parts[${index}])`
|
|
393
|
-
);
|
|
394
|
-
if (verifyCompiledParts && !outDirExists)
|
|
395
|
-
throw new Error(
|
|
396
|
-
`Invalid sanity.json: Part path references file ("${part.path}") that does not exist in compiled directory (${paths?.compiled}) (parts[${index}])`
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
function validatePartNames(part, index, options) {
|
|
400
|
-
const pluginName = options.pluginName ? options.pluginName.replace(/^sanity-plugin-/, "") : "";
|
|
401
|
-
if (!part?.name || !part?.name?.startsWith(`part:${pluginName}/`))
|
|
402
|
-
throw new Error(
|
|
403
|
-
`Invalid sanity.json: "name" must be prefixed with "part:${pluginName}/" - got "${part?.name}" (parts[${index}])`
|
|
404
|
-
);
|
|
405
|
-
if (!part?.implements?.startsWith("part:"))
|
|
406
|
-
throw new Error(
|
|
407
|
-
`Invalid sanity.json: "implements" must be prefixed with "part:" - got "${part?.implements}" (parts[${index}])`
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
function validateAllowedPartKeys(part, index) {
|
|
411
|
-
const disallowed = Object.keys(part).filter((key) => !allowedPartProps.includes(key)).map((key) => `"${key}"`);
|
|
412
|
-
if (disallowed.length > 0) {
|
|
413
|
-
const plural = disallowed.length > 1 ? "s" : "", joined = disallowed.join(", ");
|
|
414
|
-
throw new Error(
|
|
415
|
-
`Invalid sanity.json: Key${plural} ${joined} ${plural ? "are" : "is"} not allowed in a part declaration (parts[${index}])`
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
function validatePartStringValues(part, index) {
|
|
420
|
-
const nonStrings = Object.keys(part).filter((key) => typeof part[key] != "string").map((key) => `"${key}"`);
|
|
421
|
-
if (nonStrings.length > 0) {
|
|
422
|
-
const plural = nonStrings.length > 1 ? "s" : "", joined = nonStrings.join(", ");
|
|
423
|
-
throw new Error(
|
|
424
|
-
`Invalid sanity.json: Key${plural} ${joined} should be of type string (parts[${index}])`
|
|
425
|
-
);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
function isObject$1(obj) {
|
|
429
|
-
return !Array.isArray(obj) && obj !== null && typeof obj == "object";
|
|
430
|
-
}
|
|
431
|
-
async function hasSanityJson(basePath) {
|
|
432
|
-
const file = await readJsonFile(path.join(basePath, "sanity.json")).catch(
|
|
433
|
-
errorToUndefined
|
|
434
|
-
);
|
|
435
|
-
return { exists: !!file, isRoot: !!(file && file.root) };
|
|
436
|
-
}
|
|
437
|
-
async function findStudioV3Config(basePath) {
|
|
438
|
-
const jsFile = "sanity.config.js";
|
|
439
|
-
if (await fileExists(path.join(basePath, jsFile)))
|
|
440
|
-
return { v3ConfigFile: jsFile };
|
|
441
|
-
const tsFile = "sanity.config.ts";
|
|
442
|
-
return { v3ConfigFile: await fileExists(path.join(basePath, tsFile)) ? tsFile : void 0 };
|
|
443
|
-
}
|
|
18
|
+
const mergedPackages = [
|
|
19
|
+
"@sanity/base",
|
|
20
|
+
"@sanity/core",
|
|
21
|
+
"@sanity/types",
|
|
22
|
+
"@sanity/data-aspects",
|
|
23
|
+
"@sanity/default-layout",
|
|
24
|
+
"@sanity/default-login",
|
|
25
|
+
"@sanity/desk-tool",
|
|
26
|
+
"@sanity/field",
|
|
27
|
+
"@sanity/form-builder",
|
|
28
|
+
"@sanity/initial-value-templates",
|
|
29
|
+
"@sanity/language-filter",
|
|
30
|
+
"@sanity/production-preview",
|
|
31
|
+
"@sanity/react-hooks",
|
|
32
|
+
"@sanity/resolver",
|
|
33
|
+
"@sanity/state-router",
|
|
34
|
+
"@sanity/structure",
|
|
35
|
+
"@sanity/studio-hints"
|
|
36
|
+
].sort(), deprecatedDevDeps = [
|
|
37
|
+
"tsdx",
|
|
38
|
+
"sanipack",
|
|
39
|
+
"parcel",
|
|
40
|
+
"@parcel/packager-ts",
|
|
41
|
+
"@parcel/transformer-typescript-types"
|
|
42
|
+
], buildExtensions = [".js", ".jsx", ".es6", ".es", ".mjs", ".ts", ".tsx"];
|
|
444
43
|
async function prompt(message, options) {
|
|
445
44
|
const type = options.choices ? "list" : options.type, result = await inquirer.prompt([{ ...options, type, message, name: "single" }]);
|
|
446
45
|
return result && result.single;
|
|
@@ -477,7 +76,7 @@ function promptForRepoOrigin(_options, defaultVal) {
|
|
|
477
76
|
}
|
|
478
77
|
});
|
|
479
78
|
}
|
|
480
|
-
const stat = util.promisify(fs.stat), mkdir = util.promisify(fs.mkdir), readdir = util.promisify(fs.readdir), copyFile = util.promisify(fs.copyFile), readFile$
|
|
79
|
+
const stat$1 = util.promisify(fs.stat), mkdir = util.promisify(fs.mkdir), readdir = util.promisify(fs.readdir), copyFile = util.promisify(fs.copyFile), readFile$2 = util.promisify(fs.readFile), writeFile = util.promisify(fs.writeFile);
|
|
481
80
|
function hasSourceEquivalent(compiledFile, paths) {
|
|
482
81
|
if (!paths.source)
|
|
483
82
|
return fileExists(
|
|
@@ -504,13 +103,13 @@ function hasCompiledFile(filePath, paths) {
|
|
|
504
103
|
}
|
|
505
104
|
function buildCandidateExists(pathStub) {
|
|
506
105
|
const candidates = buildExtensions.map((extCandidate) => `${pathStub}${extCandidate}`);
|
|
507
|
-
return pAny(candidates.map((candidate) => stat(candidate))).then(() => !0).catch(() => !1);
|
|
106
|
+
return pAny(candidates.map((candidate) => stat$1(candidate))).then(() => !0).catch(() => !1);
|
|
508
107
|
}
|
|
509
108
|
function fileExists(filePath) {
|
|
510
|
-
return stat(filePath).then(() => !0).catch(() => !1);
|
|
109
|
+
return stat$1(filePath).then(() => !0).catch(() => !1);
|
|
511
110
|
}
|
|
512
111
|
async function readJsonFile(filePath) {
|
|
513
|
-
const content = await readFile$
|
|
112
|
+
const content = await readFile$2(filePath, "utf8");
|
|
514
113
|
return JSON.parse(content);
|
|
515
114
|
}
|
|
516
115
|
function writeJsonFile(filePath, content) {
|
|
@@ -570,7 +169,7 @@ async function readFileContent({
|
|
|
570
169
|
}) {
|
|
571
170
|
const filepath = path.normalize(path.join(basePath, filename));
|
|
572
171
|
try {
|
|
573
|
-
return await readFile$
|
|
172
|
+
return await readFile$2(filepath, "utf8");
|
|
574
173
|
} catch (err) {
|
|
575
174
|
if (err.code === "ENOENT") {
|
|
576
175
|
log.debug(`No ${filename} file found.`);
|
|
@@ -586,575 +185,15 @@ async function readJson5File({
|
|
|
586
185
|
const content = await readFileContent({ filename, basePath });
|
|
587
186
|
if (content)
|
|
588
187
|
return parseJson5(content, filename);
|
|
589
|
-
}
|
|
590
|
-
function parseJson5(content, errorKey) {
|
|
591
|
-
try {
|
|
592
|
-
return json5.parse(content);
|
|
593
|
-
} catch (err) {
|
|
594
|
-
throw new Error(`Error parsing "${errorKey}": ${err.message}`);
|
|
595
|
-
}
|
|
596
|
-
}
|
|
597
|
-
const request = getIt([
|
|
598
|
-
promise({ onlyBody: !0 }),
|
|
599
|
-
jsonRequest(),
|
|
600
|
-
jsonResponse(),
|
|
601
|
-
httpErrors(),
|
|
602
|
-
headers({ "User-Agent": `${pkg.name}@${pkg.version}` })
|
|
603
|
-
]);
|
|
604
|
-
async function getUserInfo({ requireUserConfirmation, flags }, pkg2) {
|
|
605
|
-
const userInfo = getPackageUserInfo({ author: flags.author ?? pkg2?.author }) || await getSanityUserInfo() || await getGitUserInfo();
|
|
606
|
-
return requireUserConfirmation ? promptForInfo(userInfo) : userInfo;
|
|
607
|
-
}
|
|
608
|
-
function getPackageUserInfo(pkg2) {
|
|
609
|
-
let author = pkg2?.author;
|
|
610
|
-
if (!author)
|
|
611
|
-
return;
|
|
612
|
-
if (author && typeof author != "string")
|
|
613
|
-
return author;
|
|
614
|
-
if (!author.includes("@"))
|
|
615
|
-
return { name: author };
|
|
616
|
-
const [pre, ...post] = author.replace(/[<>[\]]/g, "").split(/@/), nameParts = pre.split(/\s+/), email = [nameParts[nameParts.length - 1], ...post].join("@");
|
|
617
|
-
return { name: nameParts.slice(0, -1).join(" "), email };
|
|
618
|
-
}
|
|
619
|
-
async function promptForInfo(defValue) {
|
|
620
|
-
const name = await prompt("Author name", {
|
|
621
|
-
filter: filterString,
|
|
622
|
-
default: defValue && defValue.name,
|
|
623
|
-
validate: requiredString
|
|
624
|
-
}), email = await prompt("Author email", {
|
|
625
|
-
filter: filterString,
|
|
626
|
-
default: defValue && defValue.email,
|
|
627
|
-
validate: validOrEmptyEmail
|
|
628
|
-
});
|
|
629
|
-
return { name, email };
|
|
630
|
-
}
|
|
631
|
-
async function getSanityUserInfo() {
|
|
632
|
-
try {
|
|
633
|
-
const token = (await readJsonFile(
|
|
634
|
-
path.join(xdgBasedir.config ?? "", "sanity", "config.json")
|
|
635
|
-
))?.authToken;
|
|
636
|
-
if (!token)
|
|
637
|
-
return;
|
|
638
|
-
const user = await request({
|
|
639
|
-
url: "https://api.sanity.io/v1/users/me",
|
|
640
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
641
|
-
});
|
|
642
|
-
if (!user)
|
|
643
|
-
return;
|
|
644
|
-
const { name, email } = user;
|
|
645
|
-
return { name, email };
|
|
646
|
-
} catch {
|
|
647
|
-
return;
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
async function getGitUserInfo() {
|
|
651
|
-
try {
|
|
652
|
-
const name = execSync("git config user.name", { encoding: "utf8" }).trim(), email = execSync("git config user.email", { encoding: "utf8" }).trim();
|
|
653
|
-
return name ? { name, email: email || void 0 } : void 0;
|
|
654
|
-
} catch {
|
|
655
|
-
return;
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
function filterString(val) {
|
|
659
|
-
return (val || "").trim();
|
|
660
|
-
}
|
|
661
|
-
function requiredString(value) {
|
|
662
|
-
return value.length > 1 ? !0 : "Required";
|
|
663
|
-
}
|
|
664
|
-
function validOrEmptyEmail(value) {
|
|
665
|
-
return value ? validate(value) ? !0 : "Must either be a valid email or empty" : !0;
|
|
666
|
-
}
|
|
667
|
-
function generateReadme(data) {
|
|
668
|
-
const { user, pluginName, license } = data;
|
|
669
|
-
return outdent$1`
|
|
670
|
-
# ${pluginName}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
${installationSnippet(pluginName ?? "unknown")}
|
|
674
|
-
|
|
675
|
-
## Usage
|
|
676
|
-
|
|
677
|
-
Add it as a plugin in \`sanity.config.ts\` (or .js):
|
|
678
|
-
|
|
679
|
-
\`\`\`ts
|
|
680
|
-
import {defineConfig} from 'sanity'
|
|
681
|
-
import {myPlugin} from '${pluginName}'
|
|
682
|
-
|
|
683
|
-
export default defineConfig({
|
|
684
|
-
//...
|
|
685
|
-
plugins: [myPlugin({})],
|
|
686
|
-
})
|
|
687
|
-
\`\`\`
|
|
688
|
-
|
|
689
|
-
${getLicenseText(license?.id, user?.name ? user : void 0)}
|
|
690
|
-
${developTestSnippet()}
|
|
691
|
-
` + `
|
|
692
|
-
`;
|
|
693
|
-
}
|
|
694
|
-
function installationSnippet(packageName) {
|
|
695
|
-
return outdent$1`
|
|
696
|
-
## Installation
|
|
697
|
-
|
|
698
|
-
\`\`\`sh
|
|
699
|
-
npm install ${packageName}
|
|
700
|
-
\`\`\`
|
|
701
|
-
`;
|
|
702
|
-
}
|
|
703
|
-
function developTestSnippet() {
|
|
704
|
-
return outdent$1`
|
|
705
|
-
## Develop & test
|
|
706
|
-
|
|
707
|
-
This plugin uses [@sanity/plugin-kit](https://github.com/sanity-io/plugin-kit)
|
|
708
|
-
with default configuration for build & watch scripts.
|
|
709
|
-
|
|
710
|
-
See [Testing a plugin in Sanity Studio](https://github.com/sanity-io/plugin-kit#testing-a-plugin-in-sanity-studio)
|
|
711
|
-
on how to run this plugin with hotreload in the studio.
|
|
712
|
-
`;
|
|
713
|
-
}
|
|
714
|
-
function getLicenseText(licenseId, user) {
|
|
715
|
-
if (!licenseId)
|
|
716
|
-
return "";
|
|
717
|
-
let licenseName = licenses.find(licenseId).title;
|
|
718
|
-
licenseName = licenseName?.replace(/\s+license$/i, "");
|
|
719
|
-
let licenseText = `## License
|
|
720
|
-
`;
|
|
721
|
-
return licenseName && user?.name ? licenseText = `${licenseText}
|
|
722
|
-
[${licenseName}](LICENSE) \xA9 ${user?.name}
|
|
723
|
-
` : licenseName ? licenseText = `${licenseText}
|
|
724
|
-
[${licenseName}](LICENSE)
|
|
725
|
-
` : licenseText = `${licenseText}
|
|
726
|
-
See [LICENSE](LICENSE)`, licenseText;
|
|
727
|
-
}
|
|
728
|
-
function isDefaultGitHubReadme(readme) {
|
|
729
|
-
if (!readme)
|
|
730
|
-
return !1;
|
|
731
|
-
const lines = readme.split(`
|
|
732
|
-
`, 20).filter(Boolean);
|
|
733
|
-
return lines.length <= 2 && lines[0].startsWith("#");
|
|
734
|
-
}
|
|
735
|
-
const semverWorkflowPreset = {
|
|
736
|
-
name: "semver-workflow",
|
|
737
|
-
description: "Files and dependencies for conventional-commits, github workflow and semantic-release.",
|
|
738
|
-
apply: applyPreset$2
|
|
739
|
-
}, info = (write, msg, ...args) => write && log.info(msg, ...args);
|
|
740
|
-
async function applyPreset$2(options) {
|
|
741
|
-
await writeAssets(semverWorkflowFiles(), options), await addPrepareScript(options), await addDevDependencies$2(options), await updateReadme(options);
|
|
742
|
-
}
|
|
743
|
-
async function addPrepareScript(options) {
|
|
744
|
-
const pkg2 = await getPackage(options), didWrite = await addPackageJsonScripts(pkg2, options, (scripts) => (scripts.prepare = addScript("husky", scripts.prepare), scripts));
|
|
745
|
-
info(didWrite, "Added prepare script to package.json");
|
|
746
|
-
}
|
|
747
|
-
async function addDevDependencies$2(options) {
|
|
748
|
-
const pkg2 = await getPackage(options), devDeps = sortKeys({
|
|
749
|
-
...pkg2.devDependencies,
|
|
750
|
-
...await semverWorkflowDependencies()
|
|
751
|
-
}), newPkg = { ...pkg2 };
|
|
752
|
-
newPkg.devDependencies = devDeps, await writePackageJsonDirect(newPkg, options), log.info("Updated devDependencies."), log.info(
|
|
753
|
-
chalk.green(
|
|
754
|
-
outdent$1`
|
|
755
|
-
semantic-release preset injected.
|
|
756
|
-
|
|
757
|
-
Please confer
|
|
758
|
-
https://github.com/sanity-io/plugin-kit/blob/main/docs/semver-workflow.md#manual-steps-after-inject
|
|
759
|
-
to finalize configuration for this preset.
|
|
760
|
-
`.trim()
|
|
761
|
-
)
|
|
762
|
-
);
|
|
763
|
-
}
|
|
764
|
-
async function updateReadme(options) {
|
|
765
|
-
const { basePath } = options, readmePath = path.join(basePath, "README.md"), readme = await readFile$1(readmePath, "utf8").catch(errorToUndefined) ?? "", { install, usage, developTest, license, releaseSnippet } = await readmeSnippets(options), prependSections = missingSections(readme, [install, usage]), appendSections = missingSections(readme, [license, developTest, releaseSnippet]);
|
|
766
|
-
if (prependSections.length || appendSections.length) {
|
|
767
|
-
const updatedReadme = [...prependSections, readme, ...appendSections].filter(Boolean).join(`
|
|
768
|
-
|
|
769
|
-
`);
|
|
770
|
-
await writeFile(readmePath, updatedReadme, { encoding: "utf8" }), log.info("Updated README. Please review the changes.");
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
async function readmeSnippets(options) {
|
|
774
|
-
const pkg2 = await getPackage(options), user = await getUserInfo(options, pkg2), bestEffortUrl = readmeBaseurl(pkg2), install = installationSnippet(pkg2.name ?? "unknown"), usage = outdent$1`
|
|
775
|
-
## Usage
|
|
776
|
-
`, license = getLicenseText(typeof pkg2.license == "string" ? pkg2.license : void 0, user), releaseSnippet = outdent$1`
|
|
777
|
-
### Release new version
|
|
778
|
-
|
|
779
|
-
Run ["CI & Release" workflow](${bestEffortUrl}/actions/workflows/main.yml).
|
|
780
|
-
Make sure to select the main branch and check "Release new version".
|
|
781
|
-
|
|
782
|
-
Semantic release will only release on configured branches, so it is safe to run release on any branch.
|
|
783
|
-
`;
|
|
784
|
-
return {
|
|
785
|
-
install,
|
|
786
|
-
usage,
|
|
787
|
-
license,
|
|
788
|
-
developTest: developTestSnippet(),
|
|
789
|
-
releaseSnippet
|
|
790
|
-
};
|
|
791
|
-
}
|
|
792
|
-
function missingSections(readme, sections) {
|
|
793
|
-
return sections.filter((section) => !closeEnough(section, readme));
|
|
794
|
-
}
|
|
795
|
-
function closeEnough(a, b) {
|
|
796
|
-
const aLines = a.split(`
|
|
797
|
-
`), bLines = b.split(`
|
|
798
|
-
`);
|
|
799
|
-
return aLines.filter((line) => bLines.find((bLine) => bLine === line)).length >= aLines.length * 0.5;
|
|
800
|
-
}
|
|
801
|
-
function semverWorkflowFiles() {
|
|
802
|
-
return [
|
|
803
|
-
{
|
|
804
|
-
type: "copy",
|
|
805
|
-
from: [".github", "workflows", "main.yml"],
|
|
806
|
-
to: [".github", "workflows", "main.yml"]
|
|
807
|
-
},
|
|
808
|
-
{ type: "copy", from: [".husky", "commit-msg"], to: [".husky", "commit-msg"] },
|
|
809
|
-
{ type: "copy", from: [".husky", "pre-commit"], to: [".husky", "pre-commit"] },
|
|
810
|
-
{ type: "copy", from: [".releaserc.json"], to: ".releaserc.json" },
|
|
811
|
-
{ type: "copy", from: ["commitlint.template.js"], to: "commitlint.config.js" },
|
|
812
|
-
{ type: "copy", from: ["lint-staged.template.js"], to: "lint-staged.config.js" }
|
|
813
|
-
].map((fromTo) => fromTo.type === "copy" ? {
|
|
814
|
-
...fromTo,
|
|
815
|
-
from: ["semver-workflow", ...fromTo.from]
|
|
816
|
-
} : fromTo);
|
|
817
|
-
}
|
|
818
|
-
async function semverWorkflowDependencies() {
|
|
819
|
-
return resolveLatestVersions([
|
|
820
|
-
"@commitlint/cli",
|
|
821
|
-
"@commitlint/config-conventional",
|
|
822
|
-
"@sanity/semantic-release-preset",
|
|
823
|
-
"husky",
|
|
824
|
-
"lint-staged"
|
|
825
|
-
]);
|
|
826
|
-
}
|
|
827
|
-
function readmeBaseurl(pkg2) {
|
|
828
|
-
return (pkg2.repository?.url ?? pkg2.homepage ?? "TODO").replace(/.+:\/\//g, "https://").replace(/\.git/g, "").replace(/git@github.com\//g, "github.com/").replace(/git@github.com:/g, "https://github.com/").replace(/#.+/g, "");
|
|
829
|
-
}
|
|
830
|
-
const forcedPackageVersions = {}, forcedDevPackageVersions = {}, forcedPeerPackageVersions = {
|
|
831
|
-
react: "^18",
|
|
832
|
-
"react-dom": "^18",
|
|
833
|
-
"@types/react": "^18",
|
|
834
|
-
"@types/react-dom": "^18",
|
|
835
|
-
sanity: "^3",
|
|
836
|
-
"styled-components": "^5.2"
|
|
837
|
-
}, ui = {
|
|
838
|
-
name: "ui",
|
|
839
|
-
description: "`@sanity/ui` and dependencies",
|
|
840
|
-
apply: applyPreset$1
|
|
841
|
-
};
|
|
842
|
-
async function applyPreset$1(options) {
|
|
843
|
-
await addDependencies(options), await addDevDependencies$1(options), log.info(chalk.green("ui preset injected"));
|
|
844
|
-
}
|
|
845
|
-
async function addDependencies(options) {
|
|
846
|
-
const pkg2 = await getPackage(options), newDeps = sortKeys(
|
|
847
|
-
forceDependencyVersions(
|
|
848
|
-
{
|
|
849
|
-
...pkg2.dependencies,
|
|
850
|
-
...await resolveDependencyList()
|
|
851
|
-
},
|
|
852
|
-
forcedPackageVersions
|
|
853
|
-
)
|
|
854
|
-
), newPkg = { ...pkg2 };
|
|
855
|
-
newPkg.dependencies = newDeps, await writePackageJsonDirect(newPkg, options), log.info("Updated dependencies.");
|
|
856
|
-
}
|
|
857
|
-
async function addDevDependencies$1(options) {
|
|
858
|
-
const pkg2 = await getPackage(options), newDeps = sortKeys(
|
|
859
|
-
forceDependencyVersions(
|
|
860
|
-
{
|
|
861
|
-
...pkg2.devDependencies,
|
|
862
|
-
...await resolveDevDependencyList()
|
|
863
|
-
},
|
|
864
|
-
forcedDevPackageVersions
|
|
865
|
-
)
|
|
866
|
-
), newPkg = { ...pkg2 };
|
|
867
|
-
newPkg.devDependencies = newDeps, await writePackageJsonDirect(newPkg, options), log.info("Updated devDependencies.");
|
|
868
|
-
}
|
|
869
|
-
async function resolveDependencyList() {
|
|
870
|
-
return resolveLatestVersions(["@sanity/icons", "@sanity/ui"]);
|
|
871
|
-
}
|
|
872
|
-
async function resolveDevDependencyList() {
|
|
873
|
-
return resolveLatestVersions([
|
|
874
|
-
// install the peer dependencies of `@sanity/ui` as dev dependencies
|
|
875
|
-
"react",
|
|
876
|
-
"react-dom",
|
|
877
|
-
"styled-components"
|
|
878
|
-
]);
|
|
879
|
-
}
|
|
880
|
-
const uiWorkshop = {
|
|
881
|
-
name: "ui-workshop",
|
|
882
|
-
description: "Files for testing custom components with @sanity/ui-workshop",
|
|
883
|
-
apply: applyPreset
|
|
884
|
-
};
|
|
885
|
-
async function applyPreset(options) {
|
|
886
|
-
await writeAssets(files(), options), await addDevDependencies(options), await updateGitIgnore(options), log.info(
|
|
887
|
-
chalk.green(
|
|
888
|
-
outdent$1`
|
|
889
|
-
ui-workshop preset injected.
|
|
890
|
-
|
|
891
|
-
Please confer
|
|
892
|
-
https://github.com/sanity-io/plugin-kit/blob/main/docs/ui-workshop.md#manual-steps-after-inject
|
|
893
|
-
to finalize configuration for this preset.
|
|
894
|
-
`.trim()
|
|
895
|
-
)
|
|
896
|
-
);
|
|
897
|
-
}
|
|
898
|
-
function files() {
|
|
899
|
-
return [
|
|
900
|
-
{ type: "copy", from: ["workshop.config.ts"], to: ["workshop.config.ts"] },
|
|
901
|
-
{ type: "copy", from: ["src", "CustomField.tsx"], to: ["src", "CustomField.tsx"] },
|
|
902
|
-
{
|
|
903
|
-
type: "copy",
|
|
904
|
-
from: ["src", "__workshop__", "index.tsx"],
|
|
905
|
-
to: ["src", "__workshop__", "index.tsx"]
|
|
906
|
-
},
|
|
907
|
-
{
|
|
908
|
-
type: "copy",
|
|
909
|
-
from: ["src", "__workshop__", "props.tsx"],
|
|
910
|
-
to: ["src", "__workshop__", "props.tsx"]
|
|
911
|
-
}
|
|
912
|
-
].map((fromTo) => fromTo.type === "copy" ? {
|
|
913
|
-
...fromTo,
|
|
914
|
-
from: ["ui-workshop", ...fromTo.from]
|
|
915
|
-
} : fromTo);
|
|
916
|
-
}
|
|
917
|
-
async function updateGitIgnore(options) {
|
|
918
|
-
const { basePath } = options, gitignorePath = path.join(basePath, ".gitignore");
|
|
919
|
-
let gitignore = await readFile$1(gitignorePath, "utf8").catch(errorToUndefined) ?? "";
|
|
920
|
-
const value = ".workshop";
|
|
921
|
-
gitignore.includes(value) || (gitignore += `
|
|
922
|
-
|
|
923
|
-
${value}`, await writeFile(gitignorePath, gitignore, { encoding: "utf8" }));
|
|
924
|
-
}
|
|
925
|
-
async function addDevDependencies(options) {
|
|
926
|
-
const pkg2 = await getPackage(options), devDeps = sortKeys({
|
|
927
|
-
...pkg2.devDependencies,
|
|
928
|
-
...await devDependencies()
|
|
929
|
-
}), newPkg = { ...pkg2 };
|
|
930
|
-
newPkg.devDependencies = devDeps, await writePackageJsonDirect(newPkg, options), log.info("Updated devDependencies.");
|
|
931
|
-
}
|
|
932
|
-
async function devDependencies() {
|
|
933
|
-
return resolveLatestVersions([
|
|
934
|
-
"@sanity/ui-workshop",
|
|
935
|
-
"@sanity/icons",
|
|
936
|
-
"@sanity/ui",
|
|
937
|
-
"react",
|
|
938
|
-
"react-dom",
|
|
939
|
-
"styled-components"
|
|
940
|
-
]);
|
|
941
|
-
}
|
|
942
|
-
const presets = [semverWorkflowPreset, renovatePreset, ui, uiWorkshop], presetNames = presets.map((p) => p?.name);
|
|
943
|
-
function presetHelpList(padStart) {
|
|
944
|
-
return presets.map((p) => `${"".padStart(padStart)}${p.name.padEnd(20)}${p.description}`).join(`
|
|
945
|
-
`);
|
|
946
|
-
}
|
|
947
|
-
async function injectPresets(options) {
|
|
948
|
-
if (options.flags.presetOnly && !options.flags.preset?.length)
|
|
949
|
-
throw new Error("--preset-only, but no --preset [preset-name] was provided.");
|
|
950
|
-
const applyPresets = presetsFromInput(options.flags.preset);
|
|
951
|
-
for (const preset of applyPresets)
|
|
952
|
-
await preset.apply(options);
|
|
953
|
-
}
|
|
954
|
-
function presetsFromInput(inputPresets) {
|
|
955
|
-
if (!inputPresets)
|
|
956
|
-
return [];
|
|
957
|
-
const unknownPresets = inputPresets.filter((p) => !presetNames.includes(p));
|
|
958
|
-
if (unknownPresets.length)
|
|
959
|
-
throw new Error(
|
|
960
|
-
`Unknown --preset(s): [${unknownPresets.join(", ")}]. Must be one of: [${presetNames.join(
|
|
961
|
-
", "
|
|
962
|
-
)}]`
|
|
963
|
-
);
|
|
964
|
-
return inputPresets.filter(onlyUnique).map((presetName) => presets.find((p) => p.name === presetName)).filter((p) => !!p);
|
|
965
|
-
}
|
|
966
|
-
function onlyUnique(value, index, arr) {
|
|
967
|
-
return arr.indexOf(value) === index;
|
|
968
|
-
}
|
|
969
|
-
const bannedFields = ["login", "description", "projecturl", "email"], preferredLicenses = ["MIT", "ISC", "BSD-3-Clause"], otherLicenses = Object.keys(licenses.list).filter((id) => {
|
|
970
|
-
const license = licenses.list[id];
|
|
971
|
-
return !preferredLicenses.includes(id) && !bannedFields.some((field) => license.body.includes(`[${field}]`));
|
|
972
|
-
});
|
|
973
|
-
async function inject(options) {
|
|
974
|
-
options.flags.presetOnly ? log.info("Only apply presets, skipping default inject.") : await injectBase(options), await injectPresets(options);
|
|
975
|
-
}
|
|
976
|
-
async function injectBase(options) {
|
|
977
|
-
const { basePath, flags, requireUserConfirmation } = options, info2 = (write, msg, ...args) => write && log.info(msg, ...args), pkg2 = await getPackage(options).catch(errorToUndefined);
|
|
978
|
-
log.debug("Plugin has package.json: %s", pkg2 ? "yes" : "no");
|
|
979
|
-
const user = await getUserInfo(options, pkg2);
|
|
980
|
-
log.debug("User information: %o", user);
|
|
981
|
-
const pkgName = flags.name ?? pkg2?.name, pluginName = requireUserConfirmation || !pkgName ? await promptForPackageName(options, pkgName) : pkgName;
|
|
982
|
-
log.debug("Plugin name: %s", pluginName);
|
|
983
|
-
const license = await getLicense(flags, { user, pluginName, pkg: pkg2, requireUserConfirmation }), licenseChanged = (pkg2 && pkg2.license) !== (license && license.id);
|
|
984
|
-
log.debug("License: %s", license ? license.id : "<none>");
|
|
985
|
-
const description = await getProjectDescription(basePath, pkg2, requireUserConfirmation);
|
|
986
|
-
log.debug("Description: %s", description || "<none>");
|
|
987
|
-
const repoUrl = flags.repo ?? (await gitRemoteOriginUrl(basePath).catch(errorToUndefined) || pkg2?.repository?.url), gitOrigin = requireUserConfirmation ? await promptForRepoOrigin(options, repoUrl) : repoUrl;
|
|
988
|
-
log.debug("Remote origin: %s", gitOrigin || "<none>");
|
|
989
|
-
const data = { user, pluginName, license, description, pkg: pkg2, gitOrigin };
|
|
990
|
-
let didWrite;
|
|
991
|
-
const newPkg = await writePackageJson(data, options);
|
|
992
|
-
info2(newPkg !== pkg2, "Wrote package.json"), data.pkg = newPkg, didWrite = await writeLicense(data, options, licenseChanged), info2(didWrite, "Wrote license file (LICENSE)"), didWrite = await writeReadme(data, options), info2(didWrite, "Wrote readme file (README.md)"), didWrite = await writeStaticAssets(options), info2(didWrite.length > 0, "Wrote static asset files: %s", didWrite.join(", ")), didWrite = await addBuildScripts(newPkg, options), info2(didWrite, "Added build scripts to package.json"), didWrite = await addCompileDirToGitIgnore(options), info2(didWrite, "Added compilation output directory to .gitignore");
|
|
993
|
-
}
|
|
994
|
-
async function writeReadme(data, options) {
|
|
995
|
-
const { basePath } = options, readmePath = path.join(basePath, "README.md"), readme = await readFile$1(readmePath, "utf8").catch(errorToUndefined);
|
|
996
|
-
return readme && !isDefaultGitHubReadme(readme) ? !1 : (await writeFileWithOverwritePrompt(readmePath, generateReadme(data), {
|
|
997
|
-
encoding: "utf8",
|
|
998
|
-
force: options.flags.force
|
|
999
|
-
}), !0);
|
|
1000
|
-
}
|
|
1001
|
-
async function writeLicense({ license }, options, licenseChanged) {
|
|
1002
|
-
const { basePath, flags } = options;
|
|
1003
|
-
if (flags.license === !1 || !license)
|
|
1004
|
-
return !1;
|
|
1005
|
-
const hasLicenseMdFile = await fileExists(path.join(basePath, "LICENSE.md")), licensePath = path.join(basePath, hasLicenseMdFile ? "LICENSE.md" : "LICENSE");
|
|
1006
|
-
return await writeFileWithOverwritePrompt(licensePath, license.text, {
|
|
1007
|
-
encoding: "utf8",
|
|
1008
|
-
default: licenseChanged,
|
|
1009
|
-
force: flags.force
|
|
1010
|
-
}), !0;
|
|
1011
|
-
}
|
|
1012
|
-
async function getLicense(flags, {
|
|
1013
|
-
user,
|
|
1014
|
-
pluginName,
|
|
1015
|
-
pkg: pkg2,
|
|
1016
|
-
requireUserConfirmation
|
|
1017
|
-
}) {
|
|
1018
|
-
const license = await getLicenseIdentifier(flags, pkg2, requireUserConfirmation);
|
|
1019
|
-
if (!license)
|
|
1020
|
-
return;
|
|
1021
|
-
const text = license.body.replace(/\[fullname\]/g, user?.name).replace(/\[project\]/g, pluginName).replace(/\[year\]/g, (/* @__PURE__ */ new Date()).getFullYear());
|
|
1022
|
-
return { id: license.id, text };
|
|
1023
|
-
}
|
|
1024
|
-
async function getLicenseIdentifier(flags, pkg2, requireUserConfirmation = !1) {
|
|
1025
|
-
if (flags.license === !1)
|
|
1026
|
-
return null;
|
|
1027
|
-
if (typeof flags.license == "string") {
|
|
1028
|
-
const license = licenses.find(`${flags.license}`);
|
|
1029
|
-
if (!license)
|
|
1030
|
-
throw new Error(`License "${flags.license}" not found`);
|
|
1031
|
-
return license;
|
|
1032
|
-
}
|
|
1033
|
-
if (pkg2 && pkg2.license && !requireUserConfirmation) {
|
|
1034
|
-
const license = licenses.find(`${pkg2.license}`);
|
|
1035
|
-
if (license)
|
|
1036
|
-
return license;
|
|
1037
|
-
log.warn(`package.json contains license "${pkg2.license}", which is not recognized`);
|
|
1038
|
-
}
|
|
1039
|
-
const licenseId = await prompt("Which license do you want to use?", {
|
|
1040
|
-
default: pkg2 && pkg2.license && licenses.find(pkg2.license) ? pkg2.license : preferredLicenses[0],
|
|
1041
|
-
choices: [
|
|
1042
|
-
prompt.separator(),
|
|
1043
|
-
...preferredLicenses.map((value) => ({ value, name: licenses.list[value].title })),
|
|
1044
|
-
prompt.separator(),
|
|
1045
|
-
...otherLicenses.map((value) => ({ value, name: licenses.list[value].title }))
|
|
1046
|
-
]
|
|
1047
|
-
});
|
|
1048
|
-
return licenses.find(licenseId);
|
|
1049
|
-
}
|
|
1050
|
-
async function getProjectDescription(basePath, pkg2, requireUserConfirmation = !1) {
|
|
1051
|
-
let description = await resolveProjectDescription(basePath, pkg2);
|
|
1052
|
-
return requireUserConfirmation && (description = await prompt("Plugin description", { default: description || "" })), description ?? "";
|
|
1053
|
-
}
|
|
1054
|
-
async function resolveProjectDescription(basePath, pkg2) {
|
|
1055
|
-
if (pkg2 && typeof pkg2.description == "string" && pkg2.description.length > 5)
|
|
1056
|
-
return pkg2.description;
|
|
1057
|
-
try {
|
|
1058
|
-
const readmePath = path.join(basePath, "README.md"), readme = await readFile$1(readmePath, "utf8"), [title, description] = readme.split(`
|
|
1059
|
-
`).filter(Boolean);
|
|
1060
|
-
if (!title || !description || !title.match(/^#\s+\w+/))
|
|
1061
|
-
return null;
|
|
1062
|
-
const unlinked = description.replace(/\[(.*?)\]\(.*?\)/g, "$1");
|
|
1063
|
-
return /^[^#]/.test(unlinked) ? unlinked : null;
|
|
1064
|
-
} catch (err) {
|
|
1065
|
-
return errorToUndefined(err);
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
async function writeAssets(injectables, { basePath, flags }) {
|
|
1069
|
-
const assetsDir = await findAssetsDir(), from = (...segments) => path.join(assetsDir, "inject", ...segments), to = (...segments) => path.join(basePath, ...segments), writes = [];
|
|
1070
|
-
for (const injectable of injectables) {
|
|
1071
|
-
if (injectable.type === "copy") {
|
|
1072
|
-
const fromPath = asArray(injectable.from), toPath = asArray(injectable.to);
|
|
1073
|
-
await copyFileWithOverwritePrompt(from(...fromPath), to(...toPath), flags) && writes.push(path.join(...toPath));
|
|
1074
|
-
continue;
|
|
1075
|
-
}
|
|
1076
|
-
if (injectable.type === "template") {
|
|
1077
|
-
const toPath = asArray(injectable.to);
|
|
1078
|
-
await writeFileWithOverwritePrompt(to(...toPath), `${injectable.value.trim()}
|
|
1079
|
-
`, {
|
|
1080
|
-
default: "n",
|
|
1081
|
-
force: injectable.force || flags.force
|
|
1082
|
-
}), writes.push(path.join(...toPath));
|
|
1083
|
-
continue;
|
|
1084
|
-
}
|
|
1085
|
-
throw new Error(`Unknown operation type "${injectable.type}"`);
|
|
1086
|
-
}
|
|
1087
|
-
return writes;
|
|
1088
|
-
}
|
|
1089
|
-
async function writeStaticAssets(options) {
|
|
1090
|
-
const { outDir, flags } = options, files2 = [
|
|
1091
|
-
flags.eslint && eslintrcTemplate({ flags: options.flags }),
|
|
1092
|
-
flags.eslint && eslintignoreTemplate({ outDir, flags: options.flags }),
|
|
1093
|
-
{ type: "copy", from: "editorconfig", to: ".editorconfig" },
|
|
1094
|
-
{ type: "copy", from: "sanity.json", to: "sanity.json" },
|
|
1095
|
-
{ type: "copy", from: "v2-incompatible.js.template", to: "v2-incompatible.js" },
|
|
1096
|
-
pkgConfigTemplate({ outDir, flags: options.flags }),
|
|
1097
|
-
flags.gitignore && gitignoreTemplate(),
|
|
1098
|
-
flags.typescript && tsconfigTemplate({ flags: options.flags }),
|
|
1099
|
-
flags.typescript && tsconfigTemplateDist({ outDir, flags: options.flags }),
|
|
1100
|
-
flags.typescript && tsconfigTemplateSettings({ outDir, flags: options.flags }),
|
|
1101
|
-
flags.prettier && prettierignoreTemplate({ outDir }),
|
|
1102
|
-
flags.prettier && { type: "copy", from: "prettierrc.json", to: ".prettierrc" }
|
|
1103
|
-
].map((f) => f || void 0).filter((f) => !!f);
|
|
1104
|
-
return writeAssets(files2, options);
|
|
1105
|
-
}
|
|
1106
|
-
function asArray(input) {
|
|
1107
|
-
return typeof input == "string" ? [input] : input;
|
|
1108
|
-
}
|
|
1109
|
-
async function findAssetsDir() {
|
|
1110
|
-
let maxBackpaddle = 3, currDir = path.dirname(fileURLToPath(import.meta.url)), assetsDir = "";
|
|
1111
|
-
for (; !assetsDir && maxBackpaddle; ) {
|
|
1112
|
-
currDir = path.join(currDir, "..");
|
|
1113
|
-
const assets = path.join(currDir, "assets");
|
|
1114
|
-
await fileExists(assets) ? assetsDir = assets : maxBackpaddle--;
|
|
188
|
+
}
|
|
189
|
+
function parseJson5(content, errorKey) {
|
|
190
|
+
try {
|
|
191
|
+
return json5.parse(content);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
throw new Error(`Error parsing "${errorKey}": ${err.message}`);
|
|
1115
194
|
}
|
|
1116
|
-
if (!assetsDir)
|
|
1117
|
-
throw new Error("Could not find assets directory!");
|
|
1118
|
-
return assetsDir;
|
|
1119
|
-
}
|
|
1120
|
-
async function addCompileDirToGitIgnore(options) {
|
|
1121
|
-
const gitIgnorePath = path.join(options.basePath, ".gitignore"), gitignore = await readFile$1(gitIgnorePath, "utf8").catch(errorToUndefined);
|
|
1122
|
-
if (!gitignore)
|
|
1123
|
-
return !1;
|
|
1124
|
-
const ignore = options.outDir.replace(/^[./]+/, "").split("/")[0];
|
|
1125
|
-
if (!ignore)
|
|
1126
|
-
return !1;
|
|
1127
|
-
const lines = gitignore.trim().split(`
|
|
1128
|
-
`);
|
|
1129
|
-
return lines.includes(ignore) ? !1 : (lines.push("", "# Compiled plugin", ignore), await writeFile(gitIgnorePath, lines.join(`
|
|
1130
|
-
`) + `
|
|
1131
|
-
`, { encoding: "utf8" }), !0);
|
|
1132
195
|
}
|
|
1133
|
-
const
|
|
1134
|
-
"@sanity/base",
|
|
1135
|
-
"@sanity/core",
|
|
1136
|
-
"@sanity/types",
|
|
1137
|
-
"@sanity/data-aspects",
|
|
1138
|
-
"@sanity/default-layout",
|
|
1139
|
-
"@sanity/default-login",
|
|
1140
|
-
"@sanity/desk-tool",
|
|
1141
|
-
"@sanity/field",
|
|
1142
|
-
"@sanity/form-builder",
|
|
1143
|
-
"@sanity/initial-value-templates",
|
|
1144
|
-
"@sanity/language-filter",
|
|
1145
|
-
"@sanity/production-preview",
|
|
1146
|
-
"@sanity/react-hooks",
|
|
1147
|
-
"@sanity/resolver",
|
|
1148
|
-
"@sanity/state-router",
|
|
1149
|
-
"@sanity/structure",
|
|
1150
|
-
"@sanity/studio-hints"
|
|
1151
|
-
].sort(), deprecatedDevDeps = [
|
|
1152
|
-
"tsdx",
|
|
1153
|
-
"sanipack",
|
|
1154
|
-
"parcel",
|
|
1155
|
-
"@parcel/packager-ts",
|
|
1156
|
-
"@parcel/transformer-typescript-types"
|
|
1157
|
-
], expectedScripts = {
|
|
196
|
+
const expectedScripts = {
|
|
1158
197
|
build: "plugin-kit verify-package --silent && pkg-utils build --strict --check --clean",
|
|
1159
198
|
watch: "pkg-utils watch --strict",
|
|
1160
199
|
"link-watch": "plugin-kit link-watch",
|
|
@@ -1165,7 +204,7 @@ function filesWithSuffixes(fileBases, suffixes) {
|
|
|
1165
204
|
}
|
|
1166
205
|
function validateNodeEngine(packageJson) {
|
|
1167
206
|
return packageJson.engines?.node !== requiredNodeEngine ? [
|
|
1168
|
-
outdent
|
|
207
|
+
outdent`
|
|
1169
208
|
Expected package.json to contain engines.node: "${requiredNodeEngine}" to match @sanity/pkg-utils,
|
|
1170
209
|
but it was: ${packageJson.engines?.node}
|
|
1171
210
|
|
|
@@ -1182,7 +221,7 @@ function validateScripts(packageJson) {
|
|
|
1182
221
|
return !command || !command.includes(expectedCommand);
|
|
1183
222
|
});
|
|
1184
223
|
return divergentScripts.length && errors.push(
|
|
1185
|
-
outdent
|
|
224
|
+
outdent`
|
|
1186
225
|
The following script commands did not contain expected defaults: ${divergentScripts.map(([key]) => key).join(", ")}
|
|
1187
226
|
|
|
1188
227
|
This checks for that the commands-strings includes these terms.
|
|
@@ -1210,7 +249,7 @@ async function validateTsConfig(ts, options) {
|
|
|
1210
249
|
const expectedOutput = wrongEntries.map(([key, value]) => `"${key}": ${typeof value == "string" ? `"${value}"` : value},`).join(`
|
|
1211
250
|
`);
|
|
1212
251
|
errors.push(
|
|
1213
|
-
outdent
|
|
252
|
+
outdent`
|
|
1214
253
|
Recommended ${tsconfig} compilerOptions missing:
|
|
1215
254
|
|
|
1216
255
|
The following fields had unexpected values: [${wrongEntries.map(([key]) => key).join(", ")}]
|
|
@@ -1225,7 +264,7 @@ async function validateTsConfig(ts, options) {
|
|
|
1225
264
|
}
|
|
1226
265
|
function validatePackageType({ type }) {
|
|
1227
266
|
return type === "module" ? [] : [
|
|
1228
|
-
outdent
|
|
267
|
+
outdent`
|
|
1229
268
|
package.json must set "type": "module" — plugins built with @sanity/plugin-kit are ESM-only.
|
|
1230
269
|
Found: ${type ? `"type": "${type}"` : 'no "type" field (defaults to "commonjs")'}
|
|
1231
270
|
|
|
@@ -1235,9 +274,49 @@ function validatePackageType({ type }) {
|
|
|
1235
274
|
`.trimStart()
|
|
1236
275
|
];
|
|
1237
276
|
}
|
|
1238
|
-
function
|
|
1239
|
-
|
|
1240
|
-
|
|
277
|
+
function findRequireConditions(node, pathSegments) {
|
|
278
|
+
if (Array.isArray(node))
|
|
279
|
+
return node.flatMap(
|
|
280
|
+
(entry, index) => findRequireConditions(entry, [...pathSegments, String(index)])
|
|
281
|
+
);
|
|
282
|
+
if (!node || typeof node != "object")
|
|
283
|
+
return [];
|
|
284
|
+
const found = [];
|
|
285
|
+
for (const [key, value] of Object.entries(node))
|
|
286
|
+
key === "require" && found.push(formatExportsPath(pathSegments)), found.push(...findRequireConditions(value, [...pathSegments, key]));
|
|
287
|
+
return found;
|
|
288
|
+
}
|
|
289
|
+
function formatExportsPath(segments) {
|
|
290
|
+
return `exports${segments.map((segment) => `[${JSON.stringify(segment)}]`).join("")}`;
|
|
291
|
+
}
|
|
292
|
+
function validateEsmOnly(packageJson) {
|
|
293
|
+
const offenders = [];
|
|
294
|
+
typeof packageJson.main < "u" && offenders.push(`- the top-level "main" field (${JSON.stringify(packageJson.main)})`), typeof packageJson.module < "u" && offenders.push(`- the top-level "module" field (${JSON.stringify(packageJson.module)})`);
|
|
295
|
+
const requireConditions = [...new Set(findRequireConditions(packageJson.exports, []))];
|
|
296
|
+
for (const conditionPath of requireConditions)
|
|
297
|
+
offenders.push(`- a "require" export condition at ${conditionPath}`);
|
|
298
|
+
return offenders.length ? [
|
|
299
|
+
outdent`
|
|
300
|
+
package.json ships CommonJS (CJS) output, but Sanity plugins target Sanity Studio v5+, which is pure ESM.
|
|
301
|
+
|
|
302
|
+
Remove the following so the package stays ESM-only:
|
|
303
|
+
${offenders.join(`
|
|
304
|
+
`)}
|
|
305
|
+
|
|
306
|
+
Supporting CJS is not worth it:
|
|
307
|
+
- It can have unintended side-effects.
|
|
308
|
+
- The Node.js versions plugin-kit supports (${requiredNodeEngine}) fully support require(esm), so a
|
|
309
|
+
consumer that still uses require() loads the ESM build directly — which is far more predictable.
|
|
310
|
+
- Publishing a single format guarantees two copies of the plugin's code (ESM + CJS) can't both end up
|
|
311
|
+
in the module tree, bloating bundles and slowing down builds.
|
|
312
|
+
|
|
313
|
+
Rely on "exports" together with "type": "module", and drop "main", "module" and any "require" conditions.
|
|
314
|
+
`.trimStart()
|
|
315
|
+
] : [];
|
|
316
|
+
}
|
|
317
|
+
function validatePkgUtilsDependency({ devDependencies }) {
|
|
318
|
+
return devDependencies?.["@sanity/pkg-utils"] ? [] : [
|
|
319
|
+
outdent`
|
|
1241
320
|
package.json does not list @sanity/pkg-utils as a devDependency.
|
|
1242
321
|
@sanity/pkg-utils replaced parcel as the recommended build tool in @sanity/plugin-kit 2.0.0
|
|
1243
322
|
|
|
@@ -1252,7 +331,7 @@ function validatePkgUtilsVersion({ basePath }) {
|
|
|
1252
331
|
installedVersion = require2("@sanity/pkg-utils/package.json").version;
|
|
1253
332
|
} catch {
|
|
1254
333
|
return [
|
|
1255
|
-
outdent
|
|
334
|
+
outdent`
|
|
1256
335
|
@sanity/pkg-utils is not installed.
|
|
1257
336
|
plugin-kit loads package.config.ts through @sanity/pkg-utils (a peer dependency).
|
|
1258
337
|
|
|
@@ -1262,7 +341,7 @@ function validatePkgUtilsVersion({ basePath }) {
|
|
|
1262
341
|
}
|
|
1263
342
|
const major = Number.parseInt(installedVersion?.split(".")[0] ?? "", 10);
|
|
1264
343
|
return !Number.isFinite(major) || major < minPkgUtilsMajor ? [
|
|
1265
|
-
outdent
|
|
344
|
+
outdent`
|
|
1266
345
|
@sanity/pkg-utils ${installedVersion} is too old.
|
|
1267
346
|
plugin-kit requires @sanity/pkg-utils >=${minPkgUtilsMajor} to load package.config.ts.
|
|
1268
347
|
|
|
@@ -1271,9 +350,9 @@ function validatePkgUtilsVersion({ basePath }) {
|
|
|
1271
350
|
] : [];
|
|
1272
351
|
}
|
|
1273
352
|
function validateSanityDependencies(packageJson) {
|
|
1274
|
-
const { dependencies, devDependencies
|
|
353
|
+
const { dependencies, devDependencies, peerDependencies } = packageJson, allDependencies = { ...dependencies, ...devDependencies, ...peerDependencies }, illegalDeps = Object.keys(allDependencies).filter((dep) => mergedPackages.includes(dep)), unique = [...new Set(illegalDeps).values()];
|
|
1275
354
|
return unique.length ? [
|
|
1276
|
-
outdent
|
|
355
|
+
outdent`
|
|
1277
356
|
package.json depends on "@sanity/*" packages that have moved into "sanity" package.
|
|
1278
357
|
|
|
1279
358
|
The following dependencies should be replaced with "sanity":
|
|
@@ -1286,9 +365,9 @@ function validateSanityDependencies(packageJson) {
|
|
|
1286
365
|
] : [];
|
|
1287
366
|
}
|
|
1288
367
|
function validateDeprecatedDependencies(packageJson) {
|
|
1289
|
-
const { dependencies, devDependencies
|
|
368
|
+
const { dependencies, devDependencies, peerDependencies } = packageJson, allDependencies = { ...dependencies, ...devDependencies, ...peerDependencies }, illegalDeps = Object.keys(allDependencies).filter((dep) => deprecatedDevDeps.includes(dep)), unique = [...new Set(illegalDeps).values()];
|
|
1290
369
|
return unique.length ? [
|
|
1291
|
-
outdent
|
|
370
|
+
outdent`
|
|
1292
371
|
package.json contains deprecated dependencies that should be removed:
|
|
1293
372
|
- ${unique.join(`
|
|
1294
373
|
- `)}
|
|
@@ -1302,7 +381,7 @@ async function validateBabelConfig({ basePath }) {
|
|
|
1302
381
|
await fileExists(filepath) && babelFiles.push(filename);
|
|
1303
382
|
}
|
|
1304
383
|
return babelFiles.length ? [
|
|
1305
|
-
outdent
|
|
384
|
+
outdent`
|
|
1306
385
|
Found babel-config file: [${babelFiles.join(
|
|
1307
386
|
", "
|
|
1308
387
|
)}]. When using default @sanity/plugin-kit build command,
|
|
@@ -1313,38 +392,38 @@ async function validateBabelConfig({ basePath }) {
|
|
|
1313
392
|
] : [];
|
|
1314
393
|
}
|
|
1315
394
|
async function validateStudioConfig({ basePath }) {
|
|
1316
|
-
const suffixes = ["ts", "js", "tsx", "jsx"], filenames = filesWithSuffixes(["sanity.config", "sanity.cli"], suffixes),
|
|
395
|
+
const suffixes = ["ts", "js", "tsx", "jsx"], filenames = filesWithSuffixes(["sanity.config", "sanity.cli"], suffixes), files = {};
|
|
1317
396
|
for (const filename of filenames) {
|
|
1318
397
|
const filepath = path.normalize(path.join(basePath, filename));
|
|
1319
|
-
|
|
398
|
+
files[filename] = await fileExists(filepath);
|
|
1320
399
|
}
|
|
1321
|
-
const sanityJson = await readJson5File({ basePath, filename: "sanity.json" }), hasConfigFile = (fileBase) => filesWithSuffixes([fileBase], suffixes).some((filename) =>
|
|
400
|
+
const sanityJson = await readJson5File({ basePath, filename: "sanity.json" }), hasConfigFile = (fileBase) => filesWithSuffixes([fileBase], suffixes).some((filename) => files[filename]), hasCliConfig = hasConfigFile("sanity.cli"), hasStudioConfig = hasConfigFile("sanity.config"), errors = [];
|
|
1322
401
|
if (sanityJson) {
|
|
1323
|
-
const
|
|
1324
|
-
outdent
|
|
402
|
+
const info = [
|
|
403
|
+
outdent`
|
|
1325
404
|
Found sanity.json. This file is not used by Sanity Studio V3.
|
|
1326
405
|
|
|
1327
406
|
Please consult the Studio V3 migration guide:
|
|
1328
407
|
${urls.migrationGuideStudio}
|
|
1329
408
|
It will detail how to convert sanity.json to sanity.config.ts (or .js) and sanity.cli.ts (or .js) equivalents.
|
|
1330
409
|
`.trimStart(),
|
|
1331
|
-
sanityJson.plugins?.length && outdent
|
|
410
|
+
sanityJson.plugins?.length && outdent`
|
|
1332
411
|
For V3 versions and alternatives to V2 plugins, please refer to the Sanity Exchange:
|
|
1333
412
|
${urls.sanityExchange}
|
|
1334
413
|
`.trimStart()
|
|
1335
414
|
].filter((s) => !!s);
|
|
1336
|
-
errors.push(
|
|
415
|
+
errors.push(info.join(`
|
|
1337
416
|
|
|
1338
417
|
`));
|
|
1339
418
|
}
|
|
1340
419
|
return hasCliConfig || errors.push(
|
|
1341
|
-
outdent
|
|
420
|
+
outdent`
|
|
1342
421
|
sanity.cli.(${suffixes.join(
|
|
1343
422
|
" | "
|
|
1344
423
|
)}) missing. Please create a file named sanity.cli.ts with the following content:
|
|
1345
424
|
|
|
1346
425
|
${chalk.green(
|
|
1347
|
-
outdent
|
|
426
|
+
outdent`
|
|
1348
427
|
import {createCliConfig} from 'sanity/cli'
|
|
1349
428
|
|
|
1350
429
|
export default createCliConfig({
|
|
@@ -1360,13 +439,13 @@ async function validateStudioConfig({ basePath }) {
|
|
|
1360
439
|
For more, see ${urls.migrationGuideStudio}
|
|
1361
440
|
`.trimStart()
|
|
1362
441
|
), hasStudioConfig || errors.push(
|
|
1363
|
-
outdent
|
|
442
|
+
outdent`
|
|
1364
443
|
sanity.config.(${suffixes.join(
|
|
1365
444
|
" | "
|
|
1366
445
|
)}) missing. At a minimum sanity.config.ts should contain:
|
|
1367
446
|
|
|
1368
447
|
${chalk.green(
|
|
1369
|
-
outdent
|
|
448
|
+
outdent`
|
|
1370
449
|
import { defineConfig } from "sanity"
|
|
1371
450
|
import { deskTool } from "sanity/desk"
|
|
1372
451
|
|
|
@@ -1398,52 +477,60 @@ async function validateStudioConfig({ basePath }) {
|
|
|
1398
477
|
|
|
1399
478
|
`)] : [];
|
|
1400
479
|
}
|
|
1401
|
-
async function
|
|
480
|
+
async function validateIncompatiblePlugin({
|
|
1402
481
|
basePath,
|
|
1403
482
|
packageJson
|
|
1404
483
|
}) {
|
|
1405
|
-
const
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
firstPart?.path && !pathExists ? `The file in "path", ${firstPart?.path}, does not exist.` : null,
|
|
1419
|
-
hasDependency ? null : outdent$1`
|
|
1420
|
-
package.json should have ${incompatiblePluginPackage} as a dependency, but did not.
|
|
1421
|
-
Install it with: npm install --save ${incompatiblePluginPackage}
|
|
1422
|
-
`.trimStart()
|
|
1423
|
-
].filter((e) => !!e);
|
|
1424
|
-
return [
|
|
1425
|
-
outdent$1`
|
|
1426
|
-
Invalid sanity.json. It is used for compatibility checking in V2 studios:
|
|
484
|
+
const { dependencies, devDependencies, peerDependencies } = packageJson, inDependencies = !!(dependencies?.[incompatiblePluginPackage] || devDependencies?.[incompatiblePluginPackage] || peerDependencies?.[incompatiblePluginPackage]), hasShimFile = await fileExists(path.normalize(path.join(basePath, "v2-incompatible.js"))), sanityJsonReferencesShim = !!(await readJson5File({ basePath, filename: "sanity.json" }))?.parts?.some(
|
|
485
|
+
(part) => part?.path?.includes("v2-incompatible")
|
|
486
|
+
);
|
|
487
|
+
if (!inDependencies && !hasShimFile && !sanityJsonReferencesShim)
|
|
488
|
+
return [];
|
|
489
|
+
const found = [
|
|
490
|
+
inDependencies ? `- "${incompatiblePluginPackage}" listed in package.json` : null,
|
|
491
|
+
hasShimFile ? "- the v2-incompatible.js file" : null,
|
|
492
|
+
sanityJsonReferencesShim ? "- a sanity.json referencing v2-incompatible.js" : null
|
|
493
|
+
].filter((e) => !!e);
|
|
494
|
+
return [
|
|
495
|
+
outdent`
|
|
496
|
+
${incompatiblePluginPackage} is no longer used and should be removed.
|
|
1427
497
|
|
|
1428
|
-
|
|
1429
|
-
-
|
|
498
|
+
It only rendered an error dialog in the long end-of-life Sanity Studio v2 when a v3 plugin was
|
|
499
|
+
installed there. That compatibility shim is now obsolete, so plugin-kit no longer adds it.
|
|
1430
500
|
|
|
1431
|
-
|
|
501
|
+
Found:
|
|
502
|
+
${found.join(`
|
|
503
|
+
`)}
|
|
504
|
+
|
|
505
|
+
To fix this:
|
|
506
|
+
- Remove "${incompatiblePluginPackage}" from package.json (dependencies/devDependencies/peerDependencies)
|
|
507
|
+
- Delete the v2-incompatible.js file
|
|
508
|
+
- Delete sanity.json (if it only contains the v2-incompatible "part")
|
|
509
|
+
- Remove "sanity.json" and "v2-incompatible.js" from the package.json "files" array
|
|
1432
510
|
|
|
1433
|
-
|
|
1434
|
-
${urls.incompatiblePlugin}
|
|
511
|
+
For more, see ${urls.incompatiblePlugin}
|
|
1435
512
|
`.trimStart()
|
|
1436
|
-
|
|
1437
|
-
}
|
|
1438
|
-
return [];
|
|
513
|
+
];
|
|
1439
514
|
}
|
|
1440
515
|
function validatePackageName$1(packageJson) {
|
|
1441
|
-
const valid = validateNpmPackageName(
|
|
1442
|
-
packageJson.name
|
|
1443
|
-
);
|
|
516
|
+
const valid = validateNpmPackageName(packageJson.name ?? "");
|
|
1444
517
|
return valid.validForNewPackages ? !packageJson.name?.startsWith("@") && !packageJson.name?.startsWith("sanity-plugin-") ? [
|
|
1445
518
|
'Invalid package.json: "name" should be prefixed with "sanity-plugin-" (or scoped - @your-company/plugin-name)'
|
|
1446
|
-
] : [] : [`Invalid package.json: "name" is invalid: ${valid.errors.join(", ")}`];
|
|
519
|
+
] : [] : [`Invalid package.json: "name" is invalid: ${(valid.errors ?? valid.warnings ?? []).join(", ")}`];
|
|
520
|
+
}
|
|
521
|
+
function validateBannedFiles(packageJson) {
|
|
522
|
+
const { files } = packageJson;
|
|
523
|
+
return Array.isArray(files) ? files.some((entry) => typeof entry != "string" ? !1 : entry.trim().replace(/^\.?\/+/, "").replace(/\/+$/, "") === "src") ? [
|
|
524
|
+
outdent`
|
|
525
|
+
package.json "files" must not include "src".
|
|
526
|
+
|
|
527
|
+
Plugins built with @sanity/plugin-kit publish the compiled output in "dist" (and any v2-compatibility files).
|
|
528
|
+
Shipping the "src" directory bloats the published package and can cause bundlers that resolve the
|
|
529
|
+
"source" export condition to import raw, uncompiled TypeScript.
|
|
530
|
+
|
|
531
|
+
Please remove "src" from the "files" array in package.json.
|
|
532
|
+
`.trimStart()
|
|
533
|
+
] : [] : [];
|
|
1447
534
|
}
|
|
1448
535
|
async function validateSrcIndexFile(basePath) {
|
|
1449
536
|
const paths = ["index.js", "index.ts"].map((p) => path.join("src", p)), allowedIndexFiles = paths.map((file) => path.join(basePath, file));
|
|
@@ -1451,7 +538,7 @@ async function validateSrcIndexFile(basePath) {
|
|
|
1451
538
|
for (const indexFile of allowedIndexFiles)
|
|
1452
539
|
hasIndex = hasIndex || await fileExists(indexFile);
|
|
1453
540
|
return hasIndex ? [] : [
|
|
1454
|
-
outdent
|
|
541
|
+
outdent`
|
|
1455
542
|
Expected one of [${paths.join(", ")}] to exist.
|
|
1456
543
|
|
|
1457
544
|
@sanity/pkg-utils expects a non-jsx file to be the source entry-point for the plugin.
|
|
@@ -1463,21 +550,21 @@ async function disallowDuplicateConfig({
|
|
|
1463
550
|
basePath,
|
|
1464
551
|
pkgJson,
|
|
1465
552
|
configKey,
|
|
1466
|
-
files
|
|
553
|
+
files
|
|
1467
554
|
}) {
|
|
1468
555
|
const found = [];
|
|
1469
|
-
for (const file of
|
|
556
|
+
for (const file of files) {
|
|
1470
557
|
const filePath = path.join(basePath, file);
|
|
1471
558
|
await fileExists(filePath) && found.push(file);
|
|
1472
559
|
}
|
|
1473
560
|
return found.length > 1 ? [
|
|
1474
|
-
outdent
|
|
561
|
+
outdent`
|
|
1475
562
|
Found multiple config files that serve the same purpose: [${found.join(", ")}].
|
|
1476
563
|
|
|
1477
564
|
There should be at most one of these files. Delete the rest.
|
|
1478
565
|
`
|
|
1479
566
|
] : found.length && pkgJson[configKey] ? [
|
|
1480
|
-
outdent
|
|
567
|
+
outdent`
|
|
1481
568
|
package.json contains ${configKey}, but there also exists a config file that serves the same purpose.
|
|
1482
569
|
Config file: ${found.join("")}]
|
|
1483
570
|
|
|
@@ -1519,7 +606,210 @@ async function disallowDuplicatePrettierConfig(basePath, pkgJson) {
|
|
|
1519
606
|
]
|
|
1520
607
|
});
|
|
1521
608
|
}
|
|
1522
|
-
const
|
|
609
|
+
const forcedPackageVersions = {}, forcedDevPackageVersions = {}, forcedPeerPackageVersions = {
|
|
610
|
+
react: "^18",
|
|
611
|
+
"react-dom": "^18",
|
|
612
|
+
"@types/react": "^18",
|
|
613
|
+
"@types/react-dom": "^18",
|
|
614
|
+
sanity: "^5 || ^6.0.0-0",
|
|
615
|
+
"styled-components": "^5.2"
|
|
616
|
+
};
|
|
617
|
+
function errorToUndefined(err) {
|
|
618
|
+
if (err instanceof TypeError)
|
|
619
|
+
throw err;
|
|
620
|
+
}
|
|
621
|
+
const stat = util.promisify(fs.stat), readFile$1 = util.promisify(fs.readFile), allowedPartProps = ["name", "implements", "path", "description"], disallowedPluginProps = ["api", "project", "plugins", "env"];
|
|
622
|
+
async function getPaths(options) {
|
|
623
|
+
const { basePath } = options, manifest = await readManifest(options);
|
|
624
|
+
return manifest.paths ? absolutifyPaths(manifest.paths, basePath) : null;
|
|
625
|
+
}
|
|
626
|
+
function absolutifyPaths(paths, basePath) {
|
|
627
|
+
const getPath = (relative) => relative ? path.resolve(path.join(basePath, relative)) : void 0;
|
|
628
|
+
return paths ? {
|
|
629
|
+
basePath,
|
|
630
|
+
compiled: getPath(paths.compiled),
|
|
631
|
+
source: getPath(paths.source)
|
|
632
|
+
} : { basePath };
|
|
633
|
+
}
|
|
634
|
+
async function readManifest(options) {
|
|
635
|
+
const { basePath, validate = !0 } = options, manifestPath = path.normalize(path.join(basePath, "sanity.json"));
|
|
636
|
+
let content;
|
|
637
|
+
try {
|
|
638
|
+
content = await readFile$1(manifestPath, "utf8");
|
|
639
|
+
} catch (err) {
|
|
640
|
+
throw err.code === "ENOENT" ? new Error(
|
|
641
|
+
`No sanity.json found. sanity.json is required for plugins to function. Use \`${pkg.binname} init\` for a new plugin, or create an empty \`sanity.json\` with an empty object (\`{}\`) for existing ones.`
|
|
642
|
+
) : new Error(`Failed to read "${manifestPath}": ${err.message}`);
|
|
643
|
+
}
|
|
644
|
+
let parsed;
|
|
645
|
+
try {
|
|
646
|
+
parsed = JSON.parse(content);
|
|
647
|
+
} catch (err) {
|
|
648
|
+
throw new Error(`Error parsing "${manifestPath}": ${err.message}`);
|
|
649
|
+
}
|
|
650
|
+
return validate && await validateManifest(parsed, options), parsed;
|
|
651
|
+
}
|
|
652
|
+
async function validateManifest(manifest, opts) {
|
|
653
|
+
const options = { isPlugin: !0, ...opts };
|
|
654
|
+
if (!isObject$1(manifest))
|
|
655
|
+
throw new Error("Invalid sanity.json: Root must be an object");
|
|
656
|
+
if (options.isPlugin ? await validatePluginManifest(manifest, options) : validateProjectManifest(manifest), "root" in manifest && typeof manifest.root != "boolean")
|
|
657
|
+
throw new Error('Invalid sanity.json: "root" property must be a boolean if declared');
|
|
658
|
+
await validateParts(manifest, {
|
|
659
|
+
...options,
|
|
660
|
+
paths: absolutifyPaths(manifest.paths, options.basePath)
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
function validateProjectManifest(manifest) {
|
|
664
|
+
if ("paths" in manifest)
|
|
665
|
+
throw new Error('Invalid sanity.json: "paths" property has no meaning in a project manifest');
|
|
666
|
+
}
|
|
667
|
+
async function validatePluginManifest(manifest, options) {
|
|
668
|
+
const disallowed = Object.keys(manifest).filter((key) => disallowedPluginProps.includes(key)).map((key) => `"${key}"`);
|
|
669
|
+
if (disallowed.length > 0) {
|
|
670
|
+
const plural = disallowed.length > 1 ? "s" : "", joined = disallowed.join(", ");
|
|
671
|
+
throw new Error(
|
|
672
|
+
`Invalid sanity.json: Key${plural} ${joined} ${plural ? "are" : "is"} not allowed in a plugin manifest`
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
if (manifest.root)
|
|
676
|
+
throw new Error('Invalid sanity.json: "root" cannot be truthy in a plugin manifest');
|
|
677
|
+
await validatePaths$1(manifest, options);
|
|
678
|
+
}
|
|
679
|
+
async function validatePaths$1(manifest, options) {
|
|
680
|
+
if (!("paths" in manifest))
|
|
681
|
+
return;
|
|
682
|
+
if (!isObject$1(manifest.paths))
|
|
683
|
+
throw new Error('Invalid sanity.json: "paths" must be an object if declared');
|
|
684
|
+
if (typeof manifest.paths.compiled != "string")
|
|
685
|
+
throw new Error(
|
|
686
|
+
'Invalid sanity.json: "paths" must have a (string) "compiled" property if declared'
|
|
687
|
+
);
|
|
688
|
+
if (typeof manifest.paths.source != "string")
|
|
689
|
+
throw new Error(
|
|
690
|
+
'Invalid sanity.json: "paths" must have a (string) "source" property if declared'
|
|
691
|
+
);
|
|
692
|
+
const sourcePath = path.resolve(options.basePath, manifest.paths.source);
|
|
693
|
+
let srcStats;
|
|
694
|
+
try {
|
|
695
|
+
srcStats = await stat(sourcePath);
|
|
696
|
+
} catch (err) {
|
|
697
|
+
if (err.code === "ENOENT")
|
|
698
|
+
throw new Error(`sanity.json references "source" path which does not exist: "${sourcePath}"`);
|
|
699
|
+
}
|
|
700
|
+
if (!srcStats?.isDirectory())
|
|
701
|
+
throw new Error(
|
|
702
|
+
`sanity.json references "source" path which is not a directory: "${sourcePath}"`
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
async function validateParts(manifest, options) {
|
|
706
|
+
if (!("parts" in manifest))
|
|
707
|
+
return;
|
|
708
|
+
if (!Array.isArray(manifest.parts))
|
|
709
|
+
throw new Error('Invalid sanity.json: "parts" must be an array if declared');
|
|
710
|
+
let i = 0;
|
|
711
|
+
for (const part of manifest.parts)
|
|
712
|
+
await validatePart(part, i, options), i++;
|
|
713
|
+
}
|
|
714
|
+
async function validatePart(part, index, options) {
|
|
715
|
+
if (!isObject$1(part))
|
|
716
|
+
throw new Error(`Invalid sanity.json: "parts[${index}]" must be an object`);
|
|
717
|
+
validateAllowedPartKeys(part, index), validatePartStringValues(part, index), validatePartNames(part, index, options), await validatePartFiles(part, index, options);
|
|
718
|
+
}
|
|
719
|
+
async function validatePartFiles(part, index, options) {
|
|
720
|
+
const { verifyCompiledParts, verifySourceParts, paths } = options;
|
|
721
|
+
if (!part?.path)
|
|
722
|
+
return;
|
|
723
|
+
const ext = path.extname(part.path);
|
|
724
|
+
if (paths?.source && ext && ext !== ".js" && buildExtensions.includes(ext))
|
|
725
|
+
throw new Error(
|
|
726
|
+
`Invalid sanity.json: Part path has extension which is not applicable after compiling. ${ext} becomes .js after compiling. Specify filename without extension (${path.basename(
|
|
727
|
+
part.path
|
|
728
|
+
)}) (parts[${index}])`
|
|
729
|
+
);
|
|
730
|
+
if (!verifySourceParts && !verifyCompiledParts)
|
|
731
|
+
return;
|
|
732
|
+
const [srcExists, outDirExists] = await Promise.all([
|
|
733
|
+
hasSourceFile(part.path, paths),
|
|
734
|
+
verifyCompiledParts && hasCompiledFile(part.path, paths)
|
|
735
|
+
]);
|
|
736
|
+
if (!srcExists)
|
|
737
|
+
throw new Error(
|
|
738
|
+
`Invalid sanity.json: Part path references file that does not exist in source directory (${paths?.source || paths?.basePath}) (parts[${index}])`
|
|
739
|
+
);
|
|
740
|
+
if (verifyCompiledParts && !outDirExists)
|
|
741
|
+
throw new Error(
|
|
742
|
+
`Invalid sanity.json: Part path references file ("${part.path}") that does not exist in compiled directory (${paths?.compiled}) (parts[${index}])`
|
|
743
|
+
);
|
|
744
|
+
}
|
|
745
|
+
function validatePartNames(part, index, options) {
|
|
746
|
+
const pluginName = options.pluginName ? options.pluginName.replace(/^sanity-plugin-/, "") : "";
|
|
747
|
+
if (!part?.name || !part?.name?.startsWith(`part:${pluginName}/`))
|
|
748
|
+
throw new Error(
|
|
749
|
+
`Invalid sanity.json: "name" must be prefixed with "part:${pluginName}/" - got "${part?.name}" (parts[${index}])`
|
|
750
|
+
);
|
|
751
|
+
if (!part?.implements?.startsWith("part:"))
|
|
752
|
+
throw new Error(
|
|
753
|
+
`Invalid sanity.json: "implements" must be prefixed with "part:" - got "${part?.implements}" (parts[${index}])`
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
function validateAllowedPartKeys(part, index) {
|
|
757
|
+
const disallowed = Object.keys(part).filter((key) => !allowedPartProps.includes(key)).map((key) => `"${key}"`);
|
|
758
|
+
if (disallowed.length > 0) {
|
|
759
|
+
const plural = disallowed.length > 1 ? "s" : "", joined = disallowed.join(", ");
|
|
760
|
+
throw new Error(
|
|
761
|
+
`Invalid sanity.json: Key${plural} ${joined} ${plural ? "are" : "is"} not allowed in a part declaration (parts[${index}])`
|
|
762
|
+
);
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
function validatePartStringValues(part, index) {
|
|
766
|
+
const nonStrings = Object.keys(part).filter((key) => typeof part[key] != "string").map((key) => `"${key}"`);
|
|
767
|
+
if (nonStrings.length > 0) {
|
|
768
|
+
const plural = nonStrings.length > 1 ? "s" : "", joined = nonStrings.join(", ");
|
|
769
|
+
throw new Error(
|
|
770
|
+
`Invalid sanity.json: Key${plural} ${joined} should be of type string (parts[${index}])`
|
|
771
|
+
);
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function isObject$1(obj) {
|
|
775
|
+
return !Array.isArray(obj) && obj !== null && typeof obj == "object";
|
|
776
|
+
}
|
|
777
|
+
async function hasSanityJson(basePath) {
|
|
778
|
+
const file = await readJsonFile(path.join(basePath, "sanity.json")).catch(
|
|
779
|
+
errorToUndefined
|
|
780
|
+
);
|
|
781
|
+
return { exists: !!file, isRoot: !!(file && file.root) };
|
|
782
|
+
}
|
|
783
|
+
async function findStudioV3Config(basePath) {
|
|
784
|
+
const jsFile = "sanity.config.js";
|
|
785
|
+
if (await fileExists(path.join(basePath, jsFile)))
|
|
786
|
+
return { v3ConfigFile: jsFile };
|
|
787
|
+
const tsFile = "sanity.config.ts";
|
|
788
|
+
return { v3ConfigFile: await fileExists(path.join(basePath, tsFile)) ? tsFile : void 0 };
|
|
789
|
+
}
|
|
790
|
+
const lockedDependencies = {
|
|
791
|
+
"styled-components": "^6.1",
|
|
792
|
+
eslint: "^8.57.0"
|
|
793
|
+
};
|
|
794
|
+
function resolveLatestVersions(packages) {
|
|
795
|
+
const versions = {};
|
|
796
|
+
for (const pkgName of packages)
|
|
797
|
+
versions[pkgName] = pkgName in lockedDependencies ? lockedDependencies[pkgName] : "latest";
|
|
798
|
+
return pProps(
|
|
799
|
+
versions,
|
|
800
|
+
async (range, pkgName) => {
|
|
801
|
+
const version = await getLatestVersion(pkgName, { range });
|
|
802
|
+
if (!version)
|
|
803
|
+
throw new Error(`Found no version for ${pkgName}`);
|
|
804
|
+
return rangeify(version);
|
|
805
|
+
},
|
|
806
|
+
{ concurrency: 8 }
|
|
807
|
+
);
|
|
808
|
+
}
|
|
809
|
+
function rangeify(version) {
|
|
810
|
+
return `^${version}`;
|
|
811
|
+
}
|
|
812
|
+
const defaultDependencies = [], defaultDevDependencies = [
|
|
1523
813
|
"sanity",
|
|
1524
814
|
// peer dependencies of `sanity`
|
|
1525
815
|
"react",
|
|
@@ -1529,7 +819,7 @@ const defaultDependencies = [incompatiblePluginPackage], defaultDevDependencies
|
|
|
1529
819
|
async function getPackage(opts) {
|
|
1530
820
|
const options = { flags: {}, ...opts };
|
|
1531
821
|
validateOptions(options);
|
|
1532
|
-
const { basePath, validate
|
|
822
|
+
const { basePath, validate = !0 } = options, manifestPath = path.normalize(path.join(basePath, "package.json"));
|
|
1533
823
|
let content;
|
|
1534
824
|
try {
|
|
1535
825
|
content = await readFile(manifestPath, "utf8");
|
|
@@ -1546,7 +836,7 @@ async function getPackage(opts) {
|
|
|
1546
836
|
}
|
|
1547
837
|
if (!isObject(parsed))
|
|
1548
838
|
throw new Error("Invalid package.json: Root must be an object");
|
|
1549
|
-
return
|
|
839
|
+
return validate && await validatePackage(parsed, options), parsed;
|
|
1550
840
|
}
|
|
1551
841
|
async function validatePackage(manifest, opts) {
|
|
1552
842
|
validateOptions(opts);
|
|
@@ -1566,11 +856,9 @@ async function validatePluginPackage(manifest, options) {
|
|
|
1566
856
|
function validatePackageName(manifest) {
|
|
1567
857
|
if (typeof manifest.name != "string")
|
|
1568
858
|
throw new Error('Invalid package.json: "name" must be a string');
|
|
1569
|
-
const valid = validateNpmPackageName(
|
|
1570
|
-
manifest.name
|
|
1571
|
-
);
|
|
859
|
+
const valid = validateNpmPackageName(manifest.name);
|
|
1572
860
|
if (!valid.validForNewPackages)
|
|
1573
|
-
throw new Error(`Invalid package.json: "name" is invalid: ${valid.errors.join(", ")}`);
|
|
861
|
+
throw new Error(`Invalid package.json: "name" is invalid: ${(valid.errors ?? []).join(", ")}`);
|
|
1574
862
|
if (manifest.name[0] !== "@" && !manifest.name.startsWith("sanity-plugin-"))
|
|
1575
863
|
throw new Error(
|
|
1576
864
|
'Invalid package.json: "name" should be prefixed with "sanity-plugin-" (or scoped - @your-company/plugin-name)'
|
|
@@ -1635,7 +923,7 @@ async function writePackageJson(data, options) {
|
|
|
1635
923
|
...await resolveLatestVersions(defaultDependencies)
|
|
1636
924
|
},
|
|
1637
925
|
forcedPackageVersions
|
|
1638
|
-
),
|
|
926
|
+
), devDependencies = forceDependencyVersions(
|
|
1639
927
|
{
|
|
1640
928
|
...addDevDeps || {},
|
|
1641
929
|
...prev.devDependencies || {},
|
|
@@ -1649,8 +937,8 @@ async function writePackageJson(data, options) {
|
|
|
1649
937
|
...await resolveLatestVersions(defaultPeerDependencies)
|
|
1650
938
|
},
|
|
1651
939
|
forcedPeerPackageVersions
|
|
1652
|
-
), source = flags.typescript ? "./src/index.ts" : "./src/index.js",
|
|
1653
|
-
|
|
940
|
+
), source = flags.typescript ? "./src/index.ts" : "./src/index.js", files = [outDir];
|
|
941
|
+
files.sort();
|
|
1654
942
|
const forcedOrder = {
|
|
1655
943
|
name: pluginName,
|
|
1656
944
|
version: prev.version ?? "1.0.0",
|
|
@@ -1670,10 +958,10 @@ async function writePackageJson(data, options) {
|
|
|
1670
958
|
"./package.json": "./package.json"
|
|
1671
959
|
},
|
|
1672
960
|
...flags.typescript ? { types: `./${outDir}/index.d.ts` } : {},
|
|
1673
|
-
files
|
|
961
|
+
files,
|
|
1674
962
|
scripts: { ...prev.scripts },
|
|
1675
963
|
dependencies: sortKeys(dependencies),
|
|
1676
|
-
devDependencies: sortKeys(
|
|
964
|
+
devDependencies: sortKeys(devDependencies),
|
|
1677
965
|
peerDependencies: sortKeys(peerDependencies),
|
|
1678
966
|
engines: {
|
|
1679
967
|
node: requiredNodeEngine
|
|
@@ -1688,6 +976,8 @@ async function writePackageJson(data, options) {
|
|
|
1688
976
|
return log.debug("Does manifest differ? %s", differs ? "yes" : "no"), differs && await writePackageJsonDirect(manifest, options), differs ? manifest : prev;
|
|
1689
977
|
}
|
|
1690
978
|
function urlsFromOrigin(gitOrigin) {
|
|
979
|
+
if (!gitOrigin)
|
|
980
|
+
return {};
|
|
1691
981
|
const details = githubUrlToObject(gitOrigin);
|
|
1692
982
|
return details ? {
|
|
1693
983
|
homepage: `https://github.com/${details.user}/${details.repo}#readme`,
|
|
@@ -1728,32 +1018,49 @@ function forceDependencyVersions(deps, versions = forcedPackageVersions) {
|
|
|
1728
1018
|
return Object.fromEntries(entries);
|
|
1729
1019
|
}
|
|
1730
1020
|
export {
|
|
1021
|
+
addBuildScripts,
|
|
1022
|
+
addPackageJsonScripts,
|
|
1023
|
+
addScript,
|
|
1024
|
+
copyFileWithOverwritePrompt,
|
|
1731
1025
|
disallowDuplicateEslintConfig,
|
|
1732
1026
|
disallowDuplicatePrettierConfig,
|
|
1733
1027
|
ensureDir,
|
|
1028
|
+
errorToUndefined,
|
|
1734
1029
|
fileExists,
|
|
1735
1030
|
findStudioV3Config,
|
|
1031
|
+
forceDependencyVersions,
|
|
1032
|
+
forcedDevPackageVersions,
|
|
1033
|
+
forcedPackageVersions,
|
|
1736
1034
|
getPackage,
|
|
1737
1035
|
hasSanityJson,
|
|
1738
|
-
inject,
|
|
1739
1036
|
isEmptyish,
|
|
1740
1037
|
mergedPackages,
|
|
1741
1038
|
mkdir,
|
|
1742
|
-
presetHelpList,
|
|
1743
1039
|
prompt,
|
|
1040
|
+
promptForPackageName,
|
|
1041
|
+
promptForRepoOrigin,
|
|
1042
|
+
readFile$2 as readFile,
|
|
1043
|
+
readJsonFile,
|
|
1044
|
+
resolveLatestVersions,
|
|
1045
|
+
sortKeys,
|
|
1744
1046
|
validateBabelConfig,
|
|
1047
|
+
validateBannedFiles,
|
|
1745
1048
|
validateDeprecatedDependencies,
|
|
1049
|
+
validateEsmOnly,
|
|
1050
|
+
validateIncompatiblePlugin,
|
|
1746
1051
|
validateNodeEngine,
|
|
1747
1052
|
validatePackageName$1 as validatePackageName,
|
|
1748
1053
|
validatePackageType,
|
|
1749
1054
|
validatePkgUtilsDependency,
|
|
1750
1055
|
validatePkgUtilsVersion,
|
|
1751
|
-
validatePluginSanityJson,
|
|
1752
1056
|
validateSanityDependencies,
|
|
1753
1057
|
validateScripts,
|
|
1754
1058
|
validateSrcIndexFile,
|
|
1755
1059
|
validateStudioConfig,
|
|
1756
1060
|
validateTsConfig,
|
|
1757
|
-
writeFile
|
|
1061
|
+
writeFile,
|
|
1062
|
+
writeFileWithOverwritePrompt,
|
|
1063
|
+
writePackageJson,
|
|
1064
|
+
writePackageJsonDirect
|
|
1758
1065
|
};
|
|
1759
1066
|
//# sourceMappingURL=package.js.map
|