Code Monkey home page Code Monkey logo

Comments (116)

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024 6

@FredyC https://github.com/xiaoxiangmoe/typescript-macros/

Babel-plugin-macros use mutable API, this does not seem to be compatible with TypeScript transformer, so I chose to use immutable API.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024 2

I'm definitely in favor of supporting macros in TypeScript, but I'm totally the wrong person to work on that because I don't use TypeScript at all. I don't know whether it'd be better to have TS support in this same project or if it should be a different project.

I think it would be cool if macros authors could publish a single package that supports both though. Here's what I think we should do:

  1. Explore supporting macros using TypeScript's plugin system
  2. Implement it
  3. Implement an existing babel macros with the TypeScript one
  4. Report back on what was found.

I think this would be great. The lack of custom transforms is one of the biggest reasons I don't want to use TypeScript (it may be the only remaining reason). If we can get macros to work then I'll probably have no more reason to resist :)

from babel-plugin-macros.

longlho avatar longlho commented on July 30, 2024 2

@FredyC https://github.com/Microsoft/TypeScript/wiki/API-Breaking-Changes should give u an idea on how often the Compiler API changes. Smaller breaking changes like type union aren't documented. E.g: They change transformer return type from TransformerFactory<ts.SourceFile> to TransformerFactory<ts.SourceFile | ts.Bundle> during some commits.

Source map includes line #s & column #s and will have to be mapped differently when u modify the AST. The complexity comes in when you pull in external resources (like CSS for example), where, ideally you've have to bridge to the CSS source map as well. Source map also doesn't work perfectly in a transformation chain.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024 2

@FredyC uppercase is the first macro I wrote for testing macros-engine, so it is simple and stupid.

now I pusblish it in https://www.npmjs.com/package/typescript-macros

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024 2

It will work. I'll resolve it. Waiting for my demo in weekends😆
This will be compatible with any version of babel-plugin-macros

from babel-plugin-macros.

sibelius avatar sibelius commented on July 30, 2024 1

As Typescript is supported by Babel 7, can’t we just use Babel to keep this plugin compatible with pure js, flow and TS?

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024 1

@vhfmag

That said, as close as their ASTs may be, a hybrid Babel/Typescript macro plugin would have to implement a unified API for them, which could greatly improve complexity and maintenance burden (but that should be further studied before arriving at conclusions).

I agree. This is more like a wet dream than something that can become reality. I would definitely like to put the main burden on macro maintainers so they can decide if it's worth for them to support TypeScript or Babel or both. There should be some common API and helpers, but AST differences would always mean double work. Ultimately, it means less burden for users of such universal macros and that's the most important.

As for @sibelius suggestion, I think that's at the very least a viable option for the near future, especially because babel plugins and macros do have type information and can use it to generate code.

Please, let's not discuss this approach anymore here. If someone likes it, they can use it just fine. I don't like it and I've explained my reasons in a first comment. No need to further come up with theories in this matter.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024 1

I would definitely like to put the main burden on macro maintainers so they can decide if it's worth for them to support TypeScript or Babel or both. There should be some common API and helpers, but AST differences would always mean double work. Ultimately, it means less burden for users of such universal macros and that's the most important.

I agree with this. The nice thing about babel-plugin-macros is that often the interaction with the actual AST can actually be pretty minimal, so I see a great opportunity for code reuse here. And even if there weren't much opportunity for code reuse, just having the capability for TS would be a great win.


Anyway, we're not here to justify the existence or usefulness of this style of macros with TypeScript, just how to make it work.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024 1

That's awesome @xiaoxiangmoe! Thanks for working on that!

@FredyC, could you please make a PR to the README of this project linking to typescript-macros? Then we can close this issue and direct development on typescript-macros :)

Actually @xiaoxiangmoe, I wonder whether there's a way we could make it so people could support both macros in a single package. Something like:

module.exports = function({type, ...rest}) {
  if (type === 'typescript') {
    return require('./typescript-version')
  } else {
    return require('./babel-version')
  }
}

That way code that's using a given macros could be used with either typescript-macros or babel-plugin-macros. What do you think about making that work?

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024 1

@kentcdodds Sure, I will make a PR soon-ish.

Regarding the support for both packages, I am wondering if it can be done in some better way. Instead of condition that would have to be copied over to every package. I would like something along these lines.

import { defineTsMacro } from 'typescript-macros'
import { defineJsMacro } from 'babel-plugin-macros'

export default [
  defineTsMacro(...)
  defineJsMacro(...)
]

export default defineJsMacro() // when doing only one side

I wouldn't worry about inlining require since it's done in a build step and either of those packages would be included in the result.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024 1

This is why I was kinda suggesting that approach above, to unify it.

export default [
  defineTsMacro(fn, InputType)
  defineJsMacro(fn)
]

