JavaScript Gom Jabbar
frantic.im
frantic.im
What other things that were JS nightmares in 2013 have largely ceased to exist? (Only to be replaced by this funny list.)
This is my favorite part: "You stop to count how many tools and parsers work on your codebase: TypeScript, esbuild, swc, babel, eslint, prettier, jest, webpack, rollup, terser. You are not sure if you missed any. You are not sure if you want to know. The level of pain is so high you forget about anything else."
Ah yes, over a decade of JS.
But there are other degrees of ExtJS nostalgia:
- Being allowed to do everything in a desktop-like manner, and not having to bother too much with either mobile or more stylized web applications, even in enterprise contexts. No technical issue, no technical solution.
- Doing that in a simple manner. Even contemporary (> v4 or even v3) ExtJS might not be "ExtJS" in this regard, as the framework part got a lot more complex. First with their own weird tooling, nowadays with the worst of both worlds.
- Not having to touch HTML and CSS. Early ExtJS allowed you to do most things with heavily nested JS literal objects. With angular/react/etc. it's all separate syntaxes, most of them with angle brackets.
We may have some by 2050 thanks to https://open-ui.org
Browsers did pour billions of dollars and hundreds of thousands of man-hours into a lot of bullshit though :(
Oh the foot guns of `this`
Omg, that’s awesome. I think anyone feeling like a corporate mindless drone should sneak that into a code base.
And I thought overriding toString() was naughty.
In my defense, on my personal projects I have started using var instead of let/const, snake_case everything, don’t use semicolons, manage my objects with my own prototypical inheritance, and am fucking around with my own shitty Canvas2D widgets. Turns out ditching the DOM and using a Turing complete language still beats the crap out of wrestling with the DOM even if you have to reinvent things like word wrap and text areas.
Oh! And there’s not a framework in sight.
The funny thing this is, my productivity and error rate have way improved. Prob coincidence but I do actually enjoy it much more.
Take that ChatGPT!
edit: I actually write web assembly by hand too. Binary even. And have my own little shim in wasm to override its type safety, put code in my heap space pure von Neumann style, and have my own program counter so I can jump and goto. Can’t wait to start doing self-mutating code.
I know this sounds like I’m being sarcastic here- I often actually am- but I kid you not my error rate has plummeted and its become a blast to program again.
My internet actually slowed to dialup speeds lately so I’m only going to MDN or Stack Overflow if I really have to as well.
‘Modern javarscript’? Hahahaha.
undefined became U (for example). An 8-byte or more reduction per reference. What can I say? Size used to matter. (Been doing JS since 1996.)
Though I should try this, using a variable alias for undefined and null would maximize minification.
I have has a misfortune of spending today writing a test assignment for a company that still uses vanilla JS, without promises or declaration files. Felt right back in 2012 when I first tried node. Worked around the requirement of using JS by utilising JSDoc, but still — it was much worse than I remembered.
No sessionStorage? Use window.name. No (then-) modern CSS? Use CSS3PIE [0]. IE doesn't support the transform CSS property? Use an *.htc behavior to convert the transform to a matrix filter.
It was madness, and it was beautiful in a Cthulhu kind of way.
The BIG pain point for me, though, looking at my 10-year-old JS, is that it's all made of "classes" built up by modifying SomeFunction.prototype, which then get wrapped in a self-invoking anonymous function and returned as an object, just so you can call `new SomeFunction()` ... `SomeFunction.actualFunction()` as if SomeFunction were a class. That and also the lack of arrow notation; modifying Function.prototype itself to use .apply() to bind events to scope (or else just an endless abyss of .bind(this) in event callbacks)...
This is literally 20 lines of code, and it doesn't take that much to learn to read it easily. When I originally implemented LLM-based applications in Python, it would make about 10 times more, and all the components wouldn't be as modular. I can change the functionality of this code in a very drastic way with minimal code changes and with safety of completely statically type checked code.
That is, it's a yet another library trying to force Haskell/Haskellisms into Javascript and claiming with no proof other than emotions that it's so much better and more readable than something.
Here's an actual readable version of that:
try{
return getTurnPromptMessages(state, action)
.map(mkCCRequestFromMessages)
.map(addSchemaToRequest)
.map(handleChatCompletion)
.map(extractFirstCompletionChoice)
.map(extractChatCompletionMessage)
.map(decodeSchema)
.map(detectLocation);
} catch (e) {
mkMessageError(e);
}
It can be further simplified. For example, you don't need two separate functions to extract the first chat completion message etc.This version:
- uses existing language constructs
- can be immediately understood even by the most junior devs
- is likely to be 1000 times faster
- does not rely on an external dependency that currently has 143 issues and every two weeks releases a new version adding dozens of new methods to things
Note: one thing I do wish Javascript adopted is pipes: https://github.com/tc39/proposal-pipeline-operator
* all but the first results are not arrays, so the `.map` function doesn't apply
* doesn't handle async operations
* catches ALL exceptions here, whereas my code only handles statically typed and well-defined errors
* doesn't include the GPT-generated message and messages from the first steps in the error
* doesn't include retry functionality
I appreciate this argument and time you've taken to read my code and write this counter-argument. Please don't mistake my direct code for animosity, I clearly see that you're a smart person even though I think you're wrong about this.
Also, the fp-ts doesn't add any runtime performance overhead here. It's a couple of extra function calls for a call that has an async operation that likely takes 30 seconds (GPT-4 is slow). Obviously, if I was writing code that runs thousands of times per second, I would not use FP.
It does.
It was also somewhat tongue-in-cheek, and a five-second copy-paste of your code.
> all but the first results are not arrays, so the `.map` function doesn't apply
Unless you return arrays from all functions.
Not that different from wrapping every result into seventeen layers of functional abstractions. What do you think flow or flatMapEither does?
> doesn't include the GPT-generated message and messages from the first steps in the error
Of course it does. That's what the catch is for.
Or you could use an additional map if you want.
> Also, the fp-ts doesn't add any runtime performance overhead here. It's a couple of extra function calls
Of course it adds runtime performance overhead. Any additional function call adds a performance overhead. And in your case it's dozens of functions wrapping trivial functionality.
> Obviously, if I was writing code that runs thousands of times per second, I would not use FP.
Then why use it here? If the alternative is more readable, more traceable, has better stack traces for errors, doesn't require an unstable ever changing dependency...
But that's not... what they actually do?
> Not that different from wrapping every result into seventeen layers of functional abstractions. What do you think flow or flatMapEither does?
It changes what functions return without having to adapt them to particular use-case.
> Of course it does. That's what the catch is for.
To achieve that, the catch has to assume that the functions throw exceptions with particular data. This cannot be enforced with type system, and once again, tied implementation of these functions to this particular context where they're used.
> Of course it adds runtime performance overhead. Any additional function call adds a performance overhead.
The function call overhead is a couple of dozens nanoseconds at best. And we're talking about a remote call that takes 30 seconds to complete and costs $0,05 per OpenAI's API.
The overhead you're talking about is 0,0000001% in time and I think a few orders less in cost.
> If the alternative is more readable
It's not. An alternative that would actually have all the same functionality would be 4 times as long and more importantly, much more complex to change in a meaningful way.
> more traceable, has better stack traces for errors
But that's the point: we don't need stack traces. We don't need exceptions for errors that we know how to handle.
That's like complaining that a GC-based language doesn't have a tool like valgrind to handle error allocation and deallocation issues.
> doesn't require an unstable ever changing dependency...
I've never had a breaking change from fp-ts and don't expect it to ever happen because of the nature of the library.
Usually it's a front-end project, so I'll pick my framework. Usually React, although I've been wanting to try out Svelte.
Next my bundler, which used to be create-react-app, or some other Webpack wrapper. Today, Vite is the best choice because it works pretty well without much configuration, and it supports all of the major frameworks which is quite nice.
I'll be sure to install and configure the TypeScript compiler, which is usually copy-pasting from a template. I always make sure to target the latest well-supported version of JS and use ESM modules, because I like being on the bleeding edge.
Next up I setup linters -- eslint + prettier. They do a good job. I have to configure eslint so that it plays nice with Prettier, and I also have to configure eslint for TypeScript and add any other plugins that the project might need, e.g. those for React. I usually like to setup some kind of pre-commit hook to run Prettier and eslint before committing, which saves time.
Additionally, I always install my IDE extensions and try to create a .devcontainer file or add list of recommended extensions so that both myself and others can setup their IDEs in the future. I tend to not configure eslint much and just use off-the-shelf configurations because I don't care about the style, I just want consistency in the code and mistakes to be caught.
Next up I setup my unit tests, usually jest or vitest or similar. Lots more configuration for TypeScript and whatever, and also usually a set of eslint rules.
Then I want a CSS framework. I like tailwind, so I install that and styled-components and twin.macro so that it all works nicely with React. Fonts and icons are often needed too, so I install a plugin for Google Fonts and whatever font library.
Anyway, after about a day of doing the above I'm ready to program. Why is it so much work? Why isn't there something that just works for 90% of the use cases? So many people are using Vite + TypeScript + [React|Vue|Svelte] + [eslint|prettier] + [tailwind|bootstrap] + [jest|vitest]. Why isn't there some non-configurable library that just does everything for me?
I admit, it would be nice to have something like Maven Archetype to have things a bit more formalized, but for most of the time a good old copy-paste works well enough.
People still using text editors like EMACS or VI need to do this themselves, often through some random mish-mash of unique and special text templates, scripts, and copy-pasting from previous projects.
Then unironically they'll proudly proclaim to be productive because of all the tooling they've successfully set up over many days of effort.
But if you CR the Yeoman generator, every project’s initial commit can start from a known-good state since all you need to do is provide it the project name and location.
It doesn’t have as many rules, but you wanted to get rid of all the cruft anyway, so that’s a feature, not a bug.
I want to migrate a project I have, but I fear that it will get abandoned and in the future I will need to migrate away from it again.
https://github.com/rome/tools/discussions/4302
"But in short, the company Rome Tools ran out of funding, so the core team of last year are no longer working on the project."
Of course this also seems to be second-hand information, as there is an "Anyone feel free to correct me" disclaimer at the end of the message above.
I’ve heard Java teams use SonarCube/SonarLint, checkstyle, findbugs, and PDM in various combinations together. I guess the Java ecosystem is insane and full of pain.
I hear Go programmers use gofmt and go vet. I guess the Go ecosystem is insane and full of pain.
I hear Rust programmers use clippy and rustfmt. I guess the Rust ecosystem is insane and full of pain.
I hear Python programmers use flake8 and black. I guess the Python ecosystem is insane and full of pain.
If I were trying to do something less complicated, I'd just install less stuff. For example, I don't normally setup devcontainers or Google Fonts, and if I need icons, I might later on install some generic icon SVG library. And if it's just a small project, I probably won't add ESLint or testing. I often skip tailwind as well if I can - it's not bad, but CSS (+SCSS) is built into Vite so it's just less stuff to configure.
But each of those choices has a good reason, otherwise you wouldn't be using it. Tailwind makes writing CSS a lot quicker, linters help us catch more mistakes, devcontainers make it easier to get everyone on the same page, Typescript is incredible for so many reasons, and bundlers handle a lot of the minutiae of getting the right source code into the right destination.
And a big part of a software developer's job is looking at a list of things like that and knowing why each component is there in the list, and whether the value it adds is worth the complexity it adds. For example, in your list, I'd probably drop styled components and the macro toolchain, because tailwind on its own is good enough, and it's pretty easy to write your own components if you need to encapsulate styles somewhere. But you might be doing that a lot, so it might be a different tradeoff for you.
Which comes back to your question: why isn't there something that does everything? Well, because everything for the two of us will look different. You're using React and possibly Svelte, I'm mostly using SolidJS. You're using devcontainers, I'm not. You're using tailwind and styled components, I'm using tailwind and CSS/SCSS modules. A lot of people don't use tailwind at all.
The alternative is something more like Ruby on Rails where everything is preconfigured, but there are no choices any more - you use the tools and architecture as provided, and can't opt in or out of any parts. You don't get to try out Typescript until the framework authors have allowed it, for example. There are a lot of benefits to that style of framework, but it has issues as soon as you try to step off the beaten track. Like with anything, it's a tradeoff.
I think the other reason it's not that popular is that the current state of affairs isn't that complicated. You give a big list of things, but how hard is it to configure them? For a lot of stuff, the defaults will be good enough (for Typescript, I pretty much always just use the result of `tsc --init` but with isolated modules turned on). And the rest you've already configured - you can just copy the configuration from a previous project over. If you wanted to formalise that, you could create a template repo (and potentially publish it and get it to work with `npm init`), but that presumably depends on how often you're creating new projects.
I _love_ all of the tools that I mentioned. Like you said, I use them because they add value. I also use them because I want to learn them -- I like trying out new tools/libraries with personal projects, since I don't do much front-end at work.
> And a big part of a software developer's job is looking at a list of things like that and knowing why each component is there in the list, and whether the value it adds is worth the complexity it adds.
This is a great piece of insight. It's something I'll have to think about.
> I think the other reason it's not that popular is that the current state of affairs isn't that complicated
I do think that most not in the front-end space would disagree, especially considering how fast the ecosystem hops on something new.
$ npm init vite
And you know all of this. I, as a lowly backend dev + operations guy have tried to get into these stacks for a few times over the years and honestly, in many cases, I just failed. Or I got some tool chain going, but I felt like I had zero control over it and the slightest wobble of anything would just cause it to fall apart and I wouldn't be able to fix it.
I dunno, I just recently made the decision to consider most if not all of this JS stack as a blocker for what I want to do. Then I setup some flask application with jinja2-based server side rendering like back in the day. It doesn't look as modern or shiny (and currently rather ugly due to a lack of any CSS, but I've been told I should stick with pure modern CSS, so let's do that :) ). But it is a blast how much functionality you can get done quickly in that stack.
But I think that's just a familiarity thing. I know what I'm doing when it comes to frontend development, so it's not an issue to get a new project off the ground, but backend development - and particularly backend development in Python - is something that I only touch occasionally, and therefore have to futz around with every time.
But this is also where I'd disagree partially: The backend world moves a lot slower and feels a lot more settled to me. Sure, I'm relying on experiences as a sysadmin and a python developer, but: (i) If you need a VPS, you still have the 4-5 choices you had for years, Hetzner (Cloud by now), Digital Ocean, Linode, AWS Free Tier. (ii) SSL-Fronting had the same answer for I think 10 - 15 years by now. Front it with an Apache or Nginx proxy with Lets Encrypt. LE was the biggest change there. (iii) The ancient good old python3 -m venv + activate + pip is still very much valid and recommended. I'm currently trying out poetry, but even that uses venvs and pip in the background. (iv) And then assets, templates, and even production deployment guidelines are mentioned e.g. in the wonderful quickstart of Flask [1]. And there are several really solid and stable development tools around there for linting, formatting, typecheckind. VSCode for example recommends them quickly.
Most of this has been stable for years while being carefully improved. On a similar page, I haven't been working as an active java developer in like 6 years. I can still readily understand pretty much all java builds in the company because the tooling is just stable.
The 2-3 tries I had to get into the JS ecosystem meant I had to reset most of my knowledge because all of the frameworks, toolings had been deprecated, replaced, rewritten and anything else.
I'm sure the answers to you are fairly obvious (and in fairness, a lot of them are fairly obvious to me), but that's often the same feeling I get when people ask similar questions about the frontend ecosystem. Like, if you're not worried about doing anything complicated, pretty much all the libraries that I use on a daily basis also provide a `.min.js` download on the GitHub page, and you can just pack those into a folder and get started writing your code. If you need a bundler, just install NodeJS, and then run `npx vite` in a folder with some HTML files in it, and you'll be up and running in no time.
* My point here is to be sarcastic, but this is one of those things that genuinely surprises me about the Python ecosystem every time I have to interact with it: the default way of installing dependencies is completely insufficient for creating at least halfway consistent builds, and every alternative seems only designed to deal with the happy path.
You give up and become a pea farmer.
Like I can do `npm create vue` and I get a relatively nice and functioning Vue setup with most of the config already done. I agree it's a lot of boilerplate and random shenanigans, but also the apps we make for the web are more than they were. And you can absolutely write JS without all the tools, even with ESModules. But we don't do that much because turns out those tools actually do something. Do they do it in the cleanest way? No. Things could always be better. But they do it.
You still need to do additional configuration for your linter, and in my case (I did this last weekend) a lot of configuration for styled-component and twin.macro.
Even though it's easy to create projects, it does nothing for maintaining them. Updating all of these dependencies _can_ be painful. eslint rules are not very easy to configure, there are a lot of helpful rulesets out there which ideally would be bundled with the library they pertain to. For example, it would be nice if eslint saw I was using React and automatically applied recommended React rules, and the same for other libraries.
I've also looked into asp.net lately, and it seems to have just as many moving parts, only difference is that most of it is first party.
When you start a new project, clone it and rename some bits and you should be set.
Assuming it’s an app (and not a library) to get what you are describing you would run:
npx confgen@latest @app vite dist:app typescript eslint prettier react
Unlike create-react-app the idea is you can keep running that command, adding other runtimes and presets, and it will keep working. That’s the sense in which it’s “idempotent”.It’s somewhat opinionated (vite, react only) for now. But I’d be happy to see pull requests for other stacks so long as things don’t get too complex.
It’s also pre-1.0 but I’ve used it on dozens of projects and it’s somewhat stable.
> It’s time for your test. You are sitting in front of a computer. The test is simple: you have to open a package.json file and read it.
And it continues in that vein. For all the stuff mentioned, there's actually almost nothing that has anything to do with JS per se.
To my astonishment, it worked, without a package.json file.
I definitely prefer using npm for any non-trivial front end, but let's be real: JS is an integral part of the web stack. You can even inline JS inside a <script> tag on an HTML file. It's not that hard.
You run prettier though? How do you download and run it - it seems to require Node.
Bundle with packup: https://packup.deno.dev/
I have, and I continue to. It's not hard.
To give one example: Firefox, a serious desktop application by any measure, was written in JS from the very beginning. (Yes, really. A combination of JS and C++, at least—and, nowadays, Rust, too; not unlike the way that Emacs is written in Lisp, where that means a combination of Lisp around its C core.) That's hundreds of thousands of lines of JS we're talking about, and it predates Node and all of the bundler/package.json stuff that you are implicitly arguing is somehow unavoidable. Firefox is an existence proof that that's not true (and also happens to be a higher quality app than anything I've ever seen the Node community offer as example to serve as testaments to their methodology, including anything to do with Electron, which cribs the same Greenspun, Emacs-style architecture that's behind Firefox).
I worked on the JS docs on developer.mozilla.org in the early days of the site. (Also before Node ever existed.) I did this because I wanted people to understand JS. It's a much nicer—and more thoughtfully designed—language than it gets credit for. It pains me, therefore, when I see people refer things as JS when what they should be saying is "stuff associated with the kind of software of dubious quality that developers who decided to rally around NPM have been putting out into the world for the last decade—and more often than not actually goes against the natural grain and spirit of the language".
I also write JavaScript code without NodeJS and package.json. It's not hard when the code is simple. But it doesn't scale. For a complex code, I want a linter, I want automated tests and coverage, I want to split the source code over many files... NodeJS is the standard requirement for all of these.
> Firefox, a serious desktop application by any measure, was written in JS from the very beginning. (Yes, really. A combination of JS and C++, at least
At the very beginning, this web browser started under the name "Netscape", and JavaScript didn't exist yet. Mozilla forked out of Netscape in 1998, and there was little JS in the source code at this time.
> it predates Node and all of the bundler/package.json stuff that you are implicitly arguing is somehow unavoidable.
Firefox doesn't use the standard bundlers of the Node ecosystem, but it's because it has its own build systems and bundlers. It's a beast that needs 30GB of disk to compile, with a custom build system written in shell, Perl and mostly Python, with many tests systems (including for "mochitest" JS code).
For instance, among the dozen of linters that Firefox uses, there is eslint, which a normal JS project would install through a package.json.
> JS. It's a much nicer—and more thoughtfully designed—language than it gets credit for.
JS was notoriously designed and implemented in a rush, because Netscape was under pressure from Microsoft. It also suffered from legal constraints that block the adoption of some Java concepts. Once LiveScript/JavaScript was born, its evolution was chaotic since the browsers war prevented any consistent progress of the language. After it settled, Ecmascript did enhance the language at a fast pace, but it cannot fix the many inconsistencies and design flaws.
I have no idea what point you are trying to make here. Firefox—not Netscape—has been a JS-driven app since its very first day of existence. (Netscape Navigator 6.x, which has nothing to do with Netscape of the early-to-mid 90s, is the same way—but that's beside the point: I said Firefox, and that's what we're talking about.)
> Firefox doesn't use the standard bundlers of the Node ecosystem, but it's because it has its own build systems and bundlers. It's a beast that needs 30GB of disk to compile, with a custom build system written in shell, Perl and mostly Python, with many tests systems (including for "mochitest" JS code).
I don't know what Firefox's build system looks like now, but at the time that Node debuted (2009), Firefox 3 was the latest release, with Firefox 4 development underway. It did have (and always had) a pretty gnarly build system made of shell scripts, Perl, autotools, et cetera... the same as almost anything that involved reliably compiling a large project with C/C++ across many distros (not to mention other platforms), which was the entire reason behind it. The use of JavaScript, however, had nothing to do with that; there were no JS bundlers. You brought symbols in scope by adding a script element. You still can.
This is also how WebKit's inspector is written to this day, last I checked (~6 months ago or so).
> JS was notoriously designed and implemented in a rush
Again, no idea what point you're trying to make here. JavaScript was done in a rush. It's also thoughtfully designed—certainly more than it gets credit for—and more than other languages that had more time (and less of an excuse). And it remains pretty nice, popular misconceptions notwithstanding (and not helped by self-loathing/self-sabotaging developers from the GitHub era like the author of the submitted article).
> But it doesn't scale. For a complex code, I want a linter, I want automated tests and coverage, I want to split the source code over many files... NodeJS is the standard requirement for all of these.
Hard to cover this succinctly given how much weird stuff is packed in here.
Hundreds of thousands of lines of code of higher quality (and subject to harsher requirements) than the Node fanclub has ever produced is proof that JS scales.
Testing is a basic engineering concern and common across the industry and across all languages. It's a completely orthogonal issue.
Lint as much as you like if that's what you want. Neither Node nor the Node way of development are in any sense a requirement.
For having source code "split [...] over many files", whether you use Node or not (or follow their fads) is neither here nor there...
But then again, I’m a happy, sane person who only writes client-side JS.
I've never written backend javascript. I've used and fought all the tools mentioned
what happens if your project has tens or even hundreds files, how do you get that to client without any sort of bundler?
> I’ve been writing JS my entire career and never wrote a package.json
And you replied:
> how do you get that to client without any sort of bundler?
Please connect the dots for me.
But first, I try to avoid using JavaScript in the first place when not strictly necessary. This means server-side rendering as much as possible rather than doing the glacial "load an almost entirely blank page, load giant JS blobs, then load the page content via AJAX and hope that nothing goes wrong in that process, sorry slow connection users, non-standard browser users, and search engine bots" approach which thankfully is finally starting to see some pushback.
I have never used a package.json file in my 10+ year writing javascript & typescript. We don't have nodejs installed on our workstations or servers. A lot of work has gone into that build chain, and I have no idea what it is doing (apart from something something bazel & monorepo) but it works.
At home I use Deno and it is a joy.
It's a critique of the JavaScript ecosystem.
We just covered this.
If you have a grievance against The Node Way of software development and you want to air it publicly, then have enough of a sense of awareness that that's what you're doing—and say as much when you do it.
Where subtree constitutes ~90% or more of the ecosystem.
Aside from that, do you have anything meaningful to say about the points actually raised? Let's take your claim as a given. If it were 2003 instead of 2023 and people kept bringing up, say, problems with Windows but refused to speak about them in those terms—instead insisting on framing with a generalization about "computers"—would that have been acceptable on the basis that 90+% of the world is using Windows? It plainly wouldn't have, and that's true for a case where we actually know that we were dealing with numbers of 90% or more, and not just lazy attempts at retorts that are fired from the hip and by a reasonable guess probably won't actually hold up under scrutiny, anyway.
You didn't raise points. You went off on a rant by completely dismissing the tongue-in-cheek article about tooling issues in the frontend space as something about "node-only".
All the points the author raised are:
- about frontend aka client-side
- about javascript ecosystem
And this ecosystem is ubiquitous. Much more ubiquitous than "no, this is not frontend, I do client-side and I don't use it".
> If it were 2003 instead of 2023 and people kept bringing up, say, problems with Windows but refused to speak about them in those terms—instead insisting on framing with a generalization about "computers"
See? This is not a point. This is argument by analogy. You're pretending that this analogy is valid (it isn't) and you're expecting me to argue with you.
I wrote a perfectly lucid response which you ignored completely. Your choosing to ignore it, or to read it as if it means something other than what it does, does not mean that I don't have a point (wat).
> Much more ubiquitous than "no, this is not frontend, I do client-side and I don't use it".
Who are you quoting (and what is it even supposed to mean, besides)?
> you're expecting me to argue with you
Nope.
I wrote why, and you definitely literally ignored what I wrote.
> Who are you quoting (and what is it even supposed to mean, besides)?
I'm very shortly representing what you wrote. So it's for you to answer what this means.
> Nope
Yup. Otherwise you wouldn't resort to bad analogies.
Anyway. This is going nowhere.
Adieu.
No, you aren't—and the point at which you are attempting to explain what it is that I really meant or was thinking (explaining me... to me) is the point at which you have stumbled upon a strong indicator that you have lost the plot. Viz:
> Yup [you are expecting me to argue with you]
You are right that JavaScript !== NodeJS, but the majority of people would understand what the author meant.
It wouldn't hurt the author to be a bit more precise, but they aren't really wrong either.
I'm always surprised by developers who prefer to use Node on the backend.
I used Node for a small project. Never again.
No typing, requiring a couple of dependencies just to get poor, half-assed typing that doesn't really work anyway.
Poor dependency management, although this is probably not the fault of the tool, it's the fault of a poor ecosystem dominated by junk.
A primitive execution model, requiring hacks around hacks to get around the fact that the execution model is based on single-thread async execution.
Speaking of execution, slow execution too.
Limited IDE help (due to lack of typing). Difficult debugging tooling. No standardisation of anything.
The people using Node know that there's alternatives like Go, and yet they still patch on band-aid after band-aid and engage in drama.
Using Node for the backend is a special kind of hell.
I don't even like Go all that much, but it's a world of difference having strong static typing, nice dependency management, good tooling, simple debugging, no setup costs.
Someone upthread made the point about how they might burn a day just setting up a new Node project. A new Go project might take a few minutes from inception to "hello world".
With Node, not only is it optional, it's a third party dependency.
Really? That's your defense of both not having type-checking AND not having optional type-checking builtin?
All common languages implementations, other than Node with JS, either have type-checking or have optional type-checking builtin.
Node and JS stand alone by having no type-checking, nor having optional type-checking builtin.
Node is uniquely low-quality in this respect.
Seriously though: modern JS ecosystem is not a "language" or a "framework". It's a kernel. You can choose your own language, whether it's Typescript, Coffescript, Purescript, Clojurescript, Reason or one of it's sublings, Elm, or pure JS. Of course, like with Linux, this can be a good or a bad thing for different users and circumstances: personally, I like Linux, but I don't want to handle all of this complexity on my own desktop.
And just like with Linux, you have to choose your own implementations for everything: testing library, strings, dates, templating library, state management, web server, etc.
If you're comparing the barebones Node and vanilla JS to other languages and frameworks, you're not making a valid comparison. You can't pretend that something is inferior just because it's modular.
You "two other", right? I've used many that didn't have a GUI.
> Seriously though: modern JS ecosystem is not a "language" or a "framework". It's a kernel.
That's not how it's pitched.
Good point, however, I don't think it makes the analogy invalid. You can replace "GUI" with "init system" or any other thing that separates OS kernel from a full-fledged OS.
> That's not how it's pitched.
There's truth in it. However, I think that the problem here is that we might have different expectations of what a "language" is. For some, it should be a complete package with all libraries and tools built-in — like go or Python. Racket even comes with it's own standard IDE. For other languages, like C, not even standard library is something expected, it's very basic and it's implementations are platform-specific.
Yes, if you take the basic Node, it will not have Typescript included. And for a developer that comes from a different background modifying your "language/framework" is something so weird and out of the ordinary that you don't really do that often. So you might inclined to judge Node ecosystem by your habits, but that's a mistake.
Just as with leftpad, developers from other ecosystems were in shock to find out how many small dependencies does a typical Node project have — because including a dependency is an expensive thing in their experience! But with Node, it's not. I can build and run a typical Node project with hundreds dependencies ~10 minutes after I clone a repo, but it takes me a few hours to configure the right version of Python, requirements and native C/C++ libraries for any significantly complex Python project that has a lot less.
So, instead of comparing the "type checking" out of the box, compare two reasonably sized projects after a year of active development by 5-10 person teams. And from my personal experience, a modern Typescript project would eliminate a lot more failure conditions at compile time than any other mainstream language.
If you want a good type system then you need to change languages and treat JavaScript as a compilation target. That's what most "JavaScript" professionals have been doing for years. Install the TypeScript compiler and a single dependency to give you Node-specific types and you're set
Typescript has the best type system among modern mainstream languages, and although you're right that Node is not well suited for CPU intensive applications, the most part of backend work is IO bound, where async/await works perfectly — waiting for reply from the bd looks and works much more natural than with Go's primitives.
As far as reflection goes, I see how it could be useful with other languages with less powerful type systems, for example, to build a deserialiser. But with Typescript's type inference I just never felt the need to reach for a crutch when I can use a proper solution.
I wouldn't call something the best when it's disabled by default, even after considering the fact that it's neither part of the language nor the platform, and needs multiple dependencies in order to run, at which point it has worse type-checking than every other language.
I'm not sure when this experience was. I've used TypeScript for years. About 5 years back it was okay, but it was very hit-or-miss for a given library to have types.
Today, I haven't had that problem. Every library I use has _great_ types, and many libraries are even written in TypeScript.
Anyway, all I'm saying is that you might want to give it a second change. TypeScript and the ecosystem has greatly improved. I hate working without static typing (Python, JS, and Ruby are pain), but TypeScript is really quite great.
Applications by and large do, but when you're writing a library you want to at least start without the usual bs.
JSDoc for type information works great as a crutch in this initial period.
EDIT: ok, now I recalled that I use Node's built-in test runner for tests. But I still don't have a package.json as well as any dependencies.
On the flip side, I can throw a hungry junior dev with a new macbook into the deep end, and they can be productive within a day. They don't have to know anything about containers, .net, nginx, or the top 100 most common command line tools. When they hit an issue they can just google it.
Now they chatgpt it.
> regularly
The trouble lies in the credible sounding results that are in fact subtly (or less subtly) nonsense.
It's a bit like old W3schools but instead of poor practices, its authors were half-in-the-bag
While I will take a 4 point karma hit from downvoters who don’t know how to use ChatGPT, this information is very important to share with all programmers.
>stochastic parrot
This is incorrect. In the context of learning JavaScript, ChatGPT with GPT 4 ($20/month) can analyze any error if you give it the error and the code, or you can just describe what’s wrong and give it the code you wrote and it will analyze it and tell you how to correct it, even if your code is the first time anyone has done that. It might take it two tries but it’ll figure it out. It obviously understands what you tell it and follows your instructions pretty well.
Limitations: the code you give it has to be small; ChatGPT doesn’t scale to larger codebases.
Second limitation: if you happen to be a world expert in something, then it might have trouble understanding - haha, just kidding: if you are the best human expert in the world, ChatGPT is still smarter than you and can still fix errors you can’t fix.
Rather than a stochastic parrot, it’s the best debugger on the planet.
In fairness you are correct that ChatGPT is unreliable.
Indeed. The irony is that the trade off you’ve made means that you can throw a senior dev with 20 years of experience building things that run in browsers, and he wont be up and running in a day.
He’ll be fighting all the unnecessary layers of complexity to modify this ship-in-a-bottle with the fiddly tools provided, knowing that he could have built the entire ship in a few days if he could just touch the thing with his hands.
At least that’s what I feel like dropping into Modern Frontend gigs after sprinting along on my own projects that use javascript, html and css.
However, if you are thrown into an existing projects that does have all of these things, it's usually because they're actually necessary.
That is almost never true.
This is such nonsense. Not everything is subjective or "an experience". There is objective truth is the world and the objective truth is your previous statement is almost never true. Unless you're operating at FAANG levels, that is, which almost nobody outside of FAANG is.
“Let me create a framework to handle this complexity”
Blissfully unaware that it would take them months to write that, and it would be obsolete the next day, because part of the problem is too many frameworks.
The other day I ran `gem install wayback_machine_downloader`. I never liked Ruby the language enough to get into its ecosystem, but I note that said ecosystem has from an outside perspective remained remarkably stable. gem is still the thing to use (dear god Python what became of you), Rails is still around... I'm waiting to work on a new project with a complex enough front-end that justifies more than 0-30 lines of JS, then I can be happier with HTMX instead of whatever giant framework is most popular these days.
I detest npm and avoid it at all costs preceisely because of all this junk (personally I use Deno, but at work we use something like bazel with a monorepo that means we don't need npm), but I find Javascript/Typescript a lovely language to program in and it is my absolute number 1 goto language for anything.
It makes me sad when people say "javascript sucks!" but actually they mean that using npm to build react applications suck. Modern Javascript/typescript is an absolute joy to code in.
I went back and rewrote almost everything to remove dependencies and I just write little shell scripts to do what I need rather than relying on someone else’s little shell script that hasn’t been touched it 2 years.
The fewer dependencies, the better I can sleep at night.
This is my shared workflow which I apply to few dozens of projects in my company. Works like a charm! Extremely simple and understandable script. Build times are mere seconds (when cache works).
I tried to understand this whole github actions stuff but decided that I don't need all that complexity.
Haskell has proven very hard to use cross machine, installing it on MacOS in 2019 was painful, the ecosystem is painful to handle, there's a huge macro/extension confusion, language isn't very retro compatible while you can generally expect 2010 JS to work in 2023 out of the box.
Even creating a full fledged docker image, codespace/gitpod-like proves difficult, the base one provided by GitHub kinda sucks and has issues.
C has proven generally okay to work on most platforms but the language is honestly complex and the sheer amount of knowledge of systems (in all possible meanings of the word), produces non-portable code that doesn't fit most usecases of general programming.
I could go on, but everyone who has tried several languages knows that we all suffer and will always suffer with several shortcomings in all kind of ecosystems.
Php has always been one that I've never got to setup a project in less than few days.
Elm is the less painful but the ecosystem suffers from its own degree of benevolent dictator's state and rate of development.
This is pretty much exactly my reaction any time I see a package.json longer than ~20 lines.
Speaks out loud, hoping to keep the demons at bay.
“I must not fear. Fear is the mind-killer. Fear is the little-death that brings total obliteration. I will face my fear. I will permit it to pass over me and through me. And when it has gone past I will turn the inner eye to see its path. Where the fear has gone there will be nothing. Only I will remain.”
cries
I must not fear.
Fear is the mind-killer.
Fear is the little-death that brings total obliteration.
I will face my fear.
I will permit it to pass over me and through me.
And when it has gone past, I will turn the inner eye to see its path.
Where the fear has gone there will be nothing.
Only I will remain.
[Finishes reading the article and realizes they survived, but now all sweaty]
I love TypeScript, I love node, but I so much hate all the associated tooling. It's incredibly terrible. I don't want to make a single choice. I want a single build system which does everything good enough. I want a single "Spring Boot" framework which does everything good enough. I want a single linter, which built into my IDE and does not require me to exclude my dist folder. I don't want to configure my TypeScript compiler, it should be smart enough to configure itself. I don't want to spend days reading about project references, npm workspaces and other stuff. I want to create few folders like I do with maven and it's just works. I don't want to browse 10 different libraries for typings (why is it even needed for typescript???). I want things like JSON parsing and validation to be built into language or stdlib.
I hate go language. But their tooling is so right that I can withstand all those `if err != nil` every second line.
I am intrigued by "less js is more fun" approaches like htmx and alpine.js but that still leaves a range of options on how to organize things and some may bring you dangerously close to what OP describes.
There must be a sweetspot of tools and designs that helps organize a modest code base to get the benefits of reusability and readability without too heavy a penalty.
Imho frontend people gladly walked into the trap of "excess complexity" as this elevated this niche into a "serious" business. Whenever some aspect of tech doesnt make sense, follow the money and this usually clears things up.
The issue you might run into as you scale up is managing the state and synchronization of a large, complex UI. This is where having a reactive, declarative interface can help out a lot - which is why you arrive at React and tooling around that, or something much like it. That's where most large UI projects end up, frontend or backend.
You must be joking, Mr. jrajav :)
Native DOM APIs are just as bad as they always were. Because they are stuck in the 90s era of heavy imperative APIs.
There are a handful new additions that are somewhat nice, but all of them either underdesigned, or with warts you need to be aware of (querySelector, querySelectorAll and native fetch come to mind).
Building a UI from scratch with them is just as cumbersome and awkward as it was in the early 2000s, and you'll end up re-implementing half of jQuery at least in the process.
Ironically enough, the last paragraph of the article? About hating node while refusing to switch to bun or deno? That kind of decision might have prevented some of the pain mentioned in the previous paragraphs.
I don’t have an object-level answer for your question, and I’m not trying to mislead you into thinking it’s possible to actually have a minimal setup for JavaScript web apps - it’s always going to be painful. But with a commitment to maintaining minimalism you can pick a stack to start with and mostly keep it that way. The grass might be greener on the other side, but the fences are made of razorwire!
As systems evolve, they always get more complex. Web front ends are evolving to keep up with users, app archetypes, and business needs. People choose tribes to identify with, angular vs react vs vue. People are ambitious and pushing their own frameworks while tearing down others. Its just programmer politics, no different from windows vs linux vs osx.
I learned react, stuck with it, and it has worked out.
Had to choose a test for an interview. Some UI required.
Ended up using Blazor Server, got it done..
Thing is, most of the problems described in the post aren't related to low-JS front-end libraries like HTMX or alpine. You can write React without a linter, bundler, build tool, unit testing, or linting (or dependency manager, for that matter!). But with any of these projects at scale, you start wanting more:
- If you want to write unit tests in JS, you need to choose a test runner (probably Jest or Vitest -- until the built-in node testing module becomes more common).
- If you want linting, you need a linter (probably Eslint). If you want type safety, you need a type checker (probably Typescript).
- If you want to create smaller JS files to ship to production and to automatically handle assets, you need a bundler.
- If you want to use new language features while supporting old browsers, you need polyfills.
- If you want to use all these things together, you need something to bring it together (like Webpack).
So it really depends what you need! You may not need any. But as you can imagine, in many professional projects with multiple developers it's very nice to have unit tests, linting, and type checking :) (And you start caring about end-user performance a lot more, in which case optimizing the shipped bundle is important.)
Take all that, and then compare to a language like Rust, which has most of the "ecosystem stuff" built-in. In Rust, you get the test runner, the linter, dependency manager, type checker, and documentation tool all included. Easy! Thankfully, Rust doesn't have to care about whether users support modern language features (because it compiles down to lower-level code ahead of time), or whether the binary shipped to the client is optimally organized for downloading immediately over the internet.
It's a problem in JS because A) you have to care about more problems than many other languages since JS needs to load instantly over the wire in a web browser, and B) there is a huge amount of choice and not a lot of standardization in web tools. (And what standardization there is (Node, npm), there are still competitors trying to even further reduce the pain points.)
I think that in ten more years, we'll be in a better place, because there is push back (like this post!) against these problems, which will encourage more tools trying to solve the explosion of tools. Which seems counterintuitive, but these tools were created to solve very real problems. So I see it as a pendulum which has swung too far, but will likely swing back to a more balanced place. And you see that with tools like Vite gaining popularity.
1. Use npm. Well, I didn't try yarn, probably it doesn't matter.
2. Use vite. It sets things up good enough and pulls only 15MB dependencies. Tiny!
3. Use TypeScript. I can't imagine writing untyped code.
4. Use eslint to complement typescript checks. Takes some time to set it up first time. Use default rules, they're good.
5. Use prettier to format code. I use vscode and with corresponding plugins, it just gets out of the way. It's good thing to have and works with minimal setup.
6. Use react. Well, it's pretty much standard nowadays and things are mostly fleshed out. But it's pretty complex so beware of steep learning curve. But once you learn it, it's not going anywhere. At this point I consider react a fundamental knowledge, worth spending time to learn.
7. For state management use react-query (yes, that sounds strange).
8. Use built-in to vite methods to access CSS (I think it's called module CSS).
9. Don't add any more dependencies unless you absolutely feel like it. Don't add CSS frameworks or things like that, just write your CSS by hand.
This was the least pain setup I ever had and I consider it to be minimally viable.
Better use pnpm, faster for those that don't have 1 Gbps uplink and live outside the usa.
> 3. Use Typescript
Better use jsDoc, d.ts files are scary and are really complex, you need a typescript wizard la matt pollock just to tell you are mistakenly trying to concatenate a string and int.
> 5. Use prettier
yes use prettier but don't forget to configure eslint to work with prettier otherwise you are going to have a bad time.
> 6. Use react
Use whatever fits your problem but if you are going to really use rect then do it with Next.js, no point in still using CRA/CRACO
> 7. state management
Avoid using state, try to solve every problem with pure functions, if you must use state then consider redux/react-query
> 8 & 9.
You should still recommend stylelint, specially if you are going to write css by hand otherwise don't just use tailwind, tailwind is bootstrap with extra batteries (change my mind).
Can you elaborate? I'm using eslint and prettier with pretty much default settings and never had it conflict with each other. My impression was that they work on different aspects. May be there're some overlapping eslint checks, but they're not enabled by default (or I didn't stumble upon them yet).
Why would you use d.ts files and not just... .ts files?
Personally i think typescript isn't going to be around much time on the frontend side, maybe on the backend by some people that still believe in adding another compiler layer to their build time.
Would anything truly have been lost if Node simply ignored ESM?
The half-measure seems, abstractly, worse than announcing that Node was all-in on ESM, deprecating CJS by a certain LTS release, as the end-user has no real notion of the ultimate destination of ESM in a Node project; what is the struggle for?.
There are a number of benefits (see the link below). We could argue JavaScript shouldn’t be isomorphic, as it barely does its job well on either end. Node might in fact be better off without the ‘pollution’ of client-side packages, but well…
https://www.reddit.com/r/node/comments/rby64z/why_esm_what_d...
It's lack of quality.
When you have a badly designed layer, you have a lot of problems to progress over. And each bit of progress creates a whole lot of new problems you can solve with further progress.
- 75% of NPM is CommonJS
- It’s better for lazy loading / cold start outside the browser than `await import(…)`
I think the ESM slowness can eventually be compensated for but it will need spec changes. Possibly the defer import proposal.
But yeah, CommonJS is basically never going away.
This would require a full-regression test, but this employer was in the business of doing these fairly frequently since they were super risk averse, and so adding another full regression test that only happened every six months or so was no big deal.
We’d update a ton of stuff, inevitably break some obscure feature when used in IE11 (which at the time, they still supported), so we’d add a polyfill or chuck a dependency through babel and problem solved. Then nothing for 6–12 more months.
This was annoying, but not nearly as annoying as taking inventory at a grocery store or dealing with bad customers at a restaurant. It’s just work, and we were paid to do it.
https://docs.npmjs.com/cli/v9/configuring-npm/package-json?v...
If your CI fails because of eslint is it really something superficial and optional?
You could write a similar story for any language, but JavaScript embraces all of them at once.
Also, I started with `grunt` before using `gulp`. And I used `bower_components` to build a website. I try not to remember too much though. There is only so much that therapy helps.
I just do Rust now. It's not perfect by any means but at least there is design there.
Because that's the only version of JSON. It never changed: https://www.json.org/json-en.html
Maybe not by the original people, but still a superset. And a more useable superset.
Note: the stable version of json5 spec was only released in 2018.
"The conciseness of the grammar leaves many aspects undefined. On top of that, several specifications exist, and their various interpretations tend to be murky."
"An Exploration of JSON Interoperability Vulnerabilities"
https://bishopfox.com/blog/json-interoperability-vulnerabili...
No kidding, I had to do this recently. It was a trek and a half.
God how I hate anything to do with command line
Not having to worry about setting up TS, bundling, linting is a breath of fresh air. No node_modules, just a depts.ts file to manage my imports. This also means no yarn, NPM or PNPM to decide.
It still has a learning curve sometimes because some tooling is not mature enough, but it's nice figuring out that I don't need PM2 just a systemd service to keep the process alive.
Mind blown. Awesome.
It's like a stand-up comedy routine delivery.
...and it's red?
...and there is a line of tiny skulls on stakes near it?
...and until I checked Google Street View I could swear it was blue when I drove past?
All I know is allegedly, according to Google today, in this particular timeline, the Tardis is not supposed to be red.
And I remember this, from the VHS era: https://www.macintoshrepository.org/3913-daleks
I am thankful for it.
Edit: I too remember the Daleks game. <3 A round of Member-Berries for all of us! <3
Just one simple example: you must write `import { name } from "./file.js"`.
Not `"./file.ts"`. Why js? Because TypeScript developers are stubborn that their thing is just a layer on top of JS so they refuse to change ".ts" to ".js" in the generated output. This is just wrong approach. But here we are, importing things from "./file.js" which doesn't even exist.
I don't know if they're pushing that to be the default or what.
Second reason is quickly moving APIs. Common.js, AMD, ESM. Those are foundational APIs and they were changing every few years. Just similar Java example: java modules were introduced in Java 9 and even today maven (AFAIK) does not utilize those at all. But java modules were infinitely more compatible with old code.
Third reason is TypeScript. It does require a "stripping types" step which could be counted as a compilation.
Fourth reason is community. I don't really know what's so special about JS community, but they deliver new tools and frameworks at alarming speed. There's no single source of truth. You can't really do anything with this chaos. Google develops angular, but it's not popular. Facebook develops React and it is popular but people still use other frameworks like vue from small companies.
10 years ago I thought that with time things will settle down. Now I'm not so sure.
> engines prominently lists node. And while you hate it with the depth of your soul, you are not going to Bun or Deno because you know this will not stop the pain. This will only make the pain worse.
Deno will make the pain worse?
Nearly every complaint in the article could have been solved by using Deno.
- Package.json, no such thing for Deno (not unless you go out of your way to infect Deno with it)
- main, browser, and fs don't matter in Deno since Deno's APIs are based on browser APIs.
- type, module, requires, and imports doesn't matter in Deno since it shares the same import API as browsers.
- scripts, doesn't matter since all Deno projects have the same tools built-in (benchmarker, bundler, compiler, formatted, linter, task runner, test runner, etc...)
- watchman doesn't matter since deno has watch support built in
- dependencies doesn't matter anywhere near as much because Deno comes with a standard library
- left-pad doesn't matter because it's part of the language
- moment.js doesn't matter since you can just use the browsers built-in Temporal api
- resolutions, dev-dependencies don't matter for the same reason that dependencies doesn't matter
- eslint doesn't matter since deno has a linter
- postcss matters less because modern css is great
- jest doesn't matter because deno includes a test runner
- no need to work out typescript installation with your dependencies because deno includes typescript and has less dependencies
The only things not solved by using deno is version and name, which are universal concerns amongst all projects, not just the js ecosystem.
As programmers we fall into this trap where we think that 'just' doing something else will solve all our problems. But I can guarantee that it's never that simple, and for the people who consume your library, it won't be either.
I used the word "just" once in my comment in the point about using Temporal.
The Temporal spec was created in part specifically because of the issues with moment.js.
----------------------------------------------------------------------------------------
> But I can guarantee that it's never that simple, and for the people who consume your library, it won't be either.
Unfortunately, Node has historically not used the same standards as browsers over a whole swath of functionality, which lead to differences in the language based on whether it was running client-side or server-side.
Simple libraries intended to run anywhere would sometimes need to release a client version and a server version.
Even when node started adding support for the standard APIs instead of creating new proprietary APIs, they ended up with a fractured ecosystem where some things would only work with the proprietary APIs, and some would only work with the standard APIs.
There were so many missteps with node that the creator of node essentially said screw this, I'm starting again from scratch, hence: Deno.
Coming back to your point:
> But I can guarantee that it's never that simple, and for the people who consume your library, it won't be either.
But it *IS* that simple.
How?
Because Deno uses the standard APIs for things. If it already works in the browser, it will continue to work in the server. No more fractured language.
For greenfield projects? Maybe. For established projects? Not for a very long time.
There's a reason Deno added support for npm modules and CommonJS. Because no amount of wide-eyed naïve fanboyism can replace reality.
- "main, browser, and fs don't matter in Deno since Deno's APIs are based on browser APIs."
- fs implies we are reading files, how do you use a browser API to read files in the local filesystem? You cannot, you'd use Deno.readTextFile, which is not browser-compatible, so in Deno you have to either make it for Deno or for the browser, not both. The fields main and browser allow you to make a single package compatible with both NodeJS and front-end JS, something not possible with Deno.
- "type, module, requires, and imports doesn't matter in Deno since it shares the same import API as browsers." - Seems like Deno just doesn't support CommonJS, e.g. the vast majority of npm modules, which is a big con and not an advantage: "Current Node.js supports both CommonJS and ES Modules, while Deno only supports ES Modules"
etc.If you interpret the article as intended, yes Deno will make the pain worse because virtually no one is writing libraries that only supports Deno, so now you'd have all the above issues + Deno-specific ones.
> fs implies we are reading files, how do you use a browser API to read files in the local filesystem? You cannot, you'd use Deno.readTextFile, which is not browser-compatible, so in Deno you have to either make it for Deno or for the browser, not both. The fields main and browser allow you to make a single package compatible with both NodeJS and front-end JS, something not possible with Deno.
Both Deno and browsers have a bunch of APIs facilitating File/FileSystem/Stream, etc...
- In Deno, you can use things like Deno.readTextFile which is written on top of those APIs to read/write files on the server's file system which the server will do if it asks for permission and is granted by the developer.
- In browser, you can still use those APIs to read/write files on a private sandboxed file system, and the client can request permission to read/write between the private sandboxed file system and the client's real file system through things like the file picker API's, drag and drop APIs, etc...
- The only real difference between the two is that the developer can explicitly grant the server access to files/folders up-front before the server asks for permission in the first place.
> Seems like Deno just doesn't support CommonJS, e.g. the vast majority of npm modules, which is a big con and not an advantage: "Current Node.js supports both CommonJS and ES Modules, while Deno only supports ES Modules"
Deno *Does* support CommonJS (see the --compat flag). But you probably want to use ESM over CommonJS anyway.
> [...] Deno will make the pain worse because virtually no one is writing libraries that only supports Deno, so now you'd have all the above issues + Deno-specific ones.
If you write your library to the JS standards, then you have code that supports both browsers and Deno (and possibly Node depending on if Node supports those particular standards and you're web standard code isn't being used by your non-web standard code).
- That was a quote from Deno's official page, if it supports commonjs better ofc. "But you probably want to use ESM over CommonJS anyway" -> no way, if you want to use most of the npm packages you need support for commonjs. Ofc you'd be writing ESM for your modern app though.
- There's no "JS standards" for backend stuff like FS, running commands, launching a server, etc. so very often you need backend-specific code. You already showed two extra pain points of using Deno vs Node in this convo, adding the `--compat` flag and granting permissions.
Yeah...this always felt like a bug in Deno. ¯\_(ツ)_/¯
> scripts, doesn't matter since all Deno projects have the same tools built-in (benchmarker, bundler, compiler, formatted, linter, task runner, test runner, etc...)
AND...you therefore can't do anything outside of the box in a well-defined way. Oops.
Example: How do I run just the main server and not a worker? How do I seed the database?
> dependencies doesn't matter anywhere near as much because Deno comes with a standard library
Sigh. Yeah. Really. You said that.
OK, using the standard library, please:
- Parse Excel files, modify them, and rewrite them to disk.
- Load and modify a PNG and write it out as a JPG. Be sure you scale it down using a Lanczos kernel.
> eslint doesn't matter since deno has a linter
One of the most powerful eslint rules for TypeScript is the detection of dangling promises. A quick glance at the deno lint rules doesn't see anything that will handle this critical lint rule.
And OOPS, adding eslint to the project in a standardized way isn't possible because there's no package.json!
... I could go on.
Sorry, but Deno fanboying is so last year.
>
> AND...you therefore can't do anything outside of the box in a well-defined way. Oops.
>
> Example: How do I run just the main server and not a worker? How do I seed the database?
That doesn't really make sense. If you're trying to do anything in a well-defined way, you're going to need a box. The box is the definition.
------------------------------------------------------------------------------------------------
> > dependencies doesn't matter anywhere near as much because Deno comes with a standard library
>
> Sigh. Yeah. Really. You said that.
>
> OK, using the standard library, please: [...]
I'm not going to be able to do these in a comment here, but I can briefly tell you how I would go about doing them.
> - Parse Excel files, modify them, and rewrite them to disk.
Use streams API to create a pipeline (input -> modify -> output).
If by Excel files you mean CSVs, then I can stream the whole thing, making modifications and saving while I'm still streaming in the file.
If by Excel files you mean an xlsx file, then I tweak the pipeline to (input -> unzip -> modify -> rezip -> output).
> - Load and modify a PNG and write it out as a JPG. Be sure you scale it down using a Lanczos kernel.
First I'd make sure I have the encoder and decoder settled. If you have a specific encoder in mind (e.g. ImageMagick + specific settings) then you can box that up by compiling it to web assembly. If you don't have a specific encoder in mind then it doesn't really matter what you use. If you have a specific encoder in mind, then same thing. If you don't, you can always use the JS standard Canvas API which you can load an image into then pull out raw pixel info.
If you want to do the whole thing from scratch, you can do it like the Excel way and create a pipeline using the streams API and TypedArrays.
------------------------------------------------------------------------------------------------
> > eslint doesn't matter since deno has a linter
> One of the most powerful eslint rules for TypeScript is the detection of dangling promises. A quick glance at the deno lint rules doesn't see anything that will handle this critical lint rule.
>
> And OOPS, adding eslint to the project in a standardized way isn't possible because there's no package.json!
Linting for floating promises is tricky to accomplish (ESLint doesn't get it quite right).
Depending on the code, you can get stuck in the halting problem. To solve it correctly, you need to write a compiler/solver that has access to the relevant type information.
This is something that the typescript project itself has been investigating and once solved would be added as another strict rule (e.g. noImplicitOverride, etc...).
If you don't care about correctness and want any heuristic solution, then use any heuristic solution.
The only thing it can't do is determine that you actually did handle the promise later. Which is fine. It's a LINTING RULE, and false positives are the name of the game.
What's BAD is when you accidentally miss handling a promise at all. It's an invisible error without the linting rule.
Your other comments...don't even make sense. You're going to build a Lanczos filter by hand? Or you're only going to ... compile ImageMagick to WebAssembly?!, ... an implementation which is tremendously slower (nearly unusably so for large images) than that of Sharp:
https://www.npmjs.com/package/sharp
... which is simply an import away?
No, what you're doing is called "motivated reasoning." You've concluded that Deno is the best, and you're reinterpreting all of my complaints in convoluted ways to support your predetermined conclusion.
Standard fanboy behavior. Or troll behavior. I cite Poe's Law as why it's impossible to tell the difference.
----------------------------------------------------------------
## Eslint
We're on the same page that:
- heuristics work until they don't
- we'd all rather have false positives than missed positives
- Having something is better than nothing
- ESLint fits it's roll well for now
I've been using node since the 0.x days and used iojs while it was ahead, I've gone from jslint -> jshint -> eslint -> tslint -> eslint (because it supported ts) + sonarlint -> now (which is rome tools in editor, and sonarlint to catch what rome tools doesn't support yet).
The one problem I've had transitioning projects through each linter is just the amount of crufty exception comments to disable rules that ends up through my code.
I've found that I can get rid of a lot of those by setting up my configs and turning on most of the strict modes.
As for promises specifically, I've ended up needing to fire off a lot of floating promises in the last 18 months. There's a lot of firing things off to set values in things that may not exist anymore. (I've noticed it happens a lot a lot around using Promise.race to make code that gracefully recovers in chaotic environments)
The false positives got too much so I disabled the rule (and at a later point moved away from eslint altogether).
There's also some tricky things with catching floating promises when working with frameworks that can deal with super hard to debug side-effects.
So now I have:
- floating promises where for specific situations I don't want to catch their errors and have them throw up to the next layer
- floating promises that are conditionally caught
- floating promises that are actually a wrapped promise that fails as an uncaught promise should, but also conditionally triggers the debugger or logs out a trace.
I use different approaches depending on the situation.
Maybe for you, the only situation you need to deal with is catching all promises (in which case you should continue using ESLint then turn off that rule when TS gets the correct promise handling detection), but for me it causes more trouble than it fixes.
----------------------------------------------------------------
## Images
If I had to do what you suggested, I would have actually just used sharp, but after reading your comment about dependencies, I wasn't sure if you would accept that answer.
I suggested two alternatives:
1. Compiling an existing solution into web assembly to box up that functionality and call into it. It could be ImageMagick, libpng, libspng, libvips, etc... I suggested ImageMagick because that tends to be the go-to for when someone has a specific setting in mind (usually due to a legacy system).
2. Setting up streams and using TypedArrays (which is what sharp uses anyway)
Alternatively, don't go out of your way to install Node or Deno—instead, just use the runtime you already have on your computer; browser makers are much better at following the standards (and consequently not encouraging/inducing you to write against standards-incompatible APIs to begin) than the people peddling Node and Node-influenced stuff.