@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.
- package/LICENSE +22 -0
- package/README.md +398 -0
- package/assets/splat/LICENSE +21 -0
- package/assets/splat/editorconfig +13 -0
- package/assets/splat/eslint.config.js +5 -0
- package/assets/splat/gitignore +55 -0
- package/assets/splat/npmignore +9 -0
- package/assets/splat/prettierrc.js +6 -0
- package/assets/splat/sanity.json +8 -0
- package/assets/splat/template-tsconfig.json +23 -0
- package/assets/splat/v2-incompatible.js.template +11 -0
- package/lib/package.json +127 -0
- package/lib/src/actions/init.d.ts +65 -0
- package/lib/src/actions/init.js +83 -0
- package/lib/src/actions/init.js.map +1 -0
- package/lib/src/actions/link-watch.d.ts +3 -0
- package/lib/src/actions/link-watch.js +69 -0
- package/lib/src/actions/link-watch.js.map +1 -0
- package/lib/src/actions/splat.d.ts +26 -0
- package/lib/src/actions/splat.js +296 -0
- package/lib/src/actions/splat.js.map +1 -0
- package/lib/src/actions/verify/types.d.ts +77 -0
- package/lib/src/actions/verify/types.js +3 -0
- package/lib/src/actions/verify/types.js.map +1 -0
- package/lib/src/actions/verify/validations.d.ts +28 -0
- package/lib/src/actions/verify/validations.js +379 -0
- package/lib/src/actions/verify/validations.js.map +1 -0
- package/lib/src/actions/verify/verify-common.d.ts +43 -0
- package/lib/src/actions/verify/verify-common.js +88 -0
- package/lib/src/actions/verify/verify-common.js.map +1 -0
- package/lib/src/actions/verify-package.d.ts +5 -0
- package/lib/src/actions/verify-package.js +72 -0
- package/lib/src/actions/verify-package.js.map +1 -0
- package/lib/src/actions/verify-studio.d.ts +5 -0
- package/lib/src/actions/verify-studio.js +55 -0
- package/lib/src/actions/verify-studio.js.map +1 -0
- package/lib/src/actions/verify.d.ts +0 -0
- package/lib/src/actions/verify.js +330 -0
- package/lib/src/actions/verify.js.map +1 -0
- package/lib/src/cli.d.ts +2 -0
- package/lib/src/cli.js +86 -0
- package/lib/src/cli.js.map +1 -0
- package/lib/src/cmds/index.d.ts +8 -0
- package/lib/src/cmds/index.js +12 -0
- package/lib/src/cmds/index.js.map +1 -0
- package/lib/src/cmds/init.d.ts +4 -0
- package/lib/src/cmds/init.js +90 -0
- package/lib/src/cmds/init.js.map +1 -0
- package/lib/src/cmds/link-watch.d.ts +4 -0
- package/lib/src/cmds/link-watch.js +49 -0
- package/lib/src/cmds/link-watch.js.map +1 -0
- package/lib/src/cmds/splat.d.ts +4 -0
- package/lib/src/cmds/splat.js +63 -0
- package/lib/src/cmds/splat.js.map +1 -0
- package/lib/src/cmds/verify-package.d.ts +4 -0
- package/lib/src/cmds/verify-package.js +38 -0
- package/lib/src/cmds/verify-package.js.map +1 -0
- package/lib/src/cmds/verify-studio.d.ts +4 -0
- package/lib/src/cmds/verify-studio.js +38 -0
- package/lib/src/cmds/verify-studio.js.map +1 -0
- package/lib/src/cmds/verify.d.ts +0 -0
- package/lib/src/cmds/verify.js +42 -0
- package/lib/src/cmds/verify.js.map +1 -0
- package/lib/src/cmds/version.d.ts +4 -0
- package/lib/src/cmds/version.js +55 -0
- package/lib/src/cmds/version.js.map +1 -0
- package/lib/src/configs/buildExtensions.d.ts +1 -0
- package/lib/src/configs/buildExtensions.js +5 -0
- package/lib/src/configs/buildExtensions.js.map +1 -0
- package/lib/src/configs/default-source.d.ts +3 -0
- package/lib/src/configs/default-source.js +70 -0
- package/lib/src/configs/default-source.js.map +1 -0
- package/lib/src/configs/merged-packages.d.ts +1 -0
- package/lib/src/configs/merged-packages.js +24 -0
- package/lib/src/configs/merged-packages.js.map +1 -0
- package/lib/src/configs/uselessFiles.d.ts +1 -0
- package/lib/src/configs/uselessFiles.js +33 -0
- package/lib/src/configs/uselessFiles.js.map +1 -0
- package/lib/src/constants.d.ts +11 -0
- package/lib/src/constants.js +15 -0
- package/lib/src/constants.js.map +1 -0
- package/lib/src/dependencies/find.d.ts +0 -0
- package/lib/src/dependencies/find.js +195 -0
- package/lib/src/dependencies/find.js.map +1 -0
- package/lib/src/dependencies/import-linter.d.ts +3 -0
- package/lib/src/dependencies/import-linter.js +112 -0
- package/lib/src/dependencies/import-linter.js.map +1 -0
- package/lib/src/index.d.ts +2 -0
- package/lib/src/index.js +6 -0
- package/lib/src/index.js.map +1 -0
- package/lib/src/npm/manager.d.ts +7 -0
- package/lib/src/npm/manager.js +62 -0
- package/lib/src/npm/manager.js.map +1 -0
- package/lib/src/npm/package.d.ts +8 -0
- package/lib/src/npm/package.js +288 -0
- package/lib/src/npm/package.js.map +1 -0
- package/lib/src/npm/publish.d.ts +1 -0
- package/lib/src/npm/publish.js +14 -0
- package/lib/src/npm/publish.js.map +1 -0
- package/lib/src/npm/resolveLatestVersions.d.ts +3 -0
- package/lib/src/npm/resolveLatestVersions.js +35 -0
- package/lib/src/npm/resolveLatestVersions.js.map +1 -0
- package/lib/src/sanity/manifest.d.ts +48 -0
- package/lib/src/sanity/manifest.js +263 -0
- package/lib/src/sanity/manifest.js.map +1 -0
- package/lib/src/sharedFlags.d.ts +15 -0
- package/lib/src/sharedFlags.js +17 -0
- package/lib/src/sharedFlags.js.map +1 -0
- package/lib/src/util/command-parser.d.ts +9 -0
- package/lib/src/util/command-parser.js +41 -0
- package/lib/src/util/command-parser.js.map +1 -0
- package/lib/src/util/errorToUndefined.d.ts +1 -0
- package/lib/src/util/errorToUndefined.js +11 -0
- package/lib/src/util/errorToUndefined.js.map +1 -0
- package/lib/src/util/files.d.ts +36 -0
- package/lib/src/util/files.js +253 -0
- package/lib/src/util/files.js.map +1 -0
- package/lib/src/util/log.d.ts +14 -0
- package/lib/src/util/log.js +36 -0
- package/lib/src/util/log.js.map +1 -0
- package/lib/src/util/prompt.d.ts +13 -0
- package/lib/src/util/prompt.js +75 -0
- package/lib/src/util/prompt.js.map +1 -0
- package/lib/src/util/readme.d.ts +5 -0
- package/lib/src/util/readme.js +73 -0
- package/lib/src/util/readme.js.map +1 -0
- package/lib/src/util/request.d.ts +1 -0
- package/lib/src/util/request.js +19 -0
- package/lib/src/util/request.js.map +1 -0
- package/lib/src/util/user.d.ts +10 -0
- package/lib/src/util/user.js +106 -0
- package/lib/src/util/user.js.map +1 -0
- package/lib/test/cli.test.d.ts +1 -0
- package/lib/test/cli.test.js +64 -0
- package/lib/test/cli.test.js.map +1 -0
- package/lib/test/fixture-utils.d.ts +25 -0
- package/lib/test/fixture-utils.js +67 -0
- package/lib/test/fixture-utils.js.map +1 -0
- package/lib/test/init-verify-build.test.d.ts +1 -0
- package/lib/test/init-verify-build.test.js +75 -0
- package/lib/test/init-verify-build.test.js.map +1 -0
- package/lib/test/init.test.d.ts +1 -0
- package/lib/test/init.test.js +137 -0
- package/lib/test/init.test.js.map +1 -0
- package/lib/test/run-test-command.d.ts +1 -0
- package/lib/test/run-test-command.js +6 -0
- package/lib/test/run-test-command.js.map +1 -0
- package/lib/test/verify-package.test.d.ts +1 -0
- package/lib/test/verify-package.test.js +81 -0
- package/lib/test/verify-package.test.js.map +1 -0
- package/lib/test/version.test.d.ts +1 -0
- package/lib/test/version.test.js +48 -0
- package/lib/test/version.test.js.map +1 -0
- package/package.json +127 -0
- package/src/actions/init.ts +104 -0
- package/src/actions/link-watch.ts +74 -0
- package/src/actions/splat.ts +366 -0
- package/src/actions/verify/types.ts +84 -0
- package/src/actions/verify/validations.ts +401 -0
- package/src/actions/verify/verify-common.ts +92 -0
- package/src/actions/verify-package.ts +87 -0
- package/src/actions/verify-studio.ts +55 -0
- package/src/actions/verify.ts +328 -0
- package/src/cli.ts +77 -0
- package/src/cmds/index.ts +9 -0
- package/src/cmds/init.ts +85 -0
- package/src/cmds/link-watch.ts +51 -0
- package/src/cmds/splat.ts +59 -0
- package/src/cmds/verify-package.ts +36 -0
- package/src/cmds/verify-studio.ts +36 -0
- package/src/cmds/verify.ts +40 -0
- package/src/cmds/version.ts +67 -0
- package/src/configs/buildExtensions.ts +1 -0
- package/src/configs/default-source.ts +68 -0
- package/src/configs/merged-packages.ts +20 -0
- package/src/configs/uselessFiles.ts +29 -0
- package/src/constants.ts +13 -0
- package/src/dependencies/find.ts +193 -0
- package/src/dependencies/import-linter.ts +103 -0
- package/src/index.ts +4 -0
- package/src/npm/manager.ts +44 -0
- package/src/npm/package.ts +370 -0
- package/src/npm/publish.ts +9 -0
- package/src/npm/resolveLatestVersions.ts +26 -0
- package/src/sanity/manifest.ts +340 -0
- package/src/sharedFlags.ts +14 -0
- package/src/util/command-parser.ts +31 -0
- package/src/util/errorToUndefined.ts +7 -0
- package/src/util/files.ts +249 -0
- package/src/util/log.ts +44 -0
- package/src/util/prompt.ts +70 -0
- package/src/util/readme.ts +72 -0
- package/src/util/request.ts +13 -0
- package/src/util/user.ts +110 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import {getPackage} from '../npm/package'
|
|
2
|
+
import log from '../util/log'
|
|
3
|
+
import {readJson5File} from '../util/files'
|
|
4
|
+
import {cliName, urls} from '../constants'
|
|
5
|
+
import {validateImports} from '../dependencies/import-linter'
|
|
6
|
+
import outdent from 'outdent'
|
|
7
|
+
import chalk from 'chalk'
|
|
8
|
+
import {
|
|
9
|
+
createValidator,
|
|
10
|
+
runTscMaybe,
|
|
11
|
+
VerifyFlags,
|
|
12
|
+
VerifyPackageConfig,
|
|
13
|
+
} from './verify/verify-common'
|
|
14
|
+
import {PackageJson, TsConfig} from './verify/types'
|
|
15
|
+
import {validateSanityDependencies, validateStudioConfig} from './verify/validations'
|
|
16
|
+
|
|
17
|
+
export async function verifyStudio({basePath, flags}: {basePath: string; flags: VerifyFlags}) {
|
|
18
|
+
let errors: string[] = []
|
|
19
|
+
|
|
20
|
+
const packageJson: PackageJson = await getPackage({basePath, validate: false})
|
|
21
|
+
const verifyConfig: VerifyPackageConfig = packageJson.sanityPlugin?.verifyPackage || {}
|
|
22
|
+
|
|
23
|
+
const validation = createValidator(verifyConfig, flags, errors)
|
|
24
|
+
|
|
25
|
+
const tsConfig = await readJson5File<TsConfig>({basePath, filename: 'tsconfig.json'})
|
|
26
|
+
|
|
27
|
+
await validation('studioConfig', async () => validateStudioConfig({basePath}))
|
|
28
|
+
await validation('dependencies', async () => validateSanityDependencies(packageJson))
|
|
29
|
+
await validation('eslintImports', async () => validateImports({basePath}))
|
|
30
|
+
|
|
31
|
+
if (errors.length) {
|
|
32
|
+
throw new Error(
|
|
33
|
+
outdent`
|
|
34
|
+
Detected validation issues!
|
|
35
|
+
This Sanity Studio is not completely V3 ready. Fix the issues starting from the top, or disable any checks you deem unnecessary.
|
|
36
|
+
|
|
37
|
+
More information is available here:
|
|
38
|
+
- Migration guide: ${urls.migrationGuideStudio}
|
|
39
|
+
- Reference documentation: ${urls.refDocs}
|
|
40
|
+
|
|
41
|
+
${chalk.grey(
|
|
42
|
+
`To fail-fast on first detected issue run:\nnpx ${cliName} verify-studio --single`
|
|
43
|
+
)}
|
|
44
|
+
`.trimStart()
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await runTscMaybe(verifyConfig, tsConfig)
|
|
49
|
+
|
|
50
|
+
log.success(
|
|
51
|
+
outdent`
|
|
52
|
+
No outstanding upgrade issues detected. Studio is V3 ready!
|
|
53
|
+
`.trim()
|
|
54
|
+
)
|
|
55
|
+
}
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/*
|
|
2
|
+
import path from 'path'
|
|
3
|
+
//@ts-expect-error missing types
|
|
4
|
+
import spdxLicenseIds from 'spdx-license-ids'
|
|
5
|
+
import semver from 'semver'
|
|
6
|
+
|
|
7
|
+
import log from '../util/log'
|
|
8
|
+
import {fileExists, readJsonFile} from '../util/files'
|
|
9
|
+
import {readManifest, getReferencesPartPaths} from '../sanity/manifest'
|
|
10
|
+
import {getPackage, getReferencedPaths} from '../npm/package'
|
|
11
|
+
import {getPublishableFiles} from '../npm/publish'
|
|
12
|
+
import {findDependencies} from '../dependencies/find'
|
|
13
|
+
import {uselessFiles} from '../configs/uselessFiles'
|
|
14
|
+
|
|
15
|
+
export async function verify({basePath, flags}) {
|
|
16
|
+
const pkg = await getPackage({basePath, flags})
|
|
17
|
+
const manifest = await readManifest({
|
|
18
|
+
basePath,
|
|
19
|
+
pluginName: pkg.name,
|
|
20
|
+
flags,
|
|
21
|
+
verifyCompiledParts: true,
|
|
22
|
+
verifySourceParts: true,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
// Get all files intended to be published from npm
|
|
26
|
+
const publishableFiles = await getPublishableFiles(basePath)
|
|
27
|
+
|
|
28
|
+
// Errors
|
|
29
|
+
await verifyPublishableFiles({basePath, pkg, manifest, publishableFiles})
|
|
30
|
+
await verifyLicenseKey(pkg)
|
|
31
|
+
await verifyPluginConfig(basePath)
|
|
32
|
+
await verifyImports({pkg, manifest, basePath})
|
|
33
|
+
|
|
34
|
+
// Warnings
|
|
35
|
+
const hasWarnings = await warnOnUselessFiles(publishableFiles)
|
|
36
|
+
|
|
37
|
+
// Huzzah
|
|
38
|
+
log.success(
|
|
39
|
+
hasWarnings
|
|
40
|
+
? 'Plugin has warnings, but looks good to publish!'
|
|
41
|
+
: 'Plugin looks good to publish!'
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function verifyPublishableFiles({pkg, manifest, basePath, publishableFiles}) {
|
|
46
|
+
// Validate that these files exists, not just that they are publishable
|
|
47
|
+
if (!(await fileExists(path.resolve(basePath, 'README.md')))) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
`This plugin does not contain a README.md, which is required for Sanity plugins.`
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const [hasLicense, hasLicenseMd] = await Promise.all([
|
|
54
|
+
fileExists(path.resolve(basePath, 'LICENSE')),
|
|
55
|
+
fileExists(path.resolve(basePath, 'LICENSE.md')),
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
if (!hasLicense && !hasLicenseMd) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
`This plugin does not contain a LICENSE-file, which is required for Sanity plugins.`
|
|
61
|
+
)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Always, uhm... "kindly suggest", to include these files
|
|
65
|
+
const files = ['README.md']
|
|
66
|
+
// Get files from parts as well as the ones references in package.json
|
|
67
|
+
.concat(getReferencesPartPaths(manifest, basePath), getReferencedPaths(pkg, basePath))
|
|
68
|
+
// Make all paths relative to base path
|
|
69
|
+
.map((file) =>
|
|
70
|
+
path.relative(basePath, path.isAbsolute(file) ? file : path.resolve(basePath, file))
|
|
71
|
+
)
|
|
72
|
+
// Remove duplicates
|
|
73
|
+
.filter((file, index, arr) => arr.indexOf(file, index + 1) === -1)
|
|
74
|
+
|
|
75
|
+
// Verify that all explicitly referenced files are publishable
|
|
76
|
+
const unpublishable = files.filter((file) => !publishableFiles.includes(file))
|
|
77
|
+
|
|
78
|
+
// Warn with "default error" for unknowns
|
|
79
|
+
const unknowns = unpublishable.filter((item) => item !== 'README.md').map((file) => `"${file}"`)
|
|
80
|
+
|
|
81
|
+
if (unknowns.length > 0) {
|
|
82
|
+
const paths = unknowns.join(', ')
|
|
83
|
+
throw new Error(
|
|
84
|
+
`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.`
|
|
85
|
+
)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function verifyLicenseKey(pkg) {
|
|
90
|
+
if (!pkg.license) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`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.`
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (pkg.license !== 'UNLICENSED' && !spdxLicenseIds.includes(pkg.license)) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
`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.`
|
|
99
|
+
)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function verifyPluginConfig(basePath) {
|
|
104
|
+
const configPath = path.join(basePath, 'config.dist.json')
|
|
105
|
+
if (!(await fileExists(configPath))) {
|
|
106
|
+
return
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
let config
|
|
110
|
+
try {
|
|
111
|
+
config = await readJsonFile(configPath)
|
|
112
|
+
} catch (err: any) {
|
|
113
|
+
throw new Error(`Error reading plugin config (${configPath}): ${err.message}`)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (typeof config !== 'object' || Array.isArray(config) || !config) {
|
|
117
|
+
throw new Error(
|
|
118
|
+
`Error reading plugin config (${configPath}): must be an object, got:\n${JSON.stringify(
|
|
119
|
+
config,
|
|
120
|
+
null,
|
|
121
|
+
2
|
|
122
|
+
)}`
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function warnOnUselessFiles(files) {
|
|
128
|
+
const warnFor = files
|
|
129
|
+
.filter(
|
|
130
|
+
(file) =>
|
|
131
|
+
uselessFiles.includes(file) ||
|
|
132
|
+
uselessFiles.some((useless) => file.startsWith(`${useless}/`))
|
|
133
|
+
)
|
|
134
|
+
.map((file) => `"${file}"`)
|
|
135
|
+
.join(', ')
|
|
136
|
+
|
|
137
|
+
if (warnFor.length === 0) {
|
|
138
|
+
return false
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
log.warn(
|
|
142
|
+
`This plugin is set to publish the following files, which are generally not needed in a published npm module: ${warnFor}.`
|
|
143
|
+
)
|
|
144
|
+
log.warn(
|
|
145
|
+
`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.`
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return true
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function verifyImports({pkg, manifest, basePath}) {
|
|
152
|
+
// "Entry" as in... code may start here.
|
|
153
|
+
const entries = ([] as string[])
|
|
154
|
+
.concat(
|
|
155
|
+
getReferencedPaths(pkg, basePath), // From npm
|
|
156
|
+
getReferencesPartPaths(manifest, basePath) // From parts
|
|
157
|
+
)
|
|
158
|
+
// Remove duplicates
|
|
159
|
+
.filter((file, index, arr) => arr.indexOf(file, index + 1) === -1)
|
|
160
|
+
// Remove non-javascript/non-css entries
|
|
161
|
+
.filter((file) => ['.js', '.css'].includes(path.extname(file)))
|
|
162
|
+
|
|
163
|
+
const dependencies = findDependencies(entries)
|
|
164
|
+
const modules = dependencies.filter((dep) => !/^(all|part|config|sanity):/.test(dep))
|
|
165
|
+
|
|
166
|
+
await verifyNoUndeclaredDependencies(modules, pkg)
|
|
167
|
+
await verifyReactDependencies(modules, pkg)
|
|
168
|
+
await verifyUiDependencies(modules, pkg)
|
|
169
|
+
await verifyConfigParts(dependencies, pkg, basePath)
|
|
170
|
+
await verifyNoUnusedDependencies(modules, pkg)
|
|
171
|
+
await verifyNoUndeclaredParts(dependencies, pkg, manifest)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function verifyNoUndeclaredParts(dependencies, pkg, manifest) {
|
|
175
|
+
const parts = dependencies
|
|
176
|
+
.filter((dep) => dep.startsWith('all:') || dep.startsWith('part:'))
|
|
177
|
+
.map((part) => part.replace(/^all:/, ''))
|
|
178
|
+
.map((part) => part.replace(/\?$/, ''))
|
|
179
|
+
.filter((part) => part.startsWith(`part:${pkg.name.replace(/^sanity-plugin-/, '')}`))
|
|
180
|
+
|
|
181
|
+
const declaredParts = (manifest.parts || []).reduce(
|
|
182
|
+
(partNames, part) => [...partNames, ...[part.name, part.implements].filter(Boolean)],
|
|
183
|
+
[]
|
|
184
|
+
)
|
|
185
|
+
const undeclaredParts = parts.filter((partName) => !declaredParts.includes(partName))
|
|
186
|
+
|
|
187
|
+
if (undeclaredParts.length > 0) {
|
|
188
|
+
const aPart = undeclaredParts.length > 1 ? 'parts' : 'a part'
|
|
189
|
+
const is = undeclaredParts.length > 1 ? 'are' : 'is'
|
|
190
|
+
const partList = undeclaredParts.map((part) => `"${part}"`).join(', ')
|
|
191
|
+
throw new Error(
|
|
192
|
+
`Invalid plugin: Source is using ${aPart} that ${is} not declared in "sanity.json":\n\n${partList}.`
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function verifyNoUnusedDependencies(modules, pkg) {
|
|
198
|
+
const potentiallyUnused = Object.keys(pkg.dependencies || {}).filter(
|
|
199
|
+
(dep) => !modules.includes(dep)
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
if (potentiallyUnused.length > 0) {
|
|
203
|
+
const unusedNames = potentiallyUnused.map((dep) => `"${dep}"`).join(', ')
|
|
204
|
+
log.warn(`Found modules listed as dependencies which seem to be unused by code: ${unusedNames}`)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function verifyReactDependencies(modules, pkg) {
|
|
209
|
+
if (modules.includes('react') && 'react' in (pkg.dependencies || {})) {
|
|
210
|
+
throw new Error(
|
|
211
|
+
`Invalid plugin: "react" declared as a dependency - it should be declared as a peerDependency (package.json)`
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (modules.includes('react-dom') && 'react-dom' in (pkg.dependencies || {})) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`Invalid plugin: "react-dom" declared as a dependency - it should be declared as a peerDependency (package.json)`
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (modules.includes('prop-types') && 'prop-types' in (pkg.peerDependencies || {})) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Invalid plugin: "prop-types" declares as peerDependency - it should be declared as a dependency (package.json)`
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function verifyUiDependencies(modules, pkg) {
|
|
229
|
+
const peerDependencies = pkg.peerDependencies || {}
|
|
230
|
+
const dependencies = pkg.dependencies || {}
|
|
231
|
+
|
|
232
|
+
if (modules.includes('@sanity/ui') && '@sanity/ui' in peerDependencies) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
`Invalid plugin: "@sanity/ui" declared as a peer dependency - it should be declared as a dependency (package.json)`
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (
|
|
239
|
+
modules.includes('@sanity/ui') &&
|
|
240
|
+
dependencies['@sanity/ui'] &&
|
|
241
|
+
semver.lt(semver.minVersion(dependencies['@sanity/ui']), '0.33.1')
|
|
242
|
+
) {
|
|
243
|
+
throw new Error(
|
|
244
|
+
`Invalid plugin: "@sanity/ui" dependency must use version higher than or equal to 0.33.1 (package.json)`
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (modules.includes('@sanity/icons') && '@sanity/icons' in peerDependencies) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
`Invalid plugin: "@sanity/icons" declared as a peer dependency - it should be declared as a dependency (package.json)`
|
|
251
|
+
)
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
async function verifyConfigParts(dependencies, pkg, basePath) {
|
|
256
|
+
const configName = `config:${pkg.name.replace(/^sanity-plugin-/, '')}`
|
|
257
|
+
if (
|
|
258
|
+
dependencies.includes(configName) &&
|
|
259
|
+
!(await fileExists(path.join(basePath, 'config.dist.json')))
|
|
260
|
+
) {
|
|
261
|
+
throw new Error(
|
|
262
|
+
`Plugin imports plugin config (${configName}) but does not contain a "config.dist.json" file.`
|
|
263
|
+
)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const nonPluginConfigs = dependencies.filter(
|
|
267
|
+
(dep) => dep.startsWith('config:') && dep !== configName
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
if (nonPluginConfigs.length > 0) {
|
|
271
|
+
const configs = nonPluginConfigs.length > 1 ? 'configs' : 'config'
|
|
272
|
+
const nonPluginConfigNames = nonPluginConfigs.join(', ')
|
|
273
|
+
log.warn(
|
|
274
|
+
`Found references to external ${configs}: ${nonPluginConfigNames} - this is generally considered unsafe`
|
|
275
|
+
)
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function verifyNoUndeclaredDependencies(modules, pkg) {
|
|
280
|
+
const undeclared = getUndeclaredDependencies(modules, pkg)
|
|
281
|
+
if (undeclared.length > 0) {
|
|
282
|
+
throw new Error(getUndeclaredDependenciesError(undeclared))
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function getUndeclaredDependencies(modules, pkg) {
|
|
287
|
+
const deps = ([] as string[]).concat(
|
|
288
|
+
Object.keys(pkg.dependencies || {}),
|
|
289
|
+
Object.keys(pkg.peerDependencies || {})
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
const devDeps = Object.keys(pkg.devDependencies || {})
|
|
293
|
+
|
|
294
|
+
return modules
|
|
295
|
+
.filter((modDep) => !deps.includes(modDep))
|
|
296
|
+
.map((modDep) => ({
|
|
297
|
+
dependency: modDep,
|
|
298
|
+
isDevDep: devDeps.includes(modDep),
|
|
299
|
+
}))
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function getUndeclaredDependenciesError(undeclared) {
|
|
303
|
+
const baseError = `Invalid plugin`
|
|
304
|
+
const moduleNames = undeclared.map((mod) => mod.dependency)
|
|
305
|
+
|
|
306
|
+
let declaredWhere = `should be declared in package.json under "dependencies" or "peerDependencies".`
|
|
307
|
+
if (moduleNames.length === 1 && moduleNames[0] === 'react') {
|
|
308
|
+
declaredWhere = `should be declared in package.json under "peerDependencies"`
|
|
309
|
+
} else if (moduleNames.includes('react')) {
|
|
310
|
+
declaredWhere = `should either be declared in package.json under "dependencies" or "peerDependencies" (react should be in "peerDependencies").`
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const devDeps = undeclared.filter((dep) => dep.isDevDep).map((dep) => ` - ${dep.dependency}\n`)
|
|
314
|
+
if (devDeps.length > 0) {
|
|
315
|
+
const modules = devDeps.length > 1 ? 'modules' : 'module'
|
|
316
|
+
const depList = devDeps.join('')
|
|
317
|
+
const target = devDeps.length > 1 ? 'They' : 'It'
|
|
318
|
+
const are = devDeps.length > 1 ? 'are' : 'is'
|
|
319
|
+
return `${baseError}: Source uses ${modules} that ${are} declared in "devDependencies":\n\n${depList}\n${target} ${declaredWhere}`
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const modules = undeclared.length > 1 ? 'modules' : 'module'
|
|
323
|
+
const depList = undeclared.map((dep) => ` - ${dep.dependency}\n`).join('')
|
|
324
|
+
const target = undeclared.length > 1 ? 'They' : 'It'
|
|
325
|
+
const are = undeclared.length > 1 ? 'are' : 'is'
|
|
326
|
+
return `${baseError}: Source uses ${modules} that ${are} not declared as dependencies:\n\n${depList}\n${target} ${declaredWhere}`
|
|
327
|
+
}
|
|
328
|
+
*/
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict'
|
|
3
|
+
import meow from 'meow'
|
|
4
|
+
import log from './util/log'
|
|
5
|
+
import commands from './cmds'
|
|
6
|
+
import sharedFlags from './sharedFlags'
|
|
7
|
+
import {cliName} from './constants'
|
|
8
|
+
|
|
9
|
+
export async function cliEntry(argv = process.argv, autoExit = true) {
|
|
10
|
+
const cli = meow(
|
|
11
|
+
`
|
|
12
|
+
Usage
|
|
13
|
+
$ ${cliName} [--help] [--debug] <command> [<args>]
|
|
14
|
+
|
|
15
|
+
These are common commands used in various situations:
|
|
16
|
+
|
|
17
|
+
init Create a new Sanity plugin
|
|
18
|
+
verify-package Check that a Sanity plugin package follows V3 conventions. Prints upgrade steps.
|
|
19
|
+
verify-studio Check that a Sanity Studio follows V3 conventions. Prints upgrade steps.
|
|
20
|
+
link-watch Recompiles plugin automatically on changes and runs yalc push --publish
|
|
21
|
+
version Show the version of ${cliName} currently installed
|
|
22
|
+
|
|
23
|
+
Options
|
|
24
|
+
--silent Do not print info and warning messages
|
|
25
|
+
--verbose Log everything. This option conflicts with --silent
|
|
26
|
+
--debug Print stack trace on errors
|
|
27
|
+
--version Output the version number
|
|
28
|
+
--help Output usage information
|
|
29
|
+
|
|
30
|
+
Examples
|
|
31
|
+
# Init a new plugin in current directory
|
|
32
|
+
$ ${cliName} init
|
|
33
|
+
|
|
34
|
+
# Init a new plugin in my-sanity-plugin directory
|
|
35
|
+
$ ${cliName} init my-sanity-plugin
|
|
36
|
+
|
|
37
|
+
# Check that a Sanity plugin package in current directory follows V3 conventions
|
|
38
|
+
$ ${cliName} verify-package
|
|
39
|
+
|
|
40
|
+
# Check that a Sanity Studio in current directory follows V3 conventions
|
|
41
|
+
$ ${cliName} verify-studio
|
|
42
|
+
`,
|
|
43
|
+
{
|
|
44
|
+
autoHelp: false,
|
|
45
|
+
flags: sharedFlags,
|
|
46
|
+
argv: argv.slice(2),
|
|
47
|
+
}
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const commandName = cli.input[0]
|
|
51
|
+
if (!commandName) {
|
|
52
|
+
cli.showHelp() // Exits
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!(commandName in commands)) {
|
|
56
|
+
console.error(`Unknown command "${commandName}"`)
|
|
57
|
+
cli.showHelp() // Exits
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (cli.flags.silent && cli.flags.verbose) {
|
|
61
|
+
log.error(`--silent and --verbose are mutually exclusive`)
|
|
62
|
+
cli.showHelp() // Exits
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Lazy-load command
|
|
66
|
+
const cmd = require(commands[commandName as keyof typeof commands]).default
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
log.setVerbosity(cli.flags)
|
|
70
|
+
await cmd({argv: argv.slice(3)})
|
|
71
|
+
} catch (err: any) {
|
|
72
|
+
log.error(err instanceof TypeError || cli.flags.debug ? err.stack : err.message)
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line no-process-exit
|
|
75
|
+
process.exit(1)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
init: require.resolve('./init'),
|
|
3
|
+
'link-watch': require.resolve('./link-watch'),
|
|
4
|
+
'verify-package': require.resolve('./verify-package'),
|
|
5
|
+
'verify-studio': require.resolve('./verify-studio'),
|
|
6
|
+
version: require.resolve('./version'),
|
|
7
|
+
// wont make it for initial release
|
|
8
|
+
//splat: require.resolve('./splat'),
|
|
9
|
+
}
|
package/src/cmds/init.ts
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import meow from 'meow'
|
|
3
|
+
import log from '../util/log'
|
|
4
|
+
import {init, initFlags} from '../actions/init'
|
|
5
|
+
import {isEmptyish, ensureDir} from '../util/files'
|
|
6
|
+
import {installDependencies, promptForPackageManager} from '../npm/manager'
|
|
7
|
+
import {findStudioV3Config, hasSanityJson} from '../sanity/manifest'
|
|
8
|
+
import {prompt} from '../util/prompt'
|
|
9
|
+
import {cliName} from '../constants'
|
|
10
|
+
|
|
11
|
+
const description = `Initialize a new Sanity plugin`
|
|
12
|
+
|
|
13
|
+
const help = `
|
|
14
|
+
Usage
|
|
15
|
+
$ ${cliName} init [dir] [<args>]
|
|
16
|
+
|
|
17
|
+
Options
|
|
18
|
+
--no-eslint Disables ESLint config and dependencies from being added
|
|
19
|
+
--no-prettier Disables prettier config and dependencies from being added
|
|
20
|
+
--no-typescript Disables typescript config and dependencies from being added
|
|
21
|
+
--no-license Disables LICENSE + package.json license field from being added
|
|
22
|
+
--no-editorconfig Disables .editorconfig from being added
|
|
23
|
+
--no-gitignore Disables .gitignore from being added
|
|
24
|
+
--no-scripts Disables scripts from being added to package.json
|
|
25
|
+
--no-install Disables automatically running package manager install
|
|
26
|
+
|
|
27
|
+
--name [package-name] Use the provided package-name
|
|
28
|
+
--author [name] Use the provided author
|
|
29
|
+
--repo [url] Use the provided repo url
|
|
30
|
+
--license [spdx] Use the license with the given SPDX identifier
|
|
31
|
+
--force No promt when overwriting files
|
|
32
|
+
|
|
33
|
+
Examples
|
|
34
|
+
# Initialize a new plugin in the current directory
|
|
35
|
+
$ ${cliName} init
|
|
36
|
+
|
|
37
|
+
# Initialize a plugin in the directory ~/my-plugin
|
|
38
|
+
$ ${cliName} init ~/my-plugin
|
|
39
|
+
|
|
40
|
+
# Don't add eslint or prettier
|
|
41
|
+
$ ${cliName} init --no-eslint --no-prettier
|
|
42
|
+
`
|
|
43
|
+
|
|
44
|
+
async function run({argv}: {argv: string[]}) {
|
|
45
|
+
const cli = meow(help, {flags: initFlags, argv, description})
|
|
46
|
+
const basePath = path.resolve(cli.input[0] || process.cwd())
|
|
47
|
+
|
|
48
|
+
const {exists, isRoot} = await hasSanityJson(basePath)
|
|
49
|
+
if (exists && isRoot) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`sanity.json has a "root" property set to true - are you trying to init into a studio instead of a plugin?`
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const {v3ConfigFile} = await findStudioV3Config(basePath)
|
|
56
|
+
if (v3ConfigFile) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`${v3ConfigFile} exsists - are you trying to init into a studio instead of a plugin?`
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
log.info('Initializing new plugin in "%s"', basePath)
|
|
63
|
+
if (
|
|
64
|
+
!cli.flags.force &&
|
|
65
|
+
!(await isEmptyish(basePath)) &&
|
|
66
|
+
!(await prompt('Directory is not empty, proceed?', {type: 'confirm', default: false}))
|
|
67
|
+
) {
|
|
68
|
+
log.error('Directory is not empty. Cancelled.')
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await ensureDir(basePath)
|
|
73
|
+
await init({basePath, flags: cli.flags})
|
|
74
|
+
if (cli.flags.install) {
|
|
75
|
+
if (await installDependencies(await promptForPackageManager(), {cwd: basePath})) {
|
|
76
|
+
log.info('Done!')
|
|
77
|
+
} else {
|
|
78
|
+
log.error('Failed to install dependencies, try manually running `npm install`')
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
log.info('Dependency installation skipped.')
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default run
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import meow from 'meow'
|
|
3
|
+
import pkg from '../../package.json'
|
|
4
|
+
import sharedFlags from '../sharedFlags'
|
|
5
|
+
import {linkWatch} from '../actions/link-watch'
|
|
6
|
+
|
|
7
|
+
const description = `Run the watch command and pushes any changes to yalc`
|
|
8
|
+
|
|
9
|
+
const help = `
|
|
10
|
+
Usage
|
|
11
|
+
$ ${pkg.binname} link-watch [<args>]
|
|
12
|
+
|
|
13
|
+
Options
|
|
14
|
+
--silent Do not print info and warning messages
|
|
15
|
+
--verbose Log everything. This option conflicts with --silent
|
|
16
|
+
--version Output the version number
|
|
17
|
+
--help Output usage information
|
|
18
|
+
|
|
19
|
+
Configuration
|
|
20
|
+
To override the default watch command configuration, provide an override in package.json under sanityPlugin:
|
|
21
|
+
{
|
|
22
|
+
"sanityPlugin": {
|
|
23
|
+
"watchCommand": "microbundle watch --format modern,esm,cjs --jsx React.createElement --jsxImportSource react --css inline",
|
|
24
|
+
"linkWatch": {
|
|
25
|
+
"folder": "lib",
|
|
26
|
+
"command": "npm run watch",
|
|
27
|
+
"extensions": "js,png,svg,gif,jpeg,css"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Examples
|
|
33
|
+
# Run the watch command and pushes any changes to yalc
|
|
34
|
+
$ ${pkg.binname} link-watch
|
|
35
|
+
`
|
|
36
|
+
|
|
37
|
+
const flags = {
|
|
38
|
+
...sharedFlags,
|
|
39
|
+
watch: {
|
|
40
|
+
type: 'boolean',
|
|
41
|
+
default: false,
|
|
42
|
+
},
|
|
43
|
+
} as const
|
|
44
|
+
|
|
45
|
+
function run({argv}: {argv: string[]}) {
|
|
46
|
+
const cli = meow(help, {flags, argv, description})
|
|
47
|
+
const basePath = path.resolve(cli.input[0] || process.cwd())
|
|
48
|
+
return linkWatch({basePath})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default run
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import meow from 'meow'
|
|
3
|
+
import pkg from '../../package.json'
|
|
4
|
+
import log from '../util/log'
|
|
5
|
+
import {splat} from '../actions/splat'
|
|
6
|
+
import {hasSanityJson} from '../sanity/manifest'
|
|
7
|
+
import sharedFlags from '../sharedFlags'
|
|
8
|
+
import {initFlags} from '../actions/init'
|
|
9
|
+
|
|
10
|
+
const description = `"Splat" configuration into a Sanity plugin`
|
|
11
|
+
|
|
12
|
+
const help = `
|
|
13
|
+
Usage
|
|
14
|
+
$ ${pkg.binname} splat [dir] [<args>]
|
|
15
|
+
|
|
16
|
+
Options
|
|
17
|
+
--no-eslint Disables ESLint config and dependencies from being added
|
|
18
|
+
--no-prettier Disables prettier config and dependencies from being added
|
|
19
|
+
--no-typescript Disables typescript config and dependencies from being added
|
|
20
|
+
--no-license Disables LICENSE + package.json license field from being added
|
|
21
|
+
--no-editorconfig Disables .editorconfig from being added
|
|
22
|
+
--no-gitignore Disables .gitignore from being added
|
|
23
|
+
--no-scripts Disables scripts from being added to package.json
|
|
24
|
+
--license [spdx] Use the license with the given SPDX identifier
|
|
25
|
+
|
|
26
|
+
Examples
|
|
27
|
+
# Splat configuration into the plugin in the current directory
|
|
28
|
+
$ ${pkg.binname} splat
|
|
29
|
+
|
|
30
|
+
# Splat configuration into the plugin in ~/my-plugin
|
|
31
|
+
$ ${pkg.binname} splat ~/my-plugin
|
|
32
|
+
|
|
33
|
+
# Don't add eslint or prettier
|
|
34
|
+
$ ${pkg.binname} splat --no-eslint --no-prettier
|
|
35
|
+
`
|
|
36
|
+
|
|
37
|
+
async function run({argv}: {argv: string[]}) {
|
|
38
|
+
const cli = meow(help, {flags: initFlags, argv, description})
|
|
39
|
+
const basePath = path.resolve(cli.input[0] || process.cwd())
|
|
40
|
+
|
|
41
|
+
const {exists, isRoot} = await hasSanityJson(basePath)
|
|
42
|
+
if (exists && isRoot) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`sanity.json has a "root" property set to true - are you trying to splat into a studio instead of a plugin?`
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!exists) {
|
|
49
|
+
throw new Error(
|
|
50
|
+
`sanity.json does not exist in this directory, maybe you want "${pkg.binname} init" instead?`
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
await splat({basePath, flags: cli.flags})
|
|
55
|
+
|
|
56
|
+
log.info('Done!')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export default run
|