@sanity/cli
Version:
Sanity CLI tool for managing Sanity installations, managing plugins, schemas and datasets
139 lines (137 loc) • 6.32 kB
JavaScript
;
var fs = require("node:fs/promises"), path = require("node:path"), node_worker_threads = require("node:worker_threads"), codegen = require("@sanity/codegen"), prettier = require("prettier"), cliWorker = require("./cliWorker.js"), telemetry = require("@sanity/telemetry");
const TypesGeneratedTrace = telemetry.defineTrace({
name: "Types Generated",
version: 0,
description: "Trace emitted when generating TypeScript types for queries"
}), generatedFileWarning = `/**
* ---------------------------------------------------------------------------------
* This file has been generated by Sanity TypeGen.
* Command: \`sanity typegen generate\`
*
* Any modifications made directly to this file will be overwritten the next time
* the TypeScript definitions are generated. Please make changes to the Sanity
* schema definitions and/or GROQ queries if you need to update these types.
*
* For more information on how to use Sanity TypeGen, visit the official documentation:
* https://www.sanity.io/docs/sanity-typegen
* ---------------------------------------------------------------------------------
*/
`;
async function typegenGenerateAction(args, context) {
const flags = args.extOptions, { output, workDir, telemetry: telemetry2 } = context, trace = telemetry2.trace(TypesGeneratedTrace);
trace.start();
const codegenConfig = await codegen.readConfig(flags["config-path"] || "sanity-typegen.json");
try {
if (!(await fs.stat(codegenConfig.schema)).isFile())
throw new Error(`Schema path is not a file: ${codegenConfig.schema}`);
} catch (err) {
if (err.code === "ENOENT") {
const hint = codegenConfig.schema === "./schema.json" ? ' - did you run "sanity schema extract"?' : "";
throw new Error(`Schema file not found: ${codegenConfig.schema}${hint}`);
}
throw err;
}
const outputPath = path.join(process.cwd(), codegenConfig.generates), outputDir = path.dirname(outputPath);
await fs.mkdir(outputDir, { recursive: !0 });
const workerPath = await cliWorker.getCliWorkerPath("typegenGenerate"), spinner = output.spinner({}).start("Generating types"), worker = new node_worker_threads.Worker(workerPath, {
workerData: {
workDir,
schemaPath: codegenConfig.schema,
searchPath: codegenConfig.path,
overloadClientMethods: codegenConfig.overloadClientMethods
},
env: process.env
}), typeFile = await fs.open(
outputPath,
// eslint-disable-next-line no-bitwise
fs.constants.O_TRUNC | fs.constants.O_CREAT | fs.constants.O_WRONLY
);
typeFile.write(generatedFileWarning);
const stats = {
queryFilesCount: 0,
errors: 0,
queriesCount: 0,
schemaTypesCount: 0,
unknownTypeNodesGenerated: 0,
typeNodesGenerated: 0,
emptyUnionTypeNodesGenerated: 0,
size: 0
};
await new Promise((resolve, reject) => {
worker.addListener("message", (msg) => {
if (msg.type === "error") {
if (msg.fatal) {
trace.error(msg.error), reject(msg.error);
return;
}
const errorMessage = msg.filename ? `${msg.error.message} in "${msg.filename}"` : msg.error.message;
spinner.fail(errorMessage), stats.errors++;
return;
}
if (msg.type === "complete") {
resolve();
return;
}
if (msg.type === "typemap") {
let typeMapStr = `// Query TypeMap
`;
typeMapStr += msg.typeMap, typeFile.write(typeMapStr), stats.size += Buffer.byteLength(typeMapStr);
return;
}
let fileTypeString = `// Source: ${msg.filename}
`;
if (msg.type === "schema") {
stats.schemaTypesCount += msg.length, fileTypeString += msg.schema, typeFile.write(fileTypeString);
return;
}
if (msg.type === "types") {
stats.queryFilesCount++;
for (const {
queryName,
query,
type,
typeNodesGenerated,
unknownTypeNodesGenerated,
emptyUnionTypeNodesGenerated
} of msg.types)
fileTypeString += `// Variable: ${queryName}
`, fileTypeString += `// Query: ${query.replace(/(\r\n|\n|\r)/gm, "").trim()}
`, fileTypeString += type, stats.queriesCount++, stats.typeNodesGenerated += typeNodesGenerated, stats.unknownTypeNodesGenerated += unknownTypeNodesGenerated, stats.emptyUnionTypeNodesGenerated += emptyUnionTypeNodesGenerated;
typeFile.write(`${fileTypeString}
`), stats.size += Buffer.byteLength(fileTypeString);
}
}), worker.addListener("error", reject);
}), await typeFile.close();
const prettierConfig = codegenConfig.formatGeneratedCode ? await prettier.resolveConfig(outputPath).catch((err) => (output.warn(`Failed to load prettier config: ${err.message}`), null)) : null;
if (prettierConfig) {
const formatFile = await fs.open(outputPath, fs.constants.O_RDWR);
try {
const code = await formatFile.readFile(), formattedCode = await prettier.format(code.toString(), {
...prettierConfig,
parser: "typescript"
});
await formatFile.truncate(), await formatFile.write(formattedCode, 0), spinner.info("Formatted generated types with Prettier");
} catch (err) {
output.warn(`Failed to format generated types with Prettier: ${err.message}`);
} finally {
await formatFile.close();
}
}
trace.log({
outputSize: stats.size,
queriesCount: stats.queriesCount,
schemaTypesCount: stats.schemaTypesCount,
queryFilesCount: stats.queryFilesCount,
filesWithErrors: stats.errors,
typeNodesGenerated: stats.typeNodesGenerated,
unknownTypeNodesGenerated: stats.unknownTypeNodesGenerated,
unknownTypeNodesRatio: stats.typeNodesGenerated > 0 ? stats.unknownTypeNodesGenerated / stats.typeNodesGenerated : 0,
emptyUnionTypeNodesGenerated: stats.emptyUnionTypeNodesGenerated,
configOverloadClientMethods: codegenConfig.overloadClientMethods
}), trace.complete(), stats.errors > 0 && spinner.warn(`Encountered errors in ${stats.errors} files while generating types`), spinner.succeed(
`Generated TypeScript types for ${stats.schemaTypesCount} schema types and ${stats.queriesCount} GROQ queries in ${stats.queryFilesCount} files into: ${codegenConfig.generates}`
);
}
exports.default = typegenGenerateAction;
//# sourceMappingURL=generateAction.js.map