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