Repository: solid/community-server (ts)

Issue:
rootFilePath can not be absolute
It seems to be assumed that `rootFilePath` is always a relative path.

For example, if I call the following:

```
$ node community-server -f /Users/rtaelman/nosync/rdf-dataset-fragmenter/output-persons-subject-object/http/www.ldbc.eu/
```

Then I get the following error:

> 2021-01-19T07:38:37.478Z [CliRunner] error: Could not initialize server: Error: ENOENT: no such file or directory, mkdir '/Users/rtaelman/Documents/UGent/nosync/community-server/Users/rtaelman/nosync/rdf-dataset-fragmenter/output-persons-subject-object/http/www.ldbc.eu/'

Even though the (absolute) target path _does_ exist.

I would expect absolute paths not to be attached to the cwd.

Pull request:
fix: Accept absolute paths in CliRunner
Fixes #524

I kept the same naming conventions as the other functions, but it is sort of a misnomer to call the results of `windowsToPosixPath` POSIX paths, as `posix.isAbsolute` is still going to return false on those paths. So perhaps we should change that.

Patch:
diff --git a/src/init/CliRunner.ts b/src/init/CliRunner.ts
index 4c522027e..57610b879 100644
--- a/src/init/CliRunner.ts
+++ b/src/init/CliRunner.ts
@@ -5,7 +5,7 @@ import type { IComponentsManagerBuilderOptions, LogLevel } from 'componentsjs';
 import { ComponentsManager } from 'componentsjs';
 import yargs from 'yargs';
 import { getLoggerFor } from '../logging/LogUtil';
-import { joinFilePath, ensureTrailingSlash } from '../util/PathUtil';
+import { joinFilePath, ensureTrailingSlash, absoluteFilePath } from '../util/PathUtil';
 import type { Initializer } from './Initializer';
 
 export class CliRunner {
@@ -71,7 +71,7 @@ export class CliRunner {
    */
   protected resolveFilePath(cwdPath?: string | null, modulePath = ''): string {
     return typeof cwdPath === 'string' ?
-      joinFilePath(process.cwd(), cwdPath) :
+      absoluteFilePath(cwdPath) :
       joinFilePath(__dirname, '../../', modulePath);
   }
 
diff --git a/src/util/PathUtil.ts b/src/util/PathUtil.ts
index e521e0b59..61acecbaa 100644
--- a/src/util/PathUtil.ts
+++ b/src/util/PathUtil.ts
@@ -1,4 +1,4 @@
-import { posix } from 'path';
+import { posix, win32 } from 'path';
 import type { ResourceIdentifier } from '../ldp/representation/ResourceIdentifier';
 
 /**
@@ -13,7 +13,7 @@ function windowsToPosixPath(path: string): string {
 }
 
 /**
- * Resolves relative segments in the path/
+ * Resolves relative segments in the path.
  *
  * @param path - Path to check (POSIX or Windows).
  *
@@ -35,6 +35,26 @@ export function joinFilePath(basePath: string, ...paths: string[]): string {
   return posix.join(windowsToPosixPath(basePath), ...paths);
 }
 
+/**
+ * Resolves a path to its absolute form.
+ * Absolute inputs will not be changed (except changing Windows to POSIX).
+ * Relative inputs will be interpreted relative to process.cwd().
+ *
+ * @param path - Path to check (POSIX or Windows).
+ *
+ * @returns The potentially changed path (POSIX).
+ */
+export function absoluteFilePath(path: string): string {
+  if (posix.isAbsolute(path)) {
+    return path;
+  }
+  if (win32.isAbsolute(path)) {
+    return windowsToPosixPath(path);
+  }
+
+  return joinFilePath(process.cwd(), path);
+}
+
 /**
  * Makes sure the input path has exactly 1 slash at the end.
  * Multiple slashes will get merged into one.


Test patch:
diff --git a/test/unit/init/CliRunner.test.ts b/test/unit/init/CliRunner.test.ts
index 78c7a7bb4..d0ad36315 100644
--- a/test/unit/init/CliRunner.test.ts
+++ b/test/unit/init/CliRunner.test.ts
@@ -104,7 +104,7 @@ describe('CliRunner', (): void => {
           'urn:solid-server:default:variable:loggingLevel': 'debug',
           'urn:solid-server:default:variable:podTemplateFolder': '/var/cwd/templates',
           'urn:solid-server:default:variable:port': 4000,
-          'urn:solid-server:default:variable:rootFilePath': '/var/cwd/root',
+          'urn:solid-server:default:variable:rootFilePath': '/root',
           'urn:solid-server:default:variable:sparqlEndpoint': 'http://localhost:5000/sparql',
         },
       },
diff --git a/test/unit/util/PathUtil.test.ts b/test/unit/util/PathUtil.test.ts
index 2e9b482fb..8d0a76839 100644
--- a/test/unit/util/PathUtil.test.ts
+++ b/test/unit/util/PathUtil.test.ts
@@ -1,4 +1,5 @@
 import {
+  absoluteFilePath,
   decodeUriPathComponents,
   encodeUriPathComponents,
   ensureTrailingSlash,
@@ -8,7 +9,7 @@ import {
 } from '../../../src/util/PathUtil';
 
 describe('PathUtil', (): void => {
-  describe('normalizeFilePath', (): void => {
+  describe('#normalizeFilePath', (): void => {
     it('normalizes POSIX paths.', async(): Promise<void> => {
       expect(normalizeFilePath('/foo/bar/../baz')).toEqual('/foo/baz');
     });
@@ -18,7 +19,7 @@ describe('PathUtil', (): void => {
     });
   });
 
-  describe('joinFilePath', (): void => {
+  describe('#joinFilePath', (): void => {
     it('joins POSIX paths.', async(): Promise<void> => {
       expect(joinFilePath('/foo/bar/', '..', '/baz')).toEqual('/foo/baz');
     });
@@ -28,6 +29,20 @@ describe('PathUtil', (): void => {
     });
   });
 
+  describe('#absoluteFilePath', (): void => {
+    it('does not change absolute posix paths.', async(): Promise<void> => {
+      expect(absoluteFilePath('/foo/bar/')).toEqual('/foo/bar/');
+    });
+
+    it('converts absolute win32 paths to posix paths.', async(): Promise<void> => {
+      expect(absoluteFilePath('C:\\foo\\bar')).toEqual('C:/foo/bar');
+    });
+
+    it('makes relative paths absolute.', async(): Promise<void> => {
+      expect(absoluteFilePath('foo/bar/')).toEqual(joinFilePath(process.cwd(), 'foo/bar/'));
+    });
+  });
+
   describe('#ensureTrailingSlash', (): void => {
     it('makes sure there is always exactly 1 slash.', async(): Promise<void> => {
       expect(ensureTrailingSlash('http://test.com')).toEqual('http://test.com/');