@query-key-gen/used-generator
Version:
Vite plugin that scans your project and tracks where `queryKey` values from `globalQueryKeys` are used — useful for dead query analysis, usage stats, or documentation.
375 lines (364 loc) • 13.4 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/index.ts
var index_exports = {};
__export(index_exports, {
default: () => QueryKeyUsedPlugin
});
module.exports = __toCommonJS(index_exports);
var import_path4 = __toESM(require("path"), 1);
// ../common/src/core/Programmer.ts
var import_fast_glob = __toESM(require("fast-glob"), 1);
var import_path = __toESM(require("path"), 1);
var import_typescript = __toESM(require("typescript"), 1);
var Programmer = class {
constructor(config) {
// getCompilerOptions = () => {
// const { options: compilerOptions } = ts.parseJsonConfigFileContent(
// ts.readConfigFile('tsconfig.node.json', ts.sys.readFile).config,
// {
// fileExists: ts.sys.fileExists,
// readFile: ts.sys.readFile,
// readDirectory: ts.sys.readDirectory,
// useCaseSensitiveFileNames: ts.sys.useCaseSensitiveFileNames
// },
// path.dirname('.')
// );
// return compilerOptions;
// };
this.findFiles = (program) => __async(this, null, function* () {
const config = this.config;
const sourceFiles = program.getSourceFiles().filter((item) => {
return !item.isDeclarationFile;
});
const outputFile = import_typescript.default.createSourceFile(config.output, "", this.compilerOptions.target);
const projectFiles = sourceFiles.filter((item) => {
const relativePath = import_path.default.relative(process.cwd(), item.fileName);
const outputPath = import_path.default.relative(process.cwd(), config.output);
return !relativePath.includes(outputPath);
});
return {
outputFile,
projectFiles
};
});
this.config = config;
this.compilerOptions = {
target: import_typescript.default.ScriptTarget.ES2015
};
}
// Getter로 createProgram 정의
get create() {
return () => __async(this, null, function* () {
const files = yield (0, import_fast_glob.default)(this.config.path, {
onlyFiles: true
});
const program = import_typescript.default.createProgram({
rootNames: files,
options: this.compilerOptions
});
return {
program,
checker: program.getTypeChecker()
};
});
}
};
// ../common/src/core/GenericRunner.ts
var GenericRunner = class {
constructor(programmer, config) {
this.programmer = programmer;
this.config = config;
}
process() {
return __async(this, null, function* () {
console.log("\u{1F527} Base process logic");
});
}
execute() {
this.process();
}
};
// src/core/QueryKeyUsedInfoGenerator.ts
var import_fs2 = __toESM(require("fs"), 1);
var import_typescript3 = __toESM(require("typescript"), 1);
// src/queryFinder/QueryKeyUsedFinder.ts
var import_typescript2 = __toESM(require("typescript"), 1);
var QueryKeyUsedFinder;
((QueryKeyUsedFinder2) => {
QueryKeyUsedFinder2.find = (sourceFiles, globalQueryKeyName) => {
const globalQueryKeys = sourceFiles.map((sourceFile) => {
return findGlobalQueryKeys(sourceFile, globalQueryKeyName);
});
const queryKeyByDeclaration = globalQueryKeys.flatMap((keys) => {
const usedDeclarations = keys.findNode.map((key) => {
const declaration = findUsedDeclaration(key.node);
return __spreadProps(__spreadValues({}, key), {
declaration,
sourceFile: keys.sourceFile
});
});
return usedDeclarations;
});
return queryKeyByDeclaration;
};
const findGlobalQueryKeys = (node, globalQueryKeyName) => {
const findNode = [];
const findText = [globalQueryKeyName];
const visitor = (node2) => {
if (import_typescript2.default.isImportDeclaration(node2)) {
return;
}
if (import_typescript2.default.isCallExpression(node2) || import_typescript2.default.isIdentifier(node2)) {
if (import_typescript2.default.isIdentifier(node2) && findText.includes(node2.getText())) {
const grandParent = node2.parent.parent;
if (import_typescript2.default.isPropertyAccessExpression(node2.parent)) {
const isGlobalQueryKey = findText.some(
(text) => grandParent.getText().includes(text)
);
findNode.push({
node: node2,
parent: isGlobalQueryKey ? grandParent : null
});
}
}
}
import_typescript2.default.forEachChild(node2, visitor);
};
visitor(node);
return { findNode, sourceFile: node };
};
const findUsedDeclaration = (node) => {
let parentNode = node.parent;
let declaration = null;
while (parentNode) {
if (import_typescript2.default.isFunctionDeclaration(parentNode)) {
declaration = parentNode;
break;
}
if (import_typescript2.default.isVariableDeclaration(parentNode) && parentNode.initializer && import_typescript2.default.isArrowFunction(parentNode.initializer)) {
declaration = parentNode;
break;
}
parentNode = parentNode.parent;
}
return declaration;
};
})(QueryKeyUsedFinder || (QueryKeyUsedFinder = {}));
// src/utils/index.ts
var import_child_process = require("child_process");
var import_fs = __toESM(require("fs"), 1);
var import_path2 = __toESM(require("path"), 1);
var prettify = (output) => {
const prettier = import_path2.default.resolve("./node_modules/.bin/prettier");
if (import_fs.default.existsSync(prettier)) (0, import_child_process.execSync)(`${prettier} --write --cache ${output}`);
};
var logger = (() => {
const prefix = "[QUERY-KEY-USED-GENERATOR]: ";
const log = () => {
return {
warn: (msg) => {
console.warn(`[WARN]${prefix}${msg}`);
},
error: (msg) => {
console.error(`[ERROR]${prefix}${msg}`);
},
info: (msg) => {
console.log(`[INFO]${prefix}${msg}`);
}
};
};
return log();
})();
// src/core/QueryKeyUsedInfoGenerator.ts
var import_path3 = __toESM(require("path"), 1);
var QueryKeyUsedInfoGenerator = class extends GenericRunner {
constructor() {
super(...arguments);
this.process = () => __async(this, null, function* () {
const { program } = yield this.programmer.create();
const { projectFiles } = yield this.programmer.findFiles(program);
const queryKeyUsedInfo = QueryKeyUsedFinder.find(
projectFiles,
this.config.globalQueryKeyName
);
const result = this.makeQueryKeyUsedInfo(queryKeyUsedInfo);
this.writeInfo(result);
prettify(this.config.output);
});
this.makeQueryKeyUsedInfo = (queryKeyUsedInfo) => {
const result = queryKeyUsedInfo.map((item) => {
var _a, _b;
const { declaration, sourceFile, parent } = item;
const declarationName = function() {
var _a2;
if (!declaration) {
return;
}
if (import_typescript3.default.isVariableDeclaration(declaration)) {
return declaration.name.getText();
}
if (import_typescript3.default.isFunctionDeclaration(declaration)) {
return (_a2 = declaration.name) == null ? void 0 : _a2.getText();
}
return;
}();
const fileName = import_path3.default.relative(process.cwd(), sourceFile.fileName).replace("src/", "");
return {
sourceFile: {
name: fileName
},
["query-key"]: {
name: parent == null ? void 0 : parent.getText(),
pos: parent == null ? void 0 : parent.pos,
end: parent == null ? void 0 : parent.end
},
func: {
name: declarationName,
pos: (_a = declaration == null ? void 0 : declaration.pos) != null ? _a : 0,
end: (_b = declaration == null ? void 0 : declaration.end) != null ? _b : 0
}
};
});
return result;
};
this.writeType = () => {
return `
interface QueryKeyUsedInfo {
sourceFile: {
name: string;
};
["query-key"]: {
name: string;
pos: number;
end: number;
};
func: {
name: string;
pos: number;
end: number;
};
}
`;
};
this.writeInfo = (result) => {
const content = `
${this.writeType()}
export const queryKeyUsedInfo : QueryKeyUsedInfo[] = ${JSON.stringify(result)};
`;
import_fs2.default.writeFileSync(this.config.output, content);
};
}
};
// src/types/config.ts
var import_zod = require("zod");
var configSchema = import_zod.z.object({
output: import_zod.z.string().optional().catch("src/query-key-used-info.ts"),
path: import_zod.z.string().optional().catch("./src/**/*.{jsx,tsx,ts}"),
globalQueryKeyName: import_zod.z.string().optional().catch("globalQueryKeys"),
ignoreFiles: import_zod.z.array(import_zod.z.string()).optional().catch([])
});
var defaultConfig = {
output: "./src/query-key-used-info.ts",
path: "./src/**/*.{jsx,tsx,ts}",
ignoreFiles: [".d.ts", "queryKeys.ts"],
globalQueryKeyName: "globalQueryKeys"
};
// src/index.ts
function QueryKeyUsedPlugin(_config) {
var _a;
const config = configSchema.parse(__spreadProps(__spreadValues(__spreadValues({}, defaultConfig), _config != null ? _config : {}), {
ignoreFiles: [...defaultConfig.ignoreFiles, ...(_a = _config == null ? void 0 : _config.ignoreFiles) != null ? _a : []]
}));
const rootDir = process.cwd();
const ignoreFiles = config.ignoreFiles;
const program = new Programmer(config);
const generator = new QueryKeyUsedInfoGenerator(program, config);
return {
name: "query-key-used-generator",
enforce: "pre",
configureServer(server) {
const listener = (absolutePath = "") => {
const filePath = import_path4.default.relative(rootDir, absolutePath);
const outputPath = import_path4.default.relative(rootDir, config.output);
if (!filePath.startsWith("src") || filePath.startsWith(outputPath)) return;
if (ignoreFiles.some((item) => {
const ignorePath = import_path4.default.relative(process.cwd(), item);
return filePath.includes(ignorePath);
})) {
return;
}
generator.execute();
};
server.watcher.on("add", listener);
server.watcher.on("change", listener);
server.watcher.on("unlink", listener);
},
buildStart() {
generator.execute();
}
};
}