@inlang/paraglide-js 2.27.0 → 2.28.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 +9 -0
- package/dist/bundler-plugins/per-locale-build/index.js +14 -7
- package/dist/bundler-plugins/per-locale-build/index.js.map +1 -1
- package/dist/bundler-plugins/per-locale-build/vite-environments.test.js +235 -2
- package/dist/bundler-plugins/per-locale-build/vite-environments.test.js.map +1 -1
- package/dist/bundler-plugins/unplugin.d.ts +1 -1
- package/dist/bundler-plugins/unplugin.js +79 -10
- package/dist/bundler-plugins/unplugin.js.map +1 -1
- package/dist/bundler-plugins/unplugin.test.js +334 -1
- package/dist/bundler-plugins/unplugin.test.js.map +1 -1
- package/dist/bundler-plugins/vite.js +2 -1
- package/dist/bundler-plugins/vite.js.map +1 -1
- package/dist/bundler-plugins/vite.test.js +1 -1
- package/dist/bundler-plugins/vite.test.js.map +1 -1
- package/dist/cli/commands/compile/command.js +13 -0
- package/dist/cli/commands/compile/command.js.map +1 -1
- package/dist/cli/commands/compile/command.test.js +87 -3
- package/dist/cli/commands/compile/command.test.js.map +1 -1
- package/dist/cli/commands/init/command.d.ts +5 -0
- package/dist/cli/commands/init/command.js +213 -59
- package/dist/cli/commands/init/command.js.map +1 -1
- package/dist/cli/commands/init/command.test.d.ts +1 -0
- package/dist/cli/commands/init/command.test.js +123 -0
- package/dist/cli/commands/init/command.test.js.map +1 -0
- package/dist/cli/init-options.d.ts +11 -0
- package/dist/cli/init-options.js +2 -0
- package/dist/cli/init-options.js.map +1 -0
- package/dist/cli/steps/add-inlang-check.js +2 -2
- package/dist/cli/steps/add-inlang-check.js.map +1 -1
- package/dist/cli/steps/add-message-lint.d.ts +34 -0
- package/dist/cli/steps/add-message-lint.js +105 -0
- package/dist/cli/steps/add-message-lint.js.map +1 -0
- package/dist/cli/steps/add-message-lint.test.d.ts +1 -0
- package/dist/cli/steps/add-message-lint.test.js +74 -0
- package/dist/cli/steps/add-message-lint.test.js.map +1 -0
- package/dist/cli/steps/add-vite-plugin.d.ts +18 -0
- package/dist/cli/steps/add-vite-plugin.js +34 -6
- package/dist/cli/steps/add-vite-plugin.js.map +1 -1
- package/dist/cli/steps/add-vite-plugin.test.js +25 -9
- package/dist/cli/steps/add-vite-plugin.test.js.map +1 -1
- package/dist/cli/steps/check-for-uncomitted-changes.d.ts +3 -0
- package/dist/cli/steps/check-for-uncomitted-changes.js +15 -3
- package/dist/cli/steps/check-for-uncomitted-changes.js.map +1 -1
- package/dist/cli/steps/detect-bundler.d.ts +1 -0
- package/dist/cli/steps/detect-bundler.js +7 -1
- package/dist/cli/steps/detect-bundler.js.map +1 -1
- package/dist/cli/steps/initialize-inlang-project.d.ts +4 -3
- package/dist/cli/steps/initialize-inlang-project.js +60 -157
- package/dist/cli/steps/initialize-inlang-project.js.map +1 -1
- package/dist/cli/steps/maybe-add-machine-translation.d.ts +4 -0
- package/dist/cli/steps/maybe-add-machine-translation.js +11 -6
- package/dist/cli/steps/maybe-add-machine-translation.js.map +1 -1
- package/dist/cli/steps/maybe-add-sherlock.d.ts +2 -0
- package/dist/cli/steps/maybe-add-sherlock.js +12 -6
- package/dist/cli/steps/maybe-add-sherlock.js.map +1 -1
- package/dist/cli/steps/maybe-add-sherlock.test.d.ts +1 -0
- package/dist/cli/steps/maybe-add-sherlock.test.js +44 -0
- package/dist/cli/steps/maybe-add-sherlock.test.js.map +1 -0
- package/dist/cli/steps/prompt-for-outdir.d.ts +2 -0
- package/dist/cli/steps/prompt-for-outdir.js +4 -0
- package/dist/cli/steps/prompt-for-outdir.js.map +1 -1
- package/dist/cli/steps/update-package-json.js +8 -13
- package/dist/cli/steps/update-package-json.js.map +1 -1
- package/dist/cli/steps/update-ts-config.d.ts +6 -0
- package/dist/cli/steps/update-ts-config.js +27 -4
- package/dist/cli/steps/update-ts-config.js.map +1 -1
- package/dist/cli/utils.d.ts +2 -0
- package/dist/cli/utils.js +6 -0
- package/dist/cli/utils.js.map +1 -1
- package/dist/cli/utils.test.d.ts +1 -0
- package/dist/cli/utils.test.js +16 -0
- package/dist/cli/utils.test.js.map +1 -0
- package/dist/compiler/compile-annotation.js +6 -1
- package/dist/compiler/compile-annotation.js.map +1 -1
- package/dist/compiler/compile-bundle.d.ts +4 -0
- package/dist/compiler/compile-bundle.js +67 -2
- package/dist/compiler/compile-bundle.js.map +1 -1
- package/dist/compiler/compile-bundle.test.js +214 -1
- package/dist/compiler/compile-bundle.test.js.map +1 -1
- package/dist/compiler/compile-lifecycle.test.d.ts +1 -0
- package/dist/compiler/compile-lifecycle.test.js +69 -0
- package/dist/compiler/compile-lifecycle.test.js.map +1 -0
- package/dist/compiler/compile-local-variable.test.js +14 -14
- package/dist/compiler/compile-local-variable.test.js.map +1 -1
- package/dist/compiler/compile-message.exact-number.test.js +1 -1
- package/dist/compiler/compile-message.exact-number.test.js.map +1 -1
- package/dist/compiler/compile-message.locals.test.js +1 -1
- package/dist/compiler/compile-message.locals.test.js.map +1 -1
- package/dist/compiler/compile-message.test.js +2 -2
- package/dist/compiler/compile-message.test.js.map +1 -1
- package/dist/compiler/compile-pattern.test.js +5 -5
- package/dist/compiler/compile-pattern.test.js.map +1 -1
- package/dist/compiler/compile-project.js +1 -0
- package/dist/compiler/compile-project.js.map +1 -1
- package/dist/compiler/compile-project.test.js +94 -1
- package/dist/compiler/compile-project.test.js.map +1 -1
- package/dist/compiler/compile.js +36 -17
- package/dist/compiler/compile.js.map +1 -1
- package/dist/compiler/compile.test.js +70 -0
- package/dist/compiler/compile.test.js.map +1 -1
- package/dist/compiler/compiler-options.d.ts +41 -0
- package/dist/compiler/compiler-options.js.map +1 -1
- package/dist/compiler/output-structure/message-modules.js +1 -1
- package/dist/compiler/output-structure/message-modules.js.map +1 -1
- package/dist/compiler/registry.js +71 -13
- package/dist/compiler/registry.js.map +1 -1
- package/dist/compiler/registry.test.d.ts +1 -0
- package/dist/compiler/registry.test.js +291 -0
- package/dist/compiler/registry.test.js.map +1 -0
- package/dist/compiler/runtime/create-runtime.d.ts +2 -0
- package/dist/compiler/runtime/create-runtime.js +9 -0
- package/dist/compiler/runtime/create-runtime.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-cookie.js +8 -1
- package/dist/compiler/runtime/extract-locale-from-cookie.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-cookie.test.js +26 -0
- package/dist/compiler/runtime/extract-locale-from-cookie.test.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-header.js +47 -5
- package/dist/compiler/runtime/extract-locale-from-header.js.map +1 -1
- package/dist/compiler/runtime/extract-locale-from-header.test.js +58 -0
- package/dist/compiler/runtime/extract-locale-from-header.test.js.map +1 -1
- package/dist/compiler/runtime/get-locale.js +7 -1
- package/dist/compiler/runtime/get-locale.js.map +1 -1
- package/dist/compiler/runtime/get-locale.test.js +95 -0
- package/dist/compiler/runtime/get-locale.test.js.map +1 -1
- package/dist/compiler/runtime/localize-href.d.ts +8 -0
- package/dist/compiler/runtime/localize-href.js +60 -0
- package/dist/compiler/runtime/localize-href.js.map +1 -1
- package/dist/compiler/runtime/localize-href.test.js +227 -1
- package/dist/compiler/runtime/localize-href.test.js.map +1 -1
- package/dist/compiler/runtime/set-locale.js +24 -6
- package/dist/compiler/runtime/set-locale.js.map +1 -1
- package/dist/compiler/runtime/set-locale.test.js +182 -0
- package/dist/compiler/runtime/set-locale.test.js.map +1 -1
- package/dist/compiler/runtime/type.d.ts +2 -0
- package/dist/compiler/runtime/type.js.map +1 -1
- package/dist/compiler/runtime/type.test.js +34 -0
- package/dist/compiler/runtime/type.test.js.map +1 -1
- package/dist/compiler/runtime/variables.d.ts +2 -0
- package/dist/compiler/runtime/variables.js +7 -0
- package/dist/compiler/runtime/variables.js.map +1 -1
- package/dist/compiler/server/middleware.d.ts +8 -2
- package/dist/compiler/server/middleware.js +175 -7
- package/dist/compiler/server/middleware.js.map +1 -1
- package/dist/compiler/server/middleware.test.js +750 -3
- package/dist/compiler/server/middleware.test.js.map +1 -1
- package/dist/eslint/index.d.ts +4 -0
- package/dist/eslint/index.js +8 -0
- package/dist/eslint/index.js.map +1 -0
- package/dist/eslint/no-dynamic-message-access.d.ts +3 -0
- package/dist/eslint/no-dynamic-message-access.js +150 -0
- package/dist/eslint/no-dynamic-message-access.js.map +1 -0
- package/dist/eslint/no-dynamic-message-access.test.d.ts +1 -0
- package/dist/eslint/no-dynamic-message-access.test.js +126 -0
- package/dist/eslint/no-dynamic-message-access.test.js.map +1 -0
- package/dist/eslint/oxlint.test.d.ts +1 -0
- package/dist/eslint/oxlint.test.js +64 -0
- package/dist/eslint/oxlint.test.js.map +1 -0
- package/dist/services/config/config-schema.d.ts +2 -0
- package/dist/services/config/config-schema.js +2 -0
- package/dist/services/config/config-schema.js.map +1 -1
- package/dist/services/config/load-paraglide-config.test.js +12 -0
- package/dist/services/config/load-paraglide-config.test.js.map +1 -1
- package/dist/services/env-variables/index.js +1 -1
- package/dist/services/env-variables/index.js.map +1 -1
- package/dist/services/file-handling/write-output.bench.js +51 -11
- package/dist/services/file-handling/write-output.bench.js.map +1 -1
- package/dist/services/file-handling/write-output.js +60 -20
- package/dist/services/file-handling/write-output.js.map +1 -1
- package/dist/services/file-handling/write-output.test.js +175 -0
- package/dist/services/file-handling/write-output.test.js.map +1 -1
- package/package.json +14 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"load-paraglide-config.test.js","sourceRoot":"","sources":["../../../src/services/config/load-paraglide-config.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC3E,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EACN,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GAChB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAE5C,IAAI,eAAe,GAAa,EAAE,CAAC;AAEnC,KAAK,UAAU,sBAAsB;IACpC,MAAM,SAAS,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAC1E,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,UAAU,CAAC,GAAG,EAAE;IACf,yBAAyB,EAAE,CAAC;IAC5B,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH,SAAS,CAAC,KAAK,IAAI,EAAE;IACpB,EAAE,CAAC,eAAe,EAAE,CAAC;IACrB,KAAK,MAAM,SAAS,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;QACnD,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvD,CAAC;AACF,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACvC,IAAI,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,CAAC,MAAM,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QAC3D,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,KAAK,MAAM,QAAQ,IAAI,iBAAiB,EAAE,CAAC;YAC1C,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACvE,CAAC;QACD,MAAM,CAAC,MAAM,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CACpD,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAC5C,CAAC;QACF,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CACpD,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAC7C,CAAC;QACF,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAAC,CAAC;QACxD,MAAM,CAAC,MAAM,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CACpD,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAC5C,CAAC;QACF,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC,CAAC;QACvD,MAAM,CAAC,MAAM,sBAAsB,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CACpD,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,sBAAsB,CAAC,CAC7C,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,gBAAgB,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,MAAM,CAAC,gBAAgB,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,CAAC,gBAAgB,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,MAAM,CAAC,gBAAgB,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACpC,IAAI,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,CAAC,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QAC1E,MAAM,KAAK,GAA4B;YACtC,CAAC,qBAAqB,EAAE,uCAAuC,CAAC;YAChE,CAAC,sBAAsB,EAAE,wCAAwC,CAAC;YAClE,CAAC,sBAAsB,EAAE,0CAA0C,CAAC;YACpE;gBACC,qBAAqB;gBACrB;oBACC,kBAAkB;oBAClB,uBAAuB;oBACvB,uCAAuC;oBACvC,GAAG;oBACH,uBAAuB;iBACvB,CAAC,IAAI,CAAC,IAAI,CAAC;aACZ;SACD,CAAC;QAEF,KAAK,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,IAAI,KAAK,EAAE,CAAC;YACzC,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;YAClD,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC;YAE1D,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;YACzD,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;QAC5D,CAAC;IACF,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,qCAAqC,CACrC,CAAC;QACF,MAAM,CAAC,CAAC,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CACtE,QAAQ,CACR,CAAC;QAEF,yBAAyB,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QACrD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,4BAA4B,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,IAAI,CAC3D,CAAC;QACF,MAAM,CAAC,CAAC,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CACtE,WAAW,CACX,CAAC;IACH,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,mBAAmB,CACnB,CAAC;QAEF,MAAM,MAAM,CAAC,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAChE,oBAAoB,CACpB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,4CAA4C,CAC5C,CAAC;QAEF,MAAM,MAAM,CAAC,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAChE,gBAAgB,CAChB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,oDAAoD,CACpD,CAAC;QAEF,MAAM,MAAM,CAAC,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAChE,eAAe,CACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,kFAAkF,CAClF,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC;YACvC,eAAe;YACf,sBAAsB;YACtB,QAAQ;SACR,CAAC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,gEAAgE,CAChE,CAAC;QAEF,MAAM,MAAM,CAAC,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAChE,eAAe,CACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C;YACC,kBAAkB;YAClB,6EAA6E;YAC7E,GAAG;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CACZ,CAAC;QAEF,MAAM,MAAM,CAAC,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAChE,iBAAiB,CACjB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACzD,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,+CAA+C,CAC/C,CAAC;QAEF,MAAM,MAAM,CAAC,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAChE,mBAAmB,CACnB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,yBAAyB,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,+BAA+B,CAC/B,CAAC;QACF,MAAM,MAAM,CAAC,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAChE,UAAU,CACV,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,yDAAyD,CACzD,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACxC,MAAM,CAAC,gBAAgB,CAAC,aAAa,CAAC,CACtC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,mEAAmE,CACnE,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACxC,MAAM,CAAC,gBAAgB,CAAC,WAAW,CAAC,CACpC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,uDAAuD,CACvD,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACvD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACtD,CAAC;IACF,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,SAAS,CACd,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAC5C,mEAAmE,CACnE,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC;YACxC,UAAU;YACV,MAAM,EAAE,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;SAClD,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QAC5D,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;QAClD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC;QAChE,MAAM,SAAS,CAAC,UAAU,EAAE,sCAAsC,CAAC,CAAC;QAEpE,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QACxD,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QACzD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAE3B,MAAM,SAAS,CAAC,UAAU,EAAE,uCAAuC,CAAC,CAAC;QACrE,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAE7C,yBAAyB,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;QACxD,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,oEAAoE;IACpE,uEAAuE;IACvE,IAAI,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QAC3E,KAAK,MAAM,QAAQ,IAAI,iBAAiB,EAAE,CAAC;YAC1C,MAAM,UAAU,GAAG,MAAM,sBAAsB,EAAE,CAAC;YAClD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;YACnD,MAAM,KAAK,GAAG,CAAC,MAAc,EAAE,EAAE,CAChC,SAAS,CACR,UAAU,EACV,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;gBACxB,CAAC,CAAC,iCAAiC,MAAM,KAAK;gBAC9C,CAAC,CAAC,+BAA+B,MAAM,KAAK,CAC7C,CAAC;YAEH,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;YACrB,MAAM,KAAK,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;YACxD,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAE7C,yBAAyB,EAAE,CAAC;YAC5B,MAAM,KAAK,CAAC,QAAQ,CAAC,CAAC;YACtB,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC;YACzD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAChD,CAAC;IACF,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["import { describe, expect, test, vi, beforeEach, afterEach } from \"vitest\";\nimport { mkdtemp, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport consola from \"consola\";\nimport {\n\tclearParaglideConfigCache,\n\tloadParaglideConfig,\n\tCONFIG_FILE_NAMES,\n\tresolveConfigCandidate,\n\tisConfigFileName,\n} from \"./index.js\";\nimport { Logger } from \"../logger/index.js\";\n\nlet testDirectories: string[] = [];\n\nasync function createProjectDirectory(): Promise<string> {\n\tconst directory = await mkdtemp(path.join(tmpdir(), \"paraglide-config-\"));\n\ttestDirectories.push(directory);\n\treturn directory;\n}\n\nbeforeEach(() => {\n\tclearParaglideConfigCache();\n\tvi.spyOn(consola, \"warn\").mockImplementation(() => {});\n});\n\nafterEach(async () => {\n\tvi.restoreAllMocks();\n\tfor (const directory of testDirectories.splice(0)) {\n\t\tawait rm(directory, { recursive: true, force: true });\n\t}\n});\n\ndescribe(\"resolveConfigCandidate\", () => {\n\ttest(\"returns undefined when no config file exists\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\texpect(await resolveConfigCandidate(projectDir)).toBeUndefined();\n\t});\n\n\ttest(\"prefers .js over .mjs over .ts over .cjs\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tfor (const fileName of CONFIG_FILE_NAMES) {\n\t\t\tawait writeFile(path.join(projectDir, fileName), \"export default {}\");\n\t\t}\n\t\texpect(await resolveConfigCandidate(projectDir)).toBe(\n\t\t\tpath.join(projectDir, \"paraglide.config.js\")\n\t\t);\n\t\tawait rm(path.join(projectDir, \"paraglide.config.js\"));\n\t\texpect(await resolveConfigCandidate(projectDir)).toBe(\n\t\t\tpath.join(projectDir, \"paraglide.config.mjs\")\n\t\t);\n\t\tawait rm(path.join(projectDir, \"paraglide.config.mjs\"));\n\t\texpect(await resolveConfigCandidate(projectDir)).toBe(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\")\n\t\t);\n\t\tawait rm(path.join(projectDir, \"paraglide.config.ts\"));\n\t\texpect(await resolveConfigCandidate(projectDir)).toBe(\n\t\t\tpath.join(projectDir, \"paraglide.config.cjs\")\n\t\t);\n\t});\n\n\ttest(\"isConfigFileName matches exactly the config names\", () => {\n\t\texpect(isConfigFileName(\"/x/paraglide.config.js\")).toBe(true);\n\t\texpect(isConfigFileName(\"/x/paraglide.config.cjs\")).toBe(true);\n\t\texpect(isConfigFileName(\"/x/README.md\")).toBe(false);\n\t\texpect(isConfigFileName(\"/x/other.config.js\")).toBe(false);\n\t});\n});\n\ndescribe(\"loadParaglideConfig\", () => {\n\ttest(\"returns undefined when no config file exists\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\texpect(await loadParaglideConfig({ projectDir })).toBeUndefined();\n\t});\n\n\ttest(\"loads every supported file format with a default export\", async () => {\n\t\tconst cases: Array<[string, string]> = [\n\t\t\t[\"paraglide.config.js\", `export default { outdir: './out-js' }`],\n\t\t\t[\"paraglide.config.mjs\", `export default { outdir: './out-mjs' }`],\n\t\t\t[\"paraglide.config.cjs\", `module.exports = { outdir: './out-cjs' }`],\n\t\t\t[\n\t\t\t\t\"paraglide.config.ts\",\n\t\t\t\t[\n\t\t\t\t\t\"const config = {\",\n\t\t\t\t\t' outdir: \"./out-ts\",',\n\t\t\t\t\t' strategy: [\"cookie\", \"baseLocale\"],',\n\t\t\t\t\t\"}\",\n\t\t\t\t\t\"export default config\",\n\t\t\t\t].join(\"\\n\"),\n\t\t\t],\n\t\t];\n\n\t\tfor (const [fileName, content] of cases) {\n\t\t\tconst projectDir = await createProjectDirectory();\n\t\t\tawait writeFile(path.join(projectDir, fileName), content);\n\n\t\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\t\texpect(loaded?.path).toBe(path.join(projectDir, fileName));\n\t\t}\n\t});\n\ttest(\"leaves outdir paths untouched — the integration resolves them\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outdir: '../out' }\"\n\t\t);\n\t\texpect((await loadParaglideConfig({ projectDir }))?.config.outdir).toBe(\n\t\t\t\"../out\"\n\t\t);\n\n\t\tclearParaglideConfigCache();\n\t\tconst absoluteOut = path.join(projectDir, \"abs-out\");\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t`export default { outdir: ${JSON.stringify(absoluteOut)} }`\n\t\t);\n\t\texpect((await loadParaglideConfig({ projectDir }))?.config.outdir).toBe(\n\t\t\tabsoluteOut\n\t\t);\n\t});\n\ttest(\"throws when the config file has no object export\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default 42\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/expected an object/\n\t\t);\n\t});\n\n\ttest(\"throws when an option has an invalid value\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { cookieMaxAge: 'forever' }\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"cookieMaxAge\"/\n\t\t);\n\t});\n\n\ttest(\"throws when a strategy name is unknown\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { strategy: ['cookie', 'cookies'] }\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"strategy\\.1\"/\n\t\t);\n\t});\n\n\ttest(\"accepts custom strategies matching the runtime pattern\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { strategy: ['custom-server', 'custom-my_strategy-2', 'cookie'] }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\texpect(loaded?.config.strategy).toEqual([\n\t\t\t\"custom-server\",\n\t\t\t\"custom-my_strategy-2\",\n\t\t\t\"cookie\",\n\t\t]);\n\t});\n\n\ttest(\"rejects malformed custom strategy names\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { strategy: ['custom-', 'custom-my strategy'] }\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"strategy\\.0\"/\n\t\t);\n\t});\n\n\ttest(\"throws when routeStrategies combine strategy and exclude\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t[\n\t\t\t\t\"export default {\",\n\t\t\t\t\" routeStrategies: [{ match: '/admin', strategy: ['url'], exclude: true }],\",\n\t\t\t\t\"}\",\n\t\t\t].join(\"\\n\")\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/routeStrategies/\n\t\t);\n\t});\n\n\ttest(\"throws when outputStructure is invalid\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outputStructure: 'modules' }\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"outputStructure\"/\n\t\t);\n\t});\n\n\ttest(\"rejects an empty outdir\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outdir: '' }\"\n\t\t);\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"outdir\"/\n\t\t);\n\t});\n\n\ttest(\"warns about unknown options but loads the rest\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outdir: './out', outdirKye: './typo' }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\texpect(loaded?.config.outdir).toBe(\"./out\");\n\t\texpect(consola.warn).toHaveBeenCalledWith(\n\t\t\texpect.stringContaining('\"outdirKye\"')\n\t\t);\n\t});\n\n\ttest(\"treats a legacy project key as an unknown option\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { project: './elsewhere.inlang', outdir: './out' }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\texpect(loaded?.config.outdir).toBe(\"./out\");\n\t\texpect(consola.warn).toHaveBeenCalledWith(\n\t\t\texpect.stringContaining('\"project\"')\n\t\t);\n\t});\n\n\ttest(\"accepts cleanOutdir silently (it is ignored)\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outdir: './out', cleanOutdir: true }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\texpect(loaded?.config.cleanOutdir).toBe(true);\n\t\tfor (const call of vi.mocked(consola.warn).mock.calls) {\n\t\t\texpect(String(call[0])).not.toContain(\"cleanOutdir\");\n\t\t}\n\t});\n\ttest(\"respects a silent logger for warnings\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outdir: './out', cleanOutdir: true, typoKey: 1 }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({\n\t\t\tprojectDir,\n\t\t\tlogger: new Logger({ silent: true, prefix: true }),\n\t\t});\n\t\texpect(loaded?.config.outdir).toBe(\"./out\");\n\t\texpect(consola.warn).not.toHaveBeenCalled();\n\t});\n\n\ttest(\"caches results until the cache is cleared\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tconst configPath = path.join(projectDir, \"paraglide.config.ts\");\n\t\tawait writeFile(configPath, \"export default { outdir: './first' }\");\n\n\t\tconst first = await loadParaglideConfig({ projectDir });\n\t\tconst second = await loadParaglideConfig({ projectDir });\n\t\texpect(second).toBe(first);\n\n\t\tawait writeFile(configPath, \"export default { outdir: './second' }\");\n\t\tconst stale = await loadParaglideConfig({ projectDir });\n\t\texpect(stale?.config.outdir).toBe(\"./first\");\n\n\t\tclearParaglideConfigCache();\n\t\tconst fresh = await loadParaglideConfig({ projectDir });\n\t\texpect(fresh?.config.outdir).toBe(\"./second\");\n\t});\n\n\t// Reloading must pick up new content for every config file format —\n\t// async module imports would keep serving stale content for .mjs/.cjs.\n\ttest(\"reloading picks up changed content for every file format\", async () => {\n\t\tfor (const fileName of CONFIG_FILE_NAMES) {\n\t\t\tconst projectDir = await createProjectDirectory();\n\t\t\tconst configPath = path.join(projectDir, fileName);\n\t\t\tconst write = (outdir: string) =>\n\t\t\t\twriteFile(\n\t\t\t\t\tconfigPath,\n\t\t\t\t\tfileName.endsWith(\".cjs\")\n\t\t\t\t\t\t? `module.exports = { outdir: './${outdir}' }`\n\t\t\t\t\t\t: `export default { outdir: './${outdir}' }`\n\t\t\t\t);\n\n\t\t\tawait write(\"first\");\n\t\t\tconst first = await loadParaglideConfig({ projectDir });\n\t\t\texpect(first?.config.outdir).toBe(\"./first\");\n\n\t\t\tclearParaglideConfigCache();\n\t\t\tawait write(\"second\");\n\t\t\tconst second = await loadParaglideConfig({ projectDir });\n\t\t\texpect(second?.config.outdir).toBe(\"./second\");\n\t\t}\n\t});\n});\n"]}
|
|
1
|
+
{"version":3,"file":"load-paraglide-config.test.js","sourceRoot":"","sources":["../../../src/services/config/load-paraglide-config.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, expect, test, vi, beforeEach, afterEach } from \"vitest\";\nimport { mkdtemp, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport consola from \"consola\";\nimport {\n\tclearParaglideConfigCache,\n\tloadParaglideConfig,\n\tCONFIG_FILE_NAMES,\n\tresolveConfigCandidate,\n\tisConfigFileName,\n} from \"./index.js\";\nimport { Logger } from \"../logger/index.js\";\n\nlet testDirectories: string[] = [];\n\nasync function createProjectDirectory(): Promise<string> {\n\tconst directory = await mkdtemp(path.join(tmpdir(), \"paraglide-config-\"));\n\ttestDirectories.push(directory);\n\treturn directory;\n}\n\nbeforeEach(() => {\n\tclearParaglideConfigCache();\n\tvi.spyOn(consola, \"warn\").mockImplementation(() => {});\n});\n\nafterEach(async () => {\n\tvi.restoreAllMocks();\n\tfor (const directory of testDirectories.splice(0)) {\n\t\tawait rm(directory, { recursive: true, force: true });\n\t}\n});\n\ndescribe(\"resolveConfigCandidate\", () => {\n\ttest(\"returns undefined when no config file exists\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\texpect(await resolveConfigCandidate(projectDir)).toBeUndefined();\n\t});\n\n\ttest(\"prefers .js over .mjs over .ts over .cjs\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tfor (const fileName of CONFIG_FILE_NAMES) {\n\t\t\tawait writeFile(path.join(projectDir, fileName), \"export default {}\");\n\t\t}\n\t\texpect(await resolveConfigCandidate(projectDir)).toBe(\n\t\t\tpath.join(projectDir, \"paraglide.config.js\")\n\t\t);\n\t\tawait rm(path.join(projectDir, \"paraglide.config.js\"));\n\t\texpect(await resolveConfigCandidate(projectDir)).toBe(\n\t\t\tpath.join(projectDir, \"paraglide.config.mjs\")\n\t\t);\n\t\tawait rm(path.join(projectDir, \"paraglide.config.mjs\"));\n\t\texpect(await resolveConfigCandidate(projectDir)).toBe(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\")\n\t\t);\n\t\tawait rm(path.join(projectDir, \"paraglide.config.ts\"));\n\t\texpect(await resolveConfigCandidate(projectDir)).toBe(\n\t\t\tpath.join(projectDir, \"paraglide.config.cjs\")\n\t\t);\n\t});\n\n\ttest(\"isConfigFileName matches exactly the config names\", () => {\n\t\texpect(isConfigFileName(\"/x/paraglide.config.js\")).toBe(true);\n\t\texpect(isConfigFileName(\"/x/paraglide.config.cjs\")).toBe(true);\n\t\texpect(isConfigFileName(\"/x/README.md\")).toBe(false);\n\t\texpect(isConfigFileName(\"/x/other.config.js\")).toBe(false);\n\t});\n});\n\ndescribe(\"loadParaglideConfig\", () => {\n\ttest(\"returns undefined when no config file exists\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\texpect(await loadParaglideConfig({ projectDir })).toBeUndefined();\n\t});\n\n\ttest(\"loads every supported file format with a default export\", async () => {\n\t\tconst cases: Array<[string, string]> = [\n\t\t\t[\"paraglide.config.js\", `export default { outdir: './out-js' }`],\n\t\t\t[\"paraglide.config.mjs\", `export default { outdir: './out-mjs' }`],\n\t\t\t[\"paraglide.config.cjs\", `module.exports = { outdir: './out-cjs' }`],\n\t\t\t[\n\t\t\t\t\"paraglide.config.ts\",\n\t\t\t\t[\n\t\t\t\t\t\"const config = {\",\n\t\t\t\t\t' outdir: \"./out-ts\",',\n\t\t\t\t\t' strategy: [\"cookie\", \"baseLocale\"],',\n\t\t\t\t\t\"}\",\n\t\t\t\t\t\"export default config\",\n\t\t\t\t].join(\"\\n\"),\n\t\t\t],\n\t\t];\n\n\t\tfor (const [fileName, content] of cases) {\n\t\t\tconst projectDir = await createProjectDirectory();\n\t\t\tawait writeFile(path.join(projectDir, fileName), content);\n\n\t\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\t\texpect(loaded?.path).toBe(path.join(projectDir, fileName));\n\t\t}\n\t});\n\ttest(\"leaves outdir paths untouched — the integration resolves them\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outdir: '../out' }\"\n\t\t);\n\t\texpect((await loadParaglideConfig({ projectDir }))?.config.outdir).toBe(\n\t\t\t\"../out\"\n\t\t);\n\n\t\tclearParaglideConfigCache();\n\t\tconst absoluteOut = path.join(projectDir, \"abs-out\");\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t`export default { outdir: ${JSON.stringify(absoluteOut)} }`\n\t\t);\n\t\texpect((await loadParaglideConfig({ projectDir }))?.config.outdir).toBe(\n\t\t\tabsoluteOut\n\t\t);\n\t});\n\ttest(\"throws when the config file has no object export\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default 42\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/expected an object/\n\t\t);\n\t});\n\n\ttest(\"throws when an option has an invalid value\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { cookieMaxAge: 'forever' }\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"cookieMaxAge\"/\n\t\t);\n\t});\n\n\ttest(\"accepts the cookie samesite and secure options\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { cookieSameSite: 'none', cookieSecure: 'auto' }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\texpect(loaded?.config.cookieSameSite).toBe(\"none\");\n\t\texpect(loaded?.config.cookieSecure).toBe(\"auto\");\n\t});\n\n\ttest(\"throws when cookieSameSite has an invalid value\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { cookieSameSite: 'relaxed' }\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"cookieSameSite\"/\n\t\t);\n\t});\n\n\ttest(\"throws when a strategy name is unknown\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { strategy: ['cookie', 'cookies'] }\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"strategy\\.1\"/\n\t\t);\n\t});\n\n\ttest(\"accepts custom strategies matching the runtime pattern\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { strategy: ['custom-server', 'custom-my_strategy-2', 'cookie'] }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\texpect(loaded?.config.strategy).toEqual([\n\t\t\t\"custom-server\",\n\t\t\t\"custom-my_strategy-2\",\n\t\t\t\"cookie\",\n\t\t]);\n\t});\n\n\ttest(\"rejects malformed custom strategy names\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { strategy: ['custom-', 'custom-my strategy'] }\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"strategy\\.0\"/\n\t\t);\n\t});\n\n\ttest(\"throws when routeStrategies combine strategy and exclude\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t[\n\t\t\t\t\"export default {\",\n\t\t\t\t\" routeStrategies: [{ match: '/admin', strategy: ['url'], exclude: true }],\",\n\t\t\t\t\"}\",\n\t\t\t].join(\"\\n\")\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/routeStrategies/\n\t\t);\n\t});\n\n\ttest(\"throws when outputStructure is invalid\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outputStructure: 'modules' }\"\n\t\t);\n\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"outputStructure\"/\n\t\t);\n\t});\n\n\ttest(\"rejects an empty outdir\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outdir: '' }\"\n\t\t);\n\t\tawait expect(loadParaglideConfig({ projectDir })).rejects.toThrow(\n\t\t\t/\"outdir\"/\n\t\t);\n\t});\n\n\ttest(\"warns about unknown options but loads the rest\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outdir: './out', outdirKye: './typo' }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\texpect(loaded?.config.outdir).toBe(\"./out\");\n\t\texpect(consola.warn).toHaveBeenCalledWith(\n\t\t\texpect.stringContaining('\"outdirKye\"')\n\t\t);\n\t});\n\n\ttest(\"treats a legacy project key as an unknown option\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { project: './elsewhere.inlang', outdir: './out' }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\texpect(loaded?.config.outdir).toBe(\"./out\");\n\t\texpect(consola.warn).toHaveBeenCalledWith(\n\t\t\texpect.stringContaining('\"project\"')\n\t\t);\n\t});\n\n\ttest(\"accepts cleanOutdir silently (it is ignored)\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outdir: './out', cleanOutdir: true }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({ projectDir });\n\t\texpect(loaded?.config.cleanOutdir).toBe(true);\n\t\tfor (const call of vi.mocked(consola.warn).mock.calls) {\n\t\t\texpect(String(call[0])).not.toContain(\"cleanOutdir\");\n\t\t}\n\t});\n\ttest(\"respects a silent logger for warnings\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tawait writeFile(\n\t\t\tpath.join(projectDir, \"paraglide.config.ts\"),\n\t\t\t\"export default { outdir: './out', cleanOutdir: true, typoKey: 1 }\"\n\t\t);\n\n\t\tconst loaded = await loadParaglideConfig({\n\t\t\tprojectDir,\n\t\t\tlogger: new Logger({ silent: true, prefix: true }),\n\t\t});\n\t\texpect(loaded?.config.outdir).toBe(\"./out\");\n\t\texpect(consola.warn).not.toHaveBeenCalled();\n\t});\n\n\ttest(\"caches results until the cache is cleared\", async () => {\n\t\tconst projectDir = await createProjectDirectory();\n\t\tconst configPath = path.join(projectDir, \"paraglide.config.ts\");\n\t\tawait writeFile(configPath, \"export default { outdir: './first' }\");\n\n\t\tconst first = await loadParaglideConfig({ projectDir });\n\t\tconst second = await loadParaglideConfig({ projectDir });\n\t\texpect(second).toBe(first);\n\n\t\tawait writeFile(configPath, \"export default { outdir: './second' }\");\n\t\tconst stale = await loadParaglideConfig({ projectDir });\n\t\texpect(stale?.config.outdir).toBe(\"./first\");\n\n\t\tclearParaglideConfigCache();\n\t\tconst fresh = await loadParaglideConfig({ projectDir });\n\t\texpect(fresh?.config.outdir).toBe(\"./second\");\n\t});\n\n\t// Reloading must pick up new content for every config file format —\n\t// async module imports would keep serving stale content for .mjs/.cjs.\n\ttest(\"reloading picks up changed content for every file format\", async () => {\n\t\tfor (const fileName of CONFIG_FILE_NAMES) {\n\t\t\tconst projectDir = await createProjectDirectory();\n\t\t\tconst configPath = path.join(projectDir, fileName);\n\t\t\tconst write = (outdir: string) =>\n\t\t\t\twriteFile(\n\t\t\t\t\tconfigPath,\n\t\t\t\t\tfileName.endsWith(\".cjs\")\n\t\t\t\t\t\t? `module.exports = { outdir: './${outdir}' }`\n\t\t\t\t\t\t: `export default { outdir: './${outdir}' }`\n\t\t\t\t);\n\n\t\t\tawait write(\"first\");\n\t\t\tconst first = await loadParaglideConfig({ projectDir });\n\t\t\texpect(first?.config.outdir).toBe(\"./first\");\n\n\t\t\tclearParaglideConfigCache();\n\t\t\tawait write(\"second\");\n\t\t\tconst second = await loadParaglideConfig({ projectDir });\n\t\t\texpect(second?.config.outdir).toBe(\"./second\");\n\t\t}\n\t});\n});\n"]}
|
|
@@ -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,QAAQ;CAC/B,CAAA","sourcesContent":["\nexport const ENV_VARIABLES = {\n\tPARJS_PACKAGE_VERSION: \"2.
|
|
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,QAAQ;CAC/B,CAAA","sourcesContent":["\nexport const ENV_VARIABLES = {\n\tPARJS_PACKAGE_VERSION: \"2.28.0\",\n}\n"]}
|
|
@@ -1,24 +1,64 @@
|
|
|
1
|
-
import { bench } from "vitest";
|
|
1
|
+
import { bench, beforeAll } from "vitest";
|
|
2
2
|
import memfs from "memfs";
|
|
3
3
|
import { writeOutput } from "./write-output.js";
|
|
4
4
|
const output = Object.fromEntries(Array.from({ length: 2_000 }, (_, index) => [
|
|
5
5
|
`messages/bundle-${index}.js`,
|
|
6
6
|
`export const m${index} = ${JSON.stringify("Hello world")}`,
|
|
7
7
|
]));
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const localeOutput = {
|
|
9
|
+
"messages/en.js": Object.values(output).join("\n"),
|
|
10
|
+
"messages/de.js": Object.values(output).join("\n"),
|
|
11
|
+
};
|
|
12
|
+
const mockFs = () => memfs.createFsFromVolume(new memfs.Volume()).promises;
|
|
13
|
+
const unchangedFs = mockFs();
|
|
14
|
+
const incrementalFs = mockFs();
|
|
15
|
+
let unchangedHashes;
|
|
16
|
+
let incrementalHashes;
|
|
17
|
+
let changed = false;
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
unchangedHashes = await writeOutput({
|
|
20
|
+
directory: "/output",
|
|
21
|
+
output,
|
|
22
|
+
fs: unchangedFs,
|
|
23
|
+
});
|
|
24
|
+
incrementalHashes = await writeOutput({
|
|
25
|
+
directory: "/output",
|
|
26
|
+
output,
|
|
27
|
+
fs: incrementalFs,
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
bench("write 2,000 generated message modules (including memfs setup)", async () => {
|
|
31
|
+
await writeOutput({ directory: "/output", output, fs: mockFs() });
|
|
32
|
+
});
|
|
33
|
+
bench("unchanged 2,000 generated message modules", async () => {
|
|
34
|
+
await writeOutput({
|
|
35
|
+
directory: "/output",
|
|
36
|
+
output,
|
|
37
|
+
fs: unchangedFs,
|
|
38
|
+
previousOutputHashes: unchangedHashes,
|
|
39
|
+
});
|
|
12
40
|
});
|
|
13
41
|
bench("rewrite one of 2,000 generated message modules", async () => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
42
|
+
// Seed outside the measured operation; alternate content to ensure each
|
|
43
|
+
// iteration actually rewrites one file, rather than timing a cold compile.
|
|
44
|
+
changed = !changed;
|
|
45
|
+
incrementalHashes = await writeOutput({
|
|
46
|
+
directory: "/output",
|
|
47
|
+
output: {
|
|
48
|
+
...output,
|
|
49
|
+
"messages/bundle-0.js": changed
|
|
50
|
+
? "changed"
|
|
51
|
+
: output["messages/bundle-0.js"],
|
|
52
|
+
},
|
|
53
|
+
fs: incrementalFs,
|
|
54
|
+
previousOutputHashes: incrementalHashes,
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
bench("write two aggregate locale modules (including memfs setup)", async () => {
|
|
17
58
|
await writeOutput({
|
|
18
59
|
directory: "/output",
|
|
19
|
-
output:
|
|
20
|
-
fs:
|
|
21
|
-
previousOutputHashes: hashes,
|
|
60
|
+
output: localeOutput,
|
|
61
|
+
fs: mockFs(),
|
|
22
62
|
});
|
|
23
63
|
});
|
|
24
64
|
//# sourceMappingURL=write-output.bench.js.map
|
|
@@ -1 +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;
|
|
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,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC1C,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;AACF,MAAM,YAAY,GAAG;IACpB,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;IAClD,gBAAgB,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;CAClD,CAAC;AACF,MAAM,MAAM,GAAG,GAAG,EAAE,CACnB,KAAK,CAAC,kBAAkB,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,QAA6B,CAAC;AAC5E,MAAM,WAAW,GAAG,MAAM,EAAE,CAAC;AAC7B,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC;AAC/B,IAAI,eAAuC,CAAC;AAC5C,IAAI,iBAAyC,CAAC;AAC9C,IAAI,OAAO,GAAG,KAAK,CAAC;AAEpB,SAAS,CAAC,KAAK,IAAI,EAAE;IACpB,eAAe,GAAG,MAAM,WAAW,CAAC;QACnC,SAAS,EAAE,SAAS;QACpB,MAAM;QACN,EAAE,EAAE,WAAW;KACf,CAAC,CAAC;IACH,iBAAiB,GAAG,MAAM,WAAW,CAAC;QACrC,SAAS,EAAE,SAAS;QACpB,MAAM;QACN,EAAE,EAAE,aAAa;KACjB,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,KAAK,CACJ,+DAA+D,EAC/D,KAAK,IAAI,EAAE;IACV,MAAM,WAAW,CAAC,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;AACnE,CAAC,CACD,CAAC;AAEF,KAAK,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;IAC7D,MAAM,WAAW,CAAC;QACjB,SAAS,EAAE,SAAS;QACpB,MAAM;QACN,EAAE,EAAE,WAAW;QACf,oBAAoB,EAAE,eAAe;KACrC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,KAAK,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;IAClE,wEAAwE;IACxE,2EAA2E;IAC3E,OAAO,GAAG,CAAC,OAAO,CAAC;IACnB,iBAAiB,GAAG,MAAM,WAAW,CAAC;QACrC,SAAS,EAAE,SAAS;QACpB,MAAM,EAAE;YACP,GAAG,MAAM;YACT,sBAAsB,EAAE,OAAO;gBAC9B,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,MAAM,CAAC,sBAAsB,CAAE;SAClC;QACD,EAAE,EAAE,aAAa;QACjB,oBAAoB,EAAE,iBAAiB;KACvC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,KAAK,CACJ,4DAA4D,EAC5D,KAAK,IAAI,EAAE;IACV,MAAM,WAAW,CAAC;QACjB,SAAS,EAAE,SAAS;QACpB,MAAM,EAAE,YAAY;QACpB,EAAE,EAAE,MAAM,EAAE;KACZ,CAAC,CAAC;AACJ,CAAC,CACD,CAAC","sourcesContent":["import { bench, beforeAll } 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);\nconst localeOutput = {\n\t\"messages/en.js\": Object.values(output).join(\"\\n\"),\n\t\"messages/de.js\": Object.values(output).join(\"\\n\"),\n};\nconst mockFs = () =>\n\tmemfs.createFsFromVolume(new memfs.Volume()).promises as unknown as NodeFs;\nconst unchangedFs = mockFs();\nconst incrementalFs = mockFs();\nlet unchangedHashes: Record<string, string>;\nlet incrementalHashes: Record<string, string>;\nlet changed = false;\n\nbeforeAll(async () => {\n\tunchangedHashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput,\n\t\tfs: unchangedFs,\n\t});\n\tincrementalHashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput,\n\t\tfs: incrementalFs,\n\t});\n});\n\nbench(\n\t\"write 2,000 generated message modules (including memfs setup)\",\n\tasync () => {\n\t\tawait writeOutput({ directory: \"/output\", output, fs: mockFs() });\n\t}\n);\n\nbench(\"unchanged 2,000 generated message modules\", async () => {\n\tawait writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput,\n\t\tfs: unchangedFs,\n\t\tpreviousOutputHashes: unchangedHashes,\n\t});\n});\n\nbench(\"rewrite one of 2,000 generated message modules\", async () => {\n\t// Seed outside the measured operation; alternate content to ensure each\n\t// iteration actually rewrites one file, rather than timing a cold compile.\n\tchanged = !changed;\n\tincrementalHashes = await writeOutput({\n\t\tdirectory: \"/output\",\n\t\toutput: {\n\t\t\t...output,\n\t\t\t\"messages/bundle-0.js\": changed\n\t\t\t\t? \"changed\"\n\t\t\t\t: output[\"messages/bundle-0.js\"]!,\n\t\t},\n\t\tfs: incrementalFs,\n\t\tpreviousOutputHashes: incrementalHashes,\n\t});\n});\n\nbench(\n\t\"write two aggregate locale modules (including memfs setup)\",\n\tasync () => {\n\t\tawait writeOutput({\n\t\t\tdirectory: \"/output\",\n\t\t\toutput: localeOutput,\n\t\t\tfs: mockFs(),\n\t\t});\n\t}\n);\n"]}
|
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
+
// Bound open/write/close lifetimes, not just the filesystem thread pool.
|
|
4
|
+
// A worker holds its slot until the whole operation has settled.
|
|
5
|
+
// Real-disk profiles favor 4–16 workers over wider queues; eight keeps
|
|
6
|
+
// filesystem throughput without thousands of pending promises/descriptors.
|
|
7
|
+
const FILE_SYSTEM_CONCURRENCY = 8;
|
|
8
|
+
async function forEachConcurrent(items, operation) {
|
|
9
|
+
const iterator = items[Symbol.iterator]();
|
|
10
|
+
let failed = false;
|
|
11
|
+
let firstError;
|
|
12
|
+
async function worker(item) {
|
|
13
|
+
while (!failed) {
|
|
14
|
+
try {
|
|
15
|
+
await operation(item);
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (!failed) {
|
|
19
|
+
failed = true;
|
|
20
|
+
firstError = error;
|
|
21
|
+
}
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (failed)
|
|
25
|
+
return;
|
|
26
|
+
const next = iterator.next();
|
|
27
|
+
if (next.done)
|
|
28
|
+
return;
|
|
29
|
+
item = next.value;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const workers = [];
|
|
33
|
+
for (let index = 0; index < FILE_SYSTEM_CONCURRENCY; index++) {
|
|
34
|
+
const next = iterator.next();
|
|
35
|
+
if (next.done)
|
|
36
|
+
break;
|
|
37
|
+
workers.push(worker(next.value));
|
|
38
|
+
}
|
|
39
|
+
// Drain in-flight operations before rejecting, so a retry cannot race
|
|
40
|
+
// writes left over from the failed compilation.
|
|
41
|
+
await Promise.all(workers);
|
|
42
|
+
if (failed)
|
|
43
|
+
throw firstError;
|
|
44
|
+
}
|
|
3
45
|
export async function writeOutput(args) {
|
|
4
46
|
const currentOutputHashes = await hashOutput(args.output, args.directory);
|
|
5
47
|
// if the output hasn't changed, don't write it
|
|
@@ -33,24 +75,22 @@ export async function writeOutput(args) {
|
|
|
33
75
|
if (filesToDelete.size > 0 && !args.cleanDirectory) {
|
|
34
76
|
await deleteRemovedFiles(args.fs, args.directory, filesToDelete);
|
|
35
77
|
}
|
|
78
|
+
// Cleaning removed unchanged files too, so a changed clean compile must
|
|
79
|
+
// rewrite the entire output. An entirely unchanged compile stays a no-op.
|
|
80
|
+
const filesToWrite = args.cleanDirectory
|
|
81
|
+
? Object.keys(args.output)
|
|
82
|
+
: changedFiles;
|
|
36
83
|
// Create only the directories needed by files that are about to be written.
|
|
37
84
|
// On incremental compiles, `output` can contain thousands of unchanged
|
|
38
85
|
// message modules; creating every parent directory again adds avoidable
|
|
39
86
|
// filesystem work even though only a handful of files changed.
|
|
40
87
|
const directoriesToCreate = new Set();
|
|
41
|
-
for (const filePath of
|
|
88
|
+
for (const filePath of filesToWrite) {
|
|
42
89
|
directoriesToCreate.add(path.dirname(path.resolve(args.directory, filePath)));
|
|
43
90
|
}
|
|
44
|
-
await
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
if (!changedFiles.has(filePath)) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
const fullPath = path.resolve(args.directory, filePath);
|
|
51
|
-
await args.fs.writeFile(fullPath, fileContent);
|
|
52
|
-
}));
|
|
53
|
-
//Only update the previousOutputHashes if the write was successful
|
|
91
|
+
await forEachConcurrent(directoriesToCreate, (directory) => args.fs.mkdir(directory, { recursive: true }));
|
|
92
|
+
await forEachConcurrent(filesToWrite, (filePath) => args.fs.writeFile(path.resolve(args.directory, filePath), args.output[filePath]));
|
|
93
|
+
// Publish hashes only after every required filesystem operation succeeds.
|
|
54
94
|
return currentOutputHashes;
|
|
55
95
|
}
|
|
56
96
|
/**
|
|
@@ -60,20 +100,20 @@ async function deleteRemovedFiles(fs, baseDirectory, filesToDelete) {
|
|
|
60
100
|
// Collect directories that might need cleanup
|
|
61
101
|
const potentialEmptyDirs = new Set();
|
|
62
102
|
// First pass: delete all files and collect parent directories
|
|
63
|
-
await
|
|
103
|
+
await forEachConcurrent(filesToDelete, async (filePath) => {
|
|
64
104
|
const fullPath = path.resolve(baseDirectory, filePath);
|
|
65
105
|
try {
|
|
66
106
|
await fs.unlink(fullPath);
|
|
67
|
-
// Add parent directory for potential cleanup
|
|
68
|
-
const dirPath = path.dirname(fullPath);
|
|
69
|
-
if (dirPath !== baseDirectory) {
|
|
70
|
-
potentialEmptyDirs.add(dirPath);
|
|
71
|
-
}
|
|
72
107
|
}
|
|
73
|
-
catch {
|
|
74
|
-
//
|
|
108
|
+
catch (error) {
|
|
109
|
+
// A previously removed file is already in the desired state.
|
|
110
|
+
if (error.code !== "ENOENT")
|
|
111
|
+
throw error;
|
|
75
112
|
}
|
|
76
|
-
|
|
113
|
+
const dirPath = path.dirname(fullPath);
|
|
114
|
+
if (dirPath !== baseDirectory)
|
|
115
|
+
potentialEmptyDirs.add(dirPath);
|
|
116
|
+
});
|
|
77
117
|
// Second pass: clean up empty directories, starting from deepest paths
|
|
78
118
|
const sortedDirs = Array.from(potentialEmptyDirs).sort((a, b) => b.length - a.length);
|
|
79
119
|
for (const dirPath of sortedDirs) {
|
|
@@ -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 { createHash } from \"node:crypto\";\nimport type nodeFs from \"node:fs/promises\";\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 = await 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 hashString(input: string): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nasync function hashOutput(\n\toutput: Record<string, string>,\n\toutputDirectory: string\n): Promise<Record<string, string>> {\n\tconst entries = Object.entries(output).map(([filePath, fileContent]) => {\n\t\tconst combinedContent =\n\t\t\tfileContent + path.resolve(outputDirectory, filePath);\n\t\treturn [filePath, hashString(combinedContent)] as const;\n\t});\n\treturn Object.fromEntries(entries);\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] = await hashString(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 { createHash } from \"node:crypto\";\nimport type nodeFs from \"node:fs/promises\";\n\n// Bound open/write/close lifetimes, not just the filesystem thread pool.\n// A worker holds its slot until the whole operation has settled.\n// Real-disk profiles favor 4–16 workers over wider queues; eight keeps\n// filesystem throughput without thousands of pending promises/descriptors.\nconst FILE_SYSTEM_CONCURRENCY = 8;\n\nasync function forEachConcurrent<T>(\n\titems: Iterable<T>,\n\toperation: (item: T) => Promise<unknown>\n): Promise<void> {\n\tconst iterator = items[Symbol.iterator]();\n\tlet failed = false;\n\tlet firstError: unknown;\n\n\tasync function worker(item: T) {\n\t\twhile (!failed) {\n\t\t\ttry {\n\t\t\t\tawait operation(item);\n\t\t\t} catch (error) {\n\t\t\t\tif (!failed) {\n\t\t\t\t\tfailed = true;\n\t\t\t\t\tfirstError = error;\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (failed) return;\n\t\t\tconst next = iterator.next();\n\t\t\tif (next.done) return;\n\t\t\titem = next.value;\n\t\t}\n\t}\n\n\tconst workers: Promise<void>[] = [];\n\tfor (let index = 0; index < FILE_SYSTEM_CONCURRENCY; index++) {\n\t\tconst next = iterator.next();\n\t\tif (next.done) break;\n\t\tworkers.push(worker(next.value));\n\t}\n\t// Drain in-flight operations before rejecting, so a retry cannot race\n\t// writes left over from the failed compilation.\n\tawait Promise.all(workers);\n\tif (failed) throw firstError;\n}\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 = await 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// Cleaning removed unchanged files too, so a changed clean compile must\n\t// rewrite the entire output. An entirely unchanged compile stays a no-op.\n\tconst filesToWrite = args.cleanDirectory\n\t\t? Object.keys(args.output)\n\t\t: changedFiles;\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 filesToWrite) {\n\t\tdirectoriesToCreate.add(\n\t\t\tpath.dirname(path.resolve(args.directory, filePath))\n\t\t);\n\t}\n\tawait forEachConcurrent(directoriesToCreate, (directory) =>\n\t\targs.fs.mkdir(directory, { recursive: true })\n\t);\n\n\tawait forEachConcurrent(filesToWrite, (filePath) =>\n\t\targs.fs.writeFile(\n\t\t\tpath.resolve(args.directory, filePath),\n\t\t\targs.output[filePath]!\n\t\t)\n\t);\n\n\t// Publish hashes only after every required filesystem operation succeeds.\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 forEachConcurrent(filesToDelete, async (filePath) => {\n\t\tconst fullPath = path.resolve(baseDirectory, filePath);\n\t\ttry {\n\t\t\tawait fs.unlink(fullPath);\n\t\t} catch (error) {\n\t\t\t// A previously removed file is already in the desired state.\n\t\t\tif ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n\t\t}\n\t\tconst dirPath = path.dirname(fullPath);\n\t\tif (dirPath !== baseDirectory) potentialEmptyDirs.add(dirPath);\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 hashString(input: string): string {\n\treturn createHash(\"sha256\").update(input).digest(\"hex\");\n}\n\nasync function hashOutput(\n\toutput: Record<string, string>,\n\toutputDirectory: string\n): Promise<Record<string, string>> {\n\tconst entries = Object.entries(output).map(([filePath, fileContent]) => {\n\t\tconst combinedContent =\n\t\t\tfileContent + path.resolve(outputDirectory, filePath);\n\t\treturn [filePath, hashString(combinedContent)] as const;\n\t});\n\treturn Object.fromEntries(entries);\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] = await hashString(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"]}
|
|
@@ -241,4 +241,179 @@ const mockFs = (files) => {
|
|
|
241
241
|
const _memfs = memfs.createFsFromVolume(memfs.Volume.fromJSON(files));
|
|
242
242
|
return _memfs.promises;
|
|
243
243
|
};
|
|
244
|
+
test("bounds every filesystem phase for large nested outputs", async () => {
|
|
245
|
+
const { writeOutput } = await import("./write-output.js");
|
|
246
|
+
const fs = mockFs({});
|
|
247
|
+
const output = Object.fromEntries(Array.from({ length: 500 }, (_, i) => [
|
|
248
|
+
`messages/${i}/en.js`,
|
|
249
|
+
`message ${i}`,
|
|
250
|
+
]));
|
|
251
|
+
for (const name of ["mkdir", "writeFile", "unlink"]) {
|
|
252
|
+
const original = fs[name].bind(fs);
|
|
253
|
+
let active = 0;
|
|
254
|
+
let peak = 0;
|
|
255
|
+
vi.spyOn(fs, name).mockImplementation((async (...args) => {
|
|
256
|
+
active++;
|
|
257
|
+
peak = Math.max(peak, active);
|
|
258
|
+
try {
|
|
259
|
+
// Simulate a process with room for at most 32 operations.
|
|
260
|
+
if (active > 32)
|
|
261
|
+
throw Object.assign(new Error("too many open files"), {
|
|
262
|
+
code: "EMFILE",
|
|
263
|
+
});
|
|
264
|
+
await new Promise((resolve) => setImmediate(resolve));
|
|
265
|
+
return await original(...args);
|
|
266
|
+
}
|
|
267
|
+
finally {
|
|
268
|
+
active--;
|
|
269
|
+
}
|
|
270
|
+
}));
|
|
271
|
+
const hashes = await writeOutput({ directory: "/output", output, fs });
|
|
272
|
+
await writeOutput({
|
|
273
|
+
directory: "/output",
|
|
274
|
+
output: {},
|
|
275
|
+
previousOutputHashes: hashes,
|
|
276
|
+
fs,
|
|
277
|
+
});
|
|
278
|
+
expect(peak).toBeGreaterThan(1);
|
|
279
|
+
expect(peak).toBeLessThanOrEqual(32);
|
|
280
|
+
expect(active).toBe(0);
|
|
281
|
+
vi.restoreAllMocks();
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
test.each(["mkdir", "writeFile"])("propagates %s errors and retries the incomplete output", async (name) => {
|
|
285
|
+
const { writeOutput, hashDirectory } = await import("./write-output.js");
|
|
286
|
+
const fs = mockFs({});
|
|
287
|
+
const output = {
|
|
288
|
+
"messages/en.js": "English",
|
|
289
|
+
"messages/de.js": "German",
|
|
290
|
+
"runtime.js": "runtime",
|
|
291
|
+
};
|
|
292
|
+
const error = Object.assign(new Error(`${name}: permission denied`), {
|
|
293
|
+
code: "EACCES",
|
|
294
|
+
});
|
|
295
|
+
const spy = vi.spyOn(fs, name).mockRejectedValueOnce(error);
|
|
296
|
+
await expect(writeOutput({ directory: "/output", output, fs })).rejects.toBe(error);
|
|
297
|
+
spy.mockRestore();
|
|
298
|
+
const hashes = await writeOutput({ directory: "/output", output, fs });
|
|
299
|
+
expect(await hashDirectory("/output", fs)).toEqual(hashes);
|
|
300
|
+
});
|
|
301
|
+
test("waits for active writes and stops scheduling after a failure", async () => {
|
|
302
|
+
const { writeOutput } = await import("./write-output.js");
|
|
303
|
+
const fs = mockFs({});
|
|
304
|
+
const output = Object.fromEntries(Array.from({ length: 100 }, (_, i) => [`${i}.js`, `${i}`]));
|
|
305
|
+
const error = Object.assign(new Error("disk full"), { code: "ENOSPC" });
|
|
306
|
+
let release;
|
|
307
|
+
const blocked = new Promise((resolve) => {
|
|
308
|
+
release = resolve;
|
|
309
|
+
});
|
|
310
|
+
let signalStarted;
|
|
311
|
+
const startedWriting = new Promise((resolve) => {
|
|
312
|
+
signalStarted = resolve;
|
|
313
|
+
});
|
|
314
|
+
let calls = 0;
|
|
315
|
+
let active = 0;
|
|
316
|
+
vi.spyOn(fs, "writeFile").mockImplementation((async () => {
|
|
317
|
+
calls++;
|
|
318
|
+
if (calls === 1)
|
|
319
|
+
throw error;
|
|
320
|
+
active++;
|
|
321
|
+
signalStarted();
|
|
322
|
+
await blocked;
|
|
323
|
+
active--;
|
|
324
|
+
}));
|
|
325
|
+
let settled = false;
|
|
326
|
+
const writing = writeOutput({ directory: "/output", output, fs });
|
|
327
|
+
const rejection = writing.catch((caught) => caught);
|
|
328
|
+
void writing.catch(() => {
|
|
329
|
+
settled = true;
|
|
330
|
+
});
|
|
331
|
+
await startedWriting;
|
|
332
|
+
await Promise.resolve();
|
|
333
|
+
expect(active).toBeGreaterThan(0);
|
|
334
|
+
expect(settled).toBe(false);
|
|
335
|
+
const started = calls;
|
|
336
|
+
release();
|
|
337
|
+
expect(await rejection).toBe(error);
|
|
338
|
+
expect(active).toBe(0);
|
|
339
|
+
expect(calls).toBe(started);
|
|
340
|
+
expect(calls).toBeLessThan(100);
|
|
341
|
+
});
|
|
342
|
+
test("required deletes ignore only missing files and preserve the old hashes on failure", async () => {
|
|
343
|
+
const { writeOutput } = await import("./write-output.js");
|
|
344
|
+
const fs = mockFs({});
|
|
345
|
+
const hashes = await writeOutput({
|
|
346
|
+
directory: "/output",
|
|
347
|
+
output: { "old.js": "old" },
|
|
348
|
+
fs,
|
|
349
|
+
});
|
|
350
|
+
const error = Object.assign(new Error("unlink denied"), { code: "EACCES" });
|
|
351
|
+
const spy = vi.spyOn(fs, "unlink").mockRejectedValueOnce(error);
|
|
352
|
+
await expect(writeOutput({
|
|
353
|
+
directory: "/output",
|
|
354
|
+
output: { "new.js": "new" },
|
|
355
|
+
previousOutputHashes: hashes,
|
|
356
|
+
fs,
|
|
357
|
+
})).rejects.toBe(error);
|
|
358
|
+
expect(await fs.readFile("/output/old.js", "utf8")).toBe("old");
|
|
359
|
+
spy.mockRestore();
|
|
360
|
+
await fs.unlink("/output/old.js");
|
|
361
|
+
const next = await writeOutput({
|
|
362
|
+
directory: "/output",
|
|
363
|
+
output: { "new.js": "new" },
|
|
364
|
+
previousOutputHashes: hashes,
|
|
365
|
+
fs,
|
|
366
|
+
});
|
|
367
|
+
expect(Object.keys(next)).toEqual(["new.js"]);
|
|
368
|
+
expect(await fs.readFile("/output/new.js", "utf8")).toBe("new");
|
|
369
|
+
});
|
|
370
|
+
test("clean partial updates rewrite unchanged output too", async () => {
|
|
371
|
+
const { writeOutput, hashDirectory } = await import("./write-output.js");
|
|
372
|
+
const fs = mockFs({});
|
|
373
|
+
const output = { "messages/en.js": "English", "runtime.js": "runtime" };
|
|
374
|
+
const hashes = await writeOutput({ directory: "/output", output, fs });
|
|
375
|
+
const next = await writeOutput({
|
|
376
|
+
directory: "/output",
|
|
377
|
+
output: { ...output, "messages/en.js": "changed" },
|
|
378
|
+
previousOutputHashes: hashes,
|
|
379
|
+
cleanDirectory: true,
|
|
380
|
+
fs,
|
|
381
|
+
});
|
|
382
|
+
expect(await fs.readFile("/output/runtime.js", "utf8")).toBe("runtime");
|
|
383
|
+
expect(await hashDirectory("/output", fs)).toEqual(next);
|
|
384
|
+
});
|
|
385
|
+
test("retries a partially written incremental update with the same previous hashes", async () => {
|
|
386
|
+
const { writeOutput, hashDirectory } = await import("./write-output.js");
|
|
387
|
+
const fs = mockFs({});
|
|
388
|
+
const baseline = {
|
|
389
|
+
"a.js": "old a",
|
|
390
|
+
"b.js": "old b",
|
|
391
|
+
"unchanged.js": "unchanged",
|
|
392
|
+
};
|
|
393
|
+
const previousOutputHashes = await writeOutput({
|
|
394
|
+
directory: "/output",
|
|
395
|
+
output: baseline,
|
|
396
|
+
fs,
|
|
397
|
+
});
|
|
398
|
+
const output = { ...baseline, "a.js": "new a", "b.js": "new b" };
|
|
399
|
+
const original = fs.writeFile.bind(fs);
|
|
400
|
+
const error = Object.assign(new Error("disk full"), { code: "ENOSPC" });
|
|
401
|
+
const spy = vi.spyOn(fs, "writeFile").mockImplementation((async (...args) => {
|
|
402
|
+
if (args[0] === "/output/b.js")
|
|
403
|
+
throw error;
|
|
404
|
+
return original(...args);
|
|
405
|
+
}));
|
|
406
|
+
await expect(writeOutput({ directory: "/output", output, previousOutputHashes, fs })).rejects.toBe(error);
|
|
407
|
+
expect(await fs.readFile("/output/a.js", "utf8")).toBe("new a");
|
|
408
|
+
expect(await fs.readFile("/output/b.js", "utf8")).toBe("old b");
|
|
409
|
+
spy.mockRestore();
|
|
410
|
+
const next = await writeOutput({
|
|
411
|
+
directory: "/output",
|
|
412
|
+
output,
|
|
413
|
+
previousOutputHashes,
|
|
414
|
+
fs,
|
|
415
|
+
});
|
|
416
|
+
expect(await hashDirectory("/output", fs)).toEqual(next);
|
|
417
|
+
expect(previousOutputHashes["a.js"]).not.toBe(next["a.js"]);
|
|
418
|
+
});
|
|
244
419
|
//# sourceMappingURL=write-output.test.js.map
|