@sanity/plugin-kit 0.0.1-studio-v3.1

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.
Files changed (194) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +398 -0
  3. package/assets/splat/LICENSE +21 -0
  4. package/assets/splat/editorconfig +13 -0
  5. package/assets/splat/eslint.config.js +5 -0
  6. package/assets/splat/gitignore +55 -0
  7. package/assets/splat/npmignore +9 -0
  8. package/assets/splat/prettierrc.js +6 -0
  9. package/assets/splat/sanity.json +8 -0
  10. package/assets/splat/template-tsconfig.json +23 -0
  11. package/assets/splat/v2-incompatible.js.template +11 -0
  12. package/lib/package.json +127 -0
  13. package/lib/src/actions/init.d.ts +65 -0
  14. package/lib/src/actions/init.js +83 -0
  15. package/lib/src/actions/init.js.map +1 -0
  16. package/lib/src/actions/link-watch.d.ts +3 -0
  17. package/lib/src/actions/link-watch.js +69 -0
  18. package/lib/src/actions/link-watch.js.map +1 -0
  19. package/lib/src/actions/splat.d.ts +26 -0
  20. package/lib/src/actions/splat.js +296 -0
  21. package/lib/src/actions/splat.js.map +1 -0
  22. package/lib/src/actions/verify/types.d.ts +77 -0
  23. package/lib/src/actions/verify/types.js +3 -0
  24. package/lib/src/actions/verify/types.js.map +1 -0
  25. package/lib/src/actions/verify/validations.d.ts +28 -0
  26. package/lib/src/actions/verify/validations.js +379 -0
  27. package/lib/src/actions/verify/validations.js.map +1 -0
  28. package/lib/src/actions/verify/verify-common.d.ts +43 -0
  29. package/lib/src/actions/verify/verify-common.js +88 -0
  30. package/lib/src/actions/verify/verify-common.js.map +1 -0
  31. package/lib/src/actions/verify-package.d.ts +5 -0
  32. package/lib/src/actions/verify-package.js +72 -0
  33. package/lib/src/actions/verify-package.js.map +1 -0
  34. package/lib/src/actions/verify-studio.d.ts +5 -0
  35. package/lib/src/actions/verify-studio.js +55 -0
  36. package/lib/src/actions/verify-studio.js.map +1 -0
  37. package/lib/src/actions/verify.d.ts +0 -0
  38. package/lib/src/actions/verify.js +330 -0
  39. package/lib/src/actions/verify.js.map +1 -0
  40. package/lib/src/cli.d.ts +2 -0
  41. package/lib/src/cli.js +86 -0
  42. package/lib/src/cli.js.map +1 -0
  43. package/lib/src/cmds/index.d.ts +8 -0
  44. package/lib/src/cmds/index.js +12 -0
  45. package/lib/src/cmds/index.js.map +1 -0
  46. package/lib/src/cmds/init.d.ts +4 -0
  47. package/lib/src/cmds/init.js +90 -0
  48. package/lib/src/cmds/init.js.map +1 -0
  49. package/lib/src/cmds/link-watch.d.ts +4 -0
  50. package/lib/src/cmds/link-watch.js +49 -0
  51. package/lib/src/cmds/link-watch.js.map +1 -0
  52. package/lib/src/cmds/splat.d.ts +4 -0
  53. package/lib/src/cmds/splat.js +63 -0
  54. package/lib/src/cmds/splat.js.map +1 -0
  55. package/lib/src/cmds/verify-package.d.ts +4 -0
  56. package/lib/src/cmds/verify-package.js +38 -0
  57. package/lib/src/cmds/verify-package.js.map +1 -0
  58. package/lib/src/cmds/verify-studio.d.ts +4 -0
  59. package/lib/src/cmds/verify-studio.js +38 -0
  60. package/lib/src/cmds/verify-studio.js.map +1 -0
  61. package/lib/src/cmds/verify.d.ts +0 -0
  62. package/lib/src/cmds/verify.js +42 -0
  63. package/lib/src/cmds/verify.js.map +1 -0
  64. package/lib/src/cmds/version.d.ts +4 -0
  65. package/lib/src/cmds/version.js +55 -0
  66. package/lib/src/cmds/version.js.map +1 -0
  67. package/lib/src/configs/buildExtensions.d.ts +1 -0
  68. package/lib/src/configs/buildExtensions.js +5 -0
  69. package/lib/src/configs/buildExtensions.js.map +1 -0
  70. package/lib/src/configs/default-source.d.ts +3 -0
  71. package/lib/src/configs/default-source.js +70 -0
  72. package/lib/src/configs/default-source.js.map +1 -0
  73. package/lib/src/configs/merged-packages.d.ts +1 -0
  74. package/lib/src/configs/merged-packages.js +24 -0
  75. package/lib/src/configs/merged-packages.js.map +1 -0
  76. package/lib/src/configs/uselessFiles.d.ts +1 -0
  77. package/lib/src/configs/uselessFiles.js +33 -0
  78. package/lib/src/configs/uselessFiles.js.map +1 -0
  79. package/lib/src/constants.d.ts +11 -0
  80. package/lib/src/constants.js +15 -0
  81. package/lib/src/constants.js.map +1 -0
  82. package/lib/src/dependencies/find.d.ts +0 -0
  83. package/lib/src/dependencies/find.js +195 -0
  84. package/lib/src/dependencies/find.js.map +1 -0
  85. package/lib/src/dependencies/import-linter.d.ts +3 -0
  86. package/lib/src/dependencies/import-linter.js +112 -0
  87. package/lib/src/dependencies/import-linter.js.map +1 -0
  88. package/lib/src/index.d.ts +2 -0
  89. package/lib/src/index.js +6 -0
  90. package/lib/src/index.js.map +1 -0
  91. package/lib/src/npm/manager.d.ts +7 -0
  92. package/lib/src/npm/manager.js +62 -0
  93. package/lib/src/npm/manager.js.map +1 -0
  94. package/lib/src/npm/package.d.ts +8 -0
  95. package/lib/src/npm/package.js +288 -0
  96. package/lib/src/npm/package.js.map +1 -0
  97. package/lib/src/npm/publish.d.ts +1 -0
  98. package/lib/src/npm/publish.js +14 -0
  99. package/lib/src/npm/publish.js.map +1 -0
  100. package/lib/src/npm/resolveLatestVersions.d.ts +3 -0
  101. package/lib/src/npm/resolveLatestVersions.js +35 -0
  102. package/lib/src/npm/resolveLatestVersions.js.map +1 -0
  103. package/lib/src/sanity/manifest.d.ts +48 -0
  104. package/lib/src/sanity/manifest.js +263 -0
  105. package/lib/src/sanity/manifest.js.map +1 -0
  106. package/lib/src/sharedFlags.d.ts +15 -0
  107. package/lib/src/sharedFlags.js +17 -0
  108. package/lib/src/sharedFlags.js.map +1 -0
  109. package/lib/src/util/command-parser.d.ts +9 -0
  110. package/lib/src/util/command-parser.js +41 -0
  111. package/lib/src/util/command-parser.js.map +1 -0
  112. package/lib/src/util/errorToUndefined.d.ts +1 -0
  113. package/lib/src/util/errorToUndefined.js +11 -0
  114. package/lib/src/util/errorToUndefined.js.map +1 -0
  115. package/lib/src/util/files.d.ts +36 -0
  116. package/lib/src/util/files.js +253 -0
  117. package/lib/src/util/files.js.map +1 -0
  118. package/lib/src/util/log.d.ts +14 -0
  119. package/lib/src/util/log.js +36 -0
  120. package/lib/src/util/log.js.map +1 -0
  121. package/lib/src/util/prompt.d.ts +13 -0
  122. package/lib/src/util/prompt.js +75 -0
  123. package/lib/src/util/prompt.js.map +1 -0
  124. package/lib/src/util/readme.d.ts +5 -0
  125. package/lib/src/util/readme.js +73 -0
  126. package/lib/src/util/readme.js.map +1 -0
  127. package/lib/src/util/request.d.ts +1 -0
  128. package/lib/src/util/request.js +19 -0
  129. package/lib/src/util/request.js.map +1 -0
  130. package/lib/src/util/user.d.ts +10 -0
  131. package/lib/src/util/user.js +106 -0
  132. package/lib/src/util/user.js.map +1 -0
  133. package/lib/test/cli.test.d.ts +1 -0
  134. package/lib/test/cli.test.js +64 -0
  135. package/lib/test/cli.test.js.map +1 -0
  136. package/lib/test/fixture-utils.d.ts +25 -0
  137. package/lib/test/fixture-utils.js +67 -0
  138. package/lib/test/fixture-utils.js.map +1 -0
  139. package/lib/test/init-verify-build.test.d.ts +1 -0
  140. package/lib/test/init-verify-build.test.js +75 -0
  141. package/lib/test/init-verify-build.test.js.map +1 -0
  142. package/lib/test/init.test.d.ts +1 -0
  143. package/lib/test/init.test.js +137 -0
  144. package/lib/test/init.test.js.map +1 -0
  145. package/lib/test/run-test-command.d.ts +1 -0
  146. package/lib/test/run-test-command.js +6 -0
  147. package/lib/test/run-test-command.js.map +1 -0
  148. package/lib/test/verify-package.test.d.ts +1 -0
  149. package/lib/test/verify-package.test.js +81 -0
  150. package/lib/test/verify-package.test.js.map +1 -0
  151. package/lib/test/version.test.d.ts +1 -0
  152. package/lib/test/version.test.js +48 -0
  153. package/lib/test/version.test.js.map +1 -0
  154. package/package.json +127 -0
  155. package/src/actions/init.ts +104 -0
  156. package/src/actions/link-watch.ts +74 -0
  157. package/src/actions/splat.ts +366 -0
  158. package/src/actions/verify/types.ts +84 -0
  159. package/src/actions/verify/validations.ts +401 -0
  160. package/src/actions/verify/verify-common.ts +92 -0
  161. package/src/actions/verify-package.ts +87 -0
  162. package/src/actions/verify-studio.ts +55 -0
  163. package/src/actions/verify.ts +328 -0
  164. package/src/cli.ts +77 -0
  165. package/src/cmds/index.ts +9 -0
  166. package/src/cmds/init.ts +85 -0
  167. package/src/cmds/link-watch.ts +51 -0
  168. package/src/cmds/splat.ts +59 -0
  169. package/src/cmds/verify-package.ts +36 -0
  170. package/src/cmds/verify-studio.ts +36 -0
  171. package/src/cmds/verify.ts +40 -0
  172. package/src/cmds/version.ts +67 -0
  173. package/src/configs/buildExtensions.ts +1 -0
  174. package/src/configs/default-source.ts +68 -0
  175. package/src/configs/merged-packages.ts +20 -0
  176. package/src/configs/uselessFiles.ts +29 -0
  177. package/src/constants.ts +13 -0
  178. package/src/dependencies/find.ts +193 -0
  179. package/src/dependencies/import-linter.ts +103 -0
  180. package/src/index.ts +4 -0
  181. package/src/npm/manager.ts +44 -0
  182. package/src/npm/package.ts +370 -0
  183. package/src/npm/publish.ts +9 -0
  184. package/src/npm/resolveLatestVersions.ts +26 -0
  185. package/src/sanity/manifest.ts +340 -0
  186. package/src/sharedFlags.ts +14 -0
  187. package/src/util/command-parser.ts +31 -0
  188. package/src/util/errorToUndefined.ts +7 -0
  189. package/src/util/files.ts +249 -0
  190. package/src/util/log.ts +44 -0
  191. package/src/util/prompt.ts +70 -0
  192. package/src/util/readme.ts +72 -0
  193. package/src/util/request.ts +13 -0
  194. package/src/util/user.ts +110 -0
