node-raylib icon indicating copy to clipboard operation
node-raylib copied to clipboard

es6 module (esm)

Open konsumer opened this issue 2 years ago • 3 comments

Tree-shaking doesn't really work well with commonjs modules, in general, and es6 users (node natively supports esm, now, with type: module in package.json) would be able to use our lib better, if it was written as es6 module. We can use other tools (I like microbundle or esbuild) to target lots of different module systems (including very old node commonjs.)

This can help with user's treeshaking on es6-based bundlers, and make native node imports work nicer (currently you have to import all of raylib, not individual functions.) it would also mean we can have esm examples, in this repo, that will allow import and top-level awaits, which would be useful for showing off other addons, like async (#134.)

Microbundle takes slightly longer to build than esbuild, but it's much simpler: make a few changes to package.json that you need to do anyway, to support a bunch of targets, and it will build them all with microbundle command.

Related: #130 Related: #134

konsumer avatar Apr 22 '22 21:04 konsumer

As an example, we could have demos that look like this (just name file .mjs):

import {
  BeginDrawing,
  InitWindow,
  BeginDrawing,
  ClearBackground,
  DrawText,
  EndDrawing,
  RAYWHITE,
  LIGHTGRAY
} from 'raylib'

import { MaintainFPS } from 'raylib/addons/async.js'

const screenWidth = 800
const screenHeight = 450

InitWindow(screenWidth, screenHeight, 'raylib [async] example - async game-loop')

while(await MaintainFPS(60)) {
  BeginDrawing()
  ClearBackground(RAYWHITE)
  DrawText('Congrats! You created your first window!', 190, 200, 20, LIGHTGRAY)
  EndDrawing()
}

r.CloseWindow()

currently, we can't do mjs in a demo in this repo, but externally (like in a seperate mjs project) you still have to do this:

import r from 'raylib'

const {
  BeginDrawing,
  InitWindow,
  BeginDrawing,
  ClearBackground,
  DrawText,
  EndDrawing,
  RAYWHITE,
  LIGHTGRAY
} = r

konsumer avatar Apr 22 '22 21:04 konsumer

ES6 also supports the following too...

import * as r from 'raylib'

I'd be okay with porting everything to es6 modules, but just want to make sure tests still pass. Adopting the new for the sake of adopting the new doesn't actually accomplish much :wink:

RobLoach avatar Apr 23 '22 19:04 RobLoach

ES6 also supports the following too...

Yep, and you can also additionally export a default object to keep the same code-style, and other things won't inline that in user's code, if they don't import default and are using a treeshaking bundler.

like consider this:

export const SetTraceLogLevel = () => WHATEVER

export default { SetTraceLogLevel }

This makes it work for the current import-style and the more atomic.

I'd be okay with porting everything to es6 modules, but just want to make sure tests still pass. Adopting the new for the sake of adopting the new doesn't actually accomplish much 😉

It's not just to "adopt the new" for me. A lot of times moving to esm feels like it's just a minor syntax-change, but in reality a ton of stuff actually works way differently, which allows much greater efficiency. Esm builds a graph up-front before running other code, which makes a huge difference in terms of parse-speed, side-effects, and treeshaking. From our perspective, we can just change the generated code to be a little bit different, and through bundling, we don't really need to worry about losing anything (support our min target of 10.x that NAPI supports, with bundled commonjs exports) but we would gain being able to operate in esm-space, which has many real benefits. Sometimes, in these discussions, I think it kinda gets lumped in with typescript/coffeescript/etc, like "yeh, I guess i's a little better in some small way, but you lose versatility or compatibility with standard js, etc". Personally, I really dislike all these transpiled languages. I actually enjoy the standard es6 javascript code, which includes esm. ts/cs does nothing for the javascript language it targets, even though the module syntax is similar, it's just a wrapper that outputs regular js. Esm is different. It actually changes how the js module/dependency-tree engine works, in a fundamental, and now very common way (like you can use it browsers, deno, quickjs, etc.) Esm is the standard, and node is the edge-case with commonjs. I'm glad they did for compatibility, but in my opinion, esm is the way forward, for sure.

I'm fine with putting it off, or even never doing it. This project being commonjs doesn't super-effect me (I still use it in esm projects fine, just have to import the whole thing) except I have to write things in examples/ in cjs, which means no top-level awaits and other little things, but not the end of the world. I can still do that stuff in esm projects that import it.

konsumer avatar Apr 23 '22 22:04 konsumer