@graphql-codegen/cli
Version:
<p align="center"> <img src="https://github.com/dotansimha/graphql-code-generator/blob/master/logo.png?raw=true" /> </p>
132 lines (131 loc) • 5.55 kB
JavaScript
import { join } from 'path';
import { normalizeInstanceOrArray, normalizeOutputParam } from '@graphql-codegen/plugin-helpers';
import { isValidPath } from '@graphql-tools/utils';
import debounce from 'debounce';
import isGlob from 'is-glob';
import mm from 'micromatch';
import logSymbols from 'log-symbols';
import { executeCodegen } from '../codegen.js';
import { loadContext } from '../config.js';
import { lifecycleHooks } from '../hooks.js';
import { debugLog } from './debugging.js';
import { getLogger } from './logger.js';
function log(msg) {
// double spaces to inline the message with Listr
getLogger().info(` ${msg}`);
}
function emitWatching() {
log(`${logSymbols.info} Watching for changes...`);
}
export const createWatcher = (initalContext, onNext) => {
debugLog(`[Watcher] Starting watcher...`);
let config = initalContext.getConfig();
const files = [initalContext.filepath].filter(a => a);
const documents = normalizeInstanceOrArray(config.documents);
const schemas = normalizeInstanceOrArray(config.schema);
// Add schemas and documents from "generates"
Object.keys(config.generates)
.map(filename => normalizeOutputParam(config.generates[filename]))
.forEach(conf => {
schemas.push(...normalizeInstanceOrArray(conf.schema));
documents.push(...normalizeInstanceOrArray(conf.documents));
});
if (documents) {
documents.forEach(doc => {
if (typeof doc === 'string') {
files.push(doc);
}
else {
files.push(...Object.keys(doc));
}
});
}
schemas.forEach((schema) => {
if (isGlob(schema) || isValidPath(schema)) {
files.push(schema);
}
});
if (typeof config.watch !== 'boolean') {
files.push(...normalizeInstanceOrArray(config.watch));
}
let watcherSubscription;
const runWatcher = async () => {
const parcelWatcher = await import('@parcel/watcher');
debugLog(`[Watcher] Parcel watcher loaded...`);
let isShutdown = false;
const debouncedExec = debounce(() => {
if (!isShutdown) {
executeCodegen(initalContext)
.then(onNext, () => Promise.resolve())
.then(() => emitWatching());
}
}, 100);
emitWatching();
const ignored = [];
Object.keys(config.generates)
.map(filename => ({ filename, config: normalizeOutputParam(config.generates[filename]) }))
.forEach(entry => {
var _a;
if (entry.config.preset) {
const extension = (_a = entry.config.presetConfig) === null || _a === void 0 ? void 0 : _a.extension;
if (extension) {
ignored.push(join(entry.filename, '**', '*' + extension));
}
}
else {
ignored.push(entry.filename);
}
});
watcherSubscription = await parcelWatcher.subscribe(process.cwd(), async (_, events) => {
// it doesn't matter what has changed, need to run whole process anyway
await Promise.all(events.map(async ({ type: eventName, path }) => {
/**
* @parcel/watcher has no way to run watcher on specific files (https://github.com/parcel-bundler/watcher/issues/42)
* But we can use micromatch to filter out events that we don't care about
*/
if (!mm.contains(path, files))
return;
lifecycleHooks(config.hooks).onWatchTriggered(eventName, path);
debugLog(`[Watcher] triggered due to a file ${eventName} event: ${path}`);
const fullPath = join(process.cwd(), path);
// In ESM require is not defined
try {
delete require.cache[fullPath];
}
catch (err) { }
if (eventName === 'update' && config.configFilePath && fullPath === config.configFilePath) {
log(`${logSymbols.info} Config file has changed, reloading...`);
const context = await loadContext(config.configFilePath);
const newParsedConfig = context.getConfig();
newParsedConfig.watch = config.watch;
newParsedConfig.silent = config.silent;
newParsedConfig.overwrite = config.overwrite;
newParsedConfig.configFilePath = config.configFilePath;
config = newParsedConfig;
initalContext.updateConfig(config);
}
debouncedExec();
}));
}, { ignore: ignored });
debugLog(`[Watcher] Started`);
const shutdown = () => {
isShutdown = true;
debugLog(`[Watcher] Shutting down`);
log(`Shutting down watch...`);
watcherSubscription.unsubscribe();
lifecycleHooks(config.hooks).beforeDone();
};
process.once('SIGINT', shutdown);
process.once('SIGTERM', shutdown);
};
// the promise never resolves to keep process running
return new Promise((resolve, reject) => {
executeCodegen(initalContext)
.then(onNext, () => Promise.resolve())
.then(runWatcher)
.catch(err => {
watcherSubscription.unsubscribe();
reject(err);
});
});
};