@inlang/sdk 2.0.0 → 2.1.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 (44) hide show
  1. package/dist/json-schema/settings.d.ts +8 -10
  2. package/dist/json-schema/settings.d.ts.map +1 -1
  3. package/dist/json-schema/settings.js +4 -27
  4. package/dist/json-schema/settings.js.map +1 -1
  5. package/dist/json-schema/settings.test.d.ts +2 -0
  6. package/dist/json-schema/settings.test.d.ts.map +1 -0
  7. package/dist/json-schema/settings.test.js +26 -0
  8. package/dist/json-schema/settings.test.js.map +1 -0
  9. package/dist/plugin/importPlugins.test.js +2 -3
  10. package/dist/plugin/importPlugins.test.js.map +1 -1
  11. package/dist/project/loadProject.d.ts.map +1 -1
  12. package/dist/project/loadProject.js +13 -15
  13. package/dist/project/loadProject.js.map +1 -1
  14. package/dist/project/loadProjectFromDirectory.d.ts +2 -1
  15. package/dist/project/loadProjectFromDirectory.d.ts.map +1 -1
  16. package/dist/project/loadProjectFromDirectory.js +41 -53
  17. package/dist/project/loadProjectFromDirectory.js.map +1 -1
  18. package/dist/project/loadProjectFromDirectory.test.js +95 -4
  19. package/dist/project/loadProjectFromDirectory.test.js.map +1 -1
  20. package/dist/project/newProject.d.ts.map +1 -1
  21. package/dist/project/newProject.js +3 -3
  22. package/dist/project/newProject.js.map +1 -1
  23. package/dist/project/saveProjectToDirectory.d.ts.map +1 -1
  24. package/dist/project/saveProjectToDirectory.js +9 -2
  25. package/dist/project/saveProjectToDirectory.js.map +1 -1
  26. package/dist/project/saveProjectToDirectory.test.js +15 -2
  27. package/dist/project/saveProjectToDirectory.test.js.map +1 -1
  28. package/dist/services/env-variables/index.js +3 -3
  29. package/dist/services/env-variables/index.js.map +1 -1
  30. package/package.json +4 -5
  31. package/src/json-schema/settings.test.ts +26 -0
  32. package/src/json-schema/settings.ts +3 -36
  33. package/src/plugin/importPlugins.test.ts +0 -1
  34. package/src/project/loadProject.ts +15 -15
  35. package/src/project/loadProjectFromDirectory.test.ts +116 -2
  36. package/src/project/loadProjectFromDirectory.ts +49 -98
  37. package/src/project/newProject.ts +5 -1
  38. package/src/project/saveProjectToDirectory.test.ts +20 -0
  39. package/src/project/saveProjectToDirectory.ts +11 -1
  40. package/dist/project/initHandleSaveToLixOnChange.d.ts +0 -14
  41. package/dist/project/initHandleSaveToLixOnChange.d.ts.map +0 -1
  42. package/dist/project/initHandleSaveToLixOnChange.js +0 -87
  43. package/dist/project/initHandleSaveToLixOnChange.js.map +0 -1
  44. package/src/project/initHandleSaveToLixOnChange.ts +0 -93
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="c3af6497-ac1c-5547-9957-3b8b155ff12a")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="1c391748-b1e4-573f-a7e4-1387f21b0283")}catch(e){}}();
3
3
  import { newLixFile, openLixInMemory, toBlob } from "@lix-js/sdk";
4
4
  import { v4 } from "uuid";
5
5
  import { contentFromDatabase, createInMemoryDatabase, } from "sqlite-wasm-kysely";
@@ -29,7 +29,7 @@ export async function newProject(args) {
29
29
  },
30
30
  {
31
31
  path: "/settings.json",
32
- data: new TextEncoder().encode(JSON.stringify(args?.settings ?? defaultProjectSettings)),
32
+ data: new TextEncoder().encode(JSON.stringify(args?.settings ?? defaultProjectSettings, undefined, 2)),
33
33
  },
34
34
  {
35
35
  path: "/project_id",
@@ -68,4 +68,4 @@ export const defaultProjectSettings = {
68
68
  ],
69
69
  };
70
70
  //# sourceMappingURL=newProject.js.map
