@sanity/plugin-kit 1.1.0-ecosystem-preset.5 → 1.1.0-ecosystem-preset.7

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 (44) hide show
  1. package/lib/package.json +5 -5
  2. package/lib/src/actions/init.js +0 -8
  3. package/lib/src/actions/init.js.map +1 -1
  4. package/lib/src/actions/verify/validations.d.ts +1 -0
  5. package/lib/src/actions/verify/validations.js +38 -15
  6. package/lib/src/actions/verify/validations.js.map +1 -1
  7. package/lib/src/actions/verify-package.js +1 -0
  8. package/lib/src/actions/verify-package.js.map +1 -1
  9. package/lib/src/actions/verify-studio.js.map +1 -1
  10. package/lib/src/configs/banned-packages.d.ts +2 -0
  11. package/lib/src/configs/{merged-packages.js → banned-packages.js} +3 -2
  12. package/lib/src/configs/{merged-packages.js.map → banned-packages.js.map} +1 -1
  13. package/lib/src/configs/forced-package-versions.d.ts +10 -0
  14. package/lib/src/configs/forced-package-versions.js +15 -0
  15. package/lib/src/configs/forced-package-versions.js.map +1 -0
  16. package/lib/src/dependencies/import-linter.js +2 -2
  17. package/lib/src/npm/package.js +26 -7
  18. package/lib/src/npm/package.js.map +1 -1
  19. package/lib/src/presets/presets.js +1 -1
  20. package/lib/src/presets/presets.js.map +1 -1
  21. package/lib/src/presets/semver-workflow.d.ts +2 -0
  22. package/lib/src/presets/semver-workflow.js +86 -6
  23. package/lib/src/presets/semver-workflow.js.map +1 -1
  24. package/lib/test/semver-workflow.test.d.ts +1 -0
  25. package/lib/test/semver-workflow.test.js +42 -0
  26. package/lib/test/semver-workflow.test.js.map +1 -0
  27. package/lib/test/verify-package.test.js +1 -1
  28. package/lib/test/verify-package.test.js.map +1 -1
  29. package/package.json +5 -5
  30. package/src/actions/init.ts +1 -16
  31. package/src/actions/verify/validations.ts +48 -13
  32. package/src/actions/verify-package.ts +3 -1
  33. package/src/actions/verify-studio.ts +5 -1
  34. package/src/configs/{merged-packages.ts → banned-packages.ts} +2 -0
  35. package/src/configs/forced-package-versions.ts +12 -0
  36. package/src/dependencies/import-linter.ts +1 -1
  37. package/src/npm/package.ts +41 -12
  38. package/src/presets/presets.ts +1 -1
  39. package/src/presets/semver-workflow.ts +89 -6
  40. package/lib/src/actions/verify.d.ts +0 -0
  41. package/lib/src/actions/verify.js +0 -330
  42. package/lib/src/actions/verify.js.map +0 -1
  43. package/lib/src/configs/merged-packages.d.ts +0 -1
  44. package/src/actions/verify.ts +0 -328
@@ -2,7 +2,6 @@ import {FromTo, InjectOptions, writeAssets} from '../actions/inject'
2
2
  import {resolveLatestVersions} from '../npm/resolveLatestVersions'
3
3
  import {Preset} from './presets'
