@inlang/sdk 2.9.2 → 2.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,4 @@
1
+ import type nodeFs from "node:fs";
1
2
  import type fs from "node:fs/promises";
2
3
  import type { InlangProject } from "./api.js";
3
4
  import path from "node:path";
@@ -18,6 +19,33 @@ async function fileExists(fsModule: typeof fs, filePath: string) {
18
19
  }
19
20
  }
20
21
 
22
+ type SaveProjectFs = typeof fs | typeof nodeFs;
23
+
24
+ function getPromisesFs(fsModule: SaveProjectFs): typeof fs {
25
+ return "promises" in fsModule ? fsModule.promises : fsModule;
26
+ }
27
+
28
+ async function assertTranslationDataCanBeExported(project: InlangProject) {
29
+ const plugins = await project.plugins.get();
30
+ const hasExporter = plugins.some(
31
+ (plugin) => plugin.exportFiles || plugin.saveMessages
32
+ );
33
+ if (hasExporter) {
34
+ return;
35
+ }
36
+
37
+ const [bundle, message, variant] = await Promise.all([
38
+ project.db.selectFrom("bundle").select("id").limit(1).executeTakeFirst(),
39
+ project.db.selectFrom("message").select("id").limit(1).executeTakeFirst(),
40
+ project.db.selectFrom("variant").select("id").limit(1).executeTakeFirst(),
41
+ ]);
42
+ if (bundle || message || variant) {
43
+ throw new Error(
44
+ "saveProjectToDirectory cannot write bundles, messages, or variants without an import/export plugin. Add a plugin to settings.modules/providePlugins, or save the canonical .inlang file with project.toBlob()."
45
+ );
46
+ }
47
+ }
48
+
21
49
  /**
22
50
  * Saves a project to a directory.
23
51
  *
@@ -26,7 +54,7 @@ async function fileExists(fsModule: typeof fs, filePath: string) {
26
54
  *
27
55
  * @example
28
56
  * await saveProjectToDirectory({
29
- * fs: await import("node:fs/promises"),
57
+ * fs: await import("node:fs"),
30
58
  * project,
31
59
  * path: "./project.inlang",
32
60
  * });
@@ -34,8 +62,10 @@ async function fileExists(fsModule: typeof fs, filePath: string) {
34
62
  export async function saveProjectToDirectory(args: {
35
63
  /**
36
64
  * The file system module to use for writing files.
65
+ *
66
+ * Accepts either `node:fs` or `node:fs/promises`.
37
67
  */
38
- fs: typeof fs;
68
+ fs: SaveProjectFs;
39
69
  /**
40
70
  * The inlang project to save.
41
71
  */
