@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,358 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ import path from "path";
7
+ import { Project, ArrayLiteralExpression, Node } from "ts-morph";
8
+ import { mergeImports } from "./import-merger.js";
9
+ import * as logger from "./logger.js";
10
+ /**
11
+ * Route Merging Strategy: Replace-Matching with Deep Children Merge
12
+ *
13
+ * This function merges React Router routes from a feature into a base app using
14
+ * a "replace-matching" strategy with intelligent children array merging.
15
+ *
16
+ * ## How It Works:
17
+ *
18
+ * 1. **Top-Level Routes:**
19
+ * - Routes with the SAME path → Merge their children arrays
20
+ * - Routes with DIFFERENT paths → Add feature route to result
21
+ *
22
+ * 2. **Children Array Merging (when parent paths match):**
23
+ * - Index routes (`index: true`) → Feature replaces base if both exist
24
+ * - Named routes (`path: 'about'`) → Feature replaces base if paths match
25
+ * - New routes → Added to children array
26
+ * - Preserves base routes not in feature
27
+ *
28
+ * 3. **Route Deletion:**
29
+ * - Routes with path starting with `__delete__` are deletion markers
30
+ * - The route with matching path (without prefix) is removed from result
31
+ * - Example: `{ path: '__delete__new' }` removes the route with `path: 'new'`
32
+ *
33
+ * 4. **Recursion:**
34
+ * - Applies same logic to nested children arrays
35
+ *
36
+ * ## Example:
37
+ *
38
+ * **Base App Routes:**
39
+ * ```typescript
40
+ * [{
41
+ * path: '/',
42
+ * element: <AppLayout />,
43
+ * children: [
44
+ * { index: true, element: <Home /> }
45
+ * ]
46
+ * }]
47
+ * ```
48
+ *
49
+ * **Feature Routes:**
50
+ * ```typescript
51
+ * [{
52
+ * path: '/',
53
+ * element: <AppLayout />,
54
+ * children: [
55
+ * { path: 'about', element: <About /> },
56
+ * { path: 'contact', element: <Contact /> }
57
+ * ]
58
+ * }]
59
+ * ```
60
+ *
61
+ * **Merged Result:**
62
+ * ```typescript
63
+ * [{
64
+ * path: '/',
65
+ * element: <AppLayout />, // Uses feature's element
66
+ * children: [
67
+ * { index: true, element: <Home /> }, // Preserved from base
68
+ * { path: 'about', element: <About /> }, // Added from feature
69
+ * { path: 'contact', element: <Contact /> } // Added from feature
70
+ * ]
71
+ * }]
72
+ * ```
73
+ *
74
+ * ## Deletion Example:
75
+ *
76
+ * **Base/Previous Routes:**
77
+ * ```typescript
78
+ * [{
79
+ * path: '/',
80
+ * children: [
81
+ * { path: 'home', element: <Home /> },
82
+ * { path: 'about', element: <About /> },
83
+ * { path: 'new', element: <New /> }
84
+ * ]
85
+ * }]
86
+ * ```
87
+ *
88
+ * **Feature Routes (deleting 'new'):**
89
+ * ```typescript
90
+ * [{
91
+ * path: '/',
92
+ * children: [
93
+ * { path: '__delete__new', element: <></> }
94
+ * ]
95
+ * }]
96
+ * ```
97
+ *
98
+ * **Merged Result:**
99
+ * ```typescript
100
+ * [{
101
+ * path: '/',
102
+ * children: [
103
+ * { path: 'home', element: <Home /> }, // Preserved
104
+ * { path: 'about', element: <About /> } // Preserved
105
+ * // 'new' route removed
106
+ * ]
107
+ * }]
108
+ * ```
109
+ *
110
+ * @param featurePath - Path to the feature's routes.tsx file
111
+ * @param targetPath - Path to the base app's routes.tsx file
112
+ * @returns The merged routes source code
113
+ */
114
+ export function mergeRoutes(featurePath, targetPath, project) {
115
+ try {
116
+ const proj = project || new Project();
117
+ const targetFile = project
118
+ ? proj.getSourceFile(targetPath) || proj.addSourceFileAtPath(path.resolve(targetPath))
119
+ : proj.addSourceFileAtPath(path.resolve(targetPath));
120
+ const featureFile = project
121
+ ? proj.getSourceFile(featurePath) || proj.addSourceFileAtPath(path.resolve(featurePath))
122
+ : proj.addSourceFileAtPath(path.resolve(featurePath));
123
+ // Get initial routes arrays to validate they exist
124
+ const initialTargetArray = getRoutesArrayLiteral(targetFile, "routes");
125
+ const initialFeatureArray = getRoutesArrayLiteral(featureFile, "routes");
126
+ if (!initialTargetArray) {
127
+ logger.warning("Could not extract routes array from target file");
128
+ return featureFile.getText();
129
+ }
130
+ if (!initialFeatureArray) {
131
+ logger.warning("Could not extract routes array from feature file");
132
+ return targetFile.getText();
133
+ }
134
+ // Merge imports from feature file into target file
135
+ // Note: This calls organizeImports() which invalidates AST node references
136
+ mergeImports(featureFile, targetFile);
137
+ // Re-get routes arrays after import merging (organizeImports invalidates previous references)
138
+ const targetArray = getRoutesArrayLiteral(targetFile, "routes");
139
+ const featureArray = getRoutesArrayLiteral(featureFile, "routes");
140
+ if (!targetArray || !featureArray) {
141
+ logger.error("Routes array was lost after import merging");
142
+ throw new Error("Failed to re-acquire routes arrays after import merging");
143
+ }
144
+ // Parse arrays to RouteObject[]
145
+ const targetRoutes = parseRoutesFromAST(targetArray);
146
+ const featureRoutes = parseRoutesFromAST(featureArray);
147
+ // Deep merge the routes
148
+ const mergedRoutes = deepMergeRoutes(targetRoutes, featureRoutes);
149
+ // Convert back to code and update the target
150
+ const mergedCode = generateRoutesCode(mergedRoutes);
151
+ targetArray.replaceWithText(mergedCode);
152
+ // Remove default imports that are no longer used in routes (e.g. after __delete__ route)
153
+ removeUnusedRouteImports(targetFile, mergedRoutes);
154
+ return targetFile.getText();
155
+ }
156
+ catch (err) {
157
+ logger.error(`Route merge failed: ${err instanceof Error ? err.message : String(err)}`);
158
+ throw new Error("Failed to merge routes");
159
+ }
160
+ }
161
+ /**
162
+ * Get the array literal expression for the routes variable
163
+ */
164
+ function getRoutesArrayLiteral(file, varName) {
165
+ const variable = file.getVariableDeclaration(varName);
166
+ if (!variable) {
167
+ logger.warning(`Variable "${varName}" not found in ${file.getFilePath()}`);
168
+ return undefined;
169
+ }
170
+ const initializer = variable.getInitializer();
171
+ if (!initializer || !ArrayLiteralExpression.isArrayLiteralExpression(initializer)) {
172
+ logger.warning(`Variable "${varName}" is not initialized with an array literal`);
173
+ return undefined;
174
+ }
175
+ return initializer;
176
+ }
177
+ /**
178
+ * Parse routes from AST ArrayLiteralExpression to RouteObject[]
179
+ */
180
+ function parseRoutesFromAST(arrayLiteral) {
181
+ const routes = [];
182
+ for (const element of arrayLiteral.getElements()) {
183
+ if (Node.isObjectLiteralExpression(element)) {
184
+ const route = {};
185
+ for (const prop of element.getProperties()) {
186
+ if (Node.isPropertyAssignment(prop)) {
187
+ const name = prop.getName();
188
+ const initializer = prop.getInitializer();
189
+ if (!initializer)
190
+ continue;
191
+ if (name === "children" && Node.isArrayLiteralExpression(initializer)) {
192
+ // Recursively parse children
193
+ route.children = parseRoutesFromAST(initializer);
194
+ }
195
+ else {
196
+ // Store the raw text for other properties
197
+ route[name] = initializer.getText();
198
+ }
199
+ }
200
+ }
201
+ routes.push(route);
202
+ }
203
+ }
204
+ return routes;
205
+ }
206
+ /**
207
+ * Deep merge two route arrays
208
+ * - Routes are matched by path equality
209
+ * - Feature routes replace target routes with the same path
210
+ * - Children arrays are recursively merged
211
+ * - All properties from feature route are preserved
212
+ * - Routes with path starting with '__delete__' are deletion markers
213
+ */
214
+ function normalizePath(pathStr) {
215
+ return pathStr.replace(/^['"`]|['"`]$/g, "");
216
+ }
217
+ /** Remove the first route with matching path from the array or any nested children. Returns true if removed. */
218
+ function deleteRouteByPath(routes, targetPath) {
219
+ const index = routes.findIndex((r) => {
220
+ if (r.path && typeof r.path === "string") {
221
+ return normalizePath(r.path) === targetPath;
222
+ }
223
+ return false;
224
+ });
225
+ if (index >= 0) {
226
+ routes.splice(index, 1);
227
+ return true;
228
+ }
229
+ for (const r of routes) {
230
+ if (r.children && Array.isArray(r.children)) {
231
+ if (deleteRouteByPath(r.children, targetPath))
232
+ return true;
233
+ }
234
+ }
235
+ return false;
236
+ }
237
+ function deepMergeRoutes(targetRoutes, featureRoutes) {
238
+ const result = [...targetRoutes];
239
+ for (const featureRoute of featureRoutes) {
240
+ // Check if this is a deletion marker
241
+ const DELETE_PREFIX = "__delete__";
242
+ if (featureRoute.path && typeof featureRoute.path === "string") {
243
+ // Remove quotes from path string if present
244
+ const pathValue = featureRoute.path.replace(/^['"`]|['"`]$/g, "");
245
+ if (pathValue.startsWith(DELETE_PREFIX)) {
246
+ // This is a deletion marker - remove the route with matching path (current level or nested)
247
+ const targetPath = pathValue.substring(DELETE_PREFIX.length);
248
+ const deleted = deleteRouteByPath(result, targetPath);
249
+ if (deleted) {
250
+ logger.info(`Deleted route with path: ${targetPath}`);
251
+ }
252
+ else {
253
+ logger.info(`Skipped deletion of route '${targetPath}' (not found in current routes)`);
254
+ }
255
+ // Don't add the deletion marker to result
256
+ continue;
257
+ }
258
+ }
259
+ const matchIndex = result.findIndex((targetRoute) => {
260
+ // Match by path if both have paths (normalize for comparison)
261
+ if (featureRoute.path && targetRoute.path) {
262
+ return normalizePath(featureRoute.path) === normalizePath(targetRoute.path);
263
+ }
264
+ // Match index routes
265
+ if (featureRoute.index && targetRoute.index) {
266
+ return true;
267
+ }
268
+ return false;
269
+ });
270
+ if (matchIndex >= 0) {
271
+ const targetRoute = result[matchIndex];
272
+ // Merge the route, starting with feature route properties
273
+ const mergedRoute = { ...featureRoute };
274
+ // If both have children, recursively merge them
275
+ if (targetRoute.children && featureRoute.children) {
276
+ mergedRoute.children = deepMergeRoutes(targetRoute.children, featureRoute.children);
277
+ }
278
+ else if (targetRoute.children) {
279
+ // Only target has children, preserve them
280
+ mergedRoute.children = targetRoute.children;
281
+ }
282
+ // If only feature has children, they're already in mergedRoute
283
+ result[matchIndex] = mergedRoute;
284
+ }
285
+ else {
286
+ // No match found, add the new route
287
+ result.push(featureRoute);
288
+ }
289
+ }
290
+ return result;
291
+ }
292
+ /**
293
+ * Generate TypeScript code from RouteObject[]
294
+ */
295
+ function generateRoutesCode(routes) {
296
+ if (routes.length === 0) {
297
+ return "[]";
298
+ }
299
+ const routeStrings = routes.map((route) => generateRouteCode(route, 1));
300
+ return `[\n${routeStrings.join(",\n")}\n]`;
301
+ }
302
+ /**
303
+ * Generate code for a single route object
304
+ */
305
+ function generateRouteCode(route, indentLevel) {
306
+ const indent = " ".repeat(indentLevel);
307
+ const props = [];
308
+ for (const [key, value] of Object.entries(route)) {
309
+ if (key === "children" && Array.isArray(value)) {
310
+ // Recursively generate children
311
+ const childrenCode = value.length > 0
312
+ ? `[\n${value.map((child) => generateRouteCode(child, indentLevel + 2)).join(",\n")}\n${indent} ]`
313
+ : "[]";
314
+ props.push(`children: ${childrenCode}`);
315
+ }
316
+ else if (value !== undefined) {
317
+ // For other properties, use the raw text we stored
318
+ props.push(`${key}: ${value}`);
319
+ }
320
+ }
321
+ return `${indent}{\n${props.map((p) => `${indent} ${p}`).join(",\n")}\n${indent}}`;
322
+ }
323
+ /** Collect component names used in route elements (e.g. <Home /> -> Home). */
324
+ function collectUsedComponentNames(routes) {
325
+ const used = new Set();
326
+ function walk(r) {
327
+ for (const route of r) {
328
+ const el = route.element;
329
+ if (typeof el === "string") {
330
+ const matches = el.match(/<(\w+)/g);
331
+ if (matches) {
332
+ for (const m of matches) {
333
+ used.add(m.slice(1)); // strip "<"
334
+ }
335
+ }
336
+ }
337
+ if (route.children && Array.isArray(route.children)) {
338
+ walk(route.children);
339
+ }
340
+ }
341
+ }
342
+ walk(routes);
343
+ return used;
344
+ }
345
+ /** Remove default imports whose name is not used in the merged routes (e.g. after deleting a route). */
346
+ function removeUnusedRouteImports(targetFile, mergedRoutes) {
347
+ const used = collectUsedComponentNames(mergedRoutes);
348
+ for (const imp of targetFile.getImportDeclarations()) {
349
+ const defaultImport = imp.getDefaultImport();
350
+ if (defaultImport) {
351
+ const name = defaultImport.getText();
352
+ if (!used.has(name)) {
353
+ imp.remove();
354
+ }
355
+ }
356
+ }
357
+ }
358
+ //# sourceMappingURL=route-merger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-merger.js","sourceRoot":"","sources":["../../src/utils/route-merger.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ export declare class ValidationError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ /**
5
+ * Get the web application directory path from a base app path
6
+ * Supports both old structure (<basePath>/webapplications/<appName>) and
7
+ * new SFDX structure (<basePath>/src/force-app/main/default/webapplications/<appName>)
8
+ */
9
+ export declare function getWebApplicationPath(basePath: string): string;
10
+ /**
11
+ * Get the web application directory path in SFDX structure
12
+ * Structure: <targetDir>/force-app/main/default/webapplications/<appName>
13
+ */
14
+ export declare function getSfdxWebApplicationPath(targetDir: string, appName: string): string;
15
+ /**
16
+ * Validate that a feature path exists and resolve it to an absolute path
17
+ */
18
+ export declare function validateAndResolveFeaturePath(featurePath: string): string;
19
+ /**
20
+ * Validate and resolve an app path (can be relative or absolute)
21
+ * Validates that the web application directory exists with proper structure
22
+ * Returns the absolute path to the base app directory
23
+ */
24
+ export declare function validateAndResolveAppPath(appPath: string): string;
25
+ /**
26
+ * Validate that a app or feature name is valid
27
+ * - Must be in kebab-case (lowercase with hyphens)
28
+ * - Only alphanumeric characters and hyphens allowed
29
+ * - Cannot start or end with hyphens
30
+ * - No consecutive hyphens
31
+ * - Cannot be reserved names: "base", "cli"
32
+ * - Max 50 characters
33
+ */
34
+ export declare function validateAppOrFeatureName(name: string): void;
35
+ //# sourceMappingURL=validation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AASA,qBAAa,eAAgB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI3B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAyB9D;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpF;AAED;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CA0BzE;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAqDjE;AAID;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAyB3D"}
@@ -0,0 +1,137 @@
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, statSync } from "fs";
7
+ import { join, isAbsolute, basename } from "path";
8
+ import { getMonorepoRoot } from "./paths.js";
9
+ export class ValidationError extends Error {
10
+ constructor(message) {
11
+ super(message);
12
+ this.name = "ValidationError";
13
+ }
14
+ }
15
+ /**
16
+ * Get the web application directory path from a base app path
17
+ * Supports both old structure (<basePath>/webapplications/<appName>) and
18
+ * new SFDX structure (<basePath>/src/force-app/main/default/webapplications/<appName>)
19
+ */
20
+ export function getWebApplicationPath(basePath) {
21
+ const appName = basename(basePath);
22
+ // Try SFDX structure first (new)
23
+ const sfdxPath = join(basePath, "src", "force-app", "main", "default", "webapplications", appName);
24
+ if (existsSync(sfdxPath)) {
25
+ return sfdxPath;
26
+ }
27
+ // Fall back to old structure (for backward compatibility during migration)
28
+ const oldPath = join(basePath, "webapplications", appName);
29
+ if (existsSync(oldPath)) {
30
+ return oldPath;
31
+ }
32
+ // If neither exists, return SFDX structure (expected default)
33
+ return sfdxPath;
34
+ }
35
+ /**
36
+ * Get the web application directory path in SFDX structure
37
+ * Structure: <targetDir>/force-app/main/default/webapplications/<appName>
38
+ */
39
+ export function getSfdxWebApplicationPath(targetDir, appName) {
40
+ return join(targetDir, "force-app", "main", "default", "webapplications", appName);
41
+ }
42
+ /**
43
+ * Validate that a feature path exists and resolve it to an absolute path
44
+ */
45
+ export function validateAndResolveFeaturePath(featurePath) {
46
+ let resolvedPath;
47
+ // If it's an absolute path, use it as-is
48
+ if (isAbsolute(featurePath)) {
49
+ resolvedPath = featurePath;
50
+ }
51
+ else {
52
+ // If it's relative, resolve from monorepo root
53
+ resolvedPath = join(getMonorepoRoot(), featurePath);
54
+ }
55
+ if (!existsSync(resolvedPath)) {
56
+ throw new ValidationError(`Feature path '${featurePath}' does not exist`);
57
+ }
58
+ if (!statSync(resolvedPath).isDirectory()) {
59
+ throw new ValidationError(`Feature path '${featurePath}' is not a directory`);
60
+ }
61
+ // Check if it has a feature.ts file
62
+ const featureFilePath = join(resolvedPath, "feature.ts");
63
+ if (!existsSync(featureFilePath)) {
64
+ throw new ValidationError(`Feature path '${featurePath}' does not contain a feature.ts file`);
65
+ }
66
+ return resolvedPath;
67
+ }
68
+ /**
69
+ * Validate and resolve an app path (can be relative or absolute)
70
+ * Validates that the web application directory exists with proper structure
71
+ * Returns the absolute path to the base app directory
72
+ */
73
+ export function validateAndResolveAppPath(appPath) {
74
+ let resolvedBasePath;
75
+ // If it's an absolute path, use it as-is
76
+ if (isAbsolute(appPath)) {
77
+ resolvedBasePath = appPath;
78
+ }
79
+ else {
80
+ // If it's relative, resolve from monorepo root
81
+ resolvedBasePath = join(getMonorepoRoot(), appPath);
82
+ }
83
+ if (!existsSync(resolvedBasePath)) {
84
+ throw new ValidationError(`App path '${appPath}' does not exist`);
85
+ }
86
+ if (!statSync(resolvedBasePath).isDirectory()) {
87
+ throw new ValidationError(`App path '${appPath}' is not a directory`);
88
+ }
89
+ // Get the web application directory path
90
+ const webAppPath = getWebApplicationPath(resolvedBasePath);
91
+ if (!existsSync(webAppPath)) {
92
+ const sfdxPath = join(resolvedBasePath, "src", "force-app", "main", "default", "webapplications", basename(resolvedBasePath));
93
+ const oldPath = join(resolvedBasePath, "webapplications", basename(resolvedBasePath));
94
+ throw new ValidationError(`Web application directory does not exist. Expected structure:\n` +
95
+ ` SFDX: ${sfdxPath}\n` +
96
+ ` Legacy: ${oldPath}`);
97
+ }
98
+ if (!statSync(webAppPath).isDirectory()) {
99
+ throw new ValidationError(`Web application path '${webAppPath}' is not a directory`);
100
+ }
101
+ // Check if it has a package.json
102
+ const packageJsonPath = join(webAppPath, "package.json");
103
+ if (!existsSync(packageJsonPath)) {
104
+ throw new ValidationError(`Web application '${webAppPath}' does not contain a package.json file`);
105
+ }
106
+ return resolvedBasePath;
107
+ }
108
+ const APP_FEATURE_NAME_REGEX = /^[a-z0-9]+(-[a-z0-9]+)*$/;
109
+ /**
110
+ * Validate that a app or feature name is valid
111
+ * - Must be in kebab-case (lowercase with hyphens)
112
+ * - Only alphanumeric characters and hyphens allowed
113
+ * - Cannot start or end with hyphens
114
+ * - No consecutive hyphens
115
+ * - Cannot be reserved names: "base", "cli"
116
+ * - Max 50 characters
117
+ */
118
+ export function validateAppOrFeatureName(name) {
119
+ // Check empty
120
+ if (!name || name.trim().length === 0) {
121
+ throw new ValidationError("App or feature name cannot be empty");
122
+ }
123
+ // Check format
124
+ if (!APP_FEATURE_NAME_REGEX.test(name)) {
125
+ throw new ValidationError(`App or feature name '${name}' is invalid. Must be kebab-case (lowercase letters, numbers, and hyphens only).\nExamples: "nav-menu", "user-auth", "dashboard-v2"`);
126
+ }
127
+ // Check reserved names
128
+ const reserved = ["app", "feature"];
129
+ if (reserved.includes(name)) {
130
+ throw new ValidationError(`App or feature name '${name}' is reserved. Please choose a different name.`);
131
+ }
132
+ // Check length
133
+ if (name.length > 50) {
134
+ throw new ValidationError(`App or feature name '${name}' is too long. Maximum 50 characters.`);
135
+ }
136
+ }
137
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@salesforce/webapp-template-cli-experimental",
3
+ "version": "1.3.3",
4
+ "description": "CLI tool for applying feature patches to base apps",
5
+ "type": "module",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "bin": {
13
+ "apply-patches": "./dist/index.js"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc",
17
+ "clean": "rm -rf dist",
18
+ "dev": "tsx src/index.ts",
19
+ "test": "vitest run",
20
+ "test:ui": "vitest run --ui",
21
+ "test:e2e": "vitest run e2e",
22
+ "test:unit": "vitest run unit",
23
+ "test:coverage": "vitest run --coverage"
24
+ },
25
+ "dependencies": {
26
+ "chalk": "^5.3.0",
27
+ "commander": "^12.0.0",
28
+ "fs-extra": "^11.2.0",
29
+ "ts-morph": "^27.0.2",
30
+ "turbowatch": "^2.29.4"
31
+ },
32
+ "devDependencies": {
33
+ "@types/fs-extra": "^11.0.4",
34
+ "@types/node": "^24.10.1",
35
+ "@types/react-dom": "^19.2.3",
36
+ "@vitest/coverage-v8": "4.0.17",
37
+ "@vitest/ui": "^4.0.17",
38
+ "react-dom": "^19.2.1",
39
+ "tsx": "^4.19.2",
40
+ "typescript": "~5.9.3",
41
+ "vitest": "^4.0.17"
42
+ },
43
+ "gitHead": "80d16bc3ec3fc1ed55b8059f6389cf89eb464385"
44
+ }