4
4
  import {
5
- addBuildScripts,
6
5
  addPackageJsonScripts,
7
6
  addScript,
8
7
  getPackage,
@@ -12,6 +11,10 @@ import {
12
11
  import log from '../util/log'
13
12
  import outdent from 'outdent'
14
13
  import chalk from 'chalk'
14
+ import path from 'path'
15
+ import {readFile, writeFile} from '../util/files'
16
+ import {errorToUndefined} from '../util/errorToUndefined'
17
+ import {PackageJson} from '../actions/verify/types'
15
18
 
16
19
  export const semverWorkflowPreset: Preset = {
17
20
  name: 'semver-workflow',
@@ -26,6 +29,7 @@ async function applyPreset(options: InjectOptions) {
26
29
  await writeAssets(semverWorkflowFiles(), options)
27
30
  await addPrepareScript(options)
28
31
  await addDevDependencies(options)
32
+ await updateReadme(options)
29
33
  }
30
34
 
31
35
  async function addPrepareScript(options: InjectOptions) {
@@ -51,17 +55,87 @@ async function addDevDependencies(options: InjectOptions) {
51
55
  log.info(
52
56
  chalk.green(
53
57
  outdent`
54
- You must configure branch-config in the following files manually:
55
- - .release.json
56
- - .github/workflows/main.yml
58
+ semantic-release preset injected.
57
59
 
58
- You should remove the --dry-run from main.yml only after you have tested the workflow,
59
- and the semantic-release job reports the version number you expect.
60
+ Please confer
61
+ https://github.com/sanity-io/plugin-kit/blob/main/docs/presets.md#semver-workflow
62
+ to finalize configuration for this preset.
60
63
  `.trim()
61
64
  )
62
65
  )
63
66
  }
64
67
 
68
+ async function updateReadme(options: InjectOptions) {
69
+ const {basePath} = options
70
+
71
+ const readmePath = path.join(basePath, 'README.md')
72
+ const readme = (await readFile(readmePath, 'utf8').catch(errorToUndefined)) ?? ''
73
+
74
+ const {v3Banner, installUsage, developFooter} = await readmeSnippets(options)
75
+ const updatedReadme = [v3Banner, installUsage, readme, developFooter].filter(Boolean).join('\n\n')
76
+ await writeFile(readmePath, updatedReadme, {encoding: 'utf8'})
77
+ log.info('Updated README. Please review the changes.')
78
+ }
79
+
80
+ async function readmeSnippets(options: InjectOptions) {
81
+ const pkg = await getPackage(options)
82
+
83
+ const bestEffortUrl = readmeBaseurl(pkg)
84
+
85
+ const v3Banner = outdent`
86
+ > **NOTE**
87
+ >
88
+ > This is the **Sanity Studio v3 version** of ${pkg.name}.
89
+ >
90
+ > For the v2 version, please refer to the [v2-branch](${bestEffortUrl}).
91
+ `
92
+
93
+ const installUsage = outdent`
94
+ ## Installation
95
+
96
+ \`\`\`
97
+ npm install --save ${pkg.name}@studio-v3
98
+ \`\`\`
99
+
100
+ or
101
+
102
+ \`\`\`
103
+ yarn add ${pkg.name}@studio-v3
104
+ \`\`\`
105
+
106
+ ## Usage
107
+
108
+ <TODO: Show usage here>
109
+ `
110
+
111
+ const developFooter = outdent`
112
+ ## License
113
+
114
+ MIT-licensed. See LICENSE.
115
+
116
+ ## Develop & test
117
+
118
+ This plugin uses [@sanity/plugin-kit](https://github.com/sanity-io/plugin-kit)
119
+ with default configuration for build & watch scripts.
120
+
121
+ See [Testing a plugin in Sanity Studio](https://github.com/sanity-io/plugin-kit#testing-a-plugin-in-sanity-studio)
122
+ on how to run this plugin with hotreload in the studio.
123
+
124
+ ### Release new version
125
+
126
+ Run ["CI & Release" workflow](${bestEffortUrl}/actions/workflows/main.yml).
127
+ Make sure to select the main branch and check "Release new version".
128
+
129
+ Semantic release will only release on configured branches, so it is safe to run release on any branch.
130
+ `
131
+
132
+ return {
133
+ v3Banner,
134
+ installUsage,
135
+ developFooter,
136
+ }
137
+ }
138
+
65
139
  function semverWorkflowFiles(): FromTo[] {
66
140
  return [
67
141
  {from: ['.github', 'workflows', 'main.yml'], to: ['.github', 'workflows', 'main.yml']},
@@ -85,3 +159,12 @@ async function semverWorkflowDependencies(): Promise<Record<string, string>> {
85
159
  'lint-staged',
86
160
  ])
87
161
  }
162
+
163
+ export function readmeBaseurl(pkg: PackageJson) {
164
+ return ((pkg.repository?.url ?? pkg.homepage ?? 'TODO') as string)
165
+ .replaceAll(/.+:\/\//g, 'https://')
166
+ .replaceAll(/\.git/g, '')
167
+ .replaceAll(/git@github.com\//g, 'github.com/')
168
+ .replaceAll(/git@github.com:/g, 'https://github.com/')
169
+ .replaceAll(/#.+/g, '')
170
+ }
File without changes
@@ -1,330 +0,0 @@
1
- "use strict";
2
- /*
3
- import path from 'path'
4
- //@ts-expect-error missing types
5
- import spdxLicenseIds from 'spdx-license-ids'
6
- import semver from 'semver'
7
-
8
- import log from '../util/log'
9
- import {fileExists, readJsonFile} from '../util/files'
10
- import {readManifest, getReferencesPartPaths} from '../sanity/manifest'
11
- import {getPackage, getReferencedPaths} from '../npm/package'
12
- import {getPublishableFiles} from '../npm/publish'
13
- import {findDependencies} from '../dependencies/find'
14
- import {uselessFiles} from '../configs/uselessFiles'
15
-
16
- export async function verify({basePath, flags}) {
17
- const pkg = await getPackage({basePath, flags})
18
- const manifest = await readManifest({
19
- basePath,
20
- pluginName: pkg.name,
21
- flags,
22
- verifyCompiledParts: true,
23
- verifySourceParts: true,
24
- })
25
-
26
- // Get all files intended to be published from npm
27
- const publishableFiles = await getPublishableFiles(basePath)
28
-
29
- // Errors
30
- await verifyPublishableFiles({basePath, pkg, manifest, publishableFiles})
31
- await verifyLicenseKey(pkg)
32
- await verifyPluginConfig(basePath)
33
- await verifyImports({pkg, manifest, basePath})
34
-
35
- // Warnings
36
- const hasWarnings = await warnOnUselessFiles(publishableFiles)
37
-
38
- // Huzzah
39
- log.success(
40
- hasWarnings
41
- ? 'Plugin has warnings, but looks good to publish!'
42
- : 'Plugin looks good to publish!'
43
- )
44
- }
45
-
46
- async function verifyPublishableFiles({pkg, manifest, basePath, publishableFiles}) {
47
- // Validate that these files exists, not just that they are publishable
48
- if (!(await fileExists(path.resolve(basePath, 'README.md')))) {
49
- throw new Error(
50
- `This plugin does not contain a README.md, which is required for Sanity plugins.`
51
- )
52
- }
53
-
54
- const [hasLicense, hasLicenseMd] = await Promise.all([
55
- fileExists(path.resolve(basePath, 'LICENSE')),
56
- fileExists(path.resolve(basePath, 'LICENSE.md')),
57
- ])
58
-
59
- if (!hasLicense && !hasLicenseMd) {
60
- throw new Error(
61
- `This plugin does not contain a LICENSE-file, which is required for Sanity plugins.`
62
- )
63
- }
64
-
65
- // Always, uhm... "kindly suggest", to include these files
66
- const files = ['README.md']
67
- // Get files from parts as well as the ones references in package.json
68
- .concat(getReferencesPartPaths(manifest, basePath), getReferencedPaths(pkg, basePath))
69
- // Make all paths relative to base path
70
- .map((file) =>
71
- path.relative(basePath, path.isAbsolute(file) ? file : path.resolve(basePath, file))
72
- )
73
- // Remove duplicates
74
- .filter((file, index, arr) => arr.indexOf(file, index + 1) === -1)
75
-
76
- // Verify that all explicitly referenced files are publishable
77
- const unpublishable = files.filter((file) => !publishableFiles.includes(file))
78
-
79
- // Warn with "default error" for unknowns
80
- const unknowns = unpublishable.filter((item) => item !== 'README.md').map((file) => `"${file}"`)
81
-
82
- if (unknowns.length > 0) {
83
- const paths = unknowns.join(', ')
84
- throw new Error(
85
- `This plugin references files that are ignored from being published: ${paths}. Check .gitignore, .npmignore and/or the "files" property of package.json. See https://docs.npmjs.com/using-npm/developers.html#keeping-files-out-of-your-package for more information.`
86
- )
87
- }
88
- }
89
-
90
- function verifyLicenseKey(pkg) {
91
- if (!pkg.license) {
92
- throw new Error(
93
- `package.json is missing "license" key: see https://docs.npmjs.com/files/package.json#license and make sure it matches your "LICENSE" file. See https://choosealicense.com/ for help on choosing a license.`
94
- )
95
- }
96
-
97
- if (pkg.license !== 'UNLICENSED' && !spdxLicenseIds.includes(pkg.license)) {
98
- throw new Error(
99
- `package.json has an invalid "license" key: it should be either an SPDX license ID (https://spdx.org/licenses/) or "UNLICENSE". See https://docs.npmjs.com/files/package.json#license and refer to https://choosealicense.com/ for help on choosing a license.`
100
- )
101
- }
102
- }
103
-
104
- async function verifyPluginConfig(basePath) {
105
- const configPath = path.join(basePath, 'config.dist.json')
106
- if (!(await fileExists(configPath))) {
107
- return
108
- }
109
-
110
- let config
111
- try {
112
- config = await readJsonFile(configPath)
113
- } catch (err: any) {
114
- throw new Error(`Error reading plugin config (${configPath}): ${err.message}`)
115
- }
116
-
117
- if (typeof config !== 'object' || Array.isArray(config) || !config) {
118
- throw new Error(
119
- `Error reading plugin config (${configPath}): must be an object, got:\n${JSON.stringify(
120
- config,
121
- null,
122
- 2
123
- )}`
124
- )
125
- }
126
- }
127
-
128
- function warnOnUselessFiles(files) {
129
- const warnFor = files
130
- .filter(
131
- (file) =>
132
- uselessFiles.includes(file) ||
133
- uselessFiles.some((useless) => file.startsWith(`${useless}/`))
134
- )
135
- .map((file) => `"${file}"`)
136
- .join(', ')
137
-
138
- if (warnFor.length === 0) {
139
- return false
140
- }
141
-
142
- log.warn(
143
- `This plugin is set to publish the following files, which are generally not needed in a published npm module: ${warnFor}.`
144
- )
145
- log.warn(
146
- `Consider adding these files to an .npmignore or the package.json "files" property. See https://docs.npmjs.com/using-npm/developers.html#keeping-files-out-of-your-package for more information.`
147
- )
148
-
149
- return true
150
- }
151
-
152
- async function verifyImports({pkg, manifest, basePath}) {
153
- // "Entry" as in... code may start here.
154
- const entries = ([] as string[])
155
- .concat(
156
- getReferencedPaths(pkg, basePath), // From npm
157
- getReferencesPartPaths(manifest, basePath) // From parts
158
- )
159
- // Remove duplicates
160
- .filter((file, index, arr) => arr.indexOf(file, index + 1) === -1)
161
- // Remove non-javascript/non-css entries
162
- .filter((file) => ['.js', '.css'].includes(path.extname(file)))
163
-
164
- const dependencies = findDependencies(entries)
165
- const modules = dependencies.filter((dep) => !/^(all|part|config|sanity):/.test(dep))
166
-
167
- await verifyNoUndeclaredDependencies(modules, pkg)
168
- await verifyReactDependencies(modules, pkg)
169
- await verifyUiDependencies(modules, pkg)
170
- await verifyConfigParts(dependencies, pkg, basePath)
171
- await verifyNoUnusedDependencies(modules, pkg)
172
- await verifyNoUndeclaredParts(dependencies, pkg, manifest)
173
- }
174
-
175
- function verifyNoUndeclaredParts(dependencies, pkg, manifest) {
176
- const parts = dependencies
177
- .filter((dep) => dep.startsWith('all:') || dep.startsWith('part:'))
178
- .map((part) => part.replace(/^all:/, ''))
179
- .map((part) => part.replace(/\?$/, ''))
180
- .filter((part) => part.startsWith(`part:${pkg.name.replace(/^sanity-plugin-/, '')}`))
181
-
182
- const declaredParts = (manifest.parts || []).reduce(
183
- (partNames, part) => [...partNames, ...[part.name, part.implements].filter(Boolean)],
184
- []
185
- )
186
- const undeclaredParts = parts.filter((partName) => !declaredParts.includes(partName))
187
-
188
- if (undeclaredParts.length > 0) {
189
- const aPart = undeclaredParts.length > 1 ? 'parts' : 'a part'
190
- const is = undeclaredParts.length > 1 ? 'are' : 'is'
191
- const partList = undeclaredParts.map((part) => `"${part}"`).join(', ')
192
- throw new Error(
193
- `Invalid plugin: Source is using ${aPart} that ${is} not declared in "sanity.json":\n\n${partList}.`
194
- )
195
- }
196
- }
197
-
198
- function verifyNoUnusedDependencies(modules, pkg) {
199
- const potentiallyUnused = Object.keys(pkg.dependencies || {}).filter(
200
- (dep) => !modules.includes(dep)
201
- )
202
-
203
- if (potentiallyUnused.length > 0) {
204
- const unusedNames = potentiallyUnused.map((dep) => `"${dep}"`).join(', ')
205
- log.warn(`Found modules listed as dependencies which seem to be unused by code: ${unusedNames}`)
206
- }
207
- }
208
-
209
- function verifyReactDependencies(modules, pkg) {
210
- if (modules.includes('react') && 'react' in (pkg.dependencies || {})) {
211
- throw new Error(
212
- `Invalid plugin: "react" declared as a dependency - it should be declared as a peerDependency (package.json)`
213
- )
214
- }
215
-
216
- if (modules.includes('react-dom') && 'react-dom' in (pkg.dependencies || {})) {
217
- throw new Error(
218
- `Invalid plugin: "react-dom" declared as a dependency - it should be declared as a peerDependency (package.json)`
219
- )
220
- }
221
-
222
- if (modules.includes('prop-types') && 'prop-types' in (pkg.peerDependencies || {})) {
223
- throw new Error(
224
- `Invalid plugin: "prop-types" declares as peerDependency - it should be declared as a dependency (package.json)`
225
- )
226
- }
227
- }
228
-
229
- function verifyUiDependencies(modules, pkg) {
230
- const peerDependencies = pkg.peerDependencies || {}
231
- const dependencies = pkg.dependencies || {}
232
-
233
- if (modules.includes('@sanity/ui') && '@sanity/ui' in peerDependencies) {
234
- throw new Error(
235
- `Invalid plugin: "@sanity/ui" declared as a peer dependency - it should be declared as a dependency (package.json)`
236
- )
237
- }
238
-
239
- if (
240
- modules.includes('@sanity/ui') &&
241
- dependencies['@sanity/ui'] &&
242
- semver.lt(semver.minVersion(dependencies['@sanity/ui']), '0.33.1')
243
- ) {
244
- throw new Error(
245
- `Invalid plugin: "@sanity/ui" dependency must use version higher than or equal to 0.33.1 (package.json)`
246
- )
247
- }
248
-
249
- if (modules.includes('@sanity/icons') && '@sanity/icons' in peerDependencies) {
250
- throw new Error(
251
- `Invalid plugin: "@sanity/icons" declared as a peer dependency - it should be declared as a dependency (package.json)`
252
- )
253
- }
254
- }
255
-
256
- async function verifyConfigParts(dependencies, pkg, basePath) {
257
- const configName = `config:${pkg.name.replace(/^sanity-plugin-/, '')}`
258
- if (
259
- dependencies.includes(configName) &&
260
- !(await fileExists(path.join(basePath, 'config.dist.json')))
261
- ) {
262
- throw new Error(
263
- `Plugin imports plugin config (${configName}) but does not contain a "config.dist.json" file.`
264
- )
265
- }
266
-
267
- const nonPluginConfigs = dependencies.filter(
268
- (dep) => dep.startsWith('config:') && dep !== configName
269
- )
270
-
271
- if (nonPluginConfigs.length > 0) {
272
- const configs = nonPluginConfigs.length > 1 ? 'configs' : 'config'
273
- const nonPluginConfigNames = nonPluginConfigs.join(', ')
274
- log.warn(
275
- `Found references to external ${configs}: ${nonPluginConfigNames} - this is generally considered unsafe`
276
- )
277
- }
278
- }
279
-
280
- function verifyNoUndeclaredDependencies(modules, pkg) {
281
- const undeclared = getUndeclaredDependencies(modules, pkg)
282
- if (undeclared.length > 0) {
283
- throw new Error(getUndeclaredDependenciesError(undeclared))
284
- }
285
- }
286
-
287
- function getUndeclaredDependencies(modules, pkg) {
288
- const deps = ([] as string[]).concat(
289
- Object.keys(pkg.dependencies || {}),
290
- Object.keys(pkg.peerDependencies || {})
291
- )
292
-
293
- const devDeps = Object.keys(pkg.devDependencies || {})
294
-
295
- return modules
296
- .filter((modDep) => !deps.includes(modDep))
297
- .map((modDep) => ({
298
- dependency: modDep,
299
- isDevDep: devDeps.includes(modDep),
300
- }))
301
- }
302
-
303
- function getUndeclaredDependenciesError(undeclared) {
304
- const baseError = `Invalid plugin`
305
- const moduleNames = undeclared.map((mod) => mod.dependency)
306
-
307
- let declaredWhere = `should be declared in package.json under "dependencies" or "peerDependencies".`
308
- if (moduleNames.length === 1 && moduleNames[0] === 'react') {
309
- declaredWhere = `should be declared in package.json under "peerDependencies"`
310
- } else if (moduleNames.includes('react')) {
311
- declaredWhere = `should either be declared in package.json under "dependencies" or "peerDependencies" (react should be in "peerDependencies").`
312
- }
313
-
314
- const devDeps = undeclared.filter((dep) => dep.isDevDep).map((dep) => ` - ${dep.dependency}\n`)
315
- if (devDeps.length > 0) {
316
- const modules = devDeps.length > 1 ? 'modules' : 'module'
317
- const depList = devDeps.join('')
318
- const target = devDeps.length > 1 ? 'They' : 'It'
319
- const are = devDeps.length > 1 ? 'are' : 'is'
320
- return `${baseError}: Source uses ${modules} that ${are} declared in "devDependencies":\n\n${depList}\n${target} ${declaredWhere}`
321
- }
322
-
323
- const modules = undeclared.length > 1 ? 'modules' : 'module'
324
- const depList = undeclared.map((dep) => ` - ${dep.dependency}\n`).join('')
325
- const target = undeclared.length > 1 ? 'They' : 'It'
326
- const are = undeclared.length > 1 ? 'are' : 'is'
327
- return `${baseError}: Source uses ${modules} that ${are} not declared as dependencies:\n\n${depList}\n${target} ${declaredWhere}`
328
- }
329
- */
330
- //# sourceMappingURL=verify.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"verify.js","sourceRoot":"","sources":["../../../src/actions/verify.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuUE"}
@@ -1 +0,0 @@
1
- export declare const mergedPackages: string[];