create-forgeon 0.3.12 → 0.3.15

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.
@@ -1,4 +1,4 @@
1
- import fs from 'node:fs';
1
+ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { copyRecursive, writeJson } from '../utils/fs.mjs';
4
4
  import {
@@ -240,14 +240,77 @@ function patchApiDockerfile(targetRoot) {
240
240
  content = ensureLineAfter(content, sourceAnchor, 'COPY packages/queue packages/queue');
241
241
 
242
242
  content = content.replace(/^RUN pnpm --filter @forgeon\/queue build\r?\n?/gm, '');
243
- const buildAnchor = content.includes('RUN pnpm --filter @forgeon/api prisma:generate')
244
- ? 'RUN pnpm --filter @forgeon/api prisma:generate'
245
- : 'RUN pnpm --filter @forgeon/api build';
243
+ const buildAnchors = [
244
+ 'RUN pnpm --filter @forgeon/scheduler build',
245
+ 'RUN pnpm --filter @forgeon/api prisma:generate',
246
+ 'RUN pnpm --filter @forgeon/api build',
247
+ ];
248
+ const buildAnchor = buildAnchors.find((line) => content.includes(line)) ?? 'RUN pnpm --filter @forgeon/api build';
246
249
  content = ensureLineBefore(content, buildAnchor, 'RUN pnpm --filter @forgeon/queue build');
247
250
 
248
251
  fs.writeFileSync(dockerfilePath, `${content.trimEnd()}\n`, 'utf8');
249
252
  }
250
253
 
254
+ function ensureApiDependsOnRedis(apiBlock) {
255
+ let lines = apiBlock.replace(/\n$/, '').split('\n');
256
+ const buildIndex = lines.findIndex((line) => line === ' build:');
257
+
258
+ // Heal a previously broken queue patch where depends_on was injected inside the build block.
259
+ if (buildIndex >= 0 && lines[buildIndex + 1] === ' depends_on:') {
260
+ let malformedBlockEnd = lines.length;
261
+ for (let index = buildIndex + 2; index < lines.length; index += 1) {
262
+ if (lines[index].startsWith(' ') && !lines[index].startsWith(' ')) {
263
+ malformedBlockEnd = index;
264
+ break;
265
+ }
266
+ }
267
+
268
+ const misplacedBuildLines = lines
269
+ .slice(buildIndex + 2, malformedBlockEnd)
270
+ .filter((line) => /^( context:| dockerfile:| args:| target:)/.test(line));
271
+
272
+ if (misplacedBuildLines.length > 0) {
273
+ lines = [
274
+ ...lines.slice(0, buildIndex + 1),
275
+ ...misplacedBuildLines,
276
+ ...lines.slice(malformedBlockEnd),
277
+ ];
278
+ }
279
+ }
280
+
281
+ const dependsOnIndex = lines.findIndex((line) => line === ' depends_on:');
282
+
283
+ if (dependsOnIndex < 0) {
284
+ const restartIndex = lines.findIndex((line) => line === ' restart: unless-stopped');
285
+ const environmentIndex = lines.findIndex((line) => line === ' environment:');
286
+ const insertAt = restartIndex >= 0 ? restartIndex + 1 : environmentIndex >= 0 ? environmentIndex : lines.length;
287
+ lines.splice(insertAt, 0, ' depends_on:', ' redis:', ' condition: service_healthy');
288
+ return `${lines.join('\n')}\n`;
289
+ }
290
+
291
+ let blockEnd = lines.length;
292
+ for (let index = dependsOnIndex + 1; index < lines.length; index += 1) {
293
+ if (lines[index].startsWith(' ') && !lines[index].startsWith(' ')) {
294
+ blockEnd = index;
295
+ break;
296
+ }
297
+ }
298
+
299
+ const dependsOnBlock = lines.slice(dependsOnIndex + 1, blockEnd);
300
+ const hasRedisMapping = dependsOnBlock.includes(' redis:');
301
+ const hasRedisList = dependsOnBlock.some((line) => line.trim() === '- redis');
302
+ if (hasRedisMapping || hasRedisList) {
303
+ return `${lines.join('\n')}\n`;
304
+ }
305
+
306
+ const isListStyle = dependsOnBlock.some((line) => line.trimStart().startsWith('- '));
307
+ const insertLines = isListStyle
308
+ ? [' - redis']
309
+ : [' redis:', ' condition: service_healthy'];
310
+
311
+ lines.splice(blockEnd, 0, ...insertLines);
312
+ return `${lines.join('\n')}\n`;
313
+ }
251
314
  function patchCompose(targetRoot) {
252
315
  const composePath = path.join(targetRoot, 'infra', 'docker', 'compose.yml');
253
316
  if (!fs.existsSync(composePath)) {
@@ -300,45 +363,14 @@ function patchCompose(targetRoot) {
300
363
  );
301
364
  }
302
365
 
303
- const apiBlockMatch = content.match(/^ api:\n[\s\S]*?(?=^ [a-zA-Z0-9_-]+:\n|^volumes:\n|$)/m);
366
+ const apiBlockMatch = content.match(/^ api:\n[\s\S]*?(?=^ [a-zA-Z0-9_-]+:\n|^volumes:\n|(?![\s\S]))/m);
304
367
  if (apiBlockMatch) {
305
- let apiBlock = apiBlockMatch[0];
306
- if (!/^\s{6}redis:\s*$/m.test(apiBlock) && !/^\s{6}-\s*redis\s*$/m.test(apiBlock)) {
307
- if (/^\s{4}depends_on:\s*$/m.test(apiBlock)) {
308
- if (/^\s{6}-\s+/m.test(apiBlock)) {
309
- apiBlock = apiBlock.replace(
310
- /^(\s{4}depends_on:\n(?:\s{6}-\s+.+\n)+)/m,
311
- `$1 - redis
312
- `,
313
- );
314
- } else {
315
- apiBlock = apiBlock.replace(
316
- /^(\s{4}depends_on:\n)/m,
317
- `$1 redis:
318
- condition: service_healthy
319
- `,
320
- );
321
- }
322
- } else {
323
- const withDependsOn = apiBlock.replace(
324
- /^(\s{4}environment:\n(?:\s{6}.+\n)+)/m,
325
- `$1 depends_on:
326
- redis:
327
- condition: service_healthy
328
- `,
329
- );
330
- apiBlock =
331
- withDependsOn === apiBlock
332
- ? `${apiBlock.trimEnd()}\n depends_on:\n redis:\n condition: service_healthy\n`
333
- : withDependsOn;
334
- }
335
- }
368
+ const apiBlock = ensureApiDependsOnRedis(apiBlockMatch[0]);
336
369
  content = content.replace(apiBlockMatch[0], apiBlock);
337
370
  }
338
371
 
339
372
  fs.writeFileSync(composePath, `${content.trimEnd()}\n`, 'utf8');
340
373
  }
341
-
342
374
  function patchReadme(targetRoot) {
343
375
  const readmePath = path.join(targetRoot, 'README.md');
344
376
  if (!fs.existsSync(readmePath)) {
@@ -409,4 +441,3 @@ export function applyQueueModule({ packageRoot, targetRoot }) {
409
441
  'QUEUE_DEFAULT_BACKOFF_MS=1000',
410
442
  ]);
411
443
  }
412
-
@@ -1,4 +1,4 @@
1
- const MODULE_PRESETS = {
1
+ const MODULE_PRESETS = {
2
2
  'db-prisma': {
3
3
  id: 'db-prisma',
4
4
  label: 'DB Prisma',
@@ -1,4 +1,4 @@
1
- import fs from 'node:fs';
1
+ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { copyRecursive, writeJson } from '../utils/fs.mjs';
4
4
  import {
@@ -16,7 +16,7 @@ import {
16
16
  printOptionalIntegrationsWarning,
17
17
  runIntegrationFlow,
18
18
  } from './integrations/flow.mjs';
19
- import { writeJson } from './utils/fs.mjs';
19
+ import { readJson, writeJson } from './utils/fs.mjs';
20
20
 
21
21
  function printModuleList() {
22
22
  const modules = listModulePresets();
@@ -63,7 +63,7 @@ function collectDependencyManifestState(targetRoot) {
63
63
  }
64
64
 
65
65
  const filePath = path.join(currentDir, entry.name);
66
- const packageJson = JSON.parse(fs.readFileSync(filePath, 'utf8'));
66
+ const packageJson = readJson(filePath);
67
67
  const snapshot = {
68
68
  name: packageJson.name ?? null,
69
69
  dependencies: toSortedObject(packageJson.dependencies),
@@ -114,7 +114,7 @@ function ensureSyncTooling({ packageRoot, targetRoot }) {
114
114
  return;
115
115
  }
116
116
 
117
- const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
117
+ const packageJson = readJson(packagePath);
118
118
  if (!packageJson.scripts) {
119
119
  packageJson.scripts = {};
120
120
  }
@@ -309,3 +309,4 @@ export async function runAddModule(argv = process.argv.slice(2)) {
309
309
  console.log('Next: run pnpm install');
310
310
  }
311
311
  }
312
+
package/src/utils/fs.mjs CHANGED
@@ -1,26 +1,31 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
-
4
- export function copyRecursive(source, destination) {
5
- const stat = fs.statSync(source);
6
-
7
- if (stat.isDirectory()) {
8
- fs.mkdirSync(destination, { recursive: true });
9
- for (const entry of fs.readdirSync(source)) {
10
- copyRecursive(path.join(source, entry), path.join(destination, entry));
11
- }
12
- return;
13
- }
14
-
15
- fs.copyFileSync(source, destination);
16
- }
17
-
18
- export function writeJson(filePath, data) {
19
- fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
20
- }
21
-
22
- export function removeIfExists(targetPath) {
23
- if (fs.existsSync(targetPath)) {
24
- fs.rmSync(targetPath, { recursive: true, force: true });
25
- }
26
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ export function copyRecursive(source, destination) {
5
+ const stat = fs.statSync(source);
6
+
7
+ if (stat.isDirectory()) {
8
+ fs.mkdirSync(destination, { recursive: true });
9
+ for (const entry of fs.readdirSync(source)) {
10
+ copyRecursive(path.join(source, entry), path.join(destination, entry));
11
+ }
12
+ return;
13
+ }
14
+
15
+ fs.copyFileSync(source, destination);
16
+ }
17
+
18
+ export function writeJson(filePath, data) {
19
+ fs.writeFileSync(filePath, `${JSON.stringify(data, null, 2)}\n`, 'utf8');
20
+ }
21
+
22
+ export function readJson(filePath) {
23
+ const raw = fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '');
24
+ return JSON.parse(raw);
25
+ }
26
+
27
+ export function removeIfExists(targetPath) {
28
+ if (fs.existsSync(targetPath)) {
29
+ fs.rmSync(targetPath, { recursive: true, force: true });
30
+ }
31
+ }
@@ -1,4 +1,4 @@
1
- ## Scope
1
+ ## Scope
2
2
 
3
3
  Current implementation includes:
4
4
 
@@ -1,4 +1,4 @@
1
- {
1
+ {
2
2
  "name": "@forgeon/scheduler",
3
3
  "version": "0.1.0",
4
4
  "private": true,
@@ -1,4 +1,4 @@
1
- import { Module } from '@nestjs/common';
1
+ import { Module } from '@nestjs/common';
2
2
  import { ScheduleModule } from '@nestjs/schedule';
3
3
  import { ForgeonQueueModule } from '@forgeon/queue';
4
4
  import { SchedulerConfigModule } from './scheduler-config.module';
@@ -1,4 +1,4 @@
1
- export * from './forgeon-scheduler.module';
1
+ export * from './forgeon-scheduler.module';
2
2
  export * from './scheduler-config.loader';
3
3
  export * from './scheduler-config.module';
4
4
  export * from './scheduler-config.service';
@@ -1,4 +1,4 @@
1
- import { registerAs } from '@nestjs/config';
1
+ import { registerAs } from '@nestjs/config';
2
2
  import { parseSchedulerEnv } from './scheduler-env.schema';
3
3
 
4
4
  export const SCHEDULER_CONFIG_NAMESPACE = 'scheduler';
@@ -1,4 +1,4 @@
1
- import { Module } from '@nestjs/common';
1
+ import { Module } from '@nestjs/common';
2
2
  import { ConfigModule } from '@nestjs/config';
3
3
  import { schedulerConfig } from './scheduler-config.loader';
4
4
  import { SchedulerConfigService } from './scheduler-config.service';
@@ -1,4 +1,4 @@
1
- import { Injectable } from '@nestjs/common';
1
+ import { Injectable } from '@nestjs/common';
2
2
  import { ConfigService } from '@nestjs/config';
3
3
  import {
4
4
  SCHEDULER_CONFIG_NAMESPACE,
@@ -1,4 +1,4 @@
1
- import { z } from 'zod';
1
+ import { z } from 'zod';
2
2
 
3
3
  export const schedulerEnvSchema = z
4
4
  .object({
@@ -1,4 +1,4 @@
1
- import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
1
+ import { Injectable, Logger, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
2
2
  import { SchedulerRegistry } from '@nestjs/schedule';
3
3
  import { QueueService } from '@forgeon/queue';
4
4
  import { CronJob } from 'cron';