@inlang/sdk 0.36.1 → 0.36.3

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 (51) hide show
  1. package/dist/adapter/solidAdapter.d.ts.map +1 -1
  2. package/dist/createMessagesQuery.js +1 -1
  3. package/dist/createNewProject.d.ts.map +1 -1
  4. package/dist/createNewProject.js +19 -2
  5. package/dist/createNodeishFsWithWatcher.d.ts +1 -1
  6. package/dist/createNodeishFsWithWatcher.d.ts.map +1 -1
  7. package/dist/createNodeishFsWithWatcher.js +54 -45
  8. package/dist/createNodeishFsWithWatcher.test.js +1 -1
  9. package/dist/lint/message/lintMessages.d.ts.map +1 -1
  10. package/dist/lint/message/lintSingleMessage.d.ts.map +1 -1
  11. package/dist/listProjects.d.ts.map +1 -1
  12. package/dist/loadProject.d.ts.map +1 -1
  13. package/dist/loadProject.js +56 -50
  14. package/dist/loadProject.test.js +8 -13
  15. package/dist/migrations/maybeAddModuleCache.d.ts.map +1 -1
  16. package/dist/migrations/maybeAddModuleCache.js +24 -6
  17. package/dist/parseConfig.d.ts.map +1 -1
  18. package/dist/reactivity/map.d.ts +1 -0
  19. package/dist/reactivity/map.d.ts.map +1 -1
  20. package/dist/reactivity/solid.d.ts +11 -11
  21. package/dist/reactivity/solid.d.ts.map +1 -1
  22. package/dist/resolve-modules/cache.d.ts.map +1 -1
  23. package/dist/resolve-modules/cache.js +24 -16
  24. package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts +1 -1
  25. package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts.map +1 -1
  26. package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.js +1 -0
  27. package/dist/storage/helper.d.ts +1 -19
  28. package/dist/storage/helper.d.ts.map +1 -1
  29. package/dist/telemetry/capture.d.ts.map +1 -1
  30. package/dist/telemetry/groupIdentify.d.ts.map +1 -1
  31. package/dist/test-utilities/createMessage.d.ts.map +1 -1
  32. package/dist/v2/helper.d.ts +1 -89
  33. package/dist/v2/helper.d.ts.map +1 -1
  34. package/dist/validateProjectPath.d.ts +2 -8
  35. package/dist/validateProjectPath.d.ts.map +1 -1
  36. package/dist/validateProjectPath.js +0 -21
  37. package/dist/validateProjectPath.test.js +2 -18
  38. package/package.json +8 -8
  39. package/src/createMessagesQuery.ts +1 -1
  40. package/src/createNewProject.ts +19 -2
  41. package/src/createNodeishFsWithWatcher.test.ts +1 -1
  42. package/src/createNodeishFsWithWatcher.ts +53 -42
  43. package/src/loadProject.test.ts +11 -18
  44. package/src/loadProject.ts +69 -58
  45. package/src/migrations/maybeAddModuleCache.ts +21 -6
  46. package/src/persistence/filelock/acquireFileLock.ts +2 -2
  47. package/src/persistence/filelock/releaseLock.ts +1 -1
  48. package/src/resolve-modules/cache.ts +30 -14
  49. package/src/resolve-modules/message-lint-rules/resolveMessageLintRules.ts +1 -0
  50. package/src/validateProjectPath.test.ts +1 -19
  51. package/src/validateProjectPath.ts +4 -24
@@ -3,7 +3,7 @@ import { MessageLintRuleIsInvalidError } from "./errors.js";
3
3
  export declare const resolveMessageLintRules: (args: {
4
4
  messageLintRules: Array<MessageLintRule>;
5
5
  }) => {
6
- data: MessageLintRule[];
6
+ data: Array<MessageLintRule>;
7
7
  errors: MessageLintRuleIsInvalidError[];
8
8
  };
9
9
  //# sourceMappingURL=resolveMessageLintRules.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolveMessageLintRules.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/message-lint-rules/resolveMessageLintRules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAA;AAE3D,eAAO,MAAM,uBAAuB,SAAU;IAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC,CAAA;CAAE;;;CAqBzF,CAAA"}
