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

Use ES6 "class" as module syntax #8359

Open
worlddai opened this issue Nov 5, 2019 · 7 comments
Open

Use ES6 "class" as module syntax #8359

worlddai opened this issue Nov 5, 2019 · 7 comments

Comments

@worlddai
Copy link

worlddai commented Nov 5, 2019

So I can extend your class directly using extends,and so on.

@OmarShehata
Copy link
Contributor

This is to support browsers that don't yet support ES6 syntax without requiring a transpilation step. Here's a quote from our blog post going over the whole migration that I encourage you to check out:

Even though we’ve moved to ES6, the combined Cesium.js file still supports browsers that lack ES6 support, such as IE 11. As long as your build system can target IE 11, Cesium ES6 modules will continue to work without requiring additional transpilation.

With that said, having a transpilation is something we want to support in order to use newer ES6 syntax and features. Also from the blog:

Since Chrome, Firefox, and Safari already support some of the latest JavaScript features, we should be able to keep our minimal build during development policy in place and start to leverage newer features such as async/await/Promise/generators/etc… We would still need to run the final output through Babel as part of the build process, but Rollup already has good support for this step.

I'm going to leave this open as a feature request to use this particular ES6 feature, since even if we do add a transpilation step it would have to be another effort to change how classes are declared.

@OmarShehata OmarShehata changed the title Why does version 1.63 not use es6 "class" as module syntax Use ES6 "class" as module syntax Nov 5, 2019
@mramato
Copy link
Contributor

mramato commented Nov 5, 2019

@OmarShehata I'm actually going to close this, the use of class will be part of a much larger discussion/decision/change so having this open as a standalone issue isn't really actionable.

@mramato mramato closed this as completed Nov 5, 2019
@thw0rted
Copy link
Contributor

@mramato is there a different (good) place to discuss this publicly? I'm currently consuming Cesium from Typescript (c.f. #5717 #4434 etc) and I think I could fully automate that process if the "class-like" new-able function pattern that Cesium uses were to move to ES2015 class syntax. I explain in the "Typings" issue, but briefly, Typescript can successfully extract type information from JSDoc for almost all of Cesium, except where getter/setter functions are passed to Object.defineProperties and decorated with @memberof.

As a small test case, I converted Viewer.js by hand, which took about 5 minutes of manual copy/pasting. Most of the work was converting the getter/setter syntax from foo: { get: function(){...}, set: function(...) { ... } } to get foo() {...}; set foo(...) { ... };. I haven't looked at automating this yet but I believe I can at least make the manual process pretty straightforward. After making this conversion, the Typescript output (.d.ts files) is correct, and the test suite still passes.

@ggetz
Copy link
Contributor

ggetz commented Jan 9, 2025

I think it's valid to have this issue open:

  1. We no longer support IE 11
  2. This would be a discrete step towards full TypeScript

We will need to discuss the strategy for how to port over code.

If we'd like to take a similar approach to how we moved to using prettier, we'd need some sort of automated method. I just came across Lebab, the opposite of Babel, for converting from ES5 syntax to ES6+. We'd need to do a test run to see if its possible to use for our codebase.

@javagl
Copy link
Contributor

javagl commented Jan 10, 2025

Some brainstorming in terms of the strategy (even though some of that may be obvious):

Two important dimensions are

  1. which part of the code will be updated
  2. which updates will be applied

For 1, some options are:

  • Just update the code as you see fit, while editing it. E.g. either updating a certain class while your're refactoring it anyhow, or casually throwing in some promise-to-await change when fixing some bug in some function.
    • The drawbacks here are that this could be highly cluttered. The codebase would be a pretty inconsistent mix of different styles for a long time. We already have that to some extent (e.g. Pomises vs. await). But this could make it more apparent, more widespread, and (most importantly): It would never be "finished" (whatever "finished" means here). First, because new ECMAScript versions are created each year. And secondly, because some parts of the code are never touched in practice.
  • Update groups of classes in one pass
    • This could refer to "packages" like Core or Scene, or subsets thereof. For example, a relatively low-hanging fruit could be to start with all the math-related CartesianX/MatrixX/Ray/Polygon... classes. This could make sense because it's very basic stuff, and could be "rewarding" in many ways (also a step towards that sought-for Cesium math library). A counterargument could be: These are exactly the classes that are never touched, and they already are relatively clean and easy to understand. There are other areas of the code that could benefit more from all this.
  • The "big bang" - update everything at once.
    • This is the approach that was taken with the 'prettier' transition. But it would be far more difficult here. The 'prettier' changes did not so much change the structure of the code itself, but mainly its ... "style" (i.e. the changes had small, local impact, like "var -> const" or "prefer template over concatenation" and so).

