create-forgeon 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-forgeon.mjs +9 -6
- package/package.json +1 -1
- package/src/cli/add-help.mjs +10 -6
- package/src/cli/help.mjs +13 -9
- package/src/integrations/flow.mjs +118 -0
- package/src/modules/db-prisma.mjs +26 -172
- package/src/modules/executor.test.mjs +150 -10
- package/src/modules/i18n.mjs +29 -150
- package/src/modules/jwt-auth.mjs +37 -297
- package/src/modules/logger.mjs +10 -118
- package/src/modules/shared/patch-utils.mjs +237 -0
- package/src/modules/swagger.mjs +10 -121
- package/src/modules/sync-integrations.mjs +269 -0
- package/src/run-add-module.mjs +8 -42
- package/src/run-scan-integrations.mjs +93 -0
- package/templates/base/docs/AI/ARCHITECTURE.md +17 -0
- package/templates/base/docs/AI/MODULE_SPEC.md +4 -0
- package/templates/base/package.json +0 -3
- package/templates/base/scripts/forgeon-sync-integrations.mjs +44 -241
package/src/modules/logger.mjs
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { copyRecursive, writeJson } from '../utils/fs.mjs';
|
|
4
|
+
import {
|
|
5
|
+
ensureBuildSteps,
|
|
6
|
+
ensureDependency,
|
|
7
|
+
ensureLineAfter,
|
|
8
|
+
ensureLineBefore,
|
|
9
|
+
ensureLoadItem,
|
|
10
|
+
ensureValidatorSchema,
|
|
11
|
+
upsertEnvLines,
|
|
12
|
+
} from './shared/patch-utils.mjs';
|
|
4
13
|
|
|
5
14
|
function copyFromPreset(packageRoot, targetRoot, relativePath) {
|
|
6
15
|
const source = path.join(packageRoot, 'templates', 'module-presets', 'logger', relativePath);
|
|
@@ -11,108 +20,6 @@ function copyFromPreset(packageRoot, targetRoot, relativePath) {
|
|
|
11
20
|
copyRecursive(source, destination);
|
|
12
21
|
}
|
|
13
22
|
|
|
14
|
-
function ensureDependency(packageJson, name, version) {
|
|
15
|
-
if (!packageJson.dependencies) {
|
|
16
|
-
packageJson.dependencies = {};
|
|
17
|
-
}
|
|
18
|
-
packageJson.dependencies[name] = version;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function ensureLineAfter(content, anchorLine, lineToInsert) {
|
|
22
|
-
if (content.includes(lineToInsert)) {
|
|
23
|
-
return content;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const index = content.indexOf(anchorLine);
|
|
27
|
-
if (index < 0) {
|
|
28
|
-
return `${content.trimEnd()}\n${lineToInsert}\n`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const insertAt = index + anchorLine.length;
|
|
32
|
-
return `${content.slice(0, insertAt)}\n${lineToInsert}${content.slice(insertAt)}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function ensureLineBefore(content, anchorLine, lineToInsert) {
|
|
36
|
-
if (content.includes(lineToInsert)) {
|
|
37
|
-
return content;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const index = content.indexOf(anchorLine);
|
|
41
|
-
if (index < 0) {
|
|
42
|
-
return `${content.trimEnd()}\n${lineToInsert}\n`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return `${content.slice(0, index)}${lineToInsert}\n${content.slice(index)}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function upsertEnvLines(filePath, lines) {
|
|
49
|
-
let content = '';
|
|
50
|
-
if (fs.existsSync(filePath)) {
|
|
51
|
-
content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const keys = new Set(
|
|
55
|
-
content
|
|
56
|
-
.split('\n')
|
|
57
|
-
.filter(Boolean)
|
|
58
|
-
.map((line) => line.split('=')[0]),
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
const append = [];
|
|
62
|
-
for (const line of lines) {
|
|
63
|
-
const key = line.split('=')[0];
|
|
64
|
-
if (!keys.has(key)) {
|
|
65
|
-
append.push(line);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const next =
|
|
70
|
-
append.length > 0 ? `${content.trimEnd()}\n${append.join('\n')}\n` : `${content.trimEnd()}\n`;
|
|
71
|
-
fs.writeFileSync(filePath, next.replace(/^\n/, ''), 'utf8');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function ensureLoadItem(content, itemName) {
|
|
75
|
-
const pattern = /load:\s*\[([^\]]*)\]/m;
|
|
76
|
-
const match = content.match(pattern);
|
|
77
|
-
if (!match) {
|
|
78
|
-
return content;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const rawList = match[1];
|
|
82
|
-
const items = rawList
|
|
83
|
-
.split(',')
|
|
84
|
-
.map((item) => item.trim())
|
|
85
|
-
.filter(Boolean);
|
|
86
|
-
|
|
87
|
-
if (!items.includes(itemName)) {
|
|
88
|
-
items.push(itemName);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const next = `load: [${items.join(', ')}]`;
|
|
92
|
-
return content.replace(pattern, next);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function ensureValidatorSchema(content, schemaName) {
|
|
96
|
-
const pattern = /validate:\s*createEnvValidator\(\[([^\]]*)\]\)/m;
|
|
97
|
-
const match = content.match(pattern);
|
|
98
|
-
if (!match) {
|
|
99
|
-
return content;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const rawList = match[1];
|
|
103
|
-
const items = rawList
|
|
104
|
-
.split(',')
|
|
105
|
-
.map((item) => item.trim())
|
|
106
|
-
.filter(Boolean);
|
|
107
|
-
|
|
108
|
-
if (!items.includes(schemaName)) {
|
|
109
|
-
items.push(schemaName);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const next = `validate: createEnvValidator([${items.join(', ')}])`;
|
|
113
|
-
return content.replace(pattern, next);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
23
|
function patchApiPackage(targetRoot) {
|
|
117
24
|
const packagePath = path.join(targetRoot, 'apps', 'api', 'package.json');
|
|
118
25
|
if (!fs.existsSync(packagePath)) {
|
|
@@ -124,22 +31,7 @@ function patchApiPackage(targetRoot) {
|
|
|
124
31
|
packageJson.scripts = {};
|
|
125
32
|
}
|
|
126
33
|
|
|
127
|
-
|
|
128
|
-
const currentPredev = packageJson.scripts.predev;
|
|
129
|
-
if (typeof currentPredev === 'string') {
|
|
130
|
-
if (!currentPredev.includes(loggerBuild)) {
|
|
131
|
-
if (currentPredev.includes('pnpm --filter @forgeon/core build')) {
|
|
132
|
-
packageJson.scripts.predev = currentPredev.replace(
|
|
133
|
-
'pnpm --filter @forgeon/core build',
|
|
134
|
-
`pnpm --filter @forgeon/core build && ${loggerBuild}`,
|
|
135
|
-
);
|
|
136
|
-
} else {
|
|
137
|
-
packageJson.scripts.predev = `${loggerBuild} && ${currentPredev}`;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
} else {
|
|
141
|
-
packageJson.scripts.predev = loggerBuild;
|
|
142
|
-
}
|
|
34
|
+
ensureBuildSteps(packageJson, 'predev', ['pnpm --filter @forgeon/logger build']);
|
|
143
35
|
|
|
144
36
|
ensureDependency(packageJson, '@forgeon/logger', 'workspace:*');
|
|
145
37
|
writeJson(packagePath, packageJson);
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
|
|
3
|
+
export function ensureDependency(packageJson, name, version) {
|
|
4
|
+
if (!packageJson.dependencies) {
|
|
5
|
+
packageJson.dependencies = {};
|
|
6
|
+
}
|
|
7
|
+
packageJson.dependencies[name] = version;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function ensureDevDependency(packageJson, name, version) {
|
|
11
|
+
if (!packageJson.devDependencies) {
|
|
12
|
+
packageJson.devDependencies = {};
|
|
13
|
+
}
|
|
14
|
+
packageJson.devDependencies[name] = version;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function ensureScript(packageJson, name, command) {
|
|
18
|
+
if (!packageJson.scripts) {
|
|
19
|
+
packageJson.scripts = {};
|
|
20
|
+
}
|
|
21
|
+
packageJson.scripts[name] = command;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function ensureBuildSteps(packageJson, scriptName, requiredCommands) {
|
|
25
|
+
if (!packageJson.scripts) {
|
|
26
|
+
packageJson.scripts = {};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const current = packageJson.scripts[scriptName];
|
|
30
|
+
const steps =
|
|
31
|
+
typeof current === 'string' && current.trim().length > 0
|
|
32
|
+
? current
|
|
33
|
+
.split('&&')
|
|
34
|
+
.map((item) => item.trim())
|
|
35
|
+
.filter(Boolean)
|
|
36
|
+
: [];
|
|
37
|
+
|
|
38
|
+
for (const command of requiredCommands) {
|
|
39
|
+
if (!steps.includes(command)) {
|
|
40
|
+
steps.push(command);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (steps.length > 0) {
|
|
45
|
+
packageJson.scripts[scriptName] = steps.join(' && ');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function ensureLineAfter(content, anchorLine, lineToInsert) {
|
|
50
|
+
if (content.includes(lineToInsert)) {
|
|
51
|
+
return content;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const index = content.indexOf(anchorLine);
|
|
55
|
+
if (index < 0) {
|
|
56
|
+
return `${content.trimEnd()}\n${lineToInsert}\n`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const insertAt = index + anchorLine.length;
|
|
60
|
+
return `${content.slice(0, insertAt)}\n${lineToInsert}${content.slice(insertAt)}`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function ensureLineBefore(content, anchorLine, lineToInsert) {
|
|
64
|
+
if (content.includes(lineToInsert)) {
|
|
65
|
+
return content;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const index = content.indexOf(anchorLine);
|
|
69
|
+
if (index < 0) {
|
|
70
|
+
return `${content.trimEnd()}\n${lineToInsert}\n`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return `${content.slice(0, index)}${lineToInsert}\n${content.slice(index)}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function ensureImportLine(content, importLine) {
|
|
77
|
+
if (content.includes(importLine)) {
|
|
78
|
+
return content;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const importMatches = [...content.matchAll(/^import\s.+;$/gm)];
|
|
82
|
+
if (importMatches.length === 0) {
|
|
83
|
+
return `${importLine}\n${content}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const lastImport = importMatches.at(-1);
|
|
87
|
+
const insertAt = lastImport.index + lastImport[0].length;
|
|
88
|
+
return `${content.slice(0, insertAt)}\n${importLine}${content.slice(insertAt)}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function findClassRange(content, className) {
|
|
92
|
+
const classPattern = new RegExp(`export\\s+class\\s+${className}\\b`);
|
|
93
|
+
const classMatch = classPattern.exec(content);
|
|
94
|
+
if (!classMatch) {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const openBrace = content.indexOf('{', classMatch.index);
|
|
99
|
+
if (openBrace < 0) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let depth = 0;
|
|
104
|
+
for (let index = openBrace; index < content.length; index += 1) {
|
|
105
|
+
const char = content[index];
|
|
106
|
+
if (char === '{') {
|
|
107
|
+
depth += 1;
|
|
108
|
+
} else if (char === '}') {
|
|
109
|
+
depth -= 1;
|
|
110
|
+
if (depth === 0) {
|
|
111
|
+
return {
|
|
112
|
+
classStart: classMatch.index,
|
|
113
|
+
bodyStart: openBrace + 1,
|
|
114
|
+
classEnd: index,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function ensureClassMember(content, className, memberCode, options = {}) {
|
|
124
|
+
const member = memberCode.trim();
|
|
125
|
+
if (member.length === 0) {
|
|
126
|
+
return content;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const range = findClassRange(content, className);
|
|
130
|
+
if (!range) {
|
|
131
|
+
return `${content.trimEnd()}\n${member}\n`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const classBody = content.slice(range.bodyStart, range.classEnd);
|
|
135
|
+
if (classBody.includes(member)) {
|
|
136
|
+
return content;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let insertAt = range.classEnd;
|
|
140
|
+
const beforeNeedle = options.beforeNeedle;
|
|
141
|
+
if (typeof beforeNeedle === 'string' && beforeNeedle.length > 0) {
|
|
142
|
+
const needleIndex = classBody.indexOf(beforeNeedle);
|
|
143
|
+
if (needleIndex >= 0) {
|
|
144
|
+
insertAt = range.bodyStart + needleIndex;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return `${content.slice(0, insertAt)}\n\n${member}\n${content.slice(insertAt)}`;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function upsertEnvLines(filePath, lines) {
|
|
152
|
+
let content = '';
|
|
153
|
+
if (fs.existsSync(filePath)) {
|
|
154
|
+
content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const keys = new Set(
|
|
158
|
+
content
|
|
159
|
+
.split('\n')
|
|
160
|
+
.filter(Boolean)
|
|
161
|
+
.map((line) => line.split('=')[0]),
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const append = [];
|
|
165
|
+
for (const line of lines) {
|
|
166
|
+
const key = line.split('=')[0];
|
|
167
|
+
if (!keys.has(key)) {
|
|
168
|
+
append.push(line);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const next =
|
|
173
|
+
append.length > 0 ? `${content.trimEnd()}\n${append.join('\n')}\n` : `${content.trimEnd()}\n`;
|
|
174
|
+
fs.writeFileSync(filePath, next.replace(/^\n/, ''), 'utf8');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function ensureLoadItem(content, itemName) {
|
|
178
|
+
const pattern = /load:\s*\[([^\]]*)\]/m;
|
|
179
|
+
const match = content.match(pattern);
|
|
180
|
+
if (!match) {
|
|
181
|
+
return content;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const rawList = match[1];
|
|
185
|
+
const items = rawList
|
|
186
|
+
.split(',')
|
|
187
|
+
.map((item) => item.trim())
|
|
188
|
+
.filter(Boolean);
|
|
189
|
+
|
|
190
|
+
if (!items.includes(itemName)) {
|
|
191
|
+
items.push(itemName);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const next = `load: [${items.join(', ')}]`;
|
|
195
|
+
return content.replace(pattern, next);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function ensureValidatorSchema(content, schemaName) {
|
|
199
|
+
const pattern = /validate:\s*createEnvValidator\(\[([^\]]*)\]\)/m;
|
|
200
|
+
const match = content.match(pattern);
|
|
201
|
+
if (!match) {
|
|
202
|
+
return content;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const rawList = match[1];
|
|
206
|
+
const items = rawList
|
|
207
|
+
.split(',')
|
|
208
|
+
.map((item) => item.trim())
|
|
209
|
+
.filter(Boolean);
|
|
210
|
+
|
|
211
|
+
if (!items.includes(schemaName)) {
|
|
212
|
+
items.push(schemaName);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const next = `validate: createEnvValidator([${items.join(', ')}])`;
|
|
216
|
+
return content.replace(pattern, next);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function ensureNestCommonImport(content, importName) {
|
|
220
|
+
const pattern = /import\s*\{([^}]*)\}\s*from '@nestjs\/common';/m;
|
|
221
|
+
const match = content.match(pattern);
|
|
222
|
+
if (!match) {
|
|
223
|
+
return `import { ${importName} } from '@nestjs/common';\n${content}`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const names = match[1]
|
|
227
|
+
.split(',')
|
|
228
|
+
.map((item) => item.trim())
|
|
229
|
+
.filter(Boolean);
|
|
230
|
+
|
|
231
|
+
if (!names.includes(importName)) {
|
|
232
|
+
names.push(importName);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const replacement = `import { ${names.join(', ')} } from '@nestjs/common';`;
|
|
236
|
+
return content.replace(pattern, replacement);
|
|
237
|
+
}
|
package/src/modules/swagger.mjs
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { copyRecursive, writeJson } from '../utils/fs.mjs';
|
|
4
|
+
import {
|
|
5
|
+
ensureBuildSteps,
|
|
6
|
+
ensureDependency,
|
|
7
|
+
ensureLineAfter,
|
|
8
|
+
ensureLineBefore,
|
|
9
|
+
ensureLoadItem,
|
|
10
|
+
ensureValidatorSchema,
|
|
11
|
+
upsertEnvLines,
|
|
12
|
+
} from './shared/patch-utils.mjs';
|
|
4
13
|
|
|
5
14
|
function copyFromPreset(packageRoot, targetRoot, relativePath) {
|
|
6
15
|
const source = path.join(packageRoot, 'templates', 'module-presets', 'swagger', relativePath);
|
|
@@ -11,126 +20,6 @@ function copyFromPreset(packageRoot, targetRoot, relativePath) {
|
|
|
11
20
|
copyRecursive(source, destination);
|
|
12
21
|
}
|
|
13
22
|
|
|
14
|
-
function ensureDependency(packageJson, name, version) {
|
|
15
|
-
if (!packageJson.dependencies) {
|
|
16
|
-
packageJson.dependencies = {};
|
|
17
|
-
}
|
|
18
|
-
packageJson.dependencies[name] = version;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function ensureLineAfter(content, anchorLine, lineToInsert) {
|
|
22
|
-
if (content.includes(lineToInsert)) {
|
|
23
|
-
return content;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const index = content.indexOf(anchorLine);
|
|
27
|
-
if (index < 0) {
|
|
28
|
-
return `${content.trimEnd()}\n${lineToInsert}\n`;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const insertAt = index + anchorLine.length;
|
|
32
|
-
return `${content.slice(0, insertAt)}\n${lineToInsert}${content.slice(insertAt)}`;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function ensureLineBefore(content, anchorLine, lineToInsert) {
|
|
36
|
-
if (content.includes(lineToInsert)) {
|
|
37
|
-
return content;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const index = content.indexOf(anchorLine);
|
|
41
|
-
if (index < 0) {
|
|
42
|
-
return `${content.trimEnd()}\n${lineToInsert}\n`;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return `${content.slice(0, index)}${lineToInsert}\n${content.slice(index)}`;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function upsertEnvLines(filePath, lines) {
|
|
49
|
-
let content = '';
|
|
50
|
-
if (fs.existsSync(filePath)) {
|
|
51
|
-
content = fs.readFileSync(filePath, 'utf8').replace(/\r\n/g, '\n');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const keys = new Set(
|
|
55
|
-
content
|
|
56
|
-
.split('\n')
|
|
57
|
-
.filter(Boolean)
|
|
58
|
-
.map((line) => line.split('=')[0]),
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
const append = [];
|
|
62
|
-
for (const line of lines) {
|
|
63
|
-
const key = line.split('=')[0];
|
|
64
|
-
if (!keys.has(key)) {
|
|
65
|
-
append.push(line);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const next =
|
|
70
|
-
append.length > 0 ? `${content.trimEnd()}\n${append.join('\n')}\n` : `${content.trimEnd()}\n`;
|
|
71
|
-
fs.writeFileSync(filePath, next.replace(/^\n/, ''), 'utf8');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function ensureBuildStep(packageJson, buildCommand) {
|
|
75
|
-
if (!packageJson.scripts) {
|
|
76
|
-
packageJson.scripts = {};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const current = packageJson.scripts.predev;
|
|
80
|
-
if (typeof current !== 'string' || current.trim().length === 0) {
|
|
81
|
-
packageJson.scripts.predev = buildCommand;
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (current.includes(buildCommand)) {
|
|
86
|
-
return;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
packageJson.scripts.predev = `${buildCommand} && ${current}`;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function ensureLoadItem(content, itemName) {
|
|
93
|
-
const pattern = /load:\s*\[([^\]]*)\]/m;
|
|
94
|
-
const match = content.match(pattern);
|
|
95
|
-
if (!match) {
|
|
96
|
-
return content;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const rawList = match[1];
|
|
100
|
-
const items = rawList
|
|
101
|
-
.split(',')
|
|
102
|
-
.map((item) => item.trim())
|
|
103
|
-
.filter(Boolean);
|
|
104
|
-
|
|
105
|
-
if (!items.includes(itemName)) {
|
|
106
|
-
items.push(itemName);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const next = `load: [${items.join(', ')}]`;
|
|
110
|
-
return content.replace(pattern, next);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function ensureValidatorSchema(content, schemaName) {
|
|
114
|
-
const pattern = /validate:\s*createEnvValidator\(\[([^\]]*)\]\)/m;
|
|
115
|
-
const match = content.match(pattern);
|
|
116
|
-
if (!match) {
|
|
117
|
-
return content;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const rawList = match[1];
|
|
121
|
-
const items = rawList
|
|
122
|
-
.split(',')
|
|
123
|
-
.map((item) => item.trim())
|
|
124
|
-
.filter(Boolean);
|
|
125
|
-
|
|
126
|
-
if (!items.includes(schemaName)) {
|
|
127
|
-
items.push(schemaName);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const next = `validate: createEnvValidator([${items.join(', ')}])`;
|
|
131
|
-
return content.replace(pattern, next);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
23
|
function patchApiPackage(targetRoot) {
|
|
135
24
|
const packagePath = path.join(targetRoot, 'apps', 'api', 'package.json');
|
|
136
25
|
if (!fs.existsSync(packagePath)) {
|
|
@@ -139,7 +28,7 @@ function patchApiPackage(targetRoot) {
|
|
|
139
28
|
|
|
140
29
|
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
|
|
141
30
|
ensureDependency(packageJson, '@forgeon/swagger', 'workspace:*');
|
|
142
|
-
|
|
31
|
+
ensureBuildSteps(packageJson, 'predev', ['pnpm --filter @forgeon/swagger build']);
|
|
143
32
|
writeJson(packagePath, packageJson);
|
|
144
33
|
}
|
|
145
34
|
|