unplugin-saykit 0.2.0 → 0.4.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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  > Universal build-tool plugin for [SayKit](https://saykit.js.org).
4
4
 
5
+ [![Coverage](https://codecov.io/gh/k0d13/saykit/graph/badge.svg?flag=plugin-unplugin)](https://codecov.io/gh/k0d13/saykit?flags%5B0%5D=plugin-unplugin)
6
+
5
7
  Works with Vite, Rollup, Rolldown, Webpack, Rspack, esbuild, Farm, and Bun via [unplugin](https://github.com/unjs/unplugin).
6
8
 
7
9
  ## Install
package/dist/bun.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as src_default } from "./src-C-_DjRpL.mjs";
1
+ import { t as src_default } from "./src-sSruAedT.mjs";
2
2
  //#region src/bun.ts
3
3
  var bun_default = src_default.bun;
4
4
  //#endregion
package/dist/esbuild.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as src_default } from "./src-C-_DjRpL.mjs";
1
+ import { t as src_default } from "./src-sSruAedT.mjs";
2
2
  //#region src/esbuild.ts
3
3
  var esbuild_default = src_default.esbuild;
4
4
  //#endregion
package/dist/farm.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as src_default } from "./src-C-_DjRpL.mjs";
1
+ import { t as src_default } from "./src-sSruAedT.mjs";
2
2
  //#region src/farm.ts
3
3
  var farm_default = src_default.farm;
4
4
  //#endregion
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { t as src_default } from "./src-C-_DjRpL.mjs";
1
+ import { t as src_default } from "./src-sSruAedT.mjs";
2
2
  export { src_default as default };
@@ -1,7 +1,7 @@
1
- import { join } from "node:path";
2
- import { generateHash } from "@saykit/config/features/messages";
3
- import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
1
+ import { dirname, join } from "node:path";
2
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
3
  import { tmpdir } from "node:os";
4
+ import { generateHash } from "@saykit/config/features/messages";
5
5
  //#region ../../node_modules/.pnpm/tinyrainbow@3.1.0/node_modules/tinyrainbow/dist/index.js
6
6
  var b = {
7
7
  reset: [0, 0],
@@ -13650,20 +13650,47 @@ Object.defineProperty(globalThis, GLOBAL_EXPECT, {
13650
13650
  });
13651
13651
  //#endregion
13652
13652
  //#region src/index.test.ts
13653
- const config = { buckets: [{
13654
- match: (id) => id.endsWith(".ts"),
13655
- output: { match: (id) => id.endsWith("messages.json") },
13656
- transformer: { transform: (code) => code.replace("say", "SAY") },
13657
- formatter: { parse: (content) => JSON.parse(content) }
13658
- }] };
13653
+ const dir = mkdtempSync(join(tmpdir(), "saykit-unplugin-"));
13654
+ const config = {
13655
+ locales: [
13656
+ "en",
13657
+ "en-GB",
13658
+ "en-NZ",
13659
+ "fr"
13660
+ ],
13661
+ fallbackLocales: { "en-NZ": ["en-GB"] },
13662
+ buckets: [{
13663
+ match: (id) => id.endsWith(".ts"),
13664
+ output: Object.assign(join(dir, "{locale}", "messages.{extension}"), { match: (id) => id.endsWith("messages.json") }),
13665
+ transformer: { transform: (code) => code.replace("say", "SAY") },
13666
+ formatter: {
13667
+ extension: ".json",
13668
+ parse: (content) => JSON.parse(content)
13669
+ }
13670
+ }, {
13671
+ match: (id) => id.endsWith(".ts"),
13672
+ output: Object.assign(join(dir, "{locale}", "messages.{extension}"), { match: (id) => id.endsWith("messages.po") }),
13673
+ transformer: { transform: (code) => code },
13674
+ formatter: {
13675
+ extension: ".po",
13676
+ parse: (content) => content ? JSON.parse(content) : []
13677
+ }
13678
+ }]
13679
+ };
13659
13680
  vi.mock("@saykit/config/features/loader", () => ({ resolveConfig: () => config }));
13660
13681
  const { default: unplugin } = await import("./index.mjs");
13661
13682
  const plugin = unplugin.raw(void 0, { framework: "rollup" });
13662
- const dir = mkdtempSync(join(tmpdir(), "saykit-unplugin-"));
13663
13683
  afterAll(() => rmSync(dir, {
13664
13684
  recursive: true,
13665
13685
  force: true
13666
13686
  }));
13687
+ const write = (locale, content, extension = "json") => {
13688
+ const file = join(dir, locale, `messages.${extension}`);
13689
+ mkdirSync(dirname(file), { recursive: true });
13690
+ writeFileSync(file, typeof content === "string" ? content : JSON.stringify(content));
13691
+ return file;
13692
+ };
13693
+ const record = (output) => JSON.parse(output.replace("export default ", ""));
13667
13694
  describe("unplugin transform", () => {
13668
13695
  it("transforms code for a matching bucket", () => {
13669
13696
  globalExpect(plugin.transform.handler("say`Hi`", join(process.cwd(), "src/app.ts"))).toBe("SAY`Hi`");
@@ -13673,21 +13700,88 @@ describe("unplugin transform", () => {
13673
13700
  });
13674
13701
  });
13675
13702
  describe("unplugin load", () => {
13676
- it("loads a catalogue file into a default-exported record", async () => {
13677
- const file = join(dir, "messages.json");
13678
- writeFileSync(file, JSON.stringify([{
13703
+ it("loads the source locale into a record", async () => {
13704
+ const file = write("en", [{
13679
13705
  message: "Hello",
13680
- translation: "Bonjour",
13706
+ translation: "",
13681
13707
  id: "greeting"
13682
13708
  }, {
13683
13709
  message: "Bye",
13684
13710
  translation: ""
13685
- }]));
13711
+ }]);
13712
+ const output = await plugin.load.handler(file);
13713
+ globalExpect(output).not.toContain("export default");
13714
+ const messages = record(output);
13715
+ globalExpect(messages.greeting).toBe("Hello");
13716
+ globalExpect(messages[generateHash("Bye", void 0)]).toBe("Bye");
13717
+ });
13718
+ it("falls back to the source string for keys untranslated in a non-source locale", async () => {
13719
+ write("en", [{
13720
+ message: "Hello",
13721
+ id: "greeting"
13722
+ }, {
13723
+ message: "Bye",
13724
+ id: "farewell"
13725
+ }]);
13726
+ const file = write("fr", [{
13727
+ message: "Hello",
13728
+ translation: "Bonjour",
13729
+ id: "greeting"
13730
+ }]);
13731
+ const messages = record(await plugin.load.handler(file));
13732
+ globalExpect(messages.greeting).toBe("Bonjour");
13733
+ globalExpect(messages.farewell).toBe("Bye");
13734
+ });
13735
+ it("resolves an empty non-source locale entirely to source strings", async () => {
13736
+ write("en", [{
13737
+ message: "Hello",
13738
+ id: "greeting"
13739
+ }]);
13740
+ const file = write("fr", "");
13741
+ globalExpect(record(await plugin.load.handler(file)).greeting).toBe("Hello");
13742
+ });
13743
+ it("resolves a configured fallback chain before the source locale", async () => {
13744
+ write("en", [
13745
+ {
13746
+ message: "A",
13747
+ id: "a"
13748
+ },
13749
+ {
13750
+ message: "B",
13751
+ id: "b"
13752
+ },
13753
+ {
13754
+ message: "C",
13755
+ id: "c"
13756
+ }
13757
+ ]);
13758
+ write("en-GB", [{
13759
+ message: "B",
13760
+ translation: "B-GB",
13761
+ id: "b"
13762
+ }, {
13763
+ message: "C",
13764
+ translation: "C-GB",
13765
+ id: "c"
13766
+ }]);
13767
+ const file = write("en-NZ", [{
13768
+ message: "C",
13769
+ translation: "C-NZ",
13770
+ id: "c"
13771
+ }]);
13772
+ const messages = record(await plugin.load.handler(file));
13773
+ globalExpect(messages.a).toBe("A");
13774
+ globalExpect(messages.b).toBe("B-GB");
13775
+ globalExpect(messages.c).toBe("C-NZ");
13776
+ });
13777
+ it("wraps a non-JSON catalogue in a default export", async () => {
13778
+ const file = write("en", [{
13779
+ message: "Hello",
13780
+ id: "greeting"
13781
+ }], "po");
13686
13782
  const output = await plugin.load.handler(file);
13687
13783
  globalExpect(output).toContain("export default");
13688
- const record = JSON.parse(output.replace("export default ", ""));
13689
- globalExpect(record.greeting).toBe("Bonjour");
13690
- globalExpect(record[generateHash("Bye", void 0)]).toBe("Bye");
13784
+ globalExpect(record(output).greeting).toBe("Hello");
13691
13785
  });
13692
13786
  it("returns undefined when the id does not match a bucket output", async () => {
13693
13787
  globalExpect(await plugin.load.handler(join(dir, "other.txt"))).toBeUndefined();
package/dist/rolldown.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as src_default } from "./src-C-_DjRpL.mjs";
1
+ import { t as src_default } from "./src-sSruAedT.mjs";
2
2
  //#region src/rolldown.ts
3
3
  var rolldown_default = src_default.rolldown;
4
4
  //#endregion
package/dist/rollup.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as src_default } from "./src-C-_DjRpL.mjs";
1
+ import { t as src_default } from "./src-sSruAedT.mjs";
2
2
  //#region src/rollup.ts
3
3
  var rollup_default = src_default.rollup;
4
4
  //#endregion
package/dist/rspack.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as src_default } from "./src-C-_DjRpL.mjs";
1
+ import { t as src_default } from "./src-sSruAedT.mjs";
2
2
  //#region src/rspack.ts
3
3
  var rspack_default = src_default.rspack;
4
4
  //#endregion
@@ -1,7 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { relative } from "node:path";
3
+ import { assembleCatalogueRecord, resolveCatalogueSources } from "@saykit/config/features/catalogue";
3
4
  import { resolveConfig } from "@saykit/config/features/loader";
4
- import { generateHash } from "@saykit/config/features/messages";
5
5
  import { createUnplugin } from "unplugin";
6
6
  //#region src/index.ts
7
7
  var src_default = createUnplugin((_options) => {
@@ -18,13 +18,16 @@ var src_default = createUnplugin((_options) => {
18
18
  },
19
19
  load: {
20
20
  filter: { id: { exclude: /node_modules/ } },
21
- handler: async (id_) => {
21
+ handler: async function(id_) {
22
22
  const id = relative(process.cwd(), id_).replaceAll("\\", "/").split("?")[0];
23
23
  const bucket = config.buckets.find((b) => b.output.match(id));
24
24
  if (!bucket) return;
25
- const content = await readFile(id, "utf8");
26
- const entries = bucket.formatter.parse(content).map((m) => [m.id || generateHash(m.message, m.context), m.translation || m.message]);
27
- return `export default ${JSON.stringify(Object.fromEntries(entries))}`;
25
+ const { sources } = resolveCatalogueSources(config, bucket, id);
26
+ const contents = await Promise.all(sources.map((source) => readFile(source, "utf8").catch(() => "")));
27
+ for (const source of sources) this.addWatchFile?.(source);
28
+ const record = assembleCatalogueRecord(bucket, contents);
29
+ if (id.endsWith(".json")) return JSON.stringify(record);
30
+ return `export default ${JSON.stringify(record)}`;
28
31
  }
29
32
  }
30
33
  };
package/dist/unloader.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as src_default } from "./src-C-_DjRpL.mjs";
1
+ import { t as src_default } from "./src-sSruAedT.mjs";
2
2
  //#region src/unloader.ts
3
3
  var unloader_default = src_default.unloader;
4
4
  //#endregion
package/dist/vite.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as src_default } from "./src-C-_DjRpL.mjs";
1
+ import { t as src_default } from "./src-sSruAedT.mjs";
2
2
  //#region src/vite.ts
3
3
  var vite_default = src_default.vite;
4
4
  //#endregion
package/dist/webpack.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { t as src_default } from "./src-C-_DjRpL.mjs";
1
+ import { t as src_default } from "./src-sSruAedT.mjs";
2
2
  //#region src/webpack.ts
3
3
  var webpack_default = src_default.webpack;
4
4
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unplugin-saykit",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Universal build tool plugin for saykit (Vite, Rollup, Webpack, esbuild)",
5
5
  "keywords": [
6
6
  "i18n",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "unplugin": "^3.3.0",
44
- "@saykit/config": "^0.2.0"
44
+ "@saykit/config": "^0.4.0"
45
45
  },
46
46
  "scripts": {
47
47
  "check": "tsc --noEmit",