@inlang/sdk 0.9.0 → 0.11.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.
Files changed (51) hide show
  1. package/dist/adapter/solidAdapter.test.js +12 -8
  2. package/dist/errors.d.ts +18 -5
  3. package/dist/errors.d.ts.map +1 -1
  4. package/dist/errors.js +12 -10
  5. package/dist/loadProject.d.ts +4 -1
  6. package/dist/loadProject.d.ts.map +1 -1
  7. package/dist/loadProject.js +22 -11
  8. package/dist/loadProject.test.js +122 -43
  9. package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.d.ts +6 -0
  10. package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.d.ts.map +1 -0
  11. package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.js +20 -0
  12. package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.test.d.ts +2 -0
  13. package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.test.d.ts.map +1 -0
  14. package/dist/resolve-modules/createNodeishFsWithAbsolutePaths.test.js +85 -0
  15. package/dist/resolve-modules/errors.d.ts +6 -5
  16. package/dist/resolve-modules/errors.d.ts.map +1 -1
  17. package/dist/resolve-modules/errors.js +10 -8
  18. package/dist/resolve-modules/import.d.ts.map +1 -1
  19. package/dist/resolve-modules/import.js +5 -5
  20. package/dist/resolve-modules/message-lint-rules/errors.d.ts +5 -4
  21. package/dist/resolve-modules/message-lint-rules/errors.d.ts.map +1 -1
  22. package/dist/resolve-modules/message-lint-rules/errors.js +2 -4
  23. package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.d.ts.map +1 -1
  24. package/dist/resolve-modules/message-lint-rules/resolveMessageLintRules.js +4 -2
  25. package/dist/resolve-modules/plugins/errors.d.ts +35 -28
  26. package/dist/resolve-modules/plugins/errors.d.ts.map +1 -1
  27. package/dist/resolve-modules/plugins/errors.js +23 -30
  28. package/dist/resolve-modules/plugins/resolvePlugins.d.ts.map +1 -1
  29. package/dist/resolve-modules/plugins/resolvePlugins.js +15 -14
  30. package/dist/resolve-modules/plugins/types.d.ts +2 -2
  31. package/dist/resolve-modules/plugins/types.d.ts.map +1 -1
  32. package/dist/resolve-modules/resolveModules.d.ts.map +1 -1
  33. package/dist/resolve-modules/resolveModules.js +5 -4
  34. package/dist/resolve-modules/resolveModules.test.js +2 -2
  35. package/dist/test-utilities/createMessage.d.ts +1 -1
  36. package/package.json +1 -1
  37. package/src/adapter/solidAdapter.test.ts +12 -8
  38. package/src/errors.ts +19 -10
  39. package/src/loadProject.test.ts +132 -43
  40. package/src/loadProject.ts +30 -18
  41. package/src/resolve-modules/createNodeishFsWithAbsolutePaths.test.ts +102 -0
  42. package/src/resolve-modules/createNodeishFsWithAbsolutePaths.ts +30 -0
  43. package/src/resolve-modules/errors.ts +14 -8
  44. package/src/resolve-modules/import.ts +5 -5
  45. package/src/resolve-modules/message-lint-rules/errors.ts +5 -5
  46. package/src/resolve-modules/message-lint-rules/resolveMessageLintRules.ts +4 -2
  47. package/src/resolve-modules/plugins/errors.ts +34 -36
  48. package/src/resolve-modules/plugins/resolvePlugins.ts +17 -46
  49. package/src/resolve-modules/plugins/types.ts +2 -2
  50. package/src/resolve-modules/resolveModules.test.ts +2 -2
  51. package/src/resolve-modules/resolveModules.ts +5 -6
