kaisermann / svelte-i18n

Internationalization library for Svelte
MIT License
1.24k stars 79 forks source link

CJS / ESM conflict #230

Closed kaisermann closed 11 months ago

kaisermann commented 1 year ago

From https://github.com/kaisermann/svelte-i18n/issues/209#issuecomment-1704354362


What I observed appears to have a different source. Somehow the modules are resolved the wrong way.

My application's import resolves to node_modules/svelte-i18n/dist/runtime.cjs.js which has:

var IntlMessageFormat = require('intl-messageformat');
....
IntlMessageFormat.resolveLocale
...
new IntlMessageFormat(message, ...)

But the require('intl-messageformat') is resolved in such a way, that IntlMessageFormat is an object with a default property which contains the actual class, so the new IntlMessageFormat/IntlMessageFormat.resolveLocale errors. There probably is some mixing of CJS and ESM going on (this is always such a pain).

Unfortunately, I have not been able to create a minimal reproduction so far. This should probably be tracked in a separate issue.


The logged errors could maybe be more generic, as these are lies:

[svelte-i18n] The initial locale "de" is not a valid locale. [svelte-i18n] Message "..." has syntax error: IntlMessageFormat is not a constructor

Originally posted by @brunnerh in https://github.com/kaisermann/svelte-i18n/issues/209#issuecomment-1704354362

kaisermann commented 1 year ago

@brunnerh I don't have time right now to try to reproduce this, but I'm aware of this type of cjs/esm import conflict. Thankfully, intl-messageformat also exports the class as a named export. I just released version 3.7.4 which replaces the default import for the named ones. Can you tell me if it fixes the issue for you?

softwarecurator commented 1 year ago

3.7.4 still has same issue

brunnerh commented 1 year ago

@kaisermann Works for me now.

kaisermann commented 1 year ago

@softwarecurator can you share a minimal repro or, if not, more details about your environment?

sebastianrothe commented 1 year ago

works with 3.7.3 for me, but not with 3.7.4

Pierstoval commented 1 year ago

@kaisermann You can take this repo at this commit: https://github.com/Pierstoval/aerg/commit/3f668c22b7d665636b1ea8b2e10778d7e2c67de0 , just do "yarn install" then "yarn build" to build the app, and you'll see the error:

✓ built in 3.26s
file:///C:/dev/aergewin/.svelte-kit/output/server/chunks/runtime.esm.js:3
import { IntlMessageFormat } from "intl-messageformat";
         ^^^^^^^^^^^^^^^^^
SyntaxError: Named export 'IntlMessageFormat' not found. The requested module 'intl-messageformat' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'intl-messageformat';
const { IntlMessageFormat } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:190:5)

node:internal/event_target:1016
  process.nextTick(() => { throw err; });
                           ^
