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

IMPORTANT: Migrating to modern javascript/typescript #7596

Closed
asturur opened this issue Jan 10, 2022 · 114 comments
Closed

IMPORTANT: Migrating to modern javascript/typescript #7596

asturur opened this issue Jan 10, 2022 · 114 comments

Comments

@asturur
Copy link
Member

asturur commented Jan 10, 2022

Hello everyone!

FabricJS is nearly 12 years old.

The library has still its place around the internet, is good for many things, is never perfect, but i know that for who approach rich canvas based interactive apps, this is a nice piece of software to leverage.

While our main fault are still lack of great docs and updated and clarifying examples, now also the javascript code looks like a lot obsolete.

There are things we could leverage like Promises, default value for arguments, fat arrow for loops and readibility, native classes, proper getters and setters.

We would like to take the occasion to revisit the shape of the codebase to something more modern.
This would include proably a bundler, proper import statements instead of a single file that gets built like lego blocks, maybe tree shaking for who import just what he wants to import.

This would make the custom built unnecessary, using other libraries easier including touch interactions.

So a move to es201X is coming anyway.

The question is if we want to add type descriptors or if we want to make a TS migration right away.

The benefits of consuming TS are clear to me, the benefit of writing typescript a little less, i'm not a great fan and also i m not great a TS.
Is very easy to use TS in the wrong way and produce subpar results.

We are open to suggestions and understand what is the best way to make everyone happy.
external type definition in form of d.ts files are a good middle way if there is a way to enforce correctness at build time.
A new lint profile is required.
A new build tool too.
Legacy JS support is not going away.
A bundler will produce the correct code for older browsers.
But probably IE11 is out of the game anyway. I would love to have 2 builds, a legacy one and a modern one ( the classic last 2 versions of each browser )

So which are your reason to list TS on the pro/cons of this change?
I understand you may not like TS. that is fine. I don't like it either.
But if is terse, with types out of the way, and a transpiled build is available, what is your main point against or for it?

@asturur asturur pinned this issue Jan 10, 2022
@asturur asturur changed the title IMPORTANT: Migrating to TypeScript IMPORTANT: Migrating to modern javascript/typescript Jan 10, 2022
@jimmywarting
Copy link

please no typescript

@melchiar
Copy link
Member

@jimmywarting Typescript support is one of our most commonly requested features. Feel free to elaborate if you have specific objections to adding support for it.

@jimmywarting
Copy link

jimmywarting commented Jan 10, 2022

esm is also a desirable request, working with typescript and esm is a total nightmare. beside you can have a type safety system with vanila js + jsdoc and writing the code in such a manner that it don't require annotation... like using # instead of private, use classes instead of prototype, avoid using promise constructor and instead use async/await so the IDE can really figure out what something return and so on.

it's also possible to generate d.ts files from js also

i created https://jimmywarting.github.io/you-might-not-need-typescript/ to this end...

@andrewzolotukhin
Copy link

I think it's not necessary to migrate to Typescript, but it would be great to have up to date d.ts typings for Fabric.

@asturur
Copy link
Member Author

asturur commented Jan 10, 2022

i was supposed to fill up the issue text. I ll do soon. We are looking for suggestion, we are all ears and we don't have strong opinions yet.

We would like to move away from legacy js anyway.

@ShaMan123
Copy link
Contributor

ShaMan123 commented Jan 10, 2022

I am for TS (gave a read to what you wrote @jimmywarting, looks interesting).
IMO the disadvantage of .d.ts files is maintenance. Because it is not part of the code it can quickly become outdated and irrelevant.
Personally speaking I find TS most helpful in replacing the need for docs in my workflow.
My IDE suggests whatever TS has to offer and my learning curve grows exponentially because of it.
Bottom line, that's what won me over, besides the trivial (great 😅) advantages.
Regarding the type nightmare - I understand what you mean and I don't deny, but I would expect the pain to reduce after the first adoption phase.
I can counter with my pains from js. The first that come to mind - not knowing function signatures and having to guess, remember them or log arguments if the above fails. Or some minor typo that breaks my code and an error that sends me to a wild goose chase after the wrong thing. Is JSDOC typedef visible outside of it's file/module? If not it is an absolute killer to any kind of js typings.
From the maintainer point of view I think it cuts down Q&A as well.
So I'm for.
I do think that a simple configuration file (tsconfig.json) in a project can silence ts for a dev that doesn't want it.
And it is the standard as far as I can tell (saw many large repros migrate to ts, including google open source).
I think that when considering this move we should try to foresee what is right for 1-2 years ahead, not only for today.
In other words, we need to think what is best for fabric in the long term (and hopefully the short as well).
I would like fabric to be easy. Easy to use, easy to learn, easy to plug and play, easy to contribute to, easy to maintain.
That's what I want to achieve from the migration.

