@mdfriday/foundry
Version:
The core engine of MDFriday. Convert Markdown and shortcodes into fully themed static sites – Hugo-style, powered by TypeScript.
334 lines • 12.6 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.Modules = void 0;
exports.newModules = newModules;
const type_1 = require("../type");
const module_1 = require("../vo/module");
const lang_1 = require("./lang");
const log_1 = require("../../../../pkg/log");
const path = __importStar(require("path"));
// Create domain-specific logger for module operations
const log = (0, log_1.getDomainLogger)('module', { component: 'modules' });
class Modules {
constructor(info, httpClient, zipExtractor, moduleCache) {
this.info = info;
this.httpClient = httpClient;
this.zipExtractor = zipExtractor;
this.moduleCache = moduleCache;
this.modules = [];
this.downloadedModules = new Set();
let projectModuleDir = this.info.projDir();
// Check if this is ConfigWithModulesDir by checking if it has workspaceModuleDir method
const configWithModulesDir = this.info;
if (typeof configWithModulesDir.workspaceModuleDir === 'function') {
projectModuleDir = configWithModulesDir.workspaceModuleDir();
}
// Initialize project module with correct module directory
this.projectModule = (0, module_1.newProjectModule)(this.info.osFs(), projectModuleDir, 'project-root');
// Initialize lang after all modules are set up
this.lang = (0, lang_1.newLang)(this.all());
}
/**
* Get project module
*/
proj() {
return this.projectModule.getModule();
}
/**
* Get all modules (project + downloaded)
*/
all() {
const modules = [this.projectModule.getModule()];
modules.push(...this.modules);
return modules;
}
/**
* Check if module is the project module
*/
isProjMod(mod) {
return mod === this.projectModule.getModule();
}
/**
* Load modules from import paths
*/
async load(onProgress) {
const importPaths = this.info.importPaths();
if (importPaths.length === 0) {
log.warn('No import paths configured - no modules will be downloaded');
return;
}
for (let i = 0; i < importPaths.length; i++) {
const importPath = importPaths[i];
if (!this.downloadedModules.has(importPath)) {
await this.addModule(this.projectModule.getModule(), importPath, (downloadProgress) => {
onProgress?.({
modulePath: importPath,
downloadPercentage: downloadProgress.percentage
});
});
}
}
}
/**
* Download and add a module
*/
async downloadModule(moduleImport, onProgress) {
try {
// Check cache first
const cachedMetadata = await this.moduleCache.get(moduleImport.path);
if (cachedMetadata && cachedMetadata.downloadStatus === type_1.DownloadStatus.COMPLETED) {
const module = await this.createModuleFromCache(moduleImport, cachedMetadata);
if (await module.exists()) {
return module;
}
}
// Download module
const moduleDir = this.getModuleDir(moduleImport.path);
const zipPath = path.join(moduleDir, 'module.zip');
// Create metadata
const metadata = {
path: moduleImport.path,
version: moduleImport.version || 'latest',
url: moduleImport.url,
dir: moduleDir,
downloadStatus: type_1.DownloadStatus.DOWNLOADING,
downloadedAt: new Date(),
};
// Update cache with downloading status
await this.moduleCache.set(moduleImport.path, metadata);
try {
// Download ZIP file with progress callback
await this.httpClient.download(moduleImport.url, zipPath, {
onProgress: (progress) => {
onProgress?.({ percentage: progress.percentage });
},
});
// Extract ZIP file
await this.zipExtractor.extract(zipPath, moduleDir);
// Update metadata
metadata.downloadStatus = type_1.DownloadStatus.COMPLETED;
metadata.downloadedAt = new Date();
// Get file size
try {
const zipStat = await this.info.osFs().stat(zipPath);
metadata.size = zipStat.size();
}
catch (error) {
// Ignore if we can't get size
}
// Clean up ZIP file
try {
await this.info.osFs().remove(zipPath);
}
catch (error) {
// Ignore cleanup error
}
// Update cache
await this.moduleCache.set(moduleImport.path, metadata);
// Create module
const module = (0, module_1.newModule)(this.info.osFs(), moduleDir, moduleImport.path);
module.setMetadata(metadata);
await module.applyMounts(moduleImport);
return module;
}
catch (error) {
// Update metadata with failed status
metadata.downloadStatus = type_1.DownloadStatus.FAILED;
await this.moduleCache.set(moduleImport.path, metadata);
const message = error instanceof Error ? error.message : String(error);
throw new type_1.ModuleError(`Failed to download module ${moduleImport.path}: ${message}`, 'DOWNLOAD_FAILED');
}
}
catch (error) {
if (error instanceof type_1.ModuleError) {
throw error;
}
const message = error instanceof Error ? error.message : String(error);
throw new type_1.ModuleError(`Module download failed: ${message}`, 'DOWNLOAD_FAILED');
}
}
/**
* Add a module to the collection
*/
async addModule(owner, importPath, onProgress) {
try {
// Check if already downloaded
if (this.downloadedModules.has(importPath)) {
return;
}
const moduleImport = {
path: importPath,
url: this.getDownloadUrl(importPath),
};
// Download module with progress callback
const module = await this.downloadModule(moduleImport, onProgress);
// Add to collection
this.modules.push(module);
this.downloadedModules.add(importPath);
// TODO: Support recursive imports if needed
// const childImports = await this.getModuleImports(module);
// for (const childImport of childImports) {
// await this.addModule(module, childImport);
// }
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new type_1.ModuleError(`Failed to add module ${importPath}: ${message}`, 'ADD_MODULE_FAILED');
}
}
/**
* Create module from cached metadata
*/
async createModuleFromCache(moduleImport, metadata) {
const module = (0, module_1.newModule)(this.info.osFs(), metadata.dir, moduleImport.path);
module.setMetadata(metadata);
await module.applyMounts(moduleImport);
return module;
}
/**
* Get module directory path
*/
getModuleDir(modulePath) {
// Convert module path to safe directory name
const safeName = modulePath
.replace(/[/\\:*?"<>|]/g, '_')
.replace(/^_+|_+$/g, '');
return path.join(this.info.moduleDir(), safeName);
}
/**
* Get download URL for module path
* This would be implemented based on your backend API
*/
getDownloadUrl(modulePath) {
return modulePath;
}
/**
* Get module by path
*/
getModuleByPath(modulePath) {
return this.modules.find(m => m.path() === modulePath) || null;
}
/**
* Check if module is downloaded
*/
isModuleDownloaded(modulePath) {
return this.downloadedModules.has(modulePath);
}
/**
* Get download status for module
*/
async getModuleStatus(modulePath) {
const metadata = await this.moduleCache.get(modulePath);
return metadata?.downloadStatus || type_1.DownloadStatus.PENDING;
}
/**
* Remove module from collection
*/
async removeModule(modulePath) {
try {
// Remove from collection
const index = this.modules.findIndex(m => m.path() === modulePath);
if (index >= 0) {
const module = this.modules[index];
// Remove directory
try {
await this.info.osFs().removeAll(module.dir());
}
catch (error) {
// Ignore if directory doesn't exist
}
// Remove from collection
this.modules.splice(index, 1);
this.downloadedModules.delete(modulePath);
// Remove from cache
await this.moduleCache.delete(modulePath);
}
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new type_1.ModuleError(`Failed to remove module ${modulePath}: ${message}`, 'REMOVE_MODULE_FAILED');
}
}
/**
* Clear all downloaded modules
*/
async clearAll() {
try {
// Remove all module directories
for (const module of this.modules) {
try {
await this.info.osFs().removeAll(module.dir());
}
catch (error) {
// Continue removing others even if one fails
}
}
// Clear collections
this.modules = [];
this.downloadedModules.clear();
// Clear cache
await this.moduleCache.clear();
}
catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new type_1.ModuleError(`Failed to clear modules: ${message}`, 'CLEAR_MODULES_FAILED');
}
}
/**
* Get summary of all modules
*/
getSummary() {
return {
projectModule: this.projectModule.getModule().getSummary(),
downloadedModules: this.modules.map(m => m.getSummary()),
totalModules: this.modules.length + 1, // +1 for project module
};
}
/**
* Get source language
*/
getSourceLang(source) {
return this.lang.getSourceLang(source);
}
}
exports.Modules = Modules;
/**
* Creates a new Modules instance
*/
function newModules(info, httpClient, zipExtractor, moduleCache) {
return new Modules(info, httpClient, zipExtractor, moduleCache);
}
//# sourceMappingURL=module.js.map