@sanity/plugin-kit 4.0.20 → 5.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.
Files changed (104) hide show
  1. package/assets/inject/semver-workflow/.husky/commit-msg +0 -0
  2. package/assets/inject/semver-workflow/.husky/pre-commit +0 -0
  3. package/assets/inject/ui-workshop/src/__workshop__/props.tsx +2 -1
  4. package/bin/plugin-kit.js +3 -1
  5. package/dist/{_chunks-cjs/cli.js → _chunks-es/index.js} +53 -63
  6. package/dist/_chunks-es/index.js.map +1 -0
  7. package/dist/{_chunks-cjs/init2.js → _chunks-es/init.js} +25 -23
  8. package/dist/_chunks-es/init.js.map +1 -0
  9. package/dist/_chunks-es/init2.js +140 -0
  10. package/dist/_chunks-es/init2.js.map +1 -0
  11. package/{src/cmds/inject.ts → dist/_chunks-es/inject.js} +18 -32
  12. package/dist/{_chunks-cjs → _chunks-es}/inject.js.map +1 -1
  13. package/dist/_chunks-es/link-watch.js +91 -0
  14. package/dist/_chunks-es/link-watch.js.map +1 -0
  15. package/dist/_chunks-es/load-package-config.js +22 -0
  16. package/dist/_chunks-es/load-package-config.js.map +1 -0
  17. package/dist/_chunks-es/package.js +1759 -0
  18. package/dist/_chunks-es/package.js.map +1 -0
  19. package/dist/_chunks-es/package2.js +9 -0
  20. package/dist/{_chunks-cjs → _chunks-es}/package2.js.map +1 -1
  21. package/dist/_chunks-es/ts.js +171 -0
  22. package/dist/_chunks-es/ts.js.map +1 -0
  23. package/dist/_chunks-es/verify-package.js +92 -0
  24. package/dist/_chunks-es/verify-package.js.map +1 -0
  25. package/dist/_chunks-es/verify-studio.js +61 -0
  26. package/dist/_chunks-es/verify-studio.js.map +1 -0
  27. package/dist/_chunks-es/version.js +50 -0
  28. package/dist/_chunks-es/version.js.map +1 -0
  29. package/dist/index.d.ts +4 -1
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +4 -1
  32. package/dist/index.js.map +1 -1
  33. package/package.json +40 -98
  34. package/LICENSE +0 -21
  35. package/dist/_chunks-cjs/cli.js.map +0 -1
  36. package/dist/_chunks-cjs/init.js +0 -892
  37. package/dist/_chunks-cjs/init.js.map +0 -1
  38. package/dist/_chunks-cjs/init2.js.map +0 -1
  39. package/dist/_chunks-cjs/inject.js +0 -54
  40. package/dist/_chunks-cjs/link-watch.js +0 -84
  41. package/dist/_chunks-cjs/link-watch.js.map +0 -1
  42. package/dist/_chunks-cjs/package.js +0 -1809
  43. package/dist/_chunks-cjs/package.js.map +0 -1
  44. package/dist/_chunks-cjs/package2.js +0 -8
  45. package/dist/_chunks-cjs/ts.js +0 -160732
  46. package/dist/_chunks-cjs/ts.js.map +0 -1
  47. package/dist/_chunks-cjs/verify-package.js +0 -75
  48. package/dist/_chunks-cjs/verify-package.js.map +0 -1
  49. package/dist/_chunks-cjs/verify-studio.js +0 -57
  50. package/dist/_chunks-cjs/verify-studio.js.map +0 -1
  51. package/dist/_chunks-cjs/version.js +0 -51
  52. package/dist/_chunks-cjs/version.js.map +0 -1
  53. package/dist/cli.d.ts +0 -4
  54. package/dist/cli.js +0 -6
  55. package/dist/cli.js.map +0 -1
  56. package/src/actions/init.ts +0 -95
  57. package/src/actions/inject.ts +0 -399
  58. package/src/actions/link-watch.ts +0 -98
  59. package/src/actions/verify/types.ts +0 -56
  60. package/src/actions/verify/validations.ts +0 -505
  61. package/src/actions/verify/verify-common.ts +0 -93
  62. package/src/actions/verify-package.ts +0 -103
  63. package/src/actions/verify-studio.ts +0 -58
  64. package/src/cli.ts +0 -77
  65. package/src/cmds/index.ts +0 -20
  66. package/src/cmds/init.ts +0 -90
  67. package/src/cmds/link-watch.ts +0 -50
  68. package/src/cmds/verify-package.ts +0 -36
  69. package/src/cmds/verify-studio.ts +0 -36
  70. package/src/cmds/version.ts +0 -67
  71. package/src/configs/banned-packages.ts +0 -27
  72. package/src/configs/buildExtensions.ts +0 -1
  73. package/src/configs/default-source.ts +0 -64
  74. package/src/configs/eslint.ts +0 -51
  75. package/src/configs/forced-package-versions.ts +0 -12
  76. package/src/configs/git.ts +0 -68
  77. package/src/configs/pkg-config.ts +0 -30
  78. package/src/configs/prettier.ts +0 -11
  79. package/src/configs/tsconfig.ts +0 -78
  80. package/src/configs/uselessFiles.ts +0 -29
  81. package/src/constants.ts +0 -15
  82. package/src/dependencies/find.ts +0 -193
  83. package/src/dependencies/import-linter.ts +0 -95
  84. package/src/index.ts +0 -1
  85. package/src/npm/manager.ts +0 -44
  86. package/src/npm/package.ts +0 -427
  87. package/src/npm/publish.ts +0 -9
  88. package/src/npm/resolveLatestVersions.ts +0 -31
  89. package/src/presets/presets.ts +0 -54
  90. package/src/presets/renovatebot.ts +0 -21
  91. package/src/presets/semver-workflow.ts +0 -186
  92. package/src/presets/ui-workshop.ts +0 -97
  93. package/src/presets/ui.ts +0 -67
  94. package/src/sanity/manifest.ts +0 -340
  95. package/src/sharedFlags.ts +0 -14
  96. package/src/util/command-parser.ts +0 -36
  97. package/src/util/errorToUndefined.ts +0 -7
  98. package/src/util/files.ts +0 -260
  99. package/src/util/log.ts +0 -44
  100. package/src/util/prompt.ts +0 -70
  101. package/src/util/readme.ts +0 -88
  102. package/src/util/request.ts +0 -11
  103. package/src/util/ts.ts +0 -19
  104. package/src/util/user.ts +0 -129
