@sap-ux/project-access 1.28.9 → 1.29.0

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.
@@ -275,12 +275,12 @@ async function createApplicationAccess(appRoot, fs) {
275
275
  if (!app) {
276
276
  throw new Error(`Could not find app with root ${appRoot}`);
277
277
  }
278
- const project = await (0, info_1.getProject)(app.projectRoot);
279
- const appId = (0, path_1.relative)(project.root, appRoot);
280
278
  let options;
281
279
  if (fs) {
282
280
  options = isEditor(fs) ? { fs } : fs;
283
281
  }
282
+ const project = await (0, info_1.getProject)(app.projectRoot, options?.fs);
283
+ const appId = (0, path_1.relative)(project.root, appRoot);
284
284
  return new ApplicationAccessImp(project, appId, options);
285
285
  }
286
286
  catch (error) {
@@ -296,7 +296,7 @@ async function createApplicationAccess(appRoot, fs) {
296
296
  */
297
297
  async function createProjectAccess(root, options) {
298
298
  try {
299
- const project = await (0, info_1.getProject)(root);
299
+ const project = await (0, info_1.getProject)(root, options?.memFs);
300
300
  const projectAccess = new ProjectAccessImp(project, options);
301
301
  return projectAccess;
302
302
  }
@@ -13,16 +13,18 @@ export declare function isCapNodeJsProject(packageJson: Package): boolean;
13
13
  *
14
14
  * @param projectRoot - the root path of the project
15
15
  * @param [capCustomPaths] - optional, relative CAP paths like app, db, srv
16
+ * @param memFs - optional mem-fs-editor instance
16
17
  * @returns - true if the project is a CAP project
17
18
  */
18
- export declare function isCapJavaProject(projectRoot: string, capCustomPaths?: CapCustomPaths): Promise<boolean>;
19
+ export declare function isCapJavaProject(projectRoot: string, capCustomPaths?: CapCustomPaths, memFs?: Editor): Promise<boolean>;
19
20
  /**
20
21
  * Returns the CAP project type, undefined if it is not a CAP project.
21
22
  *
22
23
  * @param projectRoot - root of the project, where the package.json resides.
24
+ * @param memFs - optional mem-fs-editor instance
23
25
  * @returns - CAPJava for Java based CAP projects; CAPNodejs for node.js based CAP projects; undefined if it is no CAP project
24
26
  */
25
- export declare function getCapProjectType(projectRoot: string): Promise<CapProjectType | undefined>;
27
+ export declare function getCapProjectType(projectRoot: string, memFs?: Editor): Promise<CapProjectType | undefined>;
26
28
  /**
27
29
  * Returns true if the project is either a CAP Node.js or a CAP Java project.
28
30
  *
@@ -36,29 +36,64 @@ function isCapNodeJsProject(packageJson) {
36
36
  *
37
37
  * @param projectRoot - the root path of the project
38
38
  * @param [capCustomPaths] - optional, relative CAP paths like app, db, srv
39
+ * @param memFs - optional mem-fs-editor instance
39
40
  * @returns - true if the project is a CAP project
40
41
  */
41
- async function isCapJavaProject(projectRoot, capCustomPaths) {
42
+ async function isCapJavaProject(projectRoot, capCustomPaths, memFs) {
42
43
  const srv = capCustomPaths?.srv ?? (await getCapCustomPaths(projectRoot)).srv;
43
- return (0, file_1.fileExists)((0, path_1.join)(projectRoot, srv, 'src', 'main', 'resources', constants_1.FileName.CapJavaApplicationYaml));
44
+ return (0, file_1.fileExists)((0, path_1.join)(projectRoot, srv, 'src', 'main', 'resources', constants_1.FileName.CapJavaApplicationYaml), memFs);
45
+ }
46
+ /**
47
+ * Checks if there are files in the `srv` folder, using node fs or mem-fs.
48
+ *
49
+ * @param {string} srvFolderPath - The path to the `srv` folder to check for files.
50
+ * @param {Editor} [memFs] - An optional `mem-fs-editor` instance. If provided, the function checks files within the in-memory file system.
51
+ * @returns {Promise<boolean>} - Resolves to `true` if files are found in the `srv` folder; otherwise, `false`.
52
+ */
53
+ async function checkFilesInSrvFolder(srvFolderPath, memFs) {
54
+ if (!memFs) {
55
+ return await (0, file_1.fileExists)(srvFolderPath);
56
+ }
57
+ // Load the srv folder and its files into mem-fs
58
+ // This is necessary as mem-fs operates in-memory and doesn't automatically include files from disk.
59
+ // By loading the files, we ensure they are available within mem-fs.
60
+ if (await (0, file_1.fileExists)(srvFolderPath)) {
61
+ const fileSystemFiles = await (0, file_1.readDirectory)(srvFolderPath);
62
+ for (const file of fileSystemFiles) {
63
+ const filePath = (0, path_1.join)(srvFolderPath, file);
64
+ if (await (0, file_1.fileExists)(filePath)) {
65
+ const fileContent = await (0, file_1.readFile)(filePath);
66
+ memFs.write(filePath, fileContent);
67
+ }
68
+ }
69
+ }
70
+ // Dump the mem-fs state
71
+ const memFsDump = memFs.dump();
72
+ const memFsFiles = Object.keys(memFsDump).filter((filePath) => {
73
+ const normalisedFilePath = (0, path_1.resolve)(filePath);
74
+ const normalisedSrvPath = (0, path_1.resolve)(srvFolderPath);
75
+ return normalisedFilePath.startsWith(normalisedSrvPath);
76
+ });
77
+ return memFsFiles.length > 0;
44
78
  }
45
79
  /**
46
80
  * Returns the CAP project type, undefined if it is not a CAP project.
47
81
  *
48
82
  * @param projectRoot - root of the project, where the package.json resides.
83
+ * @param memFs - optional mem-fs-editor instance
49
84
  * @returns - CAPJava for Java based CAP projects; CAPNodejs for node.js based CAP projects; undefined if it is no CAP project
50
85
  */
51
- async function getCapProjectType(projectRoot) {
86
+ async function getCapProjectType(projectRoot, memFs) {
52
87
  const capCustomPaths = await getCapCustomPaths(projectRoot);
53
- if (!(await (0, file_1.fileExists)((0, path_1.join)(projectRoot, capCustomPaths.srv)))) {
88
+ if (!(await checkFilesInSrvFolder((0, path_1.join)(projectRoot, capCustomPaths.srv), memFs))) {
54
89
  return undefined;
55
90
  }
56
- if (await isCapJavaProject(projectRoot, capCustomPaths)) {
91
+ if (await isCapJavaProject(projectRoot, capCustomPaths, memFs)) {
57
92
  return 'CAPJava';
58
93
  }
59
94
  let packageJson;
60
95
  try {
61
- packageJson = await (0, file_1.readJSON)((0, path_1.join)(projectRoot, constants_1.FileName.Package));
96
+ packageJson = await (0, file_1.readJSON)((0, path_1.join)(projectRoot, constants_1.FileName.Package), memFs);
62
97
  }
63
98
  catch {
64
99
  // Ignore errors while reading the package.json file
@@ -1,12 +1,14 @@
1
1
  import type { I18nPropertiesPaths, Manifest } from '../../types';
2
+ import type { Editor } from 'mem-fs-editor';
2
3
  /**
3
4
  * Return absolute paths to i18n.properties files from manifest.
4
5
  *
5
6
  * @param manifestPath - path to manifest.json; used to parse manifest.json if not provided as second argument and to resolve absolute paths
6
7
  * @param manifest - optionally, parsed content of manifest.json, pass to avoid reading it again.
8
+ * @param memFs - optional mem-fs-editor instance
7
9
  * @returns - absolute paths to i18n.properties
8
10
  */
9
- export declare function getI18nPropertiesPaths(manifestPath: string, manifest?: Manifest): Promise<I18nPropertiesPaths>;
11
+ export declare function getI18nPropertiesPaths(manifestPath: string, manifest?: Manifest, memFs?: Editor): Promise<I18nPropertiesPaths>;
10
12
  /**
11
13
  * Return paths to i18n.properties files from manifest,
12
14
  * relative to the manifest.json.
@@ -9,10 +9,11 @@ const file_1 = require("../../file");
9
9
  *
10
10
  * @param manifestPath - path to manifest.json; used to parse manifest.json if not provided as second argument and to resolve absolute paths
11
11
  * @param manifest - optionally, parsed content of manifest.json, pass to avoid reading it again.
12
+ * @param memFs - optional mem-fs-editor instance
12
13
  * @returns - absolute paths to i18n.properties
13
14
  */
14
- async function getI18nPropertiesPaths(manifestPath, manifest) {
15
- const parsedManifest = manifest ?? (await (0, file_1.readJSON)(manifestPath));
15
+ async function getI18nPropertiesPaths(manifestPath, manifest, memFs) {
16
+ const parsedManifest = manifest ?? (await (0, file_1.readJSON)(manifestPath, memFs));
16
17
  const manifestFolder = (0, path_1.dirname)(manifestPath);
17
18
  const relativeI18nPropertiesPaths = getRelativeI18nPropertiesPaths(parsedManifest);
18
19
  const i18nPropertiesPaths = {
@@ -4,9 +4,10 @@ import type { AppProgrammingLanguage, AppType, Manifest, Project, ProjectType }
4
4
  * Returns the project structure for a given Fiori project.
5
5
  *
6
6
  * @param root - project root folder
7
+ * @param memFs - optional mem-fs-editor instance
7
8
  * @returns - project structure with project info like project type, apps, root folder
8
9
  */
9
- export declare function getProject(root: string): Promise<Project>;
10
+ export declare function getProject(root: string, memFs?: Editor): Promise<Project>;
10
11
  /**
11
12
  * Get the used programming language of an application.
12
13
  *
@@ -19,9 +20,10 @@ export declare function getAppProgrammingLanguage(appRoot: string, memFs?: Edito
19
20
  * Get the type of application or Fiori artifact.
20
21
  *
21
22
  * @param appRoot - path to application root
23
+ * @param memFs - optional mem-fs-editor instance
22
24
  * @returns - type of application, e.g. SAP Fiori elements, SAPUI5 freestyle, SAPUI5 Extension, ... see AppType.
23
25
  */
24
- export declare function getAppType(appRoot: string): Promise<AppType | undefined>;
26
+ export declare function getAppType(appRoot: string, memFs?: Editor): Promise<AppType | undefined>;
25
27
  /**
26
28
  * Returns the project type for a given Fiori project.
27
29
  *
@@ -20,16 +20,17 @@ const semver_1 = require("semver");
20
20
  * Returns the project structure for a given Fiori project.
21
21
  *
22
22
  * @param root - project root folder
23
+ * @param memFs - optional mem-fs-editor instance
23
24
  * @returns - project structure with project info like project type, apps, root folder
24
25
  */
25
- async function getProject(root) {
26
- if (!(await (0, file_1.fileExists)((0, path_1.join)(root, constants_1.FileName.Package)))) {
26
+ async function getProject(root, memFs) {
27
+ if (!(await (0, file_1.fileExists)((0, path_1.join)(root, constants_1.FileName.Package), memFs))) {
27
28
  throw new Error(`The project root folder '${root}' is not a Fiori project. No 'package.json' found.`);
28
29
  }
29
30
  const capProjectType = await (0, cap_1.getCapProjectType)(root);
30
31
  const projectType = capProjectType ?? 'EDMXBackend';
31
- const appFolders = await getAppFolders(root);
32
- const apps = await getApps(root, appFolders);
32
+ const appFolders = await getAppFolders(root, memFs);
33
+ const apps = await getApps(root, appFolders, memFs);
33
34
  return {
34
35
  root,
35
36
  projectType,
@@ -42,10 +43,11 @@ async function getProject(root) {
42
43
  * array of operating system specific relative paths to the apps.
43
44
  *
44
45
  * @param root - project root folder
46
+ * @param memFs - optional mem-fs-editor instance
45
47
  * @returns - array of operating specific application folders
46
48
  */
47
- async function getAppFolders(root) {
48
- const apps = await (0, search_1.findAllApps)([root]);
49
+ async function getAppFolders(root, memFs) {
50
+ const apps = await (0, search_1.findAllApps)([root], memFs);
49
51
  return apps.length > 0 ? apps.map((app) => (0, path_1.relative)(root, app.appRoot)) : [''];
50
52
  }
51
53
  /**
@@ -53,12 +55,13 @@ async function getAppFolders(root) {
53
55
  *
54
56
  * @param root - project root folder
55
57
  * @param appFolders - array of relative application folders
58
+ * @param memFs - optional mem-fs-editor instance
56
59
  * @returns - map of application structures
57
60
  */
58
- async function getApps(root, appFolders) {
61
+ async function getApps(root, appFolders, memFs) {
59
62
  const apps = {};
60
63
  for (const appFolder of appFolders) {
61
- const applicationStructure = await getApplicationStructure(root, appFolder);
64
+ const applicationStructure = await getApplicationStructure(root, appFolder, memFs);
62
65
  if (applicationStructure) {
63
66
  apps[appFolder] = applicationStructure;
64
67
  }
@@ -70,21 +73,22 @@ async function getApps(root, appFolders) {
70
73
  *
71
74
  * @param root - project root folder
72
75
  * @param appFolder - relative application folder
76
+ * @param memFs - optional mem-fs-editor instance
73
77
  * @returns - application structure with application info like manifest, changes, main service, services, annotations
74
78
  */
75
- async function getApplicationStructure(root, appFolder) {
79
+ async function getApplicationStructure(root, appFolder, memFs) {
76
80
  const appRoot = (0, path_1.join)(root, appFolder);
77
- const absoluteWebappPath = await (0, ui5_config_1.getWebappPath)(appRoot);
78
- const appType = (await getAppType(appRoot));
81
+ const absoluteWebappPath = await (0, ui5_config_1.getWebappPath)(appRoot, memFs);
82
+ const appType = (await getAppType(appRoot, memFs));
79
83
  const manifest = (0, path_1.join)(absoluteWebappPath, constants_1.FileName.Manifest);
80
- if (!(await (0, file_1.fileExists)(manifest))) {
84
+ if (!(await (0, file_1.fileExists)(manifest, memFs))) {
81
85
  return undefined;
82
86
  }
83
- const manifestObject = await (0, file_1.readJSON)(manifest);
87
+ const manifestObject = await (0, file_1.readJSON)(manifest, memFs);
84
88
  const changes = (0, path_1.join)(absoluteWebappPath, constants_1.DirName.Changes);
85
- const i18n = await (0, i18n_1.getI18nPropertiesPaths)(manifest, manifestObject);
89
+ const i18n = await (0, i18n_1.getI18nPropertiesPaths)(manifest, manifestObject, memFs);
86
90
  const mainService = (0, service_1.getMainService)(manifestObject);
87
- const services = await (0, service_1.getServicesAndAnnotations)(manifest, manifestObject);
91
+ const services = await (0, service_1.getServicesAndAnnotations)(manifest, manifestObject, memFs);
88
92
  return {
89
93
  appRoot,
90
94
  appType,
@@ -126,14 +130,16 @@ async function getAppProgrammingLanguage(appRoot, memFs) {
126
130
  * Get the type of application or Fiori artifact.
127
131
  *
128
132
  * @param appRoot - path to application root
133
+ * @param memFs - optional mem-fs-editor instance
129
134
  * @returns - type of application, e.g. SAP Fiori elements, SAPUI5 freestyle, SAPUI5 Extension, ... see AppType.
130
135
  */
131
- async function getAppType(appRoot) {
136
+ async function getAppType(appRoot, memFs) {
132
137
  let appType;
133
138
  try {
134
139
  const artifacts = await (0, search_1.findFioriArtifacts)({
135
140
  wsFolders: [appRoot],
136
- artifacts: ['adaptations', 'applications', 'extensions', 'libraries']
141
+ artifacts: ['adaptations', 'applications', 'extensions', 'libraries'],
142
+ memFs
137
143
  });
138
144
  if ((artifacts.applications?.length ?? 0) +
139
145
  (artifacts.adaptations?.length ?? 0) +
@@ -141,7 +147,7 @@ async function getAppType(appRoot) {
141
147
  (artifacts.libraries?.length ?? 0) ===
142
148
  1) {
143
149
  if (artifacts.applications?.length === 1) {
144
- appType = await getApplicationType(artifacts.applications[0]);
150
+ appType = await getApplicationType(artifacts.applications[0], memFs);
145
151
  }
146
152
  else if (artifacts.adaptations?.length === 1) {
147
153
  appType = 'Fiori Adaptation';
@@ -163,12 +169,15 @@ async function getAppType(appRoot) {
163
169
  * Get the application type from search results.
164
170
  *
165
171
  * @param application - application from findFioriArtifacts() results
172
+ * @param memFs - optional mem-fs-editor instance
166
173
  * @returns - type of application: 'SAP Fiori elements' or 'SAPUI5 freestyle'
167
174
  */
168
- async function getApplicationType(application) {
175
+ async function getApplicationType(application, memFs) {
169
176
  let appType;
170
177
  const rootPackageJsonPath = (0, path_1.join)(application.projectRoot, constants_1.FileName.Package);
171
- const packageJson = (await (0, file_1.fileExists)(rootPackageJsonPath)) ? await (0, file_1.readJSON)(rootPackageJsonPath) : null;
178
+ const packageJson = (await (0, file_1.fileExists)(rootPackageJsonPath, memFs))
179
+ ? await (0, file_1.readJSON)(rootPackageJsonPath, memFs)
180
+ : null;
172
181
  if (application.projectRoot === application.appRoot) {
173
182
  appType = packageJson?.sapux ? 'SAP Fiori elements' : 'SAPUI5 freestyle';
174
183
  }
@@ -88,8 +88,10 @@ async function getModule(module, version, options) {
88
88
  await (0, promises_1.rm)(moduleDirectory, { recursive: true });
89
89
  }
90
90
  await (0, promises_1.mkdir)(moduleDirectory, { recursive: true });
91
- await (0, promises_1.writeFile)(modulePackagePath, '{}');
92
- await (0, command_1.execNpmCommand)(['install', `${module}@${version}`], { cwd: moduleDirectory, logger });
91
+ await (0, command_1.execNpmCommand)(['install', '--prefix', moduleDirectory, `${module}@${version}`], {
92
+ cwd: moduleDirectory,
93
+ logger
94
+ });
93
95
  }
94
96
  return loadModuleFromProject(moduleDirectory, module);
95
97
  }
@@ -1,3 +1,4 @@
1
+ import type { Editor } from 'mem-fs-editor';
1
2
  import type { AllAppResults, FioriArtifactTypes, FoundFioriArtifacts, WorkspaceFolder } from '../types';
2
3
  /**
3
4
  * Find root folder of the project containing the given file.
@@ -5,9 +6,10 @@ import type { AllAppResults, FioriArtifactTypes, FoundFioriArtifacts, WorkspaceF
5
6
  * @param path path of a project file
6
7
  * @param sapuxRequired if true, only find sapux projects
7
8
  * @param silent if true, then does not throw an error but returns an empty path
9
+ * @param memFs - optional mem-fs-editor instance
8
10
  * @returns {*} {Promise<string>} - Project Root
9
11
  */
10
- export declare function findProjectRoot(path: string, sapuxRequired?: boolean, silent?: boolean): Promise<string>;
12
+ export declare function findProjectRoot(path: string, sapuxRequired?: boolean, silent?: boolean, memFs?: Editor): Promise<string>;
11
13
  /**
12
14
  * Get the application root for a given webapp path.
13
15
  *
@@ -28,9 +30,10 @@ export declare function getAppRootFromWebappPath(webappPath: string): Promise<st
28
30
  * - Freestyle application (non CAP) has in package.json dependency to @sap/ux-ui5-tooling and <appRoot>/ui5-local.yaml.
29
31
  *
30
32
  * @param path - path to check, e.g. to the manifest.json
33
+ * @param memFs - optional mem-fs-editor instance
31
34
  * @returns - in case a supported app is found this function returns the appRoot and projectRoot path
32
35
  */
33
- export declare function findRootsForPath(path: string): Promise<{
36
+ export declare function findRootsForPath(path: string, memFs?: Editor): Promise<{
34
37
  appRoot: string;
35
38
  projectRoot: string;
36
39
  } | null>;
@@ -39,29 +42,33 @@ export declare function findRootsForPath(path: string): Promise<{
39
42
  *
40
43
  * @param path - path inside CAP project
41
44
  * @param checkForAppRouter - if true, checks for app router in CAP project app folder
45
+ * @param memFs - optional mem-fs-editor instance
42
46
  * @returns - CAP project root path
43
47
  */
44
- export declare function findCapProjectRoot(path: string, checkForAppRouter?: boolean): Promise<string | null>;
48
+ export declare function findCapProjectRoot(path: string, checkForAppRouter?: boolean, memFs?: Editor): Promise<string | null>;
45
49
  /**
46
50
  * Find all app that are supported by Fiori tools for a given list of roots (workspace folders).
47
51
  * This is a convenient function to retrieve all apps. Same result can be achieved with call
48
52
  * findFioriArtifacts({ wsFolders, artifacts: ['applications'] }); from same module.
49
53
  *
50
54
  * @param wsFolders - list of roots, either as vscode WorkspaceFolder[] or array of paths
55
+ * @param memFs - optional mem-fs-editor instance
51
56
  * @returns - results as path to apps plus files already parsed, e.g. manifest.json
52
57
  */
53
- export declare function findAllApps(wsFolders: readonly WorkspaceFolder[] | string[] | undefined): Promise<AllAppResults[]>;
58
+ export declare function findAllApps(wsFolders: readonly WorkspaceFolder[] | string[] | undefined, memFs?: Editor): Promise<AllAppResults[]>;
54
59
  /**
55
60
  * Find all requested Fiori artifacts like apps, adaptations, extensions, that are supported by Fiori tools, for a given list of roots (workspace folders).
56
61
  *
57
62
  * @param options - find options
58
63
  * @param options.wsFolders - list of roots, either as vscode WorkspaceFolder[] or array of paths
59
64
  * @param options.artifacts - list of artifacts to search for: 'application', 'adaptation', 'extension' see FioriArtifactTypes
65
+ * @param options.memFs
60
66
  * @returns - data structure containing the search results, for app e.g. as path to app plus files already parsed, e.g. manifest.json
61
67
  */
62
68
  export declare function findFioriArtifacts(options: {
63
69
  wsFolders?: readonly WorkspaceFolder[] | string[];
64
70
  artifacts: FioriArtifactTypes[];
71
+ memFs?: Editor;
65
72
  }): Promise<FoundFioriArtifacts>;
66
73
  /**
67
74
  * Find all CAP project roots by locating pom.xml or package.json in a given workspace.
@@ -66,10 +66,11 @@ function wsFoldersToRootPaths(wsFolders) {
66
66
  * @param path path of a project file
67
67
  * @param sapuxRequired if true, only find sapux projects
68
68
  * @param silent if true, then does not throw an error but returns an empty path
69
+ * @param memFs - optional mem-fs-editor instance
69
70
  * @returns {*} {Promise<string>} - Project Root
70
71
  */
71
- async function findProjectRoot(path, sapuxRequired = true, silent = false) {
72
- const packageJson = await (0, file_1.findFileUp)(constants_1.FileName.Package, path);
72
+ async function findProjectRoot(path, sapuxRequired = true, silent = false, memFs) {
73
+ const packageJson = await (0, file_1.findFileUp)(constants_1.FileName.Package, path, memFs);
73
74
  if (!packageJson) {
74
75
  if (silent) {
75
76
  return '';
@@ -78,9 +79,9 @@ async function findProjectRoot(path, sapuxRequired = true, silent = false) {
78
79
  }
79
80
  let root = (0, path_1.dirname)(packageJson);
80
81
  if (sapuxRequired) {
81
- const sapux = (await (0, file_1.readJSON)(packageJson)).sapux;
82
+ const sapux = (await (0, file_1.readJSON)(packageJson, memFs)).sapux;
82
83
  if (!sapux) {
83
- root = await findProjectRoot((0, path_1.dirname)(root), sapuxRequired, silent);
84
+ root = await findProjectRoot((0, path_1.dirname)(root), sapuxRequired, silent, memFs);
84
85
  }
85
86
  }
86
87
  return root;
@@ -147,27 +148,28 @@ async function getAppRootFromWebappPath(webappPath) {
147
148
  * - Freestyle application (non CAP) has in package.json dependency to @sap/ux-ui5-tooling and <appRoot>/ui5-local.yaml.
148
149
  *
149
150
  * @param path - path to check, e.g. to the manifest.json
151
+ * @param memFs - optional mem-fs-editor instance
150
152
  * @returns - in case a supported app is found this function returns the appRoot and projectRoot path
151
153
  */
152
- async function findRootsForPath(path) {
154
+ async function findRootsForPath(path, memFs) {
153
155
  try {
154
156
  // Get the root of the app, that is where the package.json is, otherwise not supported
155
- const appRoot = await findProjectRoot(path, false);
157
+ const appRoot = await findProjectRoot(path, false, false, memFs);
156
158
  if (!appRoot) {
157
159
  return null;
158
160
  }
159
- const appPckJson = await (0, file_1.readJSON)((0, path_1.join)(appRoot, constants_1.FileName.Package));
161
+ const appPckJson = await (0, file_1.readJSON)((0, path_1.join)(appRoot, constants_1.FileName.Package), memFs);
160
162
  // Check for most common app, Fiori elements with sapux=true in package.json
161
163
  if (appPckJson.sapux) {
162
164
  return findRootsWithSapux(appPckJson.sapux, path, appRoot);
163
165
  }
164
- if ((await (0, cap_1.getCapProjectType)(appRoot)) !== undefined) {
166
+ if ((await (0, cap_1.getCapProjectType)(appRoot, memFs)) !== undefined) {
165
167
  // App is part of a CAP project, but doesn't have own package.json and is not mentioned in sapux array
166
168
  // in root -> not supported
167
169
  return null;
168
170
  }
169
171
  // Check if app is included in CAP project
170
- const projectRoot = await findCapProjectRoot(appRoot);
172
+ const projectRoot = await findCapProjectRoot(appRoot, undefined, memFs);
171
173
  if (projectRoot) {
172
174
  // App included in CAP
173
175
  return {
@@ -177,7 +179,7 @@ async function findRootsForPath(path) {
177
179
  }
178
180
  else if (
179
181
  // Check for freestyle non CAP
180
- (await (0, file_1.fileExists)((0, path_1.join)(appRoot, constants_1.FileName.Ui5LocalYaml))) &&
182
+ (await (0, file_1.fileExists)((0, path_1.join)(appRoot, constants_1.FileName.Ui5LocalYaml), memFs)) &&
181
183
  (0, dependencies_1.hasDependency)(appPckJson, '@sap/ux-ui5-tooling')) {
182
184
  return {
183
185
  appRoot,
@@ -195,9 +197,10 @@ async function findRootsForPath(path) {
195
197
  *
196
198
  * @param path - path inside CAP project
197
199
  * @param checkForAppRouter - if true, checks for app router in CAP project app folder
200
+ * @param memFs - optional mem-fs-editor instance
198
201
  * @returns - CAP project root path
199
202
  */
200
- async function findCapProjectRoot(path, checkForAppRouter = true) {
203
+ async function findCapProjectRoot(path, checkForAppRouter = true, memFs) {
201
204
  try {
202
205
  if (!(0, path_1.isAbsolute)(path)) {
203
206
  return null;
@@ -205,7 +208,7 @@ async function findCapProjectRoot(path, checkForAppRouter = true) {
205
208
  const { root } = (0, path_1.parse)(path);
206
209
  let projectRoot = (0, path_1.dirname)(path);
207
210
  while (projectRoot !== root) {
208
- if (await (0, cap_1.getCapProjectType)(projectRoot)) {
211
+ if (await (0, cap_1.getCapProjectType)(projectRoot, memFs)) {
209
212
  // We have found a CAP project as root. Check if the found app is not directly in CAP's 'app/' folder.
210
213
  // Sometime there is a <CAP_ROOT>/app/package.json file that is used for app router (not an app)
211
214
  // or skip app router check if checkForAppRouter is false and return the project root.
@@ -227,26 +230,28 @@ async function findCapProjectRoot(path, checkForAppRouter = true) {
227
230
  * findFioriArtifacts({ wsFolders, artifacts: ['applications'] }); from same module.
228
231
  *
229
232
  * @param wsFolders - list of roots, either as vscode WorkspaceFolder[] or array of paths
233
+ * @param memFs - optional mem-fs-editor instance
230
234
  * @returns - results as path to apps plus files already parsed, e.g. manifest.json
231
235
  */
232
- async function findAllApps(wsFolders) {
233
- const findResults = await findFioriArtifacts({ wsFolders, artifacts: ['applications'] });
236
+ async function findAllApps(wsFolders, memFs) {
237
+ const findResults = await findFioriArtifacts({ wsFolders, artifacts: ['applications'], memFs });
234
238
  return findResults.applications ?? [];
235
239
  }
236
240
  /**
237
241
  * Filter Fiori apps from a list of files.
238
242
  *
239
243
  * @param pathMap - map of files. Key is the path, on first read parsed content will be set as value to prevent multiple reads of a file.
244
+ * @param memFs - optional mem-fs-editor instance
240
245
  * @returns - results as path to apps plus files already parsed, e.g. manifest.json
241
246
  */
242
- async function filterApplications(pathMap) {
247
+ async function filterApplications(pathMap, memFs) {
243
248
  const filterApplicationByManifest = async (manifestPath) => {
244
- pathMap[manifestPath] ??= await (0, file_1.readJSON)(manifestPath);
249
+ pathMap[manifestPath] ??= await (0, file_1.readJSON)(manifestPath, memFs);
245
250
  const manifest = pathMap[manifestPath]; // cast needed as pathMap also allows strings and any other objects
246
251
  // cast allowed, as this is the only place pathMap is filled for manifests
247
252
  if (manifest['sap.app'].id && manifest['sap.app'].type === 'application') {
248
- const roots = await findRootsForPath((0, path_1.dirname)(manifestPath));
249
- if (roots && !(await (0, file_1.fileExists)((0, path_1.join)(roots.appRoot, '.adp', constants_1.FileName.AdaptationConfig)))) {
253
+ const roots = await findRootsForPath((0, path_1.dirname)(manifestPath), memFs);
254
+ if (roots && !(await (0, file_1.fileExists)((0, path_1.join)(roots.appRoot, '.adp', constants_1.FileName.AdaptationConfig), memFs))) {
250
255
  return { appRoot: roots.appRoot, projectRoot: roots.projectRoot, manifest: manifest, manifestPath };
251
256
  }
252
257
  }
@@ -262,15 +267,16 @@ async function filterApplications(pathMap) {
262
267
  * Filter adaptation projects from a list of files.
263
268
  *
264
269
  * @param pathMap - map of files. Key is the path, on first read parsed content will be set as value to prevent multiple reads of a file.
270
+ * @param memFs - optional mem-fs-editor instance
265
271
  * @returns - results as array of found adaptation projects.
266
272
  */
267
- async function filterAdaptations(pathMap) {
273
+ async function filterAdaptations(pathMap, memFs) {
268
274
  const results = [];
269
275
  const manifestAppDescrVars = Object.keys(pathMap).filter((path) => path.endsWith(constants_1.FileName.ManifestAppDescrVar));
270
276
  for (const manifestAppDescrVar of manifestAppDescrVars) {
271
- const packageJsonPath = await (0, file_1.findFileUp)(constants_1.FileName.Package, (0, path_1.dirname)(manifestAppDescrVar));
277
+ const packageJsonPath = await (0, file_1.findFileUp)(constants_1.FileName.Package, (0, path_1.dirname)(manifestAppDescrVar), memFs);
272
278
  const projectRoot = packageJsonPath ? (0, path_1.dirname)(packageJsonPath) : null;
273
- if (projectRoot && (await (0, file_1.fileExists)((0, path_1.join)(projectRoot, 'webapp', constants_1.FileName.ManifestAppDescrVar)))) {
279
+ if (projectRoot && (await (0, file_1.fileExists)((0, path_1.join)(projectRoot, 'webapp', constants_1.FileName.ManifestAppDescrVar), memFs))) {
274
280
  results.push({ appRoot: projectRoot, manifestAppdescrVariantPath: manifestAppDescrVar });
275
281
  }
276
282
  }
@@ -280,9 +286,10 @@ async function filterAdaptations(pathMap) {
280
286
  * Filter extensions projects from a list of files.
281
287
  *
282
288
  * @param pathMap - map of files. Key is the path, on first read parsed content will be set as value to prevent multiple reads of a file.
289
+ * @param memFs - optional mem-fs-editor instance
283
290
  * @returns - results as array of found extension projects.
284
291
  */
285
- async function filterExtensions(pathMap) {
292
+ async function filterExtensions(pathMap, memFs) {
286
293
  const results = [];
287
294
  const extensionConfigs = Object.keys(pathMap).filter((path) => (0, path_1.basename)(path) === constants_1.FileName.ExtConfigJson);
288
295
  for (const extensionConfig of extensionConfigs) {
@@ -290,18 +297,19 @@ async function filterExtensions(pathMap) {
290
297
  let manifest = null;
291
298
  let manifestPath = Object.keys(pathMap).find((path) => path.startsWith((0, path_1.dirname)(extensionConfig) + path_1.sep) && (0, path_1.basename)(path) === constants_1.FileName.Manifest);
292
299
  if (manifestPath) {
293
- pathMap[manifestPath] ??= await (0, file_1.readJSON)(manifestPath);
300
+ pathMap[manifestPath] ??= await (0, file_1.readJSON)(manifestPath, memFs);
294
301
  manifest = pathMap[manifestPath];
295
302
  }
296
303
  else {
297
304
  const manifests = await (0, file_1.findBy)({
298
305
  fileNames: [constants_1.FileName.Manifest],
299
306
  root: (0, path_1.dirname)(extensionConfig),
300
- excludeFolders
307
+ excludeFolders,
308
+ memFs
301
309
  });
302
310
  if (manifests.length === 1) {
303
311
  [manifestPath] = manifests;
304
- manifest = await (0, file_1.readJSON)(manifestPath);
312
+ manifest = await (0, file_1.readJSON)(manifestPath, memFs);
305
313
  }
306
314
  }
307
315
  if (manifestPath && manifest) {
@@ -319,9 +327,10 @@ async function filterExtensions(pathMap) {
319
327
  *
320
328
  * @param pathMap - path to files
321
329
  * @param manifestPaths - paths to manifest.json files
330
+ * @param memFs - optional mem-fs-editor instance
322
331
  * @returns - results as array of found .library projects.
323
332
  */
324
- async function filterDotLibraries(pathMap, manifestPaths) {
333
+ async function filterDotLibraries(pathMap, manifestPaths, memFs) {
325
334
  const dotLibraries = [];
326
335
  const dotLibraryPaths = Object.keys(pathMap)
327
336
  .filter((path) => (0, path_1.basename)(path) === constants_1.FileName.Library)
@@ -329,7 +338,7 @@ async function filterDotLibraries(pathMap, manifestPaths) {
329
338
  .filter((path) => !manifestPaths.map((manifestPath) => (0, path_1.dirname)(manifestPath)).includes(path));
330
339
  if (dotLibraryPaths) {
331
340
  for (const libraryPath of dotLibraryPaths) {
332
- const projectRoot = (0, path_1.dirname)((await (0, file_1.findFileUp)(constants_1.FileName.Package, (0, path_1.dirname)(libraryPath))) ?? libraryPath);
341
+ const projectRoot = (0, path_1.dirname)((await (0, file_1.findFileUp)(constants_1.FileName.Package, (0, path_1.dirname)(libraryPath), memFs)) ?? libraryPath);
333
342
  dotLibraries.push({ projectRoot, libraryPath });
334
343
  }
335
344
  }
@@ -339,20 +348,21 @@ async function filterDotLibraries(pathMap, manifestPaths) {
339
348
  * Filter extensions projects from a list of files.
340
349
  *
341
350
  * @param pathMap - path to files
351
+ * @param memFs - optional mem-fs-editor instance
342
352
  * @returns - results as array of found library projects.
343
353
  */
344
- async function filterLibraries(pathMap) {
354
+ async function filterLibraries(pathMap, memFs) {
345
355
  const results = [];
346
356
  const manifestPaths = Object.keys(pathMap).filter((path) => (0, path_1.basename)(path) === constants_1.FileName.Manifest);
347
- results.push(...(await filterDotLibraries(pathMap, manifestPaths)));
357
+ results.push(...(await filterDotLibraries(pathMap, manifestPaths, memFs)));
348
358
  for (const manifestPath of manifestPaths) {
349
359
  try {
350
- pathMap[manifestPath] ??= await (0, file_1.readJSON)(manifestPath);
360
+ pathMap[manifestPath] ??= await (0, file_1.readJSON)(manifestPath, memFs);
351
361
  const manifest = pathMap[manifestPath];
352
362
  if (manifest['sap.app'] && manifest['sap.app'].type === 'library') {
353
- const packageJsonPath = await (0, file_1.findFileUp)(constants_1.FileName.Package, (0, path_1.dirname)(manifestPath));
363
+ const packageJsonPath = await (0, file_1.findFileUp)(constants_1.FileName.Package, (0, path_1.dirname)(manifestPath), memFs);
354
364
  const projectRoot = packageJsonPath ? (0, path_1.dirname)(packageJsonPath) : null;
355
- if (projectRoot && (await (0, file_1.fileExists)((0, path_1.join)(projectRoot, constants_1.FileName.Ui5Yaml)))) {
365
+ if (projectRoot && (await (0, file_1.fileExists)((0, path_1.join)(projectRoot, constants_1.FileName.Ui5Yaml), memFs))) {
356
366
  results.push({ projectRoot, manifestPath, manifest });
357
367
  }
358
368
  }
@@ -384,6 +394,7 @@ function getFilterFileNames(artifacts) {
384
394
  * @param options - find options
385
395
  * @param options.wsFolders - list of roots, either as vscode WorkspaceFolder[] or array of paths
386
396
  * @param options.artifacts - list of artifacts to search for: 'application', 'adaptation', 'extension' see FioriArtifactTypes
397
+ * @param options.memFs
387
398
  * @returns - data structure containing the search results, for app e.g. as path to app plus files already parsed, e.g. manifest.json
388
399
  */
389
400
  async function findFioriArtifacts(options) {
@@ -396,7 +407,8 @@ async function findFioriArtifacts(options) {
396
407
  const foundFiles = await (0, file_1.findBy)({
397
408
  fileNames,
398
409
  root,
399
- excludeFolders
410
+ excludeFolders,
411
+ memFs: options.memFs
400
412
  });
401
413
  foundFiles.forEach((path) => (pathMap[path] = null));
402
414
  }
@@ -405,16 +417,16 @@ async function findFioriArtifacts(options) {
405
417
  }
406
418
  }
407
419
  if (options.artifacts.includes('applications')) {
408
- results.applications = await filterApplications(pathMap);
420
+ results.applications = await filterApplications(pathMap, options.memFs);
409
421
  }
410
422
  if (options.artifacts.includes('adaptations')) {
411
- results.adaptations = await filterAdaptations(pathMap);
423
+ results.adaptations = await filterAdaptations(pathMap, options.memFs);
412
424
  }
413
425
  if (options.artifacts.includes('extensions')) {
414
- results.extensions = await filterExtensions(pathMap);
426
+ results.extensions = await filterExtensions(pathMap, options.memFs);
415
427
  }
416
428
  if (options.artifacts.includes('libraries')) {
417
- results.libraries = await filterLibraries(pathMap);
429
+ results.libraries = await filterLibraries(pathMap, options.memFs);
418
430
  }
419
431
  return results;
420
432
  }
@@ -1,4 +1,5 @@
1
1
  import type { Manifest, ManifestNamespace, ServiceSpecification } from '../types';
2
+ import type { Editor } from 'mem-fs-editor';
2
3
  /**
3
4
  * Get the main service name from the manifest.
4
5
  * LROP: by definition the service name can be read from the UI5 model with "" as name.
@@ -13,9 +14,10 @@ export declare function getMainService(manifest: Manifest): string | undefined;
13
14
  *
14
15
  * @param manifestPath - path to manifest.json
15
16
  * @param manifest - optionally, parsed content of manifest.json, pass to avoid reading it again.
17
+ * @param memFs - optional mem-fs-editor instance
16
18
  * @returns - service and annotation specification
17
19
  */
18
- export declare function getServicesAndAnnotations(manifestPath: string, manifest: Manifest): Promise<{
20
+ export declare function getServicesAndAnnotations(manifestPath: string, manifest: Manifest, memFs?: Editor): Promise<{
19
21
  [index: string]: ServiceSpecification;
20
22
  }>;
21
23
  /**
@@ -24,10 +24,11 @@ function getMainService(manifest) {
24
24
  *
25
25
  * @param manifestPath - path to manifest.json
26
26
  * @param manifest - optionally, parsed content of manifest.json, pass to avoid reading it again.
27
+ * @param memFs - optional mem-fs-editor instance
27
28
  * @returns - service and annotation specification
28
29
  */
29
- async function getServicesAndAnnotations(manifestPath, manifest) {
30
- const parsedManifest = manifest ?? (await (0, file_1.readJSON)(manifestPath));
30
+ async function getServicesAndAnnotations(manifestPath, manifest, memFs) {
31
+ const parsedManifest = manifest ?? (await (0, file_1.readJSON)(manifestPath, memFs));
31
32
  const manifestFolder = (0, path_1.dirname)(manifestPath);
32
33
  const services = {};
33
34
  const dataSources = parsedManifest?.['sap.app']?.dataSources ?? {};
@@ -120,6 +120,7 @@ export interface ApplicationAccess extends BaseAccess {
120
120
  }
121
121
  export interface ProjectAccessOptions {
122
122
  logger?: Logger;
123
+ memFs?: Editor;
123
124
  }
124
125
  export interface ProjectAccess extends BaseAccess {
125
126
  getApplicationIds: () => string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sap-ux/project-access",
3
- "version": "1.28.9",
3
+ "version": "1.29.0",
4
4
  "description": "Library to access SAP Fiori tools projects",
5
5
  "repository": {
6
6
  "type": "git",