├── README-v1.md ├── README-v1.pdf ├── README.md ├── README.pdf ├── code ├── 2013-01-17T235432Z_01_NYK520_RTRIDSP_3_CAPITALONE-RESULTS.jpg ├── CapitalOne_Digital_Skills_Infographic_BW.jpg ├── async-error.js ├── binding.gyp ├── blocking.js ├── buf.js ├── build │ ├── Makefile │ ├── Release │ │ ├── .deps │ │ │ └── Release │ │ │ │ ├── addon.node.d │ │ │ │ └── obj.target │ │ │ │ └── addon │ │ │ │ └── hello.o.d │ │ ├── addon.node │ │ └── obj.target │ │ │ └── addon │ │ │ └── hello.o │ ├── addon.target.mk │ ├── binding.Makefile │ ├── config.gypi │ └── gyp-mac-tool ├── cluster.js ├── domain-async.js ├── hello.cc ├── hello.js ├── job.js ├── knock-knock.js ├── log.txt ├── server-stream.js ├── server.js ├── stdin.js ├── sync-error.js └── weekly.js ├── course.md ├── course.pdf ├── images ├── Node.js Design Patterns - Second Edition Book Cover.png ├── atwoods_law.png ├── azat.jpeg ├── azats-books-covers.png ├── baby_car_wash.gif ├── baby_elephant.gif ├── code_review.png ├── coffeeshop-blocking.jpg ├── coffeeshop-non-blocking.jpg ├── commercial.gif ├── companies-0.png ├── companies.png ├── event-loop-node-interactive.jpg ├── fullstackjavascript.jpg ├── java-compile-funny.jpg ├── kylesimpson.jpg ├── logos │ ├── 1000px-Macys.svg.png │ ├── MSFT_logo_rgb_C-Gray.png │ └── salesforce-logo-273F95FE60-seeklogo.com.png ├── node-capital-one.png ├── node-core-modules.gif ├── nodeconfcamp.JPG ├── nodeu-1.png ├── nodeu-2.png ├── nodeu-3.png ├── nodeu-4.png ├── non-blocking-docs.png ├── non-blocking.png ├── nu.png ├── practicalnode.png ├── proexpress.png ├── reactquickly.jpg ├── sucks.gif ├── threading_java.png ├── threading_node.png ├── tree_slam.gif ├── watermark-inverted-no-bk.png └── you-dk-node-course-cover-v1.png ├── move.js └── package.json /README-v1.md: -------------------------------------------------------------------------------- 1 | footer: © Node.University, 2016 2 | slidenumbers: true 3 | 4 | 5 | ![](images/you-dk-node-course-cover-v1.png) 6 | 7 | --- 8 | 9 | # You Don't Know Node 10 | ## Quick Intro to 5 Core Features 11 | 12 | --- 13 | 14 | # Code along and take notes 15 | 16 | --- 17 | 18 | # Slides & Code :page_facing_up: 💻 19 | 20 | Everything: 21 | 22 | or 23 | 24 | just PDF: 25 | 26 | --- 27 | 28 | # Better Apps—Better Life 29 | 30 | ^Big idea: Node has some cool core features. Node is everywhere. What if the world can be a better place if more developers master Node? 31 | 32 | --- 33 | 34 | # About Presenter 35 | 36 | --- 37 | 38 | ![inline](images/azats-books-covers.png) 39 | 40 | ^Wrote and published 12 books not counting Korean, Chinese, Polish and Russian translations 41 | 42 | --- 43 | 44 | Azat Mardan 45 | 46 | ![inline](images/azat.jpeg) 47 | 48 | Twitter: @azat_co 49 | Email: hi@azat.co 50 | Blog: webapplog.com 51 | 52 | --- 53 | 54 | # About Presenter 55 | 56 | * Work: Technology Fellow at Capital One (kind of a big deal) 57 | * Experience: FDIC, NIH, DocuSign, HackReactor and Storify 58 | * Books: React Quickly, Practical Node.js, Pro Express.js, Express.js API and 8 others 59 | * Teach: [NodeProgram.com](http://NodeProgram.com) 60 | * Master of Science from University of Northern Virginia 61 | 62 | 63 | --- 64 | 65 | # Capital One in Top 10 US Banks 66 | 67 | ![inline](images/commercial.gif) 68 | 69 | --- 70 | 71 | # Starting with basics: Why Use Node? 72 | 73 | --- 74 | 75 | # Input/output is one of the most expensive type tasks (>CPU) 💰 76 | 77 | --- 78 | 79 | # Node has non-blocking I/O 80 | 81 | 82 | --- 83 | 84 | ![inline](images/non-blocking.png) 85 | 86 | ^This allows processing other tasks while IO calls are unfinished like this 87 | ^Nginx vs. Apache 88 | ^Blocking I/O is expensive! 89 | 90 | 91 | --- 92 | 93 | ### Java Sleep 94 | 95 | ```java 96 | System.out.println("Step: 1"); 97 | System.out.println("Step: 2"); 98 | Thread.sleep(1000); 99 | System.out.println("Step: 3"); 100 | ``` 101 | 102 | 103 | 104 | --- 105 | 106 | ## Node "Sleep" 107 | 108 | ```js 109 | console.log('Step: 1') 110 | setTimeout(function () { 111 | console.log('Step: 3') 112 | }, 1000) 113 | console.log('Step: 2') 114 | ``` 115 | 116 | --- 117 | 118 | ## Process Multiple Tasks 119 | 120 | ```js 121 | console.log('Step: 1') 122 | setTimeout(function () { 123 | console.log('Step: 3') 124 | // console.log('Step 5') 125 | }, 1000); 126 | console.log('Step: 2') 127 | // console.log('Step 4') 128 | ``` 129 | 130 | --- 131 | 132 | # Blocking Web Server 133 | 134 | --- 135 | 136 | ![inline](images/threading_java.png) 137 | 138 | --- 139 | 140 | ![inline](images/coffeeshop-blocking.jpg) 141 | 142 | --- 143 | 144 | # Non-Blocking Web Server 145 | 146 | --- 147 | 148 | ![inline](images/threading_node.png) 149 | 150 | ^This is in contrast to today's more common concurrency model where OS threads are employed. Thread-based networking is relatively inefficient and very difficult to use. Furthermore, users of Node are free from worries of dead-locking the process --- there are no locks 151 | 152 | --- 153 | 154 | ![inline](images/coffeeshop-non-blocking.jpg) 155 | 156 | --- 157 | 158 | # [Multi-threading] is the software equivalent of a nuclear device because if it is used incorrectly, it can blow up in your face. 159 | 160 | 161 | 162 | --- 163 | 164 | # Blocking systems have to be multi-threaded 165 | 166 | --- 167 | 168 | # Node is single threaded... and that's good! 😄 169 | 170 | 171 | --- 172 | 173 | 174 | ## It's still possible to write blocking code in Node.js. :flushed: 175 | 176 | 177 | --- 178 | 179 | # Blocking Node.js Code 180 | 181 | ```js 182 | // blocking.js 183 | console.log('Step: 1') 184 | for (var i = 1; i<1000000000; i++) { 185 | // This will take 100-1000ms 186 | } 187 | console.log('Step: 2') 188 | ``` 189 | 190 | --- 191 | 192 | # Blocking Node.js Code 193 | 194 | ```js 195 | var fs = require('fs') 196 | 197 | var contents = fs.readFileSync('accounts.txt','utf8') 198 | console.log(contents) 199 | console.log('Hello Ruby\n') 200 | 201 | var contents = fs.readFileSync('ips.txt','utf8') 202 | console.log(contents) 203 | console.log('Hello Node!') 204 | //accounts.txt->Hello Ruby->ips.txt->Hello Node! 205 | ``` 206 | 207 | --- 208 | 209 | # Non-Blocking Node.js Code 210 | 211 | ```js 212 | var fs = require('fs') 213 | 214 | fs.readFile('accounts.txt','utf8', function(error, contents){ 215 | console.log(contents) 216 | }) 217 | console.log('Hello Ruby\n') 218 | 219 | fs.readFile('ips.txt','utf8', function(error, contents){ 220 | console.log(contents) 221 | }) 222 | console.log('Hello Node!') 223 | //Hello Ruby->Hello Node->... accounts.txt->ips.txt or ips.txt->accounts.txt 224 | ``` 225 | 226 | --- 227 | 228 | # Node *typically* is much faster than other platforms 229 | 230 | --- 231 | 232 | # How many of you reach the performance limitations of apps built with blocking I/O systems? 233 | 234 | --- 235 | 236 | # Probably not many 237 | 238 | --- 239 | 240 | # My Fav Node Benefit 241 | 242 | --- 243 | 244 | # JavaScript everywhere. One language to rule 'em all! 245 | 246 | --- 247 | 248 | * Think faster 249 | * Reuse code 250 | * Learn quicker 251 | 252 | --- 253 | 254 | # Most of Node is JavaScript 255 | 256 | * Array 257 | * String 258 | * Primitives 259 | * Functions 260 | * Objects 261 | 262 | --- 263 | 264 | 265 | # Node !== Browser JavaScript 266 | 267 | --- 268 | 269 | # How to create global variables (no `window` in Node), work with modules, get path to my script? 270 | 271 | --- 272 | 273 | # `global` or `GLOBAL` 274 | 275 | --- 276 | 277 | # It has properties! 278 | 279 | --- 280 | 281 | 282 | # `global.__filename` 283 | # `global.__dirname` 284 | 285 | 286 | --- 287 | 288 | 289 | # `global.module` 290 | # `global.require()` 291 | 292 | --- 293 | 294 | # How do I...? 295 | 296 | * Access CLI input? 297 | * Get system info: OS, platform, memory usage, versions, etc.? 298 | * Read env vars (passwords!)? 299 | 300 | --- 301 | 302 | # `global.process` or `process` 303 | 304 | --- 305 | 306 | # `process.pid` 307 | # `process.versions` 308 | # `process.arch` 309 | 310 | --- 311 | 312 | # `process.argv` 313 | 314 | --- 315 | 316 | # `process.env` 317 | 318 | --- 319 | 320 | 321 | # `process.uptime()` 322 | # `process.memoryUsage()` 323 | 324 | --- 325 | 326 | # `process.cwd()` 327 | 328 | --- 329 | 330 | # `process.exit()` 331 | # `process.kill()` 332 | 333 | --- 334 | 335 | # Who likes and understands callbacks? 🙋 336 | 337 | --- 338 | 339 | 340 | 341 | ```js 342 | fs.readdir(source, function (err, files) { 343 | if (err) { 344 | console.log('Error finding files: ' + err) 345 | } else { 346 | files.forEach(function (filename, fileIndex) { 347 | console.log(filename) 348 | gm(source + filename).size(function (err, values) { 349 | if (err) { 350 | console.log('Error identifying file size: ' + err) 351 | } else { 352 | console.log(filename + ' : ' + values) 353 | aspect = (values.width / values.height) 354 | widths.forEach(function (width, widthIndex) { 355 | height = Math.round(width / aspect) 356 | console.log('resizing ' + filename + 'to ' + height + 'x' + height) 357 | this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) { 358 | if (err) console.log('Error writing file: ' + err) 359 | }) 360 | }.bind(this)) 361 | } 362 | }) 363 | }) 364 | } 365 | }) 366 | ``` 367 | 368 | --- 369 | 370 | 371 | ## Callbacks are not very developmental scalable 😞 372 | 373 | --- 374 | 375 | # Me When Working With Deeply Nested Callbacks 376 | 377 | ![inline](images/tree_slam.gif) 378 | 379 | --- 380 | 381 | # Events 382 | 383 | Events are part of core and supported by most of the core modules while more advanced patterns such as promises, generators, async/await are not. 384 | 385 | 386 | --- 387 | 388 | ### Events == Node Observer Pattern 389 | 390 | * Subject 391 | * Observers (event listeners) on a subject 392 | * Event triggers 393 | 394 | --- 395 | 396 | ### Events 397 | 398 | ```js 399 | var events = require('events') 400 | var emitter = new events.EventEmitter() 401 | ``` 402 | 403 | --- 404 | 405 | ### Events 406 | 407 | In node.js an event can be described simply as a string with a corresponding callback. 408 | 409 | 410 | ```js 411 | emitter.on('done', function(results) { 412 | console.log('Done: ', results) 413 | }) 414 | ``` 415 | 416 | 417 | --- 418 | 419 | ### Using Event Emitters 420 | 421 | ```js 422 | var events = require('events') 423 | var emitter = new events.EventEmitter() 424 | 425 | emitter.on('knock', function() { 426 | console.log('Who\'s there?') 427 | }) 428 | 429 | emitter.on('knock', function() { 430 | console.log('Go away!') 431 | }) 432 | 433 | emitter.emit('knock') 434 | ``` 435 | 436 | --- 437 | 438 | ### Inheriting from EventEmitter 439 | 440 | ```js 441 | // job.js 442 | var util = require('util') 443 | var Job = function Job() { 444 | // ... 445 | this.process = function() { 446 | // ... 447 | job.emit('done', { completedOn: new Date() }) 448 | } 449 | } 450 | 451 | util.inherits(Job, require('events').EventEmitter) 452 | module.exports = Job 453 | ``` 454 | 455 | --- 456 | 457 | ### Inheriting from EventEmitter 458 | 459 | ```js 460 | // weekly.js 461 | var Job = require('./job.js') 462 | var job = new Job() 463 | 464 | job.on('done', function(details){ 465 | console.log('Job was completed at', details.completedOn) 466 | job.removeAllListeners() 467 | }) 468 | 469 | job.process() 470 | ``` 471 | 472 | --- 473 | 474 | ### Listeners 475 | 476 | ```js 477 | emitter.listeners(eventName) 478 | emitter.on(eventName, listener) 479 | emitter.once(eventName, listener) 480 | emitter.removeListener(eventName, listener) 481 | ``` 482 | 483 | --- 484 | 485 | # Other Node Patterns 486 | 487 | Node Patterns: From Callbacks to Observer: 488 | 489 | or 490 | 491 | 492 | 493 | --- 494 | 495 | # Problems with Large Data 496 | 497 | * Speed: Too slow because has to load all 498 | * Buffer limit: ~1Gb 499 | * Overhyped (JK) 500 | 501 | --- 502 | 503 | # Streams 504 | 505 | ## Abstractions for continuous chunking of data 506 | 507 | --- 508 | 509 | # No need to wait for the entire resource to load 510 | 511 | --- 512 | 513 | # Types of Streams 514 | 515 | * Readable 516 | * Writable 517 | * Duplex 518 | * Transform 519 | 520 | --- 521 | 522 | ## Streams Inherit from Event Emitter 523 | 524 | --- 525 | 526 | # Streams are Everywhere! 527 | 528 | * HTTP requests and responses 529 | * Standard input/output (stdin&stdout) 530 | * File reads and writes 531 | 532 | --- 533 | 534 | # Readable Stream Example 535 | 536 | `process.stdin` 537 | 538 | Standard input streams contain data going into applications. 539 | 540 | --- 541 | 542 | # This is achieved via a read operation. 543 | 544 | --- 545 | 546 | # Input typically comes from the keyboard used to start the process. 547 | 548 | --- 549 | 550 | To listen in on data from stdin, use the `data` and `end` events: 551 | 552 | ```js 553 | // stdin.js 554 | process.stdin.resume() 555 | process.stdin.setEncoding('utf8') 556 | 557 | process.stdin.on('data', function (chunk) { 558 | console.log('chunk: ', chunk) 559 | }) 560 | 561 | process.stdin.on('end', function () { 562 | console.log('--- END ---') 563 | }) 564 | ``` 565 | 566 | --- 567 | 568 | # Demo 569 | 570 | `$ node stdin.js` 571 | 572 | --- 573 | 574 | # New Interface `read()` 575 | 576 | ```js 577 | var readable = getReadableStreamSomehow() 578 | readable.on('readable', () => { 579 | var chunk 580 | while (null !== (chunk = readable.read())) { 581 | console.log('got %d bytes of data', chunk.length) 582 | } 583 | }) 584 | ``` 585 | 586 | ^readable.read is sync but the chunks are small 587 | 588 | --- 589 | 590 | # Writable Stream Example 591 | 592 | `process.stdout` 593 | 594 | Standard output streams contain data going out of the applications. 595 | 596 | --- 597 | 598 | # This is done via a write operation. 599 | 600 | --- 601 | 602 | # Data written to standard output is visible on the command line. 603 | 604 | --- 605 | 606 | # Writable Stream 607 | 608 | To write to `stdout`, use the `write` function: 609 | 610 | ```js 611 | process.stdout.write('A simple message\n') 612 | ``` 613 | 614 | --- 615 | 616 | # What about HTTP? 617 | 618 | --- 619 | 620 | ```js 621 | const http = require('http') 622 | var server = http.createServer( (req, res) => { 623 | req.setEncoding('utf8') 624 | req.on('data', (chunk) => { 625 | transform(chunk) // This functions is defined somewhere else 626 | }) 627 | req.on('end', () => { 628 | var data = JSON.parse(body) 629 | res.end() 630 | }) 631 | }) 632 | 633 | server.listen(1337) 634 | ``` 635 | 636 | --- 637 | 638 | # Pipe 639 | 640 | 641 | ```js 642 | var r = fs.createReadStream('file.txt') 643 | var z = zlib.createGzip() 644 | var w = fs.createWriteStream('file.txt.gz') 645 | r.pipe(z).pipe(w) 646 | ``` 647 | 648 | ^Readable.pipe takes writable and returns destination 649 | 650 | --- 651 | 652 | ## What data type to use for binary data? 653 | 654 | --- 655 | 656 | ### Buffers 657 | 658 | Binary data type, to create: 659 | 660 | * `Buffer.alloc(size)` 661 | * `Buffer.from(array)` 662 | * `Buffer.from(buffer)` 663 | * `Buffer.from(str[, encoding])` 664 | 665 | Docs: 666 | 667 | --- 668 | 669 | # Working with Buffer 670 | 671 | ```js 672 | // buf.js 673 | var buf = Buffer.alloc(26) 674 | for (var i = 0 ; i < 26 ; i++) { 675 | buf[i] = i + 97 // 97 is ASCII a 676 | } 677 | console.log(buf) // 678 | console.log(buf.toString('utf8')) // abcdefghijklmnopqrstuvwxyz 679 | ``` 680 | 681 | --- 682 | 683 | # Buffer Convertion 684 | 685 | ```js 686 | buf.toString('ascii') // outputs: abcdefghijklmnopqrstuvwxyz 687 | buf.toString('ascii', 0, 5) // outputs: abcde 688 | buf.toString('utf8', 0, 5) // outputs: abcde 689 | buf.toString(undefined, 0, 5) // encoding defaults to 'utf8', outputs abcde 690 | ``` 691 | 692 | --- 693 | 694 | ### Remember fs? 695 | 696 | ```js 697 | fs.readFile('/etc/passwd', function (err, data) { 698 | if (err) return console.error(err) 699 | console.log(data) 700 | }); 701 | ``` 702 | 703 | `data` is buffer! 704 | 705 | --- 706 | 707 | # Demo 708 | 709 | ``` 710 | $ node server-stream 711 | ``` 712 | 713 | --- 714 | 715 | # Streams and Buffer Demo 716 | 717 | ```js 718 | // server-stream.js 719 | app.get('/stream', function(req, res) { 720 | var stream = fs.createReadStream(largeImagePath) 721 | stream.pipe(res) 722 | }) 723 | ``` 724 | 725 | ``` 726 | $ node server-stream 727 | ``` 728 | 729 | 730 | 731 | 732 | --- 733 | 734 | # Results in DevTools 735 | 736 | `/stream` responds faster! 737 | 738 | ``` 739 | X-Response-Time 740 | ~300ms vs. 3-5s 741 | ``` 742 | 743 | --- 744 | 745 | # Stream Resources 746 | 747 | Stream automated workshop: 748 | 749 | ``` 750 | $ sudo npm install -g stream-adventure 751 | $ stream-adventure 752 | ``` 753 | 754 | 755 | 756 | 757 | --- 758 | 759 | 760 | 761 | 762 | # How to scale a single threaded system? 763 | 764 | --- 765 | 766 | # Cluster Usage 767 | 768 | * Master: starts workers 769 | * Worker: do the job, e.g., HTTP server 770 | 771 | Number of processes = number of CPUs 772 | 773 | --- 774 | 775 | # Clusters 776 | 777 | ```js 778 | var cluster = require('cluster') 779 | var numCPUs = require('os').cpus().length 780 | if (cluster.isMaster) { 781 | for (var i = 0; i < numCPUs; i++) { 782 | cluster.fork() 783 | } 784 | } else if (cluster.isWorker) { 785 | // your server code 786 | }) 787 | ``` 788 | 789 | --- 790 | 791 | # Cluster Demo 792 | 793 | 1. Run `code/cluster.js` with node (`$ node cluster.js`). 794 | 1. Install `loadtest` with npm: `$ npm install -g loadtest` 795 | 1. Run load testing with: `$ loadtest http://localhost:3000 -t 20 —c 10` 796 | 797 | Press control+c on the server terminal 798 | 799 | --- 800 | 801 | # Cluster Libraries 802 | 803 | * Core cluster: lean and mean 804 | * strong-cluster-control (https://github.com/strongloop/strong-cluster-control), or `$ slc run`: good choice 805 | * pm2 (https://github.com/Unitech/pm2): good choice 806 | 807 | --- 808 | 809 | ### pm2 810 | 811 | 812 | 813 | 814 | 815 | Advantages: 816 | 817 | * Load-balancer and other features 818 | * 0s reload down-time, i.e., forever alive 819 | * Good test coverage 820 | 821 | --- 822 | 823 | ### pm2 Demo: Typical Express Server 824 | 825 | ```js 826 | var express = require('express') 827 | var port = 3000 828 | global.stats = {} 829 | console.log('worker (%s) is now listening to http://localhost:%s', 830 | process.pid, port) 831 | var app = express() 832 | app.get('*', function(req, res) { 833 | if (!global.stats[process.pid]) global.stats[process.pid] = 1 834 | else global.stats[process.pid] += 1; 835 | var l ='cluser ' 836 | + process.pid 837 | + ' responded \n'; 838 | console.log(l, global.stats) 839 | res.status(200).send(l) 840 | }) 841 | app.listen(port) 842 | ``` 843 | 844 | --- 845 | 846 | ### pm2 Demo 847 | 848 | Using `server.js`: 849 | 850 | ``` 851 | $ pm2 start server.js -i 0 852 | ``` 853 | 854 | In a new window: 855 | 856 | ``` 857 | $ loadtest http://localhost:3000 -t 20 -c 10 858 | $ pm2 list 859 | ``` 860 | 861 | --- 862 | 863 | # Spawn vs Fork vs Exec 864 | 865 | * `require('child_process').spawn()` - large data, stream, no new V8 instance 866 | * `require('child_process').fork()` - new V8 instance, multiple workers 867 | * `require('child_process').exec()` - buffer, async, all the data at once 868 | 869 | --- 870 | 871 | ### Spawn Example 872 | 873 | ```js 874 | fs = require('fs') 875 | process = require('child_process') 876 | var p = process.spawn('node', 'program.js') 877 | p.stdout.on('data', function(data)) { 878 | console.log('stdout: ' + data) 879 | }) 880 | ``` 881 | 882 | --- 883 | 884 | ### Fork Example 885 | 886 | ```js 887 | fs = require('fs') 888 | process = require('child_process') 889 | var p = process.fork('program.js') 890 | p.stdout.on('data', function(data)) { 891 | console.log('stdout: ' + data) 892 | }) 893 | ``` 894 | 895 | --- 896 | 897 | # Exec Example 898 | 899 | ```js 900 | fs = require('fs') 901 | process = require('child_process') 902 | var p = process.exec('node program.js', function (error, stdout, stderr) { 903 | if (error) console.log(error.code) 904 | }) 905 | ``` 906 | 907 | --- 908 | 909 | # How to handle async errors? 910 | 911 | --- 912 | 913 | 914 | # Handling Async Errors 915 | 916 | Event Loop: Async errors are harder to handle/debug, because system loses context of the error. Then, application crashes. 917 | 918 | Try/catch is not good enough. 919 | 920 | --- 921 | 922 | 923 | ### Synchronous Error in Node 924 | 925 | ```js 926 | try { 927 | throw new Error('Fail!') 928 | } catch (e) { 929 | console.log('Custom Error: ' + e.message) 930 | } 931 | ``` 932 | 933 | For sync errors try/catch works fine. 934 | 935 | --- 936 | 937 | 938 | ### Async Error Example 939 | 940 | ```js 941 | try { 942 | setTimeout(function () { 943 | throw new Error('Fail!') 944 | }, Math.round(Math.random()*100)) 945 | } catch (e) { 946 | console.log('Custom Error: ' + e.message) 947 | } 948 | ``` 949 | 950 | The app crashes! 951 | 952 | --- 953 | 954 | # Me When Async Error's Thrown 955 | 956 | ![inline](images/baby_elephant.gif) 957 | 958 | --- 959 | 960 | ### Async Errors 961 | 962 | How to deal with it? 963 | 964 | :sweat_smile: 965 | 966 | --- 967 | 968 | ## Best Practices for Async Errors? 969 | 970 | * Listen to all “on error” events 971 | * Listen to `uncaughtException` 972 | * Use `domain` (soft deprecated) or [AsyncWrap](http://blog.trevnorris.com/2015/02/asyncwrap-tutorial-introduction.html) 973 | * Log, log, log & Trace 974 | * Notify (optional) 975 | * Exit & Restart the process 976 | 977 | --- 978 | 979 | ### on('error') 980 | 981 | Anything that inherits from or creates an instance of the above: Express, LoopBack, Sails, Hapi, etc. 982 | 983 | ```js 984 | server.on('error', function (err) { 985 | console.error(err) 986 | }) 987 | ``` 988 | 989 | --- 990 | 991 | ### on('error') Chained Method Example 992 | 993 | ```js 994 | var http = require(‘http’) 995 | var server = http.createServer(app) 996 | .on('error', function(e) { 997 | console.log(‘Failed to create server’) 998 | console.error(e) 999 | process.exit(1) 1000 | }) 1001 | ``` 1002 | 1003 | --- 1004 | 1005 | ### on(‘error’) Named Variable Example 1006 | 1007 | ```js 1008 | var req = http.request(options, function(res) { 1009 | // … processing the response 1010 | }) 1011 | 1012 | req.on('error', function(e) { 1013 | console.log('problem with request: ' + e.message) 1014 | }) 1015 | ``` 1016 | 1017 | --- 1018 | 1019 | ### uncaughtException 1020 | 1021 | `uncaughtException` is a very crude mechanism for exception handling. An unhandled exception means your application - and by extension Node.js itself - is in an undefined state. Blindly resuming means anything could happen. 1022 | 1023 | --- 1024 | 1025 | ### uncaughtException 1026 | 1027 | Always listen to `uncaughtException`! 1028 | 1029 | ```js 1030 | process.on(‘uncaughtException’, handle) 1031 | ``` 1032 | 1033 | or 1034 | 1035 | ```js 1036 | process.addListener('uncaughtException', handle) 1037 | ``` 1038 | 1039 | --- 1040 | 1041 | 1042 | ### uncaughtException Expanded Examples 1043 | 1044 | ```js 1045 | process.on('uncaughtException', function (err) { 1046 | console.error('uncaughtException: ', err.message) 1047 | console.error(err.stack) 1048 | process.exit(1) 1049 | }) 1050 | ``` 1051 | 1052 | or 1053 | 1054 | ```js 1055 | process.addListener('uncaughtException', function (err) { 1056 | console.error('uncaughtException: ', err.message) 1057 | console.error(err.stack) 1058 | process.exit(1) 1059 | ``` 1060 | 1061 | --- 1062 | 1063 | ### Domain 1064 | 1065 | This module is softly deprecated in 4.0 (most likey will be separate from core module), but there's no alternatives in core as of now. 1066 | 1067 | --- 1068 | 1069 | ### Domain Example 1070 | 1071 | ```js 1072 | var domain = require('domain').create() 1073 | domain.on('error', function(error){ 1074 | console.log(error) 1075 | }) 1076 | domain.run(function(){ 1077 | throw new Error('Failed!') 1078 | }) 1079 | ``` 1080 | 1081 | --- 1082 | 1083 | ### Domain with Async Error Demo 1084 | 1085 | domain-async.js: 1086 | 1087 | ```js 1088 | var d = require('domain').create() 1089 | d.on('error', function(e) { 1090 | console.log('Custom Error: ' + e) 1091 | }) 1092 | d.run(function() { 1093 | setTimeout(function () { 1094 | throw new Error('Failed!') 1095 | }, Math.round(Math.random()*100)) 1096 | }); 1097 | ``` 1098 | 1099 | --- 1100 | 1101 | # C++ Addons 1102 | 1103 | --- 1104 | 1105 | ## How to Write C/C++ binding for your IoT, hardware, drone, smartdevice, etc.? 1106 | 1107 | --- 1108 | 1109 | ### Node and C++ 1110 | 1111 | Create the `hello.cc` file: 1112 | 1113 | ```c 1114 | #include 1115 | 1116 | namespace demo { 1117 | 1118 | using v8::FunctionCallbackInfo; 1119 | using v8::HandleScope; 1120 | using v8::Isolate; 1121 | using v8::Local; 1122 | using v8::Object; 1123 | using v8::String; 1124 | using v8::Value; 1125 | ``` 1126 | 1127 | --- 1128 | 1129 | ### Node and C++ 1130 | 1131 | Create the `hello.cc` file: 1132 | 1133 | ```c 1134 | void Method(const FunctionCallbackInfo& args) { 1135 | Isolate* isolate = args.GetIsolate(); 1136 | args.GetReturnValue().Set(String::NewFromUtf8(isolate, "capital one")); 1137 | } 1138 | 1139 | void init(Local exports) { 1140 | NODE_SET_METHOD(exports, "hello", Method); 1141 | } 1142 | 1143 | NODE_MODULE(addon, init) 1144 | 1145 | } // namespace demo 1146 | ``` 1147 | 1148 | --- 1149 | 1150 | ### Creating `binding.gyp` 1151 | 1152 | Create `binding.gyp`: 1153 | 1154 | ``` 1155 | { 1156 | "targets": [ 1157 | { 1158 | "target_name": "addon", 1159 | "sources": [ "hello.cc" ] 1160 | } 1161 | ] 1162 | } 1163 | ``` 1164 | 1165 | --- 1166 | 1167 | ### node-gyp 1168 | 1169 | ``` 1170 | $ npm install -g node-gyp 1171 | ``` 1172 | 1173 | 1174 | 1175 | ^Needs Python 1176 | 1177 | --- 1178 | 1179 | ### Configuring and Building 1180 | 1181 | ``` 1182 | $ node-gyp configure 1183 | $ node-gyp build 1184 | ``` 1185 | 1186 | Check for compiled `.node` files in build/Release/ 1187 | 1188 | --- 1189 | 1190 | ### C++ Addons Examples 1191 | 1192 | 1193 | 1194 | --- 1195 | 1196 | ### Including Addon 1197 | 1198 | Create `hello.js` and include your C++ addon: 1199 | 1200 | ```js 1201 | var addon = require('./build/Release/addon') 1202 | console.log(addon.hello()) // 'capital one' 1203 | ``` 1204 | 1205 | Run 1206 | 1207 | ``` 1208 | $ node hello.js 1209 | ``` 1210 | 1211 | --- 1212 | 1213 | # Want to work with Node but your boss won't let you? 1214 | 1215 | Capital One is hiring ~2,000 more software engineers in UK, Canada and US. 1216 | 1217 | 1218 | 1219 | We use Node and other cutting-edge open source tech a lot! (React, Kotlin, Clojure, Angular 2, TypeScript, Go, etc.) 1220 | 1221 | --- 1222 | 1223 | # Learn More 1224 | 1225 | Node at Capital One by Azat Mardan at Node Interactive 2015 1226 | 1227 | 1228 | 1229 | ![inline](images/node-capital-one.png) 1230 | 1231 | 1232 | --- 1233 | 1234 | 1235 | # 30-Second Summary 1236 | 1237 | 1. Event Emitters 1238 | 1. Streams 1239 | 1. Buffers 1240 | 1. Clusters 1241 | 1. C++ Addons 1242 | 1. Domain 1243 | 1244 | --- 1245 | 1246 | 1247 | # Slides & Code :page_facing_up: 1248 | 1249 | Everything: 1250 | 1251 | or 1252 | 1253 | just PDF: 1254 | 1255 | 1256 | --- 1257 | 1258 | 1259 | # My Contacts 1260 | 1261 | Twitter: @azat_co 1262 | Email: hi@azat.co 1263 | 1264 | --- 1265 | 1266 | # Want to learn more about Node.js? 1267 | 1268 | Check out [Node.University](http://node.university), [Webapplog.com](http://webapplog.com) and [NodeProgram.com](http://NodeProgram.com) for the best online and in-person education! 1269 | 1270 | 1271 | --- 1272 | 1273 | 1274 | 1275 | ![inline](images/nu.png) 1276 | 1277 | 1278 | 1279 | --- 1280 | 1281 | # One Last Thing 👉 1282 | 1283 | --- 1284 | 1285 | # CodingHorror.com 1286 | 1287 | ![inline](images/atwoods_law.png) 1288 | -------------------------------------------------------------------------------- /README-v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/README-v1.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | footer: © Node.University, 2018 2 | theme: Simple 3 | slidenumbers: true 4 | build-lists: true 5 | [.hide-footer] 6 | [.slidenumbers: false] 7 | 8 | ![](images/you-dk-node-course-cover-v1.png) 9 | 10 | --- 11 | 12 | # You Don't Know Node, v2 13 | ## Quick Intro to Core Node 14 | 15 | --- 16 | 17 | # Turn OFF laptops, phones, distractors and LISTEN 18 | 19 | --- 20 | 21 | # Who am I? `¯\_(ツ)_/¯` 22 | 23 | --- 24 | 25 | 26 | # Not Kyle Simpson 27 | (Hint: He wrote "You Don't Know JS" series) 28 | 29 | ![inline](images/kylesimpson.jpg) 30 | 31 | ^You are looking for Kyle Simpson, you're in a wrong room 32 | 33 | --- 34 | 35 | # My name is Azat Mardan and I am the creator of Node University 36 | 37 | --- 38 | 39 | Azat Mardan and numbers 40 | 41 | * 📚 14 books (not counting Korean, Chinese, Polish and Russian translations) 42 | * 🎤 20+ conferences talks in 2016-17 43 | * 👨‍💻 200+ blog posts on Webapplog: 44 | * ⌨ 239 top most active GitHub contributor, higher Paul Irish, Todd Motto, TJ Holowaychuk, John Papa, etc. ([source](https://github.com/azat-co/practicalnode)) 45 | * 🎓 19 online courses on Node University 46 | 47 | --- 48 | 49 | 50 | ![inline](images/companies.png) 51 | 52 | ^Macy's, Intuit, Northwestern Mutual, Apple, DocuSign, UC Davis, Salesforce, The University of Arizona, The Orchard, M3, Twilio, Fox Studios, Michael Kors 53 | 54 | --- 55 | 56 | ![inline](images/azats-books-covers.png) 57 | 58 | --- 59 | 60 | ## Fun Facts 61 | 62 | * Read ~270 books in last 4 years 63 | * Check social media only once per week 64 | * Prefer coffee with butter instead of milk 65 | * Live in San Francisco Bay Area 66 | 67 | --- 68 | 69 | # I know Node 70 | 71 | * Storify - startup which we sold 72 | * DocuSign - 50M users 73 | * Capital One - Fortune 500 74 | 75 | --- 76 | 77 | # I dislike compiled languages 78 | 79 | ## Actually, just one. Java! 80 | 81 | --- 82 | 83 | ![inline](images/java-compile-funny.jpg) 84 | 85 | 86 | --- 87 | # My Mission is to Make a World a Better Place 88 | 89 | --- 90 | 91 | # My Plan 92 | 93 | Software helps people 94 | 95 | `+` 96 | 97 | Node is a good tool to build good software 98 | 99 | `=` 100 | 101 | I need to teach as many people Node as possible 102 | 103 | --- 104 | 105 | # My Goal (for this talk) 106 | 107 | Spike your interest in core Node features 108 | 109 | --- 110 | 111 | # Starting with basics: Why Use Node? 112 | 113 | --- 114 | 115 | # Input/output is one of the most expensive type tasks (>CPU) 💰 116 | 117 | --- 118 | 119 | # Node has non-blocking I/O 120 | 121 | 122 | --- 123 | 124 | ![inline](images/non-blocking-docs.png) 125 | 126 | ^Docs are not very ilustrative 127 | 128 | --- 129 | 130 | ![inline](images/event-loop-node-interactive.jpg) 131 | 132 | ^Good but too much tech details 133 | 134 | --- 135 | 136 | ![inline](images/non-blocking.png) 137 | 138 | ^This allows processing other tasks while IO calls are unfinished like this 139 | ^Nginx vs. Apache 140 | ^Blocking I/O is expensive! 141 | 142 | 143 | --- 144 | 145 | ### Java Sleep 146 | 147 | ```java 148 | System.out.println("Step: 1"); 149 | System.out.println("Step: 2"); 150 | Thread.sleep(1000); 151 | System.out.println("Step: 3"); 152 | ``` 153 | 154 | 155 | 156 | --- 157 | 158 | ## Node "Sleep" 159 | 160 | ```js 161 | console.log('Step: 1') 162 | setTimeout(function () { 163 | console.log('Step: 3') 164 | }, 1000) 165 | console.log('Step: 2') 166 | ``` 167 | 168 | --- 169 | 170 | ## Process Multiple Tasks 171 | 172 | ```js 173 | console.log('Step: 1') 174 | setTimeout(function () { 175 | console.log('Step: 3') 176 | // console.log('Step 5') 177 | }, 1000); 178 | console.log('Step: 2') 179 | // console.log('Step 4') 180 | ``` 181 | 182 | --- 183 | 184 | # Blocking Web Server 185 | 186 | --- 187 | 188 | ![inline](images/threading_java.png) 189 | 190 | --- 191 | 192 | ![inline](images/coffeeshop-blocking.jpg) 193 | 194 | --- 195 | 196 | # Non-Blocking Web Server 197 | 198 | --- 199 | 200 | ![inline](images/threading_node.png) 201 | 202 | ^This is in contrast to today's more common concurrency model where OS threads are employed. Thread-based networking is relatively inefficient and very difficult to use. Furthermore, users of Node are free from worries of dead-locking the process --- there are no locks 203 | 204 | --- 205 | 206 | ![inline](images/coffeeshop-non-blocking.jpg) 207 | 208 | --- 209 | 210 | # [Multi-threading] is the software equivalent of a nuclear device because if it is used incorrectly, it can blow up in your face. 211 | 212 | 213 | 214 | --- 215 | 216 | # Blocking systems have to be multi-threaded 217 | 218 | --- 219 | 220 | # Node is single threaded... and that's good! 😄 221 | 222 | 223 | --- 224 | 225 | 226 | ## It's still possible to write blocking code in Node.js. :flushed: 227 | 228 | 229 | --- 230 | 231 | # Blocking Node.js Code 232 | 233 | ```js 234 | // blocking.js 235 | console.log('Step: 1') 236 | for (var i = 1; i<1000000000; i++) { 237 | // This will take 100-1000ms 238 | } 239 | console.log('Step: 2') 240 | ``` 241 | 242 | --- 243 | 244 | # Blocking Node.js Code 245 | 246 | ```js 247 | var fs = require('fs') 248 | 249 | var contents = fs.readFileSync('accounts.txt','utf8') 250 | console.log(contents) 251 | console.log('Hello Ruby\n') 252 | 253 | var contents = fs.readFileSync('ips.txt','utf8') 254 | console.log(contents) 255 | console.log('Hello Node!') 256 | //accounts.txt->Hello Ruby->ips.txt->Hello Node! 257 | ``` 258 | 259 | --- 260 | 261 | # Non-Blocking Node.js Code 262 | 263 | ```js 264 | var fs = require('fs') 265 | 266 | fs.readFile('accounts.txt','utf8', function(error, contents){ 267 | console.log(contents) 268 | }) 269 | console.log('Hello Ruby\n') 270 | 271 | fs.readFile('ips.txt','utf8', function(error, contents){ 272 | console.log(contents) 273 | }) 274 | console.log('Hello Node!') 275 | //Hello Ruby->Hello Node->... accounts.txt->ips.txt or ips.txt->accounts.txt 276 | ``` 277 | 278 | --- 279 | 280 | # Node *typically* is much faster than other platforms 281 | 282 | --- 283 | 284 | # How many of you reach the performance limitations of apps built with blocking I/O systems? 285 | 286 | --- 287 | 288 | # Probably not many 289 | 290 | --- 291 | 292 | # My Fav Node Benefit 293 | 294 | --- 295 | 296 | # JavaScript everywhere. One language to rule 'em all! 297 | 298 | --- 299 | 300 | # When one language is used everywhere 301 | 302 | * Think faster 303 | * Reuse code 304 | * Learn quicker 305 | 306 | --- 307 | 308 | # Most of Node is JavaScript 309 | 310 | * Array 311 | * String 312 | * Primitives 313 | * Functions 314 | * Objects 315 | 316 | --- 317 | 318 | 319 | # Node !== Browser JavaScript 320 | 321 | --- 322 | 323 | # Node Core Modules 324 | 325 | ![inline](images/node-core-modules.gif) 326 | 327 | --- 328 | 329 | # How to create global variables (no `window` in Node), work with modules, get path to my script? 330 | 331 | --- 332 | 333 | # `global` 334 | 335 | Now available everywhere in your code 336 | 337 | --- 338 | 339 | # It has properties! 340 | 341 | --- 342 | 343 | 344 | # `global.__filename` 345 | # `global.__dirname` 346 | 347 | 348 | --- 349 | 350 | 351 | # `global.module` 352 | # `global.require()` 353 | 354 | --- 355 | 356 | # How do I...? 357 | 358 | * Access CLI input? 359 | * Get system info: OS, platform, memory usage, versions, etc.? 360 | * Read env vars (passwords!)? 361 | 362 | --- 363 | 364 | # `global.process` or `process` 365 | 366 | --- 367 | 368 | # `process.pid` 369 | # `process.versions` 370 | # `process.arch` 371 | 372 | --- 373 | 374 | # `process.argv` 375 | 376 | --- 377 | 378 | # `process.env` 379 | 380 | --- 381 | 382 | 383 | # `process.uptime()` 384 | # `process.memoryUsage()` 385 | 386 | --- 387 | 388 | # `process.cwd()` 389 | 390 | --- 391 | 392 | # `process.exit()` 393 | # `process.kill()` 394 | 395 | --- 396 | 397 | # Who likes and understands callbacks? 🙋 398 | 399 | --- 400 | 401 | 402 | 403 | ```js 404 | fs.readdir(source, function (err, files) { 405 | if (err) { 406 | console.log('Error finding files: ' + err) 407 | } else { 408 | files.forEach(function (filename, fileIndex) { 409 | console.log(filename) 410 | gm(source + filename).size(function (err, values) { 411 | if (err) { 412 | console.log('Error identifying file size: ' + err) 413 | } else { 414 | console.log(filename + ' : ' + values) 415 | aspect = (values.width / values.height) 416 | widths.forEach(function (width, widthIndex) { 417 | height = Math.round(width / aspect) 418 | console.log('resizing ' + filename + 'to ' + height + 'x' + height) 419 | this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) { 420 | if (err) console.log('Error writing file: ' + err) 421 | }) 422 | }.bind(this)) 423 | } 424 | }) 425 | }) 426 | } 427 | }) 428 | ``` 429 | 430 | --- 431 | 432 | 433 | ## Callbacks are not very developmental scalable 😞 434 | 435 | --- 436 | 437 | # Me When Working With Deeply Nested Callbacks 438 | 439 | ![inline](images/tree_slam.gif) 440 | 441 | --- 442 | 443 | # Events 444 | 445 | Events are part of core and supported by most of the core modules while more advanced patterns such as promises, generators, async/await are not. 446 | 447 | 448 | --- 449 | 450 | ### Events == Node Observer Pattern 451 | 452 | * Subject 453 | * Observers (event listeners) on a subject 454 | * Event triggers 455 | 456 | --- 457 | 458 | ### Events 459 | 460 | ```js 461 | var events = require('events') 462 | var emitter = new events.EventEmitter() 463 | ``` 464 | 465 | --- 466 | 467 | ### Events 468 | 469 | In node.js an event can be described simply as a string with a corresponding callback. 470 | 471 | 472 | ```js 473 | emitter.on('done', function(results) { 474 | console.log('Done: ', results) 475 | }) 476 | ``` 477 | 478 | 479 | --- 480 | 481 | ### Using Event Emitters 482 | 483 | ```js 484 | var events = require('events') 485 | var emitter = new events.EventEmitter() 486 | 487 | emitter.on('knock', function() { 488 | console.log('Who\'s there?') 489 | }) 490 | 491 | emitter.on('knock', function() { 492 | console.log('Go away!') 493 | }) 494 | 495 | emitter.emit('knock') 496 | ``` 497 | 498 | --- 499 | 500 | ### Inheriting from EventEmitter 501 | 502 | ```js 503 | // job.js 504 | var util = require('util') 505 | var Job = function Job() { 506 | // ... 507 | this.process = function() { 508 | // ... 509 | job.emit('done', { completedOn: new Date() }) 510 | } 511 | } 512 | 513 | util.inherits(Job, require('events').EventEmitter) 514 | module.exports = Job 515 | ``` 516 | 517 | --- 518 | 519 | ### Inheriting from EventEmitter 520 | 521 | ```js 522 | // weekly.js 523 | var Job = require('./job.js') 524 | var job = new Job() 525 | 526 | job.on('done', function(details){ 527 | console.log('Job was completed at', details.completedOn) 528 | job.removeAllListeners() 529 | }) 530 | 531 | job.process() 532 | ``` 533 | 534 | --- 535 | 536 | ### Listeners 537 | 538 | ```js 539 | emitter.listeners(eventName) 540 | emitter.on(eventName, listener) 541 | emitter.once(eventName, listener) 542 | emitter.removeListener(eventName, listener) 543 | ``` 544 | 545 | --- 546 | 547 | # Resources to Learn Node Patterns 548 | 549 | * Node Patterns: From Callbacks to Observer: 550 | * 551 | * [Node.js Design Patterns, Second Edition by Mario Casciaro, Luciano Mammino](https://www.packtpub.com/web-development/nodejs-design-patterns-second-edition) 552 | 553 | --- 554 | 555 | # Problems with Large Data 556 | 557 | * Speed: Too slow because has to load all 558 | * Buffer limit: ~1Gb 559 | * Overhyped (JK) 560 | 561 | --- 562 | 563 | # Streams 564 | 565 | ## Abstractions for continuous chunking of data 566 | 567 | --- 568 | 569 | # No need to wait for the entire resource to load 570 | 571 | --- 572 | 573 | # Types of Streams 574 | 575 | * Readable 576 | * Writable 577 | * Duplex 578 | * Transform 579 | 580 | --- 581 | 582 | ## Streams Inherit from Event Emitter 583 | 584 | --- 585 | 586 | # Streams are Everywhere! 587 | 588 | * HTTP requests and responses 589 | * Standard input/output (stdin&stdout) 590 | * File reads and writes 591 | 592 | --- 593 | 594 | # Readable Stream Example 595 | 596 | `process.stdin` 597 | 598 | Standard input streams contain data going into applications. 599 | 600 | --- 601 | 602 | # This is achieved via a read operation. 603 | 604 | --- 605 | 606 | # Input typically comes from the keyboard used to start the process. 607 | 608 | --- 609 | 610 | To listen in on data from stdin, use the `data` and `end` events: 611 | 612 | ```js 613 | // stdin.js 614 | process.stdin.resume() 615 | process.stdin.setEncoding('utf8') 616 | 617 | process.stdin.on('data', function (chunk) { 618 | console.log('chunk: ', chunk) 619 | }) 620 | 621 | process.stdin.on('end', function () { 622 | console.log('--- END ---') 623 | }) 624 | ``` 625 | 626 | --- 627 | 628 | # Demo 629 | 630 | `$ node stdin.js` 631 | 632 | --- 633 | 634 | # New Interface `read()` 635 | 636 | ```js 637 | var readable = getReadableStreamSomehow() 638 | readable.on('readable', () => { 639 | var chunk 640 | while (null !== (chunk = readable.read())) { 641 | console.log('got %d bytes of data', chunk.length) 642 | } 643 | }) 644 | ``` 645 | 646 | ^readable.read is sync but the chunks are small 647 | 648 | --- 649 | 650 | # Writable Stream Example 651 | 652 | `process.stdout` 653 | 654 | Standard output streams contain data going out of the applications. 655 | 656 | --- 657 | 658 | # This is done via a write operation. 659 | 660 | --- 661 | 662 | # Data written to standard output is visible on the command line. 663 | 664 | --- 665 | 666 | # Writable Stream 667 | 668 | To write to `stdout`, use the `write` function: 669 | 670 | ```js 671 | process.stdout.write('A simple message\n') 672 | ``` 673 | 674 | --- 675 | 676 | # What about HTTP? 677 | 678 | --- 679 | 680 | ```js 681 | const http = require('http') 682 | var server = http.createServer( (req, res) => { 683 | req.setEncoding('utf8') 684 | req.on('data', (chunk) => { 685 | transform(chunk) // This functions is defined somewhere else 686 | }) 687 | req.on('end', () => { 688 | var data = JSON.parse(body) 689 | res.end() 690 | }) 691 | }) 692 | 693 | server.listen(1337) 694 | ``` 695 | 696 | --- 697 | 698 | # Pipe 699 | 700 | 701 | ```js 702 | var r = fs.createReadStream('file.txt') 703 | var z = zlib.createGzip() 704 | var w = fs.createWriteStream('file.txt.gz') 705 | r.pipe(z).pipe(w) 706 | ``` 707 | 708 | ^Readable.pipe takes writable and returns destination 709 | 710 | --- 711 | 712 | ## What data type to use for binary data? 713 | 714 | --- 715 | 716 | ### Buffers 717 | 718 | Binary data type, to create: 719 | 720 | * `Buffer.alloc(size)` 721 | * `Buffer.from(array)` 722 | * `Buffer.from(buffer)` 723 | * `Buffer.from(str[, encoding])` 724 | 725 | Docs: 726 | 727 | --- 728 | 729 | # Working with Buffer 730 | 731 | ```js 732 | // buf.js 733 | var buf = Buffer.alloc(26) 734 | for (var i = 0 ; i < 26 ; i++) { 735 | buf[i] = i + 97 // 97 is ASCII a 736 | } 737 | console.log(buf) // 738 | console.log(buf.toString('utf8')) // abcdefghijklmnopqrstuvwxyz 739 | ``` 740 | 741 | --- 742 | 743 | # Buffer Convertion 744 | 745 | ```js 746 | buf.toString('ascii') // outputs: abcdefghijklmnopqrstuvwxyz 747 | buf.toString('ascii', 0, 5) // outputs: abcde 748 | buf.toString('utf8', 0, 5) // outputs: abcde 749 | buf.toString(undefined, 0, 5) // encoding defaults to 'utf8', outputs abcde 750 | ``` 751 | 752 | --- 753 | 754 | ### Remember fs? 755 | 756 | ```js 757 | fs.readFile('/etc/passwd', function (err, data) { 758 | if (err) return console.error(err) 759 | console.log(data) 760 | }); 761 | ``` 762 | 763 | `data` is buffer! 764 | 765 | --- 766 | 767 | # Demo 768 | 769 | ``` 770 | $ node server-stream 771 | ``` 772 | 773 | --- 774 | 775 | # Streams and Buffer Demo 776 | 777 | ```js 778 | // server-stream.js 779 | app.get('/stream', function(req, res) { 780 | var stream = fs.createReadStream(largeImagePath) 781 | stream.pipe(res) 782 | }) 783 | ``` 784 | 785 | ``` 786 | $ node server-stream 787 | ``` 788 | 789 | 790 | 791 | 792 | --- 793 | 794 | # Results in DevTools 795 | 796 | `/stream` responds faster! 797 | 798 | ``` 799 | X-Response-Time 800 | ~300ms vs. 3-5s 801 | ``` 802 | 803 | --- 804 | 805 | # Stream Resources 806 | 807 | Stream automated workshop: 808 | 809 | ``` 810 | $ sudo npm install -g stream-adventure 811 | $ stream-adventure 812 | ``` 813 | 814 | 815 | 816 | --- 817 | 818 | ![inline](images/nodeconfcamp.JPG) 819 | 820 | --- 821 | 822 | # How to scale a single threaded system? 823 | 824 | --- 825 | 826 | # Cluster Usage 827 | 828 | * Master: starts workers 829 | * Worker: do the job, e.g., HTTP server 830 | 831 | Number of processes = number of CPUs 832 | 833 | --- 834 | 835 | # Clusters 836 | 837 | ```js 838 | var cluster = require('cluster') 839 | var numCPUs = require('os').cpus().length 840 | if (cluster.isMaster) { 841 | for (var i = 0; i < numCPUs; i++) { 842 | cluster.fork() 843 | } 844 | } else if (cluster.isWorker) { 845 | // your server code 846 | }) 847 | ``` 848 | 849 | --- 850 | 851 | # Cluster Demo 852 | 853 | 1. Run `code/cluster.js` with node (`$ node cluster.js`). 854 | 1. Install `loadtest` with npm: `$ npm install -g loadtest` 855 | 1. Run load testing with: `$ loadtest http://localhost:3000 -t 20 —c 10` 856 | 857 | Press control+c on the server terminal 858 | 859 | --- 860 | 861 | # Cluster Libraries 862 | 863 | * Core cluster: lean and mean 864 | * strong-cluster-control (https://github.com/strongloop/strong-cluster-control), or `$ slc run`: good choice 865 | * pm2 (https://github.com/Unitech/pm2): good choice 866 | 867 | --- 868 | 869 | ### pm2 870 | 871 | 872 | 873 | 874 | 875 | Advantages: 876 | 877 | * Load-balancer and other features 878 | * 0s reload down-time, i.e., forever alive 879 | * Good test coverage 880 | 881 | --- 882 | 883 | ### pm2 Demo: Typical Express Server 884 | 885 | ```js 886 | var express = require('express') 887 | var port = 3000 888 | global.stats = {} 889 | console.log('worker (%s) is now listening to http://localhost:%s', 890 | process.pid, port) 891 | var app = express() 892 | app.get('*', function(req, res) { 893 | if (!global.stats[process.pid]) global.stats[process.pid] = 1 894 | else global.stats[process.pid] += 1; 895 | var l ='cluser ' 896 | + process.pid 897 | + ' responded \n'; 898 | console.log(l, global.stats) 899 | res.status(200).send(l) 900 | }) 901 | app.listen(port) 902 | ``` 903 | 904 | --- 905 | 906 | ### pm2 Demo 907 | 908 | Using `server.js`: 909 | 910 | ``` 911 | $ pm2 start server.js -i 0 912 | ``` 913 | 914 | In a new window: 915 | 916 | ``` 917 | $ loadtest http://localhost:3000 -t 20 -c 10 918 | $ pm2 list 919 | ``` 920 | 921 | --- 922 | 923 | # Spawn vs Fork vs Exec 924 | 925 | * `require('child_process').spawn()` - large data, stream, no new V8 instance 926 | * `require('child_process').fork()` - new V8 instance, multiple workers 927 | * `require('child_process').exec()` - buffer, async, all the data at once 928 | 929 | --- 930 | 931 | ### Spawn Example 932 | 933 | ```js 934 | fs = require('fs') 935 | process = require('child_process') 936 | var p = process.spawn('node', 'program.js') 937 | p.stdout.on('data', function(data)) { 938 | console.log('stdout: ' + data) 939 | }) 940 | ``` 941 | 942 | --- 943 | 944 | ### Fork Example 945 | 946 | ```js 947 | fs = require('fs') 948 | process = require('child_process') 949 | var p = process.fork('program.js') 950 | p.stdout.on('data', function(data)) { 951 | console.log('stdout: ' + data) 952 | }) 953 | ``` 954 | 955 | --- 956 | 957 | # Exec Example 958 | 959 | ```js 960 | fs = require('fs') 961 | process = require('child_process') 962 | var p = process.exec('node program.js', function (error, stdout, stderr) { 963 | if (error) console.log(error.code) 964 | }) 965 | ``` 966 | 967 | --- 968 | 969 | # There are few more methods 970 | 971 | * child_process.execFile() 972 | * child_process.execSync() 973 | * child_process.execFileSync() 974 | 975 | ^child_process.execFile(): similar to child_process.exec() except that it spawns the command directly without first spawning a shell. 976 | child_process.execSync(): a synchronous version of child_process.exec() that will block the Node.js event loop. 977 | child_process.execFileSync(): a synchronous version of child_process.execFile() that will block the Node.js event loop. 978 | 979 | --- 980 | 981 | # How to handle async errors? 982 | 983 | --- 984 | 985 | 986 | # Handling Async Errors 987 | 988 | Event Loop: Async errors are harder to handle/debug, because system loses context of the error. Then, application crashes. 989 | 990 | Try/catch is not good enough. 991 | 992 | --- 993 | 994 | 995 | ### Synchronous Error in Node 996 | 997 | ```js 998 | try { 999 | throw new Error('Fail!') 1000 | } catch (e) { 1001 | console.log('Custom Error: ' + e.message) 1002 | } 1003 | ``` 1004 | 1005 | For sync errors try/catch works fine. 1006 | 1007 | --- 1008 | 1009 | 1010 | ### Async Error Example 1011 | 1012 | ```js 1013 | try { 1014 | setTimeout(function () { 1015 | throw new Error('Fail!') 1016 | }, Math.round(Math.random()*100)) 1017 | } catch (e) { 1018 | console.log('Custom Error: ' + e.message) 1019 | } 1020 | ``` 1021 | 1022 | The app crashes! 1023 | 1024 | --- 1025 | 1026 | # Me When Async Error's Thrown 1027 | 1028 | ![inline](images/baby_elephant.gif) 1029 | 1030 | --- 1031 | 1032 | ### Async Errors 1033 | 1034 | How to deal with it? 1035 | 1036 | :sweat_smile: 1037 | 1038 | --- 1039 | 1040 | ## Best Practices for Async Errors? 1041 | 1042 | * Listen to all “on error” events 1043 | * Listen to `uncaughtException` 1044 | * Use `domain` (soft deprecated) or [AsyncWrap](http://blog.trevnorris.com/2015/02/asyncwrap-tutorial-introduction.html) 1045 | * Log, log, log & Trace 1046 | * Notify (optional) 1047 | * Exit & Restart the process 1048 | 1049 | --- 1050 | 1051 | ### on('error') 1052 | 1053 | Anything that inherits from or creates an instance of the above: Express, LoopBack, Sails, Hapi, etc. 1054 | 1055 | ```js 1056 | server.on('error', function (err) { 1057 | console.error(err) 1058 | }) 1059 | ``` 1060 | 1061 | --- 1062 | 1063 | ### on('error') Chained Method Example 1064 | 1065 | ```js 1066 | var http = require(‘http’) 1067 | var server = http.createServer(app) 1068 | .on('error', function(e) { 1069 | console.log(‘Failed to create server’) 1070 | console.error(e) 1071 | process.exit(1) 1072 | }) 1073 | ``` 1074 | 1075 | --- 1076 | 1077 | ### on(‘error’) Named Variable Example 1078 | 1079 | ```js 1080 | var req = http.request(options, function(res) { 1081 | // … processing the response 1082 | }) 1083 | 1084 | req.on('error', function(e) { 1085 | console.log('problem with request: ' + e.message) 1086 | }) 1087 | ``` 1088 | 1089 | --- 1090 | 1091 | ### uncaughtException 1092 | 1093 | `uncaughtException` is a very crude mechanism for exception handling. An unhandled exception means your application - and by extension Node.js itself - is in an undefined state. Blindly resuming means anything could happen. 1094 | 1095 | --- 1096 | 1097 | ### uncaughtException 1098 | 1099 | Always listen to `uncaughtException`! 1100 | 1101 | ```js 1102 | process.on(‘uncaughtException’, handle) 1103 | ``` 1104 | 1105 | or 1106 | 1107 | ```js 1108 | process.addListener('uncaughtException', handle) 1109 | ``` 1110 | 1111 | --- 1112 | 1113 | 1114 | ### uncaughtException Expanded Examples 1115 | 1116 | ```js 1117 | process.on('uncaughtException', function (err) { 1118 | console.error('uncaughtException: ', err.message) 1119 | console.error(err.stack) 1120 | process.exit(1) 1121 | }) 1122 | ``` 1123 | 1124 | or 1125 | 1126 | ```js 1127 | process.addListener('uncaughtException', function (err) { 1128 | console.error('uncaughtException: ', err.message) 1129 | console.error(err.stack) 1130 | process.exit(1) 1131 | ``` 1132 | 1133 | --- 1134 | 1135 | # Domain 1136 | 1137 | (Just out of curiosity) 1138 | 1139 | --- 1140 | 1141 | Domain Example 1142 | 1143 | ```js 1144 | let domain = require('domain').create() 1145 | domain.on('error', function(error){ 1146 | console.log(error) 1147 | }) 1148 | domain.run(function(){ 1149 | throw new Error('Failed!') 1150 | }) 1151 | ``` 1152 | 1153 | --- 1154 | 1155 | Domain with Async Error Demo 1156 | 1157 | domain-async.js: 1158 | 1159 | ```js 1160 | let d = require('domain').create() 1161 | d.on('error', function(e) { 1162 | console.log('Custom Error: ' + e) 1163 | }) 1164 | d.run(function() { 1165 | setTimeout(function () { 1166 | throw new Error('Failed!') 1167 | }, Math.round(Math.random()*100)) 1168 | }) 1169 | ``` 1170 | 1171 | --- 1172 | 1173 | # Source code for Domain 1174 | 1175 | 1176 | 1177 | ```js 1178 | var _domain = [null]; 1179 | Object.defineProperty(process, 'domain', { 1180 | enumerable: true, 1181 | get: function() { 1182 | return _domain[0]; 1183 | }, 1184 | set: function(arg) { 1185 | return _domain[0] = arg; 1186 | } 1187 | }); 1188 | ``` 1189 | 1190 | --- 1191 | 1192 | # Domain overwrites uncaught exception 1193 | 1194 | ```js 1195 | if (process.hasUncaughtExceptionCaptureCallback()) { 1196 | throw new errors.Error('ERR_DOMAIN_CALLBACK_NOT_AVAILABLE'); 1197 | } 1198 | ``` 1199 | 1200 | ```js 1201 | process.on('removeListener', (name, listener) => { 1202 | if (name === 'uncaughtException' && 1203 | listener !== domainUncaughtExceptionClear) { 1204 | // If the domain listener would be the only remaining one, remove it. 1205 | const listeners = process.listeners('uncaughtException'); 1206 | if (listeners.length === 1 && listeners[0] === domainUncaughtExceptionClear) 1207 | process.removeListener(name, domainUncaughtExceptionClear); 1208 | } 1209 | }); 1210 | ``` 1211 | 1212 | ^When domains are in use, they claim full ownership of the uncaught exception capture callback. 1213 | 1214 | --- 1215 | 1216 | 1217 | # C++ Addons 1218 | 1219 | --- 1220 | 1221 | ## How to Write C/C++ binding for your IoT, hardware, drone, smartdevice, etc.? 1222 | 1223 | --- 1224 | 1225 | ### Node and C++ 1226 | 1227 | Create the `hello.cc` file: 1228 | 1229 | ```c 1230 | #include 1231 | 1232 | namespace demo { 1233 | 1234 | using v8::FunctionCallbackInfo; 1235 | using v8::HandleScope; 1236 | using v8::Isolate; 1237 | using v8::Local; 1238 | using v8::Object; 1239 | using v8::String; 1240 | using v8::Value; 1241 | ``` 1242 | 1243 | --- 1244 | 1245 | ### Node and C++ 1246 | 1247 | Create the `hello.cc` file: 1248 | 1249 | ```c 1250 | void Method(const FunctionCallbackInfo& args) { 1251 | Isolate* isolate = args.GetIsolate(); 1252 | args.GetReturnValue().Set(String::NewFromUtf8(isolate, "capital one")); 1253 | } 1254 | 1255 | void init(Local exports) { 1256 | NODE_SET_METHOD(exports, "hello", Method); 1257 | } 1258 | 1259 | NODE_MODULE(addon, init) 1260 | 1261 | } // namespace demo 1262 | ``` 1263 | 1264 | --- 1265 | 1266 | ### Creating `binding.gyp` 1267 | 1268 | Create `binding.gyp`: 1269 | 1270 | ``` 1271 | { 1272 | "targets": [ 1273 | { 1274 | "target_name": "addon", 1275 | "sources": [ "hello.cc" ] 1276 | } 1277 | ] 1278 | } 1279 | ``` 1280 | 1281 | --- 1282 | 1283 | ### node-gyp 1284 | 1285 | ``` 1286 | $ npm install -g node-gyp 1287 | ``` 1288 | 1289 | 1290 | 1291 | ^Needs Python 1292 | 1293 | --- 1294 | 1295 | ### Configuring and Building 1296 | 1297 | ``` 1298 | $ node-gyp configure 1299 | $ node-gyp build 1300 | ``` 1301 | 1302 | Check for compiled `.node` files in build/Release/ 1303 | 1304 | --- 1305 | 1306 | ### C++ Addons Examples 1307 | 1308 | 1309 | 1310 | --- 1311 | 1312 | ### Including Addon 1313 | 1314 | Create `hello.js` and include your C++ addon: 1315 | 1316 | ```js 1317 | var addon = require('./build/Release/addon') 1318 | console.log(addon.hello()) // 'capital one' 1319 | ``` 1320 | 1321 | Run 1322 | 1323 | ``` 1324 | $ node hello.js 1325 | ``` 1326 | 1327 | --- 1328 | 1329 | Moaaarr *core* Node 1330 | 1331 | * NAPI 1332 | * Crypto 1333 | * HTTP/2 1334 | * Node modules 1335 | * npm commands and scripts 1336 | 1337 | --- 1338 | 1339 | # 30-Second Summary 1340 | 1341 | 1. Event Emitters 1342 | 1. Streams 1343 | 1. Buffers 1344 | 1. Clusters 1345 | 1. C++ Addons 1346 | 1. Domain 1347 | 1348 | ^We have covered a lot in the last 60 minutes 1349 | 1350 | --- 1351 | 1352 | # There are big, big, big problems 1353 | 1354 | * 2 days 1355 | * 5 days 1356 | * 8 days 1357 | 1358 | ^2 days - won't remember my name and most of what I said 1359 | ^5 days - remember only a joke 1360 | ^8 days - won't remember being here 1361 | 1362 | --- 1363 | 1364 | # New information 1365 | 1366 | * Repetition 1367 | * Immersion 1368 | * Practice 1369 | 1370 | --- 1371 | 1372 | # Solution: You Don't Know Node Online Course 1373 | 1374 | ![inline](images/you-dk-node-course-cover-v1.png) 1375 | 1376 | 1377 | 1378 | --- 1379 | 1380 | # Sign up for Node University NOW! 1381 | 1382 | ![inline](images/watermark-inverted-no-bk.png) 1383 | 1384 | [Node.University](https://node.university) is the ultimate resource for the best online Node education! 1385 | 1386 | 1387 | 🚀 1388 | 1389 | --- 1390 | 1391 | # This is how to do it 1392 | 1393 | --- 1394 | 1395 | # Take out your laptops or phones :computer: 📱 1396 | 1397 | Seriously. Do it now! 1398 | 1399 | --- 1400 | 1401 | # Go to node.university 1402 | 1403 | ( in Safari, Chrome, Edge) 1404 | 1405 | --- 1406 | 1407 | [.hide-footer] 1408 | [.slidenumbers: false] 1409 | 1410 | ![fit](images/nodeu-1.png) 1411 | 1412 | ^Click/press on Free courses 1413 | 1414 | --- 1415 | 1416 | [.hide-footer] 1417 | [.slidenumbers: false] 1418 | 1419 | ![fit](images/nodeu-2.png) 1420 | 1421 | ^ Click/press on You Don't Know Node 1422 | 1423 | --- 1424 | 1425 | [.hide-footer] 1426 | [.slidenumbers: false] 1427 | 1428 | ![fit](images/nodeu-3.png) 1429 | 1430 | ^Click/Press on enroll 1431 | 1432 | --- 1433 | 1434 | [.hide-footer] 1435 | [.slidenumbers: false] 1436 | 1437 | ![fit](images/nodeu-4.png) 1438 | 1439 | ^Put your email and create a password 1440 | 1441 | 1442 | --- 1443 | 1444 | # One Last Thing 👉 1445 | 1446 | --- 1447 | 1448 | # CodingHorror.com 1449 | 1450 | ![inline](images/atwoods_law.png) 1451 | 1452 | --- 1453 | 1454 | # The End 1455 | -------------------------------------------------------------------------------- /README.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/README.pdf -------------------------------------------------------------------------------- /code/2013-01-17T235432Z_01_NYK520_RTRIDSP_3_CAPITALONE-RESULTS.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/code/2013-01-17T235432Z_01_NYK520_RTRIDSP_3_CAPITALONE-RESULTS.jpg -------------------------------------------------------------------------------- /code/CapitalOne_Digital_Skills_Infographic_BW.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/code/CapitalOne_Digital_Skills_Infographic_BW.jpg -------------------------------------------------------------------------------- /code/async-error.js: -------------------------------------------------------------------------------- 1 | try { 2 | setTimeout(function () { 3 | throw new Error("Fail!"); 4 | }, Math.round(Math.random()*100)); 5 | } catch (e) { 6 | console.log('Custom Error: ' + e.message); 7 | } 8 | -------------------------------------------------------------------------------- /code/binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "addon", 5 | "sources": [ "hello.cc" ] 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /code/blocking.js: -------------------------------------------------------------------------------- 1 | console.log('Step: 1') 2 | var start = Date.now() 3 | for (var i = 1; i<1000000000; i++) { 4 | // This will take 100-1000ms 5 | } 6 | var end = Date.now() 7 | console.log('Step: 2') 8 | console.log(end-start) 9 | -------------------------------------------------------------------------------- /code/buf.js: -------------------------------------------------------------------------------- 1 | var buf = Buffer.alloc(26) 2 | for (var i = 0 ; i < 26 ; i++) { 3 | buf[i] = i + 97 // 97 is ASCII a 4 | } 5 | console.log(buf) 6 | // 7 | console.log(buf.toString('utf8')) 8 | // abcdefghijklmnopqrstuvwxyz -------------------------------------------------------------------------------- /code/build/Makefile: -------------------------------------------------------------------------------- 1 | # We borrow heavily from the kernel build setup, though we are simpler since 2 | # we don't have Kconfig tweaking settings on us. 3 | 4 | # The implicit make rules have it looking for RCS files, among other things. 5 | # We instead explicitly write all the rules we care about. 6 | # It's even quicker (saves ~200ms) to pass -r on the command line. 7 | MAKEFLAGS=-r 8 | 9 | # The source directory tree. 10 | srcdir := .. 11 | abs_srcdir := $(abspath $(srcdir)) 12 | 13 | # The name of the builddir. 14 | builddir_name ?= . 15 | 16 | # The V=1 flag on command line makes us verbosely print command lines. 17 | ifdef V 18 | quiet= 19 | else 20 | quiet=quiet_ 21 | endif 22 | 23 | # Specify BUILDTYPE=Release on the command line for a release build. 24 | BUILDTYPE ?= Release 25 | 26 | # Directory all our build output goes into. 27 | # Note that this must be two directories beneath src/ for unit tests to pass, 28 | # as they reach into the src/ directory for data with relative paths. 29 | builddir ?= $(builddir_name)/$(BUILDTYPE) 30 | abs_builddir := $(abspath $(builddir)) 31 | depsdir := $(builddir)/.deps 32 | 33 | # Object output directory. 34 | obj := $(builddir)/obj 35 | abs_obj := $(abspath $(obj)) 36 | 37 | # We build up a list of every single one of the targets so we can slurp in the 38 | # generated dependency rule Makefiles in one pass. 39 | all_deps := 40 | 41 | 42 | 43 | CC.target ?= $(CC) 44 | CFLAGS.target ?= $(CPPFLAGS) $(CFLAGS) 45 | CXX.target ?= $(CXX) 46 | CXXFLAGS.target ?= $(CPPFLAGS) $(CXXFLAGS) 47 | LINK.target ?= $(LINK) 48 | LDFLAGS.target ?= $(LDFLAGS) 49 | AR.target ?= $(AR) 50 | 51 | # C++ apps need to be linked with g++. 52 | LINK ?= $(CXX.target) 53 | 54 | # TODO(evan): move all cross-compilation logic to gyp-time so we don't need 55 | # to replicate this environment fallback in make as well. 56 | CC.host ?= gcc 57 | CFLAGS.host ?= $(CPPFLAGS_host) $(CFLAGS_host) 58 | CXX.host ?= g++ 59 | CXXFLAGS.host ?= $(CPPFLAGS_host) $(CXXFLAGS_host) 60 | LINK.host ?= $(CXX.host) 61 | LDFLAGS.host ?= 62 | AR.host ?= ar 63 | 64 | # Define a dir function that can handle spaces. 65 | # http://www.gnu.org/software/make/manual/make.html#Syntax-of-Functions 66 | # "leading spaces cannot appear in the text of the first argument as written. 67 | # These characters can be put into the argument value by variable substitution." 68 | empty := 69 | space := $(empty) $(empty) 70 | 71 | # http://stackoverflow.com/questions/1189781/using-make-dir-or-notdir-on-a-path-with-spaces 72 | replace_spaces = $(subst $(space),?,$1) 73 | unreplace_spaces = $(subst ?,$(space),$1) 74 | dirx = $(call unreplace_spaces,$(dir $(call replace_spaces,$1))) 75 | 76 | # Flags to make gcc output dependency info. Note that you need to be 77 | # careful here to use the flags that ccache and distcc can understand. 78 | # We write to a dep file on the side first and then rename at the end 79 | # so we can't end up with a broken dep file. 80 | depfile = $(depsdir)/$(call replace_spaces,$@).d 81 | DEPFLAGS = -MMD -MF $(depfile).raw 82 | 83 | # We have to fixup the deps output in a few ways. 84 | # (1) the file output should mention the proper .o file. 85 | # ccache or distcc lose the path to the target, so we convert a rule of 86 | # the form: 87 | # foobar.o: DEP1 DEP2 88 | # into 89 | # path/to/foobar.o: DEP1 DEP2 90 | # (2) we want missing files not to cause us to fail to build. 91 | # We want to rewrite 92 | # foobar.o: DEP1 DEP2 \ 93 | # DEP3 94 | # to 95 | # DEP1: 96 | # DEP2: 97 | # DEP3: 98 | # so if the files are missing, they're just considered phony rules. 99 | # We have to do some pretty insane escaping to get those backslashes 100 | # and dollar signs past make, the shell, and sed at the same time. 101 | # Doesn't work with spaces, but that's fine: .d files have spaces in 102 | # their names replaced with other characters. 103 | define fixup_dep 104 | # The depfile may not exist if the input file didn't have any #includes. 105 | touch $(depfile).raw 106 | # Fixup path as in (1). 107 | sed -e "s|^$(notdir $@)|$@|" $(depfile).raw >> $(depfile) 108 | # Add extra rules as in (2). 109 | # We remove slashes and replace spaces with new lines; 110 | # remove blank lines; 111 | # delete the first line and append a colon to the remaining lines. 112 | sed -e 's|\\||' -e 'y| |\n|' $(depfile).raw |\ 113 | grep -v '^$$' |\ 114 | sed -e 1d -e 's|$$|:|' \ 115 | >> $(depfile) 116 | rm $(depfile).raw 117 | endef 118 | 119 | # Command definitions: 120 | # - cmd_foo is the actual command to run; 121 | # - quiet_cmd_foo is the brief-output summary of the command. 122 | 123 | quiet_cmd_cc = CC($(TOOLSET)) $@ 124 | cmd_cc = $(CC.$(TOOLSET)) $(GYP_CFLAGS) $(DEPFLAGS) $(CFLAGS.$(TOOLSET)) -c -o $@ $< 125 | 126 | quiet_cmd_cxx = CXX($(TOOLSET)) $@ 127 | cmd_cxx = $(CXX.$(TOOLSET)) $(GYP_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< 128 | 129 | quiet_cmd_objc = CXX($(TOOLSET)) $@ 130 | cmd_objc = $(CC.$(TOOLSET)) $(GYP_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< 131 | 132 | quiet_cmd_objcxx = CXX($(TOOLSET)) $@ 133 | cmd_objcxx = $(CXX.$(TOOLSET)) $(GYP_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< 134 | 135 | # Commands for precompiled header files. 136 | quiet_cmd_pch_c = CXX($(TOOLSET)) $@ 137 | cmd_pch_c = $(CC.$(TOOLSET)) $(GYP_PCH_CFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< 138 | quiet_cmd_pch_cc = CXX($(TOOLSET)) $@ 139 | cmd_pch_cc = $(CC.$(TOOLSET)) $(GYP_PCH_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS.$(TOOLSET)) -c -o $@ $< 140 | quiet_cmd_pch_m = CXX($(TOOLSET)) $@ 141 | cmd_pch_m = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCFLAGS) $(DEPFLAGS) -c -o $@ $< 142 | quiet_cmd_pch_mm = CXX($(TOOLSET)) $@ 143 | cmd_pch_mm = $(CC.$(TOOLSET)) $(GYP_PCH_OBJCXXFLAGS) $(DEPFLAGS) -c -o $@ $< 144 | 145 | # gyp-mac-tool is written next to the root Makefile by gyp. 146 | # Use $(4) for the command, since $(2) and $(3) are used as flag by do_cmd 147 | # already. 148 | quiet_cmd_mac_tool = MACTOOL $(4) $< 149 | cmd_mac_tool = ./gyp-mac-tool $(4) $< "$@" 150 | 151 | quiet_cmd_mac_package_framework = PACKAGE FRAMEWORK $@ 152 | cmd_mac_package_framework = ./gyp-mac-tool package-framework "$@" $(4) 153 | 154 | quiet_cmd_infoplist = INFOPLIST $@ 155 | cmd_infoplist = $(CC.$(TOOLSET)) -E -P -Wno-trigraphs -x c $(INFOPLIST_DEFINES) "$<" -o "$@" 156 | 157 | quiet_cmd_touch = TOUCH $@ 158 | cmd_touch = touch $@ 159 | 160 | quiet_cmd_copy = COPY $@ 161 | # send stderr to /dev/null to ignore messages when linking directories. 162 | cmd_copy = rm -rf "$@" && cp -af "$<" "$@" 163 | 164 | quiet_cmd_alink = LIBTOOL-STATIC $@ 165 | cmd_alink = rm -f $@ && ./gyp-mac-tool filter-libtool libtool $(GYP_LIBTOOLFLAGS) -static -o $@ $(filter %.o,$^) 166 | 167 | quiet_cmd_link = LINK($(TOOLSET)) $@ 168 | cmd_link = $(LINK.$(TOOLSET)) $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) 169 | 170 | quiet_cmd_solink = SOLINK($(TOOLSET)) $@ 171 | cmd_solink = $(LINK.$(TOOLSET)) -shared $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o "$@" $(LD_INPUTS) $(LIBS) 172 | 173 | quiet_cmd_solink_module = SOLINK_MODULE($(TOOLSET)) $@ 174 | cmd_solink_module = $(LINK.$(TOOLSET)) -bundle $(GYP_LDFLAGS) $(LDFLAGS.$(TOOLSET)) -o $@ $(filter-out FORCE_DO_CMD, $^) $(LIBS) 175 | 176 | 177 | # Define an escape_quotes function to escape single quotes. 178 | # This allows us to handle quotes properly as long as we always use 179 | # use single quotes and escape_quotes. 180 | escape_quotes = $(subst ','\'',$(1)) 181 | # This comment is here just to include a ' to unconfuse syntax highlighting. 182 | # Define an escape_vars function to escape '$' variable syntax. 183 | # This allows us to read/write command lines with shell variables (e.g. 184 | # $LD_LIBRARY_PATH), without triggering make substitution. 185 | escape_vars = $(subst $$,$$$$,$(1)) 186 | # Helper that expands to a shell command to echo a string exactly as it is in 187 | # make. This uses printf instead of echo because printf's behaviour with respect 188 | # to escape sequences is more portable than echo's across different shells 189 | # (e.g., dash, bash). 190 | exact_echo = printf '%s\n' '$(call escape_quotes,$(1))' 191 | 192 | # Helper to compare the command we're about to run against the command 193 | # we logged the last time we ran the command. Produces an empty 194 | # string (false) when the commands match. 195 | # Tricky point: Make has no string-equality test function. 196 | # The kernel uses the following, but it seems like it would have false 197 | # positives, where one string reordered its arguments. 198 | # arg_check = $(strip $(filter-out $(cmd_$(1)), $(cmd_$@)) \ 199 | # $(filter-out $(cmd_$@), $(cmd_$(1)))) 200 | # We instead substitute each for the empty string into the other, and 201 | # say they're equal if both substitutions produce the empty string. 202 | # .d files contain ? instead of spaces, take that into account. 203 | command_changed = $(or $(subst $(cmd_$(1)),,$(cmd_$(call replace_spaces,$@))),\ 204 | $(subst $(cmd_$(call replace_spaces,$@)),,$(cmd_$(1)))) 205 | 206 | # Helper that is non-empty when a prerequisite changes. 207 | # Normally make does this implicitly, but we force rules to always run 208 | # so we can check their command lines. 209 | # $? -- new prerequisites 210 | # $| -- order-only dependencies 211 | prereq_changed = $(filter-out FORCE_DO_CMD,$(filter-out $|,$?)) 212 | 213 | # Helper that executes all postbuilds until one fails. 214 | define do_postbuilds 215 | @E=0;\ 216 | for p in $(POSTBUILDS); do\ 217 | eval $$p;\ 218 | E=$$?;\ 219 | if [ $$E -ne 0 ]; then\ 220 | break;\ 221 | fi;\ 222 | done;\ 223 | if [ $$E -ne 0 ]; then\ 224 | rm -rf "$@";\ 225 | exit $$E;\ 226 | fi 227 | endef 228 | 229 | # do_cmd: run a command via the above cmd_foo names, if necessary. 230 | # Should always run for a given target to handle command-line changes. 231 | # Second argument, if non-zero, makes it do asm/C/C++ dependency munging. 232 | # Third argument, if non-zero, makes it do POSTBUILDS processing. 233 | # Note: We intentionally do NOT call dirx for depfile, since it contains ? for 234 | # spaces already and dirx strips the ? characters. 235 | define do_cmd 236 | $(if $(or $(command_changed),$(prereq_changed)), 237 | @$(call exact_echo, $($(quiet)cmd_$(1))) 238 | @mkdir -p "$(call dirx,$@)" "$(dir $(depfile))" 239 | $(if $(findstring flock,$(word 2,$(cmd_$1))), 240 | @$(cmd_$(1)) 241 | @echo " $(quiet_cmd_$(1)): Finished", 242 | @$(cmd_$(1)) 243 | ) 244 | @$(call exact_echo,$(call escape_vars,cmd_$(call replace_spaces,$@) := $(cmd_$(1)))) > $(depfile) 245 | @$(if $(2),$(fixup_dep)) 246 | $(if $(and $(3), $(POSTBUILDS)), 247 | $(call do_postbuilds) 248 | ) 249 | ) 250 | endef 251 | 252 | # Declare the "all" target first so it is the default, 253 | # even though we don't have the deps yet. 254 | .PHONY: all 255 | all: 256 | 257 | # make looks for ways to re-generate included makefiles, but in our case, we 258 | # don't have a direct way. Explicitly telling make that it has nothing to do 259 | # for them makes it go faster. 260 | %.d: ; 261 | 262 | # Use FORCE_DO_CMD to force a target to run. Should be coupled with 263 | # do_cmd. 264 | .PHONY: FORCE_DO_CMD 265 | FORCE_DO_CMD: 266 | 267 | TOOLSET := target 268 | # Suffix rules, putting all outputs into $(obj). 269 | $(obj).$(TOOLSET)/%.o: $(srcdir)/%.c FORCE_DO_CMD 270 | @$(call do_cmd,cc,1) 271 | $(obj).$(TOOLSET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD 272 | @$(call do_cmd,cxx,1) 273 | $(obj).$(TOOLSET)/%.o: $(srcdir)/%.cpp FORCE_DO_CMD 274 | @$(call do_cmd,cxx,1) 275 | $(obj).$(TOOLSET)/%.o: $(srcdir)/%.cxx FORCE_DO_CMD 276 | @$(call do_cmd,cxx,1) 277 | $(obj).$(TOOLSET)/%.o: $(srcdir)/%.m FORCE_DO_CMD 278 | @$(call do_cmd,objc,1) 279 | $(obj).$(TOOLSET)/%.o: $(srcdir)/%.mm FORCE_DO_CMD 280 | @$(call do_cmd,objcxx,1) 281 | $(obj).$(TOOLSET)/%.o: $(srcdir)/%.S FORCE_DO_CMD 282 | @$(call do_cmd,cc,1) 283 | $(obj).$(TOOLSET)/%.o: $(srcdir)/%.s FORCE_DO_CMD 284 | @$(call do_cmd,cc,1) 285 | 286 | # Try building from generated source, too. 287 | $(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.c FORCE_DO_CMD 288 | @$(call do_cmd,cc,1) 289 | $(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD 290 | @$(call do_cmd,cxx,1) 291 | $(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cpp FORCE_DO_CMD 292 | @$(call do_cmd,cxx,1) 293 | $(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.cxx FORCE_DO_CMD 294 | @$(call do_cmd,cxx,1) 295 | $(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.m FORCE_DO_CMD 296 | @$(call do_cmd,objc,1) 297 | $(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.mm FORCE_DO_CMD 298 | @$(call do_cmd,objcxx,1) 299 | $(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.S FORCE_DO_CMD 300 | @$(call do_cmd,cc,1) 301 | $(obj).$(TOOLSET)/%.o: $(obj).$(TOOLSET)/%.s FORCE_DO_CMD 302 | @$(call do_cmd,cc,1) 303 | 304 | $(obj).$(TOOLSET)/%.o: $(obj)/%.c FORCE_DO_CMD 305 | @$(call do_cmd,cc,1) 306 | $(obj).$(TOOLSET)/%.o: $(obj)/%.cc FORCE_DO_CMD 307 | @$(call do_cmd,cxx,1) 308 | $(obj).$(TOOLSET)/%.o: $(obj)/%.cpp FORCE_DO_CMD 309 | @$(call do_cmd,cxx,1) 310 | $(obj).$(TOOLSET)/%.o: $(obj)/%.cxx FORCE_DO_CMD 311 | @$(call do_cmd,cxx,1) 312 | $(obj).$(TOOLSET)/%.o: $(obj)/%.m FORCE_DO_CMD 313 | @$(call do_cmd,objc,1) 314 | $(obj).$(TOOLSET)/%.o: $(obj)/%.mm FORCE_DO_CMD 315 | @$(call do_cmd,objcxx,1) 316 | $(obj).$(TOOLSET)/%.o: $(obj)/%.S FORCE_DO_CMD 317 | @$(call do_cmd,cc,1) 318 | $(obj).$(TOOLSET)/%.o: $(obj)/%.s FORCE_DO_CMD 319 | @$(call do_cmd,cc,1) 320 | 321 | 322 | ifeq ($(strip $(foreach prefix,$(NO_LOAD),\ 323 | $(findstring $(join ^,$(prefix)),\ 324 | $(join ^,addon.target.mk)))),) 325 | include addon.target.mk 326 | endif 327 | 328 | quiet_cmd_regen_makefile = ACTION Regenerating $@ 329 | cmd_regen_makefile = cd $(srcdir); /usr/local/lib/node_modules/node-gyp/gyp/gyp_main.py -fmake --ignore-environment "--toplevel-dir=." -I/Users/azat/Documents/Code/you-dont-know-node/code/build/config.gypi -I/usr/local/lib/node_modules/node-gyp/addon.gypi -I/Users/azat/.node-gyp/6.2.0/include/node/common.gypi "--depth=." "-Goutput_dir=." "--generator-output=build" "-Dlibrary=shared_library" "-Dvisibility=default" "-Dnode_root_dir=/Users/azat/.node-gyp/6.2.0" "-Dnode_gyp_dir=/usr/local/lib/node_modules/node-gyp" "-Dnode_lib_file=node.lib" "-Dmodule_root_dir=/Users/azat/Documents/Code/you-dont-know-node/code" binding.gyp 330 | Makefile: $(srcdir)/../../../../../../usr/local/lib/node_modules/node-gyp/addon.gypi $(srcdir)/build/config.gypi $(srcdir)/binding.gyp $(srcdir)/../../../../.node-gyp/6.2.0/include/node/common.gypi 331 | $(call do_cmd,regen_makefile) 332 | 333 | # "all" is a concatenation of the "all" targets from all the included 334 | # sub-makefiles. This is just here to clarify. 335 | all: 336 | 337 | # Add in dependency-tracking rules. $(all_deps) is the list of every single 338 | # target in our tree. Only consider the ones with .d (dependency) info: 339 | d_files := $(wildcard $(foreach f,$(all_deps),$(depsdir)/$(f).d)) 340 | ifneq ($(d_files),) 341 | include $(d_files) 342 | endif 343 | -------------------------------------------------------------------------------- /code/build/Release/.deps/Release/addon.node.d: -------------------------------------------------------------------------------- 1 | cmd_Release/addon.node := c++ -bundle -undefined dynamic_lookup -Wl,-no_pie -Wl,-search_paths_first -mmacosx-version-min=10.7 -arch x86_64 -L./Release -o Release/addon.node Release/obj.target/addon/hello.o 2 | -------------------------------------------------------------------------------- /code/build/Release/.deps/Release/obj.target/addon/hello.o.d: -------------------------------------------------------------------------------- 1 | cmd_Release/obj.target/addon/hello.o := c++ '-DNODE_GYP_MODULE_NAME=addon' '-D_DARWIN_USE_64_BIT_INODE=1' '-D_LARGEFILE_SOURCE' '-D_FILE_OFFSET_BITS=64' '-DBUILDING_NODE_EXTENSION' -I/Users/azat/.node-gyp/6.2.0/include/node -I/Users/azat/.node-gyp/6.2.0/src -I/Users/azat/.node-gyp/6.2.0/deps/uv/include -I/Users/azat/.node-gyp/6.2.0/deps/v8/include -Os -gdwarf-2 -mmacosx-version-min=10.7 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=gnu++0x -fno-rtti -fno-exceptions -fno-threadsafe-statics -fno-strict-aliasing -MMD -MF ./Release/.deps/Release/obj.target/addon/hello.o.d.raw -c -o Release/obj.target/addon/hello.o ../hello.cc 2 | Release/obj.target/addon/hello.o: ../hello.cc \ 3 | /Users/azat/.node-gyp/6.2.0/include/node/node.h \ 4 | /Users/azat/.node-gyp/6.2.0/include/node/v8.h \ 5 | /Users/azat/.node-gyp/6.2.0/include/node/v8-version.h \ 6 | /Users/azat/.node-gyp/6.2.0/include/node/v8config.h \ 7 | /Users/azat/.node-gyp/6.2.0/include/node/node_version.h 8 | ../hello.cc: 9 | /Users/azat/.node-gyp/6.2.0/include/node/node.h: 10 | /Users/azat/.node-gyp/6.2.0/include/node/v8.h: 11 | /Users/azat/.node-gyp/6.2.0/include/node/v8-version.h: 12 | /Users/azat/.node-gyp/6.2.0/include/node/v8config.h: 13 | /Users/azat/.node-gyp/6.2.0/include/node/node_version.h: 14 | -------------------------------------------------------------------------------- /code/build/Release/addon.node: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/code/build/Release/addon.node -------------------------------------------------------------------------------- /code/build/Release/obj.target/addon/hello.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/code/build/Release/obj.target/addon/hello.o -------------------------------------------------------------------------------- /code/build/addon.target.mk: -------------------------------------------------------------------------------- 1 | # This file is generated by gyp; do not edit. 2 | 3 | TOOLSET := target 4 | TARGET := addon 5 | DEFS_Debug := \ 6 | '-DNODE_GYP_MODULE_NAME=addon' \ 7 | '-D_DARWIN_USE_64_BIT_INODE=1' \ 8 | '-D_LARGEFILE_SOURCE' \ 9 | '-D_FILE_OFFSET_BITS=64' \ 10 | '-DBUILDING_NODE_EXTENSION' \ 11 | '-DDEBUG' \ 12 | '-D_DEBUG' 13 | 14 | # Flags passed to all source files. 15 | CFLAGS_Debug := \ 16 | -O0 \ 17 | -gdwarf-2 \ 18 | -mmacosx-version-min=10.7 \ 19 | -arch x86_64 \ 20 | -Wall \ 21 | -Wendif-labels \ 22 | -W \ 23 | -Wno-unused-parameter 24 | 25 | # Flags passed to only C files. 26 | CFLAGS_C_Debug := \ 27 | -fno-strict-aliasing 28 | 29 | # Flags passed to only C++ files. 30 | CFLAGS_CC_Debug := \ 31 | -std=gnu++0x \ 32 | -fno-rtti \ 33 | -fno-exceptions \ 34 | -fno-threadsafe-statics \ 35 | -fno-strict-aliasing 36 | 37 | # Flags passed to only ObjC files. 38 | CFLAGS_OBJC_Debug := 39 | 40 | # Flags passed to only ObjC++ files. 41 | CFLAGS_OBJCC_Debug := 42 | 43 | INCS_Debug := \ 44 | -I/Users/azat/.node-gyp/6.2.0/include/node \ 45 | -I/Users/azat/.node-gyp/6.2.0/src \ 46 | -I/Users/azat/.node-gyp/6.2.0/deps/uv/include \ 47 | -I/Users/azat/.node-gyp/6.2.0/deps/v8/include 48 | 49 | DEFS_Release := \ 50 | '-DNODE_GYP_MODULE_NAME=addon' \ 51 | '-D_DARWIN_USE_64_BIT_INODE=1' \ 52 | '-D_LARGEFILE_SOURCE' \ 53 | '-D_FILE_OFFSET_BITS=64' \ 54 | '-DBUILDING_NODE_EXTENSION' 55 | 56 | # Flags passed to all source files. 57 | CFLAGS_Release := \ 58 | -Os \ 59 | -gdwarf-2 \ 60 | -mmacosx-version-min=10.7 \ 61 | -arch x86_64 \ 62 | -Wall \ 63 | -Wendif-labels \ 64 | -W \ 65 | -Wno-unused-parameter 66 | 67 | # Flags passed to only C files. 68 | CFLAGS_C_Release := \ 69 | -fno-strict-aliasing 70 | 71 | # Flags passed to only C++ files. 72 | CFLAGS_CC_Release := \ 73 | -std=gnu++0x \ 74 | -fno-rtti \ 75 | -fno-exceptions \ 76 | -fno-threadsafe-statics \ 77 | -fno-strict-aliasing 78 | 79 | # Flags passed to only ObjC files. 80 | CFLAGS_OBJC_Release := 81 | 82 | # Flags passed to only ObjC++ files. 83 | CFLAGS_OBJCC_Release := 84 | 85 | INCS_Release := \ 86 | -I/Users/azat/.node-gyp/6.2.0/include/node \ 87 | -I/Users/azat/.node-gyp/6.2.0/src \ 88 | -I/Users/azat/.node-gyp/6.2.0/deps/uv/include \ 89 | -I/Users/azat/.node-gyp/6.2.0/deps/v8/include 90 | 91 | OBJS := \ 92 | $(obj).target/$(TARGET)/hello.o 93 | 94 | # Add to the list of files we specially track dependencies for. 95 | all_deps += $(OBJS) 96 | 97 | # CFLAGS et al overrides must be target-local. 98 | # See "Target-specific Variable Values" in the GNU Make manual. 99 | $(OBJS): TOOLSET := $(TOOLSET) 100 | $(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) 101 | $(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) 102 | $(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE)) 103 | $(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE)) 104 | 105 | # Suffix rules, putting all outputs into $(obj). 106 | 107 | $(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD 108 | @$(call do_cmd,cxx,1) 109 | 110 | # Try building from generated source, too. 111 | 112 | $(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD 113 | @$(call do_cmd,cxx,1) 114 | 115 | $(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD 116 | @$(call do_cmd,cxx,1) 117 | 118 | # End of this set of suffix rules 119 | ### Rules for final target. 120 | LDFLAGS_Debug := \ 121 | -undefined dynamic_lookup \ 122 | -Wl,-no_pie \ 123 | -Wl,-search_paths_first \ 124 | -mmacosx-version-min=10.7 \ 125 | -arch x86_64 \ 126 | -L$(builddir) 127 | 128 | LIBTOOLFLAGS_Debug := \ 129 | -undefined dynamic_lookup \ 130 | -Wl,-no_pie \ 131 | -Wl,-search_paths_first 132 | 133 | LDFLAGS_Release := \ 134 | -undefined dynamic_lookup \ 135 | -Wl,-no_pie \ 136 | -Wl,-search_paths_first \ 137 | -mmacosx-version-min=10.7 \ 138 | -arch x86_64 \ 139 | -L$(builddir) 140 | 141 | LIBTOOLFLAGS_Release := \ 142 | -undefined dynamic_lookup \ 143 | -Wl,-no_pie \ 144 | -Wl,-search_paths_first 145 | 146 | LIBS := 147 | 148 | $(builddir)/addon.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE)) 149 | $(builddir)/addon.node: LIBS := $(LIBS) 150 | $(builddir)/addon.node: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE)) 151 | $(builddir)/addon.node: TOOLSET := $(TOOLSET) 152 | $(builddir)/addon.node: $(OBJS) FORCE_DO_CMD 153 | $(call do_cmd,solink_module) 154 | 155 | all_deps += $(builddir)/addon.node 156 | # Add target alias 157 | .PHONY: addon 158 | addon: $(builddir)/addon.node 159 | 160 | # Short alias for building this executable. 161 | .PHONY: addon.node 162 | addon.node: $(builddir)/addon.node 163 | 164 | # Add executable to "all" target. 165 | .PHONY: all 166 | all: $(builddir)/addon.node 167 | 168 | -------------------------------------------------------------------------------- /code/build/binding.Makefile: -------------------------------------------------------------------------------- 1 | # This file is generated by gyp; do not edit. 2 | 3 | export builddir_name ?= ./build/. 4 | .PHONY: all 5 | all: 6 | $(MAKE) addon 7 | -------------------------------------------------------------------------------- /code/build/config.gypi: -------------------------------------------------------------------------------- 1 | # Do not edit. File was generated by node-gyp's "configure" step 2 | { 3 | "target_defaults": { 4 | "cflags": [], 5 | "default_configuration": "Release", 6 | "defines": [], 7 | "include_dirs": [], 8 | "libraries": [] 9 | }, 10 | "variables": { 11 | "asan": 0, 12 | "host_arch": "x64", 13 | "icu_data_file": "icudt57l.dat", 14 | "icu_data_in": "../../deps/icu-small/source/data/in/icudt57l.dat", 15 | "icu_endianness": "l", 16 | "icu_gyp_path": "tools/icu/icu-generic.gyp", 17 | "icu_locales": "en,root", 18 | "icu_path": "deps/icu-small", 19 | "icu_small": "true", 20 | "icu_ver_major": "57", 21 | "llvm_version": 0, 22 | "node_byteorder": "little", 23 | "node_enable_v8_vtunejit": "false", 24 | "node_install_npm": "true", 25 | "node_no_browser_globals": "false", 26 | "node_prefix": "/", 27 | "node_release_urlbase": "https://nodejs.org/download/release/", 28 | "node_shared_cares": "false", 29 | "node_shared_http_parser": "false", 30 | "node_shared_libuv": "false", 31 | "node_shared_openssl": "false", 32 | "node_shared_zlib": "false", 33 | "node_tag": "", 34 | "node_use_dtrace": "true", 35 | "node_use_etw": "false", 36 | "node_use_lttng": "false", 37 | "node_use_openssl": "true", 38 | "node_use_perfctr": "false", 39 | "openssl_fips": "", 40 | "openssl_no_asm": 0, 41 | "target_arch": "x64", 42 | "uv_parent_path": "/deps/uv/", 43 | "uv_use_dtrace": "true", 44 | "v8_enable_gdbjit": 0, 45 | "v8_enable_i18n_support": 1, 46 | "v8_no_strict_aliasing": 1, 47 | "v8_optimized_debug": 0, 48 | "v8_random_seed": 0, 49 | "v8_use_snapshot": "true", 50 | "want_separate_host_toolset": 0, 51 | "xcode_version": "7.0", 52 | "nodedir": "/Users/azat/.node-gyp/6.2.0", 53 | "copy_dev_lib": "true", 54 | "standalone_static_library": 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /code/build/gyp-mac-tool: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Generated by gyp. Do not edit. 3 | # Copyright (c) 2012 Google Inc. All rights reserved. 4 | # Use of this source code is governed by a BSD-style license that can be 5 | # found in the LICENSE file. 6 | 7 | """Utility functions to perform Xcode-style build steps. 8 | 9 | These functions are executed via gyp-mac-tool when using the Makefile generator. 10 | """ 11 | 12 | import fcntl 13 | import fnmatch 14 | import glob 15 | import json 16 | import os 17 | import plistlib 18 | import re 19 | import shutil 20 | import string 21 | import subprocess 22 | import sys 23 | import tempfile 24 | 25 | 26 | def main(args): 27 | executor = MacTool() 28 | exit_code = executor.Dispatch(args) 29 | if exit_code is not None: 30 | sys.exit(exit_code) 31 | 32 | 33 | class MacTool(object): 34 | """This class performs all the Mac tooling steps. The methods can either be 35 | executed directly, or dispatched from an argument list.""" 36 | 37 | def Dispatch(self, args): 38 | """Dispatches a string command to a method.""" 39 | if len(args) < 1: 40 | raise Exception("Not enough arguments") 41 | 42 | method = "Exec%s" % self._CommandifyName(args[0]) 43 | return getattr(self, method)(*args[1:]) 44 | 45 | def _CommandifyName(self, name_string): 46 | """Transforms a tool name like copy-info-plist to CopyInfoPlist""" 47 | return name_string.title().replace('-', '') 48 | 49 | def ExecCopyBundleResource(self, source, dest, convert_to_binary): 50 | """Copies a resource file to the bundle/Resources directory, performing any 51 | necessary compilation on each resource.""" 52 | extension = os.path.splitext(source)[1].lower() 53 | if os.path.isdir(source): 54 | # Copy tree. 55 | # TODO(thakis): This copies file attributes like mtime, while the 56 | # single-file branch below doesn't. This should probably be changed to 57 | # be consistent with the single-file branch. 58 | if os.path.exists(dest): 59 | shutil.rmtree(dest) 60 | shutil.copytree(source, dest) 61 | elif extension == '.xib': 62 | return self._CopyXIBFile(source, dest) 63 | elif extension == '.storyboard': 64 | return self._CopyXIBFile(source, dest) 65 | elif extension == '.strings': 66 | self._CopyStringsFile(source, dest, convert_to_binary) 67 | else: 68 | shutil.copy(source, dest) 69 | 70 | def _CopyXIBFile(self, source, dest): 71 | """Compiles a XIB file with ibtool into a binary plist in the bundle.""" 72 | 73 | # ibtool sometimes crashes with relative paths. See crbug.com/314728. 74 | base = os.path.dirname(os.path.realpath(__file__)) 75 | if os.path.relpath(source): 76 | source = os.path.join(base, source) 77 | if os.path.relpath(dest): 78 | dest = os.path.join(base, dest) 79 | 80 | args = ['xcrun', 'ibtool', '--errors', '--warnings', '--notices', 81 | '--output-format', 'human-readable-text', '--compile', dest, source] 82 | ibtool_section_re = re.compile(r'/\*.*\*/') 83 | ibtool_re = re.compile(r'.*note:.*is clipping its content') 84 | ibtoolout = subprocess.Popen(args, stdout=subprocess.PIPE) 85 | current_section_header = None 86 | for line in ibtoolout.stdout: 87 | if ibtool_section_re.match(line): 88 | current_section_header = line 89 | elif not ibtool_re.match(line): 90 | if current_section_header: 91 | sys.stdout.write(current_section_header) 92 | current_section_header = None 93 | sys.stdout.write(line) 94 | return ibtoolout.returncode 95 | 96 | def _ConvertToBinary(self, dest): 97 | subprocess.check_call([ 98 | 'xcrun', 'plutil', '-convert', 'binary1', '-o', dest, dest]) 99 | 100 | def _CopyStringsFile(self, source, dest, convert_to_binary): 101 | """Copies a .strings file using iconv to reconvert the input into UTF-16.""" 102 | input_code = self._DetectInputEncoding(source) or "UTF-8" 103 | 104 | # Xcode's CpyCopyStringsFile / builtin-copyStrings seems to call 105 | # CFPropertyListCreateFromXMLData() behind the scenes; at least it prints 106 | # CFPropertyListCreateFromXMLData(): Old-style plist parser: missing 107 | # semicolon in dictionary. 108 | # on invalid files. Do the same kind of validation. 109 | import CoreFoundation 110 | s = open(source, 'rb').read() 111 | d = CoreFoundation.CFDataCreate(None, s, len(s)) 112 | _, error = CoreFoundation.CFPropertyListCreateFromXMLData(None, d, 0, None) 113 | if error: 114 | return 115 | 116 | fp = open(dest, 'wb') 117 | fp.write(s.decode(input_code).encode('UTF-16')) 118 | fp.close() 119 | 120 | if convert_to_binary == 'True': 121 | self._ConvertToBinary(dest) 122 | 123 | def _DetectInputEncoding(self, file_name): 124 | """Reads the first few bytes from file_name and tries to guess the text 125 | encoding. Returns None as a guess if it can't detect it.""" 126 | fp = open(file_name, 'rb') 127 | try: 128 | header = fp.read(3) 129 | except e: 130 | fp.close() 131 | return None 132 | fp.close() 133 | if header.startswith("\xFE\xFF"): 134 | return "UTF-16" 135 | elif header.startswith("\xFF\xFE"): 136 | return "UTF-16" 137 | elif header.startswith("\xEF\xBB\xBF"): 138 | return "UTF-8" 139 | else: 140 | return None 141 | 142 | def ExecCopyInfoPlist(self, source, dest, convert_to_binary, *keys): 143 | """Copies the |source| Info.plist to the destination directory |dest|.""" 144 | # Read the source Info.plist into memory. 145 | fd = open(source, 'r') 146 | lines = fd.read() 147 | fd.close() 148 | 149 | # Insert synthesized key/value pairs (e.g. BuildMachineOSBuild). 150 | plist = plistlib.readPlistFromString(lines) 151 | if keys: 152 | plist = dict(plist.items() + json.loads(keys[0]).items()) 153 | lines = plistlib.writePlistToString(plist) 154 | 155 | # Go through all the environment variables and replace them as variables in 156 | # the file. 157 | IDENT_RE = re.compile(r'[/\s]') 158 | for key in os.environ: 159 | if key.startswith('_'): 160 | continue 161 | evar = '${%s}' % key 162 | evalue = os.environ[key] 163 | lines = string.replace(lines, evar, evalue) 164 | 165 | # Xcode supports various suffices on environment variables, which are 166 | # all undocumented. :rfc1034identifier is used in the standard project 167 | # template these days, and :identifier was used earlier. They are used to 168 | # convert non-url characters into things that look like valid urls -- 169 | # except that the replacement character for :identifier, '_' isn't valid 170 | # in a URL either -- oops, hence :rfc1034identifier was born. 171 | evar = '${%s:identifier}' % key 172 | evalue = IDENT_RE.sub('_', os.environ[key]) 173 | lines = string.replace(lines, evar, evalue) 174 | 175 | evar = '${%s:rfc1034identifier}' % key 176 | evalue = IDENT_RE.sub('-', os.environ[key]) 177 | lines = string.replace(lines, evar, evalue) 178 | 179 | # Remove any keys with values that haven't been replaced. 180 | lines = lines.split('\n') 181 | for i in range(len(lines)): 182 | if lines[i].strip().startswith("${"): 183 | lines[i] = None 184 | lines[i - 1] = None 185 | lines = '\n'.join(filter(lambda x: x is not None, lines)) 186 | 187 | # Write out the file with variables replaced. 188 | fd = open(dest, 'w') 189 | fd.write(lines) 190 | fd.close() 191 | 192 | # Now write out PkgInfo file now that the Info.plist file has been 193 | # "compiled". 194 | self._WritePkgInfo(dest) 195 | 196 | if convert_to_binary == 'True': 197 | self._ConvertToBinary(dest) 198 | 199 | def _WritePkgInfo(self, info_plist): 200 | """This writes the PkgInfo file from the data stored in Info.plist.""" 201 | plist = plistlib.readPlist(info_plist) 202 | if not plist: 203 | return 204 | 205 | # Only create PkgInfo for executable types. 206 | package_type = plist['CFBundlePackageType'] 207 | if package_type != 'APPL': 208 | return 209 | 210 | # The format of PkgInfo is eight characters, representing the bundle type 211 | # and bundle signature, each four characters. If that is missing, four 212 | # '?' characters are used instead. 213 | signature_code = plist.get('CFBundleSignature', '????') 214 | if len(signature_code) != 4: # Wrong length resets everything, too. 215 | signature_code = '?' * 4 216 | 217 | dest = os.path.join(os.path.dirname(info_plist), 'PkgInfo') 218 | fp = open(dest, 'w') 219 | fp.write('%s%s' % (package_type, signature_code)) 220 | fp.close() 221 | 222 | def ExecFlock(self, lockfile, *cmd_list): 223 | """Emulates the most basic behavior of Linux's flock(1).""" 224 | # Rely on exception handling to report errors. 225 | fd = os.open(lockfile, os.O_RDONLY|os.O_NOCTTY|os.O_CREAT, 0o666) 226 | fcntl.flock(fd, fcntl.LOCK_EX) 227 | return subprocess.call(cmd_list) 228 | 229 | def ExecFilterLibtool(self, *cmd_list): 230 | """Calls libtool and filters out '/path/to/libtool: file: foo.o has no 231 | symbols'.""" 232 | libtool_re = re.compile(r'^.*libtool: file: .* has no symbols$') 233 | libtool_re5 = re.compile( 234 | r'^.*libtool: warning for library: ' + 235 | r'.* the table of contents is empty ' + 236 | r'\(no object file members in the library define global symbols\)$') 237 | env = os.environ.copy() 238 | # Ref: 239 | # http://www.opensource.apple.com/source/cctools/cctools-809/misc/libtool.c 240 | # The problem with this flag is that it resets the file mtime on the file to 241 | # epoch=0, e.g. 1970-1-1 or 1969-12-31 depending on timezone. 242 | env['ZERO_AR_DATE'] = '1' 243 | libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env) 244 | _, err = libtoolout.communicate() 245 | for line in err.splitlines(): 246 | if not libtool_re.match(line) and not libtool_re5.match(line): 247 | print >>sys.stderr, line 248 | # Unconditionally touch the output .a file on the command line if present 249 | # and the command succeeded. A bit hacky. 250 | if not libtoolout.returncode: 251 | for i in range(len(cmd_list) - 1): 252 | if cmd_list[i] == "-o" and cmd_list[i+1].endswith('.a'): 253 | os.utime(cmd_list[i+1], None) 254 | break 255 | return libtoolout.returncode 256 | 257 | def ExecPackageFramework(self, framework, version): 258 | """Takes a path to Something.framework and the Current version of that and 259 | sets up all the symlinks.""" 260 | # Find the name of the binary based on the part before the ".framework". 261 | binary = os.path.basename(framework).split('.')[0] 262 | 263 | CURRENT = 'Current' 264 | RESOURCES = 'Resources' 265 | VERSIONS = 'Versions' 266 | 267 | if not os.path.exists(os.path.join(framework, VERSIONS, version, binary)): 268 | # Binary-less frameworks don't seem to contain symlinks (see e.g. 269 | # chromium's out/Debug/org.chromium.Chromium.manifest/ bundle). 270 | return 271 | 272 | # Move into the framework directory to set the symlinks correctly. 273 | pwd = os.getcwd() 274 | os.chdir(framework) 275 | 276 | # Set up the Current version. 277 | self._Relink(version, os.path.join(VERSIONS, CURRENT)) 278 | 279 | # Set up the root symlinks. 280 | self._Relink(os.path.join(VERSIONS, CURRENT, binary), binary) 281 | self._Relink(os.path.join(VERSIONS, CURRENT, RESOURCES), RESOURCES) 282 | 283 | # Back to where we were before! 284 | os.chdir(pwd) 285 | 286 | def _Relink(self, dest, link): 287 | """Creates a symlink to |dest| named |link|. If |link| already exists, 288 | it is overwritten.""" 289 | if os.path.lexists(link): 290 | os.remove(link) 291 | os.symlink(dest, link) 292 | 293 | def ExecCompileXcassets(self, keys, *inputs): 294 | """Compiles multiple .xcassets files into a single .car file. 295 | 296 | This invokes 'actool' to compile all the inputs .xcassets files. The 297 | |keys| arguments is a json-encoded dictionary of extra arguments to 298 | pass to 'actool' when the asset catalogs contains an application icon 299 | or a launch image. 300 | 301 | Note that 'actool' does not create the Assets.car file if the asset 302 | catalogs does not contains imageset. 303 | """ 304 | command_line = [ 305 | 'xcrun', 'actool', '--output-format', 'human-readable-text', 306 | '--compress-pngs', '--notices', '--warnings', '--errors', 307 | ] 308 | is_iphone_target = 'IPHONEOS_DEPLOYMENT_TARGET' in os.environ 309 | if is_iphone_target: 310 | platform = os.environ['CONFIGURATION'].split('-')[-1] 311 | if platform not in ('iphoneos', 'iphonesimulator'): 312 | platform = 'iphonesimulator' 313 | command_line.extend([ 314 | '--platform', platform, '--target-device', 'iphone', 315 | '--target-device', 'ipad', '--minimum-deployment-target', 316 | os.environ['IPHONEOS_DEPLOYMENT_TARGET'], '--compile', 317 | os.path.abspath(os.environ['CONTENTS_FOLDER_PATH']), 318 | ]) 319 | else: 320 | command_line.extend([ 321 | '--platform', 'macosx', '--target-device', 'mac', 322 | '--minimum-deployment-target', os.environ['MACOSX_DEPLOYMENT_TARGET'], 323 | '--compile', 324 | os.path.abspath(os.environ['UNLOCALIZED_RESOURCES_FOLDER_PATH']), 325 | ]) 326 | if keys: 327 | keys = json.loads(keys) 328 | for key, value in keys.iteritems(): 329 | arg_name = '--' + key 330 | if isinstance(value, bool): 331 | if value: 332 | command_line.append(arg_name) 333 | elif isinstance(value, list): 334 | for v in value: 335 | command_line.append(arg_name) 336 | command_line.append(str(v)) 337 | else: 338 | command_line.append(arg_name) 339 | command_line.append(str(value)) 340 | # Note: actool crashes if inputs path are relative, so use os.path.abspath 341 | # to get absolute path name for inputs. 342 | command_line.extend(map(os.path.abspath, inputs)) 343 | subprocess.check_call(command_line) 344 | 345 | def ExecMergeInfoPlist(self, output, *inputs): 346 | """Merge multiple .plist files into a single .plist file.""" 347 | merged_plist = {} 348 | for path in inputs: 349 | plist = self._LoadPlistMaybeBinary(path) 350 | self._MergePlist(merged_plist, plist) 351 | plistlib.writePlist(merged_plist, output) 352 | 353 | def ExecCodeSignBundle(self, key, resource_rules, entitlements, provisioning): 354 | """Code sign a bundle. 355 | 356 | This function tries to code sign an iOS bundle, following the same 357 | algorithm as Xcode: 358 | 1. copy ResourceRules.plist from the user or the SDK into the bundle, 359 | 2. pick the provisioning profile that best match the bundle identifier, 360 | and copy it into the bundle as embedded.mobileprovision, 361 | 3. copy Entitlements.plist from user or SDK next to the bundle, 362 | 4. code sign the bundle. 363 | """ 364 | resource_rules_path = self._InstallResourceRules(resource_rules) 365 | substitutions, overrides = self._InstallProvisioningProfile( 366 | provisioning, self._GetCFBundleIdentifier()) 367 | entitlements_path = self._InstallEntitlements( 368 | entitlements, substitutions, overrides) 369 | subprocess.check_call([ 370 | 'codesign', '--force', '--sign', key, '--resource-rules', 371 | resource_rules_path, '--entitlements', entitlements_path, 372 | os.path.join( 373 | os.environ['TARGET_BUILD_DIR'], 374 | os.environ['FULL_PRODUCT_NAME'])]) 375 | 376 | def _InstallResourceRules(self, resource_rules): 377 | """Installs ResourceRules.plist from user or SDK into the bundle. 378 | 379 | Args: 380 | resource_rules: string, optional, path to the ResourceRules.plist file 381 | to use, default to "${SDKROOT}/ResourceRules.plist" 382 | 383 | Returns: 384 | Path to the copy of ResourceRules.plist into the bundle. 385 | """ 386 | source_path = resource_rules 387 | target_path = os.path.join( 388 | os.environ['BUILT_PRODUCTS_DIR'], 389 | os.environ['CONTENTS_FOLDER_PATH'], 390 | 'ResourceRules.plist') 391 | if not source_path: 392 | source_path = os.path.join( 393 | os.environ['SDKROOT'], 'ResourceRules.plist') 394 | shutil.copy2(source_path, target_path) 395 | return target_path 396 | 397 | def _InstallProvisioningProfile(self, profile, bundle_identifier): 398 | """Installs embedded.mobileprovision into the bundle. 399 | 400 | Args: 401 | profile: string, optional, short name of the .mobileprovision file 402 | to use, if empty or the file is missing, the best file installed 403 | will be used 404 | bundle_identifier: string, value of CFBundleIdentifier from Info.plist 405 | 406 | Returns: 407 | A tuple containing two dictionary: variables substitutions and values 408 | to overrides when generating the entitlements file. 409 | """ 410 | source_path, provisioning_data, team_id = self._FindProvisioningProfile( 411 | profile, bundle_identifier) 412 | target_path = os.path.join( 413 | os.environ['BUILT_PRODUCTS_DIR'], 414 | os.environ['CONTENTS_FOLDER_PATH'], 415 | 'embedded.mobileprovision') 416 | shutil.copy2(source_path, target_path) 417 | substitutions = self._GetSubstitutions(bundle_identifier, team_id + '.') 418 | return substitutions, provisioning_data['Entitlements'] 419 | 420 | def _FindProvisioningProfile(self, profile, bundle_identifier): 421 | """Finds the .mobileprovision file to use for signing the bundle. 422 | 423 | Checks all the installed provisioning profiles (or if the user specified 424 | the PROVISIONING_PROFILE variable, only consult it) and select the most 425 | specific that correspond to the bundle identifier. 426 | 427 | Args: 428 | profile: string, optional, short name of the .mobileprovision file 429 | to use, if empty or the file is missing, the best file installed 430 | will be used 431 | bundle_identifier: string, value of CFBundleIdentifier from Info.plist 432 | 433 | Returns: 434 | A tuple of the path to the selected provisioning profile, the data of 435 | the embedded plist in the provisioning profile and the team identifier 436 | to use for code signing. 437 | 438 | Raises: 439 | SystemExit: if no .mobileprovision can be used to sign the bundle. 440 | """ 441 | profiles_dir = os.path.join( 442 | os.environ['HOME'], 'Library', 'MobileDevice', 'Provisioning Profiles') 443 | if not os.path.isdir(profiles_dir): 444 | print >>sys.stderr, ( 445 | 'cannot find mobile provisioning for %s' % bundle_identifier) 446 | sys.exit(1) 447 | provisioning_profiles = None 448 | if profile: 449 | profile_path = os.path.join(profiles_dir, profile + '.mobileprovision') 450 | if os.path.exists(profile_path): 451 | provisioning_profiles = [profile_path] 452 | if not provisioning_profiles: 453 | provisioning_profiles = glob.glob( 454 | os.path.join(profiles_dir, '*.mobileprovision')) 455 | valid_provisioning_profiles = {} 456 | for profile_path in provisioning_profiles: 457 | profile_data = self._LoadProvisioningProfile(profile_path) 458 | app_id_pattern = profile_data.get( 459 | 'Entitlements', {}).get('application-identifier', '') 460 | for team_identifier in profile_data.get('TeamIdentifier', []): 461 | app_id = '%s.%s' % (team_identifier, bundle_identifier) 462 | if fnmatch.fnmatch(app_id, app_id_pattern): 463 | valid_provisioning_profiles[app_id_pattern] = ( 464 | profile_path, profile_data, team_identifier) 465 | if not valid_provisioning_profiles: 466 | print >>sys.stderr, ( 467 | 'cannot find mobile provisioning for %s' % bundle_identifier) 468 | sys.exit(1) 469 | # If the user has multiple provisioning profiles installed that can be 470 | # used for ${bundle_identifier}, pick the most specific one (ie. the 471 | # provisioning profile whose pattern is the longest). 472 | selected_key = max(valid_provisioning_profiles, key=lambda v: len(v)) 473 | return valid_provisioning_profiles[selected_key] 474 | 475 | def _LoadProvisioningProfile(self, profile_path): 476 | """Extracts the plist embedded in a provisioning profile. 477 | 478 | Args: 479 | profile_path: string, path to the .mobileprovision file 480 | 481 | Returns: 482 | Content of the plist embedded in the provisioning profile as a dictionary. 483 | """ 484 | with tempfile.NamedTemporaryFile() as temp: 485 | subprocess.check_call([ 486 | 'security', 'cms', '-D', '-i', profile_path, '-o', temp.name]) 487 | return self._LoadPlistMaybeBinary(temp.name) 488 | 489 | def _MergePlist(self, merged_plist, plist): 490 | """Merge |plist| into |merged_plist|.""" 491 | for key, value in plist.iteritems(): 492 | if isinstance(value, dict): 493 | merged_value = merged_plist.get(key, {}) 494 | if isinstance(merged_value, dict): 495 | self._MergePlist(merged_value, value) 496 | merged_plist[key] = merged_value 497 | else: 498 | merged_plist[key] = value 499 | else: 500 | merged_plist[key] = value 501 | 502 | def _LoadPlistMaybeBinary(self, plist_path): 503 | """Loads into a memory a plist possibly encoded in binary format. 504 | 505 | This is a wrapper around plistlib.readPlist that tries to convert the 506 | plist to the XML format if it can't be parsed (assuming that it is in 507 | the binary format). 508 | 509 | Args: 510 | plist_path: string, path to a plist file, in XML or binary format 511 | 512 | Returns: 513 | Content of the plist as a dictionary. 514 | """ 515 | try: 516 | # First, try to read the file using plistlib that only supports XML, 517 | # and if an exception is raised, convert a temporary copy to XML and 518 | # load that copy. 519 | return plistlib.readPlist(plist_path) 520 | except: 521 | pass 522 | with tempfile.NamedTemporaryFile() as temp: 523 | shutil.copy2(plist_path, temp.name) 524 | subprocess.check_call(['plutil', '-convert', 'xml1', temp.name]) 525 | return plistlib.readPlist(temp.name) 526 | 527 | def _GetSubstitutions(self, bundle_identifier, app_identifier_prefix): 528 | """Constructs a dictionary of variable substitutions for Entitlements.plist. 529 | 530 | Args: 531 | bundle_identifier: string, value of CFBundleIdentifier from Info.plist 532 | app_identifier_prefix: string, value for AppIdentifierPrefix 533 | 534 | Returns: 535 | Dictionary of substitutions to apply when generating Entitlements.plist. 536 | """ 537 | return { 538 | 'CFBundleIdentifier': bundle_identifier, 539 | 'AppIdentifierPrefix': app_identifier_prefix, 540 | } 541 | 542 | def _GetCFBundleIdentifier(self): 543 | """Extracts CFBundleIdentifier value from Info.plist in the bundle. 544 | 545 | Returns: 546 | Value of CFBundleIdentifier in the Info.plist located in the bundle. 547 | """ 548 | info_plist_path = os.path.join( 549 | os.environ['TARGET_BUILD_DIR'], 550 | os.environ['INFOPLIST_PATH']) 551 | info_plist_data = self._LoadPlistMaybeBinary(info_plist_path) 552 | return info_plist_data['CFBundleIdentifier'] 553 | 554 | def _InstallEntitlements(self, entitlements, substitutions, overrides): 555 | """Generates and install the ${BundleName}.xcent entitlements file. 556 | 557 | Expands variables "$(variable)" pattern in the source entitlements file, 558 | add extra entitlements defined in the .mobileprovision file and the copy 559 | the generated plist to "${BundlePath}.xcent". 560 | 561 | Args: 562 | entitlements: string, optional, path to the Entitlements.plist template 563 | to use, defaults to "${SDKROOT}/Entitlements.plist" 564 | substitutions: dictionary, variable substitutions 565 | overrides: dictionary, values to add to the entitlements 566 | 567 | Returns: 568 | Path to the generated entitlements file. 569 | """ 570 | source_path = entitlements 571 | target_path = os.path.join( 572 | os.environ['BUILT_PRODUCTS_DIR'], 573 | os.environ['PRODUCT_NAME'] + '.xcent') 574 | if not source_path: 575 | source_path = os.path.join( 576 | os.environ['SDKROOT'], 577 | 'Entitlements.plist') 578 | shutil.copy2(source_path, target_path) 579 | data = self._LoadPlistMaybeBinary(target_path) 580 | data = self._ExpandVariables(data, substitutions) 581 | if overrides: 582 | for key in overrides: 583 | if key not in data: 584 | data[key] = overrides[key] 585 | plistlib.writePlist(data, target_path) 586 | return target_path 587 | 588 | def _ExpandVariables(self, data, substitutions): 589 | """Expands variables "$(variable)" in data. 590 | 591 | Args: 592 | data: object, can be either string, list or dictionary 593 | substitutions: dictionary, variable substitutions to perform 594 | 595 | Returns: 596 | Copy of data where each references to "$(variable)" has been replaced 597 | by the corresponding value found in substitutions, or left intact if 598 | the key was not found. 599 | """ 600 | if isinstance(data, str): 601 | for key, value in substitutions.iteritems(): 602 | data = data.replace('$(%s)' % key, value) 603 | return data 604 | if isinstance(data, list): 605 | return [self._ExpandVariables(v, substitutions) for v in data] 606 | if isinstance(data, dict): 607 | return {k: self._ExpandVariables(data[k], substitutions) for k in data} 608 | return data 609 | 610 | if __name__ == '__main__': 611 | sys.exit(main(sys.argv[1:])) 612 | -------------------------------------------------------------------------------- /code/cluster.js: -------------------------------------------------------------------------------- 1 | var cluster = require('cluster') 2 | var http = require('http') 3 | var numCPUs = require('os').cpus().length 4 | var express = require('express') 5 | var stats = {} 6 | 7 | if (cluster.isMaster) { 8 | console.log (' Fork %s worker(s) from master', numCPUs) 9 | for (var i = 0; i < numCPUs; i++) { 10 | cluster.fork() 11 | } 12 | cluster.on('online', function(worker) { 13 | console.log ('worker is running on %s pid', worker.process.pid) 14 | }) 15 | cluster.on('exit', function(worker, code, signal) { 16 | console.log('worker with %s is closed', worker.process.pid) 17 | }) 18 | } else if (cluster.isWorker) { 19 | var port = 3000 20 | stats[cluster.worker.process.pid] = 0 21 | console.log('worker (%s) is now listening to http://localhost:%s', 22 | cluster.worker.process.pid, port) 23 | var app = express() 24 | app.get('*', function(req, res) { 25 | stats[cluster.worker.process.pid] += 1 26 | var l ='cluster ' 27 | + cluster.worker.process.pid 28 | + ' responded \n' 29 | console.log(l) 30 | res.status(200).send(l) 31 | }) 32 | app.listen(port) 33 | } 34 | process.on('SIGINT', function(){ 35 | console.log(stats) 36 | console.log('Execute "$ killall node" to terminate') 37 | process.exit(0) 38 | }) 39 | -------------------------------------------------------------------------------- /code/domain-async.js: -------------------------------------------------------------------------------- 1 | var d = require('domain').create(); 2 | 3 | d.on('error', function(e) { 4 | console.log('Custom Error: ' + e); 5 | }); 6 | d.run(function() { 7 | setTimeout(function () { 8 | throw new Error('Failed!'); 9 | }, Math.round(Math.random()*100)); 10 | }); 11 | -------------------------------------------------------------------------------- /code/hello.cc: -------------------------------------------------------------------------------- 1 | // hello.cc 2 | #include 3 | 4 | namespace demo { 5 | 6 | using v8::FunctionCallbackInfo; 7 | using v8::HandleScope; 8 | using v8::Isolate; 9 | using v8::Local; 10 | using v8::Object; 11 | using v8::String; 12 | using v8::Value; 13 | 14 | void Method(const FunctionCallbackInfo& args) { 15 | Isolate* isolate = args.GetIsolate(); 16 | args.GetReturnValue().Set(String::NewFromUtf8(isolate, "capital one")); 17 | } 18 | 19 | void init(Local exports) { 20 | NODE_SET_METHOD(exports, "hello", Method); 21 | } 22 | 23 | NODE_MODULE(addon, init) 24 | 25 | } // namespace demo 26 | -------------------------------------------------------------------------------- /code/hello.js: -------------------------------------------------------------------------------- 1 | // hello.js 2 | var addon = require('./build/Release/addon'); 3 | 4 | console.log(addon.hello()); // world 5 | -------------------------------------------------------------------------------- /code/job.js: -------------------------------------------------------------------------------- 1 | // job.js 2 | var util = require('util') 3 | var events = require('events') 4 | var Job = function Job() { 5 | var job = this 6 | // We'll use it later to pass 'this' to closures 7 | // ... 8 | job.process = function() { 9 | // ... 10 | setTimeout(function(){ 11 | // Emulate the delay of the job - async! 12 | job.emit('done', { completedOn: new Date() }) 13 | }, 700) 14 | } 15 | job.on('start', function(){ 16 | job.process() 17 | }) 18 | } 19 | 20 | util.inherits(Job, events.EventEmitter) 21 | module.exports = Job 22 | -------------------------------------------------------------------------------- /code/knock-knock.js: -------------------------------------------------------------------------------- 1 | var events = require('events') 2 | var emitter = new events.EventEmitter() 3 | 4 | emitter.on('knock', function() { 5 | console.log('Who\'s there?') 6 | }) 7 | 8 | emitter.on('knock', function() { 9 | console.log('Go away!') 10 | }) 11 | 12 | emitter.emit('knock') 13 | emitter.emit('knock') -------------------------------------------------------------------------------- /code/server-stream.js: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | var fs = require('fs') 3 | var path = require('path') 4 | var port = 3000 5 | var app = express() 6 | var responseTime = require('response-time') 7 | var largeImagePath = path.join(__dirname,'CapitalOne_Digital_Skills_Infographic_BW.jpg') 8 | 9 | app.use(responseTime()) 10 | 11 | app.get('/non-stream', function(req, res) { 12 | var file = fs.readFile(largeImagePath, function(error, data){ 13 | res.end(data) 14 | }) 15 | }) 16 | 17 | app.get('/non-stream2', function(req, res) { 18 | var file = fs.readFileSync(largeImagePath) 19 | res.end(file) 20 | }) 21 | 22 | app.get('/stream', function(req, res) { 23 | var stream = fs.createReadStream(largeImagePath) 24 | stream.pipe(res) 25 | }) 26 | 27 | 28 | app.get('/stream2', function(req, res) { 29 | var stream = fs.createReadStream(largeImagePath) 30 | stream.on('data', function(data) { 31 | res.write(data) 32 | }) 33 | stream.on('end', function() { 34 | res.end() 35 | }) 36 | }) 37 | 38 | app.listen(port) 39 | -------------------------------------------------------------------------------- /code/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var port = 3000; 3 | global.stats = {} 4 | console.log('worker (%s) is now listening to http://localhost:%s', 5 | process.pid, port); 6 | var app = express(); 7 | app.get('*', function(req, res) { 8 | if (!global.stats[process.pid]) global.stats[process.pid] = 1 9 | else global.stats[process.pid] += 1; 10 | var l ='cluser ' 11 | + process.pid 12 | + ' responded \n'; 13 | console.log(l, global.stats); 14 | res.status(200).send(l); 15 | }) 16 | app.listen(port); 17 | -------------------------------------------------------------------------------- /code/stdin.js: -------------------------------------------------------------------------------- 1 | process.stdin.resume() 2 | process.stdin.setEncoding('utf8') 3 | 4 | process.stdin.on('data', function (chunk) { 5 | console.log('chunk: ', chunk) 6 | }) 7 | 8 | process.stdin.on('end', function () { 9 | console.log('--- END ---') 10 | }) -------------------------------------------------------------------------------- /code/sync-error.js: -------------------------------------------------------------------------------- 1 | try { 2 | throw new Error('Fail!'); 3 | } catch (e) { 4 | console.log('Custom Error: ' + e.message); 5 | } 6 | -------------------------------------------------------------------------------- /code/weekly.js: -------------------------------------------------------------------------------- 1 | // weekly.js 2 | var Job = require('./job.js') 3 | var job = new Job() 4 | 5 | job.on('done', function(details){ 6 | console.log('Weekly email job was completed at', 7 | details.completedOn) 8 | // job.removeAllListeners() 9 | 10 | }) 11 | 12 | // job.process() 13 | job.emit('start') 14 | -------------------------------------------------------------------------------- /course.md: -------------------------------------------------------------------------------- 1 | footer: © NodeProgram.com, Node.University and Azat Mardan 2016 2 | slidenumbers: true 3 | 4 | # You Don't Know Node 5 | ## Quick Intro to 5 Core Features 6 | 7 | ![inline 100%](images/azat.jpeg) 8 | Azat Mardan @azat_co 9 | 10 | 11 | ![inline right](images/nu.png) 12 | 13 | --- 14 | 15 | # Who is the instructor? 16 | 17 | --- 18 | 19 | # About Instructor: Azat Mardan 20 | 21 | ![inline](images/azats-books-covers.png) 22 | 23 | ^Wrote and published 12 books not counting Korean, Chinese, Polish and Russian translations 24 | 25 | --- 26 | 27 | # About Instructor: Azat Mardan 28 | 29 | ![inline](images/azat.jpeg) 30 | 31 | * Work: Technology Fellow at Capital One (kind of a big deal) 32 | * Experience: FDIC, NIH, DocuSign, HackReactor and Storify 33 | * Books: React Quickly, Practical Node.js, Pro Express.js, Express.js API and 8 others 34 | * Teach: [NodeProgram.com](http://NodeProgram.com) 35 | 36 | --- 37 | 38 | # What to Expect from this Course 39 | 40 | --- 41 | 42 | 43 | # Topics 44 | 45 | 1. Event Emitters 46 | 1. Streams 47 | 1. Buffers 48 | 1. Clusters 49 | 1. C++ Addons 50 | 1. Domain 51 | 52 | --- 53 | 54 | # Why listen? 55 | 56 | --- 57 | 58 | * Better code 59 | * DRY 60 | * New approaches 61 | 62 | --- 63 | 64 | # You Don't Know Node 65 | ## Starting with basics: Why Use Node? 66 | 67 | ![inline 100%](images/azat.jpeg) 68 | Azat Mardan @azat_co 69 | 70 | ![inline right](images/nu.png) 71 | 72 | 73 | --- 74 | 75 | # Input/output is one of the most expensive type tasks (>CPU) 💰 76 | 77 | --- 78 | 79 | # Node has non-blocking I/O 80 | 81 | 82 | --- 83 | 84 | ![inline](images/non-blocking.png) 85 | 86 | ^This allows processing other tasks while IO calls are unfinished like this 87 | ^Nginx vs. Apache 88 | ^Blocking I/O is expensive! 89 | 90 | 91 | --- 92 | 93 | ### Java Sleep 94 | 95 | ```java 96 | System.out.println("Step: 1"); 97 | System.out.println("Step: 2"); 98 | Thread.sleep(1000); 99 | System.out.println("Step: 3"); 100 | ``` 101 | 102 | 103 | 104 | --- 105 | 106 | ## Node "Sleep" 107 | 108 | ```js 109 | console.log('Step: 1') 110 | setTimeout(function () { 111 | console.log('Step: 3') 112 | }, 1000) 113 | console.log('Step: 2') 114 | ``` 115 | 116 | --- 117 | 118 | ## Process Multiple Tasks 119 | 120 | ```js 121 | console.log('Step: 1') 122 | setTimeout(function () { 123 | console.log('Step: 3') 124 | // console.log('Step 5') 125 | }, 1000); 126 | console.log('Step: 2') 127 | // console.log('Step 4') 128 | ``` 129 | 130 | --- 131 | 132 | # Blocking Web Server 133 | 134 | --- 135 | 136 | ![inline](images/threading_java.png) 137 | 138 | --- 139 | 140 | ![inline](images/coffeeshop-blocking.jpg) 141 | 142 | --- 143 | 144 | # Non-Blocking Web Server 145 | 146 | --- 147 | 148 | ![inline](images/threading_node.png) 149 | 150 | ^This is in contrast to today's more common concurrency model where OS threads are employed. Thread-based networking is relatively inefficient and very difficult to use. Furthermore, users of Node are free from worries of dead-locking the process --- there are no locks 151 | 152 | --- 153 | 154 | ![inline](images/coffeeshop-non-blocking.jpg) 155 | 156 | --- 157 | 158 | # [Multi-threading] is the software equivalent of a nuclear device because if it is used incorrectly, it can blow up in your face. 159 | 160 | 161 | 162 | --- 163 | 164 | # Blocking systems have to be multi-threaded 165 | 166 | --- 167 | 168 | # Node is single threaded... and that's good! 😄 169 | 170 | 171 | --- 172 | 173 | 174 | ## It's still possible to write blocking code in Node.js. :flushed: 175 | 176 | 177 | --- 178 | 179 | # Blocking Node.js Code 180 | 181 | ```js 182 | // blocking.js 183 | console.log('Step: 1') 184 | for (var i = 1; i<1000000000; i++) { 185 | // This will take 100-1000ms 186 | } 187 | console.log('Step: 2') 188 | ``` 189 | 190 | --- 191 | 192 | # Blocking Node.js Code 193 | 194 | ```js 195 | var fs = require('fs') 196 | 197 | var contents = fs.readFileSync('accounts.txt','utf8') 198 | console.log(contents) 199 | console.log('Hello Ruby\n') 200 | 201 | var contents = fs.readFileSync('ips.txt','utf8') 202 | console.log(contents) 203 | console.log('Hello Node!') 204 | //accounts.txt->Hello Ruby->ips.txt->Hello Node! 205 | ``` 206 | 207 | --- 208 | 209 | # Non-Blocking Node.js Code 210 | 211 | ```js 212 | var fs = require('fs') 213 | 214 | fs.readFile('accounts.txt','utf8', function(error, contents){ 215 | console.log(contents) 216 | }) 217 | console.log('Hello Ruby\n') 218 | 219 | fs.readFile('ips.txt','utf8', function(error, contents){ 220 | console.log(contents) 221 | }) 222 | console.log('Hello Node!') 223 | //Hello Ruby->Hello Node->... accounts.txt->ips.txt or ips.txt->accounts.txt 224 | ``` 225 | 226 | --- 227 | 228 | # Node *typically* is much faster than other platforms 229 | 230 | --- 231 | 232 | # How many of you reach the performance limitations of apps built with blocking I/O systems? 233 | 234 | --- 235 | 236 | # Probably not many 237 | 238 | --- 239 | 240 | # My Fav Node Benefit 241 | 242 | --- 243 | 244 | # JavaScript everywhere. One language to rule 'em all! 245 | 246 | --- 247 | 248 | * Think faster 249 | * Reuse code 250 | * Learn quicker 251 | 252 | --- 253 | 254 | # Most of Node is JavaScript 255 | 256 | * Array 257 | * String 258 | * Primitives 259 | * Functions 260 | * Objects 261 | 262 | --- 263 | 264 | 265 | # Node !== Browser JavaScript 266 | 267 | --- 268 | 269 | # How to create global variables (no `window` in Node), work with modules, get path to my script? 270 | 271 | --- 272 | 273 | # `global` or `GLOBAL` 274 | 275 | --- 276 | 277 | # It has properties! 278 | 279 | --- 280 | 281 | 282 | # `global.__filename` 283 | # `global.__dirname` 284 | 285 | 286 | --- 287 | 288 | 289 | # `global.module` 290 | # `global.require()` 291 | 292 | --- 293 | 294 | # How do I...? 295 | 296 | * Access CLI input? 297 | * Get system info: OS, platform, memory usage, versions, etc.? 298 | * Read env vars (passwords!)? 299 | 300 | --- 301 | 302 | # `global.process` or `process` 303 | 304 | --- 305 | 306 | # `process.pid` 307 | # `process.versions` 308 | # `process.arch` 309 | 310 | --- 311 | 312 | # `process.argv` 313 | 314 | --- 315 | 316 | # `process.env` 317 | 318 | --- 319 | 320 | 321 | # `process.uptime()` 322 | # `process.memoryUsage()` 323 | 324 | --- 325 | 326 | # `process.cwd()` 327 | 328 | --- 329 | 330 | # `process.exit()` 331 | # `process.kill()` 332 | 333 | --- 334 | 335 | # You Don't Know Node 336 | ## Event Emitters 337 | 338 | ![inline 100%](images/azat.jpeg) 339 | Azat Mardan @azat_co 340 | 341 | ![inline right](images/nu.png) 342 | 343 | --- 344 | 345 | # Who likes and understands callbacks? 🙋 346 | 347 | --- 348 | 349 | 350 | 351 | ```js 352 | fs.readdir(source, function (err, files) { 353 | if (err) { 354 | console.log('Error finding files: ' + err) 355 | } else { 356 | files.forEach(function (filename, fileIndex) { 357 | console.log(filename) 358 | gm(source + filename).size(function (err, values) { 359 | if (err) { 360 | console.log('Error identifying file size: ' + err) 361 | } else { 362 | console.log(filename + ' : ' + values) 363 | aspect = (values.width / values.height) 364 | widths.forEach(function (width, widthIndex) { 365 | height = Math.round(width / aspect) 366 | console.log('resizing ' + filename + 'to ' + height + 'x' + height) 367 | this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) { 368 | if (err) console.log('Error writing file: ' + err) 369 | }) 370 | }.bind(this)) 371 | } 372 | }) 373 | }) 374 | } 375 | }) 376 | ``` 377 | 378 | --- 379 | 380 | 381 | ## Callbacks are not very developmental scalable 😞 382 | 383 | --- 384 | 385 | # Me When Working With Deeply Nested Callbacks 386 | 387 | ![inline](images/tree_slam.gif) 388 | 389 | --- 390 | 391 | # Events 392 | 393 | Events are part of core and supported by most of the core modules while more advanced patterns such as promises, generators, async/await are not. 394 | 395 | 396 | --- 397 | 398 | ### Events == Node Observer Pattern 399 | 400 | * Subject 401 | * Observers (event listeners) on a subject 402 | * Event triggers 403 | 404 | --- 405 | 406 | ### Events 407 | 408 | ```js 409 | var events = require('events') 410 | var emitter = new events.EventEmitter() 411 | ``` 412 | 413 | --- 414 | 415 | ### Events 416 | 417 | In node.js an event can be described simply as a string with a corresponding callback. 418 | 419 | 420 | ```js 421 | emitter.on('done', function(results) { 422 | console.log('Done: ', results) 423 | }) 424 | ``` 425 | 426 | 427 | --- 428 | 429 | ### Using Event Emitters 430 | 431 | ```js 432 | var events = require('events') 433 | var emitter = new events.EventEmitter() 434 | 435 | emitter.on('knock', function() { 436 | console.log('Who\'s there?') 437 | }) 438 | 439 | emitter.on('knock', function() { 440 | console.log('Go away!') 441 | }) 442 | 443 | emitter.emit('knock') 444 | ``` 445 | 446 | --- 447 | 448 | ### Inheriting from EventEmitter 449 | 450 | ```js 451 | // job.js 452 | var util = require('util') 453 | var Job = function Job() { 454 | // ... 455 | this.process = function() { 456 | // ... 457 | job.emit('done', { completedOn: new Date() }) 458 | } 459 | } 460 | 461 | util.inherits(Job, require('events').EventEmitter) 462 | module.exports = Job 463 | ``` 464 | 465 | --- 466 | 467 | ### Inheriting from EventEmitter 468 | 469 | ```js 470 | // weekly.js 471 | var Job = require('./job.js') 472 | var job = new Job() 473 | 474 | job.on('done', function(details){ 475 | console.log('Job was completed at', details.completedOn) 476 | job.removeAllListeners() 477 | }) 478 | 479 | job.process() 480 | ``` 481 | 482 | --- 483 | 484 | ### Listeners 485 | 486 | ```js 487 | emitter.listeners(eventName) 488 | emitter.on(eventName, listener) 489 | emitter.once(eventName, listener) 490 | emitter.removeListener(eventName, listener) 491 | ``` 492 | 493 | --- 494 | 495 | # Other Node Patterns 496 | 497 | Node Patterns: From Callbacks to Observer: 498 | 499 | or 500 | 501 | 502 | 503 | --- 504 | 505 | # You Don't Know Node 506 | ## Stream 507 | 508 | ![inline 100%](images/azat.jpeg) 509 | Azat Mardan, @azat_co 510 | 511 | 512 | ![inline right](images/nu.png) 513 | 514 | --- 515 | 516 | # Problems with Large Data 517 | 518 | * Speed: Too slow because has to load all 519 | * Buffer limit: ~1Gb 520 | * Overhyped (JK) 521 | 522 | --- 523 | 524 | # Streams 525 | 526 | ## Abstractions for continuous chunking of data 527 | 528 | --- 529 | 530 | # No need to wait for the entire resource to load 531 | 532 | --- 533 | 534 | # Types of Streams 535 | 536 | * Readable 537 | * Writable 538 | * Duplex 539 | * Transform 540 | 541 | --- 542 | 543 | ## Streams Inherit from Event Emitter 544 | 545 | --- 546 | 547 | # Streams are Everywhere! 548 | 549 | * HTTP requests and responses 550 | * Standard input/output (stdin&stdout) 551 | * File reads and writes 552 | 553 | --- 554 | 555 | # Readable Stream Example 556 | 557 | `process.stdin` 558 | 559 | Standard input streams contain data going into applications. 560 | 561 | --- 562 | 563 | # This is achieved via a read operation. 564 | 565 | --- 566 | 567 | # Input typically comes from the keyboard used to start the process. 568 | 569 | --- 570 | 571 | To listen in on data from stdin, use the `data` and `end` events: 572 | 573 | ```js 574 | // stdin.js 575 | process.stdin.resume() 576 | process.stdin.setEncoding('utf8') 577 | 578 | process.stdin.on('data', function (chunk) { 579 | console.log('chunk: ', chunk) 580 | }) 581 | 582 | process.stdin.on('end', function () { 583 | console.log('--- END ---') 584 | }) 585 | ``` 586 | 587 | --- 588 | 589 | # Demo 590 | 591 | `$ node stdin.js` 592 | 593 | --- 594 | 595 | # New Interface `read()` 596 | 597 | ```js 598 | var readable = getReadableStreamSomehow() 599 | readable.on('readable', () => { 600 | var chunk 601 | while (null !== (chunk = readable.read())) { 602 | console.log('got %d bytes of data', chunk.length) 603 | } 604 | }) 605 | ``` 606 | 607 | ^readable.read is sync but the chunks are small 608 | 609 | --- 610 | 611 | # Writable Stream Example 612 | 613 | `process.stdout` 614 | 615 | Standard output streams contain data going out of the applications. 616 | 617 | --- 618 | 619 | # This is done via a write operation. 620 | 621 | --- 622 | 623 | # Data written to standard output is visible on the command line. 624 | 625 | --- 626 | 627 | # Writable Stream 628 | 629 | To write to `stdout`, use the `write` function: 630 | 631 | ```js 632 | process.stdout.write('A simple message\n') 633 | ``` 634 | 635 | --- 636 | 637 | # What about HTTP? 638 | 639 | --- 640 | 641 | ```js 642 | const http = require('http') 643 | var server = http.createServer( (req, res) => { 644 | req.setEncoding('utf8') 645 | req.on('data', (chunk) => { 646 | let transformedChunk = transform(chunk) // This functions is defined somewhere else 647 | res.write(transformedChunk) 648 | }) 649 | req.on('end', () => { 650 | res.end() 651 | }) 652 | }) 653 | 654 | server.listen(1337) 655 | ``` 656 | 657 | --- 658 | 659 | # Pipe 660 | 661 | 662 | ```js 663 | var r = fs.createReadStream('file.txt') 664 | var z = zlib.createGzip() 665 | var w = fs.createWriteStream('file.txt.gz') 666 | r.pipe(z).pipe(w) 667 | ``` 668 | 669 | ^Readable.pipe takes writable and returns destination 670 | 671 | --- 672 | 673 | ## What data type to use for binary data? 674 | 675 | --- 676 | 677 | ### Buffers 678 | 679 | Binary data type, to create: 680 | 681 | * `Buffer.alloc(size)` 682 | * `Buffer.from(array)` 683 | * `Buffer.from(buffer)` 684 | * `Buffer.from(str[, encoding])` 685 | 686 | Docs: 687 | 688 | --- 689 | 690 | # Working with Buffer 691 | 692 | ```js 693 | // buf.js 694 | var buf = Buffer.alloc(26) 695 | for (var i = 0 ; i < 26 ; i++) { 696 | buf[i] = i + 97 // 97 is ASCII a 697 | } 698 | console.log(buf) // 699 | console.log(buf.toString('utf8')) // abcdefghijklmnopqrstuvwxyz 700 | ``` 701 | 702 | --- 703 | 704 | # Buffer Convertion 705 | 706 | ```js 707 | buf.toString('ascii') // outputs: abcdefghijklmnopqrstuvwxyz 708 | buf.toString('ascii', 0, 5) // outputs: abcde 709 | buf.toString('utf8', 0, 5) // outputs: abcde 710 | buf.toString(undefined, 0, 5) // encoding defaults to 'utf8', outputs abcde 711 | ``` 712 | 713 | --- 714 | 715 | ### Remember fs? 716 | 717 | ```js 718 | fs.readFile('/etc/passwd', function (err, data) { 719 | if (err) return console.error(err) 720 | console.log(data) 721 | }); 722 | ``` 723 | 724 | `data` is buffer! 725 | 726 | --- 727 | 728 | # Demo 729 | 730 | ``` 731 | $ node server-stream 732 | ``` 733 | 734 | --- 735 | 736 | # Streams and Buffer Demo 737 | 738 | ```js 739 | // server-stream.js 740 | app.get('/stream', function(req, res) { 741 | var stream = fs.createReadStream(largeImagePath) 742 | stream.pipe(res) 743 | }) 744 | ``` 745 | 746 | ``` 747 | $ node server-stream 748 | ``` 749 | 750 | 751 | 752 | 753 | --- 754 | 755 | # Results in DevTools 756 | 757 | `/stream` responds faster! 758 | 759 | ``` 760 | X-Response-Time 761 | ~300ms vs. 3-5s 762 | ``` 763 | 764 | --- 765 | 766 | # Stream Resources 767 | 768 | Stream automated workshop: 769 | 770 | ``` 771 | $ sudo npm install -g stream-adventure 772 | $ stream-adventure 773 | ``` 774 | 775 | 776 | 777 | 778 | --- 779 | 780 | # You Don't Know Node 781 | ## Cluster 782 | 783 | ![inline 100%](images/azat.jpeg) 784 | Azat Mardan @azat_co 785 | 786 | 787 | ![inline right](images/nu.png) 788 | 789 | 790 | 791 | # How to scale a single threaded system? 792 | 793 | --- 794 | 795 | # Cluster Usage 796 | 797 | * Master: starts workers 798 | * Worker: do the job, e.g., HTTP server 799 | 800 | Number of processes = number of CPUs 801 | 802 | --- 803 | 804 | # Clusters 805 | 806 | ```js 807 | var cluster = require('cluster') 808 | if (cluster.isMaster) { 809 | for (var i = 0; i < numCPUs; i++) { 810 | cluster.fork() 811 | } 812 | } else if (cluster.isWorker) { 813 | // your server code 814 | }) 815 | ``` 816 | 817 | --- 818 | 819 | # Cluster Demo 820 | 821 | 1. Run `code/cluster.js` with node (`$ node cluster.js`). 822 | 1. Install `loadtest` with npm: `$ npm install -g loadtest` 823 | 1. Run load testing with: `$ loadtest http://localhost:3000 -t 20 —c 10` 824 | 825 | Press control+c on the server terminal 826 | 827 | --- 828 | 829 | # Cluster Libraries 830 | 831 | * Core cluster: lean and mean 832 | * strong-cluster-control (https://github.com/strongloop/strong-cluster-control), or `$ slc run`: good choice 833 | * pm2 (https://github.com/Unitech/pm2): good choice 834 | 835 | --- 836 | 837 | ### pm2 838 | 839 | 840 | 841 | 842 | 843 | Advantages: 844 | 845 | * Load-balancer and other features 846 | * 0s reload down-time, i.e., forever alive 847 | * Good test coverage 848 | 849 | --- 850 | 851 | ### pm2 Demo: Typical Express Server 852 | 853 | ```js 854 | var express = require('express') 855 | var port = 3000 856 | global.stats = {} 857 | console.log('worker (%s) is now listening to http://localhost:%s', 858 | process.pid, port) 859 | var app = express() 860 | app.get('*', function(req, res) { 861 | if (!global.stats[process.pid]) global.stats[process.pid] = 1 862 | else global.stats[process.pid] += 1; 863 | var l ='cluser ' 864 | + process.pid 865 | + ' responded \n'; 866 | console.log(l, global.stats) 867 | res.status(200).send(l) 868 | }) 869 | app.listen(port) 870 | ``` 871 | 872 | --- 873 | 874 | ### pm2 Demo 875 | 876 | Using `server.js`: 877 | 878 | ``` 879 | $ pm2 start server.js -i 0 880 | ``` 881 | 882 | In a new window: 883 | 884 | ``` 885 | $ loadtest http://localhost:3000 -t 20 -c 10 886 | $ pm2 list 887 | ``` 888 | 889 | --- 890 | 891 | # Spawn vs Fork vs Exec 892 | 893 | * `require('child_process').spawn()` - large data, stream, no new V8 instance 894 | * `require('child_process').fork()` - new V8 instance, multiple workers 895 | * `require('child_process').exec()` - buffer, async, all the data at once 896 | 897 | --- 898 | 899 | ### Spawn Example 900 | 901 | ```js 902 | fs = require('fs') 903 | process = require('child_process') 904 | var p = process.spawn('node', 'program.js') 905 | p.stdout.on('data', function(data)) { 906 | console.log('stdout: ' + data) 907 | }) 908 | ``` 909 | 910 | --- 911 | 912 | ### Fork Example 913 | 914 | ```js 915 | fs = require('fs') 916 | process = require('child_process') 917 | var p = process.fork('program.js') 918 | p.stdout.on('data', function(data)) { 919 | console.log('stdout: ' + data) 920 | }) 921 | ``` 922 | 923 | --- 924 | 925 | # Exec Example 926 | 927 | ```js 928 | fs = require('fs') 929 | process = require('child_process') 930 | var p = process.exec('node program.js', function (error, stdout, stderr) { 931 | if (error) console.log(error.code) 932 | }) 933 | ``` 934 | 935 | --- 936 | 937 | # You Don't Know Node 938 | ## Async Errors 939 | 940 | ![inline 100%](images/azat.jpeg) 941 | Azat Mardan @azat_co 942 | 943 | 944 | ![inline right](images/nu.png) 945 | 946 | --- 947 | 948 | # How to handle async errors? 949 | 950 | --- 951 | 952 | 953 | # Handling Async Errors 954 | 955 | Event Loop: Async errors are harder to handle/debug, because system loses context of the error. Then, application crashes. 956 | 957 | Try/catch is not good enough. 958 | 959 | --- 960 | 961 | 962 | ### Synchronous Error in Node 963 | 964 | ```js 965 | try { 966 | throw new Error('Fail!') 967 | } catch (e) { 968 | console.log('Custom Error: ' + e.message) 969 | } 970 | ``` 971 | 972 | For sync errors try/catch works fine. 973 | 974 | --- 975 | 976 | 977 | ### Async Error Example 978 | 979 | ```js 980 | try { 981 | setTimeout(function () { 982 | throw new Error('Fail!') 983 | }, Math.round(Math.random()*100)) 984 | } catch (e) { 985 | console.log('Custom Error: ' + e.message) 986 | } 987 | ``` 988 | 989 | The app crashes! 990 | 991 | --- 992 | 993 | # Me When Async Error's Thrown 994 | 995 | ![inline](images/baby_elephant.gif) 996 | 997 | --- 998 | 999 | ### Async Errors 1000 | 1001 | How to deal with it? 1002 | 1003 | :sweat_smile: 1004 | 1005 | --- 1006 | 1007 | ## Best Practices for Async Errors? 1008 | 1009 | * Listen to all “on error” events 1010 | * Listen to `uncaughtException` 1011 | * Use `domain` (soft deprecated) or [AsyncWrap](http://blog.trevnorris.com/2015/02/asyncwrap-tutorial-introduction.html) 1012 | * Log, log, log & Trace 1013 | * Notify (optional) 1014 | * Exit & Restart the process 1015 | 1016 | --- 1017 | 1018 | ### on('error') 1019 | 1020 | Anything that inherits from or creates an instance of the above: Express, LoopBack, Sails, Hapi, etc. 1021 | 1022 | ```js 1023 | server.on('error', function (err) { 1024 | console.error(err) 1025 | }) 1026 | ``` 1027 | 1028 | --- 1029 | 1030 | ### on('error') Chained Method Example 1031 | 1032 | ```js 1033 | var http = require(‘http’) 1034 | var server = http.createServer(app) 1035 | .on('error', function(e) { 1036 | console.log(‘Failed to create server’) 1037 | console.error(e) 1038 | process.exit(1) 1039 | }) 1040 | ``` 1041 | 1042 | --- 1043 | 1044 | ### on(‘error’) Named Variable Example 1045 | 1046 | ```js 1047 | var req = http.request(options, function(res) { 1048 | // … processing the response 1049 | }) 1050 | 1051 | req.on('error', function(e) { 1052 | console.log('problem with request: ' + e.message) 1053 | }) 1054 | ``` 1055 | 1056 | --- 1057 | 1058 | ### uncaughtException 1059 | 1060 | `uncaughtException` is a very crude mechanism for exception handling. An unhandled exception means your application - and by extension Node.js itself - is in an undefined state. Blindly resuming means anything could happen. 1061 | 1062 | --- 1063 | 1064 | ### uncaughtException 1065 | 1066 | Always listen to `uncaughtException`! 1067 | 1068 | ```js 1069 | process.on(‘uncaughtException’, handle) 1070 | ``` 1071 | 1072 | or 1073 | 1074 | ```js 1075 | process.addListener('uncaughtException', handle) 1076 | ``` 1077 | 1078 | --- 1079 | 1080 | 1081 | ### uncaughtException Expanded Examples 1082 | 1083 | ```js 1084 | process.on('uncaughtException', function (err) { 1085 | console.error('uncaughtException: ', err.message) 1086 | console.error(err.stack) 1087 | process.exit(1) 1088 | }) 1089 | ``` 1090 | 1091 | or 1092 | 1093 | ```js 1094 | process.addListener('uncaughtException', function (err) { 1095 | console.error('uncaughtException: ', err.message) 1096 | console.error(err.stack) 1097 | process.exit(1) 1098 | ``` 1099 | 1100 | --- 1101 | 1102 | ### Domain 1103 | 1104 | This module is softly deprecated in 4.0 (most likey will be separate from core module), but there's no alternatives in core as of now. 1105 | 1106 | --- 1107 | 1108 | ### Domain Example 1109 | 1110 | ```js 1111 | var domain = require('domain').create() 1112 | domain.on('error', function(error){ 1113 | console.log(error) 1114 | }) 1115 | domain.run(function(){ 1116 | throw new Error('Failed!') 1117 | }) 1118 | ``` 1119 | 1120 | --- 1121 | 1122 | ### Domain with Async Error Demo 1123 | 1124 | domain-async.js: 1125 | 1126 | ```js 1127 | var d = require('domain').create() 1128 | d.on('error', function(e) { 1129 | console.log('Custom Error: ' + e) 1130 | }) 1131 | d.run(function() { 1132 | setTimeout(function () { 1133 | throw new Error('Failed!') 1134 | }, Math.round(Math.random()*100)) 1135 | }); 1136 | ``` 1137 | 1138 | --- 1139 | 1140 | # You Don't Know Node 1141 | ## C++ Addons 1142 | 1143 | ![inline 100%](images/azat.jpeg) 1144 | Azat Mardan @azat_co 1145 | 1146 | 1147 | ![inline right](images/nu.png) 1148 | 1149 | 1150 | --- 1151 | 1152 | # C++ Addons 1153 | 1154 | --- 1155 | 1156 | ## How to Write C/C++ binding for your IoT, hardware, drone, smartdevice, etc.? 1157 | 1158 | --- 1159 | 1160 | ### Node and C++ 1161 | 1162 | Create the `hello.cc` file: 1163 | 1164 | ```c 1165 | #include 1166 | 1167 | namespace demo { 1168 | 1169 | using v8::FunctionCallbackInfo; 1170 | using v8::HandleScope; 1171 | using v8::Isolate; 1172 | using v8::Local; 1173 | using v8::Object; 1174 | using v8::String; 1175 | using v8::Value; 1176 | ``` 1177 | 1178 | --- 1179 | 1180 | ### Node and C++ 1181 | 1182 | Create the `hello.cc` file: 1183 | 1184 | ```c 1185 | void Method(const FunctionCallbackInfo& args) { 1186 | Isolate* isolate = args.GetIsolate(); 1187 | args.GetReturnValue().Set(String::NewFromUtf8(isolate, "capital one")); 1188 | } 1189 | 1190 | void init(Local exports) { 1191 | NODE_SET_METHOD(exports, "hello", Method); 1192 | } 1193 | 1194 | NODE_MODULE(addon, init) 1195 | 1196 | } // namespace demo 1197 | ``` 1198 | 1199 | --- 1200 | 1201 | ### Creating `binding.gyp` 1202 | 1203 | Create `binding.gyp`: 1204 | 1205 | ``` 1206 | { 1207 | "targets": [ 1208 | { 1209 | "target_name": "addon", 1210 | "sources": [ "hello.cc" ] 1211 | } 1212 | ] 1213 | } 1214 | ``` 1215 | 1216 | --- 1217 | 1218 | ### node-gyp 1219 | 1220 | ``` 1221 | $ npm install -g node-gyp 1222 | ``` 1223 | 1224 | 1225 | 1226 | ^Needs Python 1227 | 1228 | --- 1229 | 1230 | ### Configuring and Building 1231 | 1232 | ``` 1233 | $ node-gyp configure 1234 | $ node-gyp build 1235 | ``` 1236 | 1237 | Check for compiled `.node` files in build/Release/ 1238 | 1239 | --- 1240 | 1241 | ### C++ Addons Examples 1242 | 1243 | 1244 | 1245 | --- 1246 | 1247 | ### Including Addon 1248 | 1249 | Create `hello.js` and include your C++ addon: 1250 | 1251 | ```js 1252 | var addon = require('./build/Release/addon') 1253 | console.log(addon.hello()) // 'capital one' 1254 | ``` 1255 | 1256 | Run 1257 | 1258 | ``` 1259 | $ node hello.js 1260 | ``` 1261 | 1262 | 1263 | --- 1264 | 1265 | 1266 | # 30-Second Summary 1267 | 1268 | 1. Event Emitters 1269 | 1. Streams 1270 | 1. Buffers 1271 | 1. Clusters 1272 | 1. C++ Addons 1273 | 1. Domain 1274 | 1275 | --- 1276 | 1277 | 1278 | # One Last Thing 1279 | 1280 | --- 1281 | 1282 | # CodingHorror.com 1283 | 1284 | ![inline](images/atwoods_law.png) 1285 | -------------------------------------------------------------------------------- /course.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/course.pdf -------------------------------------------------------------------------------- /images/Node.js Design Patterns - Second Edition Book Cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/Node.js Design Patterns - Second Edition Book Cover.png -------------------------------------------------------------------------------- /images/atwoods_law.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/atwoods_law.png -------------------------------------------------------------------------------- /images/azat.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/azat.jpeg -------------------------------------------------------------------------------- /images/azats-books-covers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/azats-books-covers.png -------------------------------------------------------------------------------- /images/baby_car_wash.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/baby_car_wash.gif -------------------------------------------------------------------------------- /images/baby_elephant.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/baby_elephant.gif -------------------------------------------------------------------------------- /images/code_review.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/code_review.png -------------------------------------------------------------------------------- /images/coffeeshop-blocking.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/coffeeshop-blocking.jpg -------------------------------------------------------------------------------- /images/coffeeshop-non-blocking.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/coffeeshop-non-blocking.jpg -------------------------------------------------------------------------------- /images/commercial.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/commercial.gif -------------------------------------------------------------------------------- /images/companies-0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/companies-0.png -------------------------------------------------------------------------------- /images/companies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/companies.png -------------------------------------------------------------------------------- /images/event-loop-node-interactive.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/event-loop-node-interactive.jpg -------------------------------------------------------------------------------- /images/fullstackjavascript.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/fullstackjavascript.jpg -------------------------------------------------------------------------------- /images/java-compile-funny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/java-compile-funny.jpg -------------------------------------------------------------------------------- /images/kylesimpson.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/kylesimpson.jpg -------------------------------------------------------------------------------- /images/logos/1000px-Macys.svg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/logos/1000px-Macys.svg.png -------------------------------------------------------------------------------- /images/logos/MSFT_logo_rgb_C-Gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/logos/MSFT_logo_rgb_C-Gray.png -------------------------------------------------------------------------------- /images/logos/salesforce-logo-273F95FE60-seeklogo.com.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/logos/salesforce-logo-273F95FE60-seeklogo.com.png -------------------------------------------------------------------------------- /images/node-capital-one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/node-capital-one.png -------------------------------------------------------------------------------- /images/node-core-modules.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/node-core-modules.gif -------------------------------------------------------------------------------- /images/nodeconfcamp.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/nodeconfcamp.JPG -------------------------------------------------------------------------------- /images/nodeu-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/nodeu-1.png -------------------------------------------------------------------------------- /images/nodeu-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/nodeu-2.png -------------------------------------------------------------------------------- /images/nodeu-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/nodeu-3.png -------------------------------------------------------------------------------- /images/nodeu-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/nodeu-4.png -------------------------------------------------------------------------------- /images/non-blocking-docs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/non-blocking-docs.png -------------------------------------------------------------------------------- /images/non-blocking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/non-blocking.png -------------------------------------------------------------------------------- /images/nu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/nu.png -------------------------------------------------------------------------------- /images/practicalnode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/practicalnode.png -------------------------------------------------------------------------------- /images/proexpress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/proexpress.png -------------------------------------------------------------------------------- /images/reactquickly.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/reactquickly.jpg -------------------------------------------------------------------------------- /images/sucks.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/sucks.gif -------------------------------------------------------------------------------- /images/threading_java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/threading_java.png -------------------------------------------------------------------------------- /images/threading_node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/threading_node.png -------------------------------------------------------------------------------- /images/tree_slam.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/tree_slam.gif -------------------------------------------------------------------------------- /images/watermark-inverted-no-bk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/watermark-inverted-no-bk.png -------------------------------------------------------------------------------- /images/you-dk-node-course-cover-v1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/azat-co/you-dont-know-node/61fa4f01c0950e61db380ab325cd5ea757758666/images/you-dk-node-course-cover-v1.png -------------------------------------------------------------------------------- /move.js: -------------------------------------------------------------------------------- 1 | // npm install you-dont-know-node -g --prefix . 2 | //mv ./lib/node_modules/you-dont-know-node . && rm -rf ./etc && rm -rf ./lib" 3 | //https://github.com/npm/npm/issues/4017 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "you-dont-know-node", 3 | "version": "1.2.5", 4 | "description": "You Don't Know Node.js", 5 | "main": "cluster.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": "https://github.com/azat-co/you-dont-know-node", 10 | "author": "Azat Mardan", 11 | "license": "MIT", 12 | "dependencies": { 13 | "express": "^4.13.3", 14 | "response-time": "^2.3.1" 15 | } 16 | } 17 | --------------------------------------------------------------------------------