@@ -72,9 +72,10 @@ const $import = async (name) => ({
72
72
  describe("config", () => {
73
73
  it("should react to changes in config", async () => {
74
74
  const fs = createNodeishMemoryFs();
75
- await fs.writeFile("./project.inlang.json", JSON.stringify(config));
75
+ await fs.mkdir("/user/project", { recursive: true });
76
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(config));
76
77
  const project = solidAdapter(await loadProject({
77
- settingsFilePath: "./project.inlang.json",
78
+ settingsFilePath: "/user/project/project.inlang.json",
78
79
  nodeishFs: fs,
79
80
  _import: $import,
80
81
  }), { from });
@@ -95,9 +96,10 @@ describe("config", () => {
95
96
  describe("installed", () => {
96
97
  it("react to changes that are unrelated to installed items", async () => {
97
98
  const fs = createNodeishMemoryFs();
98
- await fs.writeFile("./project.inlang.json", JSON.stringify(config));
99
+ await fs.mkdir("/user/project", { recursive: true });
100
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(config));
99
101
  const project = solidAdapter(await loadProject({
100
- settingsFilePath: "./project.inlang.json",
102
+ settingsFilePath: "/user/project/project.inlang.json",
101
103
  nodeishFs: fs,
102
104
  _import: $import,
103
105
  }), { from });
@@ -147,9 +149,10 @@ describe("messages", () => {
147
149
  saveMessages: () => undefined,
148
150
  };
149
151
  const mockImport = async () => ({ default: mockPlugin });
150
- await fs.writeFile("./project.inlang.json", JSON.stringify(mockConfig));
152
+ await fs.mkdir("/user/project", { recursive: true });
153
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(mockConfig));
151
154
  const project = solidAdapter(await loadProject({
152
- settingsFilePath: "./project.inlang.json",
155
+ settingsFilePath: "/user/project/project.inlang.json",
153
156
  nodeishFs: fs,
154
157
  _import: mockImport,
155
158
  }), { from });
@@ -167,9 +170,10 @@ describe("messages", () => {
167
170
  });
168
171
  it("should react to changes in messages", async () => {
169
172
  const fs = createNodeishMemoryFs();
170
- await fs.writeFile("./project.inlang.json", JSON.stringify(config));
173
+ await fs.mkdir("/user/project", { recursive: true });
174
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(config));
171
175
  const project = solidAdapter(await loadProject({
172
- settingsFilePath: "./project.inlang.json",
176
+ settingsFilePath: "/user/project/project.inlang.json",
173
177
  nodeishFs: fs,
174
178
  _import: $import,
175
179
  }), { from });
package/dist/errors.d.ts CHANGED
@@ -1,16 +1,29 @@
1
+ import type { ValueError } from "@sinclair/typebox/errors";
1
2
  export declare class ProjectSettingsInvalidError extends Error {
2
- constructor(message: string, options: ErrorOptions);
3
+ constructor(options: {
4
+ errors: ValueError[];
5
+ });
3
6
  }
4
7
  export declare class ProjectSettingsFileJSONSyntaxError extends Error {
5
- constructor(message: string, options: ErrorOptions);
8
+ constructor(options: {
9
+ cause: ErrorOptions["cause"];
10
+ path: string;
11
+ });
6
12
  }
7
13
  export declare class ProjectSettingsFileNotFoundError extends Error {
8
- constructor(message: string, options: ErrorOptions);
14
+ constructor(options: {
15
+ cause?: ErrorOptions["cause"];
16
+ path: string;
17
+ });
9
18
  }
10
19
  export declare class PluginSaveMessagesError extends Error {
11
- constructor(message: string, options: ErrorOptions);
20
+ constructor(options: {
21
+ cause: ErrorOptions["cause"];
22
+ });
12
23
  }
13
24
  export declare class PluginLoadMessagesError extends Error {
14
- constructor(message: string, options: ErrorOptions);
25
+ constructor(options: {
26
+ cause: ErrorOptions["cause"];
27
+ });
15
28
  }
16
29
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,2BAA4B,SAAQ,KAAK;gBACzC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY;CAIlD;AAED,qBAAa,kCAAmC,SAAQ,KAAK;gBAChD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY;CAIlD;AAED,qBAAa,gCAAiC,SAAQ,KAAK;gBAC9C,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY;CAIlD;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY;CAIlD;AAED,qBAAa,uBAAwB,SAAQ,KAAK;gBACrC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY;CAIlD"}
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"}
package/dist/errors.js CHANGED
@@ -1,30 +1,32 @@
1
1
  export class ProjectSettingsInvalidError extends Error {
2
- constructor(message, options) {
3
- super(message, options);
2
+ constructor(options) {
3
+ super(`The project settings are invalid:\n\n${options.errors
4
+ .map((error) => `The value of "${error.path}" is invalid:\n\n${error.message}`)
5
+ .join("\n")}`);
4
6
  this.name = "ProjectSettingsInvalidError";
5
7
  }
6
8
  }
7
9
  export class ProjectSettingsFileJSONSyntaxError extends Error {
8
- constructor(message, options) {
9
- super(message, options);
10
+ constructor(options) {
11
+ super(`The settings file at "${options.path}" is not a valid JSON file:\n\n${options.cause}`, options);
10
12
  this.name = "ProjectSettingsFileJSONSyntaxError";
11
13
  }
12
14
  }
13
15
  export class ProjectSettingsFileNotFoundError extends Error {
14
- constructor(message, options) {
15
- super(message, options);
16
+ constructor(options) {
17
+ super(`The file at "${options.path}" could not be read. Does the file exists?`, options);
16
18
  this.name = "ProjectSettingsFileNotFoundError";
17
19
  }
18
20
  }
19
21
  export class PluginSaveMessagesError extends Error {
20
- constructor(message, options) {
21
- super(message, options);
22
+ constructor(options) {
23
+ super(`An error occured in saveMessages() caused by ${options.cause}.`, options);
22
24
  this.name = "PluginSaveMessagesError";
23
25
  }
24
26
  }
25
27
  export class PluginLoadMessagesError extends Error {
26
- constructor(message, options) {
27
- super(message, options);
28
+ constructor(options) {
29
+ super(`An error occured in loadMessages() caused by ${options.cause}.`, options);
28
30
  this.name = "PluginLoadMessagesError";
29
31
  }
30
32
  }
@@ -4,8 +4,11 @@ import { type NodeishFilesystemSubset } from "./versionedInterfaces.js";
4
4
  /**
5
5
  * Creates an inlang instance.
6
6
  *
7
- * - Use `_import` to pass a custom import function for testing,
7
+ * @param settingsFilePath - Absolute path to the inlang settings file.
8
+ * @param nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
9
+ * @param _import - Use `_import` to pass a custom import function for testing,
8
10
  * and supporting legacy resolvedModules such as CJS.
11
+ * @param _capture - Use `_capture` to capture events for analytics.
9
12
  *
10
13
  */
11
14
  export declare const loadProject: (args: {
@@ -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;AAahF,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAMjG;;;;;;GAMG;AACH,eAAO,MAAM,WAAW;sBACL,MAAM;eACb,uBAAuB;;qBAElB,MAAM,SAAS,OAAO,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI;MAC5D,QAAQ,aAAa,CAqLxB,CAAA;AAsGD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAQtE"}
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;AAahF,OAAO,EAA4B,KAAK,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAOjG;;;;;;;;;GASG;AACH,eAAO,MAAM,WAAW;sBACL,MAAM;eACb,uBAAuB;;qBAElB,MAAM,SAAS,OAAO,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI;MAC5D,QAAQ,aAAa,CA4LxB,CAAA;AAqGD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAQtE"}
@@ -8,21 +8,30 @@ 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 "./resolve-modules/createNodeishFsWithAbsolutePaths.js";
11
12
  const settingsCompiler = TypeCompiler.Compile(ProjectSettings);
12
13
  /**
13
14
  * Creates an inlang instance.
14
15
  *
15
- * - Use `_import` to pass a custom import function for testing,
16
+ * @param settingsFilePath - Absolute path to the inlang settings file.
17
+ * @param nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
18
+ * @param _import - Use `_import` to pass a custom import function for testing,
16
19
  * and supporting legacy resolvedModules such as CJS.
20
+ * @param _capture - Use `_capture` to capture events for analytics.
17
21
  *
18
22
  */
19
23
  export const loadProject = async (args) => {
20
24
  return await createRoot(async () => {
21
25
  const [initialized, markInitAsComplete, markInitAsFailed] = createAwaitable();
26
+ // -- absolute path env -----------------------------------------------
27
+ const nodeishFs = createNodeishFsWithAbsolutePaths({
28
+ nodeishFs: args.nodeishFs,
29
+ basePath: args.settingsFilePath.replace(/(.*?)[^/]*\..*$/, "$1"), // get directory of settings file
30
+ });
22
31
  // -- settings ------------------------------------------------------------
23
32
  const [settings, _setSettings] = createSignal();
24
33
  createEffect(() => {
25
- loadSettings({ settingsFilePath: args.settingsFilePath, nodeishFs: args.nodeishFs })
34
+ loadSettings({ settingsFilePath: args.settingsFilePath, nodeishFs })
26
35
  .then((settings) => {
27
36
  setSettings(settings);
28
37
  // rename settings to get a convenient access to the data in Posthog
@@ -34,7 +43,7 @@ export const loadProject = async (args) => {
34
43
  });
35
44
  });
36
45
  // TODO: create FS watcher and update settings on change
37
- const writeSettingsToDisk = skipFirst((settings) => _writeSettingsToDisk({ nodeishFs: args.nodeishFs, settings }));
46
+ const writeSettingsToDisk = skipFirst((settings) => _writeSettingsToDisk({ nodeishFs, settings }));
38
47
  const setSettings = (settings) => {
39
48
  try {
40
49
  const validatedSettings = parseSettings(settings);
@@ -46,7 +55,7 @@ export const loadProject = async (args) => {
46
55
  if (error instanceof ProjectSettingsInvalidError) {
47
56
  return { error };
48
57
  }
49
- throw new Error("unhandled");
58
+ throw new Error("Unhandled error in setSettings. This is an internal bug. Please file an issue.");
50
59
  }
51
60
  };
52
61
  // -- resolvedModules -----------------------------------------------------------
@@ -55,7 +64,7 @@ export const loadProject = async (args) => {
55
64
  const _settings = settings();
56
65
  if (!_settings)
57
66
  return;
58
- resolveModules({ settings: _settings, nodeishFs: args.nodeishFs, _import: args._import })
67
+ resolveModules({ settings: _settings, nodeishFs, _import: args._import })
59
68
  .then((resolvedModules) => {
60
69
  setResolvedModules(resolvedModules);
61
70
  })
@@ -83,7 +92,7 @@ export const loadProject = async (args) => {
83
92
  setMessages(messages);
84
93
  markInitAsComplete();
85
94
  })
86
- .catch((err) => markInitAsFailed(new PluginLoadMessagesError("Error in load messages", { cause: err })));
95
+ .catch((err) => markInitAsFailed(new PluginLoadMessagesError({ cause: err })));
87
96
  });
88
97
  // -- installed items ----------------------------------------------------
89
98
  const installedMessageLintRules = () => {
@@ -122,7 +131,7 @@ export const loadProject = async (args) => {
122
131
  });
123
132
  }
124
133
  catch (err) {
125
- throw new PluginSaveMessagesError("Error in saving messages", {
134
+ throw new PluginSaveMessagesError({
126
135
  cause: err,
127
136
  });
128
137
  }
@@ -160,13 +169,15 @@ export const loadProject = async (args) => {
160
169
  const loadSettings = async (args) => {
161
170
  const { data: settingsFile, error: settingsFileError } = await tryCatch(async () => await args.nodeishFs.readFile(args.settingsFilePath, { encoding: "utf-8" }));
162
171
  if (settingsFileError)
163
- throw new ProjectSettingsFileNotFoundError(`Could not locate settings file in (${args.settingsFilePath}).`, {
172
+ throw new ProjectSettingsFileNotFoundError({
164
173
  cause: settingsFileError,
174
+ path: args.settingsFilePath,
165
175
  });
166
176
  const json = tryCatch(() => JSON.parse(settingsFile));
167
177
  if (json.error) {
168
- throw new ProjectSettingsFileJSONSyntaxError(`The settings is not a valid JSON file.`, {
178
+ throw new ProjectSettingsFileJSONSyntaxError({
169
179
  cause: json.error,
180
+ path: args.settingsFilePath,
170
181
  });
171
182
  }
172
183
  return parseSettings(json.data);
@@ -176,8 +187,8 @@ const parseSettings = (settings) => {
176
187
  if (settingsCompiler.Check(withMigration) === false) {
177
188
  const typeErrors = [...settingsCompiler.Errors(settings)];
178
189
  if (typeErrors.length > 0) {
179
- throw new ProjectSettingsInvalidError(`The settings is invalid according to the schema.`, {
180
- cause: typeErrors,
190
+ throw new ProjectSettingsInvalidError({
191
+ errors: typeErrors,
181
192
  });
182
193
  }
183
194
  }
@@ -3,6 +3,7 @@ import { describe, it, expect, vi } from "vitest";
3
3
  import { loadProject } from "./loadProject.js";
4
4
  import { ProjectSettingsFileJSONSyntaxError, ProjectSettingsFileNotFoundError, ProjectSettingsInvalidError, } from "./errors.js";
5
5
  import { createNodeishMemoryFs } from "@lix-js/fs";
6
+ import { createMessage } from "./test-utilities/createMessage.js";
6
7
  // ------------------------------------------------------------------------------------------------
7
8
  const getValue = (subscribable) => {
8
9
  let value;
@@ -82,8 +83,9 @@ describe("initialization", () => {
82
83
  describe("settings", () => {
83
84
  it("should return an error if settings file is not found", async () => {
84
85
  const fs = createNodeishMemoryFs();
86
+ fs.mkdir("/user/project", { recursive: true });
85
87
  const project = await loadProject({
86
- settingsFilePath: "./test.json",
88
+ settingsFilePath: "/user/project/test.json",
87
89
  nodeishFs: fs,
88
90
  _import,
89
91
  });
@@ -91,9 +93,10 @@ describe("initialization", () => {
91
93
  });
92
94
  it("should return an error if settings file is not a valid JSON", async () => {
93
95
  const fs = await createNodeishMemoryFs();
94
- await fs.writeFile("./project.inlang.json", "invalid json");
96
+ await fs.mkdir("/user/project", { recursive: true });
97
+ await fs.writeFile("/user/project/project.inlang.json", "invalid json");
95
98
  const project = await loadProject({
96
- settingsFilePath: "./project.inlang.json",
99
+ settingsFilePath: "/user/project/project.inlang.json",
97
100
  nodeishFs: fs,
98
101
  _import,
99
102
  });
@@ -101,9 +104,10 @@ describe("initialization", () => {
101
104
  });
102
105
  it("should return an error if settings file is does not match schema", async () => {
103
106
  const fs = await createNodeishMemoryFs();
104
- await fs.writeFile("./project.inlang.json", JSON.stringify({}));
107
+ await fs.mkdir("/user/project", { recursive: true });
108
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify({}));
105
109
  const project = await loadProject({
106
- settingsFilePath: "./project.inlang.json",
110
+ settingsFilePath: "/user/project/project.inlang.json",
107
111
  nodeishFs: fs,
108
112
  _import,
109
113
  });
@@ -111,9 +115,10 @@ describe("initialization", () => {
111
115
  });
112
116
  it("should return the parsed settings", async () => {
113
117
  const fs = await createNodeishMemoryFs();
114
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
118
+ await fs.mkdir("/user/project", { recursive: true });
119
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
115
120
  const project = await loadProject({
116
- settingsFilePath: "./project.inlang.json",
121
+ settingsFilePath: "/user/project/project.inlang.json",
117
122
  nodeishFs: fs,
118
123
  _import,
119
124
  });
@@ -122,18 +127,23 @@ describe("initialization", () => {
122
127
  it("should not re-write the settings to disk when initializing", async () => {
123
128
  const fs = await createNodeishMemoryFs();
124
129
  const settingsWithDeifferentFormatting = JSON.stringify(settings, undefined, 4);
125
- await fs.writeFile("./project.inlang.json", settingsWithDeifferentFormatting);
130
+ await fs.mkdir("/user/project", { recursive: true });
131
+ await fs.writeFile("/user/project/project.inlang.json", settingsWithDeifferentFormatting);
126
132
  const project = await loadProject({
127
- settingsFilePath: "./project.inlang.json",
133
+ settingsFilePath: "/user/project/project.inlang.json",
128
134
  nodeishFs: fs,
129
135
  _import,
130
136
  });
131
- const settingsOnDisk = await fs.readFile("./project.inlang.json", { encoding: "utf-8" });
137
+ const settingsOnDisk = await fs.readFile("/user/project/project.inlang.json", {
138
+ encoding: "utf-8",
139
+ });
132
140
  expect(settingsOnDisk).toBe(settingsWithDeifferentFormatting);
133
141
  project.setSettings(project.settings());
134
142
  // TODO: how can we await `setsettings` correctly
135
143
  await new Promise((resolve) => setTimeout(resolve, 0));
136
- const newsettingsOnDisk = await fs.readFile("./project.inlang.json", { encoding: "utf-8" });
144
+ const newsettingsOnDisk = await fs.readFile("/user/project/project.inlang.json", {
145
+ encoding: "utf-8",
146
+ });
137
147
  expect(newsettingsOnDisk).not.toBe(settingsWithDeifferentFormatting);
138
148
  });
139
149
  });
@@ -143,9 +153,10 @@ describe("initialization", () => {
143
153
  default: {},
144
154
  });
145
155
  const fs = createNodeishMemoryFs();
146
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
156
+ await fs.mkdir("/user/project", { recursive: true });
157
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
147
158
  const project = await loadProject({
148
- settingsFilePath: "./project.inlang.json",
159
+ settingsFilePath: "/user/project/project.inlang.json",
149
160
  nodeishFs: fs,
150
161
  _import: $badImport,
151
162
  });
@@ -170,9 +181,10 @@ describe("functionality", () => {
170
181
  describe("settings", () => {
171
182
  it("should return the settings", async () => {
172
183
  const fs = await createNodeishMemoryFs();
173
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
184
+ await fs.mkdir("/user/project", { recursive: true });
185
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
174
186
  const project = await loadProject({
175
- settingsFilePath: "./project.inlang.json",
187
+ settingsFilePath: "/user/project/project.inlang.json",
176
188
  nodeishFs: fs,
177
189
  _import,
178
190
  });
@@ -180,9 +192,10 @@ describe("functionality", () => {
180
192
  });
181
193
  it("should set a new settings", async () => {
182
194
  const fs = await createNodeishMemoryFs();
183
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
195
+ await fs.mkdir("/user/project", { recursive: true });
196
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
184
197
  const project = await loadProject({
185
- settingsFilePath: "./project.inlang.json",
198
+ settingsFilePath: "/user/project/project.inlang.json",
186
199
  nodeishFs: fs,
187
200
  _import,
188
201
  });
@@ -201,9 +214,10 @@ describe("functionality", () => {
201
214
  describe("setSettings", () => {
202
215
  it("should fail if settings is not valid", async () => {
203
216
  const fs = await createNodeishMemoryFs();
204
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
217
+ await fs.mkdir("/user/project", { recursive: true });
218
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
205
219
  const project = await loadProject({
206
- settingsFilePath: "./project.inlang.json",
220
+ settingsFilePath: "/user/project/project.inlang.json",
207
221
  nodeishFs: fs,
208
222
  _import,
209
223
  });
@@ -213,20 +227,21 @@ describe("functionality", () => {
213
227
  });
214
228
  it("should write settings to disk", async () => {
215
229
  const fs = await createNodeishMemoryFs();
216
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
230
+ await fs.mkdir("/user/project", { recursive: true });
231
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
217
232
  const project = await loadProject({
218
- settingsFilePath: "./project.inlang.json",
233
+ settingsFilePath: "/user/project/project.inlang.json",
219
234
  nodeishFs: fs,
220
235
  _import,
221
236
  });
222
- const before = await fs.readFile("./project.inlang.json", { encoding: "utf-8" });
237
+ const before = await fs.readFile("/user/project/project.inlang.json", { encoding: "utf-8" });
223
238
  expect(before).toBeDefined();
224
239
  const result = project.setSettings({ ...settings, languageTags: [] });
225
240
  expect(result.data).toBeUndefined();
226
241
  expect(result.error).toBeUndefined();
227
242
  // TODO: how to wait for fs.writeFile to finish?
228
243
  await new Promise((resolve) => setTimeout(resolve, 0));
229
- const after = await fs.readFile("./project.inlang.json", { encoding: "utf-8" });
244
+ const after = await fs.readFile("/user/project/project.inlang.json", { encoding: "utf-8" });
230
245
  expect(after).toBeDefined();
231
246
  expect(after).not.toBe(before);
232
247
  });
@@ -239,9 +254,10 @@ describe("functionality", () => {
239
254
  languageTags: ["en"],
240
255
  modules: ["plugin.js", "lintRule.js"],
241
256
  };
242
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
257
+ await fs.mkdir("/user/project", { recursive: true });
258
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
243
259
  const project = await loadProject({
244
- settingsFilePath: "./project.inlang.json",
260
+ settingsFilePath: "/user/project/project.inlang.json",
245
261
  nodeishFs: fs,
246
262
  _import,
247
263
  });
@@ -266,9 +282,10 @@ describe("functionality", () => {
266
282
  languageTags: ["en"],
267
283
  modules: ["plugin.js", "lintRule.js"],
268
284
  };
269
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
285
+ await fs.mkdir("/user/project", { recursive: true });
286
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
270
287
  const project = await loadProject({
271
- settingsFilePath: "./project.inlang.json",
288
+ settingsFilePath: "/user/project/project.inlang.json",
272
289
  nodeishFs: fs,
273
290
  _import,
274
291
  });
@@ -296,7 +313,8 @@ describe("functionality", () => {
296
313
  saveMessages: () => undefined,
297
314
  };
298
315
  const fs = await createNodeishMemoryFs();
299
- await fs.writeFile("./project.inlang.json", JSON.stringify({
316
+ await fs.mkdir("/user/project", { recursive: true });
317
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify({
300
318
  sourceLanguageTag: "en",
301
319
  languageTags: ["en"],
302
320
  modules: ["plugin.js", "lintRule.js"],
@@ -307,7 +325,7 @@ describe("functionality", () => {
307
325
  };
308
326
  };
309
327
  const project = await loadProject({
310
- settingsFilePath: "./project.inlang.json",
328
+ settingsFilePath: "/user/project/project.inlang.json",
311
329
  nodeishFs: fs,
312
330
  _import,
313
331
  });
@@ -337,7 +355,8 @@ describe("functionality", () => {
337
355
  saveMessages: () => undefined,
338
356
  };
339
357
  const fs = await createNodeishMemoryFs();
340
- await fs.writeFile("./project.settings.json", JSON.stringify({
358
+ await fs.mkdir("/user/project", { recursive: true });
359
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify({
341
360
  sourceLanguageTag: "en",
342
361
  languageTags: ["en"],
343
362
  modules: ["plugin.js", "lintRule.js"],
@@ -348,7 +367,7 @@ describe("functionality", () => {
348
367
  };
349
368
  };
350
369
  const project = await loadProject({
351
- settingsFilePath: "./project.settings.json",
370
+ settingsFilePath: "/user/project/project.inlang.json",
352
371
  nodeishFs: fs,
353
372
  _import,
354
373
  });
@@ -359,9 +378,10 @@ describe("functionality", () => {
359
378
  describe("errors", () => {
360
379
  it("should return the errors", async () => {
361
380
  const fs = await createNodeishMemoryFs();
362
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
381
+ await fs.mkdir("/user/project", { recursive: true });
382
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
363
383
  const project = await loadProject({
364
- settingsFilePath: "./project.inlang.json",
384
+ settingsFilePath: "/user/project/project.inlang.json",
365
385
  nodeishFs: fs,
366
386
  _import,
367
387
  });
@@ -373,9 +393,10 @@ describe("functionality", () => {
373
393
  describe("customApi", () => {
374
394
  it("should return the app specific api", async () => {
375
395
  const fs = await createNodeishMemoryFs();
376
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
396
+ await fs.mkdir("/user/project", { recursive: true });
397
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
377
398
  const project = await loadProject({
378
- settingsFilePath: "./project.inlang.json",
399
+ settingsFilePath: "/user/project/project.inlang.json",
379
400
  nodeishFs: fs,
380
401
  _import,
381
402
  });
@@ -387,9 +408,10 @@ describe("functionality", () => {
387
408
  describe("messages", () => {
388
409
  it("should return the messages", async () => {
389
410
  const fs = await createNodeishMemoryFs();
390
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
411
+ await fs.mkdir("/user/project", { recursive: true });
412
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
391
413
  const project = await loadProject({
392
- settingsFilePath: "./project.inlang.json",
414
+ settingsFilePath: "/user/project/project.inlang.json",
393
415
  nodeishFs: fs,
394
416
  _import,
395
417
  });
@@ -407,7 +429,8 @@ describe("functionality", () => {
407
429
  pathPattern: "./resources/{languageTag}.json",
408
430
  },
409
431
  };
410
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
432
+ await fs.mkdir("/user/project", { recursive: true });
433
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
411
434
  await fs.mkdir("./resources");
412
435
  const mockSaveFn = vi.fn();
413
436
  const _mockPlugin = {
@@ -423,7 +446,7 @@ describe("functionality", () => {
423
446
  };
424
447
  };
425
448
  const project = await loadProject({
426
- settingsFilePath: "./project.inlang.json",
449
+ settingsFilePath: "/user/project/project.inlang.json",
427
450
  nodeishFs: fs,
428
451
  _import,
429
452
  });
@@ -543,13 +566,68 @@ describe("functionality", () => {
543
566
  },
544
567
  ]);
545
568
  });
569
+ /*
570
+ * Passing all messages to saveMessages() simplifies plugins by an order of magnitude.
571
+ *
572
+ * The alternative would be to pass only the messages that changed to saveMessages().
573
+ * But, this would require plugins to maintain a separate data structure of messages
574
+ * and create optimizations, leading to (unjustified) complexity for plugin authors.
575
+ *
576
+ * Pros:
577
+ * - plugins don't need to transform the data (time complexity).
578
+ * - plugins don't to maintain a separate data structure (space complexity).
579
+ * - plugin authors don't need to deal with optimizations (ecosystem complexity).
580
+ *
581
+ * Cons:
582
+ * - Might be slow for a large number of messages. The requirement hasn't popped up yet though.
583
+ */
584
+ it("should pass all messages, regardless of which message changed, to saveMessages()", async () => {
585
+ const fs = createNodeishMemoryFs();
586
+ const settings = {
587
+ sourceLanguageTag: "en",
588
+ languageTags: ["en", "de"],
589
+ modules: ["plugin.js"],
590
+ "plugin.project.json": {
591
+ pathPattern: "./resources/{languageTag}.json",
592
+ },
593
+ };
594
+ await fs.mkdir("/user/project", { recursive: true });
595
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
596
+ const mockSaveFn = vi.fn();
597
+ const _mockPlugin = {
598
+ id: "plugin.project.json",
599
+ description: "Mock plugin description",
600
+ displayName: "Mock Plugin",
601
+ loadMessages: () => [
602
+ createMessage("first", { en: "first message" }),
603
+ createMessage("second", { en: "second message" }),
604
+ createMessage("third", { en: "third message" }),
605
+ ],
606
+ saveMessages: mockSaveFn,
607
+ };
608
+ const _import = async () => {
609
+ return {
610
+ default: _mockPlugin,
611
+ };
612
+ };
613
+ const project = await loadProject({
614
+ settingsFilePath: "/user/project/project.inlang.json",
615
+ nodeishFs: fs,
616
+ _import,
617
+ });
618
+ project.query.messages.create({ data: createMessage("fourth", { en: "fourth message" }) });
619
+ await new Promise((resolve) => setTimeout(resolve, 510));
620
+ expect(mockSaveFn.mock.calls.length).toBe(1);
621
+ expect(mockSaveFn.mock.calls[0][0].messages).toHaveLength(4);
622
+ });
546
623
  });
547
624
  describe("lint", () => {
548
625
  it.todo("should throw if lint reports are not initialized yet", async () => {
549
626
  const fs = await createNodeishMemoryFs();
550
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
627
+ await fs.mkdir("/user/project", { recursive: true });
628
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
551
629
  const project = await loadProject({
552
- settingsFilePath: "./project.inlang.json",
630
+ settingsFilePath: "/user/project/project.inlang.json",
553
631
  nodeishFs: fs,
554
632
  _import,
555
633
  });
@@ -569,9 +647,10 @@ describe("functionality", () => {
569
647
  modules: ["lintRule.js"],
570
648
  };
571
649
  const fs = createNodeishMemoryFs();
572
- await fs.writeFile("./project.inlang.json", JSON.stringify(settings));
650
+ await fs.mkdir("/user/project", { recursive: true });
651
+ await fs.writeFile("/user/project/project.inlang.json", JSON.stringify(settings));
573
652
  const project = await loadProject({
574
- settingsFilePath: "./project.inlang.json",
653
+ settingsFilePath: "/user/project/project.inlang.json",
575
654
  nodeishFs: fs,
576
655
  _import: async () => ({
577
656
  default: mockMessageLintRule,
@@ -0,0 +1,6 @@
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
@@ -0,0 +1 @@
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"}