@sanity/plugin-kit 5.0.2 → 5.0.3

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.
@@ -1,8 +1,18 @@
1
1
  import path from "path";
2
- import outdent from "outdent";
3
- import { sharedFlags, defaultOutDir } from "./index.js";
4
- import { inject, getPackage, ensureDir, writeFile } from "./package.js";
5
- function defaultSourceJs(pkg) {
2
+ import outdent, { outdent as outdent$1 } from "outdent";
3
+ import { log, sharedFlags, defaultOutDir } from "./index.js";
4
+ import { readJsonFile, prompt, getPackage, addPackageJsonScripts, sortKeys, writePackageJsonDirect, readFile, errorToUndefined, writeFile, resolveLatestVersions, addScript, forceDependencyVersions, forcedPackageVersions, forcedDevPackageVersions, promptForPackageName, promptForRepoOrigin, writePackageJson, addBuildScripts, fileExists, writeFileWithOverwritePrompt, copyFileWithOverwritePrompt, ensureDir } from "./package.js";
5
+ import { fileURLToPath } from "url";
6
+ import licenses from "@rexxars/choosealicense-list";
7
+ import gitRemoteOriginUrl from "git-remote-origin-url";
8
+ import chalk from "chalk";
9
+ import { execSync } from "child_process";
10
+ import { validate } from "email-validator";
11
+ import xdgBasedir from "xdg-basedir";
12
+ import { getIt } from "get-it";
13
+ import { promise, jsonRequest, jsonResponse, httpErrors, headers } from "get-it/middleware";
14
+ import { pkg } from "./package2.js";
15
+ function defaultSourceJs(pkg2) {
6
16
  return outdent`
7
17
  import {definePlugin} from 'sanity'
8
18
 
@@ -11,7 +21,7 @@ function defaultSourceJs(pkg) {
11
21
  *
12
22
  * \`\`\`js
13
23
  * import {defineConfig} from 'sanity'
14
- * import {myPlugin} from '${pkg.name}'
24
+ * import {myPlugin} from '${pkg2.name}'
15
25
  *
16
26
  * export default defineConfig({
17
27
  * // ...
@@ -23,15 +33,15 @@ function defaultSourceJs(pkg) {
23
33
  */
24
34
  export const myPlugin = definePlugin((config = {}) => {
25
35
  // eslint-disable-next-line no-console
26
- console.log(\`hello from ${pkg.name}\`)
36
+ console.log(\`hello from ${pkg2.name}\`)
27
37
  return {
28
- name: '${pkg.name}',
38
+ name: '${pkg2.name}',
29
39
  }
30
40
  })
31
41
  `.trimStart() + `
32
42
  `;
33
43
  }
34
- function defaultSourceTs(pkg) {
44
+ function defaultSourceTs(pkg2) {
35
45
  return outdent`
36
46
  import {definePlugin} from 'sanity'
37
47
 
@@ -44,7 +54,7 @@ function defaultSourceTs(pkg) {
44
54
  *
45
55
  * \`\`\`ts
46
56
  * import {defineConfig} from 'sanity'
47
- * import {myPlugin} from '${pkg.name}'
57
+ * import {myPlugin} from '${pkg2.name}'
48
58
  *
49
59
  * export default defineConfig({
50
60
  * // ...
@@ -56,14 +66,767 @@ function defaultSourceTs(pkg) {
56
66
  */
57
67
  export const myPlugin = definePlugin<MyPluginConfig | void>((config = {}) => {
58
68
  // eslint-disable-next-line no-console
59
- console.log('hello from ${pkg.name}')
69
+ console.log('hello from ${pkg2.name}')
60
70
  return {
61
- name: '${pkg.name}',
71
+ name: '${pkg2.name}',
62
72
  }
63
73
  })
64
74
  `.trimStart() + `
65
75
  `;
66
76
  }
77
+ function eslintrcTemplate(options) {
78
+ const { flags } = options, eslintConfig = {
79
+ root: !0,
80
+ env: {
81
+ node: !0,
82
+ browser: !0
83
+ },
84
+ extends: [
85
+ "sanity",
86
+ flags.typescript && "sanity/typescript",
87
+ "sanity/react",
88
+ "plugin:react-hooks/recommended",
89
+ flags.prettier && "plugin:prettier/recommended",
90
+ "plugin:react/jsx-runtime"
91
+ ].filter(Boolean)
92
+ };
93
+ return {
94
+ type: "template",
95
+ force: flags.force,
96
+ to: ".eslintrc",
97
+ value: JSON.stringify(eslintConfig, null, 2)
98
+ };
99
+ }
100
+ function eslintignoreTemplate(options) {
101
+ const { flags, outDir } = options, patterns = [
102
+ ".eslintrc.js",
103
+ "commitlint.config.js",
104
+ outDir,
105
+ "lint-staged.config.js",
106
+ "package.config.ts",
107
+ flags.typescript ? "*.js" : ""
108
+ ].filter(Boolean);
109
+ return patterns.sort(), {
110
+ type: "template",
111
+ force: flags.force,
112
+ to: ".eslintignore",
113
+ value: patterns.join(`
114
+ `)
115
+ };
116
+ }
117
+ function gitignoreTemplate() {
118
+ return {
119
+ type: "template",
120
+ to: ".gitignore",
121
+ value: outdent$1`
122
+ # Logs
123
+ logs
124
+ *.log
125
+ npm-debug.log*
126
+
127
+ # Runtime data
128
+ pids
129
+ *.pid
130
+ *.seed
131
+
132
+ # Directory for instrumented libs generated by jscoverage/JSCover
133
+ lib-cov
134
+
135
+ # Coverage directory used by tools like istanbul
136
+ coverage
137
+
138
+ # nyc test coverage
139
+ .nyc_output
140
+
141
+ # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
142
+ .grunt
143
+
144
+ # node-waf configuration
145
+ .lock-wscript
146
+
147
+ # Compiled binary addons (http://nodejs.org/api/addons.html)
148
+ build/Release
149
+
150
+ # Dependency directories
151
+ node_modules
152
+ jspm_packages
153
+
154
+ # Optional npm cache directory
155
+ .npm
156
+
157
+ # Optional REPL history
158
+ .node_repl_history
159
+
160
+ # macOS finder cache file
161
+ .DS_Store
162
+
163
+ # VS Code settings
164
+ .vscode
165
+
166
+ # IntelliJ
167
+ .idea
168
+ *.iml
169
+
170
+ # Cache
171
+ .cache
172
+
173
+ # Yalc
174
+ .yalc
175
+ yalc.lock
176
+
177
+ # npm package zips
178
+ *.tgz
179
+ `
180
+ };
181
+ }
182
+ function pkgConfigTemplate(options) {
183
+ const { flags, outDir } = options;
184
+ return {
185
+ type: "template",
186
+ force: flags.force,
187
+ // Always a `.ts` config: plugins are ESM (`"type": "module"`), so `@sanity/pkg-utils`
188
+ // loads it without needing a `.mts`/`.mjs` extension to force ESM interpretation.
189
+ to: "package.config.ts",
190
+ value: outdent$1`
191
+ import {defineConfig} from '@sanity/pkg-utils'
192
+
193
+ export default defineConfig({
194
+ dist: '${outDir}',
195
+ tsconfig: 'tsconfig.${outDir}.json',
196
+
197
+ // Remove this block to enable strict export validation
198
+ extract: {
199
+ rules: {
200
+ 'ae-incompatible-release-tags': 'off',
201
+ 'ae-internal-missing-underscore': 'off',
202
+ 'ae-missing-release-tag': 'off',
203
+ },
204
+ },
205
+ })
206
+ `
207
+ };
208
+ }
209
+ function prettierignoreTemplate(options) {
210
+ const { outDir } = options;
211
+ return {
212
+ type: "template",
213
+ to: ".prettierignore",
214
+ value: [outDir, "pnpm-lock.yaml", "yarn.lock", "package-lock.json"].join(`
215
+ `)
216
+ };
217
+ }
218
+ function tsconfigTemplate(options) {
219
+ const { flags } = options;
220
+ return {
221
+ type: "template",
222
+ force: flags.force,
223
+ to: "tsconfig.json",
224
+ value: outdent$1`
225
+ {
226
+ "extends": "./tsconfig.settings",
227
+ "include": ["./src", "./package.config.ts"]
228
+ }
229
+ `
230
+ };
231
+ }
232
+ function tsconfigTemplateDist(options) {
233
+ const { flags, outDir } = options;
234
+ return {
235
+ type: "template",
236
+ force: flags.force,
237
+ to: `tsconfig.${outDir}.json`,
238
+ value: outdent$1`
239
+ {
240
+ "extends": "./tsconfig.settings",
241
+ "include": ["./src"],
242
+ "exclude": [
243
+ "./src/**/__fixtures__",
244
+ "./src/**/__mocks__",
245
+ "./src/**/*.test.ts",
246
+ "./src/**/*.test.tsx"
247
+ ]
248
+ }
249
+ `
250
+ };
251
+ }
252
+ function tsconfigTemplateSettings(options) {
253
+ const { flags, outDir } = options;
254
+ return {
255
+ type: "template",
256
+ force: flags.force,
257
+ to: "tsconfig.settings.json",
258
+ value: outdent$1`
259
+ {
260
+ "compilerOptions": {
261
+ "rootDir": ".",
262
+ "outDir": "./${outDir}",
263
+
264
+ "target": "esnext",
265
+ "jsx": "preserve",
266
+ "module": "preserve",
267
+ "moduleResolution": "bundler",
268
+ "esModuleInterop": true,
269
+ "resolveJsonModule": true,
270
+ "moduleDetection": "force",
271
+ "strict": true,
272
+ "allowSyntheticDefaultImports": true,
273
+ "skipLibCheck": true,
274
+ "forceConsistentCasingInFileNames": true,
275
+ "isolatedModules": true,
276
+
277
+ // Don't emit by default, pkg-utils will ignore this when generating .d.ts files
278
+ "noEmit": true
279
+ }
280
+ }
281
+ `
282
+ };
283
+ }
284
+ const renovatePreset = {
285
+ name: "renovatebot",
286
+ description: "Files to enable renovatebot.",
287
+ apply: applyPreset$3
288
+ };
289
+ async function applyPreset$3(options) {
290
+ await writeAssets(
291
+ [
292
+ {
293
+ type: "copy",
294
+ from: ["renovatebot", "renovate.json"],
295
+ to: "renovate.json"
296
+ }
297
+ ],
298
+ options
299
+ );
300
+ }
301
+ function generateReadme(data) {
302
+ const { user, pluginName, license } = data;
303
+ return outdent`
304
+ # ${pluginName}
305
+
306
+
307
+ ${installationSnippet(pluginName ?? "unknown")}
308
+
309
+ ## Usage
310
+
311
+ Add it as a plugin in \`sanity.config.ts\` (or .js):
312
+
313
+ \`\`\`ts
314
+ import {defineConfig} from 'sanity'
315
+ import {myPlugin} from '${pluginName}'
316
+
317
+ export default defineConfig({
318
+ //...
319
+ plugins: [myPlugin({})],
320
+ })
321
+ \`\`\`
322
+
323
+ ${getLicenseText(license?.id, user?.name ? user : void 0)}
324
+ ${developTestSnippet()}
325
+ ` + `
326
+ `;
327
+ }
328
+ function installationSnippet(packageName) {
329
+ return outdent`
330
+ ## Installation
331
+
332
+ \`\`\`sh
333
+ npm install ${packageName}
334
+ \`\`\`
335
+ `;
336
+ }
337
+ function developTestSnippet() {
338
+ return outdent`
339
+ ## Develop & test
340
+
341
+ This plugin uses [@sanity/plugin-kit](https://github.com/sanity-io/plugin-kit)
342
+ with default configuration for build & watch scripts.
343
+
344
+ See [Testing a plugin in Sanity Studio](https://github.com/sanity-io/plugin-kit#testing-a-plugin-in-sanity-studio)
345
+ on how to run this plugin with hotreload in the studio.
346
+ `;
347
+ }
348
+ function getLicenseText(licenseId, user) {
349
+ if (!licenseId)
350
+ return "";
351
+ let licenseName = licenses.find(licenseId).title;
352
+ licenseName = licenseName?.replace(/\s+license$/i, "");
353
+ let licenseText = `## License
354
+ `;
355
+ return licenseName && user?.name ? licenseText = `${licenseText}
356
+ [${licenseName}](LICENSE) \xA9 ${user?.name}
357
+ ` : licenseName ? licenseText = `${licenseText}
358
+ [${licenseName}](LICENSE)
359
+ ` : licenseText = `${licenseText}
360
+ See [LICENSE](LICENSE)`, licenseText;
361
+ }
362
+ function isDefaultGitHubReadme(readme) {
363
+ if (!readme)
364
+ return !1;
365
+ const lines = readme.split(`
366
+ `, 20).filter(Boolean);
367
+ return lines.length <= 2 && lines[0].startsWith("#");
368
+ }
369
+ const request = getIt([
370
+ promise({ onlyBody: !0 }),
371
+ jsonRequest(),
372
+ jsonResponse(),
373
+ httpErrors(),
374
+ headers({ "User-Agent": `${pkg.name}@${pkg.version}` })
375
+ ]);
376
+ async function getUserInfo({ requireUserConfirmation, flags }, pkg2) {
377
+ const userInfo = getPackageUserInfo({ author: flags.author ?? pkg2?.author }) || await getSanityUserInfo() || await getGitUserInfo();
378
+ return requireUserConfirmation ? promptForInfo(userInfo) : userInfo;
379
+ }
380
+ function getPackageUserInfo(pkg2) {
381
+ let author = pkg2?.author;
382
+ if (!author)
383
+ return;
384
+ if (author && typeof author != "string")
385
+ return author;
386
+ if (!author.includes("@"))
387
+ return { name: author };
388
+ const [pre, ...post] = author.replace(/[<>[\]]/g, "").split(/@/), nameParts = pre.split(/\s+/), email = [nameParts[nameParts.length - 1], ...post].join("@");
389
+ return { name: nameParts.slice(0, -1).join(" "), email };
390
+ }
391
+ async function promptForInfo(defValue) {
392
+ const name = await prompt("Author name", {
393
+ filter: filterString,
394
+ default: defValue && defValue.name,
395
+ validate: requiredString
396
+ }), email = await prompt("Author email", {
397
+ filter: filterString,
398
+ default: defValue && defValue.email,
399
+ validate: validOrEmptyEmail
400
+ });
401
+ return { name, email };
402
+ }
403
+ async function getSanityUserInfo() {
404
+ try {
405
+ const token = (await readJsonFile(
406
+ path.join(xdgBasedir.config ?? "", "sanity", "config.json")
407
+ ))?.authToken;
408
+ if (!token)
409
+ return;
410
+ const user = await request({
411
+ url: "https://api.sanity.io/v1/users/me",
412
+ headers: { Authorization: `Bearer ${token}` }
413
+ });
414
+ if (!user)
415
+ return;
416
+ const { name, email } = user;
417
+ return { name, email };
418
+ } catch {
419
+ return;
420
+ }
421
+ }
422
+ async function getGitUserInfo() {
423
+ try {
424
+ const name = execSync("git config user.name", { encoding: "utf8" }).trim(), email = execSync("git config user.email", { encoding: "utf8" }).trim();
425
+ return name ? { name, email: email || void 0 } : void 0;
426
+ } catch {
427
+ return;
428
+ }
429
+ }
430
+ function filterString(val) {
431
+ return (val || "").trim();
432
+ }
433
+ function requiredString(value) {
434
+ return value.length > 1 ? !0 : "Required";
435
+ }
436
+ function validOrEmptyEmail(value) {
437
+ return value ? validate(value) ? !0 : "Must either be a valid email or empty" : !0;
438
+ }
439
+ const semverWorkflowPreset = {
440
+ name: "semver-workflow",
441
+ description: "Files and dependencies for conventional-commits, github workflow and semantic-release.",
442
+ apply: applyPreset$2
443
+ }, info = (write, msg, ...args) => write && log.info(msg, ...args);
444
+ async function applyPreset$2(options) {
445
+ await writeAssets(semverWorkflowFiles(), options), await addPrepareScript(options), await addDevDependencies$2(options), await updateReadme(options);
446
+ }
447
+ async function addPrepareScript(options) {
448
+ const pkg2 = await getPackage(options), didWrite = await addPackageJsonScripts(pkg2, options, (scripts) => (scripts.prepare = addScript("husky", scripts.prepare), scripts));
449
+ info(didWrite, "Added prepare script to package.json");
450
+ }
451
+ async function addDevDependencies$2(options) {
452
+ const pkg2 = await getPackage(options), devDeps = sortKeys({
453
+ ...pkg2.devDependencies,
454
+ ...await semverWorkflowDependencies()
455
+ }), newPkg = { ...pkg2 };
456
+ newPkg.devDependencies = devDeps, await writePackageJsonDirect(newPkg, options), log.info("Updated devDependencies."), log.info(
457
+ chalk.green(
458
+ outdent`
459
+ semantic-release preset injected.
460
+
461
+ Please confer
462
+ https://github.com/sanity-io/plugin-kit/blob/main/docs/semver-workflow.md#manual-steps-after-inject
463
+ to finalize configuration for this preset.
464
+ `.trim()
465
+ )
466
+ );
467
+ }
468
+ async function updateReadme(options) {
469
+ const { basePath } = options, readmePath = path.join(basePath, "README.md"), readme = await readFile(readmePath, "utf8").catch(errorToUndefined) ?? "", { install, usage, developTest, license, releaseSnippet } = await readmeSnippets(options), prependSections = missingSections(readme, [install, usage]), appendSections = missingSections(readme, [license, developTest, releaseSnippet]);
470
+ if (prependSections.length || appendSections.length) {
471
+ const updatedReadme = [...prependSections, readme, ...appendSections].filter(Boolean).join(`
472
+
473
+ `);
474
+ await writeFile(readmePath, updatedReadme, { encoding: "utf8" }), log.info("Updated README. Please review the changes.");
475
+ }
476
+ }
477
+ async function readmeSnippets(options) {
478
+ const pkg2 = await getPackage(options), user = await getUserInfo(options, pkg2), bestEffortUrl = readmeBaseurl(pkg2), install = installationSnippet(pkg2.name ?? "unknown"), usage = outdent`
479
+ ## Usage
480
+ `, license = getLicenseText(typeof pkg2.license == "string" ? pkg2.license : void 0, user), releaseSnippet = outdent`
481
+ ### Release new version
482
+
483
+ Run ["CI & Release" workflow](${bestEffortUrl}/actions/workflows/main.yml).
484
+ Make sure to select the main branch and check "Release new version".
485
+
486
+ Semantic release will only release on configured branches, so it is safe to run release on any branch.
487
+ `;
488
+ return {
489
+ install,
490
+ usage,
491
+ license,
492
+ developTest: developTestSnippet(),
493
+ releaseSnippet
494
+ };
495
+ }
496
+ function missingSections(readme, sections) {
497
+ return sections.filter((section) => !closeEnough(section, readme));
498
+ }
499
+ function closeEnough(a, b) {
500
+ const aLines = a.split(`
501
+ `), bLines = b.split(`
502
+ `);
503
+ return aLines.filter((line) => bLines.find((bLine) => bLine === line)).length >= aLines.length * 0.5;
504
+ }
505
+ function semverWorkflowFiles() {
506
+ return [
507
+ {
508
+ type: "copy",
509
+ from: [".github", "workflows", "main.yml"],
510
+ to: [".github", "workflows", "main.yml"]
511
+ },
512
+ { type: "copy", from: [".husky", "commit-msg"], to: [".husky", "commit-msg"] },
513
+ { type: "copy", from: [".husky", "pre-commit"], to: [".husky", "pre-commit"] },
514
+ { type: "copy", from: [".releaserc.json"], to: ".releaserc.json" },
515
+ { type: "copy", from: ["commitlint.template.js"], to: "commitlint.config.js" },
516
+ { type: "copy", from: ["lint-staged.template.js"], to: "lint-staged.config.js" }
517
+ ].map((fromTo) => fromTo.type === "copy" ? {
518
+ ...fromTo,
519
+ from: ["semver-workflow", ...fromTo.from]
520
+ } : fromTo);
521
+ }
522
+ async function semverWorkflowDependencies() {
523
+ return resolveLatestVersions([
524
+ "@commitlint/cli",
525
+ "@commitlint/config-conventional",
526
+ "@sanity/semantic-release-preset",
527
+ "husky",
528
+ "lint-staged"
529
+ ]);
530
+ }
531
+ function readmeBaseurl(pkg2) {
532
+ 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, "");
533
+ }
534
+ const ui = {
535
+ name: "ui",
536
+ description: "`@sanity/ui` and dependencies",
537
+ apply: applyPreset$1
538
+ };
539
+ async function applyPreset$1(options) {
540
+ await addDependencies(options), await addDevDependencies$1(options), log.info(chalk.green("ui preset injected"));
541
+ }
542
+ async function addDependencies(options) {
543
+ const pkg2 = await getPackage(options), newDeps = sortKeys(
544
+ forceDependencyVersions(
545
+ {
546
+ ...pkg2.dependencies,
547
+ ...await resolveDependencyList()
548
+ },
549
+ forcedPackageVersions
550
+ )
551
+ ), newPkg = { ...pkg2 };
552
+ newPkg.dependencies = newDeps, await writePackageJsonDirect(newPkg, options), log.info("Updated dependencies.");
553
+ }
554
+ async function addDevDependencies$1(options) {
555
+ const pkg2 = await getPackage(options), newDeps = sortKeys(
556
+ forceDependencyVersions(
557
+ {
558
+ ...pkg2.devDependencies,
559
+ ...await resolveDevDependencyList()
560
+ },
561
+ forcedDevPackageVersions
562
+ )
563
+ ), newPkg = { ...pkg2 };
564
+ newPkg.devDependencies = newDeps, await writePackageJsonDirect(newPkg, options), log.info("Updated devDependencies.");
565
+ }
566
+ async function resolveDependencyList() {
567
+ return resolveLatestVersions(["@sanity/icons", "@sanity/ui"]);
568
+ }
569
+ async function resolveDevDependencyList() {
570
+ return resolveLatestVersions([
571
+ // install the peer dependencies of `@sanity/ui` as dev dependencies
572
+ "react",
573
+ "react-dom",
574
+ "styled-components"
575
+ ]);
576
+ }
577
+ const uiWorkshop = {
578
+ name: "ui-workshop",
579
+ description: "Files for testing custom components with @sanity/ui-workshop",
580
+ apply: applyPreset
581
+ };
582
+ async function applyPreset(options) {
583
+ await writeAssets(files(), options), await addDevDependencies(options), await updateGitIgnore(options), log.info(
584
+ chalk.green(
585
+ outdent`
586
+ ui-workshop preset injected.
587
+
588
+ Please confer
589
+ https://github.com/sanity-io/plugin-kit/blob/main/docs/ui-workshop.md#manual-steps-after-inject
590
+ to finalize configuration for this preset.
591
+ `.trim()
592
+ )
593
+ );
594
+ }
595
+ function files() {
596
+ return [
597
+ { type: "copy", from: ["workshop.config.ts"], to: ["workshop.config.ts"] },
598
+ { type: "copy", from: ["src", "CustomField.tsx"], to: ["src", "CustomField.tsx"] },
599
+ {
600
+ type: "copy",
601
+ from: ["src", "__workshop__", "index.tsx"],
602
+ to: ["src", "__workshop__", "index.tsx"]
603
+ },
604
+ {
605
+ type: "copy",
606
+ from: ["src", "__workshop__", "props.tsx"],
607
+ to: ["src", "__workshop__", "props.tsx"]
608
+ }
609
+ ].map((fromTo) => fromTo.type === "copy" ? {
610
+ ...fromTo,
611
+ from: ["ui-workshop", ...fromTo.from]
612
+ } : fromTo);
613
+ }
614
+ async function updateGitIgnore(options) {
615
+ const { basePath } = options, gitignorePath = path.join(basePath, ".gitignore");
616
+ let gitignore = await readFile(gitignorePath, "utf8").catch(errorToUndefined) ?? "";
617
+ const value = ".workshop";
618
+ gitignore.includes(value) || (gitignore += `
619
+
620
+ ${value}`, await writeFile(gitignorePath, gitignore, { encoding: "utf8" }));
621
+ }
622
+ async function addDevDependencies(options) {
623
+ const pkg2 = await getPackage(options), devDeps = sortKeys({
624
+ ...pkg2.devDependencies,
625
+ ...await devDependencies()
626
+ }), newPkg = { ...pkg2 };
627
+ newPkg.devDependencies = devDeps, await writePackageJsonDirect(newPkg, options), log.info("Updated devDependencies.");
628
+ }
629
+ async function devDependencies() {
630
+ return resolveLatestVersions([
631
+ "@sanity/ui-workshop",
632
+ "@sanity/icons",
633
+ "@sanity/ui",
634
+ "react",
635
+ "react-dom",
636
+ "styled-components"
637
+ ]);
638
+ }
639
+ const presets = [semverWorkflowPreset, renovatePreset, ui, uiWorkshop], presetNames = presets.map((p) => p?.name);
640
+ function presetHelpList(padStart) {
641
+ return presets.map((p) => `${"".padStart(padStart)}${p.name.padEnd(20)}${p.description}`).join(`
642
+ `);
643
+ }
644
+ async function injectPresets(options) {
645
+ if (options.flags.presetOnly && !options.flags.preset?.length)
646
+ throw new Error("--preset-only, but no --preset [preset-name] was provided.");
647
+ const applyPresets = presetsFromInput(options.flags.preset);
648
+ for (const preset of applyPresets)
649
+ await preset.apply(options);
650
+ }
651
+ function presetsFromInput(inputPresets) {
652
+ if (!inputPresets)
653
+ return [];
654
+ const unknownPresets = inputPresets.filter((p) => !presetNames.includes(p));
655
+ if (unknownPresets.length)
656
+ throw new Error(
657
+ `Unknown --preset(s): [${unknownPresets.join(", ")}]. Must be one of: [${presetNames.join(
658
+ ", "
659
+ )}]`
660
+ );
661
+ return inputPresets.filter(onlyUnique).map((presetName) => presets.find((p) => p.name === presetName)).filter((p) => !!p);
662
+ }
663
+ function onlyUnique(value, index, arr) {
664
+ return arr.indexOf(value) === index;
665
+ }
666
+ const bannedFields = ["login", "description", "projecturl", "email"], preferredLicenses = ["MIT", "ISC", "BSD-3-Clause"], otherLicenses = Object.keys(licenses.list).filter((id) => {
667
+ const license = licenses.list[id];
668
+ return !preferredLicenses.includes(id) && !bannedFields.some((field) => license.body.includes(`[${field}]`));
669
+ });
670
+ async function inject(options) {
671
+ options.flags.presetOnly ? log.info("Only apply presets, skipping default inject.") : await injectBase(options), await injectPresets(options);
672
+ }
673
+ async function injectBase(options) {
674
+ const { basePath, flags, requireUserConfirmation } = options, info2 = (write, msg, ...args) => write && log.info(msg, ...args), pkg2 = await getPackage(options).catch(errorToUndefined);
675
+ log.debug("Plugin has package.json: %s", pkg2 ? "yes" : "no");
676
+ const user = await getUserInfo(options, pkg2);
677
+ log.debug("User information: %o", user);
678
+ const pkgName = flags.name ?? pkg2?.name, pluginName = requireUserConfirmation || !pkgName ? await promptForPackageName(options, pkgName) : pkgName;
679
+ log.debug("Plugin name: %s", pluginName);
680
+ const license = await getLicense(flags, { user, pluginName, pkg: pkg2, requireUserConfirmation }), licenseChanged = (pkg2 && pkg2.license) !== (license && license.id);
681
+ log.debug("License: %s", license ? license.id : "<none>");
682
+ const description = await getProjectDescription(basePath, pkg2, requireUserConfirmation);
683
+ log.debug("Description: %s", description || "<none>");
684
+ const repoUrl = flags.repo ?? (await gitRemoteOriginUrl(basePath).catch(errorToUndefined) || pkg2?.repository?.url), gitOrigin = requireUserConfirmation ? await promptForRepoOrigin(options, repoUrl) : repoUrl;
685
+ log.debug("Remote origin: %s", gitOrigin || "<none>");
686
+ const data = { user, pluginName, license, description, pkg: pkg2, gitOrigin };
687
+ let didWrite;
688
+ const newPkg = await writePackageJson(data, options);
689
+ 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");
690
+ }
691
+ async function writeReadme(data, options) {
692
+ const { basePath } = options, readmePath = path.join(basePath, "README.md"), readme = await readFile(readmePath, "utf8").catch(errorToUndefined);
693
+ return readme && !isDefaultGitHubReadme(readme) ? !1 : (await writeFileWithOverwritePrompt(readmePath, generateReadme(data), {
694
+ encoding: "utf8",
695
+ force: options.flags.force
696
+ }), !0);
697
+ }
698
+ async function writeLicense({ license }, options, licenseChanged) {
699
+ const { basePath, flags } = options;
700
+ if (flags.license === !1 || !license)
701
+ return !1;
702
+ const hasLicenseMdFile = await fileExists(path.join(basePath, "LICENSE.md")), licensePath = path.join(basePath, hasLicenseMdFile ? "LICENSE.md" : "LICENSE");
703
+ return await writeFileWithOverwritePrompt(licensePath, license.text, {
704
+ encoding: "utf8",
705
+ default: licenseChanged,
706
+ force: flags.force
707
+ }), !0;
708
+ }
709
+ async function getLicense(flags, {
710
+ user,
711
+ pluginName,
712
+ pkg: pkg2,
713
+ requireUserConfirmation
714
+ }) {
715
+ const license = await getLicenseIdentifier(flags, pkg2, requireUserConfirmation);
716
+ if (!license)
717
+ return;
718
+ const text = license.body.replace(/\[fullname\]/g, user?.name).replace(/\[project\]/g, pluginName).replace(/\[year\]/g, (/* @__PURE__ */ new Date()).getFullYear());
719
+ return { id: license.id, text };
720
+ }
721
+ async function getLicenseIdentifier(flags, pkg2, requireUserConfirmation = !1) {
722
+ if (flags.license === !1)
723
+ return null;
724
+ if (typeof flags.license == "string") {
725
+ const license = licenses.find(`${flags.license}`);
726
+ if (!license)
727
+ throw new Error(`License "${flags.license}" not found`);
728
+ return license;
729
+ }
730
+ if (pkg2 && pkg2.license && !requireUserConfirmation) {
731
+ const license = licenses.find(`${pkg2.license}`);
732
+ if (license)
733
+ return license;
734
+ log.warn(`package.json contains license "${pkg2.license}", which is not recognized`);
735
+ }
736
+ const licenseId = await prompt("Which license do you want to use?", {
737
+ default: pkg2 && pkg2.license && licenses.find(pkg2.license) ? pkg2.license : preferredLicenses[0],
738
+ choices: [
739
+ prompt.separator(),
740
+ ...preferredLicenses.map((value) => ({ value, name: licenses.list[value].title })),
741
+ prompt.separator(),
742
+ ...otherLicenses.map((value) => ({ value, name: licenses.list[value].title }))
743
+ ]
744
+ });
745
+ return licenses.find(licenseId);
746
+ }
747
+ async function getProjectDescription(basePath, pkg2, requireUserConfirmation = !1) {
748
+ let description = await resolveProjectDescription(basePath, pkg2);
749
+ return requireUserConfirmation && (description = await prompt("Plugin description", { default: description || "" })), description ?? "";
750
+ }
751
+ async function resolveProjectDescription(basePath, pkg2) {
752
+ if (pkg2 && typeof pkg2.description == "string" && pkg2.description.length > 5)
753
+ return pkg2.description;
754
+ try {
755
+ const readmePath = path.join(basePath, "README.md"), readme = await readFile(readmePath, "utf8"), [title, description] = readme.split(`
756
+ `).filter(Boolean);
757
+ if (!title || !description || !title.match(/^#\s+\w+/))
758
+ return null;
759
+ const unlinked = description.replace(/\[(.*?)\]\(.*?\)/g, "$1");
760
+ return /^[^#]/.test(unlinked) ? unlinked : null;
761
+ } catch (err) {
762
+ return errorToUndefined(err);
763
+ }
764
+ }
765
+ async function writeAssets(injectables, { basePath, flags }) {
766
+ const assetsDir = await findAssetsDir(), from = (...segments) => path.join(assetsDir, "inject", ...segments), to = (...segments) => path.join(basePath, ...segments), writes = [];
767
+ for (const injectable of injectables) {
768
+ if (injectable.type === "copy") {
769
+ const fromPath = asArray(injectable.from), toPath = asArray(injectable.to);
770
+ await copyFileWithOverwritePrompt(from(...fromPath), to(...toPath), flags) && writes.push(path.join(...toPath));
771
+ continue;
772
+ }
773
+ if (injectable.type === "template") {
774
+ const toPath = asArray(injectable.to);
775
+ await writeFileWithOverwritePrompt(to(...toPath), `${injectable.value.trim()}
776
+ `, {
777
+ default: "n",
778
+ force: injectable.force || flags.force
779
+ }), writes.push(path.join(...toPath));
780
+ continue;
781
+ }
782
+ throw new Error(`Unknown operation type "${injectable.type}"`);
783
+ }
784
+ return writes;
785
+ }
786
+ async function writeStaticAssets(options) {
787
+ const { outDir, flags } = options, files2 = [
788
+ flags.eslint && eslintrcTemplate({ flags: options.flags }),
789
+ flags.eslint && eslintignoreTemplate({ outDir, flags: options.flags }),
790
+ { type: "copy", from: "editorconfig", to: ".editorconfig" },
791
+ { type: "copy", from: "sanity.json", to: "sanity.json" },
792
+ { type: "copy", from: "v2-incompatible.js.template", to: "v2-incompatible.js" },
793
+ pkgConfigTemplate({ outDir, flags: options.flags }),
794
+ flags.gitignore && gitignoreTemplate(),
795
+ flags.typescript && tsconfigTemplate({ flags: options.flags }),
796
+ flags.typescript && tsconfigTemplateDist({ outDir, flags: options.flags }),
797
+ flags.typescript && tsconfigTemplateSettings({ outDir, flags: options.flags }),
798
+ flags.prettier && prettierignoreTemplate({ outDir }),
799
+ flags.prettier && { type: "copy", from: "prettierrc.json", to: ".prettierrc" }
800
+ ].map((f) => f || void 0).filter((f) => !!f);
801
+ return writeAssets(files2, options);
802
+ }
803
+ function asArray(input) {
804
+ return typeof input == "string" ? [input] : input;
805
+ }
806
+ async function findAssetsDir() {
807
+ let maxBackpaddle = 3, currDir = path.dirname(fileURLToPath(import.meta.url)), assetsDir = "";
808
+ for (; !assetsDir && maxBackpaddle; ) {
809
+ currDir = path.join(currDir, "..");
810
+ const assets = path.join(currDir, "assets");
811
+ await fileExists(assets) ? assetsDir = assets : maxBackpaddle--;
812
+ }
813
+ if (!assetsDir)
814
+ throw new Error("Could not find assets directory!");
815
+ return assetsDir;
816
+ }
817
+ async function addCompileDirToGitIgnore(options) {
818
+ const gitIgnorePath = path.join(options.basePath, ".gitignore"), gitignore = await readFile(gitIgnorePath, "utf8").catch(errorToUndefined);
819
+ if (!gitignore)
820
+ return !1;
821
+ const ignore = options.outDir.replace(/^[./]+/, "").split("/")[0];
822
+ if (!ignore)
823
+ return !1;
824
+ const lines = gitignore.trim().split(`
825
+ `);
826
+ return lines.includes(ignore) ? !1 : (lines.push("", "# Compiled plugin", ignore), await writeFile(gitIgnorePath, lines.join(`
827
+ `) + `
828
+ `, { encoding: "utf8" }), !0);
829
+ }
67
830
  const initFlags = {
68
831
  ...sharedFlags,
69
832
  scripts: {
@@ -120,13 +883,13 @@ const initFlags = {
120
883
  }
121
884
  };
122
885
  async function init(options) {
123
- let dependencies = {}, devDependencies = {}, peerDependencies = {};
886
+ let dependencies = {}, devDependencies2 = {}, peerDependencies = {};
124
887
  await inject({
125
888
  ...options,
126
889
  outDir: defaultOutDir,
127
890
  requireUserConfirmation: !options.flags.force,
128
891
  dependencies,
129
- devDependencies,
892
+ devDependencies: devDependencies2,
130
893
  peerDependencies,
131
894
  validate: !1
132
895
  });
@@ -135,6 +898,8 @@ async function init(options) {
135
898
  }
136
899
  export {
137
900
  init,
138
- initFlags
901
+ initFlags,
902
+ inject,
903
+ presetHelpList
139
904
  };
140
905
  //# sourceMappingURL=init2.js.map