@sap-ux/fiori-migration-writer 0.0.5 → 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 +4 -4
@@ -1,134 +1,102 @@
1
1
  /**
2
- * File access utilities using mem-fs-editor
2
+ * File access utilities backed by a mem-fs editor.
3
3
  *
4
- * Uses editor from AsyncLocalStorage context set by runWithEditor().
5
- * Functions can also accept an explicit Editor parameter for backward compatibility.
6
- * If no editor is in context or provided, creates a temporary one (for tests/direct usage).
4
+ * Every function takes the editor explicitly as its first argument, matching the
5
+ * convention used across the other open-ux-tools writer packages. mem-fs reads from
6
+ * the real filesystem on a cache miss, so these work for both staged and on-disk files.
7
7
  */
8
8
  // @ts-expect-error - no type definitions available
9
9
  import parseJson from 'json-parse-even-better-errors';
10
- import { readFile as fsReadFile, access } from 'node:fs/promises';
11
- import { editorHasPath, getCurrentEditor, exists as fsAdapterExists, getEditor } from './fs-adapter.js';
10
+ import { existsSync } from 'node:fs';
11
+ import { editorHasPath } from './fs-adapter.js';
12
12
  /**
13
- * Read a text file
13
+ * Read a text file.
14
14
  *
15
- * @param pathOrFs - Path to file, or Editor instance
16
- * @param path - Path to file (if first param is Editor)
15
+ * @param fs - mem-fs editor
16
+ * @param path - Path to file
17
17
  * @returns File contents as string
18
18
  */
