user-feedback icon indicating copy to clipboard operation
user-feedback copied to clipboard

discussion points: Tooling group feedback session

Open boneskull opened this issue 6 years ago • 36 comments

Following up on the first tooling user feedback session, we want to gather points of discussion for future sessions.

From #38, these were the original questions from the first session:

  • Describe how your Tooling leverages Node.js.
  • Why do you use Node.js for Tooling?
  • What's working in the Node.js tooling ecosystem?
  • What isn't working in the Node.js tooling ecosystem?
  • What's new that could impact Node.js?

We didn't have enough time to cover everything we wanted to--and give everyone a chance to speak--so future sessions should be more limited in scope.

I'll use this issue to gather ideas, and distill them into a "living document" of sorts (a Markdown document, living in this repo) to be used going forward.

cc @dshaw @bnb to confirm I understood this correctly. :smile:

ALSO, PLEASE UNDERSTAND: This issue isn't a platform for discussion; we're just making a rough list of topics to discuss.

boneskull avatar May 02 '18 21:05 boneskull

One that came up offline (and I think I may have mentioned briefly) in the "what isn't working" category is portability. I'd like to expand on that a bit, from the perspective of a tooling author.

  • fs.watch() is seemingly neglected, and doesn't work well outside of Linux, giving rise to userland modules like chokidar. Ideally, fs.watch() would work as intended across all supported Node.js platforms.
  • graceful-fs shouldn't exist.
  • cross-spawn shouldn't exist.
  • More generally, and not necessarily a concern about portability, fs-extra, rimraf and mkdirp shouldn't exist.
  • Error codes should be normalized across platforms (@iarna)
  • More care given to buffering and standard input/output (@iarna)

boneskull avatar May 02 '18 21:05 boneskull

Feature requests

  • GPU-accelerated code bindings (original issue; @TheLarkInn)
  • Expose allowed command-line flags in machine-readable format (open PR)

boneskull avatar May 02 '18 21:05 boneskull

I also want to cc everyone else who participated in that session; please add anything here you want to discuss, but didn't get a chance to. To all of you: This issue is for gathering topics of discussion for future Tooling-specific Node.js user feedback sessions. Your input is greatly appreciated!

  • @bamutov
  • @benjamn
  • @kentcdodds
  • @jdalton
  • @uttampawar
  • @devsnek
  • @kellyselden

Please cc anyone else who might want to participate! 😁

boneskull avatar May 02 '18 21:05 boneskull

IMHO https://github.com/nodejs/user-feedback/issues/59#issuecomment-386132667 is extremely important.

mcollina avatar May 03 '18 06:05 mcollina

cc @bahmutov, sorry misspelled it

boneskull avatar May 04 '18 20:05 boneskull

Really hoping someone else can add something.

I'm happy to drive the bus here, but there's no point in driving an empty one.

boneskull avatar May 21 '18 20:05 boneskull

Going to ping a few people who I know that work on OSS projects that use Node.js for tooling in some cases.

To those I'm pinging: we're gathering points to discuss about Node.js + the tooling ecosystem and what we could do better/what's already working well. Would ❤️ your input if you've got the time to share it–and no worries if you don't!

@johnpapa may have some observations from the Angular core community 🤔

Not sure how much RxJS uses Node.js for its tooling, but @ladyleet may have some input (or know someone from RxJS who does!)

Pinging @zeke because I know he does a LOT with Node.js tooling, and works on Electron which has some tooling built in Node.js as well.

@tzmanics may have input from NativeScript/Angular + Node.js backgrounds.

@mxstbr does some pretty impressive work in the React ecosystem and always has super good feedback.

bnb avatar May 21 '18 22:05 bnb

Yeah for sure - well you know what is really missing is pinging the folks working on CLIs - they use Node.js a lot of course. This is something I really hope that in the next year or ongoing we can bring to light. It is one of the most essential and important use cases of Node.js IMO.

For RxJS - @benlesh can provide more info. For Angular CLI - @hansl or @brocco For Vue CLI - @yyx990803 or @chrisvfritz For Ember CLI - @stefanpenner For Preact CLI - @prateekbh For Create React App - @gaearon or he can point you. For React Native - maybe @thesavior? For NativeScript - @jenlooper or @sebawita can point you. For Ionic - @mhartington can point you.

ladyleet avatar May 22 '18 06:05 ladyleet

I know I have a fair amount of thoughts here but I think @zertosh has a superset of my thoughts so pinging him instead.

I think the main one I’ve talked to @zertosh about is the overhead of starting up a node script. Making scripts fast is hard and one of the main things driving us to experiment more with other languages for scripts.

