-
-
Notifications
You must be signed in to change notification settings - Fork 177
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
babel plugins #190
Comments
The problem is that currently files are first read by the Closure Comiler via JSInspector to extract metadata about them (eg. their The Inspector can only read files that are "valid" "ECMASCRIPT_NEXT" according to the closure compiler. I need to add support for processing the files FIRST anyways to support .jsx and other dialect but I haven't gotten around to doing that yet. I'm not yet sure how that will work but it'll probably be similar to https://clojurescript.org/news/2017-07-20-js-preprocessing-improvements. Ideally I want to keep the post processing out of the compilation itself since it might not be that easy to integrate other tools like typescript, coffeescript, flow, etc. |
This isn't the only plugin I we use just the easiest to demo. Fully qualified symbols seems reasonable for custom preprocessing steps, but I'm wondering if perhaps it could be done now with Custom Builds. I'm also thinking instead of custom builds it might be even better to provide a middleware pipeline around the target steps instead of redefining and delegating to the target. Maybe this middleware does preprocessing, or maybe something different. In either case, preprocessing js would make for a much more interesting custom build example. I currently transpile the js first and import from the transpiled code. I was thinking perhaps it would be possible to transpile and then update the source path from a custom build so that I don't need to reference the transpiled path, but this might be less trivial then just calling out to babel before each rebuild. I haven't had time to dig to deeply into the current implementations, but the example doesn't give much insight into the structure of the state object. Is there any existing docs around its structure and/or a spec for it? |
No Custom Builds would not be a solution. They only allow you to customize how the output is organized and can only control which resources get into a build not how. The issue I have with fully qualified symbols for custom processing is that they don't capture lifecycle. Take the cljsjs/babel-standalone preprocess as an example. It starts a nashorn JS engine via delay and will keep that running indefinitely once started. The only way to talk to it is by giving it a file to process. It is using a fairly limited version of The rough outline for my current plan looks like this: JS resources are currently always resolved via files and relative paths between each other because this is how npm/node work. They do not have the concept of a classpath and cannot follow into CLJS resources are always addressed via the classpath. They cannot "break out" of the classpath. Names must be unique. So we create a second kind of JS resource that follows the classpath rules and can use relative and absolute addressing. So technically each file then has a unique name. You could then create a I think this would provide the best of both worlds where JS can come from either With those resolve rules in place The real hurdle would be when the pre-process stuff would attempt to resolve require/import itself but those tools probably has some kind of plugin mechanism to allow hooking into resolve. |
I don't think I'm following on the lifecycle bit, I thought it was just using namespaced keywords to reference functions without them necessarily being loaded yet. That doesn't mean they need to have the same signature as those in cljsjs/babel-standalone. The function could be invoked multiple times with a state object to participate in multiple lifecycle stages in the same way custom targets do. To be more specific I thought I would try to hack something up in the example project that injects arbitrary middleware around the target function to preform pre/post processing at each stage. This is what came up with: https://github.com/thheller/shadow-cljs-examples/compare/master...kurtharriger:middlware I created my own custom target that reads a list of middleware and the original desired shadow-target from the config. I then wrap a couple middleware methods around it, one that dumps the state for each stage to an edn file before calling the target function and another middleware function that attempts to babelize a source directory. (After writing resolve-keyword I realized edn had no issue using symbol for target so why not just use symbols instead of keywords, unless the goal is to decouple from implementation, but then maybe they shouldn't be namespaced... but I digress). My main goal was to see if I could create a custom target that creates a middleware chain to do pre/post processing in each stage in a target agnostic way and and then use the middleware to transpile babel code. I was able to create the middleware chain as I wanted and I was able to call babel during the Maybe there isn't a good way of doing it yet , but I also noticed in the state there is a babel service and I'm seriously wondering if I could hijack the in and out channel from the middleware during the configure stage and startup a new babel service that uses a different config? Hopefully this implementation is useful to you and gets some ideas brewing. |
I wish it was this simple. There are a few flaws with your approach.
By lifecycle I was referring to the lifecycle of the individual server components. Right now the internal babel process is started once per server and shared among all builds. It is properly stopped on server shutdown which is important for I have a plan for plugins but nothing concrete yet. BTW the Don't worry though the plan I outlined above should take care of everything easily. |
My thinking was that I needed to intercept and replace the current babel service because that plugins are hardcoded here https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/cljs/npm/transform.cljs#L25. I guess in part I don't understand the role babel currently plays in shadow-cljs and perhaps more so the closure compiler. You mentioned earlier that the transforms need to be done before closure compiler because of a limitation in JSInspector, but if closure then is handling the transpile then when does shadow-cljs use babel or if shadow-cljs is running babel then why not run it sooner? I agree that shelling out isn't the most performant approach, but I would think that the middlware could start a service and add it to the state in a namespaced keyword for reuse in future phases (there probably should also be a cleanup stage in the pipeline to allow for middleware to properly shutdown).
Unlike shadow-cljs however I don't think webpack ever needs to flush everything back to disk when running a loader as they are provided the file contents. The problem with writing back to disk to run another step such as the closure compiler is that unless you overwrite the source files the paths change and all the import/requires need to be rewritten as well. I'm wondering if perhaps it would be easier to copy everything into a virtual filesystem so that transpiled code can be written to the exact same path as the source file. I think |
The problem I have with loaders is the If we don't do the loader but instead do the JSX conversion entirely independently it is much easier to integrate and has to deal with way less moving parts. I'll have the solution ready soon, then we'll see how it compares. |
Just pushed the implementation of my plan above. No release yet since it needs more testing. clone and Relative and absolute requires in CLJS are now resolved on the classpath which means Classpath JS is also transformed by Closure so you get the full DCE experience. Currently only the loading part is implemented which means you need to run external tools manually but a simple Running those tools via shadow-cljs will be enabled by plugins once I can decide on a proper plugin API. |
I pushed Documented here: https://shadow-cljs.github.io/docs/UsersGuide.html#_dealing_with_js_files The next step will be to do proper integration with |
babel example here using the |
I have some js files that require
transform-class-properties
I added the plugin to
.babelrc
andbabel
will transpile them it, but shadow-cljs will not.I updated the
local-js
example to demonstrate the issue:https://github.com/thheller/shadow-cljs-examples/compare/master...kurtharriger:babelrc
This works:
But
emits:
The text was updated successfully, but these errors were encountered: