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