@sfdc-webapps/cli 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (405) hide show
  1. package/README.md +1414 -0
  2. package/dist/commands/apply-patches.d.ts +23 -0
  3. package/dist/commands/apply-patches.d.ts.map +1 -0
  4. package/dist/commands/apply-patches.js +524 -0
  5. package/dist/commands/apply-patches.js.map +1 -0
  6. package/dist/commands/new-app-feature.d.ts +7 -0
  7. package/dist/commands/new-app-feature.d.ts.map +1 -0
  8. package/dist/commands/new-app-feature.js +166 -0
  9. package/dist/commands/new-app-feature.js.map +1 -0
  10. package/dist/commands/new-app.d.ts +1 -0
  11. package/dist/commands/new-app.d.ts.map +1 -0
  12. package/dist/commands/new-app.js +2 -0
  13. package/dist/commands/new-app.js.map +1 -0
  14. package/dist/commands/watch-patches.d.ts +6 -0
  15. package/dist/commands/watch-patches.d.ts.map +1 -0
  16. package/dist/commands/watch-patches.js +152 -0
  17. package/dist/commands/watch-patches.js.map +1 -0
  18. package/dist/core/dependency-resolver.d.ts +40 -0
  19. package/dist/core/dependency-resolver.d.ts.map +1 -0
  20. package/dist/core/dependency-resolver.js +122 -0
  21. package/dist/core/dependency-resolver.js.map +1 -0
  22. package/dist/core/file-operations.d.ts +37 -0
  23. package/dist/core/file-operations.d.ts.map +1 -0
  24. package/dist/core/file-operations.js +201 -0
  25. package/dist/core/file-operations.js.map +1 -0
  26. package/dist/core/package-json-merger.d.ts +30 -0
  27. package/dist/core/package-json-merger.d.ts.map +1 -0
  28. package/dist/core/package-json-merger.js +104 -0
  29. package/dist/core/package-json-merger.js.map +1 -0
  30. package/dist/core/patch-loader.d.ts +17 -0
  31. package/dist/core/patch-loader.d.ts.map +1 -0
  32. package/dist/core/patch-loader.js +100 -0
  33. package/dist/core/patch-loader.js.map +1 -0
  34. package/dist/index.d.ts +3 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +90 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/types.d.ts +21 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +2 -0
  41. package/dist/types.js.map +1 -0
  42. package/dist/utils/debounce.d.ts +6 -0
  43. package/dist/utils/debounce.d.ts.map +1 -0
  44. package/dist/utils/debounce.js +19 -0
  45. package/dist/utils/debounce.js.map +1 -0
  46. package/dist/utils/import-merger.d.ts +12 -0
  47. package/dist/utils/import-merger.d.ts.map +1 -0
  48. package/dist/utils/import-merger.js +240 -0
  49. package/dist/utils/import-merger.js.map +1 -0
  50. package/dist/utils/logger.d.ts +6 -0
  51. package/dist/utils/logger.d.ts.map +1 -0
  52. package/dist/utils/logger.js +17 -0
  53. package/dist/utils/logger.js.map +1 -0
  54. package/dist/utils/path-mappings.d.ts +88 -0
  55. package/dist/utils/path-mappings.d.ts.map +1 -0
  56. package/dist/utils/path-mappings.js +138 -0
  57. package/dist/utils/path-mappings.js.map +1 -0
  58. package/dist/utils/paths.d.ts +43 -0
  59. package/dist/utils/paths.d.ts.map +1 -0
  60. package/dist/utils/paths.js +107 -0
  61. package/dist/utils/paths.js.map +1 -0
  62. package/dist/utils/route-merger.d.ts +107 -0
  63. package/dist/utils/route-merger.d.ts.map +1 -0
  64. package/dist/utils/route-merger.js +303 -0
  65. package/dist/utils/route-merger.js.map +1 -0
  66. package/dist/utils/validation.d.ts +29 -0
  67. package/dist/utils/validation.d.ts.map +1 -0
  68. package/dist/utils/validation.js +109 -0
  69. package/dist/utils/validation.js.map +1 -0
  70. package/package.json +39 -0
  71. package/src/commands/apply-patches.ts +594 -0
  72. package/src/commands/new-app-feature.ts +203 -0
  73. package/src/commands/new-app.ts +0 -0
  74. package/src/commands/watch-patches.ts +173 -0
  75. package/src/core/dependency-resolver.ts +175 -0
  76. package/src/core/file-operations.ts +265 -0
  77. package/src/core/package-json-merger.ts +129 -0
  78. package/src/core/patch-loader.ts +128 -0
  79. package/src/index.ts +95 -0
  80. package/src/types.ts +23 -0
  81. package/src/utils/debounce.ts +23 -0
  82. package/src/utils/import-merger.ts +293 -0
  83. package/src/utils/logger.ts +21 -0
  84. package/src/utils/path-mappings.ts +154 -0
  85. package/src/utils/paths.ts +121 -0
  86. package/src/utils/route-merger.ts +357 -0
  87. package/src/utils/validation.ts +150 -0
  88. package/test/e2e/E2E_TEST_FIXTURES.md +509 -0
  89. package/test/e2e/apply-patches.spec.ts +861 -0
  90. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/package.json +10 -0
  91. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/appLayout.tsx +27 -0
  92. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/home.tsx +8 -0
  93. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/index.tsx +10 -0
  94. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/old-page.tsx +8 -0
  95. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/routes.tsx +23 -0
  96. package/test/e2e/fixtures/base-app/digitalExperiences/webApplications/base-app/src/styles/global.css +14 -0
  97. package/test/e2e/fixtures/basic-operations/dep-chain-linear/feature.ts +11 -0
  98. package/test/e2e/fixtures/basic-operations/dep-chain-linear/template/digitalExperiences/webApplications/dep-chain-linear/src/__inherit__appLayout.tsx +27 -0
  99. package/test/e2e/fixtures/basic-operations/dep-chain-linear/template/digitalExperiences/webApplications/dep-chain-linear/src/contact.tsx +8 -0
  100. package/test/e2e/fixtures/basic-operations/dep-chain-linear/template/digitalExperiences/webApplications/dep-chain-linear/src/routes.tsx +17 -0
  101. package/test/e2e/fixtures/basic-operations/feature-file-inherit-route-add/feature.ts +10 -0
  102. package/test/e2e/fixtures/basic-operations/feature-file-inherit-route-add/template/digitalExperiences/webApplications/feature-file-inherit-route-add/src/__inherit__appLayout.tsx +27 -0
  103. package/test/e2e/fixtures/basic-operations/feature-file-inherit-route-add/template/digitalExperiences/webApplications/feature-file-inherit-route-add/src/__inherit__home.tsx +0 -0
  104. package/test/e2e/fixtures/basic-operations/feature-file-inherit-route-add/template/digitalExperiences/webApplications/feature-file-inherit-route-add/src/about.tsx +8 -0
  105. package/test/e2e/fixtures/basic-operations/feature-file-inherit-route-add/template/digitalExperiences/webApplications/feature-file-inherit-route-add/src/routes.tsx +17 -0
  106. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/feature.ts +10 -0
  107. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/template/digitalExperiences/webApplications/feature-file-prepend-append/src/__inherit__appLayout.tsx +27 -0
  108. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/template/digitalExperiences/webApplications/feature-file-prepend-append/src/__prepend__index.tsx +2 -0
  109. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/template/digitalExperiences/webApplications/feature-file-prepend-append/src/routes.tsx +17 -0
  110. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/template/digitalExperiences/webApplications/feature-file-prepend-append/src/settings.tsx +9 -0
  111. package/test/e2e/fixtures/basic-operations/feature-file-prepend-append/template/digitalExperiences/webApplications/feature-file-prepend-append/src/styles/__append__global.css +10 -0
  112. package/test/e2e/fixtures/basic-operations/feature-file-route-delete/feature.ts +10 -0
  113. package/test/e2e/fixtures/basic-operations/feature-file-route-delete/template/digitalExperiences/webApplications/feature-file-route-delete/src/__delete__old-page.tsx +2 -0
  114. package/test/e2e/fixtures/basic-operations/feature-file-route-delete/template/digitalExperiences/webApplications/feature-file-route-delete/src/__inherit__appLayout.tsx +27 -0
  115. package/test/e2e/fixtures/basic-operations/feature-file-route-delete/template/digitalExperiences/webApplications/feature-file-route-delete/src/routes.tsx +16 -0
  116. package/test/e2e/fixtures/composition-5-features/5-features-about/feature.ts +3 -0
  117. package/test/e2e/fixtures/composition-5-features/5-features-about/template/digitalExperiences/webApplications/5-features-about/src/about.tsx +8 -0
  118. package/test/e2e/fixtures/composition-5-features/5-features-about/template/digitalExperiences/webApplications/5-features-about/src/routes.tsx +17 -0
  119. package/test/e2e/fixtures/composition-5-features/5-features-analytics/feature.ts +3 -0
  120. package/test/e2e/fixtures/composition-5-features/5-features-analytics/template/digitalExperiences/webApplications/5-features-analytics/src/__prepend__index.tsx +2 -0
  121. package/test/e2e/fixtures/composition-5-features/5-features-footer/feature.ts +3 -0
  122. package/test/e2e/fixtures/composition-5-features/5-features-footer/template/digitalExperiences/webApplications/5-features-footer/src/footer.tsx +7 -0
  123. package/test/e2e/fixtures/composition-5-features/5-features-footer/template/digitalExperiences/webApplications/5-features-footer/src/routes.tsx +17 -0
  124. package/test/e2e/fixtures/composition-5-features/5-features-navigation/feature.ts +3 -0
  125. package/test/e2e/fixtures/composition-5-features/5-features-navigation/template/digitalExperiences/webApplications/5-features-navigation/src/navigation.tsx +7 -0
  126. package/test/e2e/fixtures/composition-5-features/5-features-navigation/template/digitalExperiences/webApplications/5-features-navigation/src/routes.tsx +17 -0
  127. package/test/e2e/fixtures/composition-5-features/5-features-theme/feature.ts +3 -0
  128. package/test/e2e/fixtures/composition-5-features/5-features-theme/template/digitalExperiences/webApplications/5-features-theme/src/styles/__append__global.css +5 -0
  129. package/test/e2e/fixtures/composition-append/composition-append-1/feature.ts +10 -0
  130. package/test/e2e/fixtures/composition-append/composition-append-1/template/digitalExperiences/webApplications/composition-append-1/src/styles/__append__global.css +4 -0
  131. package/test/e2e/fixtures/composition-append/composition-append-2/feature.ts +10 -0
  132. package/test/e2e/fixtures/composition-append/composition-append-2/template/digitalExperiences/webApplications/composition-append-2/src/styles/__append__global.css +4 -0
  133. package/test/e2e/fixtures/composition-append/composition-append-3/feature.ts +10 -0
  134. package/test/e2e/fixtures/composition-append/composition-append-3/template/digitalExperiences/webApplications/composition-append-3/src/styles/__append__global.css +4 -0
  135. package/test/e2e/fixtures/composition-inherit-modify/composition-inherit-layout/feature.ts +3 -0
  136. package/test/e2e/fixtures/composition-inherit-modify/composition-inherit-layout/template/digitalExperiences/webApplications/composition-inherit-layout/src/__inherit__appLayout.tsx +1 -0
  137. package/test/e2e/fixtures/composition-inherit-modify/composition-modify-layout/feature.ts +3 -0
  138. package/test/e2e/fixtures/composition-inherit-modify/composition-modify-layout/template/digitalExperiences/webApplications/composition-modify-layout/src/__append__appLayout.tsx +2 -0
  139. package/test/e2e/fixtures/composition-prepend/composition-prepend-1/feature.ts +10 -0
  140. package/test/e2e/fixtures/composition-prepend/composition-prepend-1/template/digitalExperiences/webApplications/composition-prepend-1/src/__prepend__index.tsx +2 -0
  141. package/test/e2e/fixtures/composition-prepend/composition-prepend-2/feature.ts +10 -0
  142. package/test/e2e/fixtures/composition-prepend/composition-prepend-2/template/digitalExperiences/webApplications/composition-prepend-2/src/__prepend__index.tsx +2 -0
  143. package/test/e2e/fixtures/composition-prepend/composition-prepend-3/feature.ts +10 -0
  144. package/test/e2e/fixtures/composition-prepend/composition-prepend-3/template/digitalExperiences/webApplications/composition-prepend-3/src/__prepend__index.tsx +2 -0
  145. package/test/e2e/fixtures/dep-chain-long/dep-chain-a/feature.ts +11 -0
  146. package/test/e2e/fixtures/dep-chain-long/dep-chain-a/template/digitalExperiences/webApplications/dep-chain-a/src/page-a.tsx +8 -0
  147. package/test/e2e/fixtures/dep-chain-long/dep-chain-a/template/digitalExperiences/webApplications/dep-chain-a/src/routes.tsx +17 -0
  148. package/test/e2e/fixtures/dep-chain-long/dep-chain-b/feature.ts +11 -0
  149. package/test/e2e/fixtures/dep-chain-long/dep-chain-b/template/digitalExperiences/webApplications/dep-chain-b/src/page-b.tsx +8 -0
  150. package/test/e2e/fixtures/dep-chain-long/dep-chain-b/template/digitalExperiences/webApplications/dep-chain-b/src/routes.tsx +17 -0
  151. package/test/e2e/fixtures/dep-chain-long/dep-chain-c/feature.ts +11 -0
  152. package/test/e2e/fixtures/dep-chain-long/dep-chain-c/template/digitalExperiences/webApplications/dep-chain-c/src/page-c.tsx +8 -0
  153. package/test/e2e/fixtures/dep-chain-long/dep-chain-c/template/digitalExperiences/webApplications/dep-chain-c/src/routes.tsx +17 -0
  154. package/test/e2e/fixtures/dep-chain-long/dep-chain-d/feature.ts +10 -0
  155. package/test/e2e/fixtures/dep-chain-long/dep-chain-d/template/digitalExperiences/webApplications/dep-chain-d/src/page-d.tsx +8 -0
  156. package/test/e2e/fixtures/dep-chain-long/dep-chain-d/template/digitalExperiences/webApplications/dep-chain-d/src/routes.tsx +17 -0
  157. package/test/e2e/fixtures/dep-circular/dep-circular-a/feature.ts +11 -0
  158. package/test/e2e/fixtures/dep-circular/dep-circular-a/template/digitalExperiences/webApplications/dep-circular-a/src/page-a.tsx +8 -0
  159. package/test/e2e/fixtures/dep-circular/dep-circular-b/feature.ts +11 -0
  160. package/test/e2e/fixtures/dep-circular/dep-circular-b/template/digitalExperiences/webApplications/dep-circular-b/src/page-b.tsx +8 -0
  161. package/test/e2e/fixtures/dep-complex-graph/README.md +119 -0
  162. package/test/e2e/fixtures/dep-complex-graph/create-placeholders.sh +25 -0
  163. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/feature.ts +14 -0
  164. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/template/digitalExperiences/webApplications/dep-complex-graph-auth/src/__delete__old-page.tsx +1 -0
  165. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/template/digitalExperiences/webApplications/dep-complex-graph-auth/src/__inherit__appLayout.tsx +1 -0
  166. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/template/digitalExperiences/webApplications/dep-complex-graph-auth/src/login.tsx +8 -0
  167. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/template/digitalExperiences/webApplications/dep-complex-graph-auth/src/routes.tsx +29 -0
  168. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-auth/template/digitalExperiences/webApplications/dep-complex-graph-auth/src/signup.tsx +1 -0
  169. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/feature.ts +14 -0
  170. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/template/digitalExperiences/webApplications/dep-complex-graph-dashboard/src/__inherit__appLayout.tsx +1 -0
  171. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/template/digitalExperiences/webApplications/dep-complex-graph-dashboard/src/analytics.tsx +1 -0
  172. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/template/digitalExperiences/webApplications/dep-complex-graph-dashboard/src/dashboard.tsx +8 -0
  173. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/template/digitalExperiences/webApplications/dep-complex-graph-dashboard/src/reports.tsx +1 -0
  174. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-dashboard/template/digitalExperiences/webApplications/dep-complex-graph-dashboard/src/routes.tsx +32 -0
  175. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-profile/feature.ts +14 -0
  176. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-profile/template/digitalExperiences/webApplications/dep-complex-graph-profile/src/__inherit__appLayout.tsx +1 -0
  177. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-profile/template/digitalExperiences/webApplications/dep-complex-graph-profile/src/profile-edit.tsx +1 -0
  178. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-profile/template/digitalExperiences/webApplications/dep-complex-graph-profile/src/profile.tsx +8 -0
  179. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-profile/template/digitalExperiences/webApplications/dep-complex-graph-profile/src/routes.tsx +26 -0
  180. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-components/feature.ts +14 -0
  181. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-components/template/digitalExperiences/webApplications/dep-complex-graph-shared-components/src/__inherit__appLayout.tsx +1 -0
  182. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-components/template/digitalExperiences/webApplications/dep-complex-graph-shared-components/src/components.tsx +8 -0
  183. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-components/template/digitalExperiences/webApplications/dep-complex-graph-shared-components/src/routes.tsx +18 -0
  184. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-types/feature.ts +10 -0
  185. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-types/template/digitalExperiences/webApplications/dep-complex-graph-shared-types/src/__inherit__appLayout.tsx +1 -0
  186. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-types/template/digitalExperiences/webApplications/dep-complex-graph-shared-types/src/routes.tsx +17 -0
  187. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-types/template/digitalExperiences/webApplications/dep-complex-graph-shared-types/src/types.tsx +8 -0
  188. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-utils/feature.ts +10 -0
  189. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-utils/template/digitalExperiences/webApplications/dep-complex-graph-shared-utils/src/__inherit__appLayout.tsx +1 -0
  190. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-utils/template/digitalExperiences/webApplications/dep-complex-graph-shared-utils/src/routes.tsx +18 -0
  191. package/test/e2e/fixtures/dep-complex-graph/dep-complex-graph-shared-utils/template/digitalExperiences/webApplications/dep-complex-graph-shared-utils/src/tools.tsx +1 -0
  192. package/test/e2e/fixtures/dep-complex-graph/feature.ts +15 -0
  193. package/test/e2e/fixtures/dep-complex-graph/template/digitalExperiences/webApplications/dep-complex-graph/src/__inherit__appLayout.tsx +1 -0
  194. package/test/e2e/fixtures/dep-complex-graph/template/digitalExperiences/webApplications/dep-complex-graph/src/admin-dashboard.tsx +1 -0
  195. package/test/e2e/fixtures/dep-complex-graph/template/digitalExperiences/webApplications/dep-complex-graph/src/admin-layout.tsx +1 -0
  196. package/test/e2e/fixtures/dep-complex-graph/template/digitalExperiences/webApplications/dep-complex-graph/src/routes.tsx +24 -0
  197. package/test/e2e/fixtures/dep-diamond/dep-diamond-left/feature.ts +11 -0
  198. package/test/e2e/fixtures/dep-diamond/dep-diamond-left/template/digitalExperiences/webApplications/dep-diamond-left/src/left-page.tsx +8 -0
  199. package/test/e2e/fixtures/dep-diamond/dep-diamond-left/template/digitalExperiences/webApplications/dep-diamond-left/src/routes.tsx +17 -0
  200. package/test/e2e/fixtures/dep-diamond/dep-diamond-right/feature.ts +11 -0
  201. package/test/e2e/fixtures/dep-diamond/dep-diamond-right/template/digitalExperiences/webApplications/dep-diamond-right/src/right-page.tsx +8 -0
  202. package/test/e2e/fixtures/dep-diamond/dep-diamond-right/template/digitalExperiences/webApplications/dep-diamond-right/src/routes.tsx +17 -0
  203. package/test/e2e/fixtures/dep-diamond/dep-diamond-shared/feature.ts +10 -0
  204. package/test/e2e/fixtures/dep-diamond/dep-diamond-shared/template/digitalExperiences/webApplications/dep-diamond-shared/src/routes.tsx +17 -0
  205. package/test/e2e/fixtures/dep-diamond/dep-diamond-shared/template/digitalExperiences/webApplications/dep-diamond-shared/src/shared-utils.tsx +8 -0
  206. package/test/e2e/fixtures/edges/edge-binary-files/feature.ts +13 -0
  207. package/test/e2e/fixtures/edges/edge-binary-files/template/digitalExperiences/webApplications/edge-binary-files/src/__inherit__appLayout.tsx +1 -0
  208. package/test/e2e/fixtures/edges/edge-binary-files/template/digitalExperiences/webApplications/edge-binary-files/src/assets/test-image.png +0 -0
  209. package/test/e2e/fixtures/edges/edge-binary-files/template/digitalExperiences/webApplications/edge-binary-files/src/placeholder.tsx +0 -0
  210. package/test/e2e/fixtures/edges/edge-conflicting-prefixes/feature.ts +13 -0
  211. package/test/e2e/fixtures/edges/edge-conflicting-prefixes/template/digitalExperiences/webApplications/edge-conflicting-prefixes/src/__inherit____delete__conflicting.tsx +5 -0
  212. package/test/e2e/fixtures/edges/edge-conflicting-prefixes/template/digitalExperiences/webApplications/edge-conflicting-prefixes/src/placeholder.tsx +0 -0
  213. package/test/e2e/fixtures/edges/edge-empty-feature/feature.ts +16 -0
  214. package/test/e2e/fixtures/edges/edge-empty-feature/template/digitalExperiences/webApplications/edge-empty-feature/src/placeholder.tsx +0 -0
  215. package/test/e2e/fixtures/edges/edge-invalid-routes/feature.ts +12 -0
  216. package/test/e2e/fixtures/edges/edge-invalid-routes/template/digitalExperiences/webApplications/edge-invalid-routes/src/placeholder.tsx +0 -0
  217. package/test/e2e/fixtures/edges/edge-invalid-routes/template/digitalExperiences/webApplications/edge-invalid-routes/src/routes.tsx +8 -0
  218. package/test/e2e/fixtures/edges/edge-line-endings/feature.ts +13 -0
  219. package/test/e2e/fixtures/edges/edge-line-endings/template/digitalExperiences/webApplications/edge-line-endings/src/__prepend__index.tsx +2 -0
  220. package/test/e2e/fixtures/edges/edge-line-endings/template/digitalExperiences/webApplications/edge-line-endings/src/crlf.tsx +4 -0
  221. package/test/e2e/fixtures/edges/edge-line-endings/template/digitalExperiences/webApplications/edge-line-endings/src/placeholder.tsx +0 -0
  222. package/test/e2e/fixtures/edges/edge-line-endings/template/digitalExperiences/webApplications/edge-line-endings/src/styles/__append__global.css +4 -0
  223. package/test/e2e/fixtures/edges/edge-missing-base-files/feature.ts +13 -0
  224. package/test/e2e/fixtures/edges/edge-missing-base-files/template/digitalExperiences/webApplications/edge-missing-base-files/src/__inherit__missing.tsx +1 -0
  225. package/test/e2e/fixtures/edges/edge-missing-base-files/template/digitalExperiences/webApplications/edge-missing-base-files/src/__prepend__nonexistent.tsx +2 -0
  226. package/test/e2e/fixtures/edges/edge-missing-base-files/template/digitalExperiences/webApplications/edge-missing-base-files/src/placeholder.tsx +0 -0
  227. package/test/e2e/fixtures/edges/edge-package-json-conflicts-a/feature.ts +14 -0
  228. package/test/e2e/fixtures/edges/edge-package-json-conflicts-a/template/digitalExperiences/webApplications/edge-package-json-conflicts-a/src/placeholder.tsx +1 -0
  229. package/test/e2e/fixtures/edges/edge-package-json-conflicts-b/feature.ts +14 -0
  230. package/test/e2e/fixtures/edges/edge-package-json-conflicts-b/template/digitalExperiences/webApplications/edge-package-json-conflicts-b/src/placeholder.tsx +1 -0
  231. package/test/e2e/fixtures/edges/edge-performance-many/feature.ts +26 -0
  232. package/test/e2e/fixtures/edges/edge-performance-many/template/digitalExperiences/webApplications/edge-performance-many/src/placeholder.tsx +0 -0
  233. package/test/e2e/fixtures/path-mappings/feature-custom-mapping/feature.ts +16 -0
  234. package/test/e2e/fixtures/path-mappings/feature-custom-mapping/template/web/src/custom-component.tsx +3 -0
  235. package/test/e2e/fixtures/path-mappings/feature-custom-mapping/template/web/src/routes.tsx +8 -0
  236. package/test/e2e/fixtures/path-mappings/feature-mixed-paths/feature.ts +7 -0
  237. package/test/e2e/fixtures/path-mappings/feature-mixed-paths/template/digitalExperiences/webApplications/feature-mixed-paths/assets/icon.svg +3 -0
  238. package/test/e2e/fixtures/path-mappings/feature-mixed-paths/template/webApp/src/mapped-component.tsx +3 -0
  239. package/test/e2e/fixtures/path-mappings/feature-mixed-paths/template/webApp/src/routes.tsx +8 -0
  240. package/test/e2e/fixtures/path-mappings/feature-with-webapp-mapping/feature.ts +7 -0
  241. package/test/e2e/fixtures/path-mappings/feature-with-webapp-mapping/template/webApp/src/app.tsx +3 -0
  242. package/test/e2e/fixtures/path-mappings/feature-with-webapp-mapping/template/webApp/src/routes.tsx +8 -0
  243. package/test/e2e/fixtures/path-mappings/feature-without-mapping/feature.ts +11 -0
  244. package/test/e2e/fixtures/path-mappings/feature-without-mapping/template/digitalExperiences/webApplications/feature-without-mapping/src/legacy-component.tsx +3 -0
  245. package/test/e2e/fixtures/path-mappings/feature-without-mapping/template/digitalExperiences/webApplications/feature-without-mapping/src/routes.tsx +8 -0
  246. package/test/e2e/fixtures/scenarios/scenario-authentication/feature.ts +10 -0
  247. package/test/e2e/fixtures/scenarios/scenario-authentication/template/digitalExperiences/webApplications/scenario-authentication/src/components/LoginForm.tsx +25 -0
  248. package/test/e2e/fixtures/scenarios/scenario-authentication/template/digitalExperiences/webApplications/scenario-authentication/src/context/AuthContext.tsx +28 -0
  249. package/test/e2e/fixtures/scenarios/scenario-authentication/template/digitalExperiences/webApplications/scenario-authentication/src/login.tsx +10 -0
  250. package/test/e2e/fixtures/scenarios/scenario-authentication/template/digitalExperiences/webApplications/scenario-authentication/src/placeholder.tsx +0 -0
  251. package/test/e2e/fixtures/scenarios/scenario-authentication/template/digitalExperiences/webApplications/scenario-authentication/src/routes.tsx +17 -0
  252. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/feature.ts +11 -0
  253. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/__inherit__appLayout.tsx +1 -0
  254. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/components/ContactDetail.tsx +54 -0
  255. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/components/ContactForm.tsx +146 -0
  256. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/components/ContactList.tsx +77 -0
  257. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/placeholder.tsx +0 -0
  258. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/routes.tsx +36 -0
  259. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/styles/__append__global.css +176 -0
  260. package/test/e2e/fixtures/scenarios/scenario-crud-contacts/template/digitalExperiences/webApplications/scenario-crud-contacts/src/utils/contactsApi.ts +42 -0
  261. package/test/e2e/fixtures/scenarios/scenario-navigation-menu/feature.ts +10 -0
  262. package/test/e2e/fixtures/scenarios/scenario-navigation-menu/template/digitalExperiences/webApplications/scenario-navigation-menu/src/__append__appLayout.tsx +4 -0
  263. package/test/e2e/fixtures/scenarios/scenario-navigation-menu/template/digitalExperiences/webApplications/scenario-navigation-menu/src/components/NavigationMenu.tsx +17 -0
  264. package/test/e2e/fixtures/scenarios/scenario-navigation-menu/template/digitalExperiences/webApplications/scenario-navigation-menu/src/placeholder.tsx +0 -0
  265. package/test/e2e/fixtures/single-operations/feature-file-append/feature.ts +10 -0
  266. package/test/e2e/fixtures/single-operations/feature-file-append/template/digitalExperiences/webApplications/feature-file-append/src/styles/__append__global.css +10 -0
  267. package/test/e2e/fixtures/single-operations/feature-file-inherit/feature.ts +10 -0
  268. package/test/e2e/fixtures/single-operations/feature-file-inherit/template/digitalExperiences/webApplications/feature-file-inherit/src/__inherit__appLayout.tsx +1 -0
  269. package/test/e2e/fixtures/single-operations/feature-file-inherit/template/digitalExperiences/webApplications/feature-file-inherit/src/__inherit__home.tsx +1 -0
  270. package/test/e2e/fixtures/single-operations/feature-file-prepend/feature.ts +10 -0
  271. package/test/e2e/fixtures/single-operations/feature-file-prepend/template/digitalExperiences/webApplications/feature-file-prepend/src/__prepend__index.tsx +2 -0
  272. package/test/e2e/fixtures/single-operations/feature-import-merge/feature.ts +10 -0
  273. package/test/e2e/fixtures/single-operations/feature-import-merge/template/digitalExperiences/webApplications/feature-import-merge/src/routes.tsx +18 -0
  274. package/test/e2e/fixtures/single-operations/feature-import-merge/template/digitalExperiences/webApplications/feature-import-merge/src/settings.tsx +8 -0
  275. package/test/e2e/fixtures/single-operations/feature-route-add/feature.ts +10 -0
  276. package/test/e2e/fixtures/single-operations/feature-route-add/template/digitalExperiences/webApplications/feature-route-add/src/about.tsx +8 -0
  277. package/test/e2e/fixtures/single-operations/feature-route-add/template/digitalExperiences/webApplications/feature-route-add/src/routes.tsx +23 -0
  278. package/test/e2e/fixtures/single-operations/feature-route-add/template/digitalExperiences/webApplications/feature-route-add/src/services.tsx +8 -0
  279. package/test/e2e/fixtures/watch/watch-add-delete-files/feature.ts +13 -0
  280. package/test/e2e/fixtures/watch/watch-add-delete-files/template/digitalExperiences/webApplications/watch-add-delete-files/src/initial.tsx +4 -0
  281. package/test/e2e/fixtures/watch/watch-add-delete-files/template/digitalExperiences/webApplications/watch-add-delete-files/src/placeholder.tsx +4 -0
  282. package/test/e2e/fixtures/watch/watch-basic/feature.ts +13 -0
  283. package/test/e2e/fixtures/watch/watch-basic/template/digitalExperiences/webApplications/watch-basic/src/placeholder.tsx +4 -0
  284. package/test/e2e/fixtures/watch/watch-basic/template/digitalExperiences/webApplications/watch-basic/src/watchTest.tsx +3 -0
  285. package/test/e2e/fixtures/watch/watch-debounce/feature.ts +13 -0
  286. package/test/e2e/fixtures/watch/watch-debounce/template/digitalExperiences/webApplications/watch-debounce/src/placeholder.tsx +4 -0
  287. package/test/e2e/fixtures/watch/watch-multiple-files/feature.ts +13 -0
  288. package/test/e2e/fixtures/watch/watch-multiple-files/template/digitalExperiences/webApplications/watch-multiple-files/src/components/WatchComponent.tsx +3 -0
  289. package/test/e2e/fixtures/watch/watch-multiple-files/template/digitalExperiences/webApplications/watch-multiple-files/src/placeholder.tsx +4 -0
  290. package/test/e2e/fixtures/watch/watch-multiple-files/template/digitalExperiences/webApplications/watch-multiple-files/src/styles/theme.css +4 -0
  291. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/package.json +10 -0
  292. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/about.tsx +8 -0
  293. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/appLayout.tsx +27 -0
  294. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/contact.tsx +8 -0
  295. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/home.tsx +8 -0
  296. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/index.tsx +10 -0
  297. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/old-page.tsx +8 -0
  298. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/routes.tsx +35 -0
  299. package/test/e2e/gold/dep-chain-linear-apply/digitalExperiences/webApplications/dep-chain-linear/src/styles/global.css +14 -0
  300. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/package.json +10 -0
  301. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/appLayout.tsx +27 -0
  302. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/home.tsx +8 -0
  303. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/index.tsx +10 -0
  304. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/old-page.tsx +8 -0
  305. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/page-a.tsx +8 -0
  306. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/page-b.tsx +8 -0
  307. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/page-c.tsx +8 -0
  308. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/page-d.tsx +8 -0
  309. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/routes.tsx +47 -0
  310. package/test/e2e/gold/dep-chain-long-apply/digitalExperiences/webApplications/dep-chain-a/src/styles/global.css +14 -0
  311. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/package.json +10 -0
  312. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/admin-dashboard.tsx +1 -0
  313. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/admin-layout.tsx +1 -0
  314. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/analytics.tsx +1 -0
  315. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/appLayout.tsx +27 -0
  316. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/components.tsx +8 -0
  317. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/dashboard.tsx +8 -0
  318. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/home.tsx +8 -0
  319. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/index.tsx +10 -0
  320. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/login.tsx +8 -0
  321. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/profile-edit.tsx +1 -0
  322. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/profile.tsx +8 -0
  323. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/reports.tsx +1 -0
  324. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/routes.tsx +94 -0
  325. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/signup.tsx +1 -0
  326. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/styles/global.css +14 -0
  327. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/tools.tsx +1 -0
  328. package/test/e2e/gold/dep-complex-graph-apply/digitalExperiences/webApplications/dep-complex-graph/src/types.tsx +8 -0
  329. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/package.json +10 -0
  330. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/appLayout.tsx +27 -0
  331. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/custom-component.tsx +3 -0
  332. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/home.tsx +8 -0
  333. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/index.tsx +10 -0
  334. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/old-page.tsx +8 -0
  335. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/routes.tsx +28 -0
  336. package/test/e2e/gold/feature-custom-mapping-apply/digitalExperiences/webApplications/feature-custom-mapping/src/styles/global.css +14 -0
  337. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/package.json +10 -0
  338. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/about.tsx +8 -0
  339. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/appLayout.tsx +27 -0
  340. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/home.tsx +8 -0
  341. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/index.tsx +10 -0
  342. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/old-page.tsx +8 -0
  343. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/routes.tsx +29 -0
  344. package/test/e2e/gold/feature-file-inherit-route-add-apply/digitalExperiences/webApplications/feature-file-inherit-route-add/src/styles/global.css +14 -0
  345. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/package.json +10 -0
  346. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/appLayout.tsx +27 -0
  347. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/home.tsx +8 -0
  348. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/index.tsx +13 -0
  349. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/old-page.tsx +8 -0
  350. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/routes.tsx +29 -0
  351. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/settings.tsx +9 -0
  352. package/test/e2e/gold/feature-file-prepend-append-apply/digitalExperiences/webApplications/feature-file-prepend-append/src/styles/global.css +25 -0
  353. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/package.json +10 -0
  354. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/src/appLayout.tsx +27 -0
  355. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/src/home.tsx +8 -0
  356. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/src/index.tsx +10 -0
  357. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/src/routes.tsx +17 -0
  358. package/test/e2e/gold/feature-file-route-delete-apply/digitalExperiences/webApplications/feature-file-route-delete/src/styles/global.css +14 -0
  359. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/assets/icon.svg +3 -0
  360. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/package.json +10 -0
  361. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/appLayout.tsx +27 -0
  362. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/home.tsx +8 -0
  363. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/index.tsx +10 -0
  364. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/mapped-component.tsx +3 -0
  365. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/old-page.tsx +8 -0
  366. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/routes.tsx +28 -0
  367. package/test/e2e/gold/feature-mixed-paths-apply/digitalExperiences/webApplications/feature-mixed-paths/src/styles/global.css +14 -0
  368. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/package.json +10 -0
  369. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/app.tsx +3 -0
  370. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/appLayout.tsx +27 -0
  371. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/home.tsx +8 -0
  372. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/index.tsx +10 -0
  373. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/old-page.tsx +8 -0
  374. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/routes.tsx +28 -0
  375. package/test/e2e/gold/feature-with-webapp-mapping-apply/digitalExperiences/webApplications/feature-with-webapp-mapping/src/styles/global.css +14 -0
  376. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/package.json +10 -0
  377. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/appLayout.tsx +27 -0
  378. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/home.tsx +8 -0
  379. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/index.tsx +10 -0
  380. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/legacy-component.tsx +3 -0
  381. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/old-page.tsx +8 -0
  382. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/routes.tsx +28 -0
  383. package/test/e2e/gold/feature-without-mapping-apply/digitalExperiences/webApplications/feature-without-mapping/src/styles/global.css +14 -0
  384. package/test/e2e/path-mappings.spec.ts +232 -0
  385. package/test/e2e/watch-patches.spec.ts +237 -0
  386. package/test/helpers/cli-runner.ts +100 -0
  387. package/test/helpers/compare-directories.ts +197 -0
  388. package/test/helpers/create-temp-dir.ts +34 -0
  389. package/test/helpers/fixtures.ts +67 -0
  390. package/test/setup.ts +6 -0
  391. package/test/unit/debounce.spec.ts +159 -0
  392. package/test/unit/dependency-resolver.spec.ts +260 -0
  393. package/test/unit/file-operations.spec.ts +264 -0
  394. package/test/unit/import-merger.spec.ts +395 -0
  395. package/test/unit/index.spec.ts +158 -0
  396. package/test/unit/new-app.spec.ts +178 -0
  397. package/test/unit/new-feature.spec.ts +178 -0
  398. package/test/unit/package-json-merger.spec.ts +275 -0
  399. package/test/unit/patch-loader.spec.ts +238 -0
  400. package/test/unit/path-mappings.spec.ts +241 -0
  401. package/test/unit/paths.spec.ts +247 -0
  402. package/test/unit/route-merger.spec.ts +265 -0
  403. package/test/unit/validation.spec.ts +311 -0
  404. package/tsconfig.json +20 -0
  405. package/vitest.config.ts +18 -0
