@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.
@@ -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$1, { outdent } from "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 { pkg } from "./package2.js";
13
+ import { URL } from "url";
17
14
  import inquirer from "inquirer";
18
- import getLatestVersion from "get-latest-version";
15
+ import { pkg } from "./package2.js";
16
+ import { getLatestVersion } from "get-latest-version";
19
17
  import pProps from "p-props";
20
- import { getIt } from "get-it";
21
- import { promise, jsonRequest, jsonResponse, httpErrors, headers } from "get-it/middleware";
22
- import { execSync } from "child_process";
23
- import { validate } from "email-validator";
24
- import xdgBasedir from "xdg-basedir";
25
- function eslintrcTemplate(options) {
26
- const { flags } = options, eslintConfig = {
27
- root: !0,
28
- env: {
29
- node: !0,
30
- browser: !0
31
- },
32
- extends: [
33
- "sanity",
34
- flags.typescript && "sanity/typescript",
35
- "sanity/react",
36
- "plugin:react-hooks/recommended",
37
- flags.prettier && "plugin:prettier/recommended",
38
- "plugin:react/jsx-runtime"
39
- ].filter(Boolean)
40
- };
41
- return {
42
- type: "template",
43
- force: flags.force,
44
- to: ".eslintrc",
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$1 = util.promisify(fs.readFile), writeFile = util.promisify(fs.writeFile);
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$1(filePath, "utf8");
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$1(filepath, "utf8");
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 mergedPackages = [
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$1`
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$1`
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$1`
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$1`
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 validatePkgUtilsDependency({ devDependencies: devDependencies2 }) {
1239
- return devDependencies2?.["@sanity/pkg-utils"] ? [] : [
1240
- outdent$1`
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$1`
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$1`
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: devDependencies2, peerDependencies } = packageJson, allDependencies = { ...dependencies, ...devDependencies2, ...peerDependencies }, illegalDeps = Object.keys(allDependencies).filter((dep) => mergedPackages.includes(dep)), unique = [...new Set(illegalDeps).values()];
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$1`
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: devDependencies2, peerDependencies } = packageJson, allDependencies = { ...dependencies, ...devDependencies2, ...peerDependencies }, illegalDeps = Object.keys(allDependencies).filter((dep) => deprecatedDevDeps.includes(dep)), unique = [...new Set(illegalDeps).values()];
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$1`
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$1`
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), files2 = {};
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
- files2[filename] = await fileExists(filepath);
398
+ files[filename] = await fileExists(filepath);
1320
399
  }
1321
- const sanityJson = await readJson5File({ basePath, filename: "sanity.json" }), hasConfigFile = (fileBase) => filesWithSuffixes([fileBase], suffixes).some((filename) => files2[filename]), hasCliConfig = hasConfigFile("sanity.cli"), hasStudioConfig = hasConfigFile("sanity.config"), errors = [];
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 info2 = [
1324
- outdent$1`
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$1`
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(info2.join(`
415
+ errors.push(info.join(`
1337
416
 
1338
417
  `));
1339
418
  }
1340
419
  return hasCliConfig || errors.push(
1341
- outdent$1`
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$1`
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$1`
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$1`
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 validatePluginSanityJson({
480
+ async function validateIncompatiblePlugin({
1402
481
  basePath,
1403
482
  packageJson
1404
483
  }) {
1405
- const sanityJson = await readJson5File({ basePath, filename: "sanity.json" }), expectedDefaults = {
1406
- parts: [
1407
- {
1408
- implements: "part:@sanity/base/sanity-root",
1409
- path: "./v2-incompatible.js"
1410
- }
1411
- ]
1412
- }, hasSinglePart = sanityJson && Object.keys(sanityJson).length === 1 && sanityJson?.parts && sanityJson.parts.length === 1, firstPart = hasSinglePart ? sanityJson?.parts?.[0] : void 0, correctImplements = firstPart?.implements === expectedDefaults.parts[0].implements, pathExists = firstPart?.path && await fileExists(path.normalize(path.join(basePath, firstPart.path))), hasDependency = !!packageJson.dependencies?.[incompatiblePluginPackage];
1413
- if (!(sanityJson && hasSinglePart && correctImplements && pathExists && hasDependency)) {
1414
- const errors = [
1415
- sanityJson ? null : "sanity.json does not exist",
1416
- hasSinglePart ? null : 'sanity.json should have exactly one entry in "parts", but did not.',
1417
- correctImplements ? null : `The part should implement ${expectedDefaults.parts[0].implements}, but did not.`,
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
- - ${errors.join(`
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
- sanity.json will only be used when incorrectly installing a v3 plugin in a v2 Studio.
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
- This check ensures that sanity.json conforms with the usage section of
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$1`
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: files2
553
+ files
1467
554
  }) {
1468
555
  const found = [];
1469
- for (const file of files2) {
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$1`
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$1`
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 defaultDependencies = [incompatiblePluginPackage], defaultDevDependencies = [
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: validate2 = !0 } = options, manifestPath = path.normalize(path.join(basePath, "package.json"));
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 validate2 && await validatePackage(parsed, options), parsed;
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
- ), devDependencies2 = forceDependencyVersions(
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", files2 = [outDir, "sanity.json", "src", "v2-incompatible.js"];
1653
- files2.sort();
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: files2,
961
+ files,
1674
962
  scripts: { ...prev.scripts },
1675
963
  dependencies: sortKeys(dependencies),
1676
- devDependencies: sortKeys(devDependencies2),
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