TheSavior avatar May 22 '18 07:05 TheSavior

@thesavior thanks much! @zertosh love to have you as a part of this conversation.

ladyleet avatar May 22 '18 07:05 ladyleet

I'm not sure I have much to add beyond what @boneskull already mentioned regarding portability. That's the only significant pain point that's coming to mind. I personally don't mind pulling in separate packages, but when a built-in exists it's frustrating when it doesn't work cross-platform. A lot of people have to learn that the hard way.

chrisvfritz avatar May 22 '18 12:05 chrisvfritz

I am one of the lead engineers for Appcelerator Titanium SDK's Node.js-based CLI's and tooling, which began 6 years. I went through our projects and analyzed the boilerplate functions and dependencies and here's my first pass list of things:

  • As stated above, fs.watch() has serious cross platform issues which I have worked around in appcd-fswatcher, namely recursion support on Linux and the ability to watch files/directories that do not exist or require privileges to access.
  • It would be nice if path.resolve() would check if the path starts with ~ and replace it with os.homedir().
  • os.arch() should report the machine's architecture, not the architecture of the Node binary. A script should be able to identify if it's being run in a 32-bit version of Node on 64-bit machine. We use this for determining which native executable dependencies to install/run and telemetry so we track when we can drop 32-bit support. Side note, I know there was talk of Node dropping 32-bit support, but I don't think the world is there yet.
  • Built-in support for check if a path is an executable file. It's not enough to check flags or file extension. For now, we can use isexe.
  • Built-in support for which. It is very handy for finding the full path to an executable and ensures that the path is indeed an executable (see previous item).
  • Built-in support for generating UUID's. I know there are package name conflicts, so maybe hang it off util or crypto. I know I can generate random bytes using crypto, but I'd like to not have to pull in a dependency to generate a simple v4 UUID.
  • A lighter-weight sandbox construct. vm.createContext() is perfect for 100% isolation, but I want a jail. If the code in the sandbox throws a TypeError, I can catch it outside, but I can't do an instanceof TypeError because the built-in types are not the same definition.
  • The ability to complete destroy a vm context. You can create contexts all day, but apparently they can only be cleaned up on exit.
  • This is a stretch, but temp directory/file creation and cleanup would be awfully nice.
  • The ability to spawn another Node process with a TTY context such that the child Node process can pause(), setRawMode(), etc on process.stdin. This would be great for integration testing so we can use mocha instead write shell scripts.

cb1kenobi avatar May 22 '18 19:05 cb1kenobi

@boneskull We are looking at 2018-06-08 for the next session.

dshaw avatar May 25 '18 16:05 dshaw

I work on internal developer tools at Facebook - mostly on CLI tooling for developers working in our monorepo. There's a huge ecosystem of tools in this space, and some of it is written in JS running on Node. Other tooling is written in Python, PHP/Hack, Bash and even Rust. So Node often gets compared to these (I won't go into that here).

Why do you use Node.js for Tooling?

  • Cross-platform support.
    • Tooling has to work on Macs, Linux, and Windows. For the most part, if you stay clear of any native modules, Node will Just Work in whatever platform.
  • Ease of distribution.
    • We've made it really easy to use Node for tools in the monorepo. We check-in the Node runtime for all of our target platforms, and use Yarn with an "offline mirror" to install npm dependencies. This frees tool authors from having to worry about bootstrapping their tool in whatever environment it has to run (e.g. developer machines, CI system, etc).
    • Tools that need to work w/o a checkout can have a simplified build that produces a single JS artifact and includes all of the Node runtimes. This frees authors from having to worry about producing multiple releases of their tool.

What isn't working in the Node.js tooling ecosystem?

  • Startup time.
    • This is the single biggest complaint.
    • The overhead of starting the Node runtime is really hard to hide even from noop commands like mytool --help or mytool --version. Some tools that get invoked thousands of times during builds have had to be ported away from Node because of the cost of loading the runtime.
    • But even loading user code takes a perceivable amount of time. We've tried to mitigate this with bundling and v8-compile-cache. I'm hopeful and looking forward to snapshots.
  • Memory use.
    • Again, when invoking a tool thousands of times during a build, this a huge issue. One recent internal benchmark compared a Node tool against an equivalent port in another language, and found the the memory use at 83MiB vs 4MiB. That level of magnitude is really hard to argue against.
  • Standard library.
    • I agree with keeping core small, but if you're going to ship an fs module, then it should be more complete. Every one of our packages has mkdirp, rimraf, shell-escape, and temp - but I'd argue that these should be in core.
    • For small tools, it's hard sell to use Node, when another language/runtime has everything you need out-of-the-box.

