@sap-ux/fiori-migration-writer 0.0.4 → 0.0.6

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 (108) hide show
  1. package/dist/ProjectMigrator.d.ts +20 -8
  2. package/dist/ProjectMigrator.js +121 -123
  3. package/dist/adapters/ui5-config-adapter.js +64 -18
  4. package/dist/adapters/ui5-config-helpers.js +3 -10
  5. package/dist/components/extension.d.ts +3 -1
  6. package/dist/components/extension.js +5 -4
  7. package/dist/config/backend-i18n.d.ts +27 -6
  8. package/dist/config/backend-i18n.js +5 -11
  9. package/dist/config/flp/intent.d.ts +6 -3
  10. package/dist/config/flp/intent.js +6 -4
  11. package/dist/config/flp/sandbox.d.ts +3 -1
  12. package/dist/config/flp/sandbox.js +17 -12
  13. package/dist/config/launch-config.d.ts +4 -3
  14. package/dist/config/launch-config.js +5 -16
  15. package/dist/config/manifest-update.d.ts +21 -1
  16. package/dist/config/manifest-update.js +7 -12
  17. package/dist/config/package-json.d.ts +3 -1
  18. package/dist/config/package-json.js +15 -9
  19. package/dist/config/ui5-yaml.d.ts +9 -4
  20. package/dist/config/ui5-yaml.js +14 -10
  21. package/dist/data/sap-libraries.d.ts +3 -1
  22. package/dist/data/sap-libraries.js +4 -3
  23. package/dist/data/ui5-version.js +1 -1
  24. package/dist/files/project-files.d.ts +4 -3
  25. package/dist/files/project-files.js +10 -16
  26. package/dist/files/webapp.d.ts +5 -2
  27. package/dist/files/webapp.js +30 -35
  28. package/dist/index.d.ts +2 -2
  29. package/dist/index.js +2 -2
  30. package/dist/migration-process/configuration.d.ts +3 -1
  31. package/dist/migration-process/configuration.js +3 -2
  32. package/dist/migration-process/legacy-helpers.d.ts +3 -1
  33. package/dist/migration-process/legacy-helpers.js +19 -32
  34. package/dist/migration-process/legacy.d.ts +3 -1
  35. package/dist/migration-process/legacy.js +66 -79
  36. package/dist/migration-process/migration-phases.d.ts +6 -1
  37. package/dist/migration-process/migration-phases.js +26 -14
  38. package/dist/migration-process/setup.d.ts +9 -6
  39. package/dist/migration-process/setup.js +19 -10
  40. package/dist/migration-process/validation.d.ts +5 -2
  41. package/dist/migration-process/validation.js +6 -4
  42. package/dist/migration-types.d.ts +5 -2
  43. package/dist/project/adaptation-project.js +1 -0
  44. package/dist/project/project-data.js +1 -1
  45. package/dist/project/project-detection.d.ts +3 -1
  46. package/dist/project/project-detection.js +10 -4
  47. package/dist/project/project-extension.d.ts +1 -1
  48. package/dist/project/project-extension.js +3 -1
  49. package/dist/project/project-info.d.ts +3 -1
  50. package/dist/project/project-info.js +6 -3
  51. package/dist/project/regular-project.js +17 -9
  52. package/dist/template/application.d.ts +2 -0
  53. package/dist/template/application.js +11 -9
  54. package/dist/template/assembly.js +4 -4
  55. package/dist/template/base.d.ts +2 -0
  56. package/dist/template/base.js +2 -30
  57. package/dist/template/details.d.ts +3 -1
  58. package/dist/template/details.js +7 -5
  59. package/dist/template/template-helpers.d.ts +3 -1
  60. package/dist/template/template-helpers.js +6 -5
  61. package/dist/template/theme.d.ts +3 -1
  62. package/dist/template/theme.js +3 -2
  63. package/dist/types/mem-fs-types.d.ts +1 -1
  64. package/dist/utils/Project.d.ts +17 -14
  65. package/dist/utils/Project.js +94 -72
  66. package/dist/utils/checkForMigration.d.ts +4 -3
  67. package/dist/utils/checkForMigration.js +11 -8
  68. package/dist/utils/file-access.d.ts +36 -34
  69. package/dist/utils/file-access.js +61 -93
  70. package/dist/utils/file-discovery.d.ts +5 -2
  71. package/dist/utils/file-discovery.js +6 -4
  72. package/dist/utils/file-system-utils.d.ts +15 -5
  73. package/dist/utils/file-system-utils.js +34 -11
  74. package/dist/utils/fs-adapter.d.ts +5 -63
  75. package/dist/utils/fs-adapter.js +6 -111
  76. package/dist/utils/manifest-and-version-utils.d.ts +3 -1
  77. package/dist/utils/manifest-and-version-utils.js +6 -3
  78. package/dist/utils/migration-utils.d.ts +3 -1
  79. package/dist/utils/migration-utils.js +5 -4
  80. package/dist/utils/project-access-adapters.d.ts +3 -1
  81. package/dist/utils/project-access-adapters.js +3 -2
  82. package/dist/utils/project-discovery.d.ts +6 -2
  83. package/dist/utils/project-discovery.js +44 -18
  84. package/dist/utils/project-readers/adaptation-project-utils.d.ts +5 -2
  85. package/dist/utils/project-readers/adaptation-project-utils.js +21 -16
  86. package/dist/utils/project-readers/backend-utils.d.ts +3 -1
  87. package/dist/utils/project-readers/backend-utils.js +4 -3
  88. package/dist/utils/project-readers/extension-project-utils.d.ts +5 -2
  89. package/dist/utils/project-readers/extension-project-utils.js +15 -11
  90. package/dist/utils/project-readers/manifest-reader.d.ts +3 -1
  91. package/dist/utils/project-readers/manifest-reader.js +3 -2
  92. package/dist/utils/project-readers/manifest-utils.d.ts +5 -2
  93. package/dist/utils/project-readers/manifest-utils.js +9 -6
  94. package/dist/utils/project-readers/package-reader.d.ts +3 -1
  95. package/dist/utils/project-readers/package-reader.js +3 -2
  96. package/dist/utils/project-readers/reuse-lib-utils.d.ts +7 -3
  97. package/dist/utils/project-readers/reuse-lib-utils.js +13 -7
  98. package/dist/utils/project-readers/webapp-path-resolver.d.ts +3 -1
  99. package/dist/utils/project-readers/webapp-path-resolver.js +24 -19
  100. package/dist/utils/service.d.ts +3 -2
  101. package/dist/utils/service.js +10 -6
  102. package/dist/utils/template/template-post-processors.d.ts +3 -1
  103. package/dist/utils/template/template-post-processors.js +4 -3
  104. package/dist/utils/template/template-renderer.d.ts +3 -1
  105. package/dist/utils/template/template-renderer.js +3 -2
  106. package/dist/utils/template-generators/file-handlers.d.ts +5 -2
  107. package/dist/utils/template-generators/file-handlers.js +59 -32
  108. package/package.json +9 -9
