Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add babel macros as a replacement for transform plugins #193

Merged
merged 18 commits into from
Jul 26, 2018

Conversation

mlabrum
Copy link
Contributor

@mlabrum mlabrum commented Mar 18, 2018

Hey,

I've written an babel-plugin-macros implementation of @lingui/babel-plugin-transform-react based on this issue #86 I'm opening this PR for early feedback to ensure this is the direction that you wanted to go in :)

I've taken a few shortcuts as I figured that we would want to support both the macros and babel-plugin-transform-react implementations, so macros currently calls the transformer within babel-plugin-transform-react so there's only one implementation of the transformer.

Example from one of the test files....

import { Trans } from '@lingui/react-macro';

<span>Without translation</span>;
<Trans>Hello World</Trans>;
<Trans id="Hello World">Hello World</Trans>;
<Trans id="msg.hello">Hello World</Trans>;
<Trans id={msg} />;

to

import { Trans } from "@lingui/react";
<span>Without translation</span>;
<Trans id="Hello World" />;
<Trans id="Hello World" />;
<Trans id="msg.hello" defaults="Hello World" />;
<Trans id={msg} />;

TODO:

  • Refactor babel-plugin-transform-react so the transformer doesn't depend on Import Declarations so I don't need setImportDeclarations method... which breaks the babel plugin (and fails the tests :))
  • Add @lingui/react-macro to babel-plugin-lingui-extract-messages package detection
  • Change babel-plugin-transform-react to use ES6 modules again ( I changed this when I had some errors in macro, I should be able to revert this change as @lingui has a buildstep to compile this out )
  • Add react-macro to build bundles
  • Implement macro for babel-plugin-transform-js
  • Figure out how to ensure intellisense works https://github.com/lingui/js-lingui/pull/193/files#diff-13b65913d74e56772a41fd137d19a6a6R66 maybe a peer dependency on @lingui-react and copy the proptypes over

Cheers,

@tricoder42
Copy link
Contributor

This is amazing! I'm going to review it asap

@mlabrum
Copy link
Contributor Author

mlabrum commented Apr 17, 2018 via email

@mlabrum
Copy link
Contributor Author

mlabrum commented May 3, 2018

@tricoder42 any comments?

@tricoder42
Copy link
Contributor

Hey @mlabrum, I can't believe I left this PR without attention for so long. This is awesome! Are you still interested in writing js macro?

I'm thinking that this method should be the default one. After all, there's a lot of confusion how i18n components and methods work and it would be easier to comprehend if all these helpers were macros

  • it would send a clear message that macros are optional and it's possible to use jsLingui without them (although with disadvantage of writing messages by hand)
  • it would be more clear what happens under the hood (e.g. everything wrapped in macro is processed and replaced at compile time).

Let me know if I can help with anything and please accept my apology for such a late response

@tricoder42 tricoder42 changed the base branch from master to stable-2.x July 24, 2018 22:23
@tricoder42
Copy link
Contributor

@mlabrum I merged stable-2.x branch, resolved conflicts and fixed tests. I also renamed package to @lingui/react.macro and now I'm going to copy example project and migrate it to macros. I have a feeling that all I have to do is rename imports...

This is pretty good, thank you again!

I would like to release it soon and we can work on js.macro later. We have to figure out how to pass global i18n object to macros js.macro. In react.macro we simply transform macro -> component, like Trans -> Trans or Plural -> Trans, but in js.macro we need t -> i18n.t or plural -> i18n.plural 🤔 But that's another story, this macro is sweat and ready for release.

@codecov
Copy link

codecov bot commented Jul 25, 2018

Codecov Report

Merging #193 into stable-2.x will increase coverage by 0.1%.
The diff coverage is 98.37%.

Impacted file tree graph

@@              Coverage Diff              @@
##           stable-2.x     #193     +/-   ##
=============================================
+ Coverage       91.39%   91.49%   +0.1%     
=============================================
  Files              41       49      +8     
  Lines             953     1023     +70     
=============================================
+ Hits              871      936     +65     
- Misses             82       87      +5
Impacted Files Coverage Δ
packages/babel-plugin-transform-react/src/index.js 100% <100%> (ø) ⬆️
...es/babel-plugin-transform-react/src/transformer.js 100% <100%> (ø)
packages/react.macro/index.js 100% <100%> (ø)
...ackages/babel-plugin-extract-messages/src/index.js 96.93% <100%> (ø) ⬆️
packages/babel-plugin-transform-react/index.js 100% <100%> (ø)
...ckages/babel-plugin-transform-react/transformer.js 100% <100%> (ø)
packages/babel-plugin-transform-js/src/index.js 100% <100%> (+0.75%) ⬆️
packages/js.macro/index.js 100% <100%> (ø)
packages/react.macro/src/index.js 85.71% <85.71%> (ø)
packages/js.macro/src/index.js 93.75% <93.75%> (ø)
... and 9 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1be97cb...f3c5870. Read the comment docs.

@tricoder42
Copy link
Contributor

React macros looks solid. There's an example with integration tests and everything works fine.

Thinking about js.macro, the main caveat is i18n object. We basically need a macro t which is transformed into i18n._, where i18n is an object in scope.

Option 1: i18n in scope (hackish)

If we simply assume, there's an i18n object in scope and let user to manager it, then it's easy but linters will complain about unused variable:

const i18n = setupI18n()

t`Hello ${name}`

// becomes
// i18n._("Hello {name}", { name })

Option 2: Macro wrapped in i18n._ (ugly)

Another option is more explicit - pass output of macro to i18n._ call:

const i18n = setupI18n()

i18n._(t`Hello ${name}`)

// should become
// i18n._("Hello {name}", { name })

First, I'm not sure if it's even possible. We're taking a single AST node and returning a list. It may also be a bit confusing, but after all, this is exactly what js.macro is doing: transforming template literals into string (and also variables).

Option 3: Object member as a macro? (ideal)

This would be ideal, but don't think it's possible

import { t } from '@lingui/js.macro` // linter still complains here

const i18n = setupI18n()

// t is a macro here
i18n.t`Hello ${name}`

// becomes
// i18n._("Hello {name}", { name })

but still we need to cover another usecase - custom message ID:

i18n('msg.id')`Hello ${name}`

but that could be e.g.:

i18n.id('msg.id')`Hello ${name}`

where id is macro.

@tricoder42
Copy link
Contributor

js.macro is ready. I chose the first option and macro is checking if i18n is in scope. Otherwise it warns user with explanation. It's not ideal for linter, but it's definitely better than other options (like i18n(t'...') or i18n._(t'...')

Final step is to add "dummy" components/methods with proptypes/flowtypes so the auto-completition works correctly even for macros.

@tricoder42 tricoder42 changed the title Feature/macro Add babel macros as a replacement for transform plugins Jul 26, 2018
@tricoder42 tricoder42 merged commit c95a291 into lingui:stable-2.x Jul 26, 2018
@tricoder42
Copy link
Contributor

Alright, merging now and I'll fix flow types later. I need to fix #250 first anyway…

Thank you @mlabrum for working on this! You really did an awesome job here!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants