HTTPS clone URL
Subversion checkout URL
Research: Node.JS Integration
- [archives] Health report data aug 2015
- [archives] Health report data jul 2015
- API Cleanup Ideas
- Areas of Expertise
- Brackets 5 15 2012 Hackathon Notes
- Brackets CodeMirror v4 Migration Guide
- Brackets Coding Conventions
- Brackets Committer Policy
- Brackets Communication Channels
- Brackets Community Testing
- Brackets Developers Guide
- Brackets Development How Tos
- Brackets Extensions
- Brackets in Browser
- Brackets JSDocs Guidelines
- Brackets Node Process: Overview for Developers
- Brackets Screenshots
- Brackets Server Smoke Tests
- Brackets shell command line arguments (proposal)
- Brackets Shortcuts
- Brackets Smoke Tests
- Building Brackets Releases
- Building CEF and Chromium
- Cache Folder
- CEF Debugging
- CEF3 vs. Chromium Content Shell
- Code Hinting API Investigation
- Code Hinting Functional Specifications
- CodeMirror v4 Integration
- Collecting End of Sprint Stats
- Command Line Arguments
- Commands, Menus and Shortcuts Implementation
- Contributing to Brackets
- Crash Logs
- Creating and Posting Brackets Hotfix Releases
- Creating Themes
- CSS Context API implementation spec
- CSSUtils, HTMLUtils
- Customize Your Code Font
- Debugging Brackets
- Debugging Test Windows in Unit Tests
- Dependency Analysis
- Extension API Research
- Extension Dependencies
- Extension Experiments
- Extension Icon Guidelines
- Extension Locations
- Extension Manager Workflows
- Extension package format
- Extension Package Manager Research
- Extension Registry Architecture
- Extension Registry Help
- Extension Repository Server API
- Extension Robustness
- Extension Testing Recommendations for SplitView
- Extension UI Guidelines
- Extension Unit Tests
- File System
- File System API Migration
- File System Implementations
- File system watcher requirements
- Generating Brackets API Docs
- Google Summer of Code 2013 Project Ideas
- Grunt Setup
- Health Data
- Health Report Data
- How to Hack on Brackets
- How to Report an Issue
- How to Use Brackets
- How to write extensions
- Inline Color Editor
- Issue Clusters 1.0 Milestone
- Keyboard Shortcuts
- Language Support
- Language Support Changes
- Large Projects
- Linux Development for Contributors
- Linux Version
- Live Development: lifecycle research and future directions
- Live Preview API
- Live Preview Multibrowser
- Live Preview Overview
- Live Preview URL Mapping
- Localization Tests
- Migrating or Syncing Brackets Configuration
- New Code Hinting API Proposal
- Notes on CodeMirror
- Performance Profiling
- Performance Testing
- Performance Tests Procedure
- Preferences Code Hints for Developers
- Preferences Overview
- Preferences System
- Preview Images Research old drafts
- Preview Images Spec
- Programmatically Opening Files in Brackets
- Pull Request Checklist
- Pull Request Process
- Pull Request Review Checklist
- Related Documents
- Release Notes
- Release Notes: 0.40
- Release Notes: 0.41
- Release Notes: 0.42
- Release Notes: 0.43
- Release Notes: 0.44
- Release Notes: 1.0
- Release Notes: 1.1
- Release Notes: 1.2
- Release Notes: 1.3
- Release Notes: 1.4
- Release Notes: 1.5
- Release notes: sprint 10
- Release notes: Sprint 11
- Release notes: Sprint 12
- Release notes: Sprint 13
- Release notes: Sprint 14
- Release Notes: Sprint 15
- Release Notes: Sprint 16
- Release Notes: Sprint 17
- Release Notes: Sprint 18
- Release Notes: Sprint 19
- Release Notes: Sprint 20
- Release Notes: Sprint 21
- Release Notes: Sprint 22
- Release notes: sprint 23
- Release Notes: Sprint 24
- Release Notes: Sprint 25
- Release Notes: Sprint 26
- Release Notes: Sprint 27
- Release Notes: Sprint 28
- Release Notes: Sprint 29
- Release Notes: Sprint 30
- Release Notes: Sprint 31
- Release Notes: Sprint 32
- Release Notes: Sprint 33
- Release Notes: Sprint 34
- Release Notes: Sprint 34.1
- Release Notes: Sprint 35
- Release Notes: Sprint 36
- Release Notes: Sprint 37
- Release Notes: Sprint 38
- Release Notes: Sprint 39
- Release Notes: Sprint 7
- Release Notes: Sprint 8
- Release notes: sprint 9
- Research: CodeMirror document linking
- Research: Design Alternatives for Split View
- Research: Drag and Drop from Finder
- Research: Extension Management
- Research: Find Replace
- Research: HTML DOM Data Structure
- Research: jQuery 2.0 Upgrade
- Research: Multiple cursors and selections
- Research: Node.JS Integration
- RESEARCH: Preference Format
- Research: Simplified LESS Support
- Research: Topcoat
- Running Brackets Unit Tests
- Running Jasmine tests from the command line for Linux
- Simple "Hello World" extension
- Spec: Native Installers
- SplitView Architecture Notes
- SplitView Architecture Tasking
- SplitView Extension Migration Guide
- SplitView Proposed Architecture
- Suggest a Feature
- Tech Summit Product Fair Brackets
- Testing Brackets
- Testing Pritam's Linux build
- Translations community assistance
- Typing Speed Mini Spec
- Upgrading CodeMirror from Upstream
- User Key Bindings
- Using File Filters
- Working with fsevents_win.node
- Working with Multiple Selections
- ZZZ (OBSOLETE) CodeMirror v3 integration
- افزونه ها در براکتس به چه معناست؟
- براکتس چیست؟
Clone this wiki locally
This document discusses the findings from a research spike to understand the benefits and tradeoffs of using Node.JS for native functionality instead of V8 native extensions (our current solution).
The node server would be embedded in the desktop application (either as an executable that runs as a separate process, or as a dynamic library that is loaded by the app shell).
- Leverage existing Node code for cross-platform native features. For example, Node has cross-platform file and directory watchers.
- Require us to architect our code in a client/server manner (and deal with asynchronicity) so that a browser version of brackets is easier.
- Would give us an approach for third parties to write extensions that require native code (e.g. an ssh/sftp-based file system), because Node already has a mechanism for loading dynamic libraries.
- Need to re-architect some brackets code for client/server approach (but this may pay off in creating a browser version)
- Possibly more complicated debugging (two JS VMs/contexts to debug)
- Increased download size (~8 MB additional) and startup time (rough testing suggests this is small)
- Adds an additional external dependency
Yes. Using a pre-built Node executable works on both Mac and Windows. Node is run as a separate process and communication happens over pipes.
We are also able to build dynamic library (dylib/dll) versions of Node for both Mac and Windows. Doing this requires a 1-line change to the GYP build files (changing the build type from "executable" to "shared_library". Build environments are easy to set up for both Mac (install xcode) and Windows (install latest Visual Studio Express and Python). These dynamic libraries would only need to be built once (and we've already built them).
Everything should work equivalently on Linux (Node works on Linux) but because we don't have any app shell solution for Linux, this is difficult to verify. It is likely that using Node will make our Linux story simpler.
I recommend using a dynamic library as a long term solution. This removes the need for IPC, which will either a.) be brittle, or b.) require a lot of implementation work to get right.
With a dynamic library solution, native functions (e.g. to create menus or show file dialogs) can be called directly from the Node extension code, rather than having to write a bridge that shuttles all arguments as strings.
There are two approaches here: using Node, and using V8 extensions. Native UI code (e.g. showing an "open" dialog) typically needs to run in a specific UI thread (e.g. the "main" thread in Mac Cocoa). This poses some interesting challenges.
In CEF3 / Chrome Content Shell, the V8 engine runs in a separate process from the main app. So, IPC is needed to drive native UI. However, CEF3 provides a wrapper for doing this IPC. If we go with a separate process Node solution (discussed above), we'll need IPC for this as well. But, we'll have to write that IPC solution ourselves.
If we a.) decide to go with a separate process Node solution, and b.) if we can be certain that Node code will never need to reach into the main process (e.g. Node code won't ever do UI stuff), then we may be able to avoid writing complicated IPC code for Node. Instead, we can use V8 extensions in CEF3 (and CEF3s IPC solution) to do our native UI work.
However, if we go with an in-process library version of Node (my current recommendation), we could do all of our UI stuff in Node. This would allow us to avoid using IPC on the V8 side. And, this would have the added architectural benefit that all native stuff (UI and otherwise) would go through the same channel. (This may be a small benefit, though.)
Ultimately, it isn't yet clear what the best solution is. We need to decide whether we'll use an in-process library version of Node or a separate-process executable version of Node.
We built a client <-> server websocket proxy that abstracts the communication layer into regular function calls on both ends. So, if existing functionality is already (correctly) written to work asynchronously, the architecture change is minimal or non-existant.
For example, to switch file i/o from V8 extensions to Node, we simply swapped out the implementation of
brackets.fs. The higher-level FS operations (in NativeFileSystem) did not have to change at all.
For things that are not currently implemented as asynchronous calls, there will need to be a change. However, it seems likely that everything that would be implemented on the Node side should be implemented asynchronously even if we don't use Node.
The main change to the programming model is that we'll need to decide where to implement features: either on the client JS side or on the server JS side. In general, the client should only implement things that we know will be roughly the same in the desktop and browser versions of Brackets. Anything unique to the desktop version should be implemented (as much as possible) on the Node side.
The main change is that there will be two JS engines. These will need to be debugged separately.
The two main areas of performance concern are startup time (because spinning up Node, an http server, and a websocket server takes time) and file i/o time (because this will be the main use of Node). There are no performance concerns around initiating native UI interactions.
All of the performance numbers below were generated on a 2010 MacBook Air with SSD.
It appears that most of the startup time will be governed by the web component (e.g. CEF) startup time. A warm launching of a simple Cocoa application with Node and a WebKit WebView component takes 250ms. This time includes starting Node and redirecting the WebView to a webpage served by Node. The timing measurement goes from the first line of
main() to the webpage calling back to Node over a websocket.
Actual startup time measurements will need to be determined after we've decided on a web component solution.
The most intensive file i/o Brackets is likely to do is a "find in files" command over a large codebase.
To test this, we created a test codebase that consisted of all the js files in the brackets repo (located in the same directory structure). This test set consists of 89743 lines of code at a size of 3.8MB. We performed a search for the regular expression
/brackets/gi which returned ~600 results in ~95 files.
- Existing app shell using V8 extension: 475ms
- Pulling all files into Brackets client code from Node over websocket: 882ms
- Moving search code to Node server, only sending results over websocket: 198ms
- For comparison, using 'grep' at command line: 25ms
To summarize, naively switching our file i/o to Node results in a 1.85x slowdown. Moving file intensive JS code to Node (which requires only minimal changes to the code) results in a 2.4x speedup.
Yes. Loading modules in Node is simpler than in Brackets client code because Node has native support for "require" and direct access to the file system. Modules can be loaded dynamically. We can use the same loading approach (i.e. looking in a directory) as we use for client extensions.
We implemented a prototype extension that adds a "grep" command to the client/server proxy for doing "find in files" on the server. The implementation was straightforward.
This does represent an additional external dependency. However, the current version of Node seems stable enough for our use, so there isn't a big concern about whether Node will remain in active development.
Furthermore, we can use the distributed (i.e. not built by us) Node executables. They are completely statically linked, so do not require any installation.
Finally, dynamic library versions of node are easy to build (see above).
The Node server maintains an http and websocket server on the user's
localhost interface. This interface is not accessible from other computers (either on the same network or over the internet).
Another process running on the user's computer could connect to this interface and use it to execute arbitrary code on the user's computer. However, this process could also execute arbitrary code itself. Brackets doesn't run with any special privilege, so this doesn't represent a large security concern.
If the user's computer were running malicious code, this malicious code could also make the localhost interface available over the internet. Again, this doesn't really represent a security concern -- the same malicious code could present a similar interface to the Internet by itself without Brackets present.
A prototype implementation is checked into the
node-proxy branches in adobe/brackets and adobe/brackets-app repos. This prototype only provides Node file i/o and websocket implementation. It does not contain any native ui implementation.
To run this code prototype:
- Install node.js (version 0.6.19 is known to work)
- Check out the
node-proxybranch in both repos (brackets and brackets-app)
- in the
- in the same directory, run
- in your browser, go to http://localhost:3000
Shell prototypes (with native ui implementation) are currently checked in to the joelrbrandt/node-shell repo. (However, the code is a bit messy and there isn't any documentation beyond comments in the source.)