@inlang/sdk 2.5.0 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/dist/database/initDbAndSchema.test.js +1 -1
- package/dist/database/initDbAndSchema.test.js.map +1 -1
- package/dist/database/jsonbPlugin.js +1 -1
- package/dist/database/jsonbPlugin.js.map +1 -1
- package/dist/database/schema.d.ts +1 -1
- package/dist/database/schema.js.map +1 -1
- package/dist/lix-plugin/applyChanges.js +1 -1
- package/dist/lix-plugin/applyChanges.js.map +1 -1
- package/dist/lix-plugin/inlangLixPluginV1.test.js +1 -1
- package/dist/lix-plugin/inlangLixPluginV1.test.js.map +1 -1
- package/dist/plugin/schema.d.ts +2 -2
- package/dist/plugin/schema.js.map +1 -1
- package/dist/project/README_CONTENT.d.ts +1 -1
- package/dist/project/README_CONTENT.d.ts.map +1 -1
- package/dist/project/README_CONTENT.js +1 -1
- package/dist/project/README_CONTENT.js.map +1 -1
- package/dist/project/api.d.ts +1 -1
- package/dist/project/api.js.map +1 -1
- package/dist/project/loadProjectFromDirectory.d.ts +18 -25
- package/dist/project/loadProjectFromDirectory.d.ts.map +1 -1
- package/dist/project/loadProjectFromDirectory.js +54 -56
- package/dist/project/loadProjectFromDirectory.js.map +1 -1
- package/dist/project/loadProjectFromDirectory.test.js +82 -4
- package/dist/project/loadProjectFromDirectory.test.js.map +1 -1
- package/dist/project/meta.d.ts +11 -0
- package/dist/project/meta.d.ts.map +1 -0
- package/dist/project/meta.js +129 -0
- package/dist/project/meta.js.map +1 -0
- package/dist/project/meta.test.d.ts +2 -0
- package/dist/project/meta.test.d.ts.map +1 -0
- package/dist/project/meta.test.js +21 -0
- package/dist/project/meta.test.js.map +1 -0
- package/dist/project/path-helpers.d.ts +27 -0
- package/dist/project/path-helpers.d.ts.map +1 -0
- package/dist/project/path-helpers.js +59 -0
- package/dist/project/path-helpers.js.map +1 -0
- package/dist/project/path-helpers.test.d.ts +2 -0
- package/dist/project/path-helpers.test.d.ts.map +1 -0
- package/dist/project/path-helpers.test.js +27 -0
- package/dist/project/path-helpers.test.js.map +1 -0
- package/dist/project/saveProjectToDirectory.d.ts +29 -0
- package/dist/project/saveProjectToDirectory.d.ts.map +1 -1
- package/dist/project/saveProjectToDirectory.js +56 -5
- package/dist/project/saveProjectToDirectory.js.map +1 -1
- package/dist/project/saveProjectToDirectory.test.js +88 -5
- package/dist/project/saveProjectToDirectory.test.js.map +1 -1
- package/dist/services/env-variables/index.js +1 -1
- package/dist/services/env-variables/index.js.map +1 -1
- package/package.json +1 -1
- package/src/database/initDbAndSchema.test.ts +1 -1
- package/src/database/jsonbPlugin.ts +1 -1
- package/src/database/schema.ts +1 -1
- package/src/lix-plugin/applyChanges.ts +1 -1
- package/src/lix-plugin/inlangLixPluginV1.test.ts +1 -1
- package/src/plugin/schema.ts +2 -2
- package/src/project/README_CONTENT.ts +1 -1
- package/src/project/api.ts +1 -1
- package/src/project/loadProjectFromDirectory.test.ts +127 -4
- package/src/project/loadProjectFromDirectory.ts +76 -74
- package/src/project/meta.test.ts +26 -0
- package/src/project/meta.ts +147 -0
- package/src/project/path-helpers.test.ts +45 -0
- package/src/project/path-helpers.ts +71 -0
- package/src/project/saveProjectToDirectory.test.ts +129 -9
- package/src/project/saveProjectToDirectory.ts +87 -11
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { Volume } from "memfs";
|
|
3
|
+
import { absolutePathFromProject, withAbsolutePaths } from "./path-helpers.js";
|
|
4
|
+
|
|
5
|
+
describe("absolutePathFromProject", () => {
|
|
6
|
+
test("resolves relative paths against the project root", () => {
|
|
7
|
+
const result = absolutePathFromProject(
|
|
8
|
+
"/website/project.inlang",
|
|
9
|
+
"./local-plugins/mock-plugin.js"
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
expect(result).toBe("/website/local-plugins/mock-plugin.js");
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("keeps absolute paths unchanged", () => {
|
|
16
|
+
const result = absolutePathFromProject(
|
|
17
|
+
"/website/project.inlang",
|
|
18
|
+
"/shared/plugins/plugin.js"
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
expect(result).toBe("/shared/plugins/plugin.js");
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("withAbsolutePaths", () => {
|
|
26
|
+
test("maps read/write operations to the project root", async () => {
|
|
27
|
+
const volume = Volume.fromJSON({
|
|
28
|
+
"/website/local-plugins/mock-plugin.js": "plugin code",
|
|
29
|
+
});
|
|
30
|
+
const mappedFs = withAbsolutePaths(
|
|
31
|
+
volume.promises as any,
|
|
32
|
+
"/website/project.inlang"
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
const content = await mappedFs.readFile("./local-plugins/mock-plugin.js");
|
|
36
|
+
expect(content.toString()).toBe("plugin code");
|
|
37
|
+
|
|
38
|
+
await mappedFs.writeFile("./local-plugins/new-plugin.js", "new plugin");
|
|
39
|
+
const created = await volume.promises.readFile(
|
|
40
|
+
"/website/local-plugins/new-plugin.js",
|
|
41
|
+
"utf-8"
|
|
42
|
+
);
|
|
43
|
+
expect(created).toBe("new plugin");
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import nodePath from "node:path";
|
|
2
|
+
import type { NodeFsPromisesSubsetLegacy } from "../plugin/schema.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Functions from a path like `./local-plugins/mock-plugin.js` need to be able
|
|
6
|
+
* to be called with relative paths, even if their implementation expects absolute ones.
|
|
7
|
+
*
|
|
8
|
+
* This mapping is required for backwards compatibility.
|
|
9
|
+
* Relative paths in the project.inlang/settings.json
|
|
10
|
+
* file are resolved to absolute paths with `*.inlang`
|
|
11
|
+
* being pruned.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* "/website/project.inlang"
|
|
15
|
+
* "./local-plugins/mock-plugin.js"
|
|
16
|
+
* -> "/website/local-plugins/mock-plugin.js"
|
|
17
|
+
*
|
|
18
|
+
*/
|
|
19
|
+
export function withAbsolutePaths(
|
|
20
|
+
fs: NodeFsPromisesSubsetLegacy,
|
|
21
|
+
projectPath: string
|
|
22
|
+
): NodeFsPromisesSubsetLegacy {
|
|
23
|
+
return {
|
|
24
|
+
// @ts-expect-error - node type mismatch
|
|
25
|
+
readFile: (path, options) => {
|
|
26
|
+
return fs.readFile(absolutePathFromProject(projectPath, path), options);
|
|
27
|
+
},
|
|
28
|
+
writeFile: (path, data) => {
|
|
29
|
+
return fs.writeFile(absolutePathFromProject(projectPath, path), data);
|
|
30
|
+
},
|
|
31
|
+
mkdir: (path) => {
|
|
32
|
+
return fs.mkdir(absolutePathFromProject(projectPath, path));
|
|
33
|
+
},
|
|
34
|
+
readdir: (path) => {
|
|
35
|
+
return fs.readdir(absolutePathFromProject(projectPath, path));
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Joins a path from a project path.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* absolutePathFromProject("/project.inlang", "./local-plugins/mock-plugin.js") -> "/local-plugins/mock-plugin.js"
|
|
45
|
+
*
|
|
46
|
+
* absolutePathFromProject("/website/project.inlang", "./mock-plugin.js") -> "/website/mock-plugin.js"
|
|
47
|
+
*/
|
|
48
|
+
export function absolutePathFromProject(
|
|
49
|
+
projectPath: string,
|
|
50
|
+
filePath: string
|
|
51
|
+
): string {
|
|
52
|
+
// Normalize paths for consistency across platforms
|
|
53
|
+
const normalizedProjectPath = nodePath
|
|
54
|
+
.normalize(projectPath)
|
|
55
|
+
.replace(/\\/g, "/");
|
|
56
|
+
const normalizedFilePath = nodePath.normalize(filePath).replace(/\\/g, "/");
|
|
57
|
+
|
|
58
|
+
// Remove the last part of the project path (file name) to get the project root
|
|
59
|
+
const projectRoot = nodePath.dirname(normalizedProjectPath);
|
|
60
|
+
|
|
61
|
+
// If filePath is already absolute, return it directly
|
|
62
|
+
if (nodePath.isAbsolute(normalizedFilePath)) {
|
|
63
|
+
return normalizedFilePath;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Compute absolute resolved path
|
|
67
|
+
const resolvedPath = nodePath.resolve(projectRoot, normalizedFilePath);
|
|
68
|
+
|
|
69
|
+
// Ensure final path always uses forward slashes
|
|
70
|
+
return resolvedPath.replace(/\\/g, "/");
|
|
71
|
+
}
|
|
@@ -9,6 +9,7 @@ import { loadProjectFromDirectory } from "./loadProjectFromDirectory.js";
|
|
|
9
9
|
import { selectBundleNested } from "../query-utilities/selectBundleNested.js";
|
|
10
10
|
import type { ProjectSettings } from "../json-schema/settings.js";
|
|
11
11
|
import type { MessageV1 } from "../json-schema/old-v1-message/schemaV1.js";
|
|
12
|
+
import { ENV_VARIABLES } from "../services/env-variables/index.js";
|
|
12
13
|
|
|
13
14
|
test("it should throw if the path doesn't end with .inlang", async () => {
|
|
14
15
|
await expect(() =>
|
|
@@ -355,9 +356,8 @@ test("adds a gitignore file if it doesn't exist", async () => {
|
|
|
355
356
|
"/foo/bar.inlang/.gitignore",
|
|
356
357
|
"utf-8"
|
|
357
358
|
);
|
|
358
|
-
expect(gitignore).
|
|
359
|
-
|
|
360
|
-
);
|
|
359
|
+
expect(gitignore).toContain("*");
|
|
360
|
+
expect(gitignore).toContain("!settings.json");
|
|
361
361
|
});
|
|
362
362
|
|
|
363
363
|
test("emits a README.md file for coding agents", async () => {
|
|
@@ -377,11 +377,33 @@ test("emits a README.md file for coding agents", async () => {
|
|
|
377
377
|
"/foo/bar.inlang/README.md",
|
|
378
378
|
"utf-8"
|
|
379
379
|
);
|
|
380
|
-
expect(readme).toContain("// this readme is auto generated");
|
|
381
380
|
expect(readme).toContain("## What is this folder?");
|
|
382
381
|
expect(readme).toContain("@inlang/sdk");
|
|
383
382
|
});
|
|
384
383
|
|
|
384
|
+
test("emits a .meta.json file with the sdk version", async () => {
|
|
385
|
+
const fs = Volume.fromJSON({});
|
|
386
|
+
|
|
387
|
+
const project = await loadProjectInMemory({
|
|
388
|
+
blob: await newProject(),
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
await saveProjectToDirectory({
|
|
392
|
+
fs: fs.promises as any,
|
|
393
|
+
project,
|
|
394
|
+
path: "/foo/bar.inlang",
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const metaRaw = await fs.promises.readFile(
|
|
398
|
+
"/foo/bar.inlang/.meta.json",
|
|
399
|
+
"utf-8"
|
|
400
|
+
);
|
|
401
|
+
const meta = JSON.parse(
|
|
402
|
+
typeof metaRaw === "string" ? metaRaw : metaRaw.toString()
|
|
403
|
+
);
|
|
404
|
+
expect(meta.highestSdkVersion).toBe(ENV_VARIABLES.SDK_VERSION);
|
|
405
|
+
});
|
|
406
|
+
|
|
385
407
|
test("updates an existing README.md file", async () => {
|
|
386
408
|
const fs = Volume.fromJSON({
|
|
387
409
|
"/foo/bar.inlang/README.md": "custom readme",
|
|
@@ -401,10 +423,87 @@ test("updates an existing README.md file", async () => {
|
|
|
401
423
|
"/foo/bar.inlang/README.md",
|
|
402
424
|
"utf-8"
|
|
403
425
|
);
|
|
404
|
-
expect(readme).toContain("// this readme is auto generated");
|
|
405
426
|
expect(readme).not.toContain("custom readme");
|
|
406
427
|
});
|
|
407
428
|
|
|
429
|
+
test("does not overwrite README.md or .gitignore when meta has a higher sdk version", async () => {
|
|
430
|
+
const fs = Volume.fromJSON({
|
|
431
|
+
"/foo/bar.inlang/.meta.json": JSON.stringify({
|
|
432
|
+
highestSdkVersion: "99.0.0",
|
|
433
|
+
}),
|
|
434
|
+
"/foo/bar.inlang/README.md": "custom readme",
|
|
435
|
+
"/foo/bar.inlang/.gitignore": "custom gitignore",
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
const project = await loadProjectInMemory({
|
|
439
|
+
blob: await newProject(),
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
await saveProjectToDirectory({
|
|
443
|
+
fs: fs.promises as any,
|
|
444
|
+
project,
|
|
445
|
+
path: "/foo/bar.inlang",
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
const readme = await fs.promises.readFile(
|
|
449
|
+
"/foo/bar.inlang/README.md",
|
|
450
|
+
"utf-8"
|
|
451
|
+
);
|
|
452
|
+
const gitignore = await fs.promises.readFile(
|
|
453
|
+
"/foo/bar.inlang/.gitignore",
|
|
454
|
+
"utf-8"
|
|
455
|
+
);
|
|
456
|
+
const metaRaw = await fs.promises.readFile(
|
|
457
|
+
"/foo/bar.inlang/.meta.json",
|
|
458
|
+
"utf-8"
|
|
459
|
+
);
|
|
460
|
+
const meta = JSON.parse(
|
|
461
|
+
typeof metaRaw === "string" ? metaRaw : metaRaw.toString()
|
|
462
|
+
);
|
|
463
|
+
expect(readme).toBe("custom readme");
|
|
464
|
+
expect(gitignore).toBe("custom gitignore");
|
|
465
|
+
expect(meta.highestSdkVersion).toBe("99.0.0");
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test("recreates missing README.md and .gitignore when meta has a higher sdk version", async () => {
|
|
469
|
+
const fs = Volume.fromJSON({
|
|
470
|
+
"/foo/bar.inlang/.meta.json": JSON.stringify({
|
|
471
|
+
highestSdkVersion: "99.0.0",
|
|
472
|
+
}),
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
const project = await loadProjectInMemory({
|
|
476
|
+
blob: await newProject(),
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
await saveProjectToDirectory({
|
|
480
|
+
fs: fs.promises as any,
|
|
481
|
+
project,
|
|
482
|
+
path: "/foo/bar.inlang",
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
const readme = await fs.promises.readFile(
|
|
486
|
+
"/foo/bar.inlang/README.md",
|
|
487
|
+
"utf-8"
|
|
488
|
+
);
|
|
489
|
+
const gitignore = await fs.promises.readFile(
|
|
490
|
+
"/foo/bar.inlang/.gitignore",
|
|
491
|
+
"utf-8"
|
|
492
|
+
);
|
|
493
|
+
const metaRaw = await fs.promises.readFile(
|
|
494
|
+
"/foo/bar.inlang/.meta.json",
|
|
495
|
+
"utf-8"
|
|
496
|
+
);
|
|
497
|
+
const meta = JSON.parse(
|
|
498
|
+
typeof metaRaw === "string" ? metaRaw : metaRaw.toString()
|
|
499
|
+
);
|
|
500
|
+
|
|
501
|
+
expect(readme).toContain("## What is this folder?");
|
|
502
|
+
expect(gitignore).toContain("*");
|
|
503
|
+
expect(gitignore).toContain("!settings.json");
|
|
504
|
+
expect(meta.highestSdkVersion).toBe("99.0.0");
|
|
505
|
+
});
|
|
506
|
+
|
|
408
507
|
test("README.md is gitignored", async () => {
|
|
409
508
|
const fs = Volume.fromJSON({});
|
|
410
509
|
|
|
@@ -422,7 +521,6 @@ test("README.md is gitignored", async () => {
|
|
|
422
521
|
"/foo/bar.inlang/.gitignore",
|
|
423
522
|
"utf-8"
|
|
424
523
|
);
|
|
425
|
-
expect(gitignore).toContain("# this file is auto generated");
|
|
426
524
|
expect(gitignore).toContain("# everything is ignored except settings.json");
|
|
427
525
|
expect(gitignore).toContain("*");
|
|
428
526
|
expect(gitignore).toContain("!settings.json");
|
|
@@ -448,9 +546,8 @@ test("overwrites existing .gitignore with generated entries", async () => {
|
|
|
448
546
|
"/foo/bar.inlang/.gitignore",
|
|
449
547
|
"utf-8"
|
|
450
548
|
);
|
|
451
|
-
expect(gitignore).
|
|
452
|
-
|
|
453
|
-
);
|
|
549
|
+
expect(gitignore).toContain("*");
|
|
550
|
+
expect(gitignore).toContain("!settings.json");
|
|
454
551
|
});
|
|
455
552
|
|
|
456
553
|
test("uses exportFiles when both exportFiles and saveMessages are defined", async () => {
|
|
@@ -475,6 +572,29 @@ test("uses exportFiles when both exportFiles and saveMessages are defined", asyn
|
|
|
475
572
|
expect(saveMessagesSpy).not.toHaveBeenCalled();
|
|
476
573
|
});
|
|
477
574
|
|
|
575
|
+
test("skipExporting prevents exporters from running", async () => {
|
|
576
|
+
const exportFilesSpy = vi.fn().mockResolvedValue([]);
|
|
577
|
+
const saveMessagesSpy = vi.fn();
|
|
578
|
+
const mockPlugin: InlangPlugin = {
|
|
579
|
+
key: "mock",
|
|
580
|
+
exportFiles: exportFilesSpy,
|
|
581
|
+
saveMessages: saveMessagesSpy,
|
|
582
|
+
};
|
|
583
|
+
const volume = Volume.fromJSON({});
|
|
584
|
+
const project = await loadProjectInMemory({
|
|
585
|
+
blob: await newProject(),
|
|
586
|
+
providePlugins: [mockPlugin],
|
|
587
|
+
});
|
|
588
|
+
await saveProjectToDirectory({
|
|
589
|
+
path: "/foo/project.inlang",
|
|
590
|
+
fs: volume.promises as any,
|
|
591
|
+
project,
|
|
592
|
+
skipExporting: true,
|
|
593
|
+
});
|
|
594
|
+
expect(exportFilesSpy).not.toHaveBeenCalled();
|
|
595
|
+
expect(saveMessagesSpy).not.toHaveBeenCalled();
|
|
596
|
+
});
|
|
597
|
+
|
|
478
598
|
test("uses saveMessages when exportFiles is not defined", async () => {
|
|
479
599
|
const saveMessagesSpy = vi.fn().mockResolvedValue([]);
|
|
480
600
|
const mockPlugin: InlangPlugin = {
|
|
@@ -2,18 +2,55 @@ import type fs from "node:fs/promises";
|
|
|
2
2
|
import type { InlangProject } from "./api.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { toMessageV1 } from "../json-schema/old-v1-message/toMessageV1.js";
|
|
5
|
-
import {
|
|
6
|
-
absolutePathFromProject,
|
|
7
|
-
withAbsolutePaths,
|
|
8
|
-
} from "./loadProjectFromDirectory.js";
|
|
5
|
+
import { absolutePathFromProject, withAbsolutePaths } from "./path-helpers.js";
|
|
9
6
|
import { detectJsonFormatting } from "../utilities/detectJsonFormatting.js";
|
|
10
7
|
import { selectBundleNested } from "../query-utilities/selectBundleNested.js";
|
|
11
8
|
import { README_CONTENT } from "./README_CONTENT.js";
|
|
9
|
+
import { ENV_VARIABLES } from "../services/env-variables/index.js";
|
|
10
|
+
import { compareSemver, pickHighestVersion, readProjectMeta } from "./meta.js";
|
|
12
11
|
|
|
12
|
+
async function fileExists(fsModule: typeof fs, filePath: string) {
|
|
13
|
+
try {
|
|
14
|
+
await fsModule.stat(filePath);
|
|
15
|
+
return true;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Saves a project to a directory.
|
|
23
|
+
*
|
|
24
|
+
* Writes all project files to disk and runs exporters to generate
|
|
25
|
+
* resource files (e.g., JSON translation files).
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* await saveProjectToDirectory({
|
|
29
|
+
* fs: await import("node:fs/promises"),
|
|
30
|
+
* project,
|
|
31
|
+
* path: "./project.inlang",
|
|
32
|
+
* });
|
|
33
|
+
*/
|
|
13
34
|
export async function saveProjectToDirectory(args: {
|
|
35
|
+
/**
|
|
36
|
+
* The file system module to use for writing files.
|
|
37
|
+
*/
|
|
14
38
|
fs: typeof fs;
|
|
39
|
+
/**
|
|
40
|
+
* The inlang project to save.
|
|
41
|
+
*/
|
|
15
42
|
project: InlangProject;
|
|
43
|
+
/**
|
|
44
|
+
* The path to the inlang project directory. Must end with `.inlang`.
|
|
45
|
+
*/
|
|
16
46
|
path: string;
|
|
47
|
+
/**
|
|
48
|
+
* If `true`, skips running exporters and only writes internal project files.
|
|
49
|
+
*
|
|
50
|
+
* Useful when you only want to update project metadata without
|
|
51
|
+
* regenerating resource files.
|
|
52
|
+
*/
|
|
53
|
+
skipExporting?: boolean;
|
|
17
54
|
}): Promise<void> {
|
|
18
55
|
if (args.path.endsWith(".inlang") === false) {
|
|
19
56
|
throw new Error("The path must end with .inlang");
|
|
@@ -24,9 +61,32 @@ export async function saveProjectToDirectory(args: {
|
|
|
24
61
|
.execute();
|
|
25
62
|
|
|
26
63
|
const gitignoreContent = new TextEncoder().encode(
|
|
27
|
-
"#
|
|
64
|
+
"# IF GIT SHOWED THAT THIS FILE CHANGED\n#\n# 1. RUN THE FOLLOWING COMMAND\n#\n# ---\n# git rm --cached '**/*.inlang/.gitignore'\n# ---\n#\n# 2. COMMIT THE CHANGE\n#\n# ---\n# git commit -m \"fix: remove tracked .gitignore from inlang project\"\n# ---\n#\n# Inlang handles the gitignore itself starting with version ^2.5.\n#\n# everything is ignored except settings.json\n*\n!settings.json"
|
|
28
65
|
);
|
|
29
66
|
|
|
67
|
+
const existingMeta = await readProjectMeta({
|
|
68
|
+
fs: args.fs,
|
|
69
|
+
projectPath: args.path,
|
|
70
|
+
});
|
|
71
|
+
const highestSdkVersion =
|
|
72
|
+
pickHighestVersion([
|
|
73
|
+
existingMeta?.highestSdkVersion,
|
|
74
|
+
ENV_VARIABLES.SDK_VERSION,
|
|
75
|
+
]) ?? ENV_VARIABLES.SDK_VERSION;
|
|
76
|
+
const shouldWriteMetadata = (() => {
|
|
77
|
+
const comparison = compareSemver(
|
|
78
|
+
highestSdkVersion,
|
|
79
|
+
ENV_VARIABLES.SDK_VERSION
|
|
80
|
+
);
|
|
81
|
+
return comparison === null || comparison <= 0;
|
|
82
|
+
})();
|
|
83
|
+
const readmePath = path.join(args.path, "README.md");
|
|
84
|
+
const gitignorePath = path.join(args.path, ".gitignore");
|
|
85
|
+
const shouldWriteReadme =
|
|
86
|
+
shouldWriteMetadata || !(await fileExists(args.fs, readmePath));
|
|
87
|
+
const shouldWriteGitignore =
|
|
88
|
+
shouldWriteMetadata || !(await fileExists(args.fs, gitignorePath));
|
|
89
|
+
|
|
30
90
|
// write all files to the directory
|
|
31
91
|
for (const file of files) {
|
|
32
92
|
if (file.path.endsWith("db.sqlite") || file.path === "/project_id") {
|
|
@@ -37,13 +97,29 @@ export async function saveProjectToDirectory(args: {
|
|
|
37
97
|
await args.fs.writeFile(p, new Uint8Array(file.data));
|
|
38
98
|
}
|
|
39
99
|
|
|
40
|
-
|
|
100
|
+
if (shouldWriteGitignore) {
|
|
101
|
+
await args.fs.writeFile(gitignorePath, gitignoreContent);
|
|
102
|
+
}
|
|
41
103
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
104
|
+
if (shouldWriteReadme) {
|
|
105
|
+
// Write README.md for coding agents
|
|
106
|
+
await args.fs.writeFile(
|
|
107
|
+
readmePath,
|
|
108
|
+
new TextEncoder().encode(README_CONTENT)
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (shouldWriteMetadata) {
|
|
113
|
+
const metaContent = JSON.stringify({ highestSdkVersion }, null, 2);
|
|
114
|
+
await args.fs.writeFile(
|
|
115
|
+
path.join(args.path, ".meta.json"),
|
|
116
|
+
new TextEncoder().encode(metaContent)
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (args.skipExporting) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
47
123
|
|
|
48
124
|
// run exporters
|
|
49
125
|
const plugins = await args.project.plugins.get();
|