@jimmywarting
Copy link

jimmywarting commented Jan 10, 2022

IMO the disadvantage of .d.ts files is maintenance. Because it is not part of the code it can quickly become outdated and irrelevant.

this can be auto generated durning release...

I think that when considering this move we should try to foresee what is right for 1-2 years ahead, not only for today.

what would you think could happen when optional static typing comes into play? typescript will become obsolete.

saw many large repros migrate to ts

just b/c big companies did it dosen't mean it's better than one or the other, it just comes to a preferences and the developers background (like coming from c, rust, go or any other typed language)

there are ppl who have ditched typescript and went back to vanilla js afterwards (even in big companies too)

For me it feels like typescript are trying to fit a square into a circle, javascript is a dynamic language, and it's nothing wrong with that, it can be a positive thing also...

there are ways to write the code in such a way that auto completion and annotation isn't necessary from either jsdoc or typescript also.
The public/private keyword and explicit return type for example are not needed, they are unnecessary, even for typescript.
the only time you really need to explicit say what something returns is if you do something like:

function sleepFoo() {
  return new Promise(rs => {
    setTimeout(() => {
      rs('bar')
    }, 100)
  })
}

which don't have to be annotated at all if you did:

async function sleepFoo() {
  await sleep(100)
  return 'bar'
}

beside typescript don't do any type protection after you compile it to js.
ppl can still make mistake afterwards.
typescript only provide type hints/error upfront durning developing.
And there is going to be ppl that don't use typescript at all

@Gerwin-prog
Copy link
Contributor

Gerwin-prog commented Jan 11, 2022

@asturur will you also move away from supporting Legacy JS entirely & only support TypeScript and/or ECMAScript Modules?

@kirill-konshin
Copy link

Based on my experience, most of the projects can be migrated to TS with minimal effort (obviously, the types will not be very strict, some compromises will take place), but it's a proper foundation to improve these types in future.

TS can work even w/o types as plain JS, types can be introduced function by function, file by file.

Additionally, w/o TS project will have to support custom .d.ts files, which is a nightmare, it's much easier to convert code to TS and generate .d.ts automatically.

@keelii
Copy link

keelii commented Jan 12, 2022

I am for TS too, because:

  1. old style javascript sometimes is hard to understand(even though you are expert on JS for years). prototype extends, contextual-this.., there is so much lines are hard to read in fabric.js
fabric.Object.prototype._setLineDash.call(this, ctx, this.selectionDashArray)
  1. when i implement my custom object.class, the createClass method sometimes misleading people(ES 6 class looks more explicity), it hard to know which method should be implemented or overrided(the underscore method or not or both?)
var MyClass = fabric.util.createClass(fabric.Object, {
    bar: [], // dont't do this
    initialize: function () {},
})
var c1 = new MyClass()
c1.bar.push(1)
var c2 = new MyClass()
console.log(c2.bar) // => [1]

var MyClass = fabric.util.createClass(fabric.Object, {
    initialize: function () {
        this.bar = [] // do this instead
    },
})
var c1 = new MyClass()
c1.bar.push(1)
var c2 = new MyClass()
console.log(c2.bar) // => []
  1. there are some global variables in fabric namespace, which is hard to find(you have to read the source code to find it), with TS, just give me a interface, problem solved.
fabric.disableStyleCopyPaste
fabric.copiedText
  1. when you built a large canvas-based project, the code style always OOP right? TS bring the full OOP support to JS, when i coding with TS, I feel confident, enjoyable. with JS i always confused, it too dynamic. in fabric.js the actionHandler in fabric.Control class is a absolute beautiful abstraction for object transform action, but when i involved in this part of code, it's hard to find where i am.

