@expressots/cli 3.0.0 → 4.0.0-preview.3
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/README.md +41 -95
- package/bin/cicd/cli.d.ts +6 -0
- package/bin/cicd/cli.js +128 -0
- package/bin/cicd/form.d.ts +29 -0
- package/bin/cicd/form.js +346 -0
- package/bin/cicd/generators/azure-devops.d.ts +2 -0
- package/bin/cicd/generators/azure-devops.js +370 -0
- package/bin/cicd/generators/bitbucket.d.ts +2 -0
- package/bin/cicd/generators/bitbucket.js +217 -0
- package/bin/cicd/generators/circleci.d.ts +2 -0
- package/bin/cicd/generators/circleci.js +274 -0
- package/bin/cicd/generators/github-actions.d.ts +14 -0
- package/bin/cicd/generators/github-actions.js +426 -0
- package/bin/cicd/generators/gitlab-ci.d.ts +2 -0
- package/bin/cicd/generators/gitlab-ci.js +237 -0
- package/bin/cicd/generators/index.d.ts +6 -0
- package/bin/cicd/generators/index.js +15 -0
- package/bin/cicd/generators/jenkins.d.ts +2 -0
- package/bin/cicd/generators/jenkins.js +248 -0
- package/bin/cicd/generators/template-loader.d.ts +17 -0
- package/bin/cicd/generators/template-loader.js +128 -0
- package/bin/cicd/index.d.ts +1 -0
- package/bin/cicd/index.js +5 -0
- package/bin/cli.d.ts +1 -5
- package/bin/cli.js +72 -7
- package/bin/commands/project.commands.d.ts +19 -6
- package/bin/commands/project.commands.js +602 -66
- package/bin/config/index.d.ts +5 -0
- package/bin/config/index.js +10 -0
- package/bin/config/manager.d.ts +98 -0
- package/bin/config/manager.js +222 -0
- package/bin/containerize/analyzers/bootstrap-analyzer.d.ts +46 -0
- package/bin/containerize/analyzers/bootstrap-analyzer.js +187 -0
- package/bin/containerize/analyzers/project-analyzer.d.ts +20 -0
- package/bin/containerize/analyzers/project-analyzer.js +150 -0
- package/bin/containerize/cli.d.ts +4 -0
- package/bin/containerize/cli.js +113 -0
- package/bin/containerize/form.d.ts +15 -0
- package/bin/containerize/form.js +152 -0
- package/bin/containerize/generators/ci-generator.d.ts +31 -0
- package/bin/containerize/generators/ci-generator.js +940 -0
- package/bin/containerize/generators/docker-compose-generator.d.ts +8 -0
- package/bin/containerize/generators/docker-compose-generator.js +187 -0
- package/bin/containerize/generators/dockerfile-generator.d.ts +8 -0
- package/bin/containerize/generators/dockerfile-generator.js +657 -0
- package/bin/containerize/generators/kubernetes-generator.d.ts +8 -0
- package/bin/containerize/generators/kubernetes-generator.js +134 -0
- package/bin/containerize/generators/template-loader.d.ts +36 -0
- package/bin/containerize/generators/template-loader.js +129 -0
- package/bin/containerize/index.d.ts +4 -0
- package/bin/containerize/index.js +13 -0
- package/bin/containerize/presets/preset-registry.d.ts +20 -0
- package/bin/containerize/presets/preset-registry.js +102 -0
- package/bin/costs/cli.d.ts +5 -0
- package/bin/costs/cli.js +185 -0
- package/bin/costs/form.d.ts +44 -0
- package/bin/costs/form.js +412 -0
- package/bin/costs/index.d.ts +4 -0
- package/bin/costs/index.js +25 -0
- package/bin/costs/pricing-manager.d.ts +84 -0
- package/bin/costs/pricing-manager.js +342 -0
- package/bin/costs/providers/index.d.ts +32 -0
- package/bin/costs/providers/index.js +153 -0
- package/bin/costs/sources/api-source.d.ts +10 -0
- package/bin/costs/sources/api-source.js +32 -0
- package/bin/costs/sources/index.d.ts +6 -0
- package/bin/costs/sources/index.js +15 -0
- package/bin/costs/sources/local-json-source.d.ts +23 -0
- package/bin/costs/sources/local-json-source.js +59 -0
- package/bin/costs/sources/remote-json-source.d.ts +11 -0
- package/bin/costs/sources/remote-json-source.js +53 -0
- package/bin/costs/types.d.ts +53 -0
- package/bin/costs/types.js +5 -0
- package/bin/dev/cli.d.ts +4 -0
- package/bin/dev/cli.js +136 -0
- package/bin/dev/form.d.ts +36 -0
- package/bin/dev/form.js +254 -0
- package/bin/dev/index.d.ts +1 -0
- package/bin/dev/index.js +5 -0
- package/bin/generate/cli.d.ts +1 -1
- package/bin/generate/cli.js +29 -2
- package/bin/generate/form.d.ts +5 -1
- package/bin/generate/form.js +3 -3
- package/bin/generate/templates/nonopinionated/config.tpl +12 -0
- package/bin/generate/templates/nonopinionated/event.tpl +10 -0
- package/bin/generate/templates/nonopinionated/guard.tpl +18 -0
- package/bin/generate/templates/nonopinionated/handler.tpl +12 -0
- package/bin/generate/templates/nonopinionated/interceptor.tpl +27 -0
- package/bin/generate/templates/opinionated/config.tpl +47 -0
- package/bin/generate/templates/opinionated/entity.tpl +1 -8
- package/bin/generate/templates/opinionated/event.tpl +15 -0
- package/bin/generate/templates/opinionated/guard.tpl +41 -0
- package/bin/generate/templates/opinionated/handler.tpl +23 -0
- package/bin/generate/templates/opinionated/interceptor.tpl +50 -0
- package/bin/generate/utils/command-utils.d.ts +20 -5
- package/bin/generate/utils/command-utils.js +145 -48
- package/bin/generate/utils/nonopininated-cmd.d.ts +10 -1
- package/bin/generate/utils/nonopininated-cmd.js +100 -1
- package/bin/generate/utils/opinionated-cmd.d.ts +10 -1
- package/bin/generate/utils/opinionated-cmd.js +128 -16
- package/bin/generate/utils/string-utils.d.ts +6 -0
- package/bin/generate/utils/string-utils.js +13 -1
- package/bin/help/cli.d.ts +1 -1
- package/bin/help/command-help-registry.d.ts +23 -0
- package/bin/help/command-help-registry.js +303 -0
- package/bin/help/command-help.d.ts +36 -0
- package/bin/help/command-help.js +56 -0
- package/bin/help/form.js +127 -22
- package/bin/help/main-help.d.ts +8 -0
- package/bin/help/main-help.js +126 -0
- package/bin/help/render.d.ts +32 -0
- package/bin/help/render.js +46 -0
- package/bin/info/cli.d.ts +1 -1
- package/bin/info/form.d.ts +1 -1
- package/bin/info/form.js +11 -11
- package/bin/migrate/analyzers/platform-detector.d.ts +14 -0
- package/bin/migrate/analyzers/platform-detector.js +116 -0
- package/bin/migrate/cli.d.ts +6 -0
- package/bin/migrate/cli.js +98 -0
- package/bin/migrate/form.d.ts +25 -0
- package/bin/migrate/form.js +348 -0
- package/bin/migrate/generators/compose-to-k8s.d.ts +2 -0
- package/bin/migrate/generators/compose-to-k8s.js +324 -0
- package/bin/migrate/generators/compose-to-railway.d.ts +2 -0
- package/bin/migrate/generators/compose-to-railway.js +138 -0
- package/bin/migrate/generators/compose-to-render.d.ts +2 -0
- package/bin/migrate/generators/compose-to-render.js +148 -0
- package/bin/migrate/generators/generic-migration.d.ts +9 -0
- package/bin/migrate/generators/generic-migration.js +221 -0
- package/bin/migrate/generators/heroku-to-fly.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-fly.js +291 -0
- package/bin/migrate/generators/heroku-to-railway.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-railway.js +283 -0
- package/bin/migrate/generators/heroku-to-render.d.ts +2 -0
- package/bin/migrate/generators/heroku-to-render.js +148 -0
- package/bin/migrate/generators/index.d.ts +7 -0
- package/bin/migrate/generators/index.js +17 -0
- package/bin/migrate/generators/template-loader.d.ts +21 -0
- package/bin/migrate/generators/template-loader.js +59 -0
- package/bin/migrate/index.d.ts +1 -0
- package/bin/migrate/index.js +5 -0
- package/bin/new/cli.d.ts +5 -1
- package/bin/new/cli.js +77 -14
- package/bin/new/form.d.ts +27 -4
- package/bin/new/form.js +605 -75
- package/bin/profile/analyzers/dockerfile-analyzer.d.ts +27 -0
- package/bin/profile/analyzers/dockerfile-analyzer.js +122 -0
- package/bin/profile/analyzers/image-analyzer.d.ts +19 -0
- package/bin/profile/analyzers/image-analyzer.js +85 -0
- package/bin/profile/cli.d.ts +4 -0
- package/bin/profile/cli.js +94 -0
- package/bin/profile/form.d.ts +56 -0
- package/bin/profile/form.js +401 -0
- package/bin/profile/index.d.ts +1 -0
- package/bin/profile/index.js +5 -0
- package/bin/profile/optimizers/index.d.ts +19 -0
- package/bin/profile/optimizers/index.js +137 -0
- package/bin/providers/add/form.d.ts +1 -1
- package/bin/providers/add/form.js +27 -6
- package/bin/providers/create/form.js +53 -3
- package/bin/scripts/form.js +27 -5
- package/bin/studio/cli.d.ts +15 -0
- package/bin/studio/cli.js +172 -0
- package/bin/studio/index.d.ts +5 -0
- package/bin/studio/index.js +9 -0
- package/bin/templates/cache.d.ts +54 -0
- package/bin/templates/cache.js +180 -0
- package/bin/templates/cli.d.ts +8 -0
- package/bin/templates/cli.js +294 -0
- package/bin/templates/fetcher.d.ts +49 -0
- package/bin/templates/fetcher.js +208 -0
- package/bin/templates/index.d.ts +11 -0
- package/bin/templates/index.js +37 -0
- package/bin/templates/manager.d.ts +116 -0
- package/bin/templates/manager.js +323 -0
- package/bin/templates/renderer.d.ts +49 -0
- package/bin/templates/renderer.js +204 -0
- package/bin/templates/types.d.ts +51 -0
- package/bin/templates/types.js +5 -0
- package/bin/utils/add-module-to-container.d.ts +14 -3
- package/bin/utils/add-module-to-container.js +327 -98
- package/bin/utils/cli-ui.d.ts +49 -3
- package/bin/utils/cli-ui.js +133 -13
- package/bin/utils/index.d.ts +4 -0
- package/bin/utils/index.js +4 -0
- package/bin/utils/input-validation.d.ts +50 -0
- package/bin/utils/input-validation.js +143 -0
- package/bin/utils/package-manager-commands.d.ts +24 -0
- package/bin/utils/package-manager-commands.js +50 -0
- package/bin/utils/safe-spawn.d.ts +35 -0
- package/bin/utils/safe-spawn.js +51 -0
- package/bin/utils/update-tsconfig-paths.d.ts +35 -0
- package/bin/utils/update-tsconfig-paths.js +326 -0
- package/package.json +165 -156
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Manager - orchestrates template fetching, caching, and rendering
|
|
3
|
+
*/
|
|
4
|
+
import type { TemplateCategory, CICDPlatform, CIStrategy, TemplateManifest, RenderOptions, FetchResult } from "./types";
|
|
5
|
+
export interface TemplateManagerConfig {
|
|
6
|
+
repository?: string;
|
|
7
|
+
branch?: string;
|
|
8
|
+
cacheTTL?: number;
|
|
9
|
+
offline?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class TemplateManager {
|
|
12
|
+
private cache;
|
|
13
|
+
private fetcher;
|
|
14
|
+
private renderer;
|
|
15
|
+
private offline;
|
|
16
|
+
private manifest;
|
|
17
|
+
constructor(config?: TemplateManagerConfig);
|
|
18
|
+
/**
|
|
19
|
+
* Fetch and cache the template manifest
|
|
20
|
+
*/
|
|
21
|
+
getManifest(forceRefresh?: boolean): Promise<TemplateManifest | null>;
|
|
22
|
+
/**
|
|
23
|
+
* Fetch a template by type
|
|
24
|
+
*/
|
|
25
|
+
fetchTemplate(category: TemplateCategory, platform: string, variant?: string): Promise<FetchResult<string>>;
|
|
26
|
+
/**
|
|
27
|
+
* Fetch CI/CD template
|
|
28
|
+
*/
|
|
29
|
+
fetchCICDTemplate(platform: CICDPlatform, strategy?: CIStrategy): Promise<FetchResult<string>>;
|
|
30
|
+
/**
|
|
31
|
+
* Fetch Docker template
|
|
32
|
+
*/
|
|
33
|
+
fetchDockerTemplate(type: string): Promise<FetchResult<string>>;
|
|
34
|
+
/**
|
|
35
|
+
* Fetch Kubernetes template
|
|
36
|
+
*/
|
|
37
|
+
fetchKubernetesTemplate(type: string): Promise<FetchResult<string>>;
|
|
38
|
+
/**
|
|
39
|
+
* Fetch migration template
|
|
40
|
+
*/
|
|
41
|
+
fetchMigrationTemplate(from: string, to: string, file?: string): Promise<FetchResult<string>>;
|
|
42
|
+
/**
|
|
43
|
+
* Render a template with variables
|
|
44
|
+
*/
|
|
45
|
+
render(template: string, options: RenderOptions): string;
|
|
46
|
+
/**
|
|
47
|
+
* Fetch and render template in one step
|
|
48
|
+
*/
|
|
49
|
+
fetchAndRender(category: TemplateCategory, platform: string, variant: string | undefined, renderOptions: RenderOptions): Promise<FetchResult<string>>;
|
|
50
|
+
/**
|
|
51
|
+
* Update all cached templates
|
|
52
|
+
*/
|
|
53
|
+
updateCache(): Promise<{
|
|
54
|
+
updated: number;
|
|
55
|
+
errors: string[];
|
|
56
|
+
}>;
|
|
57
|
+
/**
|
|
58
|
+
* Clear template cache
|
|
59
|
+
*/
|
|
60
|
+
clearCache(): void;
|
|
61
|
+
/**
|
|
62
|
+
* Get cache statistics
|
|
63
|
+
*/
|
|
64
|
+
getCacheStats(): {
|
|
65
|
+
files: number;
|
|
66
|
+
totalSize: number;
|
|
67
|
+
oldestEntry: Date | null;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* List available templates
|
|
71
|
+
*/
|
|
72
|
+
listTemplates(): Promise<{
|
|
73
|
+
cicd: Record<string, string[]>;
|
|
74
|
+
docker: string[];
|
|
75
|
+
kubernetes: string[];
|
|
76
|
+
migrations: string[];
|
|
77
|
+
source: "remote" | "embedded";
|
|
78
|
+
}>;
|
|
79
|
+
/**
|
|
80
|
+
* Get list of embedded templates (fallback when remote unavailable)
|
|
81
|
+
*/
|
|
82
|
+
private getEmbeddedTemplatesList;
|
|
83
|
+
/**
|
|
84
|
+
* Set repository for template fetching
|
|
85
|
+
*/
|
|
86
|
+
setRepository(repository: string, branch?: string): void;
|
|
87
|
+
/**
|
|
88
|
+
* Set offline mode
|
|
89
|
+
*/
|
|
90
|
+
setOfflineMode(offline: boolean): void;
|
|
91
|
+
/**
|
|
92
|
+
* Check if templates are available (cached or remote)
|
|
93
|
+
*/
|
|
94
|
+
checkAvailability(): Promise<{
|
|
95
|
+
online: boolean;
|
|
96
|
+
cached: boolean;
|
|
97
|
+
manifest: boolean;
|
|
98
|
+
}>;
|
|
99
|
+
/**
|
|
100
|
+
* Validate a template
|
|
101
|
+
*/
|
|
102
|
+
validateTemplate(template: string): {
|
|
103
|
+
valid: boolean;
|
|
104
|
+
errors: string[];
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Extract variables from template
|
|
108
|
+
*/
|
|
109
|
+
extractVariables(template: string): string[];
|
|
110
|
+
/**
|
|
111
|
+
* Print template status (for CLI output)
|
|
112
|
+
*/
|
|
113
|
+
printStatus(): Promise<void>;
|
|
114
|
+
}
|
|
115
|
+
export declare function getTemplateManager(config?: TemplateManagerConfig): TemplateManager;
|
|
116
|
+
export declare function resetTemplateManager(): void;
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Template Manager - orchestrates template fetching, caching, and rendering
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.resetTemplateManager = exports.getTemplateManager = exports.TemplateManager = void 0;
|
|
10
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
11
|
+
const cache_1 = require("./cache");
|
|
12
|
+
const fetcher_1 = require("./fetcher");
|
|
13
|
+
const renderer_1 = require("./renderer");
|
|
14
|
+
class TemplateManager {
|
|
15
|
+
constructor(config) {
|
|
16
|
+
this.manifest = null;
|
|
17
|
+
this.cache = (0, cache_1.getTemplateCache)({ ttl: config?.cacheTTL });
|
|
18
|
+
this.fetcher = (0, fetcher_1.getGitHubFetcher)({
|
|
19
|
+
repository: config?.repository,
|
|
20
|
+
branch: config?.branch,
|
|
21
|
+
});
|
|
22
|
+
this.renderer = (0, renderer_1.getTemplateRenderer)();
|
|
23
|
+
this.offline = config?.offline ?? false;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fetch and cache the template manifest
|
|
27
|
+
*/
|
|
28
|
+
async getManifest(forceRefresh = false) {
|
|
29
|
+
// Check memory cache first
|
|
30
|
+
if (this.manifest && !forceRefresh) {
|
|
31
|
+
return this.manifest;
|
|
32
|
+
}
|
|
33
|
+
// Check disk cache
|
|
34
|
+
if (!forceRefresh) {
|
|
35
|
+
const cached = this.cache.get("manifest", "root");
|
|
36
|
+
if (cached) {
|
|
37
|
+
this.manifest = cached;
|
|
38
|
+
return cached;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Fetch from remote (if not offline)
|
|
42
|
+
if (!this.offline) {
|
|
43
|
+
const result = await this.fetcher.fetchManifest();
|
|
44
|
+
if (result.data) {
|
|
45
|
+
this.manifest = result.data;
|
|
46
|
+
this.cache.set("manifest", "root", result.data);
|
|
47
|
+
return result.data;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Fetch a template by type
|
|
54
|
+
*/
|
|
55
|
+
async fetchTemplate(category, platform, variant) {
|
|
56
|
+
const cacheKey = variant ? `${platform}-${variant}` : platform;
|
|
57
|
+
// Check cache first
|
|
58
|
+
const cached = this.cache.get(category, cacheKey);
|
|
59
|
+
if (cached) {
|
|
60
|
+
return { data: cached, source: "cache" };
|
|
61
|
+
}
|
|
62
|
+
// If offline, return null
|
|
63
|
+
if (this.offline) {
|
|
64
|
+
return {
|
|
65
|
+
data: null,
|
|
66
|
+
source: "cache",
|
|
67
|
+
error: "Offline mode - template not in cache",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Fetch from remote
|
|
71
|
+
const result = await this.fetcher.fetchByType(category, platform, variant);
|
|
72
|
+
if (result.data) {
|
|
73
|
+
// Cache the template
|
|
74
|
+
this.cache.set(category, cacheKey, result.data);
|
|
75
|
+
return { data: result.data, source: "remote" };
|
|
76
|
+
}
|
|
77
|
+
return result;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Fetch CI/CD template
|
|
81
|
+
*/
|
|
82
|
+
async fetchCICDTemplate(platform, strategy = "basic") {
|
|
83
|
+
return this.fetchTemplate("cicd", platform, strategy);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Fetch Docker template
|
|
87
|
+
*/
|
|
88
|
+
async fetchDockerTemplate(type) {
|
|
89
|
+
return this.fetchTemplate("docker", type);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Fetch Kubernetes template
|
|
93
|
+
*/
|
|
94
|
+
async fetchKubernetesTemplate(type) {
|
|
95
|
+
return this.fetchTemplate("kubernetes", type);
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Fetch migration template
|
|
99
|
+
*/
|
|
100
|
+
async fetchMigrationTemplate(from, to, file) {
|
|
101
|
+
const migrationPath = `${from}-to-${to}`;
|
|
102
|
+
return this.fetchTemplate("migrations", migrationPath, file);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Render a template with variables
|
|
106
|
+
*/
|
|
107
|
+
render(template, options) {
|
|
108
|
+
return this.renderer.render(template, options);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Fetch and render template in one step
|
|
112
|
+
*/
|
|
113
|
+
async fetchAndRender(category, platform, variant, renderOptions) {
|
|
114
|
+
const fetchResult = await this.fetchTemplate(category, platform, variant);
|
|
115
|
+
if (!fetchResult.data) {
|
|
116
|
+
return fetchResult;
|
|
117
|
+
}
|
|
118
|
+
const rendered = this.render(fetchResult.data, renderOptions);
|
|
119
|
+
return { data: rendered, source: fetchResult.source };
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Update all cached templates
|
|
123
|
+
*/
|
|
124
|
+
async updateCache() {
|
|
125
|
+
const errors = [];
|
|
126
|
+
let updated = 0;
|
|
127
|
+
// Fetch fresh manifest
|
|
128
|
+
const manifest = await this.getManifest(true);
|
|
129
|
+
if (!manifest) {
|
|
130
|
+
return { updated: 0, errors: ["Failed to fetch manifest"] };
|
|
131
|
+
}
|
|
132
|
+
// Update CI/CD templates
|
|
133
|
+
if (manifest.templates.cicd) {
|
|
134
|
+
for (const [platform, strategies] of Object.entries(manifest.templates.cicd)) {
|
|
135
|
+
for (const [strategy, info] of Object.entries(strategies)) {
|
|
136
|
+
const result = await this.fetcher.fetchTemplate(info.path);
|
|
137
|
+
if (result.data) {
|
|
138
|
+
this.cache.set("cicd", `${platform}-${strategy}`, result.data);
|
|
139
|
+
updated++;
|
|
140
|
+
}
|
|
141
|
+
else if (result.error) {
|
|
142
|
+
errors.push(`${platform}/${strategy}: ${result.error}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Update Docker templates
|
|
148
|
+
if (manifest.templates.docker) {
|
|
149
|
+
for (const [type, info] of Object.entries(manifest.templates.docker)) {
|
|
150
|
+
const result = await this.fetcher.fetchTemplate(info.path);
|
|
151
|
+
if (result.data) {
|
|
152
|
+
this.cache.set("docker", type, result.data);
|
|
153
|
+
updated++;
|
|
154
|
+
}
|
|
155
|
+
else if (result.error) {
|
|
156
|
+
errors.push(`docker/${type}: ${result.error}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Update Kubernetes templates
|
|
161
|
+
if (manifest.templates.kubernetes) {
|
|
162
|
+
for (const [type, info] of Object.entries(manifest.templates.kubernetes)) {
|
|
163
|
+
const result = await this.fetcher.fetchTemplate(info.path);
|
|
164
|
+
if (result.data) {
|
|
165
|
+
this.cache.set("kubernetes", type, result.data);
|
|
166
|
+
updated++;
|
|
167
|
+
}
|
|
168
|
+
else if (result.error) {
|
|
169
|
+
errors.push(`kubernetes/${type}: ${result.error}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return { updated, errors };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Clear template cache
|
|
177
|
+
*/
|
|
178
|
+
clearCache() {
|
|
179
|
+
this.cache.clear();
|
|
180
|
+
this.manifest = null;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get cache statistics
|
|
184
|
+
*/
|
|
185
|
+
getCacheStats() {
|
|
186
|
+
return this.cache.getStats();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* List available templates
|
|
190
|
+
*/
|
|
191
|
+
async listTemplates() {
|
|
192
|
+
const manifest = await this.getManifest();
|
|
193
|
+
// If remote manifest available, use it
|
|
194
|
+
if (manifest) {
|
|
195
|
+
const result = {
|
|
196
|
+
cicd: {},
|
|
197
|
+
docker: [],
|
|
198
|
+
kubernetes: [],
|
|
199
|
+
migrations: [],
|
|
200
|
+
source: "remote",
|
|
201
|
+
};
|
|
202
|
+
// CI/CD templates
|
|
203
|
+
if (manifest.templates.cicd) {
|
|
204
|
+
for (const [platform, strategies] of Object.entries(manifest.templates.cicd)) {
|
|
205
|
+
result.cicd[platform] = Object.keys(strategies);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// Docker templates
|
|
209
|
+
if (manifest.templates.docker) {
|
|
210
|
+
result.docker = Object.keys(manifest.templates.docker);
|
|
211
|
+
}
|
|
212
|
+
// Kubernetes templates
|
|
213
|
+
if (manifest.templates.kubernetes) {
|
|
214
|
+
result.kubernetes = Object.keys(manifest.templates.kubernetes);
|
|
215
|
+
}
|
|
216
|
+
// Migration templates
|
|
217
|
+
if (manifest.templates.migrations) {
|
|
218
|
+
for (const [from, targets] of Object.entries(manifest.templates.migrations)) {
|
|
219
|
+
for (const to of Object.keys(targets)) {
|
|
220
|
+
result.migrations.push(`${from} → ${to}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return result;
|
|
225
|
+
}
|
|
226
|
+
// Fallback to embedded templates list
|
|
227
|
+
return this.getEmbeddedTemplatesList();
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get list of embedded templates (fallback when remote unavailable)
|
|
231
|
+
*/
|
|
232
|
+
getEmbeddedTemplatesList() {
|
|
233
|
+
return {
|
|
234
|
+
cicd: {
|
|
235
|
+
github: ["basic", "comprehensive", "security-focused"],
|
|
236
|
+
gitlab: ["basic", "comprehensive", "security-focused"],
|
|
237
|
+
circleci: ["basic", "comprehensive"],
|
|
238
|
+
jenkins: ["basic"],
|
|
239
|
+
bitbucket: ["basic"],
|
|
240
|
+
azure: ["basic"],
|
|
241
|
+
},
|
|
242
|
+
docker: [
|
|
243
|
+
"production",
|
|
244
|
+
"development",
|
|
245
|
+
"compose",
|
|
246
|
+
"compose-development",
|
|
247
|
+
],
|
|
248
|
+
kubernetes: ["deployment", "service", "configmap", "ingress"],
|
|
249
|
+
migrations: [
|
|
250
|
+
"heroku → railway",
|
|
251
|
+
"heroku → render",
|
|
252
|
+
"heroku → fly",
|
|
253
|
+
"compose → kubernetes",
|
|
254
|
+
],
|
|
255
|
+
source: "embedded",
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Set repository for template fetching
|
|
260
|
+
*/
|
|
261
|
+
setRepository(repository, branch) {
|
|
262
|
+
this.fetcher.setRepository(repository, branch);
|
|
263
|
+
// Clear cache when repository changes
|
|
264
|
+
this.clearCache();
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Set offline mode
|
|
268
|
+
*/
|
|
269
|
+
setOfflineMode(offline) {
|
|
270
|
+
this.offline = offline;
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* Check if templates are available (cached or remote)
|
|
274
|
+
*/
|
|
275
|
+
async checkAvailability() {
|
|
276
|
+
const online = !this.offline && (await this.fetcher.checkConnection());
|
|
277
|
+
const cached = this.cache.getStats().files > 0;
|
|
278
|
+
const manifest = (await this.getManifest()) !== null;
|
|
279
|
+
return { online, cached, manifest };
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Validate a template
|
|
283
|
+
*/
|
|
284
|
+
validateTemplate(template) {
|
|
285
|
+
return this.renderer.validateTemplate(template);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Extract variables from template
|
|
289
|
+
*/
|
|
290
|
+
extractVariables(template) {
|
|
291
|
+
return this.renderer.extractVariables(template);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Print template status (for CLI output)
|
|
295
|
+
*/
|
|
296
|
+
async printStatus() {
|
|
297
|
+
const availability = await this.checkAvailability();
|
|
298
|
+
const stats = this.getCacheStats();
|
|
299
|
+
console.log(chalk_1.default.bold("\nTemplate System Status:\n"));
|
|
300
|
+
console.log(` Online: ${availability.online ? chalk_1.default.green("✓") : chalk_1.default.red("✗")}`);
|
|
301
|
+
console.log(` Cached: ${availability.cached ? chalk_1.default.green("✓") : chalk_1.default.yellow("No templates cached")}`);
|
|
302
|
+
console.log(` Manifest: ${availability.manifest ? chalk_1.default.green("✓") : chalk_1.default.yellow("Not loaded")}`);
|
|
303
|
+
console.log(chalk_1.default.bold("\nCache Statistics:\n"));
|
|
304
|
+
console.log(` Files: ${stats.files}`);
|
|
305
|
+
console.log(` Size: ${(stats.totalSize / 1024).toFixed(2)} KB`);
|
|
306
|
+
console.log(` Oldest: ${stats.oldestEntry?.toLocaleString() || "N/A"}`);
|
|
307
|
+
console.log(` Location: ${this.cache.getCacheDirectory()}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
exports.TemplateManager = TemplateManager;
|
|
311
|
+
// Singleton instance
|
|
312
|
+
let managerInstance = null;
|
|
313
|
+
function getTemplateManager(config) {
|
|
314
|
+
if (!managerInstance) {
|
|
315
|
+
managerInstance = new TemplateManager(config);
|
|
316
|
+
}
|
|
317
|
+
return managerInstance;
|
|
318
|
+
}
|
|
319
|
+
exports.getTemplateManager = getTemplateManager;
|
|
320
|
+
function resetTemplateManager() {
|
|
321
|
+
managerInstance = null;
|
|
322
|
+
}
|
|
323
|
+
exports.resetTemplateManager = resetTemplateManager;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template renderer - processes templates with variable substitution
|
|
3
|
+
* Uses Mustache-like syntax: {{variable}}, {{#condition}}...{{/condition}}
|
|
4
|
+
*/
|
|
5
|
+
import type { RenderOptions } from "./types";
|
|
6
|
+
export declare class TemplateRenderer {
|
|
7
|
+
/**
|
|
8
|
+
* Render template with variable substitution
|
|
9
|
+
*/
|
|
10
|
+
render(template: string, options: RenderOptions): string;
|
|
11
|
+
/**
|
|
12
|
+
* Process conditional blocks {{#condition}}...{{/condition}}
|
|
13
|
+
*/
|
|
14
|
+
private processConditionals;
|
|
15
|
+
/**
|
|
16
|
+
* Process negative conditional blocks {{^condition}}...{{/condition}}
|
|
17
|
+
*/
|
|
18
|
+
private processNegativeConditionals;
|
|
19
|
+
/**
|
|
20
|
+
* Process loop blocks for arrays
|
|
21
|
+
*/
|
|
22
|
+
private processLoops;
|
|
23
|
+
/**
|
|
24
|
+
* Process simple variable substitution {{variable}}
|
|
25
|
+
*/
|
|
26
|
+
private processVariables;
|
|
27
|
+
/**
|
|
28
|
+
* Get nested value from object using dot notation. Falls back to a
|
|
29
|
+
* flat-key lookup so callers can provide either
|
|
30
|
+
* `{ app: { version: "1.0.0" } }` or `{ "app.version": "1.0.0" }`.
|
|
31
|
+
*/
|
|
32
|
+
private getNestedValue;
|
|
33
|
+
/**
|
|
34
|
+
* Check if value is truthy for conditional evaluation
|
|
35
|
+
*/
|
|
36
|
+
private isTruthy;
|
|
37
|
+
/**
|
|
38
|
+
* Validate template syntax
|
|
39
|
+
*/
|
|
40
|
+
validateTemplate(template: string): {
|
|
41
|
+
valid: boolean;
|
|
42
|
+
errors: string[];
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Extract variable names from template
|
|
46
|
+
*/
|
|
47
|
+
extractVariables(template: string): string[];
|
|
48
|
+
}
|
|
49
|
+
export declare function getTemplateRenderer(): TemplateRenderer;
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Template renderer - processes templates with variable substitution
|
|
4
|
+
* Uses Mustache-like syntax: {{variable}}, {{#condition}}...{{/condition}}
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.getTemplateRenderer = exports.TemplateRenderer = void 0;
|
|
8
|
+
class TemplateRenderer {
|
|
9
|
+
/**
|
|
10
|
+
* Render template with variable substitution
|
|
11
|
+
*/
|
|
12
|
+
render(template, options) {
|
|
13
|
+
let result = template;
|
|
14
|
+
// Process conditionals first: {{#condition}}content{{/condition}}
|
|
15
|
+
result = this.processConditionals(result, options);
|
|
16
|
+
// Process negative conditionals: {{^condition}}content{{/condition}}
|
|
17
|
+
result = this.processNegativeConditionals(result, options);
|
|
18
|
+
// Process loops: {{#items}}{{.}}{{/items}}
|
|
19
|
+
result = this.processLoops(result, options);
|
|
20
|
+
// Process simple variable substitution: {{variable}}
|
|
21
|
+
result = this.processVariables(result, options);
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Process conditional blocks {{#condition}}...{{/condition}}
|
|
26
|
+
*/
|
|
27
|
+
processConditionals(template, options) {
|
|
28
|
+
const pattern = /\{\{#(\w+)\}\}([\s\S]*?)\{\{\/\1\}\}/g;
|
|
29
|
+
return template.replace(pattern, (match, condition, content) => {
|
|
30
|
+
const value = options.conditionals?.[condition] ??
|
|
31
|
+
options.variables[condition];
|
|
32
|
+
// Check if value is truthy
|
|
33
|
+
if (this.isTruthy(value)) {
|
|
34
|
+
// Recursively process the content
|
|
35
|
+
return this.render(content, options);
|
|
36
|
+
}
|
|
37
|
+
return "";
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Process negative conditional blocks {{^condition}}...{{/condition}}
|
|
42
|
+
*/
|
|
43
|
+
processNegativeConditionals(template, options) {
|
|
44
|
+
const pattern = /\{\{\^(\w+)\}\}([\s\S]*?)\{\{\/\1\}\}/g;
|
|
45
|
+
return template.replace(pattern, (match, condition, content) => {
|
|
46
|
+
const value = options.conditionals?.[condition] ??
|
|
47
|
+
options.variables[condition];
|
|
48
|
+
// Check if value is falsy
|
|
49
|
+
if (!this.isTruthy(value)) {
|
|
50
|
+
return this.render(content, options);
|
|
51
|
+
}
|
|
52
|
+
return "";
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Process loop blocks for arrays
|
|
57
|
+
*/
|
|
58
|
+
processLoops(template, options) {
|
|
59
|
+
const pattern = /\{\{#each\s+(\w+)\}\}([\s\S]*?)\{\{\/each\}\}/g;
|
|
60
|
+
return template.replace(pattern, (match, arrayName, content) => {
|
|
61
|
+
const array = options.variables[arrayName];
|
|
62
|
+
if (!Array.isArray(array)) {
|
|
63
|
+
return "";
|
|
64
|
+
}
|
|
65
|
+
return array
|
|
66
|
+
.map((item, index) => {
|
|
67
|
+
// Create new options with item context
|
|
68
|
+
const itemOptions = {
|
|
69
|
+
...options,
|
|
70
|
+
variables: {
|
|
71
|
+
...options.variables,
|
|
72
|
+
".": String(item),
|
|
73
|
+
"@index": index,
|
|
74
|
+
"@first": index === 0,
|
|
75
|
+
"@last": index === array.length - 1,
|
|
76
|
+
...(typeof item === "object" ? item : {}),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
return this.render(content, itemOptions);
|
|
80
|
+
})
|
|
81
|
+
.join("");
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Process simple variable substitution {{variable}}
|
|
86
|
+
*/
|
|
87
|
+
processVariables(template, options) {
|
|
88
|
+
// Match {{variable}} but not {{#variable}} or {{/variable}} or {{^variable}}
|
|
89
|
+
const pattern = /\{\{([^#/^}][^}]*?)\}\}/g;
|
|
90
|
+
return template.replace(pattern, (match, variable) => {
|
|
91
|
+
const trimmedVar = variable.trim();
|
|
92
|
+
// Handle dot notation for nested variables
|
|
93
|
+
const value = this.getNestedValue(options.variables, trimmedVar);
|
|
94
|
+
if (value === undefined || value === null) {
|
|
95
|
+
// Keep the placeholder if variable not found (for debugging)
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
return String(value);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get nested value from object using dot notation. Falls back to a
|
|
103
|
+
* flat-key lookup so callers can provide either
|
|
104
|
+
* `{ app: { version: "1.0.0" } }` or `{ "app.version": "1.0.0" }`.
|
|
105
|
+
*/
|
|
106
|
+
getNestedValue(obj, path) {
|
|
107
|
+
if (Object.prototype.hasOwnProperty.call(obj, path)) {
|
|
108
|
+
return obj[path];
|
|
109
|
+
}
|
|
110
|
+
const parts = path.split(".");
|
|
111
|
+
let current = obj;
|
|
112
|
+
for (const part of parts) {
|
|
113
|
+
if (current === null || current === undefined) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
if (typeof current !== "object") {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
current = current[part];
|
|
120
|
+
}
|
|
121
|
+
return current;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if value is truthy for conditional evaluation
|
|
125
|
+
*/
|
|
126
|
+
isTruthy(value) {
|
|
127
|
+
if (value === undefined || value === null)
|
|
128
|
+
return false;
|
|
129
|
+
if (typeof value === "boolean")
|
|
130
|
+
return value;
|
|
131
|
+
if (typeof value === "number")
|
|
132
|
+
return value !== 0;
|
|
133
|
+
if (typeof value === "string")
|
|
134
|
+
return value.length > 0;
|
|
135
|
+
if (Array.isArray(value))
|
|
136
|
+
return value.length > 0;
|
|
137
|
+
if (typeof value === "object")
|
|
138
|
+
return Object.keys(value).length > 0;
|
|
139
|
+
return Boolean(value);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Validate template syntax
|
|
143
|
+
*/
|
|
144
|
+
validateTemplate(template) {
|
|
145
|
+
const errors = [];
|
|
146
|
+
// Check for unclosed conditionals
|
|
147
|
+
const openConditionals = template.match(/\{\{#(\w+)\}\}/g) || [];
|
|
148
|
+
const closeConditionals = template.match(/\{\{\/(\w+)\}\}/g) || [];
|
|
149
|
+
for (const open of openConditionals) {
|
|
150
|
+
const name = open.match(/\{\{#(\w+)\}\}/)?.[1];
|
|
151
|
+
if (name) {
|
|
152
|
+
const closePattern = new RegExp(`\\{\\{\\/${name}\\}\\}`);
|
|
153
|
+
if (!closePattern.test(template)) {
|
|
154
|
+
errors.push(`Unclosed conditional block: {{#${name}}}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// Check for orphaned closing tags
|
|
159
|
+
for (const close of closeConditionals) {
|
|
160
|
+
const name = close.match(/\{\{\/(\w+)\}\}/)?.[1];
|
|
161
|
+
if (name) {
|
|
162
|
+
const openPattern = new RegExp(`\\{\\{[#\\^]${name}\\}\\}`);
|
|
163
|
+
if (!openPattern.test(template)) {
|
|
164
|
+
errors.push(`Orphaned closing tag: {{/${name}}}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
valid: errors.length === 0,
|
|
170
|
+
errors,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Extract variable names from template
|
|
175
|
+
*/
|
|
176
|
+
extractVariables(template) {
|
|
177
|
+
const variables = new Set();
|
|
178
|
+
// Match simple variables
|
|
179
|
+
const simplePattern = /\{\{([^#/^}][^}]*?)\}\}/g;
|
|
180
|
+
let match;
|
|
181
|
+
while ((match = simplePattern.exec(template)) !== null) {
|
|
182
|
+
const variable = match[1].trim();
|
|
183
|
+
if (!variable.startsWith(".") && !variable.startsWith("@")) {
|
|
184
|
+
variables.add(variable.split(".")[0]); // Get root variable
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// Match conditional variables
|
|
188
|
+
const conditionalPattern = /\{\{[#^](\w+)\}\}/g;
|
|
189
|
+
while ((match = conditionalPattern.exec(template)) !== null) {
|
|
190
|
+
variables.add(match[1]);
|
|
191
|
+
}
|
|
192
|
+
return Array.from(variables);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
exports.TemplateRenderer = TemplateRenderer;
|
|
196
|
+
// Singleton instance
|
|
197
|
+
let rendererInstance = null;
|
|
198
|
+
function getTemplateRenderer() {
|
|
199
|
+
if (!rendererInstance) {
|
|
200
|
+
rendererInstance = new TemplateRenderer();
|
|
201
|
+
}
|
|
202
|
+
return rendererInstance;
|
|
203
|
+
}
|
|
204
|
+
exports.getTemplateRenderer = getTemplateRenderer;
|