zertosh avatar May 27 '18 18:05 zertosh

@zertosh great feedback, thanks. "Node.js CLIs at scale" brings up some pain points I hadn't heard before.

boneskull avatar May 29 '18 17:05 boneskull

@cb1kenobi Thanks! I was wondering if you can explain this further:

A lighter-weight sandbox construct. vm.createContext() is perfect for 100% isolation, but I want a jail. If the code in the sandbox throws a TypeError, I can catch it outside, but I can't do an instanceof TypeError because the built-in types are not the same definition.

I'm not sure what you mean by "jail" or how what you want is different than this:

try { 
  vm.runInNewContext('throw new TypeError()', global);
} catch (e) { 
  console.log(e instanceof TypeError); // true
}

boneskull avatar May 29 '18 18:05 boneskull

@boneskull When I did it, I didn't use global. My use case was to run untrusted code in a sandbox/jail where the code couldn't modify global or do things like process.exit().

I tried defining my own global object, but I found myself passing globals in such as TypeError, RangeError, etc so that my instanceof checks worked. This turned out to be a huge pain and that vm2 had a much more elegant solution.

vm2 wraps everything in a proxy and wires up this contextify/decontextify thing that tries to convert objects across context like this https://github.com/patriksimek/vm2/blob/master/lib/contextify.js#L216-L232. Aside from the performance issues, vm2 suffers from many issues in which I finally gave up when I ran into https://github.com/patriksimek/vm2/issues/62.

I ended up spawning a Node script that runs the untrusted code in a subprocess and I watch for the process exiting abnormally and stdout/stderr. Turns out this works better for our use case because we can run the code with a specific Node.js version.

I have other use cases for a sandbox. My app supports .js config files (default export is the config) and it would be nice to load them in a sandbox and have it export the config.

cb1kenobi avatar May 29 '18 18:05 cb1kenobi

@cb1kenobi Ah, yeah, I understand now. I've gone down the same path to vm2 myself. 😝

boneskull avatar May 29 '18 19:05 boneskull

Describe how your Tooling leverages Node.js. Why do you use Node.js for Tooling? What's working in the Node.js tooling ecosystem?

