@sanity/plugin-kit 5.0.2 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,17 @@
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 { createRequester } from "get-it";
13
+ import { pkg } from "./package2.js";
14
+ function defaultSourceJs(pkg2) {
6
15
  return outdent`
7
16
  import {definePlugin} from 'sanity'
8
17
 
@@ -11,7 +20,7 @@ function defaultSourceJs(pkg) {
11
20
  *
12
21
  * \`\`\`js
13
22
  * import {defineConfig} from 'sanity'
14
- * import {myPlugin} from '${pkg.name}'
23
+ * import {myPlugin} from '${pkg2.name}'
15
24
  *
16
25
  * export default defineConfig({
17
26
  * // ...
@@ -23,15 +32,15 @@ function defaultSourceJs(pkg) {
23
32
  */
24
33
  export const myPlugin = definePlugin((config = {}) => {
25
34
  // eslint-disable-next-line no-console
26
- console.log(\`hello from ${pkg.name}\`)
35
+ console.log(\`hello from ${pkg2.name}\`)
27
36
  return {
28
- name: '${pkg.name}',
37
+ name: '${pkg2.name}',
29
38
  }
30
39
  })
31
40
  `.trimStart() + `
32
41
  `;
33
42
  }
34
- function defaultSourceTs(pkg) {
43
+ function defaultSourceTs(pkg2) {
35
44
  return outdent`
36
45
  import {definePlugin} from 'sanity'
37
46
 
@@ -44,7 +53,7 @@ function defaultSourceTs(pkg) {
44
53
  *
45
54
  * \`\`\`ts
46
55
  * import {defineConfig} from 'sanity'
47
- * import {myPlugin} from '${pkg.name}'
56
+ * import {myPlugin} from '${pkg2.name}'
48
57
  *
49
58
  * export default defineConfig({
50
59
  * // ...
@@ -56,14 +65,702 @@ function defaultSourceTs(pkg) {
56
65
  */
57
66
  export const myPlugin = definePlugin<MyPluginConfig | void>((config = {}) => {
58
67
  // eslint-disable-next-line no-console
59
- console.log('hello from ${pkg.name}')
68
+ console.log('hello from ${pkg2.name}')
60
69
  return {
61
- name: '${pkg.name}',
70
+ name: '${pkg2.name}',
62
71
  }
63
72
  })
64
73
  `.trimStart() + `
65
74
  `;
66
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
+ }
67
764
  const initFlags = {
68
765
  ...sharedFlags,
69
766
  scripts: {
@@ -135,6 +832,8 @@ async function init(options) {
135
832
  }
136
833
  export {
137
834
  init,
138
- initFlags
835
+ initFlags,
836
+ inject,
837
+ presetHelpList
139
838
  };
140
839
  //# sourceMappingURL=init2.js.map