genomic 4.0.2 → 5.0.0
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 +154 -1125
- package/cache/cache-manager.d.ts +60 -0
- package/cache/cache-manager.js +228 -0
- package/cache/types.d.ts +22 -0
- package/esm/cache/cache-manager.js +191 -0
- package/esm/git/git-cloner.js +92 -0
- package/esm/index.js +41 -4
- package/esm/licenses.js +120 -0
- package/esm/scaffolder/index.js +2 -0
- package/esm/scaffolder/template-scaffolder.js +310 -0
- package/esm/scaffolder/types.js +1 -0
- package/esm/template/extract.js +162 -0
- package/esm/template/prompt.js +103 -0
- package/esm/template/replace.js +110 -0
- package/esm/template/templatizer.js +73 -0
- package/esm/template/types.js +1 -0
- package/esm/types.js +1 -0
- package/esm/utils/npm-version-check.js +52 -0
- package/esm/utils/types.js +1 -0
- package/git/git-cloner.d.ts +32 -0
- package/git/git-cloner.js +129 -0
- package/git/types.d.ts +15 -0
- package/index.d.ts +29 -4
- package/index.js +43 -4
- package/licenses-templates/APACHE-2.0.txt +18 -0
- package/licenses-templates/BSD-3-CLAUSE.txt +28 -0
- package/licenses-templates/CLOSED.txt +20 -0
- package/licenses-templates/GPL-3.0.txt +18 -0
- package/licenses-templates/ISC.txt +16 -0
- package/licenses-templates/MIT.txt +22 -0
- package/licenses-templates/MPL-2.0.txt +8 -0
- package/licenses-templates/UNLICENSE.txt +22 -0
- package/licenses.d.ts +18 -0
- package/licenses.js +162 -0
- package/package.json +9 -14
- package/scaffolder/index.d.ts +2 -0
- package/{question → scaffolder}/index.js +1 -0
- package/scaffolder/template-scaffolder.d.ts +91 -0
- package/scaffolder/template-scaffolder.js +347 -0
- package/scaffolder/types.d.ts +191 -0
- package/scaffolder/types.js +2 -0
- package/template/extract.d.ts +7 -0
- package/template/extract.js +198 -0
- package/template/prompt.d.ts +19 -0
- package/template/prompt.js +107 -0
- package/template/replace.d.ts +9 -0
- package/template/replace.js +146 -0
- package/template/templatizer.d.ts +33 -0
- package/template/templatizer.js +110 -0
- package/template/types.d.ts +18 -0
- package/template/types.js +2 -0
- package/types.d.ts +99 -0
- package/types.js +2 -0
- package/utils/npm-version-check.d.ts +17 -0
- package/utils/npm-version-check.js +57 -0
- package/utils/types.d.ts +6 -0
- package/utils/types.js +2 -0
- package/commander.d.ts +0 -21
- package/commander.js +0 -57
- package/esm/commander.js +0 -50
- package/esm/keypress.js +0 -95
- package/esm/prompt.js +0 -1024
- package/esm/question/index.js +0 -1
- package/esm/resolvers/date.js +0 -11
- package/esm/resolvers/git.js +0 -26
- package/esm/resolvers/index.js +0 -103
- package/esm/resolvers/npm.js +0 -24
- package/esm/resolvers/workspace.js +0 -141
- package/esm/utils.js +0 -12
- package/keypress.d.ts +0 -45
- package/keypress.js +0 -99
- package/prompt.d.ts +0 -116
- package/prompt.js +0 -1032
- package/question/index.d.ts +0 -1
- package/question/types.d.ts +0 -65
- package/resolvers/date.d.ts +0 -5
- package/resolvers/date.js +0 -14
- package/resolvers/git.d.ts +0 -11
- package/resolvers/git.js +0 -30
- package/resolvers/index.d.ts +0 -63
- package/resolvers/index.js +0 -111
- package/resolvers/npm.d.ts +0 -10
- package/resolvers/npm.js +0 -28
- package/resolvers/types.d.ts +0 -12
- package/resolvers/workspace.d.ts +0 -6
- package/resolvers/workspace.js +0 -144
- package/utils.d.ts +0 -2
- package/utils.js +0 -16
- /package/{question → cache}/types.js +0 -0
- /package/esm/{question → cache}/types.js +0 -0
- /package/esm/{resolvers → git}/types.js +0 -0
- /package/{resolvers → git}/types.js +0 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { AppStashResult } from 'appstash';
|
|
2
|
+
import { CacheManagerConfig, CacheMetadata, CacheEntryInfo } from './types';
|
|
3
|
+
export declare class CacheManager {
|
|
4
|
+
private config;
|
|
5
|
+
private dirs;
|
|
6
|
+
private reposDir;
|
|
7
|
+
private metadataDir;
|
|
8
|
+
constructor(config: CacheManagerConfig);
|
|
9
|
+
/**
|
|
10
|
+
* Public accessor for the repos cache directory path.
|
|
11
|
+
*/
|
|
12
|
+
getReposDir(): string;
|
|
13
|
+
/**
|
|
14
|
+
* Public accessor for the metadata cache directory path.
|
|
15
|
+
*/
|
|
16
|
+
getMetadataDir(): string;
|
|
17
|
+
/**
|
|
18
|
+
* Public accessor for resolved appstash directories.
|
|
19
|
+
*/
|
|
20
|
+
getAppstashDirs(): AppStashResult;
|
|
21
|
+
/**
|
|
22
|
+
* Get cached directory path if exists and not expired
|
|
23
|
+
* Returns null if not cached or expired
|
|
24
|
+
*/
|
|
25
|
+
get(key: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Store directory in cache with metadata
|
|
28
|
+
* Does NOT perform cloning - just registers the directory
|
|
29
|
+
*/
|
|
30
|
+
set(key: string, sourcePath: string): string;
|
|
31
|
+
/**
|
|
32
|
+
* Check if cache entry is expired based on TTL
|
|
33
|
+
* Returns null if no metadata or not expired, returns metadata if expired
|
|
34
|
+
*/
|
|
35
|
+
checkExpiration(key: string): CacheMetadata | null;
|
|
36
|
+
/**
|
|
37
|
+
* Clear specific cache entry
|
|
38
|
+
*/
|
|
39
|
+
clear(key: string): void;
|
|
40
|
+
/**
|
|
41
|
+
* Clear all cache entries
|
|
42
|
+
*/
|
|
43
|
+
clearAll(): void;
|
|
44
|
+
/**
|
|
45
|
+
* List all cache entries with expiration status
|
|
46
|
+
*/
|
|
47
|
+
listAll(): CacheEntryInfo[];
|
|
48
|
+
/**
|
|
49
|
+
* Get metadata for a cache entry
|
|
50
|
+
*/
|
|
51
|
+
getMetadata(key: string): CacheMetadata | null;
|
|
52
|
+
/**
|
|
53
|
+
* Create a cache key from identifier (e.g., git URL + branch)
|
|
54
|
+
*/
|
|
55
|
+
createKey(identifier: string, variant?: string): string;
|
|
56
|
+
private ensureDirectories;
|
|
57
|
+
private isExpired;
|
|
58
|
+
private getCachePath;
|
|
59
|
+
private getMetadataPath;
|
|
60
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.CacheManager = void 0;
|
|
37
|
+
const crypto = __importStar(require("crypto"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const appstash_1 = require("appstash");
|
|
41
|
+
class CacheManager {
|
|
42
|
+
config;
|
|
43
|
+
dirs;
|
|
44
|
+
reposDir;
|
|
45
|
+
metadataDir;
|
|
46
|
+
constructor(config) {
|
|
47
|
+
// Validate required toolName
|
|
48
|
+
if (!config.toolName) {
|
|
49
|
+
throw new Error('CacheManager requires toolName parameter');
|
|
50
|
+
}
|
|
51
|
+
this.config = {
|
|
52
|
+
toolName: config.toolName,
|
|
53
|
+
baseDir: config.baseDir,
|
|
54
|
+
ttl: config.ttl,
|
|
55
|
+
dirs: config.dirs,
|
|
56
|
+
};
|
|
57
|
+
this.dirs =
|
|
58
|
+
config.dirs ??
|
|
59
|
+
(0, appstash_1.appstash)(this.config.toolName, {
|
|
60
|
+
ensure: true,
|
|
61
|
+
baseDir: this.config.baseDir,
|
|
62
|
+
});
|
|
63
|
+
this.reposDir = (0, appstash_1.resolve)(this.dirs, 'cache', 'repos');
|
|
64
|
+
this.metadataDir = (0, appstash_1.resolve)(this.dirs, 'cache', 'metadata');
|
|
65
|
+
this.ensureDirectories();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Public accessor for the repos cache directory path.
|
|
69
|
+
*/
|
|
70
|
+
getReposDir() {
|
|
71
|
+
return this.reposDir;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Public accessor for the metadata cache directory path.
|
|
75
|
+
*/
|
|
76
|
+
getMetadataDir() {
|
|
77
|
+
return this.metadataDir;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Public accessor for resolved appstash directories.
|
|
81
|
+
*/
|
|
82
|
+
getAppstashDirs() {
|
|
83
|
+
return this.dirs;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get cached directory path if exists and not expired
|
|
87
|
+
* Returns null if not cached or expired
|
|
88
|
+
*/
|
|
89
|
+
get(key) {
|
|
90
|
+
const cachePath = this.getCachePath(key);
|
|
91
|
+
if (!fs.existsSync(cachePath)) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
// Check expiration - if expired, return null
|
|
95
|
+
const expired = this.checkExpiration(key);
|
|
96
|
+
if (expired) {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
return cachePath;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Store directory in cache with metadata
|
|
103
|
+
* Does NOT perform cloning - just registers the directory
|
|
104
|
+
*/
|
|
105
|
+
set(key, sourcePath) {
|
|
106
|
+
const cachePath = this.getCachePath(key);
|
|
107
|
+
// Write metadata with current timestamp
|
|
108
|
+
const metadata = {
|
|
109
|
+
key,
|
|
110
|
+
identifier: sourcePath,
|
|
111
|
+
lastUpdated: Date.now(),
|
|
112
|
+
};
|
|
113
|
+
fs.writeFileSync(this.getMetadataPath(key), JSON.stringify(metadata, null, 2));
|
|
114
|
+
return cachePath;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Check if cache entry is expired based on TTL
|
|
118
|
+
* Returns null if no metadata or not expired, returns metadata if expired
|
|
119
|
+
*/
|
|
120
|
+
checkExpiration(key) {
|
|
121
|
+
const metadata = this.getMetadata(key);
|
|
122
|
+
if (!metadata) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
if (this.isExpired(metadata)) {
|
|
126
|
+
return metadata;
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Clear specific cache entry
|
|
132
|
+
*/
|
|
133
|
+
clear(key) {
|
|
134
|
+
const cachePath = this.getCachePath(key);
|
|
135
|
+
const metadataPath = this.getMetadataPath(key);
|
|
136
|
+
if (fs.existsSync(cachePath)) {
|
|
137
|
+
fs.rmSync(cachePath, { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
if (fs.existsSync(metadataPath)) {
|
|
140
|
+
fs.rmSync(metadataPath, { force: true });
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Clear all cache entries
|
|
145
|
+
*/
|
|
146
|
+
clearAll() {
|
|
147
|
+
if (fs.existsSync(this.reposDir)) {
|
|
148
|
+
fs.rmSync(this.reposDir, { recursive: true, force: true });
|
|
149
|
+
fs.mkdirSync(this.reposDir, { recursive: true });
|
|
150
|
+
}
|
|
151
|
+
if (fs.existsSync(this.metadataDir)) {
|
|
152
|
+
fs.rmSync(this.metadataDir, { recursive: true, force: true });
|
|
153
|
+
fs.mkdirSync(this.metadataDir, { recursive: true });
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* List all cache entries with expiration status
|
|
158
|
+
*/
|
|
159
|
+
listAll() {
|
|
160
|
+
if (!fs.existsSync(this.metadataDir)) {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
const results = [];
|
|
164
|
+
const files = fs.readdirSync(this.metadataDir);
|
|
165
|
+
for (const file of files) {
|
|
166
|
+
if (!file.endsWith('.json')) {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
const metadataPath = path.join(this.metadataDir, file);
|
|
170
|
+
try {
|
|
171
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
|
|
172
|
+
results.push({
|
|
173
|
+
...metadata,
|
|
174
|
+
path: this.getCachePath(metadata.key),
|
|
175
|
+
expired: this.isExpired(metadata),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
// Skip corrupted metadata
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return results;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get metadata for a cache entry
|
|
186
|
+
*/
|
|
187
|
+
getMetadata(key) {
|
|
188
|
+
const metadataPath = this.getMetadataPath(key);
|
|
189
|
+
if (!fs.existsSync(metadataPath)) {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
return JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Create a cache key from identifier (e.g., git URL + branch)
|
|
201
|
+
*/
|
|
202
|
+
createKey(identifier, variant) {
|
|
203
|
+
const input = variant ? `${identifier}#${variant}` : identifier;
|
|
204
|
+
return crypto.createHash('md5').update(input).digest('hex');
|
|
205
|
+
}
|
|
206
|
+
ensureDirectories() {
|
|
207
|
+
if (!fs.existsSync(this.reposDir)) {
|
|
208
|
+
fs.mkdirSync(this.reposDir, { recursive: true });
|
|
209
|
+
}
|
|
210
|
+
if (!fs.existsSync(this.metadataDir)) {
|
|
211
|
+
fs.mkdirSync(this.metadataDir, { recursive: true });
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
isExpired(metadata) {
|
|
215
|
+
if (!this.config.ttl) {
|
|
216
|
+
return false;
|
|
217
|
+
}
|
|
218
|
+
const age = Date.now() - metadata.lastUpdated;
|
|
219
|
+
return age > this.config.ttl;
|
|
220
|
+
}
|
|
221
|
+
getCachePath(key) {
|
|
222
|
+
return path.join(this.reposDir, key);
|
|
223
|
+
}
|
|
224
|
+
getMetadataPath(key) {
|
|
225
|
+
return path.join(this.metadataDir, `${key}.json`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
exports.CacheManager = CacheManager;
|
package/cache/types.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AppStashResult } from 'appstash';
|
|
2
|
+
export interface CacheManagerConfig {
|
|
3
|
+
toolName: string;
|
|
4
|
+
baseDir?: string;
|
|
5
|
+
ttl?: number;
|
|
6
|
+
/**
|
|
7
|
+
* Optional pre-resolved appstash directories owned by the caller.
|
|
8
|
+
* When provided, CacheManager will use these instead of creating its own.
|
|
9
|
+
*/
|
|
10
|
+
dirs?: AppStashResult;
|
|
11
|
+
}
|
|
12
|
+
export interface CacheMetadata {
|
|
13
|
+
key: string;
|
|
14
|
+
identifier: string;
|
|
15
|
+
variant?: string;
|
|
16
|
+
lastUpdated: number;
|
|
17
|
+
source?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface CacheEntryInfo extends CacheMetadata {
|
|
20
|
+
path: string;
|
|
21
|
+
expired: boolean;
|
|
22
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { appstash, resolve as resolveAppstash } from 'appstash';
|
|
5
|
+
export class CacheManager {
|
|
6
|
+
config;
|
|
7
|
+
dirs;
|
|
8
|
+
reposDir;
|
|
9
|
+
metadataDir;
|
|
10
|
+
constructor(config) {
|
|
11
|
+
// Validate required toolName
|
|
12
|
+
if (!config.toolName) {
|
|
13
|
+
throw new Error('CacheManager requires toolName parameter');
|
|
14
|
+
}
|
|
15
|
+
this.config = {
|
|
16
|
+
toolName: config.toolName,
|
|
17
|
+
baseDir: config.baseDir,
|
|
18
|
+
ttl: config.ttl,
|
|
19
|
+
dirs: config.dirs,
|
|
20
|
+
};
|
|
21
|
+
this.dirs =
|
|
22
|
+
config.dirs ??
|
|
23
|
+
appstash(this.config.toolName, {
|
|
24
|
+
ensure: true,
|
|
25
|
+
baseDir: this.config.baseDir,
|
|
26
|
+
});
|
|
27
|
+
this.reposDir = resolveAppstash(this.dirs, 'cache', 'repos');
|
|
28
|
+
this.metadataDir = resolveAppstash(this.dirs, 'cache', 'metadata');
|
|
29
|
+
this.ensureDirectories();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Public accessor for the repos cache directory path.
|
|
33
|
+
*/
|
|
34
|
+
getReposDir() {
|
|
35
|
+
return this.reposDir;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Public accessor for the metadata cache directory path.
|
|
39
|
+
*/
|
|
40
|
+
getMetadataDir() {
|
|
41
|
+
return this.metadataDir;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Public accessor for resolved appstash directories.
|
|
45
|
+
*/
|
|
46
|
+
getAppstashDirs() {
|
|
47
|
+
return this.dirs;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get cached directory path if exists and not expired
|
|
51
|
+
* Returns null if not cached or expired
|
|
52
|
+
*/
|
|
53
|
+
get(key) {
|
|
54
|
+
const cachePath = this.getCachePath(key);
|
|
55
|
+
if (!fs.existsSync(cachePath)) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
// Check expiration - if expired, return null
|
|
59
|
+
const expired = this.checkExpiration(key);
|
|
60
|
+
if (expired) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return cachePath;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Store directory in cache with metadata
|
|
67
|
+
* Does NOT perform cloning - just registers the directory
|
|
68
|
+
*/
|
|
69
|
+
set(key, sourcePath) {
|
|
70
|
+
const cachePath = this.getCachePath(key);
|
|
71
|
+
// Write metadata with current timestamp
|
|
72
|
+
const metadata = {
|
|
73
|
+
key,
|
|
74
|
+
identifier: sourcePath,
|
|
75
|
+
lastUpdated: Date.now(),
|
|
76
|
+
};
|
|
77
|
+
fs.writeFileSync(this.getMetadataPath(key), JSON.stringify(metadata, null, 2));
|
|
78
|
+
return cachePath;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Check if cache entry is expired based on TTL
|
|
82
|
+
* Returns null if no metadata or not expired, returns metadata if expired
|
|
83
|
+
*/
|
|
84
|
+
checkExpiration(key) {
|
|
85
|
+
const metadata = this.getMetadata(key);
|
|
86
|
+
if (!metadata) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
if (this.isExpired(metadata)) {
|
|
90
|
+
return metadata;
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Clear specific cache entry
|
|
96
|
+
*/
|
|
97
|
+
clear(key) {
|
|
98
|
+
const cachePath = this.getCachePath(key);
|
|
99
|
+
const metadataPath = this.getMetadataPath(key);
|
|
100
|
+
if (fs.existsSync(cachePath)) {
|
|
101
|
+
fs.rmSync(cachePath, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
if (fs.existsSync(metadataPath)) {
|
|
104
|
+
fs.rmSync(metadataPath, { force: true });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Clear all cache entries
|
|
109
|
+
*/
|
|
110
|
+
clearAll() {
|
|
111
|
+
if (fs.existsSync(this.reposDir)) {
|
|
112
|
+
fs.rmSync(this.reposDir, { recursive: true, force: true });
|
|
113
|
+
fs.mkdirSync(this.reposDir, { recursive: true });
|
|
114
|
+
}
|
|
115
|
+
if (fs.existsSync(this.metadataDir)) {
|
|
116
|
+
fs.rmSync(this.metadataDir, { recursive: true, force: true });
|
|
117
|
+
fs.mkdirSync(this.metadataDir, { recursive: true });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* List all cache entries with expiration status
|
|
122
|
+
*/
|
|
123
|
+
listAll() {
|
|
124
|
+
if (!fs.existsSync(this.metadataDir)) {
|
|
125
|
+
return [];
|
|
126
|
+
}
|
|
127
|
+
const results = [];
|
|
128
|
+
const files = fs.readdirSync(this.metadataDir);
|
|
129
|
+
for (const file of files) {
|
|
130
|
+
if (!file.endsWith('.json')) {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const metadataPath = path.join(this.metadataDir, file);
|
|
134
|
+
try {
|
|
135
|
+
const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
|
|
136
|
+
results.push({
|
|
137
|
+
...metadata,
|
|
138
|
+
path: this.getCachePath(metadata.key),
|
|
139
|
+
expired: this.isExpired(metadata),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Skip corrupted metadata
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return results;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get metadata for a cache entry
|
|
150
|
+
*/
|
|
151
|
+
getMetadata(key) {
|
|
152
|
+
const metadataPath = this.getMetadataPath(key);
|
|
153
|
+
if (!fs.existsSync(metadataPath)) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
return JSON.parse(fs.readFileSync(metadataPath, 'utf-8'));
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Create a cache key from identifier (e.g., git URL + branch)
|
|
165
|
+
*/
|
|
166
|
+
createKey(identifier, variant) {
|
|
167
|
+
const input = variant ? `${identifier}#${variant}` : identifier;
|
|
168
|
+
return crypto.createHash('md5').update(input).digest('hex');
|
|
169
|
+
}
|
|
170
|
+
ensureDirectories() {
|
|
171
|
+
if (!fs.existsSync(this.reposDir)) {
|
|
172
|
+
fs.mkdirSync(this.reposDir, { recursive: true });
|
|
173
|
+
}
|
|
174
|
+
if (!fs.existsSync(this.metadataDir)) {
|
|
175
|
+
fs.mkdirSync(this.metadataDir, { recursive: true });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
isExpired(metadata) {
|
|
179
|
+
if (!this.config.ttl) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
const age = Date.now() - metadata.lastUpdated;
|
|
183
|
+
return age > this.config.ttl;
|
|
184
|
+
}
|
|
185
|
+
getCachePath(key) {
|
|
186
|
+
return path.join(this.reposDir, key);
|
|
187
|
+
}
|
|
188
|
+
getMetadataPath(key) {
|
|
189
|
+
return path.join(this.metadataDir, `${key}.json`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
export class GitCloner {
|
|
6
|
+
/**
|
|
7
|
+
* Clone a git repository to a destination
|
|
8
|
+
* @param url - Repository URL (will be normalized)
|
|
9
|
+
* @param destination - Target directory path
|
|
10
|
+
* @param options - Clone options (branch, depth)
|
|
11
|
+
* @returns Clone result with normalized URL and destination
|
|
12
|
+
*/
|
|
13
|
+
clone(url, destination, options) {
|
|
14
|
+
const normalizedUrl = this.normalizeUrl(url);
|
|
15
|
+
// Clean destination if exists
|
|
16
|
+
if (fs.existsSync(destination)) {
|
|
17
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
this.executeClone(normalizedUrl, destination, options);
|
|
20
|
+
this.removeGitDir(destination);
|
|
21
|
+
return {
|
|
22
|
+
destination,
|
|
23
|
+
normalizedUrl,
|
|
24
|
+
branch: options?.branch,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Clone to a temporary directory
|
|
29
|
+
* @param url - Repository URL
|
|
30
|
+
* @param options - Clone options
|
|
31
|
+
* @returns Clone result with temp directory path
|
|
32
|
+
*/
|
|
33
|
+
cloneToTemp(url, options) {
|
|
34
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'create-gen-'));
|
|
35
|
+
return this.clone(url, tempDir, options);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Normalize a URL to git-cloneable format
|
|
39
|
+
* Handles: org/repo -> https://github.com/org/repo.git
|
|
40
|
+
*/
|
|
41
|
+
normalizeUrl(url) {
|
|
42
|
+
// Already a full URL
|
|
43
|
+
if (url.startsWith('git@') ||
|
|
44
|
+
url.startsWith('https://') ||
|
|
45
|
+
url.startsWith('http://')) {
|
|
46
|
+
return url;
|
|
47
|
+
}
|
|
48
|
+
// org/repo shorthand
|
|
49
|
+
if (/^[\w-]+\/[\w-]+$/.test(url)) {
|
|
50
|
+
return `https://github.com/${url}.git`;
|
|
51
|
+
}
|
|
52
|
+
return url;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Validate git URL format
|
|
56
|
+
*/
|
|
57
|
+
validateUrl(url) {
|
|
58
|
+
const normalized = this.normalizeUrl(url);
|
|
59
|
+
return (normalized.startsWith('git@') ||
|
|
60
|
+
normalized.startsWith('https://') ||
|
|
61
|
+
normalized.startsWith('http://'));
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Remove .git directory from cloned repo
|
|
65
|
+
*/
|
|
66
|
+
removeGitDir(directory) {
|
|
67
|
+
const gitDir = path.join(directory, '.git');
|
|
68
|
+
if (fs.existsSync(gitDir)) {
|
|
69
|
+
fs.rmSync(gitDir, { recursive: true, force: true });
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
executeClone(url, destination, options) {
|
|
73
|
+
const branch = options?.branch;
|
|
74
|
+
const depth = options?.depth ?? 1;
|
|
75
|
+
const singleBranch = options?.singleBranch ?? true;
|
|
76
|
+
const branchArgs = branch ? ` --branch ${branch}` : '';
|
|
77
|
+
const singleBranchArgs = singleBranch ? ' --single-branch' : '';
|
|
78
|
+
const depthArgs = ` --depth ${depth}`;
|
|
79
|
+
const command = `git clone${branchArgs}${singleBranchArgs}${depthArgs} ${url} ${destination}`;
|
|
80
|
+
try {
|
|
81
|
+
execSync(command, { stdio: 'inherit' });
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
// Clean up on failure
|
|
85
|
+
if (fs.existsSync(destination)) {
|
|
86
|
+
fs.rmSync(destination, { recursive: true, force: true });
|
|
87
|
+
}
|
|
88
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
89
|
+
throw new Error(`Failed to clone repository: ${errorMessage}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
package/esm/index.js
CHANGED
|
@@ -1,4 +1,41 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { registerDefaultResolver } from 'inquirerer';
|
|
2
|
+
import { extractVariables } from './template/extract';
|
|
3
|
+
import { promptUser } from './template/prompt';
|
|
4
|
+
import { replaceVariables } from './template/replace';
|
|
5
|
+
import { listSupportedLicenses } from './licenses';
|
|
6
|
+
// Register the 'licenses' resolver for optionsFrom support
|
|
7
|
+
// This allows boilerplate templates to use: "optionsFrom": "licenses"
|
|
8
|
+
registerDefaultResolver('licenses', () => listSupportedLicenses());
|
|
9
|
+
// Export new modular classes
|
|
10
|
+
export * from './cache/cache-manager';
|
|
11
|
+
export * from './cache/types';
|
|
12
|
+
export * from './git/git-cloner';
|
|
13
|
+
export * from './git/types';
|
|
14
|
+
export * from './scaffolder/template-scaffolder';
|
|
15
|
+
export * from './scaffolder/types';
|
|
16
|
+
export * from './template/templatizer';
|
|
17
|
+
export * from './template/types';
|
|
18
|
+
export * from './utils/npm-version-check';
|
|
19
|
+
export * from './utils/types';
|
|
20
|
+
// Export template processing functions
|
|
21
|
+
export * from './template/extract';
|
|
22
|
+
export * from './template/prompt';
|
|
23
|
+
export * from './template/replace';
|
|
24
|
+
// Export shared types
|
|
25
|
+
export * from './types';
|
|
26
|
+
// DEPRECATED: Legacy exports for backward compatibility (will be removed in future)
|
|
27
|
+
// Use CacheManager, GitCloner, and Templatizer classes instead
|
|
28
|
+
export { extractVariables, promptUser, replaceVariables };
|
|
29
|
+
/**
|
|
30
|
+
* @deprecated This function is deprecated and will be removed in the next major version.
|
|
31
|
+
* Use the modular approach with CacheManager, GitCloner, and Templatizer classes instead.
|
|
32
|
+
* See @genomic/scaffolds-test package for an example of the new orchestration pattern.
|
|
33
|
+
*
|
|
34
|
+
* Create a new project from a template repository
|
|
35
|
+
* @param options - Options for creating the project
|
|
36
|
+
* @returns Path to the generated project
|
|
37
|
+
*/
|
|
38
|
+
export async function createGen(options) {
|
|
39
|
+
throw new Error('createGen() has been deprecated. Please use CacheManager, GitCloner, and Templatizer classes for modular template processing. ' +
|
|
40
|
+
'See @genomic/scaffolds-test package for the new orchestration pattern.');
|
|
41
|
+
}
|