@@ -28,21 +28,29 @@ export declare class ProjectMigrator {
28
28
  * @param options - Migration options object
29
29
  * @returns Promise with fs editor, migration result, and messages
30
30
  */
31
- static migrate(// NOSONAR - Overload for backward compatibility; use options object signature instead
32
- projectRoot: string, baseUri?: string, ui5SnapshotUrl?: string, importProjectInfo?: ImportProjectInfo, vscode?: any, internalToggle?: boolean, fs?: Editor, strict?: boolean): Promise<{
33
- fs: Editor;
34
- result: boolean;
35
- messages: Message[];
36
- }>;
37
31
  static migrate(options: MigrateOptions): Promise<{
38
32
  fs: Editor;
39
33
  result: boolean;
40
34
  messages: Message[];
41
35
  }>;
36
+ /**
37
+ * Run the migration for the resolved project info, dispatching by project type.
38
+ *
39
+ * @param editor
40
+ * @param projectInfo
41
+ * @param messages
42
+ * @param opts
43
+ * @param opts.vscode
44
+ * @param opts.ui5SnapshotUrl
45
+ * @param opts.internalToggle
46
+ * @param opts.strict
47
+ */
48
+ private static runMigrationForProjectInfo;
42
49
  /**
43
50
  * Copy files from templates into the project
44
51
  * Orchestrates the migration process through distinct phases
45
52
  *
53
+ * @param fs - mem-fs editor
46
54
  * @param projectInfo - Project information
47
55
  * @param vscode - VS Code context
48
56
  * @param ui5SnapshotUrl - UI5 snapshot URL
@@ -54,6 +62,7 @@ export declare class ProjectMigrator {
54
62
  /**
55
63
  * Copy files from templates into the project
56
64
  *
65
+ * @param fs - mem-fs editor
57
66
  * @param projectInfo
58
67
  * @param snapshotUrl
59
68
  */
@@ -61,6 +70,7 @@ export declare class ProjectMigrator {
61
70
  /**
62
71
  * Copy files from templates into the project
63
72
  *
73
+ * @param fs
64
74
  * @param projectInfo
65
75
  */
66
76
  private static copyLibraryFiles;
@@ -69,6 +79,7 @@ export declare class ProjectMigrator {
69
79
  * Generates tsconfig.json and TypeScript devDependencies via {@link enableTypescript},
70
80
  * then patches ui5-local.yaml and ui5-mock.yaml with the transpile middleware and task.
71
81
  *
82
+ * @param fs - The mem-fs editor.
72
83
  * @param rootPath - The project root path.
73
84
  * @returns Messages to surface to the user after setup.
74
85
  */
@@ -79,6 +90,7 @@ export declare class ProjectMigrator {
79
90
  * Falls back to scanning the webapp folder for .ts files directly, since after a download from ABAP
80
91
  * the project will not yet have a tsconfig.json — it is added as part of the migration itself.
81
92
  *
93
+ * @param fs - The mem-fs editor.
82
94
  * @param rootPath - The project root path.
83
95
  * @param webappPath - The relative path to the webapp folder.
84
96
  * @returns True if the project contains TypeScript source files.
@@ -86,9 +98,9 @@ export declare class ProjectMigrator {
86
98
  private static detectTypeScriptApp;
87
99
  /**
88
100
  * Scans the webapp folder for a TypeScript source file (.ts, excluding .d.ts).
89
- * During migration the downloaded files live in the uncommitted mem-fs editor, so when an
90
- * editor with a store is in context the mem-fs store is scanned; otherwise the real disk is read.
101
+ * The migration tree is loaded into the mem-fs store, so the staged files are scanned.
91
102
  *
103
+ * @param fs - The mem-fs editor.
92
104
  * @param webappFullPath - Absolute path to the webapp folder.
93
105
  * @returns True if a non-declaration .ts file is present under the webapp folder.
94
106
  */
@@ -1,12 +1,10 @@
1
1
  import { FioriElementsVersion, FileName } from './project-spec-types.js';
2
- import { determineMessage, readFile, updateFile } from './utils/index.js';
3
- import { createMemFsEditor, getCurrentEditor, runWithEditor, exists, loadProjectIntoEditor } from './utils/fs-adapter.js';
4
- import { commitFileSystemChanges } from './files/file-system.js';
2
+ import { determineMessage, readFile, updateFile, fileExists } from './utils/index.js';
3
+ import { createMemFsEditor, loadProjectIntoEditor } from './utils/fs-adapter.js';
5
4
  import { ui5VersionRequestInfo } from '@sap-ux/ui5-info';
6
5
  import { getAppProgrammingLanguage } from '@sap-ux/project-access';
7
6
  import { enableTypescript, ui5TSSupport } from '@sap-ux/ui5-application-writer';
8
7
  import { UI5Config } from '@sap-ux/ui5-config';
9
- import { readdir } from 'node:fs/promises';
10
8
  import { join } from 'node:path';
11
9
  // Migration-specific functions
12
10
  import { determineSourceTemplateType, checkForErrors, createMigrationErrorMessage, prepareConfigurationData, setupProjectStructure, generateAppSettings, applyAllTemplates, postProcessMigration } from './migration-process/index.js';
@@ -22,93 +20,57 @@ import { MigrationTypes } from './utils/constants.js';
22
20
  import { hasStore } from './types/mem-fs-types.js';
23
21
  import { i18nText, initI18n } from './i18n.js';
24
22
  export class ProjectMigrator {
25
- // Implementation
26
- static async migrate(
27
- // NOSONAR - Implementation must support both overload signatures
28
- projectRootOrOptions, baseUri, ui5SnapshotUrl, importProjectInfo, vscode, internalToggle, fs, strict) {
29
- // Convert old signature to options object if called with positional parameters
30
- const options = typeof projectRootOrOptions === 'string'
31
- ? {
32
- projectRoot: projectRootOrOptions,
33
- baseUri: baseUri ?? '',
34
- ui5SnapshotUrl: ui5SnapshotUrl ?? '',
35
- importProjectInfo,
36
- vscode,
37
- internalToggle,
38
- fs,
39
- strict
40
- }
41
- : projectRootOrOptions;
23
+ /**
24
+ * migrate project
25
+ *
26
+ * @param options - Migration options object
27
+ * @returns Promise with fs editor, migration result, and messages
28
+ */
29
+ static async migrate(options) {
42
30
  const { projectRoot, baseUri: baseUriOpt, ui5SnapshotUrl: ui5SnapshotUrlOpt, importProjectInfo: importProjectInfoOpt, vscode: vscodeOpt, internalToggle: internalToggleOpt = false, fs: fsOpt, strict: strictOpt = false } = options;
43
31
  // Create a new isolated editor instance for this migration if not provided
44
32
  // This enables concurrent migrations without shared state corruption
45
33
  const ownsEditor = !fsOpt;
46
34
  const editor = fsOpt ?? createMemFsEditor();
47
35
  await initI18n();
48
- // Run migration within editor context so all functions have access to it
49
- const migrationResult = await runWithEditor(editor, async () => {
50
- let messages = [];
51
- let result = false;
52
- try {
53
- // Load the project tree into mem-fs so existence checks see untracked files
54
- // on disk and migration writes never silently overwrite them.
55
- await loadProjectIntoEditor(editor, projectRoot);
56
- // Load or fetch project information
57
- const { projectInfo, messages: projectInfoMessages } = await loadOrFetchProjectInfo(projectRoot, importProjectInfoOpt);
58
- messages = messages.concat(projectInfoMessages);
59
- projectInfo.baseUri = baseUriOpt;
60
- // Resolve UI5 versions for migration
61
- const ui5Versions = await resolveUI5VersionsForMigration(projectInfo, ui5SnapshotUrlOpt);
62
- projectInfo.localUI5Version = ui5Versions?.[0]?.version;
63
- if (/^(snapshot(-untested)?)?$/i.test(projectInfo.ui5Version?.trim() ?? '')) {
64
- projectInfo.ui5Version = projectInfo.localUI5Version ?? '';
65
- }
66
- // Validate project is suitable for migration (not a Fiori app in CAP project)
67
- await validateProjectForMigration(projectRoot);
68
- if (projectInfo &&
69
- (projectInfo.isSAPApp ||
70
- projectInfo.FEVersion === FioriElementsVersion.v2 ||
71
- projectInfo.FEVersion === FioriElementsVersion.v4 ||
72
- projectInfo.type === MigrationTypes.projectExtension) &&
73
- checkForErrors(messages)) {
74
- // V2 is supported (checkForErrors returns true if no ERROR-level messages)
75
- const { messages: copyMessages, result: isSuccess } = await this.copyCommonFiles(projectInfo, vscodeOpt, ui5SnapshotUrlOpt, internalToggleOpt, strictOpt);
76
- messages = messages.concat(copyMessages);
77
- result = isSuccess;
78
- }
79
- else if (projectInfo.type === MigrationTypes.library) {
80
- const { fs: updatedFs, messages: copyMessages, result: isSuccess } = await this.copyLibraryFiles(projectInfo);
81
- messages = messages.concat(copyMessages);
82
- result = isSuccess;
83
- // Update editor to the one returned from copyLibraryFiles
84
- return { fs: updatedFs, result, messages };
85
- }
86
- else if (projectInfo.uiAdaptation) {
87
- const { fs: updatedFs, messages: copyMessages, result: isSuccess } = await this.copyAdaptationFiles(projectInfo, ui5SnapshotUrlOpt);
88
- messages = messages.concat(copyMessages);
89
- result = isSuccess;
90
- // Update editor to the one returned from copyAdaptationFiles
91
- return { fs: updatedFs, result, messages };
92
- }
93
- else {
94
- messages.push({ type: 'ERROR', description: i18nText('ERROR_FAILED_TO_GET_PROJECT_INFO') });
95
- }
96
- }
97
- catch (e) {
98
- const error = e instanceof Error ? e : new Error(String(e));
99
- const useMessage = typeof e === 'object' && e !== null && 'useMessage' in e ? Boolean(e.useMessage) : false;
100
- messages.push({
101
- type: 'ERROR',
102
- description: `Error during migration: ${determineMessage(error, undefined, useMessage)}`
103
- });
104
- }
105
- // Return fs editor for caller to commit
106
- // This matches the pattern used by other open-ux-tools writers
107
- return { fs: editor, result, messages };
108
- });
36
+ let messages = [];
37
+ let result = false;
38
+ try {
39
+ // Load the project tree into mem-fs so existence checks see untracked files
40
+ // on disk and migration writes never silently overwrite them.
41
+ await loadProjectIntoEditor(editor, projectRoot);
42
+ // Load or fetch project information
43
+ const { projectInfo, messages: projectInfoMessages } = await loadOrFetchProjectInfo(projectRoot, importProjectInfoOpt, editor);
44
+ messages = messages.concat(projectInfoMessages);
45
+ projectInfo.baseUri = baseUriOpt;
46
+ // Resolve UI5 versions for migration
47
+ const ui5Versions = await resolveUI5VersionsForMigration(projectInfo, ui5SnapshotUrlOpt);
48
+ projectInfo.localUI5Version = ui5Versions?.[0]?.version;
49
+ // ui5Version left as-is from project/neo-app data
50
+ // Validate project is suitable for migration (not a Fiori app in CAP project)
51
+ await validateProjectForMigration(editor, projectRoot);
52
+ const dispatch = await this.runMigrationForProjectInfo(editor, projectInfo, messages, {
53
+ vscode: vscodeOpt,
54
+ ui5SnapshotUrl: ui5SnapshotUrlOpt,
55
+ internalToggle: internalToggleOpt,
56
+ strict: strictOpt
57
+ });
58
+ messages = dispatch.messages;
59
+ result = dispatch.result;
60
+ }
61
+ catch (e) {
62
+ const error = e instanceof Error ? e : new Error(String(e));
63
+ const useMessage = typeof e === 'object' && e !== null && 'useMessage' in e ? Boolean(e.useMessage) : false;
64
+ messages.push({
65
+ type: 'ERROR',
66
+ description: `Error during migration: ${determineMessage(error, undefined, useMessage)}`
67
+ });
68
+ }
69
+ const migrationResult = { fs: editor, result, messages };
70
+ // Commit only when we own the editor; otherwise the caller commits (matches other writers).
109
71
  if (ownsEditor && migrationResult.result) {
110
72
  try {
111
- await commitFileSystemChanges(migrationResult.fs);
73
+ await new Promise((resolve, reject) => migrationResult.fs.commit((error) => (error ? reject(error) : resolve())));
112
74
  }
113
75
  catch (error) {
114
76
  migrationResult.result = false;
@@ -120,10 +82,45 @@ export class ProjectMigrator {
120
82
  }
121
83
  return migrationResult;
122
84
  }
85
+ /**
86
+ * Run the migration for the resolved project info, dispatching by project type.
87
+ *
88
+ * @param editor
89
+ * @param projectInfo
90
+ * @param messages
91
+ * @param opts
92
+ * @param opts.vscode
93
+ * @param opts.ui5SnapshotUrl
94
+ * @param opts.internalToggle
95
+ * @param opts.strict
96
+ */
97
+ static async runMigrationForProjectInfo(editor, projectInfo, messages, opts) {
98
+ const isSupportedApp = projectInfo.isSAPApp ||
99
+ projectInfo.FEVersion === FioriElementsVersion.v2 ||
100
+ projectInfo.FEVersion === FioriElementsVersion.v4 ||
101
+ projectInfo.type === MigrationTypes.projectExtension;
102
+ if (isSupportedApp && checkForErrors(messages)) {
103
+ const { messages: copyMessages, result } = await this.copyCommonFiles(editor, projectInfo, opts.vscode, opts.ui5SnapshotUrl, opts.internalToggle, opts.strict);
104
+ return { result, messages: messages.concat(copyMessages) };
105
+ }
106
+ if (projectInfo.type === MigrationTypes.library) {
107
+ const { messages: copyMessages, result } = await this.copyLibraryFiles(editor, projectInfo);
108
+ return { result, messages: messages.concat(copyMessages) };
109
+ }
110
+ if (projectInfo.uiAdaptation) {
111
+ const { messages: copyMessages, result } = await this.copyAdaptationFiles(editor, projectInfo, opts.ui5SnapshotUrl);
112
+ return { result, messages: messages.concat(copyMessages) };
113
+ }
114
+ return {
115
+ result: false,
116
+ messages: messages.concat({ type: 'ERROR', description: i18nText('ERROR_FAILED_TO_GET_PROJECT_INFO') })
117
+ };
118
+ }
123
119
  /**
124
120
  * Copy files from templates into the project
125
121
  * Orchestrates the migration process through distinct phases
126
122
  *
123
+ * @param fs - mem-fs editor
127
124
  * @param projectInfo - Project information
128
125
  * @param vscode - VS Code context
129
126
  * @param ui5SnapshotUrl - UI5 snapshot URL
@@ -131,19 +128,20 @@ export class ProjectMigrator {
131
128
  * @param strict - Enable strict validation mode (WARNINGs become ERRORs for critical issues)
132
129
  * @returns Migration result with status and messages
133
130
  */
134
- static async copyCommonFiles(projectInfo, vscode, ui5SnapshotUrl, internalToggle = false, strict = false) {
131
+ static async copyCommonFiles(fs, projectInfo, vscode, ui5SnapshotUrl, internalToggle = false, strict = false) {
135
132
  const messages = [];
136
133
  const { isSAPApp = false, rootPath, destination, neoappDestinations, firstNeoAppDestination } = projectInfo;
137
134
  try {
138
135
  // Detect TypeScript project
139
- const isTypeScriptApp = await ProjectMigrator.detectTypeScriptApp(rootPath, projectInfo.webappPath);
136
+ const isTypeScriptApp = await ProjectMigrator.detectTypeScriptApp(fs, rootPath, projectInfo.webappPath);
140
137
  // Prepare configuration data
141
- const configData = await prepareConfigurationData(projectInfo, projectInfo.ui5Version, isTypeScriptApp);
138
+ const configData = await prepareConfigurationData(fs, projectInfo, projectInfo.ui5Version, isTypeScriptApp);
142
139
  // Phase 1: Setup project structure
143
- const { webappPath, keepIndex } = await setupProjectStructure(projectInfo, rootPath);
140
+ const { webappPath, keepIndex } = await setupProjectStructure(fs, projectInfo, rootPath);
144
141
  projectInfo.webappPath = webappPath;
145
142
  // Phase 2: Assemble template data
146
143
  const templateContext = await assembleTemplateData({
144
+ fs,
147
145
  projectInfo,
148
146
  rootPath,
149
147
  projectData: configData.projectData,
@@ -193,6 +191,7 @@ export class ProjectMigrator {
193
191
  // Phase 3: Generate app settings
194
192
  const sourceTemplateType = determineSourceTemplateType(projectInfo, MigrationTypes);
195
193
  await generateAppSettings({
194
+ fs,
196
195
  rootPath,
197
196
  templateData: templateContext.templateData,
198
197
  destination,
@@ -207,6 +206,7 @@ export class ProjectMigrator {
207
206
  });
208
207
  // Phase 4: Apply all templates
209
208
  await applyAllTemplates({
209
+ fs,
210
210
  projectInfo,
211
211
  rootPath,
212
212
  templateData: templateContext.templateData,
@@ -220,6 +220,7 @@ export class ProjectMigrator {
220
220
  });
221
221
  // Phase 5: Post-processing
222
222
  await postProcessMigration({
223
+ fs,
223
224
  projectInfo,
224
225
  rootPath,
225
226
  manifestJSON: templateContext.manifestJSON,
@@ -232,7 +233,7 @@ export class ProjectMigrator {
232
233
  });
233
234
  // Phase 6: TypeScript setup (if TypeScript app detected)
234
235
  if (isTypeScriptApp) {
235
- const tsMessages = await ProjectMigrator.setupTypeScript(rootPath);
236
+ const tsMessages = await ProjectMigrator.setupTypeScript(fs, rootPath);
236
237
  messages.push(...tsMessages);
237
238
  }
238
239
  return { result: checkForErrors(messages), messages };
@@ -247,32 +248,35 @@ export class ProjectMigrator {
247
248
  /**
248
249
  * Copy files from templates into the project
249
250
  *
251
+ * @param fs - mem-fs editor
250
252
  * @param projectInfo
251
253
  * @param snapshotUrl
252
254
  */
253
- static async copyAdaptationFiles(projectInfo, snapshotUrl) {
254
- return copyAdaptationFiles(projectInfo, snapshotUrl, undefined);
255
+ static async copyAdaptationFiles(fs, projectInfo, snapshotUrl) {
256
+ return copyAdaptationFiles(fs, projectInfo, snapshotUrl);
255
257
  }
256
258
  /**
257
259
  * Copy files from templates into the project
258
260
  *
261
+ * @param fs
259
262
  * @param projectInfo
260
263
  */
261
- static async copyLibraryFiles(projectInfo) {
262
- return copyLibraryFiles(projectInfo);
264
+ static async copyLibraryFiles(fs, projectInfo) {
265
+ return copyLibraryFiles(fs, projectInfo);
263
266
  }
264
267
  /**
265
268
  * Configures a downloaded TypeScript app for local development.
266
269
  * Generates tsconfig.json and TypeScript devDependencies via {@link enableTypescript},
267
270
  * then patches ui5-local.yaml and ui5-mock.yaml with the transpile middleware and task.
268
271
  *
272
+ * @param fs - The mem-fs editor.
269
273
  * @param rootPath - The project root path.
270
274
  * @returns Messages to surface to the user after setup.
271
275
  */
272
- static async setupTypeScript(rootPath) {
276
+ static async setupTypeScript(fs, rootPath) {
273
277
  const messages = [];
274
278
  try {
275
- await enableTypescript(rootPath, getCurrentEditor());
279
+ await enableTypescript(rootPath, fs);
276
280
  }
277
281
  catch (err) {
278
282
  messages.push({
@@ -284,14 +288,12 @@ export class ProjectMigrator {
284
288
  // Check and update ui5*.yaml files sequentially - order doesn't matter but list is small
285
289
  for (const yamlName of [FileName.UI5LocalYaml, FileName.UI5MockYaml]) {
286
290
  const yamlPath = join(rootPath, yamlName);
287
- if (await exists(yamlPath)) {
288
- // NOSONAR - Only 2 files, acceptable sequential check
289
- // readFile and updateFile are sync mem-fs operations
290
- const yamlContent = readFile(yamlPath);
291
+ if (fileExists(fs, yamlPath)) {
292
+ const yamlContent = readFile(fs, yamlPath);
291
293
  const yamlConfig = await UI5Config.newInstance(yamlContent); // NOSONAR - Sequential YAML parsing required
292
294
  yamlConfig.addCustomMiddleware([ui5TSSupport.middleware]);
293
295
  yamlConfig.addCustomTasks([ui5TSSupport.task]);
294
- updateFile(yamlPath, yamlConfig.toString());
296
+ updateFile(fs, yamlPath, yamlConfig.toString());
295
297
  }
296
298
  }
297
299
  messages.push({
@@ -306,11 +308,12 @@ export class ProjectMigrator {
306
308
  * Falls back to scanning the webapp folder for .ts files directly, since after a download from ABAP
307
309
  * the project will not yet have a tsconfig.json — it is added as part of the migration itself.
308
310
  *
311
+ * @param fs - The mem-fs editor.
309
312
  * @param rootPath - The project root path.
310
313
  * @param webappPath - The relative path to the webapp folder.
311
314
  * @returns True if the project contains TypeScript source files.
312
315
  */
313
- static async detectTypeScriptApp(rootPath, webappPath) {
316
+ static async detectTypeScriptApp(fs, rootPath, webappPath) {
314
317
  const webappFullPath = join(rootPath, webappPath);
315
318
  // Try getAppProgrammingLanguage first (needs tsconfig.json and package.json)
316
319
  let isTypeScript = false;
@@ -324,39 +327,34 @@ export class ProjectMigrator {
324
327
  if (isTypeScript) {
325
328
  return true;
326
329
  }
327
- return ProjectMigrator.webappHasTypeScriptFile(webappFullPath);
330
+ return ProjectMigrator.webappHasTypeScriptFile(fs, webappFullPath);
328
331
  }
329
332
  /**
330
333
  * Scans the webapp folder for a TypeScript source file (.ts, excluding .d.ts).
331
- * During migration the downloaded files live in the uncommitted mem-fs editor, so when an
332
- * editor with a store is in context the mem-fs store is scanned; otherwise the real disk is read.
334
+ * The migration tree is loaded into the mem-fs store, so the staged files are scanned.
333
335
  *
336
+ * @param fs - The mem-fs editor.
334
337
  * @param webappFullPath - Absolute path to the webapp folder.
335
338
  * @returns True if a non-declaration .ts file is present under the webapp folder.
336
339
  */
337
- static async webappHasTypeScriptFile(webappFullPath) {
340
+ static webappHasTypeScriptFile(fs, webappFullPath) {
338
341
  const isTypeScriptFile = (filePath) => filePath.endsWith('.ts') && !filePath.endsWith('.d.ts');
339
- const editor = getCurrentEditor();
340
- if (editor && hasStore(editor)) {
341
- // mem-fs mode: scan staged files under the webapp folder (paths use forward slashes)
342
- const normalizedPrefix = webappFullPath.replaceAll('\\', '/').replace(/\/?$/, '/');
343
- let found = false;
344
- editor.store.each((file) => {
345
- if (file.state === 'deleted') {
346
- return;
347
- }
348
- const normalizedFilePath = file.path.replaceAll('\\', '/');
349
- if (normalizedFilePath.startsWith(normalizedPrefix) && isTypeScriptFile(normalizedFilePath)) {
350
- found = true;
351
- }
352
- });
353
- return found;
354
- }
355
- // No editor context: scan the real filesystem
356
- if (!(await exists(webappFullPath))) {
342
+ if (!hasStore(fs)) {
357
343
  return false;
358
344
  }
359
- return (await readdir(webappFullPath, { recursive: true })).some((f) => typeof f === 'string' && isTypeScriptFile(f));
345
+ // Scan staged files under the webapp folder (paths use forward slashes)
346
+ const normalizedPrefix = webappFullPath.replaceAll('\\', '/').replace(/\/?$/, '/');
347
+ let found = false;
348
+ fs.store.each((file) => {
349
+ if (file.state === 'deleted') {
350
+ return;
351
+ }
352
+ const normalizedFilePath = file.path.replaceAll('\\', '/');
353
+ if (normalizedFilePath.startsWith(normalizedPrefix) && isTypeScriptFile(normalizedFilePath)) {
354
+ found = true;
355
+ }
356
+ });
357
+ return found;
360
358
  }
361
359
  }
362
360
  //# sourceMappingURL=ProjectMigrator.js.map
@@ -37,14 +37,14 @@ function addMockserverMiddleware(orderedYaml, templateData) {
37
37
  }
38
38
  }
39
39
  };
40
- // Find index of fiori-tools-proxy and insert before it
40
+ // Find index of fiori-tools-proxy and insert after it
41
41
  const proxyIndex = orderedYaml.server.customMiddleware.findIndex((mw) => mw.name === 'fiori-tools-proxy');
42
42
  if (proxyIndex >= 0) {
43
- orderedYaml.server.customMiddleware.splice(proxyIndex, 0, mockserverMiddleware);
43
+ orderedYaml.server.customMiddleware.splice(proxyIndex + 1, 0, mockserverMiddleware);
44
44
  }
45
45
  else {
46
- // If proxy not found, add at beginning
47
- orderedYaml.server.customMiddleware.unshift(mockserverMiddleware);
46
+ // If proxy not found, add at end
47
+ orderedYaml.server.customMiddleware.push(mockserverMiddleware);
48
48
  }
49
49
  }
50
50
  /**
@@ -202,22 +202,68 @@ export async function generateUI5LocalYamlContent(options) {
202
202
  */
203
203
  export async function generateUI5MockYamlContent(templateData, webappPath, setUI5Version) {
204
204
  const config = await createBaseConfig(templateData);
205
- // Add fiori-tools-proxy middleware with UI5 version if provided
206
- const proxyConfig = {
207
- ignoreCertErrors: false,
208
- backend: [],
209
- ui5: {
210
- path: ['/resources', '/test-resources'],
211
- url: templateData.ui5Yaml?.ui5Url || ''
212
- }
213
- };
214
- // Add UI5 version to proxy if provided and setUI5Version is true
215
- if (setUI5Version && templateData.ui5Yaml?.ui5Version) {
216
- proxyConfig.ui5.version = templateData.ui5Yaml.ui5Version;
217
- }
205
+ // Add fiori-tools-proxy middleware with the main backend (ui5-mock.yaml uses only the
206
+ // primary backend, not the per-service neo-app destinations that ui5.yaml carries).
207
+ const mainBackend = buildMainBackend(templateData);
208
+ const backends = mainBackend ? [mainBackend] : [];
209
+ const proxyConfig = buildProxyConfig(backends, templateData, setUI5Version);
218
210
  config.addFioriToolsProxyMiddleware(proxyConfig);
219
211
  // Add fiori-tools-appreload middleware
220
212
  config.addFioriToolsAppReloadMiddleware();
221
- return postProcessYaml(config.toString(), webappPath);
213
+ // Add fiori-tools-preview middleware if configured
214
+ const previewMiddleware = buildPreviewMiddleware(templateData);
215
+ if (previewMiddleware) {
216
+ config.addCustomMiddleware(previewMiddleware);
217
+ }
218
+ const yamlContent = config.toString();
219
+ const yamlJson = parse(yamlContent);
220
+ const orderedYaml = { specVersion: '4.0' };
221
+ Object.keys(yamlJson).forEach((key) => {
222
+ orderedYaml[key] = yamlJson[key];
223
+ });
224
+ // Add sap-fe-mockserver middleware (the core of ui5-mock.yaml) when a service is configured.
225
+ if (templateData.ui5Yaml?.servicePath) {
226
+ addMockServerServicesMiddleware(orderedYaml, templateData);
227
+ }
228
+ // Apply webappPath if provided
229
+ if (webappPath) {
230
+ setWebappPath(orderedYaml, webappPath);
231
+ setAppreloadPath(orderedYaml, webappPath);
232
+ }
233
+ return stringify(orderedYaml);
234
+ }
235
+ /**
236
+ * Add a sap-fe-mockserver middleware (mountPath/services[] shape used by ui5-mock.yaml)
237
+ * before the fiori-tools-proxy middleware.
238
+ *
239
+ * @param orderedYaml - Parsed YAML object to mutate
240
+ * @param templateData - Template data with the service/mock configuration
241
+ */
242
+ function addMockServerServicesMiddleware(orderedYaml, templateData) {
243
+ orderedYaml.server ??= { customMiddleware: [] };
244
+ orderedYaml.server.customMiddleware ??= [];
245
+ const mockserverMiddleware = {
246
+ name: 'sap-fe-mockserver',
247
+ beforeMiddleware: 'fiori-tools-proxy',
248
+ configuration: {
249
+ mountPath: '/',
250
+ services: [
251
+ {
252
+ urlPath: (templateData.ui5Yaml?.servicePath ?? '').replace(/\/$/, ''),
253
+ metadataPath: templateData.ui5Yaml?.metadataXmlPath || '',
254
+ mockdataPath: templateData.ui5Yaml?.mockdataRootPath || '',
255
+ generateMockData: templateData.ui5Yaml?.generateMockData ?? true,
256
+ annotations: []
257
+ }
258
+ ]
259
+ }
260
+ };
261
+ const proxyIndex = orderedYaml.server.customMiddleware.findIndex((mw) => mw.name === 'fiori-tools-proxy');
262
+ if (proxyIndex >= 0) {
263
+ orderedYaml.server.customMiddleware.splice(proxyIndex + 1, 0, mockserverMiddleware);
264
+ }
265
+ else {
266
+ orderedYaml.server.customMiddleware.push(mockserverMiddleware);
267
+ }
222
268
  }
223
269
  //# sourceMappingURL=ui5-config-adapter.js.map
@@ -7,12 +7,12 @@
7
7
  * @returns Backend configuration or undefined if not configured
8
8
  */
9
9
  export function buildMainBackend(templateData) {
10
- if (!templateData.ui5Yaml?.proxyPath || !templateData.ui5Yaml?.proxyHost) {
10
+ if (!templateData.ui5Yaml?.proxyPath || (!templateData.ui5Yaml?.proxyHost && !templateData.ui5Yaml?.destination)) {
11
11
  return undefined;
12
12
  }
13
13
  const backend = {
14
14
  path: templateData.ui5Yaml.proxyPath,
15
- url: templateData.ui5Yaml.proxyHost
15
+ url: templateData.ui5Yaml.proxyHost ?? ''
16
16
  };
17
17
  // Add optional properties
18
18
  if (templateData.ui5Yaml.client) {
@@ -49,15 +49,8 @@ export function buildProxyConfig(backends, templateData, setUI5Version) {
49
49
  url: templateData.ui5Yaml?.ui5Url || ''
50
50
  }
51
51
  };
52
- // Add UI5 version to proxy config when setUI5Version is true
53
- // This matches the behavior in tools-suite master for ui5-mock.yaml
54
- // Skip if version is a placeholder/fallback value like "snapshot-version"
55
52
  if (setUI5Version && templateData.ui5Yaml?.ui5Version) {
56
- const version = templateData.ui5Yaml.ui5Version;
57
- // Only add real versions, not placeholder values
58
- if (version && !version.includes('snapshot-version')) {
59
- proxyConfig.ui5.version = version;
60
- }
53
+ proxyConfig.ui5.version = templateData.ui5Yaml.ui5Version;
61
54
  }
62
55
  return proxyConfig;
63
56
  }
@@ -1,10 +1,12 @@
1
+ import type { Editor } from 'mem-fs-editor';
1
2
  import type { ImportProjectInfo } from '../types.js';
2
3
  /**
3
4
  * Updates the .extconfig.json file for extension projects
4
5
  * Handles WebIDE Personal Edition migration and destination updates
5
6
  *
7
+ * @param fs - mem-fs editor
6
8
  * @param rootPath - Root path of the project
7
9
  * @param projectInfo - Project information containing extension settings
8
10
  */
9
- export declare function updateExtConfigJson(rootPath: string, projectInfo: ImportProjectInfo): Promise<void>;
11
+ export declare function updateExtConfigJson(fs: Editor, rootPath: string, projectInfo: ImportProjectInfo): void;
10
12
  //# sourceMappingURL=extension.d.ts.map
@@ -5,10 +5,11 @@ import { FileName } from '../project-spec-types.js';
5
5
  * Updates the .extconfig.json file for extension projects
6
6
  * Handles WebIDE Personal Edition migration and destination updates
7
7
  *
8
+ * @param fs - mem-fs editor
8
9
  * @param rootPath - Root path of the project
9
10
  * @param projectInfo - Project information containing extension settings
10
11
  */
11
- export async function updateExtConfigJson(rootPath, projectInfo) {
12
+ export function updateExtConfigJson(fs, rootPath, projectInfo) {
12
13
  let extensionProjectSettingsJSON;
13
14
  let deleteProjectJson = false;
14
15
  try {
@@ -52,10 +53,10 @@ export async function updateExtConfigJson(rootPath, projectInfo) {
52
53
  }
53
54
  }
54
55
  // write .extconfig.json for extension project
55
- updateJSON(join(rootPath, FileName.ExtConfigJson), extensionProjectSettingsJSON);
56
+ updateJSON(fs, join(rootPath, FileName.ExtConfigJson), extensionProjectSettingsJSON);
56
57
  const projectJsonPath = join(rootPath, '.project.json');
57
- if (deleteProjectJson && (await fileExists(projectJsonPath))) {
58
- deleteFile(projectJsonPath);
58
+ if (deleteProjectJson && fileExists(fs, projectJsonPath)) {
59
+ deleteFile(fs, projectJsonPath);
59
60
  }
60
61
  }
61
62
  //# sourceMappingURL=extension.js.map