@sfdc-webapps/cli 1.0.2

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 (405) hide show
  1. package/README.md +1414 -0
  2. package/dist/commands/apply-patches.d.ts +23 -0
  3. package/dist/commands/apply-patches.d.ts.map +1 -0
  4. package/dist/commands/apply-patches.js +524 -0
  5. package/dist/commands/apply-patches.js.map +1 -0
  6. package/dist/commands/new-app-feature.d.ts +7 -0
  7. package/dist/commands/new-app-feature.d.ts.map +1 -0
  8. package/dist/commands/new-app-feature.js +166 -0
  9. package/dist/commands/new-app-feature.js.map +1 -0
  10. package/dist/commands/new-app.d.ts +1 -0
  11. package/dist/commands/new-app.d.ts.map +1 -0
  12. package/dist/commands/new-app.js +2 -0
  13. package/dist/commands/new-app.js.map +1 -0
  14. package/dist/commands/watch-patches.d.ts +6 -0
  15. package/dist/commands/watch-patches.d.ts.map +1 -0
  16. package/dist/commands/watch-patches.js +152 -0
  17. package/dist/commands/watch-patches.js.map +1 -0
  18. package/dist/core/dependency-resolver.d.ts +40 -0
  19. package/dist/core/dependency-resolver.d.ts.map +1 -0
  20. package/dist/core/dependency-resolver.js +122 -0
  21. package/dist/core/dependency-resolver.js.map +1 -0
  22. package/dist/core/file-operations.d.ts +37 -0
  23. package/dist/core/file-operations.d.ts.map +1 -0
  24. package/dist/core/file-operations.js +201 -0
  25. package/dist/core/file-operations.js.map +1 -0
  26. package/dist/core/package-json-merger.d.ts +30 -0
  27. package/dist/core/package-json-merger.d.ts.map +1 -0
  28. package/dist/core/package-json-merger.js +104 -0
  29. package/dist/core/package-json-merger.js.map +1 -0
  30. package/dist/core/patch-loader.d.ts +17 -0
  31. package/dist/core/patch-loader.d.ts.map +1 -0
  32. package/dist/core/patch-loader.js +100 -0
  33. package/dist/core/patch-loader.js.map +1 -0
  34. package/dist/index.d.ts +3 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +90 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/types.d.ts +21 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +2 -0
  41. package/dist/types.js.map +1 -0
  42. package/dist/utils/debounce.d.ts +6 -0
  43. package/dist/utils/debounce.d.ts.map +1 -0
  44. package/dist/utils/debounce.js +19 -0
  45. package/dist/utils/debounce.js.map +1 -0
  46. package/dist/utils/import-merger.d.ts +12 -0
  47. package/dist/utils/import-merger.d.ts.map +1 -0
  48. package/dist/utils/import-merger.js +240 -0
  49. package/dist/utils/import-merger.js.map +1 -0
  50. package/dist/utils/logger.d.ts +6 -0
  51. package/dist/utils/logger.d.ts.map +1 -0
  52. package/dist/utils/logger.js +17 -0
  53. package/dist/utils/logger.js.map +1 -0
  54. package/dist/utils/path-mappings.d.ts +88 -0
  55. package/dist/utils/path-mappings.d.ts.map +1 -0
  56. package/dist/utils/path-mappings.js +138 -0
  57. package/dist/utils/path-mappings.js.map +1 -0
  58. package/dist/utils/paths.d.ts +43 -0
  59. package/dist/utils/paths.d.ts.map +1 -0
  60. package/dist/utils/paths.js +107 -0
  61. package/dist/utils/paths.js.map +1 -0
  62. package/dist/utils/route-merger.d.ts +107 -0
  63. package/dist/utils/route-merger.d.ts.map +1 -0
  64. package/dist/utils/route-merger.js +303 -0
  65. package/dist/utils/route-merger.js.map +1 -0
  66. package/dist/utils/validation.d.ts +29 -0
  67. package/dist/utils/validation.d.ts.map +1 -0
  68. package/dist/utils/validation.js +109 -0
  69. package/dist/utils/validation.js.map +1 -0
  70. package/package.json +39 -0
  71. package/src/commands/apply-patches.ts +594 -0
  72. package/src/commands/new-app-feature.ts +203 -0
  73. package/src/commands/new-app.ts +0 -0
  74. package/src/commands/watch-patches.ts +173 -0
  75. package/src/core/dependency-resolver.ts +175 -0
  76. package/src/core/file-operations.ts +265 -0
  77. package/src/core/package-json-merger.ts +129 -0
  78. package/src/core/patch-loader.ts +128 -0
  79. package/src/index.ts +95 -0
  80. package/src/types.ts +23 -0
  81. package/src/utils/debounce.ts +23 -0
  82. package/src/utils/import-merger.ts +293 -0
  83. package/src/utils/logger.ts +21 -0
  84. package/src/utils/path-mappings.ts +154 -0
  85. package/src/utils/paths.ts +121 -0
  86. package/src/utils/route-merger.ts +357 -0
  87. package/src/utils/validation.ts +150 -0
  88. package/test/e2e/E2E_TEST_FIXTURES.md +509 -0
  89. package/test/e2e/apply-patches.spec.ts +861 -0
  90. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/package.json +10 -0
  91. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/appLayout.tsx +27 -0
  92. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/home.tsx +8 -0
  93. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/index.tsx +10 -0
  94. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/old-page.tsx +8 -0
  95. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/routes.tsx +23 -0
  96. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/styles/global.css +14 -0
  97. package/test/e2e/fixtures/basic-operations/dep-chain-linear/feature.ts +11 -0
  98. package/test/e2e/fixtures/basic-operations/dep-chain-linear/template/digitalExperiences/webApplications/dep-chain-linear/src/__inherit__appLayout.tsx +27 -0
  99. package/test/e2e/fixtures/basic-operations/dep-chain-linear/template/digitalExperiences/webApplications/dep-chain-linear/src/contact.tsx +8 -0
  100. package/test/e2e/fixtures/basic-operations/dep-chain-linear/template/digitalExperiences/webApplications/dep-chain-linear/src/routes.tsx +17 -0
  101. package/test/e2e/fixtures/basic-operations/feature-file-inherit-route-add/feature.ts +10 -0
  102. package/test/e2e/fixtures/basic-operations/feature-file-inherit-route-add/template/digitalExperiences/webApplications/feature-file-inherit-route-add/src/__inherit__appLayout.tsx +27 -0
  103. package/test/e2e/fixtures/basic-operations/feature-file-inherit-route-add/template/digitalExperiences/webApplications/feature-file-inherit-route-add/src/__inherit__home.tsx +0 -0
  104. package/test/e2e/fixtures/basic-operations/feature-file-inherit-route-add/template/digitalExperiences/webApplications/feature-file-inherit-route-add/src/about.tsx +8 -0
  105. package/test/e2e/fixtures/basic-operations/feature-file-inherit-route-add/template/digitalExperiences/webApplications/feature-file-inherit-route-add/src/routes.tsx +17 -0
  106. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/feature.ts +10 -0
  107. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/template/digitalExperiences/webApplications/feature-file-prepend-append/src/__inherit__appLayout.tsx +27 -0
  108. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/template/digitalExperiences/webApplications/feature-file-prepend-append/src/__prepend__index.tsx +2 -0
  109. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/template/digitalExperiences/webApplications/feature-file-prepend-append/src/routes.tsx +17 -0
  110. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/template/digitalExperiences/webApplications/feature-file-prepend-append/src/settings.tsx +9 -0
  111. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/template/digitalExperiences/webApplications/feature-file-prepend-append/src/styles/__append__global.css +10 -0
  112. package/test/e2e/fixtures/basic-operations/feature-file-route-delete/feature.ts +10 -0
  113. package/test/e2e/fixtures/basic-operations/feature-file-route-delete/template/digitalExperiences/webApplications/feature-file-route-delete/src/__delete__old-page.tsx +2 -0
  114. package/test/e2e/fixtures/basic-operations/feature-file-route-delete/template/digitalExperiences/webApplications/feature-file-route-delete/src/__inherit__appLayout.tsx +27 -0
  115. package/test/e2e/fixtures/basic-operations/feature-file-route-delete/template/digitalExperiences/webApplications/feature-file-route-delete/src/routes.tsx +16 -0
  116. package/test/e2e/fixtures/composition-5-features/5-features-about/feature.ts +3 -0
  117. package/test/e2e/fixtures/composition-5-features/5-features-about/template/digitalExperiences/webApplications/5-features-about/src/about.tsx +8 -0
  118. package/test/e2e/fixtures/composition-5-features/5-features-about/template/digitalExperiences/webApplications/5-features-about/src/routes.tsx +17 -0
  119. package/test/e2e/fixtures/composition-5-features/5-features-analytics/feature.ts +3 -0
  120. package/test/e2e/fixtures/composition-5-features/5-features-analytics/template/digitalExperiences/webApplications/5-features-analytics/src/__prepend__index.tsx +2 -0
  121. package/test/e2e/fixtures/composition-5-features/5-features-footer/feature.ts +3 -0
  122. package/test/e2e/fixtures/composition-5-features/5-features-footer/template/digitalExperiences/webApplications/5-features-footer/src/footer.tsx +7 -0
  123. package/test/e2e/fixtures/composition-5-features/5-features-footer/template/digitalExperiences/webApplications/5-features-footer/src/routes.tsx +17 -0
  124. package/test/e2e/fixtures/composition-5-features/5-features-navigation/feature.ts +3 -0
  125. package/test/e2e/fixtures/composition-5-features/5-features-navigation/template/digitalExperiences/webApplications/5-features-navigation/src/navigation.tsx +7 -0
  126. package/test/e2e/fixtures/composition-5-features/5-features-navigation/template/digitalExperiences/webApplications/5-features-navigation/src/routes.tsx +17 -0
  127. package/test/e2e/fixtures/composition-5-features/5-features-theme/feature.ts +3 -0
  128. package/test/e2e/fixtures/composition-5-features/5-features-theme/template/digitalExperiences/webApplications/5-features-theme/src/styles/__append__global.css +5 -0
  129. package/test/e2e/fixtures/composition-append/composition-append-1/feature.ts +10 -0
  130. package/test/e2e/fixtures/composition-append/composition-append-1/template/digitalExperiences/webApplications/composition-append-1/src/styles/__append__global.css +4 -0
  131. package/test/e2e/fixtures/composition-append/composition-append-2/feature.ts +10 -0
  132. package/test/e2e/fixtures/composition-append/composition-append-2/template/digitalExperiences/webApplications/composition-append-2/src/styles/__append__global.css +4 -0
  133. package/test/e2e/fixtures/composition-append/composition-append-3/feature.ts +10 -0
  134. package/test/e2e/fixtures/composition-append/composition-append-3/template/digitalExperiences/webApplications/composition-append-3/src/styles/__append__global.css +4 -0
  135. package/test/e2e/fixtures/composition-inherit-modify/composition-inherit-layout/feature.ts +3 -0
  136. package/test/e2e/fixtures/composition-inherit-modify/composition-inherit-layout/template/digitalExperiences/webApplications/composition-inherit-layout/src/__inherit__appLayout.tsx +1 -0
  137. package/test/e2e/fixtures/composition-inherit-modify/composition-modify-layout/feature.ts +3 -0
  138. package/test/e2e/fixtures/composition-inherit-modify/composition-modify-layout/template/digitalExperiences/webApplications/composition-modify-layout/src/__append__appLayout.tsx +2 -0
  139. package/test/e2e/fixtures/composition-prepend/composition-prepend-1/feature.ts +10 -0
  140. package/test/e2e/fixtures/composition-prepend/composition-prepend-1/template/digitalExperiences/webApplications/composition-prepend-1/src/__prepend__index.tsx +2 -0
  141. package/test/e2e/fixtures/composition-prepend/composition-prepend-2/feature.ts +10 -0
  142. package/test/e2e/fixtures/composition-prepend/composition-prepend-2/template/digitalExperiences/webApplications/composition-prepend-2/src/__prepend__index.tsx +2 -0
  143. package/test/e2e/fixtures/composition-prepend/composition-prepend-3/feature.ts +10 -0
  144. package/test/e2e/fixtures/composition-prepend/composition-prepend-3/template/digitalExperiences/webApplications/composition-prepend-3/src/__prepend__index.tsx +2 -0
  145. package/test/e2e/fixtures/dep-chain-long/dep-chain-a/feature.ts +11 -0
  146. package/test/e2e/fixtures/dep-chain-long/dep-chain-a/template/digitalExperiences/webApplications/dep-chain-a/src/page-a.tsx +8 -0
  147. package/test/e2e/fixtures/dep-chain-long/dep-chain-a/template/digitalExperiences/webApplications/dep-chain-a/src/routes.tsx +17 -0
  148. package/test/e2e/fixtures/dep-chain-long/dep-chain-b/feature.ts +11 -0
  149. package/test/e2e/fixtures/dep-chain-long/dep-chain-b/template/digitalExperiences/webApplications/dep-chain-b/src/page-b.tsx +8 -0
  150. package/test/e2e/fixtures/dep-chain-long/dep-chain-b/template/digitalExperiences/webApplications/dep-chain-b/src/routes.tsx +17 -0
  151. package/test/e2e/fixtures/dep-chain-long/dep-chain-c/feature.ts +11 -0
  152. package/test/e2e/fixtures/dep-chain-long/dep-chain-c/template/digitalExperiences/webApplications/dep-chain-c/src/page-c.tsx +8 -0
  153. package/test/e2e/fixtures/dep-chain-long/dep-chain-c/template/digitalExperiences/webApplications/dep-chain-c/src/routes.tsx +17 -0
  154. package/test/e2e/fixtures/dep-chain-long/dep-chain-d/feature.ts +10 -0
  155. package/test/e2e/fixtures/dep-chain-long/dep-chain-d/template/digitalExperiences/webApplications/dep-chain-d/src/page-d.tsx +8 -0
  156. package/test/e2e/fixtures/dep-chain-long/dep-chain-d/template/digitalExperiences/webApplications/dep-chain-d/src/routes.tsx +17 -0
  157. package/test/e2e/fixtures/dep-circular/dep-circular-a/feature.ts +11 -0
  158. package/test/e2e/fixtures/dep-circular/dep-circular-a/template/digitalExperiences/webApplications/dep-circular-a/src/page-a.tsx +8 -0
  159. package/test/e2e/fixtures/dep-circular/dep-circular-b/feature.ts +11 -0
  160. package/test/e2e/fixtures/dep-circular/dep-circular-b/template/digitalExperiences/webApplications/dep-circular-b/src/page-b.tsx +8 -0
  161. package/test/e2e/fixtures/dep-complex-graph/README.md +119 -0
  162. package/test/e2e/fixtures/dep-complex-graph/create-placeholders.sh +25 -0
  163. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/feature.ts +14 -0
  164. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/template/digitalExperiences/webApplications/dep-complex-graph-auth/src/__delete__old-page.tsx +1 -0
  165. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/template/digitalExperiences/webApplications/dep-complex-graph-auth/src/__inherit__appLayout.tsx +1 -0
  166. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/template/digitalExperiences/webApplications/dep-complex-graph-auth/src/login.tsx +8 -0
  167. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/template/digitalExperiences/webApplications/dep-complex-graph-auth/src/routes.tsx +29 -0
  168. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/template/digitalExperiences/webApplications/dep-complex-graph-auth/src/signup.tsx +1 -0
  169. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/feature.ts +14 -0
  170. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/template/digitalExperiences/webApplications/dep-complex-graph-dashboard/src/__inherit__appLayout.tsx +1 -0
  171. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/template/digitalExperiences/webApplications/dep-complex-graph-dashboard/src/analytics.tsx +1 -0
  172. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/template/digitalExperiences/webApplications/dep-complex-graph-dashboard/src/dashboard.tsx +8 -0
  173. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/template/digitalExperiences/webApplications/dep-complex-graph-dashboard/src/reports.tsx +1 -0
  174. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/template/digitalExperiences/webApplications/dep-complex-graph-dashboard/src/routes.tsx +32 -0
  175. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-profile/feature.ts +14 -0
  176. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-profile/template/digitalExperiences/webApplications/dep-complex-graph-profile/src/__inherit__appLayout.tsx +1 -0
  177. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-profile/template/digitalExperiences/webApplications/dep-complex-graph-profile/src/profile-edit.tsx +1 -0
  178. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-profile/template/digitalExperiences/webApplications/dep-complex-graph-profile/src/profile.tsx +8 -0
  179. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-profile/template/digitalExperiences/webApplications/dep-complex-graph-profile/src/routes.tsx +26 -0
  180. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-components/feature.ts +14 -0
  181. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-components/template/digitalExperiences/webApplications/dep-complex-graph-shared-components/src/__inherit__appLayout.tsx +1 -0
  182. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-components/template/digitalExperiences/webApplications/dep-complex-graph-shared-components/src/components.tsx +8 -0
  183. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-components/template/digitalExperiences/webApplications/dep-complex-graph-shared-components/src/routes.tsx +18 -0
  184. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-types/feature.ts +10 -0
  185. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-types/template/digitalExperiences/webApplications/dep-complex-graph-shared-types/src/__inherit__appLayout.tsx +1 -0
  186. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-types/template/digitalExperiences/webApplications/dep-complex-graph-shared-types/src/routes.tsx +17 -0
  187. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-types/template/digitalExperiences/webApplications/dep-complex-graph-shared-types/src/types.tsx +8 -0
  188. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-utils/feature.ts +10 -0
  189. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-utils/template/digitalExperiences/webApplications/dep-complex-graph-shared-utils/src/__inherit__appLayout.tsx +1 -0
  190. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-utils/template/digitalExperiences/webApplications/dep-complex-graph-shared-utils/src/routes.tsx +18 -0
  191. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-utils/template/digitalExperiences/webApplications/dep-complex-graph-shared-utils/src/tools.tsx +1 -0
  192. package/test/e2e/fixtures/dep-complex-graph/feature.ts +15 -0
  193. package/test/e2e/fixtures/dep-complex-graph/template/digitalExperiences/webApplications/dep-complex-graph/src/__inherit__appLayout.tsx +1 -0
  194. package/test/e2e/fixtures/dep-complex-graph/template/digitalExperiences/webApplications/dep-complex-graph/src/admin-dashboard.tsx +1 -0
  195. package/test/e2e/fixtures/dep-complex-graph/template/digitalExperiences/webApplications/dep-complex-graph/src/admin-layout.tsx +1 -0
  196. package/test/e2e/fixtures/dep-complex-graph/template/digitalExperiences/webApplications/dep-complex-graph/src/routes.tsx +24 -0
  197. package/test/e2e/fixtures/dep-diamond/dep-diamond-left/feature.ts +11 -0
  198. package/test/e2e/fixtures/dep-diamond/dep-diamond-left/template/digitalExperiences/webApplications/dep-diamond-left/src/left-page.tsx +8 -0
  199. package/test/e2e/fixtures/dep-diamond/dep-diamond-left/template/digitalExperiences/webApplications/dep-diamond-left/src/routes.tsx +17 -0
  200. package/test/e2e/fixtures/dep-diamond/dep-diamond-right/feature.ts +11 -0
  201. package/test/e2e/fixtures/dep-diamond/dep-diamond-right/template/digitalExperiences/webApplications/dep-diamond-right/src/right-page.tsx +8 -0
  202. package/test/e2e/fixtures/dep-diamond/dep-diamond-right/template/digitalExperiences/webApplications/dep-diamond-right/src/routes.tsx +17 -0
  203. package/test/e2e/fixtures/dep-diamond/dep-diamond-shared/feature.ts +10 -0
  204. package/test/e2e/fixtures/dep-diamond/dep-diamond-shared/template/digitalExperiences/webApplications/dep-diamond-shared/src/routes.tsx +17 -0
  205. package/test/e2e/fixtures/dep-diamond/dep-diamond-shared/template/digitalExperiences/webApplications/dep-diamond-shared/src/shared-utils.tsx +8 -0
  206. package/test/e2e/fixtures/edges/edge-binary-files/feature.ts +13 -0
  207. package/test/e2e/fixtures/edges/edge-binary-files/template/digitalExperiences/webApplications/edge-binary-files/src/__inherit__appLayout.tsx +1 -0
  208. package/test/e2e/fixtures/edges/edge-binary-files/template/digitalExperiences/webApplications/edge-binary-files/src/assets/test-image.png +0 -0
  209. package/test/e2e/fixtures/edges/edge-binary-files/template/digitalExperiences/webApplications/edge-binary-files/src/placeholder.tsx +0 -0
  210. package/test/e2e/fixtures/edges/edge-conflicting-prefixes/feature.ts +13 -0
  211. package/test/e2e/fixtures/edges/edge-conflicting-prefixes/template/digitalExperiences/webApplications/edge-conflicting-prefixes/src/__inherit____delete__conflicting.tsx +5 -0
  212. package/test/e2e/fixtures/edges/edge-conflicting-prefixes/template/digitalExperiences/webApplications/edge-conflicting-prefixes/src/placeholder.tsx +0 -0
  213. package/test/e2e/fixtures/edges/edge-empty-feature/feature.ts +16 -0
  214. package/test/e2e/fixtures/edges/edge-empty-feature/template/digitalExperiences/webApplications/edge-empty-feature/src/placeholder.tsx +0 -0
  215. package/test/e2e/fixtures/edges/edge-invalid-routes/feature.ts +12 -0
  216. package/test/e2e/fixtures/edges/edge-invalid-routes/template/digitalExperiences/webApplications/edge-invalid-routes/src/placeholder.tsx +0 -0
  217. package/test/e2e/fixtures/edges/edge-invalid-routes/template/digitalExperiences/webApplications/edge-invalid-routes/src/routes.tsx +8 -0
  218. package/test/e2e/fixtures/edges/edge-line-endings/feature.ts +13 -0
  219. package/test/e2e/fixtures/edges/edge-line-endings/template/digitalExperiences/webApplications/edge-line-endings/src/__prepend__index.tsx +2 -0
  220. package/test/e2e/fixtures/edges/edge-line-endings/template/digitalExperiences/webApplications/edge-line-endings/src/crlf.tsx +4 -0
  221. package/test/e2e/fixtures/edges/edge-line-endings/template/digitalExperiences/webApplications/edge-line-endings/src/placeholder.tsx +0 -0
  222. package/test/e2e/fixtures/edges/edge-line-endings/template/digitalExperiences/webApplications/edge-line-endings/src/styles/__append__global.css +4 -0
  223. package/test/e2e/fixtures/edges/edge-missing-base-files/feature.ts +13 -0
  224. package/test/e2e/fixtures/edges/edge-missing-base-files/template/digitalExperiences/webApplications/edge-missing-base-files/src/__inherit__missing.tsx +1 -0
  225. package/test/e2e/fixtures/edges/edge-missing-base-files/template/digitalExperiences/webApplications/edge-missing-base-files/src/__prepend__nonexistent.tsx +2 -0
  226. package/test/e2e/fixtures/edges/edge-missing-base-files/template/digitalExperiences/webApplications/edge-missing-base-files/src/placeholder.tsx +0 -0
  227. package/test/e2e/fixtures/edges/edge-package-json-conflicts-a/feature.ts +14 -0
  228. package/test/e2e/fixtures/edges/edge-package-json-conflicts-a/template/digitalExperiences/webApplications/edge-package-json-conflicts-a/src/placeholder.tsx +1 -0
  229. package/test/e2e/fixtures/edges/edge-package-json-conflicts-b/feature.ts +14 -0
  230. package/test/e2e/fixtures/edges/edge-package-json-conflicts-b/template/digitalExperiences/webApplications/edge-package-json-conflicts-b/src/placeholder.tsx +1 -0
  231. package/test/e2e/fixtures/edges/edge-performance-many/feature.ts +26 -0
  232. package/test/e2e/fixtures/edges/edge-performance-many/template/digitalExperiences/webApplications/edge-performance-many/src/placeholder.tsx +0 -0
  233. package/test/e2e/fixtures/path-mappings/feature-custom-mapping/feature.ts +16 -0
  234. package/test/e2e/fixtures/path-mappings/feature-custom-mapping/template/web/src/custom-component.tsx +3 -0
  235. package/test/e2e/fixtures/path-mappings/feature-custom-mapping/template/web/src/routes.tsx +8 -0
  236. package/test/e2e/fixtures/path-mappings/feature-mixed-paths/feature.ts +7 -0
  237. package/test/e2e/fixtures/path-mappings/feature-mixed-paths/template/digitalExperiences/webApplications/feature-mixed-paths/assets/icon.svg +3 -0
  238. package/test/e2e/fixtures/path-mappings/feature-mixed-paths/template/webApp/src/mapped-component.tsx +3 -0
  239. package/test/e2e/fixtures/path-mappings/feature-mixed-paths/template/webApp/src/routes.tsx +8 -0
  240. package/test/e2e/fixtures/path-mappings/feature-with-webapp-mapping/feature.ts +7 -0
  241. package/test/e2e/fixtures/path-mappings/feature-with-webapp-mapping/template/webApp/src/app.tsx +3 -0
  242. package/test/e2e/fixtures/path-mappings/feature-with-webapp-mapping/template/webApp/src/routes.tsx +8 -0
  243. package/test/e2e/fixtures/path-mappings/feature-without-mapping/feature.ts +11 -0
  244. package/test/e2e/fixtures/path-mappings/feature-without-mapping/template/digitalExperiences/webApplications/feature-without-mapping/src/legacy-component.tsx +3 -0
  245. package/test/e2e/fixtures/path-mappings/feature-without-mapping/template/digitalExperiences/webApplications/feature-without-mapping/src/routes.tsx +8 -0
  246. package/test/e2e/fixtures/scenarios/scenario-authentication/feature.ts +10 -0
  247. package/test/e2e/fixtures/scenarios/scenario-authentication/template/digitalExperiences/webApplications/scenario-authentication/src/components/LoginForm.tsx +25 -0
  248. package/test/e2e/fixtures/scenarios/scenario-authentication/template/digitalExperiences/webApplications/scenario-authentication/src/context/AuthContext.tsx +28 -0
  249. package/test/e2e/fixtures/scenarios/scenario-authentication/template/digitalExperiences/webApplications/scenario-authentication/src/login.tsx +10 -0
  250. package/test/e2e/fixtures/scenarios/scenario-authentication/template/digitalExperiences/webApplications/scenario-authentication/src/placeholder.tsx +0 -0
  251. package/test/e2e/fixtures/scenarios/scenario-authentication/template/digitalExperiences/webApplications/scenario-authentication/src/routes.tsx +17 -0
  252. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/feature.ts +11 -0
  253. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/__inherit__appLayout.tsx +1 -0
  254. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/components/ContactDetail.tsx +54 -0
  255. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/components/ContactForm.tsx +146 -0
  256. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/components/ContactList.tsx +77 -0
  257. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/placeholder.tsx +0 -0
  258. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/routes.tsx +36 -0
  259. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/styles/__append__global.css +176 -0
  260. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/utils/contactsApi.ts +42 -0
  261. package/test/e2e/fixtures/scenarios/scenario-navigation-menu/feature.ts +10 -0
  262. package/test/e2e/fixtures/scenarios/scenario-navigation-menu/template/digitalExperiences/webApplications/scenario-navigation-menu/src/__append__appLayout.tsx +4 -0
  263. package/test/e2e/fixtures/scenarios/scenario-navigation-menu/template/digitalExperiences/webApplications/scenario-navigation-menu/src/components/NavigationMenu.tsx +17 -0
  264. package/test/e2e/fixtures/scenarios/scenario-navigation-menu/template/digitalExperiences/webApplications/scenario-navigation-menu/src/placeholder.tsx +0 -0
  265. package/test/e2e/fixtures/single-operations/feature-file-append/feature.ts +10 -0
  266. package/test/e2e/fixtures/single-operations/feature-file-append/template/digitalExperiences/webApplications/feature-file-append/src/styles/__append__global.css +10 -0
  267. package/test/e2e/fixtures/single-operations/feature-file-inherit/feature.ts +10 -0
  268. package/test/e2e/fixtures/single-operations/feature-file-inherit/template/digitalExperiences/webApplications/feature-file-inherit/src/__inherit__appLayout.tsx +1 -0
  269. package/test/e2e/fixtures/single-operations/feature-file-inherit/template/digitalExperiences/webApplications/feature-file-inherit/src/__inherit__home.tsx +1 -0
  270. package/test/e2e/fixtures/single-operations/feature-file-prepend/feature.ts +10 -0
  271. package/test/e2e/fixtures/single-operations/feature-file-prepend/template/digitalExperiences/webApplications/feature-file-prepend/src/__prepend__index.tsx +2 -0
  272. package/test/e2e/fixtures/single-operations/feature-import-merge/feature.ts +10 -0
  273. package/test/e2e/fixtures/single-operations/feature-import-merge/template/digitalExperiences/webApplications/feature-import-merge/src/routes.tsx +18 -0
  274. package/test/e2e/fixtures/single-operations/feature-import-merge/template/digitalExperiences/webApplications/feature-import-merge/src/settings.tsx +8 -0
  275. package/test/e2e/fixtures/single-operations/feature-route-add/feature.ts +10 -0
  276. package/test/e2e/fixtures/single-operations/feature-route-add/template/digitalExperiences/webApplications/feature-route-add/src/about.tsx +8 -0
  277. package/test/e2e/fixtures/single-operations/feature-route-add/template/digitalExperiences/webApplications/feature-route-add/src/routes.tsx +23 -0
  278. package/test/e2e/fixtures/single-operations/feature-route-add/template/digitalExperiences/webApplications/feature-route-add/src/services.tsx +8 -0
  279. package/test/e2e/fixtures/watch/watch-add-delete-files/feature.ts +13 -0
  280. package/test/e2e/fixtures/watch/watch-add-delete-files/template/digitalExperiences/webApplications/watch-add-delete-files/src/initial.tsx +4 -0
  281. package/test/e2e/fixtures/watch/watch-add-delete-files/template/digitalExperiences/webApplications/watch-add-delete-files/src/placeholder.tsx +4 -0
  282. package/test/e2e/fixtures/watch/watch-basic/feature.ts +13 -0
  283. package/test/e2e/fixtures/watch/watch-basic/template/digitalExperiences/webApplications/watch-basic/src/placeholder.tsx +4 -0
  284. package/test/e2e/fixtures/watch/watch-basic/template/digitalExperiences/webApplications/watch-basic/src/watchTest.tsx +3 -0
  285. package/test/e2e/fixtures/watch/watch-debounce/feature.ts +13 -0
  286. package/test/e2e/fixtures/watch/watch-debounce/template/digitalExperiences/webApplications/watch-debounce/src/placeholder.tsx +4 -0
  287. package/test/e2e/fixtures/watch/watch-multiple-files/feature.ts +13 -0
  288. package/test/e2e/fixtures/watch/watch-multiple-files/template/digitalExperiences/webApplications/watch-multiple-files/src/components/WatchComponent.tsx +3 -0
  289. package/test/e2e/fixtures/watch/watch-multiple-files/template/digitalExperiences/webApplications/watch-multiple-files/src/placeholder.tsx +4 -0
  290. package/test/e2e/fixtures/watch/watch-multiple-files/template/digitalExperiences/webApplications/watch-multiple-files/src/styles/theme.css +4 -0
  291. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/package.json +10 -0
  292. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/about.tsx +8 -0
  293. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/appLayout.tsx +27 -0
  294. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/contact.tsx +8 -0
  295. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/home.tsx +8 -0
  296. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/index.tsx +10 -0
  297. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/old-page.tsx +8 -0
  298. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/routes.tsx +35 -0
  299. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/styles/global.css +14 -0
  300. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/package.json +10 -0
  301. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/appLayout.tsx +27 -0
  302. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/home.tsx +8 -0
  303. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/index.tsx +10 -0
  304. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/old-page.tsx +8 -0
  305. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/page-a.tsx +8 -0
  306. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/page-b.tsx +8 -0
  307. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/page-c.tsx +8 -0
  308. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/page-d.tsx +8 -0
  309. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/routes.tsx +47 -0
  310. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/styles/global.css +14 -0
  311. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/package.json +10 -0
  312. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/admin-dashboard.tsx +1 -0
  313. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/admin-layout.tsx +1 -0
  314. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/analytics.tsx +1 -0
  315. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/appLayout.tsx +27 -0
  316. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/components.tsx +8 -0
  317. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/dashboard.tsx +8 -0
  318. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/home.tsx +8 -0
  319. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/index.tsx +10 -0
  320. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/login.tsx +8 -0
  321. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/profile-edit.tsx +1 -0
  322. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/profile.tsx +8 -0
  323. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/reports.tsx +1 -0
  324. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/routes.tsx +94 -0
  325. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/signup.tsx +1 -0
  326. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/styles/global.css +14 -0
  327. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/tools.tsx +1 -0
  328. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/types.tsx +8 -0
  329. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/package.json +10 -0
  330. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/appLayout.tsx +27 -0
  331. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/custom-component.tsx +3 -0
  332. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/home.tsx +8 -0
  333. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/index.tsx +10 -0
  334. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/old-page.tsx +8 -0
  335. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/routes.tsx +28 -0
  336. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/styles/global.css +14 -0
  337. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/package.json +10 -0
  338. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/about.tsx +8 -0
  339. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/appLayout.tsx +27 -0
  340. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/home.tsx +8 -0
  341. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/index.tsx +10 -0
  342. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/old-page.tsx +8 -0
  343. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/routes.tsx +29 -0
  344. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/styles/global.css +14 -0
  345. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/package.json +10 -0
  346. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/appLayout.tsx +27 -0
  347. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/home.tsx +8 -0
  348. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/index.tsx +13 -0
  349. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/old-page.tsx +8 -0
  350. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/routes.tsx +29 -0
  351. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/settings.tsx +9 -0
  352. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/styles/global.css +25 -0
  353. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/package.json +10 -0
  354. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/src/appLayout.tsx +27 -0
  355. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/src/home.tsx +8 -0
  356. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/src/index.tsx +10 -0
  357. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/src/routes.tsx +17 -0
  358. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/src/styles/global.css +14 -0
  359. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/assets/icon.svg +3 -0
  360. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/package.json +10 -0
  361. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/appLayout.tsx +27 -0
  362. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/home.tsx +8 -0
  363. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/index.tsx +10 -0
  364. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/mapped-component.tsx +3 -0
  365. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/old-page.tsx +8 -0
  366. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/routes.tsx +28 -0
  367. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/styles/global.css +14 -0
  368. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/package.json +10 -0
  369. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/app.tsx +3 -0
  370. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/appLayout.tsx +27 -0
  371. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/home.tsx +8 -0
  372. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/index.tsx +10 -0
  373. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/old-page.tsx +8 -0
  374. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/routes.tsx +28 -0
  375. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/styles/global.css +14 -0
  376. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/package.json +10 -0
  377. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/appLayout.tsx +27 -0
  378. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/home.tsx +8 -0
  379. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/index.tsx +10 -0
  380. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/legacy-component.tsx +3 -0
  381. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/old-page.tsx +8 -0
  382. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/routes.tsx +28 -0
  383. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/styles/global.css +14 -0
  384. package/test/e2e/path-mappings.spec.ts +232 -0
  385. package/test/e2e/watch-patches.spec.ts +237 -0
  386. package/test/helpers/cli-runner.ts +100 -0
  387. package/test/helpers/compare-directories.ts +197 -0
  388. package/test/helpers/create-temp-dir.ts +34 -0
  389. package/test/helpers/fixtures.ts +67 -0
  390. package/test/setup.ts +6 -0
  391. package/test/unit/debounce.spec.ts +159 -0
  392. package/test/unit/dependency-resolver.spec.ts +260 -0
  393. package/test/unit/file-operations.spec.ts +264 -0
  394. package/test/unit/import-merger.spec.ts +395 -0
  395. package/test/unit/index.spec.ts +158 -0
  396. package/test/unit/new-app.spec.ts +178 -0
  397. package/test/unit/new-feature.spec.ts +178 -0
  398. package/test/unit/package-json-merger.spec.ts +275 -0
  399. package/test/unit/patch-loader.spec.ts +238 -0
  400. package/test/unit/path-mappings.spec.ts +241 -0
  401. package/test/unit/paths.spec.ts +247 -0
  402. package/test/unit/route-merger.spec.ts +265 -0
  403. package/test/unit/validation.spec.ts +311 -0
  404. package/tsconfig.json +20 -0
  405. package/vitest.config.ts +18 -0
