@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,861 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { join } from 'path';
3
+ import { existsSync } from 'fs';
4
+ import { createTempDir, cleanupTempDir } from '../helpers/create-temp-dir.js';
5
+ import { copyFixture, getFixturePath, getGoldPath } from '../helpers/fixtures.js';
6
+ import { compareOrUpdate } from '../helpers/compare-directories.js';
7
+ import { runApplyPatches } from '../helpers/cli-runner.js';
8
+
9
+ describe('apply-patches E2E', () => {
10
+ let tempDir: string;
11
+
12
+ beforeEach(() => {
13
+ tempDir = createTempDir();
14
+ });
15
+
16
+ afterEach(() => {
17
+ cleanupTempDir(tempDir);
18
+ });
19
+
20
+ describe('Simple feature application', () => {
21
+ it('should apply a simple feature correctly', async () => {
22
+ // Setup: Copy base-app
23
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
24
+ const targetDir = join(tempDir, 'simple-apply');
25
+ const featurePath = getFixturePath('basic-operations/feature-file-inherit-route-add');
26
+
27
+ // Execute: Apply patches via CLI
28
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
29
+ skipDependencyChanges: true
30
+ });
31
+
32
+ // Assert: Command succeeded
33
+ expect(result.exitCode).toBe(0);
34
+
35
+ // Assert: Compare with gold files
36
+ const goldDir = getGoldPath('feature-file-inherit-route-add-apply');
37
+ const differences = compareOrUpdate(targetDir, goldDir);
38
+
39
+ expect(differences).toEqual([]);
40
+ });
41
+
42
+ it('should verify all expected files exist after simple apply', async () => {
43
+ // Setup
44
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
45
+ const targetDir = join(tempDir, 'simple-apply-verify');
46
+ const featurePath = getFixturePath('basic-operations/feature-file-inherit-route-add');
47
+
48
+ // Execute
49
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
50
+ skipDependencyChanges: true
51
+ });
52
+ expect(result.exitCode).toBe(0);
53
+
54
+ // Assert: Check that expected files exist (in feature-file-inherit-route-add nested structure)
55
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-inherit-route-add/src/about.tsx'))).toBe(true);
56
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-inherit-route-add/src/routes.tsx'))).toBe(true);
57
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-inherit-route-add/src/appLayout.tsx'))).toBe(true);
58
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-inherit-route-add/src/home.tsx'))).toBe(true);
59
+ });
60
+ });
61
+
62
+ describe('File deletion', () => {
63
+ it('should delete files marked with __delete__', async () => {
64
+ // Setup: Copy base-app
65
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
66
+ const targetDir = join(tempDir, 'delete-apply');
67
+ const featurePath = getFixturePath('basic-operations/feature-file-route-delete');
68
+
69
+ // Verify file exists before deletion (in base-app structure)
70
+ expect(existsSync(join(baseAppDir, 'digitalExperiences/webApplications/base-app/src/old-page.tsx'))).toBe(true);
71
+
72
+ // Execute
73
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
74
+ skipDependencyChanges: true
75
+ });
76
+ expect(result.exitCode).toBe(0);
77
+
78
+ // Assert: File should be deleted (feature-file-route-delete becomes the target app name)
79
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-route-delete/src/old-page.tsx'))).toBe(false);
80
+
81
+ // Compare with gold files
82
+ const goldDir = getGoldPath('feature-file-route-delete-apply');
83
+ const differences = compareOrUpdate(targetDir, goldDir);
84
+ expect(differences).toEqual([]);
85
+ });
86
+
87
+ it('should clean up imports from deleted files', async () => {
88
+ // Setup
89
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
90
+ const targetDir = join(tempDir, 'delete-apply-imports');
91
+ const featurePath = getFixturePath('basic-operations/feature-file-route-delete');
92
+
93
+ // Execute
94
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
95
+ skipDependencyChanges: true
96
+ });
97
+ expect(result.exitCode).toBe(0);
98
+
99
+ // Assert: Check routes file doesn't have old-page import
100
+ const { readFileSync } = await import('fs');
101
+ const routesContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-route-delete/src/routes.tsx'), 'utf-8');
102
+
103
+ // Should not contain import or reference to old-page
104
+ expect(routesContent).not.toContain('old-page');
105
+ expect(routesContent).not.toContain('OldPage');
106
+ });
107
+ });
108
+
109
+ describe('Route deletion', () => {
110
+ it('should delete routes marked with __delete__ in path', async () => {
111
+ // Setup
112
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
113
+ const targetDir = join(tempDir, 'route-delete-apply');
114
+ const featurePath = getFixturePath('basic-operations/feature-file-route-delete');
115
+
116
+ // Execute
117
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
118
+ skipDependencyChanges: true
119
+ });
120
+ expect(result.exitCode).toBe(0);
121
+
122
+ // Assert: Check routes file doesn't have the old route
123
+ const { readFileSync } = await import('fs');
124
+ const routesContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-route-delete/src/routes.tsx'), 'utf-8');
125
+
126
+ // Should not contain path: 'old'
127
+ expect(routesContent).not.toMatch(/path:\s*['"]old['"]/);
128
+ });
129
+ });
130
+
131
+ describe('Dependency resolution', () => {
132
+ it('should apply features in dependency order', async () => {
133
+ // Setup: dep-chain-linear depends on feature-file-inherit-route-add
134
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
135
+ const targetDir = join(tempDir, 'deps-apply');
136
+ const featurePath = getFixturePath('basic-operations/dep-chain-linear');
137
+
138
+ // Execute
139
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
140
+ skipDependencyChanges: true
141
+ });
142
+ expect(result.exitCode).toBe(0);
143
+
144
+ // Assert: Both features should be applied
145
+ // From feature-file-inherit-route-add (dependency) and dep-chain-linear (main) - both write to dep-chain-linear path
146
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-chain-linear/src/about.tsx'))).toBe(true);
147
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-chain-linear/src/contact.tsx'))).toBe(true);
148
+
149
+ // Routes should have both /about and /contact
150
+ const { readFileSync } = await import('fs');
151
+ const routesContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/dep-chain-linear/src/routes.tsx'), 'utf-8');
152
+ expect(routesContent).toContain('about');
153
+ expect(routesContent).toContain('contact');
154
+
155
+ // Compare with gold files
156
+ const goldDir = getGoldPath('dep-chain-linear-apply');
157
+ const differences = compareOrUpdate(targetDir, goldDir);
158
+ expect(differences).toEqual([]);
159
+ });
160
+ });
161
+
162
+ describe('Complex operations', () => {
163
+ it('should handle __inherit__, __prepend__, __append__', async () => {
164
+ // Setup
165
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
166
+ const targetDir = join(tempDir, 'complex-apply');
167
+ const featurePath = getFixturePath('basic-operations/feature-file-prepend-append');
168
+
169
+ // Execute
170
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
171
+ skipDependencyChanges: true
172
+ });
173
+ expect(result.exitCode).toBe(0);
174
+
175
+ // Assert: Check files exist (in feature-file-prepend-append path)
176
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-prepend-append/src/settings.tsx'))).toBe(true);
177
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-prepend-append/src/appLayout.tsx'))).toBe(true);
178
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-prepend-append/src/index.tsx'))).toBe(true);
179
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-prepend-append/src/styles/global.css'))).toBe(true);
180
+
181
+ // Check prepend operation
182
+ const { readFileSync } = await import('fs');
183
+ const indexContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-prepend-append/src/index.tsx'), 'utf-8');
184
+ expect(indexContent).toContain('Feature complex initialized');
185
+
186
+ // Check append operation
187
+ const cssContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-prepend-append/src/styles/global.css'), 'utf-8');
188
+ expect(cssContent).toContain('--feature-color');
189
+ expect(cssContent).toContain('.feature-specific');
190
+
191
+ // Compare with gold files
192
+ const goldDir = getGoldPath('feature-file-prepend-append-apply');
193
+ const differences = compareOrUpdate(targetDir, goldDir);
194
+ expect(differences).toEqual([]);
195
+ });
196
+
197
+ it('should verify appLayout is inherited correctly', async () => {
198
+ // Setup
199
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
200
+ const targetDir = join(tempDir, 'complex-apply-verify');
201
+ const featurePath = getFixturePath('basic-operations/feature-file-prepend-append');
202
+
203
+ // Execute
204
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
205
+ skipDependencyChanges: true
206
+ });
207
+ expect(result.exitCode).toBe(0);
208
+
209
+ // Assert: appLayout should exist (inherited from base)
210
+ const { readFileSync } = await import('fs');
211
+ const appLayoutContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-prepend-append/src/appLayout.tsx'), 'utf-8');
212
+
213
+ // Should still have the base navigation structure
214
+ expect(appLayoutContent).toContain('routes[0].children?.map');
215
+ expect(appLayoutContent).toContain('Outlet');
216
+ });
217
+ });
218
+
219
+ describe('Error handling', () => {
220
+ it('should fail gracefully when feature path does not exist', async () => {
221
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
222
+ const targetDir = join(tempDir, 'error-test');
223
+ const featurePath = getFixturePath('non-existent-feature');
224
+
225
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
226
+ skipDependencyChanges: true
227
+ });
228
+
229
+ expect(result.exitCode).not.toBe(0);
230
+ });
231
+
232
+ it('should fail when trying to delete non-existent file', async () => {
233
+ // Setup: Create a custom feature that tries to delete a file that doesn't exist
234
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
235
+ const targetDir = join(tempDir, 'delete-error-test');
236
+
237
+ // Create a feature that tries to delete a non-existent file
238
+ const { mkdirSync, writeFileSync } = await import('fs');
239
+ const customFeature = join(tempDir, 'custom-feature');
240
+ mkdirSync(join(customFeature, 'template/digitalExperiences/webApplications/custom-feature/src'), { recursive: true });
241
+
242
+ // Write feature.ts
243
+ writeFileSync(
244
+ join(customFeature, 'feature.ts'),
245
+ `export default { templateDir: 'template' };`
246
+ );
247
+
248
+ // Write a delete marker for non-existent file
249
+ writeFileSync(
250
+ join(customFeature, 'template/digitalExperiences/webApplications/custom-feature/src/__delete__non-existent.tsx'),
251
+ '// This file does not exist in base app'
252
+ );
253
+
254
+ const result = await runApplyPatches(customFeature, baseAppDir, targetDir, {
255
+ skipDependencyChanges: true
256
+ });
257
+
258
+ expect(result.exitCode).not.toBe(0);
259
+ expect(result.stderr).toMatch(/Cannot delete file that doesn't exist/);
260
+ });
261
+ });
262
+
263
+ describe('Idempotency', () => {
264
+ it('should handle applying patches multiple times idempotently', async () => {
265
+ // Setup: Copy base-app
266
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
267
+ const targetDir = join(tempDir, 'idempotent-test');
268
+ const featurePath = getFixturePath('basic-operations/feature-file-inherit-route-add');
269
+
270
+ // First application
271
+ const result1 = await runApplyPatches(featurePath, baseAppDir, targetDir, {
272
+ skipDependencyChanges: true
273
+ });
274
+ expect(result1.exitCode).toBe(0);
275
+
276
+ // Verify it worked
277
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-inherit-route-add/src/about.tsx'))).toBe(true);
278
+
279
+ // Apply again (should be idempotent - not fail or corrupt files)
280
+ const result2 = await runApplyPatches(featurePath, baseAppDir, targetDir, {
281
+ skipDependencyChanges: true
282
+ });
283
+ expect(result2.exitCode).toBe(0);
284
+
285
+ // Should still work correctly
286
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-inherit-route-add/src/about.tsx'))).toBe(true);
287
+ const { readFileSync } = await import('fs');
288
+ const routesContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-inherit-route-add/src/routes.tsx'), 'utf-8');
289
+ expect(routesContent).toContain('about');
290
+ });
291
+ });
292
+
293
+ describe('Long dependency chains', () => {
294
+ it('should apply features in a long chain (A → B → C → D)', async () => {
295
+ // Setup: dep-chain-a depends on B, which depends on C, which depends on D
296
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
297
+ const targetDir = join(tempDir, 'dep-chain-long-apply');
298
+ const featurePath = getFixturePath('dep-chain-long/dep-chain-a');
299
+
300
+ // Execute
301
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
302
+ skipDependencyChanges: true
303
+ });
304
+ expect(result.exitCode).toBe(0);
305
+
306
+ // Assert: All 4 features should be applied
307
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-chain-a/src/page-a.tsx'))).toBe(true);
308
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-chain-a/src/page-b.tsx'))).toBe(true);
309
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-chain-a/src/page-c.tsx'))).toBe(true);
310
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-chain-a/src/page-d.tsx'))).toBe(true);
311
+
312
+ // Routes should have all 4 pages
313
+ const { readFileSync } = await import('fs');
314
+ const routesContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/dep-chain-a/src/routes.tsx'), 'utf-8');
315
+ expect(routesContent).toContain('page-a');
316
+ expect(routesContent).toContain('page-b');
317
+ expect(routesContent).toContain('page-c');
318
+ expect(routesContent).toContain('page-d');
319
+
320
+ // Compare with gold files
321
+ const goldDir = getGoldPath('dep-chain-long-apply');
322
+ const differences = compareOrUpdate(targetDir, goldDir);
323
+ expect(differences).toEqual([]);
324
+ });
325
+ });
326
+
327
+ describe('Diamond dependencies', () => {
328
+ it('should apply shared dependency only once in diamond pattern', async () => {
329
+ // Setup: Both left and right depend on shared
330
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
331
+ const targetDirLeft = join(tempDir, 'dep-diamond-left-apply');
332
+ const targetDirRight = join(tempDir, 'dep-diamond-right-apply');
333
+
334
+ // Apply left feature (depends on shared)
335
+ const leftPath = getFixturePath('dep-diamond/dep-diamond-left');
336
+ const resultLeft = await runApplyPatches(leftPath, baseAppDir, targetDirLeft, {
337
+ skipDependencyChanges: true
338
+ });
339
+ expect(resultLeft.exitCode).toBe(0);
340
+
341
+ // Apply right feature (also depends on shared)
342
+ const rightPath = getFixturePath('dep-diamond/dep-diamond-right');
343
+ const resultRight = await runApplyPatches(rightPath, baseAppDir, targetDirRight, {
344
+ skipDependencyChanges: true
345
+ });
346
+ expect(resultRight.exitCode).toBe(0);
347
+
348
+ // Assert: Left should have shared and left-page
349
+ expect(existsSync(join(targetDirLeft, 'digitalExperiences/webApplications/dep-diamond-left/src/shared-utils.tsx'))).toBe(true);
350
+ expect(existsSync(join(targetDirLeft, 'digitalExperiences/webApplications/dep-diamond-left/src/left-page.tsx'))).toBe(true);
351
+
352
+ // Right should have shared and right-page
353
+ expect(existsSync(join(targetDirRight, 'digitalExperiences/webApplications/dep-diamond-right/src/shared-utils.tsx'))).toBe(true);
354
+ expect(existsSync(join(targetDirRight, 'digitalExperiences/webApplications/dep-diamond-right/src/right-page.tsx'))).toBe(true);
355
+
356
+ // Routes should have shared content in both
357
+ const { readFileSync } = await import('fs');
358
+ const routesLeft = readFileSync(join(targetDirLeft, 'digitalExperiences/webApplications/dep-diamond-left/src/routes.tsx'), 'utf-8');
359
+ const routesRight = readFileSync(join(targetDirRight, 'digitalExperiences/webApplications/dep-diamond-right/src/routes.tsx'), 'utf-8');
360
+
361
+ expect(routesLeft).toContain('shared');
362
+ expect(routesLeft).toContain('left');
363
+ expect(routesRight).toContain('shared');
364
+ expect(routesRight).toContain('right');
365
+ });
366
+ });
367
+
368
+ describe('Circular dependencies', () => {
369
+ it('should detect and reject circular dependencies', async () => {
370
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
371
+ const targetDir = join(tempDir, 'circular-error-test');
372
+ const featurePath = getFixturePath('dep-circular/dep-circular-a');
373
+
374
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
375
+ skipDependencyChanges: true
376
+ });
377
+
378
+ expect(result.exitCode).not.toBe(0);
379
+ expect(result.stderr).toMatch(/circular/i);
380
+ });
381
+ });
382
+
383
+ describe('Complex dependency graph', () => {
384
+ it('should resolve complex multi-branch dependencies correctly', async () => {
385
+ // Setup: Apply all 3 main features (auth, dashboard, profile)
386
+ // Each has different combinations of shared dependencies
387
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
388
+ const targetDir = join(tempDir, 'dep-complex-graph-apply');
389
+
390
+ // Apply the parent dep-complex-graph feature which depends on all 3 main features
391
+ const complexGraphPath = getFixturePath('dep-complex-graph');
392
+ const result = await runApplyPatches(complexGraphPath, baseAppDir, targetDir, {
393
+ skipDependencyChanges: true
394
+ });
395
+ expect(result.exitCode).toBe(0);
396
+
397
+ // Assert: All features and shared dependencies should be present
398
+ // Shared dependencies
399
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/tools.tsx'))).toBe(true);
400
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/types.tsx'))).toBe(true);
401
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/components.tsx'))).toBe(true);
402
+
403
+ // Main features with enhanced route manipulation
404
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/login.tsx'))).toBe(true);
405
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/signup.tsx'))).toBe(true);
406
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/dashboard.tsx'))).toBe(true);
407
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/analytics.tsx'))).toBe(true);
408
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/reports.tsx'))).toBe(true);
409
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/profile.tsx'))).toBe(true);
410
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/profile-edit.tsx'))).toBe(true);
411
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/admin-layout.tsx'))).toBe(true);
412
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/admin-dashboard.tsx'))).toBe(true);
413
+
414
+ // Verify old route was deleted
415
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/old-page.tsx'))).toBe(false);
416
+
417
+ // Routes should have all new routes
418
+ const { readFileSync } = await import('fs');
419
+ const routesContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/dep-complex-graph/src/routes.tsx'), 'utf-8');
420
+ expect(routesContent).toContain('tools');
421
+ expect(routesContent).toContain('components');
422
+ expect(routesContent).toContain('login');
423
+ expect(routesContent).toContain('signup');
424
+ expect(routesContent).toContain('dashboard');
425
+ expect(routesContent).toContain('analytics');
426
+ expect(routesContent).toContain('reports');
427
+ expect(routesContent).toContain('profile/:id');
428
+ expect(routesContent).toContain('edit');
429
+ expect(routesContent).toContain('admin');
430
+ expect(routesContent).not.toContain('old');
431
+
432
+ // Compare with gold files
433
+ const goldDir = getGoldPath('dep-complex-graph-apply');
434
+ const differences = compareOrUpdate(targetDir, goldDir);
435
+ expect(differences).toEqual([]);
436
+ });
437
+ });
438
+
439
+ describe('Phase 2: Single feature tests', () => {
440
+ it('should apply prepend-only feature correctly', async () => {
441
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
442
+ const targetDir = join(tempDir, 'prepend-only-apply');
443
+ const featurePath = getFixturePath('single-operations/feature-file-prepend');
444
+
445
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
446
+ skipDependencyChanges: true
447
+ });
448
+ expect(result.exitCode).toBe(0);
449
+
450
+ const { readFileSync } = await import('fs');
451
+ const indexContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-prepend/src/index.tsx'), 'utf-8');
452
+ expect(indexContent).toContain('Feature prepend initialized');
453
+ });
454
+
455
+ it('should apply append-only feature correctly', async () => {
456
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
457
+ const targetDir = join(tempDir, 'append-only-apply');
458
+ const featurePath = getFixturePath('single-operations/feature-file-append');
459
+
460
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
461
+ skipDependencyChanges: true
462
+ });
463
+ expect(result.exitCode).toBe(0);
464
+
465
+ const { readFileSync } = await import('fs');
466
+ const cssContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-append/src/styles/global.css'), 'utf-8');
467
+ expect(cssContent).toContain('feature-append-specific');
468
+ });
469
+
470
+ it('should apply route-add feature correctly', async () => {
471
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
472
+ const targetDir = join(tempDir, 'route-add-apply');
473
+ const featurePath = getFixturePath('single-operations/feature-route-add');
474
+
475
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
476
+ skipDependencyChanges: true
477
+ });
478
+ expect(result.exitCode).toBe(0);
479
+
480
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-route-add/src/services.tsx'))).toBe(true);
481
+
482
+ const { readFileSync } = await import('fs');
483
+ const routesContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/feature-route-add/src/routes.tsx'), 'utf-8');
484
+ expect(routesContent).toContain('services');
485
+ });
486
+
487
+ it('should apply inherit-only feature correctly', async () => {
488
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
489
+ const targetDir = join(tempDir, 'inherit-only-apply');
490
+ const featurePath = getFixturePath('single-operations/feature-file-inherit');
491
+
492
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
493
+ skipDependencyChanges: true
494
+ });
495
+ expect(result.exitCode).toBe(0);
496
+
497
+ // Verify inherited files exist
498
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-inherit/src/appLayout.tsx'))).toBe(true);
499
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/feature-file-inherit/src/home.tsx'))).toBe(true);
500
+ });
501
+
502
+ it('should apply import-merge feature correctly', async () => {
503
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
504
+ const targetDir = join(tempDir, 'import-merge-apply');
505
+ const featurePath = getFixturePath('single-operations/feature-import-merge');
506
+
507
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
508
+ skipDependencyChanges: true
509
+ });
510
+ expect(result.exitCode).toBe(0);
511
+
512
+ // Verify route file has merged imports
513
+ const { readFileSync } = await import('fs');
514
+ const routesContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/feature-import-merge/src/routes.tsx'), 'utf-8');
515
+ expect(routesContent).toContain('settings');
516
+ });
517
+ });
518
+
519
+ describe('Phase 4: Composition tests', () => {
520
+ it('should handle multiple features appending to same file', async () => {
521
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
522
+ const targetDir1 = join(tempDir, 'append1');
523
+ const targetDir2 = join(tempDir, 'append2');
524
+ const targetDir3 = join(tempDir, 'append3');
525
+
526
+ // Apply three append features sequentially
527
+ const result1 = await runApplyPatches(getFixturePath('composition-append/composition-append-1'), baseAppDir, targetDir1, {
528
+ skipDependencyChanges: true
529
+ });
530
+ expect(result1.exitCode).toBe(0);
531
+
532
+ const result2 = await runApplyPatches(getFixturePath('composition-append/composition-append-2'), baseAppDir, targetDir2, {
533
+ skipDependencyChanges: true
534
+ });
535
+ expect(result2.exitCode).toBe(0);
536
+
537
+ const result3 = await runApplyPatches(getFixturePath('composition-append/composition-append-3'), baseAppDir, targetDir3, {
538
+ skipDependencyChanges: true
539
+ });
540
+ expect(result3.exitCode).toBe(0);
541
+
542
+ // Verify each append was applied
543
+ const { readFileSync } = await import('fs');
544
+ const css1 = readFileSync(join(targetDir1, 'digitalExperiences/webApplications/composition-append-1/src/styles/global.css'), 'utf-8');
545
+ const css2 = readFileSync(join(targetDir2, 'digitalExperiences/webApplications/composition-append-2/src/styles/global.css'), 'utf-8');
546
+ const css3 = readFileSync(join(targetDir3, 'digitalExperiences/webApplications/composition-append-3/src/styles/global.css'), 'utf-8');
547
+
548
+ expect(css1).toContain('composition-append-1');
549
+ expect(css2).toContain('composition-append-2');
550
+ expect(css3).toContain('composition-append-3');
551
+ });
552
+
553
+ it('should handle multiple features prepending to same file', async () => {
554
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
555
+ const targetDir1 = join(tempDir, 'prepend1');
556
+
557
+ const result = await runApplyPatches(getFixturePath('composition-prepend/composition-prepend-1'), baseAppDir, targetDir1, {
558
+ skipDependencyChanges: true
559
+ });
560
+ expect(result.exitCode).toBe(0);
561
+
562
+ const { readFileSync } = await import('fs');
563
+ const indexContent = readFileSync(join(targetDir1, 'digitalExperiences/webApplications/composition-prepend-1/src/index.tsx'), 'utf-8');
564
+ expect(indexContent).toContain('composition-prepend-1');
565
+ });
566
+
567
+ it('should handle five features applied together', async () => {
568
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
569
+ const targetDir = join(tempDir, '5-features-apply');
570
+
571
+ // Apply all 5 features to test integration
572
+ const result1 = await runApplyPatches(getFixturePath('composition-5-features/5-features-navigation'), baseAppDir, targetDir, {
573
+ skipDependencyChanges: true
574
+ });
575
+ expect(result1.exitCode).toBe(0);
576
+
577
+ const result2 = await runApplyPatches(getFixturePath('composition-5-features/5-features-footer'), baseAppDir, targetDir, {
578
+ skipDependencyChanges: true
579
+ });
580
+ expect(result2.exitCode).toBe(0);
581
+
582
+ const result3 = await runApplyPatches(getFixturePath('composition-5-features/5-features-theme'), baseAppDir, targetDir, {
583
+ skipDependencyChanges: true
584
+ });
585
+ expect(result3.exitCode).toBe(0);
586
+
587
+ const result4 = await runApplyPatches(getFixturePath('composition-5-features/5-features-analytics'), baseAppDir, targetDir, {
588
+ skipDependencyChanges: true
589
+ });
590
+ expect(result4.exitCode).toBe(0);
591
+
592
+ const result5 = await runApplyPatches(getFixturePath('composition-5-features/5-features-about'), baseAppDir, targetDir, {
593
+ skipDependencyChanges: true
594
+ });
595
+ expect(result5.exitCode).toBe(0);
596
+
597
+ // Verify at least some of the features were applied
598
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/5-features-about/src/about.tsx'))).toBe(true);
599
+ });
600
+
601
+ it('should handle inherit then modify pattern', async () => {
602
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
603
+ const targetDir1 = join(tempDir, 'inherit-layout');
604
+ const targetDir2 = join(tempDir, 'modify-layout');
605
+
606
+ // Apply inherit feature first
607
+ const result1 = await runApplyPatches(getFixturePath('composition-inherit-modify/composition-inherit-layout'), baseAppDir, targetDir1, {
608
+ skipDependencyChanges: true
609
+ });
610
+ expect(result1.exitCode).toBe(0);
611
+
612
+ // Apply modify feature
613
+ const result2 = await runApplyPatches(getFixturePath('composition-inherit-modify/composition-modify-layout'), baseAppDir, targetDir2, {
614
+ skipDependencyChanges: true
615
+ });
616
+ expect(result2.exitCode).toBe(0);
617
+
618
+ // Verify both applied correctly
619
+ const { readFileSync } = await import('fs');
620
+ const layout1 = readFileSync(join(targetDir1, 'digitalExperiences/webApplications/composition-inherit-layout/src/appLayout.tsx'), 'utf-8');
621
+ const layout2 = readFileSync(join(targetDir2, 'digitalExperiences/webApplications/composition-modify-layout/src/appLayout.tsx'), 'utf-8');
622
+
623
+ expect(layout1).toBeTruthy();
624
+ expect(layout2).toBeTruthy();
625
+ });
626
+
627
+ it('should handle multiple prepends in order (prepend-2 and prepend-3)', async () => {
628
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
629
+ const targetDir2 = join(tempDir, 'prepend2');
630
+ const targetDir3 = join(tempDir, 'prepend3');
631
+
632
+ const result2 = await runApplyPatches(getFixturePath('composition-prepend/composition-prepend-2'), baseAppDir, targetDir2, {
633
+ skipDependencyChanges: true
634
+ });
635
+ expect(result2.exitCode).toBe(0);
636
+
637
+ const result3 = await runApplyPatches(getFixturePath('composition-prepend/composition-prepend-3'), baseAppDir, targetDir3, {
638
+ skipDependencyChanges: true
639
+ });
640
+ expect(result3.exitCode).toBe(0);
641
+
642
+ const { readFileSync } = await import('fs');
643
+ const indexContent2 = readFileSync(join(targetDir2, 'digitalExperiences/webApplications/composition-prepend-2/src/index.tsx'), 'utf-8');
644
+ const indexContent3 = readFileSync(join(targetDir3, 'digitalExperiences/webApplications/composition-prepend-3/src/index.tsx'), 'utf-8');
645
+
646
+ expect(indexContent2).toContain('composition-prepend-2');
647
+ expect(indexContent3).toContain('composition-prepend-3');
648
+ });
649
+ });
650
+
651
+ describe('Phase 5: Real-world scenarios', () => {
652
+ it('should apply navigation menu feature', async () => {
653
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
654
+ const targetDir = join(tempDir, 'nav-menu-apply');
655
+ const featurePath = getFixturePath('scenarios/scenario-navigation-menu');
656
+
657
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
658
+ skipDependencyChanges: true
659
+ });
660
+ expect(result.exitCode).toBe(0);
661
+
662
+ // Verify navigation component exists
663
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/scenario-navigation-menu/src/components/NavigationMenu.tsx'))).toBe(true);
664
+
665
+ // Verify appLayout was modified
666
+ const { readFileSync } = await import('fs');
667
+ const appLayoutContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/scenario-navigation-menu/src/appLayout.tsx'), 'utf-8');
668
+ expect(appLayoutContent).toContain('NavigationMenu');
669
+ });
670
+
671
+ it('should apply authentication feature', async () => {
672
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
673
+ const targetDir = join(tempDir, 'auth-apply');
674
+ const featurePath = getFixturePath('scenarios/scenario-authentication');
675
+
676
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
677
+ skipDependencyChanges: true
678
+ });
679
+ expect(result.exitCode).toBe(0);
680
+
681
+ // Verify auth components exist
682
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/scenario-authentication/src/context/AuthContext.tsx'))).toBe(true);
683
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/scenario-authentication/src/components/LoginForm.tsx'))).toBe(true);
684
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/scenario-authentication/src/login.tsx'))).toBe(true);
685
+
686
+ // Verify routes include login
687
+ const { readFileSync } = await import('fs');
688
+ const routesContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/scenario-authentication/src/routes.tsx'), 'utf-8');
689
+ expect(routesContent).toContain('login');
690
+ expect(routesContent).toContain('Login');
691
+ });
692
+
693
+ it('should apply CRUD contacts feature', async () => {
694
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
695
+ const targetDir = join(tempDir, 'crud-apply');
696
+ const featurePath = getFixturePath('scenarios/scenario-crud-contacts');
697
+
698
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
699
+ skipDependencyChanges: true
700
+ });
701
+ expect(result.exitCode).toBe(0);
702
+
703
+ // Verify CRUD components exist
704
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/scenario-crud-contacts/src/components/ContactList.tsx'))).toBe(true);
705
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/scenario-crud-contacts/src/components/ContactDetail.tsx'))).toBe(true);
706
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/scenario-crud-contacts/src/components/ContactForm.tsx'))).toBe(true);
707
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/scenario-crud-contacts/src/utils/contactsApi.ts'))).toBe(true);
708
+
709
+ // Verify routes include contacts with nested routes
710
+ const { readFileSync } = await import('fs');
711
+ const routesContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/scenario-crud-contacts/src/routes.tsx'), 'utf-8');
712
+ expect(routesContent).toContain('contacts');
713
+ expect(routesContent).toContain(':id');
714
+ expect(routesContent).toContain('edit');
715
+
716
+ // Verify CSS was appended
717
+ const cssContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/scenario-crud-contacts/src/styles/global.css'), 'utf-8');
718
+ expect(cssContent).toContain('contact-list');
719
+ expect(cssContent).toContain('form-group');
720
+ });
721
+ });
722
+
723
+ describe('Phase 6: Edge cases', () => {
724
+ it('should handle empty feature with only dependencies', async () => {
725
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
726
+ const targetDir = join(tempDir, 'edge-empty-apply');
727
+ const featurePath = getFixturePath('edges/edge-empty-feature');
728
+
729
+ // Should succeed - empty features are valid "meta-features"
730
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
731
+ skipDependencyChanges: true
732
+ });
733
+ expect(result.exitCode).toBe(0);
734
+
735
+ // Dependencies should be applied
736
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/edge-empty-feature/src/appLayout.tsx'))).toBe(true);
737
+ });
738
+
739
+ it('should reject conflicting prefixes', async () => {
740
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
741
+ const targetDir = join(tempDir, 'edge-conflict-apply');
742
+ const featurePath = getFixturePath('edges/edge-conflicting-prefixes');
743
+
744
+ // Should fail - can't have both __inherit__ and __delete__
745
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
746
+ skipDependencyChanges: true
747
+ });
748
+
749
+ expect(result.exitCode).not.toBe(0);
750
+ });
751
+
752
+ it('should reject operations on missing base files', async () => {
753
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
754
+ const targetDir = join(tempDir, 'edge-missing-apply');
755
+ const featurePath = getFixturePath('edges/edge-missing-base-files');
756
+
757
+ // Should fail - can't prepend/append/inherit non-existent files
758
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
759
+ skipDependencyChanges: true
760
+ });
761
+
762
+ expect(result.exitCode).not.toBe(0);
763
+ });
764
+
765
+ it('should detect package.json version conflicts', async () => {
766
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
767
+ const targetDir = join(tempDir, 'edge-pkg-conflict-apply');
768
+
769
+ // Apply first feature with react@18.0.0
770
+ const featurePathA = getFixturePath('edges/edge-package-json-conflicts-a');
771
+ const resultA = await runApplyPatches(featurePathA, baseAppDir, targetDir, {
772
+ skipDependencyChanges: true
773
+ });
774
+ expect(resultA.exitCode).toBe(0);
775
+
776
+ // Try to apply second feature with react@18.2.0 - should conflict
777
+ const featurePathB = getFixturePath('edges/edge-package-json-conflicts-b');
778
+ const resultB = await runApplyPatches(featurePathB, targetDir, targetDir, {
779
+ skipDependencyChanges: true
780
+ });
781
+
782
+ expect(resultB.exitCode).not.toBe(0);
783
+ expect(resultB.stderr).toMatch(/conflict|version/i);
784
+ });
785
+
786
+ it('should reject invalid route operations', async () => {
787
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
788
+ const targetDir = join(tempDir, 'edge-invalid-routes-apply');
789
+ const featurePath = getFixturePath('edges/edge-invalid-routes');
790
+
791
+ // Should fail - trying to delete non-existent routes
792
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
793
+ skipDependencyChanges: true
794
+ });
795
+
796
+ expect(result.exitCode).not.toBe(0);
797
+ });
798
+
799
+ it('should handle binary files correctly', async () => {
800
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
801
+ const targetDir = join(tempDir, 'edge-binary-apply');
802
+ const featurePath = getFixturePath('edges/edge-binary-files');
803
+
804
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
805
+ skipDependencyChanges: true
806
+ });
807
+ expect(result.exitCode).toBe(0);
808
+
809
+ // Binary file should be copied
810
+ const imagePath = join(targetDir, 'digitalExperiences/webApplications/edge-binary-files/src/assets/test-image.png');
811
+ expect(existsSync(imagePath)).toBe(true);
812
+
813
+ // Verify it's actually a binary file (not corrupted)
814
+ const { readFileSync } = await import('fs');
815
+ const imageBuffer = readFileSync(imagePath);
816
+ expect(imageBuffer.length).toBeGreaterThan(0);
817
+ });
818
+
819
+ it('should handle different line endings correctly', async () => {
820
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
821
+ const targetDir = join(tempDir, 'edge-line-endings-apply');
822
+ const featurePath = getFixturePath('edges/edge-line-endings');
823
+
824
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
825
+ skipDependencyChanges: true
826
+ });
827
+ expect(result.exitCode).toBe(0);
828
+
829
+ // Prepend and append should work correctly regardless of line endings
830
+ const { readFileSync } = await import('fs');
831
+ const indexContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/edge-line-endings/src/index.tsx'), 'utf-8');
832
+ const cssContent = readFileSync(join(targetDir, 'digitalExperiences/webApplications/edge-line-endings/src/styles/global.css'), 'utf-8');
833
+
834
+ expect(indexContent).toContain('line-endings');
835
+ expect(cssContent).toContain('line-endings');
836
+ });
837
+
838
+ it('should handle many features with reasonable performance', async () => {
839
+ const baseAppDir = copyFixture('base-app', join(tempDir, 'base-app'));
840
+ const targetDir = join(tempDir, 'edge-performance-apply');
841
+ const featurePath = getFixturePath('edges/edge-performance-many');
842
+
843
+ const startTime = Date.now();
844
+
845
+ const result = await runApplyPatches(featurePath, baseAppDir, targetDir, {
846
+ skipDependencyChanges: true
847
+ });
848
+
849
+ const duration = Date.now() - startTime;
850
+
851
+ expect(result.exitCode).toBe(0);
852
+
853
+ // Should complete in reasonable time (12 features should take less than 5 seconds)
854
+ expect(duration).toBeLessThan(5000);
855
+
856
+ // Verify at least some features were applied
857
+ expect(existsSync(join(targetDir, 'digitalExperiences/webApplications/edge-performance-many'))).toBe(true);
858
+ });
859
+ });
860
+
861
+ });