19
- export function readFile(pathOrFs, path) {
20
- const fs = getEditor(pathOrFs);
21
- const filePath = typeof pathOrFs === 'string' ? pathOrFs : path;
22
- return fs.read(filePath);
19
+ export function readFile(fs, path) {
20
+ return fs.read(path);
23
21
  }
24
22
  /**
25
- * Read a JSON file
23
+ * Read and parse a JSON file, preserving indentation metadata (Symbol(indent))
24
+ * so that a later updateJSON can keep the original formatting.
26
25
  *
27
- * @param pathOrFs - Path to file, or Editor instance
28
- * @param path - Path to file (if first param is Editor)
26
+ * @param fs - mem-fs editor
27
+ * @param path - Path to file
29
28
  * @returns Parsed JSON object
30
29
  */
31
- export async function readJSON(pathOrFs, path) {
32
- const filePath = typeof pathOrFs === 'string' ? pathOrFs : path;
33
- // When called with just a path string and no editor context, read from filesystem directly
34
- // This keeps behavior consistent with fileExists which also falls back to real filesystem
35
- let content;
36
- if (typeof pathOrFs === 'string' && !getCurrentEditor()) {
37
- content = await fsReadFile(filePath, 'utf-8');
38
- }
39
- else {
40
- const fs = getEditor(pathOrFs);
41
- content = fs.read(filePath);
42
- }
43
- return JSON.parse(content);
44
- }
45
- /**
46
- * Check if a file exists
47
- *
48
- * @param pathOrFs - Path to file, or Editor instance
49
- * @param path - Path to file (if first param is Editor)
50
- * @returns true if file exists, false otherwise
51
- */
52
- export async function fileExists(pathOrFs, path) {
53
- // String path: delegate to the single shared existence check (mem-fs staged OR real fs).
54
- if (typeof pathOrFs === 'string') {
55
- return fsAdapterExists(pathOrFs);
56
- }
57
- // Explicit editor argument: check that editor's staged files, then fall back to real fs.
58
- // The real-fs fallback prevents overwriting files not yet loaded into the given editor
59
- // (e.g., the mockserver.js overwrite bug).
60
- const filePath = path;
61
- if (editorHasPath(pathOrFs, filePath)) {
62
- return true;
63
- }
30
+ export function readJSON(fs, path) {
31
+ const content = fs.read(path);
32
+ const result = JSON.parse(content);
64
33
  try {
65
- await access(filePath);
66
- return true;
34
+ const withIndent = parseJson(content);
35
+ const indent = Symbol.for('indent');
36
+ if (withIndent[indent]) {
37
+ result[indent] = withIndent[indent];
38
+ }
67
39
  }
68
40
  catch {
69
- return false;
41
+ // JSON.parse already succeeded; indentation metadata is best-effort.
70
42
  }
43
+ return result;
71
44
  }
72
45
  /**
73
- * Write a text file
46
+ * Check if a file or directory exists (staged in mem-fs or present on the real filesystem).
74
47
  *
75
- * @param pathOrFs - Path to file, or Editor instance
76
- * @param contentOrPath - Content to write, or path (if first param is Editor)
77
- * @param content - Content to write (if first param is Editor)
48
+ * Unlike mem-fs's `editor.exists()`, this also reports directories as present — many migration
49
+ * checks test for folders (e.g. the webapp directory) — and reads through to the real filesystem
50
+ * so files not yet staged in mem-fs are still seen.
51
+ *
52
+ * @param fs - mem-fs editor
53
+ * @param path - Path to file or directory
54
+ * @returns true if the path exists
78
55
  */
79
- export function writeFile(pathOrFs, contentOrPath, content) {
80
- const fs = getEditor(pathOrFs);
81
- const filePath = typeof pathOrFs === 'string' ? pathOrFs : contentOrPath;
82
- const fileContent = typeof pathOrFs === 'string' ? contentOrPath : content;
83
- fs.write(filePath, fileContent);
56
+ export function fileExists(fs, path) {
57
+ return editorHasPath(fs, path) || existsSync(path);
84
58
  }
85
59
  /**
86
- * Update a text file
87
- * Alias for writeFile for backward compatibility
60
+ * Write a text file.
88
61
  *
89
- * @param pathOrFs - Path to file, or Editor instance
90
- * @param contentOrPath - Content to write, or path (if first param is Editor)
91
- * @param content - Content to write (if first param is Editor)
62
+ * @param fs - mem-fs editor
63
+ * @param path - Path to file
64
+ * @param content - Content to write
92
65
  */
93
- export function updateFile(pathOrFs, contentOrPath, content) {
94
- writeFile(pathOrFs, contentOrPath, content);
66
+ export function writeFile(fs, path, content) {
67
+ fs.write(path, content);
95
68
  }
96
69
  /**
97
- * Update a JSON file while preserving indentation
70
+ * Update a text file. Alias of writeFile; kept as a distinct name for call-site clarity
71
+ * (updating an existing file vs. creating a new one).
72
+ */
73
+ export const updateFile = writeFile;
74
+ /**
75
+ * Update a JSON file while preserving the indentation of the existing file.
76
+ * New files are written with 4-space indentation and a trailing newline.
98
77
  *
99
- * @param pathOrFs - Path to file, or Editor instance
100
- * @param contentOrPath - Object to write, or path (if first param is Editor)
101
- * @param _content - Object to write (if first param is Editor)
78
+ * @param fs - mem-fs editor
79
+ * @param path - Path to file
80
+ * @param content - Object to serialize
102
81
  */
103
- export function updateJSON(pathOrFs, contentOrPath, _content) {
104
- const fs = getEditor(pathOrFs);
105
- const filePath = typeof pathOrFs === 'string' ? pathOrFs : contentOrPath;
106
- const fileContent = typeof pathOrFs === 'string' ? contentOrPath : _content;
82
+ export function updateJSON(fs, path, content) {
107
83
  try {
108
- // Read old contents and indentation of the JSON file
109
- const oldContentText = fs.read(filePath);
110
- const oldContentJson = parseJson(oldContentText);
84
+ const oldContentJson = parseJson(fs.read(path));
111
85
  const indent = Symbol.for('indent');
112
- // Prepare new JSON file content with previous indentation
113
- const result = JSON.stringify(fileContent, null, oldContentJson[indent]) + '\n';
114
- fs.write(filePath, result);
86
+ fs.write(path, JSON.stringify(content, null, oldContentJson[indent]) + '\n');
115
87
  }
116
88
  catch {
117
- // Expected: file may not exist yet or may contain invalid JSON (new file creation scenario).
118
- // Safe to write with default 4-space indentation and trailing newline - this is the standard format.
119
- const newContent = JSON.stringify(fileContent, null, 4) + '\n';
120
- fs.write(filePath, newContent);
89
+ // Expected: file may not exist yet or may contain invalid JSON (new file creation).
90
+ fs.write(path, JSON.stringify(content, null, 4) + '\n');
121
91
  }
122
92
  }
123
93
  /**
124
- * Delete a file
94
+ * Delete a file.
125
95
  *
126
- * @param pathOrFs - Path to file, or Editor instance
127
- * @param path - Path to file (if first param is Editor)
96
+ * @param fs - mem-fs editor
97
+ * @param path - Path to file
128
98
  */
129
- export function deleteFile(pathOrFs, path) {
130
- const fs = getEditor(pathOrFs);
131
- const filePath = typeof pathOrFs === 'string' ? pathOrFs : path;
132
- fs.delete(filePath);
99
+ export function deleteFile(fs, path) {
100
+ fs.delete(path);
133
101
  }
134
102
  //# sourceMappingURL=file-access.js.map
@@ -4,6 +4,7 @@
4
4
  * Replaces @sap/ux-project-access file discovery functions with native implementations
5
5
  * using fast-glob and Node.js fs APIs.
6
6
  */
7
+ import type { Editor } from 'mem-fs-editor';
7
8
  import type { ProjectFolder } from '../types.js';
8
9
  /**
9
10
  * Reuse library type enum
@@ -19,11 +20,12 @@ export type ReuseLibType = (typeof ReuseLibType)[keyof typeof ReuseLibType];
19
20
  *
20
21
  * Replaces: findAllProjectRoots from @sap/ux-project-access
21
22
  *
23
+ * @param fs - mem-fs editor
22
24
  * @param paths - Array of root paths to search
23
25
  * @param sapuxRequired - If true, only return projects with sapux in dependencies
24
26
  * @returns Array of project root paths
25
27
  */
26
- export declare function findAllProjectRoots(paths: string[], sapuxRequired?: boolean): Promise<string[]>;
28
+ export declare function findAllProjectRoots(fs: Editor, paths: string[], sapuxRequired?: boolean): Promise<string[]>;
27
29
  /**
28
30
  * Reuse library result structure
29
31
  * Matches @sap/ux-project-access getReuseLibs return type
@@ -44,10 +46,11 @@ export interface ReuseLibResult {
44
46
  * Searches for UI5 libraries and components by finding manifest.json files
45
47
  * with "sap.app.type": "library" or "component"
46
48
  *
49
+ * @param fs - mem-fs editor
47
50
  * @param workspaceFolders - Array of workspace folders to search
48
51
  * @returns Array of reuse library information
49
52
  */
50
- export declare function getReuseLibs(workspaceFolders: readonly ProjectFolder[]): Promise<ReuseLibResult[]>;
53
+ export declare function getReuseLibs(fs: Editor, workspaceFolders: readonly ProjectFolder[]): Promise<ReuseLibResult[]>;
51
54
  /**
52
55
  * Find all files matching a pattern in a directory
53
56
  *
@@ -22,11 +22,12 @@ export const ReuseLibType = {
22
22
  *
23
23
  * Replaces: findAllProjectRoots from @sap/ux-project-access
24
24
  *
25
+ * @param fs - mem-fs editor
25
26
  * @param paths - Array of root paths to search
26
27
  * @param sapuxRequired - If true, only return projects with sapux in dependencies
27
28
  * @returns Array of project root paths
28
29
  */
29
- export async function findAllProjectRoots(paths, sapuxRequired = false) {
30
+ export async function findAllProjectRoots(fs, paths, sapuxRequired = false) {
30
31
  const roots = [];
31
32
  const ignorePatterns = ['**/node_modules/**', '**/dist/**', '**/.git/**'];
32
33
  // Process all search paths in parallel for better performance
@@ -46,7 +47,7 @@ export async function findAllProjectRoots(paths, sapuxRequired = false) {
46
47
  const dir = normalize(dirname(pkgPath));
47
48
  if (sapuxRequired) {
48
49
  try {
49
- const pkg = await readJSON(pkgPath);
50
+ const pkg = await readJSON(fs, pkgPath);
50
51
  // Check for SAP UX / Fiori Tools markers:
51
52
  // 1. "sapux": true property (Fiori Tools marker)
52
53
  // 2. Dependencies starting with @sap-ux/ or @sap/ux- (Fiori Tools packages)
@@ -143,10 +144,11 @@ async function findLibraryProjectRoot(manifestPath, workspaceBoundary) {
143
144
  * Searches for UI5 libraries and components by finding manifest.json files
144
145
  * with "sap.app.type": "library" or "component"
145
146
  *
147
+ * @param fs - mem-fs editor
146
148
  * @param workspaceFolders - Array of workspace folders to search
147
149
  * @returns Array of reuse library information
148
150
  */
149
- export async function getReuseLibs(workspaceFolders) {
151
+ export async function getReuseLibs(fs, workspaceFolders) {
150
152
  // Process all workspace folders in parallel
151
153
  const folderResults = await Promise.all(workspaceFolders.map(async (folder) => {
152
154
  try {
@@ -163,7 +165,7 @@ export async function getReuseLibs(workspaceFolders) {
163
165
  // so paths match what callers build with path.join (fails on Windows otherwise).
164
166
  const manifestPath = normalize(rawManifestPath);
165
167
  try {
166
- const manifest = await readJSON(manifestPath);
168
+ const manifest = await readJSON(fs, manifestPath);
167
169
  const sapApp = manifest?.['sap.app'];
168
170
  const type = sapApp?.type;
169
171
  // Only include libraries and components
@@ -25,12 +25,22 @@ export declare const escapeSingleQuotes: (s: string) => string;
25
25
  * @param s
26
26
  */
27
27
  export declare const escapeDoubleQuotes: (s: string) => string;
28
+ export declare function doesPropertyExist(obj: unknown, fieldName: string): boolean;
28
29
  /**
29
- * Check if directory exists
30
- * Uses editorHasPath to avoid fs.exists() side effect of staging paths
30
+ * Check if directory exists.
31
+ * Uses editorHasPath to avoid fs.exists() side effect of staging paths, then falls back
32
+ * to the real filesystem for directories that are not yet staged in mem-fs.
31
33
  *
32
- * @param directoryOrFs - Directory path, or Editor instance
33
- * @param directory - Directory path (if first param is Editor)
34
+ * @param directory - Directory path
35
+ * @param fsEditor - optional mem-fs editor; a fresh one is created when omitted
34
36
  */
35
- export declare function doesDirectoryExists(directoryOrFs: string | Editor, directory?: string): boolean;
37
+ export declare function doesDirectoryExists(directory: string, fsEditor?: Editor): boolean;
38
+ /**
39
+ * Create directory if it doesn't exist
40
+ *
41
+ * @param directory - Directory path to create
42
+ * @param fsEditor - optional mem-fs editor; a fresh one is created when omitted
43
+ * @returns True if directory was created, false if it already existed
44
+ */
45
+ export declare function createDirectory(directory: string, fsEditor?: Editor): Promise<boolean>;
36
46
  //# sourceMappingURL=file-system-utils.d.ts.map
@@ -1,4 +1,6 @@
1
- import { editorHasPath, getEditor } from './fs-adapter.js';
1
+ import { existsSync } from 'node:fs';
2
+ import { mkdir } from 'node:fs/promises';
3
+ import { editorHasPath, createMemFsEditor } from './fs-adapter.js';
2
4
  /**
3
5
  * Normalize path separators to forward slashes for cross-platform compatibility
4
6
  * mem-fs and many tools expect forward slashes regardless of OS
@@ -24,18 +26,39 @@ export const escapeSingleQuotes = (s) => s.replaceAll('\\', String.raw `\\`).rep
24
26
  *
25
27
  * @param s
26
28
  */
27
- export const escapeDoubleQuotes = (s) => s.replaceAll('\\', String.raw `\\`).replaceAll('"', String.raw `\"`);
29
+ export const escapeDoubleQuotes = (s) => s.replaceAll('"', String.raw `\"`);
30
+ export function doesPropertyExist(obj, fieldName) {
31
+ return Object.hasOwn(obj, fieldName);
32
+ }
33
+ /**
34
+ * Check if directory exists.
35
+ * Uses editorHasPath to avoid fs.exists() side effect of staging paths, then falls back
36
+ * to the real filesystem for directories that are not yet staged in mem-fs.
37
+ *
38
+ * @param directory - Directory path
39
+ * @param fsEditor - optional mem-fs editor; a fresh one is created when omitted
40
+ */
41
+ export function doesDirectoryExists(directory, fsEditor) {
42
+ const fs = fsEditor ?? createMemFsEditor();
43
+ if (editorHasPath(fs, directory)) {
44
+ return true;
45
+ }
46
+ return existsSync(directory);
47
+ }
28
48
  /**
29
- * Check if directory exists
30
- * Uses editorHasPath to avoid fs.exists() side effect of staging paths
49
+ * Create directory if it doesn't exist
31
50
  *
32
- * @param directoryOrFs - Directory path, or Editor instance
33
- * @param directory - Directory path (if first param is Editor)
51
+ * @param directory - Directory path to create
52
+ * @param fsEditor - optional mem-fs editor; a fresh one is created when omitted
53
+ * @returns True if directory was created, false if it already existed
34
54
  */
35
- export function doesDirectoryExists(directoryOrFs, directory) {
36
- const fs = getEditor(directoryOrFs);
37
- const dir = typeof directoryOrFs === 'string' ? directoryOrFs : directory;
38
- // Use editorHasPath instead of fs.exists() to avoid side effect of staging paths
39
- return editorHasPath(fs, dir);
55
+ export async function createDirectory(directory, fsEditor) {
56
+ const fs = fsEditor ?? createMemFsEditor();
57
+ let isCreated = false;
58
+ if (!doesDirectoryExists(directory, fs)) {
59
+ await mkdir(directory, { recursive: true });
60
+ isCreated = true;
61
+ }
62
+ return isCreated;
40
63
  }
41
64
  //# sourceMappingURL=file-system-utils.js.map
@@ -1,33 +1,17 @@
1
1
  /**
2
- * Mem-fs editor utilities with context-based management
2
+ * Mem-fs editor utilities.
3
3
  *
4
- * Provides thread-safe, per-invocation Editor instances for the migration writer.
5
- * Uses AsyncLocalStorage to track the current migration's editor without requiring
6
- * it to be passed through every function call.
4
+ * Provides per-invocation Editor instances for the migration writer. The editor is
5
+ * threaded explicitly through the call tree (as in the other open-ux-tools writers),
6
+ * so there is no implicit/global context here.
7
7
  */
8
8
  import type { Editor } from 'mem-fs-editor';
9
- /**
10
- * Get editor from context, parameter, or create a new one.
11
- * Priority: explicit parameter > context > new instance
12
- *
13
- * @param editorOrPath - Editor instance or a path string
14
- */
15
- export declare function getEditor(editorOrPath: string | Editor): Editor;
16
9
  /**
17
10
  * Create a new mem-fs editor instance
18
11
  *
19
12
  * @returns New mem-fs editor instance
20
13
  */
21
14
  export declare function createMemFsEditor(): Editor;
22
- /**
23
- * Run a function with an editor in context
24
- * The editor will be available to all functions called within the callback
25
- *
26
- * @param editor - Editor instance to use
27
- * @param callback - Function to run with the editor in context
28
- * @returns Result of the callback
29
- */
30
- export declare function runWithEditor<T>(editor: Editor, callback: () => T | Promise<T>): T | Promise<T>;
31
15
  /**
32
16
  * Load every file under a project folder into the mem-fs store.
33
17
  *
@@ -39,56 +23,14 @@ export declare function runWithEditor<T>(editor: Editor, callback: () => T | Pro
39
23
  * @param projectRoot - Absolute path to the project folder to load
40
24
  */
41
25
  export declare function loadProjectIntoEditor(editor: Editor, projectRoot: string): Promise<void>;
42
- /**
43
- * Get the current editor from context
44
- *
45
- * @returns Current editor or undefined if not in a migration context
46
- */
47
- export declare function getCurrentEditor(): Editor | undefined;
48
- /**
49
- * Check if an editor is available in the current context
50
- *
51
- * @returns True if editor is available
52
- */
53
- export declare function isMemFsEnabled(): boolean;
54
26
  /**
55
27
  * Check whether an editor contains a file or a directory represented by staged files.
56
28
  *
57
- * WARNING: Do NOT use editor.exists() as it has a side effect of staging the file!
29
+ * WARNING: Do NOT use editor.exists() for directories as it only reports files.
58
30
  *
59
31
  * @param editor - Mem-fs editor to inspect
60
32
  * @param path - Absolute file or directory path
61
33
  * @returns True when the path or one of its children is staged in mem-fs
62
34
  */
63
35
  export declare function editorHasPath(editor: Editor, path: string): boolean;
64
- /**
65
- * Check if a file/directory exists (async).
66
- *
67
- * When an editor is in context, the migration tree is already loaded into mem-fs, so a staged
68
- * path is authoritative. A real-filesystem check is still used as a fallback so files that were
69
- * never loaded into mem-fs are not reported missing (and therefore not silently overwritten).
70
- *
71
- * @param path - Path to check
72
- * @returns True if staged in mem-fs OR present on the real filesystem
73
- */
74
- export declare function exists(path: string): Promise<boolean>;
75
- /**
76
- * Copy file using mem-fs or real filesystem
77
- *
78
- * @param src - Source path
79
- * @param dest - Destination path
80
- */
81
- export declare function copyFile(src: string, dest: string): void;
82
- /**
83
- * Delete file using mem-fs or real filesystem
84
- *
85
- * @param path - Path to delete
86
- */
87
- export declare function deleteFile(path: string): void;
88
- /**
89
- * Create directory (no-op in mem-fs, directories created implicitly)
90
- *
91
- * @param _path - Directory path
92
- */
93
- export declare function mkdir(_path: string): void;
94
36
  //# sourceMappingURL=fs-adapter.d.ts.map
@@ -1,29 +1,7 @@
1
1
  import { create as createMemFs } from 'mem-fs';
2
2
  import { create as createEditor } from 'mem-fs-editor';
3
- import { access } from 'node:fs/promises';
4
- import { AsyncLocalStorage } from 'node:async_hooks';
5
3
  import fastGlob from 'fast-glob';
6
4
  import { normalizePath } from './file-system-utils.js';
7
- /**
8
- * AsyncLocalStorage for tracking the current migration's editor
9
- */
10
- const editorContext = new AsyncLocalStorage();
11
- /**
12
- * Get editor from context, parameter, or create a new one.
13
- * Priority: explicit parameter > context > new instance
14
- *
15
- * @param editorOrPath - Editor instance or a path string
16
- */
17
- export function getEditor(editorOrPath) {
18
- if (typeof editorOrPath !== 'string') {
19
- return editorOrPath;
20
- }
21
- const contextEditor = getCurrentEditor();
22
- if (contextEditor) {
23
- return contextEditor;
24
- }
25
- return createMemFsEditor();
26
- }
27
5
  /**
28
6
  * Create a new mem-fs editor instance
29
7
  *
@@ -33,17 +11,6 @@ export function createMemFsEditor() {
33
11
  const store = createMemFs();
34
12
  return createEditor(store);
35
13
  }
36
- /**
37
- * Run a function with an editor in context
38
- * The editor will be available to all functions called within the callback
39
- *
40
- * @param editor - Editor instance to use
41
- * @param callback - Function to run with the editor in context
42
- * @returns Result of the callback
43
- */
44
- export function runWithEditor(editor, callback) {
45
- return editorContext.run(editor, callback);
46
- }
47
14
  /**
48
15
  * Load every file under a project folder into the mem-fs store.
49
16
  *
@@ -70,26 +37,10 @@ export async function loadProjectIntoEditor(editor, projectRoot) {
70
37
  store.get(file);
71
38
  }
72
39
  }
73
- /**
74
- * Get the current editor from context
75
- *
76
- * @returns Current editor or undefined if not in a migration context
77
- */
78
- export function getCurrentEditor() {
79
- return editorContext.getStore();
80
- }
81
- /**
82
- * Check if an editor is available in the current context
83
- *
84
- * @returns True if editor is available
85
- */
86
- export function isMemFsEnabled() {
87
- return getCurrentEditor() !== undefined;
88
- }
89
40
  /**
90
41
  * Check whether an editor contains a file or a directory represented by staged files.
91
42
  *
92
- * WARNING: Do NOT use editor.exists() as it has a side effect of staging the file!
43
+ * WARNING: Do NOT use editor.exists() for directories as it only reports files.
93
44
  *
94
45
  * @param editor - Mem-fs editor to inspect
95
46
  * @param path - Absolute file or directory path
@@ -105,6 +56,11 @@ export function editorHasPath(editor, path) {
105
56
  if (file.state === 'deleted') {
106
57
  return;
107
58
  }
59
+ // Ignore entries with no content: a missing path that was only probed via store.get()
60
+ // (e.g. by fs.exists/fs.read) is cached with contents === null and must not count as present.
61
+ if (file.contents === null) {
62
+ return;
63
+ }
108
64
  // Normalize file path as well for comparison
109
65
  const normalizedFilePath = normalizePath(file.path);
110
66
  // Check exact match OR files under this directory
@@ -114,65 +70,4 @@ export function editorHasPath(editor, path) {
114
70
  });
115
71
  return found;
116
72
  }
117
- /**
118
- * Check if a file/directory exists (async).
119
- *
120
- * When an editor is in context, the migration tree is already loaded into mem-fs, so a staged
121
- * path is authoritative. A real-filesystem check is still used as a fallback so files that were
122
- * never loaded into mem-fs are not reported missing (and therefore not silently overwritten).
123
- *
124
- * @param path - Path to check
125
- * @returns True if staged in mem-fs OR present on the real filesystem
126
- */
127
- export async function exists(path) {
128
- const editor = getCurrentEditor();
129
- if (editor && editorHasPath(editor, path)) {
130
- return true;
131
- }
132
- try {
133
- await access(path);
134
- return true;
135
- }
136
- catch {
137
- return false;
138
- }
139
- }
140
- /**
141
- * Copy file using mem-fs or real filesystem
142
- *
143
- * @param src - Source path
144
- * @param dest - Destination path
145
- */
146
- export function copyFile(src, dest) {
147
- const editor = getCurrentEditor();
148
- if (editor) {
149
- editor.copy(src, dest);
150
- }
151
- else {
152
- throw new Error('Editor not available. Call runWithEditor() to set up the migration context.');
153
- }
154
- }
155
- /**
156
- * Delete file using mem-fs or real filesystem
157
- *
158
- * @param path - Path to delete
159
- */
160
- export function deleteFile(path) {
161
- const editor = getCurrentEditor();
162
- if (editor) {
163
- editor.delete(path);
164
- }
165
- else {
166
- throw new Error('Editor not available. Call runWithEditor() to set up the migration context.');
167
- }
168
- }
169
- /**
170
- * Create directory (no-op in mem-fs, directories created implicitly)
171
- *
172
- * @param _path - Directory path
173
- */
174
- export function mkdir(_path) {
175
- // mem-fs handles directories implicitly when writing files
176
- // No explicit mkdir needed
177
- }
178
73
  //# sourceMappingURL=fs-adapter.js.map
@@ -1,3 +1,4 @@
1
+ import type { Editor } from 'mem-fs-editor';
1
2
  import type { Manifest } from '../project-spec-types.js';
2
3
  import { FioriElementsVersion } from '../project-spec-types.js';
3
4
  import type { ImportProjectInfo } from '../types.js';
@@ -19,8 +20,9 @@ export declare function checkManifestUI5Version(manifestUI5Version: string): str
19
20
  * @param rootPath
20
21
  * @param webappPath
21
22
  * @param uiAdaptation
23
+ * @param fsEditor - optional mem-fs editor; a fresh one is created when omitted
22
24
  */
23
- export declare function readManifest(rootPath: string, webappPath: string, uiAdaptation?: any): Promise<Manifest | undefined>;
25
+ export declare function readManifest(rootPath: string, webappPath: string, uiAdaptation?: any, fsEditor?: Editor): Promise<Manifest | undefined>;
24
26
  /**
25
27
  * Determine if we are dealing with a SAP Freestyle application
26
28
  *
@@ -1,6 +1,7 @@
1
1
  import { join } from 'node:path';
2
2
  import { FileName, FioriElementsVersion } from '../project-spec-types.js';
3
3
  import { fileExists, readJSON } from './file-access.js';
4
+ import { createMemFsEditor } from './fs-adapter.js';
4
5
  import semver from 'semver';
5
6
  import { distVar } from './constants.js';
6
7
  import { stripSpaces } from './file-system-utils.js';
@@ -38,12 +39,14 @@ export function checkManifestUI5Version(manifestUI5Version) {
38
39
  * @param rootPath
39
40
  * @param webappPath
40
41
  * @param uiAdaptation
42
+ * @param fsEditor - optional mem-fs editor; a fresh one is created when omitted
41
43
  */
42
- export async function readManifest(rootPath, webappPath, uiAdaptation) {
44
+ export async function readManifest(rootPath, webappPath, uiAdaptation, fsEditor) {
45
+ const fs = fsEditor ?? createMemFsEditor();
43
46
  const manifestPath = join(rootPath, webappPath, FileName.Manifest);
44
47
  let manifestJSON;
45
- if (uiAdaptation === undefined && (await fileExists(manifestPath))) {
46
- manifestJSON = await readJSON(manifestPath);
48
+ if (uiAdaptation === undefined && fileExists(fs, manifestPath)) {
49
+ manifestJSON = await readJSON(fs, manifestPath);
47
50
  }
48
51
  return manifestJSON;
49
52
  }