@@ -55,6 +85,11 @@ export async function saveProjectToDirectory(args: {
55
85
  if (args.path.endsWith(".inlang") === false) {
56
86
  throw new Error("The path must end with .inlang");
57
87
  }
88
+ if (!args.skipExporting) {
89
+ await assertTranslationDataCanBeExported(args.project);
90
+ }
91
+ const fsModule = getPromisesFs(args.fs);
92
+
58
93
  const files = await args.project.lix.db
59
94
  .selectFrom("file")
60
95
  .selectAll()
@@ -65,7 +100,7 @@ export async function saveProjectToDirectory(args: {
65
100
  );
66
101
 
67
102
  const existingMeta = await readProjectMeta({
68
- fs: args.fs,
103
+ fs: fsModule,
69
104
  projectPath: args.path,
70
105
  });
71
106
  const highestSdkVersion =
@@ -83,9 +118,9 @@ export async function saveProjectToDirectory(args: {
83
118
  const readmePath = path.join(args.path, "README.md");
84
119
  const gitignorePath = path.join(args.path, ".gitignore");
85
120
  const shouldWriteReadme =
86
- shouldWriteMetadata || !(await fileExists(args.fs, readmePath));
121
+ shouldWriteMetadata || !(await fileExists(fsModule, readmePath));
87
122
  const shouldWriteGitignore =
88
- shouldWriteMetadata || !(await fileExists(args.fs, gitignorePath));
123
+ shouldWriteMetadata || !(await fileExists(fsModule, gitignorePath));
89
124
 
90
125
  // write all files to the directory
91
126
  for (const file of files) {
@@ -93,17 +128,17 @@ export async function saveProjectToDirectory(args: {
93
128
  continue;
94
129
  }
95
130
  const p = path.join(args.path, file.path);
96
- await args.fs.mkdir(path.dirname(p), { recursive: true });
97
- await args.fs.writeFile(p, new Uint8Array(file.data));
131
+ await fsModule.mkdir(path.dirname(p), { recursive: true });
132
+ await fsModule.writeFile(p, new Uint8Array(file.data));
98
133
  }
99
134
 
100
135
  if (shouldWriteGitignore) {
101
- await args.fs.writeFile(gitignorePath, gitignoreContent);
136
+ await fsModule.writeFile(gitignorePath, gitignoreContent);
102
137
  }
103
138
 
104
139
  if (shouldWriteReadme) {
105
140
  // Write README.md for coding agents
106
- await args.fs.writeFile(
141
+ await fsModule.writeFile(
107
142
  readmePath,
108
143
  new TextEncoder().encode(README_CONTENT)
109
144
  );
@@ -111,7 +146,7 @@ export async function saveProjectToDirectory(args: {
111
146
 
112
147
  if (shouldWriteMetadata) {
113
148
  const metaContent = JSON.stringify({ highestSdkVersion }, null, 2);
114
- await args.fs.writeFile(
149
+ await fsModule.writeFile(
115
150
  path.join(args.path, ".meta.json"),
116
151
  new TextEncoder().encode(metaContent)
117
152
  );
@@ -161,15 +196,12 @@ export async function saveProjectToDirectory(args: {
161
196
  pathPattern.replace(/\{(languageTag|locale)\}/g, file.locale)
162
197
  )
163
198
  : absolutePathFromProject(args.path, file.name);
164
- const dirname = path.dirname(p);
165
- if ((await args.fs.stat(dirname)).isDirectory() === false) {
166
- await args.fs.mkdir(dirname, { recursive: true });
167
- }
199
+ await fsModule.mkdir(path.dirname(p), { recursive: true });
168
200
  if (p.endsWith(".json")) {
169
201
  try {
170
- const existing = await args.fs.readFile(p, "utf-8");
202
+ const existing = await fsModule.readFile(p, "utf-8");
171
203
  const stringify = detectJsonFormatting(existing);
172
- await args.fs.writeFile(
204
+ await fsModule.writeFile(
173
205
  p,
174
206
  new TextEncoder().encode(
175
207
  stringify(JSON.parse(new TextDecoder().decode(file.content)))
@@ -178,10 +210,10 @@ export async function saveProjectToDirectory(args: {
178
210
  } catch {
179
211
  // write the file to disk (json doesn't exist yet)
180
212
  // yeah ugly duplication of write file but it works.
181
- await args.fs.writeFile(p, new Uint8Array(file.content));
213
+ await fsModule.writeFile(p, new Uint8Array(file.content));
182
214
  }
183
215
  } else {
184
- await args.fs.writeFile(p, new Uint8Array(file.content));
216
+ await fsModule.writeFile(p, new Uint8Array(file.content));
185
217
  }
186
218
  }
187
219
  }
@@ -194,7 +226,7 @@ export async function saveProjectToDirectory(args: {
194
226
  await plugin.saveMessages({
195
227
  messages: bundlesNested.map((b) => toMessageV1(b)),
196
228
  // @ts-expect-error - legacy
197
- nodeishFs: withAbsolutePaths(args.fs, args.path),
229
+ nodeishFs: withAbsolutePaths(fsModule, args.path),
198
230
  settings,
199
231
  });
200
232
  }