Skip to content

Commit

Permalink
Merge pull request #3123 from opral:mesdk-236-tobeimportedfiles-shoul…
Browse files Browse the repository at this point in the history
…d-returns-paths-not-loaded-files

refactor: returns paths to be imported
  • Loading branch information
samuelstroschein authored Sep 12, 2024
2 parents e63b4e8 + e593ad2 commit ee3456f
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 27 deletions.
5 changes: 3 additions & 2 deletions inlang/source-code/sdk2/src/plugin/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ export type InlangPlugin<
}) => Promise<void> | void;
/**
* Import / Export files.
* see https://linear.app/opral/issue/MESDK-157/sdk-v2-release-on-sqlite
*
* @returns The paths of the files that should be imported.
*/
toBeImportedFiles?: (args: {
settings: ProjectSettings & ExternalSettings;
nodeFs: NodeFsPromisesSubset;
}) => MaybePromise<Array<Omit<ResourceFile, "pluginKey">>>;
}) => MaybePromise<string[]>;
importFiles?: (args: {
files: Array<ResourceFile>;
settings: ProjectSettings & ExternalSettings; // we expose the settings in case the importFunction needs to access the plugin config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ProjectSettings } from "../json-schema/settings.js";
import { Volume } from "memfs";
import {
loadProjectFromDirectoryInMemory,
ResourceFileImportError,
WarningDeprecatedLintRule,
WarningLocalPluginImport,
} from "./loadProjectFromDirectory.js";
Expand Down Expand Up @@ -228,6 +229,38 @@ test.skip("it should copy all files in a directory into lix", async () => {
expect(filesByPath["/settings.json"]).toBe(JSON.stringify(mockSettings));
});

test("errors from importing translation files should be shown", async () => {
const mock = {
"/project.inlang/settings.json": JSON.stringify({
baseLocale: "en",
locales: ["en", "de"],
modules: [],
} satisfies ProjectSettings),
};

const fs = Volume.fromJSON(mock).promises;

const mockPlugin: InlangPlugin = {
key: "mock-plugin",
importFiles: async () => {
return { bundles: [] };
},
toBeImportedFiles: async () => {
return ["./non-existing-file.json"];
},
};

const project = await loadProjectFromDirectoryInMemory({
fs: fs as any,
path: "/project.inlang",
providePlugins: [mockPlugin],
});

const errors = await project.errors.get();
expect(errors).toHaveLength(1);
expect(errors[0]).toBeInstanceOf(ResourceFileImportError);
});

test("it should provide plugins from disk for backwards compatibility but warn that those plugins are not portable", async () => {
const mockRepo = {
"/local-plugins/mock-plugin.js": "export default { key: 'mock-plugin' }",
Expand Down
51 changes: 34 additions & 17 deletions inlang/source-code/sdk2/src/project/loadProjectFromDirectory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { fromMessageV1 } from "../json-schema/old-v1-message/fromMessageV1.js";
import type { ProjectSettings } from "../json-schema/settings.js";
import type { PreprocessPluginBeforeImportFunction } from "../plugin/importPlugins.js";
import { PluginImportError } from "../plugin/errors.js";
import type { InlangProject } from "./api.js";
import type { InlangProject, ResourceFile } from "./api.js";

/**
* Loads a project from a directory.
Expand Down Expand Up @@ -95,22 +95,32 @@ export async function loadProjectFromDirectoryInMemory(
);
}

const importedResourceFileErrors: Error[] = [];

for (const importer of importPlugins) {
const files = importer.toBeImportedFiles
? await importer.toBeImportedFiles({
settings: await project.settings.get(),
nodeFs: args.fs,
})
: [];
const files: ResourceFile[] = [];

if (importer.toBeImportedFiles) {
const paths = await importer.toBeImportedFiles({
settings: await project.settings.get(),
nodeFs: args.fs,
});
for (const path of paths) {
const absolute = absolutePathFromProject(args.path, path);
try {
const data = await args.fs.readFile(absolute);
files.push({ path, content: data, pluginKey: importer.key });
} catch (e) {
importedResourceFileErrors.push(
new ResourceFileImportError({ cause: e as Error, path })
);
}
}
}

await project.importFiles({
pluginKey: importer.key,
files: files.map((file) => ({ ...file, pluginKey: importer.key })),
});

// TODO check user id and description (where will this one appear?)
await project.lix.commit({
description: "Executed importFiles",
files,
});
}

Expand All @@ -124,10 +134,6 @@ export async function loadProjectFromDirectoryInMemory(
pluginKey: chosenLegacyPlugin.key ?? chosenLegacyPlugin.id,
loadMessagesFn: chosenLegacyPlugin.loadMessages,
});
// TODO check user id and description (where will this one appear?)
await project.lix.commit({
description: "legacy load and save messages",
});
}

return {
Expand All @@ -138,6 +144,7 @@ export async function loadProjectFromDirectoryInMemory(
return [
...withLocallyImportedPluginWarning(errors),
...localImport.errors,
...importedResourceFileErrors,
];
},
subscribe: (
Expand All @@ -147,6 +154,7 @@ export async function loadProjectFromDirectoryInMemory(
callback([
...withLocallyImportedPluginWarning(value),
...localImport.errors,
...importedResourceFileErrors,
]);
});
},
Expand Down Expand Up @@ -402,4 +410,13 @@ export function absolutePathFromProject(projectPath: string, path: string) {
return resolvedPath;
}

export class ResourceFileImportError extends Error {
path: string;

constructor(args: { cause: Error; path: string }) {
super("Could not import a resource file");
this.name = "ResourceFileImportError";
this.cause = args.cause;
this.path = args.path;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ test("it should overwrite all files to the directory except the db.sqlite file",
});

test("a roundtrip should work", async () => {
const volume = Volume.fromJSON({});

const mockBundleNested: BundleNested = {
id: "mock-bundle",
alias: { "mock-plugin": "peter-gruen" },
Expand All @@ -78,15 +76,14 @@ test("a roundtrip should work", async () => {
],
};

const volume = Volume.fromJSON({
"/mock-file.json": JSON.stringify([mockBundleNested]),
});

const mockPlugin: InlangPlugin = {
key: "mock-plugin",
toBeImportedFiles: async () => {
return [
{
path: "./mock-file.json",
content: new TextEncoder().encode(JSON.stringify([mockBundleNested])),
},
];
return ["/mock-file.json"];
},
importFiles: async ({ files }) => {
const bundles = JSON.parse(new TextDecoder().decode(files[0]?.content));
Expand Down

0 comments on commit ee3456f

Please sign in to comment.