@@ -1,186 +0,0 @@
1
- import {Injectable, InjectOptions, writeAssets} from '../actions/inject'
2
- import {resolveLatestVersions} from '../npm/resolveLatestVersions'
3
- import {Preset} from './presets'
4
- import {
5
- addPackageJsonScripts,
6
- addScript,
7
- getPackage,
8
- sortKeys,
9
- writePackageJsonDirect,
10
- } from '../npm/package'
11
- import log from '../util/log'
12
- import outdent from 'outdent'
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'
18
- import {developTestSnippet, getLicenseText, installationSnippet} from '../util/readme'
19
- import {getUserInfo} from '../util/user'
20
-
21
- export const semverWorkflowPreset: Preset = {
22
- name: 'semver-workflow',
23
- description:
24
- 'Files and dependencies for conventional-commits, github workflow and semantic-release.',
25
- apply: applyPreset,
26
- }
27
-
28
- const info = (write: boolean, msg: string, ...args: string[]) => write && log.info(msg, ...args)
29
-
30
- async function applyPreset(options: InjectOptions) {
31
- await writeAssets(semverWorkflowFiles(), options)
32
- await addPrepareScript(options)
33
- await addDevDependencies(options)
34
- await updateReadme(options)
35
- }
36
-
37
- async function addPrepareScript(options: InjectOptions) {
38
- const pkg = await getPackage(options)
39
- const didWrite = await addPackageJsonScripts(pkg, options, (scripts) => {
40
- scripts.prepare = addScript(`husky`, scripts.prepare)
41
- return scripts
42
- })
43
- info(didWrite, 'Added prepare script to package.json')
44
- }
45
-
46
- async function addDevDependencies(options: InjectOptions) {
47
- const pkg = await getPackage(options)
48
- const devDeps = sortKeys({
49
- ...pkg.devDependencies,
50
- ...(await semverWorkflowDependencies()),
51
- })
52
- const newPkg = {...pkg}
53
- newPkg.devDependencies = devDeps
54
- await writePackageJsonDirect(newPkg, options)
55
- log.info('Updated devDependencies.')
56
-
57
- log.info(
58
- chalk.green(
59
- outdent`
60
- semantic-release preset injected.
61
-
62
- Please confer
63
- https://github.com/sanity-io/plugin-kit/blob/main/docs/semver-workflow.md#manual-steps-after-inject
64
- to finalize configuration for this preset.
65
- `.trim(),
66
- ),
67
- )
68
- }
69
-
70
- async function updateReadme(options: InjectOptions) {
71
- const {basePath} = options
72
-
73
- const readmePath = path.join(basePath, 'README.md')
74
- const readme = (await readFile(readmePath, 'utf8').catch(errorToUndefined)) ?? ''
75
-
76
- const {install, usage, developTest, license, releaseSnippet} = await readmeSnippets(options)
77
-
78
- const prependSections = missingSections(readme, [install, usage])
79
- const appendSections = missingSections(readme, [license, developTest, releaseSnippet])
80
-
81
- if (prependSections.length || appendSections.length) {
82
- const updatedReadme = [...prependSections, readme, ...appendSections]
83
- .filter(Boolean)
84
- .join('\n\n')
85
- await writeFile(readmePath, updatedReadme, {encoding: 'utf8'})
86
- log.info('Updated README. Please review the changes.')
87
- }
88
- }
89
-
90
- async function readmeSnippets(options: InjectOptions) {
91
- const pkg = await getPackage(options)
92
- const user = await getUserInfo(options, pkg)
93
-
94
- const bestEffortUrl = readmeBaseurl(pkg)
95
-
96
- const install = installationSnippet(pkg.name ?? 'unknown')
97
-
98
- const usage = outdent`
99
- ## Usage
100
- `
101
-
102
- const license = getLicenseText(typeof pkg.license === 'string' ? pkg.license : undefined, user)
103
-
104
- const releaseSnippet = outdent`
105
- ### Release new version
106
-
107
- Run ["CI & Release" workflow](${bestEffortUrl}/actions/workflows/main.yml).
108
- Make sure to select the main branch and check "Release new version".
109
-
110
- Semantic release will only release on configured branches, so it is safe to run release on any branch.
111
- `
112
-
113
- return {
114
- install,
115
- usage,
116
- license,
117
- developTest: developTestSnippet(),
118
- releaseSnippet,
119
- }
120
- }
121
-
122
- /**
123
- * Returns sections that does not exists "close enough" in readme
124
- */
125
- export function missingSections(readme: string, sections: string[]) {
126
- return sections.filter((section) => !closeEnough(section, readme))
127
- }
128
-
129
- /**
130
- * a and b are considered "close enough" if > 50% of a lines exist in b lines
131
- * @param a
132
- * @param b
133
- */
134
- function closeEnough(a: string, b: string) {
135
- const aLines = a.split('\n')
136
- const bLines = b.split('\n')
137
-
138
- const matchingLines = aLines.filter((line) => bLines.find((bLine) => bLine === line)).length
139
- const isCloseEnough = matchingLines >= aLines.length * 0.5
140
- return isCloseEnough
141
- }
142
-
143
- function semverWorkflowFiles(): Injectable[] {
144
- const base: Injectable[] = [
145
- {
146
- type: 'copy',
147
- from: ['.github', 'workflows', 'main.yml'],
148
- to: ['.github', 'workflows', 'main.yml'],
149
- },
150
- {type: 'copy', from: ['.husky', 'commit-msg'], to: ['.husky', 'commit-msg']},
151
- {type: 'copy', from: ['.husky', 'pre-commit'], to: ['.husky', 'pre-commit']},
152
- {type: 'copy', from: ['.releaserc.json'], to: '.releaserc.json'},
153
- {type: 'copy', from: ['commitlint.template.js'], to: 'commitlint.config.js'},
154
- {type: 'copy', from: ['lint-staged.template.js'], to: 'lint-staged.config.js'},
155
- ]
156
-
157
- return base.map((fromTo) => {
158
- if (fromTo.type === 'copy') {
159
- return {
160
- ...fromTo,
161
- from: ['semver-workflow', ...fromTo.from],
162
- }
163
- }
164
-
165
- return fromTo
166
- })
167
- }
168
-
169
- async function semverWorkflowDependencies(): Promise<Record<string, string>> {
170
- return resolveLatestVersions([
171
- '@commitlint/cli',
172
- '@commitlint/config-conventional',
173
- '@sanity/semantic-release-preset',
174
- 'husky',
175
- 'lint-staged',
176
- ])
177
- }
178
-
179
- export function readmeBaseurl(pkg: PackageJson) {
180
- return ((pkg.repository?.url ?? pkg.homepage ?? 'TODO') as string)
181
- .replace(/.+:\/\//g, 'https://')
182
- .replace(/\.git/g, '')
183
- .replace(/git@github.com\//g, 'github.com/')
184
- .replace(/git@github.com:/g, 'https://github.com/')
185
- .replace(/#.+/g, '')
186
- }
@@ -1,97 +0,0 @@
1
- import {Preset} from './presets'
2
- import {Injectable, InjectOptions, writeAssets} from '../actions/inject'
3
- import {getPackage, sortKeys, writePackageJsonDirect} from '../npm/package'
4
- import log from '../util/log'
5
- import chalk from 'chalk'
6
- import outdent from 'outdent'
7
- import {resolveLatestVersions} from '../npm/resolveLatestVersions'
8
- import path from 'path'
9
- import {readFile, writeFile} from '../util/files'
10
- import {errorToUndefined} from '../util/errorToUndefined'
11
-
12
- export const uiWorkshop: Preset = {
13
- name: 'ui-workshop',
14
- description: 'Files for testing custom components with @sanity/ui-workshop',
15
- apply: applyPreset,
16
- }
17
-
18
- async function applyPreset(options: InjectOptions) {
19
- await writeAssets(files(), options)
20
- await addDevDependencies(options)
21
- await updateGitIgnore(options)
22
- log.info(
23
- chalk.green(
24
- outdent`
25
- ui-workshop preset injected.
26
-
27
- Please confer
28
- https://github.com/sanity-io/plugin-kit/blob/main/docs/ui-workshop.md#manual-steps-after-inject
29
- to finalize configuration for this preset.
30
- `.trim(),
31
- ),
32
- )
33
- }
34
-
35
- function files(): Injectable[] {
36
- const base: Injectable[] = [
37
- {type: 'copy', from: ['workshop.config.ts'], to: ['workshop.config.ts']},
38
- {type: 'copy', from: ['src', 'CustomField.tsx'], to: ['src', 'CustomField.tsx']},
39
- {
40
- type: 'copy',
41
- from: ['src', '__workshop__', 'index.tsx'],
42
- to: ['src', '__workshop__', 'index.tsx'],
43
- },
44
- {
45
- type: 'copy',
46
- from: ['src', '__workshop__', 'props.tsx'],
47
- to: ['src', '__workshop__', 'props.tsx'],
48
- },
49
- ]
50
-
51
- return base.map((fromTo) => {
52
- if (fromTo.type === 'copy') {
53
- return {
54
- ...fromTo,
55
- from: ['ui-workshop', ...fromTo.from],
56
- }
57
- }
58
-
59
- return fromTo
60
- })
61
- }
62
-
63
- async function updateGitIgnore(options: InjectOptions) {
64
- const {basePath} = options
65
- const gitignorePath = path.join(basePath, '.gitignore')
66
- let gitignore = (await readFile(gitignorePath, 'utf8').catch(errorToUndefined)) ?? ''
67
- const value = '.workshop'
68
- if (gitignore.includes(value)) {
69
- return
70
- }
71
-
72
- gitignore += `\n\n${value}`
73
- await writeFile(gitignorePath, gitignore, {encoding: 'utf8'})
74
- }
75
-
76
- async function addDevDependencies(options: InjectOptions) {
77
- const pkg = await getPackage(options)
78
- const devDeps = sortKeys({
79
- ...pkg.devDependencies,
80
- ...(await devDependencies()),
81
- })
82
- const newPkg = {...pkg}
83
- newPkg.devDependencies = devDeps
84
- await writePackageJsonDirect(newPkg, options)
85
- log.info('Updated devDependencies.')
86
- }
87
-
88
- async function devDependencies(): Promise<Record<string, string>> {
89
- return resolveLatestVersions([
90
- '@sanity/ui-workshop',
91
- '@sanity/icons',
92
- '@sanity/ui',
93
- 'react',
94
- 'react-dom',
95
- 'styled-components',
96
- ])
97
- }
package/src/presets/ui.ts DELETED
@@ -1,67 +0,0 @@
1
- import {Preset} from './presets'
2
- import {InjectOptions} from '../actions/inject'
3
- import {forceDependencyVersions, getPackage, sortKeys, writePackageJsonDirect} from '../npm/package'
4
- import log from '../util/log'
5
- import chalk from 'chalk'
6
- import {resolveLatestVersions} from '../npm/resolveLatestVersions'
7
- import {forcedDevPackageVersions, forcedPackageVersions} from '../configs/forced-package-versions'
8
-
9
- export const ui: Preset = {
10
- name: 'ui',
11
- description: '`@sanity/ui` and dependencies',
12
- apply: applyPreset,
13
- }
14
-
15
- async function applyPreset(options: InjectOptions) {
16
- await addDependencies(options)
17
- await addDevDependencies(options)
18
-
19
- log.info(chalk.green('ui preset injected'))
20
- }
21
-
22
- async function addDependencies(options: InjectOptions) {
23
- const pkg = await getPackage(options)
24
- const newDeps = sortKeys(
25
- forceDependencyVersions(
26
- {
27
- ...pkg.dependencies,
28
- ...(await resolveDependencyList()),
29
- },
30
- forcedPackageVersions,
31
- ),
32
- )
33
- const newPkg = {...pkg}
34
- newPkg.dependencies = newDeps
35
- await writePackageJsonDirect(newPkg, options)
36
- log.info('Updated dependencies.')
37
- }
38
-
39
- async function addDevDependencies(options: InjectOptions) {
40
- const pkg = await getPackage(options)
41
- const newDeps = sortKeys(
42
- forceDependencyVersions(
43
- {
44
- ...pkg.devDependencies,
45
- ...(await resolveDevDependencyList()),
46
- },
47
- forcedDevPackageVersions,
48
- ),
49
- )
50
- const newPkg = {...pkg}
51
- newPkg.devDependencies = newDeps
52
- await writePackageJsonDirect(newPkg, options)
53
- log.info('Updated devDependencies.')
54
- }
55
-
56
- async function resolveDependencyList(): Promise<Record<string, string>> {
57
- return resolveLatestVersions(['@sanity/icons', '@sanity/ui'])
58
- }
59
-
60
- async function resolveDevDependencyList(): Promise<Record<string, string>> {
61
- return resolveLatestVersions([
62
- // install the peer dependencies of `@sanity/ui` as dev dependencies
63
- 'react',
64
- 'react-dom',
65
- 'styled-components',
66
- ])
67
- }
@@ -1,340 +0,0 @@
1
- import fs from 'fs'
2
- import path from 'path'
3
- import util from 'util'
4
- import pkg from '../../package.json'
5
- import {buildExtensions} from '../configs/buildExtensions'
6
- import {hasSourceFile, hasCompiledFile, readJsonFile, fileExists} from '../util/files'
7
- import {errorToUndefined} from '../util/errorToUndefined'
8
-
9
- const stat = util.promisify(fs.stat)
10
- const readFile = util.promisify(fs.readFile)
11
-
12
- const allowedPartProps = ['name', 'implements', 'path', 'description']
13
- const disallowedPluginProps = ['api', 'project', 'plugins', 'env']
14
-
15
- export interface SanityV2Manifest {
16
- root?: boolean
17
- name: string
18
- paths: ManifestPaths
19
- parts?: {
20
- path: string
21
- }[]
22
- }
23
-
24
- export interface ManifestPaths {
25
- basePath: string
26
- compiled?: string
27
- source?: string
28
- }
29
-
30
- export interface ManifestOptions {
31
- isPlugin?: boolean
32
- validate?: boolean
33
- pluginName?: string
34
- basePath: string
35
- verifySourceParts?: boolean
36
- verifyCompiledParts?: boolean
37
- paths?: ManifestPaths
38
- flags?: Record<string, any>
39
- }
40
-
41
- export async function getPaths(options: ManifestOptions) {
42
- const {basePath} = options
43
- const manifest = await readManifest(options)
44
- if (!manifest.paths) {
45
- return null
46
- }
47
-
48
- return absolutifyPaths(manifest.paths, basePath)
49
- }
50
-
51
- function absolutifyPaths(paths: ManifestPaths | undefined, basePath: string) {
52
- const getPath = (relative?: string) =>
53
- relative ? path.resolve(path.join(basePath, relative)) : undefined
54
- return paths
55
- ? {
56
- basePath,
57
- compiled: getPath(paths.compiled),
58
- source: getPath(paths.source),
59
- }
60
- : {basePath}
61
- }
62
-
63
- export async function readManifest(options: ManifestOptions) {
64
- const {basePath, validate = true} = options
65
- const manifestPath = path.normalize(path.join(basePath, 'sanity.json'))
66
-
67
- let content
68
- try {
69
- content = await readFile(manifestPath, 'utf8')
70
- } catch (err: any) {
71
- if (err.code === 'ENOENT') {
72
- throw new Error(
73
- `No sanity.json found. sanity.json is required for plugins to function. Use \`${pkg.binname} init\` for a new plugin, or create an empty \`sanity.json\` with an empty object (\`{}\`) for existing ones.`,
74
- )
75
- }
76
-
77
- throw new Error(`Failed to read "${manifestPath}": ${err.message}`)
78
- }
79
-
80
- let parsed
81
- try {
82
- parsed = JSON.parse(content)
83
- } catch (err: any) {
84
- throw new Error(`Error parsing "${manifestPath}": ${err.message}`)
85
- }
86
-
87
- if (validate) {
88
- await validateManifest(parsed, options)
89
- }
90
-
91
- return parsed
92
- }
93
-
94
- export async function validateManifest(manifest: SanityV2Manifest, opts: ManifestOptions) {
95
- const options = {isPlugin: true, ...opts}
96
-
97
- if (!isObject(manifest)) {
98
- throw new Error(`Invalid sanity.json: Root must be an object`)
99
- }
100
-
101
- if (options.isPlugin) {
102
- await validatePluginManifest(manifest, options)
103
- } else {
104
- await validateProjectManifest(manifest)
105
- }
106
-
107
- if ('root' in manifest && typeof manifest.root !== 'boolean') {
108
- throw new Error(`Invalid sanity.json: "root" property must be a boolean if declared`)
109
- }
110
-
111
- await validateParts(manifest, {
112
- ...options,
113
- paths: absolutifyPaths(manifest.paths, options.basePath),
114
- })
115
- }
116
-
117
- function validateProjectManifest(manifest: SanityV2Manifest) {
118
- if ('paths' in manifest) {
119
- throw new Error(`Invalid sanity.json: "paths" property has no meaning in a project manifest`)
120
- }
121
- }
122
-
123
- export async function validatePluginManifest(
124
- manifest: SanityV2Manifest,
125
- options: {basePath: string},
126
- ) {
127
- const disallowed = Object.keys(manifest)
128
- .filter((key) => disallowedPluginProps.includes(key))
129
- .map((key) => `"${key}"`)
130
-
131
- if (disallowed.length > 0) {
132
- const plural = disallowed.length > 1 ? 's' : ''
133
- const joined = disallowed.join(', ')
134
- throw new Error(
135
- `Invalid sanity.json: Key${plural} ${joined} ${
136
- plural ? 'are' : 'is'
137
- } not allowed in a plugin manifest`,
138
- )
139
- }
140
-
141
- if (manifest.root) {
142
- throw new Error(`Invalid sanity.json: "root" cannot be truthy in a plugin manifest`)
143
- }
144
-
145
- await validatePaths(manifest, options)
146
- }
147
-
148
- export async function validatePaths(manifest: SanityV2Manifest, options: {basePath: string}) {
149
- if (!('paths' in manifest)) {
150
- return
151
- }
152
-
153
- if (!isObject(manifest.paths)) {
154
- throw new Error(`Invalid sanity.json: "paths" must be an object if declared`)
155
- }
156
-
157
- if (typeof manifest.paths.compiled !== 'string') {
158
- throw new Error(
159
- `Invalid sanity.json: "paths" must have a (string) "compiled" property if declared`,
160
- )
161
- }
162
-
163
- if (typeof manifest.paths.source !== 'string') {
164
- throw new Error(
165
- `Invalid sanity.json: "paths" must have a (string) "source" property if declared`,
166
- )
167
- }
168
-
169
- const sourcePath = path.resolve(options.basePath, manifest.paths.source)
170
- let srcStats
171
- try {
172
- srcStats = await stat(sourcePath)
173
- } catch (err: any) {
174
- if (err.code === 'ENOENT') {
175
- throw new Error(`sanity.json references "source" path which does not exist: "${sourcePath}"`)
176
- }
177
- }
178
-
179
- if (!srcStats?.isDirectory()) {
180
- throw new Error(
181
- `sanity.json references "source" path which is not a directory: "${sourcePath}"`,
182
- )
183
- }
184
- }
185
-
186
- async function validateParts(manifest: SanityV2Manifest, options: ManifestOptions) {
187
- if (!('parts' in manifest)) {
188
- return
189
- }
190
-
191
- if (!Array.isArray(manifest.parts)) {
192
- throw new Error(`Invalid sanity.json: "parts" must be an array if declared`)
193
- }
194
-
195
- let i = 0
196
- for (const part of manifest.parts) {
197
- await validatePart(part, i, options)
198
- i++
199
- }
200
- }
201
-
202
- async function validatePart(part: Record<string, any>, index: number, options: ManifestOptions) {
203
- if (!isObject(part)) {
204
- throw new Error(`Invalid sanity.json: "parts[${index}]" must be an object`)
205
- }
206
-
207
- validateAllowedPartKeys(part, index)
208
- validatePartStringValues(part, index)
209
- validatePartNames(part, index, options)
210
- await validatePartFiles(part, index, options)
211
- }
212
-
213
- async function validatePartFiles(
214
- part: {path?: string} | undefined,
215
- index: number,
216
- options: ManifestOptions,
217
- ) {
218
- const {verifyCompiledParts, verifySourceParts, paths} = options
219
- if (!part?.path) {
220
- return
221
- }
222
-
223
- const ext = path.extname(part.path)
224
- if (paths?.source && ext && ext !== '.js' && buildExtensions.includes(ext)) {
225
- throw new Error(
226
- `Invalid sanity.json: Part path has extension which is not applicable after compiling. ${ext} becomes .js after compiling. Specify filename without extension (${path.basename(
227
- part.path,
228
- )}) (parts[${index}])`,
229
- )
230
- }
231
-
232
- if (!verifySourceParts && !verifyCompiledParts) {
233
- return
234
- }
235
-
236
- const [srcExists, outDirExists] = await Promise.all([
237
- hasSourceFile(part.path, paths),
238
- verifyCompiledParts && hasCompiledFile(part.path, paths),
239
- ])
240
-
241
- if (!srcExists) {
242
- throw new Error(
243
- `Invalid sanity.json: Part path references file that does not exist in source directory (${
244
- paths?.source || paths?.basePath
245
- }) (parts[${index}])`,
246
- )
247
- }
248
-
249
- if (verifyCompiledParts && !outDirExists) {
250
- throw new Error(
251
- `Invalid sanity.json: Part path references file ("${part.path}") that does not exist in compiled directory (${paths?.compiled}) (parts[${index}])`,
252
- )
253
- }
254
- }
255
-
256
- function validatePartNames(
257
- part: {name?: string; implements?: string} | undefined,
258
- index: number,
259
- options: ManifestOptions,
260
- ) {
261
- const pluginName = options.pluginName ? options.pluginName.replace(/^sanity-plugin-/, '') : ''
262
- if (!part?.name || !part?.name?.startsWith(`part:${pluginName}/`)) {
263
- throw new Error(
264
- `Invalid sanity.json: "name" must be prefixed with "part:${pluginName}/" - got "${part?.name}" (parts[${index}])`,
265
- )
266
- }
267
-
268
- if (!part?.implements?.startsWith('part:')) {
269
- throw new Error(
270
- `Invalid sanity.json: "implements" must be prefixed with "part:" - got "${part?.implements}" (parts[${index}])`,
271
- )
272
- }
273
- }
274
-
275
- function validateAllowedPartKeys(part: Record<string, any>, index: number) {
276
- const disallowed = Object.keys(part)
277
- .filter((key) => !allowedPartProps.includes(key))
278
- .map((key) => `"${key}"`)
279
-
280
- if (disallowed.length > 0) {
281
- const plural = disallowed.length > 1 ? 's' : ''
282
- const joined = disallowed.join(', ')
283
- throw new Error(
284
- `Invalid sanity.json: Key${plural} ${joined} ${
285
- plural ? 'are' : 'is'
286
- } not allowed in a part declaration (parts[${index}])`,
287
- )
288
- }
289
- }
290
-
291
- function validatePartStringValues(part: Record<string, any>, index: number) {
292
- const nonStrings = Object.keys(part)
293
- .filter((key) => typeof part[key] !== 'string')
294
- .map((key) => `"${key}"`)
295
-
296
- if (nonStrings.length > 0) {
297
- const plural = nonStrings.length > 1 ? 's' : ''
298
- const joined = nonStrings.join(', ')
299
- throw new Error(
300
- `Invalid sanity.json: Key${plural} ${joined} should be of type string (parts[${index}])`,
301
- )
302
- }
303
- }
304
-
305
- function isObject(obj: any) {
306
- return !Array.isArray(obj) && obj !== null && typeof obj === 'object'
307
- }
308
-
309
- export function getReferencesPartPaths(manifest: SanityV2Manifest, basePath: string) {
310
- const {paths, parts = []} = manifest
311
- const compiledPath = path.resolve(basePath, paths?.compiled || '')
312
-
313
- return parts
314
- .filter((part) => part.path)
315
- .map((part) => part.path)
316
- .map((partPath) => (path.extname(partPath) === '' ? `${partPath}.js` : partPath))
317
- .map((partPath) =>
318
- path.isAbsolute(partPath)
319
- ? partPath // Not sure if this ever happens, but :shrugs:
320
- : path.resolve(compiledPath, partPath),
321
- )
322
- }
323
-
324
- export async function hasSanityJson(basePath: string) {
325
- const file = await readJsonFile<{root?: boolean}>(path.join(basePath, 'sanity.json')).catch(
326
- errorToUndefined,
327
- )
328
- return {exists: Boolean(file), isRoot: Boolean(file && file.root)}
329
- }
330
-
331
- export async function findStudioV3Config(basePath: string) {
332
- const jsFile = 'sanity.config.js'
333
- const jsExists = await fileExists(path.join(basePath, jsFile))
334
- if (jsExists) {
335
- return {v3ConfigFile: jsFile}
336
- }
337
- const tsFile = 'sanity.config.ts'
338
- const tsExists = await fileExists(path.join(basePath, tsFile))
339
- return {v3ConfigFile: tsExists ? tsFile : undefined}
340
- }
@@ -1,14 +0,0 @@
1
- export default {
2
- debug: {
3
- default: false,
4
- type: 'boolean',
5
- },
6
- silent: {
7
- type: 'boolean',
8
- default: false,
9
- },
10
- verbose: {
11
- type: 'boolean',
12
- default: false,
13
- },
14
- } as const