Underneath these functions could make an object like

export default [
  { type: 'typescript', execute: fn, inputType },
  { type: 'javascript', execute: fn },
]

Both babel-plugin-macros and typescript-macros would see that array is returned, simply filter by type they are interested in and call the execute function which would be the main entry point for macro, in the case of Typescript it would also take care of typing with other arguments. I don't know, I find this a bit cleaner instead of using some long and cryptic export names :)

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024 1

That would basically mean that the end user will also have to do macro.babel() in the code?

No, not at all. The changes we're discussing would change nothing with how the macro is used. This would be an opt-in for macro authors to support both tools.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024 1

okay, I think we should make this name more long with special chars leading(So we can avoid misuse)

maybe some old ide will support

import {babel} from 'foo.macro'

or I really want to use babel or typescript in my own use.

so I think we should use a very long special export name in typescript macros.

Sorry, these are the troubles introduced after supporting typescript. I know it is ugly for macros authors, but it will benifit macros users

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024 1

Maybe I need to clarify my future plans here.

In the future I may provide some tools to generate macros

such as

// file util/aaa.ts (normal file name, not end with '.macros')

import someMacroTool from './utils/someMacroTool.ts'

/**
 * export macros function
 * 
 * @param input  the input 
 * @returns the output
 * @macro
 */
export function someMacroFunction(input: string){
  return someMacroTool`
    write macros by using sweetjs or other tools
  `
}

// export normal function
export const a = ()=>1+1;

// export normal value
export const b = 114514

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024 1

I am sorry, @xiaoxiangmoe, it's kinda hard to follow your thoughts on this. I don't want to offend you or anything, but your weaker English is not helping it either :)

Um, can you like remove uppercase from the interop package to make it's more clear that it's the universal package? Let's un-prototype it a more to get a full picture, please.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@kentcdodds I am we aware that you are more of the FlowType guy and I respect that. I just felt this is a good place as any to open the discussion :)

I like your proposed route. Would love to get some people on board here who have a more insight into how is that working. There are very few of those typescript transforms and it's a kinda uncertain area.

Apparently, there is also a long standing discussion on simplifying the use of transforms. As of now, it's not as simple as adding them in config. It has to go through API layer. The ts-loader for a Webpack allows that and probably some other tools as well, but it's far from ideal.

Also, I found this interesting extension to VSCode. Just from that image it looks like that TypeScript AST is not that much different, but I am no expert for sure.

TypeScript AST Explorer

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

Interesting!

Well, as I said, I wont be working on this, but I'd love it if someone were to do it :)

Considering config is so complicated, that makes the case for macros even more compelling. Because people don't have to touch config more than it takes to add the macros plugin, then they can use all the macros they want without messing with config.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@kentcdodds Feel free to point some people in here. I certainly do not have such a good network like you and not sure who to ask for help.

Perhaps consider RT: https://twitter.com/danielk_cz/status/1071761665631367168 (or make your own)

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@sibelius Please read the last paragraph in initial comment...

One might argue that since there is TypeScript support in Babel now, it's a clear path and no need to bother with this. I would agree in case that support would be building on a real TypeScript compiler. Since it's completely separate implementation, it might introduce unexpected errors and incompatibilities. VSCode checking can tell A and then Babel will transform it into B. Current lack of some features is an example of that and more might come. Personally, I think it's wrong path, but that's not the point of this discussion. Perhaps I am mistaken on this I would love to be proven otherwise.

from babel-plugin-macros.

vhfmag avatar vhfmag commented on July 30, 2024

I have been rooting for macros in Typescript for quite a long time and that feature request has been discussed for at least 3 years (since 2015 - microsoft/TypeScript#4892 (comment)), so I would love to see this being worked on! That said, as close as their ASTs may be, a hybrid Babel/Typescript macro plugin would have to implement a unified API for them, which could greatly improve complexity and maintenance burden (but that should be further studied before arriving at conclusions).

As for @sibelius suggestion, I think that's at the very least a viable option for the near future, especially because babel plugins and macros do have type information and can use it to generate code. It's been something I've had in my mind since I've first heard about Babel Typescript support, so I decided to finally implement a proof of concept.

With tsguard.macro, I'm trying to implement type guards generated at compile time (it's my first ever babel plugin or macro, so be careful down there). I did it with babel-plugin-macros and it works just fine for that use case. There are some disclaimers about this approach, though:

  • Although it is possible to modify type information in the AST, that isn't used for type checking, since it is done by typescript (that is the most important use case for direct typescript macros).
  • Advanced typescript features, like conditional and inferred types, require either reimplementing their logic or converting Babel's AST to Typescript's AST, then work with it in Typescript and finally convert it back (which kind of nullifies my point in the first paragrah)

