vite-plugin-graphql-codegen
Version:
Zero-config vite plugin that uses the vite file watcher to run graphql codegen programmatically without needing to start a separate watcher
242 lines (230 loc) • 7.68 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
const process = require('node:process');
const cli = require('@graphql-codegen/cli');
const vite = require('vite');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
const process__default = /*#__PURE__*/_interopDefaultCompat(process);
const RESET = "\x1B[0m";
const BRIGHT = "\x1B[1m";
const DIM = "\x1B[2m";
const FG_CYAN = "\x1B[36m";
const LOG_PREFIX = `${FG_CYAN}${BRIGHT}VITE PLUGIN GRAPHQL CODEGEN${RESET} `;
function debugLog(...args) {
console.log(LOG_PREFIX, DIM, ...args, RESET);
}
async function getDocumentPaths(context) {
const config = context.getConfig();
const sourceDocuments = Object.values(config.generates).map(
(output) => Array.isArray(output) ? void 0 : output.documents
);
if (config.documents) {
sourceDocuments.unshift(config.documents);
}
const normalized = sourceDocuments.filter((item) => item !== void 0).flat();
if (!normalized.length) return [];
const documents = await context.loadDocuments(normalized);
if (!documents.length) return [];
return documents.map(({ location = "" }) => location).filter(Boolean).map(vite.normalizePath);
}
async function getSchemaPaths(context) {
const config = context.getConfig();
const sourceSchemas = Object.values(config.generates).map(
(output) => Array.isArray(output) ? void 0 : output.schema
);
if (config.schema) {
sourceSchemas.unshift(config.schema);
}
const normalized = sourceSchemas.filter((item) => !!item).flat();
if (!normalized.length) return [];
const schemas = await context.loadSchema(
// loadSchema supports array of string, but typings are wrong
normalized
);
return schemas.extensions.sources.map(({ name = "" }) => name).filter(Boolean).map(vite.normalizePath);
}
function getGeneratesPaths(context) {
const config = context.getConfig();
return Object.keys(config.generates).map(vite.normalizePath);
}
function isCodegenConfig(filePath, context) {
if (!context.filepath) return false;
return vite.normalizePath(filePath) === vite.normalizePath(context.filepath);
}
function isGeneratedFile(filePath, context) {
const generatesPaths = getGeneratesPaths(context);
const normalizedFilePath = vite.normalizePath(filePath);
return generatesPaths.some((path) => normalizedFilePath.includes(path));
}
function createMatchCache(context, options) {
const cache = /* @__PURE__ */ new Set();
const refresh = async () => {
const matchers = [];
if (options.matchOnDocuments) matchers.push(getDocumentPaths(context));
if (options.matchOnSchemas) matchers.push(getSchemaPaths(context));
const results = await Promise.all(matchers);
const entries = results.flat().map(vite.normalizePath);
cache.clear();
for (const entry of entries) {
cache.add(entry);
}
};
return {
init: refresh,
refresh,
has: (filePath) => cache.has(vite.normalizePath(filePath)),
entries: () => Array.from(cache)
};
}
const modes = {
serve: "serve",
build: "build"
};
const { serve, build } = modes;
function isServeMode(mode) {
return mode === serve;
}
function isBuildMode(mode) {
return mode === build;
}
function GraphQLCodegen(options) {
let codegenContext;
let viteMode;
const {
runOnStart = true,
runOnBuild = true,
enableWatcher = true,
watchCodegenConfigFiles = true,
throwOnStart = false,
throwOnBuild = true,
matchOnDocuments = true,
matchOnSchemas = false,
project = null,
config = null,
configOverride = {},
configOverrideOnStart = {},
configOverrideOnBuild = {},
configOverrideWatcher = {},
configFilePathOverride,
debug = false
} = options ?? {};
const log = (...args) => {
if (!debug) return;
debugLog(...args);
};
const generateWithOverride = async (overrideConfig) => {
const currentConfig = codegenContext.getConfig();
return await cli.generate({
...currentConfig,
...configOverride,
...overrideConfig,
// Vite handles file watching
watch: false
});
};
if (options) log("Plugin initialized with options:", options);
return {
name: "graphql-codegen",
async config(_userConfig, env) {
try {
if (config) {
log("Manual config passed, creating codegen context");
codegenContext = new cli.CodegenContext({ config });
} else {
const cwd = process__default.cwd();
log("Loading codegen context:", configFilePathOverride ?? cwd);
codegenContext = await cli.loadContext(configFilePathOverride);
}
if (project != null) codegenContext.useProject(project);
log("Loading codegen context successful");
} catch (error) {
log("Loading codegen context failed");
throw error;
}
viteMode = env.command;
},
async buildStart() {
if (isServeMode(viteMode)) {
if (!runOnStart) return;
try {
await generateWithOverride(configOverrideOnStart);
log("Generation successful on start");
} catch (error) {
log("Generation failed on start");
if (throwOnStart) throw error;
}
}
if (isBuildMode(viteMode)) {
if (!runOnBuild) return;
try {
await generateWithOverride(configOverrideOnBuild);
log("Generation successful on build");
} catch (error) {
log("Generation failed on build");
if (throwOnBuild) throw error;
}
}
},
configureServer(server) {
if (!enableWatcher) return;
const matchCache = createMatchCache(codegenContext, {
matchOnDocuments,
matchOnSchemas
});
async function checkFile(filePath) {
log(`Checking file: ${filePath}`);
if (matchCache.has(filePath)) {
log("File is in match cache");
try {
await generateWithOverride(configOverrideWatcher);
log("Generation successful in file watcher");
} catch {
log("Generation failed in file watcher");
}
return;
}
if (isCodegenConfig(filePath, codegenContext)) {
log("Codegen config file matched, restarting vite");
server.restart();
return;
}
log("File did not match");
}
async function initializeWatcher() {
try {
log("Match cache initialing");
await matchCache.init();
if (watchCodegenConfigFiles) {
log("Adding codegen config files to watcher", matchCache.entries());
server.watcher.add(matchCache.entries());
}
log("Match cache initialized");
} catch (error) {
log("Match cache initialization failed", error);
}
server.watcher.on("add", async (filePath) => {
log(`File added: ${filePath}`);
if (isGeneratedFile(filePath, codegenContext)) {
log("File is a generated output file, skipping");
return;
}
try {
log("Match cache refreshing");
await matchCache.refresh();
log("Match cache refreshed");
} catch (error) {
log("Match cache refresh failed", error);
}
await checkFile(filePath);
});
server.watcher.on("change", async (filePath) => {
log(`File changed: ${filePath}`);
await checkFile(filePath);
});
}
initializeWatcher();
}
};
}
exports.GraphQLCodegen = GraphQLCodegen;
exports.default = GraphQLCodegen;