@asturur
Copy link
Member Author

asturur commented Jan 12, 2022

I updated the description.
Probably the best option is to maybe convert a non working example of a class, to see how does it looks like in modern JS and TS.
Maybe RECT that is a quick one.

@jafferhaider
Copy link
Contributor

I'm for vanilla JS, because we finally have decent traction on modern features being implemented in ES. Looking at the long-term, I feel it would be better for FabricJS to stay with a maturing vanilla JS because many new features on the roadmap will make TS irrelevant anyway.

If we were having this discussion a half a decade ago, I would have been all for TypeScript.

@kirill-konshin
Copy link

kirill-konshin commented Jan 13, 2022

Colleagues, main point about Typescript is it’s strong and flexible type system. No vanilla js (even with jsdoc) gives true type checking during compilation time. In all my JS projects migrated to TS, type checks found a number of potential issues that were overlooked.

Second reason is DTS (definition) files. Since we’re talking about library development, TS is a de facto standard. Definitions must be produced in any case, if we target large audience, which includes other TS projects.

Definitions on top of JS tend to have errors, mismatches and discrepancies, and require a lot of manual maintenance. Of course one can generate dts from jsdoc, but this will not do type checking… and in case JSDoc has issues those issues will silently leak into DTS, which means more maintenance efforts.

Third, TS is mature tool, with huge community, it’s safe and proven. Vanilla JS is catching up, it’s still years behind TS in terms of features. And even when it will catch up, vanilla won’t have types anyway, which brings us back to previous points.

PS. I forgot to mention, that most IDE are able to infer TS types even when they are not explicitly defined. TS compiler can do that too. This means less things to write, less things to support which in turn means less things that can go wrong.

@jimmywarting
Copy link

I don't consider any compile to js a "standard" language... It don't run anywhere without being compiled to js in the first place

It is built on top of standard js

@jimmywarting
Copy link

jimmywarting commented Jan 13, 2022

I like buildless setup a lot
It loads so insanely fast 🚀
Having esm would be cool too

Only time when i actually use any build tool is if I need to down level anything for older environments

But all browser basically have auto update now and IIE it's pretty dead...

@asturur
Copy link
Member Author

asturur commented Jan 13, 2022

which are the functions of TS that are better than JS, types apart? i thought where identical.
I wonder if we can get best of bot, we write in ts, and we can build into plain js and bundled legacy js

@jimmywarting
Copy link