71
- //# debugId=c3af6497-ac1c-5547-9957-3b8b155ff12a
71
+ //# debugId=1c391748-b1e4-573f-a7e4-1387f21b0283
@@ -1 +1 @@
1
- {"version":3,"file":"newProject.js","sources":["project/newProject.ts"],"sourceRoot":"/","sourcesContent":["import { newLixFile, openLixInMemory, toBlob } from \"@lix-js/sdk\";\nimport { v4 } from \"uuid\";\nimport type { ProjectSettings } from \"../json-schema/settings.js\";\nimport {\n\tcontentFromDatabase,\n\tcreateInMemoryDatabase,\n} from \"sqlite-wasm-kysely\";\nimport { initDb } from \"../database/initDb.js\";\nimport { captureError } from \"../services/error-reporting/index.js\";\n\n/**\n * Creates a new inlang project.\n *\n * The app is responsible for saving the project \"whereever\"\n * e.g. the user's computer, cloud storage, or OPFS in the browser.\n */\nexport async function newProject(args?: {\n\tsettings?: ProjectSettings;\n}): Promise<Blob> {\n\tconst sqlite = await createInMemoryDatabase({\n\t\treadOnly: false,\n\t});\n\tinitDb({ sqlite });\n\n\ttry {\n\t\tconst inlangDbContent = contentFromDatabase(sqlite);\n\n\t\tconst lix = await openLixInMemory({ blob: await newLixFile() });\n\n\t\t// write files to lix\n\t\tawait lix.db\n\t\t\t.insertInto(\"file\")\n\t\t\t.values([\n\t\t\t\t{\n\t\t\t\t\tpath: \"/db.sqlite\",\n\t\t\t\t\tdata: inlangDbContent,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tpath: \"/settings.json\",\n\t\t\t\t\tdata: new TextEncoder().encode(\n\t\t\t\t\t\tJSON.stringify(args?.settings ?? defaultProjectSettings)\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tpath: \"/project_id\",\n\t\t\t\t\tdata: new TextEncoder().encode(v4()),\n\t\t\t\t},\n\t\t\t])\n\t\t\t.execute();\n\t\tconst blob = toBlob({ lix });\n\t\tlix.sqlite.close();\n\t\treturn blob;\n\t} catch (e) {\n\t\tconst error = new Error(`Failed to create new inlang project: ${e}`, {\n\t\t\tcause: e,\n\t\t});\n\t\tcaptureError(error);\n\t\tthrow error;\n\t} finally {\n\t\tsqlite.close();\n\t}\n}\n\nexport const defaultProjectSettings = {\n\t$schema: \"https://inlang.com/schema/project-settings\",\n\tbaseLocale: \"en\",\n\tlocales: [\"en\"],\n\tmodules: [\n\t\t// for instant gratification, we're adding common rules\n\t\t// \"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js\",\n\t\t// \"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js\",\n\t\t// \"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js\",\n\t\t// default to the message format plugin because it supports all features\n\t\t// \"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js\",\n\t\t// the m function matcher should be installed by default in case Sherlock (VS Code extension) is adopted\n\t\t// \"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js\",\n\t],\n} satisfies ProjectSettings;\n"],"names":[],"mappings":";;AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAE1B,OAAO,EACN,mBAAmB,EACnB,sBAAsB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAEhC;IACA,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC;QAC3C,QAAQ,EAAE,KAAK;KACf,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAEnB,IAAI,CAAC;QACJ,MAAM,eAAe,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAEpD,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,EAAE,IAAI,EAAE,MAAM,UAAU,EAAE,EAAE,CAAC,CAAC;QAEhE,qBAAqB;QACrB,MAAM,GAAG,CAAC,EAAE;aACV,UAAU,CAAC,MAAM,CAAC;aAClB,MAAM,CAAC;YACP;gBACC,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,eAAe;aACrB;YACD;gBACC,IAAI,EAAE,gBAAgB;gBACtB,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAC7B,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,QAAQ,IAAI,sBAAsB,CAAC,CACxD;aACD;YACD;gBACC,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;aACpC;SACD,CAAC;aACD,OAAO,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7B,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,wCAAwC,CAAC,EAAE,EAAE;YACpE,KAAK,EAAE,CAAC;SACR,CAAC,CAAC;QACH,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,KAAK,CAAC;IACb,CAAC;YAAS,CAAC;QACV,MAAM,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACrC,OAAO,EAAE,4CAA4C;IACrD,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE,CAAC,IAAI,CAAC;IACf,OAAO,EAAE;IACR,uDAAuD;IACvD,+FAA+F;IAC/F,qGAAqG;IACrG,gGAAgG;IAChG,wEAAwE;IACxE,qFAAqF;IACrF,wGAAwG;IACxG,yFAAyF;KACzF;CACyB,CAAC","debug_id":"c3af6497-ac1c-5547-9957-3b8b155ff12a"}
1
+ {"version":3,"file":"newProject.js","sources":["project/newProject.ts"],"sourceRoot":"/","sourcesContent":["import { newLixFile, openLixInMemory, toBlob } from \"@lix-js/sdk\";\nimport { v4 } from \"uuid\";\nimport type { ProjectSettings } from \"../json-schema/settings.js\";\nimport {\n\tcontentFromDatabase,\n\tcreateInMemoryDatabase,\n} from \"sqlite-wasm-kysely\";\nimport { initDb } from \"../database/initDb.js\";\nimport { captureError } from \"../services/error-reporting/index.js\";\n\n/**\n * Creates a new inlang project.\n *\n * The app is responsible for saving the project \"whereever\"\n * e.g. the user's computer, cloud storage, or OPFS in the browser.\n */\nexport async function newProject(args?: {\n\tsettings?: ProjectSettings;\n}): Promise<Blob> {\n\tconst sqlite = await createInMemoryDatabase({\n\t\treadOnly: false,\n\t});\n\tinitDb({ sqlite });\n\n\ttry {\n\t\tconst inlangDbContent = contentFromDatabase(sqlite);\n\n\t\tconst lix = await openLixInMemory({ blob: await newLixFile() });\n\n\t\t// write files to lix\n\t\tawait lix.db\n\t\t\t.insertInto(\"file\")\n\t\t\t.values([\n\t\t\t\t{\n\t\t\t\t\tpath: \"/db.sqlite\",\n\t\t\t\t\tdata: inlangDbContent,\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tpath: \"/settings.json\",\n\t\t\t\t\tdata: new TextEncoder().encode(\n\t\t\t\t\t\tJSON.stringify(\n\t\t\t\t\t\t\targs?.settings ?? defaultProjectSettings,\n\t\t\t\t\t\t\tundefined,\n\t\t\t\t\t\t\t2\n\t\t\t\t\t\t)\n\t\t\t\t\t),\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\tpath: \"/project_id\",\n\t\t\t\t\tdata: new TextEncoder().encode(v4()),\n\t\t\t\t},\n\t\t\t])\n\t\t\t.execute();\n\t\tconst blob = toBlob({ lix });\n\t\tlix.sqlite.close();\n\t\treturn blob;\n\t} catch (e) {\n\t\tconst error = new Error(`Failed to create new inlang project: ${e}`, {\n\t\t\tcause: e,\n\t\t});\n\t\tcaptureError(error);\n\t\tthrow error;\n\t} finally {\n\t\tsqlite.close();\n\t}\n}\n\nexport const defaultProjectSettings = {\n\t$schema: \"https://inlang.com/schema/project-settings\",\n\tbaseLocale: \"en\",\n\tlocales: [\"en\"],\n\tmodules: [\n\t\t// for instant gratification, we're adding common rules\n\t\t// \"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-empty-pattern@latest/dist/index.js\",\n\t\t// \"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-missing-translation@latest/dist/index.js\",\n\t\t// \"https://cdn.jsdelivr.net/npm/@inlang/message-lint-rule-without-source@latest/dist/index.js\",\n\t\t// default to the message format plugin because it supports all features\n\t\t// \"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@latest/dist/index.js\",\n\t\t// the m function matcher should be installed by default in case Sherlock (VS Code extension) is adopted\n\t\t// \"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@latest/dist/index.js\",\n\t],\n} satisfies ProjectSettings;\n"],"names":[],"mappings":";;AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,EAAE,EAAE,MAAM,MAAM,CAAC;AAE1B,OAAO,EACN,mBAAmB,EACnB,sBAAsB,GACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAEhC;IACA,MAAM,MAAM,GAAG,MAAM,sBAAsB,CAAC;QAC3C,QAAQ,EAAE,KAAK;KACf,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAEnB,IAAI,CAAC;QACJ,MAAM,eAAe,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;QAEpD,MAAM,GAAG,GAAG,MAAM,eAAe,CAAC,EAAE,IAAI,EAAE,MAAM,UAAU,EAAE,EAAE,CAAC,CAAC;QAEhE,qBAAqB;QACrB,MAAM,GAAG,CAAC,EAAE;aACV,UAAU,CAAC,MAAM,CAAC;aAClB,MAAM,CAAC;YACP;gBACC,IAAI,EAAE,YAAY;gBAClB,IAAI,EAAE,eAAe;aACrB;YACD;gBACC,IAAI,EAAE,gBAAgB;gBACtB,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAC7B,IAAI,CAAC,SAAS,CACb,IAAI,EAAE,QAAQ,IAAI,sBAAsB,EACxC,SAAS,EACT,CAAC,CACD,CACD;aACD;YACD;gBACC,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;aACpC;SACD,CAAC;aACD,OAAO,EAAE,CAAC;QACZ,MAAM,IAAI,GAAG,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7B,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACnB,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,wCAAwC,CAAC,EAAE,EAAE;YACpE,KAAK,EAAE,CAAC;SACR,CAAC,CAAC;QACH,YAAY,CAAC,KAAK,CAAC,CAAC;QACpB,MAAM,KAAK,CAAC;IACb,CAAC;YAAS,CAAC;QACV,MAAM,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC;AACF,CAAC;AAED,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACrC,OAAO,EAAE,4CAA4C;IACrD,UAAU,EAAE,IAAI;IAChB,OAAO,EAAE,CAAC,IAAI,CAAC;IACf,OAAO,EAAE;IACR,uDAAuD;IACvD,+FAA+F;IAC/F,qGAAqG;IACrG,gGAAgG;IAChG,wEAAwE;IACxE,qFAAqF;IACrF,wGAAwG;IACxG,yFAAyF;KACzF;CACyB,CAAC","debug_id":"1c391748-b1e4-573f-a7e4-1387f21b0283"}
@@ -1 +1 @@
1
- {"version":3,"file":"saveProjectToDirectory.d.ts","sourceRoot":"/","sources":["project/saveProjectToDirectory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAU9C,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IAClD,EAAE,EAAE,OAAO,EAAE,CAAC;IACd,OAAO,EAAE,aAAa,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CAmFhB"}
1
+ {"version":3,"file":"saveProjectToDirectory.d.ts","sourceRoot":"/","sources":["project/saveProjectToDirectory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAU9C,wBAAsB,sBAAsB,CAAC,IAAI,EAAE;IAClD,EAAE,EAAE,OAAO,EAAE,CAAC;IACd,OAAO,EAAE,aAAa,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACb,GAAG,OAAO,CAAC,IAAI,CAAC,CA8FhB"}
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="a5af0acc-9549-52c7-ab73-3f703b45ebce")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0df5356b-8797-5431-8ab9-557514db2181")}catch(e){}}();
3
3
  import path from "node:path";
4
4
  import { toMessageV1 } from "../json-schema/old-v1-message/toMessageV1.js";
5
5
  import { absolutePathFromProject, withAbsolutePaths, } from "./loadProjectFromDirectory.js";
@@ -13,15 +13,22 @@ export async function saveProjectToDirectory(args) {
13
13
  .selectFrom("file")
14
14
  .selectAll()
15
15
  .execute();
16
+ let hasGitignore = false;
16
17
  // write all files to the directory
17
18
  for (const file of files) {
18
19
  if (file.path.endsWith("db.sqlite")) {
19
20
  continue;
20
21
  }
22
+ else if (file.path.endsWith(".gitignore")) {
23
+ hasGitignore = true;
24
+ }
21
25
  const p = path.join(args.path, file.path);
22
26
  await args.fs.mkdir(path.dirname(p), { recursive: true });
23
27
  await args.fs.writeFile(p, new Uint8Array(file.data));
24
28
  }
29
+ if (hasGitignore === false) {
30
+ await args.fs.writeFile(path.join(args.path, ".gitignore"), new TextEncoder().encode("cache"));
31
+ }
25
32
  // run exporters
26
33
  const plugins = await args.project.plugins.get();
27
34
  const settings = await args.project.settings.get();
@@ -83,4 +90,4 @@ export async function saveProjectToDirectory(args) {
83
90
  }
84
91
  }
85
92
  //# sourceMappingURL=saveProjectToDirectory.js.map
86
- //# debugId=a5af0acc-9549-52c7-ab73-3f703b45ebce
93
+ //# debugId=0df5356b-8797-5431-8ab9-557514db2181
@@ -1 +1 @@
1
- {"version":3,"file":"saveProjectToDirectory.js","sources":["project/saveProjectToDirectory.ts"],"sourceRoot":"/","sourcesContent":["/* eslint-disable no-restricted-imports */\nimport type fs from \"node:fs/promises\";\nimport type { InlangProject } from \"./api.js\";\nimport path from \"node:path\";\nimport { toMessageV1 } from \"../json-schema/old-v1-message/toMessageV1.js\";\nimport {\n\tabsolutePathFromProject,\n\twithAbsolutePaths,\n} from \"./loadProjectFromDirectory.js\";\nimport { detectJsonFormatting } from \"../utilities/detectJsonFormatting.js\";\nimport { selectBundleNested } from \"../query-utilities/selectBundleNested.js\";\n\nexport async function saveProjectToDirectory(args: {\n\tfs: typeof fs;\n\tproject: InlangProject;\n\tpath: string;\n}): Promise<void> {\n\tif (args.path.endsWith(\".inlang\") === false) {\n\t\tthrow new Error(\"The path must end with .inlang\");\n\t}\n\tconst files = await args.project.lix.db\n\t\t.selectFrom(\"file\")\n\t\t.selectAll()\n\t\t.execute();\n\n\t// write all files to the directory\n\tfor (const file of files) {\n\t\tif (file.path.endsWith(\"db.sqlite\")) {\n\t\t\tcontinue;\n\t\t}\n\t\tconst p = path.join(args.path, file.path);\n\t\tawait args.fs.mkdir(path.dirname(p), { recursive: true });\n\t\tawait args.fs.writeFile(p, new Uint8Array(file.data));\n\t}\n\n\t// run exporters\n\tconst plugins = await args.project.plugins.get();\n\tconst settings = await args.project.settings.get();\n\n\tfor (const plugin of plugins) {\n\t\t// old legacy remove with v3\n\t\tif (plugin.saveMessages) {\n\t\t\t// in-efficient re-qeuery but it's a legacy function that will be removed.\n\t\t\t// the effort of adjusting the code to not re-query is not worth it.\n\t\t\tconst bundlesNested = await selectBundleNested(args.project.db).execute();\n\t\t\tawait plugin.saveMessages({\n\t\t\t\tmessages: bundlesNested.map((b) => toMessageV1(b)),\n\t\t\t\t// @ts-expect-error - legacy\n\t\t\t\tnodeishFs: withAbsolutePaths(args.fs, args.path),\n\t\t\t\tsettings,\n\t\t\t});\n\t\t}\n\n\t\tif (plugin.exportFiles) {\n\t\t\tconst bundles = await args.project.db\n\t\t\t\t.selectFrom(\"bundle\")\n\t\t\t\t.selectAll()\n\t\t\t\t.execute();\n\t\t\tconst messages = await args.project.db\n\t\t\t\t.selectFrom(\"message\")\n\t\t\t\t.selectAll()\n\t\t\t\t.execute();\n\t\t\tconst variants = await args.project.db\n\t\t\t\t.selectFrom(\"variant\")\n\t\t\t\t.selectAll()\n\t\t\t\t.execute();\n\t\t\tconst files = await plugin.exportFiles({\n\t\t\t\tbundles,\n\t\t\t\tmessages,\n\t\t\t\tvariants,\n\t\t\t\tsettings,\n\t\t\t});\n\t\t\tfor (const file of files) {\n\t\t\t\tconst p = absolutePathFromProject(args.path, file.name);\n\t\t\t\tconst dirname = path.dirname(p);\n\t\t\t\tif ((await args.fs.stat(dirname)).isDirectory() === false) {\n\t\t\t\t\tawait args.fs.mkdir(dirname, { recursive: true });\n\t\t\t\t}\n\t\t\t\tif (p.endsWith(\".json\")) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst existing = await args.fs.readFile(p, \"utf-8\");\n\t\t\t\t\t\tconst stringify = detectJsonFormatting(existing);\n\t\t\t\t\t\tawait args.fs.writeFile(\n\t\t\t\t\t\t\tp,\n\t\t\t\t\t\t\tnew TextEncoder().encode(\n\t\t\t\t\t\t\t\tstringify(JSON.parse(new TextDecoder().decode(file.content)))\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// write the file to disk (json doesn't exist yet)\n\t\t\t\t\t\t// yeah ugly duplication of write file but it works.\n\t\t\t\t\t\tawait args.fs.writeFile(p, new Uint8Array(file.content));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tawait args.fs.writeFile(p, new Uint8Array(file.content));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"],"names":[],"mappings":"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","debug_id":"a5af0acc-9549-52c7-ab73-3f703b45ebce"}
1
+ {"version":3,"file":"saveProjectToDirectory.js","sources":["project/saveProjectToDirectory.ts"],"sourceRoot":"/","sourcesContent":["import type fs from \"node:fs/promises\";\nimport type { InlangProject } from \"./api.js\";\nimport path from \"node:path\";\nimport { toMessageV1 } from \"../json-schema/old-v1-message/toMessageV1.js\";\nimport {\n\tabsolutePathFromProject,\n\twithAbsolutePaths,\n} from \"./loadProjectFromDirectory.js\";\nimport { detectJsonFormatting } from \"../utilities/detectJsonFormatting.js\";\nimport { selectBundleNested } from \"../query-utilities/selectBundleNested.js\";\n\nexport async function saveProjectToDirectory(args: {\n\tfs: typeof fs;\n\tproject: InlangProject;\n\tpath: string;\n}): Promise<void> {\n\tif (args.path.endsWith(\".inlang\") === false) {\n\t\tthrow new Error(\"The path must end with .inlang\");\n\t}\n\tconst files = await args.project.lix.db\n\t\t.selectFrom(\"file\")\n\t\t.selectAll()\n\t\t.execute();\n\n\tlet hasGitignore = false;\n\n\t// write all files to the directory\n\tfor (const file of files) {\n\t\tif (file.path.endsWith(\"db.sqlite\")) {\n\t\t\tcontinue;\n\t\t} else if (file.path.endsWith(\".gitignore\")) {\n\t\t\thasGitignore = true;\n\t\t}\n\t\tconst p = path.join(args.path, file.path);\n\t\tawait args.fs.mkdir(path.dirname(p), { recursive: true });\n\t\tawait args.fs.writeFile(p, new Uint8Array(file.data));\n\t}\n\n\tif (hasGitignore === false) {\n\t\tawait args.fs.writeFile(\n\t\t\tpath.join(args.path, \".gitignore\"),\n\t\t\tnew TextEncoder().encode(\"cache\")\n\t\t);\n\t}\n\n\t// run exporters\n\tconst plugins = await args.project.plugins.get();\n\tconst settings = await args.project.settings.get();\n\n\tfor (const plugin of plugins) {\n\t\t// old legacy remove with v3\n\t\tif (plugin.saveMessages) {\n\t\t\t// in-efficient re-qeuery but it's a legacy function that will be removed.\n\t\t\t// the effort of adjusting the code to not re-query is not worth it.\n\t\t\tconst bundlesNested = await selectBundleNested(args.project.db).execute();\n\t\t\tawait plugin.saveMessages({\n\t\t\t\tmessages: bundlesNested.map((b) => toMessageV1(b)),\n\t\t\t\t// @ts-expect-error - legacy\n\t\t\t\tnodeishFs: withAbsolutePaths(args.fs, args.path),\n\t\t\t\tsettings,\n\t\t\t});\n\t\t}\n\n\t\tif (plugin.exportFiles) {\n\t\t\tconst bundles = await args.project.db\n\t\t\t\t.selectFrom(\"bundle\")\n\t\t\t\t.selectAll()\n\t\t\t\t.execute();\n\t\t\tconst messages = await args.project.db\n\t\t\t\t.selectFrom(\"message\")\n\t\t\t\t.selectAll()\n\t\t\t\t.execute();\n\t\t\tconst variants = await args.project.db\n\t\t\t\t.selectFrom(\"variant\")\n\t\t\t\t.selectAll()\n\t\t\t\t.execute();\n\t\t\tconst files = await plugin.exportFiles({\n\t\t\t\tbundles,\n\t\t\t\tmessages,\n\t\t\t\tvariants,\n\t\t\t\tsettings,\n\t\t\t});\n\t\t\tfor (const file of files) {\n\t\t\t\tconst p = absolutePathFromProject(args.path, file.name);\n\t\t\t\tconst dirname = path.dirname(p);\n\t\t\t\tif ((await args.fs.stat(dirname)).isDirectory() === false) {\n\t\t\t\t\tawait args.fs.mkdir(dirname, { recursive: true });\n\t\t\t\t}\n\t\t\t\tif (p.endsWith(\".json\")) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst existing = await args.fs.readFile(p, \"utf-8\");\n\t\t\t\t\t\tconst stringify = detectJsonFormatting(existing);\n\t\t\t\t\t\tawait args.fs.writeFile(\n\t\t\t\t\t\t\tp,\n\t\t\t\t\t\t\tnew TextEncoder().encode(\n\t\t\t\t\t\t\t\tstringify(JSON.parse(new TextDecoder().decode(file.content)))\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t);\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// write the file to disk (json doesn't exist yet)\n\t\t\t\t\t\t// yeah ugly duplication of write file but it works.\n\t\t\t\t\t\tawait args.fs.writeFile(p, new Uint8Array(file.content));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tawait args.fs.writeFile(p, new Uint8Array(file.content));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"],"names":[],"mappings":"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","debug_id":"0df5356b-8797-5431-8ab9-557514db2181"}
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="122e013b-eddd-5263-bffe-5c8db572bc32")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="678660ca-c6c8-5d55-81d3-15e051507e75")}catch(e){}}();
3
3
  import { test, expect, vi } from "vitest";
