@currentjs/gen 0.1.1
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/CHANGELOG.md +7 -0
- package/LICENSE +56 -0
- package/README.md +686 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +143 -0
- package/dist/commands/commit.d.ts +1 -0
- package/dist/commands/commit.js +153 -0
- package/dist/commands/createApp.d.ts +1 -0
- package/dist/commands/createApp.js +64 -0
- package/dist/commands/createModule.d.ts +1 -0
- package/dist/commands/createModule.js +121 -0
- package/dist/commands/diff.d.ts +1 -0
- package/dist/commands/diff.js +164 -0
- package/dist/commands/generateAll.d.ts +4 -0
- package/dist/commands/generateAll.js +305 -0
- package/dist/commands/infer.d.ts +1 -0
- package/dist/commands/infer.js +179 -0
- package/dist/generators/controllerGenerator.d.ts +20 -0
- package/dist/generators/controllerGenerator.js +280 -0
- package/dist/generators/domainModelGenerator.d.ts +33 -0
- package/dist/generators/domainModelGenerator.js +175 -0
- package/dist/generators/serviceGenerator.d.ts +39 -0
- package/dist/generators/serviceGenerator.js +379 -0
- package/dist/generators/storeGenerator.d.ts +31 -0
- package/dist/generators/storeGenerator.js +191 -0
- package/dist/generators/templateGenerator.d.ts +11 -0
- package/dist/generators/templateGenerator.js +143 -0
- package/dist/generators/templates/appTemplates.d.ts +27 -0
- package/dist/generators/templates/appTemplates.js +1621 -0
- package/dist/generators/templates/controllerTemplates.d.ts +43 -0
- package/dist/generators/templates/controllerTemplates.js +82 -0
- package/dist/generators/templates/index.d.ts +5 -0
- package/dist/generators/templates/index.js +21 -0
- package/dist/generators/templates/serviceTemplates.d.ts +15 -0
- package/dist/generators/templates/serviceTemplates.js +54 -0
- package/dist/generators/templates/storeTemplates.d.ts +9 -0
- package/dist/generators/templates/storeTemplates.js +260 -0
- package/dist/generators/templates/validationTemplates.d.ts +25 -0
- package/dist/generators/templates/validationTemplates.js +66 -0
- package/dist/generators/templates/viewTemplates.d.ts +16 -0
- package/dist/generators/templates/viewTemplates.js +359 -0
- package/dist/generators/validationGenerator.d.ts +24 -0
- package/dist/generators/validationGenerator.js +199 -0
- package/dist/utils/cliUtils.d.ts +6 -0
- package/dist/utils/cliUtils.js +71 -0
- package/dist/utils/colors.d.ts +26 -0
- package/dist/utils/colors.js +80 -0
- package/dist/utils/commitUtils.d.ts +46 -0
- package/dist/utils/commitUtils.js +377 -0
- package/dist/utils/constants.d.ts +52 -0
- package/dist/utils/constants.js +64 -0
- package/dist/utils/generationRegistry.d.ts +25 -0
- package/dist/utils/generationRegistry.js +192 -0
- package/howto.md +556 -0
- package/package.json +44 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export type ColorizeFn = (text: string | number) => string;
|
|
2
|
+
export declare const colors: {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
dim: ColorizeFn;
|
|
5
|
+
bold: ColorizeFn;
|
|
6
|
+
italic: ColorizeFn;
|
|
7
|
+
underline: ColorizeFn;
|
|
8
|
+
inverse: ColorizeFn;
|
|
9
|
+
black: ColorizeFn;
|
|
10
|
+
red: ColorizeFn;
|
|
11
|
+
green: ColorizeFn;
|
|
12
|
+
yellow: ColorizeFn;
|
|
13
|
+
blue: ColorizeFn;
|
|
14
|
+
magenta: ColorizeFn;
|
|
15
|
+
cyan: ColorizeFn;
|
|
16
|
+
white: ColorizeFn;
|
|
17
|
+
gray: ColorizeFn;
|
|
18
|
+
brightRed: ColorizeFn;
|
|
19
|
+
brightGreen: ColorizeFn;
|
|
20
|
+
brightYellow: ColorizeFn;
|
|
21
|
+
brightBlue: ColorizeFn;
|
|
22
|
+
brightMagenta: ColorizeFn;
|
|
23
|
+
brightCyan: ColorizeFn;
|
|
24
|
+
brightWhite: ColorizeFn;
|
|
25
|
+
};
|
|
26
|
+
export declare function stripAnsi(input: string): string;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* Simple ANSI color utilities with TTY/NO_COLOR/FOREG_COLOR detection */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.colors = void 0;
|
|
5
|
+
exports.stripAnsi = stripAnsi;
|
|
6
|
+
function isColorSupported() {
|
|
7
|
+
const force = process.env.FORCE_COLOR;
|
|
8
|
+
if (force && force !== '0')
|
|
9
|
+
return true;
|
|
10
|
+
if (force === '0')
|
|
11
|
+
return false;
|
|
12
|
+
if (process.env.NO_COLOR)
|
|
13
|
+
return false;
|
|
14
|
+
// Prefer stdout TTY; fall back to true in CI with FORCE_COLOR
|
|
15
|
+
return !!process.stdout && !!process.stdout.isTTY;
|
|
16
|
+
}
|
|
17
|
+
const enabled = isColorSupported();
|
|
18
|
+
function createStyle(openCode, closeCode) {
|
|
19
|
+
const open = `\u001b[${openCode}m`;
|
|
20
|
+
const close = `\u001b[${closeCode}m`;
|
|
21
|
+
return (text) => {
|
|
22
|
+
const value = String(text);
|
|
23
|
+
if (!enabled)
|
|
24
|
+
return value;
|
|
25
|
+
return open + value + close;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const dim = createStyle(2, 22);
|
|
29
|
+
const bold = createStyle(1, 22);
|
|
30
|
+
const italic = createStyle(3, 23);
|
|
31
|
+
const underline = createStyle(4, 24);
|
|
32
|
+
const inverse = createStyle(7, 27);
|
|
33
|
+
const black = createStyle(30, 39);
|
|
34
|
+
const red = createStyle(31, 39);
|
|
35
|
+
const green = createStyle(32, 39);
|
|
36
|
+
const yellow = createStyle(33, 39);
|
|
37
|
+
const blue = createStyle(34, 39);
|
|
38
|
+
const magenta = createStyle(35, 39);
|
|
39
|
+
const cyan = createStyle(36, 39);
|
|
40
|
+
const white = createStyle(37, 39);
|
|
41
|
+
const gray = createStyle(90, 39);
|
|
42
|
+
const brightRed = createStyle(91, 39);
|
|
43
|
+
const brightGreen = createStyle(92, 39);
|
|
44
|
+
const brightYellow = createStyle(93, 39);
|
|
45
|
+
const brightBlue = createStyle(94, 39);
|
|
46
|
+
const brightMagenta = createStyle(95, 39);
|
|
47
|
+
const brightCyan = createStyle(96, 39);
|
|
48
|
+
const brightWhite = createStyle(97, 39);
|
|
49
|
+
exports.colors = {
|
|
50
|
+
enabled,
|
|
51
|
+
// modifiers
|
|
52
|
+
dim,
|
|
53
|
+
bold,
|
|
54
|
+
italic,
|
|
55
|
+
underline,
|
|
56
|
+
inverse,
|
|
57
|
+
// base colors
|
|
58
|
+
black,
|
|
59
|
+
red,
|
|
60
|
+
green,
|
|
61
|
+
yellow,
|
|
62
|
+
blue,
|
|
63
|
+
magenta,
|
|
64
|
+
cyan,
|
|
65
|
+
white,
|
|
66
|
+
gray,
|
|
67
|
+
// bright variants
|
|
68
|
+
brightRed,
|
|
69
|
+
brightGreen,
|
|
70
|
+
brightYellow,
|
|
71
|
+
brightBlue,
|
|
72
|
+
brightMagenta,
|
|
73
|
+
brightCyan,
|
|
74
|
+
brightWhite
|
|
75
|
+
};
|
|
76
|
+
function stripAnsi(input) {
|
|
77
|
+
// eslint-disable-next-line no-control-regex
|
|
78
|
+
const ansiRegex = /[\u001b\u009b][[\]()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g;
|
|
79
|
+
return input.replace(ansiRegex, '');
|
|
80
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export type DiffHunk = {
|
|
2
|
+
oldStart: number;
|
|
3
|
+
oldLines: number;
|
|
4
|
+
newStart: number;
|
|
5
|
+
newLines: number;
|
|
6
|
+
oldContent: string[];
|
|
7
|
+
newContent: string[];
|
|
8
|
+
ctxBeforeOld?: string[];
|
|
9
|
+
ctxAfterOld?: string[];
|
|
10
|
+
};
|
|
11
|
+
type StoredCommit = {
|
|
12
|
+
createdAt?: string;
|
|
13
|
+
files: Array<{
|
|
14
|
+
file: string;
|
|
15
|
+
status: 'modified';
|
|
16
|
+
oldHash?: string;
|
|
17
|
+
newHash?: string;
|
|
18
|
+
diff?: string;
|
|
19
|
+
format?: 'hunks-v1';
|
|
20
|
+
baseHash?: string;
|
|
21
|
+
resultHash?: string;
|
|
22
|
+
hunks?: DiffHunk[];
|
|
23
|
+
meta?: Record<string, any>;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
26
|
+
export declare function loadAllCommitRecords(rootDir: string): Array<{
|
|
27
|
+
createdAt: number;
|
|
28
|
+
file: string;
|
|
29
|
+
record: StoredCommit['files'][number];
|
|
30
|
+
}>;
|
|
31
|
+
export declare function computeLineDiff(oldStr: string, newStr: string): string;
|
|
32
|
+
export declare function applyDiffToBase(base: string, diff: string): string | null;
|
|
33
|
+
export declare function computeHunks(oldStr: string, newStr: string): DiffHunk[];
|
|
34
|
+
export declare function applyHunksToBase(base: string, hunks: DiffHunk[]): string | null;
|
|
35
|
+
/**
|
|
36
|
+
* Try to apply hunks to an arbitrary base using contextual search.
|
|
37
|
+
* - If oldContent exists, locate it and replace by newContent.
|
|
38
|
+
* - If oldLines === 0 (pure insertion), use ctxBeforeOld/ctxAfterOld anchors to place newContent.
|
|
39
|
+
* Returns null if cannot apply cleanly.
|
|
40
|
+
*/
|
|
41
|
+
export declare function applyHunksToAnyBase(base: string, hunks: DiffHunk[]): string | null;
|
|
42
|
+
export declare function tryApplyCommitsToGenerated(fileAbsPath: string, generatedContent: string): {
|
|
43
|
+
applied: boolean;
|
|
44
|
+
content: string;
|
|
45
|
+
};
|
|
46
|
+
export {};
|
|
@@ -0,0 +1,377 @@
|
|
|
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.loadAllCommitRecords = loadAllCommitRecords;
|
|
37
|
+
exports.computeLineDiff = computeLineDiff;
|
|
38
|
+
exports.applyDiffToBase = applyDiffToBase;
|
|
39
|
+
exports.computeHunks = computeHunks;
|
|
40
|
+
exports.applyHunksToBase = applyHunksToBase;
|
|
41
|
+
exports.applyHunksToAnyBase = applyHunksToAnyBase;
|
|
42
|
+
exports.tryApplyCommitsToGenerated = tryApplyCommitsToGenerated;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const generationRegistry_1 = require("./generationRegistry");
|
|
46
|
+
function parseCommitTimestamp(name) {
|
|
47
|
+
// commit-YYYY-MM-DDTHH-MM-SS-sssZ.json
|
|
48
|
+
const match = name.match(/commit-(.*)\.json$/);
|
|
49
|
+
if (!match)
|
|
50
|
+
return 0;
|
|
51
|
+
const iso = match[1].replace(/-/g, ':');
|
|
52
|
+
// restore last hyphens: not exact; fallback to Date.parse of createdAt inside file
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
function loadAllCommitRecords(rootDir) {
|
|
56
|
+
const commitsDir = path.join(rootDir, 'commits');
|
|
57
|
+
if (!fs.existsSync(commitsDir))
|
|
58
|
+
return [];
|
|
59
|
+
const files = fs.readdirSync(commitsDir).filter(f => f.endsWith('.json'));
|
|
60
|
+
const results = [];
|
|
61
|
+
files.forEach(f => {
|
|
62
|
+
const abs = path.join(commitsDir, f);
|
|
63
|
+
try {
|
|
64
|
+
const data = JSON.parse(fs.readFileSync(abs, 'utf8'));
|
|
65
|
+
const ts = data.createdAt ? Date.parse(data.createdAt) : parseCommitTimestamp(f);
|
|
66
|
+
data.files.forEach(rec => {
|
|
67
|
+
results.push({ createdAt: ts || 0, file: rec.file, record: rec });
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// ignore broken commit files
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
// sort by createdAt ascending
|
|
75
|
+
results.sort((a, b) => a.createdAt - b.createdAt);
|
|
76
|
+
return results;
|
|
77
|
+
}
|
|
78
|
+
function parseDiff(diff) {
|
|
79
|
+
return diff.split(/\r?\n/).map(line => {
|
|
80
|
+
const type = line[0];
|
|
81
|
+
const content = line.length > 1 && line[1] === ' ' ? line.slice(2) : line.slice(1);
|
|
82
|
+
return { type: type === ' ' || type === '+' || type === '-' ? type : ' ', line: content };
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function computeLineDiff(oldStr, newStr) {
|
|
86
|
+
const oldLines = oldStr.split(/\r?\n/);
|
|
87
|
+
const newLines = newStr.split(/\r?\n/);
|
|
88
|
+
const n = oldLines.length;
|
|
89
|
+
const m = newLines.length;
|
|
90
|
+
const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0));
|
|
91
|
+
for (let i = n - 1; i >= 0; i -= 1) {
|
|
92
|
+
for (let j = m - 1; j >= 0; j -= 1) {
|
|
93
|
+
if (oldLines[i] === newLines[j])
|
|
94
|
+
dp[i][j] = dp[i + 1][j + 1] + 1;
|
|
95
|
+
else
|
|
96
|
+
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const result = [];
|
|
100
|
+
let i = 0;
|
|
101
|
+
let j = 0;
|
|
102
|
+
while (i < n && j < m) {
|
|
103
|
+
if (oldLines[i] === newLines[j]) {
|
|
104
|
+
result.push({ type: ' ', line: oldLines[i] });
|
|
105
|
+
i += 1;
|
|
106
|
+
j += 1;
|
|
107
|
+
}
|
|
108
|
+
else if (dp[i + 1][j] >= dp[i][j + 1]) {
|
|
109
|
+
result.push({ type: '-', line: oldLines[i] });
|
|
110
|
+
i += 1;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
result.push({ type: '+', line: newLines[j] });
|
|
114
|
+
j += 1;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
while (i < n) {
|
|
118
|
+
result.push({ type: '-', line: oldLines[i] });
|
|
119
|
+
i += 1;
|
|
120
|
+
}
|
|
121
|
+
while (j < m) {
|
|
122
|
+
result.push({ type: '+', line: newLines[j] });
|
|
123
|
+
j += 1;
|
|
124
|
+
}
|
|
125
|
+
const lines = result.map(op => `${op.type} ${op.line}`);
|
|
126
|
+
return lines.join('\n');
|
|
127
|
+
}
|
|
128
|
+
function applyDiffToBase(base, diff) {
|
|
129
|
+
const baseLines = base.split(/\r?\n/);
|
|
130
|
+
const ops = parseDiff(diff);
|
|
131
|
+
const result = [];
|
|
132
|
+
let i = 0;
|
|
133
|
+
for (const op of ops) {
|
|
134
|
+
if (op.type === ' ') {
|
|
135
|
+
if (baseLines[i] !== op.line) {
|
|
136
|
+
return null; // context mismatch
|
|
137
|
+
}
|
|
138
|
+
result.push(op.line);
|
|
139
|
+
i += 1;
|
|
140
|
+
}
|
|
141
|
+
else if (op.type === '+') {
|
|
142
|
+
if (baseLines[i] !== op.line) {
|
|
143
|
+
return null; // base content deviates
|
|
144
|
+
}
|
|
145
|
+
// skip this line in result (removed compared to base)
|
|
146
|
+
i += 1;
|
|
147
|
+
}
|
|
148
|
+
else if (op.type === '-') {
|
|
149
|
+
// insert user-only line
|
|
150
|
+
result.push(op.line);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// any remaining base lines are unexpected; consider they should have been represented
|
|
154
|
+
if (i < baseLines.length) {
|
|
155
|
+
// append remaining lines only if ops end with matching context; otherwise fail
|
|
156
|
+
result.push(...baseLines.slice(i));
|
|
157
|
+
}
|
|
158
|
+
return result.join('\n');
|
|
159
|
+
}
|
|
160
|
+
function lcsMatrix(a, b) {
|
|
161
|
+
const n = a.length;
|
|
162
|
+
const m = b.length;
|
|
163
|
+
const dp = Array.from({ length: n + 1 }, () => Array(m + 1).fill(0));
|
|
164
|
+
for (let i = n - 1; i >= 0; i -= 1) {
|
|
165
|
+
for (let j = m - 1; j >= 0; j -= 1) {
|
|
166
|
+
if (a[i] === b[j])
|
|
167
|
+
dp[i][j] = dp[i + 1][j + 1] + 1;
|
|
168
|
+
else
|
|
169
|
+
dp[i][j] = Math.max(dp[i + 1][j], dp[i][j + 1]);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return dp;
|
|
173
|
+
}
|
|
174
|
+
function computeHunks(oldStr, newStr) {
|
|
175
|
+
var _a, _b;
|
|
176
|
+
const a = oldStr.split(/\r?\n/);
|
|
177
|
+
const b = newStr.split(/\r?\n/);
|
|
178
|
+
const dp = lcsMatrix(a, b);
|
|
179
|
+
const hunks = [];
|
|
180
|
+
let i = 0;
|
|
181
|
+
let j = 0;
|
|
182
|
+
let pending = null;
|
|
183
|
+
while (i < a.length && j < b.length) {
|
|
184
|
+
if (a[i] === b[j]) {
|
|
185
|
+
if (pending) {
|
|
186
|
+
hunks.push(pending);
|
|
187
|
+
pending = null;
|
|
188
|
+
}
|
|
189
|
+
i += 1;
|
|
190
|
+
j += 1;
|
|
191
|
+
}
|
|
192
|
+
else if (((_a = dp[i + 1]) === null || _a === void 0 ? void 0 : _a[j]) >= ((_b = dp[i]) === null || _b === void 0 ? void 0 : _b[j + 1])) {
|
|
193
|
+
// deletion from old (i advanced)
|
|
194
|
+
if (!pending) {
|
|
195
|
+
pending = { oldStart: i, oldLines: 0, newStart: j, newLines: 0, oldContent: [], newContent: [] };
|
|
196
|
+
}
|
|
197
|
+
pending.oldLines += 1;
|
|
198
|
+
pending.oldContent.push(a[i]);
|
|
199
|
+
i += 1;
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
// insertion to new (j advanced)
|
|
203
|
+
if (!pending) {
|
|
204
|
+
pending = { oldStart: i, oldLines: 0, newStart: j, newLines: 0, oldContent: [], newContent: [] };
|
|
205
|
+
}
|
|
206
|
+
pending.newLines += 1;
|
|
207
|
+
pending.newContent.push(b[j]);
|
|
208
|
+
j += 1;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// tail remainders
|
|
212
|
+
if (i < a.length || j < b.length) {
|
|
213
|
+
const tail = {
|
|
214
|
+
oldStart: i,
|
|
215
|
+
oldLines: a.length - i,
|
|
216
|
+
newStart: j,
|
|
217
|
+
newLines: b.length - j,
|
|
218
|
+
oldContent: a.slice(i),
|
|
219
|
+
newContent: b.slice(j)
|
|
220
|
+
};
|
|
221
|
+
if (tail.oldLines > 0 || tail.newLines > 0) {
|
|
222
|
+
if (pending) {
|
|
223
|
+
// merge if adjacent
|
|
224
|
+
if (pending.oldStart + pending.oldLines === tail.oldStart && pending.newStart + pending.newLines === tail.newStart) {
|
|
225
|
+
pending.oldLines += tail.oldLines;
|
|
226
|
+
pending.newLines += tail.newLines;
|
|
227
|
+
pending.oldContent.push(...tail.oldContent);
|
|
228
|
+
pending.newContent.push(...tail.newContent);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
hunks.push(pending);
|
|
232
|
+
hunks.push(tail);
|
|
233
|
+
pending = null;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
pending = tail;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (pending)
|
|
242
|
+
hunks.push(pending);
|
|
243
|
+
// Attach lightweight context (up to 3 lines) around old segments to help rebase
|
|
244
|
+
const CONTEXT = 3;
|
|
245
|
+
return hunks.map(h => {
|
|
246
|
+
const beforeStart = Math.max(0, h.oldStart - CONTEXT);
|
|
247
|
+
const before = a.slice(beforeStart, h.oldStart);
|
|
248
|
+
const afterEnd = h.oldStart + h.oldLines + CONTEXT;
|
|
249
|
+
const after = a.slice(h.oldStart + h.oldLines, Math.min(a.length, afterEnd));
|
|
250
|
+
return { ...h, ctxBeforeOld: before, ctxAfterOld: after };
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function applyHunksToBase(base, hunks) {
|
|
254
|
+
const baseLines = base.split(/\r?\n/);
|
|
255
|
+
let offset = 0;
|
|
256
|
+
for (const h of hunks) {
|
|
257
|
+
const start = h.oldStart + offset;
|
|
258
|
+
const end = start + h.oldLines;
|
|
259
|
+
if (start < 0 || start > baseLines.length || end < 0 || end > baseLines.length) {
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
baseLines.splice(start, h.oldLines, ...h.newContent);
|
|
263
|
+
offset += h.newContent.length - h.oldLines;
|
|
264
|
+
}
|
|
265
|
+
return baseLines.join('\n');
|
|
266
|
+
}
|
|
267
|
+
function findSubsequence(haystack, needle, fromIndex = 0) {
|
|
268
|
+
if (needle.length === 0)
|
|
269
|
+
return fromIndex;
|
|
270
|
+
for (let i = fromIndex; i <= haystack.length - needle.length; i += 1) {
|
|
271
|
+
let ok = true;
|
|
272
|
+
for (let j = 0; j < needle.length; j += 1) {
|
|
273
|
+
if (haystack[i + j] !== needle[j]) {
|
|
274
|
+
ok = false;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (ok)
|
|
279
|
+
return i;
|
|
280
|
+
}
|
|
281
|
+
return -1;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Try to apply hunks to an arbitrary base using contextual search.
|
|
285
|
+
* - If oldContent exists, locate it and replace by newContent.
|
|
286
|
+
* - If oldLines === 0 (pure insertion), use ctxBeforeOld/ctxAfterOld anchors to place newContent.
|
|
287
|
+
* Returns null if cannot apply cleanly.
|
|
288
|
+
*/
|
|
289
|
+
function applyHunksToAnyBase(base, hunks) {
|
|
290
|
+
let baseLines = base.split(/\r?\n/);
|
|
291
|
+
let searchFrom = 0;
|
|
292
|
+
for (const h of hunks) {
|
|
293
|
+
if (h.oldLines > 0 && h.oldContent.length > 0) {
|
|
294
|
+
// Try to find exact oldContent sequence
|
|
295
|
+
let idx = findSubsequence(baseLines, h.oldContent, searchFrom);
|
|
296
|
+
if (idx === -1 && h.ctxBeforeOld && h.ctxBeforeOld.length > 0) {
|
|
297
|
+
// Try to locate by ctxBefore then expect oldContent after
|
|
298
|
+
const beforeIdx = findSubsequence(baseLines, h.ctxBeforeOld, Math.max(0, searchFrom - h.ctxBeforeOld.length));
|
|
299
|
+
if (beforeIdx !== -1) {
|
|
300
|
+
idx = findSubsequence(baseLines, h.oldContent, beforeIdx + h.ctxBeforeOld.length);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
if (idx === -1 && h.ctxAfterOld && h.ctxAfterOld.length > 0) {
|
|
304
|
+
// Try to locate by ctxAfter before it
|
|
305
|
+
const afterIdx = findSubsequence(baseLines, h.ctxAfterOld, searchFrom);
|
|
306
|
+
if (afterIdx !== -1) {
|
|
307
|
+
// Scan backwards window before afterIdx
|
|
308
|
+
const windowStart = Math.max(0, afterIdx - h.oldLines - h.ctxBeforeOld.length);
|
|
309
|
+
idx = findSubsequence(baseLines.slice(windowStart, afterIdx), h.oldContent);
|
|
310
|
+
if (idx !== -1)
|
|
311
|
+
idx += windowStart;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (idx === -1)
|
|
315
|
+
return null;
|
|
316
|
+
baseLines.splice(idx, h.oldLines, ...h.newContent);
|
|
317
|
+
searchFrom = idx + h.newContent.length;
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
// Insertion-only hunk: place using context anchors
|
|
321
|
+
let insertAt = h.newStart; // fallback
|
|
322
|
+
if (h.ctxBeforeOld && h.ctxBeforeOld.length > 0) {
|
|
323
|
+
const beforeIdx = findSubsequence(baseLines, h.ctxBeforeOld, Math.max(0, searchFrom - h.ctxBeforeOld.length));
|
|
324
|
+
if (beforeIdx !== -1)
|
|
325
|
+
insertAt = beforeIdx + h.ctxBeforeOld.length;
|
|
326
|
+
}
|
|
327
|
+
else if (h.ctxAfterOld && h.ctxAfterOld.length > 0) {
|
|
328
|
+
const afterIdx = findSubsequence(baseLines, h.ctxAfterOld, searchFrom);
|
|
329
|
+
if (afterIdx !== -1)
|
|
330
|
+
insertAt = afterIdx;
|
|
331
|
+
}
|
|
332
|
+
if (insertAt < 0 || insertAt > baseLines.length)
|
|
333
|
+
insertAt = baseLines.length;
|
|
334
|
+
baseLines.splice(insertAt, 0, ...h.newContent);
|
|
335
|
+
searchFrom = insertAt + h.newContent.length;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return baseLines.join('\n');
|
|
339
|
+
}
|
|
340
|
+
function tryApplyCommitsToGenerated(fileAbsPath, generatedContent) {
|
|
341
|
+
const root = process.cwd();
|
|
342
|
+
const rel = path.relative(root, path.resolve(fileAbsPath));
|
|
343
|
+
const baseHash = (0, generationRegistry_1.computeContentHash)(generatedContent);
|
|
344
|
+
// Prefer registry snapshot if present and compatible
|
|
345
|
+
const registry = (0, generationRegistry_1.loadRegistry)();
|
|
346
|
+
const regEntry = registry[rel];
|
|
347
|
+
if (regEntry && regEntry.diffFormat === 'hunks-v1' && regEntry.diffHunks) {
|
|
348
|
+
// If baseHash matches, use fast exact apply; else attempt contextual apply
|
|
349
|
+
const appliedViaRegistry = regEntry.diffBaseHash === baseHash
|
|
350
|
+
? applyHunksToBase(generatedContent, regEntry.diffHunks)
|
|
351
|
+
: applyHunksToAnyBase(generatedContent, regEntry.diffHunks);
|
|
352
|
+
if (appliedViaRegistry != null) {
|
|
353
|
+
return { applied: true, content: appliedViaRegistry };
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
// Fallback to commit files if registry snapshot not usable
|
|
357
|
+
const all = loadAllCommitRecords(root).filter(r => r.file === rel && r.record.status === 'modified');
|
|
358
|
+
if (all.length === 0)
|
|
359
|
+
return { applied: false, content: generatedContent };
|
|
360
|
+
// Prefer hunks format
|
|
361
|
+
const hunksCandidates = all.filter(r => r.record.format === 'hunks-v1' && r.record.baseHash === baseHash && r.record.hunks);
|
|
362
|
+
if (hunksCandidates.length > 0) {
|
|
363
|
+
const latest = hunksCandidates[hunksCandidates.length - 1].record;
|
|
364
|
+
const applied = applyHunksToBase(generatedContent, latest.hunks);
|
|
365
|
+
if (applied != null)
|
|
366
|
+
return { applied: true, content: applied };
|
|
367
|
+
}
|
|
368
|
+
// Fallback to legacy line diff
|
|
369
|
+
const legacyCandidates = all.filter(r => r.record.newHash === baseHash && r.record.diff);
|
|
370
|
+
if (legacyCandidates.length > 0) {
|
|
371
|
+
const latest = legacyCandidates[legacyCandidates.length - 1].record;
|
|
372
|
+
const applied = applyDiffToBase(generatedContent, latest.diff);
|
|
373
|
+
if (applied != null)
|
|
374
|
+
return { applied: true, content: applied };
|
|
375
|
+
}
|
|
376
|
+
return { applied: false, content: generatedContent };
|
|
377
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export declare const FILE_EXTENSIONS: {
|
|
2
|
+
readonly YAML: ".yaml";
|
|
3
|
+
readonly TYPESCRIPT: ".ts";
|
|
4
|
+
readonly JAVASCRIPT: ".js";
|
|
5
|
+
readonly JSON: ".json";
|
|
6
|
+
};
|
|
7
|
+
export declare const COMMON_FILES: {
|
|
8
|
+
readonly APP_YAML: "app.yaml";
|
|
9
|
+
readonly APP_TS: "app.ts";
|
|
10
|
+
readonly REGISTRY_JSON: "registry.json";
|
|
11
|
+
readonly STORE_INTERFACE: "StoreInterface.ts";
|
|
12
|
+
};
|
|
13
|
+
export declare const GENERATOR_MARKERS: {
|
|
14
|
+
readonly CONTROLLERS_START: "// currentjs:controllers:start";
|
|
15
|
+
readonly CONTROLLERS_END: "// currentjs:controllers:end";
|
|
16
|
+
};
|
|
17
|
+
export declare const PATH_PATTERNS: {
|
|
18
|
+
readonly MODULES_PREFIX: "modules/";
|
|
19
|
+
readonly MODULES_RELATIVE: "./modules/";
|
|
20
|
+
readonly MODULES_DIRECTIVE: "@modules/";
|
|
21
|
+
readonly SRC_MODULES: "src/modules/";
|
|
22
|
+
readonly INFRASTRUCTURE: "infrastructure";
|
|
23
|
+
readonly APPLICATION: "application";
|
|
24
|
+
readonly DOMAIN: "domain";
|
|
25
|
+
readonly ENTITIES: "entities";
|
|
26
|
+
readonly STORES: "stores";
|
|
27
|
+
readonly SERVICES: "services";
|
|
28
|
+
readonly CONTROLLERS: "controllers";
|
|
29
|
+
};
|
|
30
|
+
export declare const DEFAULTS: {
|
|
31
|
+
readonly SERVER_PORT: 3000;
|
|
32
|
+
readonly SERVER_HOST: "localhost";
|
|
33
|
+
readonly PAGINATION_LIMIT: 10;
|
|
34
|
+
readonly PAGINATION_PAGE: 1;
|
|
35
|
+
};
|
|
36
|
+
export declare const MODULE_STRUCTURE: {
|
|
37
|
+
readonly DOMAIN_ENTITIES: "domain/entities";
|
|
38
|
+
readonly APPLICATION_SERVICES: "application/services";
|
|
39
|
+
readonly INFRASTRUCTURE_STORES: "infrastructure/stores";
|
|
40
|
+
readonly INFRASTRUCTURE_CONTROLLERS: "infrastructure/controllers";
|
|
41
|
+
};
|
|
42
|
+
export declare const SERVER: {
|
|
43
|
+
readonly BASE_URL: "http://localhost:3000";
|
|
44
|
+
readonly START_MESSAGE: "Server started on http://localhost:3000";
|
|
45
|
+
};
|
|
46
|
+
export declare const GENERATOR_SUFFIXES: {
|
|
47
|
+
readonly STORE: "Store";
|
|
48
|
+
readonly SERVICE: "Service";
|
|
49
|
+
readonly CONTROLLER: "Controller";
|
|
50
|
+
readonly API_CONTROLLER: "ApiController";
|
|
51
|
+
readonly WEB_CONTROLLER: "WebController";
|
|
52
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Common constants used across the generator
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.GENERATOR_SUFFIXES = exports.SERVER = exports.MODULE_STRUCTURE = exports.DEFAULTS = exports.PATH_PATTERNS = exports.GENERATOR_MARKERS = exports.COMMON_FILES = exports.FILE_EXTENSIONS = void 0;
|
|
5
|
+
// File extensions
|
|
6
|
+
exports.FILE_EXTENSIONS = {
|
|
7
|
+
YAML: '.yaml',
|
|
8
|
+
TYPESCRIPT: '.ts',
|
|
9
|
+
JAVASCRIPT: '.js',
|
|
10
|
+
JSON: '.json'
|
|
11
|
+
};
|
|
12
|
+
// Common file names
|
|
13
|
+
exports.COMMON_FILES = {
|
|
14
|
+
APP_YAML: 'app.yaml',
|
|
15
|
+
APP_TS: 'app.ts',
|
|
16
|
+
REGISTRY_JSON: 'registry.json',
|
|
17
|
+
STORE_INTERFACE: 'StoreInterface.ts'
|
|
18
|
+
};
|
|
19
|
+
// Generator markers
|
|
20
|
+
exports.GENERATOR_MARKERS = {
|
|
21
|
+
CONTROLLERS_START: '// currentjs:controllers:start',
|
|
22
|
+
CONTROLLERS_END: '// currentjs:controllers:end'
|
|
23
|
+
};
|
|
24
|
+
// Path patterns
|
|
25
|
+
exports.PATH_PATTERNS = {
|
|
26
|
+
MODULES_PREFIX: 'modules/',
|
|
27
|
+
MODULES_RELATIVE: './modules/',
|
|
28
|
+
MODULES_DIRECTIVE: '@modules/',
|
|
29
|
+
SRC_MODULES: 'src/modules/',
|
|
30
|
+
INFRASTRUCTURE: 'infrastructure',
|
|
31
|
+
APPLICATION: 'application',
|
|
32
|
+
DOMAIN: 'domain',
|
|
33
|
+
ENTITIES: 'entities',
|
|
34
|
+
STORES: 'stores',
|
|
35
|
+
SERVICES: 'services',
|
|
36
|
+
CONTROLLERS: 'controllers'
|
|
37
|
+
};
|
|
38
|
+
// Default values
|
|
39
|
+
exports.DEFAULTS = {
|
|
40
|
+
SERVER_PORT: 3000,
|
|
41
|
+
SERVER_HOST: 'localhost',
|
|
42
|
+
PAGINATION_LIMIT: 10,
|
|
43
|
+
PAGINATION_PAGE: 1
|
|
44
|
+
};
|
|
45
|
+
// Module structure paths
|
|
46
|
+
exports.MODULE_STRUCTURE = {
|
|
47
|
+
DOMAIN_ENTITIES: 'domain/entities',
|
|
48
|
+
APPLICATION_SERVICES: 'application/services',
|
|
49
|
+
INFRASTRUCTURE_STORES: 'infrastructure/stores',
|
|
50
|
+
INFRASTRUCTURE_CONTROLLERS: 'infrastructure/controllers'
|
|
51
|
+
};
|
|
52
|
+
// Server URLs and messages
|
|
53
|
+
exports.SERVER = {
|
|
54
|
+
BASE_URL: `http://${exports.DEFAULTS.SERVER_HOST}:${exports.DEFAULTS.SERVER_PORT}`,
|
|
55
|
+
START_MESSAGE: `Server started on http://${exports.DEFAULTS.SERVER_HOST}:${exports.DEFAULTS.SERVER_PORT}`
|
|
56
|
+
};
|
|
57
|
+
// Generator suffixes
|
|
58
|
+
exports.GENERATOR_SUFFIXES = {
|
|
59
|
+
STORE: 'Store',
|
|
60
|
+
SERVICE: 'Service',
|
|
61
|
+
CONTROLLER: 'Controller',
|
|
62
|
+
API_CONTROLLER: 'ApiController',
|
|
63
|
+
WEB_CONTROLLER: 'WebController'
|
|
64
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type DiffHunk } from './commitUtils';
|
|
2
|
+
type RegistryEntry = {
|
|
3
|
+
hash: string;
|
|
4
|
+
updatedAt: string;
|
|
5
|
+
diffFormat?: 'hunks-v1';
|
|
6
|
+
diffBaseHash?: string;
|
|
7
|
+
diffResultHash?: string;
|
|
8
|
+
diffHunks?: DiffHunk[];
|
|
9
|
+
diffUpdatedAt?: string;
|
|
10
|
+
};
|
|
11
|
+
type RegistryData = Record<string, RegistryEntry>;
|
|
12
|
+
export declare function initGenerationRegistry(rootDir: string): void;
|
|
13
|
+
export declare function computeContentHash(contents: string): string;
|
|
14
|
+
export declare function loadRegistry(): RegistryData;
|
|
15
|
+
export declare function saveRegistry(data: RegistryData): void;
|
|
16
|
+
export declare function getStoredHash(filePath: string): string | undefined;
|
|
17
|
+
export declare function updateStoredHash(filePath: string, hash: string): void;
|
|
18
|
+
export declare function updateStoredHunks(filePath: string, hunks: DiffHunk[], baseHash?: string, resultHash?: string): void;
|
|
19
|
+
export declare function writeGeneratedFile(filePath: string, contents: string, options?: {
|
|
20
|
+
force?: boolean;
|
|
21
|
+
silent?: boolean;
|
|
22
|
+
skipOnConflict?: boolean;
|
|
23
|
+
}): Promise<'written' | 'skipped' | 'unchanged'>;
|
|
24
|
+
export declare function ensureCommitsDir(): string;
|
|
25
|
+
export {};
|