@sanity/plugin-kit 6.0.3 → 7.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/dist/_chunks-es/constants.js +12 -0
- package/dist/_chunks-es/constants.js.map +1 -0
- package/dist/_chunks-es/init.js +863 -81
- package/dist/_chunks-es/init.js.map +1 -1
- package/dist/_chunks-es/init2.js +97 -837
- package/dist/_chunks-es/init2.js.map +1 -1
- package/dist/_chunks-es/inject.js +22 -16
- package/dist/_chunks-es/inject.js.map +1 -1
- package/dist/_chunks-es/link-watch.js +51 -49
- package/dist/_chunks-es/link-watch.js.map +1 -1
- package/dist/_chunks-es/load-package-config.js +24 -15
- package/dist/_chunks-es/load-package-config.js.map +1 -1
- package/dist/_chunks-es/log.js +31 -0
- package/dist/_chunks-es/log.js.map +1 -0
- package/dist/_chunks-es/package.js +615 -771
- package/dist/_chunks-es/package.js.map +1 -1
- package/dist/_chunks-es/package2.js +4 -9
- package/dist/_chunks-es/package2.js.map +1 -1
- package/dist/_chunks-es/verify-common.js +102 -126
- package/dist/_chunks-es/verify-common.js.map +1 -1
- package/dist/_chunks-es/verify-package.js +46 -52
- package/dist/_chunks-es/verify-package.js.map +1 -1
- package/dist/_chunks-es/verify-studio.js +31 -27
- package/dist/_chunks-es/verify-studio.js.map +1 -1
- package/dist/_chunks-es/version.js +40 -36
- package/dist/_chunks-es/version.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +72 -4
- package/dist/index.js.map +1 -1
- package/package.json +15 -13
- package/dist/_chunks-es/index.js +0 -125
- package/dist/_chunks-es/index.js.map +0 -1
|
@@ -1,210 +1,210 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import githubUrlToObject from "github-url-to-object";
|
|
5
|
-
import validateNpmPackageName from "validate-npm-package-name";
|
|
1
|
+
import { a as urls, i as requiredNodeEngine, r as incompatiblePluginPackage, t as cliName } from "./constants.js";
|
|
2
|
+
import { t as log_default } from "./log.js";
|
|
3
|
+
import { t as binname } from "./package2.js";
|
|
6
4
|
import { createRequire } from "node:module";
|
|
7
5
|
import chalk from "chalk";
|
|
6
|
+
import path from "path";
|
|
8
7
|
import outdent from "outdent";
|
|
9
|
-
import
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import util from "util";
|
|
10
|
+
import githubUrlToObject from "github-url-to-object";
|
|
11
|
+
import validNpmName from "validate-npm-package-name";
|
|
10
12
|
import crypto from "crypto";
|
|
11
13
|
import json5 from "json5";
|
|
12
14
|
import pAny from "p-any";
|
|
13
15
|
import { URL } from "url";
|
|
14
16
|
import inquirer from "inquirer";
|
|
15
|
-
import { pkg } from "./package2.js";
|
|
16
17
|
import { getLatestVersion } from "get-latest-version";
|
|
17
18
|
import pProps from "p-props";
|
|
18
19
|
const mergedPackages = [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
"@sanity/base",
|
|
21
|
+
"@sanity/core",
|
|
22
|
+
"@sanity/types",
|
|
23
|
+
"@sanity/data-aspects",
|
|
24
|
+
"@sanity/default-layout",
|
|
25
|
+
"@sanity/default-login",
|
|
26
|
+
"@sanity/desk-tool",
|
|
27
|
+
"@sanity/field",
|
|
28
|
+
"@sanity/form-builder",
|
|
29
|
+
"@sanity/initial-value-templates",
|
|
30
|
+
"@sanity/language-filter",
|
|
31
|
+
"@sanity/production-preview",
|
|
32
|
+
"@sanity/react-hooks",
|
|
33
|
+
"@sanity/resolver",
|
|
34
|
+
"@sanity/state-router",
|
|
35
|
+
"@sanity/structure",
|
|
36
|
+
"@sanity/studio-hints"
|
|
36
37
|
].sort(), deprecatedDevDeps = [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
], buildExtensions = [
|
|
38
|
+
"tsdx",
|
|
39
|
+
"sanipack",
|
|
40
|
+
"parcel",
|
|
41
|
+
"@parcel/packager-ts",
|
|
42
|
+
"@parcel/transformer-typescript-types"
|
|
43
|
+
], buildExtensions = [
|
|
44
|
+
".js",
|
|
45
|
+
".jsx",
|
|
46
|
+
".es6",
|
|
47
|
+
".es",
|
|
48
|
+
".mjs",
|
|
49
|
+
".ts",
|
|
50
|
+
".tsx"
|
|
51
|
+
];
|
|
43
52
|
async function prompt(message, options) {
|
|
44
|
-
|
|
45
|
-
|
|
53
|
+
let type = options.choices ? "list" : options.type, result = await inquirer.prompt([{
|
|
54
|
+
...options,
|
|
55
|
+
type,
|
|
56
|
+
message,
|
|
57
|
+
name: "single"
|
|
58
|
+
}]);
|
|
59
|
+
return result && result.single;
|
|
46
60
|
}
|
|
47
61
|
prompt.separator = () => new inquirer.Separator();
|
|
48
62
|
function promptForPackageName({ basePath }, defaultVal) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
return prompt("Plugin name (sanity-plugin-...)", {
|
|
64
|
+
default: defaultVal || path.basename(basePath),
|
|
65
|
+
filter: (name) => {
|
|
66
|
+
let prefixless = name.trim().replace(/^sanity-plugin-/, "");
|
|
67
|
+
return name[0] === "@" ? name : `sanity-plugin-${prefixless}`;
|
|
68
|
+
},
|
|
69
|
+
validate: (name) => {
|
|
70
|
+
let valid = validNpmName(name);
|
|
71
|
+
return valid.errors ? valid.errors[0] : name[0] !== "@" && name.endsWith("plugin") ? `Name shouldn't include "plugin" multiple times (${name})` : !0;
|
|
72
|
+
}
|
|
73
|
+
});
|
|
60
74
|
}
|
|
61
75
|
function promptForRepoOrigin(_options, defaultVal) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
});
|
|
76
|
+
return prompt("Git repository URL", {
|
|
77
|
+
default: defaultVal,
|
|
78
|
+
filter: (raw) => {
|
|
79
|
+
let url = (raw || "").trim(), gh = githubUrlToObject(url);
|
|
80
|
+
return gh ? `git+ssh://git@github.com/${gh.user}/${gh.repo}.git` : url;
|
|
81
|
+
},
|
|
82
|
+
validate: (url) => {
|
|
83
|
+
if (!url) return !0;
|
|
84
|
+
try {
|
|
85
|
+
return new URL(url), !0;
|
|
86
|
+
} catch {
|
|
87
|
+
return "Invalid URL";
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
78
91
|
}
|
|
79
92
|
const stat$1 = util.promisify(fs.stat), mkdir = util.promisify(fs.mkdir), readdir = util.promisify(fs.readdir), copyFile = util.promisify(fs.copyFile), readFile$2 = util.promisify(fs.readFile), writeFile = util.promisify(fs.writeFile);
|
|
80
93
|
function hasSourceEquivalent(compiledFile, paths) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
);
|
|
85
|
-
const baseDir = path.dirname(compiledFile.replace(paths.compiled, paths.source)), baseName = path.basename(compiledFile, path.extname(compiledFile)), pathStub = path.join(baseDir, baseName);
|
|
86
|
-
return buildCandidateExists(pathStub);
|
|
94
|
+
if (!paths.source) return fileExists(path.isAbsolute(compiledFile) ? compiledFile : path.resolve(paths.basePath, compiledFile));
|
|
95
|
+
let baseDir = path.dirname(compiledFile.replace(paths.compiled, paths.source)), baseName = path.basename(compiledFile, path.extname(compiledFile));
|
|
96
|
+
return buildCandidateExists(path.join(baseDir, baseName));
|
|
87
97
|
}
|
|
88
98
|
async function hasSourceFile(filePath, paths) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
);
|
|
93
|
-
const pathStub = path.isAbsolute(filePath) ? filePath : path.resolve(paths.source, filePath);
|
|
94
|
-
return await fileExists(pathStub) ? !0 : buildCandidateExists(pathStub);
|
|
99
|
+
if (!paths?.source) return fileExists(path.isAbsolute(filePath) ? filePath : path.resolve(paths?.basePath ?? "", filePath));
|
|
100
|
+
let pathStub = path.isAbsolute(filePath) ? filePath : path.resolve(paths.source, filePath);
|
|
101
|
+
return await fileExists(pathStub) ? !0 : buildCandidateExists(pathStub);
|
|
95
102
|
}
|
|
96
103
|
function hasCompiledFile(filePath, paths) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
);
|
|
101
|
-
const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(paths.compiled, filePath), withExt = path.extname(absPath) === "" ? `${absPath}.js` : absPath;
|
|
102
|
-
return fileExists(withExt);
|
|
104
|
+
if (!paths?.compiled) return fileExists(path.isAbsolute(filePath) ? filePath : path.resolve(paths?.basePath ?? "", filePath));
|
|
105
|
+
let absPath = path.isAbsolute(filePath) ? filePath : path.resolve(paths.compiled, filePath);
|
|
106
|
+
return fileExists(path.extname(absPath) === "" ? `${absPath}.js` : absPath);
|
|
103
107
|
}
|
|
104
108
|
function buildCandidateExists(pathStub) {
|
|
105
|
-
|
|
106
|
-
return pAny(candidates.map((candidate) => stat$1(candidate))).then(() => !0).catch(() => !1);
|
|
109
|
+
return pAny(buildExtensions.map((extCandidate) => `${pathStub}${extCandidate}`).map((candidate) => stat$1(candidate))).then(() => !0).catch(() => !1);
|
|
107
110
|
}
|
|
108
111
|
function fileExists(filePath) {
|
|
109
|
-
|
|
112
|
+
return stat$1(filePath).then(() => !0).catch(() => !1);
|
|
110
113
|
}
|
|
111
114
|
async function readJsonFile(filePath) {
|
|
112
|
-
|
|
113
|
-
|
|
115
|
+
let content = await readFile$2(filePath, "utf8");
|
|
116
|
+
return JSON.parse(content);
|
|
114
117
|
}
|
|
115
118
|
function writeJsonFile(filePath, content) {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return writeFile(filePath, data, { encoding: "utf8" });
|
|
119
|
+
let data = JSON.stringify(content, null, 2) + "\n";
|
|
120
|
+
return writeFile(filePath, data, { encoding: "utf8" });
|
|
119
121
|
}
|
|
120
122
|
async function writeFileWithOverwritePrompt(filePath, content, options) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
123
|
+
let { default: defaultVal, force = !1, ...writeOptions } = options, printablePath = filePath.startsWith(process.cwd()) ? path.relative(process.cwd(), filePath) : filePath;
|
|
124
|
+
return await fileEqualsData(filePath, content) || !force && await fileExists(filePath) && !await prompt(`File "${printablePath}" already exists. Overwrite?`, {
|
|
125
|
+
type: "confirm",
|
|
126
|
+
default: defaultVal
|
|
127
|
+
}) ? !1 : (await writeFile(filePath, content, writeOptions), !0);
|
|
126
128
|
}
|
|
127
129
|
async function copyFileWithOverwritePrompt(from, to, flags) {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
130
|
+
let printablePath = to.startsWith(process.cwd()) ? path.relative(process.cwd(), to) : to;
|
|
131
|
+
return await filesAreEqual(from, to) || (await ensureDirectoryExists(to), !flags.force && await fileExists(to) && !await prompt(`File "${printablePath}" already exists. Overwrite?`, {
|
|
132
|
+
type: "confirm",
|
|
133
|
+
default: !1
|
|
134
|
+
})) ? !1 : (await copyFile(from, to), !0);
|
|
133
135
|
}
|
|
134
136
|
async function ensureDirectoryExists(filePath) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
+
let dirname = path.dirname(filePath);
|
|
138
|
+
await fileExists(dirname) || (await ensureDirectoryExists(dirname), await mkdir(dirname));
|
|
137
139
|
}
|
|
138
140
|
async function fileEqualsData(filePath, content) {
|
|
139
|
-
|
|
140
|
-
return contentHash === remoteHash;
|
|
141
|
+
return crypto.createHash("sha1").update(content).digest("hex") === await getFileHash(filePath);
|
|
141
142
|
}
|
|
142
143
|
async function filesAreEqual(file1, file2) {
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
let [hash1, hash2] = await Promise.all([getFileHash(file1, !1), getFileHash(file2)]);
|
|
145
|
+
return hash1 === hash2;
|
|
145
146
|
}
|
|
146
147
|
function getFileHash(filePath, allowMissing = !0) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
148
|
+
return new Promise((resolve, reject) => {
|
|
149
|
+
let hash = crypto.createHash("sha1"), stream = fs.createReadStream(filePath);
|
|
150
|
+
stream.on("error", (err) => {
|
|
151
|
+
err.code === "ENOENT" && allowMissing ? resolve(null) : reject(err);
|
|
152
|
+
}), stream.on("end", () => resolve(hash.digest("hex"))), stream.on("data", (chunk) => hash.update(chunk));
|
|
153
|
+
});
|
|
153
154
|
}
|
|
154
155
|
async function ensureDir(dirPath) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
156
|
+
try {
|
|
157
|
+
await mkdir(dirPath);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
if (err.code !== "EEXIST") throw err;
|
|
160
|
+
}
|
|
161
161
|
}
|
|
162
162
|
async function isEmptyish(dirPath) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
163
|
+
let ignoredFiles = [
|
|
164
|
+
".git",
|
|
165
|
+
".gitignore",
|
|
166
|
+
"license",
|
|
167
|
+
"readme.md"
|
|
168
|
+
];
|
|
169
|
+
return (await readdir(dirPath).catch(() => [])).filter((file) => !ignoredFiles.includes(file.toLowerCase())).length === 0;
|
|
170
|
+
}
|
|
171
|
+
async function readFileContent({ filename, basePath }) {
|
|
172
|
+
let filepath = path.normalize(path.join(basePath, filename));
|
|
173
|
+
try {
|
|
174
|
+
return await readFile$2(filepath, "utf8");
|
|
175
|
+
} catch (err) {
|
|
176
|
+
if (err.code === "ENOENT") {
|
|
177
|
+
log_default.debug(`No ${filename} file found.`);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
throw Error(`Failed to read "${filepath}": ${err.message}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function readJson5File({ filename, basePath }) {
|
|
184
|
+
let content = await readFileContent({
|
|
185
|
+
filename,
|
|
186
|
+
basePath
|
|
187
|
+
});
|
|
188
|
+
if (content) return parseJson5(content, filename);
|
|
188
189
|
}
|
|
189
190
|
function parseJson5(content, errorKey) {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
try {
|
|
192
|
+
return json5.parse(content);
|
|
193
|
+
} catch (err) {
|
|
194
|
+
throw Error(`Error parsing "${errorKey}": ${err.message}`);
|
|
195
|
+
}
|
|
195
196
|
}
|
|
196
197
|
const expectedScripts = {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
build: "plugin-kit verify-package --silent && pkg-utils build --strict --check --clean",
|
|
199
|
+
watch: "pkg-utils watch --strict",
|
|
200
|
+
"link-watch": "plugin-kit link-watch",
|
|
201
|
+
prepublishOnly: "npm run build"
|
|
201
202
|
};
|
|
202
203
|
function filesWithSuffixes(fileBases, suffixes) {
|
|
203
|
-
|
|
204
|
+
return fileBases.flatMap((file) => suffixes.map((suffix) => `${file}.${suffix}`));
|
|
204
205
|
}
|
|
205
206
|
function validateNodeEngine(packageJson) {
|
|
206
|
-
|
|
207
|
-
outdent`
|
|
207
|
+
return packageJson.engines?.node === ">=20.19 <22 || >=22.12" ? [] : [outdent`
|
|
208
208
|
Expected package.json to contain engines.node: "${requiredNodeEngine}" to match @sanity/pkg-utils,
|
|
209
209
|
but it was: ${packageJson.engines?.node}
|
|
210
210
|
|
|
@@ -212,44 +212,38 @@ function validateNodeEngine(packageJson) {
|
|
|
212
212
|
|
|
213
213
|
"engines": {
|
|
214
214
|
"node": "${requiredNodeEngine}"
|
|
215
|
-
}`.trimStart()
|
|
216
|
-
] : [];
|
|
215
|
+
}`.trimStart()];
|
|
217
216
|
}
|
|
218
217
|
function validateScripts(packageJson) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
outdent`
|
|
218
|
+
let errors = [], divergentScripts = Object.entries(expectedScripts).filter(([key, expectedCommand]) => {
|
|
219
|
+
let command = packageJson.scripts?.[key];
|
|
220
|
+
return !command || !command.includes(expectedCommand);
|
|
221
|
+
});
|
|
222
|
+
return divergentScripts.length && errors.push(outdent`
|
|
225
223
|
The following script commands did not contain expected defaults: ${divergentScripts.map(([key]) => key).join(", ")}
|
|
226
224
|
|
|
227
225
|
This checks for that the commands-strings includes these terms.
|
|
228
226
|
|
|
229
227
|
Please add the following to your package.json "scripts":
|
|
230
228
|
|
|
231
|
-
${divergentScripts.map(([key, value]) => `"${key}": "${value}"`).join(
|
|
232
|
-
|
|
233
|
-
`.trimStart()
|
|
234
|
-
), errors;
|
|
229
|
+
${divergentScripts.map(([key, value]) => `"${key}": "${value}"`).join(",\n")}
|
|
230
|
+
`.trimStart()), errors;
|
|
235
231
|
}
|
|
236
232
|
async function validateTsConfig(ts, options) {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
`
|
|
251
|
-
errors.push(
|
|
252
|
-
outdent`
|
|
233
|
+
let { basePath, outDir, tsconfig } = options, errors = [], wrongEntries = Object.entries({
|
|
234
|
+
target: "esnext",
|
|
235
|
+
jsx: "preserve",
|
|
236
|
+
module: "preserve",
|
|
237
|
+
rootDir: ".",
|
|
238
|
+
outDir,
|
|
239
|
+
noEmit: !0
|
|
240
|
+
}).filter(([key, value]) => {
|
|
241
|
+
let option = ts.options[key];
|
|
242
|
+
return key === "rootDir" && typeof option == "string" && (option = path.relative(basePath, option) || "."), key === "outDir" && typeof option == "string" && (option = path.relative(basePath, option) || "."), key === "target" && option === 99 && (option = "esnext"), key === "module" && option === 200 && (option = "preserve"), key === "jsx" && option === 1 && (option = "preserve"), typeof value == "string" && typeof option == "string" ? value.toLowerCase() !== option?.toLowerCase() : value !== option;
|
|
243
|
+
});
|
|
244
|
+
if (wrongEntries.length) {
|
|
245
|
+
let expectedOutput = wrongEntries.map(([key, value]) => `"${key}": ${typeof value == "string" ? `"${value}"` : value},`).join("\n");
|
|
246
|
+
errors.push(outdent`
|
|
253
247
|
Recommended ${tsconfig} compilerOptions missing:
|
|
254
248
|
|
|
255
249
|
The following fields had unexpected values: [${wrongEntries.map(([key]) => key).join(", ")}]
|
|
@@ -257,51 +251,63 @@ async function validateTsConfig(ts, options) {
|
|
|
257
251
|
${expectedOutput}
|
|
258
252
|
|
|
259
253
|
Please update your ${tsconfig} accordingly.
|
|
260
|
-
`.trimStart()
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
254
|
+
`.trimStart());
|
|
255
|
+
}
|
|
256
|
+
return errors;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Hard requirement: plugins must be ESM (`"type": "module"`).
|
|
260
|
+
*
|
|
261
|
+
* plugin-kit loads `package.config.ts` through `@sanity/pkg-utils`, which can only load ESM
|
|
262
|
+
* TypeScript configs reliably when the plugin itself is ESM. CommonJS (or an omitted `type`)
|
|
263
|
+
* is not supported and cannot be opted out of.
|
|
264
|
+
*/
|
|
265
265
|
function validatePackageType({ type }) {
|
|
266
|
-
|
|
267
|
-
outdent`
|
|
266
|
+
return type === "module" ? [] : [outdent`
|
|
268
267
|
package.json must set "type": "module" — plugins built with @sanity/plugin-kit are ESM-only.
|
|
269
|
-
Found: ${type ? `"type": "${type}"` :
|
|
268
|
+
Found: ${type ? `"type": "${type}"` : "no \"type\" field (defaults to \"commonjs\")"}
|
|
270
269
|
|
|
271
270
|
Please add the following to package.json:
|
|
272
271
|
|
|
273
272
|
"type": "module"
|
|
274
|
-
`.trimStart()
|
|
275
|
-
|
|
276
|
-
|
|
273
|
+
`.trimStart()];
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Recursively collects the locations of any `require` condition within a package.json `exports`
|
|
277
|
+
* field. Conditions can be nested arbitrarily deep (and inside fallback arrays), so we walk the
|
|
278
|
+
* whole tree rather than only inspecting the first level.
|
|
279
|
+
*
|
|
280
|
+
* Subpath keys always start with `.` (e.g. `"./feature"`), while condition keys never do, so an
|
|
281
|
+
* exact `require` key is unambiguously a CommonJS export condition.
|
|
282
|
+
*/
|
|
277
283
|
function findRequireConditions(node, pathSegments) {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
return [];
|
|
284
|
-
const found = [];
|
|
285
|
-
for (const [key, value] of Object.entries(node))
|
|
286
|
-
key === "require" && found.push(formatExportsPath(pathSegments)), found.push(...findRequireConditions(value, [...pathSegments, key]));
|
|
287
|
-
return found;
|
|
284
|
+
if (Array.isArray(node)) return node.flatMap((entry, index) => findRequireConditions(entry, [...pathSegments, String(index)]));
|
|
285
|
+
if (!node || typeof node != "object") return [];
|
|
286
|
+
let found = [];
|
|
287
|
+
for (let [key, value] of Object.entries(node)) key === "require" && found.push(formatExportsPath(pathSegments)), found.push(...findRequireConditions(value, [...pathSegments, key]));
|
|
288
|
+
return found;
|
|
288
289
|
}
|
|
289
290
|
function formatExportsPath(segments) {
|
|
290
|
-
|
|
291
|
-
}
|
|
291
|
+
return `exports${segments.map((segment) => `[${JSON.stringify(segment)}]`).join("")}`;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Bans CommonJS interop in package.json. The plugin baseline is Sanity Studio v5 or later, which is
|
|
295
|
+
* pure ESM, so there is no reason to publish a parallel CJS build anymore. This flags:
|
|
296
|
+
*
|
|
297
|
+
* - `require` export conditions
|
|
298
|
+
* - the top-level `main` field
|
|
299
|
+
* - the top-level `module` field
|
|
300
|
+
*/
|
|
292
301
|
function validateEsmOnly(packageJson) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
return offenders.length ? [
|
|
299
|
-
outdent`
|
|
302
|
+
let offenders = [];
|
|
303
|
+
packageJson.main !== void 0 && offenders.push(`- the top-level "main" field (${JSON.stringify(packageJson.main)})`), packageJson.module !== void 0 && offenders.push(`- the top-level "module" field (${JSON.stringify(packageJson.module)})`);
|
|
304
|
+
let requireConditions = [...new Set(findRequireConditions(packageJson.exports, []))];
|
|
305
|
+
for (let conditionPath of requireConditions) offenders.push(`- a "require" export condition at ${conditionPath}`);
|
|
306
|
+
return offenders.length ? [outdent`
|
|
300
307
|
package.json ships CommonJS (CJS) output, but Sanity plugins target Sanity Studio v5+, which is pure ESM.
|
|
301
308
|
|
|
302
309
|
Remove the following so the package stays ESM-only:
|
|
303
|
-
${offenders.join(
|
|
304
|
-
`)}
|
|
310
|
+
${offenders.join("\n")}
|
|
305
311
|
|
|
306
312
|
Supporting CJS is not worth it:
|
|
307
313
|
- It can have unintended side-effects.
|
|
@@ -311,119 +317,111 @@ function validateEsmOnly(packageJson) {
|
|
|
311
317
|
in the module tree, bloating bundles and slowing down builds.
|
|
312
318
|
|
|
313
319
|
Rely on "exports" together with "type": "module", and drop "main", "module" and any "require" conditions.
|
|
314
|
-
`.trimStart()
|
|
315
|
-
] : [];
|
|
320
|
+
`.trimStart()] : [];
|
|
316
321
|
}
|
|
317
322
|
function validatePkgUtilsDependency({ devDependencies }) {
|
|
318
|
-
|
|
319
|
-
outdent`
|
|
323
|
+
return devDependencies?.["@sanity/pkg-utils"] ? [] : [outdent`
|
|
320
324
|
package.json does not list @sanity/pkg-utils as a devDependency.
|
|
321
325
|
@sanity/pkg-utils replaced parcel as the recommended build tool in @sanity/plugin-kit 2.0.0
|
|
322
326
|
|
|
323
327
|
Please add it by running 'npm install --save-dev @sanity/pkg-utils'.
|
|
324
|
-
`.trimStart()
|
|
325
|
-
];
|
|
328
|
+
`.trimStart()];
|
|
326
329
|
}
|
|
330
|
+
/**
|
|
331
|
+
* Verifies that the installed `@sanity/pkg-utils` (the peer dependency plugin-kit loads
|
|
332
|
+
* `package.config.ts` with) is recent enough to expose the `loadConfig({cwd, pkgPath})` API.
|
|
333
|
+
*/
|
|
327
334
|
function validatePkgUtilsVersion({ basePath }) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
return [
|
|
334
|
-
outdent`
|
|
335
|
+
let require = createRequire(path.join(basePath, "package.json")), installedVersion;
|
|
336
|
+
try {
|
|
337
|
+
installedVersion = require("@sanity/pkg-utils/package.json").version;
|
|
338
|
+
} catch {
|
|
339
|
+
return [outdent`
|
|
335
340
|
@sanity/pkg-utils is not installed.
|
|
336
341
|
plugin-kit loads package.config.ts through @sanity/pkg-utils (a peer dependency).
|
|
337
342
|
|
|
338
343
|
Please install it by running 'npm install --save-dev @sanity/pkg-utils'.
|
|
339
|
-
`.trimStart()
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
return !Number.isFinite(major) || major < minPkgUtilsMajor ? [
|
|
344
|
-
outdent`
|
|
344
|
+
`.trimStart()];
|
|
345
|
+
}
|
|
346
|
+
let major = Number.parseInt(installedVersion?.split(".")[0] ?? "", 10);
|
|
347
|
+
return !Number.isFinite(major) || major < 10 ? [outdent`
|
|
345
348
|
@sanity/pkg-utils ${installedVersion} is too old.
|
|
346
|
-
plugin-kit requires @sanity/pkg-utils >=${
|
|
349
|
+
plugin-kit requires @sanity/pkg-utils >=${10} to load package.config.ts.
|
|
347
350
|
|
|
348
351
|
Please upgrade it by running 'npm install --save-dev @sanity/pkg-utils@latest'.
|
|
349
|
-
`.trimStart()
|
|
350
|
-
] : [];
|
|
352
|
+
`.trimStart()] : [];
|
|
351
353
|
}
|
|
352
354
|
function validateSanityDependencies(packageJson) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
355
|
+
let { dependencies, devDependencies, peerDependencies } = packageJson, allDependencies = {
|
|
356
|
+
...dependencies,
|
|
357
|
+
...devDependencies,
|
|
358
|
+
...peerDependencies
|
|
359
|
+
}, illegalDeps = Object.keys(allDependencies).filter((dep) => mergedPackages.includes(dep)), unique = [...new Set(illegalDeps).values()];
|
|
360
|
+
return unique.length ? [outdent`
|
|
356
361
|
package.json depends on "@sanity/*" packages that have moved into "sanity" package.
|
|
357
362
|
|
|
358
363
|
The following dependencies should be replaced with "sanity":
|
|
359
|
-
- ${unique.join(
|
|
360
|
-
- `)}
|
|
364
|
+
- ${unique.join("\n- ")}
|
|
361
365
|
|
|
362
366
|
Refer to the reference docs to find replacement imports:
|
|
363
367
|
${urls.refDocs}
|
|
364
|
-
`.trimStart()
|
|
365
|
-
] : [];
|
|
368
|
+
`.trimStart()] : [];
|
|
366
369
|
}
|
|
367
370
|
function validateDeprecatedDependencies(packageJson) {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
+
let { dependencies, devDependencies, peerDependencies } = packageJson, allDependencies = {
|
|
372
|
+
...dependencies,
|
|
373
|
+
...devDependencies,
|
|
374
|
+
...peerDependencies
|
|
375
|
+
}, illegalDeps = Object.keys(allDependencies).filter((dep) => deprecatedDevDeps.includes(dep)), unique = [...new Set(illegalDeps).values()];
|
|
376
|
+
return unique.length ? [outdent`
|
|
371
377
|
package.json contains deprecated dependencies that should be removed:
|
|
372
|
-
- ${unique.join(
|
|
373
|
-
|
|
374
|
-
`.trimStart()
|
|
375
|
-
] : [];
|
|
378
|
+
- ${unique.join("\n- ")}
|
|
379
|
+
`.trimStart()] : [];
|
|
376
380
|
}
|
|
377
381
|
async function validateBabelConfig({ basePath }) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
)}]. When using default @sanity/plugin-kit build command,
|
|
382
|
+
let filenames = [".babelrc", ...filesWithSuffixes([".babelrc", "babel.config"], [
|
|
383
|
+
"json",
|
|
384
|
+
"js",
|
|
385
|
+
"cjs",
|
|
386
|
+
"mjs"
|
|
387
|
+
])], babelFiles = [];
|
|
388
|
+
for (let filename of filenames) await fileExists(path.normalize(path.join(basePath, filename))) && babelFiles.push(filename);
|
|
389
|
+
return babelFiles.length ? [outdent`
|
|
390
|
+
Found babel-config file: [${babelFiles.join(", ")}]. When using default @sanity/plugin-kit build command,
|
|
388
391
|
this is probably not needed.
|
|
389
392
|
|
|
390
393
|
Delete the file, or disable this check.
|
|
391
|
-
`.trimStart()
|
|
392
|
-
] : [];
|
|
394
|
+
`.trimStart()] : [];
|
|
393
395
|
}
|
|
394
396
|
async function validateStudioConfig({ basePath }) {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
397
|
+
let suffixes = [
|
|
398
|
+
"ts",
|
|
399
|
+
"js",
|
|
400
|
+
"tsx",
|
|
401
|
+
"jsx"
|
|
402
|
+
], filenames = filesWithSuffixes(["sanity.config", "sanity.cli"], suffixes), files = {};
|
|
403
|
+
for (let filename of filenames) files[filename] = await fileExists(path.normalize(path.join(basePath, filename)));
|
|
404
|
+
let sanityJson = await readJson5File({
|
|
405
|
+
basePath,
|
|
406
|
+
filename: "sanity.json"
|
|
407
|
+
}), hasConfigFile = (fileBase) => filesWithSuffixes([fileBase], suffixes).some((filename) => files[filename]), hasCliConfig = hasConfigFile("sanity.cli"), hasStudioConfig = hasConfigFile("sanity.config"), errors = [];
|
|
408
|
+
if (sanityJson) {
|
|
409
|
+
let info = [outdent`
|
|
404
410
|
Found sanity.json. This file is not used by Sanity Studio V3.
|
|
405
411
|
|
|
406
412
|
Please consult the Studio V3 migration guide:
|
|
407
413
|
${urls.migrationGuideStudio}
|
|
408
414
|
It will detail how to convert sanity.json to sanity.config.ts (or .js) and sanity.cli.ts (or .js) equivalents.
|
|
409
|
-
`.trimStart(),
|
|
410
|
-
sanityJson.plugins?.length && outdent`
|
|
415
|
+
`.trimStart(), sanityJson.plugins?.length && outdent`
|
|
411
416
|
For V3 versions and alternatives to V2 plugins, please refer to the Sanity Exchange:
|
|
412
417
|
${urls.sanityExchange}
|
|
413
|
-
`.trimStart()
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
return hasCliConfig || errors.push(
|
|
420
|
-
outdent`
|
|
421
|
-
sanity.cli.(${suffixes.join(
|
|
422
|
-
" | "
|
|
423
|
-
)}) missing. Please create a file named sanity.cli.ts with the following content:
|
|
418
|
+
`.trimStart()].filter((s) => !!s);
|
|
419
|
+
errors.push(info.join("\n\n"));
|
|
420
|
+
}
|
|
421
|
+
return hasCliConfig || errors.push(outdent`
|
|
422
|
+
sanity.cli.(${suffixes.join(" | ")}) missing. Please create a file named sanity.cli.ts with the following content:
|
|
424
423
|
|
|
425
|
-
${chalk.green(
|
|
426
|
-
outdent`
|
|
424
|
+
${chalk.green(outdent`
|
|
427
425
|
import {createCliConfig} from 'sanity/cli'
|
|
428
426
|
|
|
429
427
|
export default createCliConfig({
|
|
@@ -431,21 +429,15 @@ async function validateStudioConfig({ basePath }) {
|
|
|
431
429
|
projectId: '${sanityJson?.api?.projectId ?? "project-id"}',
|
|
432
430
|
dataset: '${sanityJson?.api?.dataset ?? "dataset"}',
|
|
433
431
|
}
|
|
434
|
-
})`
|
|
435
|
-
)}
|
|
432
|
+
})`)}
|
|
436
433
|
|
|
437
434
|
Make sure to replace the projectId and dataset fields with your own.
|
|
438
435
|
|
|
439
436
|
For more, see ${urls.migrationGuideStudio}
|
|
440
|
-
`.trimStart()
|
|
441
|
-
|
|
442
|
-
outdent`
|
|
443
|
-
sanity.config.(${suffixes.join(
|
|
444
|
-
" | "
|
|
445
|
-
)}) missing. At a minimum sanity.config.ts should contain:
|
|
437
|
+
`.trimStart()), hasStudioConfig || errors.push(outdent`
|
|
438
|
+
sanity.config.(${suffixes.join(" | ")}) missing. At a minimum sanity.config.ts should contain:
|
|
446
439
|
|
|
447
|
-
${chalk.green(
|
|
448
|
-
outdent`
|
|
440
|
+
${chalk.green(outdent`
|
|
449
441
|
import { defineConfig } from "sanity"
|
|
450
442
|
import { deskTool } from "sanity/desk"
|
|
451
443
|
|
|
@@ -464,43 +456,37 @@ async function validateStudioConfig({ basePath }) {
|
|
|
464
456
|
/* put your v2 schema-types here */
|
|
465
457
|
],
|
|
466
458
|
},
|
|
467
|
-
})`
|
|
468
|
-
).trimStart()}
|
|
459
|
+
})`).trimStart()}
|
|
469
460
|
|
|
470
461
|
Make sure to replace the projectId and dataset fields with your own.
|
|
471
462
|
|
|
472
463
|
For more, see ${urls.migrationGuideStudio}
|
|
473
|
-
`.trimStart()
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
`
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}) {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
return [];
|
|
489
|
-
const found = [
|
|
490
|
-
inDependencies ? `- "${incompatiblePluginPackage}" listed in package.json` : null,
|
|
491
|
-
hasShimFile ? "- the v2-incompatible.js file" : null,
|
|
492
|
-
sanityJsonReferencesShim ? "- a sanity.json referencing v2-incompatible.js" : null
|
|
493
|
-
].filter((e) => !!e);
|
|
494
|
-
return [
|
|
495
|
-
outdent`
|
|
464
|
+
`.trimStart()), errors.length ? [errors.join("\n\n---\n\n")] : [];
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Detects leftover usage of the legacy `@sanity/incompatible-plugin` shim and asks for its removal.
|
|
468
|
+
*
|
|
469
|
+
* The shim (a `sanity.json` + `v2-incompatible.js` entry point, plus the `@sanity/incompatible-plugin`
|
|
470
|
+
* dependency) only rendered an error dialog in the long end-of-life Sanity Studio v2 when a v3 plugin
|
|
471
|
+
* was installed there. plugin-kit no longer scaffolds it, so a plugin should not ship it anymore.
|
|
472
|
+
*/
|
|
473
|
+
async function validateIncompatiblePlugin({ basePath, packageJson }) {
|
|
474
|
+
let { dependencies, devDependencies, peerDependencies } = packageJson, inDependencies = !!(dependencies?.["@sanity/incompatible-plugin"] || devDependencies?.["@sanity/incompatible-plugin"] || peerDependencies?.["@sanity/incompatible-plugin"]), hasShimFile = await fileExists(path.normalize(path.join(basePath, "v2-incompatible.js"))), sanityJsonReferencesShim = !!(await readJson5File({
|
|
475
|
+
basePath,
|
|
476
|
+
filename: "sanity.json"
|
|
477
|
+
}))?.parts?.some((part) => part?.path?.includes("v2-incompatible"));
|
|
478
|
+
return !inDependencies && !hasShimFile && !sanityJsonReferencesShim ? [] : [outdent`
|
|
496
479
|
${incompatiblePluginPackage} is no longer used and should be removed.
|
|
497
480
|
|
|
498
481
|
It only rendered an error dialog in the long end-of-life Sanity Studio v2 when a v3 plugin was
|
|
499
482
|
installed there. That compatibility shim is now obsolete, so plugin-kit no longer adds it.
|
|
500
483
|
|
|
501
484
|
Found:
|
|
502
|
-
${
|
|
503
|
-
`
|
|
485
|
+
${[
|
|
486
|
+
inDependencies ? `- "${incompatiblePluginPackage}" listed in package.json` : null,
|
|
487
|
+
hasShimFile ? "- the v2-incompatible.js file" : null,
|
|
488
|
+
sanityJsonReferencesShim ? "- a sanity.json referencing v2-incompatible.js" : null
|
|
489
|
+
].filter((e) => !!e).join("\n")}
|
|
504
490
|
|
|
505
491
|
To fix this:
|
|
506
492
|
- Remove "${incompatiblePluginPackage}" from package.json (dependencies/devDependencies/peerDependencies)
|
|
@@ -509,19 +495,20 @@ async function validateIncompatiblePlugin({
|
|
|
509
495
|
- Remove "sanity.json" and "v2-incompatible.js" from the package.json "files" array
|
|
510
496
|
|
|
511
497
|
For more, see ${urls.incompatiblePlugin}
|
|
512
|
-
`.trimStart()
|
|
513
|
-
];
|
|
498
|
+
`.trimStart()];
|
|
514
499
|
}
|
|
515
500
|
function validatePackageName$1(packageJson) {
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
501
|
+
let valid = validNpmName(packageJson.name ?? "");
|
|
502
|
+
return valid.validForNewPackages ? !packageJson.name?.startsWith("@") && !packageJson.name?.startsWith("sanity-plugin-") ? ["Invalid package.json: \"name\" should be prefixed with \"sanity-plugin-\" (or scoped - @your-company/plugin-name)"] : [] : [`Invalid package.json: "name" is invalid: ${(valid.errors ?? valid.warnings ?? []).join(", ")}`];
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Plugins built with @sanity/plugin-kit publish the compiled output (the `dist` directory) plus any
|
|
506
|
+
* v2-compatibility files. The `src` directory should not be published: it bloats the package and can
|
|
507
|
+
* cause bundlers that resolve the `source` export condition to pull in raw, uncompiled TypeScript.
|
|
508
|
+
*/
|
|
521
509
|
function validateBannedFiles(packageJson) {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
outdent`
|
|
510
|
+
let { files } = packageJson;
|
|
511
|
+
return !Array.isArray(files) || !files.some((entry) => typeof entry == "string" && entry.trim().replace(/^\.?\/+/, "").replace(/\/+$/, "") === "src") ? [] : [outdent`
|
|
525
512
|
package.json "files" must not include "src".
|
|
526
513
|
|
|
527
514
|
Plugins built with @sanity/plugin-kit publish the compiled output in "dist" (and any v2-compatibility files).
|
|
@@ -529,538 +516,395 @@ function validateBannedFiles(packageJson) {
|
|
|
529
516
|
"source" export condition to import raw, uncompiled TypeScript.
|
|
530
517
|
|
|
531
518
|
Please remove "src" from the "files" array in package.json.
|
|
532
|
-
`.trimStart()
|
|
533
|
-
] : [] : [];
|
|
519
|
+
`.trimStart()];
|
|
534
520
|
}
|
|
535
521
|
async function validateSrcIndexFile(basePath) {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
hasIndex = hasIndex || await fileExists(indexFile);
|
|
540
|
-
return hasIndex ? [] : [
|
|
541
|
-
outdent`
|
|
522
|
+
let paths = ["index.js", "index.ts"].map((p) => path.join("src", p)), allowedIndexFiles = paths.map((file) => path.join(basePath, file)), hasIndex = !1;
|
|
523
|
+
for (let indexFile of allowedIndexFiles) hasIndex ||= await fileExists(indexFile);
|
|
524
|
+
return hasIndex ? [] : [outdent`
|
|
542
525
|
Expected one of [${paths.join(", ")}] to exist.
|
|
543
526
|
|
|
544
527
|
@sanity/pkg-utils expects a non-jsx file to be the source entry-point for the plugin.
|
|
545
528
|
If you currently have JSX in your index file, extract it into a separate file and import it.
|
|
546
|
-
`
|
|
547
|
-
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
configKey,
|
|
553
|
-
files
|
|
554
|
-
}) {
|
|
555
|
-
const found = [];
|
|
556
|
-
for (const file of files) {
|
|
557
|
-
const filePath = path.join(basePath, file);
|
|
558
|
-
await fileExists(filePath) && found.push(file);
|
|
559
|
-
}
|
|
560
|
-
return found.length > 1 ? [
|
|
561
|
-
outdent`
|
|
529
|
+
`];
|
|
530
|
+
}
|
|
531
|
+
async function disallowDuplicateConfig({ basePath, pkgJson, configKey, files }) {
|
|
532
|
+
let found = [];
|
|
533
|
+
for (let file of files) await fileExists(path.join(basePath, file)) && found.push(file);
|
|
534
|
+
return found.length > 1 ? [outdent`
|
|
562
535
|
Found multiple config files that serve the same purpose: [${found.join(", ")}].
|
|
563
536
|
|
|
564
537
|
There should be at most one of these files. Delete the rest.
|
|
565
|
-
`
|
|
566
|
-
] : found.length && pkgJson[configKey] ? [
|
|
567
|
-
outdent`
|
|
538
|
+
`] : found.length && pkgJson[configKey] ? [outdent`
|
|
568
539
|
package.json contains ${configKey}, but there also exists a config file that serves the same purpose.
|
|
569
540
|
Config file: ${found.join("")}]
|
|
570
541
|
|
|
571
542
|
Either delete the file or remove ${configKey} entry from package.json.
|
|
572
|
-
`
|
|
573
|
-
] : [];
|
|
543
|
+
`] : [];
|
|
574
544
|
}
|
|
575
545
|
async function disallowDuplicateEslintConfig(basePath, pkgJson) {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
546
|
+
return disallowDuplicateConfig({
|
|
547
|
+
basePath,
|
|
548
|
+
pkgJson,
|
|
549
|
+
configKey: "eslint",
|
|
550
|
+
files: [
|
|
551
|
+
".eslintrc",
|
|
552
|
+
".eslintrc.js",
|
|
553
|
+
".eslintrc.cjs",
|
|
554
|
+
".eslintrc.yaml",
|
|
555
|
+
".eslintrc.yml",
|
|
556
|
+
".eslintrc.json"
|
|
557
|
+
]
|
|
558
|
+
});
|
|
589
559
|
}
|
|
590
560
|
async function disallowDuplicatePrettierConfig(basePath, pkgJson) {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
561
|
+
return disallowDuplicateConfig({
|
|
562
|
+
basePath,
|
|
563
|
+
pkgJson,
|
|
564
|
+
configKey: "prettier",
|
|
565
|
+
files: [
|
|
566
|
+
".prettierrc",
|
|
567
|
+
".prettierrc.json5",
|
|
568
|
+
".prettierrc.json",
|
|
569
|
+
".prettierrc.yaml",
|
|
570
|
+
".prettierrc.yml",
|
|
571
|
+
".prettierrc.js",
|
|
572
|
+
".prettierrc.cjs",
|
|
573
|
+
".prettier.config,js",
|
|
574
|
+
".prettier.config.cjs",
|
|
575
|
+
".prettierrc.toml"
|
|
576
|
+
]
|
|
577
|
+
});
|
|
608
578
|
}
|
|
609
579
|
const forcedPackageVersions = {}, forcedDevPackageVersions = {}, forcedPeerPackageVersions = {
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
580
|
+
react: "^18",
|
|
581
|
+
"react-dom": "^18",
|
|
582
|
+
"@types/react": "^18",
|
|
583
|
+
"@types/react-dom": "^18",
|
|
584
|
+
sanity: "^5 || ^6.0.0-0",
|
|
585
|
+
"styled-components": "^5.2"
|
|
616
586
|
};
|
|
617
587
|
function errorToUndefined(err) {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
588
|
+
if (err instanceof TypeError) throw err;
|
|
589
|
+
}
|
|
590
|
+
const stat = util.promisify(fs.stat), readFile$1 = util.promisify(fs.readFile), allowedPartProps = [
|
|
591
|
+
"name",
|
|
592
|
+
"implements",
|
|
593
|
+
"path",
|
|
594
|
+
"description"
|
|
595
|
+
], disallowedPluginProps = [
|
|
596
|
+
"api",
|
|
597
|
+
"project",
|
|
598
|
+
"plugins",
|
|
599
|
+
"env"
|
|
600
|
+
];
|
|
622
601
|
async function getPaths(options) {
|
|
623
|
-
|
|
624
|
-
|
|
602
|
+
let { basePath } = options, manifest = await readManifest(options);
|
|
603
|
+
return manifest.paths ? absolutifyPaths(manifest.paths, basePath) : null;
|
|
625
604
|
}
|
|
626
605
|
function absolutifyPaths(paths, basePath) {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
606
|
+
let getPath = (relative) => relative ? path.resolve(path.join(basePath, relative)) : void 0;
|
|
607
|
+
return paths ? {
|
|
608
|
+
basePath,
|
|
609
|
+
compiled: getPath(paths.compiled),
|
|
610
|
+
source: getPath(paths.source)
|
|
611
|
+
} : { basePath };
|
|
633
612
|
}
|
|
634
613
|
async function readManifest(options) {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
throw new Error(`Error parsing "${manifestPath}": ${err.message}`);
|
|
649
|
-
}
|
|
650
|
-
return validate && await validateManifest(parsed, options), parsed;
|
|
614
|
+
let { basePath, validate = !0 } = options, manifestPath = path.normalize(path.join(basePath, "sanity.json")), content;
|
|
615
|
+
try {
|
|
616
|
+
content = await readFile$1(manifestPath, "utf8");
|
|
617
|
+
} catch (err) {
|
|
618
|
+
throw err.code === "ENOENT" ? Error(`No sanity.json found. sanity.json is required for plugins to function. Use \`${binname} init\` for a new plugin, or create an empty \`sanity.json\` with an empty object (\`{}\`) for existing ones.`) : Error(`Failed to read "${manifestPath}": ${err.message}`);
|
|
619
|
+
}
|
|
620
|
+
let parsed;
|
|
621
|
+
try {
|
|
622
|
+
parsed = JSON.parse(content);
|
|
623
|
+
} catch (err) {
|
|
624
|
+
throw Error(`Error parsing "${manifestPath}": ${err.message}`);
|
|
625
|
+
}
|
|
626
|
+
return validate && await validateManifest(parsed, options), parsed;
|
|
651
627
|
}
|
|
652
628
|
async function validateManifest(manifest, opts) {
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
629
|
+
let options = {
|
|
630
|
+
isPlugin: !0,
|
|
631
|
+
...opts
|
|
632
|
+
};
|
|
633
|
+
if (!isObject$1(manifest)) throw Error("Invalid sanity.json: Root must be an object");
|
|
634
|
+
if (options.isPlugin ? await validatePluginManifest(manifest, options) : validateProjectManifest(manifest), "root" in manifest && typeof manifest.root != "boolean") throw Error("Invalid sanity.json: \"root\" property must be a boolean if declared");
|
|
635
|
+
await validateParts(manifest, {
|
|
636
|
+
...options,
|
|
637
|
+
paths: absolutifyPaths(manifest.paths, options.basePath)
|
|
638
|
+
});
|
|
662
639
|
}
|
|
663
640
|
function validateProjectManifest(manifest) {
|
|
664
|
-
|
|
665
|
-
throw new Error('Invalid sanity.json: "paths" property has no meaning in a project manifest');
|
|
641
|
+
if ("paths" in manifest) throw Error("Invalid sanity.json: \"paths\" property has no meaning in a project manifest");
|
|
666
642
|
}
|
|
667
643
|
async function validatePluginManifest(manifest, options) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (manifest.root)
|
|
676
|
-
throw new Error('Invalid sanity.json: "root" cannot be truthy in a plugin manifest');
|
|
677
|
-
await validatePaths$1(manifest, options);
|
|
644
|
+
let disallowed = Object.keys(manifest).filter((key) => disallowedPluginProps.includes(key)).map((key) => `"${key}"`);
|
|
645
|
+
if (disallowed.length > 0) {
|
|
646
|
+
let plural = disallowed.length > 1 ? "s" : "", joined = disallowed.join(", ");
|
|
647
|
+
throw Error(`Invalid sanity.json: Key${plural} ${joined} ${plural ? "are" : "is"} not allowed in a plugin manifest`);
|
|
648
|
+
}
|
|
649
|
+
if (manifest.root) throw Error("Invalid sanity.json: \"root\" cannot be truthy in a plugin manifest");
|
|
650
|
+
await validatePaths$1(manifest, options);
|
|
678
651
|
}
|
|
679
652
|
async function validatePaths$1(manifest, options) {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
);
|
|
692
|
-
const sourcePath = path.resolve(options.basePath, manifest.paths.source);
|
|
693
|
-
let srcStats;
|
|
694
|
-
try {
|
|
695
|
-
srcStats = await stat(sourcePath);
|
|
696
|
-
} catch (err) {
|
|
697
|
-
if (err.code === "ENOENT")
|
|
698
|
-
throw new Error(`sanity.json references "source" path which does not exist: "${sourcePath}"`);
|
|
699
|
-
}
|
|
700
|
-
if (!srcStats?.isDirectory())
|
|
701
|
-
throw new Error(
|
|
702
|
-
`sanity.json references "source" path which is not a directory: "${sourcePath}"`
|
|
703
|
-
);
|
|
653
|
+
if (!("paths" in manifest)) return;
|
|
654
|
+
if (!isObject$1(manifest.paths)) throw Error("Invalid sanity.json: \"paths\" must be an object if declared");
|
|
655
|
+
if (typeof manifest.paths.compiled != "string") throw Error("Invalid sanity.json: \"paths\" must have a (string) \"compiled\" property if declared");
|
|
656
|
+
if (typeof manifest.paths.source != "string") throw Error("Invalid sanity.json: \"paths\" must have a (string) \"source\" property if declared");
|
|
657
|
+
let sourcePath = path.resolve(options.basePath, manifest.paths.source), srcStats;
|
|
658
|
+
try {
|
|
659
|
+
srcStats = await stat(sourcePath);
|
|
660
|
+
} catch (err) {
|
|
661
|
+
if (err.code === "ENOENT") throw Error(`sanity.json references "source" path which does not exist: "${sourcePath}"`);
|
|
662
|
+
}
|
|
663
|
+
if (!srcStats?.isDirectory()) throw Error(`sanity.json references "source" path which is not a directory: "${sourcePath}"`);
|
|
704
664
|
}
|
|
705
665
|
async function validateParts(manifest, options) {
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
let i = 0;
|
|
711
|
-
for (const part of manifest.parts)
|
|
712
|
-
await validatePart(part, i, options), i++;
|
|
666
|
+
if (!("parts" in manifest)) return;
|
|
667
|
+
if (!Array.isArray(manifest.parts)) throw Error("Invalid sanity.json: \"parts\" must be an array if declared");
|
|
668
|
+
let i = 0;
|
|
669
|
+
for (let part of manifest.parts) await validatePart(part, i, options), i++;
|
|
713
670
|
}
|
|
714
671
|
async function validatePart(part, index, options) {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
validateAllowedPartKeys(part, index), validatePartStringValues(part, index), validatePartNames(part, index, options), await validatePartFiles(part, index, options);
|
|
672
|
+
if (!isObject$1(part)) throw Error(`Invalid sanity.json: "parts[${index}]" must be an object`);
|
|
673
|
+
validateAllowedPartKeys(part, index), validatePartStringValues(part, index), validatePartNames(part, index, options), await validatePartFiles(part, index, options);
|
|
718
674
|
}
|
|
719
675
|
async function validatePartFiles(part, index, options) {
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
)}) (parts[${index}])`
|
|
729
|
-
);
|
|
730
|
-
if (!verifySourceParts && !verifyCompiledParts)
|
|
731
|
-
return;
|
|
732
|
-
const [srcExists, outDirExists] = await Promise.all([
|
|
733
|
-
hasSourceFile(part.path, paths),
|
|
734
|
-
verifyCompiledParts && hasCompiledFile(part.path, paths)
|
|
735
|
-
]);
|
|
736
|
-
if (!srcExists)
|
|
737
|
-
throw new Error(
|
|
738
|
-
`Invalid sanity.json: Part path references file that does not exist in source directory (${paths?.source || paths?.basePath}) (parts[${index}])`
|
|
739
|
-
);
|
|
740
|
-
if (verifyCompiledParts && !outDirExists)
|
|
741
|
-
throw new Error(
|
|
742
|
-
`Invalid sanity.json: Part path references file ("${part.path}") that does not exist in compiled directory (${paths?.compiled}) (parts[${index}])`
|
|
743
|
-
);
|
|
676
|
+
let { verifyCompiledParts, verifySourceParts, paths } = options;
|
|
677
|
+
if (!part?.path) return;
|
|
678
|
+
let ext = path.extname(part.path);
|
|
679
|
+
if (paths?.source && ext && ext !== ".js" && buildExtensions.includes(ext)) throw Error(`Invalid sanity.json: Part path has extension which is not applicable after compiling. ${ext} becomes .js after compiling. Specify filename without extension (${path.basename(part.path)}) (parts[${index}])`);
|
|
680
|
+
if (!verifySourceParts && !verifyCompiledParts) return;
|
|
681
|
+
let [srcExists, outDirExists] = await Promise.all([hasSourceFile(part.path, paths), verifyCompiledParts && hasCompiledFile(part.path, paths)]);
|
|
682
|
+
if (!srcExists) throw Error(`Invalid sanity.json: Part path references file that does not exist in source directory (${paths?.source || paths?.basePath}) (parts[${index}])`);
|
|
683
|
+
if (verifyCompiledParts && !outDirExists) throw Error(`Invalid sanity.json: Part path references file ("${part.path}") that does not exist in compiled directory (${paths?.compiled}) (parts[${index}])`);
|
|
744
684
|
}
|
|
745
685
|
function validatePartNames(part, index, options) {
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
`Invalid sanity.json: "name" must be prefixed with "part:${pluginName}/" - got "${part?.name}" (parts[${index}])`
|
|
750
|
-
);
|
|
751
|
-
if (!part?.implements?.startsWith("part:"))
|
|
752
|
-
throw new Error(
|
|
753
|
-
`Invalid sanity.json: "implements" must be prefixed with "part:" - got "${part?.implements}" (parts[${index}])`
|
|
754
|
-
);
|
|
686
|
+
let pluginName = options.pluginName ? options.pluginName.replace(/^sanity-plugin-/, "") : "";
|
|
687
|
+
if (!part?.name || !part?.name?.startsWith(`part:${pluginName}/`)) throw Error(`Invalid sanity.json: "name" must be prefixed with "part:${pluginName}/" - got "${part?.name}" (parts[${index}])`);
|
|
688
|
+
if (!part?.implements?.startsWith("part:")) throw Error(`Invalid sanity.json: "implements" must be prefixed with "part:" - got "${part?.implements}" (parts[${index}])`);
|
|
755
689
|
}
|
|
756
690
|
function validateAllowedPartKeys(part, index) {
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
);
|
|
763
|
-
}
|
|
691
|
+
let disallowed = Object.keys(part).filter((key) => !allowedPartProps.includes(key)).map((key) => `"${key}"`);
|
|
692
|
+
if (disallowed.length > 0) {
|
|
693
|
+
let plural = disallowed.length > 1 ? "s" : "", joined = disallowed.join(", ");
|
|
694
|
+
throw Error(`Invalid sanity.json: Key${plural} ${joined} ${plural ? "are" : "is"} not allowed in a part declaration (parts[${index}])`);
|
|
695
|
+
}
|
|
764
696
|
}
|
|
765
697
|
function validatePartStringValues(part, index) {
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
);
|
|
772
|
-
}
|
|
698
|
+
let nonStrings = Object.keys(part).filter((key) => typeof part[key] != "string").map((key) => `"${key}"`);
|
|
699
|
+
if (nonStrings.length > 0) {
|
|
700
|
+
let plural = nonStrings.length > 1 ? "s" : "", joined = nonStrings.join(", ");
|
|
701
|
+
throw Error(`Invalid sanity.json: Key${plural} ${joined} should be of type string (parts[${index}])`);
|
|
702
|
+
}
|
|
773
703
|
}
|
|
774
704
|
function isObject$1(obj) {
|
|
775
|
-
|
|
705
|
+
return !Array.isArray(obj) && typeof obj == "object" && !!obj;
|
|
776
706
|
}
|
|
777
707
|
async function hasSanityJson(basePath) {
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
708
|
+
let file = await readJsonFile(path.join(basePath, "sanity.json")).catch(errorToUndefined);
|
|
709
|
+
return {
|
|
710
|
+
exists: !!file,
|
|
711
|
+
isRoot: !!(file && file.root)
|
|
712
|
+
};
|
|
782
713
|
}
|
|
783
714
|
async function findStudioV3Config(basePath) {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
return { v3ConfigFile: await fileExists(path.join(basePath, tsFile)) ? tsFile : void 0 };
|
|
715
|
+
let jsFile = "sanity.config.js";
|
|
716
|
+
if (await fileExists(path.join(basePath, jsFile))) return { v3ConfigFile: jsFile };
|
|
717
|
+
let tsFile = "sanity.config.ts";
|
|
718
|
+
return { v3ConfigFile: await fileExists(path.join(basePath, tsFile)) ? tsFile : void 0 };
|
|
789
719
|
}
|
|
790
720
|
const lockedDependencies = {
|
|
791
|
-
|
|
792
|
-
|
|
721
|
+
"styled-components": "^6.1",
|
|
722
|
+
eslint: "^8.57.0",
|
|
723
|
+
typescript: "^6"
|
|
793
724
|
};
|
|
794
725
|
function resolveLatestVersions(packages) {
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
if (!version)
|
|
803
|
-
throw new Error(`Found no version for ${pkgName}`);
|
|
804
|
-
return rangeify(version);
|
|
805
|
-
},
|
|
806
|
-
{ concurrency: 8 }
|
|
807
|
-
);
|
|
726
|
+
let versions = {};
|
|
727
|
+
for (let pkgName of packages) versions[pkgName] = pkgName in lockedDependencies ? lockedDependencies[pkgName] : "latest";
|
|
728
|
+
return pProps(versions, async (range, pkgName) => {
|
|
729
|
+
let version = await getLatestVersion(pkgName, { range });
|
|
730
|
+
if (!version) throw Error(`Found no version for ${pkgName}`);
|
|
731
|
+
return rangeify(version);
|
|
732
|
+
}, { concurrency: 8 });
|
|
808
733
|
}
|
|
809
734
|
function rangeify(version) {
|
|
810
|
-
|
|
735
|
+
return `^${version}`;
|
|
811
736
|
}
|
|
812
737
|
const defaultDependencies = [], defaultDevDependencies = [
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
738
|
+
"sanity",
|
|
739
|
+
"react",
|
|
740
|
+
"react-dom",
|
|
741
|
+
"styled-components"
|
|
742
|
+
], defaultPeerDependencies = ["react", "sanity"], readFile = util.promisify(fs.readFile), pathKeys = [
|
|
743
|
+
"main",
|
|
744
|
+
"module",
|
|
745
|
+
"browser",
|
|
746
|
+
"types"
|
|
747
|
+
];
|
|
819
748
|
async function getPackage(opts) {
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
return validate && await validatePackage(parsed, options), parsed;
|
|
749
|
+
let options = {
|
|
750
|
+
flags: {},
|
|
751
|
+
...opts
|
|
752
|
+
};
|
|
753
|
+
validateOptions(options);
|
|
754
|
+
let { basePath, validate = !0 } = options, manifestPath = path.normalize(path.join(basePath, "package.json")), content;
|
|
755
|
+
try {
|
|
756
|
+
content = await readFile(manifestPath, "utf8");
|
|
757
|
+
} catch (err) {
|
|
758
|
+
throw err.code === "ENOENT" ? Error(`No package.json found. package.json is required to publish to npm. Use \`${cliName} init\` for a new plugin, or \`npm init\` for an existing one`) : Error(`Failed to read "${manifestPath}": ${err.message}`);
|
|
759
|
+
}
|
|
760
|
+
let parsed;
|
|
761
|
+
try {
|
|
762
|
+
parsed = JSON.parse(content);
|
|
763
|
+
} catch (err) {
|
|
764
|
+
throw Error(`Error parsing "${manifestPath}": ${err.message}`);
|
|
765
|
+
}
|
|
766
|
+
if (!isObject(parsed)) throw Error("Invalid package.json: Root must be an object");
|
|
767
|
+
return validate && await validatePackage(parsed, options), parsed;
|
|
840
768
|
}
|
|
841
769
|
async function validatePackage(manifest, opts) {
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
770
|
+
validateOptions(opts);
|
|
771
|
+
let options = {
|
|
772
|
+
isPlugin: !0,
|
|
773
|
+
...opts
|
|
774
|
+
};
|
|
775
|
+
options.isPlugin && await validatePluginPackage(manifest, options), validateLockFiles(options);
|
|
845
776
|
}
|
|
846
777
|
function validateOptions(opts) {
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
if (typeof options.basePath != "string")
|
|
851
|
-
throw new Error('"options.basePath" must be a string (path to plugin base path)');
|
|
778
|
+
let options = opts || {};
|
|
779
|
+
if (!isObject(options)) throw Error("Options must be an object");
|
|
780
|
+
if (typeof options.basePath != "string") throw Error("\"options.basePath\" must be a string (path to plugin base path)");
|
|
852
781
|
}
|
|
853
782
|
async function validatePluginPackage(manifest, options) {
|
|
854
|
-
|
|
783
|
+
validatePackageName(manifest), await validatePaths(manifest, options);
|
|
855
784
|
}
|
|
856
785
|
function validatePackageName(manifest) {
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
throw new Error(`Invalid package.json: "name" is invalid: ${(valid.errors ?? []).join(", ")}`);
|
|
862
|
-
if (manifest.name[0] !== "@" && !manifest.name.startsWith("sanity-plugin-"))
|
|
863
|
-
throw new Error(
|
|
864
|
-
'Invalid package.json: "name" should be prefixed with "sanity-plugin-" (or scoped - @your-company/plugin-name)'
|
|
865
|
-
);
|
|
786
|
+
if (typeof manifest.name != "string") throw Error("Invalid package.json: \"name\" must be a string");
|
|
787
|
+
let valid = validNpmName(manifest.name);
|
|
788
|
+
if (!valid.validForNewPackages) throw Error(`Invalid package.json: "name" is invalid: ${(valid.errors ?? []).join(", ")}`);
|
|
789
|
+
if (manifest.name[0] !== "@" && !manifest.name.startsWith("sanity-plugin-")) throw Error("Invalid package.json: \"name\" should be prefixed with \"sanity-plugin-\" (or scoped - @your-company/plugin-name)");
|
|
866
790
|
}
|
|
867
791
|
async function validatePaths(manifest, options) {
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
`Invalid package.json: "${key}" points to file that will not exist after compiling`
|
|
887
|
-
);
|
|
888
|
-
if (!exists(manifestValue) && !await willExist(manifestValue)) {
|
|
889
|
-
if (!paths)
|
|
890
|
-
throw new Error(`Invalid package.json: "${key}" points to file that does not exist`);
|
|
891
|
-
const inOutDir = paths.compiled && !abs(manifestValue).startsWith(paths.compiled);
|
|
892
|
-
throw new Error(
|
|
893
|
-
inOutDir ? `Invalid package.json: "${key}" points to file that does not exist, and "paths" is not configured to compile to this location` : `Invalid package.json: "${key}" points to file that does not exist, and no equivalent is found in source directory`
|
|
894
|
-
);
|
|
895
|
-
}
|
|
896
|
-
}
|
|
792
|
+
let paths = await getPaths({
|
|
793
|
+
...options,
|
|
794
|
+
pluginName: manifest.name ?? "unknown",
|
|
795
|
+
verifySourceParts: !1,
|
|
796
|
+
verifyCompiledParts: !1
|
|
797
|
+
}), abs = (file) => path.isAbsolute(file) ? file : path.resolve(path.join(options.basePath, file)), exists = (file) => fs.existsSync(abs(file)), willExist = (file) => paths && hasSourceEquivalent(abs(file), paths), withinSourceDir = (file) => paths?.source && abs(file).startsWith(paths.source), withinTargetDir = (file) => paths?.compiled && abs(file).startsWith(paths.compiled);
|
|
798
|
+
for (let key of pathKeys) {
|
|
799
|
+
if (!(key in manifest)) continue;
|
|
800
|
+
let manifestValue = manifest[key];
|
|
801
|
+
if (typeof manifestValue != "string") throw Error(`Invalid package.json: "${key}" must be a string if defined`);
|
|
802
|
+
if (!options?.flags?.allowSourceTarget && paths && withinSourceDir(manifestValue)) throw Error(`Invalid package.json: "${key}" points to file within source (uncompiled) directory. Use --allow-source-target if you really want to do this.`);
|
|
803
|
+
if (exists(manifestValue) && paths && withinTargetDir(manifestValue) && !await willExist(manifestValue)) throw Error(`Invalid package.json: "${key}" points to file that will not exist after compiling`);
|
|
804
|
+
if (!exists(manifestValue) && !await willExist(manifestValue)) {
|
|
805
|
+
if (!paths) throw Error(`Invalid package.json: "${key}" points to file that does not exist`);
|
|
806
|
+
let inOutDir = paths.compiled && !abs(manifestValue).startsWith(paths.compiled);
|
|
807
|
+
throw Error(inOutDir ? `Invalid package.json: "${key}" points to file that does not exist, and "paths" is not configured to compile to this location` : `Invalid package.json: "${key}" points to file that does not exist, and no equivalent is found in source directory`);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
897
810
|
}
|
|
898
811
|
function isObject(obj) {
|
|
899
|
-
|
|
812
|
+
return !Array.isArray(obj) && typeof obj == "object" && !!obj;
|
|
900
813
|
}
|
|
901
814
|
function validateLockFiles(options) {
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
throw new Error("Invalid plugin: contains both package-lock.json and yarn.lock");
|
|
815
|
+
let npm = fs.existsSync(path.join(options.basePath, "package-lock.json")), yarn = fs.existsSync(path.join(options.basePath, "yarn.lock"));
|
|
816
|
+
if (npm && yarn) throw Error("Invalid plugin: contains both package-lock.json and yarn.lock");
|
|
905
817
|
}
|
|
906
818
|
async function writePackageJson(data, options) {
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
".": {
|
|
955
|
-
source,
|
|
956
|
-
default: `./${outDir}/index.js`
|
|
957
|
-
},
|
|
958
|
-
"./package.json": "./package.json"
|
|
959
|
-
},
|
|
960
|
-
...flags.typescript ? { types: `./${outDir}/index.d.ts` } : {},
|
|
961
|
-
files,
|
|
962
|
-
scripts: { ...prev.scripts },
|
|
963
|
-
dependencies: sortKeys(dependencies),
|
|
964
|
-
devDependencies: sortKeys(devDependencies),
|
|
965
|
-
peerDependencies: sortKeys(peerDependencies),
|
|
966
|
-
engines: {
|
|
967
|
-
node: requiredNodeEngine
|
|
968
|
-
}
|
|
969
|
-
}, manifest = {
|
|
970
|
-
...forcedOrder,
|
|
971
|
-
// Use already configured values by default (if not otherwise specified)
|
|
972
|
-
...prev || {},
|
|
973
|
-
// We're de-declaring properties because of key order in package.json
|
|
974
|
-
...forcedOrder
|
|
975
|
-
}, differs = JSON.stringify(prev) !== JSON.stringify(manifest);
|
|
976
|
-
return log.debug("Does manifest differ? %s", differs ? "yes" : "no"), differs && await writePackageJsonDirect(manifest, options), differs ? manifest : prev;
|
|
819
|
+
let { user, pluginName, license, description, pkg: prevPkg, gitOrigin } = data, { outDir, peerDependencies: addPeers, dependencies: addDeps, devDependencies: addDevDeps } = options, { flags } = options, prev = prevPkg || {}, usePrettier = flags.prettier !== !1, useEslint = flags.eslint !== !1, useTypescript = flags.eslint !== !1, newDevDependencies = [cliName, "@sanity/pkg-utils"];
|
|
820
|
+
useTypescript && (log_default.debug("Using TypeScript. Adding to dev dependencies."), newDevDependencies.push("@types/react", "typescript")), usePrettier && (log_default.debug("Using prettier. Adding to dev dependencies."), newDevDependencies.push("prettier", "prettier-plugin-packagejson")), useEslint && (log_default.debug("Using eslint. Adding to dev dependencies."), newDevDependencies.push("eslint", "eslint-config-sanity", "eslint-plugin-react", "eslint-plugin-react-hooks"), usePrettier && newDevDependencies.push("eslint-config-prettier", "eslint-plugin-prettier"), useTypescript && newDevDependencies.push("@typescript-eslint/eslint-plugin", "@typescript-eslint/parser")), log_default.debug("Resolving latest versions for %s", newDevDependencies.join(", "));
|
|
821
|
+
let dependencies = forceDependencyVersions({
|
|
822
|
+
...prev.dependencies || {},
|
|
823
|
+
...addDeps || {},
|
|
824
|
+
...await resolveLatestVersions(defaultDependencies)
|
|
825
|
+
}, forcedPackageVersions), devDependencies = forceDependencyVersions({
|
|
826
|
+
...addDevDeps || {},
|
|
827
|
+
...prev.devDependencies || {},
|
|
828
|
+
...await resolveLatestVersions([...newDevDependencies, ...defaultDevDependencies])
|
|
829
|
+
}, forcedDevPackageVersions), peerDependencies = forceDependencyVersions({
|
|
830
|
+
...prev.peerDependencies || {},
|
|
831
|
+
...addPeers || {},
|
|
832
|
+
...await resolveLatestVersions(defaultPeerDependencies)
|
|
833
|
+
}, forcedPeerPackageVersions), source = flags.typescript ? "./src/index.ts" : "./src/index.js", files = [outDir];
|
|
834
|
+
files.sort();
|
|
835
|
+
let forcedOrder = {
|
|
836
|
+
name: pluginName,
|
|
837
|
+
version: prev.version ?? "1.0.0",
|
|
838
|
+
description: description || "",
|
|
839
|
+
keywords: prev.keywords ?? ["sanity", "sanity-plugin"],
|
|
840
|
+
...urlsFromOrigin(gitOrigin),
|
|
841
|
+
...repoFromOrigin(gitOrigin),
|
|
842
|
+
license: license ? license.id : "UNLICENSED",
|
|
843
|
+
author: user?.email ? `${user.name} <${user.email}>` : user?.name,
|
|
844
|
+
sideEffects: !1,
|
|
845
|
+
type: "module",
|
|
846
|
+
exports: {
|
|
847
|
+
".": {
|
|
848
|
+
source,
|
|
849
|
+
default: `./${outDir}/index.js`
|
|
850
|
+
},
|
|
851
|
+
"./package.json": "./package.json"
|
|
852
|
+
},
|
|
853
|
+
...flags.typescript ? { types: `./${outDir}/index.d.ts` } : {},
|
|
854
|
+
files,
|
|
855
|
+
scripts: { ...prev.scripts },
|
|
856
|
+
dependencies: sortKeys(dependencies),
|
|
857
|
+
devDependencies: sortKeys(devDependencies),
|
|
858
|
+
peerDependencies: sortKeys(peerDependencies),
|
|
859
|
+
engines: { node: requiredNodeEngine }
|
|
860
|
+
}, manifest = {
|
|
861
|
+
...forcedOrder,
|
|
862
|
+
...prev || {},
|
|
863
|
+
...forcedOrder
|
|
864
|
+
}, differs = JSON.stringify(prev) !== JSON.stringify(manifest);
|
|
865
|
+
return log_default.debug("Does manifest differ? %s", differs ? "yes" : "no"), differs && await writePackageJsonDirect(manifest, options), differs ? manifest : prev;
|
|
977
866
|
}
|
|
978
867
|
function urlsFromOrigin(gitOrigin) {
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
url: `https://github.com/${details.user}/${details.repo}/issues`
|
|
986
|
-
}
|
|
987
|
-
} : {};
|
|
868
|
+
if (!gitOrigin) return {};
|
|
869
|
+
let details = githubUrlToObject(gitOrigin);
|
|
870
|
+
return details ? {
|
|
871
|
+
homepage: `https://github.com/${details.user}/${details.repo}#readme`,
|
|
872
|
+
bugs: { url: `https://github.com/${details.user}/${details.repo}/issues` }
|
|
873
|
+
} : {};
|
|
988
874
|
}
|
|
989
875
|
function repoFromOrigin(gitOrigin) {
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
}
|
|
995
|
-
} : {};
|
|
876
|
+
return gitOrigin ? { repository: {
|
|
877
|
+
type: "git",
|
|
878
|
+
url: gitOrigin
|
|
879
|
+
} } : {};
|
|
996
880
|
}
|
|
997
881
|
function addScript(cmd, existing) {
|
|
998
|
-
|
|
882
|
+
return existing && existing.includes(cmd) ? existing : cmd;
|
|
999
883
|
}
|
|
1000
884
|
async function addPackageJsonScripts(manifest, options, updateScripts) {
|
|
1001
|
-
|
|
1002
|
-
|
|
885
|
+
let originalScripts = manifest.scripts || {}, scripts = updateScripts({ ...originalScripts }), differs = Object.keys(scripts).some((key) => scripts[key] !== originalScripts[key]);
|
|
886
|
+
return differs && await writePackageJsonDirect({
|
|
887
|
+
...manifest,
|
|
888
|
+
scripts
|
|
889
|
+
}, options), differs;
|
|
1003
890
|
}
|
|
1004
891
|
async function writePackageJsonDirect(manifest, { basePath }) {
|
|
1005
|
-
|
|
892
|
+
await writeJsonFile(path.join(basePath, "package.json"), manifest);
|
|
1006
893
|
}
|
|
1007
894
|
async function addBuildScripts(manifest, options) {
|
|
1008
|
-
|
|
895
|
+
return options.flags.scripts ? addPackageJsonScripts(manifest, options, (scripts) => (scripts.build = addScript(expectedScripts.build, scripts.build), scripts.format = addScript("prettier --write --cache --ignore-unknown .", scripts.format), scripts["link-watch"] = addScript(expectedScripts["link-watch"], scripts["link-watch"]), scripts.lint = addScript("eslint .", scripts.lint), scripts.prepublishOnly = addScript(expectedScripts.prepublishOnly, scripts.prepublishOnly), scripts.watch = addScript(expectedScripts.watch, scripts.watch), scripts)) : !1;
|
|
1009
896
|
}
|
|
1010
897
|
function sortKeys(unordered) {
|
|
1011
|
-
|
|
898
|
+
return Object.keys(unordered).sort().reduce((obj, key) => (obj[key] = unordered[key], obj), {});
|
|
1012
899
|
}
|
|
900
|
+
/** @internal */
|
|
1013
901
|
function forceDependencyVersions(deps, versions = forcedPackageVersions) {
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
}
|
|
1020
|
-
export {
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
addScript,
|
|
1024
|
-
copyFileWithOverwritePrompt,
|
|
1025
|
-
disallowDuplicateEslintConfig,
|
|
1026
|
-
disallowDuplicatePrettierConfig,
|
|
1027
|
-
ensureDir,
|
|
1028
|
-
errorToUndefined,
|
|
1029
|
-
fileExists,
|
|
1030
|
-
findStudioV3Config,
|
|
1031
|
-
forceDependencyVersions,
|
|
1032
|
-
forcedDevPackageVersions,
|
|
1033
|
-
forcedPackageVersions,
|
|
1034
|
-
getPackage,
|
|
1035
|
-
hasSanityJson,
|
|
1036
|
-
isEmptyish,
|
|
1037
|
-
mergedPackages,
|
|
1038
|
-
mkdir,
|
|
1039
|
-
prompt,
|
|
1040
|
-
promptForPackageName,
|
|
1041
|
-
promptForRepoOrigin,
|
|
1042
|
-
readFile$2 as readFile,
|
|
1043
|
-
readJsonFile,
|
|
1044
|
-
resolveLatestVersions,
|
|
1045
|
-
sortKeys,
|
|
1046
|
-
validateBabelConfig,
|
|
1047
|
-
validateBannedFiles,
|
|
1048
|
-
validateDeprecatedDependencies,
|
|
1049
|
-
validateEsmOnly,
|
|
1050
|
-
validateIncompatiblePlugin,
|
|
1051
|
-
validateNodeEngine,
|
|
1052
|
-
validatePackageName$1 as validatePackageName,
|
|
1053
|
-
validatePackageType,
|
|
1054
|
-
validatePkgUtilsDependency,
|
|
1055
|
-
validatePkgUtilsVersion,
|
|
1056
|
-
validateSanityDependencies,
|
|
1057
|
-
validateScripts,
|
|
1058
|
-
validateSrcIndexFile,
|
|
1059
|
-
validateStudioConfig,
|
|
1060
|
-
validateTsConfig,
|
|
1061
|
-
writeFile,
|
|
1062
|
-
writeFileWithOverwritePrompt,
|
|
1063
|
-
writePackageJson,
|
|
1064
|
-
writePackageJsonDirect
|
|
1065
|
-
};
|
|
1066
|
-
//# sourceMappingURL=package.js.map
|
|
902
|
+
let entries = Object.entries(deps).map((entry) => {
|
|
903
|
+
let [pkg] = entry, forceVersion = versions[pkg];
|
|
904
|
+
return forceVersion ? [pkg, forceVersion] : entry;
|
|
905
|
+
});
|
|
906
|
+
return Object.fromEntries(entries);
|
|
907
|
+
}
|
|
908
|
+
export { validateStudioConfig as A, writeFileWithOverwritePrompt as B, validatePackageName$1 as C, validateSanityDependencies as D, validatePkgUtilsVersion as E, isEmptyish as F, promptForPackageName as H, mkdir as I, readFile$2 as L, copyFileWithOverwritePrompt as M, ensureDir as N, validateScripts as O, fileExists as P, readJsonFile as R, validateNodeEngine as S, validatePkgUtilsDependency as T, promptForRepoOrigin as U, prompt as V, mergedPackages as W, validateBabelConfig as _, getPackage as a, validateEsmOnly as b, writePackageJsonDirect as c, hasSanityJson as d, errorToUndefined as f, disallowDuplicatePrettierConfig as g, disallowDuplicateEslintConfig as h, forceDependencyVersions as i, validateTsConfig as j, validateSrcIndexFile as k, resolveLatestVersions as l, forcedPackageVersions as m, addPackageJsonScripts as n, sortKeys as o, forcedDevPackageVersions as p, addScript as r, writePackageJson as s, addBuildScripts as t, findStudioV3Config as u, validateBannedFiles as v, validatePackageType as w, validateIncompatiblePlugin as x, validateDeprecatedDependencies as y, writeFile as z };
|
|
909
|
+
|
|
910
|
+
//# sourceMappingURL=package.js.map
|