4
4
  import { saveProjectToDirectory } from "./saveProjectToDirectory.js";
5
5
  import { Volume } from "memfs";
@@ -241,5 +241,18 @@ test("it should preserve the formatting of existing json resource files", async
241
241
  const fileAfterSave = await volume.promises.readFile("/foo/en.json", "utf-8");
242
242
  expect(fileAfterSave).toBe(mockJson);
243
243
  });
244
+ test("adds a gitignore file if it doesn't exist", async () => {
245
+ const fs = Volume.fromJSON({});
246
+ const project = await loadProjectInMemory({
247
+ blob: await newProject(),
248
+ });
249
+ await saveProjectToDirectory({
250
+ fs: fs.promises,
251
+ project,
252
+ path: "/foo/bar.inlang",
253
+ });
254
+ const gitignore = await fs.promises.readFile("/foo/bar.inlang/.gitignore", "utf-8");
255
+ expect(gitignore).toBe("cache");
256
+ });
244
257
  //# sourceMappingURL=saveProjectToDirectory.test.js.map
245
- //# debugId=122e013b-eddd-5263-bffe-5c8db572bc32
258
+ //# debugId=678660ca-c6c8-5d55-81d3-15e051507e75
@@ -1 +1 @@
1
- {"version":3,"file":"saveProjectToDirectory.test.js","sources":["project/saveProjectToDirectory.test.ts"],"sourceRoot":"/","sourcesContent":["import { test, expect, vi } from \"vitest\";\nimport { saveProjectToDirectory } from \"./saveProjectToDirectory.js\";\nimport { Volume } from \"memfs\";\nimport { loadProjectInMemory } from \"./loadProjectInMemory.js\";\nimport { newProject } from \"./newProject.js\";\nimport type { InlangPlugin } from \"../plugin/schema.js\";\nimport type { Bundle, NewMessage, Variant } from \"../database/schema.js\";\nimport { loadProjectFromDirectory } from \"./loadProjectFromDirectory.js\";\nimport { selectBundleNested } from \"../query-utilities/selectBundleNested.js\";\nimport type { ProjectSettings } from \"../json-schema/settings.js\";\nimport type { MessageV1 } from \"../json-schema/old-v1-message/schemaV1.js\";\n\ntest(\"it should throw if the path doesn't end with .inlang\", async () => {\n\tawait expect(() =>\n\t\tsaveProjectToDirectory({\n\t\t\tfs: {} as any,\n\t\t\tproject: {} as any,\n\t\t\tpath: \"/foo/bar\",\n\t\t})\n\t).rejects.toThrowError(\"The path must end with .inlang\");\n});\n\ntest(\"it should overwrite all files to the directory except the db.sqlite file\", async () => {\n\tconst mockFs = Volume.fromJSON({\n\t\t\"/foo/bar.inlang/settings.json\": JSON.stringify({\n\t\t\tbaseLocale: \"en\",\n\t\t\tlocales: [\"en\"],\n\t\t}),\n\t}).promises as any;\n\n\tconst project = await loadProjectInMemory({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"mock\"],\n\t\t\t},\n\t\t}),\n\t});\n\n\tawait saveProjectToDirectory({\n\t\tfs: mockFs,\n\t\tproject,\n\t\tpath: \"/foo/bar.inlang\",\n\t});\n\n\tconst files = await mockFs.readdir(\"/foo/bar.inlang\");\n\tconst updatedSettingsFile = await mockFs.readFile(\n\t\t\"/foo/bar.inlang/settings.json\",\n\t\t\"utf-8\"\n\t);\n\tconst updatedSettings = JSON.parse(updatedSettingsFile);\n\n\t// only testing known files at the time of the test.\n\t// this test should be updated for files that should NOT\n\t// be contained in the directory in the future\n\texpect(files).toContain(\"settings.json\");\n\texpect(files).not.toContain(\"db.sqlite\");\n\texpect(updatedSettings.baseLocale).toBe(\"en\");\n\texpect(updatedSettings.locales).toEqual([\"en\", \"fr\", \"mock\"]);\n});\n\ntest(\"a roundtrip should work\", async () => {\n\tconst bundles: Bundle[] = [{ id: \"mock-bundle\", declarations: [] }];\n\tconst messages: NewMessage[] = [{ bundleId: \"mock-bundle\", locale: \"en\" }];\n\tconst variants: Variant[] = [];\n\n\tconst volume = Volume.fromJSON({\n\t\t\"/mock-file.json\": JSON.stringify({ bundles, messages, variants }),\n\t});\n\n\tconst mockPlugin: InlangPlugin = {\n\t\tkey: \"mock-plugin\",\n\t\ttoBeImportedFiles: async () => {\n\t\t\treturn [{ path: \"/mock-file.json\", locale: \"mock\" }];\n\t\t},\n\t\timportFiles: async ({ files }) => {\n\t\t\tconst { bundles, messages, variants } = JSON.parse(\n\t\t\t\tnew TextDecoder().decode(files[0]?.content)\n\t\t\t);\n\t\t\treturn { bundles, messages, variants };\n\t\t},\n\t\texportFiles: async ({ bundles }) => {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tcontent: new TextEncoder().encode(JSON.stringify(bundles)),\n\t\t\t\t\tname: \"mock-file.json\",\n\t\t\t\t\tlocale: \"mock\",\n\t\t\t\t},\n\t\t\t];\n\t\t},\n\t};\n\n\tconst exportFilesSpy = vi.spyOn(mockPlugin, \"exportFiles\");\n\tconst importFilesSpy = vi.spyOn(mockPlugin, \"importFiles\");\n\n\tconst project = await loadProjectInMemory({\n\t\tblob: await newProject(),\n\t\tprovidePlugins: [mockPlugin],\n\t});\n\n\tawait project.db.insertInto(\"bundle\").values(bundles).execute();\n\tawait project.db.insertInto(\"message\").values(messages).execute();\n\n\tawait saveProjectToDirectory({\n\t\tfs: volume.promises as any,\n\t\tproject,\n\t\tpath: \"/foo/bar.inlang\",\n\t});\n\n\t// const fileTree = volume.toJSON();\n\n\texpect(exportFilesSpy).toHaveBeenCalled();\n\texpect(importFilesSpy).not.toHaveBeenCalled();\n\n\t// TODO deactivated since mockBundleNested no longer contains the id of the messages\n\t// expect(fileTree).toEqual(\n\t// \texpect.objectContaining({\n\t// \t\t\"/foo/mock-file.json\": JSON.stringify([mockBundleNested]),\n\t// \t})\n\t// );\n\n\t// testing roundtrip\n\n\tconst project2 = await loadProjectFromDirectory({\n\t\tfs: volume as any,\n\t\tpath: \"/foo/bar.inlang\",\n\t\tprovidePlugins: [mockPlugin],\n\t});\n\n\texpect(mockPlugin.importFiles).toHaveBeenCalled();\n\n\tconst bundlesAfter = await project2.db\n\t\t.selectFrom(\"bundle\")\n\t\t.selectAll()\n\t\t.execute();\n\tconst messagesAfter = await project2.db\n\t\t.selectFrom(\"message\")\n\t\t.selectAll()\n\t\t.execute();\n\tconst variantsAfter = await project2.db\n\t\t.selectFrom(\"variant\")\n\t\t.selectAll()\n\t\t.execute();\n\n\texpect(bundlesAfter).lengthOf(1);\n\texpect(messagesAfter).lengthOf(1);\n\texpect(variantsAfter).lengthOf(0);\n\n\texpect(bundlesAfter[0]).toStrictEqual(expect.objectContaining(bundles[0]));\n\texpect(messagesAfter[0]).toStrictEqual(\n\t\texpect.objectContaining(messagesAfter[0])\n\t);\n});\n\ntest.todo(\n\t\"a roundtrip with legacy load and save messages should work\",\n\tasync () => {\n\t\tconst mockMessageV1: MessageV1 = {\n\t\t\tid: \"mock-legacy-message\",\n\t\t\talias: {},\n\t\t\tselectors: [],\n\t\t\tvariants: [\n\t\t\t\t{\n\t\t\t\t\tlanguageTag: \"en\",\n\t\t\t\t\tmatch: [],\n\t\t\t\t\tpattern: [{ type: \"Text\", value: \"Hello from legacy message\" }],\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\n\t\tconst volume = Volume.fromJSON({\n\t\t\t\"/foo/bar.inlang/settings.json\": JSON.stringify({\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\"],\n\t\t\t} satisfies ProjectSettings),\n\t\t\t\"/foo/i18n/en.json\": JSON.stringify([mockMessageV1]),\n\t\t});\n\n\t\tconst mockPlugin: InlangPlugin = {\n\t\t\tid: \"mock-legacy-plugin\",\n\t\t\tkey: \"mock-legacy-plugin\",\n\t\t\tloadMessages: async ({ nodeishFs }) => {\n\t\t\t\t// expecting `loadMessages` to transform the relative path\n\t\t\t\t// to an absolute path `./i18n/en.json` -> `/foo/i18n/en.json`\n\t\t\t\tconst file = await nodeishFs.readFile(\"./i18n/en.json\", {\n\t\t\t\t\tencoding: \"utf-8\",\n\t\t\t\t});\n\t\t\t\treturn JSON.parse(file as string);\n\t\t\t},\n\t\t\tsaveMessages: async ({ messages, nodeishFs }) => {\n\t\t\t\tawait nodeishFs.writeFile(\n\t\t\t\t\t\"./i18n/en.json\",\n\t\t\t\t\tnew TextEncoder().encode(JSON.stringify(messages))\n\t\t\t\t\t\t.buffer as ArrayBuffer\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\n\t\tconst loadMessagesSpy = vi.spyOn(mockPlugin, \"loadMessages\");\n\t\tconst saveMessagesSpy = vi.spyOn(mockPlugin, \"saveMessages\");\n\n\t\tconst project = await loadProjectFromDirectory({\n\t\t\tfs: volume as any,\n\t\t\tpath: \"/foo/bar.inlang\",\n\t\t\tprovidePlugins: [mockPlugin],\n\t\t});\n\n\t\texpect(loadMessagesSpy).toHaveBeenCalled();\n\t\texpect(saveMessagesSpy).not.toHaveBeenCalled();\n\n\t\tconst bundles1 = await selectBundleNested(project.db).execute();\n\n\t\texpect(bundles1[0]?.messages).lengthOf(1);\n\t\texpect(bundles1[0]?.messages[0]?.variants).toEqual([\n\t\t\texpect.objectContaining({\n\t\t\t\tpattern: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\tvalue: \"Hello from legacy message\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t]);\n\n\t\t// await project.db\n\t\t// \t.updateTable(\"variant\")\n\t\t// \t.set({\n\t\t// \t\tpattern: [{ type: \"text\", value: \"Updated message\" }],\n\t\t// \t})\n\t\t// \t.where(\"id\", \"=\", bundles1[0]?.messages[0]?.variants[0]?.id as string)\n\t\t// \t.execute();\n\n\t\t// testing the saveMessages function by removing the en.json file\n\t\tawait volume.promises.rm(\"/foo/i18n/en.json\");\n\n\t\tawait saveProjectToDirectory({\n\t\t\tfs: volume.promises as any,\n\t\t\tproject,\n\t\t\tpath: \"/foo/bar.inlang\",\n\t\t});\n\n\t\texpect(saveMessagesSpy).toHaveBeenCalled();\n\n\t\tconst fileTree = volume.toJSON();\n\t\tconst parsed = JSON.parse(fileTree[\"/foo/i18n/en.json\"] as string);\n\n\t\texpect(parsed).toEqual(expect.objectContaining([mockMessageV1]));\n\n\t\t// testing roundtrip\n\n\t\tconst project2 = await loadProjectFromDirectory({\n\t\t\tfs: volume as any,\n\t\t\tpath: \"/foo/bar.inlang\",\n\t\t\tprovidePlugins: [mockPlugin],\n\t\t});\n\n\t\tconst bundles2 = await selectBundleNested(project2.db).execute();\n\n\t\t// TODO deactivated since the ids must not be equal for separate imports - matching happens on language and matcher now\n\t\texpect(bundles1).toStrictEqual(bundles2);\n\t}\n);\n\ntest(\"it should preserve the formatting of existing json resource files\", async () => {\n\tconst mockJson =\n\t\tJSON.stringify(\n\t\t\t{ key: \"value\" },\n\t\t\tundefined,\n\t\t\t// tab spacing\n\t\t\t\"\\t\"\n\t\t) +\n\t\t// ends with new line\n\t\t\"\\n\";\n\n\tconst mockPlugin: InlangPlugin = {\n\t\tkey: \"mock\",\n\t\texportFiles: async () => {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tname: \"en.json\",\n\t\t\t\t\t// no beautified json\n\t\t\t\t\tcontent: new TextEncoder().encode(JSON.stringify({ key: \"value\" })),\n\t\t\t\t\tlocale: \"en\",\n\t\t\t\t},\n\t\t\t];\n\t\t},\n\t};\n\n\tconst volume = Volume.fromJSON({\n\t\t\"/foo/project.inlang/settings.json\": JSON.stringify({\n\t\t\tbaseLocale: \"en\",\n\t\t\tlocales: [\"en\"],\n\t\t} satisfies ProjectSettings),\n\t\t\"/foo/en.json\": mockJson,\n\t});\n\n\tconst project = await loadProjectInMemory({\n\t\tblob: await newProject(),\n\t\tprovidePlugins: [mockPlugin],\n\t});\n\n\tawait saveProjectToDirectory({\n\t\tpath: \"/foo/project.inlang\",\n\t\tfs: volume.promises as any,\n\t\tproject,\n\t});\n\n\tconst fileAfterSave = await volume.promises.readFile(\"/foo/en.json\", \"utf-8\");\n\texpect(fileAfterSave).toBe(mockJson);\n});\n"],"names":[],"mappings":";;AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAG7C,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,0CAA0C,CAAC;AAI9E,IAAI,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;IACvE,MAAM,MAAM,CAAC,GAAG,EAAE,CACjB,sBAAsB,CAAC;QACtB,EAAE,EAAE,EAAS;QACb,OAAO,EAAE,EAAS;QAClB,IAAI,EAAE,UAAU;KAChB,CAAC,CACF,CAAC,OAAO,CAAC,YAAY,CAAC,gCAAgC,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;IAC3F,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC9B,+BAA+B,EAAE,IAAI,CAAC,SAAS,CAAC;YAC/C,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE,CAAC,IAAI,CAAC;SACf,CAAC;KACF,CAAC,CAAC,QAAe,CAAC;IAEnB,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC;QACzC,IAAI,EAAE,MAAM,UAAU,CAAC;YACtB,QAAQ,EAAE;gBACT,UAAU,EAAE,IAAI;gBAChB,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC;aAC7B;SACD,CAAC;KACF,CAAC,CAAC;IAEH,MAAM,sBAAsB,CAAC;QAC5B,EAAE,EAAE,MAAM;QACV,OAAO;QACP,IAAI,EAAE,iBAAiB;KACvB,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACtD,MAAM,mBAAmB,GAAG,MAAM,MAAM,CAAC,QAAQ,CAChD,+BAA+B,EAC/B,OAAO,CACP,CAAC;IACF,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAExD,oDAAoD;IACpD,wDAAwD;IACxD,8CAA8C;IAC9C,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;IACzC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;AAC/D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;IAC1C,MAAM,OAAO,GAAa,CAAC,EAAE,EAAE,EAAE,aAAa,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,CAAC;IACpE,MAAM,QAAQ,GAAiB,CAAC,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC9B,iBAAiB,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;KAClE,CAAC,CAAC;IAEH,MAAM,UAAU,GAAiB;QAChC,GAAG,EAAE,aAAa;QAClB,iBAAiB,EAAE,KAAK,IAAI,EAAE;YAC7B,OAAO,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,WAAW,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YAChC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,CACjD,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,CAC3C,CAAC;YACF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;QACxC,CAAC;QACD,WAAW,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;YAClC,OAAO;gBACN;oBACC,OAAO,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;oBAC1D,IAAI,EAAE,gBAAgB;oBACtB,MAAM,EAAE,MAAM;iBACd;aACD,CAAC;QACH,CAAC;KACD,CAAC;IAEF,MAAM,cAAc,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAC3D,MAAM,cAAc,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IAE3D,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC;QACzC,IAAI,EAAE,MAAM,UAAU,EAAE;QACxB,cAAc,EAAE,CAAC,UAAU,CAAC;KAC5B,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,CAAC;IAChE,MAAM,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;IAElE,MAAM,sBAAsB,CAAC;QAC5B,EAAE,EAAE,MAAM,CAAC,QAAe;QAC1B,OAAO;QACP,IAAI,EAAE,iBAAiB;KACvB,CAAC,CAAC;IAEH,oCAAoC;IAEpC,MAAM,CAAC,cAAc,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAC1C,MAAM,CAAC,cAAc,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAE9C,oFAAoF;IACpF,4BAA4B;IAC5B,6BAA6B;IAC7B,+DAA+D;IAC/D,MAAM;IACN,KAAK;IAEL,oBAAoB;IAEpB,MAAM,QAAQ,GAAG,MAAM,wBAAwB,CAAC;QAC/C,EAAE,EAAE,MAAa;QACjB,IAAI,EAAE,iBAAiB;QACvB,cAAc,EAAE,CAAC,UAAU,CAAC;KAC5B,CAAC,CAAC;IAEH,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAElD,MAAM,YAAY,GAAG,MAAM,QAAQ,CAAC,EAAE;SACpC,UAAU,CAAC,QAAQ,CAAC;SACpB,SAAS,EAAE;SACX,OAAO,EAAE,CAAC;IACZ,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,EAAE;SACrC,UAAU,CAAC,SAAS,CAAC;SACrB,SAAS,EAAE;SACX,OAAO,EAAE,CAAC;IACZ,MAAM,aAAa,GAAG,MAAM,QAAQ,CAAC,EAAE;SACrC,UAAU,CAAC,SAAS,CAAC;SACrB,SAAS,EAAE;SACX,OAAO,EAAE,CAAC;IAEZ,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IACjC,MAAM,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,CAAC,aAAa,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAElC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3E,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa,CACrC,MAAM,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CACzC,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,IAAI,CACR,4DAA4D,EAC5D,KAAK,IAAI,EAAE;IACV,MAAM,aAAa,GAAc;QAChC,EAAE,EAAE,qBAAqB;QACzB,KAAK,EAAE,EAAE;QACT,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE;YACT;gBACC,WAAW,EAAE,IAAI;gBACjB,KAAK,EAAE,EAAE;gBACT,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,2BAA2B,EAAE,CAAC;aAC/D;SACD;KACD,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC9B,+BAA+B,EAAE,IAAI,CAAC,SAAS,CAAC;YAC/C,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE,CAAC,IAAI,CAAC;SACW,CAAC;QAC5B,mBAAmB,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,aAAa,CAAC,CAAC;KACpD,CAAC,CAAC;IAEH,MAAM,UAAU,GAAiB;QAChC,EAAE,EAAE,oBAAoB;QACxB,GAAG,EAAE,oBAAoB;QACzB,YAAY,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE;YACrC,0DAA0D;YAC1D,8DAA8D;YAC9D,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE;gBACvD,QAAQ,EAAE,OAAO;aACjB,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAc,CAAC,CAAC;QACnC,CAAC;QACD,YAAY,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE,EAAE;YAC/C,MAAM,SAAS,CAAC,SAAS,CACxB,gBAAgB,EAChB,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;iBAChD,MAAqB,CACvB,CAAC;QACH,CAAC;KACD,CAAC;IAEF,MAAM,eAAe,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IAC7D,MAAM,eAAe,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IAE7D,MAAM,OAAO,GAAG,MAAM,wBAAwB,CAAC;QAC9C,EAAE,EAAE,MAAa;QACjB,IAAI,EAAE,iBAAiB;QACvB,cAAc,EAAE,CAAC,UAAU,CAAC;KAC5B,CAAC,CAAC;IAEH,MAAM,CAAC,eAAe,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAC3C,MAAM,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAE/C,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAEhE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC1C,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,OAAO,CAAC;QAClD,MAAM,CAAC,gBAAgB,CAAC;YACvB,OAAO,EAAE;gBACR;oBACC,IAAI,EAAE,MAAM;oBACZ,KAAK,EAAE,2BAA2B;iBAClC;aACD;SACD,CAAC;KACF,CAAC,CAAC;IAEH,mBAAmB;IACnB,2BAA2B;IAC3B,UAAU;IACV,2DAA2D;IAC3D,MAAM;IACN,0EAA0E;IAC1E,eAAe;IAEf,iEAAiE;IACjE,MAAM,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,mBAAmB,CAAC,CAAC;IAE9C,MAAM,sBAAsB,CAAC;QAC5B,EAAE,EAAE,MAAM,CAAC,QAAe;QAC1B,OAAO;QACP,IAAI,EAAE,iBAAiB;KACvB,CAAC,CAAC;IAEH,MAAM,CAAC,eAAe,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAE3C,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC;IACjC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,mBAAmB,CAAW,CAAC,CAAC;IAEnE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAEjE,oBAAoB;IAEpB,MAAM,QAAQ,GAAG,MAAM,wBAAwB,CAAC;QAC/C,EAAE,EAAE,MAAa;QACjB,IAAI,EAAE,iBAAiB;QACvB,cAAc,EAAE,CAAC,UAAU,CAAC;KAC5B,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;IAEjE,uHAAuH;IACvH,MAAM,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;AAC1C,CAAC,CACD,CAAC;AAEF,IAAI,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;IACpF,MAAM,QAAQ,GACb,IAAI,CAAC,SAAS,CACb,EAAE,GAAG,EAAE,OAAO,EAAE,EAChB,SAAS;IACT,cAAc;IACd,IAAI,CACJ;QACD,qBAAqB;QACrB,IAAI,CAAC;IAEN,MAAM,UAAU,GAAiB;QAChC,GAAG,EAAE,MAAM;QACX,WAAW,EAAE,KAAK,IAAI,EAAE;YACvB,OAAO;gBACN;oBACC,IAAI,EAAE,SAAS;oBACf,qBAAqB;oBACrB,OAAO,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;oBACnE,MAAM,EAAE,IAAI;iBACZ;aACD,CAAC;QACH,CAAC;KACD,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC;QAC9B,mCAAmC,EAAE,IAAI,CAAC,SAAS,CAAC;YACnD,UAAU,EAAE,IAAI;YAChB,OAAO,EAAE,CAAC,IAAI,CAAC;SACW,CAAC;QAC5B,cAAc,EAAE,QAAQ;KACxB,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC;QACzC,IAAI,EAAE,MAAM,UAAU,EAAE;QACxB,cAAc,EAAE,CAAC,UAAU,CAAC;KAC5B,CAAC,CAAC;IAEH,MAAM,sBAAsB,CAAC;QAC5B,IAAI,EAAE,qBAAqB;QAC3B,EAAE,EAAE,MAAM,CAAC,QAAe;QAC1B,OAAO;KACP,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;IAC9E,MAAM,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;AACtC,CAAC,CAAC,CAAC","debug_id":"122e013b-eddd-5263-bffe-5c8db572bc32"}
1
+ {"version":3,"file":"saveProjectToDirectory.test.js","sources":["project/saveProjectToDirectory.test.ts"],"sourceRoot":"/","sourcesContent":["import { test, expect, vi } from \"vitest\";\nimport { saveProjectToDirectory } from \"./saveProjectToDirectory.js\";\nimport { Volume } from \"memfs\";\nimport { loadProjectInMemory } from \"./loadProjectInMemory.js\";\nimport { newProject } from \"./newProject.js\";\nimport type { InlangPlugin } from \"../plugin/schema.js\";\nimport type { Bundle, NewMessage, Variant } from \"../database/schema.js\";\nimport { loadProjectFromDirectory } from \"./loadProjectFromDirectory.js\";\nimport { selectBundleNested } from \"../query-utilities/selectBundleNested.js\";\nimport type { ProjectSettings } from \"../json-schema/settings.js\";\nimport type { MessageV1 } from \"../json-schema/old-v1-message/schemaV1.js\";\n\ntest(\"it should throw if the path doesn't end with .inlang\", async () => {\n\tawait expect(() =>\n\t\tsaveProjectToDirectory({\n\t\t\tfs: {} as any,\n\t\t\tproject: {} as any,\n\t\t\tpath: \"/foo/bar\",\n\t\t})\n\t).rejects.toThrowError(\"The path must end with .inlang\");\n});\n\ntest(\"it should overwrite all files to the directory except the db.sqlite file\", async () => {\n\tconst mockFs = Volume.fromJSON({\n\t\t\"/foo/bar.inlang/settings.json\": JSON.stringify({\n\t\t\tbaseLocale: \"en\",\n\t\t\tlocales: [\"en\"],\n\t\t}),\n\t}).promises as any;\n\n\tconst project = await loadProjectInMemory({\n\t\tblob: await newProject({\n\t\t\tsettings: {\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\", \"fr\", \"mock\"],\n\t\t\t},\n\t\t}),\n\t});\n\n\tawait saveProjectToDirectory({\n\t\tfs: mockFs,\n\t\tproject,\n\t\tpath: \"/foo/bar.inlang\",\n\t});\n\n\tconst files = await mockFs.readdir(\"/foo/bar.inlang\");\n\tconst updatedSettingsFile = await mockFs.readFile(\n\t\t\"/foo/bar.inlang/settings.json\",\n\t\t\"utf-8\"\n\t);\n\tconst updatedSettings = JSON.parse(updatedSettingsFile);\n\n\t// only testing known files at the time of the test.\n\t// this test should be updated for files that should NOT\n\t// be contained in the directory in the future\n\texpect(files).toContain(\"settings.json\");\n\texpect(files).not.toContain(\"db.sqlite\");\n\texpect(updatedSettings.baseLocale).toBe(\"en\");\n\texpect(updatedSettings.locales).toEqual([\"en\", \"fr\", \"mock\"]);\n});\n\ntest(\"a roundtrip should work\", async () => {\n\tconst bundles: Bundle[] = [{ id: \"mock-bundle\", declarations: [] }];\n\tconst messages: NewMessage[] = [{ bundleId: \"mock-bundle\", locale: \"en\" }];\n\tconst variants: Variant[] = [];\n\n\tconst volume = Volume.fromJSON({\n\t\t\"/mock-file.json\": JSON.stringify({ bundles, messages, variants }),\n\t});\n\n\tconst mockPlugin: InlangPlugin = {\n\t\tkey: \"mock-plugin\",\n\t\ttoBeImportedFiles: async () => {\n\t\t\treturn [{ path: \"/mock-file.json\", locale: \"mock\" }];\n\t\t},\n\t\timportFiles: async ({ files }) => {\n\t\t\tconst { bundles, messages, variants } = JSON.parse(\n\t\t\t\tnew TextDecoder().decode(files[0]?.content)\n\t\t\t);\n\t\t\treturn { bundles, messages, variants };\n\t\t},\n\t\texportFiles: async ({ bundles }) => {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tcontent: new TextEncoder().encode(JSON.stringify(bundles)),\n\t\t\t\t\tname: \"mock-file.json\",\n\t\t\t\t\tlocale: \"mock\",\n\t\t\t\t},\n\t\t\t];\n\t\t},\n\t};\n\n\tconst exportFilesSpy = vi.spyOn(mockPlugin, \"exportFiles\");\n\tconst importFilesSpy = vi.spyOn(mockPlugin, \"importFiles\");\n\n\tconst project = await loadProjectInMemory({\n\t\tblob: await newProject(),\n\t\tprovidePlugins: [mockPlugin],\n\t});\n\n\tawait project.db.insertInto(\"bundle\").values(bundles).execute();\n\tawait project.db.insertInto(\"message\").values(messages).execute();\n\n\tawait saveProjectToDirectory({\n\t\tfs: volume.promises as any,\n\t\tproject,\n\t\tpath: \"/foo/bar.inlang\",\n\t});\n\n\t// const fileTree = volume.toJSON();\n\n\texpect(exportFilesSpy).toHaveBeenCalled();\n\texpect(importFilesSpy).not.toHaveBeenCalled();\n\n\t// TODO deactivated since mockBundleNested no longer contains the id of the messages\n\t// expect(fileTree).toEqual(\n\t// \texpect.objectContaining({\n\t// \t\t\"/foo/mock-file.json\": JSON.stringify([mockBundleNested]),\n\t// \t})\n\t// );\n\n\t// testing roundtrip\n\n\tconst project2 = await loadProjectFromDirectory({\n\t\tfs: volume as any,\n\t\tpath: \"/foo/bar.inlang\",\n\t\tprovidePlugins: [mockPlugin],\n\t});\n\n\texpect(mockPlugin.importFiles).toHaveBeenCalled();\n\n\tconst bundlesAfter = await project2.db\n\t\t.selectFrom(\"bundle\")\n\t\t.selectAll()\n\t\t.execute();\n\tconst messagesAfter = await project2.db\n\t\t.selectFrom(\"message\")\n\t\t.selectAll()\n\t\t.execute();\n\tconst variantsAfter = await project2.db\n\t\t.selectFrom(\"variant\")\n\t\t.selectAll()\n\t\t.execute();\n\n\texpect(bundlesAfter).lengthOf(1);\n\texpect(messagesAfter).lengthOf(1);\n\texpect(variantsAfter).lengthOf(0);\n\n\texpect(bundlesAfter[0]).toStrictEqual(expect.objectContaining(bundles[0]));\n\texpect(messagesAfter[0]).toStrictEqual(\n\t\texpect.objectContaining(messagesAfter[0])\n\t);\n});\n\ntest.todo(\n\t\"a roundtrip with legacy load and save messages should work\",\n\tasync () => {\n\t\tconst mockMessageV1: MessageV1 = {\n\t\t\tid: \"mock-legacy-message\",\n\t\t\talias: {},\n\t\t\tselectors: [],\n\t\t\tvariants: [\n\t\t\t\t{\n\t\t\t\t\tlanguageTag: \"en\",\n\t\t\t\t\tmatch: [],\n\t\t\t\t\tpattern: [{ type: \"Text\", value: \"Hello from legacy message\" }],\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\n\t\tconst volume = Volume.fromJSON({\n\t\t\t\"/foo/bar.inlang/settings.json\": JSON.stringify({\n\t\t\t\tbaseLocale: \"en\",\n\t\t\t\tlocales: [\"en\"],\n\t\t\t} satisfies ProjectSettings),\n\t\t\t\"/foo/i18n/en.json\": JSON.stringify([mockMessageV1]),\n\t\t});\n\n\t\tconst mockPlugin: InlangPlugin = {\n\t\t\tid: \"mock-legacy-plugin\",\n\t\t\tkey: \"mock-legacy-plugin\",\n\t\t\tloadMessages: async ({ nodeishFs }) => {\n\t\t\t\t// expecting `loadMessages` to transform the relative path\n\t\t\t\t// to an absolute path `./i18n/en.json` -> `/foo/i18n/en.json`\n\t\t\t\tconst file = await nodeishFs.readFile(\"./i18n/en.json\", {\n\t\t\t\t\tencoding: \"utf-8\",\n\t\t\t\t});\n\t\t\t\treturn JSON.parse(file as string);\n\t\t\t},\n\t\t\tsaveMessages: async ({ messages, nodeishFs }) => {\n\t\t\t\tawait nodeishFs.writeFile(\n\t\t\t\t\t\"./i18n/en.json\",\n\t\t\t\t\tnew TextEncoder().encode(JSON.stringify(messages))\n\t\t\t\t\t\t.buffer as ArrayBuffer\n\t\t\t\t);\n\t\t\t},\n\t\t};\n\n\t\tconst loadMessagesSpy = vi.spyOn(mockPlugin, \"loadMessages\");\n\t\tconst saveMessagesSpy = vi.spyOn(mockPlugin, \"saveMessages\");\n\n\t\tconst project = await loadProjectFromDirectory({\n\t\t\tfs: volume as any,\n\t\t\tpath: \"/foo/bar.inlang\",\n\t\t\tprovidePlugins: [mockPlugin],\n\t\t});\n\n\t\texpect(loadMessagesSpy).toHaveBeenCalled();\n\t\texpect(saveMessagesSpy).not.toHaveBeenCalled();\n\n\t\tconst bundles1 = await selectBundleNested(project.db).execute();\n\n\t\texpect(bundles1[0]?.messages).lengthOf(1);\n\t\texpect(bundles1[0]?.messages[0]?.variants).toEqual([\n\t\t\texpect.objectContaining({\n\t\t\t\tpattern: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\tvalue: \"Hello from legacy message\",\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t}),\n\t\t]);\n\n\t\t// await project.db\n\t\t// \t.updateTable(\"variant\")\n\t\t// \t.set({\n\t\t// \t\tpattern: [{ type: \"text\", value: \"Updated message\" }],\n\t\t// \t})\n\t\t// \t.where(\"id\", \"=\", bundles1[0]?.messages[0]?.variants[0]?.id as string)\n\t\t// \t.execute();\n\n\t\t// testing the saveMessages function by removing the en.json file\n\t\tawait volume.promises.rm(\"/foo/i18n/en.json\");\n\n\t\tawait saveProjectToDirectory({\n\t\t\tfs: volume.promises as any,\n\t\t\tproject,\n\t\t\tpath: \"/foo/bar.inlang\",\n\t\t});\n\n\t\texpect(saveMessagesSpy).toHaveBeenCalled();\n\n\t\tconst fileTree = volume.toJSON();\n\t\tconst parsed = JSON.parse(fileTree[\"/foo/i18n/en.json\"] as string);\n\n\t\texpect(parsed).toEqual(expect.objectContaining([mockMessageV1]));\n\n\t\t// testing roundtrip\n\n\t\tconst project2 = await loadProjectFromDirectory({\n\t\t\tfs: volume as any,\n\t\t\tpath: \"/foo/bar.inlang\",\n\t\t\tprovidePlugins: [mockPlugin],\n\t\t});\n\n\t\tconst bundles2 = await selectBundleNested(project2.db).execute();\n\n\t\t// TODO deactivated since the ids must not be equal for separate imports - matching happens on language and matcher now\n\t\texpect(bundles1).toStrictEqual(bundles2);\n\t}\n);\n\ntest(\"it should preserve the formatting of existing json resource files\", async () => {\n\tconst mockJson =\n\t\tJSON.stringify(\n\t\t\t{ key: \"value\" },\n\t\t\tundefined,\n\t\t\t// tab spacing\n\t\t\t\"\\t\"\n\t\t) +\n\t\t// ends with new line\n\t\t\"\\n\";\n\n\tconst mockPlugin: InlangPlugin = {\n\t\tkey: \"mock\",\n\t\texportFiles: async () => {\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\tname: \"en.json\",\n\t\t\t\t\t// no beautified json\n\t\t\t\t\tcontent: new TextEncoder().encode(JSON.stringify({ key: \"value\" })),\n\t\t\t\t\tlocale: \"en\",\n\t\t\t\t},\n\t\t\t];\n\t\t},\n\t};\n\n\tconst volume = Volume.fromJSON({\n\t\t\"/foo/project.inlang/settings.json\": JSON.stringify({\n\t\t\tbaseLocale: \"en\",\n\t\t\tlocales: [\"en\"],\n\t\t} satisfies ProjectSettings),\n\t\t\"/foo/en.json\": mockJson,\n\t});\n\n\tconst project = await loadProjectInMemory({\n\t\tblob: await newProject(),\n\t\tprovidePlugins: [mockPlugin],\n\t});\n\n\tawait saveProjectToDirectory({\n\t\tpath: \"/foo/project.inlang\",\n\t\tfs: volume.promises as any,\n\t\tproject,\n\t});\n\n\tconst fileAfterSave = await volume.promises.readFile(\"/foo/en.json\", \"utf-8\");\n\texpect(fileAfterSave).toBe(mockJson);\n});\n\ntest(\"adds a gitignore file if it doesn't exist\", async () => {\n\tconst fs = Volume.fromJSON({});\n\n\tconst project = await loadProjectInMemory({\n\t\tblob: await newProject(),\n\t});\n\n\tawait saveProjectToDirectory({\n\t\tfs: fs.promises as any,\n\t\tproject,\n\t\tpath: \"/foo/bar.inlang\",\n\t});\n\n\tconst gitignore = await fs.promises.readFile(\n\t\t\"/foo/bar.inlang/.gitignore\",\n\t\t\"utf-8\"\n\t);\n\texpect(gitignore).toBe(\"cache\");\n});\n"],"names":[],"mappings":"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","debug_id":"678660ca-c6c8-5d55-81d3-15e051507e75"}
@@ -1,9 +1,9 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="f3e983ce-c068-5854-aaf3-d4facbf432f5")}catch(e){}}();
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="59200f1b-8a13-58e1-8239-5a5836d1dfdc")}catch(e){}}();
3
3
  export const ENV_VARIABLES = {
4
4
  PUBLIC_POSTHOG_TOKEN: "phc_m5yJZCxjOGxF8CJvP5sQ3H0d76xpnLrsmiZHduT4jDz",
5
5
  PUBLIC_INLANG_SDK_SENTRY_DSN: "https://c3d92d5d011122e525e9f9b368e0905d@o4504345873285120.ingest.us.sentry.io/4507903389335553",
6
- SDK_VERSION: "2.0.0",
6
+ SDK_VERSION: "2.1.0",
7
7
  };
