@inlang/sdk 0.11.0 → 0.12.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.
- package/dist/createNodeishFsWithAbsolutePaths.d.ts +13 -0
- package/dist/createNodeishFsWithAbsolutePaths.d.ts.map +1 -0
- package/dist/createNodeishFsWithAbsolutePaths.js +29 -0
- package/dist/createNodeishFsWithAbsolutePaths.test.d.ts.map +1 -0
- package/dist/createNodeishFsWithAbsolutePaths.test.js +37 -0
- package/dist/errors.d.ts +5 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +6 -0
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +11 -7
- package/dist/loadProject.test.js +12 -1
- package/package.json +1 -1
- package/src/createNodeishFsWithAbsolutePaths.test.ts +47 -0
- package/src/createNodeishFsWithAbsolutePaths.ts +40 -0
- package/src/errors.ts +7 -0
- package/src/loadProject.test.ts +16 -0
- package/src/loadProject.ts +18 -10
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.d.ts +0 -6
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.d.ts.map +0 -1
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.js +0 -20
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.test.d.ts.map +0 -1
- package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.test.js +0 -85
- package/src/resolve-modules/createNodeishFsWithAbsolutePaths.test.ts +0 -102
- package/src/resolve-modules/createNodeishFsWithAbsolutePaths.ts +0 -30
- /package/dist/{resolve-modules/createNodeishFsWithAbsolutePaths.test.d.ts → createNodeishFsWithAbsolutePaths.test.d.ts} +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NodeishFilesystemSubset } from "@inlang/plugin";
|
|
2
|
+
export declare const isAbsolutePath: (path: string) => boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Wraps the nodeish filesystem subset with a function that intercepts paths
|
|
5
|
+
* and prepends the base path.
|
|
6
|
+
*
|
|
7
|
+
* The paths are resolved from the `settingsFilePath` argument.
|
|
8
|
+
*/
|
|
9
|
+
export declare const createNodeishFsWithAbsolutePaths: (args: {
|
|
10
|
+
settingsFilePath: string;
|
|
11
|
+
nodeishFs: NodeishFilesystemSubset;
|
|
12
|
+
}) => NodeishFilesystemSubset;
|
|
13
|
+
//# sourceMappingURL=createNodeishFsWithAbsolutePaths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createNodeishFsWithAbsolutePaths.d.ts","sourceRoot":"","sources":["../src/createNodeishFsWithAbsolutePaths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AAG7D,eAAO,MAAM,cAAc,SAAU,MAAM,YAAwB,CAAA;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,SAAU;IACtD,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,uBAAuB,CAAA;CAClC,KAAG,uBAyBH,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { normalizePath } from "@lix-js/fs";
|
|
2
|
+
export const isAbsolutePath = (path) => /^[/\\]/.test(path);
|
|
3
|
+
/**
|
|
4
|
+
* Wraps the nodeish filesystem subset with a function that intercepts paths
|
|
5
|
+
* and prepends the base path.
|
|
6
|
+
*
|
|
7
|
+
* The paths are resolved from the `settingsFilePath` argument.
|
|
8
|
+
*/
|
|
9
|
+
export const createNodeishFsWithAbsolutePaths = (args) => {
|
|
10
|
+
if (!isAbsolutePath(args.settingsFilePath)) {
|
|
11
|
+
throw new Error("The argument `settingsFilePath` must be an absolute path.");
|
|
12
|
+
}
|
|
13
|
+
// get the base path of the settings file by
|
|
14
|
+
// removing the file name from the path
|
|
15
|
+
const bathPath = args.settingsFilePath.split("/").slice(0, -1).join("/");
|
|
16
|
+
const makeAbsolute = (path) => {
|
|
17
|
+
if (isAbsolutePath(path)) {
|
|
18
|
+
return path;
|
|
19
|
+
}
|
|
20
|
+
return normalizePath(bathPath + "/" + path);
|
|
21
|
+
};
|
|
22
|
+
return {
|
|
23
|
+
// @ts-expect-error
|
|
24
|
+
readFile: (path, options) => args.nodeishFs.readFile(makeAbsolute(path), options),
|
|
25
|
+
readdir: (path) => args.nodeishFs.readdir(makeAbsolute(path)),
|
|
26
|
+
mkdir: (path) => args.nodeishFs.mkdir(makeAbsolute(path)),
|
|
27
|
+
writeFile: (path, data) => args.nodeishFs.writeFile(makeAbsolute(path), data),
|
|
28
|
+
};
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createNodeishFsWithAbsolutePaths.test.d.ts","sourceRoot":"","sources":["../src/createNodeishFsWithAbsolutePaths.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { it, expect, vi } from "vitest";
|
|
2
|
+
import { createNodeishFsWithAbsolutePaths } from "./createNodeishFsWithAbsolutePaths.js";
|
|
3
|
+
it("throws an error if settingsFilePath is not an absolute path", () => {
|
|
4
|
+
const relativePath = "relative/path";
|
|
5
|
+
expect(() => createNodeishFsWithAbsolutePaths({ settingsFilePath: relativePath, nodeishFs: {} })).toThrow();
|
|
6
|
+
});
|
|
7
|
+
it("intercepts paths correctly for readFile", async () => {
|
|
8
|
+
const settingsFilePath = `/Users/samuel/Documents/paraglide/example/project.inlang.json`;
|
|
9
|
+
const filePaths = [
|
|
10
|
+
["file.txt", `/Users/samuel/Documents/paraglide/example/file.txt`],
|
|
11
|
+
["./file.txt", `/Users/samuel/Documents/paraglide/example/file.txt`],
|
|
12
|
+
["./folder/file.txt", `/Users/samuel/Documents/paraglide/example/folder/file.txt`],
|
|
13
|
+
["../file.txt", `/Users/samuel/Documents/paraglide/file.txt`],
|
|
14
|
+
["../folder/file.txt", `/Users/samuel/Documents/paraglide/folder/file.txt`],
|
|
15
|
+
["../../file.txt", `/Users/samuel/Documents/file.txt`],
|
|
16
|
+
["../../../file.txt", `/Users/samuel/file.txt`],
|
|
17
|
+
];
|
|
18
|
+
const mockNodeishFs = {
|
|
19
|
+
readFile: vi.fn(),
|
|
20
|
+
readdir: vi.fn(),
|
|
21
|
+
mkdir: vi.fn(),
|
|
22
|
+
writeFile: vi.fn(),
|
|
23
|
+
};
|
|
24
|
+
const interceptedFs = createNodeishFsWithAbsolutePaths({
|
|
25
|
+
settingsFilePath,
|
|
26
|
+
nodeishFs: mockNodeishFs,
|
|
27
|
+
});
|
|
28
|
+
for (const [path, expectedPath] of filePaths) {
|
|
29
|
+
for (const fn of Object.keys(mockNodeishFs)) {
|
|
30
|
+
// @ts-expect-error
|
|
31
|
+
await interceptedFs[fn](path);
|
|
32
|
+
// @ts-expect-error
|
|
33
|
+
// expect the first argument to be the expectedPath
|
|
34
|
+
expect(mockNodeishFs[fn].mock.lastCall[0]).toBe(expectedPath);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
package/dist/errors.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { ValueError } from "@sinclair/typebox/errors";
|
|
2
|
+
export declare class LoadProjectInvalidArgument extends Error {
|
|
3
|
+
constructor(message: string, options: {
|
|
4
|
+
argument: string;
|
|
5
|
+
});
|
|
6
|
+
}
|
|
2
7
|
export declare class ProjectSettingsInvalidError extends Error {
|
|
3
8
|
constructor(options: {
|
|
4
9
|
errors: ValueError[];
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE1D,qBAAa,2BAA4B,SAAQ,KAAK;gBACzC,OAAO,EAAE;QAAE,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE;CAQ7C;AAED,qBAAa,kCAAmC,SAAQ,KAAK;gBAChD,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CAOnE;AAED,qBAAa,gCAAiC,SAAQ,KAAK;gBAC9C,OAAO,EAAE;QAAE,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CAIpE;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;KAAE;CAIrD;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;KAAE;CAIrD"}
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE1D,qBAAa,0BAA2B,SAAQ,KAAK;gBACxC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE;CAI1D;AAED,qBAAa,2BAA4B,SAAQ,KAAK;gBACzC,OAAO,EAAE;QAAE,MAAM,EAAE,UAAU,EAAE,CAAA;KAAE;CAQ7C;AAED,qBAAa,kCAAmC,SAAQ,KAAK;gBAChD,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CAOnE;AAED,qBAAa,gCAAiC,SAAQ,KAAK;gBAC9C,OAAO,EAAE;QAAE,KAAK,CAAC,EAAE,YAAY,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE;CAIpE;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;KAAE;CAIrD;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE;QAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,CAAA;KAAE;CAIrD"}
|
package/dist/errors.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export class LoadProjectInvalidArgument extends Error {
|
|
2
|
+
constructor(message, options) {
|
|
3
|
+
super(`The argument "${options.argument}" of loadProject() is invalid: ${message}`);
|
|
4
|
+
this.name = "LoadProjectInvalidArgument";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
1
7
|
export class ProjectSettingsInvalidError extends Error {
|
|
2
8
|
constructor(options) {
|
|
3
9
|
super(`The project settings are invalid:\n\n${options.errors
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadProject.d.ts","sourceRoot":"","sources":["../src/loadProject.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,aAAa,EAGb,YAAY,EACZ,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,KAAK,cAAc,EAAkB,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"loadProject.d.ts","sourceRoot":"","sources":["../src/loadProject.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,aAAa,EAGb,YAAY,EACZ,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,KAAK,cAAc,EAAkB,MAAM,4BAA4B,CAAA;AAchF,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAUjG;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW;sBACL,MAAM;eACb,uBAAuB;;qBAElB,MAAM,SAAS,OAAO,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI;MAC5D,QAAQ,aAAa,CAkMxB,CAAA;AAqGD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAQtE"}
|
package/dist/loadProject.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveModules } from "./resolve-modules/index.js";
|
|
2
2
|
import { TypeCompiler } from "@sinclair/typebox/compiler";
|
|
3
|
-
import { ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, PluginLoadMessagesError, PluginSaveMessagesError, } from "./errors.js";
|
|
3
|
+
import { ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, PluginLoadMessagesError, PluginSaveMessagesError, LoadProjectInvalidArgument, } from "./errors.js";
|
|
4
4
|
import { createRoot, createSignal, createEffect } from "./reactivity/solid.js";
|
|
5
5
|
import { createMessagesQuery } from "./createMessagesQuery.js";
|
|
6
6
|
import { debounce } from "throttle-debounce";
|
|
@@ -8,7 +8,7 @@ import { createMessageLintReportsQuery } from "./createMessageLintReportsQuery.j
|
|
|
8
8
|
import { ProjectSettings, Message } from "./versionedInterfaces.js";
|
|
9
9
|
import { tryCatch } from "@inlang/result";
|
|
10
10
|
import { migrateIfOutdated } from "@inlang/project-settings/migration";
|
|
11
|
-
import { createNodeishFsWithAbsolutePaths } from "./
|
|
11
|
+
import { createNodeishFsWithAbsolutePaths, isAbsolutePath, } from "./createNodeishFsWithAbsolutePaths.js";
|
|
12
12
|
const settingsCompiler = TypeCompiler.Compile(ProjectSettings);
|
|
13
13
|
/**
|
|
14
14
|
* Creates an inlang instance.
|
|
@@ -21,13 +21,17 @@ const settingsCompiler = TypeCompiler.Compile(ProjectSettings);
|
|
|
21
21
|
*
|
|
22
22
|
*/
|
|
23
23
|
export const loadProject = async (args) => {
|
|
24
|
+
// -- validation --------------------------------------------------------
|
|
25
|
+
//! the only place where throwing is acceptable because the project
|
|
26
|
+
//! won't even be loaded. do not throw anywhere else. otherwise, apps
|
|
27
|
+
//! can't handle errors gracefully.
|
|
28
|
+
if (!isAbsolutePath(args.settingsFilePath)) {
|
|
29
|
+
throw new LoadProjectInvalidArgument(`Expected an absolute path but received "${args.settingsFilePath}".`, { argument: "settingsFilePath" });
|
|
30
|
+
}
|
|
31
|
+
// -- load project ------------------------------------------------------
|
|
24
32
|
return await createRoot(async () => {
|
|
25
33
|
const [initialized, markInitAsComplete, markInitAsFailed] = createAwaitable();
|
|
26
|
-
|
|
27
|
-
const nodeishFs = createNodeishFsWithAbsolutePaths({
|
|
28
|
-
nodeishFs: args.nodeishFs,
|
|
29
|
-
basePath: args.settingsFilePath.replace(/(.*?)[^/]*\..*$/, "$1"), // get directory of settings file
|
|
30
|
-
});
|
|
34
|
+
const nodeishFs = createNodeishFsWithAbsolutePaths(args);
|
|
31
35
|
// -- settings ------------------------------------------------------------
|
|
32
36
|
const [settings, _setSettings] = createSignal();
|
|
33
37
|
createEffect(() => {
|
package/dist/loadProject.test.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
2
|
import { describe, it, expect, vi } from "vitest";
|
|
3
3
|
import { loadProject } from "./loadProject.js";
|
|
4
|
-
import { ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, } from "./errors.js";
|
|
4
|
+
import { LoadProjectInvalidArgument, ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, } from "./errors.js";
|
|
5
5
|
import { createNodeishMemoryFs } from "@lix-js/fs";
|
|
6
6
|
import { createMessage } from "./test-utilities/createMessage.js";
|
|
7
|
+
import { tryCatch } from "@inlang/result";
|
|
7
8
|
// ------------------------------------------------------------------------------------------------
|
|
8
9
|
const getValue = (subscribable) => {
|
|
9
10
|
let value;
|
|
@@ -80,6 +81,16 @@ const _import = async (name) => ({
|
|
|
80
81
|
});
|
|
81
82
|
// ------------------------------------------------------------------------------------------------
|
|
82
83
|
describe("initialization", () => {
|
|
84
|
+
it("should throw if settingsFilePath is not an absolute path", async () => {
|
|
85
|
+
const fs = createNodeishMemoryFs();
|
|
86
|
+
const result = await tryCatch(() => loadProject({
|
|
87
|
+
settingsFilePath: "relative/path",
|
|
88
|
+
nodeishFs: fs,
|
|
89
|
+
_import,
|
|
90
|
+
}));
|
|
91
|
+
expect(result.error).toBeInstanceOf(LoadProjectInvalidArgument);
|
|
92
|
+
expect(result.data).toBeUndefined();
|
|
93
|
+
});
|
|
83
94
|
describe("settings", () => {
|
|
84
95
|
it("should return an error if settings file is not found", async () => {
|
|
85
96
|
const fs = createNodeishMemoryFs();
|
package/package.json
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { it, expect, vi } from "vitest"
|
|
2
|
+
import { createNodeishFsWithAbsolutePaths } from "./createNodeishFsWithAbsolutePaths.js"
|
|
3
|
+
import type { NodeishFilesystemSubset } from "./versionedInterfaces.js"
|
|
4
|
+
|
|
5
|
+
it("throws an error if settingsFilePath is not an absolute path", () => {
|
|
6
|
+
const relativePath = "relative/path"
|
|
7
|
+
|
|
8
|
+
expect(() =>
|
|
9
|
+
createNodeishFsWithAbsolutePaths({ settingsFilePath: relativePath, nodeishFs: {} as any })
|
|
10
|
+
).toThrow()
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it("intercepts paths correctly for readFile", async () => {
|
|
14
|
+
const settingsFilePath = `/Users/samuel/Documents/paraglide/example/project.inlang.json`
|
|
15
|
+
|
|
16
|
+
const filePaths = [
|
|
17
|
+
["file.txt", `/Users/samuel/Documents/paraglide/example/file.txt`],
|
|
18
|
+
["./file.txt", `/Users/samuel/Documents/paraglide/example/file.txt`],
|
|
19
|
+
["./folder/file.txt", `/Users/samuel/Documents/paraglide/example/folder/file.txt`],
|
|
20
|
+
["../file.txt", `/Users/samuel/Documents/paraglide/file.txt`],
|
|
21
|
+
["../folder/file.txt", `/Users/samuel/Documents/paraglide/folder/file.txt`],
|
|
22
|
+
["../../file.txt", `/Users/samuel/Documents/file.txt`],
|
|
23
|
+
["../../../file.txt", `/Users/samuel/file.txt`],
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
const mockNodeishFs = {
|
|
27
|
+
readFile: vi.fn(),
|
|
28
|
+
readdir: vi.fn(),
|
|
29
|
+
mkdir: vi.fn(),
|
|
30
|
+
writeFile: vi.fn(),
|
|
31
|
+
} satisfies Record<keyof NodeishFilesystemSubset, any>
|
|
32
|
+
|
|
33
|
+
const interceptedFs = createNodeishFsWithAbsolutePaths({
|
|
34
|
+
settingsFilePath,
|
|
35
|
+
nodeishFs: mockNodeishFs,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
for (const [path, expectedPath] of filePaths) {
|
|
39
|
+
for (const fn of Object.keys(mockNodeishFs)) {
|
|
40
|
+
// @ts-expect-error
|
|
41
|
+
await interceptedFs[fn](path)
|
|
42
|
+
// @ts-expect-error
|
|
43
|
+
// expect the first argument to be the expectedPath
|
|
44
|
+
expect(mockNodeishFs[fn].mock.lastCall[0]).toBe(expectedPath)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
})
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { NodeishFilesystemSubset } from "@inlang/plugin"
|
|
2
|
+
import { normalizePath } from "@lix-js/fs"
|
|
3
|
+
|
|
4
|
+
export const isAbsolutePath = (path: string) => /^[/\\]/.test(path)
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Wraps the nodeish filesystem subset with a function that intercepts paths
|
|
8
|
+
* and prepends the base path.
|
|
9
|
+
*
|
|
10
|
+
* The paths are resolved from the `settingsFilePath` argument.
|
|
11
|
+
*/
|
|
12
|
+
export const createNodeishFsWithAbsolutePaths = (args: {
|
|
13
|
+
settingsFilePath: string
|
|
14
|
+
nodeishFs: NodeishFilesystemSubset
|
|
15
|
+
}): NodeishFilesystemSubset => {
|
|
16
|
+
if (!isAbsolutePath(args.settingsFilePath)) {
|
|
17
|
+
throw new Error("The argument `settingsFilePath` must be an absolute path.")
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// get the base path of the settings file by
|
|
21
|
+
// removing the file name from the path
|
|
22
|
+
const bathPath = args.settingsFilePath.split("/").slice(0, -1).join("/")
|
|
23
|
+
|
|
24
|
+
const makeAbsolute = (path: string) => {
|
|
25
|
+
if (isAbsolutePath(path)) {
|
|
26
|
+
return path
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return normalizePath(bathPath + "/" + path)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
// @ts-expect-error
|
|
34
|
+
readFile: (path: string, options: { encoding: "utf-8" | "binary" }) =>
|
|
35
|
+
args.nodeishFs.readFile(makeAbsolute(path), options),
|
|
36
|
+
readdir: (path: string) => args.nodeishFs.readdir(makeAbsolute(path)),
|
|
37
|
+
mkdir: (path: string) => args.nodeishFs.mkdir(makeAbsolute(path)),
|
|
38
|
+
writeFile: (path: string, data: string) => args.nodeishFs.writeFile(makeAbsolute(path), data),
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/errors.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { ValueError } from "@sinclair/typebox/errors"
|
|
2
2
|
|
|
3
|
+
export class LoadProjectInvalidArgument extends Error {
|
|
4
|
+
constructor(message: string, options: { argument: string }) {
|
|
5
|
+
super(`The argument "${options.argument}" of loadProject() is invalid: ${message}`)
|
|
6
|
+
this.name = "LoadProjectInvalidArgument"
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
3
10
|
export class ProjectSettingsInvalidError extends Error {
|
|
4
11
|
constructor(options: { errors: ValueError[] }) {
|
|
5
12
|
super(
|
package/src/loadProject.test.ts
CHANGED
|
@@ -5,12 +5,14 @@ import type { ProjectSettings, Plugin, MessageLintRule, Message } from "./versio
|
|
|
5
5
|
import type { ImportFunction } from "./resolve-modules/index.js"
|
|
6
6
|
import type { InlangModule } from "@inlang/module"
|
|
7
7
|
import {
|
|
8
|
+
LoadProjectInvalidArgument,
|
|
8
9
|
ProjectSettingsFileJSONSyntaxError,
|
|
9
10
|
ProjectSettingsFileNotFoundError,
|
|
10
11
|
ProjectSettingsInvalidError,
|
|
11
12
|
} from "./errors.js"
|
|
12
13
|
import { createNodeishMemoryFs } from "@lix-js/fs"
|
|
13
14
|
import { createMessage } from "./test-utilities/createMessage.js"
|
|
15
|
+
import { tryCatch } from "@inlang/result"
|
|
14
16
|
|
|
15
17
|
// ------------------------------------------------------------------------------------------------
|
|
16
18
|
|
|
@@ -97,6 +99,20 @@ const _import: ImportFunction = async (name) =>
|
|
|
97
99
|
// ------------------------------------------------------------------------------------------------
|
|
98
100
|
|
|
99
101
|
describe("initialization", () => {
|
|
102
|
+
it("should throw if settingsFilePath is not an absolute path", async () => {
|
|
103
|
+
const fs = createNodeishMemoryFs()
|
|
104
|
+
|
|
105
|
+
const result = await tryCatch(() =>
|
|
106
|
+
loadProject({
|
|
107
|
+
settingsFilePath: "relative/path",
|
|
108
|
+
nodeishFs: fs,
|
|
109
|
+
_import,
|
|
110
|
+
})
|
|
111
|
+
)
|
|
112
|
+
expect(result.error).toBeInstanceOf(LoadProjectInvalidArgument)
|
|
113
|
+
expect(result.data).toBeUndefined()
|
|
114
|
+
})
|
|
115
|
+
|
|
100
116
|
describe("settings", () => {
|
|
101
117
|
it("should return an error if settings file is not found", async () => {
|
|
102
118
|
const fs = createNodeishMemoryFs()
|
package/src/loadProject.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
ProjectSettingsInvalidError,
|
|
14
14
|
PluginLoadMessagesError,
|
|
15
15
|
PluginSaveMessagesError,
|
|
16
|
+
LoadProjectInvalidArgument,
|
|
16
17
|
} from "./errors.js"
|
|
17
18
|
import { createRoot, createSignal, createEffect } from "./reactivity/solid.js"
|
|
18
19
|
import { createMessagesQuery } from "./createMessagesQuery.js"
|
|
@@ -21,7 +22,10 @@ import { createMessageLintReportsQuery } from "./createMessageLintReportsQuery.j
|
|
|
21
22
|
import { ProjectSettings, Message, type NodeishFilesystemSubset } from "./versionedInterfaces.js"
|
|
22
23
|
import { tryCatch, type Result } from "@inlang/result"
|
|
23
24
|
import { migrateIfOutdated } from "@inlang/project-settings/migration"
|
|
24
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
createNodeishFsWithAbsolutePaths,
|
|
27
|
+
isAbsolutePath,
|
|
28
|
+
} from "./createNodeishFsWithAbsolutePaths.js"
|
|
25
29
|
|
|
26
30
|
const settingsCompiler = TypeCompiler.Compile(ProjectSettings)
|
|
27
31
|
|
|
@@ -41,15 +45,21 @@ export const loadProject = async (args: {
|
|
|
41
45
|
_import?: ImportFunction
|
|
42
46
|
_capture?: (id: string, props: Record<string, unknown>) => void
|
|
43
47
|
}): Promise<InlangProject> => {
|
|
48
|
+
// -- validation --------------------------------------------------------
|
|
49
|
+
//! the only place where throwing is acceptable because the project
|
|
50
|
+
//! won't even be loaded. do not throw anywhere else. otherwise, apps
|
|
51
|
+
//! can't handle errors gracefully.
|
|
52
|
+
if (!isAbsolutePath(args.settingsFilePath)) {
|
|
53
|
+
throw new LoadProjectInvalidArgument(
|
|
54
|
+
`Expected an absolute path but received "${args.settingsFilePath}".`,
|
|
55
|
+
{ argument: "settingsFilePath" }
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// -- load project ------------------------------------------------------
|
|
44
60
|
return await createRoot(async () => {
|
|
45
61
|
const [initialized, markInitAsComplete, markInitAsFailed] = createAwaitable()
|
|
46
|
-
|
|
47
|
-
// -- absolute path env -----------------------------------------------
|
|
48
|
-
|
|
49
|
-
const nodeishFs = createNodeishFsWithAbsolutePaths({
|
|
50
|
-
nodeishFs: args.nodeishFs,
|
|
51
|
-
basePath: args.settingsFilePath.replace(/(.*?)[^/]*\..*$/, "$1"), // get directory of settings file
|
|
52
|
-
})
|
|
62
|
+
const nodeishFs = createNodeishFsWithAbsolutePaths(args)
|
|
53
63
|
|
|
54
64
|
// -- settings ------------------------------------------------------------
|
|
55
65
|
|
|
@@ -338,5 +348,3 @@ export function createSubscribable<T>(signal: () => T): Subscribable<T> {
|
|
|
338
348
|
},
|
|
339
349
|
})
|
|
340
350
|
}
|
|
341
|
-
|
|
342
|
-
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { NodeishFilesystemSubset } from "@inlang/plugin";
|
|
2
|
-
export declare const createNodeishFsWithAbsolutePaths: (args: {
|
|
3
|
-
basePath: string;
|
|
4
|
-
nodeishFs: NodeishFilesystemSubset;
|
|
5
|
-
}) => NodeishFilesystemSubset;
|
|
6
|
-
//# sourceMappingURL=createNodeishFsWithAbsolutePaths.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createNodeishFsWithAbsolutePaths.d.ts","sourceRoot":"","sources":["../../src/resolve-modules/createNodeishFsWithAbsolutePaths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAA;AAG7D,eAAO,MAAM,gCAAgC,SAAU;IACtD,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,uBAAuB,CAAA;CAClC,KAAG,uBAuBH,CAAA"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { normalizePath } from "@lix-js/fs";
|
|
2
|
-
export const createNodeishFsWithAbsolutePaths = (args) => {
|
|
3
|
-
const isAbsolutePath = (path) => /^[/\\]/.test(path);
|
|
4
|
-
if (!isAbsolutePath(args.basePath)) {
|
|
5
|
-
throw new Error("The argument `settingsFilePath` of `loadProject()` must be an absolute path.");
|
|
6
|
-
}
|
|
7
|
-
const intercept = (path) => {
|
|
8
|
-
if (isAbsolutePath(path)) {
|
|
9
|
-
return path;
|
|
10
|
-
}
|
|
11
|
-
return normalizePath(args.basePath + "/" + path);
|
|
12
|
-
};
|
|
13
|
-
return {
|
|
14
|
-
// @ts-expect-error
|
|
15
|
-
readFile: (path, options) => args.nodeishFs.readFile(intercept(path), options),
|
|
16
|
-
readdir: (path) => args.nodeishFs.readdir(intercept(path)),
|
|
17
|
-
mkdir: (path) => args.nodeishFs.mkdir(intercept(path)),
|
|
18
|
-
writeFile: (path, data) => args.nodeishFs.writeFile(intercept(path), data),
|
|
19
|
-
};
|
|
20
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createNodeishFsWithAbsolutePaths.test.d.ts","sourceRoot":"","sources":["../../src/resolve-modules/createNodeishFsWithAbsolutePaths.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { createNodeishFsWithAbsolutePaths } from "./createNodeishFsWithAbsolutePaths.js";
|
|
3
|
-
describe("createNodeishFsWithAbsolutePaths", () => {
|
|
4
|
-
it("throws an error if basePath is not an absolute path", () => {
|
|
5
|
-
const invalidBasePath = "relative/path";
|
|
6
|
-
const nodeishFs = {
|
|
7
|
-
// @ts-expect-error
|
|
8
|
-
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
9
|
-
readdir: async () => Promise.resolve([]),
|
|
10
|
-
mkdir: async () => Promise.resolve(""),
|
|
11
|
-
writeFile: async () => Promise.resolve(),
|
|
12
|
-
};
|
|
13
|
-
expect(() => createNodeishFsWithAbsolutePaths({ basePath: invalidBasePath, nodeishFs })).toThrowError("The argument `settingsFilePath` of `loadProject()` must be an absolute path.");
|
|
14
|
-
});
|
|
15
|
-
it("intercepts paths correctly for readFile", async () => {
|
|
16
|
-
const basePath = "/absolute/path";
|
|
17
|
-
const filePath = "file.txt";
|
|
18
|
-
const expectedPath = "/absolute/path/file.txt";
|
|
19
|
-
const nodeishFs = {
|
|
20
|
-
// @ts-expect-error
|
|
21
|
-
readFile: async (path) => {
|
|
22
|
-
expect(path).toBe(expectedPath);
|
|
23
|
-
return Promise.resolve(new Uint8Array(0));
|
|
24
|
-
},
|
|
25
|
-
readdir: async () => Promise.resolve([]),
|
|
26
|
-
mkdir: async () => Promise.resolve(""),
|
|
27
|
-
writeFile: async () => Promise.resolve(),
|
|
28
|
-
};
|
|
29
|
-
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs });
|
|
30
|
-
await interceptedFs.readFile(filePath, { encoding: "utf-8" });
|
|
31
|
-
});
|
|
32
|
-
it("intercepts paths correctly for readdir", async () => {
|
|
33
|
-
const basePath = "/absolute/path";
|
|
34
|
-
const dirPath = "dir";
|
|
35
|
-
const expectedPath = "/absolute/path/dir";
|
|
36
|
-
const nodeishFs = {
|
|
37
|
-
// @ts-expect-error
|
|
38
|
-
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
39
|
-
readdir: async (path) => {
|
|
40
|
-
expect(path).toBe(expectedPath);
|
|
41
|
-
return Promise.resolve([]);
|
|
42
|
-
},
|
|
43
|
-
mkdir: async () => Promise.resolve(""),
|
|
44
|
-
writeFile: async () => Promise.resolve(),
|
|
45
|
-
};
|
|
46
|
-
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs });
|
|
47
|
-
await interceptedFs.readdir(dirPath);
|
|
48
|
-
});
|
|
49
|
-
it("intercepts paths correctly for mkdir", async () => {
|
|
50
|
-
const basePath = "/absolute/path";
|
|
51
|
-
const dirPath = "newDir";
|
|
52
|
-
const expectedPath = "/absolute/path/newDir";
|
|
53
|
-
const nodeishFs = {
|
|
54
|
-
// @ts-expect-error
|
|
55
|
-
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
56
|
-
readdir: async () => Promise.resolve([]),
|
|
57
|
-
mkdir: async (path) => {
|
|
58
|
-
expect(path).toBe(expectedPath);
|
|
59
|
-
return Promise.resolve("");
|
|
60
|
-
},
|
|
61
|
-
writeFile: async () => Promise.resolve(),
|
|
62
|
-
};
|
|
63
|
-
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs });
|
|
64
|
-
await interceptedFs.mkdir(dirPath);
|
|
65
|
-
});
|
|
66
|
-
it("intercepts paths correctly for writeFile", async () => {
|
|
67
|
-
const basePath = "/absolute/path";
|
|
68
|
-
const filePath = "file.txt";
|
|
69
|
-
const expectedPath = "/absolute/path/file.txt";
|
|
70
|
-
const data = "Hello, World!";
|
|
71
|
-
const nodeishFs = {
|
|
72
|
-
// @ts-expect-error
|
|
73
|
-
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
74
|
-
readdir: async () => Promise.resolve([]),
|
|
75
|
-
mkdir: async () => Promise.resolve(""),
|
|
76
|
-
writeFile: async (path, content) => {
|
|
77
|
-
expect(path).toBe(expectedPath);
|
|
78
|
-
expect(content).toBe(data);
|
|
79
|
-
return Promise.resolve();
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs });
|
|
83
|
-
await interceptedFs.writeFile(filePath, data);
|
|
84
|
-
});
|
|
85
|
-
});
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import type { NodeishFilesystemSubset } from "@inlang/plugin"
|
|
2
|
-
import { describe, it, expect } from "vitest"
|
|
3
|
-
import { createNodeishFsWithAbsolutePaths } from "./createNodeishFsWithAbsolutePaths.js"
|
|
4
|
-
|
|
5
|
-
describe("createNodeishFsWithAbsolutePaths", () => {
|
|
6
|
-
it("throws an error if basePath is not an absolute path", () => {
|
|
7
|
-
const invalidBasePath = "relative/path"
|
|
8
|
-
const nodeishFs: NodeishFilesystemSubset = {
|
|
9
|
-
// @ts-expect-error
|
|
10
|
-
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
11
|
-
readdir: async () => Promise.resolve([]),
|
|
12
|
-
mkdir: async () => Promise.resolve(""),
|
|
13
|
-
writeFile: async () => Promise.resolve(),
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
expect(() =>
|
|
17
|
-
createNodeishFsWithAbsolutePaths({ basePath: invalidBasePath, nodeishFs })
|
|
18
|
-
).toThrowError("The argument `settingsFilePath` of `loadProject()` must be an absolute path.")
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
it("intercepts paths correctly for readFile", async () => {
|
|
22
|
-
const basePath = "/absolute/path"
|
|
23
|
-
const filePath = "file.txt"
|
|
24
|
-
const expectedPath = "/absolute/path/file.txt"
|
|
25
|
-
|
|
26
|
-
const nodeishFs: NodeishFilesystemSubset = {
|
|
27
|
-
// @ts-expect-error
|
|
28
|
-
readFile: async (path) => {
|
|
29
|
-
expect(path).toBe(expectedPath)
|
|
30
|
-
return Promise.resolve(new Uint8Array(0))
|
|
31
|
-
},
|
|
32
|
-
readdir: async () => Promise.resolve([]),
|
|
33
|
-
mkdir: async () => Promise.resolve(""),
|
|
34
|
-
writeFile: async () => Promise.resolve(),
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs })
|
|
38
|
-
await interceptedFs.readFile(filePath, { encoding: "utf-8" })
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it("intercepts paths correctly for readdir", async () => {
|
|
42
|
-
const basePath = "/absolute/path"
|
|
43
|
-
const dirPath = "dir"
|
|
44
|
-
const expectedPath = "/absolute/path/dir"
|
|
45
|
-
|
|
46
|
-
const nodeishFs: NodeishFilesystemSubset = {
|
|
47
|
-
// @ts-expect-error
|
|
48
|
-
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
49
|
-
readdir: async (path) => {
|
|
50
|
-
expect(path).toBe(expectedPath)
|
|
51
|
-
return Promise.resolve([])
|
|
52
|
-
},
|
|
53
|
-
mkdir: async () => Promise.resolve(""),
|
|
54
|
-
writeFile: async () => Promise.resolve(),
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs })
|
|
58
|
-
await interceptedFs.readdir(dirPath)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it("intercepts paths correctly for mkdir", async () => {
|
|
62
|
-
const basePath = "/absolute/path"
|
|
63
|
-
const dirPath = "newDir"
|
|
64
|
-
const expectedPath = "/absolute/path/newDir"
|
|
65
|
-
|
|
66
|
-
const nodeishFs: NodeishFilesystemSubset = {
|
|
67
|
-
// @ts-expect-error
|
|
68
|
-
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
69
|
-
readdir: async () => Promise.resolve([]),
|
|
70
|
-
mkdir: async (path) => {
|
|
71
|
-
expect(path).toBe(expectedPath)
|
|
72
|
-
return Promise.resolve("")
|
|
73
|
-
},
|
|
74
|
-
writeFile: async () => Promise.resolve(),
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs })
|
|
78
|
-
await interceptedFs.mkdir(dirPath)
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it("intercepts paths correctly for writeFile", async () => {
|
|
82
|
-
const basePath = "/absolute/path"
|
|
83
|
-
const filePath = "file.txt"
|
|
84
|
-
const expectedPath = "/absolute/path/file.txt"
|
|
85
|
-
const data = "Hello, World!"
|
|
86
|
-
|
|
87
|
-
const nodeishFs: NodeishFilesystemSubset = {
|
|
88
|
-
// @ts-expect-error
|
|
89
|
-
readFile: async () => Promise.resolve(new Uint8Array(0)),
|
|
90
|
-
readdir: async () => Promise.resolve([]),
|
|
91
|
-
mkdir: async () => Promise.resolve(""),
|
|
92
|
-
writeFile: async (path, content) => {
|
|
93
|
-
expect(path).toBe(expectedPath)
|
|
94
|
-
expect(content).toBe(data)
|
|
95
|
-
return Promise.resolve()
|
|
96
|
-
},
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const interceptedFs = createNodeishFsWithAbsolutePaths({ basePath, nodeishFs })
|
|
100
|
-
await interceptedFs.writeFile(filePath, data)
|
|
101
|
-
})
|
|
102
|
-
})
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { NodeishFilesystemSubset } from "@inlang/plugin"
|
|
2
|
-
import { normalizePath } from "@lix-js/fs"
|
|
3
|
-
|
|
4
|
-
export const createNodeishFsWithAbsolutePaths = (args: {
|
|
5
|
-
basePath: string
|
|
6
|
-
nodeishFs: NodeishFilesystemSubset
|
|
7
|
-
}): NodeishFilesystemSubset => {
|
|
8
|
-
const isAbsolutePath = (path: string) => /^[/\\]/.test(path)
|
|
9
|
-
|
|
10
|
-
if (!isAbsolutePath(args.basePath)) {
|
|
11
|
-
throw new Error("The argument `settingsFilePath` of `loadProject()` must be an absolute path.")
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const intercept = (path: string) => {
|
|
15
|
-
if (isAbsolutePath(path)) {
|
|
16
|
-
return path
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
return normalizePath(args.basePath + "/" + path)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return {
|
|
23
|
-
// @ts-expect-error
|
|
24
|
-
readFile: (path: string, options: { encoding: "utf-8" | "binary" }) =>
|
|
25
|
-
args.nodeishFs.readFile(intercept(path), options),
|
|
26
|
-
readdir: (path: string) => args.nodeishFs.readdir(intercept(path)),
|
|
27
|
-
mkdir: (path: string) => args.nodeishFs.mkdir(intercept(path)),
|
|
28
|
-
writeFile: (path: string, data: string) => args.nodeishFs.writeFile(intercept(path), data),
|
|
29
|
-
}
|
|
30
|
-
}
|