@inlang/sdk 0.18.0 → 0.20.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 +1 -1
- package/dist/adapter/solidAdapter.test.js +16 -15
- package/dist/createMessageLintReportsQuery.d.ts +2 -3
- package/dist/createMessageLintReportsQuery.d.ts.map +1 -1
- package/dist/createMessageLintReportsQuery.js +42 -21
- package/dist/createNodeishFsWithAbsolutePaths.d.ts +2 -2
- package/dist/createNodeishFsWithAbsolutePaths.d.ts.map +1 -1
- package/dist/createNodeishFsWithAbsolutePaths.js +4 -4
- package/dist/createNodeishFsWithAbsolutePaths.test.js +4 -4
- package/dist/createNodeishFsWithWatcher.d.ts +1 -1
- package/dist/createNodeishFsWithWatcher.d.ts.map +1 -1
- package/dist/createNodeishFsWithWatcher.js +6 -4
- package/dist/generateProjectId.d.ts +3 -0
- package/dist/generateProjectId.d.ts.map +1 -0
- package/dist/generateProjectId.js +11 -0
- package/dist/generateProjectId.test.d.ts +2 -0
- package/dist/generateProjectId.test.d.ts.map +1 -0
- package/dist/generateProjectId.test.js +18 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/isAbsolutePath.test.js +1 -1
- package/dist/listProjects.d.ts +5 -0
- package/dist/listProjects.d.ts.map +1 -0
- package/dist/listProjects.js +31 -0
- package/dist/listProjects.test.d.ts +2 -0
- package/dist/listProjects.test.d.ts.map +1 -0
- package/dist/listProjects.test.js +56 -0
- package/dist/loadProject.d.ts +6 -4
- package/dist/loadProject.d.ts.map +1 -1
- package/dist/loadProject.js +58 -22
- package/dist/loadProject.test.js +157 -75
- package/dist/migrations/migrateToDirectory.d.ts +10 -0
- package/dist/migrations/migrateToDirectory.d.ts.map +1 -0
- package/dist/migrations/migrateToDirectory.js +46 -0
- package/dist/migrations/migrateToDirectory.test.d.ts +2 -0
- package/dist/migrations/migrateToDirectory.test.d.ts.map +1 -0
- package/dist/migrations/migrateToDirectory.test.js +48 -0
- package/dist/resolve-modules/validateModuleSettings.test.js +1 -1
- package/package.json +57 -56
- package/src/adapter/solidAdapter.test.ts +16 -15
- package/src/createMessageLintReportsQuery.ts +57 -28
- package/src/createNodeishFsWithAbsolutePaths.test.ts +4 -4
- package/src/createNodeishFsWithAbsolutePaths.ts +5 -5
- package/src/createNodeishFsWithWatcher.ts +6 -4
- package/src/generateProjectId.test.ts +22 -0
- package/src/generateProjectId.ts +14 -0
- package/src/index.ts +1 -0
- package/src/isAbsolutePath.test.ts +1 -1
- package/src/listProjects.test.ts +69 -0
- package/src/listProjects.ts +39 -0
- package/src/loadProject.test.ts +182 -76
- package/src/loadProject.ts +77 -28
- package/src/migrations/migrateToDirectory.test.ts +54 -0
- package/src/migrations/migrateToDirectory.ts +59 -0
- package/src/resolve-modules/validateModuleSettings.test.ts +1 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { tryCatch } from "@inlang/result";
|
|
2
|
+
/**
|
|
3
|
+
* Migrates to the new project directory structure
|
|
4
|
+
* https://github.com/inlang/monorepo/issues/1678
|
|
5
|
+
*/
|
|
6
|
+
export const maybeMigrateToDirectory = async (args) => {
|
|
7
|
+
// the migration assumes that the projectPath ends with project.inlang
|
|
8
|
+
if (args.projectPath.endsWith("project.inlang") === false) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
// we assume that stat will throw when the directory does not exist
|
|
12
|
+
const projectDirectory = await tryCatch(() => args.nodeishFs.stat(args.projectPath));
|
|
13
|
+
// the migration has already been conducted.
|
|
14
|
+
if (projectDirectory.data) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const settingsFile = await tryCatch(() => args.nodeishFs.readFile(args.projectPath + ".json", { encoding: "utf-8" }));
|
|
18
|
+
// the settings file does not exist or something else is wrong, let loadProject handle it
|
|
19
|
+
if (settingsFile.error) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
await args.nodeishFs.mkdir(args.projectPath);
|
|
23
|
+
await args.nodeishFs.writeFile(`${args.projectPath}/settings.json`, settingsFile.data);
|
|
24
|
+
await args.nodeishFs.writeFile(args.projectPath + ".README.md", readme);
|
|
25
|
+
};
|
|
26
|
+
const readme = `
|
|
27
|
+
# DELETE THE \`project.inlang.json\` FILE
|
|
28
|
+
|
|
29
|
+
The \`project.inlang.json\` file is now contained in a project directory e.g. \`project.inlang/settings.json\`.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## What you need to do
|
|
33
|
+
|
|
34
|
+
1. Update the inlang CLI (if you use it) to use the new path \`project.inlang\` instead of \`project.inlang.json\`.
|
|
35
|
+
2. Delete the \`project.inlang.json\` file.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Why is this happening?
|
|
39
|
+
|
|
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/inlang/monorepo/issues/1678.
|
|
42
|
+
|
|
43
|
+
- Monorepo support https://github.com/inlang/monorepo/discussions/258.
|
|
44
|
+
- Required for many other future features like caching, first class offline support, and more.
|
|
45
|
+
- Stablize the inlang project format.
|
|
46
|
+
`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrateToDirectory.test.d.ts","sourceRoot":"","sources":["../../src/migrations/migrateToDirectory.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { test, expect, vi } from "vitest";
|
|
2
|
+
import { maybeMigrateToDirectory } from "./migrateToDirectory.js";
|
|
3
|
+
import { createNodeishMemoryFs } from "@lix-js/fs";
|
|
4
|
+
test("it should return if the settings file has an error (let loadProject handle it)", async () => {
|
|
5
|
+
const projectPath = "./project.inlang";
|
|
6
|
+
const mockFs = {
|
|
7
|
+
stat: vi.fn(() => { }),
|
|
8
|
+
readFile: vi.fn(() => {
|
|
9
|
+
throw Error();
|
|
10
|
+
}),
|
|
11
|
+
};
|
|
12
|
+
await maybeMigrateToDirectory({ nodeishFs: mockFs, projectPath });
|
|
13
|
+
// something goes wrong in readFile
|
|
14
|
+
expect(mockFs.readFile).toHaveBeenCalled();
|
|
15
|
+
});
|
|
16
|
+
test("it should create the project directory if it does not exist and a project settings file exists", async () => {
|
|
17
|
+
const projectPath = "./project.inlang";
|
|
18
|
+
const mockFs = {
|
|
19
|
+
readFile: vi.fn(() => `{
|
|
20
|
+
"sourceLanguageTag": "en",
|
|
21
|
+
"languageTags": ["en", "de"],
|
|
22
|
+
"modules": []
|
|
23
|
+
}`),
|
|
24
|
+
stat: vi.fn(() => {
|
|
25
|
+
throw Error();
|
|
26
|
+
}),
|
|
27
|
+
mkdir: vi.fn(),
|
|
28
|
+
writeFile: vi.fn(),
|
|
29
|
+
};
|
|
30
|
+
await maybeMigrateToDirectory({ nodeishFs: mockFs, projectPath });
|
|
31
|
+
expect(mockFs.mkdir).toHaveBeenCalled();
|
|
32
|
+
expect(mockFs.writeFile).toHaveBeenCalled();
|
|
33
|
+
});
|
|
34
|
+
test("it should write the settings file to the new path", async () => {
|
|
35
|
+
const fs = createNodeishMemoryFs();
|
|
36
|
+
const mockSettings = {
|
|
37
|
+
sourceLanguageTag: "en",
|
|
38
|
+
languageTags: ["en", "de"],
|
|
39
|
+
modules: [],
|
|
40
|
+
};
|
|
41
|
+
await fs.writeFile("./project.inlang.json", JSON.stringify(mockSettings));
|
|
42
|
+
await maybeMigrateToDirectory({ nodeishFs: fs, projectPath: "./project.inlang" });
|
|
43
|
+
const migratedSettingsFile = await fs.readFile("./project.inlang/settings.json", {
|
|
44
|
+
encoding: "utf-8",
|
|
45
|
+
});
|
|
46
|
+
expect(migratedSettingsFile).toEqual(JSON.stringify(mockSettings));
|
|
47
|
+
expect(await fs.stat("./project.inlang.README.md")).toBeDefined();
|
|
48
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
2
2
|
import { expect, test } from "vitest";
|
|
3
|
-
import { Plugin, MessageLintRule } from "
|
|
3
|
+
import { Plugin, MessageLintRule } from "../index.js";
|
|
4
4
|
// import { createNodeishMemoryFs } from "@lix-js/fs"
|
|
5
5
|
import { Type } from "@sinclair/typebox";
|
|
6
6
|
import { validatedModuleSettings } from "./validatedModuleSettings.js";
|
package/package.json
CHANGED
|
@@ -1,57 +1,58 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
2
|
+
"name": "@inlang/sdk",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"version": "0.20.0",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": "./dist/index.js",
|
|
11
|
+
"./test-utilities": "./dist/test-utilities/index.js",
|
|
12
|
+
"./lint": "./dist/lint/index.js",
|
|
13
|
+
"./messages": "./dist/messages/index.js"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"./dist",
|
|
17
|
+
"./src"
|
|
18
|
+
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=18.0.0"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@sinclair/typebox": "^0.31.17",
|
|
24
|
+
"dedent": "1.5.1",
|
|
25
|
+
"deepmerge-ts": "^5.1.0",
|
|
26
|
+
"solid-js": "1.6.12",
|
|
27
|
+
"throttle-debounce": "^5.0.0",
|
|
28
|
+
"@inlang/json-types": "1.1.0",
|
|
29
|
+
"@inlang/message": "2.0.0",
|
|
30
|
+
"@inlang/language-tag": "1.2.0",
|
|
31
|
+
"@inlang/message-lint-rule": "1.4.0",
|
|
32
|
+
"@inlang/plugin": "2.4.0",
|
|
33
|
+
"@inlang/module": "1.2.0",
|
|
34
|
+
"@inlang/project-settings": "2.2.0",
|
|
35
|
+
"@inlang/result": "1.1.0",
|
|
36
|
+
"@lix-js/fs": "0.4.0",
|
|
37
|
+
"@lix-js/client": "0.2.0",
|
|
38
|
+
"@inlang/translatable": "1.2.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/throttle-debounce": "5.0.0",
|
|
42
|
+
"@vitest/coverage-v8": "^0.33.0",
|
|
43
|
+
"jsdom": "22.1.0",
|
|
44
|
+
"patch-package": "6.5.1",
|
|
45
|
+
"tsd": "^0.25.0",
|
|
46
|
+
"typescript": "5.2.2",
|
|
47
|
+
"vitest": "^0.33.0",
|
|
48
|
+
"@lix-js/fs": "0.4.0"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "tsc --build",
|
|
52
|
+
"dev": "tsc --watch",
|
|
53
|
+
"test": "tsc --noEmit && vitest run --passWithNoTests --coverage",
|
|
54
|
+
"lint": "eslint ./src --fix",
|
|
55
|
+
"format": "prettier ./src --write",
|
|
56
|
+
"clean": "rm -rf ./dist ./node_modules"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -90,11 +90,11 @@ const $import: ImportFunction = async (name) => ({
|
|
|
90
90
|
describe("config", () => {
|
|
91
91
|
it("should react to changes in config", async () => {
|
|
92
92
|
const fs = createNodeishMemoryFs()
|
|
93
|
-
await fs.mkdir("/user/project", { recursive: true })
|
|
94
|
-
await fs.writeFile("/user/project
|
|
93
|
+
await fs.mkdir("/user/project.inlang", { recursive: true })
|
|
94
|
+
await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(config))
|
|
95
95
|
const project = solidAdapter(
|
|
96
96
|
await loadProject({
|
|
97
|
-
|
|
97
|
+
projectPath: "/user/project.inlang",
|
|
98
98
|
nodeishFs: fs,
|
|
99
99
|
_import: $import,
|
|
100
100
|
}),
|
|
@@ -123,11 +123,11 @@ describe("config", () => {
|
|
|
123
123
|
describe("installed", () => {
|
|
124
124
|
it("react to changes that are unrelated to installed items", async () => {
|
|
125
125
|
const fs = createNodeishMemoryFs()
|
|
126
|
-
await fs.mkdir("/user/project", { recursive: true })
|
|
127
|
-
await fs.writeFile("/user/project
|
|
126
|
+
await fs.mkdir("/user/project.inlang", { recursive: true })
|
|
127
|
+
await fs.writeFile("/user/project.inlang/settings.json", JSON.stringify(config))
|
|
128
128
|
const project = solidAdapter(
|
|
129
129
|
await loadProject({
|
|
130
|
-
|
|
130
|
+
projectPath: "/user/project.inlang",
|
|
131
131
|
nodeishFs: fs,
|
|
132
132
|
_import: $import,
|
|
133
133
|
}),
|
|
@@ -188,11 +188,11 @@ describe("messages", () => {
|
|
|
188
188
|
|
|
189
189
|
const mockImport: ImportFunction = async () => ({ default: mockPlugin })
|
|
190
190
|
|
|
191
|
-
await fs.mkdir("/user/project", { recursive: true })
|
|
192
|
-
await fs.writeFile("/user/project
|
|
191
|
+
await fs.mkdir("/user/project.inlang.inlang", { recursive: true })
|
|
192
|
+
await fs.writeFile("/user/project.inlang.inlang/settings.json", JSON.stringify(mockConfig))
|
|
193
193
|
const project = solidAdapter(
|
|
194
194
|
await loadProject({
|
|
195
|
-
|
|
195
|
+
projectPath: "/user/project.inlang.inlang",
|
|
196
196
|
nodeishFs: fs,
|
|
197
197
|
_import: mockImport,
|
|
198
198
|
}),
|
|
@@ -218,11 +218,11 @@ describe("messages", () => {
|
|
|
218
218
|
|
|
219
219
|
it("should react to changes in messages", async () => {
|
|
220
220
|
const fs = createNodeishMemoryFs()
|
|
221
|
-
await fs.mkdir("/user/project", { recursive: true })
|
|
222
|
-
await fs.writeFile("/user/project
|
|
221
|
+
await fs.mkdir("/user/project.inlang.inlang", { recursive: true })
|
|
222
|
+
await fs.writeFile("/user/project.inlang.inlang/settings.json", JSON.stringify(config))
|
|
223
223
|
const project = solidAdapter(
|
|
224
224
|
await loadProject({
|
|
225
|
-
|
|
225
|
+
projectPath: "/user/project.inlang.inlang",
|
|
226
226
|
nodeishFs: fs,
|
|
227
227
|
_import: $import,
|
|
228
228
|
}),
|
|
@@ -280,10 +280,11 @@ describe("lint", () => {
|
|
|
280
280
|
it.todo("should react to changes in config", async () => {
|
|
281
281
|
await createRoot(async () => {
|
|
282
282
|
const fs = createNodeishMemoryFs()
|
|
283
|
-
await fs.
|
|
283
|
+
await fs.mkdir("./project.inlang", { recursive: true })
|
|
284
|
+
await fs.writeFile("./project.inlang/settings.json", JSON.stringify(config))
|
|
284
285
|
const project = solidAdapter(
|
|
285
286
|
await loadProject({
|
|
286
|
-
|
|
287
|
+
projectPath: "./project.inlang",
|
|
287
288
|
nodeishFs: fs,
|
|
288
289
|
_import: $import,
|
|
289
290
|
}),
|
|
@@ -321,7 +322,7 @@ describe("lint", () => {
|
|
|
321
322
|
await fs.writeFile("./project.config.json", JSON.stringify(config))
|
|
322
323
|
const project = solidAdapter(
|
|
323
324
|
await loadProject({
|
|
324
|
-
|
|
325
|
+
projectPath: "./project.config.json",
|
|
325
326
|
nodeishFs: fs,
|
|
326
327
|
_import: $import,
|
|
327
328
|
}),
|
|
@@ -1,50 +1,79 @@
|
|
|
1
|
-
import { createEffect } from "./reactivity/solid.js"
|
|
2
1
|
import { createSubscribable } from "./loadProject.js"
|
|
3
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
InlangProject,
|
|
4
|
+
InstalledMessageLintRule,
|
|
5
|
+
MessageLintReportsQueryApi,
|
|
6
|
+
MessageQueryApi,
|
|
7
|
+
} from "./api.js"
|
|
4
8
|
import type { ProjectSettings } from "@inlang/project-settings"
|
|
5
9
|
import type { resolveModules } from "./resolve-modules/index.js"
|
|
6
10
|
import type { MessageLintReport, Message } from "./versionedInterfaces.js"
|
|
7
11
|
import { lintSingleMessage } from "./lint/index.js"
|
|
8
12
|
import { ReactiveMap } from "./reactivity/map.js"
|
|
13
|
+
import { debounce } from "throttle-debounce"
|
|
14
|
+
import { createEffect } from "./reactivity/solid.js"
|
|
9
15
|
|
|
10
16
|
/**
|
|
11
17
|
* Creates a reactive query API for messages.
|
|
12
18
|
*/
|
|
13
19
|
export function createMessageLintReportsQuery(
|
|
14
|
-
|
|
20
|
+
messagesQuery: MessageQueryApi,
|
|
15
21
|
settings: () => ProjectSettings,
|
|
16
22
|
installedMessageLintRules: () => Array<InstalledMessageLintRule>,
|
|
17
|
-
resolvedModules: () => Awaited<ReturnType<typeof resolveModules>> | undefined
|
|
23
|
+
resolvedModules: () => Awaited<ReturnType<typeof resolveModules>> | undefined,
|
|
24
|
+
hasWatcher: boolean
|
|
18
25
|
): InlangProject["query"]["messageLintReports"] {
|
|
19
26
|
// @ts-expect-error
|
|
20
27
|
const index = new ReactiveMap<MessageLintReport["messageId"], MessageLintReport[]>()
|
|
21
28
|
|
|
22
|
-
|
|
23
|
-
const modules = resolvedModules()
|
|
24
|
-
const _messages = messages()
|
|
25
|
-
const _settings = settings()
|
|
29
|
+
const modules = resolvedModules()
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
const rulesArray = modules?.messageLintRules
|
|
32
|
+
const messageLintRuleLevels = Object.fromEntries(
|
|
33
|
+
installedMessageLintRules().map((rule) => [rule.id, rule.level])
|
|
34
|
+
)
|
|
35
|
+
const settingsObject = () => {
|
|
36
|
+
return {
|
|
37
|
+
...settings(),
|
|
38
|
+
messageLintRuleLevels,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const messages = messagesQuery.getAll() as Message[]
|
|
31
43
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
createEffect(() => {
|
|
45
|
+
if (rulesArray) {
|
|
46
|
+
for (const messageId of messagesQuery.includedMessageIds()) {
|
|
47
|
+
createEffect(() => {
|
|
48
|
+
const message = messagesQuery.get({ where: { id: messageId } })
|
|
49
|
+
if (hasWatcher) {
|
|
50
|
+
lintSingleMessage({
|
|
51
|
+
rules: rulesArray,
|
|
52
|
+
settings: settingsObject(),
|
|
53
|
+
messages: messages,
|
|
54
|
+
message: message,
|
|
55
|
+
}).then((report) => {
|
|
56
|
+
if (report.errors.length === 0 && index.get(messageId) !== report.data) {
|
|
57
|
+
index.set(messageId, report.data)
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
} else {
|
|
61
|
+
debounce(
|
|
62
|
+
500,
|
|
63
|
+
(message) => {
|
|
64
|
+
lintSingleMessage({
|
|
65
|
+
rules: rulesArray,
|
|
66
|
+
settings: settingsObject(),
|
|
67
|
+
messages: messages,
|
|
68
|
+
message: message,
|
|
69
|
+
}).then((report) => {
|
|
70
|
+
if (report.errors.length === 0 && index.get(messageId) !== report.data) {
|
|
71
|
+
index.set(messageId, report.data)
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
},
|
|
75
|
+
{ atBegin: false }
|
|
76
|
+
)(message)
|
|
48
77
|
}
|
|
49
78
|
})
|
|
50
79
|
}
|
|
@@ -2,16 +2,16 @@ import { it, expect, vi } from "vitest"
|
|
|
2
2
|
import { createNodeishFsWithAbsolutePaths } from "./createNodeishFsWithAbsolutePaths.js"
|
|
3
3
|
import type { NodeishFilesystemSubset } from "./versionedInterfaces.js"
|
|
4
4
|
|
|
5
|
-
it("throws an error if
|
|
5
|
+
it("throws an error if projectPath is not an absolute path", () => {
|
|
6
6
|
const relativePath = "relative/path"
|
|
7
7
|
|
|
8
8
|
expect(() =>
|
|
9
|
-
createNodeishFsWithAbsolutePaths({
|
|
9
|
+
createNodeishFsWithAbsolutePaths({ projectPath: relativePath, nodeishFs: {} as any })
|
|
10
10
|
).toThrow()
|
|
11
11
|
})
|
|
12
12
|
|
|
13
13
|
it("intercepts paths correctly for readFile", async () => {
|
|
14
|
-
const
|
|
14
|
+
const projectPath = `/Users/samuel/Documents/paraglide/example/project.inlang`
|
|
15
15
|
|
|
16
16
|
const filePaths = [
|
|
17
17
|
["file.txt", `/Users/samuel/Documents/paraglide/example/file.txt`],
|
|
@@ -32,7 +32,7 @@ it("intercepts paths correctly for readFile", async () => {
|
|
|
32
32
|
} satisfies Record<keyof NodeishFilesystemSubset, any>
|
|
33
33
|
|
|
34
34
|
const interceptedFs = createNodeishFsWithAbsolutePaths({
|
|
35
|
-
|
|
35
|
+
projectPath,
|
|
36
36
|
nodeishFs: mockNodeishFs,
|
|
37
37
|
})
|
|
38
38
|
|
|
@@ -6,19 +6,19 @@ import { isAbsolutePath } from "./isAbsolutePath.js"
|
|
|
6
6
|
* Wraps the nodeish filesystem subset with a function that intercepts paths
|
|
7
7
|
* and prepends the base path.
|
|
8
8
|
*
|
|
9
|
-
* The paths are resolved from the `
|
|
9
|
+
* The paths are resolved from the `projectPath` argument.
|
|
10
10
|
*/
|
|
11
11
|
export const createNodeishFsWithAbsolutePaths = (args: {
|
|
12
|
-
|
|
12
|
+
projectPath: string
|
|
13
13
|
nodeishFs: NodeishFilesystemSubset
|
|
14
14
|
}): NodeishFilesystemSubset => {
|
|
15
|
-
if (!isAbsolutePath(args.
|
|
16
|
-
throw new Error(`Expected an absolute path but received "${args.
|
|
15
|
+
if (!isAbsolutePath(args.projectPath)) {
|
|
16
|
+
throw new Error(`Expected an absolute path but received "${args.projectPath}".`)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
// get the base path of the settings file by
|
|
20
20
|
// removing the file name from the path
|
|
21
|
-
const basePath = normalizePath(args.
|
|
21
|
+
const basePath = normalizePath(args.projectPath).split("/").slice(0, -1).join("/")
|
|
22
22
|
|
|
23
23
|
const makeAbsolute = (path: string) => {
|
|
24
24
|
if (isAbsolutePath(path)) {
|
|
@@ -4,7 +4,7 @@ import type { NodeishFilesystemSubset } from "@inlang/plugin"
|
|
|
4
4
|
* Wraps the nodeish filesystem subset with a function that intercepts paths
|
|
5
5
|
* and prepends the base path.
|
|
6
6
|
*
|
|
7
|
-
* The paths are resolved from the `
|
|
7
|
+
* The paths are resolved from the `projectPath` argument.
|
|
8
8
|
*/
|
|
9
9
|
export const createNodeishFsWithWatcher = (args: {
|
|
10
10
|
nodeishFs: NodeishFilesystemSubset
|
|
@@ -20,9 +20,11 @@ export const createNodeishFsWithWatcher = (args: {
|
|
|
20
20
|
signal: abortController.signal,
|
|
21
21
|
persistent: false,
|
|
22
22
|
})
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if (watcher) {
|
|
24
|
+
//eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
25
|
+
for await (const event of watcher) {
|
|
26
|
+
args.updateMessages()
|
|
27
|
+
}
|
|
26
28
|
}
|
|
27
29
|
} catch (err: any) {
|
|
28
30
|
if (err.name === "AbortError") return
|
|
@@ -0,0 +1,22 @@
|
|
|
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
|
+
})
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export type {
|
|
|
13
13
|
} from "./api.js"
|
|
14
14
|
export { type ImportFunction, createImport } from "./resolve-modules/index.js"
|
|
15
15
|
export { loadProject } from "./loadProject.js"
|
|
16
|
+
export { listProjects } from "./listProjects.js"
|
|
16
17
|
export { solidAdapter, type InlangProjectWithSolidAdapter } from "./adapter/solidAdapter.js"
|
|
17
18
|
export { createMessagesQuery } from "./createMessagesQuery.js"
|
|
18
19
|
export {
|
|
@@ -10,7 +10,7 @@ describe("isAbsolutePath", () => {
|
|
|
10
10
|
|
|
11
11
|
it("should correctly identify Windows absolute paths", () => {
|
|
12
12
|
assert.isTrue(isAbsolutePath("C:\\Users\\User\\Documents\\File.txt"))
|
|
13
|
-
assert.isTrue(isAbsolutePath("C:/Users/user/project
|
|
13
|
+
assert.isTrue(isAbsolutePath("C:/Users/user/project.inlang/settings.json"))
|
|
14
14
|
assert.isFalse(isAbsolutePath("Projects\\Project1\\source\\file.txt"))
|
|
15
15
|
})
|
|
16
16
|
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { assert, describe, it } from "vitest"
|
|
2
|
+
import { listProjects } from "./listProjects.js"
|
|
3
|
+
import { createNodeishMemoryFs } from "@lix-js/fs"
|
|
4
|
+
import type { ProjectSettings } from "@inlang/project-settings"
|
|
5
|
+
|
|
6
|
+
const settings: ProjectSettings = {
|
|
7
|
+
sourceLanguageTag: "en",
|
|
8
|
+
languageTags: ["en"],
|
|
9
|
+
modules: ["plugin.js", "lintRule.js"],
|
|
10
|
+
messageLintRuleLevels: {
|
|
11
|
+
"messageLintRule.project.missingTranslation": "error",
|
|
12
|
+
},
|
|
13
|
+
"plugin.project.i18next": {
|
|
14
|
+
pathPattern: "./examples/example01/{languageTag}.json",
|
|
15
|
+
variableReferencePattern: ["{", "}"],
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
describe("listProjects", () => {
|
|
20
|
+
it("should find all projects a given path", async () => {
|
|
21
|
+
const fs = createNodeishMemoryFs()
|
|
22
|
+
await fs.mkdir("/user/dir1/project.inlang", { recursive: true })
|
|
23
|
+
await fs.writeFile("/user/dir1/project.inlang/settings.json", JSON.stringify(settings))
|
|
24
|
+
await fs.mkdir("/user/dir2/project.inlang", { recursive: true })
|
|
25
|
+
await fs.writeFile("/user/dir2/project.inlang/settings.json", JSON.stringify(settings))
|
|
26
|
+
|
|
27
|
+
await listProjects(fs, "/user").then((projects) => {
|
|
28
|
+
assert(projects.length === 2)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it("should return objects inside of an array with the projectPath", async () => {
|
|
33
|
+
const fs = createNodeishMemoryFs()
|
|
34
|
+
await fs.mkdir("/user/dir1/project.inlang", { recursive: true })
|
|
35
|
+
await fs.writeFile("/user/dir1/project.inlang/settings.json", JSON.stringify(settings))
|
|
36
|
+
await fs.mkdir("/user/dir2/project.inlang", { recursive: true })
|
|
37
|
+
await fs.writeFile("/user/dir2/project.inlang/settings.json", JSON.stringify(settings))
|
|
38
|
+
|
|
39
|
+
await listProjects(fs, "/user").then((projects) => {
|
|
40
|
+
assert.isTrue(typeof projects[0] === "object")
|
|
41
|
+
assert.isTrue(typeof projects[0]?.projectPath === "string")
|
|
42
|
+
})
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it("should limit the recursion depth to 5", async () => {
|
|
46
|
+
const fs = createNodeishMemoryFs()
|
|
47
|
+
await fs.mkdir("/user/dir1/dir2/dir3/dir4/dir5/dir6/project.inlang", { recursive: true })
|
|
48
|
+
await fs.writeFile(
|
|
49
|
+
"/user/dir1/dir2/dir3/dir4/dir5/dir6/project.inlang/settings.json",
|
|
50
|
+
JSON.stringify(settings)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
await listProjects(fs, "/user").then((projects) => {
|
|
54
|
+
assert(projects.length === 0)
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it("should also find files inside of a dir that ends with *.inlang", async () => {
|
|
59
|
+
const fs = createNodeishMemoryFs()
|
|
60
|
+
await fs.mkdir("/user/dir1/go.inlang", { recursive: true })
|
|
61
|
+
await fs.writeFile("/user/dir1/go.inlang/settings.json", JSON.stringify(settings))
|
|
62
|
+
await fs.mkdir("/user/dir2/flutter.inlang", { recursive: true })
|
|
63
|
+
await fs.writeFile("/user/dir2/flutter.inlang/settings.json", JSON.stringify(settings))
|
|
64
|
+
|
|
65
|
+
await listProjects(fs, "/user").then((projects) => {
|
|
66
|
+
assert(projects.length === 2)
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
})
|