@@ -0,0 +1,594 @@
1
+ import { cpSync, existsSync, rmSync, readdirSync, statSync, mkdirSync, unlinkSync } from 'fs';
2
+ import { join, relative, basename, dirname, isAbsolute } from 'path';
3
+ import { tmpdir } from 'os';
4
+ import { randomBytes } from 'crypto';
5
+ import { validateAndResolveFeaturePath, validateAndResolveAppPath, getWebApplicationPath } from '../utils/validation.js';
6
+ import { resolveDependencies, type ResolvedFeature } from '../core/dependency-resolver.js';
7
+ import { applyFile, deleteFile, prependToFile, appendToFile } from '../core/file-operations.js';
8
+ import { mergePackageJson } from '../core/package-json-merger.js';
9
+ import { getMonorepoRoot, translatePathToBaseApp, translatePathToTargetApp } from '../utils/paths.js';
10
+ import { getEffectivePathMappings, applyPathMappings } from '../utils/path-mappings.js';
11
+ import * as logger from '../utils/logger.js';
12
+
13
+ /**
14
+ * Recursively discover all files in a directory
15
+ */
16
+ function discoverFiles(dir: string, baseDir: string = dir): string[] {
17
+ const files: string[] = [];
18
+ const entries = readdirSync(dir);
19
+
20
+ for (const entry of entries) {
21
+ const fullPath = join(dir, entry);
22
+ const stat = statSync(fullPath);
23
+
24
+ if (stat.isDirectory()) {
25
+ files.push(...discoverFiles(fullPath, baseDir));
26
+ } else if (stat.isFile()) {
27
+ // Get relative path from baseDir
28
+ files.push(relative(baseDir, fullPath));
29
+ }
30
+ }
31
+
32
+ return files;
33
+ }
34
+
35
+ const DELETE_PREFIX = '__delete__';
36
+ const INHERIT_PREFIX = '__inherit__';
37
+ const PREPEND_PREFIX = '__prepend__';
38
+ const APPEND_PREFIX = '__append__';
39
+
40
+ /**
41
+ * Check if a path component has the delete prefix and return the actual path
42
+ * Returns null if no delete prefix is found, otherwise returns the actual path
43
+ *
44
+ * Examples:
45
+ * - "src/__delete__routes.tsx" -> "src/routes.tsx"
46
+ * - "src/__delete__pages" -> "src/pages"
47
+ * - "__delete__src/routes.tsx" -> "src/routes.tsx"
48
+ * - "src/routes.tsx" -> null (no prefix)
49
+ */
50
+ function extractDeletePath(relativePath: string): string | null {
51
+ const parts = relativePath.split('/');
52
+ let hasDeletePrefix = false;
53
+
54
+ const transformedParts = parts.map(part => {
55
+ if (part.startsWith(DELETE_PREFIX)) {
56
+ hasDeletePrefix = true;
57
+ return part.substring(DELETE_PREFIX.length);
58
+ }
59
+ return part;
60
+ });
61
+
62
+ return hasDeletePrefix ? transformedParts.join('/') : null;
63
+ }
64
+
65
+ /**
66
+ * Check if a path component has the inherit prefix and return the actual path
67
+ * Returns null if no inherit prefix is found, otherwise returns the actual path
68
+ *
69
+ * Examples:
70
+ * - "src/__inherit__routes.tsx" -> "src/routes.tsx"
71
+ * - "__inherit__src/routes.tsx" -> "src/routes.tsx"
72
+ * - "src/routes.tsx" -> null (no prefix)
73
+ */
74
+ function extractInheritPath(relativePath: string): string | null {
75
+ const parts = relativePath.split('/');
76
+ let hasInheritPrefix = false;
77
+
78
+ const transformedParts = parts.map(part => {
79
+ if (part.startsWith(INHERIT_PREFIX)) {
80
+ hasInheritPrefix = true;
81
+ return part.substring(INHERIT_PREFIX.length);
82
+ }
83
+ return part;
84
+ });
85
+
86
+ return hasInheritPrefix ? transformedParts.join('/') : null;
87
+ }
88
+
89
+ /**
90
+ * Check if a path component has the prepend prefix and return the actual path
91
+ * Returns null if no prepend prefix is found, otherwise returns the actual path
92
+ *
93
+ * Examples:
94
+ * - "src/styles/__prepend__global.css" -> "src/styles/global.css"
95
+ * - "__prepend__file.txt" -> "file.txt"
96
+ * - "src/file.tsx" -> null (no prefix)
97
+ */
98
+ function extractPrependPath(relativePath: string): string | null {
99
+ const parts = relativePath.split('/');
100
+ let hasPrependPrefix = false;
101
+
102
+ const transformedParts = parts.map(part => {
103
+ if (part.startsWith(PREPEND_PREFIX)) {
104
+ hasPrependPrefix = true;
105
+ return part.substring(PREPEND_PREFIX.length);
106
+ }
107
+ return part;
108
+ });
109
+
110
+ return hasPrependPrefix ? transformedParts.join('/') : null;
111
+ }
112
+
113
+ /**
114
+ * Check if a path component has the append prefix and return the actual path
115
+ * Returns null if no append prefix is found, otherwise returns the actual path
116
+ *
117
+ * Examples:
118
+ * - "src/styles/__append__global.css" -> "src/styles/global.css"
119
+ * - "__append__file.txt" -> "file.txt"
120
+ * - "src/file.tsx" -> null (no prefix)
121
+ */
122
+ function extractAppendPath(relativePath: string): string | null {
123
+ const parts = relativePath.split('/');
124
+ let hasAppendPrefix = false;
125
+
126
+ const transformedParts = parts.map(part => {
127
+ if (part.startsWith(APPEND_PREFIX)) {
128
+ hasAppendPrefix = true;
129
+ return part.substring(APPEND_PREFIX.length);
130
+ }
131
+ return part;
132
+ });
133
+
134
+ return hasAppendPrefix ? transformedParts.join('/') : null;
135
+ }
136
+
137
+ /**
138
+ * Apply a single feature's files to the target directory
139
+ * This is the core application logic extracted for reuse
140
+ */
141
+ async function applySingleFeature(
142
+ resolvedFeature: ResolvedFeature,
143
+ baseAppPath: string,
144
+ targetDir: string,
145
+ targetAppName: string
146
+ ): Promise<void> {
147
+ const { featureDir, config, featurePath } = resolvedFeature;
148
+ const { templateDir, routeFilePath, pathMappings } = config;
149
+
150
+ // Extract feature name for path translation
151
+ const featureName = basename(featurePath);
152
+
153
+ // Get effective path mappings for this feature
154
+ const effectiveMappings = getEffectivePathMappings(featureName, pathMappings);
155
+
156
+ // Discover and apply files from feature's template directory
157
+ const templatePath = join(featureDir, templateDir);
158
+
159
+ if (!existsSync(templatePath)) {
160
+ throw new Error(`Template directory '${templateDir}' does not exist in feature path '${featurePath}'`);
161
+ }
162
+
163
+ logger.heading(`\nApplying: ${featurePath}`);
164
+ logger.info('Discovering files...');
165
+ const discoveredFiles = discoverFiles(templatePath);
166
+
167
+ if (discoveredFiles.length === 0) {
168
+ throw new Error(`Feature '${featurePath}' has no files in ${templateDir} directory`);
169
+ }
170
+
171
+ logger.info(`Found ${discoveredFiles.length} file(s)`);
172
+
173
+ // Validate that there are no conflicting paths and that base files exist
174
+ logger.info('Validating paths...');
175
+ const normalizedPaths = new Map<string, { path: string, type: 'normal' | 'delete' | 'inherit' | 'prepend' | 'append' }>();
176
+ const deleteFiles: string[] = [];
177
+ const inheritFiles: string[] = [];
178
+ const prependFiles: string[] = [];
179
+ const appendFiles: string[] = [];
180
+
181
+ for (const relativePath of discoveredFiles) {
182
+ // Apply path mappings to transform template path
183
+ const mappedPath = applyPathMappings(relativePath, effectiveMappings);
184
+
185
+ const deleteTargetPath = extractDeletePath(mappedPath);
186
+ const inheritTargetPath = extractInheritPath(mappedPath);
187
+ const prependTargetPath = extractPrependPath(mappedPath);
188
+ const appendTargetPath = extractAppendPath(mappedPath);
189
+
190
+ let normalizedPath: string;
191
+ let type: 'normal' | 'delete' | 'inherit' | 'prepend' | 'append';
192
+
193
+ if (deleteTargetPath) {
194
+ normalizedPath = deleteTargetPath;
195
+ type = 'delete';
196
+ deleteFiles.push(deleteTargetPath);
197
+ } else if (inheritTargetPath) {
198
+ normalizedPath = inheritTargetPath;
199
+ type = 'inherit';
200
+ inheritFiles.push(inheritTargetPath);
201
+ } else if (prependTargetPath) {
202
+ normalizedPath = prependTargetPath;
203
+ type = 'prepend';
204
+ prependFiles.push(prependTargetPath);
205
+ } else if (appendTargetPath) {
206
+ normalizedPath = appendTargetPath;
207
+ type = 'append';
208
+ appendFiles.push(appendTargetPath);
209
+ } else {
210
+ normalizedPath = mappedPath;
211
+ type = 'normal';
212
+ }
213
+
214
+ // Check for conflicts
215
+ if (normalizedPaths.has(normalizedPath)) {
216
+ const existing = normalizedPaths.get(normalizedPath)!;
217
+ logger.error('\nPath conflict detected!\n');
218
+ logger.info(`The following paths resolve to the same target file:`);
219
+ logger.info(` 1. ${existing.path} (${existing.type})`);
220
+ logger.info(` 2. ${relativePath} (${type})`);
221
+ logger.info(` → Both target: ${normalizedPath}\n`);
222
+ logger.warning('You cannot have multiple files targeting the same path.');
223
+ logger.info('Please remove one of these files from the template directory.\n');
224
+ throw new Error('Path conflict - cannot proceed');
225
+ }
226
+
227
+ normalizedPaths.set(normalizedPath, { path: relativePath, type });
228
+ }
229
+
230
+ // Validate that files marked for deletion exist
231
+ // Note: Files may exist in target (from dependencies) or will be inherited from base app
232
+ for (const deleteFile of deleteFiles) {
233
+ // Translate feature path to target app path
234
+ const targetPath = translatePathToTargetApp(deleteFile, featureName, targetAppName);
235
+ const targetFilePath = join(targetDir, targetPath);
236
+
237
+ // If file doesn't exist in target yet, check if it will be copied from base app during deletion
238
+ if (!existsSync(targetFilePath)) {
239
+ // Check if file exists in base app (will be inherited during delete operation)
240
+ const translatedPath = translatePathToBaseApp(deleteFile, featureName, baseAppPath);
241
+ const baseFilePath = join(baseAppPath, translatedPath);
242
+
243
+ if (!existsSync(baseFilePath)) {
244
+ logger.error(`\nValidation error: Cannot delete file that doesn't exist!\n`);
245
+ logger.info(`File marked for deletion: ${deleteFile}`);
246
+ logger.info(`Not found in target: ${targetFilePath}`);
247
+ logger.info(`Not found in base app: ${baseFilePath}`);
248
+ logger.warning(`The file must exist in target or base app to be deleted.\n`);
249
+ throw new Error('Delete validation failed - cannot proceed');
250
+ }
251
+ }
252
+ }
253
+
254
+ // Validate that files marked for inheritance exist in base app
255
+ for (const inheritFile of inheritFiles) {
256
+
257
+ // Translate feature path to target app path
258
+ const targetPath = translatePathToTargetApp(inheritFile, featureName, targetAppName);
259
+ const targetFilePath = join(targetDir, targetPath);
260
+
261
+ // If file doesn't exist in target yet, check if it will be copied from base app during inheritance
262
+ if (!existsSync(targetFilePath)) {
263
+
264
+ // Translate feature path to base app path (we inherit from base app)
265
+ const translatedPath = translatePathToBaseApp(inheritFile, featureName, baseAppPath);
266
+ const baseFilePath = join(baseAppPath, translatedPath);
267
+ if (!existsSync(baseFilePath)) {
268
+ logger.error(`\nValidation error: Cannot inherit file that doesn't exist!\n`);
269
+ logger.info(`File marked for inheritance: ${inheritFile}`);
270
+ logger.info(`Translated to: ${translatedPath}`);
271
+ logger.info(`Expected location in base app: ${baseFilePath}`);
272
+ logger.warning(`The file doesn't exist in the base app.\n`);
273
+ throw new Error('Inherit validation failed - cannot proceed');
274
+ }
275
+ }
276
+ }
277
+
278
+ // Validate that files marked for prepending exist in base app
279
+ for (const prependFile of prependFiles) {
280
+ // Translate feature path to base app path (we prepend to base app files)
281
+ const translatedPath = translatePathToBaseApp(prependFile, featureName, baseAppPath);
282
+ const baseFilePath = join(baseAppPath, translatedPath);
283
+ if (!existsSync(baseFilePath)) {
284
+ logger.error(`\nValidation error: Cannot prepend to file that doesn't exist!\n`);
285
+ logger.info(`File marked for prepending: ${prependFile}`);
286
+ logger.info(`Translated to: ${translatedPath}`);
287
+ logger.info(`Expected location in base app: ${baseFilePath}`);
288
+ logger.warning(`The file doesn't exist in the base app.\n`);
289
+ throw new Error('Prepend validation failed - cannot proceed');
290
+ }
291
+ }
292
+
293
+ // Validate that files marked for appending exist in base app
294
+ for (const appendFile of appendFiles) {
295
+ // Translate feature path to base app path (we append to base app files)
296
+ const translatedPath = translatePathToBaseApp(appendFile, featureName, baseAppPath);
297
+ const baseFilePath = join(baseAppPath, translatedPath);
298
+ if (!existsSync(baseFilePath)) {
299
+ logger.error(`\nValidation error: Cannot append to file that doesn't exist!\n`);
300
+ logger.info(`File marked for appending: ${appendFile}`);
301
+ logger.info(`Translated to: ${translatedPath}`);
302
+ logger.info(`Expected location in base app: ${baseFilePath}`);
303
+ logger.warning(`The file doesn't exist in the base app.\n`);
304
+ throw new Error('Append validation failed - cannot proceed');
305
+ }
306
+ }
307
+
308
+ logger.success('Paths validated');
309
+
310
+ // Apply each discovered file
311
+ for (const relativePath of discoveredFiles) {
312
+ // Apply path mappings to transform template path
313
+ const mappedPath = applyPathMappings(relativePath, effectiveMappings);
314
+
315
+ // Translate feature path to base app path (for finding base files)
316
+ const translatedPath = translatePathToBaseApp(mappedPath, featureName, baseAppPath);
317
+ // Translate feature path to target app path (for writing files)
318
+ const targetPath = translatePathToTargetApp(mappedPath, featureName, targetAppName);
319
+
320
+ // Check if this is a special operation
321
+ const deleteTargetPath = extractDeletePath(mappedPath);
322
+ const inheritTargetPath = extractInheritPath(mappedPath);
323
+ const prependTargetPath = extractPrependPath(mappedPath);
324
+ const appendTargetPath = extractAppendPath(mappedPath);
325
+
326
+ if (deleteTargetPath) {
327
+ // This is a delete operation - delete the target file/directory
328
+ const targetDeletePath = translatePathToTargetApp(deleteTargetPath, featureName, targetAppName);
329
+ const targetFilePath = join(targetDir, targetDeletePath);
330
+ await deleteFile(targetFilePath, targetDeletePath);
331
+ } else if (inheritTargetPath) {
332
+ // This is an inherit operation - copy from base app if target doesn't have it
333
+ const targetInheritPath = translatePathToTargetApp(inheritTargetPath, featureName, targetAppName);
334
+ const targetFilePath = join(targetDir, targetInheritPath);
335
+ if (!existsSync(targetFilePath)) {
336
+ // Copy from base app to preserve inherited file in new target structure
337
+ const translatedInheritPath = translatePathToBaseApp(inheritTargetPath, featureName, baseAppPath);
338
+ const baseFilePath = join(baseAppPath, translatedInheritPath);
339
+ if (existsSync(baseFilePath)) {
340
+ const targetFileDir = dirname(targetFilePath);
341
+ if (!existsSync(targetFileDir)) {
342
+ mkdirSync(targetFileDir, { recursive: true });
343
+ }
344
+ cpSync(baseFilePath, targetFilePath);
345
+ logger.info(`Copied ${targetInheritPath} (inherited from base app)`);
346
+ } else {
347
+ logger.info(`Skipped ${targetInheritPath} (file not in base app)`);
348
+ }
349
+ } else {
350
+ logger.info(`Skipped ${targetInheritPath} (already exists in target)`);
351
+ }
352
+ } else if (prependTargetPath) {
353
+ // This is a prepend operation - ensure target file exists first
354
+ const featureFilePath = join(templatePath, relativePath);
355
+ const targetPrependPath = translatePathToTargetApp(prependTargetPath, featureName, targetAppName);
356
+ const targetFilePath = join(targetDir, targetPrependPath);
357
+
358
+ // Copy from base app if target doesn't have the file yet
359
+ if (!existsSync(targetFilePath)) {
360
+ const translatedPrependPath = translatePathToBaseApp(prependTargetPath, featureName, baseAppPath);
361
+ const baseFilePath = join(baseAppPath, translatedPrependPath);
362
+ if (existsSync(baseFilePath)) {
363
+ const targetFileDir = dirname(targetFilePath);
364
+ if (!existsSync(targetFileDir)) {
365
+ mkdirSync(targetFileDir, { recursive: true });
366
+ }
367
+ cpSync(baseFilePath, targetFilePath);
368
+ }
369
+ }
370
+
371
+ await prependToFile(featureFilePath, targetFilePath, targetPrependPath);
372
+ } else if (appendTargetPath) {
373
+ // This is an append operation - ensure target file exists first
374
+ const featureFilePath = join(templatePath, relativePath);
375
+ const targetAppendPath = translatePathToTargetApp(appendTargetPath, featureName, targetAppName);
376
+ const targetFilePath = join(targetDir, targetAppendPath);
377
+
378
+ // Copy from base app if target doesn't have the file yet
379
+ if (!existsSync(targetFilePath)) {
380
+ const translatedAppendPath = translatePathToBaseApp(appendTargetPath, featureName, baseAppPath);
381
+ const baseFilePath = join(baseAppPath, translatedAppendPath);
382
+ if (existsSync(baseFilePath)) {
383
+ const targetFileDir = dirname(targetFilePath);
384
+ if (!existsSync(targetFileDir)) {
385
+ mkdirSync(targetFileDir, { recursive: true });
386
+ }
387
+ cpSync(baseFilePath, targetFilePath);
388
+ }
389
+ }
390
+
391
+ await appendToFile(featureFilePath, targetFilePath, targetAppendPath);
392
+ } else {
393
+ // Normal file operation - add/update/merge
394
+ const patchFilePath = join(templatePath, relativePath);
395
+ // Use target app path to ensure all features write to same location
396
+ const targetFilePath = join(targetDir, targetPath);
397
+
398
+ // Determine if this should be merged (only for the routes file)
399
+ // The routeFilePath from config includes the full path from template root
400
+ const shouldMerge = mappedPath === routeFilePath;
401
+
402
+ // For route merging with dependencies:
403
+ // - Use target file as base if it exists (accumulates routes from previous features)
404
+ // - Otherwise use base app file (first feature merges with base app)
405
+ // Create a temp copy of target to avoid ts-morph issues with same file as base and target
406
+ let baseFilePath = join(baseAppPath, translatedPath);
407
+ let tempFilePath: string | undefined;
408
+
409
+ if (shouldMerge && existsSync(targetFilePath)) {
410
+ // Create temp copy of target to use as base
411
+ tempFilePath = join(tmpdir(), `route-merge-${randomBytes(8).toString('hex')}.tsx`);
412
+ cpSync(targetFilePath, tempFilePath);
413
+ baseFilePath = tempFilePath;
414
+ }
415
+
416
+ try {
417
+ await applyFile(patchFilePath, baseFilePath, targetFilePath, targetPath, shouldMerge);
418
+ } finally {
419
+ // Clean up temp file
420
+ if (tempFilePath && existsSync(tempFilePath)) {
421
+ unlinkSync(tempFilePath);
422
+ }
423
+ }
424
+ }
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Apply feature patches to create a new app
430
+ *
431
+ * @param featurePath - Path to the feature directory containing feature.ts
432
+ * @param appPath - Path to the base app directory (used as reference, remains unchanged)
433
+ * @param options - Configuration options
434
+ * @param options.targetDirName - Required. Target directory where feature will be applied
435
+ * @param options.skipDependencyChanges - Optional. Skip npm dependency installation
436
+ * @param options.clean - Optional. Clean target directory before applying
437
+ *
438
+ * @example
439
+ * await applyPatchesCommand(
440
+ * 'packages/feature-navigation-menu',
441
+ * 'packages/base-react-app',
442
+ * { targetDirName: 'my-app' }
443
+ * );
444
+ */
445
+ export async function applyPatchesCommand(
446
+ featurePath: string,
447
+ appPath: string,
448
+ options: { targetDirName: string; skipDependencyChanges?: boolean; clean?: boolean; }
449
+ ): Promise<void> {
450
+ try {
451
+ logger.heading(`Applying patches: ${featurePath} → ${options.targetDirName}`);
452
+
453
+ // Step 1: Validate inputs
454
+ logger.info('Validating paths...');
455
+ validateAndResolveFeaturePath(featurePath); // Fast-fail validation
456
+ const baseAppPath = validateAndResolveAppPath(appPath);
457
+ logger.success('Validation passed');
458
+
459
+ // Step 2: Prepare target directory
460
+ // Extract target app name from the main feature path for nested structure
461
+ const mainFeatureName = basename(featurePath);
462
+ const targetAppName: string = mainFeatureName;
463
+
464
+ // Resolve target directory - if absolute, use as-is; otherwise resolve relative to monorepo root
465
+ const resolvedTargetDir = isAbsolute(options.targetDirName)
466
+ ? options.targetDirName
467
+ : join(getMonorepoRoot(), options.targetDirName);
468
+
469
+ if (existsSync(resolvedTargetDir)) {
470
+ if (options.clean) {
471
+ logger.info(`Cleaning existing directory ${options.targetDirName}...`);
472
+ rmSync(resolvedTargetDir, { recursive: true, force: true });
473
+ logger.success('Directory cleaned');
474
+ }
475
+ } else {
476
+ logger.info(`Creating target directory ${options.targetDirName}...`);
477
+ mkdirSync(resolvedTargetDir, { recursive: true });
478
+ logger.success('Target directory created');
479
+ }
480
+
481
+ // Copy base app to target directory with nested structure
482
+ logger.info(`Copying base app to ${options.targetDirName}...`);
483
+ const targetAppDir = join(resolvedTargetDir, 'digitalExperiences/webApplications', targetAppName);
484
+ const sourceWebAppPath = getWebApplicationPath(baseAppPath);
485
+ mkdirSync(dirname(targetAppDir), { recursive: true });
486
+ cpSync(sourceWebAppPath, targetAppDir, { recursive: true });
487
+ logger.success('Base app copied');
488
+
489
+ const targetDir = resolvedTargetDir;
490
+
491
+ // Step 3: Resolve dependencies
492
+ logger.heading('\nResolving Dependencies');
493
+ const { orderedFeatures } = await resolveDependencies(featurePath);
494
+
495
+ if (orderedFeatures.length > 1) {
496
+ logger.info(`Resolved dependency chain (${orderedFeatures.length} features):`);
497
+ for (let i = 0; i < orderedFeatures.length; i++) {
498
+ const { featurePath: path } = orderedFeatures[i];
499
+ const isMainFeature = i === orderedFeatures.length - 1;
500
+ const prefix = isMainFeature ? '└─' : '├─';
501
+ const label = isMainFeature ? ' (main)' : ' (dependency)';
502
+ logger.info(` ${prefix} ${path}${label}`);
503
+ }
504
+ logger.success('Dependency resolution complete');
505
+ } else {
506
+ logger.info('No dependencies found');
507
+ }
508
+
509
+ // Step 4: Apply all features in dependency order
510
+ for (const resolvedFeature of orderedFeatures) {
511
+ await applySingleFeature(resolvedFeature, baseAppPath, targetDir, targetAppName);
512
+ }
513
+
514
+ // Step 5: Aggregate and install all dependencies once
515
+ if (options.skipDependencyChanges) {
516
+ logger.info('\n⏭ Skipping dependency installation');
517
+ } else {
518
+ // Aggregate all dependencies from all resolved features, checking for conflicts
519
+ const aggregatedDependencies: Record<string, string> = {};
520
+ const aggregatedDevDependencies: Record<string, string> = {};
521
+
522
+ // Collect all features from all resolved features
523
+ const allFeatures = orderedFeatures.flatMap(rf => rf.config.features);
524
+
525
+ // First pass: collect all regular dependencies
526
+ for (const feature of allFeatures) {
527
+ if (feature.packageJson?.dependencies) {
528
+ for (const [name, version] of Object.entries(feature.packageJson.dependencies)) {
529
+ if (aggregatedDependencies[name] && aggregatedDependencies[name] !== version) {
530
+ throw new Error(
531
+ `Dependency version conflict for '${name}': feature requires '${version}' but another feature requires '${aggregatedDependencies[name]}'`
532
+ );
533
+ }
534
+ aggregatedDependencies[name] = version;
535
+ }
536
+ }
537
+ }
538
+
539
+ // Second pass: collect dev dependencies (skip if already in dependencies)
540
+ for (const feature of allFeatures) {
541
+ if (feature.packageJson?.devDependencies) {
542
+ for (const [name, version] of Object.entries(feature.packageJson.devDependencies)) {
543
+ // Skip if already in regular dependencies
544
+ if (aggregatedDependencies[name]) {
545
+ continue;
546
+ }
547
+
548
+ if (aggregatedDevDependencies[name] && aggregatedDevDependencies[name] !== version) {
549
+ throw new Error(
550
+ `Dev dependency version conflict for '${name}': feature requires '${version}' but another feature requires '${aggregatedDevDependencies[name]}'`
551
+ );
552
+ }
553
+ aggregatedDevDependencies[name] = version;
554
+ }
555
+ }
556
+ }
557
+
558
+ const hasAnyDependencies =
559
+ Object.keys(aggregatedDependencies).length > 0 ||
560
+ Object.keys(aggregatedDevDependencies).length > 0;
561
+
562
+ if (hasAnyDependencies) {
563
+ logger.heading('\nInstalling Dependencies');
564
+ await mergePackageJson(targetDir, {
565
+ dependencies: Object.keys(aggregatedDependencies).length > 0 ? aggregatedDependencies : undefined,
566
+ devDependencies: Object.keys(aggregatedDevDependencies).length > 0 ? aggregatedDevDependencies : undefined
567
+ }, targetAppDir);
568
+ } else {
569
+ logger.info('\n✓ No dependencies to install');
570
+ }
571
+ }
572
+
573
+ // Step 6: Success message
574
+ logger.heading('\n✓ Success');
575
+ logger.success(`Created: ${targetDir}`);
576
+
577
+ } catch (err) {
578
+ // Only log error if it's not already been logged (check for our custom error messages)
579
+ const errorMessage = err instanceof Error ? err.message : String(err);
580
+ const skipLogging = [
581
+ 'Path conflict - cannot proceed',
582
+ 'Delete validation failed - cannot proceed',
583
+ 'Inherit validation failed - cannot proceed',
584
+ 'Prepend validation failed - cannot proceed',
585
+ 'Append validation failed - cannot proceed',
586
+ 'Route deletion failed - cannot proceed'
587
+ ].includes(errorMessage);
588
+
589
+ if (!skipLogging) {
590
+ logger.error(`Failed to apply feature: ${errorMessage}`);
591
+ }
592
+ throw err;
593
+ }
594
+ }