@t1mmen/srtd
Version:
Supabase Repeatable Template Definitions (srtd): 🪄 Live-reloading SQL templates for Supabase DX. Make your database changes reviewable and migrations maintainable! 🚀
236 lines • 10.6 kB
JavaScript
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
if (value !== null && value !== void 0) {
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
var dispose, inner;
if (async) {
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
dispose = value[Symbol.asyncDispose];
}
if (dispose === void 0) {
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
dispose = value[Symbol.dispose];
if (async) inner = dispose;
}
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
env.stack.push({ value: value, dispose: dispose, async: async });
}
else if (async) {
env.stack.push({ async: true });
}
return value;
};
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
return function (env) {
function fail(e) {
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
env.hasError = true;
}
var r, s = 0;
function next() {
while (r = env.stack.pop()) {
try {
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
if (r.dispose) {
var result = r.dispose.call(r.value);
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
}
else s |= 1;
}
catch (e) {
fail(e);
}
}
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
if (env.hasError) throw env.error;
}
return next();
};
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
});
// src/commands/register.ts
import path from 'node:path';
import { checkbox } from '@inquirer/prompts';
import chalk from 'chalk';
import { Command } from 'commander';
import figures from 'figures';
import { createBaseJsonOutput, writeJson } from '../output/index.js';
import { Orchestrator } from '../services/Orchestrator.js';
import { renderBranding } from '../ui/index.js';
import { getConfig } from '../utils/config.js';
import { findProjectRoot } from '../utils/findProjectRoot.js';
import { getErrorMessage, isPromptExit } from '../utils/getErrorMessage.js';
function formatRegisterJsonOutput(result) {
return {
...createBaseJsonOutput('register', result.failed.length === 0),
registered: result.registered,
failed: result.failed,
};
}
async function handleTemplateRegistration(templates, orchestrator, projectRoot, jsonMode = false) {
const registered = [];
const failed = [];
for (const templatePath of templates) {
try {
// Use Orchestrator for registration (single source of truth)
await orchestrator.registerTemplate(templatePath);
const relativePath = path.relative(projectRoot, templatePath);
registered.push(relativePath);
if (!jsonMode) {
console.log(chalk.green(`${figures.tick} Registered template:`), relativePath);
}
}
catch (err) {
const relativePath = path.relative(projectRoot, templatePath);
failed.push({ file: relativePath, error: getErrorMessage(err) });
if (!jsonMode) {
console.log(chalk.yellow(`${figures.warning} Failed: ${templatePath} - ${getErrorMessage(err)}`));
}
}
}
if (!jsonMode) {
console.log();
if (registered.length > 0) {
console.log(chalk.green(`${figures.tick} Successfully registered ${registered.length} template(s)`));
}
if (failed.length > 0) {
console.log(chalk.red(`${figures.cross} Failed to register ${failed.length} template(s)`));
}
}
return {
exitCode: failed.length > 0 ? 1 : 0,
result: { registered, failed },
};
}
export const registerCommand = new Command('register')
.description('Register templates to track them in the build log')
.argument('[templates...]', 'Template files to register (optional)')
.option('--json', 'Output results as JSON')
.action(async (templateArgs, options) => {
let exitCode = 0;
let result = { registered: [], failed: [] };
try {
const env_1 = { stack: [], error: void 0, hasError: false };
try {
// Skip branding in JSON mode
if (!options.json) {
await renderBranding({ subtitle: 'Register templates' });
}
// Initialize Orchestrator and get templates
const projectRoot = await findProjectRoot();
const { config } = await getConfig(projectRoot);
const orchestrator = __addDisposableResource(env_1, await Orchestrator.create(projectRoot, config, { silent: true }), true);
// If templates were provided as arguments, register them directly
if (templateArgs?.length) {
// Resolve template paths (may be relative)
const resolvedPaths = templateArgs.map(t => path.resolve(projectRoot, t));
const res = await handleTemplateRegistration(resolvedPaths, orchestrator, projectRoot, options.json);
exitCode = res.exitCode;
result = res.result;
}
else {
// In JSON mode without templates, return empty result
if (options.json) {
result = { registered: [], failed: [] };
exitCode = 0;
}
else {
// Load templates for interactive selection
const templatePaths = await orchestrator.findTemplates();
const templates = [];
for (const templatePath of templatePaths) {
try {
const template = await orchestrator.getTemplateStatusExternal(templatePath);
templates.push(template);
}
catch {
// Skip templates that can't be loaded
}
}
// Filter to unregistered templates by default
const unregisteredTemplates = templates
.filter(t => !t.buildState.lastMigrationFile)
.sort((a, b) => a.name.localeCompare(b.name));
if (unregisteredTemplates.length === 0) {
if (templates.length === 0) {
console.log(chalk.yellow(`${figures.warning} No templates found`));
console.log(chalk.dim(`${figures.info} Start by creating a template in the templates directory.`));
}
else {
console.log(chalk.yellow(`${figures.warning} No unregistered templates found`));
console.log(chalk.dim(`${figures.info} All ${templates.length} template(s) are already registered.`));
}
exitCode = 0;
}
else if (!process.stdin.isTTY) {
// Interactive mode requires TTY
console.log(chalk.red(`${figures.cross} Interactive mode requires a TTY.`));
console.log(chalk.dim('Provide template paths as arguments: srtd register <template1> <template2>'));
exitCode = 1;
}
else {
// Show interactive multi-select
const choices = unregisteredTemplates.map(template => ({
name: `${template.name} (new)`,
value: template.path,
}));
const selectedTemplates = await checkbox({
message: 'Select templates to register:',
choices,
});
if (selectedTemplates.length === 0) {
console.log(chalk.yellow(`${figures.warning} No templates selected`));
exitCode = 0;
}
else {
const res = await handleTemplateRegistration(selectedTemplates, orchestrator, projectRoot);
exitCode = res.exitCode;
result = res.result;
}
}
}
}
// Output JSON if in JSON mode
if (options.json) {
const jsonOutput = formatRegisterJsonOutput(result);
writeJson(jsonOutput);
}
}
catch (e_1) {
env_1.error = e_1;
env_1.hasError = true;
}
finally {
const result_1 = __disposeResources(env_1);
if (result_1)
await result_1;
}
}
catch (error) {
// Handle Ctrl+C gracefully
if (isPromptExit(error)) {
exitCode = 0;
}
else {
if (options.json) {
// Fatal errors use top-level error field, not failed array
writeJson({
...createBaseJsonOutput('register', false, getErrorMessage(error)),
registered: [],
failed: [],
});
}
else {
console.log();
console.log(chalk.red(`${figures.cross} Error loading templates:`));
console.log(chalk.red(getErrorMessage(error)));
}
exitCode = 1;
}
}
// Exit AFTER the await using block has completed, ensuring dispose() runs
process.exit(exitCode);
});
//# sourceMappingURL=register.js.map