"JavaScript can only run in a browser, and it relies strongly on CSS & HTML." WRONG!!!
"JS is restricted to web applications, and its primary purpose is to create dynamic, interactive pages, web and mobile apps, and fully-featured GUIs." WRONG!!!
On Angular: I really wish it weren't the first framework mentioned... it's a horror show, I never want to touch it again, and it can die a painful/quick death for all I care. It's not worth the time, hassle or effort and people that like it tend to create some massive monstrocities without ever really understanding it to begin with.
On Backbone: I'm surprised many use it anymore... it's okay-ish, and can be combined with React, Vue, Svelt and other view engines without much difficulty.
On React: Love it... React + Redux +_Thunks + Fetch + Material-UI is the single best combination of modules for web based applications I've ever used. Can be a bit of a pain to get setup the way one wants, create-react-app and/or parcel go a long way to help that though. Other libraries with JSX adapters are useful as well... SVG support is an uncut gem in terms of application development in this space as well.
On Node.js, the cons seem to be written by someone that hasn't used it... the API changes are usually from V8 for compiled modules, and is generally not a concern for most apps (sqlite is probably the most used compiled module in broad use). As for lack of standards and documentation, the API docs are massive and pretty thorough... though could use more examples for some of the use of crypto libraries, which can sometimes be confusing, though not nearly as much as the .Net or Java crypto libraries.
On backend frameworks, still love Koa the best, it has all the hindsite of Express and started by the same developer. Much cleaner structure, better middleware abstractions and a dream for standing up server applications in general imho.
On JavaScript Widgets, for the love of god, don't use commercial widgets for JS projects... they tend to be massive, bloated messes and the only concern is how they look, never how they perform and will almost always fail on mobile devices. Stick to components that are generally available via github/npm. Material-UI, not mentioned, is the gold standard component library as far as I'm concerned, for more complex needs, there are decent options (see above mention of SVG in React/JSX).
On test frameworks... as heavy as Jest is, it's the cleanest integration with coverage setup and supports running concurrent tests in parallel. Nothing comes close imho. The methods are jasmine based, and while I like mocha+chai more, the extra setup for coverage is so painful, just use Jest.
On JS vs PHP, if your front end is an SPA, it's easier to get Node/JS on the backend. I am leaning towards Deno for some future backend projects though.
On JS vs Ruby, if you have Rails developers in-house, then sure leverage that... if you don't, full stack JS is likely better as an option.
On JS vs Dar, imho dart is a dead end.
C++: only for CPU critical code, rust or go are generally better options for many modern projects though.
C#: see Ruby comments above, if you have C# devs in-house, leverage them... Same for Java.
TypeScript: for services and libraries, I'm starting to lean towards TS + JSDoc comments. TS has gotten better and parcel (or ts-node) has made it pretty easy to bootstrap a TS project with, not to mention Deno is TS in the box. For single-dev projects that aren't shared libraries, I'm inclined to go plain JS.
On JS costs: stick to open-source as much as possible. Leverage platform integrations from Lambda/Functions support or Docker/Kubernetes deployment models. Applicaiton/Library costs should be really avoided as usually there's an open alternative that's better already.
On saving money with remote JS devs... this is *REALLY* hit or miss... the range of skill/talent for those considered "senior" developers (quotes intentional) is really broad. There are many who consider themselves at a senior level with skills so poor it wouldn't fly with any other language or tool-set. That's also why the salary range is so big as well, and doesn't necessarily align in either case. Despite the range of $80-120k on the document higher up, the top end for the best developers can clear $140k+, rare exceptions over $180-240k for FAANG companies. To reach these top end positions, you really need to know JS the language, how to leverage architectures and build systems that work together, and having experiences with other languages helps.
Aside: on that last note, you should try to be practiced in at least two platform languages if you want to level up. I'd recommend Python, C#, Rust and/or Go for this. A functional language like Haskel, Closure, F# or others can help as well. Knowing different languages, and styles of languages can really help in terms of understanding how to leverage different styles. JS is a very flexible language where you can use Imperative, Procedural, Functional and OO patters and/or mix them as needed, without exposure to other platform languages, it's difficult to know when/how/why.
OT: but letting it stand as it's a good explanation and is broadly useful even in JS circles.
----
I have one difference of opinion on SOLID, if you're writing projects in JS with JS Modules... the D (Dependency Injection) may not be necessary. You can use module substitution techniques for testing, and unless you absolutely are supporting multiple concurrent types of modules with your application, wire them directly to keep the code more simple and easy to reason with.
Time and time again, I see DI libraries for JS that really aren't needed and only serve to add unnecessary complexity.
----
On BDUF, it doesn't need to be Waterfall... If I'm creating a package for a library/service, I will often start on the README.md first and include the documentation on how to use the library *BEFORE* I start writing code. This lets me think about the abstractions for that library and how it will be used in practice before I paint myself into a corner. It can also help with TDD and some test-first approaches.
I tend to only really use this approach for libraries that can be abstracted away from the rest of an application.
Interresting approach(es)... for recent applications, I've been using Koa, just because the middleware and context handling are much better than everything else.
I create an EnvironmentContext on startup that contains my settings (based on environment variables), configuration (based on application configurations) additional services (database client, redis client etc) and a general logger (bunyan, with console and optionally ES and Seq sinks).
The AppContext extends EnvironmentContext and my first middleware (begin-request) adds the environment context, adds methods for RequestInfo and ResponseInfo and wraps the logger methods to include RequestInfo for each log statement, and the final output being the general error logging middleware then the response middleware (which doesn't output, if error middleware already did). I also add additional providers for easy apiResult and apiError responses as well as for Role based authentication (Jwt bearer tokens).
It works pretty well... made the switch to TS to make context handling easier... I also have it generically typed Context<TBody, TParams, TQueryString> so that I can have typed instances in my route handlers.
Each of my route handlers returns an array, so that it can include parameter/body/query validators (Joi or Yup), with the Role validation where all the routes are mounted.
Hoping to get permission to put the Configuration Service, which only delivers build configuration(s) output on Github in the next month or so.
Decent start on these things... might be worth digging in a bit deeper in a few areas.
I would suggest taking the next steps, which would be adding unit tests with an effort for high (>80%) code coverage, I find Jest is easiest to configure, and has some nice integrations for React apps. This will help you to understand how to make your code more modular/accessible.
From there, would create Dockerfile(s) and docker compose files to make it easier to stand up an instance for local testing/usage. You could go another step and setup an integration test project to work with this using puppeteer or Playwright.
After that, would circle around and look into Redux patterns as well as possibly GraphQL services. Once you've started writing tests for your applications, it's much easier to see how helpful Redux as a control flow pattern can really help. There are other options, but I find Redux + thunks extension cover most of the bases for most applications, and allow for horizontal feature growth without blowing out complexity.
It's a little more wordy than it needs to be. The "title" example is contrived and doesn't need useEffect. Multiple calls for useEffect should be avoided, in fact too many hook calls in components can be detrimental to performance in general, for some things, it should be closer to the top level and integrated with Redux, or whatever you use for state management.
In practice, I'm generally using Redux for almost everything. Two areas I am not is specifically strings/language and sometimes with Forms. I use context for language, and may use state or context for forms.
One thing that is a side effect I explicitly do with language/strings is that the current/selected language as well as the current language strings are accessible as globals in the language/strings module(s). This is only so that they are accessible in error messages/conditions that might be in an Action or Reducer. It's "sloppy" but effective.
For the components, I use "useStrings()" which gets the current strings from context.
I also use the browser language events as well as an override in the UI that override is set in redux and those events update the current strings to use in context and globally.
I'm not seeing anything regarding access restrictions... I would emphatically *NOT* use this unless everything the account in use has access to is meant to be public, and has no write access to the database.