1
+ {"version":3,"file":"resolveMessageLintRules.d.ts","sourceRoot":"","sources":["../../../src/resolve-modules/message-lint-rules/resolveMessageLintRules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAA;AAC3D,OAAO,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAA;AAE3D,eAAO,MAAM,uBAAuB,SAAU;IAAE,gBAAgB,EAAE,KAAK,CAAC,eAAe,CAAC,CAAA;CAAE;UAE5E,KAAK,CAAC,eAAe,CAAC;YACpB,6BAA6B,EAAE;CAmB9C,CAAA"}
@@ -10,6 +10,7 @@ export const resolveMessageLintRules = (args) => {
10
10
  if (Value.Check(MessageLintRule, rule) === false) {
11
11
  const errors = [...Value.Errors(MessageLintRule, rule)];
12
12
  result.errors.push(new MessageLintRuleIsInvalidError({
13
+ // @ts-ignore
13
14
  id: rule.id,
14
15
  errors,
15
16
  }));
@@ -6,24 +6,6 @@ export declare function getPathFromMessageId(id: string): string;
6
6
  * This produces a deterministic result when passed to stringify
7
7
  * independent of the initialization order.
8
8
  */
9
- export declare function normalizeMessage(message: Message): {
10
- id: string;
11
- alias: Record<string, string>;
12
- selectors: {
13
- type: "VariableReference";
14
- name: string;
15
- }[];
16
- variants: {
17
- languageTag: string;
18
- match: string[];
19
- pattern: ({
20
- type: "Text";
21
- value: string;
22
- } | {
23
- type: "VariableReference";
24
- name: string;
25
- })[];
26
- }[];
27
- };
9
+ export declare function normalizeMessage(message: Message): Message;
28
10
  export declare function stringifyMessage(message: Message): string;
29
11
  //# sourceMappingURL=helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../src/storage/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAW,MAAM,2BAA2B,CAAA;AAI5D,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,sBAahD;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,UAG9C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO;;;;;;;;;;;;;;;;;;EAwChD;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,UAEhD"}
1
+ {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../src/storage/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAW,MAAM,2BAA2B,CAAA;AAI5D,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,sBAahD;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,MAAM,UAG9C;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAuChB,OAAO,CACvC;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,UAEhD"}
@@ -1 +1 @@
1
- {"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/telemetry/capture.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,QAAA,MAAM,MAAM,iCAAkC,CAAA;AAE9C;;;;GAIG;AACH,eAAO,MAAM,OAAO,UACZ,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,QACxB;IACL,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,UAAU,EAAE,OAAO,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B,kBAyBD,CAAA"}
1
+ {"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/telemetry/capture.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,QAAA,MAAM,MAAM,iCAAkC,CAAA;AAE9C;;;;GAIG;AACH,eAAO,MAAM,OAAO,UACZ,CAAC,OAAO,MAAM,EAAE,MAAM,CAAC,QACxB;IACL,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B,kBAyBD,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"groupIdentify.d.ts","sourceRoot":"","sources":["../../src/telemetry/groupIdentify.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,eAAe,SAAgB;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,UAAU,EAAE,OAAO,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B,kBA2BA,CAAA"}
1
+ {"version":3,"file":"groupIdentify.d.ts","sourceRoot":"","sources":["../../src/telemetry/groupIdentify.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,eAAe,SAAgB;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B,kBA2BA,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"createMessage.d.ts","sourceRoot":"","sources":["../../src/test-utilities/createMessage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAEvD,eAAO,MAAM,aAAa,OAAQ,MAAM,YAAY,OAAO,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;;;;;;;;;;;;;;;CAkB/D,CAAA"}
1
+ {"version":3,"file":"createMessage.d.ts","sourceRoot":"","sources":["../../src/test-utilities/createMessage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAW,OAAO,EAAE,MAAM,iBAAiB,CAAA;AAEvD,eAAO,MAAM,aAAa,OAAQ,MAAM,YAAY,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;;;;;;;;;;;;;;;CAkB/D,CAAA"}
@@ -35,95 +35,7 @@ export declare function addSlots(messageBundle: MessageBundle, locales: string[]
35
35
  /**
36
36
  * remove empty message slots without first creating a structured clone
37
37
  */
38
- export declare function removeSlots(messageBundle: MessageBundleWithSlots): {
39
- id: string;
40
- alias: Record<string, string>;
41
- messages: {
42
- selectors: {
43
- annotation?: {
44
- type: "function";
45
- name: string;
46
- options: {
47
- name: string;
48
- value: {
49
- type: "literal";
50
- value: string;
51
- } | {
52
- type: "variable";
53
- name: string;
54
- };
55
- }[];
56
- } | undefined;
57
- type: "expression";
58
- arg: {
59
- type: "literal";
60
- value: string;
61
- } | {
62
- type: "variable";
63
- name: string;
64
- };
65
- }[];
66
- variants: {
67
- match: string[];
68
- pattern: ({
69
- type: "text";
70
- value: string;
71
- } | {
72
- annotation?: {
73
- type: "function";
74
- name: string;
75
- options: {
76
- name: string;
77
- value: {
78
- type: "literal";
79
- value: string;
80
- } | {
81
- type: "variable";
82
- name: string;
83
- };
84
- }[];
85
- } | undefined;
86
- type: "expression";
87
- arg: {
88
- type: "literal";
89
- value: string;
90
- } | {
91
- type: "variable";
92
- name: string;
93
- };
94
- })[];
95
- }[];
96
- locale: string;
97
- declarations: {
98
- type: "input";
99
- name: string;
100
- value: {
101
- annotation?: {
102
- type: "function";
103
- name: string;
104
- options: {
105
- name: string;
106
- value: {
107
- type: "literal";
108
- value: string;
109
- } | {
110
- type: "variable";
111
- name: string;
112
- };
113
- }[];
114
- } | undefined;
115
- type: "expression";
116
- arg: {
117
- type: "literal";
118
- value: string;
119
- } | {
120
- type: "variable";
121
- name: string;
122
- };
123
- };
124
- }[];
125
- }[];
126
- };
38
+ export declare function removeSlots(messageBundle: MessageBundleWithSlots): MessageBundle;
127
39
  /**
128
40
  * Add newlines between bundles and messages to avoid merge conflicts
129
41
  */
@@ -1 +1 @@
1
- {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../src/v2/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,WAAW,EACX,aAAa,EACb,sBAAsB,EACtB,OAAO,EACP,WAAW,EACX,IAAI,EACJ,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACzC,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;CAC9B,GAAG,aAAa,CAMhB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE;IACnC,MAAM,EAAE,WAAW,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CACrB,GAAG,OAAO,CAOV;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAKhD;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAKlE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,sBAAsB,CAMhG;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGhE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMvD"}
1
+ {"version":3,"file":"helper.d.ts","sourceRoot":"","sources":["../../src/v2/helper.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,WAAW,EACX,aAAa,EACb,sBAAsB,EACtB,OAAO,EACP,WAAW,EACX,IAAI,EACJ,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACzC,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,OAAO,EAAE,CAAA;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;CAC9B,GAAG,aAAa,CAMhB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE;IACnC,MAAM,EAAE,WAAW,CAAA;IACnB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CACrB,GAAG,OAAO,CAOV;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAKhD;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW,CAKlE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,aAAa,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,sBAAsB,CAMhG;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,sBAAsB,GAExC,aAAa,CACrC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMvD"}
@@ -1,23 +1,17 @@
1
- import type { NodeishFilesystem } from "@lix-js/fs";
2
1
  /**
3
2
  * validate that a project path is absolute and ends with {name}.inlang.
4
3
  *
5
4
  * @throws if the path is not valid.
6
5
  */
7
- export declare function assertValidProjectPath(projectPath: string): void;
6
+ export declare function assertValidProjectPath(projectPath: string): asserts projectPath is `${string}.inlang`;
8
7
  /**
9
8
  * tests whether a path ends with {name}.inlang
10
9
  * (does not remove trailing slash)
11
10
  */
12
- export declare function isInlangProjectPath(path: string): boolean;
11
+ export declare function isInlangProjectPath(path: string): path is `${string}.inlang`;
13
12
  /**
14
13
  * tests whether a path starts with a forward slash (/) or a windows-style
15
14
  * drive letter (C: or D:, etc.) followed by a slash
16
15
  */
17
16
  export declare function isAbsolutePath(path: string): boolean;
18
- /**
19
- * Returns true if the path exists (file or directory), false otherwise.
20
- *
21
- */
22
- export declare function pathExists(filePath: string, nodeishFs: NodeishFilesystem): Promise<boolean>;
23
17
  //# sourceMappingURL=validateProjectPath.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validateProjectPath.d.ts","sourceRoot":"","sources":["../src/validateProjectPath.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,QASzD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,WAE/C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,WAO1C;AAED;;;GAGG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,oBAc9E"}
1
+ {"version":3,"file":"validateProjectPath.d.ts","sourceRoot":"","sources":["../src/validateProjectPath.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,sBAAsB,CACrC,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,WAAW,IAAI,GAAG,MAAM,SAAS,CAS3C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,GAAG,MAAM,SAAS,CAE5E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,WAO1C"}
@@ -29,24 +29,3 @@ export function isAbsolutePath(path) {
29
29
  // /^(?:[A-Za-z]:\\(?:[^\\]+\\)*[^\\]+|[A-Za-z]:\/(?:[^/]+\/)*[^/]+|\/(?:[^/]+\/)*[^/]+)$/
30
30
  // return matchPosixAndWindowsAbsolutePaths.test(path)
31
31
  }
32
- /**
33
- * Returns true if the path exists (file or directory), false otherwise.
34
- *
35
- */
36
- export async function pathExists(filePath, nodeishFs) {
37
- // from paraglide-js/src/services/file-handling/exists.ts
38
- // TODO: add fs.exists to @lix-js/fs
39
- try {
40
- await nodeishFs.stat(filePath);
41
- return true;
42
- }
43
- catch (error) {
44
- //@ts-ignore
45
- if (error.code === "ENOENT") {
46
- return false;
47
- }
48
- else {
49
- throw new Error(`Failed to check if path exists: ${error}`, { cause: error });
50
- }
51
- }
52
- }
@@ -1,6 +1,5 @@
1
- import { assert, describe, expect, it } from "vitest";
2
- import { assertValidProjectPath, isAbsolutePath, isInlangProjectPath, pathExists, } from "./validateProjectPath.js";
3
- import { mockRepo } from "@lix-js/client";
1
+ import { assert, describe, it } from "vitest";
2
+ import { assertValidProjectPath, isAbsolutePath, isInlangProjectPath, } from "./validateProjectPath.js";
4
3
  describe("isAbsolutePath", () => {
5
4
  it("should correctly identify Unix absolute paths", () => {
6
5
  assert.isTrue(isAbsolutePath("/home/user/documents/file.txt"));
@@ -39,18 +38,3 @@ describe("assertValidProjectPath", () => {
39
38
  assert.throws(() => assertValidProjectPath("/path/to/green-elephant.inlang/settings.json"));
40
39
  });
41
40
  });
42
- // moar tests in paraglide-js/src/services/file-handling/exists.test.ts
43
- describe("pathExists", () => {
44
- it("should work for files", async () => {
45
- const repo = await mockRepo();
46
- await repo.nodeishFs.writeFile("/test.txt", "hello");
47
- expect(await pathExists("/test.txt", repo.nodeishFs)).toBe(true);
48
- expect(await pathExists("/does-not-exist.txt", repo.nodeishFs)).toBe(false);
49
- });
50
- it("should work for directories", async () => {
51
- const repo = await mockRepo();
52
- await repo.nodeishFs.mkdir("/test/project.inlang", { recursive: true });
53
- expect(await pathExists("/test/project.inlang", repo.nodeishFs)).toBe(true);
54
- expect(await pathExists("/test/white-gorilla.inlang", repo.nodeishFs)).toBe(false);
55
- });
56
- });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@inlang/sdk",
3
3
  "type": "module",
4
- "version": "0.36.1",
4
+ "version": "0.36.3",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
7
7
  "access": "public"
@@ -35,16 +35,16 @@
35
35
  "solid-js": "1.6.12",
36
36
  "throttle-debounce": "^5.0.0",
37
37
  "@inlang/json-types": "1.1.0",
38
- "@inlang/language-tag": "1.5.1",
39
38
  "@inlang/message": "2.1.0",
40
- "@inlang/module": "1.2.13",
41
39
  "@inlang/message-lint-rule": "1.4.7",
42
- "@inlang/plugin": "2.4.13",
40
+ "@inlang/language-tag": "1.5.1",
41
+ "@inlang/module": "1.2.14",
42
+ "@inlang/plugin": "2.4.14",
43
43
  "@inlang/project-settings": "2.4.2",
44
- "@lix-js/client": "2.2.0",
45
- "@lix-js/fs": "2.1.0",
44
+ "@inlang/translatable": "1.3.1",
45
+ "@lix-js/client": "2.2.1",
46
46
  "@inlang/result": "1.1.0",
47
- "@inlang/translatable": "1.3.1"
47
+ "@lix-js/fs": "2.2.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/debug": "^4.1.12",
@@ -54,7 +54,7 @@
54
54
  "jsdom": "22.1.0",
55
55
  "patch-package": "6.5.1",
56
56
  "tsd": "^0.25.0",
57
- "typescript": "5.2.2",
57
+ "typescript": "^5.5.2",
58
58
  "vitest": "^0.33.0"
59
59
  },
60
60
  "scripts": {
@@ -107,7 +107,7 @@ export function createMessagesQuery({
107
107
  nodeishFs: nodeishFs,
108
108
  // this message is called whenever a file changes that was read earlier by this filesystem
109
109
  // - the plugin loads messages -> reads the file messages.json -> start watching on messages.json -> updateMessages
110
- updateMessages: () => {
110
+ onChange: () => {
111
111
  // reload
112
112
  loadMessagesViaPlugin(
113
113
  fsWithWatcher,
@@ -1,6 +1,7 @@
1
1
  import type { Repository } from "@lix-js/client"
2
+ import type { NodeishFilesystem } from "@lix-js/fs"
2
3
  import { ProjectSettings } from "@inlang/project-settings"
3
- import { assertValidProjectPath, pathExists } from "./validateProjectPath.js"
4
+ import { assertValidProjectPath } from "./validateProjectPath.js"
4
5
  import { defaultProjectSettings } from "./defaultProjectSettings.js"
5
6
 
6
7
  /**
@@ -15,7 +16,7 @@ export async function createNewProject(args: {
15
16
  assertValidProjectPath(args.projectPath)
16
17
 
17
18
  const nodeishFs = args.repo.nodeishFs
18
- if (await pathExists(args.projectPath, nodeishFs)) {
19
+ if (await directoryExists(args.projectPath, nodeishFs)) {
19
20
  throw new Error(`projectPath already exists, received "${args.projectPath}"`)
20
21
  }
21
22
  const settingsText = JSON.stringify(args.projectSettings ?? defaultProjectSettings, undefined, 2)
@@ -27,3 +28,19 @@ export async function createNewProject(args: {
27
28
  nodeishFs.mkdir(`${args.projectPath}/cache/modules`, { recursive: true }),
28
29
  ])
29
30
  }
31
+
32
+ /**
33
+ * Returns true if the path exists (file or directory), false otherwise.
34
+ */
35
+ async function directoryExists(filePath: string, nodeishFs: NodeishFilesystem) {
36
+ try {
37
+ const stat = await nodeishFs.stat(filePath)
38
+ return stat.isDirectory()
39
+ } catch (error: any) {
40
+ if (error && "code" in error && error.code === "ENOENT") {
41
+ return false
42
+ } else {
43
+ throw new Error(`Failed to check if path exists: ${error}`, { cause: error })
44
+ }
45
+ }
46
+ }
@@ -8,7 +8,7 @@ describe("watcher", () => {
8
8
 
9
9
  const fs = createNodeishFsWithWatcher({
10
10
  nodeishFs: createNodeishMemoryFs(),
11
- updateMessages: () => {
11
+ onChange: () => {
12
12
  counter++
13
13
  },
14
14
  })
@@ -8,67 +8,78 @@ import type { NodeishFilesystem } from "@lix-js/fs"
8
8
  */
9
9
  export const createNodeishFsWithWatcher = (args: {
10
10
  nodeishFs: NodeishFilesystem
11
- updateMessages: () => void
11
+ onChange: () => void
12
12
  }): NodeishFilesystem & {
13
13
  stopWatching: () => void
14
14
  } => {
15
- const pathList: string[] = []
16
- let abortControllers: AbortController[] = []
15
+ const pathList = new Set<string>()
16
+ const abortControllers = new Set<AbortController>()
17
17
 
18
18
  const stopWatching = () => {
19
19
  for (const ac of abortControllers) {
20
20
  ac.abort()
21
+ abortControllers.delete(ac) // release reference
21
22
  }
22
- // release references
23
- abortControllers = []
24
23
  }
25
24
 
26
- const makeWatcher = (path: string) => {
27
- ;(async () => {
28
- try {
29
- const ac = new AbortController()
30
- abortControllers.push(ac)
31
- const watcher = args.nodeishFs.watch(path, {
32
- signal: ac.signal,
33
- persistent: false,
34
- })
35
- if (watcher) {
36
- //eslint-disable-next-line @typescript-eslint/no-unused-vars
37
- for await (const event of watcher) {
38
- args.updateMessages()
39
- }
25
+ const makeWatcher = async (path: string) => {
26
+ try {
27
+ const ac = new AbortController()
28
+ abortControllers.add(ac)
29
+ const watcher = args.nodeishFs.watch(path, {
30
+ signal: ac.signal,
31
+ recursive: true,
32
+ persistent: false,
33
+ })
34
+ if (watcher) {
35
+ //eslint-disable-next-line @typescript-eslint/no-unused-vars
36
+ for await (const event of watcher) {
37
+ // whenever the watcher changes we need to update the messages
38
+ args.onChange()
40
39
  }
41
- } catch (err: any) {
42
- if (err.name === "AbortError") return
43
- // https://github.com/opral/monorepo/issues/1647
44
- // the file does not exist (yet)
45
- // this is not testable beacause the fs.watch api differs
46
- // from node and lix. lenghty
47
- else if (err.code === "ENOENT") return
48
- throw err
49
40
  }
50
- })()
41
+ } catch (err: any) {
42
+ if (err.name === "AbortError") return
43
+ // https://github.com/opral/monorepo/issues/1647
44
+ // the file does not exist (yet)
45
+ // this is not testable beacause the fs.watch api differs
46
+ // from node and lix. lenghty
47
+ else if (err.code === "ENOENT") return
48
+ throw err
49
+ }
51
50
  }
52
51
 
53
- const readFileAndExtractPath = (path: string, options: { encoding: "utf-8" | "binary" }) => {
54
- if (!pathList.includes(path)) {
55
- makeWatcher(path)
56
- pathList.push(path)
52
+ /**
53
+ * Creates watchers on-the-fly for any file or directory that is not yet watched.
54
+ *
55
+ * We do this instead of recursively watching the entire project because fs.watch does not support
56
+ * recursive watching on linux in node 18. Once node 18 support is dropped this can be drastically simplified.
57
+ */
58
+ const watched = <T extends any[], R>(
59
+ fn: (path: string, ...rest: T) => R
60
+ ): ((path: string, ...rest: T) => R) => {
61
+ return (path: string, ...rest: T): R => {
62
+ if (!pathList.has(path)) {
63
+ makeWatcher(path)
64
+ pathList.add(path)
65
+ }
66
+ return fn(path, ...rest)
57
67
  }
58
- return args.nodeishFs.readFile(path, options)
59
68
  }
60
69
 
61
70
  return {
71
+ ...args.nodeishFs,
72
+ /**
73
+ * Reads the file and automatically adds it to the list of watched files.
74
+ * Any changes to the file will trigger a message update.
75
+ */
62
76
  // @ts-expect-error
63
- readFile: (path: string, options: { encoding: "utf-8" | "binary" }) =>
64
- readFileAndExtractPath(path, options),
65
- rm: args.nodeishFs.rm,
66
- readdir: args.nodeishFs.readdir,
67
- mkdir: args.nodeishFs.mkdir,
68
- rmdir: (args.nodeishFs as any).rmdir,
69
- writeFile: args.nodeishFs.writeFile,
70
- watch: args.nodeishFs.watch,
71
- stat: args.nodeishFs.stat,
77
+ readFile: watched(args.nodeishFs.readFile),
78
+ /**
79
+ * Reads the directory and automatically adds it to the list of watched files.
80
+ * Any changes to the directory will trigger a message update.
81
+ */
82
+ readdir: watched(args.nodeishFs.readdir),
72
83
  stopWatching,
73
84
  }
74
85
  }
@@ -278,26 +278,19 @@ describe("initialization", () => {
278
278
  expect(result.data).toBeDefined()
279
279
  })
280
280
 
281
- // TODO: fix this test
282
- // https://github.com/opral/inlang-message-sdk/issues/76
283
- // it doesn't work because failure to open the settings file doesn't throw
284
- // errors are returned in project.errors()
285
- it("should resolve from a windows path", async () => {
281
+ it.skipIf(() => process.platform !== "win32")("should resolve from a windows path", async () => {
286
282
  const repo = await mockRepo()
287
283
  const fs = repo.nodeishFs
288
284
  await fs.mkdir("C:\\Users\\user\\project.inlang", { recursive: true })
289
285
  await fs.writeFile("C:\\Users\\user\\project.inlang\\settings.json", JSON.stringify(settings))
290
286
 
291
- const result = await tryCatch(() =>
292
- loadProject({
293
- projectPath: "C:\\Users\\user\\project.inlang",
294
- repo,
295
- _import,
296
- })
297
- )
287
+ const project = await loadProject({
288
+ projectPath: "C:\\Users\\user\\project.inlang",
289
+ repo,
290
+ _import,
291
+ })
298
292
 
299
- expect(result.error).toBeUndefined()
300
- expect(result.data).toBeDefined()
293
+ expect(project.errors()).toEqual([])
301
294
  })
302
295
 
303
296
  describe("settings", () => {
@@ -362,9 +355,9 @@ describe("initialization", () => {
362
355
  it("should not re-write the settings to disk when initializing", async () => {
363
356
  const repo = await mockRepo()
364
357
  const fs = repo.nodeishFs
365
- const settingsWithDeifferentFormatting = JSON.stringify(settings, undefined, 4)
358
+ const settingsWithDifferentFormatting = JSON.stringify(settings, undefined, 4)
366
359
  await fs.mkdir("/user/project.inlang", { recursive: true })
367
- await fs.writeFile("/user/project.inlang/settings.json", settingsWithDeifferentFormatting)
360
+ await fs.writeFile("/user/project.inlang/settings.json", settingsWithDifferentFormatting)
368
361
 
369
362
  const project = await loadProject({
370
363
  projectPath: "/user/project.inlang",
@@ -375,7 +368,7 @@ describe("initialization", () => {
375
368
  const settingsOnDisk = await fs.readFile("/user/project.inlang/settings.json", {
376
369
  encoding: "utf-8",
377
370
  })
378
- expect(settingsOnDisk).toBe(settingsWithDeifferentFormatting)
371
+ expect(settingsOnDisk).toBe(settingsWithDifferentFormatting)
379
372
 
380
373
  project.setSettings(project.settings())
381
374
  // TODO: how can we await `setsettings` correctly
@@ -384,7 +377,7 @@ describe("initialization", () => {
384
377
  const newsettingsOnDisk = await fs.readFile("/user/project.inlang/settings.json", {
385
378
  encoding: "utf-8",
386
379
  })
387
- expect(newsettingsOnDisk).not.toBe(settingsWithDeifferentFormatting)
380
+ expect(newsettingsOnDisk).not.toBe(settingsWithDifferentFormatting)
388
381
  })
389
382
  })
390
383