lingui / js-lingui

🌍 📖 A readable, automated, and optimized (3 kb) internationalization for JavaScript
https://lingui.dev
MIT License
4.6k stars 382 forks source link

Document compatibility steps for Vite integration #975

Closed ocavue closed 3 years ago

ocavue commented 3 years ago

Describe the bug

@lingui/core doesn't provide a valid ESM package. Thus, it can't work with Vite, which is an ESM based bundler.

node_modules/@lingui/core/esm/index.js will import i18nModule from node_modules/@lingui/core/index.js. However, node_modules/@lingui/core/index.js only export module in CJS way instead of in ESM way.

To Reproduce

$ git clone https://github.com/ocavue/lingui-esm-issue.git
$ cd lingui-esm-issue
$ yarn install 
$ yarn dev 

Open http://localhost:3000 and you can see Uncaught TypeError: Cannot read property 'setupI18n' of undefined in the console.

Expected behavior

No error occurs.

Additional context

semoal commented 3 years ago

Alright, never tried Vite, could you tell me what do we need? Just shipping esm on each package is enough?

ocavue commented 3 years ago

Shipping ESM on each package should be fine. That might means you should build twice for each package: one for CJS and one for ESM.

BTW, I saw that @lingui/core provides two CJS files:cjs/core.production.min.js and cjs/core.development.js. This might be impossible to implement in ESM package since ESM doesn't allow export inside a if block.

$ cat ./node_modules/@lingui/core/index.js                               
if (process.env.NODE_ENV === "production") {
  module.exports = require("./cjs/core.production.min.js")
} else {
  module.exports = require("./cjs/core.development.js")
}
semoal commented 3 years ago

I've just started working on this and got it working sucessfully and we should ship this esm packages... but Lingui at least today won't work on Vite.

Lingui is a library heavily inspired and created above babel-plugin-macros, actually there's a similar issue for Snowpack integration, so we should decide together an alternative implementation of macros for this new bundlers like vite, esbuild, swc, snowpack... #951

I'll release the next tuesday anyways a new version with ESM shipping =)

capaj commented 3 years ago

please let's aim to support these bundlers. babel-plugin-macros are elegant, but as more and more project get developed with vite/snowpack they will be blocked from using lingui.js. We need a way to resolve this incompatibility.

ocavue commented 3 years ago

@semoal What is the progress of ESM shipping? I would be happy to help if there is something I can do.

semoal commented 3 years ago

I couldn't work what i've expected last week, so probably i'll finish the pending work tomorrow or monday for introduce it on the next release (tuesday 23) Sorry for the delay =)

aulneau commented 3 years ago

@semoal I see your work in https://github.com/lingui/js-lingui/pull/979, are you planning on adjusting all packages to have this same esm output? testing that PR, I see detect-locale needs the same updates. /core works perfectly in my vite app :)

semoal commented 3 years ago

@semoal I see your work in #979, are you planning on adjusting all packages to have this same esm output? testing that PR, I see detect-locale needs the same updates. /core works perfectly in my vite app :)

Yes, I'm going to adjust all the modules that were universal:

I tough that detect-locale was already fixed. Don't you have a esm folder inside the detect-locale package?

semoal commented 3 years ago

Released 3.6.0 with ESM for the universal packages (core, detect-locale, react).

We have some internal discussions how we'll resolve the "incompatibility" without macros dependency. We'll create another issue once we conclude with something viable with the progress and open to collaborate.

capaj commented 3 years ago

@semoal can you clarify why the macros are a problem? vite is able to run babel macros by default. just put it in .babelrc and it will do them when transpiling the code.

ocavue commented 3 years ago

@capaj can you post a document link about this vite feature?

capaj commented 3 years ago

@ocavue it's not documented, but Vite uses babel to support react-refresh. You can see it in the source-code here: https://github.com/vitejs/vite-plugin-react/blob/feec17691f09d1e19efaeae65021b37c367873d1/src/transform.ts#L22

so if you're using fast refresh plugin babel transform is run and it respects your .babelrc

if you're not using fast refresh, you can use https://github.com/itsMapleLeaf/vite-plugin-babel-macros

anyway I had a crack at it and got it working here: https://github.com/capaj/vite-lingui-poc

The only issue for vite is this line import { getConfig } from "@lingui/conf" here https://github.com/lingui/js-lingui/blob/23b06b5dfbf0db306cdfab83801898caceb5a8b0/packages/macro/src/index.ts#L2. ~~When I remove that import and just inline some kind of config there it seems to work in Vite. I do get one weird issue with the macro, but I have the same problem in webpack so it's probably not related to Vite.~~

capaj commented 3 years ago

So it seems that getConfig was some weird glitch on my side. I have updated the POC on master to use the actual @lingui/macro and it works :tada:!

The only issue is, when I try to remove this resolve alias:

{ find: '@lingui/react', replacement: './lingui/react/Trans' },

it always breaks down like this:

react.esm.js:1 Uncaught TypeError: Cannot read property 'i18n' of null
    at y (react.esm.js:1)
    at renderWithHooks (react-dom.development.js:14985)
    at mountIndeterminateComponent (react-dom.development.js:17811)
    at beginWork (react-dom.development.js:19049)
    at HTMLUnknownElement.callCallback2 (react-dom.development.js:3945)
    at Object.invokeGuardedCallbackDev (react-dom.development.js:3994)
    at invokeGuardedCallback (react-dom.development.js:4056)
    at beginWork$1 (react-dom.development.js:23964)
    at performUnitOfWork (react-dom.development.js:22776)
    at workLoopSync (react-dom.development.js:22707)

And I suspect this is down to the bundled file. So the only thing that needs to happen for lingui.js to work with vite is to fix this bundling problem.

semoal commented 3 years ago

Give it a try to the pull request i've did tonight to you @capaj, everything works fine without aliasing.

capaj commented 3 years ago

Thank you very much @semoal. I did a lot more investigations in my POC repo today and I think I have a solution: https://github.com/capaj/vite-lingui-poc

We should close this issue or rename it, because lingui.js is indeed compatible with vite. Here's a running react app bundled with Vite as proof: https://capaj.github.io/vite-lingui-poc/

Maybe we can rename this as Add vite usage example to the docs? WDYT @semoal ?

semoal commented 3 years ago

Yes, we should document everything we found.

stale[bot] commented 3 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Gorthog commented 3 years ago

Is there documentation anywhere? Does the repo you published can be used with Typescript, or there are some changes needed to .babelrc etc.?

Sorry, just noticed the published repo is written in Typescript

khaphannm commented 3 years ago

many thanks @capaj for the POC

a1ooha commented 2 years ago

I wrote a plugin for vite and more, which can compiles messages on the fly! It contains a demo for vite integration in the playground catalogs, Hope this helps.

dmitryshelomanov commented 2 years ago

@a1ooha not working for me

error

Add @babel/preset-react (https://github.com/babel/babel/tree/main/packages/babel-preset-react) to the 'presets' section of your Babel config to enable transformation.
If you want to leave it as-is, add @babel/plugin-syntax-jsx (https://github.com/babel/babel/tree/main/packages/babel-plugin-syntax-jsx) to the 'plugins' section to enable parsing.
Catalog statistics for src/i18n/locales/{locale}:

package

 "vite": "^3.1.0"

Vite config

export default defineConfig({
  plugins: [react(), LinguiLoader()],
  css: {
    preprocessorOptions: {
      less: {
        javascriptEnabled: true,
      },
    },
  },
});
dmitryshelomanov commented 2 years ago
  "extractBabelOptions": {
    "presets": [
      "@babel/preset-react"
    ]
  }

Example above solved my case