@salesforce/webapp-template-cli-experimental 1.3.3

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 (71) hide show
  1. package/LICENSE.txt +82 -0
  2. package/README.md +1464 -0
  3. package/dist/commands/apply-patches.d.ts +23 -0
  4. package/dist/commands/apply-patches.d.ts.map +1 -0
  5. package/dist/commands/apply-patches.js +650 -0
  6. package/dist/commands/apply-patches.js.map +1 -0
  7. package/dist/commands/new-app-feature.d.ts +7 -0
  8. package/dist/commands/new-app-feature.d.ts.map +1 -0
  9. package/dist/commands/new-app-feature.js +208 -0
  10. package/dist/commands/new-app-feature.js.map +1 -0
  11. package/dist/commands/new-app.d.ts +6 -0
  12. package/dist/commands/new-app.d.ts.map +1 -0
  13. package/dist/commands/new-app.js +7 -0
  14. package/dist/commands/new-app.js.map +1 -0
  15. package/dist/commands/watch-patches.d.ts +4 -0
  16. package/dist/commands/watch-patches.d.ts.map +1 -0
  17. package/dist/commands/watch-patches.js +128 -0
  18. package/dist/commands/watch-patches.js.map +1 -0
  19. package/dist/core/dependency-resolver.d.ts +40 -0
  20. package/dist/core/dependency-resolver.d.ts.map +1 -0
  21. package/dist/core/dependency-resolver.js +126 -0
  22. package/dist/core/dependency-resolver.js.map +1 -0
  23. package/dist/core/file-operations.d.ts +55 -0
  24. package/dist/core/file-operations.d.ts.map +1 -0
  25. package/dist/core/file-operations.js +350 -0
  26. package/dist/core/file-operations.js.map +1 -0
  27. package/dist/core/package-json-merger.d.ts +31 -0
  28. package/dist/core/package-json-merger.d.ts.map +1 -0
  29. package/dist/core/package-json-merger.js +149 -0
  30. package/dist/core/package-json-merger.js.map +1 -0
  31. package/dist/core/patch-loader.d.ts +18 -0
  32. package/dist/core/patch-loader.d.ts.map +1 -0
  33. package/dist/core/patch-loader.js +115 -0
  34. package/dist/core/patch-loader.js.map +1 -0
  35. package/dist/index.d.ts +3 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +95 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/types.d.ts +28 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +2 -0
  42. package/dist/types.js.map +1 -0
  43. package/dist/utils/debounce.d.ts +9 -0
  44. package/dist/utils/debounce.d.ts.map +1 -0
  45. package/dist/utils/debounce.js +20 -0
  46. package/dist/utils/debounce.js.map +1 -0
  47. package/dist/utils/import-merger.d.ts +17 -0
  48. package/dist/utils/import-merger.d.ts.map +1 -0
  49. package/dist/utils/import-merger.js +244 -0
  50. package/dist/utils/import-merger.js.map +1 -0
  51. package/dist/utils/logger.d.ts +6 -0
  52. package/dist/utils/logger.d.ts.map +1 -0
  53. package/dist/utils/logger.js +22 -0
  54. package/dist/utils/logger.js.map +1 -0
  55. package/dist/utils/path-mappings.d.ts +94 -0
  56. package/dist/utils/path-mappings.d.ts.map +1 -0
  57. package/dist/utils/path-mappings.js +139 -0
  58. package/dist/utils/path-mappings.js.map +1 -0
  59. package/dist/utils/paths.d.ts +61 -0
  60. package/dist/utils/paths.d.ts.map +1 -0
  61. package/dist/utils/paths.js +178 -0
  62. package/dist/utils/paths.js.map +1 -0
  63. package/dist/utils/route-merger.d.ts +107 -0
  64. package/dist/utils/route-merger.d.ts.map +1 -0
  65. package/dist/utils/route-merger.js +358 -0
  66. package/dist/utils/route-merger.js.map +1 -0
  67. package/dist/utils/validation.d.ts +35 -0
  68. package/dist/utils/validation.d.ts.map +1 -0
  69. package/dist/utils/validation.js +137 -0
  70. package/dist/utils/validation.js.map +1 -0
  71. package/package.json +44 -0
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { type PathMapping, type PathMappings } from "../types.js";
7
+ /**
8
+ * Get default path mappings for a feature
9
+ * Maps webApp → webapplications/<featureName> (old format for path translation)
10
+ * Note: This still uses the old format because translatePathToTargetApp will convert it to SFDX format
11
+ *
12
+ * @param featureName - The name of the feature (e.g., 'feature-react-nav-menu')
13
+ * @returns Array containing the default path mapping
14
+ *
15
+ * @example
16
+ * getDefaultPathMappings('feature-nav')
17
+ * // Returns: [{ from: 'webApp', to: 'webapplications/feature-nav' }]
18
+ */
19
+ export declare function getDefaultPathMappings(featureName: string): PathMapping[];
20
+ /**
21
+ * Check if path mappings are enabled for a feature
22
+ * Default: true (enabled by default, opt-out)
23
+ *
24
+ * @param pathMappings - The path mappings configuration from the feature
25
+ * @returns true if mappings are enabled (default), false if explicitly disabled
26
+ *
27
+ * @example
28
+ * arePathMappingsEnabled(undefined) // Returns: true (default enabled)
29
+ * arePathMappingsEnabled({ enabled: false }) // Returns: false
30
+ * arePathMappingsEnabled({ enabled: true }) // Returns: true
31
+ * arePathMappingsEnabled({ mappings: [...] }) // Returns: true (enabled by default)
32
+ */
33
+ export declare function arePathMappingsEnabled(pathMappings?: PathMappings): boolean;
34
+ /**
35
+ * Get the effective path mappings for a feature
36
+ * Returns custom mappings if provided, otherwise default mappings
37
+ *
38
+ * @param featureName - The name of the feature
39
+ * @param pathMappings - The path mappings configuration from the feature
40
+ * @returns Array of path mappings to apply (empty if disabled)
41
+ *
42
+ * @example
43
+ * // Default behavior (no config)
44
+ * getEffectivePathMappings('feature-nav', undefined)
45
+ * // Returns: [{ from: 'webApp', to: 'webapplications/feature-nav' }]
46
+ *
47
+ * // Disabled
48
+ * getEffectivePathMappings('feature-nav', { enabled: false })
49
+ * // Returns: []
50
+ *
51
+ * // Custom mappings
52
+ * getEffectivePathMappings('feature-nav', { mappings: [{ from: 'web', to: 'custom/path' }] })
53
+ * // Returns: [{ from: 'web', to: 'custom/path' }]
54
+ */
55
+ export declare function getEffectivePathMappings(featureName: string, pathMappings?: PathMappings): PathMapping[];
56
+ /**
57
+ * Apply path mappings to a template file path
58
+ *
59
+ * This function transforms simplified paths (e.g., "webApp/src/app.tsx") into
60
+ * their full target paths (e.g., "webapplications/feature-name/src/app.tsx")
61
+ * based on the provided mappings.
62
+ *
63
+ * The function checks each mapping in order and applies the first one that matches.
64
+ * If no mapping matches, the path is returned unchanged (backwards compatibility).
65
+ *
66
+ * @param templatePath - Path relative to template directory (e.g., "webApp/src/app.tsx")
67
+ * @param mappings - Array of path mappings to apply
68
+ * @returns Transformed path with mapping applied
69
+ *
70
+ * @example
71
+ * // Apply default webApp mapping
72
+ * applyPathMappings('webApp/src/app.tsx', [{
73
+ * from: 'webApp',
74
+ * to: 'webapplications/feature-react-nav-menu'
75
+ * }])
76
+ * // Returns: 'webapplications/feature-react-nav-menu/src/app.tsx'
77
+ *
78
+ * @example
79
+ * // Backwards compatibility - old paths pass through
80
+ * applyPathMappings('webapplications/feature-react-nav-menu/src/app.tsx', [...])
81
+ * // Returns: 'webapplications/feature-react-nav-menu/src/app.tsx'
82
+ *
83
+ * @example
84
+ * // No mappings - path unchanged
85
+ * applyPathMappings('webApp/src/app.tsx', [])
86
+ * // Returns: 'webApp/src/app.tsx'
87
+ *
88
+ * @example
89
+ * // No match - path unchanged
90
+ * applyPathMappings('src/app.tsx', [{ from: 'webApp', to: '...' }])
91
+ * // Returns: 'src/app.tsx'
92
+ */
93
+ export declare function applyPathMappings(templatePath: string, mappings: PathMapping[]): string;
94
+ //# sourceMappingURL=path-mappings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-mappings.d.ts","sourceRoot":"","sources":["../../src/utils/path-mappings.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAElE;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,WAAW,EAAE,CAOzE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,YAAY,CAAC,EAAE,YAAY,GAAG,OAAO,CAS3E;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,wBAAwB,CACvC,WAAW,EAAE,MAAM,EACnB,YAAY,CAAC,EAAE,YAAY,GACzB,WAAW,EAAE,CAaf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,CA4BvF"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Get default path mappings for a feature
3
+ * Maps webApp → webapplications/<featureName> (old format for path translation)
4
+ * Note: This still uses the old format because translatePathToTargetApp will convert it to SFDX format
5
+ *
6
+ * @param featureName - The name of the feature (e.g., 'feature-react-nav-menu')
7
+ * @returns Array containing the default path mapping
8
+ *
9
+ * @example
10
+ * getDefaultPathMappings('feature-nav')
11
+ * // Returns: [{ from: 'webApp', to: 'webapplications/feature-nav' }]
12
+ */
13
+ export function getDefaultPathMappings(featureName) {
14
+ return [
15
+ {
16
+ from: "webApp",
17
+ to: `webapplications/${featureName}`,
18
+ },
19
+ ];
20
+ }
21
+ /**
22
+ * Check if path mappings are enabled for a feature
23
+ * Default: true (enabled by default, opt-out)
24
+ *
25
+ * @param pathMappings - The path mappings configuration from the feature
26
+ * @returns true if mappings are enabled (default), false if explicitly disabled
27
+ *
28
+ * @example
29
+ * arePathMappingsEnabled(undefined) // Returns: true (default enabled)
30
+ * arePathMappingsEnabled({ enabled: false }) // Returns: false
31
+ * arePathMappingsEnabled({ enabled: true }) // Returns: true
32
+ * arePathMappingsEnabled({ mappings: [...] }) // Returns: true (enabled by default)
33
+ */
34
+ export function arePathMappingsEnabled(pathMappings) {
35
+ // If pathMappings is undefined, default to enabled
36
+ if (!pathMappings)
37
+ return true;
38
+ // If pathMappings.enabled is explicitly set, use that value
39
+ if (pathMappings.enabled !== undefined)
40
+ return pathMappings.enabled;
41
+ // Otherwise, enabled by default
42
+ return true;
43
+ }
44
+ /**
45
+ * Get the effective path mappings for a feature
46
+ * Returns custom mappings if provided, otherwise default mappings
47
+ *
48
+ * @param featureName - The name of the feature
49
+ * @param pathMappings - The path mappings configuration from the feature
50
+ * @returns Array of path mappings to apply (empty if disabled)
51
+ *
52
+ * @example
53
+ * // Default behavior (no config)
54
+ * getEffectivePathMappings('feature-nav', undefined)
55
+ * // Returns: [{ from: 'webApp', to: 'webapplications/feature-nav' }]
56
+ *
57
+ * // Disabled
58
+ * getEffectivePathMappings('feature-nav', { enabled: false })
59
+ * // Returns: []
60
+ *
61
+ * // Custom mappings
62
+ * getEffectivePathMappings('feature-nav', { mappings: [{ from: 'web', to: 'custom/path' }] })
63
+ * // Returns: [{ from: 'web', to: 'custom/path' }]
64
+ */
65
+ export function getEffectivePathMappings(featureName, pathMappings) {
66
+ // If disabled, return empty array (no mappings)
67
+ if (!arePathMappingsEnabled(pathMappings)) {
68
+ return [];
69
+ }
70
+ // If custom mappings provided, use those
71
+ if (pathMappings?.mappings && pathMappings.mappings.length > 0) {
72
+ return pathMappings.mappings;
73
+ }
74
+ // Otherwise, use default mappings
75
+ return getDefaultPathMappings(featureName);
76
+ }
77
+ /**
78
+ * Apply path mappings to a template file path
79
+ *
80
+ * This function transforms simplified paths (e.g., "webApp/src/app.tsx") into
81
+ * their full target paths (e.g., "webapplications/feature-name/src/app.tsx")
82
+ * based on the provided mappings.
83
+ *
84
+ * The function checks each mapping in order and applies the first one that matches.
85
+ * If no mapping matches, the path is returned unchanged (backwards compatibility).
86
+ *
87
+ * @param templatePath - Path relative to template directory (e.g., "webApp/src/app.tsx")
88
+ * @param mappings - Array of path mappings to apply
89
+ * @returns Transformed path with mapping applied
90
+ *
91
+ * @example
92
+ * // Apply default webApp mapping
93
+ * applyPathMappings('webApp/src/app.tsx', [{
94
+ * from: 'webApp',
95
+ * to: 'webapplications/feature-react-nav-menu'
96
+ * }])
97
+ * // Returns: 'webapplications/feature-react-nav-menu/src/app.tsx'
98
+ *
99
+ * @example
100
+ * // Backwards compatibility - old paths pass through
101
+ * applyPathMappings('webapplications/feature-react-nav-menu/src/app.tsx', [...])
102
+ * // Returns: 'webapplications/feature-react-nav-menu/src/app.tsx'
103
+ *
104
+ * @example
105
+ * // No mappings - path unchanged
106
+ * applyPathMappings('webApp/src/app.tsx', [])
107
+ * // Returns: 'webApp/src/app.tsx'
108
+ *
109
+ * @example
110
+ * // No match - path unchanged
111
+ * applyPathMappings('src/app.tsx', [{ from: 'webApp', to: '...' }])
112
+ * // Returns: 'src/app.tsx'
113
+ */
114
+ export function applyPathMappings(templatePath, mappings) {
115
+ // If no mappings, return path as-is (backwards compatibility)
116
+ if (mappings.length === 0) {
117
+ return templatePath;
118
+ }
119
+ // Try each mapping in order (first match wins)
120
+ for (const mapping of mappings) {
121
+ const fromPrefix = mapping.from + "/";
122
+ // Check if path starts with the mapping's "from" prefix
123
+ if (templatePath.startsWith(fromPrefix)) {
124
+ // Replace prefix with "to" prefix
125
+ const remainder = templatePath.substring(fromPrefix.length);
126
+ return mapping.to + "/" + remainder;
127
+ }
128
+ // Also handle exact match (no trailing slash)
129
+ if (templatePath === mapping.from) {
130
+ return mapping.to;
131
+ }
132
+ }
133
+ // No mapping matched, return original path
134
+ // This handles both:
135
+ // 1. Old-style full paths (webapplications/...)
136
+ // 2. Other paths that don't use the mapping shorthand
137
+ return templatePath;
138
+ }
139
+ //# sourceMappingURL=path-mappings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-mappings.js","sourceRoot":"","sources":["../../src/utils/path-mappings.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CAAC,WAAmB;IACzD,OAAO;QACN;YACC,IAAI,EAAE,QAAQ;YACd,EAAE,EAAE,mBAAmB,WAAW,EAAE;SACpC;KACD,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,sBAAsB,CAAC,YAA2B;IACjE,mDAAmD;IACnD,IAAI,CAAC,YAAY;QAAE,OAAO,IAAI,CAAC;IAE/B,4DAA4D;IAC5D,IAAI,YAAY,CAAC,OAAO,KAAK,SAAS;QAAE,OAAO,YAAY,CAAC,OAAO,CAAC;IAEpE,gCAAgC;IAChC,OAAO,IAAI,CAAC;AACb,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,wBAAwB,CACvC,WAAmB,EACnB,YAA2B;IAE3B,gDAAgD;IAChD,IAAI,CAAC,sBAAsB,CAAC,YAAY,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,CAAC;IACX,CAAC;IAED,yCAAyC;IACzC,IAAI,YAAY,EAAE,QAAQ,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChE,OAAO,YAAY,CAAC,QAAQ,CAAC;IAC9B,CAAC;IAED,kCAAkC;IAClC,OAAO,sBAAsB,CAAC,WAAW,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,MAAM,UAAU,iBAAiB,CAAC,YAAoB,EAAE,QAAuB;IAC9E,8DAA8D;IAC9D,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,YAAY,CAAC;IACrB,CAAC;IAED,+CAA+C;IAC/C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;QAChC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,GAAG,GAAG,CAAC;QAEtC,wDAAwD;QACxD,IAAI,YAAY,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YACzC,kCAAkC;YAClC,MAAM,SAAS,GAAG,YAAY,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAC5D,OAAO,OAAO,CAAC,EAAE,GAAG,GAAG,GAAG,SAAS,CAAC;QACrC,CAAC;QAED,8CAA8C;QAC9C,IAAI,YAAY,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC;YACnC,OAAO,OAAO,CAAC,EAAE,CAAC;QACnB,CAAC;IACF,CAAC;IAED,2CAA2C;IAC3C,qBAAqB;IACrB,gDAAgD;IAChD,sDAAsD;IACtD,OAAO,YAAY,CAAC;AACrB,CAAC"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Find the monorepo root by searching upward for package.json with workspaces
3
+ */
4
+ export declare function getMonorepoRoot(): string;
5
+ /**
6
+ * Get the features directory path
7
+ */
8
+ export declare function getFeaturesDir(): string;
9
+ /**
10
+ * Get the directory path for a specific feature
11
+ */
12
+ export declare function getFeatureDir(featureName: string): string;
13
+ /**
14
+ * Resolve a file path within a feature
15
+ */
16
+ export declare function resolveFeatureFile(featureName: string, filePath: string): string;
17
+ /**
18
+ * Get the SFDX project template path
19
+ */
20
+ export declare function getSfdxProjectTemplatePath(): string;
21
+ /**
22
+ * Get the site-container template path (force-app/main/default with digitalExperienceConfigs, etc.)
23
+ */
24
+ export declare function getSiteContainerTemplatePath(): string;
25
+ /**
26
+ * Get the web application path in SFDX structure
27
+ * Structure: force-app/main/default/webapplications/<appName>
28
+ */
29
+ export declare function getSfdxWebApplicationPath(targetDir: string, appName: string): string;
30
+ /**
31
+ * Translate a feature file path to the corresponding base app path
32
+ * Replaces feature name with base app name in the nested structure
33
+ *
34
+ * Handles multiple formats:
35
+ * 1. SFDX path: "force-app/main/default/webapplications/feature-simple/src/routes.tsx"
36
+ * → "force-app/main/default/webapplications/base-app/src/routes.tsx" (SFDX structure)
37
+ * → "webapplications/base-app/src/routes.tsx" (legacy structure)
38
+ * 2. Full path: "webapplications/feature-simple/src/routes.tsx"
39
+ * → "webapplications/base-app/src/routes.tsx"
40
+ * 3. WebApp path: "webApp/src/routes.tsx"
41
+ * → "webapplications/base-app/src/routes.tsx"
42
+ * 4. Other SFDX paths: "classes/NavMenu.cls"
43
+ * → "classes/NavMenu.cls" (unchanged, stays at root)
44
+ */
45
+ export declare function translatePathToBaseApp(featurePath: string, featureName: string, baseAppPath: string): string;
46
+ /**
47
+ * Translate a feature file path to use a target app name in SFDX structure
48
+ * Replaces feature name with target app name in the nested structure
49
+ *
50
+ * Handles multiple formats:
51
+ * 1. SFDX path: "force-app/main/default/webapplications/feature-simple/src/routes.tsx"
52
+ * → "force-app/main/default/webapplications/target-app/src/routes.tsx"
53
+ * 2. Full path: "webapplications/feature-simple/src/routes.tsx"
54
+ * → "force-app/main/default/webapplications/target-app/src/routes.tsx"
55
+ * 3. WebApp path: "webApp/src/routes.tsx"
56
+ * → "force-app/main/default/webapplications/target-app/src/routes.tsx"
57
+ * 4. Other SFDX paths: "classes/NavMenu.cls"
58
+ * → "force-app/main/default/classes/NavMenu.cls" (moved to SFDX structure)
59
+ */
60
+ export declare function translatePathToTargetApp(featurePath: string, featureName: string, targetAppName: string): string;
61
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAiBxC;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,CAWrD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpF;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CACrC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GACjB,MAAM,CAoDR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,wBAAwB,CACvC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,GACnB,MAAM,CAwCR"}
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import { existsSync, readFileSync } from "fs";
7
+ import { join, dirname, basename } from "path";
8
+ import { fileURLToPath } from "url";
9
+ /**
10
+ * Find the monorepo root by searching upward for package.json with workspaces
11
+ */
12
+ export function getMonorepoRoot() {
13
+ let currentDir = dirname(fileURLToPath(import.meta.url));
14
+ while (currentDir !== "/") {
15
+ const packageJsonPath = join(currentDir, "package.json");
16
+ if (existsSync(packageJsonPath)) {
17
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
18
+ if (packageJson.workspaces) {
19
+ return currentDir;
20
+ }
21
+ }
22
+ currentDir = dirname(currentDir);
23
+ }
24
+ throw new Error("Could not find monorepo root (package.json with workspaces)");
25
+ }
26
+ /**
27
+ * Get the features directory path
28
+ */
29
+ export function getFeaturesDir() {
30
+ return join(getMonorepoRoot(), "packages", "template", "feature");
31
+ }
32
+ /**
33
+ * Get the directory path for a specific feature
34
+ */
35
+ export function getFeatureDir(featureName) {
36
+ return join(getFeaturesDir(), featureName);
37
+ }
38
+ /**
39
+ * Resolve a file path within a feature
40
+ */
41
+ export function resolveFeatureFile(featureName, filePath) {
42
+ return join(getFeatureDir(featureName), filePath);
43
+ }
44
+ /**
45
+ * Get the SFDX project template path
46
+ */
47
+ export function getSfdxProjectTemplatePath() {
48
+ return join(getMonorepoRoot(), "packages", "template", "template", "template-sfdx-project");
49
+ }
50
+ /**
51
+ * Get the site-container template path (force-app/main/default with digitalExperienceConfigs, etc.)
52
+ */
53
+ export function getSiteContainerTemplatePath() {
54
+ return join(getMonorepoRoot(), "packages", "template", "template", "template-site-container", "force-app", "main", "default");
55
+ }
56
+ /**
57
+ * Get the web application path in SFDX structure
58
+ * Structure: force-app/main/default/webapplications/<appName>
59
+ */
60
+ export function getSfdxWebApplicationPath(targetDir, appName) {
61
+ return join(targetDir, "force-app", "main", "default", "webapplications", appName);
62
+ }
63
+ /**
64
+ * Translate a feature file path to the corresponding base app path
65
+ * Replaces feature name with base app name in the nested structure
66
+ *
67
+ * Handles multiple formats:
68
+ * 1. SFDX path: "force-app/main/default/webapplications/feature-simple/src/routes.tsx"
69
+ * → "force-app/main/default/webapplications/base-app/src/routes.tsx" (SFDX structure)
70
+ * → "webapplications/base-app/src/routes.tsx" (legacy structure)
71
+ * 2. Full path: "webapplications/feature-simple/src/routes.tsx"
72
+ * → "webapplications/base-app/src/routes.tsx"
73
+ * 3. WebApp path: "webApp/src/routes.tsx"
74
+ * → "webapplications/base-app/src/routes.tsx"
75
+ * 4. Other SFDX paths: "classes/NavMenu.cls"
76
+ * → "classes/NavMenu.cls" (unchanged, stays at root)
77
+ */
78
+ export function translatePathToBaseApp(featurePath, featureName, baseAppPath) {
79
+ // Extract base app name from base app path (last directory component)
80
+ const baseAppName = basename(baseAppPath);
81
+ const sfdxPrefix = "force-app/main/default/";
82
+ const webAppPrefix = "webapplications/";
83
+ // Check for SFDX structure path
84
+ if (featurePath.startsWith(sfdxPrefix + webAppPrefix)) {
85
+ // SFDX path format: force-app/main/default/webapplications/<feature-name>/...
86
+ const remainder = featurePath.substring((sfdxPrefix + webAppPrefix).length);
87
+ if (remainder.startsWith(featureName + "/")) {
88
+ // Replace feature name with base app name
89
+ const pathAfterFeatureName = remainder.substring(featureName.length + 1);
90
+ // Check if base app uses SFDX structure
91
+ if (existsSync(join(baseAppPath, "src", "force-app"))) {
92
+ // Base app uses SFDX structure - include src/ prefix
93
+ return `src/${sfdxPrefix}${webAppPrefix}${baseAppName}/${pathAfterFeatureName}`;
94
+ }
95
+ else {
96
+ // Base app uses legacy structure
97
+ return `${webAppPrefix}${baseAppName}/${pathAfterFeatureName}`;
98
+ }
99
+ }
100
+ }
101
+ // Pattern: webapplications/<feature-name>/...
102
+ if (featurePath.startsWith(webAppPrefix)) {
103
+ // Full path format - remove prefix to get: <feature-name>/...
104
+ const remainder = featurePath.substring(webAppPrefix.length);
105
+ // Check if it starts with the feature name
106
+ if (remainder.startsWith(featureName + "/")) {
107
+ // Replace feature name with base app name
108
+ const pathAfterFeatureName = remainder.substring(featureName.length + 1);
109
+ // Match base app structure (SFDX vs legacy)
110
+ if (existsSync(join(baseAppPath, "src", "force-app"))) {
111
+ return `src/${sfdxPrefix}${webAppPrefix}${baseAppName}/${pathAfterFeatureName}`;
112
+ }
113
+ return `${webAppPrefix}${baseAppName}/${pathAfterFeatureName}`;
114
+ }
115
+ }
116
+ else if (featurePath.startsWith("webApp/")) {
117
+ // WebApp path - strip webApp/ prefix and add full webapplications path
118
+ const pathAfterWebApp = featurePath.substring("webApp/".length);
119
+ if (existsSync(join(baseAppPath, "src", "force-app"))) {
120
+ return `src/${sfdxPrefix}${webAppPrefix}${baseAppName}/${pathAfterWebApp}`;
121
+ }
122
+ return `${webAppPrefix}${baseAppName}/${pathAfterWebApp}`;
123
+ }
124
+ // All other paths (SFDX metadata, etc.) - return as is (stays at root level)
125
+ return featurePath;
126
+ }
127
+ /**
128
+ * Translate a feature file path to use a target app name in SFDX structure
129
+ * Replaces feature name with target app name in the nested structure
130
+ *
131
+ * Handles multiple formats:
132
+ * 1. SFDX path: "force-app/main/default/webapplications/feature-simple/src/routes.tsx"
133
+ * → "force-app/main/default/webapplications/target-app/src/routes.tsx"
134
+ * 2. Full path: "webapplications/feature-simple/src/routes.tsx"
135
+ * → "force-app/main/default/webapplications/target-app/src/routes.tsx"
136
+ * 3. WebApp path: "webApp/src/routes.tsx"
137
+ * → "force-app/main/default/webapplications/target-app/src/routes.tsx"
138
+ * 4. Other SFDX paths: "classes/NavMenu.cls"
139
+ * → "force-app/main/default/classes/NavMenu.cls" (moved to SFDX structure)
140
+ */
141
+ export function translatePathToTargetApp(featurePath, featureName, targetAppName) {
142
+ const sfdxPrefix = "force-app/main/default/";
143
+ const webAppPrefix = "webapplications/";
144
+ // Check for SFDX structure path (already has force-app prefix)
145
+ if (featurePath.startsWith(sfdxPrefix + webAppPrefix)) {
146
+ // SFDX path format: force-app/main/default/webapplications/<feature-name>/...
147
+ const remainder = featurePath.substring((sfdxPrefix + webAppPrefix).length);
148
+ if (remainder.startsWith(featureName + "/")) {
149
+ // Replace feature name with target app name
150
+ const pathAfterFeatureName = remainder.substring(featureName.length + 1);
151
+ return `${sfdxPrefix}${webAppPrefix}${targetAppName}/${pathAfterFeatureName}`;
152
+ }
153
+ }
154
+ // Pattern: webapplications/<feature-name>/...
155
+ if (featurePath.startsWith(webAppPrefix)) {
156
+ // Full path format - remove prefix to get: <feature-name>/...
157
+ const remainder = featurePath.substring(webAppPrefix.length);
158
+ // Check if it starts with the feature name
159
+ if (remainder.startsWith(featureName + "/")) {
160
+ // Replace feature name with target app name and add SFDX prefix
161
+ const pathAfterFeatureName = remainder.substring(featureName.length + 1);
162
+ return `${sfdxPrefix}${webAppPrefix}${targetAppName}/${pathAfterFeatureName}`;
163
+ }
164
+ }
165
+ else if (featurePath.startsWith("webApp/")) {
166
+ // WebApp path - strip webApp/ prefix and add full SFDX webapplications path
167
+ const pathAfterWebApp = featurePath.substring("webApp/".length);
168
+ return `${sfdxPrefix}${webAppPrefix}${targetAppName}/${pathAfterWebApp}`;
169
+ }
170
+ // All other paths (SFDX metadata, etc.) - move to SFDX structure
171
+ // e.g., "classes/NavMenu.cls" → "force-app/main/default/classes/NavMenu.cls"
172
+ // But skip if already has SFDX prefix
173
+ if (featurePath.startsWith(sfdxPrefix)) {
174
+ return featurePath;
175
+ }
176
+ return `${sfdxPrefix}${featurePath}`;
177
+ }
178
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ import { Project } from "ts-morph";
2
+ /**
3
+ * Route Merging Strategy: Replace-Matching with Deep Children Merge
4
+ *
5
+ * This function merges React Router routes from a feature into a base app using
6
+ * a "replace-matching" strategy with intelligent children array merging.
7
+ *
8
+ * ## How It Works:
9
+ *
10
+ * 1. **Top-Level Routes:**
11
+ * - Routes with the SAME path → Merge their children arrays
12
+ * - Routes with DIFFERENT paths → Add feature route to result
13
+ *
14
+ * 2. **Children Array Merging (when parent paths match):**
15
+ * - Index routes (`index: true`) → Feature replaces base if both exist
16
+ * - Named routes (`path: 'about'`) → Feature replaces base if paths match
17
+ * - New routes → Added to children array
18
+ * - Preserves base routes not in feature
19
+ *
20
+ * 3. **Route Deletion:**
21
+ * - Routes with path starting with `__delete__` are deletion markers
22
+ * - The route with matching path (without prefix) is removed from result
23
+ * - Example: `{ path: '__delete__new' }` removes the route with `path: 'new'`
24
+ *
25
+ * 4. **Recursion:**
26
+ * - Applies same logic to nested children arrays
27
+ *
28
+ * ## Example:
29
+ *
30
+ * **Base App Routes:**
31
+ * ```typescript
32
+ * [{
33
+ * path: '/',
34
+ * element: <AppLayout />,
35
+ * children: [
36
+ * { index: true, element: <Home /> }
37
+ * ]
38
+ * }]
39
+ * ```
40
+ *
41
+ * **Feature Routes:**
42
+ * ```typescript
43
+ * [{
44
+ * path: '/',
45
+ * element: <AppLayout />,
46
+ * children: [
47
+ * { path: 'about', element: <About /> },
48
+ * { path: 'contact', element: <Contact /> }
49
+ * ]
50
+ * }]
51
+ * ```
52
+ *
53
+ * **Merged Result:**
54
+ * ```typescript
55
+ * [{
56
+ * path: '/',
57
+ * element: <AppLayout />, // Uses feature's element
58
+ * children: [
59
+ * { index: true, element: <Home /> }, // Preserved from base
60
+ * { path: 'about', element: <About /> }, // Added from feature
61
+ * { path: 'contact', element: <Contact /> } // Added from feature
62
+ * ]
63
+ * }]
64
+ * ```
65
+ *
66
+ * ## Deletion Example:
67
+ *
68
+ * **Base/Previous Routes:**
69
+ * ```typescript
70
+ * [{
71
+ * path: '/',
72
+ * children: [
73
+ * { path: 'home', element: <Home /> },
74
+ * { path: 'about', element: <About /> },
75
+ * { path: 'new', element: <New /> }
76
+ * ]
77
+ * }]
78
+ * ```
79
+ *
80
+ * **Feature Routes (deleting 'new'):**
81
+ * ```typescript
82
+ * [{
83
+ * path: '/',
84
+ * children: [
85
+ * { path: '__delete__new', element: <></> }
86
+ * ]
87
+ * }]
88
+ * ```
89
+ *
90
+ * **Merged Result:**
91
+ * ```typescript
92
+ * [{
93
+ * path: '/',
94
+ * children: [
95
+ * { path: 'home', element: <Home /> }, // Preserved
96
+ * { path: 'about', element: <About /> } // Preserved
97
+ * // 'new' route removed
98
+ * ]
99
+ * }]
100
+ * ```
101
+ *
102
+ * @param featurePath - Path to the feature's routes.tsx file
103
+ * @param targetPath - Path to the base app's routes.tsx file
104
+ * @returns The merged routes source code
105
+ */
106
+ export declare function mergeRoutes(featurePath: string, targetPath: string, project?: Project): string;
107
+ //# sourceMappingURL=route-merger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-merger.d.ts","sourceRoot":"","sources":["../../src/utils/route-merger.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAiD,MAAM,UAAU,CAAC;AAkBlF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuGG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CA0D9F"}