@valkyrjaio/sindri 26.5.1 → 26.6.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.
Files changed (92) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/bin/sindri.cjs +574 -268
  3. package/package.json +2 -2
  4. package/src/Sindri/Ast/Abstract/AstReader.ts +187 -5
  5. package/src/Sindri/Ast/Abstract/RouteAttributeReader.ts +2 -3
  6. package/src/Sindri/Ast/CliRouteAttributeReader.ts +102 -17
  7. package/src/Sindri/Ast/CliRouteParameterReader.ts +44 -26
  8. package/src/Sindri/Ast/ComponentProviderReader.ts +2 -3
  9. package/src/Sindri/Ast/ConfigReader.ts +2 -3
  10. package/src/Sindri/Ast/ConfigSourceReader.ts +2 -3
  11. package/src/Sindri/Ast/Contract/CliRouteAttributeReaderContract.ts +2 -3
  12. package/src/Sindri/Ast/Contract/CliRouteParameterReaderContract.ts +2 -3
  13. package/src/Sindri/Ast/Contract/ComponentProviderReaderContract.ts +2 -3
  14. package/src/Sindri/Ast/Contract/ConfigReaderContract.ts +2 -3
  15. package/src/Sindri/Ast/Contract/ConfigSourceReaderContract.ts +2 -3
  16. package/src/Sindri/Ast/Contract/HttpRouteAttributeReaderContract.ts +2 -3
  17. package/src/Sindri/Ast/Contract/HttpRouteMiddlewareReaderContract.ts +5 -10
  18. package/src/Sindri/Ast/Contract/HttpRouteParameterReaderContract.ts +3 -7
  19. package/src/Sindri/Ast/Contract/ListenerAttributeReaderContract.ts +2 -3
  20. package/src/Sindri/Ast/Contract/ListenerProviderReaderContract.ts +2 -3
  21. package/src/Sindri/Ast/Contract/RouteProviderReaderContract.ts +2 -3
  22. package/src/Sindri/Ast/Contract/ServiceProviderReaderContract.ts +2 -3
  23. package/src/Sindri/Ast/Data/CliArgumentParameterData.ts +2 -3
  24. package/src/Sindri/Ast/Data/CliOptionParameterData.ts +2 -3
  25. package/src/Sindri/Ast/Data/CliRouteData.ts +2 -3
  26. package/src/Sindri/Ast/Data/ConfigData.ts +2 -3
  27. package/src/Sindri/Ast/Data/ConfigImport.ts +2 -3
  28. package/src/Sindri/Ast/Data/Contract/CliArgumentParameterDataContract.ts +2 -3
  29. package/src/Sindri/Ast/Data/Contract/CliOptionParameterDataContract.ts +2 -3
  30. package/src/Sindri/Ast/Data/Contract/CliRouteDataContract.ts +2 -3
  31. package/src/Sindri/Ast/Data/Contract/ConfigDataContract.ts +2 -3
  32. package/src/Sindri/Ast/Data/Contract/HandlerDataContract.ts +2 -3
  33. package/src/Sindri/Ast/Data/Contract/HttpParameterDataContract.ts +4 -3
  34. package/src/Sindri/Ast/Data/Contract/HttpRouteDataContract.ts +2 -3
  35. package/src/Sindri/Ast/Data/Contract/ListenerDataContract.ts +2 -3
  36. package/src/Sindri/Ast/Data/HandlerData.ts +2 -3
  37. package/src/Sindri/Ast/Data/HttpParameterData.ts +3 -3
  38. package/src/Sindri/Ast/Data/HttpRouteData.ts +2 -3
  39. package/src/Sindri/Ast/Data/ListenerData.ts +2 -3
  40. package/src/Sindri/Ast/Data/Result/CliRouteAttributeResult.ts +7 -4
  41. package/src/Sindri/Ast/Data/Result/ComponentProviderResult.ts +2 -3
  42. package/src/Sindri/Ast/Data/Result/ConfigResult.ts +2 -3
  43. package/src/Sindri/Ast/Data/Result/ConfigSourceResult.ts +2 -3
  44. package/src/Sindri/Ast/Data/Result/HttpRouteAttributeResult.ts +4 -3
  45. package/src/Sindri/Ast/Data/Result/ListenerAttributeResult.ts +2 -3
  46. package/src/Sindri/Ast/Data/Result/ListenerProviderResult.ts +2 -3
  47. package/src/Sindri/Ast/Data/Result/RouteProviderResult.ts +2 -3
  48. package/src/Sindri/Ast/Data/Result/ServiceProviderResult.ts +2 -3
  49. package/src/Sindri/Ast/HttpRouteAttributeReader.ts +180 -51
  50. package/src/Sindri/Ast/HttpRouteMiddlewareReader.ts +38 -18
  51. package/src/Sindri/Ast/HttpRouteParameterReader.ts +50 -109
  52. package/src/Sindri/Ast/ListenerAttributeReader.ts +2 -3
  53. package/src/Sindri/Ast/ListenerProviderReader.ts +2 -3
  54. package/src/Sindri/Ast/RouteProviderReader.ts +2 -3
  55. package/src/Sindri/Ast/ServiceProviderReader.ts +2 -3
  56. package/src/Sindri/Ast/Throwable/Contract/AstThrowable.ts +2 -3
  57. package/src/Sindri/Ast/Throwable/Exception/Abstract/AstInvalidArgumentException.ts +2 -3
  58. package/src/Sindri/Ast/Throwable/Exception/Abstract/AstRuntimeException.ts +2 -3
  59. package/src/Sindri/Ast/Throwable/Exception/AstFileReadException.ts +2 -3
  60. package/src/Sindri/Cli/Command/GenerateCachedConfigCommand.ts +2 -3
  61. package/src/Sindri/Cli/Command/GenerateDataFromConfigCommand.ts +2 -3
  62. package/src/Sindri/Constant/CommandName.ts +2 -3
  63. package/src/Sindri/Constant/SindriInfo.ts +4 -5
  64. package/src/Sindri/Constant/SindriServiceId.ts +2 -3
  65. package/src/Sindri/Generate/Abstract/GenerateCachedConfigFromAst.ts +2 -3
  66. package/src/Sindri/Generate/Abstract/GenerateDataFromAst.ts +39 -27
  67. package/src/Sindri/Generate/Abstract/GenerateFromAst.ts +2 -3
  68. package/src/Sindri/Generator/Abstract/AstFileGenerator.ts +2 -3
  69. package/src/Sindri/Generator/Abstract/FileGenerator.ts +2 -3
  70. package/src/Sindri/Generator/Ast/Cli/AstCliDataFileGenerator.ts +21 -6
  71. package/src/Sindri/Generator/Ast/Config/AstCachedConfigFileGenerator.ts +2 -3
  72. package/src/Sindri/Generator/Ast/Container/AstContainerDataFileGenerator.ts +2 -3
  73. package/src/Sindri/Generator/Ast/Event/AstEventDataFileGenerator.ts +2 -3
  74. package/src/Sindri/Generator/Ast/Http/AstHttpDataFileGenerator.ts +83 -45
  75. package/src/Sindri/Generator/Cli/Contract/CliDataFileGeneratorContract.ts +10 -3
  76. package/src/Sindri/Generator/Config/Contract/CachedConfigFileGeneratorContract.ts +2 -3
  77. package/src/Sindri/Generator/Container/Contract/ContainerDataFileGeneratorContract.ts +2 -3
  78. package/src/Sindri/Generator/Contract/FileGeneratorContract.ts +2 -3
  79. package/src/Sindri/Generator/Enum/GenerateStatus.ts +2 -3
  80. package/src/Sindri/Generator/Event/Contract/EventDataFileGeneratorContract.ts +2 -3
  81. package/src/Sindri/Generator/Http/Contract/HttpDataFileGeneratorContract.ts +11 -3
  82. package/src/Sindri/Generator/Throwable/Contract/GeneratorThrowable.ts +2 -3
  83. package/src/Sindri/Generator/Throwable/Exception/Abstract/GeneratorInvalidArgumentException.ts +2 -3
  84. package/src/Sindri/Generator/Throwable/Exception/Abstract/GeneratorRuntimeException.ts +2 -3
  85. package/src/Sindri/Generator/Throwable/Exception/GeneratorUnreachableException.ts +2 -3
  86. package/src/Sindri/Provider/SindriAstServiceProvider.ts +2 -3
  87. package/src/Sindri/Provider/SindriCliRouteProvider.ts +2 -3
  88. package/src/Sindri/Provider/SindriCommandServiceProvider.ts +2 -3
  89. package/src/Sindri/Provider/SindriComponentProvider.ts +2 -3
  90. package/src/Sindri/Throwable/Contract/SindriThrowable.ts +2 -3
  91. package/src/Sindri/Throwable/Exception/Abstract/SindriInvalidArgumentException.ts +2 -3
  92. package/src/Sindri/Throwable/Exception/Abstract/SindriRuntimeException.ts +2 -3
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import type { ts } from 'ts-morph';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { ts } from 'ts-morph';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { ts } from 'ts-morph';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import type { SindriThrowable } from '../../../Throwable/Contract/SindriThrowable.ts';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { SindriInvalidArgumentException } from '../../../../Throwable/Exception/Abstract/SindriInvalidArgumentException.ts';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { SindriRuntimeException } from '../../../../Throwable/Exception/Abstract/SindriRuntimeException.ts';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { AstRuntimeException } from './Abstract/AstRuntimeException.ts';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { ConfigReader } from '../../Ast/ConfigReader.ts';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { CliRouteAttributeReader } from '../../Ast/CliRouteAttributeReader.ts';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  export class CommandName {
@@ -1,15 +1,14 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  export class SindriInfo {
11
- static readonly VERSION = '26.5.1' as const;
12
- static readonly VERSION_BUILD_DATE_TIME = 'August 1 2026 14:12:56 MST' as const;
10
+ static readonly VERSION = '26.6.1' as const;
11
+ static readonly VERSION_BUILD_DATE_TIME = 'August 2 2026 08:21:26 MST' as const;
13
12
  static readonly ICON = `\
14
13
  ██████████
15
14
  ████ ████
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  export class SindriServiceId {
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import * as path from 'path';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import * as fs from 'fs';
@@ -344,20 +343,20 @@ export abstract class GenerateDataFromAst extends GenerateFromAst {
344
343
  const attrResult = this.cliRouteAttributeReader.readFile(controllerPath);
345
344
 
346
345
  Object.assign(allRoutes, attrResult.routes);
346
+ this.mergeReaderImports(importMap, attrResult.importMap, config);
347
347
  }
348
348
  }
349
349
 
350
350
  this.cliGenerator.classImportMap = importMap;
351
351
 
352
- const status =
353
- imperativeRoutes.length > 0
354
- ? this.cliGenerator.generateFileFromRoutes(
355
- config.dataPath,
356
- 'AppCliRoutingData',
357
- config.dataNamespace,
358
- imperativeRoutes,
359
- )
360
- : this.cliGenerator.generateFile(config.dataPath, 'AppCliRoutingData', config.dataNamespace, allRoutes);
352
+ // Merge attribute-scanned command routes with imperative getRoutes() routes.
353
+ const status = this.cliGenerator.generateMergedFile(
354
+ config.dataPath,
355
+ 'AppCliRoutingData',
356
+ config.dataNamespace,
357
+ allRoutes,
358
+ imperativeRoutes,
359
+ );
361
360
 
362
361
  return this.addMessagesForGenerateStatus(output, status).withAddedMessages(new NewLine()).writeMessages();
363
362
  }
@@ -409,30 +408,43 @@ export abstract class GenerateDataFromAst extends GenerateFromAst {
409
408
 
410
409
  Object.assign(allRoutes, attrResult.routes);
411
410
  Object.assign(allRouteData, attrResult.routeData);
411
+ this.mergeReaderImports(importMap, attrResult.importMap, config);
412
412
  }
413
413
  }
414
414
 
415
415
  this.httpGenerator.classImportMap = importMap;
416
416
 
417
- const status =
418
- imperativeRoutes.length > 0
419
- ? this.httpGenerator.generateFileFromRoutes(
420
- config.dataPath,
421
- 'AppHttpRoutingData',
422
- config.dataNamespace,
423
- imperativeRoutes,
424
- )
425
- : this.httpGenerator.generateFile(
426
- config.dataPath,
427
- 'AppHttpRoutingData',
428
- config.dataNamespace,
429
- allRoutes,
430
- allRouteData,
431
- );
417
+ // Merge attribute-scanned routes with imperative getRoutes() routes.
418
+ const status = this.httpGenerator.generateMergedFile(
419
+ config.dataPath,
420
+ 'AppHttpRoutingData',
421
+ config.dataNamespace,
422
+ allRoutes,
423
+ allRouteData,
424
+ imperativeRoutes,
425
+ );
432
426
 
433
427
  return this.addMessagesForGenerateStatus(output, status).withAddedMessages(new NewLine()).writeMessages();
434
428
  }
435
429
 
430
+ /**
431
+ * Merge the class-name → absolute-file-path imports a route attribute reader
432
+ * resolved (handler/help-text/middleware classes) into the generated data
433
+ * file's import map, converting each absolute path to the specifier the
434
+ * generated file imports it by — relative to the output directory for
435
+ * application source, or the owning package's own specifier for a class an
436
+ * installed package exposes.
437
+ */
438
+ protected mergeReaderImports(
439
+ importMap: Record<string, string>,
440
+ readerImportMap: Record<string, string>,
441
+ config: ConfigResult,
442
+ ): void {
443
+ for (const [name, absolutePath] of Object.entries(readerImportMap)) {
444
+ importMap[name] = this.importSpecifier(config.dataPath, absolutePath);
445
+ }
446
+ }
447
+
436
448
  protected addMessagesForGenerateStatus(output: OutputContract, status: GenerateStatus): OutputContract {
437
449
  let text = 'Failed';
438
450
  let formatter = new ErrorFormatter();
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import * as fs from 'fs';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import * as fs from 'fs';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import * as fs from 'fs';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { ts } from 'ts-morph';
@@ -84,7 +83,23 @@ export class AstCliDataFileGenerator extends AstFileGenerator implements CliData
84
83
  namespace: string,
85
84
  routeExprs: readonly ts.Expression[],
86
85
  ): GenerateStatus {
87
- const routes: Record<string, ts.Expression> = {};
86
+ return this.generateMergedFile(directory, className, namespace, {}, routeExprs);
87
+ }
88
+
89
+ /**
90
+ * Generate the CLI routing data file by MERGING attribute-scanned command
91
+ * routes with imperative `getRoutes()` route objects — both are keyed by
92
+ * command name into a single routes map. Attribute entries come first;
93
+ * imperative entries override on a name collision.
94
+ */
95
+ public generateMergedFile(
96
+ directory: string,
97
+ className: string,
98
+ namespace: string,
99
+ routes: Record<string, ts.Expression>,
100
+ routeExprs: readonly ts.Expression[],
101
+ ): GenerateStatus {
102
+ const merged: Record<string, ts.Expression> = { ...routes };
88
103
 
89
104
  for (const expr of routeExprs) {
90
105
  if (!ts.isNewExpression(expr)) {
@@ -97,10 +112,10 @@ export class AstCliDataFileGenerator extends AstFileGenerator implements CliData
97
112
  continue;
98
113
  }
99
114
 
100
- routes[key] = expr;
115
+ merged[key] = expr;
101
116
  }
102
117
 
103
- return this.generateFile(directory, className, namespace, routes);
118
+ return this.generateFile(directory, className, namespace, merged);
104
119
  }
105
120
 
106
121
  /**
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { AstFileGenerator } from '../../Abstract/AstFileGenerator.ts';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { ts } from 'ts-morph';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { ts } from 'ts-morph';
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import { ts } from 'ts-morph';
@@ -75,19 +74,7 @@ export class AstHttpDataFileGenerator extends AstFileGenerator implements HttpDa
75
74
  routes: Record<string, ts.Expression>,
76
75
  routeData: Record<string, HttpRouteData>,
77
76
  ): GenerateStatus {
78
- const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
79
- ...Object.keys(AstHttpDataFileGenerator.FRAMEWORK_IMPORTS),
80
- ...Object.keys(AstHttpDataFileGenerator.FRAMEWORK_TYPE_IMPORTS),
81
- ]);
82
-
83
- const routesContent = this.getRoutesAsContent(routes, routeData);
84
- const paths = this.printNestedObject(this.buildPaths(routeData));
85
- const dynamicPaths = this.printNestedObject(this.buildDynamicPaths(routeData));
86
- const regexes = this.printNestedObject(this.buildRegexes(routeData));
87
-
88
- const data = this.assembleFile(className, userImportsBlock, routesContent, paths, dynamicPaths, regexes);
89
-
90
- return this.writeFile(directory, className, data);
77
+ return this.generateMergedFile(directory, className, namespace, routes, routeData, []);
91
78
  }
92
79
 
93
80
  /**
@@ -100,9 +87,26 @@ export class AstHttpDataFileGenerator extends AstFileGenerator implements HttpDa
100
87
  * (literal path and regex, request methods defaulting to `[HEAD, GET]`).
101
88
  */
102
89
  public generateFileFromRoutes(
90
+ directory: string,
91
+ className: string,
92
+ namespace: string,
93
+ routeExprs: readonly ts.Expression[],
94
+ ): GenerateStatus {
95
+ return this.generateMergedFile(directory, className, namespace, {}, {}, routeExprs);
96
+ }
97
+
98
+ /**
99
+ * Generate the routing data file by MERGING attribute-scanned routes with
100
+ * imperative `getRoutes()` route objects — the four lookup maps (routes,
101
+ * paths, dynamicPaths, regexes) are built from both sources and combined.
102
+ * Attribute entries are emitted first, then imperative ones.
103
+ */
104
+ public generateMergedFile(
103
105
  directory: string,
104
106
  className: string,
105
107
  _namespace: string,
108
+ routes: Record<string, ts.Expression>,
109
+ routeData: Record<string, HttpRouteData>,
106
110
  routeExprs: readonly ts.Expression[],
107
111
  ): GenerateStatus {
108
112
  const userImportsBlock = this.buildUserImportsBlock(this.classImportMap, [
@@ -114,16 +118,39 @@ export class AstHttpDataFileGenerator extends AstFileGenerator implements HttpDa
114
118
  .map((expr) => this.extractRouteMeta(expr))
115
119
  .filter((meta): meta is ImperativeRouteMeta => meta !== undefined);
116
120
 
117
- const routesContent = this.getImperativeRoutesContent(metas);
118
- const paths = this.printNestedObject(this.buildImperativePaths(metas, false));
119
- const dynamicPaths = this.printNestedObject(this.buildImperativePaths(metas, true));
120
- const regexes = this.printNestedObject(this.buildImperativeRegexes(metas));
121
+ const routesContent = this.wrapRoutes([
122
+ ...this.getRouteLines(routes, routeData),
123
+ ...this.getImperativeRouteLines(metas),
124
+ ]);
125
+ const paths = this.printNestedObject(
126
+ this.mergeNested(this.buildPaths(routeData), this.buildImperativePaths(metas, false)),
127
+ );
128
+ const dynamicPaths = this.printNestedObject(
129
+ this.mergeNested(this.buildDynamicPaths(routeData), this.buildImperativePaths(metas, true)),
130
+ );
131
+ const regexes = this.printNestedObject(
132
+ this.mergeNested(this.buildRegexes(routeData), this.buildImperativeRegexes(metas)),
133
+ );
121
134
 
122
135
  const data = this.assembleFile(className, userImportsBlock, routesContent, paths, dynamicPaths, regexes);
123
136
 
124
137
  return this.writeFile(directory, className, data);
125
138
  }
126
139
 
140
+ /** Deep-merge two `method → key → name` maps; entries in `b` override `a`. */
141
+ protected mergeNested(
142
+ a: Record<string, Record<string, string>>,
143
+ b: Record<string, Record<string, string>>,
144
+ ): Record<string, Record<string, string>> {
145
+ const merged: Record<string, Record<string, string>> = {};
146
+
147
+ for (const [outerKey, innerMap] of [...Object.entries(a), ...Object.entries(b)]) {
148
+ merged[outerKey] = { ...(merged[outerKey] ?? {}), ...innerMap };
149
+ }
150
+
151
+ return merged;
152
+ }
153
+
127
154
  protected assembleFile(
128
155
  className: string,
129
156
  userImportsBlock: string,
@@ -339,23 +366,25 @@ export class AstHttpDataFileGenerator extends AstFileGenerator implements HttpDa
339
366
  routes: Record<string, ts.Expression>,
340
367
  routeData: Record<string, HttpRouteData>,
341
368
  ): string {
342
- const entries = Object.entries(routes);
369
+ return this.wrapRoutes(this.getRouteLines(routes, routeData));
370
+ }
343
371
 
344
- if (entries.length === 0) {
372
+ /** Wrap route closure lines in the `{ ... }` object-literal body (or `{}`). */
373
+ protected wrapRoutes(lines: readonly string[]): string {
374
+ if (lines.length === 0) {
345
375
  return '{}';
346
376
  }
347
377
 
378
+ return ['{', ...lines, ' }'].join('\n ');
379
+ }
380
+
381
+ /** Build the per-route closure lines for attribute-scanned routes. */
382
+ protected getRouteLines(routes: Record<string, ts.Expression>, routeData: Record<string, HttpRouteData>): string[] {
348
383
  const lines: string[] = [];
349
384
 
350
- for (const [key, routeExpr] of entries) {
385
+ for (const [key, routeExpr] of Object.entries(routes)) {
351
386
  const data = routeData[key] ?? null;
352
- let expr = routeExpr;
353
-
354
- if (data !== null && data.isDynamic && ts.isNewExpression(expr)) {
355
- const computedRegex = data.parameters.length > 0 ? this.computeRegex(data) : '';
356
- const args = [...(expr.arguments ?? []), ts.factory.createStringLiteral(computedRegex)];
357
- expr = ts.factory.createNewExpression(expr.expression, expr.typeArguments, args);
358
- }
387
+ const expr = data !== null ? this.injectRegex(routeExpr, data) : routeExpr;
359
388
 
360
389
  const printedRoute = this.printer.printNode(ts.EmitHint.Unspecified, expr, this.dummySourceFile);
361
390
  const formattedKey = key.includes('::')
@@ -365,7 +394,25 @@ export class AstHttpDataFileGenerator extends AstFileGenerator implements HttpDa
365
394
  lines.push(` ${formattedKey}: (): RouteContract => ${printedRoute},`);
366
395
  }
367
396
 
368
- return ['{', ...lines, ' }'].join('\n ');
397
+ return lines;
398
+ }
399
+
400
+ /**
401
+ * Replace the regex argument (slot 2) of a dynamic `new DynamicRoute(...)`
402
+ * expression with the regex computed from the route's parameters. The reader
403
+ * emits an empty-string placeholder there; the generator owns the routing
404
+ * `Processor` and computes the real value here.
405
+ */
406
+ protected injectRegex(expr: ts.Expression, data: HttpRouteData): ts.Expression {
407
+ if (!(data.isDynamic && ts.isNewExpression(expr))) {
408
+ return expr;
409
+ }
410
+
411
+ const computedRegex = data.parameters.length > 0 ? this.computeRegex(data) : '';
412
+ const original = expr.arguments ?? ts.factory.createNodeArray<ts.Expression>();
413
+ const args = original.map((arg, index) => (index === 2 ? ts.factory.createStringLiteral(computedRegex) : arg));
414
+
415
+ return ts.factory.createNewExpression(expr.expression, expr.typeArguments, args);
369
416
  }
370
417
 
371
418
  // -------------------------------------------------------------------------
@@ -433,20 +480,11 @@ export class AstHttpDataFileGenerator extends AstFileGenerator implements HttpDa
433
480
  return methods;
434
481
  }
435
482
 
436
- protected getImperativeRoutesContent(metas: readonly ImperativeRouteMeta[]): string {
437
- if (metas.length === 0) {
438
- return '{}';
439
- }
440
-
441
- const lines: string[] = [];
442
-
443
- for (const meta of metas) {
444
- // Imperative route expressions come parsed from a provider's `getRoutes()`
445
- // body, so they are emitted verbatim via their original source text.
446
- lines.push(` ['${meta.name}']: (): RouteContract => ${meta.expr.getText()},`);
447
- }
448
-
449
- return ['{', ...lines, ' }'].join('\n ');
483
+ /** Build the per-route closure lines for imperative `getRoutes()` routes. */
484
+ protected getImperativeRouteLines(metas: readonly ImperativeRouteMeta[]): string[] {
485
+ // Imperative route expressions come parsed from a provider's `getRoutes()`
486
+ // body, so they are emitted verbatim via their original source text.
487
+ return metas.map((meta) => ` ['${meta.name}']: (): RouteContract => ${meta.expr.getText()},`);
450
488
  }
451
489
 
452
490
  protected buildImperativePaths(
@@ -1,10 +1,9 @@
1
1
  /*
2
2
  * This file is part of the Sindri package.
3
3
  *
4
- * (c) Melech Mizrachi <melechmizrachi@gmail.com>
4
+ * Copyright (c) 2016-present Melech Mizrachi
5
5
  *
6
- * For the full copyright and license information, please view the LICENSE
7
- * file that was distributed with this source code.
6
+ * Released under the MIT License. See LICENSE.md for details.
8
7
  */
9
8
 
10
9
  import type { ts } from 'ts-morph';
@@ -29,5 +28,13 @@ export interface CliDataFileGeneratorContract {
29
28
  routeExprs: readonly ts.Expression[],
30
29
  ): GenerateStatus;
31
30
 
31
+ generateMergedFile(
32
+ directory: string,
33
+ className: string,
34
+ namespace: string,
35
+ routes: Record<string, ts.Expression>,
36
+ routeExprs: readonly ts.Expression[],
37
+ ): GenerateStatus;
38
+
32
39
  generateClassContents(routes: Record<string, ts.Expression>): string;
33
40
  }