For 2. the options are ... well, at least everything that is listed as "Transforms" on https://github.com/lebab/lebab , and more...

(Note: The considerations here are independent of whether we give 'lebab' a try or not. It seems unlikely for me that this could be a one-shot solution in general. And even if it was, technically, the following still has to be kept in mind: )

The possible modernization changes vary wildly in terms of their impact and their potential for "breaking" something. One recommendation from the lebap README is

The recommended way of using Lebab is to apply one transform at a time, read what exactly the transform does and what are its limitations, apply it for your code and inspect the diff carefully.

and this also applies if we did certain changes manually.


Note that these are really independent dimensions. For example, we could

  • Change Promises to async/await only in DataSources, or everywhere at once
  • Change prototype to class everywhere, or only in the math-related classes
  • ... (all combinations thereof...)

I think that 2., "Which updates will be applied", can be answered more generically. A few random examples of the changes that could be part of such a modernization (roughly inspired by the lebab README):

  • Changing prototype to class? Yes!!!
  • Changing array.indexOf(foo) !== -1 to array.includes(foo): Yeah... why not?
  • Changing Math.pow() to **? Ouch - I didn't even know that this existed. That's a "nope" for me...
  • Changing for (let i=0; i<array.length; i++) { const item = array[i]; }-loops to for (const item of array)? I think that this has far worse performance, so probably not (or not everywhere...)
  • (not in the lebab README): Changing promise chains to async/await? Yes!!!
  • ...

Now... we can argue. Or vote 🙂

@jjspace
Copy link
Contributor

jjspace commented Jan 10, 2025

I'll start by saying I fully support swapping to class simply for the better support from TS and Intellisense which should make working on the library better and as mentioned before could be an easier midway step toward #4434

Just update the code as you see fit, while editing it

Even if this seems like the smallest effort up front I think of all the options this is the only one we should definitely not do. I think this approach can easily turn every PR into a mini-refactor and make them take longer. It also obfuscates the actual change of a PR. Much easier to review 1 pr for some change and a second PR that is only code restructure but should behave identically before and after.

I think going section by section could work pretty well and unless we can fully automate transformation through something like lebab then that's probably the only feasible approach. I do think we should try and have a concerted effort to move through the whole codebase (or at least package level engine/widgets) fairly quickly to avoid messy merges or out of sync branches.

which updates will be applied

I think we should focus this issue and the subsequent PRs/changes on only the transition from prototype based classes to the keyword class classes.

Unrelated to your 2 points we may want to just take a peek at performance. I believe there were articles years ago when class was a new "syntax sugar" about how people should "not" use them because it obfuscates how the JS actually works and could be worse for performance. My assumption is that is no longer a concern and with newer class specific features like proper private properties or static and instance properties introduced in ES2022 I would expect that browsers and JS engines have gotten a lot better at handling class classes.

Some random articles I found doing a quick google

@javagl
Copy link
Contributor

javagl commented Jan 13, 2025

Even if this seems like the smallest effort up front I think of all the options this is the only one we should definitely not do. I think this approach can easily turn every PR into a mini-refactor and make them take longer. It also obfuscates the actual change of a PR. Much easier to review 1 pr for some change and a second PR that is only code restructure but should behave identically before and after.

Although it probably does not make sense to try and talk through all degrees of freedom, scenarios, and granularities here, I think it's important to be aware that there are many degrees for this. The point is: There is no clear cut between "applying style and conventions" and "a refactoring".

For example, when someone is touching a piece of code, and, say, adds another option to an options structure, which is later extracted via the common

const option1 = options.option1;
const option2 = options.option2;
const thatNewOption = options.thatNewOption;

then one could make a case to just update this, to

const {
  option1,
  option2,
  thatNewOption,
} = options;

iff (if an only if) the coding guidelines suggest that this is the preferred way (see #12196 ).

Similarly, when someone is fixing a bug where some wrong argument is passed to a function as in

loadData().then(function(data) {
    process(data, "wrongArgument");
});

one could make a case to not just change the "wrongArgument", but change this to

const data = await loadData();
process(data, "rightArgument");

However, this is related to the question of which changes are (supposed to be) applied.

And when there is one, specific change, like the "prototype-to-class", then I agree that this should be addressed holistically, and (important:) in isolation. Later, we could do another holistic change, also in isolation (like "then-to-await" or so).

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

No branches or pull requests

7 participants