ember-cli is implemented in node, this enables us to:

  • share developer mindshare between our consumers (often JS developers) and our tooling
  • utilize tools such as Babel for client side code (and it's various TC39 experiments)
  • relatively easy and portable installation story (compared to ruby, or etc..)
  • gives us the knowledge/power of V8/TC39 and the various Node TC's in our foundation. (each upgrade gives us cool stuff, like async/await etc)
  • LTS is great, we aligned ours with nodes. This makes rollout at companies simple and easy.

What isn't working in the Node.js tooling ecosystem?

  • Parallelism, Threads: Although we achieve parallelism in some build steps (such as babel/uglify etc) via spawned subprocesses, this is relatively costly and brittle experience. I hope the node worker or similar efforts will help us here. Specifically, lightweight parallelism (with debuggability)would be quite empowering
  • CaptureExit: Coordination between multiple libraries wanting to do on-process shutdown work, is fraught with peril: context work around library
  • Native extensions: Although quite powerful, we essentially cannot rely on them for our use-case. As when we have relied on them, issues with python/node-gyp or recompiling between node version switches, swamped our support and hurt our getting started story.
  • npm: consistent issue preventing ember-cli + node 8 rollout (broke CITGM's ember-cli scenario), NPM maintainer pushed work-around, that hides the error but doesn't address the issue, this was done without tests or deeper investigation. When concerns are raised, and deeper analysis provided, comments were simply deleted. (I am hoping My search skills are just poor, but when searching today I could not longer find...)
  • file watching: fs.watch is ok for small projects, but for large ones persistent systems such as watchman are required.
  • CLI startup time: requiring files is costly, especially if you CLI has an add-on system.

What's new that could impact Node.js?

stefanpenner avatar May 29 '18 23:05 stefanpenner

@stefanpenner Thanks. I've also had struggles with exit hooks. Core could provide a better experience here.

(Discussion around npm is out-of-scope for us, however.)

A proper async module loader seems like it could improve startup time of CLIs (I'm not current on what's happening w/ Modules, unfortunately).

boneskull avatar Jun 06 '18 18:06 boneskull

Thx @stefanpenner for chiming in!

ladyleet avatar Jun 06 '18 18:06 ladyleet

Hi there, Angular CLI lead here.

Describe how your Tooling leverages Node.js.

Angular CLI is built using TypeScript and runs using Node. It's a CLI tool to create, manage and deploy Angular applications and libraries. We do not have an addon system (yet), but we do use libraries for workflows (Schematics) and tasks (Architect) which allow extension points from the user. These are dynamically loaded on a need basis.

Why do you use Node.js for Tooling?

Reusability of knowledge between framework developers and tooling developers. Easy to setup on Mac and Linux. The community is vast and eager to help, which is always nice.

What's working in the Node.js tooling ecosystem?

Debugging story, LTS, release cycle (clear and to the point), respect for semver.

What isn't working in the Node.js tooling ecosystem?

  • I have a few gripes about npm itself;
    1. never contacted library authors about the npm audit changes, resulting in panic from a lot of people before we even had a chance to update and react appropriately. We're still not fully updated yet, and have issues filed regularly because some dependency five level down have a regex with backtrace and .*.
    2. On a similar track; it is impossible to replace (or remove) a deprecated, obsolete or invalid indirect dependency on install. Optional indirect dependencies can also lead to user errors who think something went wrong and didn't. Allowing this means we could override versions of indirect dependencies that don't pass audit with versions that do, without having to wait for a release of every other packages down the line. This can be very hard to work with sometimes.
    3. lack of optionalPeerDependencies (or a similar mechanism) that which leads to a lot of warnings even if some dependencies could be provided by the project. I understand that this is akin to not having a dependency at all, but there's no way to communicate to projects that "we support this dep".
    4. lack of a good (or any, really) API for calling npm from Node. We do npm installs a lot (on new projects, on updates, on adding dependencies, etc) and always end up spawning an npm process using the command line, which IMO is an antipattern.
    5. I would like to have an official supported JSON Schema for package.json. Something we could validate against would be nice. The ones I found were neither official nor complete (closest is probably http://json.schemastore.org/package).
  • Similar to Stefan's complaint, I'd like to have a proper native fs watch API. Depending on chokidar led to some inconsistencies over minor versions, licensing issues, and npm audit problems.
  • A better install story on Windows, but I don't know how easy that is to setup. We have many newcomers from Windows who are used to click-and-get-started, but with Node+NPM it takes at least a few hours to explain command line stuff and how to use a command line.
  • Node async API are still entirely callback based (my search skill might be poor but I don't think this is in anyone's todo list), which makes promise chaining and async/await a bit hard to use (natively).
  • Streams-to-Observable libraries helped us significantly to make sense of Node's pipes, I would like to have a native solution for pipe APIs (that is typed properly and isn't string based).
  • No way to resolve a package globally, hard to discover which is the global node modules.
  • Using Bazel (which makes extensive usage of symlinks), we found in general that Node was confusing and really hard to work with because of symlinks. When using require() with a local path it doesn't check whether the script was symlinked or not. Using --preserve-symlinks work for some use cases, but then breaks some libraries. We're working with the libraries that broke to resolve it.
  • Custom module resolvers could be easier to extend for Node. Overloading methods that start with an underscore is a bit awkward, but maybe I'm misunderstanding how to do it.

What's new that could impact Node.js?

Native support for import (both dynamic and static), better startup time. Better typing for Streams (might be more a complaint about the poor stream typing in typescript package).

I think in general Node itself is in pretty good shape.

hansl avatar Jun 07 '18 19:06 hansl

@hansl thank you so much! :)

ladyleet avatar Jun 07 '18 19:06 ladyleet

A proper async module loader seems like it could improve startup time of CLIs (I'm not current on what's happening w/ Modules, unfortunately).

@boneskull we use: time-require to debug these issues, but its a fairly brittle/easy to regress model. Strategies to reduce disk IO during startup for CLI's would likely be handy. I can imagine several approaches, all possible but require some someones time.

stefanpenner avatar Jun 07 '18 22:06 stefanpenner

We're now starting a user feedback session. Join us at https://zoom.us/j/168395218 or just watch at https://youtube.com/c/nodejs+foundation/live

boneskull avatar Jun 08 '18 16:06 boneskull

@hansl @stefanpenner @zertosh please join!

boneskull avatar Jun 08 '18 16:06 boneskull

I understand the desire for fs to be minimal, but if it's going to support creation and removal of objects, it should also support nested creation and removal. Admittedly, these are not operations the underlying C/C++ API perform, but Node is a higher level and should provide at least some extra benefit in this area.

jsumners avatar Jun 08 '18 18:06 jsumners

@boneskull oh man, I wish I would have known. I would have attended, any future sessions planned?

stefanpenner avatar Jun 08 '18 19:06 stefanpenner

@jsumner This is a good point

boneskull avatar Jun 08 '18 19:06 boneskull

@stefanpenner yes but no dates yet. it will not be the same time slot.

boneskull avatar Jun 08 '18 19:06 boneskull