@mailmodo/cli 0.0.3 → 0.0.4-beta.pr6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/dev.cmd +1 -1
- package/bin/dev.js +17 -2
- package/dist/commands/billing/index.d.ts +26 -0
- package/dist/commands/billing/index.js +92 -0
- package/dist/commands/contacts/index.d.ts +32 -0
- package/dist/commands/contacts/index.js +134 -0
- package/dist/commands/deploy/index.d.ts +25 -0
- package/dist/commands/deploy/index.js +194 -0
- package/dist/commands/domain/index.d.ts +27 -0
- package/dist/commands/domain/index.js +163 -0
- package/dist/commands/edit/index.d.ts +14 -0
- package/dist/commands/edit/index.js +96 -0
- package/dist/commands/emails/index.d.ts +10 -0
- package/dist/commands/emails/index.js +62 -0
- package/dist/commands/init/index.d.ts +11 -0
- package/dist/commands/init/index.js +124 -0
- package/dist/commands/login/index.d.ts +6 -2
- package/dist/commands/login/index.js +62 -6
- package/dist/commands/logs/index.d.ts +20 -0
- package/dist/commands/logs/index.js +82 -0
- package/dist/commands/preview/index.d.ts +30 -0
- package/dist/commands/preview/index.js +213 -0
- package/dist/commands/settings/index.d.ts +19 -0
- package/dist/commands/settings/index.js +147 -0
- package/dist/commands/status/index.d.ts +10 -0
- package/dist/commands/status/index.js +53 -0
- package/dist/lib/api-client.d.ts +41 -0
- package/dist/lib/api-client.js +125 -0
- package/dist/lib/base-command.d.ts +45 -0
- package/dist/lib/base-command.js +69 -0
- package/dist/lib/config.d.ts +30 -0
- package/dist/lib/config.js +47 -0
- package/dist/lib/constants.d.ts +27 -0
- package/dist/lib/constants.js +27 -0
- package/dist/lib/yaml-config.d.ts +65 -0
- package/dist/lib/yaml-config.js +70 -0
- package/oclif.manifest.json +559 -4
- package/package.json +8 -8
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
import { ApiClient } from './api-client.js';
|
|
3
|
+
import { type MailmodoConfig } from './config.js';
|
|
4
|
+
import { type MailmodoYaml } from './yaml-config.js';
|
|
5
|
+
/**
|
|
6
|
+
* Abstract base command providing shared functionality for all Mailmodo CLI commands.
|
|
7
|
+
* Subclasses inherit --json and --yes base flags, authentication enforcement,
|
|
8
|
+
* YAML config loading, and consistent API error handling.
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class BaseCommand extends Command {
|
|
11
|
+
static baseFlags: {
|
|
12
|
+
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
protected apiClient: ApiClient;
|
|
16
|
+
/**
|
|
17
|
+
* Validates that the user is authenticated and initializes the API client.
|
|
18
|
+
* Checks MAILMODO_API_KEY environment variable first (for AI agent usage),
|
|
19
|
+
* then falls back to ~/.mailmodo/config.
|
|
20
|
+
* Exits with an error if no valid API key is found.
|
|
21
|
+
*
|
|
22
|
+
* @returns {Promise<MailmodoConfig>} The resolved configuration containing the API key.
|
|
23
|
+
*/
|
|
24
|
+
protected ensureAuth(): Promise<MailmodoConfig>;
|
|
25
|
+
/**
|
|
26
|
+
* Loads and returns the mailmodo.yaml configuration from the current directory.
|
|
27
|
+
* Exits with an error if the file is not found, directing the user to run init.
|
|
28
|
+
*
|
|
29
|
+
* @returns {Promise<MailmodoYaml>} The parsed mailmodo.yaml containing project
|
|
30
|
+
* settings and all email sequence definitions.
|
|
31
|
+
*/
|
|
32
|
+
protected ensureYaml(): Promise<MailmodoYaml>;
|
|
33
|
+
/**
|
|
34
|
+
* Handles a failed API response by mapping HTTP status codes to
|
|
35
|
+
* user-friendly error messages and exiting the process.
|
|
36
|
+
*
|
|
37
|
+
* @param {{ status: number; error?: string }} response - The API response object with ok=false.
|
|
38
|
+
* Status 401 prompts re-authentication, 429 indicates rate limiting,
|
|
39
|
+
* all others display the server's error message or a generic fallback.
|
|
40
|
+
*/
|
|
41
|
+
protected handleApiError(response: {
|
|
42
|
+
error?: string;
|
|
43
|
+
status: number;
|
|
44
|
+
}): never;
|
|
45
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Command, Flags } from '@oclif/core';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { ApiClient } from './api-client.js';
|
|
4
|
+
import { loadConfig } from './config.js';
|
|
5
|
+
import { loadYaml } from './yaml-config.js';
|
|
6
|
+
/**
|
|
7
|
+
* Abstract base command providing shared functionality for all Mailmodo CLI commands.
|
|
8
|
+
* Subclasses inherit --json and --yes base flags, authentication enforcement,
|
|
9
|
+
* YAML config loading, and consistent API error handling.
|
|
10
|
+
*/
|
|
11
|
+
export class BaseCommand extends Command {
|
|
12
|
+
static baseFlags = {
|
|
13
|
+
json: Flags.boolean({ default: false, description: 'Output as JSON' }),
|
|
14
|
+
yes: Flags.boolean({ char: 'y', default: false, description: 'Skip confirmation prompts' }),
|
|
15
|
+
};
|
|
16
|
+
apiClient;
|
|
17
|
+
/**
|
|
18
|
+
* Validates that the user is authenticated and initializes the API client.
|
|
19
|
+
* Checks MAILMODO_API_KEY environment variable first (for AI agent usage),
|
|
20
|
+
* then falls back to ~/.mailmodo/config.
|
|
21
|
+
* Exits with an error if no valid API key is found.
|
|
22
|
+
*
|
|
23
|
+
* @returns {Promise<MailmodoConfig>} The resolved configuration containing the API key.
|
|
24
|
+
*/
|
|
25
|
+
async ensureAuth() {
|
|
26
|
+
const envKey = process.env.MAILMODO_API_KEY;
|
|
27
|
+
if (envKey) {
|
|
28
|
+
this.apiClient = new ApiClient(envKey);
|
|
29
|
+
return { apiKey: envKey };
|
|
30
|
+
}
|
|
31
|
+
const config = await loadConfig();
|
|
32
|
+
if (!config?.apiKey) {
|
|
33
|
+
this.error(`Not logged in. Run ${chalk.cyan('mailmodo login')} to authenticate.`);
|
|
34
|
+
}
|
|
35
|
+
this.apiClient = new ApiClient(config.apiKey);
|
|
36
|
+
return config;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Loads and returns the mailmodo.yaml configuration from the current directory.
|
|
40
|
+
* Exits with an error if the file is not found, directing the user to run init.
|
|
41
|
+
*
|
|
42
|
+
* @returns {Promise<MailmodoYaml>} The parsed mailmodo.yaml containing project
|
|
43
|
+
* settings and all email sequence definitions.
|
|
44
|
+
*/
|
|
45
|
+
async ensureYaml() {
|
|
46
|
+
const config = await loadYaml();
|
|
47
|
+
if (!config) {
|
|
48
|
+
this.error(`No mailmodo.yaml found. Run ${chalk.cyan('mailmodo init')} first.`);
|
|
49
|
+
}
|
|
50
|
+
return config;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Handles a failed API response by mapping HTTP status codes to
|
|
54
|
+
* user-friendly error messages and exiting the process.
|
|
55
|
+
*
|
|
56
|
+
* @param {{ status: number; error?: string }} response - The API response object with ok=false.
|
|
57
|
+
* Status 401 prompts re-authentication, 429 indicates rate limiting,
|
|
58
|
+
* all others display the server's error message or a generic fallback.
|
|
59
|
+
*/
|
|
60
|
+
handleApiError(response) {
|
|
61
|
+
if (response.status === 401) {
|
|
62
|
+
this.error(`Invalid API key. Run ${chalk.cyan('mailmodo login')} to re-authenticate.`);
|
|
63
|
+
}
|
|
64
|
+
if (response.status === 429) {
|
|
65
|
+
this.error('Rate limit exceeded. Please try again later.');
|
|
66
|
+
}
|
|
67
|
+
this.error(response.error || 'An unexpected API error occurred.');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface MailmodoConfig {
|
|
2
|
+
accountName?: string;
|
|
3
|
+
apiKey: string;
|
|
4
|
+
email?: string;
|
|
5
|
+
freeRemaining?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Loads the Mailmodo CLI configuration from ~/.mailmodo/config.
|
|
9
|
+
* The config file stores the API key and account metadata as JSON.
|
|
10
|
+
*
|
|
11
|
+
* @returns {Promise<MailmodoConfig | null>} The parsed configuration object
|
|
12
|
+
* containing apiKey, email, accountName, and freeRemaining quota,
|
|
13
|
+
* or null if the config file does not exist or is corrupted.
|
|
14
|
+
*/
|
|
15
|
+
export declare function loadConfig(): Promise<MailmodoConfig | null>;
|
|
16
|
+
/**
|
|
17
|
+
* Persists the Mailmodo CLI configuration to ~/.mailmodo/config.
|
|
18
|
+
* Creates the ~/.mailmodo directory if it does not exist.
|
|
19
|
+
* Overwrites any existing config file.
|
|
20
|
+
*
|
|
21
|
+
* @param {MailmodoConfig} config - The configuration to persist, must include
|
|
22
|
+
* at minimum an apiKey. Optional fields: email, accountName, freeRemaining.
|
|
23
|
+
*/
|
|
24
|
+
export declare function saveConfig(config: MailmodoConfig): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Returns the absolute path to the Mailmodo config directory (~/.mailmodo).
|
|
27
|
+
*
|
|
28
|
+
* @returns {string} The config directory path.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getConfigDir(): string;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
const CONFIG_DIR = join(homedir(), '.mailmodo');
|
|
6
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config');
|
|
7
|
+
/**
|
|
8
|
+
* Loads the Mailmodo CLI configuration from ~/.mailmodo/config.
|
|
9
|
+
* The config file stores the API key and account metadata as JSON.
|
|
10
|
+
*
|
|
11
|
+
* @returns {Promise<MailmodoConfig | null>} The parsed configuration object
|
|
12
|
+
* containing apiKey, email, accountName, and freeRemaining quota,
|
|
13
|
+
* or null if the config file does not exist or is corrupted.
|
|
14
|
+
*/
|
|
15
|
+
export async function loadConfig() {
|
|
16
|
+
if (!existsSync(CONFIG_FILE))
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
const content = await readFile(CONFIG_FILE, 'utf8');
|
|
20
|
+
return JSON.parse(content);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Persists the Mailmodo CLI configuration to ~/.mailmodo/config.
|
|
28
|
+
* Creates the ~/.mailmodo directory if it does not exist.
|
|
29
|
+
* Overwrites any existing config file.
|
|
30
|
+
*
|
|
31
|
+
* @param {MailmodoConfig} config - The configuration to persist, must include
|
|
32
|
+
* at minimum an apiKey. Optional fields: email, accountName, freeRemaining.
|
|
33
|
+
*/
|
|
34
|
+
export async function saveConfig(config) {
|
|
35
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
36
|
+
await mkdir(CONFIG_DIR, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
await writeFile(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Returns the absolute path to the Mailmodo config directory (~/.mailmodo).
|
|
42
|
+
*
|
|
43
|
+
* @returns {string} The config directory path.
|
|
44
|
+
*/
|
|
45
|
+
export function getConfigDir() {
|
|
46
|
+
return CONFIG_DIR;
|
|
47
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const API_BASE_URL: string;
|
|
2
|
+
export declare const API_ENDPOINTS: Readonly<{
|
|
3
|
+
ANALYTICS: "/analytics";
|
|
4
|
+
ANALYZE: "/analyze";
|
|
5
|
+
ASSETS_LOGO: "/assets/logo";
|
|
6
|
+
AUTH_VALIDATE: "/auth/validate";
|
|
7
|
+
BILLING_CAP: "/billing/cap";
|
|
8
|
+
BILLING_STATUS: "/billing/status";
|
|
9
|
+
CONTACTS: "/contacts";
|
|
10
|
+
CONTACTS_EXPORT: "/contacts/export";
|
|
11
|
+
DOMAIN: "/domain";
|
|
12
|
+
DOMAIN_STATUS: "/domain/status";
|
|
13
|
+
DOMAIN_VERIFY: "/domain/verify";
|
|
14
|
+
EDIT: "/edit";
|
|
15
|
+
EVENTS: "/events";
|
|
16
|
+
GENERATE: "/generate";
|
|
17
|
+
LOGS: "/logs";
|
|
18
|
+
PREVIEW: "/preview";
|
|
19
|
+
SEQUENCES: "/sequences";
|
|
20
|
+
}>;
|
|
21
|
+
export declare const SIGNUP_URL = "https://mailmodo.com/cli";
|
|
22
|
+
export declare const DOCS_URL = "https://mailmodo.com/docs/cli";
|
|
23
|
+
export declare const DNS_GUIDE_URL = "https://mailmodo.com/docs/dns";
|
|
24
|
+
export declare const PREVIEW_PORT = 3421;
|
|
25
|
+
export declare const DEFAULT_BRAND_COLOR = "#1A56DB";
|
|
26
|
+
export declare const TEMPLATES_DIR = "mailmodo";
|
|
27
|
+
export declare const YAML_FILE = "mailmodo.yaml";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const API_BASE_URL = process.env.MAILMODO_API_URL || 'https://api.mailmodo.com/cli/v1';
|
|
2
|
+
export const API_ENDPOINTS = Object.freeze({
|
|
3
|
+
ANALYTICS: '/analytics',
|
|
4
|
+
ANALYZE: '/analyze',
|
|
5
|
+
ASSETS_LOGO: '/assets/logo',
|
|
6
|
+
AUTH_VALIDATE: '/auth/validate',
|
|
7
|
+
BILLING_CAP: '/billing/cap',
|
|
8
|
+
BILLING_STATUS: '/billing/status',
|
|
9
|
+
CONTACTS: '/contacts',
|
|
10
|
+
CONTACTS_EXPORT: '/contacts/export',
|
|
11
|
+
DOMAIN: '/domain',
|
|
12
|
+
DOMAIN_STATUS: '/domain/status',
|
|
13
|
+
DOMAIN_VERIFY: '/domain/verify',
|
|
14
|
+
EDIT: '/edit',
|
|
15
|
+
EVENTS: '/events',
|
|
16
|
+
GENERATE: '/generate',
|
|
17
|
+
LOGS: '/logs',
|
|
18
|
+
PREVIEW: '/preview',
|
|
19
|
+
SEQUENCES: '/sequences',
|
|
20
|
+
});
|
|
21
|
+
export const SIGNUP_URL = 'https://mailmodo.com/cli';
|
|
22
|
+
export const DOCS_URL = 'https://mailmodo.com/docs/cli';
|
|
23
|
+
export const DNS_GUIDE_URL = 'https://mailmodo.com/docs/dns';
|
|
24
|
+
export const PREVIEW_PORT = 3421;
|
|
25
|
+
export const DEFAULT_BRAND_COLOR = '#1A56DB';
|
|
26
|
+
export const TEMPLATES_DIR = 'mailmodo';
|
|
27
|
+
export const YAML_FILE = 'mailmodo.yaml';
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export interface EmailConfig {
|
|
2
|
+
condition?: string;
|
|
3
|
+
delay: number | string;
|
|
4
|
+
goal?: string;
|
|
5
|
+
id: string;
|
|
6
|
+
previewText?: string;
|
|
7
|
+
style?: 'branded' | 'plain';
|
|
8
|
+
subject: string;
|
|
9
|
+
template: string;
|
|
10
|
+
trigger: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ProjectConfig {
|
|
13
|
+
address?: string;
|
|
14
|
+
brandColor?: string;
|
|
15
|
+
domain?: string;
|
|
16
|
+
emailStyle?: 'branded' | 'plain';
|
|
17
|
+
fromEmail?: string;
|
|
18
|
+
fromName?: string;
|
|
19
|
+
logoFile?: string;
|
|
20
|
+
logoUrl?: string;
|
|
21
|
+
monthlyCap?: number;
|
|
22
|
+
name?: string;
|
|
23
|
+
replyTo?: string;
|
|
24
|
+
type?: string;
|
|
25
|
+
url?: string;
|
|
26
|
+
webhookUrl?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface MailmodoYaml {
|
|
29
|
+
emails: EmailConfig[];
|
|
30
|
+
project: ProjectConfig;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Loads and parses the mailmodo.yaml configuration file from the specified
|
|
34
|
+
* directory (or current working directory).
|
|
35
|
+
*
|
|
36
|
+
* @param {string} [cwd] - Directory containing mailmodo.yaml. Defaults to process.cwd().
|
|
37
|
+
* @returns {Promise<MailmodoYaml | null>} The parsed configuration with project
|
|
38
|
+
* settings and email array, or null if the file doesn't exist or can't be parsed.
|
|
39
|
+
*/
|
|
40
|
+
export declare function loadYaml(cwd?: string): Promise<MailmodoYaml | null>;
|
|
41
|
+
/**
|
|
42
|
+
* Serializes and writes the mailmodo.yaml configuration to disk.
|
|
43
|
+
*
|
|
44
|
+
* @param {MailmodoYaml} config - The complete configuration to persist.
|
|
45
|
+
* @param {string} [cwd] - Target directory. Defaults to process.cwd().
|
|
46
|
+
*/
|
|
47
|
+
export declare function saveYaml(config: MailmodoYaml, cwd?: string): Promise<void>;
|
|
48
|
+
/**
|
|
49
|
+
* Writes an HTML email template file into the /mailmodo templates directory,
|
|
50
|
+
* creating the directory if it doesn't yet exist.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} filename - Template filename relative to the templates dir (e.g., 'welcome.html').
|
|
53
|
+
* @param {string} html - The complete HTML content for the email template.
|
|
54
|
+
* @param {string} [cwd] - Base project directory. Defaults to process.cwd().
|
|
55
|
+
*/
|
|
56
|
+
export declare function saveTemplate(filename: string, html: string, cwd?: string): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Reads an HTML email template from the /mailmodo templates directory.
|
|
59
|
+
*
|
|
60
|
+
* @param {string} filename - Template filename relative to the templates dir (e.g., 'welcome.html').
|
|
61
|
+
* @param {string} [cwd] - Base project directory. Defaults to process.cwd().
|
|
62
|
+
* @returns {Promise<string | null>} The raw HTML content of the template,
|
|
63
|
+
* or null if the file doesn't exist or can't be read.
|
|
64
|
+
*/
|
|
65
|
+
export declare function loadTemplate(filename: string, cwd?: string): Promise<null | string>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { dump, load } from 'js-yaml';
|
|
5
|
+
import { TEMPLATES_DIR, YAML_FILE } from './constants.js';
|
|
6
|
+
/**
|
|
7
|
+
* Loads and parses the mailmodo.yaml configuration file from the specified
|
|
8
|
+
* directory (or current working directory).
|
|
9
|
+
*
|
|
10
|
+
* @param {string} [cwd] - Directory containing mailmodo.yaml. Defaults to process.cwd().
|
|
11
|
+
* @returns {Promise<MailmodoYaml | null>} The parsed configuration with project
|
|
12
|
+
* settings and email array, or null if the file doesn't exist or can't be parsed.
|
|
13
|
+
*/
|
|
14
|
+
export async function loadYaml(cwd) {
|
|
15
|
+
const filePath = join(cwd || process.cwd(), YAML_FILE);
|
|
16
|
+
if (!existsSync(filePath))
|
|
17
|
+
return null;
|
|
18
|
+
try {
|
|
19
|
+
const content = await readFile(filePath, 'utf8');
|
|
20
|
+
return load(content);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Serializes and writes the mailmodo.yaml configuration to disk.
|
|
28
|
+
*
|
|
29
|
+
* @param {MailmodoYaml} config - The complete configuration to persist.
|
|
30
|
+
* @param {string} [cwd] - Target directory. Defaults to process.cwd().
|
|
31
|
+
*/
|
|
32
|
+
export async function saveYaml(config, cwd) {
|
|
33
|
+
const filePath = join(cwd || process.cwd(), YAML_FILE);
|
|
34
|
+
const content = dump(config, { lineWidth: -1, noRefs: true, quotingType: '"' });
|
|
35
|
+
await writeFile(filePath, content);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Writes an HTML email template file into the /mailmodo templates directory,
|
|
39
|
+
* creating the directory if it doesn't yet exist.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} filename - Template filename relative to the templates dir (e.g., 'welcome.html').
|
|
42
|
+
* @param {string} html - The complete HTML content for the email template.
|
|
43
|
+
* @param {string} [cwd] - Base project directory. Defaults to process.cwd().
|
|
44
|
+
*/
|
|
45
|
+
export async function saveTemplate(filename, html, cwd) {
|
|
46
|
+
const dir = join(cwd || process.cwd(), TEMPLATES_DIR);
|
|
47
|
+
if (!existsSync(dir)) {
|
|
48
|
+
await mkdir(dir, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
await writeFile(join(dir, filename), html);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Reads an HTML email template from the /mailmodo templates directory.
|
|
54
|
+
*
|
|
55
|
+
* @param {string} filename - Template filename relative to the templates dir (e.g., 'welcome.html').
|
|
56
|
+
* @param {string} [cwd] - Base project directory. Defaults to process.cwd().
|
|
57
|
+
* @returns {Promise<string | null>} The raw HTML content of the template,
|
|
58
|
+
* or null if the file doesn't exist or can't be read.
|
|
59
|
+
*/
|
|
60
|
+
export async function loadTemplate(filename, cwd) {
|
|
61
|
+
const filePath = join(cwd || process.cwd(), TEMPLATES_DIR, filename);
|
|
62
|
+
if (!existsSync(filePath))
|
|
63
|
+
return null;
|
|
64
|
+
try {
|
|
65
|
+
return await readFile(filePath, 'utf8');
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|