Even with its limitations, the Babel Typescript macro approach can enable several use cases:

  • Generating React's propTypes from Typescript types 🎉
  • Otherwise generating values from types, like ts-transformer-keys (which generates an array with all of an interface's keys) or ts-transformer-enumerate (which generates an array with all possible values of a union type of string literals), etc.

from babel-plugin-macros.

longlho avatar longlho commented on July 30, 2024

What are you trying to achieve specifically w/ macro support in TS? The transformer API already does what you're looking for.

  • If you want things like scope in the example it's already doable, given that you provide the correct type definition
  • If you want custom syntax like CSS in JS or graphql it's also already doable.
  • If you want to tap into type checker it's not doable right now, for good reason, cause it's super complex and will likely slow down compilation.

If you're talking about cross-tooling problem, it's kinda moot bc it'll always be a problem, unless all tool developers synchronize on their development initiatives & API. Say there's a working solution now, will in work w/ TS@4, webpack@18 & React@20 or whatever.

Some build systems aren't even compatible at all, e.g: PostCSS plugins are async (can be sync but few support it), while TS transformers/jest custom plugins are sync as it modifies AST synchronously. Not to mention things like generating proper sourcemaps.

A lot of production build systems also don't go directly from source to bundled assets (at least at places where I've work at). It's typically source code from a bunch of libs -> ES6 -> published to internal npm -> app code consumes -> webpack -> bundled assets so ES6 is basically the cross-platform binary. The build problem basically breaks down to lib -> ES6, then ES6 -> chunking, which is a lot more sane and is kinda the only solution I've seen that scales.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

@vhfmag

that feature request has been discussed for at least 3 years

Just to be clear for everyone who's new to babel-plugin-macros... When we say "macros" here, we're not talking about typical macros that teaches the complier new syntax tricks. These macros aren't adding syntax to the language, they're simply providing an easier way for manipulating the AST that the compiler creates.

With that in mind, @longlho, when you say "The transformer API already does what you're looking for" that's great news! I'm not proposing that we change anything in TypeScript to support this, we just need to use what's already provided by TypeScript to allow for the kinds of macros people build with babel-plugin-macros.

We're not really talking about a cross-tooling problem yet. Right now we're just trying to see whether it's feasible to have a tool like babel-plugin-macros for TypeScript. From the look of things, the biggest road block is:

Apparently, there is also a long standing discussion on simplifying the use of transforms. As of now, it's not as simple as adding them in config. It has to go through API layer. The ts-loader for a Webpack allows that and probably some other tools as well, but it's far from ideal.

from babel-plugin-macros.

longlho avatar longlho commented on July 30, 2024

@kentcdodds It's already doable to have macros in TS. I think the problem that @FredyC is talking about is:

  1. You can't inject transformer in TS compiler via tsconfig. You have to write a wrapper on top of the Compiler API yourselves.
  2. babel & TS AST aren't compatible.
  3. TS Transformers can't tap into typechecker so while you can introduce custom syntax during transpiling process, it won't compile.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@longlho

What are you trying to achieve specifically w/ macro support in TS? The transformer API already does what you're looking for.

I would quote Goal from initial comment

To prepare a TypeScript plugin/transform that would work in a similar fashion to babel-plugin-macros allowing user code driven transformation instead of relying on bunch of hard-to-configure plugins. Ultimately, if we can achieve some level of compatibility between these projects and bridge the gap, it make life easier for everyone.

I assume you haven't been using/writing macros yet. The major difference is that instead of having bunch of separate transform that you need to configure (which is painful) you would have just a single transform and everything else is driven from user code. You just install/write macro and use it directly in the code without any further configuration.

Some build systems aren't even compatible at all, e.g: PostCSS plugins are async (can be sync but few support it), while TS transformers/jest custom plugins are sync as it modifies AST synchronously. Not to mention things like generating proper sourcemaps.

That's a bit troubling for sure. I assume it would require to make synchronous version of such plugins. I really don't expect to have a plug & play of currently existing plugins 😄

A lot of production build systems also don't go directly from source to bundled assets (at least at places where I've work at).

I am not sure if I understand how is it related to discussion. In the end macros are just another transformation plugin and it's up to you where in your pipeline it will be.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@kentcdodds Thank you for clearing that out, much appreciated.

From the look of things, the biggest road block is

It may not be such a big problem after all. There is already ttypescript project which shows how it can be done. End users could either use that or I can imagine making something similar that would just insert macros transform and nothing else to ease on a configuration. Point being it's actually solved problem and we don't need to focus on that right now.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@longlho

3. TS Transformers can't tap into typechecker so while you can introduce custom syntax during transpiling process, it won't compile.

I don't think we need that. Let's leave type checking to Typescript alone. Macros should be really only about transformation of a syntactically valid code into something else, also syntactically valid. For example in case of Lingui project it's capable of transforming <Trans>text</Trans> into <Trans id="text" /> to basically unify stuff. These small bits and pieces is what's this should be about. We are not going to transform whole file to something completely different :)

from babel-plugin-macros.

longlho avatar longlho commented on July 30, 2024

Actually I've written a bunch of ts transformers (like https://github.com/longlho/ts-transform-css-modules) so I do feel the pains you're talking about. TS compiler isn't designed in the same way as babel & its compiler API can be backwards incompatible during minor version (or even patch version). It's designed to be monolithic, transformer API was a huge deal, primarily driven by Angular bc they use TS in their AOT compiler.

You have to tap into typechecker for some macros especially when you want to maintain type safety in your macro. E.g: The <Trans/> component, when being replaced during compilation, will have to produce something of the same type. At the same time, it'll also have to modify the emitted type declaration files to adjust to the new type as well so it's not as simple as just changing AST syntax.

I added transformer support to ts-loader (TypeStrong/ts-loader#535) and ttypescript is very similar. The majority of the code is wrapping around the CLI and it's quite a hassle to upgrade everytime TS compiler changes.

But anw, if babel-plugin-macros wants to support TS, looks like it'll just have to bridge the AST API. The bulk of the work would be on macro authors to make sure typecheck works.

Oh, and accurate source map is a whole other can of worms....

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

Thanks for the input @longlho!

So I think we're back to this:

I think it would be cool if macros authors could publish a single package that supports both though. Here's what I think we should do:

  1. Explore supporting macros using TypeScript's plugin system
  2. Implement it
  3. Implement an existing babel macros with the TypeScript one
  4. Report back on what was found.

Once we've done that we can see whether including this within babel-plugin-macros makes sense or if it'd be better to just be a separate project (I think it'd probably be better to be separate).

from babel-plugin-macros.

longlho avatar longlho commented on July 30, 2024

Yeah I agree. If we abandon the whole using Babel AST to write macros & just use TS AST to write them, it's completely doable. The work would just be recreate the babel-plugin-macros interface in TS.

The scope example in this repo is very easy (we already did stuff like that like https://github.com/longlho/ts-transform-react-intl/blob/master/src/macro.ts) where _('asd') tags it for translation.

Even https://github.com/kentcdodds/babel-plugin-preval is also fairly straightforward. As long as the macros don't involve complicated types, they're all doable. The value really comes in though when you can change types. That's way more powerful.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

You have to tap into typechecker for some macros especially when you want to maintain type safety in your macro. E.g: The <Trans/> component, when being replaced during compilation, will have to produce something of the same type. At the same time, it'll also have to modify the emitted type declaration files to adjust to the new type as well so it's not as simple as just changing AST syntax.

Ah, haven't thought that about, you are completely right. It depends of complexity of concrete plugin, but key point here is that the resulting transformation is not some "in the wind" code. It's perfectly working code that user can import instead of macro one and use that. Macro would just need to switch that import and it would work just fine. It's definitely extra layer of concern, but nothing too huge imo.

The majority of the code is wrapping around the CLI and it's quite a hassle to upgrade everytime TS compiler changes.

Is API of the compiler really changing that much often? I wasn't really following that. It would be definitely better if TS would support plugins out of the box, but until then I guess that hassle needs to be accepted.

Oh, and accurate source map is a whole other can of worms....

Is it? I mean if TypeScript can do source map by comparing source code with final output, it shouldn't matter how many transforms are in the middle? I don't know source maps on such level to recognize an issue here. Is it actually necessary for each transform to somehow instruct how the source map should look like?

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

The work would just be recreate the babel-plugin-macros interface in TS.

It's a good thing that babel-plugin-macros is actually pretty small and has had very minimal changes in the last year :)

from babel-plugin-macros.

longlho avatar longlho commented on July 30, 2024

side note @kentcdodds what are the macros that use babel-plugin-macros right now? is there a registry?

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

There's a list here: https://github.com/jgierer12/awesome-babel-macros

There's also a keyword people are supposed to use: https://www.npmjs.com/search?q=keywords:babel-plugin-macros

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

I want to make one thing clear here. I started this discussion and I certainly would like to guide it through, but I won't be much involved regarding coding except some code reviews if needed. I have really shallow knowledge about all this and I don't feel like I want to dive into that too much.

So what we need right now is some volunteer(s) who could examine how macros are working and try to implement some very basic transform that can handle simple macros. Let's ignore details like source mapping and anything that's not really important right now.

Personally, I would pick a macro like https://www.npmjs.com/package/ms.macro to be an initial target for an implementation. Or if you have some other preference, I don't really mind. Let's have it in a separate repo for now and later we can see how much common ground is there and if it's worth it to have a single project.

So ... volunteers? :)

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@xiaoxiangmoe I had just a brief look, but I am bit confused. Is there a separate transform there or is it all bundled in that single package there? It's definitely cool if it works, but next step then should be to separate common transform (macros engine) and the actual code required for macro.

Edit: Duh, nevermind me, should have read the checklist in readme more carefully :) Go on, you are definitely on a good path there.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@FredyC
I'll only publish the packages/typescript-macros in npm latter.
package in the examples directory is private and it only works for debug macros engine.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@xiaoxiangmoe Ok I understand now, haven't thought that examples contain code for actual macros :) This is looking really well. Amazing how fast you managed to put it together.

I am amused by uppercase macro, that's crazy overkill, especially seeing how the macro itself looks inside 😮 However the hooks macro looks fairly useful already. Nice work indeed!

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

@FredyC, I don't see that is any better than what I have shown.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

Well, it's better than some arbitrary string you have to compare which is prone to typos. And it's more declarative and slightly less boilerplaty. Just my opinion. I have never written a single macro. Your approach will work too for sure.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@kentcdodds I prefer support both macros in a single package, also.

But this raises a question. In what steps do we need to provide such a configuration (configuration of macros in bundle tools)?

My current implementation relies on the compile plugin for TypeScript in bundle tools and config in compile plugins options.(compile plugins: rollup-plugin-typescript2, ts-loader, awesome-typescript-loader, parcel-plugin-typescript, etc ).

And babel-plugin-macros is just babel-plugin and config in .babelrc file. (compile plugins: babel-loader )

They don't seem to be compatible in bundle tools plugin.

Do you have any good idea?

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

I'm mostly concerned about the source code being capable of being used interchangeably (independent of how it's configured). People will have too configure the respective tools. But the real benefit will be being able to use the same macro with either tool.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

How it is configured will affect the corresponding API we design.

module.exports = function({type, ...rest}) {
  if (type === 'typescript') {
    return require('./typescript-version')
  } else {
    return require('./babel-version')
  }
}

Any idea about how to implement this?

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

Our tools would call these functions. Creators of macros will implement the function.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

Here's where I require and call that function:

https://github.com/kentcdodds/babel-plugin-macros/blob/master/src/index.js#L158

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@kentcdodds TypeScript may consider more things:

for example:

https://github.com/xiaoxiangmoe/typescript-macros/blob/f961fd99e3c4bd198ff992c11a8b87ceecbc30b8/examples/uppercase.tsmacro/src/index.ts#L4-L12

this function is wroten for generate .d.ts file with tsdoc.

So I should use another export slot. So I choose using export __typescriptMacroNodeTransformFunction for typescript macros as example:

https://github.com/xiaoxiangmoe/typescript-macros/blob/f961fd99e3c4bd198ff992c11a8b87ceecbc30b8/examples/uppercase.tsmacro/src/index.ts#L19


Here's where I require and call that function:

https://github.com/xiaoxiangmoe/typescript-macros/blob/f961fd99e3c4bd198ff992c11a8b87ceecbc30b8/packages/typescript-macros/src/transformerFactoryCreator.ts#L51-L53

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

I just had an idea, but I'm going to bed. I'll share it in the morning.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@FredyC I won't use default export. see:

https://github.com/xiaoxiangmoe/typescript-macros/blob/f961fd99e3c4bd198ff992c11a8b87ceecbc30b8/examples/uppercase.tsmacro/src/index.ts#L17

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

Ok, so my idea won't work. Do you have ideas @xiaoxiangmoe?

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

Emmm, maybe we should use an special export slot for users rather than using default export(module.export)

__typescriptMacroNodeTransformFunction
__babelMacroNodeTransformFunction

and so on.

