Skip to content

Commit

Permalink
fs async
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilkisiela authored and charlypoly committed Feb 9, 2022
1 parent fb97b4e commit f2e1420
Show file tree
Hide file tree
Showing 5 changed files with 347 additions and 210 deletions.
2 changes: 1 addition & 1 deletion packages/graphql-codegen-cli/package.json
Expand Up @@ -61,6 +61,7 @@
"debounce": "^1.2.0",
"dependency-graph": "^0.11.0",
"detect-indent": "^6.0.0",
"fs-extra": "^9.1.0",
"glob": "^7.1.6",
"globby": "^11.0.4",
"graphql-config": "^4.1.0",
Expand All @@ -72,7 +73,6 @@
"listr-update-renderer": "^0.5.0",
"log-symbols": "^4.0.0",
"minimatch": "^3.0.4",
"mkdirp": "^1.0.4",
"string-env-interpolation": "^1.0.1",
"ts-log": "^2.2.3",
"tslib": "~2.3.0",
Expand Down
58 changes: 50 additions & 8 deletions packages/graphql-codegen-cli/src/generate-and-save.ts
Expand Up @@ -2,8 +2,8 @@ import { lifecycleHooks } from './hooks';
import { Types } from '@graphql-codegen/plugin-helpers';
import { executeCodegen } from './codegen';
import { createWatcher } from './utils/watcher';
import { fileExists, readSync, writeSync, unlinkFile } from './utils/file-system';
import mkdirp from 'mkdirp';
import { fileExists, readFile, writeFile, unlinkFile } from './utils/file-system';
import { mkdirp } from 'fs-extra';
import { dirname, join, isAbsolute } from 'path';
import { debugLog } from './utils/debugging';
import { CodegenContext, ensureContext } from './config';
Expand All @@ -26,7 +26,7 @@ export async function generate(
const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
staleFilenames.forEach(filename => {
if (shouldOverwrite(config, filename)) {
unlinkFile(filename, err => {
return unlinkFile(filename, err => {
const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
if (err) {
debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
Expand All @@ -50,9 +50,51 @@ export async function generate(
}

await context.profiler.run(
() => lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename)),
async () => {
await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));

await Promise.all(
generationResult.map(async (result: Types.FileOutput) => {
const exists = await fileExists(result.filename);

if (!shouldOverwrite(config, result.filename) && exists) {
return;
}

const content = result.content || '';
const currentHash = hash(content);
let previousHash = recentOutputHash.get(result.filename);

if (!previousHash && exists) {
previousHash = hash(await readFile(result.filename));
}

if (previousHash && currentHash === previousHash) {
debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);

return;
}

if (content.length === 0) {
return;
}

recentOutputHash.set(result.filename, currentHash);
const basedir = dirname(result.filename);
await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
await mkdirp(basedir);
const absolutePath = isAbsolute(result.filename)
? result.filename
: join(input.cwd || process.cwd(), result.filename);
await writeFile(absolutePath, result.content);
await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
})
)
},
'Lifecycle: beforeAllFileWrite'
);
)

await context.profiler.run(
() =>
Expand All @@ -69,7 +111,7 @@ export async function generate(
let previousHash = recentOutputHash.get(result.filename);

if (!previousHash && exists) {
previousHash = hash(readSync(result.filename));
previousHash = hash(await readFile(result.filename));
}

if (previousHash && currentHash === previousHash) {
Expand All @@ -90,7 +132,7 @@ export async function generate(
const absolutePath = isAbsolute(result.filename)
? result.filename
: join(input.cwd || process.cwd(), result.filename);
writeSync(absolutePath, result.content);
await writeFile(absolutePath, result.content);
await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
})
Expand All @@ -117,7 +159,7 @@ export async function generate(
await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');

if (context.profilerOutput) {
writeSync(join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
await writeFile(join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
}

return outputFiles;
Expand Down
16 changes: 8 additions & 8 deletions packages/graphql-codegen-cli/src/utils/file-system.ts
@@ -1,21 +1,21 @@
import { writeFileSync, statSync, readFileSync, unlink } from 'fs';
import { writeFile as fsWriteFile, stat as fsStat, readFile as fsReadFile, unlink as fsUnlink } from 'fs-extra';

export function writeSync(filepath: string, content: string) {
return writeFileSync(filepath, content);
export function writeFile(filepath: string, content: string) {
return fsWriteFile(filepath, content);
}

export function readSync(filepath: string) {
return readFileSync(filepath, 'utf-8');
export function readFile(filepath: string) {
return fsReadFile(filepath, 'utf-8');
}

export function fileExists(filePath: string): boolean {
export async function fileExists(filePath: string): Promise<boolean> {
try {
return statSync(filePath).isFile();
return (await fsStat(filePath)).isFile();
} catch (err) {
return false;
}
}

export function unlinkFile(filePath: string, cb?: (err?: Error) => any): void {
unlink(filePath, cb);
fsUnlink(filePath, cb);
}
22 changes: 11 additions & 11 deletions packages/graphql-codegen-cli/tests/generate-and-save.spec.ts
Expand Up @@ -21,7 +21,7 @@ describe('generate-and-save', () => {

test('allow to specify overwrite for specific output (should write file)', async () => {
const filename = 'overwrite.ts';
const writeSpy = jest.spyOn(fs, 'writeSync').mockImplementation();
const writeSpy = jest.spyOn(fs, 'writeFile').mockImplementation();

const output = await generate(
{
Expand All @@ -47,10 +47,10 @@ describe('generate-and-save', () => {

test('allow to specify overwrite for specific output (should not write file)', async () => {
const filename = 'overwrite.ts';
const writeSpy = jest.spyOn(fs, 'writeSync').mockImplementation();
const writeSpy = jest.spyOn(fs, 'writeFile').mockImplementation();
// forces file to exist
const fileExistsSpy = jest.spyOn(fs, 'fileExists');
fileExistsSpy.mockImplementation(file => file === filename);
fileExistsSpy.mockImplementation(async file => file === filename);

const output = await generate(
{
Expand Down Expand Up @@ -78,7 +78,7 @@ describe('generate-and-save', () => {

test('should use global overwrite option and write a file', async () => {
const filename = 'overwrite.ts';
const writeSpy = jest.spyOn(fs, 'writeSync').mockImplementation();
const writeSpy = jest.spyOn(fs, 'writeFile').mockImplementation();

const output = await generate(
{
Expand All @@ -103,10 +103,10 @@ describe('generate-and-save', () => {

test('should use global overwrite option and not write a file', async () => {
const filename = 'overwrite.ts';
const writeSpy = jest.spyOn(fs, 'writeSync').mockImplementation();
const writeSpy = jest.spyOn(fs, 'writeFile').mockImplementation();
// forces file to exist
const fileExistsSpy = jest.spyOn(fs, 'fileExists');
fileExistsSpy.mockImplementation(file => file === filename);
fileExistsSpy.mockImplementation(async file => file === filename);

const output = await generate(
{
Expand All @@ -133,12 +133,12 @@ describe('generate-and-save', () => {

test('should overwrite a file by default', async () => {
const filename = 'overwrite.ts';
const writeSpy = jest.spyOn(fs, 'writeSync').mockImplementation();
const readSpy = jest.spyOn(fs, 'readSync').mockImplementation();
readSpy.mockImplementation(_f => '');
const writeSpy = jest.spyOn(fs, 'writeFile').mockImplementation();
const readSpy = jest.spyOn(fs, 'readFile').mockImplementation();
readSpy.mockImplementation(async _f => '');
// forces file to exist
const fileExistsSpy = jest.spyOn(fs, 'fileExists');
fileExistsSpy.mockImplementation(file => file === filename);
fileExistsSpy.mockImplementation(async file => file === filename);

const output = await generate(
{
Expand Down Expand Up @@ -197,7 +197,7 @@ describe('generate-and-save', () => {
});
test('should extract a document from the gql tag (imported from apollo-server)', async () => {
const filename = 'overwrite.ts';
const writeSpy = jest.spyOn(fs, 'writeSync').mockImplementation();
const writeSpy = jest.spyOn(fs, 'writeFile').mockImplementation();

const output = await generate(
{
Expand Down

0 comments on commit f2e1420

Please sign in to comment.