@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
package/README.md ADDED
@@ -0,0 +1,1414 @@
1
+ # Feature Patch CLI
2
+
3
+ CLI tool for applying feature patches to apps in the webapps-templates monorepo.
4
+
5
+ ## Installation
6
+
7
+ Build the CLI from the monorepo root:
8
+
9
+ ```bash
10
+ yarn build --workspace=@sfdc-webapps/cli
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Apply patches
16
+
17
+ ```bash
18
+ # Apply patches to a target directory (base app is used as reference only)
19
+ yarn apply-patches <feature-path> <app-path> <target-dir>
20
+
21
+ # Examples:
22
+ # Apply navigation-menu patches to my-app (using base-react-app as reference)
23
+ yarn apply-patches packages/feature-navigation-menu packages/base-react-app my-app
24
+
25
+ # Skip dependency installation
26
+ yarn apply-patches packages/feature-navigation-menu packages/base-react-app my-app -- --skip-dependency-changes
27
+
28
+ # Clean target directory before applying
29
+ yarn apply-patches packages/feature-navigation-menu packages/base-react-app my-app -- --clean
30
+ ```
31
+
32
+ ### Direct usage
33
+
34
+ ```bash
35
+ # From monorepo root
36
+ node packages/cli/dist/index.js <feature-path> <app-path> <target-dir>
37
+
38
+ # With flags
39
+ node packages/cli/dist/index.js <feature-path> <app-path> my-app --skip-dependency-changes --clean
40
+ ```
41
+
42
+ ### Create a new feature
43
+
44
+ Create a new feature package from the base-feature template:
45
+
46
+ ```bash
47
+ # Create a new feature (will be prefixed with "feature-")
48
+ yarn new-feature <feature-name>
49
+
50
+ # Examples:
51
+ yarn new-feature navigation # Creates packages/feature-navigation
52
+ yarn new-feature user-dashboard # Creates packages/feature-user-dashboard
53
+ yarn new-feature api-client # Creates packages/feature-api-client
54
+ ```
55
+
56
+ #### Direct usage
57
+
58
+ ```bash
59
+ # From monorepo root
60
+ node packages/cli/dist/index.js new-feature <feature-name>
61
+ ```
62
+
63
+ #### Feature Name Requirements
64
+
65
+ - **Format**: Must be in kebab-case (lowercase with hyphens)
66
+ - **Characters**: Only alphanumeric characters and hyphens allowed
67
+ - **Cannot**: Start or end with hyphens, have consecutive hyphens
68
+ - **Reserved**: Cannot be "base" or "cli"
69
+ - **Auto-prefix**: Automatically prefixed with "feature-" (e.g., `nav-menu` → `feature-nav-menu`)
70
+
71
+ Examples:
72
+ - ✅ `navigation` → `feature-navigation`
73
+ - ✅ `user-auth` → `feature-user-auth`
74
+ - ✅ `dashboard-v2` → `feature-dashboard-v2`
75
+ - ❌ `Navigation` (uppercase)
76
+ - ❌ `user_auth` (underscore)
77
+ - ❌ `--menu` (starts with hyphen)
78
+ - ❌ `nav--menu` (consecutive hyphens)
79
+
80
+ #### What the command does
81
+
82
+ The `new-feature` command:
83
+
84
+ 1. **Validates** the feature name format
85
+ 2. **Checks** that the feature doesn't already exist
86
+ 3. **Copies** the base-feature template to `packages/feature-{name}/`
87
+ 4. **Renames** the template directory from `base-feature` to `{name}`
88
+ 5. **Updates** `package.json` with the new feature name
89
+ 6. **Updates** `tsconfig.app.json` with the new directory path
90
+ 7. **Creates** a ready-to-use feature package
91
+
92
+ The created feature will have:
93
+ - ✅ Correct `package.json` configuration
94
+ - ✅ TypeScript configuration
95
+ - ✅ Feature structure following conventions
96
+ - ✅ Ready for customization and development
97
+
98
+ #### Next steps after creation
99
+
100
+ ```bash
101
+ cd packages/feature-{name}
102
+ # 1. Customize template files in template/ directory
103
+ # 2. Update feature.ts with feature configuration
104
+ # 3. Test with: yarn apply-patches packages/feature-{name} packages/base-react-app test-app
105
+ ```
106
+
107
+ ### Options
108
+
109
+ - `<feature-path>`: Path to the feature directory (can be relative to monorepo root or absolute). The feature must contain a feature.ts file.
110
+ - `<app-path>`: Path to the base app directory (can be relative to monorepo root or absolute). Used as a reference for file inheritance and validation. The base app remains unchanged.
111
+ - `<target-dir>`: **Required**. Target directory where the feature will be applied. Can be a relative or absolute path. The CLI will create this directory and apply all features to it.
112
+ - `--skip-dependency-changes`: Skip installing dependencies from package.json. Only file changes will be applied.
113
+ - `--clean`: Remove target directory if it exists before applying patches.
114
+
115
+ ## What it does
116
+
117
+ The CLI tool:
118
+
119
+ 1. **Validates** that the specified feature path exists and contains a feature.ts file, and the base app path is valid
120
+ 2. **Prepares target directory**: Creates the target directory (or cleans it if `--clean` flag is used)
121
+ 3. **Resolves dependencies**: Recursively resolves all feature dependencies, detecting circular dependencies and building an ordered list where dependencies are applied before dependent features
122
+ 4. **Loads** the feature definitions from each `feature.ts` file in dependency order
123
+ 5. **Discovers files** from each feature's template directory (defaults to `template`, configurable via `templateDir` in feature.ts)
124
+ 6. **Validates paths**: For each feature, ensures:
125
+ - No conflicting paths exist (e.g., both `routes.tsx` and `__delete__routes.tsx`, or `__prepend__global.css` and `__append__global.css`)
126
+ - Files marked with `__delete__`, `__inherit__`, `__prepend__`, or `__append__` exist in the base app
127
+ 7. **Applies file changes** from each feature's template directory in dependency order:
128
+ - **Delete operations**: Removes files/directories marked with `__delete__` prefix from the target app
129
+ - **Inherit operations**: Skips files marked with `__inherit__` (inherited from base app)
130
+ - **Prepend operations**: Adds content from feature file before the base file's content (for files marked with `__prepend__`)
131
+ - **Append operations**: Adds content from feature file after the base file's content (for files marked with `__append__`)
132
+ - **Import path fixing**: Automatically removes `__inherit__` prefix from import statements in JS/TS files
133
+ - **File changes**: Copies each file from the template directory to the target
134
+ - **Route merging**: Intelligently merges route files (`routes.tsx`), accumulating routes from all features
135
+ 8. **Aggregates and installs dependencies**: Collects all NPM dependencies from all features and installs them in a single `yarn` command (unless `--skip-dependency-changes` is used). Detects and errors on version conflicts.
136
+
137
+ ## Examples
138
+
139
+ ### Example: Apply feature patches to create a new app
140
+
141
+ ```bash
142
+ $ yarn apply-patches packages/feature-navigation-menu packages/base-react-app my-app
143
+
144
+ Applying patches: packages/feature-navigation-menu → my-app
145
+ ℹ Validating paths...
146
+ ✓ Validation passed
147
+ ℹ Creating target directory my-app...
148
+ ✓ Target directory created
149
+
150
+ Resolving Dependencies
151
+ ℹ No dependencies to resolve
152
+
153
+
154
+ Applying: packages/feature-navigation-menu
155
+ ℹ Discovering files...
156
+ ℹ Found 6 file(s)
157
+ ℹ Validating paths...
158
+ ✓ Paths validated
159
+ ✓ Added digitalExperiences/webApplications/feature-navigation-menu/src/navigationMenu.tsx
160
+ ✓ Added digitalExperiences/webApplications/feature-navigation-menu/src/appLayout.tsx
161
+ ✓ Added digitalExperiences/webApplications/feature-navigation-menu/src/routes.tsx
162
+ ✓ Added digitalExperiences/webApplications/feature-navigation-menu/src/router-utils.tsx
163
+ ✓ Added digitalExperiences/webApplications/feature-navigation-menu/src/about.tsx
164
+ ✓ Added digitalExperiences/webApplications/feature-navigation-menu/src/new.tsx
165
+
166
+ Installing dependencies
167
+ ℹ Installing dependencies...
168
+ [yarn output]
169
+ ✓ Dependencies installed
170
+
171
+ ✓ Success
172
+ ✓ Created: /path/to/monorepo/my-app
173
+ ```
174
+
175
+ ## Creating Features
176
+
177
+ **Quick Start:** Use the CLI to create a new feature from the template:
178
+
179
+ ```bash
180
+ yarn new-feature your-feature-name
181
+ ```
182
+
183
+ This creates a new feature at `packages/feature-your-feature-name/` with all the necessary configuration files. Then customize the template files in the `template/` directory.
184
+
185
+ For manual setup or advanced configuration, see below...
186
+
187
+ ---
188
+
189
+ Features are defined in your feature's `feature.ts` file and must be exported as a **default export**. The default export can be either a single feature object or an array of features. Import types from `packages/cli/src/types.js`.
190
+
191
+ ### Feature Structure
192
+
193
+ ```
194
+ packages/feature-my-feature/
195
+ ├── feature.ts # Feature configuration
196
+ ├── package.json # NPM dependencies for development
197
+ └── template/ # Template files (default directory name)
198
+ ├── webApp/ # Web application files (mapped to digitalExperiences/webApplications/<feature-name>/)
199
+ │ └── src/
200
+ │ ├── routes.tsx
201
+ │ ├── component1.tsx
202
+ │ └── component2.tsx
203
+ └── classes/ # SFDX metadata (placed at root level in dist)
204
+ └── MyClass.cls
205
+ ```
206
+
207
+ **Note**: The CLI handles two types of files differently:
208
+ - **Web Application files** (under `webApp/`): Automatically mapped to `digitalExperiences/webApplications/<feature-name>/`
209
+ - **SFDX metadata files** (like `classes/`, `triggers/`, `objects/`, etc.): Placed at root level in the output directory
210
+
211
+ This structure ensures proper organization for both digital experience applications and Salesforce metadata.
212
+
213
+ ### Feature Configuration
214
+
215
+ The feature configuration file specifies:
216
+ - `templateDir`: Directory containing template files (defaults to `template`)
217
+ - `webAppName`: Name of the web application (defaults to the feature name extracted from the directory). This is used for constructing the default route path.
218
+ - `routeFilePath`: Path to the routes file for merging (defaults to `digitalExperiences/webApplications/<webAppName>/src/routes.tsx`)
219
+ - `packageJson`: NPM dependencies to install in the target app
220
+ - `dependencies`: Array of other features this feature depends on (applied first)
221
+
222
+ ### Basic Feature Example:
223
+
224
+ ```typescript
225
+ import type { Feature } from '../cli/src/types.js';
226
+
227
+ const feature: Feature = {
228
+ // All fields are optional with sensible defaults
229
+ templateDir: 'template', // Optional, defaults to 'template'
230
+ webAppName: 'my-feature', // Optional, defaults to feature directory name
231
+ // routeFilePath defaults to 'digitalExperiences/webApplications/<webAppName>/src/routes.tsx'
232
+ packageJson: {
233
+ dependencies: {
234
+ 'react-router': '^7.10.1',
235
+ },
236
+ },
237
+ };
238
+
239
+ export default feature;
240
+ ```
241
+
242
+ ### Feature with Dependencies:
243
+
244
+ ```typescript
245
+ import type { Feature } from '../cli/src/types.js';
246
+
247
+ const feature: Feature = {
248
+ // This feature depends on navigation-menu feature
249
+ // navigation-menu will be applied first, then this feature
250
+ dependencies: ['packages/feature-navigation-menu'],
251
+ packageJson: {
252
+ dependencies: {
253
+ 'some-package': '^1.0.0',
254
+ },
255
+ },
256
+ };
257
+
258
+ export default feature;
259
+ ```
260
+
261
+ ### Custom Configuration:
262
+
263
+ ```typescript
264
+ import type { Feature } from '../cli/src/types.js';
265
+
266
+ const feature: Feature = {
267
+ templateDir: 'src', // Use 'src' instead of 'template'
268
+ webAppName: 'custom-app-name', // Override default app name
269
+ routeFilePath: 'custom/path/to/routes.tsx', // Custom route file path
270
+ packageJson: {
271
+ dependencies: {
272
+ 'react-router': '^7.10.1',
273
+ },
274
+ },
275
+ };
276
+
277
+ export default feature;
278
+ ```
279
+
280
+ **Notes:**
281
+ - `templateDir`: All files in this directory will be discovered and applied to the target app
282
+ - `webAppName`: Used to construct the default route path and organize files. Defaults to the feature directory name (e.g., `feature-navigation-menu` → `feature-navigation-menu`)
283
+ - `routeFilePath`: Must be a path relative to `templateDir`. If not specified, defaults to `digitalExperiences/webApplications/<webAppName>/src/routes.tsx`
284
+
285
+ ## Path Mappings
286
+
287
+ Path mappings allow features to use simplified directory structures that are automatically transformed to the full Salesforce Digital Experience structure. This makes feature templates easier to create and maintain by removing repetitive nested directory paths.
288
+
289
+ ### Default Behavior (Enabled by Default)
290
+
291
+ By default, all features automatically get the `webApp` mapping, which transforms web application files into the proper nested structure. For example, in `feature-navigation-menu`:
292
+
293
+ ```
294
+ template/webApp/src/app.tsx → dist/digitalExperiences/webApplications/feature-navigation-menu/src/app.tsx
295
+ ```
296
+
297
+ This simplifies feature templates by removing the repetitive nested directory structure.
298
+
299
+ **Important**: Only files under `webApp/` get the nested structure. SFDX metadata types (like `classes/`, `triggers/`, `objects/`, `lwc/`, etc.) are placed at root level:
300
+
301
+ ```
302
+ feature-navigation-menu/template/
303
+ ├── webApp/
304
+ │ └── src/
305
+ │ └── app.tsx → dist/digitalExperiences/webApplications/feature-navigation-menu/src/app.tsx
306
+ └── classes/
307
+ └── NavMenu.cls → dist/classes/NavMenu.cls (root level)
308
+ ```
309
+
310
+ ### Using the Default Mapping
311
+
312
+ Simply organize your template files under `webApp/` for web application code, and at the root level for SFDX metadata:
313
+
314
+ ```typescript
315
+ // feature.ts - No configuration needed
316
+ export default {};
317
+ ```
318
+
319
+ ```
320
+ template/
321
+ ├── webApp/ # Web application files (automatically mapped)
322
+ │ └── src/
323
+ │ ├── routes.tsx
324
+ │ ├── app.tsx
325
+ │ └── components/
326
+ │ └── Header.tsx
327
+ └── classes/ # SFDX metadata (placed at root level)
328
+ └── MyClass.cls
329
+ ```
330
+
331
+ **Result**:
332
+ - Web app files go to `dist/digitalExperiences/webApplications/feature-name/src/`
333
+ - SFDX metadata stays at root: `dist/classes/MyClass.cls`
334
+
335
+ ### Disabling Path Mappings (Opt-Out)
336
+
337
+ Use full paths when you need precise control or for backwards compatibility:
338
+
339
+ ```typescript
340
+ // feature.ts
341
+ export default {
342
+ pathMappings: {
343
+ enabled: false // Disable automatic mapping
344
+ }
345
+ };
346
+ ```
347
+
348
+ ```
349
+ template/
350
+ └── digitalExperiences/ # Use full structure
351
+ └── webApplications/
352
+ └── <feature-name>/
353
+ └── src/
354
+ └── routes.tsx
355
+ ```
356
+
357
+ ### Custom Path Mappings
358
+
359
+ Define custom mappings for non-standard structures:
360
+
361
+ ```typescript
362
+ // feature.ts
363
+ export default {
364
+ pathMappings: {
365
+ mappings: [
366
+ {
367
+ from: 'web',
368
+ to: 'digitalExperiences/webApplications/custom-app-name'
369
+ }
370
+ ]
371
+ }
372
+ };
373
+ ```
374
+
375
+ ```
376
+ template/
377
+ └── web/ # Custom prefix
378
+ └── src/
379
+ └── app.tsx
380
+ ```
381
+
382
+ **Result**: Maps `web/src/app.tsx` → `digitalExperiences/webApplications/custom-app-name/src/app.tsx`
383
+
384
+ ### Multiple Mappings
385
+
386
+ You can define multiple mappings in one feature:
387
+
388
+ ```typescript
389
+ export default {
390
+ pathMappings: {
391
+ mappings: [
392
+ { from: 'webApp', to: 'digitalExperiences/webApplications/my-app' },
393
+ { from: 'shared', to: 'digitalExperiences/shared-resources' }
394
+ ]
395
+ }
396
+ };
397
+ ```
398
+
399
+ ### Mixed Path Formats
400
+
401
+ You can mix web application files, SFDX metadata, and full paths in the same feature:
402
+
403
+ ```
404
+ template/
405
+ ├── webApp/ # Mapped to digitalExperiences/webApplications/<feature-name>/
406
+ │ └── src/
407
+ │ └── app.tsx
408
+ ├── classes/ # SFDX metadata (placed at root)
409
+ │ └── MyClass.cls
410
+ ├── triggers/ # SFDX metadata (placed at root)
411
+ │ └── MyTrigger.trigger
412
+ └── digitalExperiences/ # Full paths (passed through)
413
+ └── siteAssets/
414
+ └── logo.png
415
+ ```
416
+
417
+ All formats work together seamlessly:
418
+ - `webApp/` files → `digitalExperiences/webApplications/<feature-name>/`
419
+ - SFDX metadata (`classes/`, `triggers/`, `objects/`, `lwc/`, etc.) → Root level
420
+ - Full paths (already containing `digitalExperiences/`) → Used as-is
421
+
422
+ ### Path Mapping Examples
423
+
424
+ **Example 1: Default mapping (recommended)**
425
+ ```typescript
426
+ export default {}; // That's it!
427
+ ```
428
+
429
+ **Example 2: Opt-out for backwards compatibility**
430
+ ```typescript
431
+ export default {
432
+ pathMappings: { enabled: false }
433
+ };
434
+ ```
435
+
436
+ **Example 3: Custom app name**
437
+ ```typescript
438
+ export default {
439
+ pathMappings: {
440
+ mappings: [
441
+ { from: 'webApp', to: 'digitalExperiences/webApplications/custom-name' }
442
+ ]
443
+ }
444
+ };
445
+ ```
446
+
447
+ **Example 4: Completely custom structure**
448
+ ```typescript
449
+ export default {
450
+ pathMappings: {
451
+ mappings: [
452
+ { from: 'src', to: 'app/sources' },
453
+ { from: 'assets', to: 'public/static' }
454
+ ]
455
+ }
456
+ };
457
+ ```
458
+
459
+ ### How Path Mappings Work
460
+
461
+ 1. **Discovery**: CLI discovers all files in your template directory
462
+ 2. **Mapping**: Each file path is checked against mapping rules (first match wins)
463
+ 3. **Transformation**: Matching prefix is replaced with target prefix
464
+ 4. **Pass-Through**: Paths that don't match any mapping are used as-is
465
+ 5. **Application**: Transformed paths are used for file operations
466
+
467
+ This ensures:
468
+ - ✅ Backwards compatibility (old features still work)
469
+ - ✅ Simplified templates (new features are easier to create)
470
+ - ✅ Flexibility (custom mappings for special cases)
471
+ - ✅ No breaking changes (opt-in for existing features, opt-out available)
472
+
473
+ ## Feature Dependencies
474
+
475
+ Features can depend on other features. Dependencies are automatically resolved and applied in the correct order.
476
+
477
+ ### How Dependencies Work
478
+
479
+ 1. **Declaration**: Specify dependencies in your `feature.ts` file
480
+ 2. **Resolution**: CLI recursively resolves all dependencies (including nested dependencies)
481
+ 3. **Ordering**: Dependencies are always applied before the feature that depends on them
482
+ 4. **Circular Detection**: CLI detects and prevents circular dependencies
483
+ 5. **File Layering**: Files from dependencies can be overridden by dependent features (main feature wins)
484
+ 6. **Route Accumulation**: Routes from all features are merged together
485
+
486
+ ### Dependency Resolution Order
487
+
488
+ When you apply a feature with dependencies:
489
+
490
+ ```
491
+ Feature A depends on Feature B
492
+ Feature B depends on Feature C
493
+
494
+ Application order: C → B → A (dependencies first)
495
+ ```
496
+
497
+ The CLI builds a complete dependency graph and applies features in topological order.
498
+
499
+ ### Example: Building on Navigation Features
500
+
501
+ ```typescript
502
+ // packages/feature-navigation-menu/feature.ts
503
+ import type { Feature } from '../cli/src/types.js';
504
+
505
+ const feature: Feature = {
506
+ // Navigation menu has no dependencies
507
+ };
508
+
509
+ export default feature;
510
+ ```
511
+
512
+ ```typescript
513
+ // packages/feature-admin-dashboard/feature.ts
514
+ import type { Feature } from '../cli/src/types.js';
515
+
516
+ const feature: Feature = {
517
+ // Admin dashboard builds on top of navigation menu
518
+ dependencies: ['packages/feature-navigation-menu'],
519
+ };
520
+
521
+ export default feature;
522
+ ```
523
+
524
+ When you apply `feature-admin-dashboard`:
525
+ 1. CLI resolves `feature-navigation-menu` as a dependency
526
+ 2. Applies `feature-navigation-menu` first (navigation menu files and routes)
527
+ 3. Applies `feature-admin-dashboard` second (dashboard files and routes)
528
+ 4. Result: App has both navigation menu and admin dashboard
529
+
530
+ ### Nested Dependencies
531
+
532
+ Dependencies can have their own dependencies. The CLI resolves them recursively:
533
+
534
+ ```
535
+ Feature App depends on Feature Dashboard
536
+ Feature Dashboard depends on Feature Navigation
537
+ Feature Navigation depends on Feature Auth
538
+
539
+ Application order: Auth → Navigation → Dashboard → App
540
+ ```
541
+
542
+ ### Circular Dependency Detection
543
+
544
+ The CLI detects and prevents circular dependencies:
545
+
546
+ ```typescript
547
+ // Feature A depends on B
548
+ // Feature B depends on C
549
+ // Feature C depends on A
550
+
551
+ // CLI will error:
552
+ // "Circular dependency detected:
553
+ // packages/feature-a → packages/feature-b → packages/feature-c → packages/feature-a"
554
+ ```
555
+
556
+ **Fix**: Remove one of the dependencies to break the cycle.
557
+
558
+ ### Main Feature Wins
559
+
560
+ When multiple features modify the same file:
561
+ - **Dependencies applied first**: Their files are written to the target
562
+ - **Main feature applied last**: Its files overwrite dependency files
563
+ - **Result**: Main feature can customize/override dependency behavior
564
+
565
+ Example:
566
+ ```
567
+ feature-navigation-menu provides: src/appLayout.tsx
568
+ feature-custom-app also provides: src/appLayout.tsx
569
+
570
+ When applying feature-custom-app:
571
+ 1. navigation-menu's appLayout.tsx is applied
572
+ 2. custom-app's appLayout.tsx overwrites it
573
+ 3. Final result: custom-app's version is used
574
+ ```
575
+
576
+ ### Route Merging with Dependencies
577
+
578
+ Routes from all features are merged together:
579
+
580
+ **Base App Routes:**
581
+ ```typescript
582
+ export const routes = [
583
+ {
584
+ path: '/',
585
+ children: [
586
+ { index: true, element: <Home /> }
587
+ ]
588
+ }
589
+ ];
590
+ ```
591
+
592
+ **navigation-menu Routes:**
593
+ ```typescript
594
+ export const routes = [
595
+ {
596
+ path: '/',
597
+ children: [
598
+ { path: 'about', element: <About /> },
599
+ { path: 'contact', element: <Contact /> }
600
+ ]
601
+ }
602
+ ];
603
+ ```
604
+
605
+ **Final Merged Routes:**
606
+ ```typescript
607
+ export const routes = [
608
+ {
609
+ path: '/',
610
+ children: [
611
+ { index: true, element: <Home /> }, // From base
612
+ { path: 'about', element: <About /> }, // From navigation-menu
613
+ { path: 'contact', element: <Contact /> } // From navigation-menu
614
+ ]
615
+ }
616
+ ];
617
+ ```
618
+
619
+ Routes accumulate across all features, preserving routes from base app and all dependencies.
620
+
621
+ ### Dependency Paths
622
+
623
+ Dependency paths can be:
624
+ - **Relative to monorepo root**: `'packages/feature-navigation-menu'`
625
+ - **Absolute paths**: `'/absolute/path/to/feature'`
626
+
627
+ The CLI normalizes and resolves all paths consistently.
628
+
629
+ ### Diamond Dependencies
630
+
631
+ When multiple features depend on the same feature:
632
+
633
+ ```
634
+ Feature A depends on Feature C
635
+ Feature B depends on Feature C
636
+ Feature Main depends on A and B
637
+
638
+ Dependency graph:
639
+ Main
640
+ / \
641
+ A B
642
+ \ /
643
+ C
644
+ ```
645
+
646
+ **Resolution**: Feature C is applied once (not duplicated).
647
+
648
+ **Application order**: `C → A → B → Main`
649
+
650
+ ### Watch Mode with Dependencies
651
+
652
+ When using watch mode, dependencies are:
653
+ - Applied once on initial startup
654
+ - Re-applied when template files change (with `--clean` flag)
655
+ - Not watched for changes (only the main feature is watched)
656
+
657
+ This is efficient for development: edit your main feature while keeping dependencies stable.
658
+
659
+ ## File Application
660
+
661
+ The CLI discovers all files in your feature's template directory and applies them to the target app:
662
+
663
+ 1. **Standard Files**: Copied directly to the target, creating directories as needed
664
+ 2. **Delete Markers**: Files/directories prefixed with `__delete__` mark files for deletion from the target app
665
+ 3. **Inherit Markers**: Files prefixed with `__inherit__` are kept in the feature for type safety but not copied (inherited from base app)
666
+ 4. **Prepend Markers**: Files prefixed with `__prepend__` add their content before the base file's content
667
+ 5. **Append Markers**: Files prefixed with `__append__` add their content after the base file's content
668
+ 6. **Route Files** (`routes.tsx`): Intelligently merged with existing routes to preserve base app routes
669
+
670
+ ### Deleting Files and Directories
671
+
672
+ Features can delete files or directories from the target app by using the `__delete__` prefix in the template directory.
673
+
674
+ #### How It Works
675
+
676
+ 1. Create a file with the `__delete__` prefix in your feature's template directory
677
+ 2. The prefix can appear anywhere in the path
678
+ 3. When the feature is applied, the corresponding file/directory will be deleted from the target app
679
+ 4. The content of the delete marker file is ignored (can be empty or contain comments)
680
+
681
+ #### Examples
682
+
683
+ **Delete a single file:**
684
+ ```
685
+ template/
686
+ └── src/
687
+ └── __delete__routes.tsx # Deletes src/routes.tsx from target app
688
+ ```
689
+
690
+ **Delete a directory:**
691
+ ```
692
+ template/
693
+ └── src/
694
+ └── __delete__pages/ # Deletes src/pages/ directory from target app
695
+ └── .gitkeep # Placeholder file (content ignored)
696
+ ```
697
+
698
+ **Delete from nested path:**
699
+ ```
700
+ template/
701
+ └── src/
702
+ └── components/
703
+ └── __delete__Footer.tsx # Deletes src/components/Footer.tsx
704
+ ```
705
+
706
+ **Delete a parent directory:**
707
+ ```
708
+ template/
709
+ └── __delete__src/
710
+ └── legacy/ # Deletes src/legacy/ directory from target app
711
+ └── .gitkeep
712
+ ```
713
+
714
+ #### Validation
715
+
716
+ The CLI validates that you don't have conflicting paths:
717
+ - ❌ **Invalid**: Having both `routes.tsx` and `__delete__routes.tsx` in the same template
718
+ - ✅ **Valid**: Having only `__delete__routes.tsx` (to delete) or only `routes.tsx` (to add/update)
719
+
720
+ If a conflict is detected, the CLI will throw an error:
721
+ ```
722
+ Path conflict detected: "src/routes.tsx" appears multiple times in the template.
723
+ This can happen when both a file and its __delete__ marker exist.
724
+ Please remove one of them.
725
+ ```
726
+
727
+ #### Use Cases
728
+
729
+ - **Remove obsolete files**: Delete deprecated components or utilities that the feature replaces
730
+ - **Clean up after refactoring**: Remove files that are no longer needed with the new feature
731
+ - **Remove base app scaffolding**: Delete placeholder files from the base app that the feature supersedes
732
+
733
+ For example, the `vibe-coding-starter` feature deletes the base app's `routes.tsx` because it provides a single-page app in `index.tsx` instead.
734
+
735
+ ### Inheriting Files from Base App
736
+
737
+ Features can maintain type-safe references to base app files without copying them using the `__inherit__` prefix. This is useful when your feature code needs to import and reference files from the base app while keeping TypeScript/IDE support in your feature directory.
738
+
739
+ #### How It Works
740
+
741
+ 1. Create a file with the `__inherit__` prefix in your feature's template directory
742
+ 2. Copy the base app file's contents to the `__inherit__` file (for type checking and IDE support)
743
+ 3. When the feature is applied, the file is NOT copied to the target (the base app's file is used)
744
+ 4. The feature gets type safety and autocomplete for the inherited file
745
+
746
+ #### Why Use `__inherit__`?
747
+
748
+ **Problem**: You want to import a base app file in your feature code, but:
749
+ - If you don't have the file in your feature, TypeScript shows errors and IDE autocomplete doesn't work
750
+ - If you copy the file to your feature, it will overwrite the base app file when applied
751
+
752
+ **Solution**: Use `__inherit__` to keep the file in your feature for development, but skip it during application.
753
+
754
+ #### Automatic Import Path Fixing
755
+
756
+ **Important**: The CLI automatically fixes import paths in your feature files!
757
+
758
+ When you import from `__inherit__` files in your feature code:
759
+ ```typescript
760
+ // In your feature's template
761
+ import { routes } from './__inherit__routes';
762
+ import AppLayout from './__inherit__appLayout';
763
+ ```
764
+
765
+ The CLI automatically removes the `__inherit__` prefix when applying the feature:
766
+ ```typescript
767
+ // In the target app after applying
768
+ import { routes } from './routes';
769
+ import AppLayout from './appLayout';
770
+ ```
771
+
772
+ **Supported patterns:**
773
+ - `import ... from './__inherit__file'` → `import ... from './file'`
774
+ - `export ... from './__inherit__file'` → `export ... from './file'`
775
+ - `require('./__inherit__file')` → `require('./file')`
776
+ - `import('./__inherit__file')` → `import('./file')`
777
+ - Works with single quotes (`'`), double quotes (`"`), and backticks (`` ` ``)
778
+ - Works with relative paths (`../`, `./`)
779
+
780
+ **Only processes JavaScript/TypeScript files:**
781
+ - `.js`, `.jsx`, `.ts`, `.tsx`, `.mjs`, `.cjs`
782
+ - Other files (`.md`, `.json`, etc.) are not processed
783
+
784
+ This means you can freely import from `__inherit__` files in your feature code, and the imports will "just work" when the feature is applied!
785
+
786
+ #### Examples
787
+
788
+ **Inherit routes for type safety:**
789
+ ```typescript
790
+ // Feature structure
791
+ template/
792
+ └── src/
793
+ ├── __inherit__routes.tsx // Copy of base app routes for types
794
+ └── index.tsx // Can import routes safely
795
+
796
+ // In your feature's index.tsx
797
+ import { routes } from './routes'; // TypeScript works!
798
+
799
+ // When applied:
800
+ // - routes.tsx from base app is used (not overwritten)
801
+ // - Your index.tsx can still import it
802
+ ```
803
+
804
+ **Inherit shared layout:**
805
+ ```
806
+ template/
807
+ └── src/
808
+ ├── __inherit__appLayout.tsx # Copy from base for types
809
+ └── pages/
810
+ └── MyPage.tsx # Can import appLayout safely
811
+ ```
812
+
813
+ #### Validation
814
+
815
+ The CLI validates `__inherit__` files:
816
+
817
+ 1. **No conflicts**: You cannot have both `routes.tsx` and `__inherit__routes.tsx` in the same template
818
+ - ❌ **Invalid**: Both `routes.tsx` and `__inherit__routes.tsx`
819
+ - ✅ **Valid**: Only `__inherit__routes.tsx`
820
+
821
+ 2. **Base file must exist**: The file must exist in the base app
822
+ - ❌ **Invalid**: `__inherit__nonexistent.tsx` when file doesn't exist in base app
823
+ - ✅ **Valid**: `__inherit__routes.tsx` when `routes.tsx` exists in base app
824
+
825
+ If validation fails, you'll see clear error messages:
826
+ ```
827
+ Validation error: Cannot inherit file that doesn't exist!
828
+
829
+ File marked for inheritance: src/routes.tsx
830
+ Expected location in base app: /path/to/base/src/routes.tsx
831
+ The file doesn't exist in the base app.
832
+ ```
833
+
834
+ #### Use Cases
835
+
836
+ - **Type-safe imports**: Import base app files in your feature with full TypeScript support
837
+ - **Shared layouts**: Reference base app layouts without overwriting them
838
+ - **Route definitions**: Import base routes to extend or reference them
839
+ - **Shared utilities**: Reference base app utility functions with autocomplete
840
+
841
+ #### Complete Example
842
+
843
+ ```typescript
844
+ // Base app has: src/routes.tsx, src/appLayout.tsx
845
+
846
+ // Feature template structure:
847
+ template/
848
+ └── src/
849
+ ├── __inherit__routes.tsx # Copy from base (for types only)
850
+ ├── __inherit__appLayout.tsx # Copy from base (for types only)
851
+ └── pages/
852
+ └── Dashboard.tsx # New page that imports both
853
+
854
+ // In template/src/pages/Dashboard.tsx (during development):
855
+ import { routes } from '../__inherit__routes'; // Import from __inherit__ file
856
+ import AppLayout from '../__inherit__appLayout'; // TypeScript works!
857
+
858
+ export default function Dashboard() {
859
+ return <AppLayout>Dashboard using {routes.length} routes</AppLayout>;
860
+ }
861
+
862
+ // When applied, Dashboard.tsx is automatically transformed to:
863
+ import { routes } from '../routes'; // __inherit__ removed!
864
+ import AppLayout from '../appLayout'; // __inherit__ removed!
865
+
866
+ export default function Dashboard() {
867
+ return <AppLayout>Dashboard using {routes.length} routes</AppLayout>;
868
+ }
869
+
870
+ // Final result in target app:
871
+ // - src/routes.tsx: inherited from base (not overwritten) ✓
872
+ // - src/appLayout.tsx: inherited from base (not overwritten) ✓
873
+ // - src/pages/Dashboard.tsx: added from feature with fixed imports ✓
874
+ ```
875
+
876
+ ### Prepending and Appending to Base Files
877
+
878
+ Features can add content to the beginning (`__prepend__`) or end (`__append__`) of existing base app files. This is useful for adding styles, imports, or configuration to base files without completely replacing them.
879
+
880
+ #### How It Works
881
+
882
+ 1. **Prepend**: Content from the feature file is added **before** the base file's content
883
+ 2. **Append**: Content from the feature file is added **after** the base file's content
884
+ 3. The prefix can appear anywhere in the path
885
+ 4. Import paths with `__inherit__` are automatically fixed in the content
886
+
887
+ #### Examples
888
+
889
+ **Append CSS to global styles:**
890
+ ```
891
+ template/
892
+ └── src/
893
+ └── styles/
894
+ └── __append__global.css # Adds content after base global.css
895
+ ```
896
+
897
+ ```css
898
+ /* Feature's __append__global.css */
899
+ :root {
900
+ --feature-color: #066afe;
901
+ --feature-background: #ffffff;
902
+ }
903
+
904
+ .feature-specific {
905
+ color: var(--feature-color);
906
+ }
907
+ ```
908
+
909
+ **Result in target app's `global.css`:**
910
+ ```css
911
+ /* Base app's existing content */
912
+ @import "tailwindcss";
913
+
914
+ body {
915
+ margin: 0;
916
+ }
917
+
918
+ /* Content appended from feature */
919
+ :root {
920
+ --feature-color: #066afe;
921
+ --feature-background: #ffffff;
922
+ }
923
+
924
+ .feature-specific {
925
+ color: var(--feature-color);
926
+ }
927
+ ```
928
+
929
+ **Prepend imports to a TypeScript file:**
930
+ ```
931
+ template/
932
+ └── src/
933
+ └── __prepend__index.tsx # Adds imports before base index.tsx
934
+ ```
935
+
936
+ ```typescript
937
+ /* Feature's __prepend__index.tsx */
938
+ import { initializeFeature } from './feature-init';
939
+
940
+ initializeFeature();
941
+ ```
942
+
943
+ **Result in target app's `index.tsx`:**
944
+ ```typescript
945
+ /* Content prepended from feature */
946
+ import { initializeFeature } from './feature-init';
947
+
948
+ initializeFeature();
949
+
950
+ /* Base app's existing content */
951
+ import React from 'react';
952
+ import ReactDOM from 'react-dom';
953
+ // ... rest of base file
954
+ ```
955
+
956
+ #### Validation
957
+
958
+ The CLI validates prepend/append operations:
959
+
960
+ 1. **No conflicts**: You cannot target the same file with multiple operations
961
+ - ❌ **Invalid**: Both `__prepend__global.css` and `__append__global.css`
962
+ - ❌ **Invalid**: Both `global.css` and `__append__global.css`
963
+ - ✅ **Valid**: Only `__append__global.css`
964
+
965
+ 2. **Base file must exist**: The target file must exist in the base app
966
+ - ❌ **Invalid**: `__append__nonexistent.css` when file doesn't exist
967
+ - ✅ **Valid**: `__append__global.css` when `global.css` exists in base app
968
+
969
+ If validation fails, you'll see clear error messages:
970
+ ```
971
+ Path conflict detected!
972
+
973
+ The following paths resolve to the same target file:
974
+ 1. src/styles/__append__global.css (append)
975
+ 2. src/styles/__prepend__global.css (prepend)
976
+ → Both target: src/styles/global.css
977
+
978
+ You cannot have multiple files targeting the same path.
979
+ ```
980
+
981
+ #### Automatic Import Fixing
982
+
983
+ When prepending or appending TypeScript/JavaScript files, import paths with `__inherit__` are automatically fixed:
984
+
985
+ ```typescript
986
+ // In feature's __prepend__index.tsx
987
+ import { routes } from './__inherit__routes';
988
+
989
+ // After prepending to target app
990
+ import { routes } from './routes'; // __inherit__ removed!
991
+ ```
992
+
993
+ This works the same as regular file operations (see "Automatic Import Path Fixing" above).
994
+
995
+ #### Use Cases
996
+
997
+ **Prepend:**
998
+ - Add initialization code at the start of entry files
999
+ - Add imports before existing code
1000
+ - Add type declarations or interfaces
1001
+
1002
+ **Append:**
1003
+ - Add CSS variables and styles to global stylesheets
1004
+ - Add routes or configuration entries
1005
+ - Extend existing files with additional functionality
1006
+ - Add utility functions or helpers
1007
+
1008
+ #### Complete Example
1009
+
1010
+ ```typescript
1011
+ // Base app has: src/styles/global.css
1012
+ // with Tailwind configuration
1013
+
1014
+ // Feature adds Salesforce Design Tokens by appending
1015
+
1016
+ // Feature structure:
1017
+ template/
1018
+ └── src/
1019
+ └── styles/
1020
+ └── __append__global.css
1021
+
1022
+ // Feature's __append__global.css:
1023
+ :root {
1024
+ /* Salesforce Design Tokens */
1025
+ --electric-blue-50: #066afe;
1026
+ --constant-white: #ffffff;
1027
+ }
1028
+
1029
+ button {
1030
+ background-color: var(--electric-blue-50);
1031
+ color: var(--constant-white);
1032
+ }
1033
+
1034
+ // Final result in target app's global.css:
1035
+ @import "tailwindcss"; // ← Base content
1036
+
1037
+ body {
1038
+ @apply antialiased; // ← Base content
1039
+ }
1040
+
1041
+ :root {
1042
+ /* Salesforce Design Tokens */
1043
+ --electric-blue-50: #066afe; // ← Appended content
1044
+ --constant-white: #ffffff; // ← Appended content
1045
+ }
1046
+
1047
+ button {
1048
+ background-color: var(--electric-blue-50); // ← Appended content
1049
+ color: var(--constant-white); // ← Appended content
1050
+ }
1051
+ ```
1052
+
1053
+ ## Route Merging Strategy
1054
+
1055
+ The `merge` change type with `routes` strategy intelligently combines route definitions from features with base app routes.
1056
+
1057
+ ### How It Works
1058
+
1059
+ **Replace-Matching with Deep Children Merge:**
1060
+
1061
+ 1. **Top-Level Routes:**
1062
+ - Routes with the **same path** → Merge their children arrays
1063
+ - Routes with **different paths** → Add feature route to result
1064
+
1065
+ 2. **Children Array Merging** (when parent paths match):
1066
+ - Index routes (`index: true`) → Feature replaces base if both exist
1067
+ - Named routes (`path: 'about'`) → Feature replaces base if paths match
1068
+ - **New routes** → Added to children array
1069
+ - **Base routes not in feature** → Preserved
1070
+
1071
+ 3. **Route Deletion:**
1072
+ - Routes with path starting with `__delete__` → Remove matching route from result
1073
+ - Example: `path: '__delete__new'` removes the route with `path: 'new'`
1074
+ - Throws error if route to delete doesn't exist
1075
+
1076
+ 4. **Recursion:**
1077
+ - Applies same logic to nested children arrays
1078
+
1079
+ ### Example
1080
+
1081
+ **Base App Routes**:
1082
+ ```typescript
1083
+ export const routes: RouteObject[] = [
1084
+ {
1085
+ path: '/',
1086
+ element: <AppLayout />,
1087
+ children: [
1088
+ {
1089
+ index: true,
1090
+ element: <Home />,
1091
+ handle: { showInNavigation: true, label: 'Home' }
1092
+ }
1093
+ ]
1094
+ }
1095
+ ]
1096
+ ```
1097
+
1098
+ **Feature Routes** (in `template/digitalExperiences/webApplications/<feature-name>/src/routes.tsx`):
1099
+ ```typescript
1100
+ export const routes: RouteObject[] = [
1101
+ {
1102
+ path: '/',
1103
+ element: <AppLayout />,
1104
+ children: [
1105
+ {
1106
+ path: 'about',
1107
+ element: <About />,
1108
+ handle: { showInNavigation: true, label: 'About' }
1109
+ },
1110
+ {
1111
+ path: 'contact',
1112
+ element: <Contact />,
1113
+ handle: { showInNavigation: false }
1114
+ }
1115
+ ]
1116
+ }
1117
+ ]
1118
+ ```
1119
+
1120
+ **Merged Result** (after applying feature):
1121
+ ```typescript
1122
+ export const routes: RouteObject[] = [
1123
+ {
1124
+ path: '/',
1125
+ element: <AppLayout />, // Uses feature's element
1126
+ children: [
1127
+ {
1128
+ index: true,
1129
+ element: <Home />, // ✓ Preserved from base
1130
+ handle: { showInNavigation: true, label: 'Home' }
1131
+ },
1132
+ {
1133
+ path: 'about',
1134
+ element: <About />, // ✓ Added from feature
1135
+ handle: { showInNavigation: true, label: 'About' }
1136
+ },
1137
+ {
1138
+ path: 'contact',
1139
+ element: <Contact />, // ✓ Added from feature
1140
+ handle: { showInNavigation: false }
1141
+ }
1142
+ ]
1143
+ }
1144
+ ]
1145
+ ```
1146
+
1147
+ ### Key Benefits
1148
+
1149
+ - **Preserves existing routes**: Base app's Home route stays intact
1150
+ - **Adds new routes**: Feature routes (About, Contact) are added
1151
+ - **No duplication**: Routes with matching paths are replaced, not duplicated
1152
+ - **Deep merging**: Works with nested route structures
1153
+ - **Multiple features**: Apply multiple route-adding features sequentially
1154
+
1155
+ Route merging happens automatically for `routes.tsx` files.
1156
+
1157
+ ### Deleting Routes
1158
+
1159
+ Features can delete routes from the base app or previously applied features by using the `__delete__` prefix in the route path. This is useful when a feature needs to remove routes that were added by dependencies or the base app.
1160
+
1161
+ #### How It Works
1162
+
1163
+ 1. Add a route with `path: '__delete__<route-name>'` in your feature's routes file
1164
+ 2. The route with the matching path (without the prefix) will be removed during merging
1165
+ 3. If the route doesn't exist, an error will be thrown
1166
+
1167
+ #### Example
1168
+
1169
+ **Base/Previous Routes:**
1170
+ ```typescript
1171
+ export const routes: RouteObject[] = [
1172
+ {
1173
+ path: '/',
1174
+ element: <AppLayout />,
1175
+ children: [
1176
+ { index: true, element: <Home /> },
1177
+ { path: 'about', element: <About /> },
1178
+ { path: 'new', element: <New /> }
1179
+ ]
1180
+ }
1181
+ ]
1182
+ ```
1183
+
1184
+ **Feature Routes (deleting 'new'):**
1185
+ ```typescript
1186
+ export const routes: RouteObject[] = [
1187
+ {
1188
+ path: '/',
1189
+ children: [
1190
+ {
1191
+ path: '__delete__new',
1192
+ element: <></> // Element value is ignored for deletion markers
1193
+ }
1194
+ ]
1195
+ }
1196
+ ]
1197
+ ```
1198
+
1199
+ **Merged Result:**
1200
+ ```typescript
1201
+ export const routes: RouteObject[] = [
1202
+ {
1203
+ path: '/',
1204
+ element: <AppLayout />,
1205
+ children: [
1206
+ { index: true, element: <Home /> }, // ✓ Preserved
1207
+ { path: 'about', element: <About /> } // ✓ Preserved
1208
+ // 'new' route deleted ✓
1209
+ ]
1210
+ }
1211
+ ]
1212
+ ```
1213
+
1214
+ #### Validation
1215
+
1216
+ - **Error if route doesn't exist**: The CLI will throw an error if you attempt to delete a route that doesn't exist in the current routes
1217
+ - This prevents silent failures and ensures routes are being deleted as expected
1218
+
1219
+ #### Use Cases
1220
+
1221
+ - **Remove dependency routes**: Delete routes added by feature dependencies that aren't needed
1222
+ - **Clean up base routes**: Remove placeholder or example routes from the base app
1223
+ - **Override parent features**: Child features can remove routes added by parent features they depend on
1224
+
1225
+ For example, a feature that provides a single-page app might delete all routes from the base app to start fresh.
1226
+
1227
+ ### Automatic Import Cleanup for Deleted Files
1228
+
1229
+ When using route deletion (or file deletion with `__delete__` prefix), imports from deleted files are automatically removed during route merging. This prevents broken import references in the final merged code.
1230
+
1231
+ #### How It Works
1232
+
1233
+ 1. During route merging, after all imports are merged from the feature file
1234
+ 2. The import merger scans all import statements in the target file
1235
+ 3. Any imports with `__delete__` in the module specifier are automatically removed
1236
+ 4. This ensures deleted components don't leave broken import references
1237
+
1238
+ #### Example
1239
+
1240
+ **Feature Routes File:**
1241
+ ```typescript
1242
+ import type { RouteObject } from "react-router";
1243
+ import AppLayout from "./__inherit__appLayout";
1244
+ import Home from ".";
1245
+ import New from "./__delete__new"; // Import from deleted file
1246
+
1247
+ export const routes: RouteObject[] = [
1248
+ {
1249
+ path: '/',
1250
+ element: <AppLayout />,
1251
+ children: [
1252
+ {
1253
+ index: true,
1254
+ element: <Home />,
1255
+ handle: { showInNavigation: true, label: 'Home' }
1256
+ },
1257
+ {
1258
+ path: '__delete__new', // Delete the 'new' route
1259
+ element: <New />,
1260
+ }
1261
+ ]
1262
+ }
1263
+ ]
1264
+ ```
1265
+
1266
+ **Merged Result:**
1267
+ ```typescript
1268
+ import type { RouteObject } from "react-router";
1269
+ import AppLayout from "./appLayout";
1270
+ import Home from ".";
1271
+ // ✓ Import from ./__delete__new automatically removed
1272
+
1273
+ export const routes: RouteObject[] = [
1274
+ {
1275
+ path: '/',
1276
+ element: <AppLayout />,
1277
+ children: [
1278
+ {
1279
+ index: true,
1280
+ element: <Home />,
1281
+ handle: { showInNavigation: true, label: 'Home' }
1282
+ }
1283
+ // ✓ 'new' route deleted
1284
+ ]
1285
+ }
1286
+ ]
1287
+ ```
1288
+
1289
+ #### Key Benefits
1290
+
1291
+ - **No broken imports**: Automatically removes imports from deleted files
1292
+ - **Clean merged code**: Final output doesn't reference non-existent files
1293
+ - **Works with file deletion**: Applies to any file marked with `__delete__` prefix
1294
+ - **Seamless integration**: Happens automatically during route merging, no manual cleanup needed
1295
+
1296
+ ## Testing
1297
+
1298
+ The CLI has a comprehensive test suite using Vitest, including E2E tests with gold files and unit tests for critical utilities.
1299
+
1300
+ **Test Coverage:**
1301
+ - ✅ 12 E2E test scenarios covering all major CLI workflows
1302
+ - ✅ 46+ unit tests covering route merging, import merging, and file operations
1303
+ - ✅ Gold file comparison for E2E validation
1304
+ - ✅ No skipped tests - all functionality fully tested
1305
+
1306
+ ### Running Tests
1307
+
1308
+ ```bash
1309
+ # Run all tests
1310
+ npm test
1311
+
1312
+ # Run with UI
1313
+ yarn test:ui
1314
+
1315
+ # Run E2E tests only
1316
+ yarn test:e2e
1317
+
1318
+ # Run unit tests only
1319
+ yarn test:unit
1320
+
1321
+ # Run with coverage
1322
+ yarn test:coverage
1323
+ ```
1324
+
1325
+ ### Test Structure
1326
+
1327
+ ```
1328
+ packages/cli/
1329
+ ├── test/
1330
+ │ ├── e2e/ # End-to-end tests
1331
+ │ │ ├── fixtures/ # Test fixtures (base apps, features)
1332
+ │ │ ├── gold/ # Expected outputs for E2E tests
1333
+ │ │ └── apply-patches.spec.ts
1334
+ │ ├── unit/ # Unit tests
1335
+ │ │ ├── route-merger.spec.ts
1336
+ │ │ ├── import-merger.spec.ts
1337
+ │ │ └── file-operations.spec.ts
1338
+ │ └── helpers/ # Test utilities
1339
+ │ ├── compare-directories.ts
1340
+ │ ├── create-temp-dir.ts
1341
+ │ └── fixtures.ts
1342
+ ```
1343
+
1344
+ ### E2E Tests
1345
+
1346
+ E2E tests verify complete CLI workflows using gold files (expected outputs). Tests cover:
1347
+
1348
+ - Simple feature application (adding routes/files)
1349
+ - File deletion with `__delete__` prefix
1350
+ - Route deletion and import cleanup
1351
+ - Feature dependency resolution
1352
+ - Complex operations (`__inherit__`, `__prepend__`, `__append__`)
1353
+ - Error handling and validation
1354
+
1355
+ ### Unit Tests
1356
+
1357
+ Unit tests focus on individual modules:
1358
+
1359
+ - **route-merger**: Route merging logic and deletion
1360
+ - **import-merger**: Import statement merging, deduplication, type imports, and formatting
1361
+ - **file-operations**: File deletion, prepending, and appending
1362
+
1363
+ ### Updating Gold Files
1364
+
1365
+ When intentionally changing CLI behavior, update gold files:
1366
+
1367
+ ```bash
1368
+ UPDATE_GOLD=1 npm test
1369
+ ```
1370
+
1371
+ ⚠️ **Warning**: Only update gold files after verifying the new output is correct!
1372
+
1373
+ ### Creating New Tests
1374
+
1375
+ **E2E Test Example**:
1376
+ ```typescript
1377
+ it('should apply a simple feature correctly', async () => {
1378
+ const outputDir = copyFixture('base-app', join(tempDir, 'output'));
1379
+ const featurePath = getFixturePath('feature-simple');
1380
+
1381
+ await applyPatchesCommand(featurePath, outputDir, {
1382
+ skipDependencyChanges: true
1383
+ });
1384
+
1385
+ const goldDir = getGoldPath('simple-apply');
1386
+ const differences = compareOrUpdate(outputDir, goldDir);
1387
+
1388
+ expect(differences).toEqual([]);
1389
+ });
1390
+ ```
1391
+
1392
+ **Unit Test Example**:
1393
+ ```typescript
1394
+ it('should merge routes correctly', () => {
1395
+ const project = new Project({ useInMemoryFileSystem: true });
1396
+
1397
+ const targetFile = project.createSourceFile('target.tsx', `...`);
1398
+ const featureFile = project.createSourceFile('feature.tsx', `...`);
1399
+
1400
+ const result = mergeRoutes('feature.tsx', 'target.tsx', project);
1401
+
1402
+ expect(result).toContain('expected content');
1403
+ });
1404
+ ```
1405
+
1406
+ ## Development
1407
+
1408
+ ```bash
1409
+ # Build the CLI
1410
+ yarn build
1411
+
1412
+ # Run without building (development)
1413
+ yarn dev -- <feature> <base-app>
1414
+ ```