In this way, when there is no macro config provided, we can also let users use some compatibility dependent runtime solutions(see https://github.com/xiaoxiangmoe/typescript-macros/blob/f961fd99e3c4bd198ff992c11a8b87ceecbc30b8/examples/uppercase.tsmacro/src/index.ts#L11 ).

it will also provide type hint and jsdoc/tsdoc support for IDE

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

babel-plugin-macros using module.exports = createMacro(...)
you can export some object whose keys include __babelMacroNodeTransformFunction.

and in the future, we can only export __typescriptMacroNodeTransformFunction and
__babelMacroNodeTransformFunction for macros

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

or maybe we can only use the same macro export slot something like

export const ____$$$$macroNodeTransformFunctionObject$$$$____={
  babelMacro: ...,
  typescriptMacro: ...,
}

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

It is going to be 1 AM in my time zone. See you tomorrow.
(⁄ ⁄•⁄ω⁄•⁄ ⁄)

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

we can also let users use some compatibility dependent runtime solutions

I think that could get messy quickly. If people want to support runtime and build-time, they would be best to separate those into different packages I think. Otherwise there could be some unexpected issues if there's a config problem.

Hmmm....

As a macro author, I think it would be easiest to do this:

// my.macro/index.js
module.exports = {
  babel: require('./babel-macro'),
  typescript: require('./ts-macro'),
}

What do you think of that?

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@kentcdodds That would basically mean that the end user will also have to do macro.babel() in the code? That doesn't sound about right. As a consumer of a package I would expect to import a macro function directly and possibly some types as named exports. For the same reasons, my idea above is also nonsense.

Or am I missing something here? I am possibly getting confused that there is a runtime part and transformation part. I am not feeling well today, so it's totally possible it does not make any sense what I am talking about 🙊

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@kentcdodds see:

https://github.com/xiaoxiangmoe/typescript-macros/blob/f961fd99e3c4bd198ff992c11a8b87ceecbc30b8/examples/uppercase.tsmacro/src/index.ts#L17

I've already use default export.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

Right, but you can change that...

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

I can't change it for it helps to generate .d.ts file for users.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

Oh interesting... I suppose we could do:

import babel from './babel-macro'
import typescript from './ts-macro'
export {babel, typescript}

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

I think I understand. You're saying that because TypeScript will autocomplete the exports, people could accidentally import the exported names.

Isn't there a way to write typings that override what the code actually says? This will definitely need to be possible because the whole idea of macros is that you can import stuff that's not exported at all. For example:

import { t } from "@lingui/macro"

The @lingui/macro package does not have a named export called t.

So it doesn't matter what the macro file actually exports, what matters is what the type definitions say.

And if it doesn't matter what they actually export, then let's have them export something that's sensible.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

It does matter what the macro file actually exports for it matters is what the type definitions say.

If I didn't publish it as package, for example

import foo, { bar, baz } from '../utils/some.macro'

It matters if ../utils/some.macro import ../utils/some.macro.ts. it is not compiled, but IDE will get proper type hint if we export function bar and baz and export default function foo.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

It does matter what the macro file actually exports.

Hmmm... If that's the case then most macros will be impossible with typescript... Because all current macros only have a single default export that is a function that does not have the same type definition as the actual functionality... There has to be a way to override the type definition for this to work...

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@kentcdodds Don't worry, we can find some transition solution.

for example, for exists macros:

module.exports = createMacro(require('./babel-macros'))
module.exports.__typescriptMacroNodeTransformFunction = require('./ts-macros');
module.exports.__babelMacroNodeTransformFunction = require('./babel-macros');

for new macros(ts or js):

export const __typescriptMacroNodeTransformFunction = ...
export const __babelMacroNodeTransformFunction = ...

override the type definition should write .d.ts file with .js file. but we prefer write .ts file for typescript macros.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

Or can we consider supporting multiple modes?(module.exports = createMacro () and export const __typescriptMacroNodeTransformFunction; export const __babelMacroNodeTransformFunction;)
Although I don't like it, giving users a variety of choices & changing API with compatibility are always right.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

If we using special import name, maybe we can also support something like ts-import-plugin

transform such code:

import { Alert, Card as C } from 'antd'

into:

import Alert from 'antd/lib/alert'
import 'antd/lib/alert/style/index.less'
import { default as C } from 'antd/lib/card'
import 'antd/lib/card/style/index.less'

only if

const antd = require('antd')
// we can find __typescriptMacroNodeTransformFunction property in antd

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

It does matter what the macro file actually exports for it matters is what the type definitions say.

If the package has a separate index.d.ts the definition will be taken from that and doesn't matter what the actual typing in index.ts is. It can be even a different name of typing name and use types property in package.json to designate that. Perhaps I am misunderstanding problem here.

This is also related to those creepy long names. If type definition would be overridden, we can use simple short names as @kentcdodds suggested an IDE won't see these to intellisense them.

import babel from './babel-macro'
import typescript from './ts-macro'
export {babel, typescript}

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@FredyC Another thing I wanna todo is macros can do somthing like ts-import-plugin #94 (comment)

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

Yes, you could definitely still build that if we do what @FredyC and I are suggesting.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@kentcdodds I want to support import with something without '.macro'.
For example: import React component from library with auto import css.
So I prefer use something special export name for future development.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

I can see that our visions for this effort are not aligned. Feel free to do whatever you like with your tool. I don't think that we will be able to collaborate though. Sorry.

@FredyC, perhaps you could fork @xiaoxiangmoe's and make it something that can allow macros authors to build a single macro for both tools?

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

Emmm, okay. If you prefer using default export. I'll deal with it.
Maybe I'll wrote a macro for typescript macro authors to build their macro to both way.
It won't need any change in babel-plugin-macros. I'll show you a demo in two days.
Waiting for my job , thanks(⁄ ⁄•⁄ω⁄•⁄ ⁄)


We are not aligned in how to export it. So I will wrote a macro to deal with it.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

I didn't mean to force your hand @xiaoxiangmoe. I can understand if you would prefer to do things differently with your project.

If we do collaborate, I think that our tools should each allow the following formats:

export default myMacroFunction

This would only support one tool and the macro author would have to document which tool their macro supports.

If they want to support both, then they can do this:

import babel from './babel-macro'
import typescript from './ts-macro'
export {babel, typescript}

This would require a small amount of change on my end, but it would not be a breaking change for macro authors or users.

For the ts tool, authors would be required to write type definitions that override the types from the actual macro source code (they would have to do this either way).

Thanks!

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

How about

import exportMacros from 'export-macros.macro' 
import babel from './babel-macro'
import typescript from './ts-macro'
exportMacros({babel, typescript})

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

ummm... What does exportMacros do? I don't understand why this needs to be so complicated...

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

I could do something in import exportMacros from 'export-macros.macro' for any export format or type definitions. So babel-plugin-macros can do nothing to adapt typescript-macros. painless.
(ಡωಡ)

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

I'm really confused. Why is this not sufficient?

import babel from './babel-macro'
import typescript from './ts-macro'
export {babel, typescript}

Adding another macro just complicates things a bunch.....

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

I'll support this format, also.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

Ok, I probably wont support whatever that exportMacros thing is doing...

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

Yes, there is nothing babel-plugin-macros should do. I'll do all things.🤔

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

What I'm saying is that if someone tries to make a macro that uses that exportMacros thing, it wont work with babel-plugin-macros.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@FredyC, perhaps you could fork @xiaoxiangmoe's and make it something that can allow macros authors to build a single macro for both tools?

Umm, honestly I am totally confused after your discussion here. Also as I said above, it's not in my current mental capacity to tackle this to some glorious finale. I would rather stay in a role of a guide and "silly-ideas-maker" for now.

The main point of confusion for me is that when the macro package would export something like { babel, typescript } what that means for the end user? I mean when I look at some current macros, eg. ms, there is a default export which is made as the output of createMacro, that makes total sense to me.

Now when there would two exports, how would that work? What would user import to be able to use the macro? Shouldn't it be something like this?

import babel from './babel-macro'
import typescript from './ts-macro'
export default createMacro({ babel, typescript })

I feel this kinda gives more power as package author doesn't need to use default export, there can be multiple macros in a single package with named exports.

It would also mean that both "macro engines" would need to export such a function or there would need to be a third common package, which might be useful as well if we find enough of the common ground.

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

It would also mean that both "macro engines" would need to export such a function or there would need to be a third common package, which might be useful as well if we find enough of the common ground.

I don't want to have a third package. That's why I'm suggesting the format I suggested. The macro tool would be the one to consume the exports (not the end user). The way that you'd write the ./babel-macro.js file is like:

export default createMacro(macroFn)

So for me it'd be a very small change. For users it would be no change at all, and for authors who only want to support babel it would be no change at all as well.

The same would go for typescript.

I think we're on a good course here now though as @xiaoxiangmoe will enable that API to work for the typescript macros transform so I think we'll get some consistency and collaboration here which will be cool.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

Alright, not that I would still understand how it will work, but I am glad there is some light at end of the tunnel and I will eagerly await the final result. Thank you Kent for being so active around this, it would probably take a much longer time to figure out basics without you. 👏

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@FredyC @kentcdodds

see https://github.com/xiaoxiangmoe/typescript-macros/blob/998b5bee4052f4fce6039d5c6b62cf5a53c41661/examples/lowercase.macro/src/index.ts

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

And see lowercase.usage.ts and lowercase.usage.js in examples directory.

I need your feedback, Thanks.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

I hope that one day ,maybe a few years later, we can migrate painlessly to the situation where macro and ordinary code coexist.(no '.macro' or '.tsmacro' ). I know our goals and routes seem to be inconsistent. But I don't want to make quarrels and splits in community members, so I will support the existing babel-plugin-macros style. Thank you.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

Looks interesting, I am just confused about this part...

import {
  exportBabelMacro,
  exportTypeScriptMacro,
} from '@-.-/interop-export-macros.tsmacro';

exportTypeScriptMacro(tsFunc);
exportBabelMacro(babelFunc);

Is it like a macro to export macros? Why is needed? And why does it include uppercase function again? I assume it's some leftover?

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@FredyC Oh I found I bug I wrote, wait a minute.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

see https://github.com/xiaoxiangmoe/typescript-macros/blob/998b5bee4052f4fce6039d5c6b62cf5a53c41661/examples/lowercase.macro/src/index.ts#L19-L21

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

If you run yarn build, it will generate:

file dist/index.d.ts

/**
 * lowercase input string
 * @param input only a StringLiteral
 * @returns lowercased StringLiteral
 */
declare function lowercase(input: string): string;
export default lowercase;

file dist/index.js

'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var ts = require('typescript');

const { createMacro } = require('babel-plugin-macros');
function lowercaseMacro({ references, state, babel, config }) {
  references.default.forEach(referencePath => {
    if (referencePath.parentPath.type !== 'CallExpression') {
      throw new Error('Not allowed');
    }

    const t = babel.types;

    const args = referencePath.parentPath.node.arguments;
    if (args.length !== 1) {
      throw new Error("args's length should be 1");
    }
    const arg = args[0];

    if (arg.type !== 'StringLiteral') {
      throw new Error('only allow StringLiteral as arg');
    }

    referencePath.parentPath.replaceWith(
      t.stringLiteral(arg.value.toLowerCase())
    );
  });
}
var babelFunc = createMacro(lowercaseMacro);
// export default createMacro;

var transformFunction = function (_a) {
    var reference = _a.reference, node = _a.node;
    if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
        var refName = reference(node.expression);
        if (refName !== false) {
            if (refName !== 'default') {
                throw Error('only allow default export');
            }
            if (node.arguments.length !== 1) {
                throw Error('only allow 1 param');
            }
            var param = node.arguments[0];
            if (!ts.isStringLiteralLike(param)) {
                throw Error('only allow StringLiteral & NoSubstitutionTemplateLiteral');
            }
            return ts.createStringLiteral(param.text.toLowerCase());
        }
    }
    return node;
};