I wonder if we can get best of both, we write in TS, and we can build into plain JS and bundled legacy JS

  • When you write in TS and build to JS then there isn't any plain vanilla javascript anywhere anymore...
  • When you write in TS you get the best of TypeScript and the worst of javascript, annoying sourcemaps, extra compile time, bloated code that looks nowhere near the actual input. most often you get one single bundled file that are not so friendly code splitted, One more more language to learn, soft private methods that should not be used (if you use public/private instead of #), broken path resolving (when it comes to using extension - for esm) stuck with one extra chain of development pipeline for getting the newest hottest features b/c you need to wait for TypeScript to catch up with new standard javascript specification.
  • TS can transform your working code to broken stuff that no longer work (did u see my ESM example?)

The only way you are going to get both of both world is if you write in JS and turn on checkJS to utilize all TypeScript features, you may even use typescript to bundle to legacy js form plain js
jsdoc can do type checking

VScode has a nice interfear type from usage that helps u fill in the jsdoc when necessary, you can write interfaces (typedef) and import them with jsdoc as well...

@asturur
Copy link
Member Author

asturur commented Jan 13, 2022

yes my initial idea was that strong jsdoc could generate d.ts files.
But i never found a proper tool to do it.

Isn't TS with ripped types just plain JS?
There must be an option for the compiler, don't touch js no?

@asturur
Copy link
Member Author

asturur commented Jan 13, 2022

ok let's start to check checkJS

@jimmywarting
Copy link

jimmywarting commented Jan 13, 2022

Isn't TS with ripped types just plain JS?

not when you have target set to like some very old environment, it's always going to do some form of transformation to your code. 1000 of developers think they write es syntax with their extension-less imports but the reality is that it most often is compiled to cjs, the lack of extension create wasteful IO time to look for the correct file...

There must be an option for the compiler, don't touch js no?

alloJS? ignore, exclude, include... something something

@kirill-konshin
Copy link

@jimmywarting Your point "annoying sourcemaps, extra compile time, bloated code that looks nowhere near the actual input" contradicts with your other point "target set to like some very old environment, it's always going to do some form of transformation to your code" — you can't publish raw ESM code.

"all browser basically have auto update now and IE it's pretty dead" — Random old browsers still exist in corporate sector, where auto update may be turned off... Also don't forget government sector. You still have to compile CJS and maybe even ES5. Obviously we should not target IE6 or IE7, but only targeting latest Chrome is another extreme.

Fabric is a very popular lib, most popular based on stars, it cannot throw away compatibility, so there WILL be a build/compile script, one or the other.

image

Competition is tough...

VSCode does have a good support for JSDoc, but I've never seen a reliable tool to check JSDoc types on CI, whereas TS compiler will do it during the build as normal part of CI process.

@asturur wrote:

which are the functions of TS that are better than JS, types apart? i thought where identical.
I wonder if we can get best of bot, we write in ts, and we can build into plain js and bundled legacy js
Isn't TS with ripped types just plain JS?"

This is true, for the most part JS now is TS without types, except a few features like decorators (which Fabric may use as strong-typed mixin replacement). When TS is used you get types for free, DTS for external consumption.

@jimmywarting
Copy link

jimmywarting commented Jan 13, 2022

you can't publish raw ESM code.

I could if i wanted to. I can code and develop in raw ESM, making sure it run just fine in latest chrome and maybe firefox, without having to compile anything.
And when i want to make a release then i can run some test that builds some UMD, test it in several browsers and releases both ESM (maybe as is - for those who wish to simply import() it) and also release the UMD version for the rest.

it's possible to use webpack, rollup or tsc or whatever.

@kirill-konshin
Copy link

kirill-konshin commented Jan 13, 2022

@jimmywarting

I could if i wanted to. I can code and develop in raw ESM, making sure it run just fine in latest chrome and maybe firefox, without having to compile anything. And when i want to make a release then i can run some test that builds some UMD

Yeah, you can do that, but you trade compilation time during dev at expense of testing and verification time. And again, you won't have to support DTS files separately... It's always a tradeoff. So unless you actually ship raw ESM I don't see practical benefits. I advocate for "Eating your own dog food" to be sure I am seeing what others will see, as close as it can be. And as early as I can, ideally during development, not verification.

Compilation time in watch mode of a library the size of fabric is few seconds. It's not a big deal. But then during development you can be sure the code you see is exactly what will be shipped.

To me, when you develop a LIBRARY — TS is a no brainer choice. When you develop an APP — your approach may be OK.

Also there are rust (SWC) or Go (esbuild) based compilers that are even faster than regular TSC/Babel/Rollup/etc., and looks like it's the general direction where industry is moving, Next.js used to be a JS project, and they did full transition to TS, it's just one example... overall the trend is towards TS in lib development.

Overall the choice should be based on if team will be able to deliver faster and better quality with TS or with JS + custom DTS. From my experience in multiple teams and products, TS helps to speed up overall delivery if all factors are considered (development, dev experience, CI, QA, support).

@ShaMan123
Copy link
Contributor

ShaMan123 commented Jan 13, 2022

I think it is clear that fabric should not and cannot deal with the build process. It is unwise, to say the least, and a damn waste of effort.
The effort should be targeted towards fabric and making it awesome. Let everyone do what they do best.
And of course fabric must use a liable mechanism, no alpha/beta products. Too much headache in something that no one should have to manage except the occasional ci config.
The patience to except alpha/beta and the occasional break/bug is ONLY intended for fabric features. We cannot abuse that. Already I argue it is abused with no need.
I mean to say the decision needs to be mainstream.
And it MUST make this repo easier and readable! Because it isn't!
I am an expert in fabric internals not because I want too. But because I had no choice but to dig in the source code and break my head against it. What other choice is there today??
Why should anyone want to work with a repo that demands so much just to get some minor customization working? It's too hard, messy, horrible DX.
The point is taking fabric forward, not drowning it with more noise.

@asturur
Copy link
Member Author

asturur commented Jan 14, 2022

Yes but indeed there would be 2 targets eventually. One to get modern JS out of TS, another to have the standard browser ready lib

@asturur
Copy link
Member Author

asturur commented Jan 14, 2022

I think it is clear that fabric should not and cannot deal with the build process. It is unwise, to say the least, and a damn waste of effort.

Well we don't get away without publishing a ready to consume module. Is what npm user expect.
You don't build stuff that is in your node_modules folder usually. You expect it to be ready to use.
If you want to use the modules right away you usually import them from the src directory.
There is not just a single way to do things of course, but i m sure we have to build.

@CaptEmulation
Copy link
Contributor

CaptEmulation commented Jan 14, 2022

Types would be amazing whether provided by declaration files or an actual Typescript port.

https://jimmywarting.github.io/you-might-not-need-typescript/ is interesting, and does make valid points. However, here I stand in a sea of Typescript projects wanting more Typescript :-)

@ShaMan123
Copy link
Contributor

ShaMan123 commented Sep 26, 2022

Could you define the next steps as you see it?
I would prefer seeing a PR you've done to understand the new standard.
Or make a list of dos/donts

@asturur
Copy link
Member Author

asturur commented Sep 26, 2022

i can convert object for the sake of seeing how it would look like.
The next step would be pull out what we lose if we don't add public fields in the fabricJS codebase and evaluate.
From my small deep dive, we don't lose anything.
Which are the different opinions?

@ShaMan123
Copy link
Contributor

I suggest leaving Object to the end if possible. It will be complex and messy with all the mixins and logic broken all across fabric, some even shared with canvas.
I would choose Rect or Circle, the simplest possible, wondering if it will be a viable POC if Object remains as is (??).

I am not fully grasping your point, forgive me. I will need to see code and review and ask and discuss then.
Sounds to me that some of these fields (e.g. cacheProperties) should become static.
Defaults of course can't.

@asturur
Copy link
Member Author

asturur commented Sep 26, 2022

Well but is exactly with the mixins and complexity that you can judge if an approach is good or bad, Circle will look good whatever you do, is very few lines.
I don't have to do a full functional object all in one go, but a PR that shows the code will be clear enough. On top of the thing that i suppose we have to let go the name Object, and use something else, like BaseObject? fabricObject?

@ShaMan123
Copy link
Contributor

sounds good
naming should be changed for sure
Shape?

@asturur
Copy link
Member Author

asturur commented Sep 26, 2022

Shape is too similar to what then people call generically all simple rects, circle, stars, and all the geometric basic figures. Also the Object comes with no shape, since has no render method. It is just our base class and we can't take the reserved word Object, BaseObject or BaseClass or FabricObject something like that. _Object is too lame

@Lazauya
Copy link
Contributor

Lazauya commented Sep 26, 2022

I vote FabricObject. I think it's the most clear and will be the least confusing when used in other codebases.

@asturur
Copy link
Member Author

asturur commented Sep 27, 2022

Yesterday i prepared the code but i got stuck in one of those problem where our mixed import strategy makes now object undefined and i have no idea how to fixed. Working on it.

@asturur
Copy link
Member Author

asturur commented Sep 27, 2022

#8322

Ok this is a PR.
Go with the negatives please, pointing out issues ( that are not cited already in the pr description )

@ideaviewes
Copy link

please provide a version of react native

@ShaMan123
Copy link
Contributor

@ideaviewes it is out of scope for this thread.
I suggest you open a dedicated discussion.

@gloriousjob
Copy link
Contributor

Hi, what's next for this? Is there a way to contribute? I've been rooting for Typescript integration since I've been using this.
I noticed there were concerns about making cacheProperties static. If it's about getting the property using this, I think that might work with static, as long as there's not a non-static with the same name. If that idea isn't exciting, there's also creating a method that could be overridden by subclasses and each class would just return the static variable in its version.

@fallenartist
Copy link

I'm sorry for bumping into this adult conversation but I thought I might represent a voice of us little folk that: a) are enjoying Fabric and are hoping to utilise it more in the future, and b) are not coding ninjas and sometimes struggle with more advanced concepts of JS. I do hope that moving in whatever direction will not mean making using Fabric any less problematic for non-expert programmers like me and there will be enough examples and well written docs that it will be possible to use it without a need to hire a pro or rush to SO with every little issue. I've had this experience with D3 where it became somewhat harder and harder to use recently (but that might well be the little old brain of mine!). Thanks and good luck!

@SLKnutson
Copy link
Contributor

Just a minor thing, you can use ts-expect-error instead of ts-ignore. The difference is that ts-expect-error won't compile if the next line is not a typescript error. That way, when the underlying issue is fixed, you will automatically notice that the issue is fixed and typing can be re-enabled for that line.

@ShaMan123
Copy link
Contributor

ShaMan123 commented Feb 14, 2023

Hi everyone,

A year has passed since this ticket was opened.

Beta is out!
Published last week, just in case anyone missed it.
You can install it via

npm i fabric@beta

We migrated the entire codebase and made significant changes and fixes, see #8299 and the CHANGELOG.
Most of the codebase is fully typed. Some complex modules remain in need of typing and perhaps more touch ups.

Building is done with rollup.
fabric has its own cli now so you can enter npm run cli -- -h and continue from there.
You may experience stuff with consuming types and/or the build, submit an issue if you do. We are still figuring that out.

Repository docs have been updated as well to reflect v6 changes (README, CONTIBUTION etc.). More work is needed in this field. The website remains outdated.
This will be the next major effort after we release v6. We will announce updates about that in the future.

Remember it is beta, things aren't final and may change (the entry point especially).
Considering the scope of changes, regressions might surface.
Also take into consideration a shift in the way fabric is used. Subclassing is now super easy (class is no. 1 citizen in v6) and there is no fabric namespace.

As always, contributions are welcome (either TS, website or anything else), see #8316. Ping one of us in an issue/discussion/PR so we can direct the efforts and give a hand or suggest a solution.

@bladerunner2020
Copy link

With T or without, but we are badly missing some types in v6. For example, BaseFabricObject, TEvent, ObjectEvents, etc.

@ShaMan123
Copy link
Contributor

@bladerunner2020 tracker issue #8868

@urschrei
Copy link

urschrei commented May 15, 2023

Is there a new way to access the .fabric property of a Canvas element in the beta?

This

import {Canvas, Image} from 'fabric';

const canvas = new Canvas('certcanvas');
document.getElementById('certcanvas').fabric = canvas;
Image.fromURL('assets/img/cert.png', function(img) {
    img.set({
        lockMovementX: true,
        lockMovementY: true,
        selectable: false
    });
    img.scaleToHeight(566);
    img.scaleToWidth(800);
    canvas.add(img);

Doesn't seem to work for me…

Oops, just catching up on the promise-ification of Image and Textbox … any links to some examples would be great.

@ShaMan123
Copy link
Contributor

fabric is now typed - use that.
I wouldn't wait for the website, unless you join the effort and give a hand.

@ShaMan123
Copy link
Contributor

ShaMan123 commented Jul 20, 2023

For the greater community:

We want to wrap up the TS migration and we need help.

Contributions are needed!

Current task is to type the remaining files and address TS errors.
Contributors can search for files with @ts-nocheck and get to work.
Open a draft PR with the title chore(TS): [SCOPE] once you get started stating which files you wish to address so people don't work on the same thing.
Then when you are done, move it out of the draft state and review it and then one of us will review as well.
Be sure to check for open PRs before you start working.
If someone wishes to do it all go ahead.

Thanks!

Originally posted by @ShaMan123 in #8299 (comment)

@asturur
Copy link
Member Author

asturur commented Jul 25, 2023

Ok we are ready to close this.
The last ts-nocheck have been removed, now the library is fully typed.

We are doing a last check up of the api we changed during this year of migrations and we are releasing a stable 6.0.
We will handle missing exported types as patches ( 6.0.x ) but we will try to catch all the missing exports before.

Eraser brush is not ready yet and is going to be added as the next thing.
We are also working on docs and we will try to update all the introductions to fabricJS on the website with modern content that make sense
Some of them have been written in 2011 and are outadated.

@asturur asturur closed this as completed Jul 25, 2023
@asturur asturur unpinned this issue Oct 17, 2023
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