Skip to content

Commit

Permalink
feat: add a script that determines wheter a deprecated field is used …
Browse files Browse the repository at this point in the history
…or not
  • Loading branch information
sixmen committed Feb 27, 2024
1 parent c5ab9a0 commit 7f02373
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 0 deletions.
4 changes: 4 additions & 0 deletions packages/graphql/bin/check-deprecated
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env node

var path = require('path');
require(path.resolve(__dirname, '../lib/cli/check-deprecated.js')).run(process.argv);
1 change: 1 addition & 0 deletions packages/graphql/lib/cli/check-deprecated.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export declare function run(argv: string[]): Promise<void>;
64 changes: 64 additions & 0 deletions packages/graphql/lib/cli/check-deprecated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = void 0;
const promises_1 = __importDefault(require("fs/promises"));
const graphql_1 = require("graphql");
const common_1 = require("./common");
function isDeprecated(directives) {
for (const directive of directives || []) {
if (directive.name.value === 'deprecated') {
return true;
}
}
return false;
}
async function check(schema_file, query_list_file) {
const schema = (0, graphql_1.buildSchema)(await promises_1.default.readFile(schema_file, 'utf-8'));
const query_list = (0, common_1.extractQueryList)(await promises_1.default.readFile(query_list_file, 'utf-8'));
for (const query of query_list) {
if (!query) {
continue;
}
const parsed_query = (0, graphql_1.parse)(query);
const type_info = new graphql_1.TypeInfo(schema);
const visitor = {
enter(node) {
if (node.kind === graphql_1.Kind.FIELD) {
const type = type_info.getParentType();
const field = type_info.getFieldDef();
if (type && field) {
const is_field_deprecated = isDeprecated(field.astNode?.directives);
if (is_field_deprecated) {
console.log(`${type.name}.${field.name} is deprecated`);
}
if (node.arguments) {
for (const argument of node.arguments) {
const field_arg = field.args.find((arg) => arg.name === argument.name.value);
if (field_arg) {
const is_arg_deprecated = isDeprecated(field_arg.astNode?.directives);
if (is_arg_deprecated) {
console.log(`${type.name}.${field.name}(${field_arg.name}) is deprecated`);
}
}
}
}
}
}
},
};
(0, graphql_1.visit)(parsed_query, (0, graphql_1.visitWithTypeInfo)(type_info, visitor));
}
return schema;
}
async function run(argv) {
if (argv.length < 4) {
throw new Error('Usage: check-deprecated <schema file> <query list> [type or field]');
}
const schema_file = argv[2];
const query_list_file = argv[3];
await check(schema_file, query_list_file);
}
exports.run = run;
1 change: 1 addition & 0 deletions packages/graphql/lib/cli/common.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export declare function extractQueryList(text: string): string[];
19 changes: 19 additions & 0 deletions packages/graphql/lib/cli/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractQueryList = void 0;
function extractQueryList(text) {
return text
.split('\n')
.map((str) => str
.trim()
.replace(/^query /, '✓query ')
.replace(/^mutation /, '✓mutation ')
.replace(/^fragment /, '✓fragment '))
.join(' ')
.replace(/mutation /g, '\nmutation ')
.replace(/query /g, '\nquery ')
.replace(/fragment /g, '\nfragment ')
.trim()
.split('\n');
}
exports.extractQueryList = extractQueryList;
61 changes: 61 additions & 0 deletions packages/graphql/src/cli/check-deprecated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import fs from 'fs/promises';
import { ASTVisitor, buildSchema, ConstDirectiveNode, Kind, parse, TypeInfo, visit, visitWithTypeInfo } from 'graphql';
import { extractQueryList } from './common';

function isDeprecated(directives: readonly ConstDirectiveNode[] | undefined) {
for (const directive of directives || []) {
if (directive.name.value === 'deprecated') {
return true;
}
}
return false;
}

async function check(schema_file: string, query_list_file: string) {
const schema = buildSchema(await fs.readFile(schema_file, 'utf-8'));
const query_list = extractQueryList(await fs.readFile(query_list_file, 'utf-8'));
for (const query of query_list) {
if (!query) {
continue;
}
const parsed_query = parse(query);
const type_info = new TypeInfo(schema);
const visitor: ASTVisitor = {
enter(node) {
if (node.kind === Kind.FIELD) {
const type = type_info.getParentType();
const field = type_info.getFieldDef();
if (type && field) {
const is_field_deprecated = isDeprecated(field.astNode?.directives);
if (is_field_deprecated) {
console.log(`${type.name}.${field.name} is deprecated`);
}
if (node.arguments) {
for (const argument of node.arguments) {
const field_arg = field.args.find((arg) => arg.name === argument.name.value);
if (field_arg) {
const is_arg_deprecated = isDeprecated(field_arg.astNode?.directives);
if (is_arg_deprecated) {
console.log(`${type.name}.${field.name}(${field_arg.name}) is deprecated`);
}
}
}
}
}
}
},
};
visit(parsed_query, visitWithTypeInfo(type_info, visitor));
}
return schema;
}

export async function run(argv: string[]) {
if (argv.length < 4) {
throw new Error('Usage: check-deprecated <schema file> <query list> [type or field]');
}
const schema_file = argv[2];
const query_list_file = argv[3];

await check(schema_file, query_list_file);
}
17 changes: 17 additions & 0 deletions packages/graphql/src/cli/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export function extractQueryList(text: string) {
return text
.split('\n')
.map((str) =>
str
.trim()
.replace(/^query /, '✓query ')
.replace(/^mutation /, '✓mutation ')
.replace(/^fragment /, '✓fragment '),
)
.join(' ')
.replace(/mutation /g, '\nmutation ')
.replace(/query /g, '\nquery ')
.replace(/fragment /g, '\nfragment ')
.trim()
.split('\n');
}

0 comments on commit 7f02373

Please sign in to comment.