UNPKG

@launchql/core

Version:
169 lines (168 loc) 7.21 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DEFAULT_TEMPLATE_TOOL_NAME = exports.DEFAULT_TEMPLATE_TTL_MS = exports.DEFAULT_TEMPLATE_REPO = void 0; exports.scaffoldTemplate = scaffoldTemplate; const fs_1 = __importDefault(require("fs")); const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); const create_gen_app_1 = require("create-gen-app"); const boilerplate_scanner_1 = require("./boilerplate-scanner"); exports.DEFAULT_TEMPLATE_REPO = 'https://github.com/constructive-io/pgpm-boilerplates.git'; exports.DEFAULT_TEMPLATE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 1 week exports.DEFAULT_TEMPLATE_TOOL_NAME = 'pgpm'; const templatizer = new create_gen_app_1.Templatizer(); const looksLikePath = (value) => { return (value.startsWith('.') || value.startsWith('/') || value.startsWith('~')); }; const normalizeQuestions = (questions) => questions?.map((q) => ({ ...q, type: q.type || 'text', })); const attachQuestionsToTemplatizer = (templ, questions) => { if (!questions?.length || typeof templ?.extract !== 'function') return; const originalExtract = templ.extract.bind(templ); templ.extract = async (templateDir) => { const extracted = await originalExtract(templateDir); extracted.projectQuestions = { questions: normalizeQuestions(questions), }; return extracted; }; }; /** * Resolve the template path using the new metadata-driven resolution. * * Resolution order: * 1. If explicit `templatePath` is provided, use it directly * 2. If `.boilerplates.json` exists, use its `dir` field to find the base directory * 3. Look for `{baseDir}/{type}` (e.g., "default/module") * 4. Fallback to legacy structure: `{type}` directly in root */ const resolveFromPath = (templateDir, templatePath, type, dirOverride) => { // If explicit templatePath is provided, use it directly if (templatePath) { const candidateDir = path_1.default.isAbsolute(templatePath) ? templatePath : path_1.default.join(templateDir, templatePath); if (fs_1.default.existsSync(candidateDir) && fs_1.default.statSync(candidateDir).isDirectory()) { return { fromPath: path_1.default.relative(templateDir, candidateDir) || '.', resolvedTemplatePath: candidateDir, }; } return { fromPath: templatePath, resolvedTemplatePath: path_1.default.join(templateDir, templatePath), }; } // Try new metadata-driven resolution const rootConfig = (0, boilerplate_scanner_1.readBoilerplatesConfig)(templateDir); const baseDir = dirOverride ?? rootConfig?.dir; if (baseDir) { // New structure: {templateDir}/{baseDir}/{type} const newStructurePath = path_1.default.join(templateDir, baseDir, type); if (fs_1.default.existsSync(newStructurePath) && fs_1.default.statSync(newStructurePath).isDirectory()) { return { fromPath: path_1.default.join(baseDir, type), resolvedTemplatePath: newStructurePath, }; } } // Fallback to legacy structure: {templateDir}/{type} const legacyPath = path_1.default.join(templateDir, type); if (fs_1.default.existsSync(legacyPath) && fs_1.default.statSync(legacyPath).isDirectory()) { return { fromPath: type, resolvedTemplatePath: legacyPath, }; } // Default fallback return { fromPath: type, resolvedTemplatePath: path_1.default.join(templateDir, type), }; }; async function scaffoldTemplate(options) { const { type, outputDir, templateRepo = exports.DEFAULT_TEMPLATE_REPO, branch, templatePath, answers, noTty = false, cacheTtlMs = exports.DEFAULT_TEMPLATE_TTL_MS, toolName = exports.DEFAULT_TEMPLATE_TOOL_NAME, cwd, cacheBaseDir, dir, } = options; const resolvedRepo = looksLikePath(templateRepo) ? path_1.default.resolve(cwd ?? process.cwd(), templateRepo) : templateRepo; // Handle local template directories without caching if (looksLikePath(templateRepo) && fs_1.default.existsSync(resolvedRepo) && fs_1.default.statSync(resolvedRepo).isDirectory()) { const { fromPath, resolvedTemplatePath } = resolveFromPath(resolvedRepo, templatePath, type, dir); // Read boilerplate config for questions const boilerplateConfig = (0, boilerplate_scanner_1.readBoilerplateConfig)(resolvedTemplatePath); // Inject questions into the templatizer pipeline so prompt types and defaults are applied attachQuestionsToTemplatizer(templatizer, boilerplateConfig?.questions); await templatizer.process(resolvedRepo, outputDir, { argv: answers, noTty, fromPath, }); return { cacheUsed: false, cacheExpired: false, templateDir: resolvedRepo, questions: boilerplateConfig?.questions, }; } // Remote repo with caching const cacheManager = new create_gen_app_1.CacheManager({ toolName, ttl: cacheTtlMs, baseDir: cacheBaseDir ?? process.env.PGPM_CACHE_BASE_DIR ?? (process.env.JEST_WORKER_ID ? path_1.default.join(os_1.default.tmpdir(), `pgpm-cache-${process.env.JEST_WORKER_ID}`) : undefined), }); const gitCloner = new create_gen_app_1.GitCloner(); const normalizedUrl = gitCloner.normalizeUrl(resolvedRepo); const cacheKey = cacheManager.createKey(normalizedUrl, branch); const expiredMetadata = cacheManager.checkExpiration(cacheKey); if (expiredMetadata) { cacheManager.clear(cacheKey); } let templateDir; let cacheUsed = false; const cachedPath = cacheManager.get(cacheKey); if (cachedPath && !expiredMetadata) { templateDir = cachedPath; cacheUsed = true; } else { const tempDest = path_1.default.join(cacheManager.getReposDir(), cacheKey); gitCloner.clone(normalizedUrl, tempDest, { branch, depth: 1, singleBranch: true, }); cacheManager.set(cacheKey, tempDest); templateDir = tempDest; } const { fromPath, resolvedTemplatePath } = resolveFromPath(templateDir, templatePath, type, dir); // Read boilerplate config for questions const boilerplateConfig = (0, boilerplate_scanner_1.readBoilerplateConfig)(resolvedTemplatePath); // Inject questions into the templatizer pipeline so prompt types and defaults are applied attachQuestionsToTemplatizer(templatizer, boilerplateConfig?.questions); await templatizer.process(templateDir, outputDir, { argv: answers, noTty, fromPath, }); return { cacheUsed, cacheExpired: Boolean(expiredMetadata), cachePath: templateDir, templateDir, questions: boilerplateConfig?.questions, }; }