8
8
  //# sourceMappingURL=index.js.map
9
- //# debugId=f3e983ce-c068-5854-aaf3-d4facbf432f5
9
+ //# debugId=59200f1b-8a13-58e1-8239-5a5836d1dfdc
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["services/env-variables/index.ts"],"sourceRoot":"/","sourcesContent":["\nexport const ENV_VARIABLES = {\n PUBLIC_POSTHOG_TOKEN: \"phc_m5yJZCxjOGxF8CJvP5sQ3H0d76xpnLrsmiZHduT4jDz\",\n\tPUBLIC_INLANG_SDK_SENTRY_DSN: \"https://c3d92d5d011122e525e9f9b368e0905d@o4504345873285120.ingest.us.sentry.io/4507903389335553\",\n\tSDK_VERSION: \"2.0.0\",\n}\n"],"names":[],"mappings":";;AACA,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,oBAAoB,EAAE,iDAAiD;IACxE,4BAA4B,EAAE,iGAAiG;IAC/H,WAAW,EAAE,OAAO;CACpB,CAAA","debug_id":"f3e983ce-c068-5854-aaf3-d4facbf432f5"}
1
+ {"version":3,"file":"index.js","sources":["services/env-variables/index.ts"],"sourceRoot":"/","sourcesContent":["\nexport const ENV_VARIABLES = {\n PUBLIC_POSTHOG_TOKEN: \"phc_m5yJZCxjOGxF8CJvP5sQ3H0d76xpnLrsmiZHduT4jDz\",\n\tPUBLIC_INLANG_SDK_SENTRY_DSN: \"https://c3d92d5d011122e525e9f9b368e0905d@o4504345873285120.ingest.us.sentry.io/4507903389335553\",\n\tSDK_VERSION: \"2.1.0\",\n}\n"],"names":[],"mappings":";;AACA,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,oBAAoB,EAAE,iDAAiD;IACxE,4BAA4B,EAAE,iGAAiG;IAC/H,WAAW,EAAE,OAAO;CACpB,CAAA","debug_id":"59200f1b-8a13-58e1-8239-5a5836d1dfdc"}
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@inlang/sdk",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
7
- "access": "public",
8
- "tag": "beta"
7
+ "access": "public"
9
8
  },
