[HN Gopher] JavaScript macros in Bun
___________________________________________________________________
JavaScript macros in Bun
Author : fagnerbrack
Score : 82 points
Date : 2023-06-29 11:02 UTC (1 days ago)
(HTM) web link (bun.sh)
(TXT) w3m dump (bun.sh)
| creatable wrote:
| Every time I see a new Bun update it's always because Bun has
| implemented a feature that violates the spec in some way. While
| this does rely on syntax from a proposal, said proposal has not
| made it into the language yet. Another failure I see is the fact
| that the "type" property is not meant to be used for this type of
| language augmentation, it's meant to describe the type of the
| file without inferring from the file extension. I think Bun has a
| habit of moving too fast and breaking things and if it catches on
| I worry it will have just as many legacy anti-spec things as Node
| does.
| paulddraper wrote:
| How do you think features make it into the spec?
|
| Specification and implementation are chickend-and-egg.
| badrequest wrote:
| Is bun actively participating in these discussions, or are
| they just implementing whatever they wish and not
| participating in the chicken-and-egg process?
| spankalee wrote:
| The main JS implementations do not run ahead of the spec.
| They implement at Stage 3.
| paulddraper wrote:
| A. Import attributes are stage 3.
|
| B. If you blur the line between "runtime" and "compiler",
| you'll realize that a lot of JS ecosystem (e.g. Babel) run
| ahead of that.
| jakelazaroff wrote:
| Import attributes are at Stage 3: https://tc39.es/proposal-
| import-attributes/
| spankalee wrote:
| I'm well aware, but this is not just import attributes.
| It's using import attributes syntax to trigger a
| transform of the importing module, in addition to
| changing the behavior of the exporting module.
| pictur wrote:
| I guess today's software development culture requires it.
| constantly discover new things and continue by breaking the
| old.
| the__alchemist wrote:
| [flagged]
| bluepnume wrote:
| Eh. Just treat Bun as a testing ground for (very) experimental
| new features. It's no secret that the project moves very
| quickly, and anything that gains enough traction can be folded
| back into the spec.
| Jarred wrote:
| Import Attributes are a stage3 proposal which means it is very
| likely to become part of the language. It's the last stage
| before becoming part of the language.
|
| > Another failure I see is the fact that the "type" property is
| not meant to be used for this type of language augmentation,
| it's meant to describe the type of the file without inferring
| from the file extension
|
| It sounds like you're confusing Import Attributes with Import
| Assertions, which was the previous iteration of Import
| Attributes.
|
| Interpretation of import attributes is host defined. For Import
| Assertions, that wasn't true - they were intended never to
| impact runtime evaluation. That's the difference with Import
| Attributes. Import Attributes do impact runtime evaluation.
| creatable wrote:
| What I specifically meant is that the "type" property can
| impact runtime evaluation as per the spec, however it's
| implied to be used specifically for impacting runtime
| evaluation based on a file's type. Say I have a module in a
| language that transpiles to JavaScript based off of the
| "type" property. In Bun, I would not be able to use any
| functions exported by it as a macro because the "type"
| property is being used for defining the module as a macro.
| That's the specific issue I have.
| spankalee wrote:
| Exactly. Import attributes are supposed to effect the
| imported module, not the _importing_ module.
|
| This turns what looks like a function call in the importer
| into something like macro expansion (it doesn't look like
| actual macros though).
| unlog wrote:
| What about adding signals?
| [deleted]
| DanHulton wrote:
| I mean, it looks like they support Import Assertions, too:
|
| https://bun.sh/blog/bun-macros#how-it-works
|
| So the worry about having legacy anti-spec things still seems
| pretty valid.
| jgalt212 wrote:
| Is Bun the current killer app in Zig?
| pyrolistical wrote:
| Written in zig? But that doesn't matter?
| revskill wrote:
| It matters for Ziglang and Zig users.
| pyrolistical wrote:
| I agree. I just think "killer app" means "killer end user
| application", whereas you seem to be using it as "killer
| application of zig"
| paulddraper wrote:
| How so?
|
| The "killer app" is the app that necessitates adoption of
| the platform.
|
| E.g. Lotus 1-2-3 is the killer app of the IBM PC. Lots of
| users want Lotus 1-2-3, therefore they need to adopt the
| IBC PC platform.
| revskill wrote:
| Real world testing requires real world workload.
| detaro wrote:
| Sure, but that's not what "killer app" usually means.
| jitl wrote:
| This blog post is missing a few things from the official docs:
| https://bun.sh/docs/bundler/macros
|
| ---
|
| The way we do this kind of thing at Notion is very simple. We
| have normal CLI commands that generate code and write it to disk,
| and we check in those outputs. Then in CI, we run all the
| generation commands and verify the codegen is up-to-date.
| Checking in generated code means it's really easy to understand
| the behavior of codegen and how it changes, and everyone gets
| excellent typechecking and auto-completion of codegen'd
| artifacts.
|
| The downside to static codegen is that the "templates" we use for
| codegen need to be valid Typescript files, or we risk breakage if
| imports or types change.
|
| Bun macros have some advantages like execution at build time for
| stuff like Git SHA interpolation, but because these macros can't
| actually emit code, they feel _less_ powerful that straight-up
| codegen. I could replace a lot of the Notion CLI commands with
| Bun macros if:
|
| 1. There's a return type for macros that emits code, like:
| // src/macros/prism.ts export function emitPrismImports()
| { const importOrder =
| toposortLangagues(PRISM_LANGUAGES) const imports =
| importOrder .map(lang => `await import(/*
| webpackChunkName: "prism" */ "${importPath}"`)
| .join('\n') return Bun.code`${imports}` }
| // src/client/syntaxHighlighting.ts import {
| emitPrismImports } from '@notionhq/macros/prism' with { type:
| 'macro' } async function highlight(text, lang) {
| emitPrismImports() return Prism.highlight(text, lang)
| }
|
| 2. There's a way to run `bun build` and _just_ do macro
| evaluation. I need to continue using Typescript, Webpack, Jest,
| etc for now, so we need eval 'd macros on disk so they can be
| typechecked, tab-completed, and bundled by other tools.
|
| The uncompiled versions still wouldn't typecheck nicely though
| :thinking_face:
| explaininjs wrote:
| Very nice. This should get rid of a lot of clunky
| webpack/esbuild/etc junk I have lying around to inline various
| constants or otherwise one-time configure the runtime.
| jakelazaroff wrote:
| Pretty cool! Sounds like it was inspired by comptime in Zig
| (which shouldn't be too surprising since Bun is written in Zig).
| Jarred wrote:
| It was mostly inspired by comptime in Zig. Babel macros came to
| mind too, but those rely on an AST and Babel-specific api.
| What's really cool about comptime in Zig is it feels like
| ordinary code. Macros in Bun aim for that too.
| zigobserver wrote:
| If Zig drops LLVM, where does that leave Bun development?
| There is no alternative Zig implementation.
| erichocean wrote:
| Woah, nice. Zig has a nice sweet spot of providing useful
| functionality at compile time vs. having "macros can make the
| text you read mean practically anything" like in, e.g., Lisp.
| Zambyte wrote:
| It's cool to be able to compute things at compile time, but one
| thing that doesn't seem clear from this post is: can macros
| return code? If so, how? It seems like they can only return
| values that are computed at compile time, which severly limits
| what you can do with macros.
| colinmcd wrote:
| This is mentioned under "Limitations"
|
| > The result of the macro must be serializable!
|
| > Functions and instances of most classes (except those
| mentioned above) are not serializable.
| Zambyte wrote:
| Well those are both slightly different than "code". I mean
| something more like returning an S-expression, or a JS AST
| object, and having that be inlined. That should have no
| problem with serialization, since it could just be dropped
| into the AST (unlike a function or class).
| skybrian wrote:
| Neat! I like that it's not allowed in npm modules. Module authors
| can do whatever compile-time codegen they want as part of their
| own builds.
| bitblender wrote:
| Is there a way to disable arbitrary I/O in macros? I would like
| to use this feature to compute lookup tables and inline the
| results of pure functions, but I really really do not want my
| bundler making arbitrary http calls or other sorts of
| nondeterminism. I need to be able to reproduce my bundles
| confidently.
| iddan wrote:
| I love macros and feel like they would be a great addition for
| the JavaScript language and ecosystem. That being said I would
| much rather them to be standardised. Now with the past few years
| of TC39 it feels like introducing new big things worked only when
| one of the big vendors made them available de-facto and then they
| got real chance to progress in the review stages (take for
| example decorators that were boosted by TypeScript).
| goranmoomin wrote:
| I was ready to be excited from the title, but was utterly
| disappointed :(
|
| IMO these aren't macros in the Lisp-sense of the word (or Rust,
| or even C); yeah they run code at compile time, but that's where
| the common ends.
|
| Macros should be able to apply syntactic transformation on the
| code. Lisp is famous for allowing that by representing code as
| lists. Rust has a compiler-level API to give tokens and run
| arbitrary code, then spit new tokens out. C macros operate on the
| tokens level, so with enough magic you can transform code to the
| shape you want.
|
| This... isn't any of that.
|
| A pretty good example (and something I'm still sad that it didn't
| take off) of macros in JS is Sweet.js[0]. Babel macros[1] are a
| bit higher level, where macros require the input to already be a
| valid AST, but that's also something I'd call macros.
|
| This... I'd say it's more of a compile time code execution
| feature, not a macro feature.
|
| [0]: https://www.sweetjs.org/ [1]:
| https://babeljs.io/blog/2017/09/11/zero-config-with-babel-ma...
| Jarred wrote:
| An earlier version of macros in Bun looked more like what you
| describe. We nerfed it because the API was too complicated. But
| we probably will revisit in the future.
|
| https://gist.github.com/Jarred-Sumner/454da846d614f7bb4bcceb...
| paxys wrote:
| Hmm, seems to me that this is for a _really_ niche use case
| (adding metadata like timestamps or git commit tags to your JS
| bundle). Is it really something that needs to have native
| language and syntax support over just adding a custom build step?
| Zekio wrote:
| gotta say I'm puzzled why time is spent on features like these,
| rather than focusing on getting the remaining node apis finished
| monroewalker wrote:
| Second this. I've tried Bun a couple times and have been
| surprised by how fast it actually is but also run into issues
| getting it to work with existing libraries. Issues that make it
| seem like it's _almost_ there but just not quite able to
| replace node yet in most cases.
|
| It's surprising to see effort spent coming up with and
| developing niche new features rather than on bridging the gaps
| (within reason) with node.
|
| I've never worked on a language project like this before though
| so I'm not in a place to cast any judgement, just echoing the
| sentiment that this seems strange from the outside. Maybe this
| is just what it takes to keep the project interesting for
| Jarred? I can relate to getting bored with a project once I've
| proved out the difficult parts and most of what remains just
| feels like chores
| Jarred wrote:
| I wrote nearly all of macros in Bun over a year ago, when Bun
| was in private beta. We just never documented it or talked
| about it much
|
| Our focus is very much on Node.js compatibility
___________________________________________________________________
(page generated 2023-06-30 23:00 UTC)