@sebbro/paraglide-lite 0.1.1 → 0.1.2

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.
Files changed (109) hide show
  1. package/dist/bundler-plugins/per-locale-build/index.js +5 -3
  2. package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
  3. package/dist/bundler-plugins/per-locale-build/vite-environments.js +12 -7
  4. package/dist/bundler-plugins/per-locale-build/vite-environments.js.map +1 -1
  5. package/dist/bundler-plugins/plugin-core.d.ts +38 -2
  6. package/dist/bundler-plugins/plugin-core.js +198 -11
  7. package/dist/bundler-plugins/plugin-core.js.map +1 -1
  8. package/dist/bundler-plugins/rolldown.test.js +15 -6
  9. package/dist/bundler-plugins/rolldown.test.js.map +1 -1
  10. package/dist/bundler-plugins/unplugin.js +5 -0
  11. package/dist/bundler-plugins/unplugin.js.map +1 -1
  12. package/dist/bundler-plugins/unplugin.test.js +67 -0
  13. package/dist/bundler-plugins/unplugin.test.js.map +1 -1
  14. package/dist/bundler-plugins/virtual-modules.test.js +64 -1
  15. package/dist/bundler-plugins/virtual-modules.test.js.map +1 -1
  16. package/dist/cli/commands/compile/command.js +8 -1
  17. package/dist/cli/commands/compile/command.js.map +1 -1
  18. package/dist/cli/commands/compile/command.test.js +51 -0
  19. package/dist/cli/commands/compile/command.test.js.map +1 -1
  20. package/dist/compiler/compile-annotation.d.ts +2 -2
  21. package/dist/compiler/compile-annotation.js +9 -9
  22. package/dist/compiler/compile-annotation.js.map +1 -1
  23. package/dist/compiler/compile-local-variable.d.ts +2 -1
  24. package/dist/compiler/compile-local-variable.js +4 -4
  25. package/dist/compiler/compile-local-variable.js.map +1 -1
  26. package/dist/compiler/compile-local-variable.test.js +82 -0
  27. package/dist/compiler/compile-local-variable.test.js.map +1 -1
  28. package/dist/compiler/compile-message.js +10 -2
  29. package/dist/compiler/compile-message.js.map +1 -1
  30. package/dist/compiler/compile-message.test.js +132 -2
  31. package/dist/compiler/compile-message.test.js.map +1 -1
  32. package/dist/compiler/compile-pattern.js +4 -2
  33. package/dist/compiler/compile-pattern.js.map +1 -1
  34. package/dist/compiler/compile-pattern.test.js +31 -0
  35. package/dist/compiler/compile-pattern.test.js.map +1 -1
  36. package/dist/compiler/compile.bench.js +1 -1
  37. package/dist/compiler/compile.bench.js.map +1 -1
  38. package/dist/compiler/compile.test.js +14 -0
  39. package/dist/compiler/compile.test.js.map +1 -1
  40. package/dist/compiler/compiler-options.d.ts +9 -1
  41. package/dist/compiler/compiler-options.js.map +1 -1
  42. package/dist/compiler/runtime/create-runtime.d.ts +1 -0
  43. package/dist/compiler/runtime/create-runtime.js +11 -0
  44. package/dist/compiler/runtime/create-runtime.js.map +1 -1
  45. package/dist/compiler/runtime/exec-url-pattern.d.ts +9 -0
  46. package/dist/compiler/runtime/exec-url-pattern.js +31 -0
  47. package/dist/compiler/runtime/exec-url-pattern.js.map +1 -0
  48. package/dist/compiler/runtime/extract-locale-from-url.js +9 -4
  49. package/dist/compiler/runtime/extract-locale-from-url.js.map +1 -1
  50. package/dist/compiler/runtime/generate-static-localized-urls.js +13 -6
  51. package/dist/compiler/runtime/generate-static-localized-urls.js.map +1 -1
  52. package/dist/compiler/runtime/generate-static-localized-urls.test.js +38 -0
  53. package/dist/compiler/runtime/generate-static-localized-urls.test.js.map +1 -1
  54. package/dist/compiler/runtime/localize-href.bench.d.ts +1 -0
  55. package/dist/compiler/runtime/localize-href.bench.js +68 -0
  56. package/dist/compiler/runtime/localize-href.bench.js.map +1 -0
  57. package/dist/compiler/runtime/localize-href.test.js +35 -0
  58. package/dist/compiler/runtime/localize-href.test.js.map +1 -1
  59. package/dist/compiler/runtime/localize-url.d.ts +14 -0
  60. package/dist/compiler/runtime/localize-url.js +338 -19
  61. package/dist/compiler/runtime/localize-url.js.map +1 -1
  62. package/dist/compiler/runtime/localize-url.test.js +139 -0
  63. package/dist/compiler/runtime/localize-url.test.js.map +1 -1
  64. package/dist/compiler/runtime/normalize-trailing-slash.d.ts +10 -0
  65. package/dist/compiler/runtime/normalize-trailing-slash.js +23 -0
  66. package/dist/compiler/runtime/normalize-trailing-slash.js.map +1 -0
  67. package/dist/compiler/runtime/route-strategy.js +4 -2
  68. package/dist/compiler/runtime/route-strategy.js.map +1 -1
  69. package/dist/compiler/runtime/should-redirect.js +4 -1
  70. package/dist/compiler/runtime/should-redirect.js.map +1 -1
  71. package/dist/compiler/runtime/should-redirect.test.js +26 -2
  72. package/dist/compiler/runtime/should-redirect.test.js.map +1 -1
  73. package/dist/compiler/runtime/type.d.ts +1 -0
  74. package/dist/compiler/runtime/type.js.map +1 -1
  75. package/dist/compiler/runtime/url-pattern.d.ts +11 -0
  76. package/dist/compiler/runtime/url-pattern.js +39 -1
  77. package/dist/compiler/runtime/url-pattern.js.map +1 -1
  78. package/dist/compiler/runtime/variables.d.ts +6 -0
  79. package/dist/compiler/runtime/variables.js +6 -0
  80. package/dist/compiler/runtime/variables.js.map +1 -1
  81. package/dist/compiler/server/create-server-file.js +18 -4
  82. package/dist/compiler/server/create-server-file.js.map +1 -1
  83. package/dist/compiler/server/create-server-file.test.js +76 -0
  84. package/dist/compiler/server/create-server-file.test.js.map +1 -1
  85. package/dist/compiler/server/middleware.js +6 -2
  86. package/dist/compiler/server/middleware.js.map +1 -1
  87. package/dist/compiler/server/middleware.test.js +30 -0
  88. package/dist/compiler/server/middleware.test.js.map +1 -1
  89. package/dist/compiler/variable-access.d.ts +2 -0
  90. package/dist/compiler/variable-access.js +6 -0
  91. package/dist/compiler/variable-access.js.map +1 -1
  92. package/dist/index.test.d.ts +1 -0
  93. package/dist/index.test.js +9 -0
  94. package/dist/index.test.js.map +1 -0
  95. package/dist/messages/load-messages.js +4 -5
  96. package/dist/messages/load-messages.js.map +1 -1
  97. package/dist/services/env-variables/index.js +1 -1
  98. package/dist/services/env-variables/index.js.map +1 -1
  99. package/dist/services/file-handling/write-output.bench.d.ts +1 -0
  100. package/dist/services/file-handling/write-output.bench.js +24 -0
  101. package/dist/services/file-handling/write-output.bench.js.map +1 -0
  102. package/dist/services/file-handling/write-output.d.ts +5 -0
  103. package/dist/services/file-handling/write-output.js +54 -9
  104. package/dist/services/file-handling/write-output.js.map +1 -1
  105. package/dist/services/file-handling/write-output.test.js +27 -0
  106. package/dist/services/file-handling/write-output.test.js.map +1 -1
  107. package/dist/services/logger/index.js +2 -0
  108. package/dist/services/logger/index.js.map +1 -1
  109. package/package.json +86 -86
@@ -1 +1 @@
1
- {"version":3,"file":"load-messages.js","sourceRoot":"","sources":["../../src/messages/load-messages.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAkBhD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,OAA4B;IAE5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAsB;QACnC,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;KAC7B,CAAC;IAEF,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACd,eAAe,QAAQ,CAAC,UAAU,6BAA6B,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5F,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAIN,EAAE,CAAC;IAER,KAAK,MAAM,MAAM,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;QACvC,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAClE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QACrD,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACJ,OAAO,GAAG,MAAM,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CACd,oCAAoC,MAAM,MAAM,YAAY,EAAE,CAC9D,CAAC;YACH,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;QAED,KAAK,CAAC,IAAI,CAAC;YACV,MAAM;YACN,OAAO,EAAE,IAAI,UAAU,CAAC,OAAO,CAAC;YAChC,IAAI,EAAE,YAAY;SAClB,CAAC,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAEtC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,KAAK,CAAC,IAAI,KAAK,QAAQ,CACvB,CAAC;AACH,CAAC","sourcesContent":["import path from \"node:path\";\nimport type { BundleNested, ParaglideSettings } from \"./ast.js\";\nimport { importFiles } from \"./message-format/import-files.js\";\nimport { nestBundles } from \"./nest-bundles.js\";\n\nexport type LoadMessagesOptions = ParaglideSettings & {\n\t/**\n\t * Path pattern with a `{locale}` placeholder.\n\t *\n\t * @example \"./messages/{locale}.json\"\n\t */\n\tmessages: string;\n\tfs: typeof import(\"node:fs\");\n\tcwd?: string;\n};\n\nexport type LoadedMessages = {\n\tbundles: BundleNested[];\n\tsettings: ParaglideSettings;\n};\n\n/**\n * Reads locale JSON files and parses them into compiler-ready bundles.\n */\nexport async function loadMessages(\n\toptions: LoadMessagesOptions\n): Promise<LoadedMessages> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst settings: ParaglideSettings = {\n\t\tbaseLocale: options.baseLocale,\n\t\tlocales: [...options.locales],\n\t};\n\n\tif (settings.locales.length === 0) {\n\t\tthrow new Error(\"At least one locale is required.\");\n\t}\n\n\tif (!settings.locales.includes(settings.baseLocale)) {\n\t\tthrow new Error(\n\t\t\t`baseLocale \"${settings.baseLocale}\" must be one of locales: ${settings.locales.join(\", \")}`\n\t\t);\n\t}\n\n\tconst files: Array<{\n\t\tlocale: string;\n\t\tcontent: Uint8Array;\n\t\tname: string;\n\t}> = [];\n\n\tfor (const locale of settings.locales) {\n\t\tconst relativePath = options.messages.replace(\"{locale}\", locale);\n\t\tconst absolutePath = path.resolve(cwd, relativePath);\n\t\tlet content: Buffer;\n\t\ttry {\n\t\t\tcontent = await options.fs.promises.readFile(absolutePath);\n\t\t} catch (error) {\n\t\t\tif (isEnoent(error)) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Missing message file for locale \"${locale}\": ${relativePath}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\n\t\tfiles.push({\n\t\t\tlocale,\n\t\t\tcontent: new Uint8Array(content),\n\t\t\tname: relativePath,\n\t\t});\n\t}\n\n\tconst imported = importFiles({ files });\n\tconst bundles = nestBundles(imported);\n\n\treturn { bundles, settings };\n}\n\nfunction isEnoent(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\terror.code === \"ENOENT\"\n\t);\n}\n"]}
1
+ {"version":3,"file":"load-messages.js","sourceRoot":"","sources":["../../src/messages/load-messages.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAkBhD;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,OAA4B;IAE5B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,QAAQ,GAAsB;QACnC,UAAU,EAAE,OAAO,CAAC,UAAU;QAC9B,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;KAC7B,CAAC;IAEF,IAAI,QAAQ,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACd,eAAe,QAAQ,CAAC,UAAU,6BAA6B,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC5F,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAClE,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QACrD,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACJ,OAAO,GAAG,MAAM,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC5D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACrB,MAAM,IAAI,KAAK,CACd,oCAAoC,MAAM,MAAM,YAAY,EAAE,CAC9D,CAAC;YACH,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;QAED,OAAO;YACN,MAAM;YACN,OAAO,EAAE,IAAI,UAAU,CAAC,OAAO,CAAC;YAChC,IAAI,EAAE,YAAY;SAClB,CAAC;IACH,CAAC,CAAC,CACF,CAAC;IAEF,MAAM,QAAQ,GAAG,WAAW,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;IAEtC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AAC9B,CAAC;AAED,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,CACN,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,MAAM,IAAI,KAAK;QACf,KAAK,CAAC,IAAI,KAAK,QAAQ,CACvB,CAAC;AACH,CAAC","sourcesContent":["import path from \"node:path\";\nimport type { BundleNested, ParaglideSettings } from \"./ast.js\";\nimport { importFiles } from \"./message-format/import-files.js\";\nimport { nestBundles } from \"./nest-bundles.js\";\n\nexport type LoadMessagesOptions = ParaglideSettings & {\n\t/**\n\t * Path pattern with a `{locale}` placeholder.\n\t *\n\t * @example \"./messages/{locale}.json\"\n\t */\n\tmessages: string;\n\tfs: typeof import(\"node:fs\");\n\tcwd?: string;\n};\n\nexport type LoadedMessages = {\n\tbundles: BundleNested[];\n\tsettings: ParaglideSettings;\n};\n\n/**\n * Reads locale JSON files and parses them into compiler-ready bundles.\n */\nexport async function loadMessages(\n\toptions: LoadMessagesOptions\n): Promise<LoadedMessages> {\n\tconst cwd = options.cwd ?? process.cwd();\n\tconst settings: ParaglideSettings = {\n\t\tbaseLocale: options.baseLocale,\n\t\tlocales: [...options.locales],\n\t};\n\n\tif (settings.locales.length === 0) {\n\t\tthrow new Error(\"At least one locale is required.\");\n\t}\n\n\tif (!settings.locales.includes(settings.baseLocale)) {\n\t\tthrow new Error(\n\t\t\t`baseLocale \"${settings.baseLocale}\" must be one of locales: ${settings.locales.join(\", \")}`\n\t\t);\n\t}\n\n\tconst files = await Promise.all(\n\t\tsettings.locales.map(async (locale) => {\n\t\t\tconst relativePath = options.messages.replace(\"{locale}\", locale);\n\t\t\tconst absolutePath = path.resolve(cwd, relativePath);\n\t\t\tlet content: Buffer;\n\t\t\ttry {\n\t\t\t\tcontent = await options.fs.promises.readFile(absolutePath);\n\t\t\t} catch (error) {\n\t\t\t\tif (isEnoent(error)) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Missing message file for locale \"${locale}\": ${relativePath}`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tlocale,\n\t\t\t\tcontent: new Uint8Array(content),\n\t\t\t\tname: relativePath,\n\t\t\t};\n\t\t})\n\t);\n\n\tconst imported = importFiles({ files });\n\tconst bundles = nestBundles(imported);\n\n\treturn { bundles, settings };\n}\n\nfunction isEnoent(error: unknown): boolean {\n\treturn (\n\t\ttypeof error === \"object\" &&\n\t\terror !== null &&\n\t\t\"code\" in error &&\n\t\terror.code === \"ENOENT\"\n\t);\n}\n"]}
@@ -1,4 +1,4 @@
1
1
  export const ENV_VARIABLES = {
2
- PARJS_PACKAGE_VERSION: "0.1.0",
2
+ PARJS_PACKAGE_VERSION: "0.1.2",
3
3
  };
4
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/env-variables/index.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,aAAa,GAAG;IAC5B,qBAAqB,EAAE,OAAO;CAC9B,CAAA","sourcesContent":["\nexport const ENV_VARIABLES = {\n\tPARJS_PACKAGE_VERSION: \"0.1.0\",\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/env-variables/index.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,aAAa,GAAG;IAC5B,qBAAqB,EAAE,OAAO;CAC9B,CAAA","sourcesContent":["\nexport const ENV_VARIABLES = {\n\tPARJS_PACKAGE_VERSION: \"0.1.2\",\n}\n"]}
@@ -0,0 +1,24 @@
1
+ import { bench } from "vitest";
2
+ import memfs from "memfs";
3
+ import { writeOutput } from "./write-output.js";
4
+ const output = Object.fromEntries(Array.from({ length: 2_000 }, (_, index) => [
5
+ `messages/bundle-${index}.js`,
6
+ `export const m${index} = ${JSON.stringify("Hello world")}`,
7
+ ]));
8
+ bench("write 2,000 generated message modules", async () => {
9
+ const volume = memfs.Volume.fromJSON({});
10
+ const fsp = memfs.createFsFromVolume(volume).promises;
11
+ await writeOutput({ directory: "/output", output, fs: fsp });
12
+ });
13
+ bench("rewrite one of 2,000 generated message modules", async () => {
14
+ const volume = memfs.Volume.fromJSON({});
15
+ const fsp = memfs.createFsFromVolume(volume).promises;
16
+ const hashes = await writeOutput({ directory: "/output", output, fs: fsp });
17
+ await writeOutput({
18
+ directory: "/output",
19
+ output: { ...output, "messages/bundle-0.js": "changed" },
20
+ fs: fsp,
21
+ previousOutputHashes: hashes,
22
+ });
23
+ });
24
+ //# sourceMappingURL=write-output.bench.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-output.bench.js","sourceRoot":"","sources":["../../../src/services/file-handling/write-output.bench.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC/B,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAChC,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;IAC3C,mBAAmB,KAAK,KAAK;IAC7B,iBAAiB,KAAK,MAAM,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,EAAE;CAC3D,CAAC,CACF,CAAC;AAEF,KAAK,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;IACzD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,QAA6B,CAAC;IAC3E,MAAM,WAAW,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AAC9D,CAAC,CAAC,CAAC;AAEH,KAAK,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;IAClE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,QAA6B,CAAC;IAC3E,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC5E,MAAM,WAAW,CAAC;QACjB,SAAS,EAAE,SAAS;QACpB,MAAM,EAAE,EAAE,GAAG,MAAM,EAAE,sBAAsB,EAAE,SAAS,EAAE;QACxD,EAAE,EAAE,GAAG;QACP,oBAAoB,EAAE,MAAM;KAC5B,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { bench } from \"vitest\";\nimport memfs from \"memfs\";\nimport { writeOutput } from \"./write-output.js\";\ntype NodeFs = typeof import(\"node:fs/promises\");\n\nconst output = Object.fromEntries(\n\tArray.from({ length: 2_000 }, (_, index) => [\n\t\t`messages/bundle-${index}.js`,\n\t\t`export const m${index} = ${JSON.stringify(\"Hello world\")}`,\n\t])\n);\n\nbench(\"write 2,000 generated message modules\", async () => {\n\tconst volume = memfs.Volume.fromJSON({});\n\tconst fsp = memfs.createFsFromVolume(volume).promises as unknown as NodeFs;\n\tawait writeOutput({ directory: \"/output\", output, fs: fsp });\n});\n\nbench(\"rewrite one of 2,000 generated message modules\", async () => {\n\tconst volume = memfs.Volume.fromJSON({});\n\tconst fsp = memfs.createFsFromVolume(volume).promises as unknown as NodeFs;\n\tconst hashes = await writeOutput({ directory: \"/output\", output, fs: fsp });\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { ...output, \"messages/bundle-0.js\": \"changed\" },\n\t\tfs: fsp,\n\t\tpreviousOutputHashes: hashes,\n\t});\n});\n"]}
@@ -19,3 +19,8 @@ export declare function writeOutput(args: {
19
19
  * forward-slash relative paths to match {@link hashOutput}.
20
20
  */
21
21
  export declare function hashDirectory(directory: string, fs: typeof nodeFs): Promise<Record<string, string> | undefined>;
22
+ /**
23
+ * Read generated output files from disk so virtual modules can be restored
24
+ * without compiling again.
25
+ */
26
+ export declare function readDirectoryOutput(directory: string, fs: typeof nodeFs): Promise<Record<string, string> | undefined>;
@@ -28,20 +28,20 @@ export async function writeOutput(args) {
28
28
  if (args.cleanDirectory) {
29
29
  await args.fs.rm(args.directory, { recursive: true, force: true });
30
30
  }
31
- else {
32
- await args.fs.mkdir(args.directory, { recursive: true });
33
- }
34
31
  // Delete files that have been removed
35
32
  // ignore if cleanDirectory is true because the directory will be cleaned anyway
36
33
  if (filesToDelete.size > 0 && !args.cleanDirectory) {
37
34
  await deleteRemovedFiles(args.fs, args.directory, filesToDelete);
38
35
  }
39
- //Create missing directories inside the output directory
40
- await Promise.allSettled(Object.keys(args.output).map(async (filePath) => {
41
- const fullPath = path.resolve(args.directory, filePath);
42
- const directory = path.dirname(fullPath);
43
- await args.fs.mkdir(directory, { recursive: true });
44
- }));
36
+ // Create only the directories needed by files that are about to be written.
37
+ // On incremental compiles, `output` can contain thousands of unchanged
38
+ // message modules; creating every parent directory again adds avoidable
39
+ // filesystem work even though only a handful of files changed.
40
+ const directoriesToCreate = new Set();
41
+ for (const filePath of changedFiles) {
42
+ directoriesToCreate.add(path.dirname(path.resolve(args.directory, filePath)));
43
+ }
44
+ await Promise.allSettled(Array.from(directoriesToCreate, (directory) => args.fs.mkdir(directory, { recursive: true })));
45
45
  //Write files
46
46
  await Promise.allSettled(Object.entries(args.output).map(async ([filePath, fileContent]) => {
47
47
  if (!changedFiles.has(filePath)) {
@@ -154,4 +154,49 @@ export async function hashDirectory(directory, fs) {
154
154
  await walk(directory, "");
155
155
  return hashes;
156
156
  }
157
+ /**
158
+ * Read generated output files from disk so virtual modules can be restored
159
+ * without compiling again.
160
+ */
161
+ export async function readDirectoryOutput(directory, fs) {
162
+ try {
163
+ const stat = await fs.stat(directory);
164
+ if (!stat.isDirectory())
165
+ return undefined;
166
+ }
167
+ catch {
168
+ return undefined;
169
+ }
170
+ const output = {};
171
+ async function walk(currentDir, relativePrefix) {
172
+ let entries;
173
+ try {
174
+ entries = await fs.readdir(currentDir, { withFileTypes: true });
175
+ }
176
+ catch {
177
+ return;
178
+ }
179
+ for (const entry of entries) {
180
+ const entryAbsolutePath = path.resolve(currentDir, entry.name);
181
+ const relativePath = relativePrefix
182
+ ? `${relativePrefix}/${entry.name}`
183
+ : entry.name;
184
+ if (entry.isDirectory()) {
185
+ if (entry.name === "cache")
186
+ continue;
187
+ await walk(entryAbsolutePath, relativePath);
188
+ }
189
+ else if (entry.isFile()) {
190
+ try {
191
+ output[relativePath] = await fs.readFile(entryAbsolutePath, "utf-8");
192
+ }
193
+ catch {
194
+ // Partial reads are safe; a later compile will repair.
195
+ }
196
+ }
197
+ }
198
+ }
199
+ await walk(directory, "");
200
+ return output;
201
+ }
157
202
  //# sourceMappingURL=write-output.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"write-output.js","sourceRoot":"","sources":["../../../src/services/file-handling/write-output.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport type nodeFs from \"node:fs/promises\";\nimport { hashOutputContents, hashContent } from \"./content-hash.js\";\n\nexport async function writeOutput(args: {\n\tdirectory: string;\n\toutput: Record<string, string>;\n\tcleanDirectory?: boolean;\n\tfs: typeof nodeFs;\n\tpreviousOutputHashes?: Record<string, string>;\n}) {\n\tconst currentOutputHashes = hashOutput(args.output, args.directory);\n\n\t// if the output hasn't changed, don't write it\n\tconst changedFiles = new Set();\n\n\tfor (const [filePath, hash] of Object.entries(currentOutputHashes)) {\n\t\tif (args.previousOutputHashes?.[filePath] !== hash) {\n\t\t\tchangedFiles.add(filePath);\n\t\t}\n\t}\n\n\t// Find files that have been removed in the current output\n\tconst filesToDelete = new Set<string>();\n\tif (args.previousOutputHashes) {\n\t\tfor (const filePath of Object.keys(args.previousOutputHashes)) {\n\t\t\tif (!currentOutputHashes[filePath]) {\n\t\t\t\tfilesToDelete.add(filePath);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (changedFiles.size === 0 && filesToDelete.size === 0) {\n\t\treturn currentOutputHashes;\n\t}\n\n\t// clear the output directory\n\t//\n\t// disabled because of https://github.com/opral/inlang-paraglide-js/issues/350\n\t// and re-enabled because of https://github.com/opral/inlang-paraglide-js/issues/420\n\tif (args.cleanDirectory) {\n\t\tawait args.fs.rm(args.directory, { recursive: true, force: true });\n\t} else {\n\t\tawait args.fs.mkdir(args.directory, { recursive: true });\n\t}\n\t// Delete files that have been removed\n\t// ignore if cleanDirectory is true because the directory will be cleaned anyway\n\tif (filesToDelete.size > 0 && !args.cleanDirectory) {\n\t\tawait deleteRemovedFiles(args.fs, args.directory, filesToDelete);\n\t}\n\n\t//Create missing directories inside the output directory\n\tawait Promise.allSettled(\n\t\tObject.keys(args.output).map(async (filePath) => {\n\t\t\tconst fullPath = path.resolve(args.directory, filePath);\n\t\t\tconst directory = path.dirname(fullPath);\n\t\t\tawait args.fs.mkdir(directory, { recursive: true });\n\t\t})\n\t);\n\n\t//Write files\n\tawait Promise.allSettled(\n\t\tObject.entries(args.output).map(async ([filePath, fileContent]) => {\n\t\t\tif (!changedFiles.has(filePath)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst fullPath = path.resolve(args.directory, filePath);\n\t\t\tawait args.fs.writeFile(fullPath, fileContent);\n\t\t})\n\t);\n\n\t//Only update the previousOutputHashes if the write was successful\n\treturn currentOutputHashes;\n}\n\n/**\n * Delete files that have been removed and clean up empty directories\n */\nasync function deleteRemovedFiles(\n\tfs: typeof nodeFs,\n\tbaseDirectory: string,\n\tfilesToDelete: Set<string>\n) {\n\t// Collect directories that might need cleanup\n\tconst potentialEmptyDirs = new Set<string>();\n\n\t// First pass: delete all files and collect parent directories\n\tawait Promise.allSettled(\n\t\tArray.from(filesToDelete).map(async (filePath) => {\n\t\t\tconst fullPath = path.resolve(baseDirectory, filePath);\n\t\t\ttry {\n\t\t\t\tawait fs.unlink(fullPath);\n\n\t\t\t\t// Add parent directory for potential cleanup\n\t\t\t\tconst dirPath = path.dirname(fullPath);\n\t\t\t\tif (dirPath !== baseDirectory) {\n\t\t\t\t\tpotentialEmptyDirs.add(dirPath);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore errors if the file doesn't exist\n\t\t\t}\n\t\t})\n\t);\n\n\t// Second pass: clean up empty directories, starting from deepest paths\n\tconst sortedDirs = Array.from(potentialEmptyDirs).sort(\n\t\t(a, b) => b.length - a.length\n\t);\n\n\tfor (const dirPath of sortedDirs) {\n\t\t// Only process directories within the base directory\n\t\tif (!dirPath.startsWith(baseDirectory) || dirPath === baseDirectory) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst dirContents = await fs.readdir(dirPath);\n\t\t\tif (dirContents.length === 0) {\n\t\t\t\tawait fs.rmdir(dirPath);\n\n\t\t\t\t// Add parent directory for potential cleanup\n\t\t\t\tconst parentDir = path.dirname(dirPath);\n\t\t\t\tif (parentDir !== baseDirectory) {\n\t\t\t\t\tsortedDirs.push(parentDir);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore errors during directory cleanup\n\t\t}\n\t}\n}\n\nfunction hashOutput(\n\toutput: Record<string, string>,\n\toutputDirectory: string\n): Record<string, string> {\n\treturn hashOutputContents(\n\t\toutput,\n\t\t(filePath, fileContent) =>\n\t\t\tfileContent + path.resolve(outputDirectory, filePath)\n\t);\n}\n\n/**\n * Walk an existing output directory and produce the same hash map that\n * {@link writeOutput} would have returned on a previous compile.\n *\n * Used by the bundler plugins to seed `previousCompilation` on a fresh\n * process so the first compile's diff against unchanged inputs is empty\n * and `writeOutput` short-circuits — letting us avoid wiping the directory\n * out from under concurrent SSR/prerender readers (#659).\n *\n * Returns `undefined` if the directory does not exist. Keys are\n * forward-slash relative paths to match {@link hashOutput}.\n */\nexport async function hashDirectory(\n\tdirectory: string,\n\tfs: typeof nodeFs\n): Promise<Record<string, string> | undefined> {\n\ttry {\n\t\tconst stat = await fs.stat(directory);\n\t\tif (!stat.isDirectory()) return undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tconst hashes: Record<string, string> = {};\n\n\tasync function walk(currentDir: string, relativePrefix: string) {\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = await fs.readdir(currentDir, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tconst entryAbsolutePath = path.resolve(currentDir, entry.name);\n\t\t\tconst relativePath = relativePrefix\n\t\t\t\t? `${relativePrefix}/${entry.name}`\n\t\t\t\t: entry.name;\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tawait walk(entryAbsolutePath, relativePath);\n\t\t\t} else if (entry.isFile()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst fileContent = await fs.readFile(entryAbsolutePath, \"utf-8\");\n\t\t\t\t\tconst combinedContent =\n\t\t\t\t\t\tfileContent + path.resolve(directory, relativePath);\n\t\t\t\t\thashes[relativePath] = hashContent(combinedContent);\n\t\t\t\t} catch {\n\t\t\t\t\t// Another compiler process may be rewriting this file. A partial\n\t\t\t\t\t// seed is safe; the following compile will repair missing outputs.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tawait walk(directory, \"\");\n\treturn hashes;\n}\n"]}
1
+ {"version":3,"file":"write-output.js","sourceRoot":"","sources":["../../../src/services/file-handling/write-output.ts"],"names":[],"mappings":"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","sourcesContent":["import path from \"node:path\";\nimport type nodeFs from \"node:fs/promises\";\nimport { hashOutputContents, hashContent } from \"./content-hash.js\";\n\nexport async function writeOutput(args: {\n\tdirectory: string;\n\toutput: Record<string, string>;\n\tcleanDirectory?: boolean;\n\tfs: typeof nodeFs;\n\tpreviousOutputHashes?: Record<string, string>;\n}) {\n\tconst currentOutputHashes = hashOutput(args.output, args.directory);\n\n\t// if the output hasn't changed, don't write it\n\tconst changedFiles = new Set<string>();\n\n\tfor (const [filePath, hash] of Object.entries(currentOutputHashes)) {\n\t\tif (args.previousOutputHashes?.[filePath] !== hash) {\n\t\t\tchangedFiles.add(filePath);\n\t\t}\n\t}\n\n\t// Find files that have been removed in the current output\n\tconst filesToDelete = new Set<string>();\n\tif (args.previousOutputHashes) {\n\t\tfor (const filePath of Object.keys(args.previousOutputHashes)) {\n\t\t\tif (!currentOutputHashes[filePath]) {\n\t\t\t\tfilesToDelete.add(filePath);\n\t\t\t}\n\t\t}\n\t}\n\n\tif (changedFiles.size === 0 && filesToDelete.size === 0) {\n\t\treturn currentOutputHashes;\n\t}\n\n\t// clear the output directory\n\t//\n\t// disabled because of https://github.com/opral/inlang-paraglide-js/issues/350\n\t// and re-enabled because of https://github.com/opral/inlang-paraglide-js/issues/420\n\tif (args.cleanDirectory) {\n\t\tawait args.fs.rm(args.directory, { recursive: true, force: true });\n\t}\n\t// Delete files that have been removed\n\t// ignore if cleanDirectory is true because the directory will be cleaned anyway\n\tif (filesToDelete.size > 0 && !args.cleanDirectory) {\n\t\tawait deleteRemovedFiles(args.fs, args.directory, filesToDelete);\n\t}\n\n\t// Create only the directories needed by files that are about to be written.\n\t// On incremental compiles, `output` can contain thousands of unchanged\n\t// message modules; creating every parent directory again adds avoidable\n\t// filesystem work even though only a handful of files changed.\n\tconst directoriesToCreate = new Set<string>();\n\tfor (const filePath of changedFiles) {\n\t\tdirectoriesToCreate.add(\n\t\t\tpath.dirname(path.resolve(args.directory, filePath))\n\t\t);\n\t}\n\tawait Promise.allSettled(\n\t\tArray.from(directoriesToCreate, (directory) =>\n\t\t\targs.fs.mkdir(directory, { recursive: true })\n\t\t)\n\t);\n\n\t//Write files\n\tawait Promise.allSettled(\n\t\tObject.entries(args.output).map(async ([filePath, fileContent]) => {\n\t\t\tif (!changedFiles.has(filePath)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst fullPath = path.resolve(args.directory, filePath);\n\t\t\tawait args.fs.writeFile(fullPath, fileContent);\n\t\t})\n\t);\n\n\t//Only update the previousOutputHashes if the write was successful\n\treturn currentOutputHashes;\n}\n\n/**\n * Delete files that have been removed and clean up empty directories\n */\nasync function deleteRemovedFiles(\n\tfs: typeof nodeFs,\n\tbaseDirectory: string,\n\tfilesToDelete: Set<string>\n) {\n\t// Collect directories that might need cleanup\n\tconst potentialEmptyDirs = new Set<string>();\n\n\t// First pass: delete all files and collect parent directories\n\tawait Promise.allSettled(\n\t\tArray.from(filesToDelete).map(async (filePath) => {\n\t\t\tconst fullPath = path.resolve(baseDirectory, filePath);\n\t\t\ttry {\n\t\t\t\tawait fs.unlink(fullPath);\n\n\t\t\t\t// Add parent directory for potential cleanup\n\t\t\t\tconst dirPath = path.dirname(fullPath);\n\t\t\t\tif (dirPath !== baseDirectory) {\n\t\t\t\t\tpotentialEmptyDirs.add(dirPath);\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore errors if the file doesn't exist\n\t\t\t}\n\t\t})\n\t);\n\n\t// Second pass: clean up empty directories, starting from deepest paths\n\tconst sortedDirs = Array.from(potentialEmptyDirs).sort(\n\t\t(a, b) => b.length - a.length\n\t);\n\n\tfor (const dirPath of sortedDirs) {\n\t\t// Only process directories within the base directory\n\t\tif (!dirPath.startsWith(baseDirectory) || dirPath === baseDirectory) {\n\t\t\tcontinue;\n\t\t}\n\n\t\ttry {\n\t\t\tconst dirContents = await fs.readdir(dirPath);\n\t\t\tif (dirContents.length === 0) {\n\t\t\t\tawait fs.rmdir(dirPath);\n\n\t\t\t\t// Add parent directory for potential cleanup\n\t\t\t\tconst parentDir = path.dirname(dirPath);\n\t\t\t\tif (parentDir !== baseDirectory) {\n\t\t\t\t\tsortedDirs.push(parentDir);\n\t\t\t\t}\n\t\t\t}\n\t\t} catch {\n\t\t\t// Ignore errors during directory cleanup\n\t\t}\n\t}\n}\n\nfunction hashOutput(\n\toutput: Record<string, string>,\n\toutputDirectory: string\n): Record<string, string> {\n\treturn hashOutputContents(\n\t\toutput,\n\t\t(filePath, fileContent) =>\n\t\t\tfileContent + path.resolve(outputDirectory, filePath)\n\t);\n}\n\n/**\n * Walk an existing output directory and produce the same hash map that\n * {@link writeOutput} would have returned on a previous compile.\n *\n * Used by the bundler plugins to seed `previousCompilation` on a fresh\n * process so the first compile's diff against unchanged inputs is empty\n * and `writeOutput` short-circuits — letting us avoid wiping the directory\n * out from under concurrent SSR/prerender readers (#659).\n *\n * Returns `undefined` if the directory does not exist. Keys are\n * forward-slash relative paths to match {@link hashOutput}.\n */\nexport async function hashDirectory(\n\tdirectory: string,\n\tfs: typeof nodeFs\n): Promise<Record<string, string> | undefined> {\n\ttry {\n\t\tconst stat = await fs.stat(directory);\n\t\tif (!stat.isDirectory()) return undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tconst hashes: Record<string, string> = {};\n\n\tasync function walk(currentDir: string, relativePrefix: string) {\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = await fs.readdir(currentDir, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tconst entryAbsolutePath = path.resolve(currentDir, entry.name);\n\t\t\tconst relativePath = relativePrefix\n\t\t\t\t? `${relativePrefix}/${entry.name}`\n\t\t\t\t: entry.name;\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tawait walk(entryAbsolutePath, relativePath);\n\t\t\t} else if (entry.isFile()) {\n\t\t\t\ttry {\n\t\t\t\t\tconst fileContent = await fs.readFile(entryAbsolutePath, \"utf-8\");\n\t\t\t\t\tconst combinedContent =\n\t\t\t\t\t\tfileContent + path.resolve(directory, relativePath);\n\t\t\t\t\thashes[relativePath] = hashContent(combinedContent);\n\t\t\t\t} catch {\n\t\t\t\t\t// Another compiler process may be rewriting this file. A partial\n\t\t\t\t\t// seed is safe; the following compile will repair missing outputs.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tawait walk(directory, \"\");\n\treturn hashes;\n}\n\n/**\n * Read generated output files from disk so virtual modules can be restored\n * without compiling again.\n */\nexport async function readDirectoryOutput(\n\tdirectory: string,\n\tfs: typeof nodeFs\n): Promise<Record<string, string> | undefined> {\n\ttry {\n\t\tconst stat = await fs.stat(directory);\n\t\tif (!stat.isDirectory()) return undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n\n\tconst output: Record<string, string> = {};\n\n\tasync function walk(currentDir: string, relativePrefix: string) {\n\t\tlet entries;\n\t\ttry {\n\t\t\tentries = await fs.readdir(currentDir, { withFileTypes: true });\n\t\t} catch {\n\t\t\treturn;\n\t\t}\n\t\tfor (const entry of entries) {\n\t\t\tconst entryAbsolutePath = path.resolve(currentDir, entry.name);\n\t\t\tconst relativePath = relativePrefix\n\t\t\t\t? `${relativePrefix}/${entry.name}`\n\t\t\t\t: entry.name;\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tif (entry.name === \"cache\") continue;\n\t\t\t\tawait walk(entryAbsolutePath, relativePath);\n\t\t\t} else if (entry.isFile()) {\n\t\t\t\ttry {\n\t\t\t\t\toutput[relativePath] = await fs.readFile(entryAbsolutePath, \"utf-8\");\n\t\t\t\t} catch {\n\t\t\t\t\t// Partial reads are safe; a later compile will repair.\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tawait walk(directory, \"\");\n\treturn output;\n}\n"]}
@@ -79,6 +79,33 @@ test("should write again if the output has changed", async () => {
79
79
  expect(await fs.readFile("/output/test.txt", { encoding: "utf-8" })).toBe("test2");
80
80
  expect(writeFileSpy).toHaveBeenCalledTimes(2);
81
81
  });
82
+ test("should only create directories for files that changed", async () => {
83
+ const { writeOutput } = await import("./write-output.js");
84
+ const fs = mockFs({});
85
+ const mkdirSpy = vi.spyOn(fs, "mkdir");
86
+ const hashes = await writeOutput({
87
+ directory: "/output",
88
+ output: {
89
+ "messages/first.js": "first",
90
+ "messages/second.js": "second",
91
+ },
92
+ fs,
93
+ });
94
+ mkdirSpy.mockClear();
95
+ await writeOutput({
96
+ directory: "/output",
97
+ output: {
98
+ "messages/first.js": "changed",
99
+ "messages/second.js": "second",
100
+ },
101
+ fs,
102
+ previousOutputHashes: hashes,
103
+ });
104
+ expect(mkdirSpy).toHaveBeenCalledTimes(1);
105
+ expect(mkdirSpy).toHaveBeenCalledWith("/output/messages", {
106
+ recursive: true,
107
+ });
108
+ });
82
109
  test("should write files if output has partially changed", async () => {
83
110
  const { writeOutput } = await import("./write-output.js");
84
111
  const fs = mockFs({});
@@ -1 +1 @@
1
- {"version":3,"file":"write-output.test.js","sourceRoot":"","sources":["../../../src/services/file-handling/write-output.test.ts"],"names":[],"mappings":"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","sourcesContent":["import memfs from \"memfs\";\nimport type fs from \"node:fs/promises\";\nimport { test, expect, vi, beforeEach } from \"vitest\";\n\nbeforeEach(() => {\n\tvi.resetModules();\n});\n\ntest(\"should write the output to a non-existing directory\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"test\" },\n\t\tfs,\n\t});\n\texpect(await fs.readFile(\"/output/test.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"test\"\n\t);\n});\n\ntest(\"should clear & overwrite output that's already there\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({\n\t\t\"/output/test.txt\": \"old\",\n\t\t\"/output/other.txt\": \"other\",\n\t});\n\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"new\" },\n\t\tcleanDirectory: true,\n\t\tfs,\n\t});\n\n\texpect(await fs.readFile(\"/output/test.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"new\"\n\t);\n\tawait expect(\n\t\tasync () => await fs.readFile(\"/output/other.txt\", { encoding: \"utf-8\" })\n\t).rejects.toBeDefined();\n});\n\ntest(\"should create any missing directories\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tawait writeOutput({\n\t\tdirectory: \"/output/messages\",\n\t\toutput: {\n\t\t\t\"de/test.txt\": \"de\",\n\t\t\t\"en/test.txt\": \"en\",\n\t\t},\n\t\tfs,\n\t});\n\texpect(\n\t\tawait fs.readFile(\"/output/messages/de/test.txt\", { encoding: \"utf-8\" })\n\t).toBe(\"de\");\n\texpect(\n\t\tawait fs.readFile(\"/output/messages/en/test.txt\", { encoding: \"utf-8\" })\n\t).toBe(\"en\");\n});\n\ntest(\"should only write once if the output hasn't changed\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tconst writeFileSpy = vi.spyOn(fs as any, \"writeFile\");\n\n\tconst hashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"test\" },\n\t\tfs,\n\t});\n\tconst hashes2 = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"test\" },\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\texpect(hashes).toEqual(hashes2);\n\texpect(await fs.readFile(\"/output/test.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"test\"\n\t);\n\texpect(writeFileSpy).toHaveBeenCalledTimes(1);\n});\n\ntest(\"should write again if the output has changed\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tconst writeFileSpy = vi.spyOn(fs as any, \"writeFile\");\n\n\tconst hashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"test\" },\n\t\tfs,\n\t});\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"test2\" },\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\texpect(await fs.readFile(\"/output/test.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"test2\"\n\t);\n\texpect(writeFileSpy).toHaveBeenCalledTimes(2);\n});\n\ntest(\"should write files if output has partially changed\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tconst writeFileSpy = vi.spyOn(fs as any, \"writeFile\");\n\n\tconst hashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"file1.txt\": \"test\", \"file2.txt\": \"test\" },\n\t\tfs,\n\t});\n\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"file1.txt\": \"test\", \"file2.txt\": \"test2\" },\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\texpect(writeFileSpy).toHaveBeenCalledWith(\"/output/file2.txt\", \"test2\");\n\texpect(writeFileSpy).toHaveBeenCalledTimes(3);\n});\n\ntest(\"should delete files that have been removed from the output\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\t// First write with three files\n\tconst hashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t\"file1.txt\": \"content1\",\n\t\t\t\"file2.txt\": \"content2\",\n\t\t\t\"subdir/file3.txt\": \"content3\",\n\t\t},\n\t\tfs,\n\t});\n\n\t// Verify all files were written\n\texpect(await fs.readFile(\"/output/file1.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"content1\"\n\t);\n\texpect(await fs.readFile(\"/output/file2.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"content2\"\n\t);\n\texpect(\n\t\tawait fs.readFile(\"/output/subdir/file3.txt\", { encoding: \"utf-8\" })\n\t).toBe(\"content3\");\n\n\t// Second write with file2.txt removed\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t\"file1.txt\": \"content1\",\n\t\t\t\"subdir/file3.txt\": \"content3updated\",\n\t\t},\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\n\t// Verify file1.txt still exists\n\texpect(await fs.readFile(\"/output/file1.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"content1\"\n\t);\n\t// Verify file2.txt has been deleted\n\tawait expect(\n\t\tasync () => await fs.readFile(\"/output/file2.txt\", { encoding: \"utf-8\" })\n\t).rejects.toBeDefined();\n\t// Verify file3.txt was updated\n\texpect(\n\t\tawait fs.readFile(\"/output/subdir/file3.txt\", { encoding: \"utf-8\" })\n\t).toBe(\"content3updated\");\n\n\t// Get the new hashes\n\tconst newHashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t\"file1.txt\": \"content1\",\n\t\t\t\"subdir/file3.txt\": \"content3updated\",\n\t\t},\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\n\t// Third write with subdir/file3.txt removed\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t\"file1.txt\": \"content1\",\n\t\t},\n\t\tfs,\n\t\tpreviousOutputHashes: newHashes,\n\t});\n\n\t// Verify file1.txt still exists\n\texpect(await fs.readFile(\"/output/file1.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"content1\"\n\t);\n\t// Verify subdir/file3.txt has been deleted\n\tawait expect(\n\t\tasync () =>\n\t\t\tawait fs.readFile(\"/output/subdir/file3.txt\", { encoding: \"utf-8\" })\n\t).rejects.toBeDefined();\n\t// Verify the subdir directory has also been removed\n\tawait expect(\n\t\tasync () => await fs.readdir(\"/output/subdir\")\n\t).rejects.toBeDefined();\n});\n\ntest(\"hashDirectory matches the hashes writeOutput returns\", async () => {\n\tconst { writeOutput, hashDirectory } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tconst output = {\n\t\t\"a.txt\": \"alpha\",\n\t\t\"nested/b.txt\": \"beta\",\n\t\t\"nested/deep/c.txt\": \"gamma\",\n\t};\n\n\tconst writtenHashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput,\n\t\tfs,\n\t});\n\n\tconst seededHashes = await hashDirectory(\"/output\", fs);\n\n\texpect(seededHashes).toEqual(writtenHashes);\n});\n\ntest(\"hashDirectory returns undefined when directory does not exist\", async () => {\n\tconst { hashDirectory } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\texpect(await hashDirectory(\"/nope\", fs)).toBeUndefined();\n});\n\ntest(\"hashDirectory skips files that disappear while walking\", async () => {\n\tconst { hashDirectory } = await import(\"./write-output.js\");\n\tconst fs = mockFs({\n\t\t\"/output/a.txt\": \"alpha\",\n\t\t\"/output/b.txt\": \"beta\",\n\t});\n\tconst readFile = fs.readFile.bind(fs);\n\tvi.spyOn(fs, \"readFile\").mockImplementation((async (...args) => {\n\t\tif (args[0] === \"/output/b.txt\") {\n\t\t\tthrow new Error(\"file disappeared\");\n\t\t}\n\t\treturn readFile(args[0], args[1]);\n\t}) as typeof fs.readFile);\n\n\tconst seededHashes = await hashDirectory(\"/output\", fs);\n\n\texpect(seededHashes).toEqual({\n\t\t\"a.txt\": expect.any(String),\n\t});\n});\n\ntest(\"hashDirectory skips directories that disappear while walking\", async () => {\n\tconst { hashDirectory } = await import(\"./write-output.js\");\n\tconst fs = mockFs({\n\t\t\"/output/nested/b.txt\": \"beta\",\n\t});\n\tconst readdir = fs.readdir.bind(fs);\n\tvi.spyOn(fs, \"readdir\").mockImplementation((async (...args) => {\n\t\tif (args[0] === \"/output/nested\") {\n\t\t\tthrow new Error(\"directory disappeared\");\n\t\t}\n\t\treturn readdir(args[0], args[1] as never);\n\t}) as typeof fs.readdir);\n\n\tconst seededHashes = await hashDirectory(\"/output\", fs);\n\n\texpect(seededHashes).toEqual({});\n});\n\nconst mockFs = (files: memfs.DirectoryJSON) => {\n\tconst _memfs = memfs.createFsFromVolume(memfs.Volume.fromJSON(files));\n\treturn _memfs.promises as unknown as typeof fs;\n};\n"]}
1
+ {"version":3,"file":"write-output.test.js","sourceRoot":"","sources":["../../../src/services/file-handling/write-output.test.ts"],"names":[],"mappings":"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","sourcesContent":["import memfs from \"memfs\";\nimport type fs from \"node:fs/promises\";\nimport { test, expect, vi, beforeEach } from \"vitest\";\n\nbeforeEach(() => {\n\tvi.resetModules();\n});\n\ntest(\"should write the output to a non-existing directory\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"test\" },\n\t\tfs,\n\t});\n\texpect(await fs.readFile(\"/output/test.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"test\"\n\t);\n});\n\ntest(\"should clear & overwrite output that's already there\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({\n\t\t\"/output/test.txt\": \"old\",\n\t\t\"/output/other.txt\": \"other\",\n\t});\n\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"new\" },\n\t\tcleanDirectory: true,\n\t\tfs,\n\t});\n\n\texpect(await fs.readFile(\"/output/test.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"new\"\n\t);\n\tawait expect(\n\t\tasync () => await fs.readFile(\"/output/other.txt\", { encoding: \"utf-8\" })\n\t).rejects.toBeDefined();\n});\n\ntest(\"should create any missing directories\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tawait writeOutput({\n\t\tdirectory: \"/output/messages\",\n\t\toutput: {\n\t\t\t\"de/test.txt\": \"de\",\n\t\t\t\"en/test.txt\": \"en\",\n\t\t},\n\t\tfs,\n\t});\n\texpect(\n\t\tawait fs.readFile(\"/output/messages/de/test.txt\", { encoding: \"utf-8\" })\n\t).toBe(\"de\");\n\texpect(\n\t\tawait fs.readFile(\"/output/messages/en/test.txt\", { encoding: \"utf-8\" })\n\t).toBe(\"en\");\n});\n\ntest(\"should only write once if the output hasn't changed\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tconst writeFileSpy = vi.spyOn(fs as any, \"writeFile\");\n\n\tconst hashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"test\" },\n\t\tfs,\n\t});\n\tconst hashes2 = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"test\" },\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\texpect(hashes).toEqual(hashes2);\n\texpect(await fs.readFile(\"/output/test.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"test\"\n\t);\n\texpect(writeFileSpy).toHaveBeenCalledTimes(1);\n});\n\ntest(\"should write again if the output has changed\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tconst writeFileSpy = vi.spyOn(fs as any, \"writeFile\");\n\n\tconst hashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"test\" },\n\t\tfs,\n\t});\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"test.txt\": \"test2\" },\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\texpect(await fs.readFile(\"/output/test.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"test2\"\n\t);\n\texpect(writeFileSpy).toHaveBeenCalledTimes(2);\n});\n\ntest(\"should only create directories for files that changed\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\tconst mkdirSpy = vi.spyOn(fs, \"mkdir\");\n\n\tconst hashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t\"messages/first.js\": \"first\",\n\t\t\t\"messages/second.js\": \"second\",\n\t\t},\n\t\tfs,\n\t});\n\tmkdirSpy.mockClear();\n\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t\"messages/first.js\": \"changed\",\n\t\t\t\"messages/second.js\": \"second\",\n\t\t},\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\n\texpect(mkdirSpy).toHaveBeenCalledTimes(1);\n\texpect(mkdirSpy).toHaveBeenCalledWith(\"/output/messages\", {\n\t\trecursive: true,\n\t});\n});\n\ntest(\"should write files if output has partially changed\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tconst writeFileSpy = vi.spyOn(fs as any, \"writeFile\");\n\n\tconst hashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"file1.txt\": \"test\", \"file2.txt\": \"test\" },\n\t\tfs,\n\t});\n\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: { \"file1.txt\": \"test\", \"file2.txt\": \"test2\" },\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\texpect(writeFileSpy).toHaveBeenCalledWith(\"/output/file2.txt\", \"test2\");\n\texpect(writeFileSpy).toHaveBeenCalledTimes(3);\n});\n\ntest(\"should delete files that have been removed from the output\", async () => {\n\tconst { writeOutput } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\t// First write with three files\n\tconst hashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t\"file1.txt\": \"content1\",\n\t\t\t\"file2.txt\": \"content2\",\n\t\t\t\"subdir/file3.txt\": \"content3\",\n\t\t},\n\t\tfs,\n\t});\n\n\t// Verify all files were written\n\texpect(await fs.readFile(\"/output/file1.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"content1\"\n\t);\n\texpect(await fs.readFile(\"/output/file2.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"content2\"\n\t);\n\texpect(\n\t\tawait fs.readFile(\"/output/subdir/file3.txt\", { encoding: \"utf-8\" })\n\t).toBe(\"content3\");\n\n\t// Second write with file2.txt removed\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t\"file1.txt\": \"content1\",\n\t\t\t\"subdir/file3.txt\": \"content3updated\",\n\t\t},\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\n\t// Verify file1.txt still exists\n\texpect(await fs.readFile(\"/output/file1.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"content1\"\n\t);\n\t// Verify file2.txt has been deleted\n\tawait expect(\n\t\tasync () => await fs.readFile(\"/output/file2.txt\", { encoding: \"utf-8\" })\n\t).rejects.toBeDefined();\n\t// Verify file3.txt was updated\n\texpect(\n\t\tawait fs.readFile(\"/output/subdir/file3.txt\", { encoding: \"utf-8\" })\n\t).toBe(\"content3updated\");\n\n\t// Get the new hashes\n\tconst newHashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t\"file1.txt\": \"content1\",\n\t\t\t\"subdir/file3.txt\": \"content3updated\",\n\t\t},\n\t\tfs,\n\t\tpreviousOutputHashes: hashes,\n\t});\n\n\t// Third write with subdir/file3.txt removed\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t\"file1.txt\": \"content1\",\n\t\t},\n\t\tfs,\n\t\tpreviousOutputHashes: newHashes,\n\t});\n\n\t// Verify file1.txt still exists\n\texpect(await fs.readFile(\"/output/file1.txt\", { encoding: \"utf-8\" })).toBe(\n\t\t\"content1\"\n\t);\n\t// Verify subdir/file3.txt has been deleted\n\tawait expect(\n\t\tasync () =>\n\t\t\tawait fs.readFile(\"/output/subdir/file3.txt\", { encoding: \"utf-8\" })\n\t).rejects.toBeDefined();\n\t// Verify the subdir directory has also been removed\n\tawait expect(\n\t\tasync () => await fs.readdir(\"/output/subdir\")\n\t).rejects.toBeDefined();\n});\n\ntest(\"hashDirectory matches the hashes writeOutput returns\", async () => {\n\tconst { writeOutput, hashDirectory } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\tconst output = {\n\t\t\"a.txt\": \"alpha\",\n\t\t\"nested/b.txt\": \"beta\",\n\t\t\"nested/deep/c.txt\": \"gamma\",\n\t};\n\n\tconst writtenHashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput,\n\t\tfs,\n\t});\n\n\tconst seededHashes = await hashDirectory(\"/output\", fs);\n\n\texpect(seededHashes).toEqual(writtenHashes);\n});\n\ntest(\"hashDirectory returns undefined when directory does not exist\", async () => {\n\tconst { hashDirectory } = await import(\"./write-output.js\");\n\tconst fs = mockFs({});\n\n\texpect(await hashDirectory(\"/nope\", fs)).toBeUndefined();\n});\n\ntest(\"hashDirectory skips files that disappear while walking\", async () => {\n\tconst { hashDirectory } = await import(\"./write-output.js\");\n\tconst fs = mockFs({\n\t\t\"/output/a.txt\": \"alpha\",\n\t\t\"/output/b.txt\": \"beta\",\n\t});\n\tconst readFile = fs.readFile.bind(fs);\n\tvi.spyOn(fs, \"readFile\").mockImplementation((async (...args) => {\n\t\tif (args[0] === \"/output/b.txt\") {\n\t\t\tthrow new Error(\"file disappeared\");\n\t\t}\n\t\treturn readFile(args[0], args[1]);\n\t}) as typeof fs.readFile);\n\n\tconst seededHashes = await hashDirectory(\"/output\", fs);\n\n\texpect(seededHashes).toEqual({\n\t\t\"a.txt\": expect.any(String),\n\t});\n});\n\ntest(\"hashDirectory skips directories that disappear while walking\", async () => {\n\tconst { hashDirectory } = await import(\"./write-output.js\");\n\tconst fs = mockFs({\n\t\t\"/output/nested/b.txt\": \"beta\",\n\t});\n\tconst readdir = fs.readdir.bind(fs);\n\tvi.spyOn(fs, \"readdir\").mockImplementation((async (...args) => {\n\t\tif (args[0] === \"/output/nested\") {\n\t\t\tthrow new Error(\"directory disappeared\");\n\t\t}\n\t\treturn readdir(args[0], args[1] as never);\n\t}) as typeof fs.readdir);\n\n\tconst seededHashes = await hashDirectory(\"/output\", fs);\n\n\texpect(seededHashes).toEqual({});\n});\n\nconst mockFs = (files: memfs.DirectoryJSON) => {\n\tconst _memfs = memfs.createFsFromVolume(memfs.Volume.fromJSON(files));\n\treturn _memfs.promises as unknown as typeof fs;\n};\n"]}
@@ -35,6 +35,8 @@ export class Logger {
35
35
  return this;
36
36
  }
37
37
  warn(message, ...args) {
38
+ if (this.options.silent)
39
+ return this;
38
40
  const prefix = this.options.prefix
39
41
  ? colors.bold(colors.yellow("[paraglide-lite] "))
40
42
  : "";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/logger/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,CAAC,OAAO,GAAG;IACjB,GAAG,OAAO,CAAC,OAAO;IAClB,aAAa,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;CAC9B,CAAC;AAWF,MAAM,OAAO,MAAM;IAET,OAAO;IADhB,YACS,OAAO,GAAkB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;uBAAxD,OAAO;IACb,CAAC;IAEG,GAAG,CAAC,OAAe;QACzB,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,IAAI,CAAC,OAAe;QAC1B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;YACjC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAC/C,CAAC,CAAC,EAAE,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,OAAO,CAAC,OAAe;QAC7B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;YACjC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAChD,CAAC,CAAC,EAAE,CAAC;QACN,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,IAAI,CAAC,OAAY,EAAE,GAAG,IAAW;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;YACjC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;YACjD,CAAC,CAAC,EAAE,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,KAAK,CAAC,OAAY,EAAE,GAAG,IAAW;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;YACjC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;YAC9C,CAAC,CAAC,EAAE,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,GAAG,CAAC,OAAY,EAAE,GAAG,IAAW;QACtC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACb,CAAC;CACD","sourcesContent":["import { colors } from \"consola/utils\";\nimport consola from \"consola\";\n\nconsola.options = {\n\t...consola.options,\n\tformatOptions: { date: false },\n};\n\nexport type LoggerOptions = {\n\tsilent: boolean;\n\n\t/**\n\t * If the [paraglide] prefix should be printed before each log message.\n\t */\n\tprefix: boolean;\n};\n\nexport class Logger {\n\tconstructor(\n\t\tprivate options: LoggerOptions = { silent: false, prefix: true }\n\t) {}\n\n\tpublic log(message: string): Logger {\n\t\tif (this.options.silent) return this;\n\t\tconst prefix = this.options.prefix ? colors.bold(\"[paraglide-lite] \") : \"\";\n\t\tconsola.log(prefix + message);\n\t\treturn this;\n\t}\n\n\tpublic info(message: string): Logger {\n\t\tif (this.options.silent) return this;\n\t\tconst prefix = this.options.prefix\n\t\t\t? colors.bold(colors.blue(\"[paraglide-lite] \"))\n\t\t\t: \"\";\n\t\tconsola.info(prefix + message);\n\t\treturn this;\n\t}\n\n\tpublic success(message: string): Logger {\n\t\tif (this.options.silent) return this;\n\t\tconst prefix = this.options.prefix\n\t\t\t? colors.bold(colors.green(\"[paraglide-lite] \"))\n\t\t\t: \"\";\n\t\tconsola.success(prefix + message);\n\t\treturn this;\n\t}\n\n\tpublic warn(message: any, ...args: any[]): Logger {\n\t\tconst prefix = this.options.prefix\n\t\t\t? colors.bold(colors.yellow(\"[paraglide-lite] \"))\n\t\t\t: \"\";\n\t\tconsola.warn(prefix + message, ...args);\n\t\treturn this;\n\t}\n\n\tpublic error(message: any, ...args: any[]): Logger {\n\t\tconst prefix = this.options.prefix\n\t\t\t? colors.bold(colors.red(\"[paraglide-lite] \"))\n\t\t\t: \"\";\n\t\tconsola.error(prefix + message, ...args);\n\t\treturn this;\n\t}\n\n\tpublic box(message: any, ...args: any[]): Logger {\n\t\tif (this.options.silent) return this;\n\t\tconsola.box(message, ...args);\n\t\treturn this;\n\t}\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/services/logger/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,OAAO,CAAC,OAAO,GAAG;IACjB,GAAG,OAAO,CAAC,OAAO;IAClB,aAAa,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;CAC9B,CAAC;AAWF,MAAM,OAAO,MAAM;IAET,OAAO;IADhB,YACS,OAAO,GAAkB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE;uBAAxD,OAAO;IACb,CAAC;IAEG,GAAG,CAAC,OAAe;QACzB,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3E,OAAO,CAAC,GAAG,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,IAAI,CAAC,OAAe;QAC1B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;YACjC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;YAC/C,CAAC,CAAC,EAAE,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,OAAO,CAAC,OAAe;QAC7B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;YACjC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;YAChD,CAAC,CAAC,EAAE,CAAC;QACN,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,OAAO,CAAC,CAAC;QAClC,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,IAAI,CAAC,OAAY,EAAE,GAAG,IAAW;QACvC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACrC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;YACjC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;YACjD,CAAC,CAAC,EAAE,CAAC;QACN,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,KAAK,CAAC,OAAY,EAAE,GAAG,IAAW;QACxC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;YACjC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;YAC9C,CAAC,CAAC,EAAE,CAAC;QACN,OAAO,CAAC,KAAK,CAAC,MAAM,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACb,CAAC;IAEM,GAAG,CAAC,OAAY,EAAE,GAAG,IAAW;QACtC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;QAC9B,OAAO,IAAI,CAAC;IACb,CAAC;CACD","sourcesContent":["import { colors } from \"consola/utils\";\nimport consola from \"consola\";\n\nconsola.options = {\n\t...consola.options,\n\tformatOptions: { date: false },\n};\n\nexport type LoggerOptions = {\n\tsilent: boolean;\n\n\t/**\n\t * If the [paraglide] prefix should be printed before each log message.\n\t */\n\tprefix: boolean;\n};\n\nexport class Logger {\n\tconstructor(\n\t\tprivate options: LoggerOptions = { silent: false, prefix: true }\n\t) {}\n\n\tpublic log(message: string): Logger {\n\t\tif (this.options.silent) return this;\n\t\tconst prefix = this.options.prefix ? colors.bold(\"[paraglide-lite] \") : \"\";\n\t\tconsola.log(prefix + message);\n\t\treturn this;\n\t}\n\n\tpublic info(message: string): Logger {\n\t\tif (this.options.silent) return this;\n\t\tconst prefix = this.options.prefix\n\t\t\t? colors.bold(colors.blue(\"[paraglide-lite] \"))\n\t\t\t: \"\";\n\t\tconsola.info(prefix + message);\n\t\treturn this;\n\t}\n\n\tpublic success(message: string): Logger {\n\t\tif (this.options.silent) return this;\n\t\tconst prefix = this.options.prefix\n\t\t\t? colors.bold(colors.green(\"[paraglide-lite] \"))\n\t\t\t: \"\";\n\t\tconsola.success(prefix + message);\n\t\treturn this;\n\t}\n\n\tpublic warn(message: any, ...args: any[]): Logger {\n\t\tif (this.options.silent) return this;\n\t\tconst prefix = this.options.prefix\n\t\t\t? colors.bold(colors.yellow(\"[paraglide-lite] \"))\n\t\t\t: \"\";\n\t\tconsola.warn(prefix + message, ...args);\n\t\treturn this;\n\t}\n\n\tpublic error(message: any, ...args: any[]): Logger {\n\t\tconst prefix = this.options.prefix\n\t\t\t? colors.bold(colors.red(\"[paraglide-lite] \"))\n\t\t\t: \"\";\n\t\tconsola.error(prefix + message, ...args);\n\t\treturn this;\n\t}\n\n\tpublic box(message: any, ...args: any[]): Logger {\n\t\tif (this.options.silent) return this;\n\t\tconsola.box(message, ...args);\n\t\treturn this;\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1,87 +1,87 @@
1
1
  {
2
- "name": "@sebbro/paraglide-lite",
3
- "type": "module",
4
- "version": "0.1.1",
5
- "license": "MIT",
6
- "publishConfig": {
7
- "access": "public",
8
- "tag": "latest"
9
- },
10
- "author": "Sebastian Broman <sebastian@broman.tv>",
11
- "homepage": "https://github.com/SebbeBroman/paraglide-js",
12
- "repository": {
13
- "type": "git",
14
- "url": "https://github.com/SebbeBroman/paraglide-js"
15
- },
16
- "bin": {
17
- "paraglide-lite": "./bin/run.js"
18
- },
19
- "files": [
20
- "./dist",
21
- "./bin"
22
- ],
23
- "types": "./dist/index.d.ts",
24
- "exports": {
25
- ".": "./dist/index.js"
26
- },
27
- "scripts": {
28
- "dev": "tsc --watch",
29
- "bench": "vitest bench --run",
30
- "build:examples": "pnpm -r --filter \"./examples/**\" --if-present --workspace-concurrency=1 build",
31
- "build": "npm run env-variables && tsc --build",
32
- "test:examples": "pnpm -r --filter \"./examples/**\" --if-present test",
33
- "test": "npm run env-variables && tsc --noEmit && vitest run --coverage ./src/**/*",
34
- "ci": "pnpm run build && pnpm run build:examples && pnpm run test && pnpm run test:examples",
35
- "test:watch": "npm run env-variables && vitest --watch ./src/**/*",
36
- "env-variables": "node ./src/services/env-variables/create-index-file.js",
37
- "lint": "oxlint --config .oxlintrc.json --fix",
38
- "format": "prettier ./src --write",
39
- "clean": "rm -rf ./dist ./node_modules"
40
- },
41
- "dependencies": {
42
- "commander": "15.0.0",
43
- "consola": "3.4.2",
44
- "json5": "2.2.3",
45
- "unplugin": "^3.3.0"
46
- },
47
- "peerDependencies": {
48
- "typescript": ">=5.6",
49
- "vite": ">=5.0.0"
50
- },
51
- "peerDependenciesMeta": {
52
- "typescript": {
53
- "optional": true
54
- },
55
- "vite": {
56
- "optional": true
57
- }
58
- },
59
- "devDependencies": {
60
- "@inlang/paraglide-js-baseline": "npm:@inlang/paraglide-js@2.23.2",
61
- "@inlang/plugin-message-format": "^4.4.1",
62
- "@rollup/plugin-virtual": "3.0.2",
63
- "@ts-morph/bootstrap": "0.29.0",
64
- "@types/node": "^22.20.1",
65
- "@vitest/coverage-v8": "4.1.10",
66
- "memfs": "4.68.0",
67
- "oxlint": "^1.77.0",
68
- "prettier": "^3.9.6",
69
- "rolldown": "1.2.3",
70
- "typescript": "5.9.3",
71
- "typescript-go": "npm:typescript@7.0.2",
72
- "vite": "8.2.1",
73
- "vitest": "4.1.10"
74
- },
75
- "keywords": [
76
- "i18n",
77
- "l10n",
78
- "internationalization",
79
- "localization",
80
- "translation",
81
- "compiler",
82
- "vite",
83
- "vite-plugin",
84
- "sveltekit",
85
- "paraglide-lite"
86
- ]
87
- }
2
+ "name": "@sebbro/paraglide-lite",
3
+ "type": "module",
4
+ "version": "0.1.2",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public",
8
+ "tag": "latest"
9
+ },
10
+ "author": "Sebastian Broman <sebastian@broman.tv>",
11
+ "homepage": "https://github.com/SebbeBroman/paraglide-js",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/SebbeBroman/paraglide-js"
15
+ },
16
+ "bin": {
17
+ "paraglide-lite": "./bin/run.js"
18
+ },
19
+ "files": [
20
+ "./dist",
21
+ "./bin"
22
+ ],
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": "./dist/index.js"
26
+ },
27
+ "dependencies": {
28
+ "commander": "15.0.0",
29
+ "consola": "3.4.2",
30
+ "json5": "2.2.3",
31
+ "unplugin": "^3.3.0"
32
+ },
33
+ "peerDependencies": {
34
+ "typescript": ">=5.6",
35
+ "vite": ">=5.0.0"
36
+ },
37
+ "peerDependenciesMeta": {
38
+ "typescript": {
39
+ "optional": true
40
+ },
41
+ "vite": {
42
+ "optional": true
43
+ }
44
+ },
45
+ "devDependencies": {
46
+ "@inlang/paraglide-js-baseline": "npm:@inlang/paraglide-js@2.25.0",
47
+ "@inlang/plugin-message-format": "^4.4.1",
48
+ "@rollup/plugin-virtual": "3.0.2",
49
+ "@ts-morph/bootstrap": "0.29.0",
50
+ "@types/node": "^22.20.1",
51
+ "@vitest/coverage-v8": "4.1.10",
52
+ "memfs": "4.68.0",
53
+ "oxlint": "^1.77.0",
54
+ "prettier": "^3.9.6",
55
+ "rolldown": "1.2.3",
56
+ "typescript": "5.9.3",
57
+ "typescript-go": "npm:typescript@7.0.2",
58
+ "vite": "8.2.1",
59
+ "vitest": "4.1.10"
60
+ },
61
+ "keywords": [
62
+ "i18n",
63
+ "l10n",
64
+ "internationalization",
65
+ "localization",
66
+ "translation",
67
+ "compiler",
68
+ "vite",
69
+ "vite-plugin",
70
+ "sveltekit",
71
+ "paraglide-lite"
72
+ ],
73
+ "scripts": {
74
+ "dev": "tsc --watch",
75
+ "bench": "vitest bench --run",
76
+ "build:examples": "pnpm -r --filter \"./examples/**\" --if-present --workspace-concurrency=1 build",
77
+ "build": "npm run env-variables && tsc --build",
78
+ "test:examples": "pnpm -r --filter \"./examples/**\" --if-present test",
79
+ "test": "npm run env-variables && tsc --noEmit && vitest run --coverage ./src/**/*",
80
+ "ci": "pnpm run build && pnpm run build:examples && pnpm run test && pnpm run test:examples",
81
+ "test:watch": "npm run env-variables && vitest --watch ./src/**/*",
82
+ "env-variables": "node ./src/services/env-variables/create-index-file.js",
83
+ "lint": "oxlint --config .oxlintrc.json --fix",
84
+ "format": "prettier ./src --write",
85
+ "clean": "rm -rf ./dist ./node_modules"
86
+ }
87
+ }