exports.__typescriptMacroNodeTransformFunction = transformFunction;
exports.default = babelFunc;

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

This is just a prototype to demonstrate my idea and it does not cover a lot of edge case.


Why I should wrote a marco to export macro? Because I didn't like to write .d.ts file for index.ts file.
And after some years, maybe I'll support macros in ts file which name not endWith .macro or .tsmacro.

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

Well, it still doesn't explain why there is a duplicit declaration of uppercase function, in macro itself and in the interop as well. Doesn't make any sense.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@FredyC
This function make sense for generate dist/index.d.ts

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

I'll still use default export to interop with babel-plugin-macros, but I should let there exists a special export slot for my tool to check if a normal file also has a macro.

I will stick to my approach, but this increases the burden on the user, I will provide a macro to give the user a clean experience.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

Well, it still doesn't explain why there is a duplicit declaration of uppercase function, in macro itself and in the interop as well. Doesn't make any sense.

Ok, you are right, it is useless now.
I remove function body of this.

see:
xiaoxiangmoe/typescript-macros@91c5e10

from babel-plugin-macros.

kentcdodds avatar kentcdodds commented on July 30, 2024

Ok, so I'm not totally sure I understand what's going on here, but if you'd like to make a pull request on babel-plugin-macros for what changes we would need to change to make this work (without breaking things) that would help me understand I think.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