10
9
  "homepage": "https://inlang.com/documentation/sdk",
11
10
  "repository": {
@@ -31,8 +30,8 @@
31
30
  "@sinclair/typebox": "^0.31.17",
32
31
  "kysely": "^0.27.4",
33
32
  "uuid": "^10.0.0",
34
- "sqlite-wasm-kysely": "0.1.1",
35
- "@lix-js/sdk": "0.3.2"
33
+ "@lix-js/sdk": "0.3.3",
34
+ "sqlite-wasm-kysely": "0.1.1"
36
35
  },
37
36
  "devDependencies": {
38
37
  "@eslint/js": "^9.12.0",
@@ -0,0 +1,26 @@
1
+ import { TypeCompiler } from "@sinclair/typebox/compiler";
2
+ import { ProjectSettings } from "./settings.js";
3
+ import { test, expect } from "vitest";
4
+
5
+ const C = TypeCompiler.Compile(ProjectSettings);
6
+
7
+ test("valid settings file", () => {
8
+ const settings: ProjectSettings = {
9
+ baseLocale: "en",
10
+ locales: ["en", "de"],
11
+ modules: [
12
+ "https://cdn.jsdelivr.net/npm/@inlang/plugin-i18next@3/dist/index.js",
13
+ "https://cdn.jsdelivr.net/npm/@inlang/plugin-csv@1/dist/index.js",
14
+ ],
15
+ "plugin.something": {
16
+ key: "value",
17
+ nested: {
18
+ moreNested: {},
19
+ },
20
+ },
21
+ };
22
+
23
+ const errors = [...C.Errors(settings)];
24
+
25
+ expect(errors).toEqual([]);
26
+ });
@@ -1,9 +1,4 @@
1
- import {
2
- type Static,
3
- Type,
4
- type TTemplateLiteral,
5
- type TLiteral,
6
- } from "@sinclair/typebox";
1
+ import { type Static, Type } from "@sinclair/typebox";
7
2
 
8
3
  const SDKSettings = Type.Object({
9
4
  // TODO SDK-v2 SETTINGS do we need to generate a settings v2 schema?
@@ -59,10 +54,6 @@ const SDKSettings = Type.Object({
59
54
  description: "The module must end with `.js`.",
60
55
  pattern: ".*\\.js$",
61
56
  }),
62
- Type.String({
63
- description: "The module can only contain a major version number.",
64
- pattern: "^(?!.*@\\d\\.)[^]*$",
65
- }),
66
57
  ]),
67
58
  {
68
59
  uniqueItems: true,
@@ -100,30 +91,6 @@ const SDKSettings = Type.Object({
100
91
  */
101
92
  });
102
93
 
103
- /**
104
- * Settings defined via apps, plugins, lint rules, etc.
105
- *
106
- * Using external settings to only allow `plugin.*` keys
107
- * and don't block the SDK from adding new settings.
108
- */
109
- const ExternalSettings = Type.Record(
110
- Type.String({
111
- pattern: `^((plugin)\\.([a-z][a-zA-Z0-9]*(?:[A-Z][a-z0-9]*)*)|\\$schema|${
112
- // pattern must include the settings properties
113
- Object.keys(SDKSettings.properties)
114
- .map((key) => key.replaceAll(".", "\\."))
115
- .join("|")
116
- })$`,
117
- description: "The key must be conform to `plugin.*`.",
118
- examples: ["plugin.csv-importer", "plugin.i18next"],
119
- }) as unknown as TTemplateLiteral<[TLiteral<`${"plugin"}.${string}`>]>,
120
- // Using JSON (array and object) as a workaround to make the
121
- // intersection between `InternalSettings`, which contains an array,
122
- // and `ExternalSettings` which are objects possible
123
- Type.Record(Type.String(), Type.Any()),
124
- { description: "Settings defined by apps, plugins, etc." }
125
- );
126
-
127
94
  export type ProjectSettings = Omit<
128
95
  Static<typeof ProjectSettings>,
129
96
  "languageTags" | "sourceLanguageTag"
@@ -134,5 +101,5 @@ export type ProjectSettings = Omit<
134
101
  languageTags?: string[];
135
102
  /** @deprecated This will soon be replaced by `Lix Validation Rules` */
136
103
  messageLintRuleLevels?: Record<string, "error" | "warning">;
137
- };
138
- export const ProjectSettings = Type.Intersect([SDKSettings, ExternalSettings]);
104
+ } & Record<string, any>;
105
+ export const ProjectSettings = SDKSettings;
@@ -22,7 +22,6 @@ test("it should preprocess a plugin", async () => {
22
22
  },
23
23
  });
