@graphql-codegen/cli
Version:
<p align="center"> <img src="https://github.com/dotansimha/graphql-code-generator/blob/master/logo.png?raw=true" /> </p>
124 lines (123 loc) • 5.63 kB
JavaScript
import { lifecycleHooks } from './hooks.js';
import { executeCodegen } from './codegen.js';
import { createWatcher } from './utils/watcher.js';
import { readFile, writeFile, unlinkFile, mkdirp } from './utils/file-system.js';
import { dirname, join, isAbsolute } from 'path';
import { debugLog } from './utils/debugging.js';
import { ensureContext } from './config.js';
import { createHash } from 'crypto';
const hash = (content) => createHash('sha1').update(content).digest('base64');
export async function generate(input, saveToFile = true) {
const context = ensureContext(input);
const config = context.getConfig();
await context.profiler.run(() => lifecycleHooks(config.hooks).afterStart(), 'Lifecycle: afterStart');
let previouslyGeneratedFilenames = [];
function removeStaleFiles(config, generationResult) {
const filenames = generationResult.map(o => o.filename);
// find stale files from previous build which are not present in current build
const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
staleFilenames.forEach(filename => {
if (shouldOverwrite(config, filename)) {
return unlinkFile(filename, err => {
const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
if (err) {
debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
}
else {
debugLog(`Removed stale file: ${prettyFilename}`);
}
});
}
});
previouslyGeneratedFilenames = filenames;
}
const recentOutputHash = new Map();
async function writeOutput(generationResult) {
if (!saveToFile) {
return generationResult;
}
if (config.watch) {
removeStaleFiles(config, generationResult);
}
await context.profiler.run(async () => {
await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
}, 'Lifecycle: beforeAllFileWrite');
await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
const previousHash = recentOutputHash.get(result.filename) || (await hashFile(result.filename));
const exists = previousHash !== null;
// Store previous hash to avoid reading from disk again
if (previousHash) {
recentOutputHash.set(result.filename, previousHash);
}
if (!shouldOverwrite(config, result.filename) && exists) {
return;
}
const content = result.content || '';
const currentHash = hash(content);
if (previousHash && currentHash === previousHash) {
debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
return;
}
// skip updating file in dry mode
if (context.checkMode) {
context.checkModeStaleFiles.push(result.filename);
return;
}
if (content.length === 0) {
return;
}
await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
const absolutePath = isAbsolute(result.filename)
? result.filename
: join(input.cwd || process.cwd(), result.filename);
const basedir = dirname(absolutePath);
await mkdirp(basedir);
await writeFile(absolutePath, content);
recentOutputHash.set(result.filename, currentHash);
await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
})), 'Write files');
await context.profiler.run(() => lifecycleHooks(config.hooks).afterAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: afterAllFileWrite');
return generationResult;
}
// watch mode
if (config.watch) {
return createWatcher(context, writeOutput);
}
const outputFiles = await context.profiler.run(() => executeCodegen(context), 'executeCodegen');
await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
if (context.profilerOutput) {
await writeFile(join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
}
return outputFiles;
}
function shouldOverwrite(config, outputPath) {
const globalValue = config.overwrite === undefined ? true : !!config.overwrite;
const outputConfig = config.generates[outputPath];
if (!outputConfig) {
debugLog(`Couldn't find a config of ${outputPath}`);
return globalValue;
}
if (isConfiguredOutput(outputConfig) && typeof outputConfig.overwrite === 'boolean') {
return outputConfig.overwrite;
}
return globalValue;
}
function isConfiguredOutput(output) {
return typeof output.plugins !== 'undefined';
}
async function hashFile(filePath) {
try {
return hash(await readFile(filePath));
}
catch (err) {
if (err && err.code === 'ENOENT') {
// return null if file does not exist
return null;
}
// rethrow unexpected errors
throw err;
}
}