type-compiler
Version:
A TypeScript compiler plugin for enhanced runtime type checking and analysis with Zod validation
235 lines (234 loc) • 8.75 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.FileCacheSchema = exports.FileCache = exports.globalTypeCache = void 0;
exports.getFileCache = getFileCache;
exports.isFileUnchanged = isFileUnchanged;
exports.markFileAsProcessed = markFileAsProcessed;
const zod_1 = require("zod");
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const crypto = __importStar(require("crypto"));
const logger_1 = require("./logger");
/**
* Global cache to store computed Zod schemas across transformer invocations
*/
exports.globalTypeCache = new Map();
/**
* File cache for tracking file modifications for incremental compilation
*/
class FileCache {
constructor(cachePath) {
this.cache = new Map();
this.initialized = false;
this.cachePath = cachePath;
this.initialized = false;
this.loadCache();
}
/**
* Load the cache from disk if a cache path is provided
*/
loadCache() {
if (this.initialized)
return;
if (this.cachePath && fs.existsSync(this.cachePath)) {
try {
logger_1.logger.debug(`Loading cache from ${this.cachePath}`);
const cacheData = JSON.parse(fs.readFileSync(this.cachePath, 'utf-8'));
for (const [key, value] of Object.entries(cacheData)) {
this.cache.set(key, value);
}
logger_1.logger.info(`Loaded ${this.cache.size} entries from cache`);
}
catch (error) {
logger_1.logger.error('Error loading cache', {
error: error instanceof Error ? error.message : String(error),
path: this.cachePath
});
// Continue without cache if there's an error
}
}
else if (this.cachePath) {
logger_1.logger.debug(`Cache file does not exist yet: ${this.cachePath}`);
}
this.initialized = true;
}
/**
* Save the cache to disk if a cache path is provided
*/
saveCache() {
if (!this.cachePath)
return;
try {
logger_1.logger.debug(`Saving cache to ${this.cachePath}`);
const cacheData = {};
for (const [key, value] of this.cache.entries()) {
cacheData[key] = value;
}
// Create directory if it doesn't exist
const cacheDir = path.dirname(this.cachePath);
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir, { recursive: true });
}
fs.writeFileSync(this.cachePath, JSON.stringify(cacheData, null, 2));
logger_1.logger.info(`Saved ${this.cache.size} entries to cache`);
}
catch (error) {
logger_1.logger.error('Error saving cache', {
error: error instanceof Error ? error.message : String(error),
path: this.cachePath
});
// Continue even if we can't save the cache
}
}
/**
* Check if a file has changed since the last time it was processed
*/
hasFileChanged(filePath) {
try {
if (!fs.existsSync(filePath)) {
// File doesn't exist, so consider it changed
logger_1.logger.debug(`File does not exist: ${filePath}`);
return true;
}
const stats = fs.statSync(filePath);
const currentTimestamp = stats.mtimeMs;
// Compute a hash of the file content
const fileContent = fs.readFileSync(filePath, 'utf-8');
const contentHash = crypto
.createHash('md5')
.update(fileContent)
.digest('hex');
const cachedInfo = this.cache.get(filePath);
if (!cachedInfo) {
// File not in cache, so consider it changed
logger_1.logger.trace(`File not in cache: ${filePath}`);
this.updateFile(filePath, currentTimestamp, contentHash);
logger_1.logger.incrementMetric('cacheMisses');
return true;
}
// Check if timestamp or content has changed
if (cachedInfo.timestamp !== currentTimestamp ||
cachedInfo.contentHash !== contentHash) {
logger_1.logger.trace(`File has changed: ${filePath}`);
this.updateFile(filePath, currentTimestamp, contentHash);
logger_1.logger.incrementMetric('cacheMisses');
return true;
}
logger_1.logger.trace(`File unchanged: ${filePath}`);
logger_1.logger.incrementMetric('cacheHits');
return false;
}
catch (error) {
logger_1.logger.error(`Error checking if file has changed: ${filePath}`, {
error: error instanceof Error ? error.message : String(error)
});
// If there's an error, assume the file has changed to be safe
logger_1.logger.incrementMetric('cacheMisses');
return true;
}
}
/**
* Update the cache entry for a file
*/
updateFile(filePath, timestamp, contentHash) {
this.cache.set(filePath, {
timestamp,
contentHash
});
}
/**
* Mark a file as processed without checking its content
*/
markFileProcessed(filePath) {
try {
const stats = fs.statSync(filePath);
const currentTimestamp = stats.mtimeMs;
const fileContent = fs.readFileSync(filePath, 'utf-8');
const contentHash = crypto
.createHash('md5')
.update(fileContent)
.digest('hex');
this.updateFile(filePath, currentTimestamp, contentHash);
logger_1.logger.trace(`Marked file as processed: ${filePath}`);
}
catch (error) {
logger_1.logger.error(`Error marking file as processed: ${filePath}`, {
error: error instanceof Error ? error.message : String(error)
});
}
}
}
exports.FileCache = FileCache;
/**
* Get or create a file cache based on the provided options
*/
function getFileCache(options) {
const cachePath = options.incrementalCompilation && options.incrementalCachePath
? path.resolve(options.incrementalCachePath)
: undefined;
if (cachePath) {
logger_1.logger.debug(`Creating file cache with path: ${cachePath}`);
}
else {
logger_1.logger.debug('Creating in-memory file cache');
}
return new FileCache(cachePath);
}
/**
* Check if a file has changed since the last compilation
*/
function isFileUnchanged(filePath) {
// This is a stub that would be replaced with the real implementation
// that uses the FileCache class
return false;
}
/**
* Mark a file as processed in the cache
*/
function markFileAsProcessed(filePath) {
// This is a stub that would be replaced with the real implementation
// that uses the FileCache class
}
exports.FileCacheSchema = z.object({
cache: z.any(),
cachePath: z.any(),
initialized: z.any(),
loadCache: z.any(),
saveCache: z.any(),
hasFileChanged: z.any(),
updateFile: z.any(),
markFileProcessed: z.any()
});