@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.
- package/assets/inject/semver-workflow/.husky/commit-msg +0 -0
- package/assets/inject/semver-workflow/.husky/pre-commit +0 -0
- package/assets/inject/ui-workshop/src/__workshop__/props.tsx +2 -1
- package/bin/plugin-kit.js +3 -1
- package/dist/{_chunks-cjs/cli.js → _chunks-es/index.js} +53 -63
- package/dist/_chunks-es/index.js.map +1 -0
- package/dist/{_chunks-cjs/init2.js → _chunks-es/init.js} +25 -23
- package/dist/_chunks-es/init.js.map +1 -0
- package/dist/_chunks-es/init2.js +140 -0
- package/dist/_chunks-es/init2.js.map +1 -0
- package/{src/cmds/inject.ts → dist/_chunks-es/inject.js} +18 -32
- package/dist/{_chunks-cjs → _chunks-es}/inject.js.map +1 -1
- package/dist/_chunks-es/link-watch.js +91 -0
- package/dist/_chunks-es/link-watch.js.map +1 -0
- package/dist/_chunks-es/load-package-config.js +22 -0
- package/dist/_chunks-es/load-package-config.js.map +1 -0
- package/dist/_chunks-es/package.js +1759 -0
- package/dist/_chunks-es/package.js.map +1 -0
- package/dist/_chunks-es/package2.js +9 -0
- package/dist/{_chunks-cjs → _chunks-es}/package2.js.map +1 -1
- package/dist/_chunks-es/ts.js +171 -0
- package/dist/_chunks-es/ts.js.map +1 -0
- package/dist/_chunks-es/verify-package.js +92 -0
- package/dist/_chunks-es/verify-package.js.map +1 -0
- package/dist/_chunks-es/verify-studio.js +61 -0
- package/dist/_chunks-es/verify-studio.js.map +1 -0
- package/dist/_chunks-es/version.js +50 -0
- package/dist/_chunks-es/version.js.map +1 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/package.json +40 -98
- package/LICENSE +0 -21
- package/dist/_chunks-cjs/cli.js.map +0 -1
- package/dist/_chunks-cjs/init.js +0 -892
- package/dist/_chunks-cjs/init.js.map +0 -1
- package/dist/_chunks-cjs/init2.js.map +0 -1
- package/dist/_chunks-cjs/inject.js +0 -54
- package/dist/_chunks-cjs/link-watch.js +0 -84
- package/dist/_chunks-cjs/link-watch.js.map +0 -1
- package/dist/_chunks-cjs/package.js +0 -1809
- package/dist/_chunks-cjs/package.js.map +0 -1
- package/dist/_chunks-cjs/package2.js +0 -8
- package/dist/_chunks-cjs/ts.js +0 -160732
- package/dist/_chunks-cjs/ts.js.map +0 -1
- package/dist/_chunks-cjs/verify-package.js +0 -75
- package/dist/_chunks-cjs/verify-package.js.map +0 -1
- package/dist/_chunks-cjs/verify-studio.js +0 -57
- package/dist/_chunks-cjs/verify-studio.js.map +0 -1
- package/dist/_chunks-cjs/version.js +0 -51
- package/dist/_chunks-cjs/version.js.map +0 -1
- package/dist/cli.d.ts +0 -4
- package/dist/cli.js +0 -6
- package/dist/cli.js.map +0 -1
- package/src/actions/init.ts +0 -95
- package/src/actions/inject.ts +0 -399
- package/src/actions/link-watch.ts +0 -98
- package/src/actions/verify/types.ts +0 -56
- package/src/actions/verify/validations.ts +0 -505
- package/src/actions/verify/verify-common.ts +0 -93
- package/src/actions/verify-package.ts +0 -103
- package/src/actions/verify-studio.ts +0 -58
- package/src/cli.ts +0 -77
- package/src/cmds/index.ts +0 -20
- package/src/cmds/init.ts +0 -90
- package/src/cmds/link-watch.ts +0 -50
- package/src/cmds/verify-package.ts +0 -36
- package/src/cmds/verify-studio.ts +0 -36
- package/src/cmds/version.ts +0 -67
- package/src/configs/banned-packages.ts +0 -27
- package/src/configs/buildExtensions.ts +0 -1
- package/src/configs/default-source.ts +0 -64
- package/src/configs/eslint.ts +0 -51
- package/src/configs/forced-package-versions.ts +0 -12
- package/src/configs/git.ts +0 -68
- package/src/configs/pkg-config.ts +0 -30
- package/src/configs/prettier.ts +0 -11
- package/src/configs/tsconfig.ts +0 -78
- package/src/configs/uselessFiles.ts +0 -29
- package/src/constants.ts +0 -15
- package/src/dependencies/find.ts +0 -193
- package/src/dependencies/import-linter.ts +0 -95
- package/src/index.ts +0 -1
- package/src/npm/manager.ts +0 -44
- package/src/npm/package.ts +0 -427
- package/src/npm/publish.ts +0 -9
- package/src/npm/resolveLatestVersions.ts +0 -31
- package/src/presets/presets.ts +0 -54
- package/src/presets/renovatebot.ts +0 -21
- package/src/presets/semver-workflow.ts +0 -186
- package/src/presets/ui-workshop.ts +0 -97
- package/src/presets/ui.ts +0 -67
- package/src/sanity/manifest.ts +0 -340
- package/src/sharedFlags.ts +0 -14
- package/src/util/command-parser.ts +0 -36
- package/src/util/errorToUndefined.ts +0 -7
- package/src/util/files.ts +0 -260
- package/src/util/log.ts +0 -44
- package/src/util/prompt.ts +0 -70
- package/src/util/readme.ts +0 -88
- package/src/util/request.ts +0 -11
- package/src/util/ts.ts +0 -19
- 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
|
-
}
|
package/src/sanity/manifest.ts
DELETED
|
@@ -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
|
-
}
|