package/package.json ADDED
@@ -0,0 +1,127 @@
1
+ {
2
+ "name": "@sanity/plugin-kit",
3
+ "version": "0.0.1-studio-v3.1",
4
+ "description": "Enhanced Sanity.io plugin development experience",
5
+ "scripts": {
6
+ "clean": "rimraf lib",
7
+ "prebuild": "npm run clean",
8
+ "build": "tsc",
9
+ "postbuild": "node executable-index.js",
10
+ "watch": "tsc --watch",
11
+ "test": "tap",
12
+ "prepare": "husky install",
13
+ "commit": "git-cz",
14
+ "lint": "eslint .",
15
+ "compile": "tsc --noEmit",
16
+ "format": "prettier src -w",
17
+ "semantic-release": "semantic-release",
18
+ "prepublishOnly": "npm run build"
19
+ },
20
+ "binname": "sanity-plugin",
21
+ "source": "./src/index.ts",
22
+ "types": "./lib/src/index.d.ts",
23
+ "bin": "lib/src/index.js",
24
+ "main": "./lib/src/index.js",
25
+ "files": [
26
+ "v2-incompatible.js",
27
+ "src",
28
+ "lib",
29
+ "assets"
30
+ ],
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+ssh://git@github.com/rexxars/sanipack.git"
34
+ },
35
+ "engines": {
36
+ "node": ">=14.0.0"
37
+ },
38
+ "keywords": [
39
+ "sanity-io",
40
+ "sanity",
41
+ "plugin",
42
+ "development",
43
+ "babel",
44
+ "typescript",
45
+ "bootstrap"
46
+ ],
47
+ "author": "Sanity.io <hello@sanity.io>",
48
+ "license": "MIT",
49
+ "bugs": {
50
+ "url": "https://github.com/rexxars/sanipack/issues"
51
+ },
52
+ "homepage": "https://github.com/rexxars/sanipack#readme",
53
+ "dependencies": {
54
+ "@rexxars/choosealicense-list": "^1.1.2",
55
+ "chalk": "^4.1.2",
56
+ "concurrently": "^7.1.0",
57
+ "discover-path": "^1.0.0",
58
+ "email-validator": "^2.0.4",
59
+ "execa": "^5.1.1",
60
+ "find-babel-config": "^1.2.0",
61
+ "get-it": "^5.0.5",
62
+ "get-latest-version": "^2.0.0",
63
+ "git-remote-origin-url": "^3.1.0",
64
+ "git-user-info": "^1.0.1",
65
+ "github-url-to-object": "^4.0.6",
66
+ "inquirer": "^8.2.0",
67
+ "meow": "^9.0.0",
68
+ "nodemon": "^2.0.15",
69
+ "npm-packlist": "^3.0.0",
70
+ "npm-run-path": "^4.0.1",
71
+ "outdent": "^0.8.0",
72
+ "p-any": "^3.0.0",
73
+ "p-props": "^4.0.0",
74
+ "postcss": "^8.3.9",
75
+ "rimraf": "^3.0.2",
76
+ "semver": "^7.3.5",
77
+ "spdx-license-ids": "^3.0.10",
78
+ "validate-npm-package-name": "^3.0.0",
79
+ "xdg-basedir": "^4.0.0",
80
+ "yalc": "^1.0.0-pre.53"
81
+ },
82
+ "peerDependencies": {
83
+ "eslint": ">=8.0.0"
84
+ },
85
+ "devDependencies": {
86
+ "@commitlint/cli": "^16.2.3",
87
+ "@commitlint/config-conventional": "^16.2.1",
88
+ "@parcel/packager-ts": "^2.6.0",
89
+ "@parcel/transformer-typescript-types": "^2.6.0",
90
+ "@types/eslint": "^8.4.3",
91
+ "@types/inquirer": "^8.2.1",
92
+ "@types/node": "^17.0.40",
93
+ "@types/nodemon": "^1.19.1",
94
+ "@types/tap": "^15.0.7",
95
+ "@typescript-eslint/eslint-plugin": "^5.20.0",
96
+ "@typescript-eslint/parser": "^5.20.0",
97
+ "eslint": "^8.15.0",
98
+ "eslint-config-prettier": "^8.5.0",
99
+ "eslint-config-sanity": "^5.1.0",
100
+ "eslint-plugin-prettier": "^4.0.0",
101
+ "husky": "^7.0.4",
102
+ "json5": "^2.2.1",
103
+ "lint-staged": "^12.4.0",
104
+ "parcel": "^2.6.0",
105
+ "prettier": "^2.6.2",
106
+ "readdirp": "^3.6.0",
107
+ "rimraf": "^3.0.2",
108
+ "semantic-release": "^19.0.2",
109
+ "sanity": "^2.30.1-purple-unicorn.940",
110
+ "sinon": "^14.0.0",
111
+ "tap": "^16.2.0",
112
+ "ts-node": "^10.8.0",
113
+ "typescript": "^4.7.2"
114
+ },
115
+ "tap": {
116
+ "jobs": 2,
117
+ "browser": false,
118
+ "timeout": 120,
119
+ "reporter": "spec",
120
+ "check-coverage": false,
121
+ "coverage-report": [
122
+ "html"
123
+ ],
124
+ "test-ignore": "^lib/.*|.*ignore.*|.*run-test-command.*|.*fixture.utils.*",
125
+ "ts": true
126
+ }
127
+ }
@@ -0,0 +1,104 @@
1
+ import path from 'path'
2
+ import {splat} from './splat'
3
+ import {ensureDir, writeFile} from '../util/files'
4
+ import {resolveLatestVersions} from '../npm/resolveLatestVersions'
5
+ import sharedFlags from '../sharedFlags'
6
+ import {TypedFlags} from 'meow'
7
+ import {getPackage} from '../npm/package'
8
+ import {defaultSourceJs, defaultSourceTs} from '../configs/default-source'
9
+ import {incompatiblePluginPackage} from '../constants'
10
+
11
+ export const initFlags = {
12
+ ...sharedFlags,
13
+ scripts: {
14
+ type: 'boolean',
15
+ default: true,
16
+ },
17
+ eslint: {
18
+ type: 'boolean',
19
+ default: true,
20
+ },
21
+ typescript: {
22
+ type: 'boolean',
23
+ default: true,
24
+ },
25
+ prettier: {
26
+ type: 'boolean',
27
+ default: true,
28
+ },
29
+ license: {
30
+ type: 'string',
31
+ },
32
+ editorconfig: {
33
+ type: 'boolean',
34
+ default: true,
35
+ },
36
+ gitignore: {
37
+ type: 'boolean',
38
+ default: true,
39
+ },
40
+ force: {
41
+ type: 'boolean',
42
+ default: false,
43
+ },
44
+ install: {
45
+ type: 'boolean',
46
+ default: true,
47
+ },
48
+ name: {
49
+ type: 'string',
50
+ },
51
+ author: {
52
+ type: 'string',
53
+ },
54
+ repo: {
55
+ type: 'string',
56
+ },
57
+ } as const
58
+
59
+ export type InitFlags = TypedFlags<typeof initFlags>
60
+
61
+ const defaultDependencies = [incompatiblePluginPackage]
62
+ // purple-unicorn as version in devDependencies will install 2.29.5, so must be explicit
63
+ // using ^2.29.5-purple-unicorn.856 is not safe either
64
+ const defaultDevDependencies = {react: '^17.0.0 || ^18.0.0', sanity: '2.29.5-purple-unicorn.856'}
65
+ const defaultPeerDependencies = {react: '^17.0.0 || ^18.0.0', sanity: 'purple-unicorn'}
66
+
67
+ export interface InitOptions {
68
+ basePath: string
69
+ flags: InitFlags
70
+ }
71
+
72
+ export async function init(options: InitOptions) {
73
+ let dependencies = {}
74
+ let devDependencies = {}
75
+ let peerDependencies = {}
76
+
77
+ dependencies = {...dependencies, ...(await resolveLatestVersions(defaultDependencies))}
78
+ devDependencies = {
79
+ ...devDependencies,
80
+ ...defaultDevDependencies,
81
+ ...(await resolveLatestVersions(['rimraf'])),
82
+ }
83
+ peerDependencies = {
84
+ ...peerDependencies,
85
+ ...defaultPeerDependencies,
86
+ }
87
+
88
+ await splat({
89
+ ...options,
90
+ requireUserConfirmation: !options.flags.force,
91
+ dependencies,
92
+ devDependencies,
93
+ peerDependencies,
94
+ validate: false,
95
+ })
96
+
97
+ const packageJson = await getPackage({basePath: options.basePath, validate: false})
98
+ const typescript = options.flags.typescript
99
+ const source = typescript ? defaultSourceTs(packageJson) : defaultSourceJs(packageJson)
100
+ const filename = typescript ? 'index.ts' : 'index.js'
101
+ const srcDir = path.resolve(options.basePath, 'src')
102
+ await ensureDir(srcDir)
103
+ await writeFile(path.join(srcDir, filename), source, {encoding: 'utf8'})
104
+ }
@@ -0,0 +1,74 @@
1
+ /*
2
+ ISC License (ISC)
3
+ Copyright 2019 Johan Otterud
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
6
+ provided that the above copyright notice and this permission notice appear in all copies.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
9
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
11
+ WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH
12
+ THE USE OR PERFORMANCE OF THIS SOFTWARE.
13
+ */
14
+
15
+ /*
16
+ This code is a modified version of https://github.com/johot/yalc-watch,
17
+ and the ISC License has been added for this file only, in accordance with the package.json license field in that package
18
+ */
19
+
20
+ import nodemon from 'nodemon'
21
+ import concurrently from 'concurrently'
22
+ import chalk from 'chalk'
23
+ import fs from 'fs'
24
+ import path from 'path'
25
+ import log from '../util/log'
26
+ import {getPackage} from '../npm/package'
27
+
28
+ interface YalcWatchConfig {
29
+ folder?: string
30
+ command?: string
31
+ extensions?: string
32
+ }
33
+
34
+ interface PackageJson {
35
+ sanityPlugin?: {linkWatch?: YalcWatchConfig}
36
+ }
37
+
38
+ export async function linkWatch({basePath}: {basePath: string}) {
39
+ const packageJson: PackageJson = JSON.parse(
40
+ fs.readFileSync(path.join(process.cwd(), 'package.json'), 'utf8')
41
+ )
42
+
43
+ const watch: Required<YalcWatchConfig> = {
44
+ folder: 'lib',
45
+ command: 'npm run watch',
46
+ extensions: 'js,png,svg,gif,jpeg,css',
47
+ ...packageJson.sanityPlugin?.linkWatch,
48
+ }
49
+
50
+ nodemon({
51
+ watch: [watch.folder],
52
+ ext: watch.extensions,
53
+ exec: 'yalc push --changed',
54
+ //delay: 1000
55
+ })
56
+
57
+ const pkg = await getPackage({basePath, validate: false})
58
+
59
+ nodemon
60
+ .on('start', function () {
61
+ log.info(`Watching ${watch.folder} for changes to files with extensions: ${watch.extensions}`)
62
+ log.info(`\nTo test this package in another repository directory run:`)
63
+ log.success(`yalc add --link \n${pkg.name} && yarn install`)
64
+ })
65
+ .on('quit', function () {
66
+ process.exit()
67
+ })
68
+ .on('restart', function (files: any) {
69
+ log.info('Found changes in files:', chalk.magentaBright(files))
70
+ log.info('Pushing new yalc package...')
71
+ })
72
+
73
+ concurrently([watch.command])
74
+ }
@@ -0,0 +1,366 @@
1
+ import path from 'path'
2
+ // @ts-expect-error missing types
3
+ import licenses from '@rexxars/choosealicense-list'
4
+ import gitRemoteOriginUrl from 'git-remote-origin-url'
5
+ import log from '../util/log'
6
+ import {getUserInfo} from '../util/user'
7
+ import {errorToUndefined} from '../util/errorToUndefined'
8
+ import {addBuildScripts, getPackage, writePackageJson} from '../npm/package'
9
+ import {prompt, promptForPackageName, promptForRepoOrigin} from '../util/prompt'
10
+ import {generateReadme, isDefaultGitHubReadme} from '../util/readme'
11
+
12
+ import {
13
+ copyFileWithOverwritePrompt,
14
+ fileExists,
15
+ readFile,
16
+ writeFile,
17
+ writeFileWithOverwritePrompt,
18
+ } from '../util/files'
19
+ import {InitFlags} from './init'
20
+ import {PackageJson} from './verify/types'
21
+
22
+ const bannedFields = ['login', 'description', 'projecturl', 'email']
23
+ const preferredLicenses = ['MIT', 'ISC', 'BSD-3-Clause']
24
+ const otherLicenses = Object.keys(licenses.list).filter((id) => {
25
+ const license = licenses.list[id]
26
+ return (
27
+ !preferredLicenses.includes(id) &&
28
+ !bannedFields.some((field) => license.body.includes(`[${field}]`))
29
+ )
30
+ })
31
+
32
+ export interface SplatOptions {
33
+ basePath: string
34
+ requireUserConfirmation?: boolean
35
+ flags: InitFlags
36
+ dependencies?: Record<string, string>
37
+ devDependencies?: Record<string, string>
38
+ peerDependencies?: Record<string, string>
39
+ validate?: boolean
40
+ }
41
+
42
+ export interface PackageData {
43
+ user?: {name?: string; email?: string}
44
+ pluginName?: string
45
+ license?: {id: string; text: string}
46
+ description?: string
47
+ pkg?: PackageJson
48
+ gitOrigin?: string
49
+ }
50
+
51
+ export async function splat(options: SplatOptions) {
52
+ const {basePath, flags, requireUserConfirmation} = options
53
+ const info = (write: boolean, msg: string, ...args: string[]) => write && log.info(msg, ...args)
54
+ // Gather data
55
+ const pkg = await getPackage(options).catch(errorToUndefined)
56
+ log.debug('Plugin has package.json: %s', pkg ? 'yes' : 'no')
57
+
58
+ const user = await getUserInfo(options, pkg)
59
+ log.debug('User information: %o', user)
60
+
61
+ const pkgName = flags.name ?? pkg?.name
62
+ const pluginName =
63
+ requireUserConfirmation || !pkgName ? await promptForPackageName(options, pkgName) : pkgName
64
+
65
+ log.debug('Plugin name: %s', pluginName)
66
+
67
+ const license = await getLicense(flags, {user, pluginName, pkg, requireUserConfirmation})
68
+ const licenseChanged = (pkg && pkg.license) !== (license && license.id)
69
+ log.debug('License: %s', license ? license.id : '<none>')
70
+
71
+ const description = await getProjectDescription(basePath, pkg, requireUserConfirmation)
72
+ log.debug('Description: %s', description || '<none>')
73
+
74
+ const repoUrl =
75
+ flags.repo ??
76
+ ((await gitRemoteOriginUrl(basePath).catch(errorToUndefined)) || pkg?.repository?.url)
77
+
78
+ const gitOrigin = requireUserConfirmation ? await promptForRepoOrigin(options, repoUrl) : repoUrl
79
+
80
+ log.debug('Remote origin: %s', gitOrigin || '<none>')
81
+
82
+ // Output
83
+ const data: PackageData = {user, pluginName, license, description, pkg, gitOrigin}
84
+ let didWrite
85
+
86
+ // Write package.json, if returns the original (data.pkg) if it was unchanged,
87
+ // otherwise it returns the new object
88
+ const newPkg = await writePackageJson(data, options)
89
+ info(newPkg !== pkg, 'Wrote package.json')
90
+ data.pkg = newPkg
91
+
92
+ didWrite = await writeLicense(data, options, licenseChanged)
93
+ info(didWrite, 'Wrote license file (LICENSE)')
94
+
95
+ didWrite = await writeReadme(data, options)
96
+ info(didWrite, 'Wrote readme file (README.md)')
97
+
98
+ didWrite = await writeStaticAssets(options)
99
+ info(didWrite.length > 0, 'Wrote static asset files: %s', didWrite.join(', '))
100
+
101
+ didWrite = await writeEslintrc(options)
102
+ info(didWrite, 'Wrote .eslintrc.js')
103
+
104
+ didWrite = await addBuildScripts(newPkg, options)
105
+ info(didWrite, 'Added build scripts to package.json')
106
+
107
+ didWrite = await addCompileDirToGitIgnore(data, options)
108
+ info(didWrite, 'Added compilation output directory to .gitignore')
109
+ }
110
+
111
+ async function writeReadme(data: PackageData, options: SplatOptions) {
112
+ const {basePath} = options
113
+
114
+ const readmePath = path.join(basePath, 'README.md')
115
+ const readme = await readFile(readmePath, 'utf8').catch(errorToUndefined)
116
+
117
+ if (readme && !isDefaultGitHubReadme(readme)) {
118
+ return false
119
+ }
120
+
121
+ await writeFileWithOverwritePrompt(readmePath, generateReadme(data), {
122
+ encoding: 'utf8',
123
+ force: options.flags.force,
124
+ })
125
+ return true
126
+ }
127
+
128
+ async function writeEslintrc(options: SplatOptions) {
129
+ if (!options.flags.eslint) {
130
+ return false
131
+ }
132
+ const {basePath} = options
133
+
134
+ const eslintrc = path.join(basePath, '.eslintrc')
135
+
136
+ const config = {
137
+ root: true,
138
+ env: {
139
+ node: true,
140
+ browser: true,
141
+ },
142
+ extends: [
143
+ 'sanity',
144
+ options.flags.typescript && 'sanity/typescript',
145
+ 'sanity/react',
146
+ 'plugin:react-hooks/recommended',
147
+ options.flags.prettier && 'plugin:prettier/recommended',
148
+ ].filter(Boolean),
149
+ }
150
+
151
+ const content = JSON.stringify(config, null, 2)
152
+ await writeFileWithOverwritePrompt(eslintrc, content, {
153
+ encoding: 'utf8',
154
+ force: options.flags.force,
155
+ })
156
+ return true
157
+ }
158
+
159
+ async function writeLicense(
160
+ {license}: PackageData,
161
+ options: SplatOptions,
162
+ licenseChanged: boolean
163
+ ) {
164
+ const {basePath, flags} = options
165
+
166
+ if ((flags.license as unknown as boolean) === false || !license) {
167
+ return false
168
+ }
169
+
170
+ // Prefer whatever path the user is currenly using (LICENSE.md or LICENSE)
171
+ const hasLicenseMdFile = await fileExists(path.join(basePath, 'LICENSE.md'))
172
+ const licensePath = path.join(basePath, hasLicenseMdFile ? 'LICENSE.md' : 'LICENSE')
173
+
174
+ await writeFileWithOverwritePrompt(licensePath, license.text, {
175
+ encoding: 'utf8',
176
+ default: licenseChanged,
177
+ force: flags.force,
178
+ })
179
+
180
+ return true
181
+ }
182
+
183
+ async function getLicense(
184
+ flags: InitFlags,
185
+ {
186
+ user,
187
+ pluginName,
188
+ pkg,
189
+ requireUserConfirmation,
190
+ }: PackageData & {requireUserConfirmation?: boolean}
191
+ ) {
192
+ const license = await getLicenseIdentifier(flags, pkg, requireUserConfirmation)
193
+ if (!license) {
194
+ return undefined
195
+ }
196
+
197
+ const text = license.body
198
+ .replace(/\[fullname\]/g, user?.name)
199
+ .replace(/\[project\]/g, pluginName)
200
+ .replace(/\[year\]/g, new Date().getFullYear())
201
+
202
+ return {id: license.id, text}
203
+ }
204
+
205
+ async function getLicenseIdentifier(
206
+ flags: InitFlags,
207
+ pkg: PackageJson | undefined,
208
+ requireUserConfirmation = false
209
+ ) {
210
+ // --no-license
211
+ if ((flags.license as unknown) === false) {
212
+ return null
213
+ }
214
+
215
+ // --license becomes "", --license mit beocomes "mit"
216
+ if (typeof flags.license === 'string') {
217
+ const license = licenses.find(`${flags.license}`)
218
+ if (!license) {
219
+ throw new Error(`License "${flags.license}" not found`)
220
+ }
221
+ return license
222
+ }
223
+
224
+ // no --license flag provided, do we have one in package already?
225
+ if (pkg && pkg.license && !requireUserConfirmation) {
226
+ const license = licenses.find(`${pkg.license}`)
227
+ if (license) {
228
+ return license
229
+ }
230
+
231
+ // Warn, then prompt the user
232
+ log.warn(`package.json contains license "${pkg.license}", which is not recognized`)
233
+ }
234
+
235
+ const licenseId = await prompt('Which license do you want to use?', {
236
+ default: pkg && pkg.license && licenses.find(pkg.license) ? pkg.license : preferredLicenses[0],
237
+ choices: [
238
+ prompt.separator(),
239
+ ...preferredLicenses.map((value) => ({value, name: licenses.list[value].title})),
240
+ prompt.separator(),
241
+ ...otherLicenses.map((value) => ({value, name: licenses.list[value].title})),
242
+ ],
243
+ })
244
+
245
+ return licenses.find(licenseId)
246
+ }
247
+
248
+ async function getProjectDescription(
249
+ basePath: string,
250
+ pkg: PackageJson | undefined,
251
+ requireUserConfirmation = false
252
+ ) {
253
+ let description = await resolveProjectDescription(basePath, pkg)
254
+ if (requireUserConfirmation) {
255
+ description = await prompt('Plugin description', {default: description || ''})
256
+ }
257
+ return description ?? ''
258
+ }
259
+
260
+ async function resolveProjectDescription(basePath: string, pkg: PackageJson | undefined) {
261
+ // Try to grab from package.json
262
+ if (pkg && typeof pkg.description === 'string' && pkg.description.length > 5) {
263
+ return pkg.description
264
+ }
265
+
266
+ // Try to grab a project description from a standard GitHub-generated readme
267
+ try {
268
+ const readmePath = path.join(basePath, 'README.md')
269
+ const readme = await readFile(readmePath, 'utf8')
270
+ const [title, description] = readme.split('\n').filter(Boolean)
271
+ if (!title || !description || !title.match(/^#\s+\w+/)) {
272
+ return null
273
+ }
274
+
275
+ // Naive, but this isn't too important
276
+ const unlinked = description.replace(/\[(.*?)\]\(.*?\)/g, '$1')
277
+ if (/^[^#]/.test(unlinked)) {
278
+ return unlinked
279
+ }
280
+
281
+ return null
282
+ } catch (err) {
283
+ return errorToUndefined(err)
284
+ }
285
+ }
286
+
287
+ type FromTo = {from: string; to: string}
288
+
289
+ async function writeStaticAssets({basePath, flags}: SplatOptions) {
290
+ const assetsDir = await findAssetsDir()
291
+
292
+ const from = (...segments: string[]) => path.join(assetsDir, 'splat', ...segments)
293
+ const to = (...segments: string[]) => path.join(basePath, ...segments)
294
+
295
+ const files: FromTo[] = [
296
+ {from: 'editorconfig', to: '.editorconfig'},
297
+ {from: 'npmignore', to: '.npmignore'},
298
+ {from: 'sanity.json', to: 'sanity.json'},
299
+ {from: 'v2-incompatible.js.template', to: 'v2-incompatible.js'},
300
+ flags.gitignore && {from: 'gitignore', to: '.gitignore'},
301
+ flags.typescript && {from: 'template-tsconfig.json', to: 'tsconfig.json'},
302
+ flags.prettier && {from: 'prettierrc.js', to: '.prettierrc.js'},
303
+ ].filter((f): f is FromTo => !!f)
304
+
305
+ const writes: string[] = []
306
+ for (const file of files) {
307
+ if (await copyFileWithOverwritePrompt(from(file.from), to(file.to), flags)) {
308
+ writes.push(file.to)
309
+ }
310
+ }
311
+
312
+ return writes
313
+ }
314
+
315
+ /**
316
+ * assets dir might be in higher or lower in the dir hierarchy depending on
317
+ * if we run from lib or src
318
+ */
319
+ async function findAssetsDir(): Promise<string> {
320
+ let maxBackpaddle = 3
321
+ let currDir = __dirname
322
+ let assetsDir: string = ''
323
+ while (!assetsDir && maxBackpaddle) {
324
+ currDir = path.join(currDir, '..')
325
+ const assets = path.join(currDir, 'assets')
326
+ if (await fileExists(assets)) {
327
+ assetsDir = assets
328
+ } else {
329
+ maxBackpaddle--
330
+ }
331
+ }
332
+
333
+ if (!assetsDir) {
334
+ throw new Error('Could not find assets directory!')
335
+ }
336
+ return assetsDir
337
+ }
338
+
339
+ async function addCompileDirToGitIgnore(data: PackageData, options: SplatOptions) {
340
+ const gitIgnorePath = path.join(options.basePath, '.gitignore')
341
+ const gitignore = await readFile(gitIgnorePath, 'utf8').catch(errorToUndefined)
342
+ if (!gitignore) {
343
+ return false
344
+ }
345
+
346
+ const output = data.pkg?.main ?? data.pkg?.module
347
+ if (!output) {
348
+ return false
349
+ }
350
+
351
+ const ignorePath = output.replace(/^[./]+/, '')
352
+ const ignore = ignorePath.split('/')[0]
353
+ if (!ignore) {
354
+ return false
355
+ }
356
+
357
+ const lines = gitignore.split('\n')
358
+ if (lines.includes(ignore)) {
359
+ return false
360
+ }
361
+
362
+ lines.push('# Compiled plugin', ignore, '\n')
363
+
364
+ await writeFile(gitIgnorePath, lines.join('\n'), {encoding: 'utf8'})
365
+ return true
366
+ }