@inlang/sdk 0.22.0 → 0.23.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.
- package/README.md +2 -2
- package/dist/createNodeishFsWithWatcher.js +1 -1
- package/dist/env-variables/index.d.ts +4 -0
- package/dist/env-variables/index.d.ts.map +1 -0
- package/dist/env-variables/index.js +3 -0
- package/dist/listProjects.d.ts.map +1 -1
- package/dist/listProjects.js +15 -9
- package/dist/listProjects.test.js +14 -1
- package/dist/loadProject.d.ts +28 -9
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +49 -50
- package/dist/loadProject.test.js +8 -5
- package/dist/migrations/maybeCreateFirstProjectId.d.ts +16 -0
- package/dist/migrations/maybeCreateFirstProjectId.d.ts.map +1 -0
- package/dist/migrations/maybeCreateFirstProjectId.js +38 -0
- package/dist/migrations/maybeCreateFirstProjectId.test.d.ts +2 -0
- package/dist/migrations/maybeCreateFirstProjectId.test.d.ts.map +1 -0
- package/dist/migrations/maybeCreateFirstProjectId.test.js +27 -0
- package/dist/migrations/migrateToDirectory.d.ts +1 -1
- package/dist/migrations/migrateToDirectory.js +3 -3
- package/dist/telemetry/capture.d.ts +21 -0
- package/dist/telemetry/capture.d.ts.map +1 -0
- package/dist/telemetry/capture.js +39 -0
- package/package.json +13 -12
- package/src/createNodeishFsWithWatcher.ts +1 -1
- package/src/env-variables/.prettierignore +1 -0
- package/src/env-variables/createIndexFile.js +28 -0
- package/src/env-variables/index.d.ts +13 -0
- package/src/listProjects.test.ts +21 -2
- package/src/listProjects.ts +14 -7
- package/src/loadProject.test.ts +12 -5
- package/src/loadProject.ts +91 -48
- package/src/migrations/maybeCreateFirstProjectId.test.ts +34 -0
- package/src/migrations/maybeCreateFirstProjectId.ts +44 -0
- package/src/migrations/migrateToDirectory.ts +3 -3
- package/src/telemetry/capture.ts +49 -0
- package/dist/generateProjectId.d.ts +0 -3
- package/dist/generateProjectId.d.ts.map +0 -1
- package/dist/generateProjectId.js +0 -11
- package/dist/generateProjectId.test.d.ts +0 -2
- package/dist/generateProjectId.test.d.ts.map +0 -1
- package/dist/generateProjectId.test.js +0 -18
- package/src/generateProjectId.test.ts +0 -22
- package/src/generateProjectId.ts +0 -14
package/README.md
CHANGED
|
@@ -2,12 +2,12 @@ Developer-first localization infrastructure that is built on git. Your git repos
|
|
|
2
2
|
|
|
3
3
|
<div>
|
|
4
4
|
<p align="center">
|
|
5
|
-
<img width="300" src="https://cdn.jsdelivr.net/gh/
|
|
5
|
+
<img width="300" src="https://cdn.jsdelivr.net/gh/opral/monorepo/inlang/assets/logo-white-background.png"/>
|
|
6
6
|
</p>
|
|
7
7
|
<h4 align="center">
|
|
8
8
|
<!-- <a href="https://inlang.com/documentation" target="_blank">Get Started</a>
|
|
9
9
|
· -->
|
|
10
|
-
<a href="https://github.com/
|
|
10
|
+
<a href="https://github.com/opral/monorepo/discussions" target="_blank">Discussions</a> · <a href="https://twitter.com/inlangHQ" target="_blank">Twitter</a>
|
|
11
11
|
</h4>
|
|
12
12
|
</div>
|
|
13
13
|
|
|
@@ -24,7 +24,7 @@ export const createNodeishFsWithWatcher = (args) => {
|
|
|
24
24
|
catch (err) {
|
|
25
25
|
if (err.name === "AbortError")
|
|
26
26
|
return;
|
|
27
|
-
// https://github.com/
|
|
27
|
+
// https://github.com/opral/monorepo/issues/1647
|
|
28
28
|
// the file does not exist (yet)
|
|
29
29
|
// this is not testable beacause the fs.watch api differs
|
|
30
30
|
// from node and lix. lenghty
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/env-variables/index.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,aAAa;;CAEzB,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listProjects.d.ts","sourceRoot":"","sources":["../src/listProjects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD,eAAO,MAAM,YAAY,cACb,iBAAiB,QACtB,MAAM,KACV,QAAQ,MAAM;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"listProjects.d.ts","sourceRoot":"","sources":["../src/listProjects.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD,eAAO,MAAM,YAAY,cACb,iBAAiB,QACtB,MAAM,KACV,QAAQ,MAAM;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAwCxC,CAAA"}
|
package/dist/listProjects.js
CHANGED
|
@@ -9,17 +9,23 @@ export const listProjects = async (nodeishFs, from) => {
|
|
|
9
9
|
const files = await nodeishFs.readdir(path);
|
|
10
10
|
for (const file of files) {
|
|
11
11
|
const filePath = `${path}/${file}`;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
try {
|
|
13
|
+
const stats = await nodeishFs.stat(filePath);
|
|
14
|
+
if (stats.isDirectory()) {
|
|
15
|
+
if (file === "node_modules") {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (file.endsWith(".inlang")) {
|
|
19
|
+
projects.push({ projectPath: filePath });
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
await searchDir(filePath, depth + 1);
|
|
23
|
+
}
|
|
21
24
|
}
|
|
22
25
|
}
|
|
26
|
+
catch {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
23
29
|
}
|
|
24
30
|
}
|
|
25
31
|
await searchDir(from, 0);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import { assert, describe, it } from "vitest";
|
|
1
|
+
import { assert, describe, it, expect } from "vitest";
|
|
2
2
|
import { listProjects } from "./listProjects.js";
|
|
3
3
|
import { createNodeishMemoryFs } from "@lix-js/fs";
|
|
4
|
+
import { mockRepo } from "@lix-js/client";
|
|
5
|
+
// eslint-disable-next-line no-restricted-imports -- test
|
|
6
|
+
import { readFileSync } from "node:fs";
|
|
4
7
|
const settings = {
|
|
5
8
|
sourceLanguageTag: "en",
|
|
6
9
|
languageTags: ["en"],
|
|
@@ -43,6 +46,16 @@ describe("listProjects", () => {
|
|
|
43
46
|
assert(projects.length === 0);
|
|
44
47
|
});
|
|
45
48
|
});
|
|
49
|
+
it("should not crash on broken symlinks as cal.com has", async () => {
|
|
50
|
+
const ciTestRepo = JSON.parse(readFileSync("./mocks/ci-test-repo-no-shallow.json", { encoding: "utf-8" }));
|
|
51
|
+
const repo = await mockRepo({ fromSnapshot: ciTestRepo });
|
|
52
|
+
repo.checkout({ branch: "test-symlink" });
|
|
53
|
+
const link = await repo.nodeishFs.readlink("test-symlink-not-existing-target");
|
|
54
|
+
expect(link).toBe("/test-symlink-not-existing-target//.././no-exist");
|
|
55
|
+
await listProjects(repo.nodeishFs, "/").then((projects) => {
|
|
56
|
+
assert(projects.length === 1);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
46
59
|
it("should also find files inside of a dir that ends with *.inlang", async () => {
|
|
47
60
|
const fs = createNodeishMemoryFs();
|
|
48
61
|
await fs.mkdir("/user/dir1/go.inlang", { recursive: true });
|
package/dist/loadProject.d.ts
CHANGED
|
@@ -1,23 +1,42 @@
|
|
|
1
1
|
import type { InlangProject, Subscribable } from "./api.js";
|
|
2
2
|
import { type ImportFunction } from "./resolve-modules/index.js";
|
|
3
|
-
import { type NodeishFilesystem } from "@lix-js/fs";
|
|
4
3
|
import type { Repository } from "@lix-js/client";
|
|
5
4
|
/**
|
|
6
5
|
* Creates an inlang instance.
|
|
7
6
|
*
|
|
8
7
|
* @param projectPath - Absolute path to the inlang settings file.
|
|
9
|
-
* @param nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
|
|
8
|
+
* @param @deprecated nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
|
|
10
9
|
* @param _import - Use `_import` to pass a custom import function for testing,
|
|
11
10
|
* and supporting legacy resolvedModules such as CJS.
|
|
12
|
-
* @param _capture - Use `_capture` to capture events for analytics.
|
|
13
11
|
*
|
|
14
12
|
*/
|
|
15
|
-
export declare
|
|
13
|
+
export declare function loadProject(args: {
|
|
16
14
|
projectPath: string;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
nodeishFs: Repository["nodeishFs"];
|
|
16
|
+
/**
|
|
17
|
+
* The app id is used to identify the app that is using the SDK.
|
|
18
|
+
*
|
|
19
|
+
* We use the app id to group events in telemetry to answer questions
|
|
20
|
+
* like "Which apps causes these errors?" or "Which apps are used more than others?".
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* appId: "app.inlang.badge"
|
|
24
|
+
*/
|
|
25
|
+
appId?: string;
|
|
26
|
+
_import?: ImportFunction;
|
|
27
|
+
}): Promise<InlangProject>;
|
|
28
|
+
/**
|
|
29
|
+
* @param projectPath - Absolute path to the inlang settings file.
|
|
30
|
+
* @param repo - An instance of a lix repo as returned by `openRepository`.
|
|
31
|
+
* @param _import - Use `_import` to pass a custom import function for testing,
|
|
32
|
+
* and supporting legacy resolvedModules such as CJS.
|
|
33
|
+
*
|
|
34
|
+
*/
|
|
35
|
+
export declare function loadProject(args: {
|
|
36
|
+
projectPath: string;
|
|
37
|
+
repo: Repository;
|
|
38
|
+
appId?: string;
|
|
39
|
+
_import?: ImportFunction;
|
|
40
|
+
}): Promise<InlangProject>;
|
|
22
41
|
export declare function createSubscribable<T>(signal: () => T): Subscribable<T>;
|
|
23
42
|
//# sourceMappingURL=loadProject.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadProject.d.ts","sourceRoot":"","sources":["../src/loadProject.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,aAAa,EAGb,YAAY,EACZ,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,KAAK,cAAc,EAAkB,MAAM,4BAA4B,CAAA;
|
|
1
|
+
{"version":3,"file":"loadProject.d.ts","sourceRoot":"","sources":["../src/loadProject.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,aAAa,EAGb,YAAY,EACZ,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,KAAK,cAAc,EAAkB,MAAM,4BAA4B,CAAA;AAuBhF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAKhD;;;;;;;;GAQG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACvC,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,CAAA;IAClC;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,cAAc,CAAA;CACxB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;AAE1B;;;;;;GAMG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE;IACvC,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,EAAE,UAAU,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,cAAc,CAAA;CACxB,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;AA+Y1B,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAQtE"}
|
package/dist/loadProject.js
CHANGED
|
@@ -13,22 +13,11 @@ import { normalizePath } from "@lix-js/fs";
|
|
|
13
13
|
import { isAbsolutePath } from "./isAbsolutePath.js";
|
|
14
14
|
import { createNodeishFsWithWatcher } from "./createNodeishFsWithWatcher.js";
|
|
15
15
|
import { maybeMigrateToDirectory } from "./migrations/migrateToDirectory.js";
|
|
16
|
-
import {
|
|
16
|
+
import { maybeCreateFirstProjectId } from "./migrations/maybeCreateFirstProjectId.js";
|
|
17
|
+
import { capture } from "./telemetry/capture.js";
|
|
17
18
|
const settingsCompiler = TypeCompiler.Compile(ProjectSettings);
|
|
18
|
-
|
|
19
|
-
* Creates an inlang instance.
|
|
20
|
-
*
|
|
21
|
-
* @param projectPath - Absolute path to the inlang settings file.
|
|
22
|
-
* @param nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
|
|
23
|
-
* @param _import - Use `_import` to pass a custom import function for testing,
|
|
24
|
-
* and supporting legacy resolvedModules such as CJS.
|
|
25
|
-
* @param _capture - Use `_capture` to capture events for analytics.
|
|
26
|
-
*
|
|
27
|
-
*/
|
|
28
|
-
export const loadProject = async (args) => {
|
|
19
|
+
export async function loadProject(args) {
|
|
29
20
|
const projectPath = normalizePath(args.projectPath);
|
|
30
|
-
// -- migrate if outdated ------------------------------------------------
|
|
31
|
-
await maybeMigrateToDirectory({ nodeishFs: args.nodeishFs, projectPath });
|
|
32
21
|
// -- validation --------------------------------------------------------
|
|
33
22
|
// the only place where throwing is acceptable because the project
|
|
34
23
|
// won't even be loaded. do not throw anywhere else. otherwise, apps
|
|
@@ -39,34 +28,31 @@ export const loadProject = async (args) => {
|
|
|
39
28
|
else if (/[^\\/]+\.inlang$/.test(projectPath) === false) {
|
|
40
29
|
throw new LoadProjectInvalidArgument(`Expected a path ending in "{name}.inlang" but received "${projectPath}".\n\nValid examples: \n- "/path/to/micky-mouse.inlang"\n- "/path/to/green-elephant.inlang\n`, { argument: "projectPath" });
|
|
41
30
|
}
|
|
31
|
+
let fs;
|
|
32
|
+
if (args.nodeishFs) {
|
|
33
|
+
// TODO: deprecate
|
|
34
|
+
fs = args.nodeishFs;
|
|
35
|
+
}
|
|
36
|
+
else if (args.repo) {
|
|
37
|
+
fs = args.repo.nodeishFs;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new LoadProjectInvalidArgument(`Repo missing from arguments.`, { argument: "repo" });
|
|
41
|
+
}
|
|
42
|
+
const nodeishFs = createNodeishFsWithAbsolutePaths({
|
|
43
|
+
projectPath,
|
|
44
|
+
nodeishFs: fs,
|
|
45
|
+
});
|
|
46
|
+
// -- migratations ------------------------------------------------
|
|
47
|
+
await maybeMigrateToDirectory({ nodeishFs: fs, projectPath });
|
|
48
|
+
await maybeCreateFirstProjectId({ projectPath, repo: args.repo });
|
|
42
49
|
// -- load project ------------------------------------------------------
|
|
43
|
-
let idError;
|
|
44
50
|
return await createRoot(async () => {
|
|
51
|
+
// TODO remove tryCatch after https://github.com/opral/monorepo/issues/2013
|
|
52
|
+
// - a repo will always be present
|
|
53
|
+
// - if a repo is present, the project id will always be present
|
|
54
|
+
const { data: projectId } = await tryCatch(() => fs.readFile(args.projectPath + "/project_id", { encoding: "utf-8" }));
|
|
45
55
|
const [initialized, markInitAsComplete, markInitAsFailed] = createAwaitable();
|
|
46
|
-
const nodeishFs = createNodeishFsWithAbsolutePaths({
|
|
47
|
-
projectPath,
|
|
48
|
-
nodeishFs: args.nodeishFs,
|
|
49
|
-
});
|
|
50
|
-
let projectId;
|
|
51
|
-
try {
|
|
52
|
-
projectId = await nodeishFs.readFile(projectPath + "/project_id", {
|
|
53
|
-
encoding: "utf-8",
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
// @ts-ignore
|
|
58
|
-
if (error.code === "ENOENT") {
|
|
59
|
-
if (args.repo) {
|
|
60
|
-
projectId = await generateProjectId(args.repo, projectPath);
|
|
61
|
-
if (projectId) {
|
|
62
|
-
await nodeishFs.writeFile(projectPath + "/project_id", projectId);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
idError = error;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
56
|
// -- settings ------------------------------------------------------------
|
|
71
57
|
const [settings, _setSettings] = createSignal();
|
|
72
58
|
createEffect(() => {
|
|
@@ -77,12 +63,7 @@ export const loadProject = async (args) => {
|
|
|
77
63
|
// })
|
|
78
64
|
// }
|
|
79
65
|
loadSettings({ settingsFilePath: projectPath + "/settings.json", nodeishFs })
|
|
80
|
-
.then((settings) =>
|
|
81
|
-
setSettings(settings);
|
|
82
|
-
// rename settings to get a convenient access to the data in Posthog
|
|
83
|
-
const project_settings = settings;
|
|
84
|
-
args._capture?.("SDK used settings", { project_settings, group: projectId });
|
|
85
|
-
})
|
|
66
|
+
.then((settings) => setSettings(settings))
|
|
86
67
|
.catch((err) => {
|
|
87
68
|
markInitAsFailed(err);
|
|
88
69
|
});
|
|
@@ -158,7 +139,7 @@ export const loadProject = async (args) => {
|
|
|
158
139
|
description: rule.description,
|
|
159
140
|
module: resolvedModules()?.meta.find((m) => m.id.includes(rule.id))?.module ??
|
|
160
141
|
"Unknown module. You stumbled on a bug in inlang's source code. Please open an issue.",
|
|
161
|
-
// default to warning, see https://github.com/
|
|
142
|
+
// default to warning, see https://github.com/opral/monorepo/issues/1254
|
|
162
143
|
level: settingsValue["messageLintRuleLevels"]?.[rule.id] ?? "warning",
|
|
163
144
|
}));
|
|
164
145
|
};
|
|
@@ -203,6 +184,27 @@ export const loadProject = async (args) => {
|
|
|
203
184
|
createEffect(() => {
|
|
204
185
|
debouncedSave(messagesQuery.getAll());
|
|
205
186
|
});
|
|
187
|
+
/**
|
|
188
|
+
* Utility to escape reactive tracking and avoid multiple calls to
|
|
189
|
+
* the capture event.
|
|
190
|
+
*
|
|
191
|
+
* Should be addressed with https://github.com/opral/monorepo/issues/1772
|
|
192
|
+
*/
|
|
193
|
+
let projectLoadedCapturedAlready = false;
|
|
194
|
+
if (projectId && projectLoadedCapturedAlready === false) {
|
|
195
|
+
projectLoadedCapturedAlready = true;
|
|
196
|
+
// TODO ensure that capture is "awaited" without blocking the the app from starting
|
|
197
|
+
await capture("SDK loaded project", {
|
|
198
|
+
projectId,
|
|
199
|
+
properties: {
|
|
200
|
+
appId: args.appId,
|
|
201
|
+
settings: settings(),
|
|
202
|
+
installedPluginIds: installedPlugins().map((p) => p.id),
|
|
203
|
+
installedMessageLintRuleIds: installedMessageLintRules().map((r) => r.id),
|
|
204
|
+
numberOfMessages: messagesQuery.includedMessageIds().length,
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
206
208
|
return {
|
|
207
209
|
installed: {
|
|
208
210
|
plugins: createSubscribable(() => installedPlugins()),
|
|
@@ -210,10 +212,7 @@ export const loadProject = async (args) => {
|
|
|
210
212
|
},
|
|
211
213
|
errors: createSubscribable(() => [
|
|
212
214
|
...(initializeError ? [initializeError] : []),
|
|
213
|
-
...(idError ? [idError] : []),
|
|
214
215
|
...(resolvedModules() ? resolvedModules().errors : []),
|
|
215
|
-
// have a query error exposed
|
|
216
|
-
//...(lintErrors() ?? []),
|
|
217
216
|
]),
|
|
218
217
|
settings: createSubscribable(() => settings()),
|
|
219
218
|
setSettings,
|
|
@@ -224,7 +223,7 @@ export const loadProject = async (args) => {
|
|
|
224
223
|
},
|
|
225
224
|
};
|
|
226
225
|
});
|
|
227
|
-
}
|
|
226
|
+
}
|
|
228
227
|
//const x = {} as InlangProject
|
|
229
228
|
// ------------------------------------------------------------------------------------------------
|
|
230
229
|
const loadSettings = async (args) => {
|
package/dist/loadProject.test.js
CHANGED
|
@@ -6,6 +6,9 @@ import { createNodeishMemoryFs, normalizePath } from "@lix-js/fs";
|
|
|
6
6
|
import { createMessage } from "./test-utilities/createMessage.js";
|
|
7
7
|
import { tryCatch } from "@inlang/result";
|
|
8
8
|
import { mockRepo } from "@lix-js/client";
|
|
9
|
+
import {} from "@lix-js/fs";
|
|
10
|
+
// eslint-disable-next-line no-restricted-imports -- test
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
9
12
|
// ------------------------------------------------------------------------------------------------
|
|
10
13
|
const getValue = (subscribable) => {
|
|
11
14
|
let value;
|
|
@@ -124,7 +127,8 @@ describe("initialization", () => {
|
|
|
124
127
|
expect(result.data).toBeUndefined();
|
|
125
128
|
});
|
|
126
129
|
it("should generate projectId on missing projectid", async () => {
|
|
127
|
-
const
|
|
130
|
+
const ciTestRepo = JSON.parse(readFileSync("./mocks/ci-test-repo-no-shallow.json", { encoding: "utf-8" }));
|
|
131
|
+
const repo = await mockRepo({ fromSnapshot: ciTestRepo });
|
|
128
132
|
const existing = await repo.nodeishFs
|
|
129
133
|
.readFile("/project.inlang/project_id", {
|
|
130
134
|
encoding: "utf-8",
|
|
@@ -136,7 +140,6 @@ describe("initialization", () => {
|
|
|
136
140
|
expect(existing.error.code).toBe("ENOENT");
|
|
137
141
|
const result = await tryCatch(() => loadProject({
|
|
138
142
|
projectPath: "/project.inlang",
|
|
139
|
-
nodeishFs: repo.nodeishFs,
|
|
140
143
|
repo,
|
|
141
144
|
_import,
|
|
142
145
|
}));
|
|
@@ -147,16 +150,16 @@ describe("initialization", () => {
|
|
|
147
150
|
.catch((error) => {
|
|
148
151
|
return { error };
|
|
149
152
|
});
|
|
150
|
-
expect(newId).toBe("
|
|
153
|
+
expect(newId).toBe("e8c61726bc2f437ec6a260abb632b3c59195059b60031e648b5afbafa7f3d79a");
|
|
151
154
|
expect(result.error).toBeUndefined();
|
|
152
155
|
expect(result.data).toBeDefined();
|
|
153
156
|
});
|
|
154
157
|
it("should reuse projectId on existing projectid", async () => {
|
|
155
|
-
const
|
|
158
|
+
const ciTestRepo = JSON.parse(readFileSync("./mocks/ci-test-repo-no-shallow.json", { encoding: "utf-8" }));
|
|
159
|
+
const repo = await mockRepo({ fromSnapshot: ciTestRepo });
|
|
156
160
|
repo.nodeishFs.writeFile("/project.inlang/project_id", "testId");
|
|
157
161
|
const result = await tryCatch(() => loadProject({
|
|
158
162
|
projectPath: "/project.inlang",
|
|
159
|
-
nodeishFs: repo.nodeishFs,
|
|
160
163
|
repo,
|
|
161
164
|
_import,
|
|
162
165
|
}));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Repository } from "@lix-js/client";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a project id if it does not exist yet.
|
|
4
|
+
*
|
|
5
|
+
* - this is a migration to ensure that all projects have a project id
|
|
6
|
+
* - new projects are created with a project id (in the future)
|
|
7
|
+
*/
|
|
8
|
+
export declare function maybeCreateFirstProjectId(args: {
|
|
9
|
+
projectPath: string;
|
|
10
|
+
repo?: Repository;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
export declare function generateProjectId(args: {
|
|
13
|
+
repo?: Repository;
|
|
14
|
+
projectPath: string;
|
|
15
|
+
}): Promise<string | undefined>;
|
|
16
|
+
//# sourceMappingURL=maybeCreateFirstProjectId.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maybeCreateFirstProjectId.d.ts","sourceRoot":"","sources":["../../src/migrations/maybeCreateFirstProjectId.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGhD;;;;;GAKG;AACH,wBAAsB,yBAAyB,CAAC,IAAI,EAAE;IACrD,WAAW,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,UAAU,CAAA;CACjB,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBhB;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,UAAU,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,+BAWvF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { hash } from "@lix-js/client";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a project id if it does not exist yet.
|
|
4
|
+
*
|
|
5
|
+
* - this is a migration to ensure that all projects have a project id
|
|
6
|
+
* - new projects are created with a project id (in the future)
|
|
7
|
+
*/
|
|
8
|
+
export async function maybeCreateFirstProjectId(args) {
|
|
9
|
+
// the migration assumes a repository
|
|
10
|
+
if (args.repo === undefined) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
await args.repo.nodeishFs.readFile(args.projectPath + "/project_id", {
|
|
15
|
+
encoding: "utf-8",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
// @ts-ignore
|
|
20
|
+
if (error.code === "ENOENT" && args.repo) {
|
|
21
|
+
const projectId = await generateProjectId({ repo: args.repo, projectPath: args.projectPath });
|
|
22
|
+
if (projectId) {
|
|
23
|
+
await args.repo.nodeishFs.writeFile(args.projectPath + "/project_id", projectId);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export async function generateProjectId(args) {
|
|
29
|
+
if (!args.repo || !args.projectPath) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const firstCommitHash = await args.repo.getFirstCommitHash();
|
|
33
|
+
const originHash = await args.repo.getOrigin({ safeHashOnly: true });
|
|
34
|
+
if (firstCommitHash) {
|
|
35
|
+
return hash(`${firstCommitHash + args.projectPath + originHash}`);
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maybeCreateFirstProjectId.test.d.ts","sourceRoot":"","sources":["../../src/migrations/maybeCreateFirstProjectId.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { generateProjectId } from "./maybeCreateFirstProjectId.js";
|
|
2
|
+
import { it, expect } from "vitest";
|
|
3
|
+
import { openRepository } from "@lix-js/client/src/openRepository.ts";
|
|
4
|
+
import { mockRepo, createNodeishMemoryFs } from "@lix-js/client";
|
|
5
|
+
import {} from "@lix-js/fs";
|
|
6
|
+
// eslint-disable-next-line no-restricted-imports -- test
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
const ciTestRepo = JSON.parse(readFileSync("./mocks/ci-test-repo-no-shallow.json", { encoding: "utf-8" }));
|
|
9
|
+
const repo = await mockRepo({ fromSnapshot: ciTestRepo });
|
|
10
|
+
it("should return if repo is undefined", async () => {
|
|
11
|
+
const projectId = await generateProjectId({ repo: undefined, projectPath: "mocked_project_path" });
|
|
12
|
+
expect(projectId).toBeUndefined();
|
|
13
|
+
});
|
|
14
|
+
it("should generate a project id", async () => {
|
|
15
|
+
const projectId = await generateProjectId({ repo, projectPath: "mocked_project_path" });
|
|
16
|
+
expect(projectId).toBe("959bcf0a30e678c9b90a3c76d1a281d085eab55f289c5439b6b10849baa1920c");
|
|
17
|
+
});
|
|
18
|
+
it("should return undefined if repoMeta contains error", async () => {
|
|
19
|
+
const repoWithError = await openRepository("https://github.com/inlang/no-exist", {
|
|
20
|
+
nodeishFs: createNodeishMemoryFs(),
|
|
21
|
+
});
|
|
22
|
+
const projectId = await generateProjectId({
|
|
23
|
+
repo: repoWithError,
|
|
24
|
+
projectPath: "mocked_project_path",
|
|
25
|
+
});
|
|
26
|
+
expect(projectId).toBeUndefined();
|
|
27
|
+
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { NodeishFilesystem } from "@lix-js/fs";
|
|
2
2
|
/**
|
|
3
3
|
* Migrates to the new project directory structure
|
|
4
|
-
* https://github.com/
|
|
4
|
+
* https://github.com/opral/monorepo/issues/1678
|
|
5
5
|
*/
|
|
6
6
|
export declare const maybeMigrateToDirectory: (args: {
|
|
7
7
|
nodeishFs: NodeishFilesystem;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { tryCatch } from "@inlang/result";
|
|
2
2
|
/**
|
|
3
3
|
* Migrates to the new project directory structure
|
|
4
|
-
* https://github.com/
|
|
4
|
+
* https://github.com/opral/monorepo/issues/1678
|
|
5
5
|
*/
|
|
6
6
|
export const maybeMigrateToDirectory = async (args) => {
|
|
7
7
|
// the migration assumes that the projectPath ends with project.inlang
|
|
@@ -38,9 +38,9 @@ The \`project.inlang.json\` file is now contained in a project directory e.g. \`
|
|
|
38
38
|
## Why is this happening?
|
|
39
39
|
|
|
40
40
|
See this RFC https://docs.google.com/document/d/1OYyA1wYfQRbIJOIBDliYoWjiUlkFBNxH_U2R4WpVRZ4/edit#heading=h.pecv6xb7ial6
|
|
41
|
-
and the following GitHub issue for more information https://github.com/
|
|
41
|
+
and the following GitHub issue for more information https://github.com/opral/monorepo/issues/1678.
|
|
42
42
|
|
|
43
|
-
- Monorepo support https://github.com/
|
|
43
|
+
- Monorepo support https://github.com/opral/monorepo/discussions/258.
|
|
44
44
|
- Required for many other future features like caching, first class offline support, and more.
|
|
45
45
|
- Stablize the inlang project format.
|
|
46
46
|
`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List of telemetry events for typesafety.
|
|
3
|
+
*
|
|
4
|
+
* - prefix with `SDK` to avoid collisions with other apps
|
|
5
|
+
* - use past tense to indicate that the event is completed
|
|
6
|
+
*/
|
|
7
|
+
declare const events: readonly ["SDK loaded project"];
|
|
8
|
+
/**
|
|
9
|
+
* Capture an event.
|
|
10
|
+
*
|
|
11
|
+
* - manually calling the PostHog API because the SDKs were not platform angostic (and generally bloated)
|
|
12
|
+
*/
|
|
13
|
+
export declare const capture: (event: (typeof events)[number], args: {
|
|
14
|
+
projectId: string;
|
|
15
|
+
/**
|
|
16
|
+
* Please use snake_case for property names.
|
|
17
|
+
*/
|
|
18
|
+
properties: Record<string, any>;
|
|
19
|
+
}) => Promise<void>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/telemetry/capture.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,QAAA,MAAM,MAAM,iCAAkC,CAAA;AAE9C;;;;GAIG;AACH,eAAO,MAAM,OAAO,UACZ,CAAC,aAAa,CAAC,CAAC,MAAM,CAAC,QACxB;IACL,SAAS,EAAE,MAAM,CAAA;IACjB;;OAEG;IACH,UAAU,EAAE,OAAO,MAAM,EAAE,GAAG,CAAC,CAAA;CAC/B,kBAyBD,CAAA"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { ENV_VARIABLES } from "../env-variables/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* List of telemetry events for typesafety.
|
|
4
|
+
*
|
|
5
|
+
* - prefix with `SDK` to avoid collisions with other apps
|
|
6
|
+
* - use past tense to indicate that the event is completed
|
|
7
|
+
*/
|
|
8
|
+
const events = ["SDK loaded project"];
|
|
9
|
+
/**
|
|
10
|
+
* Capture an event.
|
|
11
|
+
*
|
|
12
|
+
* - manually calling the PostHog API because the SDKs were not platform angostic (and generally bloated)
|
|
13
|
+
*/
|
|
14
|
+
export const capture = async (event, args) => {
|
|
15
|
+
// do not send events if the token is not set
|
|
16
|
+
// (assuming this eases testing)
|
|
17
|
+
if (ENV_VARIABLES.PUBLIC_POSTHOG_TOKEN === undefined) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
try {
|
|
21
|
+
await fetch("https://eu.posthog.com/capture/", {
|
|
22
|
+
method: "POST",
|
|
23
|
+
body: JSON.stringify({
|
|
24
|
+
api_key: ENV_VARIABLES.PUBLIC_POSTHOG_TOKEN,
|
|
25
|
+
event,
|
|
26
|
+
// id is "unknown" because no user information is available
|
|
27
|
+
distinct_id: "unknown",
|
|
28
|
+
properties: {
|
|
29
|
+
$groups: { project: args.projectId },
|
|
30
|
+
...args.properties,
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
// TODO implement sentry logging
|
|
37
|
+
// do not console.log and avoid exposing internal errors to the user
|
|
38
|
+
}
|
|
39
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inlang/sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.23.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"access": "public"
|
|
@@ -26,16 +26,16 @@
|
|
|
26
26
|
"solid-js": "1.6.12",
|
|
27
27
|
"throttle-debounce": "^5.0.0",
|
|
28
28
|
"@inlang/json-types": "1.1.0",
|
|
29
|
-
"@inlang/language-tag": "1.
|
|
30
|
-
"@inlang/message
|
|
31
|
-
"@inlang/message": "
|
|
32
|
-
"@inlang/module": "1.2.
|
|
33
|
-
"@inlang/plugin": "2.4.
|
|
34
|
-
"@inlang/project-settings": "2.2.
|
|
35
|
-
"@inlang/
|
|
36
|
-
"@
|
|
29
|
+
"@inlang/language-tag": "1.4.0",
|
|
30
|
+
"@inlang/message": "2.0.1",
|
|
31
|
+
"@inlang/message-lint-rule": "1.4.1",
|
|
32
|
+
"@inlang/module": "1.2.2",
|
|
33
|
+
"@inlang/plugin": "2.4.2",
|
|
34
|
+
"@inlang/project-settings": "2.2.1",
|
|
35
|
+
"@inlang/translatable": "1.2.1",
|
|
36
|
+
"@lix-js/fs": "0.5.0",
|
|
37
37
|
"@lix-js/client": "0.5.0",
|
|
38
|
-
"@
|
|
38
|
+
"@inlang/result": "1.1.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/throttle-debounce": "5.0.0",
|
|
@@ -47,8 +47,9 @@
|
|
|
47
47
|
"vitest": "^0.33.0"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
|
-
"build": "tsc --build",
|
|
51
|
-
"dev": "tsc --watch",
|
|
50
|
+
"build": "npm run prepare-env-variables && tsc --build",
|
|
51
|
+
"dev": "npm run prepare-env-variables && tsc --watch",
|
|
52
|
+
"prepare-env-variables": "node ./src/env-variables/createIndexFile.js",
|
|
52
53
|
"test": "tsc --noEmit && vitest run --passWithNoTests --coverage",
|
|
53
54
|
"lint": "eslint ./src --fix",
|
|
54
55
|
"format": "prettier ./src --write",
|
|
@@ -28,7 +28,7 @@ export const createNodeishFsWithWatcher = (args: {
|
|
|
28
28
|
}
|
|
29
29
|
} catch (err: any) {
|
|
30
30
|
if (err.name === "AbortError") return
|
|
31
|
-
// https://github.com/
|
|
31
|
+
// https://github.com/opral/monorepo/issues/1647
|
|
32
32
|
// the file does not exist (yet)
|
|
33
33
|
// this is not testable beacause the fs.watch api differs
|
|
34
34
|
// from node and lix. lenghty
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
index.js
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-imports */
|
|
2
|
+
/* eslint-disable no-undef */
|
|
3
|
+
/**
|
|
4
|
+
* This script writes public environment variables
|
|
5
|
+
* to an importable env file.
|
|
6
|
+
*
|
|
7
|
+
* - The SDK must bundle this file with the rest of the SDK
|
|
8
|
+
* - This scripts avoids the need for a bundler
|
|
9
|
+
* - Must be ran before building the SDK
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import fs from "node:fs/promises"
|
|
13
|
+
import url from "node:url"
|
|
14
|
+
import path from "node:path"
|
|
15
|
+
|
|
16
|
+
const dirname = path.dirname(url.fileURLToPath(import.meta.url))
|
|
17
|
+
|
|
18
|
+
await fs.writeFile(
|
|
19
|
+
dirname + "/index.ts",
|
|
20
|
+
`
|
|
21
|
+
export const ENV_VARIABLES = {
|
|
22
|
+
PUBLIC_POSTHOG_TOKEN: "${process.env.PUBLIC_POSTHOG_TOKEN}",
|
|
23
|
+
}
|
|
24
|
+
`
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
// eslint-disable-next-line no-console
|
|
28
|
+
console.log("✅ Created env variable index file.")
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Avoiding TypeScript errors before the `createIndexFile` script
|
|
3
|
+
* is invoked by defining the type ahead of time.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Env variables that are available at runtime.
|
|
8
|
+
*
|
|
9
|
+
* - assume that each env variable might be undefined (to ease development/contributions)
|
|
10
|
+
*/
|
|
11
|
+
export declare const ENV_VARIABLES: Partial<{
|
|
12
|
+
PUBLIC_POSTHOG_TOKEN: string
|
|
13
|
+
}>
|
package/src/listProjects.test.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
import { assert, describe, it } from "vitest"
|
|
1
|
+
import { assert, describe, it, expect } from "vitest"
|
|
2
2
|
import { listProjects } from "./listProjects.js"
|
|
3
|
-
import { createNodeishMemoryFs } from "@lix-js/fs"
|
|
3
|
+
import { createNodeishMemoryFs, type Snapshot } from "@lix-js/fs"
|
|
4
4
|
import type { ProjectSettings } from "@inlang/project-settings"
|
|
5
|
+
import { mockRepo } from "@lix-js/client"
|
|
6
|
+
// eslint-disable-next-line no-restricted-imports -- test
|
|
7
|
+
import { readFileSync } from "node:fs"
|
|
5
8
|
|
|
6
9
|
const settings: ProjectSettings = {
|
|
7
10
|
sourceLanguageTag: "en",
|
|
@@ -55,6 +58,22 @@ describe("listProjects", () => {
|
|
|
55
58
|
})
|
|
56
59
|
})
|
|
57
60
|
|
|
61
|
+
it("should not crash on broken symlinks as cal.com has", async () => {
|
|
62
|
+
const ciTestRepo: Snapshot = JSON.parse(
|
|
63
|
+
readFileSync("./mocks/ci-test-repo-no-shallow.json", { encoding: "utf-8" })
|
|
64
|
+
)
|
|
65
|
+
const repo = await mockRepo({ fromSnapshot: ciTestRepo })
|
|
66
|
+
repo.checkout({ branch: "test-symlink" })
|
|
67
|
+
|
|
68
|
+
const link = await repo.nodeishFs.readlink("test-symlink-not-existing-target")
|
|
69
|
+
|
|
70
|
+
expect(link).toBe("/test-symlink-not-existing-target//.././no-exist")
|
|
71
|
+
|
|
72
|
+
await listProjects(repo.nodeishFs, "/").then((projects) => {
|
|
73
|
+
assert(projects.length === 1)
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
|
|
58
77
|
it("should also find files inside of a dir that ends with *.inlang", async () => {
|
|
59
78
|
const fs = createNodeishMemoryFs()
|
|
60
79
|
await fs.mkdir("/user/dir1/go.inlang", { recursive: true })
|
package/src/listProjects.ts
CHANGED
|
@@ -17,14 +17,21 @@ export const listProjects = async (
|
|
|
17
17
|
const files = await nodeishFs.readdir(path)
|
|
18
18
|
for (const file of files) {
|
|
19
19
|
const filePath = `${path}/${file}`
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
try {
|
|
21
|
+
const stats = await nodeishFs.stat(filePath)
|
|
22
|
+
if (stats.isDirectory()) {
|
|
23
|
+
if (file === "node_modules") {
|
|
24
|
+
continue
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (file.endsWith(".inlang")) {
|
|
28
|
+
projects.push({ projectPath: filePath })
|
|
29
|
+
} else {
|
|
30
|
+
await searchDir(filePath, depth + 1)
|
|
31
|
+
}
|
|
27
32
|
}
|
|
33
|
+
} catch {
|
|
34
|
+
continue
|
|
28
35
|
}
|
|
29
36
|
}
|
|
30
37
|
}
|
package/src/loadProject.test.ts
CHANGED
|
@@ -20,6 +20,9 @@ import { createNodeishMemoryFs, normalizePath } from "@lix-js/fs"
|
|
|
20
20
|
import { createMessage } from "./test-utilities/createMessage.js"
|
|
21
21
|
import { tryCatch } from "@inlang/result"
|
|
22
22
|
import { mockRepo } from "@lix-js/client"
|
|
23
|
+
import { type Snapshot } from "@lix-js/fs"
|
|
24
|
+
// eslint-disable-next-line no-restricted-imports -- test
|
|
25
|
+
import { readFileSync } from "node:fs"
|
|
23
26
|
|
|
24
27
|
// ------------------------------------------------------------------------------------------------
|
|
25
28
|
|
|
@@ -161,7 +164,10 @@ describe("initialization", () => {
|
|
|
161
164
|
})
|
|
162
165
|
|
|
163
166
|
it("should generate projectId on missing projectid", async () => {
|
|
164
|
-
const
|
|
167
|
+
const ciTestRepo: Snapshot = JSON.parse(
|
|
168
|
+
readFileSync("./mocks/ci-test-repo-no-shallow.json", { encoding: "utf-8" })
|
|
169
|
+
)
|
|
170
|
+
const repo = await mockRepo({ fromSnapshot: ciTestRepo })
|
|
165
171
|
|
|
166
172
|
const existing = await repo.nodeishFs
|
|
167
173
|
.readFile("/project.inlang/project_id", {
|
|
@@ -177,7 +183,6 @@ describe("initialization", () => {
|
|
|
177
183
|
const result = await tryCatch(() =>
|
|
178
184
|
loadProject({
|
|
179
185
|
projectPath: "/project.inlang",
|
|
180
|
-
nodeishFs: repo.nodeishFs,
|
|
181
186
|
repo,
|
|
182
187
|
_import,
|
|
183
188
|
})
|
|
@@ -191,21 +196,23 @@ describe("initialization", () => {
|
|
|
191
196
|
return { error }
|
|
192
197
|
})
|
|
193
198
|
|
|
194
|
-
expect(newId).toBe("
|
|
199
|
+
expect(newId).toBe("e8c61726bc2f437ec6a260abb632b3c59195059b60031e648b5afbafa7f3d79a")
|
|
195
200
|
|
|
196
201
|
expect(result.error).toBeUndefined()
|
|
197
202
|
expect(result.data).toBeDefined()
|
|
198
203
|
})
|
|
199
204
|
|
|
200
205
|
it("should reuse projectId on existing projectid", async () => {
|
|
201
|
-
const
|
|
206
|
+
const ciTestRepo: Snapshot = JSON.parse(
|
|
207
|
+
readFileSync("./mocks/ci-test-repo-no-shallow.json", { encoding: "utf-8" })
|
|
208
|
+
)
|
|
209
|
+
const repo = await mockRepo({ fromSnapshot: ciTestRepo })
|
|
202
210
|
|
|
203
211
|
repo.nodeishFs.writeFile("/project.inlang/project_id", "testId")
|
|
204
212
|
|
|
205
213
|
const result = await tryCatch(() =>
|
|
206
214
|
loadProject({
|
|
207
215
|
projectPath: "/project.inlang",
|
|
208
|
-
nodeishFs: repo.nodeishFs,
|
|
209
216
|
repo,
|
|
210
217
|
_import,
|
|
211
218
|
})
|
package/src/loadProject.ts
CHANGED
|
@@ -23,12 +23,13 @@ import { ProjectSettings, Message, type NodeishFilesystemSubset } from "./versio
|
|
|
23
23
|
import { tryCatch, type Result } from "@inlang/result"
|
|
24
24
|
import { migrateIfOutdated } from "@inlang/project-settings/migration"
|
|
25
25
|
import { createNodeishFsWithAbsolutePaths } from "./createNodeishFsWithAbsolutePaths.js"
|
|
26
|
-
import { normalizePath
|
|
26
|
+
import { normalizePath } from "@lix-js/fs"
|
|
27
27
|
import { isAbsolutePath } from "./isAbsolutePath.js"
|
|
28
28
|
import { createNodeishFsWithWatcher } from "./createNodeishFsWithWatcher.js"
|
|
29
29
|
import { maybeMigrateToDirectory } from "./migrations/migrateToDirectory.js"
|
|
30
|
+
import { maybeCreateFirstProjectId } from "./migrations/maybeCreateFirstProjectId.js"
|
|
30
31
|
import type { Repository } from "@lix-js/client"
|
|
31
|
-
import {
|
|
32
|
+
import { capture } from "./telemetry/capture.js"
|
|
32
33
|
|
|
33
34
|
const settingsCompiler = TypeCompiler.Compile(ProjectSettings)
|
|
34
35
|
|
|
@@ -36,24 +37,49 @@ const settingsCompiler = TypeCompiler.Compile(ProjectSettings)
|
|
|
36
37
|
* Creates an inlang instance.
|
|
37
38
|
*
|
|
38
39
|
* @param projectPath - Absolute path to the inlang settings file.
|
|
39
|
-
* @param nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
|
|
40
|
+
* @param @deprecated nodeishFs - Filesystem that implements the NodeishFilesystemSubset interface.
|
|
40
41
|
* @param _import - Use `_import` to pass a custom import function for testing,
|
|
41
42
|
* and supporting legacy resolvedModules such as CJS.
|
|
42
|
-
* @param _capture - Use `_capture` to capture events for analytics.
|
|
43
43
|
*
|
|
44
44
|
*/
|
|
45
|
-
export
|
|
45
|
+
export async function loadProject(args: {
|
|
46
46
|
projectPath: string
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
nodeishFs: Repository["nodeishFs"]
|
|
48
|
+
/**
|
|
49
|
+
* The app id is used to identify the app that is using the SDK.
|
|
50
|
+
*
|
|
51
|
+
* We use the app id to group events in telemetry to answer questions
|
|
52
|
+
* like "Which apps causes these errors?" or "Which apps are used more than others?".
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* appId: "app.inlang.badge"
|
|
56
|
+
*/
|
|
57
|
+
appId?: string
|
|
49
58
|
_import?: ImportFunction
|
|
50
|
-
|
|
51
|
-
}): Promise<InlangProject> => {
|
|
52
|
-
const projectPath = normalizePath(args.projectPath)
|
|
59
|
+
}): Promise<InlangProject>
|
|
53
60
|
|
|
54
|
-
|
|
61
|
+
/**
|
|
62
|
+
* @param projectPath - Absolute path to the inlang settings file.
|
|
63
|
+
* @param repo - An instance of a lix repo as returned by `openRepository`.
|
|
64
|
+
* @param _import - Use `_import` to pass a custom import function for testing,
|
|
65
|
+
* and supporting legacy resolvedModules such as CJS.
|
|
66
|
+
*
|
|
67
|
+
*/
|
|
68
|
+
export async function loadProject(args: {
|
|
69
|
+
projectPath: string
|
|
70
|
+
repo: Repository
|
|
71
|
+
appId?: string
|
|
72
|
+
_import?: ImportFunction
|
|
73
|
+
}): Promise<InlangProject>
|
|
55
74
|
|
|
56
|
-
|
|
75
|
+
export async function loadProject(args: {
|
|
76
|
+
projectPath: string
|
|
77
|
+
repo?: Repository
|
|
78
|
+
appId?: string
|
|
79
|
+
_import?: ImportFunction
|
|
80
|
+
nodeishFs?: Repository["nodeishFs"]
|
|
81
|
+
}): Promise<InlangProject> {
|
|
82
|
+
const projectPath = normalizePath(args.projectPath)
|
|
57
83
|
|
|
58
84
|
// -- validation --------------------------------------------------------
|
|
59
85
|
// the only place where throwing is acceptable because the project
|
|
@@ -72,35 +98,37 @@ export const loadProject = async (args: {
|
|
|
72
98
|
)
|
|
73
99
|
}
|
|
74
100
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
})
|
|
101
|
+
let fs: Repository["nodeishFs"]
|
|
102
|
+
if (args.nodeishFs) {
|
|
103
|
+
// TODO: deprecate
|
|
104
|
+
fs = args.nodeishFs
|
|
105
|
+
} else if (args.repo) {
|
|
106
|
+
fs = args.repo.nodeishFs
|
|
107
|
+
} else {
|
|
108
|
+
throw new LoadProjectInvalidArgument(`Repo missing from arguments.`, { argument: "repo" })
|
|
109
|
+
}
|
|
83
110
|
|
|
84
|
-
|
|
111
|
+
const nodeishFs = createNodeishFsWithAbsolutePaths({
|
|
112
|
+
projectPath,
|
|
113
|
+
nodeishFs: fs,
|
|
114
|
+
})
|
|
85
115
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if (error.code === "ENOENT") {
|
|
93
|
-
if (args.repo) {
|
|
94
|
-
projectId = await generateProjectId(args.repo, projectPath)
|
|
95
|
-
if (projectId) {
|
|
96
|
-
await nodeishFs.writeFile(projectPath + "/project_id", projectId)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
} else {
|
|
100
|
-
idError = error as Error
|
|
101
|
-
}
|
|
102
|
-
}
|
|
116
|
+
// -- migratations ------------------------------------------------
|
|
117
|
+
|
|
118
|
+
await maybeMigrateToDirectory({ nodeishFs: fs, projectPath })
|
|
119
|
+
await maybeCreateFirstProjectId({ projectPath, repo: args.repo })
|
|
120
|
+
|
|
121
|
+
// -- load project ------------------------------------------------------
|
|
103
122
|
|
|
123
|
+
return await createRoot(async () => {
|
|
124
|
+
// TODO remove tryCatch after https://github.com/opral/monorepo/issues/2013
|
|
125
|
+
// - a repo will always be present
|
|
126
|
+
// - if a repo is present, the project id will always be present
|
|
127
|
+
const { data: projectId } = await tryCatch(() =>
|
|
128
|
+
fs.readFile(args.projectPath + "/project_id", { encoding: "utf-8" })
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
const [initialized, markInitAsComplete, markInitAsFailed] = createAwaitable()
|
|
104
132
|
// -- settings ------------------------------------------------------------
|
|
105
133
|
|
|
106
134
|
const [settings, _setSettings] = createSignal<ProjectSettings>()
|
|
@@ -113,12 +141,7 @@ export const loadProject = async (args: {
|
|
|
113
141
|
// }
|
|
114
142
|
|
|
115
143
|
loadSettings({ settingsFilePath: projectPath + "/settings.json", nodeishFs })
|
|
116
|
-
.then((settings) =>
|
|
117
|
-
setSettings(settings)
|
|
118
|
-
// rename settings to get a convenient access to the data in Posthog
|
|
119
|
-
const project_settings = settings
|
|
120
|
-
args._capture?.("SDK used settings", { project_settings, group: projectId })
|
|
121
|
-
})
|
|
144
|
+
.then((settings) => setSettings(settings))
|
|
122
145
|
.catch((err) => {
|
|
123
146
|
markInitAsFailed(err)
|
|
124
147
|
})
|
|
@@ -218,7 +241,7 @@ export const loadProject = async (args: {
|
|
|
218
241
|
module:
|
|
219
242
|
resolvedModules()?.meta.find((m) => m.id.includes(rule.id))?.module ??
|
|
220
243
|
"Unknown module. You stumbled on a bug in inlang's source code. Please open an issue.",
|
|
221
|
-
// default to warning, see https://github.com/
|
|
244
|
+
// default to warning, see https://github.com/opral/monorepo/issues/1254
|
|
222
245
|
level: settingsValue["messageLintRuleLevels"]?.[rule.id] ?? "warning",
|
|
223
246
|
} satisfies InstalledMessageLintRule)
|
|
224
247
|
) satisfies Array<InstalledMessageLintRule>
|
|
@@ -285,6 +308,29 @@ export const loadProject = async (args: {
|
|
|
285
308
|
debouncedSave(messagesQuery.getAll())
|
|
286
309
|
})
|
|
287
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Utility to escape reactive tracking and avoid multiple calls to
|
|
313
|
+
* the capture event.
|
|
314
|
+
*
|
|
315
|
+
* Should be addressed with https://github.com/opral/monorepo/issues/1772
|
|
316
|
+
*/
|
|
317
|
+
let projectLoadedCapturedAlready = false
|
|
318
|
+
|
|
319
|
+
if (projectId && projectLoadedCapturedAlready === false) {
|
|
320
|
+
projectLoadedCapturedAlready = true
|
|
321
|
+
// TODO ensure that capture is "awaited" without blocking the the app from starting
|
|
322
|
+
await capture("SDK loaded project", {
|
|
323
|
+
projectId,
|
|
324
|
+
properties: {
|
|
325
|
+
appId: args.appId,
|
|
326
|
+
settings: settings(),
|
|
327
|
+
installedPluginIds: installedPlugins().map((p) => p.id),
|
|
328
|
+
installedMessageLintRuleIds: installedMessageLintRules().map((r) => r.id),
|
|
329
|
+
numberOfMessages: messagesQuery.includedMessageIds().length,
|
|
330
|
+
},
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
|
|
288
334
|
return {
|
|
289
335
|
installed: {
|
|
290
336
|
plugins: createSubscribable(() => installedPlugins()),
|
|
@@ -292,10 +338,7 @@ export const loadProject = async (args: {
|
|
|
292
338
|
},
|
|
293
339
|
errors: createSubscribable(() => [
|
|
294
340
|
...(initializeError ? [initializeError] : []),
|
|
295
|
-
...(idError ? [idError] : []),
|
|
296
341
|
...(resolvedModules() ? resolvedModules()!.errors : []),
|
|
297
|
-
// have a query error exposed
|
|
298
|
-
//...(lintErrors() ?? []),
|
|
299
342
|
]),
|
|
300
343
|
settings: createSubscribable(() => settings() as ProjectSettings),
|
|
301
344
|
setSettings,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { generateProjectId } from "./maybeCreateFirstProjectId.js"
|
|
2
|
+
import { it, expect } from "vitest"
|
|
3
|
+
import { openRepository } from "@lix-js/client/src/openRepository.ts"
|
|
4
|
+
import { mockRepo, createNodeishMemoryFs } from "@lix-js/client"
|
|
5
|
+
import { type Snapshot } from "@lix-js/fs"
|
|
6
|
+
// eslint-disable-next-line no-restricted-imports -- test
|
|
7
|
+
import { readFileSync } from "node:fs"
|
|
8
|
+
|
|
9
|
+
const ciTestRepo: Snapshot = JSON.parse(
|
|
10
|
+
readFileSync("./mocks/ci-test-repo-no-shallow.json", { encoding: "utf-8" })
|
|
11
|
+
)
|
|
12
|
+
const repo = await mockRepo({ fromSnapshot: ciTestRepo as Snapshot })
|
|
13
|
+
|
|
14
|
+
it("should return if repo is undefined", async () => {
|
|
15
|
+
const projectId = await generateProjectId({ repo: undefined, projectPath: "mocked_project_path" })
|
|
16
|
+
expect(projectId).toBeUndefined()
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
it("should generate a project id", async () => {
|
|
20
|
+
const projectId = await generateProjectId({ repo, projectPath: "mocked_project_path" })
|
|
21
|
+
expect(projectId).toBe("959bcf0a30e678c9b90a3c76d1a281d085eab55f289c5439b6b10849baa1920c")
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it("should return undefined if repoMeta contains error", async () => {
|
|
25
|
+
const repoWithError = await openRepository("https://github.com/inlang/no-exist", {
|
|
26
|
+
nodeishFs: createNodeishMemoryFs(),
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
const projectId = await generateProjectId({
|
|
30
|
+
repo: repoWithError,
|
|
31
|
+
projectPath: "mocked_project_path",
|
|
32
|
+
})
|
|
33
|
+
expect(projectId).toBeUndefined()
|
|
34
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { Repository } from "@lix-js/client"
|
|
2
|
+
import { hash } from "@lix-js/client"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a project id if it does not exist yet.
|
|
6
|
+
*
|
|
7
|
+
* - this is a migration to ensure that all projects have a project id
|
|
8
|
+
* - new projects are created with a project id (in the future)
|
|
9
|
+
*/
|
|
10
|
+
export async function maybeCreateFirstProjectId(args: {
|
|
11
|
+
projectPath: string
|
|
12
|
+
repo?: Repository
|
|
13
|
+
}): Promise<void> {
|
|
14
|
+
// the migration assumes a repository
|
|
15
|
+
if (args.repo === undefined) {
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
try {
|
|
19
|
+
await args.repo.nodeishFs.readFile(args.projectPath + "/project_id", {
|
|
20
|
+
encoding: "utf-8",
|
|
21
|
+
})
|
|
22
|
+
} catch (error) {
|
|
23
|
+
// @ts-ignore
|
|
24
|
+
if (error.code === "ENOENT" && args.repo) {
|
|
25
|
+
const projectId = await generateProjectId({ repo: args.repo, projectPath: args.projectPath })
|
|
26
|
+
if (projectId) {
|
|
27
|
+
await args.repo.nodeishFs.writeFile(args.projectPath + "/project_id", projectId)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function generateProjectId(args: { repo?: Repository; projectPath: string }) {
|
|
34
|
+
if (!args.repo || !args.projectPath) {
|
|
35
|
+
return undefined
|
|
36
|
+
}
|
|
37
|
+
const firstCommitHash = await args.repo.getFirstCommitHash()
|
|
38
|
+
const originHash = await args.repo.getOrigin({ safeHashOnly: true })
|
|
39
|
+
|
|
40
|
+
if (firstCommitHash) {
|
|
41
|
+
return hash(`${firstCommitHash + args.projectPath + originHash}`)
|
|
42
|
+
}
|
|
43
|
+
return undefined
|
|
44
|
+
}
|
|
@@ -3,7 +3,7 @@ import type { NodeishFilesystem } from "@lix-js/fs"
|
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Migrates to the new project directory structure
|
|
6
|
-
* https://github.com/
|
|
6
|
+
* https://github.com/opral/monorepo/issues/1678
|
|
7
7
|
*/
|
|
8
8
|
export const maybeMigrateToDirectory = async (args: {
|
|
9
9
|
nodeishFs: NodeishFilesystem
|
|
@@ -51,9 +51,9 @@ The \`project.inlang.json\` file is now contained in a project directory e.g. \`
|
|
|
51
51
|
## Why is this happening?
|
|
52
52
|
|
|
53
53
|
See this RFC https://docs.google.com/document/d/1OYyA1wYfQRbIJOIBDliYoWjiUlkFBNxH_U2R4WpVRZ4/edit#heading=h.pecv6xb7ial6
|
|
54
|
-
and the following GitHub issue for more information https://github.com/
|
|
54
|
+
and the following GitHub issue for more information https://github.com/opral/monorepo/issues/1678.
|
|
55
55
|
|
|
56
|
-
- Monorepo support https://github.com/
|
|
56
|
+
- Monorepo support https://github.com/opral/monorepo/discussions/258.
|
|
57
57
|
- Required for many other future features like caching, first class offline support, and more.
|
|
58
58
|
- Stablize the inlang project format.
|
|
59
59
|
`
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ENV_VARIABLES } from "../env-variables/index.js"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* List of telemetry events for typesafety.
|
|
5
|
+
*
|
|
6
|
+
* - prefix with `SDK` to avoid collisions with other apps
|
|
7
|
+
* - use past tense to indicate that the event is completed
|
|
8
|
+
*/
|
|
9
|
+
const events = ["SDK loaded project"] as const
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Capture an event.
|
|
13
|
+
*
|
|
14
|
+
* - manually calling the PostHog API because the SDKs were not platform angostic (and generally bloated)
|
|
15
|
+
*/
|
|
16
|
+
export const capture = async (
|
|
17
|
+
event: (typeof events)[number],
|
|
18
|
+
args: {
|
|
19
|
+
projectId: string
|
|
20
|
+
/**
|
|
21
|
+
* Please use snake_case for property names.
|
|
22
|
+
*/
|
|
23
|
+
properties: Record<string, any>
|
|
24
|
+
}
|
|
25
|
+
) => {
|
|
26
|
+
// do not send events if the token is not set
|
|
27
|
+
// (assuming this eases testing)
|
|
28
|
+
if (ENV_VARIABLES.PUBLIC_POSTHOG_TOKEN === undefined) {
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
await fetch("https://eu.posthog.com/capture/", {
|
|
33
|
+
method: "POST",
|
|
34
|
+
body: JSON.stringify({
|
|
35
|
+
api_key: ENV_VARIABLES.PUBLIC_POSTHOG_TOKEN,
|
|
36
|
+
event,
|
|
37
|
+
// id is "unknown" because no user information is available
|
|
38
|
+
distinct_id: "unknown",
|
|
39
|
+
properties: {
|
|
40
|
+
$groups: { project: args.projectId },
|
|
41
|
+
...args.properties,
|
|
42
|
+
},
|
|
43
|
+
}),
|
|
44
|
+
})
|
|
45
|
+
} catch (e) {
|
|
46
|
+
// TODO implement sentry logging
|
|
47
|
+
// do not console.log and avoid exposing internal errors to the user
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generateProjectId.d.ts","sourceRoot":"","sources":["../src/generateProjectId.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAGhD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,+BAU5E"}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { hash } from "@lix-js/client";
|
|
2
|
-
export async function generateProjectId(repo, projectPath) {
|
|
3
|
-
if (!repo || !projectPath) {
|
|
4
|
-
return undefined;
|
|
5
|
-
}
|
|
6
|
-
const repoMeta = await repo.getMeta();
|
|
7
|
-
if (repoMeta && !("error" in repoMeta)) {
|
|
8
|
-
return hash(`${repoMeta.id + projectPath}`);
|
|
9
|
-
}
|
|
10
|
-
return undefined;
|
|
11
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"generateProjectId.test.d.ts","sourceRoot":"","sources":["../src/generateProjectId.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { generateProjectId } from "./generateProjectId.js";
|
|
2
|
-
import { describe, it, expect } from "vitest";
|
|
3
|
-
import { openRepository } from "@lix-js/client/src/openRepository.ts";
|
|
4
|
-
import { mockRepo, createNodeishMemoryFs } from "@lix-js/client";
|
|
5
|
-
describe("generateProjectId", async () => {
|
|
6
|
-
const repo = await mockRepo();
|
|
7
|
-
it("should generate a project id", async () => {
|
|
8
|
-
const projectId = await generateProjectId(repo, "mocked_project_path");
|
|
9
|
-
expect(projectId).toBe("0c83325bf9068eb01091c522d4b8e3765aff42e36fc781c041b44439bbe3e734");
|
|
10
|
-
});
|
|
11
|
-
it("should return undefined if repoMeta contains error", async () => {
|
|
12
|
-
const errorRepo = await openRepository("https://github.com/inlang/no-exist", {
|
|
13
|
-
nodeishFs: createNodeishMemoryFs(),
|
|
14
|
-
});
|
|
15
|
-
const projectId = await generateProjectId(errorRepo, "mocked_project_path");
|
|
16
|
-
expect(projectId).toBeUndefined();
|
|
17
|
-
});
|
|
18
|
-
});
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { generateProjectId } from "./generateProjectId.js"
|
|
2
|
-
import { describe, it, expect } from "vitest"
|
|
3
|
-
import { openRepository } from "@lix-js/client/src/openRepository.ts"
|
|
4
|
-
import { mockRepo, createNodeishMemoryFs } from "@lix-js/client"
|
|
5
|
-
|
|
6
|
-
describe("generateProjectId", async () => {
|
|
7
|
-
const repo = await mockRepo()
|
|
8
|
-
|
|
9
|
-
it("should generate a project id", async () => {
|
|
10
|
-
const projectId = await generateProjectId(repo, "mocked_project_path")
|
|
11
|
-
expect(projectId).toBe("0c83325bf9068eb01091c522d4b8e3765aff42e36fc781c041b44439bbe3e734")
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it("should return undefined if repoMeta contains error", async () => {
|
|
15
|
-
const errorRepo = await openRepository("https://github.com/inlang/no-exist", {
|
|
16
|
-
nodeishFs: createNodeishMemoryFs(),
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
const projectId = await generateProjectId(errorRepo, "mocked_project_path")
|
|
20
|
-
expect(projectId).toBeUndefined()
|
|
21
|
-
})
|
|
22
|
-
})
|
package/src/generateProjectId.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { Repository } from "@lix-js/client"
|
|
2
|
-
import { hash } from "@lix-js/client"
|
|
3
|
-
|
|
4
|
-
export async function generateProjectId(repo: Repository, projectPath: string) {
|
|
5
|
-
if (!repo || !projectPath) {
|
|
6
|
-
return undefined
|
|
7
|
-
}
|
|
8
|
-
const repoMeta = await repo.getMeta()
|
|
9
|
-
|
|
10
|
-
if (repoMeta && !("error" in repoMeta)) {
|
|
11
|
-
return hash(`${repoMeta.id + projectPath}`)
|
|
12
|
-
}
|
|
13
|
-
return undefined
|
|
14
|
-
}
|