24
24
 
25
- expect(global.fetch).toHaveBeenCalledTimes(1);
26
25
  expect(result.plugins.length).toBe(1);
27
26
  expect(result.errors.length).toBe(0);
28
27
  expect(result.plugins[0]?.key).toBe("preprocessed");
@@ -1,9 +1,8 @@
1
1
  import { toBlob, type Account, type Lix } from "@lix-js/sdk";
2
2
  import type { InlangPlugin } from "../plugin/schema.js";
3
3
  import type { ProjectSettings } from "../json-schema/settings.js";
4
- import { type SqliteDatabase } from "sqlite-wasm-kysely";
4
+ import { contentFromDatabase, type SqliteDatabase } from "sqlite-wasm-kysely";
5
5
  import { initDb } from "../database/initDb.js";
6
- import { initHandleSaveToLixOnChange } from "./initHandleSaveToLixOnChange.js";
7
6
  import {
8
7
  importPlugins,
9
8
  type PreprocessPluginBeforeImportFunction,
@@ -98,18 +97,6 @@ export async function loadProject(args: {
98
97
  // settings,
99
98
  // });
100
99
 
101
- // TODO implement garbage collection/a proper queue.
102
- // for the protoype and tests, it seems good enough
103
- // without garbage collection of old promises.
104
- const pendingSaveToLixPromises: Promise<unknown>[] = [];
105
-
106
- await initHandleSaveToLixOnChange({
107
- sqlite: args.sqlite,
108
- db,
109
- lix: args.lix,
110
- pendingPromises: pendingSaveToLixPromises,
111
- });
112
-
113
100
  // not awaiting to not block the load time of a project
114
101
  maybeCaptureLoadedProject({
115
102
  db,
@@ -210,18 +197,31 @@ export async function loadProject(args: {
210
197
  ).map((output) => ({ ...output, pluginKey }));
211
198
  },
212
199
  close: async () => {
200
+ await saveDbToLix({ sqlite: args.sqlite, lix: args.lix });
213
201
  await db.destroy();
214
202
  await args.lix.db.destroy();
215
203
  },
216
204
  _sqlite: args.sqlite,
217
205
  toBlob: async () => {
218
- await Promise.all(pendingSaveToLixPromises);
206
+ await saveDbToLix({ sqlite: args.sqlite, lix: args.lix });
219
207
  return await toBlob({ lix: args.lix });
220
208
  },
221
209
  lix: args.lix,
222
210
  };
223
211
  }
224
212
 
213
+ async function saveDbToLix(args: {
214
+ sqlite: SqliteDatabase;
215
+ lix: Lix;
216
+ }): Promise<void> {
217
+ const data = contentFromDatabase(args.sqlite);
218
+ await args.lix.db
219
+ .updateTable("file")
220
+ .set("data", data)
221
+ .where("path", "=", "/db.sqlite")
222
+ .execute();
223
+ }
224
+
225
225
  /**
226
226
  * Old leftover migration from v1. Probably not needed anymore.
227
227
  *
@@ -2,7 +2,8 @@
2
2
 
3
3
  import { beforeEach, describe, expect, test, vi } from "vitest";
4
4
  import { ProjectSettings } from "../json-schema/settings.js";
5
- import { fs, vol, Volume } from "memfs";
5
+ import { Volume } from "memfs";
6
+ import nodePath from "node:path";
6
7
  import {
7
8
  loadProjectFromDirectory,
8
9
  ResourceFileImportError,
@@ -292,6 +293,51 @@ describe("it should keep files between the inlang directory and lix in sync", as
292
293
  expect(filesByPath["/settings.json"]).toBe(JSON.stringify(mockSettings));
293
294
  });
294
295
 
296
+ // the test doesn't work on non-windows systems
297
+ // mocking the node:path to use backlashes has no effect
298
+ test.skip("files from directory should be available via lix if the OS uses backlashes as folder separators", async () => {
299
+ const mockWindowsDirectory = {
300
+ "\\project.inlang\\cache\\plugin\\29j49j2": "cache value",
301
+ "\\project.inlang\\.gitignore": "git value",
302
+ "\\project.inlang\\prettierrc.json": "prettier value",
303
+ "\\project.inlang\\README.md": "readme value",
304
+ "\\project.inlang\\settings.json": JSON.stringify(mockSettings),
305
+ };
306
+
307
+ // Temporarily set the platform to win32
308
+ Object.defineProperty(process, "platform", {
309
+ value: "win32",
310
+ });
311
+
312
+ Object.defineProperty(nodePath, "sep", {
313
+ value: "\\",
314
+ });
315
+
316
+ const fs = Volume.fromJSON(mockWindowsDirectory);
317
+
318
+ const project = await loadProjectFromDirectory({
319
+ fs: fs as any,
320
+ path: "\\project.inlang",
321
+ });
322
+
323
+ const files = await project.lix.db.selectFrom("file").selectAll().execute();
324
+
325
+ expect(files.length).toBe(
326
+ 5 + 1 /* the db.sqlite file */ + 1 /* project_id */
327
+ );
328
+
329
+ const filesByPath = files.reduce((acc, file) => {
330
+ acc[file.path] = new TextDecoder().decode(file.data);
331
+ return acc;
332
+ }, {} as any);
333
+
334
+ expect(filesByPath["/cache/plugin/29j49j2"]).toBe("cache value");
335
+ expect(filesByPath["/.gitignore"]).toBe("git value");
336
+ expect(filesByPath["/prettierrc.json"]).toBe("prettier value");
337
+ expect(filesByPath["/README.md"]).toBe("readme value");
338
+ expect(filesByPath["/settings.json"]).toBe(JSON.stringify(mockSettings));
339
+ });
340
+
295
341
  test("file created in fs should be avaialable in lix ", async () => {
296
342
  const syncInterval = 100;
297
343
  const fs = Volume.fromJSON(mockDirectory);
@@ -658,7 +704,6 @@ test("it should pass toBeImportedMetadata on import", async () => {
658
704
  expect.objectContaining({
659
705
  files: [
660
706
  expect.objectContaining({
661
- name: "en.json",
662
707
  toBeImportedFilesMetadata: {
663
708
  foo: "bar",
664
709
  },
@@ -852,3 +897,72 @@ test("it can import plugins via http", async () => {
852
897
  "expecting the plugin to be cached"
853
898
  ).toBe(true);
854
899
  });
900
+
901
+ test("plugins that provide both loadMessages and importFiles should be allowed and the importFiles should be called", async () => {
902
+ const mockRepo = {
903
+ "/project.inlang/settings.json": JSON.stringify({
904
+ baseLocale: "en",
905
+ locales: ["en"],
906
+ modules: [],
907
+ } satisfies ProjectSettings),
908
+ };
909
+
910
+ const fs = Volume.fromJSON(mockRepo);
911
+
912
+ const mockPlugin: InlangPlugin = {
913
+ key: "mock-plugin",
914
+ loadMessages: vi.fn(async () => []),
915
+ importFiles: vi.fn(async () => {
916
+ return { bundles: [], messages: [], variants: [] };
917
+ }),
918
+ };
919
+
920
+ const project = await loadProjectFromDirectory({
921
+ fs: fs as any,
922
+ path: "/project.inlang",
923
+ providePlugins: [mockPlugin],
924
+ });
925
+
926
+ expect(mockPlugin.importFiles).toHaveBeenCalled();
927
+ expect(mockPlugin.loadMessages).not.toHaveBeenCalled();
928
+ });
929
+
930
+ test("providing multiple plugins that have legacy loadMessages and saveMessages function should be possible if they have import/export functions", async () => {
931
+ const mockRepo = {
932
+ "/project.inlang/settings.json": JSON.stringify({
933
+ baseLocale: "en",
934
+ locales: ["en"],
935
+ modules: [],
936
+ } satisfies ProjectSettings),
937
+ };
938
+
939
+ const fs = Volume.fromJSON(mockRepo);
940
+
941
+ const mockPlugin1: InlangPlugin = {
942
+ key: "mock-plugin1",
943
+ loadMessages: vi.fn(async () => []),
944
+ importFiles: vi.fn(async () => {
945
+ return { bundles: [], messages: [], variants: [] };
946
+ }),
947
+ };
948
+
949
+ const mockPlugin2: InlangPlugin = {
950
+ key: "mock-plugin2",
951
+ loadMessages: vi.fn(async () => []),
952
+ importFiles: vi.fn(async () => {
953
+ return { bundles: [], messages: [], variants: [] };
954
+ }),
955
+ };
956
+
957
+ const project = await loadProjectFromDirectory({
958
+ fs: fs as any,
959
+ path: "/project.inlang",
960
+ providePlugins: [mockPlugin1, mockPlugin2],
961
+ });
962
+
963
+ expect(mockPlugin1.importFiles).toHaveBeenCalled();
964
+ expect(mockPlugin1.loadMessages).not.toHaveBeenCalled();
965
+
966
+ expect(mockPlugin2.importFiles).toHaveBeenCalled();
967
+ expect(mockPlugin2.loadMessages).not.toHaveBeenCalled();
968
+ });