@@ -0,0 +1,159 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import { debounce } from '../../src/utils/debounce.js';
3
+
4
+ describe('debounce', () => {
5
+ beforeEach(() => {
6
+ vi.useFakeTimers();
7
+ });
8
+
9
+ afterEach(() => {
10
+ vi.restoreAllMocks();
11
+ });
12
+
13
+ it('should delay function execution until after wait time', () => {
14
+ const mockFn = vi.fn();
15
+ const debouncedFn = debounce(mockFn, 100);
16
+
17
+ debouncedFn();
18
+ expect(mockFn).not.toHaveBeenCalled();
19
+
20
+ vi.advanceTimersByTime(50);
21
+ expect(mockFn).not.toHaveBeenCalled();
22
+
23
+ vi.advanceTimersByTime(50);
24
+ expect(mockFn).toHaveBeenCalledTimes(1);
25
+ });
26
+
27
+ it('should reset timer if called again before wait time', () => {
28
+ const mockFn = vi.fn();
29
+ const debouncedFn = debounce(mockFn, 100);
30
+
31
+ debouncedFn();
32
+ vi.advanceTimersByTime(50);
33
+
34
+ // Call again - should reset timer
35
+ debouncedFn();
36
+ vi.advanceTimersByTime(50);
37
+ expect(mockFn).not.toHaveBeenCalled();
38
+
39
+ // Now complete the full wait time from second call
40
+ vi.advanceTimersByTime(50);
41
+ expect(mockFn).toHaveBeenCalledTimes(1);
42
+ });
43
+
44
+ it('should only call function once even with multiple rapid calls', () => {
45
+ const mockFn = vi.fn();
46
+ const debouncedFn = debounce(mockFn, 100);
47
+
48
+ // Rapid fire calls
49
+ debouncedFn();
50
+ debouncedFn();
51
+ debouncedFn();
52
+ debouncedFn();
53
+
54
+ // Function should not be called yet
55
+ expect(mockFn).not.toHaveBeenCalled();
56
+
57
+ // Fast forward past debounce time
58
+ vi.advanceTimersByTime(100);
59
+
60
+ // Should only be called once
61
+ expect(mockFn).toHaveBeenCalledTimes(1);
62
+ });
63
+
64
+ it('should pass arguments to debounced function', () => {
65
+ const mockFn = vi.fn();
66
+ const debouncedFn = debounce(mockFn, 100);
67
+
68
+ debouncedFn('arg1', 'arg2', 'arg3');
69
+
70
+ vi.advanceTimersByTime(100);
71
+
72
+ expect(mockFn).toHaveBeenCalledWith('arg1', 'arg2', 'arg3');
73
+ });
74
+
75
+ it('should use latest arguments when called multiple times', () => {
76
+ const mockFn = vi.fn();
77
+ const debouncedFn = debounce(mockFn, 100);
78
+
79
+ debouncedFn('first');
80
+ vi.advanceTimersByTime(50);
81
+ debouncedFn('second');
82
+ vi.advanceTimersByTime(50);
83
+ debouncedFn('third');
84
+
85
+ vi.advanceTimersByTime(100);
86
+
87
+ // Should be called with the last set of arguments
88
+ expect(mockFn).toHaveBeenCalledTimes(1);
89
+ expect(mockFn).toHaveBeenCalledWith('third');
90
+ });
91
+
92
+ it('should allow multiple separate debounced executions', () => {
93
+ const mockFn = vi.fn();
94
+ const debouncedFn = debounce(mockFn, 100);
95
+
96
+ // First execution
97
+ debouncedFn('first');
98
+ vi.advanceTimersByTime(100);
99
+ expect(mockFn).toHaveBeenCalledTimes(1);
100
+ expect(mockFn).toHaveBeenLastCalledWith('first');
101
+
102
+ // Second execution after debounce period
103
+ debouncedFn('second');
104
+ vi.advanceTimersByTime(100);
105
+ expect(mockFn).toHaveBeenCalledTimes(2);
106
+ expect(mockFn).toHaveBeenLastCalledWith('second');
107
+ });
108
+
109
+ it('should handle different wait times', () => {
110
+ const mockFn = vi.fn();
111
+ const debouncedFn = debounce(mockFn, 500);
112
+
113
+ debouncedFn();
114
+ vi.advanceTimersByTime(499);
115
+ expect(mockFn).not.toHaveBeenCalled();
116
+
117
+ vi.advanceTimersByTime(1);
118
+ expect(mockFn).toHaveBeenCalledTimes(1);
119
+ });
120
+
121
+ it('should work with zero wait time', () => {
122
+ const mockFn = vi.fn();
123
+ const debouncedFn = debounce(mockFn, 0);
124
+
125
+ debouncedFn();
126
+ expect(mockFn).not.toHaveBeenCalled();
127
+
128
+ vi.advanceTimersByTime(0);
129
+ expect(mockFn).toHaveBeenCalledTimes(1);
130
+ });
131
+
132
+ it('should preserve function return type through generics', () => {
133
+ // Type test - this should compile without errors
134
+ const returnsNumber = (x: number): number => x * 2;
135
+ const debouncedReturnsNumber = debounce(returnsNumber, 100);
136
+
137
+ // Debounced function returns void (fire and forget)
138
+ const result: void = debouncedReturnsNumber(5);
139
+ expect(result).toBeUndefined();
140
+ });
141
+
142
+ it('should clear previous timeout before setting new one', () => {
143
+ const mockFn = vi.fn();
144
+ const debouncedFn = debounce(mockFn, 100);
145
+
146
+ debouncedFn();
147
+
148
+ vi.advanceTimersByTime(50);
149
+ debouncedFn(); // This should clear the first timeout and start a new one
150
+
151
+ // If the first timeout wasn't cleared, the function would be called after 50ms more
152
+ // But since it was cleared, it should only be called after 100ms more
153
+ vi.advanceTimersByTime(50);
154
+ expect(mockFn).not.toHaveBeenCalled(); // Not called yet because timer was reset
155
+
156
+ vi.advanceTimersByTime(50);
157
+ expect(mockFn).toHaveBeenCalledTimes(1); // Called after full 100ms from second call
158
+ });
159
+ });
@@ -0,0 +1,260 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import {
3
+ resolveDependencies,
4
+ CircularDependencyError,
5
+ DependencyResolutionError,
6
+ type ResolvedFeature
7
+ } from '../../src/core/dependency-resolver.js';
8
+ import { mkdirSync, writeFileSync, rmSync } from 'fs';
9
+ import { join } from 'path';
10
+ import { tmpdir } from 'os';
11
+
12
+ describe('dependency-resolver', () => {
13
+ let tempDir: string;
14
+
15
+ beforeEach(() => {
16
+ // Create a temporary directory for test fixtures
17
+ tempDir = join(tmpdir(), `dep-resolver-test-${Date.now()}`);
18
+ mkdirSync(tempDir, { recursive: true });
19
+ });
20
+
21
+ afterEach(() => {
22
+ // Clean up
23
+ if (tempDir) {
24
+ rmSync(tempDir, { recursive: true, force: true });
25
+ }
26
+ });
27
+
28
+ /**
29
+ * Helper to create a feature directory with feature.ts
30
+ */
31
+ function createFeature(name: string, dependencies: string[] = []): string {
32
+ const featureDir = join(tempDir, name);
33
+ mkdirSync(featureDir, { recursive: true });
34
+
35
+ const patchesContent = `
36
+ const feature = {
37
+ dependencies: ${JSON.stringify(dependencies)}
38
+ };
39
+
40
+ export default feature;
41
+ `;
42
+
43
+ writeFileSync(join(featureDir, 'feature.ts'), patchesContent);
44
+ return featureDir;
45
+ }
46
+
47
+ describe('resolveDependencies', () => {
48
+ it('should resolve a feature with no dependencies', async () => {
49
+ const featurePath = createFeature('feature-a');
50
+
51
+ const result = await resolveDependencies(featurePath);
52
+
53
+ expect(result.orderedFeatures).toHaveLength(1);
54
+ expect(result.orderedFeatures[0].featurePath).toBe(featurePath);
55
+ expect(result.orderedFeatures[0].config.dependencies).toEqual([]);
56
+ });
57
+
58
+ it('should resolve a simple dependency chain', async () => {
59
+ // Create features: B depends on A
60
+ const featureA = createFeature('feature-a');
61
+ const featureB = createFeature('feature-b', [featureA]);
62
+
63
+ const result = await resolveDependencies(featureB);
64
+
65
+ expect(result.orderedFeatures).toHaveLength(2);
66
+ // A should come before B (dependencies first)
67
+ expect(result.orderedFeatures[0].featurePath).toBe(featureA);
68
+ expect(result.orderedFeatures[1].featurePath).toBe(featureB);
69
+ });
70
+
71
+ it('should resolve a nested dependency chain', async () => {
72
+ // Create features: C depends on B, B depends on A
73
+ const featureA = createFeature('feature-a');
74
+ const featureB = createFeature('feature-b', [featureA]);
75
+ const featureC = createFeature('feature-c', [featureB]);
76
+
77
+ const result = await resolveDependencies(featureC);
78
+
79
+ expect(result.orderedFeatures).toHaveLength(3);
80
+ // Order should be A, B, C
81
+ expect(result.orderedFeatures[0].featurePath).toBe(featureA);
82
+ expect(result.orderedFeatures[1].featurePath).toBe(featureB);
83
+ expect(result.orderedFeatures[2].featurePath).toBe(featureC);
84
+ });
85
+
86
+ it('should handle diamond dependencies (apply common dependency once)', async () => {
87
+ // Create diamond: D depends on [B, C], both B and C depend on A
88
+ const featureA = createFeature('feature-a');
89
+ const featureB = createFeature('feature-b', [featureA]);
90
+ const featureC = createFeature('feature-c', [featureA]);
91
+ const featureD = createFeature('feature-d', [featureB, featureC]);
92
+
93
+ const result = await resolveDependencies(featureD);
94
+
95
+ // A should only appear once
96
+ expect(result.orderedFeatures).toHaveLength(4);
97
+
98
+ // A must come first
99
+ expect(result.orderedFeatures[0].featurePath).toBe(featureA);
100
+
101
+ // B and C must come after A but before D
102
+ const bIndex = result.orderedFeatures.findIndex(f => f.featurePath === featureB);
103
+ const cIndex = result.orderedFeatures.findIndex(f => f.featurePath === featureC);
104
+ expect(bIndex).toBeGreaterThan(0);
105
+ expect(cIndex).toBeGreaterThan(0);
106
+
107
+ // D must come last
108
+ expect(result.orderedFeatures[3].featurePath).toBe(featureD);
109
+ });
110
+
111
+ it('should detect direct circular dependencies', async () => {
112
+ // Create circular dependency: A depends on B, B depends on A
113
+ const featureA = createFeature('feature-a-circular');
114
+ const featureB = createFeature('feature-b-circular', [featureA]);
115
+
116
+ // Update A to depend on B (creating cycle)
117
+ const patchesContent = `
118
+ const feature = {
119
+ dependencies: ['${featureB}']
120
+ };
121
+
122
+ export default feature;
123
+ `;
124
+ writeFileSync(join(featureA, 'feature.ts'), patchesContent);
125
+
126
+ await expect(resolveDependencies(featureB)).rejects.toThrow(CircularDependencyError);
127
+
128
+ try {
129
+ await resolveDependencies(featureB);
130
+ } catch (err) {
131
+ expect(err).toBeInstanceOf(CircularDependencyError);
132
+ const error = err as CircularDependencyError;
133
+ expect(error.message).toContain('Circular dependency detected');
134
+ expect(error.cycle).toBeDefined();
135
+ expect(error.cycle.length).toBeGreaterThan(1);
136
+ }
137
+ });
138
+
139
+ it('should detect indirect circular dependencies', async () => {
140
+ // Create circular dependency: A → B → C → A
141
+ const featureA = createFeature('feature-a-indirect');
142
+ const featureB = createFeature('feature-b-indirect', [featureA]);
143
+ const featureC = createFeature('feature-c-indirect', [featureB]);
144
+
145
+ // Update A to depend on C (creating cycle)
146
+ const patchesContent = `
147
+ const feature = {
148
+ dependencies: ['${featureC}']
149
+ };
150
+
151
+ export default feature;
152
+ `;
153
+ writeFileSync(join(featureA, 'feature.ts'), patchesContent);
154
+
155
+ await expect(resolveDependencies(featureC)).rejects.toThrow(CircularDependencyError);
156
+ });
157
+
158
+ it('should throw DependencyResolutionError for non-existent dependency', async () => {
159
+ const featureA = createFeature('feature-a', ['/non-existent/path']);
160
+
161
+ await expect(resolveDependencies(featureA)).rejects.toThrow(DependencyResolutionError);
162
+
163
+ try {
164
+ await resolveDependencies(featureA);
165
+ } catch (err) {
166
+ expect(err).toBeInstanceOf(DependencyResolutionError);
167
+ expect((err as Error).message).toContain('Failed to resolve dependencies');
168
+ }
169
+ });
170
+
171
+ it('should throw error for feature path without feature.ts', async () => {
172
+ const featurePath = join(tempDir, 'no-feature');
173
+ mkdirSync(featurePath, { recursive: true });
174
+ // Don't create feature.ts
175
+
176
+ await expect(resolveDependencies(featurePath)).rejects.toThrow();
177
+ });
178
+
179
+ it('should handle multiple features depending on the same dependency', async () => {
180
+ // Create: D and E both depend on A, F depends on [D, E]
181
+ const featureA = createFeature('feature-shared');
182
+ const featureD = createFeature('feature-d', [featureA]);
183
+ const featureE = createFeature('feature-e', [featureA]);
184
+ const featureF = createFeature('feature-f', [featureD, featureE]);
185
+
186
+ const result = await resolveDependencies(featureF);
187
+
188
+ // A should only appear once, even though D and E both depend on it
189
+ const aCount = result.orderedFeatures.filter(f => f.featurePath === featureA).length;
190
+ expect(aCount).toBe(1);
191
+
192
+ // A must come first
193
+ expect(result.orderedFeatures[0].featurePath).toBe(featureA);
194
+
195
+ // F must come last
196
+ expect(result.orderedFeatures[result.orderedFeatures.length - 1].featurePath).toBe(featureF);
197
+ });
198
+
199
+ it('should return dependency graph for debugging', async () => {
200
+ const featureA = createFeature('feature-a-graph');
201
+ const featureB = createFeature('feature-b-graph', [featureA]);
202
+
203
+ const result = await resolveDependencies(featureB);
204
+
205
+ expect(result.dependencyGraph).toBeDefined();
206
+ expect(result.dependencyGraph.size).toBe(2);
207
+
208
+ // Verify graph structure
209
+ const normalizedA = result.orderedFeatures[0].featureDir;
210
+ const normalizedB = result.orderedFeatures[1].featureDir;
211
+
212
+ expect(result.dependencyGraph.get(normalizedB)).toContain(featureA);
213
+ expect(result.dependencyGraph.get(normalizedA)).toEqual([]);
214
+ });
215
+
216
+ it('should handle relative and absolute paths consistently', async () => {
217
+ const featureA = createFeature('feature-abs');
218
+ const featureB = createFeature('feature-rel', [featureA]);
219
+
220
+ const result = await resolveDependencies(featureB);
221
+
222
+ expect(result.orderedFeatures).toHaveLength(2);
223
+ // Both should be resolved regardless of path format
224
+ expect(result.orderedFeatures[0].featurePath).toBe(featureA);
225
+ expect(result.orderedFeatures[1].featurePath).toBe(featureB);
226
+ });
227
+
228
+ it('should preserve feature configuration in resolved features', async () => {
229
+ const featureA = createFeature('feature-config');
230
+
231
+ const result = await resolveDependencies(featureA);
232
+
233
+ expect(result.orderedFeatures[0].config).toBeDefined();
234
+ expect(result.orderedFeatures[0].config.dependencies).toEqual([]);
235
+ expect(result.orderedFeatures[0].featureDir).toBe(featureA);
236
+ });
237
+ });
238
+
239
+ describe('CircularDependencyError', () => {
240
+ it('should format cycle information in error message', () => {
241
+ const cycle = ['feature-a', 'feature-b', 'feature-c', 'feature-a'];
242
+ const error = new CircularDependencyError(cycle);
243
+
244
+ expect(error.message).toContain('Circular dependency detected');
245
+ expect(error.message).toContain('feature-a → feature-b → feature-c → feature-a');
246
+ expect(error.cycle).toEqual(cycle);
247
+ expect(error.name).toBe('CircularDependencyError');
248
+ });
249
+ });
250
+
251
+ describe('DependencyResolutionError', () => {
252
+ it('should be throwable with custom message', () => {
253
+ const error = new DependencyResolutionError('Custom error message');
254
+
255
+ expect(error.message).toBe('Custom error message');
256
+ expect(error.name).toBe('DependencyResolutionError');
257
+ expect(error).toBeInstanceOf(Error);
258
+ });
259
+ });
260
+ });
@@ -0,0 +1,264 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, rmSync, statSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { createTempDir, cleanupTempDir } from '../helpers/create-temp-dir.js';
5
+ import { deleteFile, prependToFile, appendToFile } from '../../src/core/file-operations.js';
6
+
7
+ describe('file-operations', () => {
8
+ let testDir: string;
9
+ let sourceFile: string;
10
+ let targetFile: string;
11
+
12
+ beforeEach(() => {
13
+ testDir = createTempDir();
14
+ sourceFile = join(testDir, 'source.ts');
15
+ targetFile = join(testDir, 'target.ts');
16
+ });
17
+
18
+ afterEach(() => {
19
+ cleanupTempDir(testDir);
20
+ });
21
+
22
+ /**
23
+ * Helper function that simulates the upsert logic with content checking
24
+ */
25
+ function upsertWithContentCheck(sourcePath: string, targetPath: string): 'added' | 'updated' | 'skipped' {
26
+ const sourceContent = readFileSync(sourcePath, 'utf-8');
27
+
28
+ const fileExists = existsSync(targetPath);
29
+ if (fileExists) {
30
+ const existingContent = readFileSync(targetPath, 'utf-8');
31
+ if (existingContent === sourceContent) {
32
+ return 'skipped';
33
+ }
34
+ }
35
+
36
+ writeFileSync(targetPath, sourceContent, 'utf-8');
37
+ return fileExists ? 'updated' : 'added';
38
+ }
39
+
40
+ describe('Content checking', () => {
41
+ it('should add file on initial write', async () => {
42
+ writeFileSync(sourceFile, 'export const foo = "bar";\n');
43
+ const result = upsertWithContentCheck(sourceFile, targetFile);
44
+
45
+ expect(result).toBe('added');
46
+ expect(existsSync(targetFile)).toBe(true);
47
+ });
48
+
49
+ it('should skip write when content is unchanged', async () => {
50
+ writeFileSync(sourceFile, 'export const foo = "bar";\n');
51
+ upsertWithContentCheck(sourceFile, targetFile);
52
+
53
+ const firstStat = statSync(targetFile);
54
+ const firstMtime = firstStat.mtimeMs;
55
+
56
+ // Wait to ensure timestamp would differ if file is written
57
+ await new Promise(resolve => setTimeout(resolve, 20));
58
+
59
+ // Write same content (should skip)
60
+ const result = upsertWithContentCheck(sourceFile, targetFile);
61
+
62
+ expect(result).toBe('skipped');
63
+
64
+ const secondStat = statSync(targetFile);
65
+ const secondMtime = secondStat.mtimeMs;
66
+
67
+ expect(firstMtime).toBe(secondMtime);
68
+ });
69
+
70
+ it('should update file when content changes', async () => {
71
+ writeFileSync(sourceFile, 'export const foo = "bar";\n');
72
+ upsertWithContentCheck(sourceFile, targetFile);
73
+
74
+ const secondStat = statSync(targetFile);
75
+ const secondMtime = secondStat.mtimeMs;
76
+
77
+ await new Promise(resolve => setTimeout(resolve, 20));
78
+
79
+ // Write different content (should update)
80
+ writeFileSync(sourceFile, 'export const foo = "baz";\n');
81
+ const result = upsertWithContentCheck(sourceFile, targetFile);
82
+
83
+ expect(result).toBe('updated');
84
+
85
+ const thirdStat = statSync(targetFile);
86
+ const thirdMtime = thirdStat.mtimeMs;
87
+
88
+ expect(thirdMtime).not.toBe(secondMtime);
89
+
90
+ const finalContent = readFileSync(targetFile, 'utf-8');
91
+ expect(finalContent).toBe('export const foo = "baz";\n');
92
+ });
93
+ });
94
+
95
+ describe('deleteFile', () => {
96
+ it('should delete existing file', async () => {
97
+ const testFile = join(testDir, 'delete-me.txt');
98
+ writeFileSync(testFile, 'content to delete');
99
+
100
+ expect(existsSync(testFile)).toBe(true);
101
+
102
+ await deleteFile(testFile, 'delete-me.txt');
103
+
104
+ expect(existsSync(testFile)).toBe(false);
105
+ });
106
+
107
+ it('should not error when deleting non-existent file', async () => {
108
+ const testFile = join(testDir, 'does-not-exist.txt');
109
+
110
+ // Should not throw
111
+ await expect(deleteFile(testFile, 'does-not-exist.txt')).resolves.toBeUndefined();
112
+ });
113
+
114
+ it('should delete directory recursively', async () => {
115
+ const testSubDir = join(testDir, 'subdir');
116
+ mkdirSync(testSubDir, { recursive: true });
117
+ writeFileSync(join(testSubDir, 'file1.txt'), 'file1');
118
+ writeFileSync(join(testSubDir, 'file2.txt'), 'file2');
119
+
120
+ expect(existsSync(testSubDir)).toBe(true);
121
+
122
+ await deleteFile(testSubDir, 'subdir');
123
+
124
+ expect(existsSync(testSubDir)).toBe(false);
125
+ });
126
+ });
127
+
128
+ describe('prependToFile', () => {
129
+ it('should prepend content to text file', async () => {
130
+ const featureFile = join(testDir, 'feature.txt');
131
+ const targetFile1 = join(testDir, 'target1.txt');
132
+
133
+ writeFileSync(featureFile, 'PREPENDED CONTENT');
134
+ writeFileSync(targetFile1, 'ORIGINAL CONTENT');
135
+
136
+ await prependToFile(featureFile, targetFile1, 'target1.txt');
137
+
138
+ const result = readFileSync(targetFile1, 'utf-8');
139
+ expect(result).toBe('PREPENDED CONTENT\nORIGINAL CONTENT');
140
+ });
141
+
142
+ it('should prepend to TypeScript file and fix __inherit__ imports', async () => {
143
+ const featureTsFile = join(testDir, 'feature.tsx');
144
+ const targetTsFile = join(testDir, 'target.tsx');
145
+
146
+ writeFileSync(featureTsFile, "import { x } from './__inherit__routes';\n");
147
+ writeFileSync(targetTsFile, 'const y = 1;');
148
+
149
+ await prependToFile(featureTsFile, targetTsFile, 'target.tsx');
150
+
151
+ const result = readFileSync(targetTsFile, 'utf-8');
152
+ expect(result).toBe("import { x } from './routes';\n\nconst y = 1;");
153
+ });
154
+
155
+ it('should modify content when prepending (even if prefix exists)', async () => {
156
+ const featureFile3 = join(testDir, 'feature3.txt');
157
+ const targetFile3 = join(testDir, 'target3.txt');
158
+
159
+ writeFileSync(featureFile3, 'PREFIX');
160
+ // Target already has the exact result of prepending PREFIX
161
+ writeFileSync(targetFile3, 'PREFIX\nORIGINAL');
162
+
163
+ await new Promise(resolve => setTimeout(resolve, 20));
164
+
165
+ // This should NOT skip because prepending PREFIX to "PREFIX\nORIGINAL"
166
+ // results in "PREFIX\nPREFIX\nORIGINAL" which is different
167
+ await prependToFile(featureFile3, targetFile3, 'target3.txt');
168
+
169
+ const result = readFileSync(targetFile3, 'utf-8');
170
+ expect(result).toBe('PREFIX\nPREFIX\nORIGINAL');
171
+ });
172
+
173
+ it('should error if feature file does not exist', async () => {
174
+ await expect(
175
+ prependToFile(join(testDir, 'nonexistent.txt'), targetFile, 'target.txt')
176
+ ).rejects.toThrow(/Feature file not found/);
177
+ });
178
+
179
+ it('should error if target file does not exist', async () => {
180
+ const featureFile = join(testDir, 'feature.txt');
181
+ writeFileSync(featureFile, 'content');
182
+
183
+ await expect(
184
+ prependToFile(featureFile, join(testDir, 'nonexistent-target.txt'), 'target.txt')
185
+ ).rejects.toThrow(/Target file not found/);
186
+ });
187
+ });
188
+
189
+ describe('appendToFile', () => {
190
+ it('should append content to text file', async () => {
191
+ const featureFile = join(testDir, 'feature-append.txt');
192
+ const targetFile1 = join(testDir, 'target-append1.txt');
193
+
194
+ writeFileSync(featureFile, 'APPENDED CONTENT');
195
+ writeFileSync(targetFile1, 'ORIGINAL CONTENT');
196
+
197
+ await appendToFile(featureFile, targetFile1, 'target-append1.txt');
198
+
199
+ const result = readFileSync(targetFile1, 'utf-8');
200
+ expect(result).toBe('ORIGINAL CONTENT\nAPPENDED CONTENT');
201
+ });
202
+
203
+ it('should append to CSS file', async () => {
204
+ const featureCssFile = join(testDir, 'feature.css');
205
+ const targetCssFile = join(testDir, 'target.css');
206
+
207
+ const baseCSS = '@import "tailwindcss";\n\nbody { margin: 0; }';
208
+ const appendCSS = ':root { --color: blue; }';
209
+
210
+ writeFileSync(targetCssFile, baseCSS);
211
+ writeFileSync(featureCssFile, appendCSS);
212
+
213
+ await appendToFile(featureCssFile, targetCssFile, 'target.css');
214
+
215
+ const result = readFileSync(targetCssFile, 'utf-8');
216
+ expect(result).toBe(`${baseCSS}\n${appendCSS}`);
217
+ });
218
+
219
+ it('should append to TypeScript file and fix __inherit__ imports', async () => {
220
+ const featureTsFile = join(testDir, 'feature-append.tsx');
221
+ const targetTsFile = join(testDir, 'target-append.tsx');
222
+
223
+ writeFileSync(targetTsFile, 'const y = 1;');
224
+ writeFileSync(featureTsFile, "import { x } from './__inherit__routes';");
225
+
226
+ await appendToFile(featureTsFile, targetTsFile, 'target-append.tsx');
227
+
228
+ const result = readFileSync(targetTsFile, 'utf-8');
229
+ expect(result).toBe("const y = 1;\nimport { x } from './routes';");
230
+ });
231
+
232
+ it('should modify content when appending (even if suffix exists)', async () => {
233
+ const featureFile4 = join(testDir, 'feature4.txt');
234
+ const targetFile4 = join(testDir, 'target4.txt');
235
+
236
+ writeFileSync(featureFile4, 'SUFFIX');
237
+ writeFileSync(targetFile4, 'ORIGINAL\nSUFFIX');
238
+
239
+ await new Promise(resolve => setTimeout(resolve, 20));
240
+
241
+ // This should NOT skip because appending SUFFIX to "ORIGINAL\nSUFFIX"
242
+ // results in "ORIGINAL\nSUFFIX\nSUFFIX" which is different
243
+ await appendToFile(featureFile4, targetFile4, 'target4.txt');
244
+
245
+ const result = readFileSync(targetFile4, 'utf-8');
246
+ expect(result).toBe('ORIGINAL\nSUFFIX\nSUFFIX');
247
+ });
248
+
249
+ it('should error if feature file does not exist', async () => {
250
+ await expect(
251
+ appendToFile(join(testDir, 'nonexistent.txt'), targetFile, 'target.txt')
252
+ ).rejects.toThrow(/Feature file not found/);
253
+ });
254
+
255
+ it('should error if target file does not exist', async () => {
256
+ const featureFile = join(testDir, 'feature.txt');
257
+ writeFileSync(featureFile, 'content');
258
+
259
+ await expect(
260
+ appendToFile(featureFile, join(testDir, 'nonexistent-target.txt'), 'target.txt')
261
+ ).rejects.toThrow(/Target file not found/);
262
+ });
263
+ });
264
+ });