Error: 500 /
To suppress or handle this error, implement `handleHttpError` in https://kit.svelte.dev/docs/configuration#prerender
    at file:///C:/dev/aergewin/node_modules/@sveltejs/kit/src/core/config/options.js:212:13
    at file:///C:/dev/aergewin/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js:64:25
    at save (file:///C:/dev/aergewin/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js:403:4)
    at visit (file:///C:/dev/aergewin/node_modules/@sveltejs/kit/src/core/postbuild/prerender.js:236:3)
Emitted 'error' event on Worker instance at:
    at [kOnErrorMessage] (node:internal/worker:300:10)
    at [kOnMessage] (node:internal/worker:311:37)
    at MessagePort.<anonymous> (node:internal/worker:212:57)
    at [nodejs.internal.kHybridDispatch] (node:internal/event_target:741:20)
    at exports.emitMessage (node:internal/per_context/messageport:23:28)

Node.js v18.17.1
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
brunnerh commented 1 year ago

@Pierstoval There appears to be something off with your module resolution (and that is not a minimal repro).

The loaded file is the ES module (runtime.esm.js) of this package, yet it claims that "The requested module 'intl-messageformat' is a CommonJS module" even though both svelte-i18n and intl-messageformat provide CJS and ESM formats.

I tried to install and use v3.7.4 in my local SvelteKit test project and it works just fine.

Pierstoval commented 1 year ago

I must happen to have certain dependencies of the project that make this not work properly. Maybe it's how locale is set, I don't know, I will try to investigate further

softwarecurator commented 1 year ago

same not sure what is causing it, will stay on lower version for now though

Pierstoval commented 1 year ago

Apparently it's caused when building with the static adapter.

I created a super slight reproducer: https://github.com/pierstoval/svelte-i18n-reproducer

What's been done in this:

To reproduce the bug, install the project via yarn install, and then run yarn build && yarn preview, if you open the page in your browser, you'll see the same error that I posted above:

$ vite preview

  ➜  Local:   http://localhost:4173/
  ➜  Network: use --host to expose
  ➜  press h to show help
file:///C:/dev/aergewin/myapp/.svelte-kit/output/server/entries/pages/_page.svelte.js:4
import { IntlMessageFormat } from "intl-messageformat";
         ^^^^^^^^^^^^^^^^^
SyntaxError: Named export 'IntlMessageFormat' not found. The requested module 'intl-messageformat' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:

import pkg from 'intl-messageformat';
const { IntlMessageFormat } = pkg;

    at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:190:5)
sebastianrothe commented 1 year ago

I am using static-adapter as well.

softwarecurator commented 1 year ago

i'm using vercel-adapter and getting it

softwarecurator commented 1 year ago

this is a breaking change for 3.7.4 would love to have this fixed and or make it not the mandatory wanted version

brunnerh commented 1 year ago

It's been breaking either way; for me since 3.4.0. Though my issue might be less common since I am not using SvelteKit which is rather popular and a first-party framework.

You should be able to make Vite's SSR work by adding this to the vite.config.js:

export default defineConfig({
    // ...
    ssr: {
        noExternal: [
            'intl-messageformat',
            '@formatjs/icu-messageformat-parser',
            '@formatjs/icu-skeleton-parser',
        ],
    },
});

Context:

Dependencies are "externalized" from Vite's SSR transform module system by default when running SSR. This speeds up both dev and build.

If a dependency needs to be transformed by Vite's pipeline, for example, because Vite features are used untranspiled in them, they can be added to ssr.noExternal.

There are also options related to optimization that could be relevant; there is a note regarding ESM modules referencing CJS modules. (Tried a few things there, but it did not seem to matter.)

Rather annoying that this doesn't "just work". As noted, both packages in theory support both module types. I'd say the issue may be on the side of the build tooling here.


@oyvindo found another fix/workaround (#232):

I fixed the problem by installing intl-messageformat in my project ^10.5.1

Tested that and it seems to work, the reason is beyond me...

Steffen8608 commented 1 year ago

Maybe a dumb idea but could it be that it's a wrong version of intl-messageformat set in package.json? If i install intl-messageformat locally it installs version 10.15.x and everything works. The package.json of svelte-i18n has 9.x.x as a dependency and the install leads to this error. I mean:


 import { IntlMessageFormat } from "intl-messageformat"; 
 SyntaxError: Named export 'IntlMessageFormat' not found. The requested module 'intl-messageformat' is a CommonJS module, which may not support all module.exports as named exports. 
 CommonJS modules can always be imported via the default export, for example using:)
brunnerh commented 1 year ago

I spent hours looking at different versions of the package and the behavior made no sense to me at all.

After seeing in #232 that installing the latest version fixed this, I went through the various 10.x.x versions to see what change might have caused the fix and it started to work at v.10.1.2 which is a completely broken release that only contains the raw source. Later versions like v.10.1.3 appear to work as well, meanwhile the changelog is useless for those versions:

Changelog Snippet > # [10.3.0](https://github.com/formatjs/formatjs/compare/intl-messageformat@10.2.6...intl-messageformat@10.3.0) (2023-01-30) > > ### Features > > * **intl-messageformat:** support more parse options in constructor ([e6b43dc](https://github.com/formatjs/formatjs/commit/e6b43dcc244c53dbfae2a877e1e07546678741db)) > > ## [10.2.6](https://github.com/formatjs/formatjs/compare/intl-messageformat@10.2.5...intl-messageformat@10.2.6) (2023-01-26) > > **Note:** Version bump only for package intl-messageformat > > ## [10.2.5](https://github.com/formatjs/formatjs/compare/intl-messageformat@10.2.4...intl-messageformat@10.2.5) (2022-12-02) > > **Note:** Version bump only for package intl-messageformat > > ## [10.2.4](https://github.com/formatjs/formatjs/compare/intl-messageformat@10.2.2...intl-messageformat@10.2.4) (2022-12-01) > > **Note:** Version bump only for package intl-messageformat > > ## [10.2.3](https://github.com/formatjs/formatjs/compare/intl-messageformat@10.2.2...intl-messageformat@10.2.3) (2022-12-01) > > **Note:** Version bump only for package intl-messageformat > > ## [10.2.2](https://github.com/formatjs/formatjs/compare/intl-messageformat@10.2.1...intl-messageformat@10.2.2) (2022-11-29) > > **Note:** Version bump only for package intl-messageformat > > ## [10.2.1](https://github.com/formatjs/formatjs/compare/intl-messageformat@10.2.0...intl-messageformat@10.2.1) (2022-10-17) > > **Note:** Version bump only for package intl-messageformat > > # [10.2.0](https://github.com/formatjs/formatjs/compare/intl-messageformat@10.1.5...intl-messageformat@10.2.0) (2022-10-13) > > ### Features > > * **@formatjs/intl,react-intl:** move IntlFormatter type parameters to methods ([#3858](https://github.com/formatjs/formatjs/issues/3858)) ([0d03bb6](https://github.com/formatjs/formatjs/commit/0d03bb66123cb49fbd1c7d27908979bc4521b41f))

Also, a locally installed v.10 should not even be used as far as I understand it, as the version specified by svelte-i18n is ^9.13.0 which is not compatible with a major version change to version v.10.

Pierstoval commented 1 year ago

Also, a locally installed v.10 should not even be used as far as I understand it, as the version specified by svelte-i18n is ^9.13.0 which is not compatible with a major version change to version v.10.

Maybe there's some kind of problem in module resolution that makes a local install of v10 supersede svelte-i18n's dependency? Could that even be possible?

benmccann commented 11 months ago

I recommend using publint to diagnose issues like these: https://publint.dev/svelte-i18n@3.7.4

Also, given that this is a Svelte-specific library, there's really no reason to have a CJS build. The best solution would be to drop the CJS version which would simplify things a fair amount

brunnerh commented 11 months ago

Also, given that this is a Svelte-specific library, there's really no reason to have a CJS build.

Except that you might want to use the library on a Node server, so you don't have to use another library to do the same thing you already do on the client. Which is exactly what I do.

benmccann commented 11 months ago

Except that you might want to use the library on a Node server, so you don't have to use another library to do the same thing you already do on the client. Which is exactly what I do.

ESM runs on the server as well. All SvelteKit projects have been ESM-only since day 1. Svelte 4 only outputs ESM. Vite is dropping CJS support for the server in Vite 5 to be released shortly and 70% of Svelte users use Vite. CJS support in Svelte is basically dead at this point. If you still need it for some reason you can always import the library as ESM and then bundle your project to CJS.

tipy01 commented 11 months ago

I made some change in PR #220 so it could solve this issue.

kaisermann commented 11 months ago

I've released 3.7.5-alpha.1 with the updated intl-messageformat version. I'm gonna do a major bump soon with the appropriate pkg.exports field in the package.json, but just want to guarantee that the current major will be working in its final version.

I've tested it with the repo made by @Pierstoval and the build is passing and the preview working