@kentcdodds Nothing will change for babel-plugin-macros.
Maybe you could try this

git clone https://github.com/xiaoxiangmoe/typescript-macros.git
cd typescript-macros
yarn
yarn build
cat examples/lowercase.usage.js/dist/main.js

And you'll see

// file: examples/lowercase.usage.js/src/main.js
import lowercase from '@-.-/lowercase.macro';

export const van = lowercase(
  "My name is Van. Uh...I'm an artist. I'm a performance artist."
);

transformed by babel-plugin-macros into

// file: examples/lowercase.usage.js/dist/main.js
const van = "my name is van. uh...i'm an artist. i'm a performance artist.";

export { van };

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

This would require a small amount of change on my end, but it would not be a breaking change for macro authors or users.

Your end and macro authors or users will have no change if they don't want to support TypeScript

If they want to support TypeScript in single package, see example lowercase.macro

from babel-plugin-macros.

danielkcz avatar danielkcz commented on July 30, 2024

@xiaoxiangmoe It still doesn't make sense to me. I am confused the most by the interop package. Does it mean that alongside the regular macro package you need the extra one to do what actually? If the interop package is meant to be some kind of universal macro to generate macro then it shouldn't contain any reference to "uppercase" macro.

And what about that weird @-.- scope? Is that going to be some official scope for all macros? :)

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

I say it just a prototype.

see https://github.com/xiaoxiangmoe/typescript-macros/blob/master/examples/README.md

all package's start with scope @-.- which is private scope for my own use.

I'll provide an officially interop macro package without @-.- scope future.

I still don't know why you think interop package didn't make any sense. It make sense for our TypeScript users. It help us generate dts file.

from babel-plugin-macros.

xiaoxiangmoe avatar xiaoxiangmoe commented on July 30, 2024

If you like to write dts file like JavaScript users, then it really doesn't make sense to you now.

from babel-plugin-macros.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.