@patchworkdev/pdk
Version:
Patchwork Development Kit
186 lines (185 loc) • 7.74 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 (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.tsLoaderSync = tsLoaderSync;
exports.tsLoader = tsLoader;
const fs = __importStar(require("fs"));
const module_1 = require("module");
const path = __importStar(require("path"));
const ts_morph_1 = require("ts-morph");
const error_1 = require("./error");
const logger_1 = require("./logger");
// Added to grab the files dependencies so they can be added to the compilation
function collectDependencies(filePath) {
const deps = new Set();
const queue = [filePath];
while (queue.length > 0) {
const currentPath = queue.pop();
if (deps.has(currentPath))
continue;
deps.add(currentPath);
try {
const source = fs.readFileSync(currentPath, 'utf-8');
// Very basic import scanning - you might want to make this more robust
const importMatches = source.matchAll(/from\s+['"](\.[^'"]+)['"]/g);
for (const match of importMatches) {
const importPath = match[1];
try {
// Try to resolve the TypeScript file directly
const resolvedPath = tryResolveTypescript(importPath, path.dirname(currentPath));
if (resolvedPath) {
queue.push(resolvedPath);
}
}
catch (error) {
logger_1.logger.debug(`Could not resolve import ${importPath}: ${error}`);
}
}
}
catch (error) {
logger_1.logger.debug(`Error processing dependency ${currentPath}: ${error}`);
}
}
return deps;
}
function tryResolveTypescript(request, containingPath) {
const extensions = ['.ts', '.tsx', '.d.ts'];
const possiblePaths = [];
// Try exact path with extensions, check for index.*
for (const ext of extensions) {
possiblePaths.push(path.resolve(containingPath, request + ext), path.resolve(containingPath, request, `index${ext}`));
}
// Also try exact path in case it already has an extension
possiblePaths.push(path.resolve(containingPath, request));
for (const possiblePath of possiblePaths) {
if (fs.existsSync(possiblePath)) {
return possiblePath;
}
}
return undefined;
}
function tryResolveCompiled(request, containingPath, compiledFiles) {
const tsPath = tryResolveTypescript(request, containingPath);
if (tsPath && compiledFiles.has(tsPath)) {
return tsPath;
}
return undefined;
}
function tsLoaderSync(filePath, options) {
try {
logger_1.logger.debug(`Attempting to access ${filePath}`);
fs.accessSync(filePath);
}
catch (error) {
throw new error_1.PDKError(error_1.ErrorCode.FILE_NOT_FOUND, `Unable to access file at ${filePath}`);
}
// Collect all dependencies first
const dependencies = collectDependencies(filePath);
logger_1.logger.debug(`Found ${dependencies.size} dependencies`);
// Create a new project instance with in-memory filesystem
const project = new ts_morph_1.Project({
compilerOptions: {
target: ts_morph_1.ScriptTarget.Latest,
module: ts_morph_1.ModuleKind.CommonJS,
moduleResolution: ts_morph_1.ts.ModuleResolutionKind.NodeNext,
...options?.compilerOptions,
},
skipLoadingLibFiles: true,
useInMemoryFileSystem: true,
});
// Add all files to the in-memory filesystem
for (const depPath of dependencies) {
const source = fs.readFileSync(depPath, 'utf-8');
project.createSourceFile(depPath, source);
}
// Get all output files
const compiledFiles = new Map();
project.getSourceFiles().forEach((sourceFile) => {
const output = sourceFile.getEmitOutput();
const compiled = output.getOutputFiles()[0].getText();
compiledFiles.set(sourceFile.getFilePath(), compiled);
});
// Create a new module object for the main file
const moduleObj = new module_1.Module(filePath);
moduleObj.filename = filePath;
moduleObj.paths = module.paths;
// Set up module interception
let originalRequire;
if (options?.moduleOverrides || compiledFiles.size > 1) {
logger_1.logger.debug(`Setting up module interception for ${compiledFiles.size} files`);
originalRequire = module_1.Module.prototype.require;
const newRequire = function (id) {
// First check explicit overrides
const override = options?.moduleOverrides?.[id];
if (override) {
return require(override);
}
// Then check if it's a relative import
if (id.startsWith('.')) {
// First try to resolve as a TypeScript file
const resolvedTsPath = tryResolveCompiled(id, path.dirname(this.filename), compiledFiles);
if (resolvedTsPath) {
const compiledCode = compiledFiles.get(resolvedTsPath);
const mod = new module_1.Module(resolvedTsPath);
mod.filename = resolvedTsPath;
mod.paths = this.paths;
mod._compile(compiledCode, resolvedTsPath);
return mod.exports;
}
// If not found as TypeScript, try normal require
try {
return originalRequire.call(this, id);
}
catch (error) {
// If normal require fails, try one more time with .js extension
// This handles cases where TypeScript emits require('./file') but Node expects require('./file.js')
if (error.code === 'MODULE_NOT_FOUND') {
return originalRequire.call(this, id + '.js');
}
throw error;
}
}
// Fall back to normal require for non-relative imports
return originalRequire.call(this, id);
};
Object.assign(newRequire, originalRequire);
module_1.Module.prototype.require = newRequire;
}
try {
// Execute the compiled code
const compiledCode = compiledFiles.get(filePath);
moduleObj._compile(compiledCode, filePath);
logger_1.logger.debug(`File compiled successfully: ${filePath}`);
return moduleObj.exports;
}
finally {
if (originalRequire) {
module_1.Module.prototype.require = originalRequire;
}
}
}
async function tsLoader(path, options) {
return Promise.resolve(tsLoaderSync(path, options));
}