["15\t Introduction\tto\tNode.js Node.js\tis\tan\topen-source\tproject\tthat\tlets\tyou\twrite\tJavaScript\tthat\truns\toutside\tthe browser. When\tyou\twrite\tJavaScript\tfor\tthe\tbrowser,\tyour\tcode\tis\tgiven\taccess\tto\tglobal\tobjects like\tthe\tdocument\tand\twindow,\tas\twell\tas\tother\tAPIs\tand\tlibraries.\tWith\tNode,\tyour\tcode can\taccess\tthe\thard\tdrive,\tdatabases,\tand\tthe\tnetwork\t(Figure\t15.1). Figure\t15.1\t\tJavaScript\trunning\tin\tbrowser\tvs\tvia\tNode Using\tNode,\tyou\tcan\tcreate\tanything\tfrom\tcommand-line\ttools\tto\tweb\tservers.\tOver\tthe next\tfour\tchapters,\tyou\twill\tuse\tNode\tto\thelp\tcreate\ta\treal-time\tchat\tapplication\tcalled Chattrbox\t(Figure\t15.2).","Figure\t15.2\t\tChattrbox:\tstrictly\tfor\timportant\tconversations Chattrbox\twill\tconsist\tof\ttwo\tparts:\ta\tNode.js\tserver\tand\ta\tJavaScript\tapp\trunning\tin\tthe browser.\tThe\tbrowser\twill\tconnect\tto\tthe\tNode\tserver\tand\treceive\tthe\tHTML,\tCSS,\tand JavaScript\tfiles.\tAt\tthat\tpoint,\tthe\tJavaScript\tapp\tin\tthe\tbrowser\twill\tbegin\thandling\treal- time\tcommunication\tover\tWebSockets.\tThis\tprocess\tis\tdiagrammed\tin\tFigure\t15.3.","Figure\t15.3\t\tNetwork\tdiagram\tof\tthe\tChattrbox\tapplication You\twill\tlearn\tabout\tWebSockets\tin\tthe\tnext\tchapter.\tThis\tchapter\tfocuses\ton\tgetting\tyou familiar\twith\tNode. Node\tand\tnpm When\tyou\tinstalled\tNode.js\tin\tChapter\t1,\tyou\tgot\taccess\tto\ttwo\tcommand-line\tprograms: node\tand\tthe\tNode\tpackage\tmanager,\tnpm.\tYou\tmay\trecall\tthat\tnpm\tallows\tyou\tto\tinstall open-source\tdevelopment\ttools,\tlike\tbrowser-sync.\tThe\tnode\tprogram\tdoes\tthe\twork\tof running\tprograms\twritten\tin\tJavaScript. Most\tof\tyour\twork\twill\tbe\twith\tnpm\tin\tthis\tchapter.\tThe\tnpm\tcommand-line\ttool\tcan perform\ta\tvariety\tof\ttasks,\tlike\tinstalling\tthird-party\tcode\tthat\tyou\tcan\tincorporate\tinto your\tproject\tand\tmanaging\tyour\tproject\u2019s\tworkflow\tand\texternal\tdependencies.\tIn\tthis chapter,\tyou\twill\tbe\tusing\tnpm\tto: create\tthe\tpackage.json\tfile,\tusing\tnpm\tinit add\tthird-party\tmodules,\tusing\tnpm\tinstall\t--save run\tfrequently\tused\ttools\tsaved\tin\tpackage.json\u2019s\tscripts\tsection Node\tis\tmuch\tmore\tthan\tthe\tnode\tand\tnpm\tcommands.\tIt\talso\tincludes\ta\tnumber\tof\tuseful modules\tthat\tprovide\tconstructors\tto\thelp\tyou\tdo\tthings\tlike\twork\twith\tfiles\tand\tfolders, communicate\tover\ta\tnetwork,\tand\thandle\tevents.\tAlso,\twhen\twriting\tJavaScript\tfor\tNode","you\twill\thave\taccess\tto\tutility\tfunctions\tthat\tfacilitate\tJavaScript\u2019s\tinteraction\twith\tthe Node\tmodule\tecosystem.\tFor\texample,\tNode\tprovides\ta\tmuch\tsimpler\tmodule\tpattern\tthan the\tIIFEs\tyou\tused\tfor\tCoffeeRun. The\tpackage.json\tfile\tmentioned\tabove\tis\ta\tfile\tthat\tacts\tas\tyour\tNode\tproject\u2019s manifest.\tIt\tholds\tyour\tproject\u2019s\tname,\tversion\tnumber,\tdescription,\tand\tother\tinformation. More\timportant,\tit\tis\twhere\tyou\tcan\tstore\tconfiguration\tsettings\tand\tcommands\tfor\tnpm\tto use\twhen\ttesting\tand\tbuilding\tyour\tapplication. You\tcould\tcreate\tthis\tfile\tby\thand,\tbut\tit\tis\tmuch\teasier\tto\tlet\tnpm\tdo\tit\tfor\tyou. npm\tinit Create\ta\tdirectory\tin\tyour\tprojects\tfolder\tnamed\tchattrbox.\tIn\tyour\tterminal\tprogram, change\tto\tthat\tdirectory\tand\trun\tnpm\tinit\tto\thave\tnpm\tcreate\tpackage.json. npm\twill\tprompt\tyou\tfor\tinformation\tabout\tthe\tproject.\tIt\twill\talso\toffer\tdefault\tanswers, which\tare\tfine\tfor\tnow.\tPress\tthe\tReturn\tkey\tto\taccept\tthe\tdefaults\t(Figure\t15.4).","Figure\t15.4\t\tRunning\tnpm\tinit Open\tthe\tchattrbox\tproject\tfolder\tin\tAtom.\tYou\twill\tsee\tthat\tthe\tpackage.json\tfile was\tindeed\tcreated\tfor\tyou\t(Figure\t15.5).","Figure\t15.5\t\tpackage.json\tcontents\tafter\tnpm\tinit npm\tscripts In\tpackage.json,\tnotice\tthe\tsection\tlabeled\t\\\"scripts\\\".\tThis\tis\tfor\tcommands\tthat\tyou might\tneed\tto\trun\tagain\tand\tagain\twhile\tworking\ton\tyour\tproject. As\tyou\tbuild\tChattrbox,\tyou\twill\tadd\tto\tthe\t\\\"scripts\\\"\tsection\tof\tpackage.json\tto make\tyour\tdevelopment\tworkflow\tmore\tefficient.\tCreate\tyour\tfirst\tnpm\tworkflow\tscript\tby adding\ta\t\\\"start\\\"\tscript\t(note\tthat\tyou\tmust\tadd\ta\tcomma\tto\tthe\tend\tof\tthe\t\\\"test\\\"\tline): ... \t\t\\\"scripts\\\":\t{ \t\t\t\t\\\"test\\\":\t\\\"echo\t\\\\\\\"Error:\tno\ttest\tspecified\\\\\\\"\t&&\texit\t1\\\", \t\t\t\t\\\"start\\\":\t\\\"node\tindex.js\\\" \t\t}, ... This\tlets\tyou\tstart\tyour\tNode\tserver\tby\trunning\tnpm\tstart\tfrom\tthe\tcommand\tline.","Hello,\tWorld To\tintroduce\tyou\tto\tthe\tworld\tof\twriting\tJavaScript\toutside\tthe\tbrowser,\tyou\tare\tgoing\tto start\twith\ta\tclassic\tHello,\tWorld\tprogram.\tCreate\ta\tnew\tfile\tnamed\tindex.js\twithin\tyour chattrbox\tfolder\tand\ttype\tthe\tfollowing,\twhich\twe\twill\texplain\tafter\tyou\thave\tentered it: var\thttp\t=\trequire('http'); var\tserver\t=\thttp.createServer(function\t(req,\tres)\t{ \t\tconsole.log('Responding\tto\ta\trequest.'); \t\tres.end('<h1>Hello,\tWorld<\/h1>'); }); server.listen(3000); On\tthe\tfirst\tline,\tyou\tused\tNode\u2019s\tbuilt-in\trequire\tfunction\tto\taccess\tthe\thttp\tmodule included\twith\tNode.\tThis\tmodule\tprovides\ta\tnumber\tof\ttools\tfor\tworking\twith\tHTTP requests\tand\tresponses,\tsuch\tas\tthe\thttp.createServer\tfunction. http.createServer\ttakes\tin\tone\targument,\ta\tfunction.\tThis\tfunction\tis\tcalled\tfor every\tHTTP\trequest.\tYou\tmay\trecognize\tthis\tas\tthe\tcallback\tpattern\tyou\tused\twith browser\tevents\t\u2013\texcept\tthat\tin\tthis\tcase\tit\tis\ta\tserver-side\tevent\t(receiving\tan\tHTTP request)\tthat\ttriggers\tthe\tcallback. In\tyour\tcallback,\tyou\tlog\ta\tmessage\tto\tthe\tconsole\tand\twrite\tsome\tHTML\ttext\tto\tthe response.\tIn\tNode,\tit\tis\tcommon\tto\tuse\treq\tand\tres\tas\tthe\tvariable\tnames\tfor\tHTTP request\tand\tresponse\tobjects. Finally,\tyou\ttell\tthe\tserver\tto\tlisten\ton\tport\t3000\tusing\tserver.listen.\tThis\tis commonly\treferred\tto\tas\t\u201cbinding\tto\ta\tport.\u201d Save\tyour\tfiles.\tTo\tsee\tyour\tNode\tserver\tin\taction,\trun\tthe\tcommand\tnpm\tstart.\tThe terminal\tresults\tare\tshown\tin\tFigure\t15.6. Figure\t15.6\t\tRunning\tindex.js\tvia\tnpm\tstart Next,\topen\tyour\tbrowser\tto\thttp:\/\/\u200blocalhost:3000.\tYour\tresults\twill\tlook\tlike Figure\t15.7.\t(Note\tthat\tin\tsome\tbrowsers\tother\tthan\tChrome,\tyou\tmay\tsee\tyour\tHTML\tas plain\ttext.\tThese\tbrowsers\tare\texpecting\teither\ta\tdoctype\tor\tan\textra\tpiece\tof\tmetadata\tin the\tresponse\tdeclaring\tthat\tthe\tresponse\tshould\tbe\tinterpreted\tas\tHTML.\tYou\twill\taddress this\tas\tone\tof\tthe\tchallenges\tat\tthe\tend\tof\tthe\tchapter.)","Figure\t15.7\t\tAccessing\tyour\tNode\tserver\tin\ta\tbrowser Unlike\twhen\tyou\tran\tOttergram\tand\tCoffeeRun,\tthere\tis\tno\tJavaScript\tto\tsee\tin\tthe browser.\tBy\tthe\ttime\tyou\tsee\tthis\tpage,\tyour\tJavaScript\tcode\thas\talready\tdone\tits\twork\ton the\tserver. Return\tto\tyour\tterminal.\tYou\tshould\tsee\tthat\tconsole.log\tprinted\tResponding\tto\ta request\twhen\tthe\trequest\twas\treceived\t(Figure\t15.8). Figure\t15.8\t\tconsole.log\twhen\trequest\tarrives","Adding\tan\tnpm\tScript In\taddition\tto\tletting\tyou\twrite\tcommand-line\tJavaScript\tprograms,\tNode\tgives\tyou\ta\tway to\torchestrate\tyour\tworkflow\tas\tyou\tdevelop\tthese\tprograms.\tIt\tis\ta\tpowerful\tfeature\tthat you\tshould\ttake\tadvantage\tof.\tTo\tsee\thow\tthis\tworks,\tyou\twill\tadd\ta\tbit\tof\tautomation\tto your\tproject. Take\trunning\tyour\tserver,\tfor\texample.\tEvery\ttime\tyou\twant\tto\ttry\tsomething\tnew\tin\tyour code,\tyou\thave\tto\trepeat\ta\tfew\tsteps: make\tthe\tchange\tto\tthe\tcode\tin\tyour\teditor switch\tto\tyour\tterminal press\tControl-C\tto\tstop\tthe\tprogram run\tnpm\tstart\tto\tstart\tyour\tprogram\tagain You\tcould\twrite\ta\tprogram\tto\tautomate\tthe\twork\tof\trestarting\tyour\tservice.\tYou\tare\tin luck,\tthough\t\u2013\tsomeone\thas\talready\twritten\tit\tfor\tyou,\tin\ta\tmodule\tcalled\tnodemon. Integrating\tnodemon\tinto\tyour\tworkflow\tearly\ton\twill\tmake\twriting\tyour\tprogram\ta\tmuch smoother\texperience. In\tthe\tterminal,\tstop\tyour\tprogram\tand\trun\tthe\tfollowing\tcommand\tto\tinstall\tthe\tnodemon module: npm\tinstall\t--save-dev\tnodemon You\twill\tsee\tthe\tlines\tbelow,\tin\twhich\tnpm\tis\twarning\tyou\tabout\tsome\tblank\tfields\tin\tyour package.json\tfile.\tDo\tnot\tbe\talarmed\t\u2013\tjust\tbe\taware\tthat\tnpm\tis\ta\tstickler\tfor\tdetails. npm\tWARN\[email protected]\tNo\tdescription npm\tWARN\[email protected]\tNo\trepository\tfield. Notice\tthe\t--save-dev\toption\tthat\tyou\tused\tin\tthis\tnpm\tinstall\tcommand.\tIt\ttells\tnpm\tto help\tyou\tkeep\ta\tlist\tof\tany\tthird-party\tmodules\tyour\tapplication\tdepends\ton.\tThat\tlist\tis stored\tin\tyour\tpackage.json\tfile.\tIf\tnecessary,\tall\tof\tthe\tdependencies\tin\tthat\tlist\tcan be\tinstalled\tby\trunning\tthe\tnpm\tinstall\tcommand\t(with\tno\targuments).\tThis\tmeans\tthat\twhen you\tare\tsharing\tyour\tcode\tyou\tdo\tnot\tneed\tto\tinclude\tall\tof\tthe\tthird-party\tmodules\tas well. If\tyou\tlook\tin\tyour\tpackage.json\tfile,\tyou\twill\tsee\tthat\tnpm\tcreated\ta\tnew \\\"devDependencies\\\"\tsection\tfor\tyou,\twith\tan\tentry\tfor\tnodemon. ... \t\t\\\"author\\\":\t\\\"\\\", \t\t\\\"license\\\":\t\\\"ISC\\\", \t\t\\\"devDependencies\\\":\t{ \t\t\t\t\\\"nodemon\\\":\t\\\"^1.9.1\\\" \t\t} Now,\tupdate\tyour\tpackage.json\tto\tadd\tanother\titem\tto\tthe\t\\\"scripts\\\"\tsection: ... \\\"scripts\\\":\t{ \t\t\\\"test\\\":\t\\\"echo\t\\\\\\\"Error:\tno\ttest\tspecified\\\\\\\"\t&&\texit\t1\\\", \t\t\\\"start\\\":\t\\\"node\tindex.js\\\", \t\t\\\"dev\\\":\t\\\"nodemon\tindex.js\\\" }, ...","In\tthe\tterminal,\trestart\tyour\tnode\tprogram\tvia\tyour\tnew\tnpm\tscript\tusing\tthe\tcommand\tnpm run\tdev.\tNote\tthat\tthe\tcommand\tis\tnot\tsimply\tnpm\tdev.\tThis\tdiffers\tfrom\tnpm\tstart\tin that\tnpm\tassumes\tthat\tcertain\tcommands\t(like\tstart)\twill\texist.\tFor\tcustom\tnpm\tscripts, you\tmust\tspecify\tthat\tyou\twant\tto\trun\tthem. You\tshould\tsee\tthat\tnodemon\tis\tnow\tmanaging\tyour\tnode\tprogram\t(Figure\t15.9). Figure\t15.9\t\tRunning\tvia\tnpm\trun\tdev In\tindex.js,\tchange\t\\\"Hello,\tWorld\\\"\tto\t\\\"Hello,\tWorld!!\\\"\tand\tsave\tthe\tchanges. nodemon\tnotices\tand\trestarts\tyour\tnode\tprogram\tautomatically\t(Figure\t15.10). Figure\t15.10\t\tnodemon\trestarts\twhen\tyour\tcode\tchanges As\tyou\tcontinue\tto\twork\twith\tnode\tand\tnpm\tin\tthe\tfollowing\tchapters,\tyou\twill\tperiodically pull\tin\ta\tnew\tmodule\tto\thelp\tout.","Serving\tfrom\tFiles Being\table\tto\twrite\tand\trun\tJavaScript\ton\tyour\tserver\tis\tnice.\tMost\tservers\twill\twant\tto vend\tout\tand\tprocess\tcontent\tliving\tin\tfiles,\ttoo.\tYour\tnext\tjob\tis\tto\tmake\tyour\tserver\tread files\tfrom\ta\tsubfolder\tand\tsend\tthem\tin\ta\tresponse\tback\tto\tthe\tbrowser.\tThis\tis\tsimilar\tto what\tbrowser-sync\twas\tdoing\tfor\tyou\tin\tearlier\tchapters. Create\ta\tnew\tfolder\tnamed\tapp\tinside\tof\tyour\tchattrbox\tproject\tfolder.\tIn\tit,\tcreate\tan index.html\tfile\twith\tthe\tfollowing\ttext: Hello,\tFile! You\tdo\tnot\tneed\tany\tactual\tHTML\tin\tthis\tfile\t\u2013\tit\tonly\tneeds\tsome\tcontent\tthat\tcan\tbe read.\tYour\tproject\tfolder\tshould\tlook\tlike\tFigure\t15.11. Figure\t15.11\t\tChattrbox\tproject\tlayout Reading\ta\tfile\twith\tthe\tfs\tmodule In\tindex.js,\timport\tthe\tNode.js\tfile\tsystem\tmodule,\tfs,\tand\tcall\tits\treadFile\tmethod. var\thttp\t=\trequire('http'); var\tfs\t=\trequire('fs'); var\tserver\t=\thttp.createServer(function\t(req,\tres)\t{ \t\tconsole.log('Responding\tto\ta\trequest.'); \t\tres.end('<h1>Hello,\tWorld<\/h1>'); \t\tfs.readFile('app\/index.html',\tfunction\t(err,\tdata)\t{ \t\t\t\tres.end(data); \t\t}); }); server.listen(3000); The\treadFile\tmethod\ttakes\ta\tfile\tname\tand\ta\tcallback.\tInside\tyour\tcallback,\tyou\tsent the\tcontents\tof\tthe\tfile\tinstead\tof\tthe\tHTML\ttext\tusing\tres.end. Notice\tthat\tyour\tcallback\taccepts\tan\terr\targument\tas\twell\tas\tthe\tdata\tfrom\treading\tthe","file.\tThis\tis\ta\tNode.js\tprogramming\tconvention\tthat\twe\twill\tdiscuss\tlater\tin\tthis\tchapter. nodemon\tshould\thave\trestarted\tyour\tprogram,\tso\tyou\tcan\tgo\tdirectly\tto\tyour\tbrowser\tand reload.\tIn\tyour\tbrowser,\tyou\tshould\tsee\texactly\twhat\tyou\twrote\tin\tyour\tindex.html file: Hello,\tFile! This\tis\ta\tgood\tstart,\tbut\tyour\tchat\tapplication\twill\tneed\tto\tdo\tmore\tthan\tserve\ta\tsingle HTML\tfile.\tThat\tHTML\tfile\tmay\trequest\tother\tCSS\tor\tJavaScript\tfiles.\tTo\tfulfill\tthose requests,\tyour\tnode\tprogram\twill\tneed\tto\tunderstand\twhat\tfile\tis\tbeing\trequested\tand where\tto\tlook\tfor\tthe\trequested\tfile.\tYou\twill\twork\ton\tthat\tnext. Working\twith\tthe\trequest\tURL First,\tyou\tneed\tto\tget\tthe\tURL\tpath\tfrom\tthe\trequest\tobject.\tIf\tthe\tpath\tis\tjust\t'\/',\tit\tis best\tto\treturn\tthe\tindex.html\tfile.\tThis\tis\ta\tcommon\tconvention\tfrom\tthe\tearly\tdays\tof the\tweb. Otherwise,\tyou\tshould\ttry\tto\treturn\tthe\tfile\tthe\trequest\tobject\tis\tasking\tfor. In\tindex.js,\tupdate\tyour\tcallback\tto\tcheck\twhat\tfile\tthe\tbrowser\tis\trequesting. var\thttp\t=\trequire('http'); var\tfs\t=\trequire('fs'); var\tserver\t=\thttp.createServer(function\t(req,\tres)\t{ \t\tconsole.log('Responding\tto\ta\trequest.'); \t\tvar\turl\t=\treq.url; \t\tvar\tfileName\t=\t'index.html'; \t\tif\t(url.length\t>\t1)\t{ \t\t\t\tfileName\t=\turl.substring(1); \t\t} \t\tconsole.log(fileName); \t\tfs.readFile('app\/index.html',\tfunction\t(err,\tdata)\t{ \t\t\t\tres.end(data); \t\t}); }); server.listen(3000); Using\tthe\trequest\tobject\u2019s\turl\tproperty,\tyou\tcan\tsee\twhether\tthe\tbrowser\tis\tasking\tfor\tthe default\t(index.html)\tor\tanother\tfile.\tIf\tit\tis\tanother\tfile,\tyou\tcall url.substring(1)\tto\tstrip\toff\tthe\tfirst\tcharacter,\twhich\twill\tbe\ta\t'\/'. For\tnow,\tyou\tare\tjust\tlogging\tthe\tfilename\tto\tthe\tconsole. After\tnodemon\trestarts\tyour\tprogram,\ttry\tgoing\tto\thttp:\/\/localhost:3000\/woohoo\tor\tany other\tpath,\tincluding\tthe\tdefault\t'\/'\tpath.\tThe\tresults\tin\tyour\tterminal\twill\tlook\tsomething like\tFigure\t15.12.","Figure\t15.12\t\tLogging\tthe\trequested\tfile\tpath (Recall\tfrom\tChapter\t2\tthat\tbrowsers\twill\tautomatically\task\tfor\ta\tfavicon.ico\tfile,\tso you\tmay\tsee\ta\trequest\tfor\tit\tlogged\tto\tthe\tterminal\tas\twell.) Now\tit\tis\ttime\tto\tmake\tuse\tof\tthis\tpath\tinformation. Using\tthe\tpath\tmodule You\tcould\tjust\tpass\tthe\tfileName\tto\tfs.readFile,\tbut\tit\tis\tbetter\tto\tuse\tthe\tpath module,\twhich\thas\tutilities\tfor\thandling\tand\ttransforming\tfile\tpaths.\tOne\tsmall\tbut important\treason\tfor\tusing\tthe\tmodule\tis\tthat\tsome\toperating\tsystems\tuse\ta\tforward\tslash and\tsome\tuse\ta\tbackslash.\tThe\tpath\tmodule\thandles\tthese\tdifferences\twith\tease. Update\tindex.js\tto\timport\tthe\tpath\tmodule\tand\tuse\tit\tto\tfind\tthe\tfile\tthat\twas requested. var\thttp\t=\trequire('http'); var\tfs\t=\trequire('fs'); var\tpath\t=\trequire('path'); var\tserver\t=\thttp.createServer(function\t(req,\tres)\t{ \t\tconsole.log('Responding\tto\ta\trequest.'); \t\tvar\turl\t=\treq.url; \t\tvar\tfileName\t=\t'index.html'; \t\tif\t(url.length\t>\t1)\t{ \t\t\t\tfileName\t=\turl.substring(1); \t\t} \t\tconsole.log(fileName); \t\tvar\tfilePath\t=\tpath.resolve(__dirname,\t'app',\tfileName); \t\tfs.readFile('app\/index.html'filePath,\tfunction\t(err,\tdata)\t{ \t\t\t\tres.end(data); \t\t}); }); server.listen(3000);","Test\ta\tfew\tfilepaths\tin\tthe\tbrowser\tto\tmake\tsure\tyour\tapplication\tstill\tworks\tthe\tsame.\tThe default\tpath\tshould\treturn\tindex.html,\tand\tnonexistent\tpaths\t(like\t'\/woohoo\/')\tshould show\tnothing\tand\tlog\tout\ttheir\tfilename. Next,\tcreate\ta\ttest.html\tfile\tin\tthe\tapp\tfolder.\tWrite\tthe\tfollowing\tinside\tit: Hola,\tNode! Try\tto\taccess\tit\tin\tthe\tbrowser.\tYour\tnode\tprogram\tshould\treturn\tit\twithout\tany\ttrouble (Figure\t15.13). Figure\t15.13\t\tRetrieving\ttest.html You\thave\tadded\tcode\tthat\tsuccessfully\tserves\ta\tspecific\tfile\tbased\ton\tthe\tURL\tpath.\tThe next\tthing\tto\tdo\tis\tabstract\tout\tthat\tfunctionality\tinto\tits\town\tmodule. Creating\ta\tcustom\tmodule Your\tcallback\thas\t(at\tleast)\ttwo\tjobs.\tIt\tfigures\tout\twhat\tfile\tis\tbeing\trequested,\tand\tit reads\tthat\tfile\tto\tsend\tback\tin\tthe\tresponse.\tTo\tmake\tthe\tcode\ta\tbit\tmore\tmodular\tand maintainable,\tone\tof\tthose\tresponsibilities\tshould\tbe\tmoved\tto\tits\town\tmodule. In\tCoffeeRun,\tyou\tdeclared\tmodules\tin\tan\tIIFE\tthat\tassigned\ta\tvalue\tto\ta\tproperty\tof\tthe global\tnamespace.\tModules\tin\tNode\tprograms\twork\tdifferently.\tYou\tstill\twrite\tyour module\tcode\tin\ta\tfile\tby\titself,\tbut\tyou\tdo\tnot\tneed\tthe\tIIFE. Create\ta\tnew\tfile\tcalled\textract.js\tin\tthe\tsame\tdirectory\tas\tyour\tindex.js\t(not\tin the\tapp\tdirectory).\tAdd\ta\tfunction\tcalled\textractFilePath\tthat\tfinds\tthe\tappropriate file.\t(This\tcode\tis\tvery\tsimilar\tto\twhat\tyou\talready\twrote\tin\tindex.js.) var\tpath\t=\trequire('path'); var\textractFilePath\t=\tfunction\t(url)\t{ \t\tvar\tfilePath; \t\tvar\tfileName\t=\t'index.html'; \t\tif\t(url.length\t>\t1)\t{ \t\t\t\tfileName\t=\turl.substring(1); \t\t} \t\tconsole.log('The\tfileName\tis:\t'\t+\tfileName); \t\tfilePath\t=\tpath.resolve(__dirname,\t'app',\tfileName); \t\treturn\tfilePath; }; You\thave\ttaken\tmuch\tof\tthe\tcode\tfrom\tindex.js\tand\tplaced\tit\tin\tits\town\tfunction,\tcalled","extractFilePath.\tNext,\tmake\tthe\textractFilePath\tfunction\tavailable\tso\tthat other\tmodules\tcan\timport\tit\twith\trequire.\tTo\tdo\tthis,\tassign\textractFilePath\tto\ta global\tvariable\tnamed\tmodule.exports.\tThis\tis\ta\tspecial\tvariable\tprovided\tby\tNode. Whatever\tvalue\tis\tassigned\tto\tit\tis\tthe\tvalue\tother\tmodules\tare\table\tto\timport.\tAny\tother variables\tor\tfunctions\twill\tnot\tbe\tvisible\tto\tother\tmodules. ... \t\tfilePath\t=\tpath.resolve(__dirname,\t'app',\tfileName); \t\treturn\tfilePath; }; module.exports\t=\textractFilePath; This\tnew\tline\ttells\tNode\tthat\twhen\tyou\timport\tthe\textract\tmodule\tby\tcalling require('.\/extract'),\tthe\tvalue\treturned\tis\tthe\textractFilePath\tfunction.\tDo\tthat now\tin\tindex.js. Using\tyour\tcustom\tmodule Update\tindex.js\tto\tuse\tyour\tnew\textract\tmodule\tinstead\tof\thandling\tthose responsibilities. var\thttp\t=\trequire('http'); var\tfs\t=\trequire('fs'); var\tpath\t=\trequire('path'); var\textract\t=\trequire('.\/extract'); var\tserver\t=\thttp.createServer(function\t(req,\tres)\t{ \t\tconsole.log('Responding\tto\ta\trequest.'); \t\tvar\turl\t=\treq.url; \t\tvar\tfileName\t=\t'index.html'; \t\tif\t(url.length\t>\t1)\t{ \t\t\t\tfileName\t=\turl.substring(1); \t\t} \t\tconsole.log(fileName); \t\tvar\tfilePath\t=\tpath.resolve(__dirname,\t'app',\tfileName); \t\tvar\tfilePath\t=\textract(req.url); \t\tfs.readFile(filePath,\tfunction\t(err,\tdata)\t{ \t\t\t\tres.end(data); \t\t}); }); server.listen(3000); You\timported\tyour\tcustom\tmodule\tusing\tthe\trequire\tfunction.\tYou\tassigned\tthe\tvalue of\tthe\tmodule\tto\tthe\tnew\tvariable\textract.\tNow\tyou\tare\table\tto\tuse\tthe\textract function\tjust\tas\tyou\twould\tthe\textractFilePath\tfunction. After\tnodemon\thas\treloaded\tyour\tcode,\ttest\tsome\tURL\tpaths\tand\tconfirm\tthat\tthe\tdefault index.html\tand\ttest.html\tstill\tload.\tAlso,\tmake\tsure\tthat\tnonexistent\tpaths\tcome up\tas\ta\tblank\tpage\tand\twithout\tan\terror.","Error\tHandling One\tlast\tjob\tremains.\tWhen\ta\tfile\tcannot\tbe\tfound,\tit\tis\tbetter\tto\treturn\tan\terror\tcode\tthan to\tsilently\tpretend\tthat\tall\tis\twell.\tTo\tdo\tthat,\tyou\twill\tneed\tto\tdetect\twhen fs.readFile\treturns\tan\terror\tinstead\tof\ta\tfile. In\tJavaScript,\tit\tis\tcommon\tto\tpass\tcallbacks\tto\tAPI\tmethods.\tThe\tsame\tis\ttrue\tfor\tNode.js, and\tcallbacks\ttypically\ttake\tin\tan\terror\tas\ttheir\tfirst\targument.\tBecause\tthe\terror\tcomes before\tthe\tresult,\tyou\tare\tforced\tto\tat\tleast\tsee\tthe\terror,\twhether\tor\tnot\tyou\thandle\tit. In\tindex.js,\tcheck\tfor\ta\tfile\terror\tand\twrite\ta\t404\terror\tcode\tif\tone\tis\tfound: var\thttp\t=\trequire('http'); var\tfs\t=\trequire('fs'); var\textract\t=\trequire('.\/extract'); var\thandleError\t=\tfunction\t(err,\tres)\t{ \t\tres.writeHead(404); \t\tres.end(); }; var\tserver\t=\thttp.createServer(function\t(req,\tres)\t{ \t\tconsole.log('Responding\tto\ta\trequest.'); \t\tvar\tfilePath\t=\textract(req.url); \t\tfs.readFile(filePath,\tfunction\t(err,\tdata)\t{ \t\t\t\tif\t(err)\t{ \t\t\t\t\t\thandleError(err,\tres); \t\t\t\t\t\treturn; \t\t\t\t}\telse\t{ \t\t\t\t\t\tres.end(data); \t\t\t\t} \t\t}); }); server.listen(3000); Save\tyour\tchanges.\tAfter\tnodemon\trestarts,\tgo\tto\ta\tnonexistent\tpath,\tsuch\tas http:\/\/localhost:3000\/woohoo.\tOpen\tthe\tnetwork\tpanel\tin\tthe\tDevTools,\tand\tyou should\tsee\tthe\terror\tcode,\tas\tin\tFigure\t15.14.","Figure\t15.14\t\t404\tStatus\tcode\tin\tthe\tnetwork\tpanel In\tyour\tcallback,\tthe\tvery\tfirst\tthing\tyou\tdo\tis\tcheck\twhether\tthis\terr\targument\thas\ta value\tthat\tis\tnot\tnull\tor\tundefined\tand\tdo\tsomething\twith\tit.\tIn\tthis\texample,\tyou\tpass the\terror\tinformation\talong\tto\tthe\tfunction\thandleError\tand\tthen\treturn\tto\texit\tthe anonymous\tcallback. Errors\tshould\tnever\tbe\tsilently\tdiscarded.\tA\tsimple\t404\twill\tdo\tfine\tfor\tnow,\twhich\tis what\thandleError\tdoes. This\tpattern\t\u2013\t\u201cerrors\tfirst,\treturn\tearly\u201d\t\u2013\tis\tone\tof\tthe\tbest\tpractices\tthat\tis\ta\tpart\tof\tthe Node\tecosystem.\tAll\tof\tthe\tmodules\tthat\tcome\twith\tNode\tfollow\tthis\tpattern,\tas\tdo\tmost open-source\tmodules. You\thave\tbuilt\ta\tworking\tweb\tserver\twith\tjust\ta\tfew\tdozen\tlines\tof\tJavaScript,\tusing patterns\t(such\tas\tcallbacks)\tthat\tyou\twere\talready\tfamiliar\twith. Node\tprovides\ta\trich\tset\tof\tmodules\tfor\tworking\twith\tnetworks\tand\tfiles,\tsuch\tas\tthe\thttp and\tfs\tmodules\tyou\tused\tin\tthis\tproject.\tThanks\tto\tNode\u2019s\trequire\tand\tmodule.exports keywords,\tyou\tcan\tmodularize\tyour\town\tcode\tvery\teasily. Over\tthe\tnext\tthree\tchapters,\tyou\twill\tcontinue\tto\tbuild\tthe\tChattrbox\tserver\tas\twell\tas\ta working\tfront\tend.","For\tthe\tMore\tCurious:\tnpm\tModule\tRegistry There\tare\ta\twealth\tof\tavailable\tpackages\tthat\tcan\tbe\tinstalled\tvia\tnpm.\tYou\tcan\tsearch\tor browse\tthese\tpackages\tin\tthe\tModule\tRegistry,\twww.npmjs.com. Make\tsure\tto\tlook\tat\tthe\tdocumentation\tfor\tnpm\tat\tdocs.npmjs.com.\tYou\tmight\talso be\tinterested\tin\tcreating\tyour\town\tmodules\tfor\tothers\tto\tuse.\tIf\tso,\tsee docs.npmjs.com\/g\u200b etting-started\/c\u200b reating-node-modules.","Bronze\tChallenge:\tCreating\ta\tCustom\tError\tPage When\tyou\tgo\tto\ta\tpath\tfor\ta\tfile\tthat\tdoes\tnot\texist,\tyou\tcurrently\tget\ta\tblank\tbrowser page\tand\ta\t404\tstatus\tcode. For\tthis\tchallenge,\tcreate\ta\tspecial\terror\tpage\tto\tdisplay\tto\tthe\tuser\tinstead\tof\treturning\tthe error\tas\ta\tstatus\tcode.","For\tthe\tMore\tCurious:\tMIME\tTypes Have\tyou\tever\twondered\thow\ta\tcomputer\tknows\thow\tto\topen\ta\tmovie\tfile\twith\ta\tvideo player\tand\ta\tPDF\twith\ta\tdocument\tviewer?\tYour\tcomputer\tkeeps\ta\ttable\tof\tfile\ttypes\tand the\tprograms\tassociated\twith\tthose\tfile\ttypes.\tIt\tinfers\ta\tfile\u2019s\ttype\tby\tlooking\tat\tthe\tfile extension\t(e.g.,\t.html\tor\t.pdf). A\tbrowser\tneeds\tthose\tsame\tassociations\tso\tthat\tit\tknows\twhether\tto\trender\tthe\tresponse as\tHTML,\tuse\ta\tplug-in\tto\tplay\tmusic,\tor\tdownload\ta\tfile\tto\tthe\thard\tdrive.\tBut\tHTTP responses\tdo\tnot\thave\tfile\textensions.\tInstead,\tthe\tserver\tmust\ttell\tthe\tbrowser\twhat\ttype of\tinformation\tis\tin\tthe\tresponse. It\tdoes\tthis\tby\tspecifying\tthe\tMIME\ttype\tor\tmedia\ttype\tin\tthe\tresponse\u2019s\tContent-Type header.\tFor\texample,\tFigure\t15.15\tshows\twhat\tyou\twould\tsee\tin\tthe\tnetwork\tpanel\tof\tthe DevTools\tif\tyou\tinspected\tthe\tresponse\tfor\twww.bignerdranch.com. Figure\t15.15\t\tInspecting\tthe\tContent-Type\theader\ton www.bignerdranch.com The\tContent-Type\theader\tis\tset\tto\ttext\/html\t\u2013\tthe\tMIME\ttype\tfor\tHTML.\tYou\tcan\tset this\theader\tin\tyour\tprojects.\tThis\tis\twhat\tthat\twould\tlook\tlike\tfor\tChattrbox:","... var\tserver\t=\thttp.createServer(function\t(req,\tres)\t{ \t\tconsole.log('Responding\tto\ta\trequest.'); \t\tvar\tfilePath\t=\textract(req.url); \t\tfs.readFile(filePath,\tfunction\t(err,\tdata)\t{ \t\t\t\tif\t(err)\t{ \t\t\t\t\t\thandleError(err,\tres); \t\t\t\t\t\treturn; \t\t\t\t}\telse\t{ \t\t\t\t\t\tres.setHeader('Content-Type',\t'text\/html'); \t\t\t\t\t\tres.end(data); \t\t\t\t} \t\t}); }); server.listen(3000); (Note\tthat\tyou\tmust\tset\tthe\theader\tbefore\tyou\tend\tthe\tresponse.) To\tfind\tout\tmore\tabout\tMIME\ttypes\tin\tgeneral,\tcheck\tout\ten.wikipedia.org\/\u200b wiki\/M\u200b edia_type.\tFor\tmore\tabout\tsetting\theaders\tin\tyour\tNode\tprograms,\tgo\tto nodejs.org\/\u200bapi\/\u200bhttp.html#http_response_setheader_name_value.","Silver\tChallenge:\tProviding\ta\tMIME\tType\tDynamically Dynamically\tprovide\ta\tMIME\ttype\tfor\tyour\tresponses\tbased\ton\tthe\tfile\ttype.\tTo\thelp\tyou with\tthis\ttask,\tinstall\tthe\tmime\tmodule\tusing\tnpm.\tInformation\tand\tdocumentation\tabout the\tmime\tmodule\tis\tavailable\tat\tgithub.com\/b\u200b roofa\/n\u200b ode-mime. Add\tdifferent\tfiles\tto\tyour\tapp\tfolder,\tincluding\tplain\ttext,\tPDFs,\taudio\tfiles,\tand\tmovies. Make\tsure\tthat\tyour\tbrowser\tis\tdisplaying\teach\ttype\tcorrectly.","Gold\tChallenge:\tMoving\tError\tHandling\tto\tIts\tOwn Module Take\tthe\tcode\tthat\tdoes\tthe\tfile\treading\tand\terror\thandling\tand\tmove\tit\tinto\tits\town module. Also,\tmake\tthe\tmodule\tconfigurable\tso\tthat\twhen\tyou\timport\tit\tyou\tcan\tspecify\twhat\tbase folder\tthe\tstatic\tHTML,\tCSS,\tand\tJavaScript\tare\tin.","16\t Real-Time\tCommunication\twith WebSockets With\tregular\tGET\tand\tPOST\trequests,\tyour\tbrowser\thas\tto\tmake\ta\tnew\trequest\tand\twait\tfor a\tresponse\tfor\teach\texchange\tof\tdata\twith\tthe\tserver.\tAs\tyou\tlearned\tin\tearlier\tchapters, this\tis\talso\tthe\tcase\twith\tAjax\trequests.\tWhile\tAjax\trequests\tdo\tnot\tcause\tpage\treloads, they\tdo\tgenerate\tjust\tas\tmuch\tnetwork\ttraffic,\twith\teach\trequest\tand\tresponse\trequiring\ta little\tbit\tof\toverhead\tto\tproduce\tand\tprocess. WebSockets,\ton\tthe\tother\thand,\tprovides\ta\ttwo-way\tcommunication\tprotocol\tover\tHTTP. It\tcreates\ta\tsingle\tconnection\tand\tkeeps\tit\topen\tfor\treal-time\tcommunication\t(Figure\t16.1). Figure\t16.1\t\tMultiple\tAjax\trequests\tvs\ta\tsingle\tWebSockets\tconnection With\tWebSockets,\tweb\tapplications\tcan\tgo\tbeyond\tsaving\tand\tloading\tremote\tdata.\tPush notifications,\tcollaborative\tdocument\tediting,\tand\treal-time\tchat\tare\tjust\tthe\tbeginning. WebSockets\tmakes\tit\tpossible\tfor\tservers\tto\thandle\tthe\tload\tof\tthe\tInternet\tof\tThings\t(e.g., smart\tlights,\tsmart\tlocks,\tsmart\tcars).\tTraditional\ttechniques\tlike\tAjax\tpolling\tare ineffective\tat\tcoordinating\tsuch\tintense\ttraffic. In\tthis\tchapter,\tyou\twill\tbuild\ta\tchat\tclient\tand\tserver.\tIf\tyou\twere\tto\tuse\tAjax\tto\tbuild this,\tyou\twould\thave\tto\tjuggle\tat\tleast\ttwo\tconnections\t\u2013\tone\tto\tpoll\tfor\tnew\tmessages\tand a\tsecond\tto\tsend\tmessages.\tUsing\tWebSockets,\tyou\tcan\taccomplish\tthe\tsame\tthing\twith\ta single\tconnection. When\tyou\treach\tthe\tend\tof\tthe\tchapter,\tChattrbox\twill\tbe\table\tto\thandle\tmultiple,","simultaneous\tchat\tclients,\tsending\tnew\tmessages\talong\tto\teach\tclient\t(Figure\t16.2). Figure\t16.2\t\tPirate\tchat-arr Setting\tUp\tWebSockets To\tset\tChattrbox\tup\twith\tWebSockets,\tyou\tare\tgoing\tto: 1.\t install\tthe\tws\tmodule 2.\t create\ta\tWebSockets\tserver 3.\t add\tchat\tfunctionality\tto\tthe\tserver 4.\t accommodate\tnew\tusers\tby\thaving\tthe\tserver\tsend\tthem\tthe\tmessage\thistory Begin\tat\tthe\tbeginning. The\thttp\tmodule\tthat\tcomes\twith\tNode.js\tgives\tyou\ta\tsimple\tway\tto\tstart\tan\tHTTP\tserver","so\tthat\tbrowsers\tcan\ttalk\tto\tthe\tserver. Similarly,\tthe\tws\tmodule\tgives\tyour\tNode.js\tprograms\tan\teasy\tway\tto\tcommunicate\tvia WebSockets.\tThere\tare\tseveral\tmodules\tthat\timplement\tWebSockets\tfor\tNode.js,\tbut\tws\tis the\tstandard\timplementation\tand\tperforms\twell. Begin\tby\tinstalling\tthe\tws\tWebSockets\tmodule\tin\tyour\tChattrbox\tdirectory.\t(Do\tnot\tbe alarmed\tif\tyou\tsee\twarnings\tfrom\tnpm\tabout\ta\tmissing\tdescription\tor\trepository\tfield\tfor your\tproject.) npm\tinstall\t--save\tws Next,\tcreate\ta\tfile\tin\tChattrbox\u2019s\troot\tfolder\tnamed\twebsockets-server.js.\tAdd the\tfollowing\tcode\tto\timport\tthe\tws\tmodule\tand\tstart\tlistening. var\tWebSocket\t=\trequire('ws'); var\tWebSocketServer\t=\tWebSocket.Server; var\tport\t=\t3001; var\tws\t=\tnew\tWebSocketServer({ \t\t\t\tport:\tport }); console.log('websockets\tserver\tstarted'); You\timported\tthe\tws\tmodule\twith\tthe\trequire\tstatement.\tThe\tmodule\tcontains\ta\tServer property\tthat\tyou\twill\tneed\tin\torder\tto\tcreate\ta\tworking\tWebSockets\tserver. The\tcode\tvar\tws\t=\tnew\tWebSocketServer(\/*...*\/);\tdoes\tjust\tthat.\tWhen\tthis\truns,\tthe WebSockets\tserver\tis\testablished\tand\tbound\tto\tthe\tspecified\tport\t(here,\t3001). Unlike\tthe\tmodule\tyou\tcreated\tin\textract.js,\tyou\twill\tnot\tneed\ta\tmodule.exports assignment.\tThe\tcode\tin\twebsockets-server.js\twill\trun\twhen\tit\tis\timported.\tIt handles\tall\tinitialization\tand\tevents\trelated\tto\tthe\tWebSocket. Now\tthat\tyou\thave\ta\tWebSockets\tserver,\tthe\tfirst\tthing\tyou\twill\tdo\twith\tit\tis\thandle connections.\tIn\twebsockets-server.js,\testablish\ta\tcallback\tfor\tany\tconnection events\tfor\tyour\tWebSockets\tserver: var\tWebSocket\t=\trequire('ws'); var\tWebSocketServer\t=\tWebSocket.Server; var\tport\t=\t3001; var\tws\t=\tnew\tWebSocketServer({ \t\t\t\tport:\tport }); console.log('websockets\tserver\tstarted'); ws.on('connection',\tfunction\t(socket)\t{ \t\tconsole.log('client\tconnection\testablished'); }); The\tevent-handling\tsyntax\tis\tsimilar\tto\tjQuery\u2019s.\tYou\twill\tnotice\tthat\tmany\tJavaScript libraries\t(in\tNode\tand\tin\tthe\tbrowser)\tuse\tthis\tstyle. Your\tevent-handler\tcallback\taccepts\ta\tsingle\targument\tnamed\tsocket.\tWhen\ta\tclient makes\ta\tconnection\tto\tyour\tWebSockets\tserver,\tyou\thave\taccess\tto\tthat\tconnection\tvia\tthis socket\tobject. Before\tyou\twrite\tthe\tchat\tserver\tcode,\tyou\twill\tset\tup\tyour\tserver\tto\trepeat\tany\tmessages sent\tto\tit.\tThis\tis\tcommonly\tknown\tas\tan\techo\tserver. Add\tthe\techo\tfunctionality\tin\twebsockets-server.js\tby\tregistering\ta\tcallback\tfor any\tmessage\tevents\tgenerated\tby\tthe\tclient\tconnection. ... console.log('websockets\tserver\tstarted');","ws.on('connection',\tfunction\t(socket)\t{ \t\tconsole.log('client\tconnection\testablished'); \t\tsocket.on('message',\tfunction\t(data)\t{ \t\t\t\tconsole.log('message\treceived:\t'\t+\tdata); \t\t\t\tsocket.send(data); \t\t}); }); You\tregistered\tthe\tevent\thandler\tdirectly\ton\tthe\tsocket\tobject.\tYour\tmessage\tevent callback\tis\tpassed\tany\tinformation\tsent\tby\tthe\tclient.\tFor\tnow,\tyou\tare\tsimply\tsending\tit back\tto\tthe\tsame\tsocket\tconnection. You\twill\tsee\tthis\tin\taction\tin\tjust\ta\tmoment. You\tcould\trun\tyour\tWebSockets\tserver\tby\titself\twith\tthe\tcommand\tnode\twebsockets- server.js,\tbut\tit\tis\tjust\tas\teasy\tto\tconnect\tit\tto\tindex.js.\tThis\thas\tthe\tbenefit\tof\ttaking advantage\tof\tnodemon\tto\tautomatically\treload\tyour\tcode\twhen\tyou\tmake\tchanges\tto\teither websockets-server.js\tor\tindex.js. At\tthe\ttop\tof\tindex.js,\tadd\ta\trequire\tstatement\tto\timport\tthe\twebsockets-server module. var\thttp\t=\trequire('http'); var\tfs\t=\trequire('fs'); var\textract\t=\trequire('.\/extract'); var\twss\t=\trequire('.\/websockets-server'); ... Save\tyour\tfile\tand\tnodemon\twill\treload\tyour\tcode,\tmaking\tit\tready\tfor\tyou\tto\ttry\tout.","Testing\tYour\tWebSockets\tServer One\tway\tto\teasily\ttest\tyour\tserver\tis\tto\tuse\tthe\twscat\tnpm\tmodule.\twscat\tis\ta\ttool\tfor connecting\tto\tand\tcommunicating\twith\ta\tWebSockets\tserver.\tThe\tmodule\tprovides\ta command-line\tprogram\tthat\tyou\twill\tuse\tas\ta\tchat\tclient. Open\ta\tsecond\tterminal\twindow\tand\tinstall\twscat\tglobally.\tYou\tmay\tneed\tto\trun\tthis command\twith\tadministrator\tprivileges.\t(If\tyou\tneed\ta\trefresher,\trefer\tto\tChapter\t1.) npm\tinstall\t-g\twscat When\twscat\tis\tinstalled,\tyou\tare\tready\tto\tconnect\tto\tyour\tWebSockets\tserver. In\tthe\tsecond\tterminal\twindow,\trun\tthe\tcommand\twscat\t-c\tws:\/\/localhost:3001.\tYou should\tsee\tthe\tmessage\tconnected\t(press\tCTRL+C\tto\tquit)\tin\tthe\tsecond\tterminal window\tand\t'client\tconnection\testablished'\tin\tthe\tfirst\twindow. In\tthe\tsecond\tterminal\twindow,\tenter\tsome\ttext\tat\tthe\tprompt.\tEach\ttime\tyou\ttype\tsome text\tand\tpress\tthe\tReturn\tkey,\tyour\ttext\twill\tbe\trepeated\tby\tthe\tWebSockets\tserver,\tas shown\tin\tFigure\t16.3. Figure\t16.3\t\tTesting\tthe\tserver\twith\twscat Now\tthat\tyou\thave\tconfirmed\tthat\tyou\tare\table\tto\tcommunicate\twith\tyour\tserver\tvia WebSockets,\tit\tis\ttime\tto\tadd\tthe\treal\tfunctionality\tthat\twill\tpower\tChattrbox\u2019s\tchat system.","Creating\tthe\tChat\tServer\tFunctionality With\tyour\tWebSockets\tserver\tup\tand\trunning,\tyou\tare\tnow\tready\tto\tbuild\tout\tyour\tchat server.\tYour\tchat\tserver\tneeds\tto\tdo\ta\tfew\tthings: keep\ta\tlog\tof\tthe\tmessages\tsent\tso\tfar\tto\tthe\tserver broadcast\tolder\tmessages\tto\tnew\tpeople\tjoining\tthe\tchat broadcast\tnew\tmessages\tto\tall\tclients Keeping\ta\tlog\tof\tmessages\tas\tyour\tusers\tsend\tthem\tis\tnecessary\tin\torder\tto\tsend\tthe message\thistory\tto\tnew\tusers,\tso\tyou\twill\ttackle\tthat\tfirst. In\twebsockets-server.js,\tcreate\tan\tarray\tto\thold\ton\tto\tmessages. var\tWebSocket\t=\trequire('ws'); var\tWebSocketServer\t=\tWebSocket.Server; var\tport\t=\t3001; var\tws\t=\tnew\tWebSocketServer({ \t\t\t\tport:\tport }); var\tmessages\t=\t[]; console.log('websockets\tserver\tstarted'); ... If\tyou\twere\tcreating\ta\tmore\trobust\tchat\tsystem,\tyou\tmight\tstore\tyour\tmessages\tin\ta database.\tFor\tnow,\ta\tsimple\tarray\tis\tfine. Next,\tcall\tmessages.push(data)\tto\tadd\teach\tnew\tmessage\tto\tyour\tarray\tas\tit arrives. ... ws.on('connection',\tfunction\t(socket)\t{ \t\tconsole.log('client\tconnection\testablished'); \t\tsocket.on('message',\tfunction\t(data)\t{ \t\t\t\tconsole.log('message\treceived:\t'\t+\tdata); \t\t\t\tmessages.push(data); \t\t\t\tsocket.send(data); \t\t}); }); Just\tlike\tthat,\tyou\thave\tan\tarray\tof\tall\tthe\tmessages\tthat\thave\tbeen\treceived\tby\tyour\tchat server. The\tnext\tstep\tis\tto\tallow\tnew\tusers\tto\tsee\tall\tthe\tprevious\tmessages.\tUpdate\tthe connection\tevent\thandler\tin\twebsockets-server.js\tto\tsend\tout\tall\tthe\told\tmessages to\teach\tnew\tconnection\tas\tit\tarrives. ... ws.on('connection',\tfunction\t(socket)\t{ \t\tconsole.log('client\tconnection\testablished'); \t\tmessages.forEach(function\t(msg)\t{ \t\t\t\tsocket.send(msg); \t\t}); \t\tsocket.on('message',\tfunction\t(data)\t{ \t\t\t\tconsole.log('message\treceived:\t'\t+\tdata); \t\t\t\tmessages.push(data); \t\t\t\tsocket.send(data); \t\t}); }); As\tsoon\tas\ta\tconnection\tis\tmade,\tthe\tserver\titerates\tthrough\tthe\tmessages\tand\tsends\teach one\tto\tthe\tnew\tconnection.","The\tlast\tjob\tis\tto\tsend\tnew\tmessages\tto\tall\tthe\tusers\tas\teach\tnew\tmessage\tcomes\tin. WebSockets\tkeeps\ttrack\tof\tyour\tconnected\tusers\tfor\tyou.\tUse\tthis\tmechanism\tin websockets-server.js\tto\trebroadcast\tyour\treceived\tmessages. ... ws.on('connection',\tfunction\t(socket)\t{ \t\tconsole.log('client\tconnection\testablished'); \t\tmessages.forEach(function\t(msg)\t{ \t\t\t\tsocket.send(msg); \t\t}); \t\tsocket.on('message',\tfunction\t(data)\t{ \t\t\t\tconsole.log('message\treceived:\t'\t+\tdata); \t\t\t\tmessages.push(data); \t\t\t\tws.clients.forEach(function\t(clientSocket)\t{ \t\t\t\t\t\tclientSocket.send(data) \t\t\t\t}); \t\t\t\tsocket.send(data); \t\t}); }); The\tws\tobject\tkeeps\ttrack\tof\tall\tconnections\tvia\tits\tclients\tproperty.\tIt\tis\tan\tarray\tthat you\tcan\titerate\tthrough.\tIn\tyour\titerator\tcallback,\tyou\tonly\tneed\tto\tsend\tthe\tmessage\tdata. Finally,\tbecause\tyou\tend\tup\tsending\tyour\tmessage\tto\tyour\town\tsocket\twhen\tyou\titerate over\tall\tthe\tsockets,\tyou\tno\tlonger\tneed\tthe\tcall\tto\tsocket.send(data).\tDeleting\tit cleans\tthings\tup\tnicely.","First\tChat! Let\u2019s\ttest\tthe\tnew\tfunctionality.\tMake\tsure\tthat\tnodemon\thas\treloaded\tyour\tcode.\t(If\tyou need\tto,\tyou\tcan\tmanually\tstop\tnodemon\twith\tControl-C\tand\trestart\tit\twith\tnpm\trun\tdev.) Open\ta\tthird\tterminal\twindow\tand\trun\tthe\tcommand\twscat\t-c\thttp:\/\/localhost:3001. (You\tshould\thave\tone\tterminal\trunning\tnodemon\tand\ttwo\trunning\twscat.)\tEnter\tsome\tchat messages\tin\tthe\ttwo\twindows\tconnected\tto\tthe\tserver. After\tyou\thave\tchatted\twith\tyourself\tfor\ta\tbit,\topen\ta\tfourth\tterminal\tand\trun\twscat\t-c http:\/\/localhost:3001.\tThis\tchat\tclient\tshould\tbe\tsent\tall\tthe\tprevious\tmessages. If\teverything\twent\twell,\tyou\tshould\tsee\tsomething\tlike\tFigure\t16.4. Figure\t16.4\t\tChatting\twith\tsome\tfriends Congratulations!\tYou\thave\twritten\ta\tfully\tfunctional\tchat\tserver\tusing\tWebSockets\t\u2013\tand it\ttook\tless\tthan\ttwo\tdozen\tlines\tof\tJavaScript.","For\tthe\tMore\tCurious:\tsocket.io\tWebSockets\tLibrary The\tws\tnpm\tmodule\tis\ta\tperfectly\tfine\tWebSockets\timplementation.\tBut,\tadmittedly,\tit\tis lacking\tin\ta\tfew\tways.\tFor\texample,\tWebSockets\tconnections\tsometimes\tget\tdropped,\tbut the\tws\tmodule\tprovides\tno\tway\tto\tautomatically\treconnect. Another\tproblem\tis\tthat\tws\tlives\tin\tNode.js.\tThat\tmeans\tthat\tit\tis\tonly\tavailable\ton\tthe server.\tIn\tyour\tclient-side\tJavaScript,\tyou\twould\tneed\tto\tlearn\ta\ttotally\tdifferent\tlibrary that\taccomplishes\tessentially\tthe\tsame\ttask. On\ttop\tof\tthat,\ton\tthe\tclient\tside\tyou\tmay\thave\tadditional\tchallenges:\tWhat\tif\tyour browser\tis\told\tand\tdoes\tnot\tsupport\tWebSockets?\tYou\twould\tneed\tto\tprovide\ta\tfallback mechanism\tof\tsome\tkind. socket.io\t(socket.io)\tprovides\ta\tsolution\tto\tthese\tproblems.\tFor\tbrowsers,\tit\tprovides backward-compatible\tfallbacks,\tincluding\ta\tFlash\timplementation.\tIn\taddition,\tit\thas\tbeen ported\tto\ta\tnumber\tof\tother\tplatforms,\tincluding\tiOS\tand\tAndroid.","For\tthe\tMore\tCurious:\tWebSockets\tas\ta\tService If\tyou\tare\tinterested\tin\ta\treal-time\tplatform\tas\ta\tservice,\tyou\tmay\twant\tto\tlook\tinto firebase\t(www.firebase.com).\tIf\tsocket.io\ttries\tto\tmake\twriting\tyour\tserver simpler,\tfirebase\tgoes\tone\tstep\tfurther:\tIt\tprovides\tthe\tentire\tserver\tfor\tyou,\tincluding mechanisms\tfor\tclients\tto\tshare\tand\tsynchronize\tdata.\tfirebase\tprovides\tsolutions\tfor web,\tiOS,\tand\tAndroid.","Bronze\tChallenge:\tAm\tI\tRepeating\tMyself? Update\tyour\tmessage\thandler\tso\tthat\tevery\tmessage\treceived\tis\tsent\ttwice\tto\teach\tuser. Test\tit\tout\tusing\twscat\tand\tconfirm\tthat\teach\tmessage\tis\trepeated. For\ta\treally\tinteresting\teffect,\tincrement\tthe\tnumber\tof\trepetitions\tby\tone\teach\ttime\ta\tnew message\tis\tsent.","Silver\tChallenge:\tSpeakeasy In\tthe\tUnited\tStates,\tit\twas\tillegal\tto\tproduce\tor\tsell\talcohol\tduring\tthe\t1920s.\tIn\tresponse, \u201cspeakeasies\u201d\twere\tcreated:\tsecret\testablishments\tthat\tsold\talcoholic\tbeverages\tand required\ta\tpassword\tbefore\tpatrons\twere\tallowed\tto\tenter. Create\ta\tspeakeasy\tversion\tof\tyour\tchat\tprogram\t\u2013\tbut\twithout\tthe\talcohol.\tHide\tall messages\tfrom\ta\tuser\tuntil\tthey\tenter\ta\tsecret\tpassword.\t(\u201cSwordfish\u201d\tis\ta\tgood\tone,\tfor historical\treasons.) When\tusers\tenter\tthe\tpassword,\tsend\tthem\tall\tprevious\tmessages\tand\tallow\tthem\tto\tsee new\tmessages.","Gold\tChallenge:\tChat\tBot You\tused\tthe\tWebSocket.Server\tproperty\tto\tcreate\tthe\tchat\tserver.\tYou\tcan\talso programmatically\tcreate\ta\tchat\tclient\tby\tusing\tWebSocket\tas\ta\tconstructor. The\tfollowing\tline\tis\tan\texample: var\tchatClient\t=\tnew\tWebSocket('http:\/\/localhost:3001'); The\tdocumentation\tat\tgithub.com\/\u200bwebsockets\/\u200bws\thas\ta\tsimple\texample\tof sending\tand\treceiving\ttext\tdata. Create\ta\tchat\tbot\tthat\tautomatically\tconnects\tto\tthe\tchat\tserver.\tIt\tshould\tgreet\teach\tnew user\tbut\totherwise\tremain\tsilent\tunless\tdirectly\tspoken\tto.\tFor\texample,\tif\tyour\tchat\tbot responds\tto\tthe\tname\t\u201cJinx,\u201d\tyou\tcould\ttype\t\u201cJinx,\tput\tMax\tin\tspace,\u201d\tand\tyour\tchat\tbot would\trespond\tappropriately.\t(The\tappropriate\tresponse\tis\tup\tto\tyou.) Make\tsure\tthat\tthe\tcode\tfor\tyour\tchat\tbot\tis\tin\ta\tseparate\tmodule,\tnot\tbuilt\tdirectly\tinto\tthe chat\tserver\tcode.","17\t Using\tES6\twith\tBabel The\tJavaScript\tlanguage\twas\tcreated\tin\t1994,\treceived\ta\tfew\tupdates\tin\t1999,\tbut\twent unchanged\tfrom\t1999\tto\t2009.\tA\tset\tof\tsmall\tchanges\twas\tintroduced\tin\t2009,\tresulting\tin the\tversion\tof\tJavaScript\tknown\tas\tES5,\tor\tthe\tfifth\tedition\tof\tthe\tstandard. In\t2015,\ta\tnumber\tof\tlanguage\timprovements\twere\tadded\tas\tthe\tsixth\tedition\tof\tthe standard.\tMany\tof\tthese\tnew\tlanguage\tfeatures\twere\tinfluenced\tby\tlanguages\tlike\tRuby and\tPython.\tTechnically,\tthis\tsixth\tedition\tis\tnamed\tES2015,\tbut\tit\tis\tmore\tcommonly known\tas\tES6. ES6\tis\twell\tsupported\tby\tGoogle\tChrome,\tMozilla\tFirefox,\tand\tMicrosoft\tEdge.\tThese\tare evergreen\tbrowsers,\tmeaning\tthat\tthey\tself-update\twithout\tthe\tuser\tneeding\tto\tmanually download\tand\tinstall\tthe\tlatest\tversion.\tAs\tGoogle,\tMozilla,\tand\tMicrosoft\thave\tadded more\tand\tmore\tES6\tcompatibility\tto\ttheir\tbrowsers,\tthey\thave\tbeen\table\tto\troll\tout\tthese enhancements\tquickly\tto\ttheir\tusers. However,\tnon-evergreen\tand\tmost\tmobile\tbrowsers\thave\tpoor\tsupport\tfor\tES6. Figure\t17.1\tshows\tthe\tpercentage\tof\tES6\tfeatures\tsupported\tby\trecent\tversions\tof\tdesktop and\tmobile\tbrowsers.\t(In\tthe\tfigure,\tIE\t=\tInternet\tExplorer,\tFF\t=\tMozilla\tFirefox,\tCH\t=\tGoogle Chrome,\tSF\t=\tSafari,\tKQ\t=\tKonqueror,\tand\tAN\t=\tAndroid.)","Figure\t17.1\t\tES6\tfeature\tsupport\tas\tof\tspring\t2016 If\tyou\twould\tlike\ta\tcloser\tor\tup-to-date\tlook\tat\tbrowser\tsupport\tfor\tES6,\tgo\tto kangax.github.io\/c\u200b ompat-table\/\u200bes6\/\tto\tcheck\tout\tthe\tlatest\tinformation. The\ttable\u2019s\tcreator,\tJuriy\tZaytsev,\tupdates\tthe\tdata\tfrequently. Support\tmay\tbe\tspotty\tamong\tolder\tbrowsers,\tbut\twe\tlove,\tlove,\tlove\tES6.\tIt\tis\ta\tbeautiful thing,\tand\tit\tis\tworth\tthe\teffort\tto\tswitch\tover\tas\tsoon\tas\tpossible\trather\tthan\twaiting\tuntil support\tis\tuniversal. In\tthis\tchapter,\tyou\twill\tbegin\tworking\ton\tthe\tuser-facing\tportion\tof\tChattrbox,\twhich\tyou will\twrite\tusing\ta\tnumber\tof\tES6\tfeatures.\tTo\tmake\tsure\tyour\tapplication\tworks\ton\tall browsers,\tyou\twill\tuse\tthe\topen-source\ttool\tBabel\tto\ttake\tcare\tof\tcompatibility. There\tis\tone\titem\tof\thousekeeping\tto\ttake\tcare\tof\tbefore\tyou\tbegin.\tSo\tthat\tyou\tcan\tfocus on\tlearning\tES6\tand\tusing\tBabel,\tthe\tindex.html\tand\tstylesheets\/styles.css files\tfor\tChattrbox\tare\tprovided\tfor\tyou\tat\twww.bignerdranch.com\/\u200bdownloads\/\u200b front-end-dev-resources.zip.\tDownload\tthe\t.zip\tfile\tand\textract\tthe\tcontents (including\tthe\tentire\tstylesheets\/\tfolder)\tinto\tyour\tchattrbox\/app\tdirectory. (index.html\twill\treplace\tyour\texisting\tcopy\tof\tindex.html.) Also,\tone\tnote:\tWhile\tworking\tthrough\tthe\tcode\tin\tthis\tchapter,\tyou\tmay\tsee\ta\twarning\tin the\tconsole\tabout\tthe\tMIME\ttype\tof\tyour\tCSS\tfiles.\tIt\tis\tsafe\tto\tignore\tthis\twarning. Onward\tand\tupward!\tBy\tthe\tend\tof\tthis\tchapter,\tChattrbox\twill\tcommunicate\tover WebSockets\twith\tyour\tchat\tserver\t(Figure\t17.2).","Figure\t17.2\t\tChattrbox\tat\tthe\tend\tof\tthis\tchapter Tools\tfor\tCompiling\tJavaScript Babel\tis\ta\tcompiler.\tIts\tjob\tis\tto\ttranslate\tES6\tsyntax\tinto\tthe\tequivalent\tES5\tcode\tto\tbe\trun by\ta\tbrowser\u2019s\tJavaScript\tengine\t(Figure\t17.3). Figure\t17.3\t\tBuilding\tES5\tcode\tfrom\tES6\tfiles To\tuse\tBabel\teffectively,\tyou\twill\tneed\tto\tinstall\ta\tfew\tnpm\tmodules\tto\tcreate\tan\tautomated build\tprocess.\tYou\twill\tuse\tBabel\tto\tcompile\tyour\tES6\tcode\tto\tES5,\tBrowserify\tto\tbundle your\tmodules\ttogether\tinto\ta\tsingle\tfile,\tand\tBabelify\tto\tmake\tthe\ttwo\twork\ttogether.","Additionally,\tyou\twill\tuse\tWatchify\tto\ttrigger\tthe\tbuild\tprocess\tany\ttime\tyou\tsave\tchanges to\tyour\tcode\t(Figure\t17.4). Figure\t17.4\t\tCompilation\tworkflow First,\tyou\tneed\tto\tinstall\tBabel.\tIt\thas\ta\tfew\tdifferent\tmoving\tparts,\tdepending\ton\tyour needs.\tIn\tyour\tcase,\tyou\twill\tneed\tthe\tability\tto\tcompile\tin\ttwo\tways:\tfrom\tthe\tcommand line\tand\tprogrammatically.\tThe\ttools\tbabel-cli\tand\tbabel-core,\trespectively,\twill address\tthese\tneeds.\tYou\twill\talso\tneed\tto\tinstall\ta\tBabel\tconfiguration\tsuitable\tfor compiling\tthe\tES6\tstandard,\twhich\tis\tcalled\tbabel-preset-es2015. Run\tthe\tfollowing\tnpm\tcommands\tin\tyour\tchattrbox\tdirectory\tto\tinstall\tthe\tappropriate Babel\ttooling.\t(If\tyou\tneed\ta\trefresher\ton\thow\tto\trun\tnpm\tinstall\t-g\twith\tadministrator privileges,\trefer\tto\tChapter\t1.) npm\tinstall\t-g\tbabel-cli npm\tinstall\t--save-dev\tbabel-core npm\tinstall\t--save-dev\tbabel-preset-es2015 Now\tyou\tneed\tto\tconfigure\tBabel\tto\tcompile\tusing\tthe\tes2015\tpreset\tyou\tinstalled.\tCreate a\tfile\tcalled\t.babelrc\tin\tyour\troot\tchattrbox\tfolder\tand\tadd\tthe\tfollowing configuration\tinformation\tto\tit: { \t\t\\\"presets\\\":\t[ \t\t\t\t\\\"es2015\\\" \t\t], \t\t\\\"plugins\\\":\t[] } Finally,\tinstall\tBabelify,\tBrowserify,\tand\tWatchify\tto\tthe\tchattrbox\/node_modules\/ directory: npm\tinstall\t--save-dev\tbrowserify\tbabelify\twatchify You\twill\tbe\tusing\tthese\tthree\ttools\tlater\tin\tthis\tchapter,\tafter\tyou\thave\tBabel\tup\tand running.","The\tChattrbox\tClient\tApplication You\talready\tbuilt\tthe\tChattrbox\tserver,\twhich\tserves\tout\tthe\tstatic\tfiles\tand\thandles communication\tover\tWebSockets.\tThe\tclient\tapplication\twill\tsend\tand\treceive\tmessages to\tand\tfrom\tthe\tserver\tover\tWebSockets.\tIt\twill\tdefine\ta\tformat\tfor\tindividual\tmessages. The\tuser\twill\tbe\table\tto\tview\tthe\tmessages\tin\ta\tlist\tas\twell\tas\tcreate\tnew\tmessages\tby entering\ttext\tinto\ta\tform. Those\tresponsibilities\twill\tbe\thandled\tby\tthree\tmodules: the\tws-client\tmodule\twill\tmanage\tthe\tWebSockets\tcommunication\tfor\tthe client the\tdom\tmodule\twill\tdisplay\tdata\tto\tthe\tUI\tand\thandle\tform\tsubmissions the\tapp\tmodule\twill\tdefine\tthe\tstructure\tof\tmessages\tand\tpass\tmessages\tbetween ws-client\tand\tdom Figure\t17.5\tdiagrams\tthe\trelationships\tamong\tthe\tthree\tmodules. Figure\t17.5\t\tChattrbox\tapplication\tmodules In\tyour\tchattrbox\/app\tfolder,\tcreate\tscripts,\tscripts\/dist,\tand scripts\/src\tsubfolders,\tas\tshown\tin\tFigure\t17.6.","Figure\t17.6\t\tchattrbox\/app\tfolder\tstructure Now,\tcreate\tfour\tJavaScript\tfiles\tin\tscripts\/src: app.js dom.js main.js ws-client.js Your\tfile\tstructure\tshould\tlook\tlike\tFigure\t17.7. Figure\t17.7\t\tchattrbox\/app app.js,\tdom.js,\tand\tws-client.js\tcorrespond\tto\tthe\tmodules\tshown\tin Figure\t17.5.\tThe\tmain.js\tfile\twill\tcontain\tthe\tcode\tthat\tinitializes\tyour\tapplication.","First\tSteps\twith\tBabel Now\tthat\tyou\thave\tyour\ttools\tinstalled\tand\tyour\tfiles\tin\tplace,\tit\tis\ttime\tto\tget\tstarted\twith ES6. For\tthe\tmoment,\tyou\twill\tuse\tBabel\tfrom\tthe\tcommand\tline.\tLater,\tyou\twill\tadd\tit\tto\tyour npm\tscripts\tso\tthat\tcompilation\thappens\tautomatically.\tThat\tway,\tas\tyou\twork\twith\tnew ES6\tfeatures\tyou\tcan\tfocus\ton\tthe\tnew\tsyntax\twithout\tworrying\tabout\trunning\textra commands\tin\tthe\tterminal. Class\tsyntax The\tfirst\tES6\tfeature\tyou\twill\tuse\tfor\tbuilding\tthe\tChattrbox\tclient\tis\tthe\tclass\tkeyword. It\tis\timportant\tto\tkeep\tin\tmind\tthat\tthe\tES6\tclass\tkeyword\tdoes\tnot\twork\texactly\tlike classes\tin\tother\tprogramming\tlanguages.\tInstead,\tES6\tclasses\tmerely\tprovide\ta\tshorter syntax\tfor\tconstructor\tfunctions\tand\tprototype\tmethods. Open\tapp.js\tand\tdefine\ta\tnew\tclass\tcalled\tChatApp. class\tChatApp\t{ } In\tthis\tchapter,\tChatApp\twill\tnot\tdo\tmuch.\tUltimately,\tthough,\tChatApp\twill\tbe responsible\tfor\tmost\tof\tyour\tapplication\tlogic. The\tdefinition\tof\tthe\tclass\tis\tcurrently\tempty.\tAdd\ta\tconstructor\tmethod\twith\ta console.log\tstatement: class\tChatApp\t{ \t\tconstructor()\t{ \t\t\t\tconsole.log('Hello\tES6!'); \t\t} } constructor\tis\ta\tmethod\tthat\tis\trun\tany\ttime\tyou\tcreate\ta\tnew\tinstance\tof\ta\tclass. Usually,\tthe\tconstructor\twill\tset\tvalues\tfor\tproperties\tbelonging\tto\tthe\tinstance. Next,\tcreate\tan\tinstance\tof\tChatApp\tin\tapp.js,\tright\tafter\tthe\tclass\tdeclaration: class\tChatApp\t{ \t\tconstructor()\t{ \t\t\t\tconsole.log('Hello\tES6!'); \t\t} } new\tChatApp(); Let\u2019s\tgive\tyour\tcode\ta\ttest\trun.\tOpen\ta\tsecond\tterminal\twindow\tand\tswitch\tto\tChattrbox\u2019s root\tdirectory,\twhere\tpackage.json,\tindex.js,\tand\tapp\/\tlive.\tYou\twill\tuse\tthis window\tto\trun\tyour\tbuild\tcommands\tand\tkeep\tthe\tother\tone\topen\tfor\trunning\tyour\tserver. To\ttest\tyour\tcode,\tuse\tBabel\tto\tcompile\tapp\/scripts\/src\/app.js\tand\toutput\tthe result\tto\tapp\/scripts\/dist\/main.js: babel\tapp\/scripts\/src\/app.js\t-o\tapp\/scripts\/dist\/main.js If\tyou\tdo\tnot\tsee\tanything\thappen\tin\tyour\tterminal,\tthat\tis\tnormal\t\u2013\tand\tgood\tnews.\tBabel will\tnot\treport\tanything\ton\tthe\tcommand\tline\tunless\tthere\tis\tan\terror\t(Figure\t17.8).","Figure\t17.8\t\tBabel\tworks\tquietly Make\tsure\tyour\tNode\tserver\tis\trunning\tin\tyour\tother\tterminal\t(with\tnpm\trun\tdev),\tand open\tyour\tbrowser\tto\thttp:\/\/localhost:3000.\tNow\tyou\twill\tsee\tyour\tresults (Figure\t17.9). Figure\t17.9\t\tHello,\tES6! Your\tapp\/index.html\tsources\tthe\tmain.js\tyou\tgenerated\tfrom\tapp.js.\tAnd because\tapp.js\tcreates\ta\tnew\tChatApp,\tthe\tcode\tin\tChatApp\u2019s\tconstructor\tis\trun, logging\tout\t\u201cHello\tES6!\u201d Now\tthat\tyou\thave\tconfirmed\tthat\tBabel\tis\tworking\twith\ta\tsingle\tJavaScript\tfile,\tit\tis\ttime to\tstart\tworking\twith\tmultiple\tmodules.","Using\tBrowserify\tfor\tPackaging\tModules One\tthing\tthat\tES5\tdoes\tnot\thave\tis\ta\tbuilt-in\tmodule\tsystem.\tWhen\tyou\tbuilt\tCoffeeRun, you\tused\ta\tworkaround\tthat\tlet\tyou\twrite\tmodular\tcode\t\u2013\tbut\tdepended\ton\tmodifying\ta global\tvariable. ES6\tprovides\ttrue\tmodules,\tlike\tthose\tin\tmany\tother\tprogramming\tlanguages.\tBabel understands\tES6\tmodule\tsyntax,\tbut\tthere\tis\tno\tequivalent\tES5\tcode\tfor\tit\tto\tconvert\tto. That\tis\twhy\tBrowserify\tis\tnecessary. Figure\t17.10\tshows\thow\tBrowserify\tand\tBabel\twill\twork\ttogether. Figure\t17.10\t\tConverting\tfrom\tES6\tmodules\tto\tES5\tmodules\twith\tBabel\tand Browserify By\tdefault,\tBabel\tconverts\tES6\tmodule\tsyntax\tinto\tthe\tequivalent\tNode.js-style\trequire\tand module.exports\tsyntax.\tBrowserify\tthen\tconverts\tNode.js\tmodule\tcode\tinto\tES5-friendly functions. Open\tpackage.json\tand\tadd\ta\tconfiguration\tsection\tfor\tBrowserify: ... \t\t\\\"scripts\\\":\t{ \t\t\t\t\\\"test\\\":\t\\\"echo\t\\\\\\\"Error:\tno\ttest\tspecified\\\\\\\"\t&& \t\t\t\t\texit\t1\\\", \t\t\t\t\\\"start\\\":\t\\\"node\tindex.js\\\", \t\t\t\t\\\"dev\\\":\t\\\"nodemon\tindex.js\\\", \t\t}, \t\t\\\"browserify\\\":\t{ \t\t\t\t\\\"transform\\\":\t[ \t\t\t\t\t\t[\\\"babelify\\\",\t{\\\"presets\\\":\t[\\\"es2015\\\"],\t\\\"sourceMap\\\":\ttrue}] \t\t\t\t] \t\t}, ... This\ttells\tBrowserify\tto\tuse\tBabelify\tas\ta\tplug-in.\tIt\tpasses\ttwo\toptions\tto\tBabelify:\tFirst,\tit activates\tthe\tES2015\tcompiler\toption.\tIt\talso\tturns\ton\tthe\tsourceMap\toption,\twhich\thelps with\tdebugging.\tYou\twill\tlearn\thow\tto\tdebug\twith\tsource\tmaps\tas\tyou\tbuild\tthe\trest\tof Chattrbox. You\twill\talso\twant\tto\twrite\tsome\tscripts\tfor\tcommon\tBrowserify\ttasks,\tas\tyou\tdid\tfor nodemon.\tWrite\tthose\tin\tyour\t\\\"scripts\\\"\tsection\tin\tpackage.json.\t(Remember\tto\tadd the\tcomma\tat\tthe\tend\tof\t\\\"dev\\\":\t\\\"nodemon\tindex.js\\\".) ... \t\t\\\"scripts\\\":\t{ \t\t\t\t\\\"test\\\":\t\\\"echo\t\\\\\\\"Error:\tno\ttest\tspecified\\\\\\\"\t&&\texit\t1\\\", \t\t\t\t\\\"start\\\":\t\\\"node\tindex.js\\\", \t\t\t\t\\\"dev\\\":\t\\\"nodemon\tindex.js\\\", \t\t\t\t\\\"build\\\":\t\\\"browserify\t-d\tapp\/scripts\/src\/main.js\t-o\tapp\/scripts\/dist\/main.js\\\", \t\t\t\t\\\"watch\\\":\t\\\"watchify\t-v\t-d\tapp\/scripts\/src\/main.js\t-o\tapp\/scripts\/dist\/main.js\\\" \t\t},","\\\"browserify\\\":\t{ \t\t\t\t\\\"transform\\\":\t[ \t\t\t\t\t\t[\\\"babelify\\\",\t{\\\"presets\\\":\t[\\\"es2015\\\"],\t\\\"sourceMap\\\":\ttrue}] \t\t\t\t] \t\t}, ... The\tfirst\tscript,\tbuild,\tuses\tthe\tbrowserify\tcommand\tdirectly.\tThe\tsecond\tscript,\twatch, uses\twatchify\tto\trerun\tbrowserify\twhenever\tyour\tcode\tchanges.\t(It\tserves\ta\tsimilar purpose\tto\tnodemon.) Now\tto\tuse\tthe\tES6\tmodule\tsystem.\tIn\tES6\tmodules,\tyou\tmust\texplicitly\texport\tthe\tpieces of\tyour\tmodule\tyou\twant\tothers\tto\tuse.\tUpdate\tapp.js\tto\texport\tyour\tChatApp\tclass rather\tthan\tsimply\tcreating\tan\tinstance. class\tChatApp\t{ \t\tconstructor()\t{ \t\t\t\tconsole.log('Hello\tES6!'); \t\t} } new\tChatApp(); export\tdefault\tChatApp; You\tspecified\tthat\tChatApp\tis\tthe\tdefault\tvalue\tavailable\tfrom\tthe\tapp\tmodule.\tSome\tof your\tother\tmodules\twill\texport\tmultiple\tvalues.\tWhen\tyou\tonly\tneed\tto\texport\ta\tsingle value,\tit\tis\tbest\tto\tuse\texport\tdefault. In\tmain.js,\timport\tthe\tChatApp\tclass\tand\tcreate\ta\tnew\tinstance\tof\tit. import\tChatApp\tfrom\t'.\/app'; new\tChatApp(); main.js\tis\timporting\tthe\tChatApp\tclass\tthat\tapp.js\texported.\tAfter\tthe\timport,\tyou create\ta\tnew\tinstance\tof\tthe\tChatApp\tclass. One\timportant\tnote\there:\tThe\tname\tChatApp\tis\tnot\tsignificant\tin\tmain.js.\tBecause ChatApp\tis\tthe\tdefault\texport\tfrom\tapp.js,\twriting\timport\tMyChatApp\tfrom\t'.\/app', for\texample,\twould\tassign\tthe\tdefault\texport\tvalue\tto\tthe\tlocal\tMyChatApp\tname. Naming\tit\tChatApp\tis\ta\tbest\tpractice,\thowever,\tbecause\tthat\tis\tits\tname\tinside\tapp.js. Running\tthe\tbuild\tprocess Next,\tgo\tto\tyour\tterminal\tand\trun\tyour\tbuild\tscript: npm\trun\tbuild npm\twill\trun\tyour\tbuild\tcommand,\twhich\twill\tthen\trun\tbrowserify.\tAs\tit\truns\teach command,\tit\twill\tshow\tyou\twhat\tit\tis\tdoing.\tBrowserify\titself\twill\tbe\tsilent,\tthough,\tunless there\tis\tan\terror\t(Figure\t17.11).","Figure\t17.11\t\tRunning\tBrowserify\tvia\tnpm\trun\tbuild When\tBrowserify\tis\tsuccessful,\tit\twill\tpackage\tyour\ten-Babeled\tmain.js\tin\tyour app\/dist\/\tfolder,\tjust\tas\tyou\tdid\tearlier\tby\thand. Now\tto\treload\tyour\tbrowser\tand\tsee\tthe\toutput.\tYou\tadded\tno\tnew\tfunctionality,\tother than\trestructuring\twhere\tthe\tcall\tto\tChatApp\u2019s\tconstructor\tlives.\tSo\tyou\tshould\tsee\tthe same\tmessage\tin\tthe\tconsole\tthat\tyou\tsaw\tearlier\t(Figure\t17.12). Figure\t17.12\t\tHello\tagain! The\tnext\tpiece\tto\tintegrate\tis\tWatchify.\tWatchify\twill\tdo\tthe\tsame\tthing\tfor\trunning\tyour Browserify\tbuild\tthat\tnodemon\tdid\tfor\trunning\tyour\tNode.js\tserver:\tIt\twill\tautomatically trigger\ta\trebuild\tevery\ttime\tyou\tmodify\tone\tof\tyour\tsource\tfiles. Start\tWatchify\tto\tkick\toff\tthe\tbuild\tprocess\tany\ttime\tyou\tmake\ta\tchange\tto\tyour\tcode: npm\trun\twatch Watchify\twill\tconfirm\tthat\tit\tis\trunning\t(Figure\t17.13).","Figure\t17.13\t\tRunning\tWatchify\tvia\tnpm\trun\twatch Watchify\tis\ta\tbit\tchattier\tthan\tBrowserify.\tEvery\ttime\tit\truns\tBrowserify,\tit\ttells\tyou\thow\tmany bytes\tit\twrote\tto\tthe\tfile.\tThat\tis\tnot\tterribly\tinteresting,\tbut\tit\tdoes\ttell\tyou\twhen\tthe output\tchanges.\tLeave\tWatchify\trunning\tin\tone\tof\tyour\tterminals\tas\tyou\tcontinue\tto\twork on\tChattrbox.\t(Your\tserver\tshould\tstill\tbe\trunning\tin\tthe\tother\tterminal.)","Adding\tthe\tChatMessage\tClass Chatting\tbetween\ttwo\tterminals\tis\tfun\t(and\tmakes\tyou\tlook\tcool\tat\tthe\tcoffee\tshop),\tbut\tit is\ttime\tto\tupgrade\tto\tsending\tmessages\tfrom\tbrowser\tto\tbrowser.\tYou\twill\twrite\ta\thelper class\tthat\thandles\tconstructing\tand\tformatting\tmessage\tdata. There\tare\tthree\tpieces\tof\tinformation\tyou\twill\twant\tto\ttrack\tfor\teach\tmessage.\tYou\tneed\tto know\tthe\ttext\tof\tthe\tmessage,\twho\tsent\tit,\tand\tat\twhat\ttime. JavaScript\tObject\tNotation\t\u2013\tmore\tcommonly\tknown\tas\tJSON\t(pronounced\t\u201cJason,\u201d\tper creator\tDouglas\tCrockford)\t\u2013\tis\ta\tlightweight\tdata-interchange\tformat.\tYou\thave\talready been\tusing\tJSON\tfor\tyour\tpackage.json\tfile.\tIt\tis\thuman\treadable\tand\tlanguage independent,\tand\tit\tis\tideal\tfor\tsending\tand\treceiving\tthe\tkind\tof\tdata\tyou\twant\tto exchange\twith\tChattrbox. Here\tis\ta\tsample\tmessage\tformatted\tas\tJSON: { \t\t\\\"message\\\":\t\\\"I'm\tBatman\\\", \t\t\\\"user\\\":\t\\\"batman\\\", \t\t\\\"timestamp\\\":\t614653200000 } Chattrbox\tmessage\tdata\twill\tcome\tfrom\ttwo\tdifferent\tsources.\tOne\tsource\tis\tin\tthe\tclient, when\tthe\tuser\tfills\tout\tthe\tform.\tThe\tother\tsource\tis\tthe\tserver,\twhen\tthe\tmessage\tis\tsent over\ta\tWebSocket\tconnection\tto\tother\tclients. When\tmessage\tdata\tcomes\tfrom\tthe\tform,\tyou\twill\tneed\tto\tadd\tthe\tusername\tand timestamp\tbefore\tsending\tit\tto\tthe\tserver.\tWhen\tthe\tdata\tcomes\tfrom\tthe\tserver,\tall\tthree pieces\tof\tinformation\tshould\tbe\tincluded.\tHow\tshould\tyou\thandle\tthis\tdiscrepancy?\tThere are\ta\tnumber\tof\toptions.\tLet\u2019s\tbriefly\tlook\tat\ta\tfew\tof\tthem,\tincluding\tsome\tthat\ttake advantage\tof\tsome\thandy\tES6\tfeatures. Create\ta\tclass\tto\trepresent\tindividual\tchat\tmessages\tin\tapp.js. class\tChatApp\t{ \t\tconstructor()\t{ \t\t\t\tconsole.log('Hello\tES6!'); \t\t} } class\tChatMessage\t{ \t\tconstructor(data)\t{ \t\t} } export\tdefault\tChatApp; The\tfirst\tway\tto\tapproach\tthe\tproblem\tis\ta\tsimple\tconstructor\tthat\taccepts\tthe\tmessage text,\tusername,\tand\ttimestamp.\t(Do\tnot\tmake\tthis\tchange\tin\tyour\tfile.\tIt\tis\tonly\tan example.) ... class\tChatMessage\t{ \t\tconstructor(message,\tuser,\ttimestamp)\t{ \t\t\t\tthis.message\t=\tmessage; \t\t\t\tthis.user\t=\tuser\t||\t'batman'; \t\t\t\tthis.timestamp\t=\ttimestamp\t||\t(new\tDate()).getTime(); \t\t} } ... You\thave\tseen\tthis\tpattern\ta\tnumber\tof\ttimes.\tYou\tassign\tthe\tparameter\tvalues\tto\tinstance properties,\tproviding\tfallbacks\tfor\tusername\tand\ttimestamp\tusing\tthe\t||\toperator.","This\tis\tfine,\tbut\tES6\tgives\tyou\ta\tmore\tcompact\tway\tto\twrite\tthis\tsame\tpattern\tusing default\targuments. ... class\tChatMessage\t{ \t\tconstructor(message,\tuser='batman',\ttimestamp=(new\tDate()).getTime())\t{ \t\t\t\tthis.message\t=\tmessage; \t\t\t\tthis.user\t=\tuser; \t\t\t\tthis.timestamp\t=\ttimestamp; \t\t} } ... This\tsyntax\tmakes\tit\tobvious\twhich\tvalues\tmust\tbe\tpassed\tin\tand\twhich\tones\tare\toptional. You\tcan\tsee\tthat\tonly\tthe\tmessage\targument\tis\tmandatory.\tThe\tothers\thave\tdefaults. This\tversion\tof\tthe\tconstructor\tcan\thandle\tmessages\treceived\tfrom\tthe\tserver\tor\tcreated\tby the\tform.\tBut\tit\trequires\tthat\tthe\tcaller\tknow\tthe\torder\tof\targuments,\twhich\tcan\tget cumbersome\tfor\tfunctions\tand\tmethods\tthat\thave\tthree\tor\tmore\targuments. An\talternative\tto\tthis\tis\tfor\tthe\tconstructor\tto\treceive\ta\tsingle\tobject\tas\targument,\twith\tthe key\/value\tpairs\tspecifying\tthe\tvalues\tfor\tmessage,\tuser,\tand\ttimestamp.\tFor\tthat, you\tcan\tuse\tthe\tdestructuring\tassignment\tsyntax. ... class\tChatMessage\t{ \t\tconstructor({message:\tm,\tuser:\tu,\ttimestamp:\tt})\t{ \t\t\t\tthis.message\t=\tm; \t\t\t\tthis.user\t=\tu; \t\t\t\tthis.timestamp\t=\tt; \t\t} } ... Destructuring\tmay\tlook\ta\tlittle\todd,\tbut\there\tis\thow\tit\tworks.\tYou\tcall\tthe\tconstructor\tlike this: new\tChatMessage({message:\t'hello\tfrom\tthe\toutside', \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tuser='[email protected]',\ttimestamp=1462399523859}); The\tdestructuring\tsyntax\tlooks\tfor\tthe\tkey\tmessage\tin\tthe\targument.\tIt\tfinds\tthe\tvalue 'hello\tfrom\tthe\toutside'\tand\tassigns\tit\tto\ta\tnew\tlocal\tvariable\tm.\tThis\tvariable\tcan then\tbe\tused\tinside\tthe\tbody\tof\tthe\tconstructor.\tThe\tsame\tthing\thappens\tfor\tthe username\tand\ttimestamp\tproperties. But\twith\tthis\tsyntax\tyou\tlose\tthe\tconvenience\tof\tthe\tdefault\tparameters.\tLuckily,\tyou\tcan combine\tthe\ttwo\ttechniques.\tThis\tfinal\tversion\tof\tthe\tconstructor\tis\tthe\tone\tyou\tshould add\tto\tapp.js: ... class\tChatMessage\t{ \t\tconstructor(data){ \t\t\t\tmessage:\tm, \t\t\t\tuser:\tu='batman', \t\t\t\ttimestamp:\tt=(new\tDate()).getTime() })\t{ \t\t\t\tthis.message\t=\tm; \t\t\t\tthis.user\t=\tu; \t\t\t\tthis.timestamp\t=\tt; \t\t} } ... In\tthis\tversion,\tyou\tare\tplucking\tvalues\tout\tof\tthe\tobject\tthat\tis\tpassed\tto\tthe\tconstructor. For\tany\tvalues\tthat\tdo\tnot\texist,\tdefaults\tare\tprovided. While\tdefault\targuments\tcan\tonly\texist\tas\tpart\tof\ta\tfunction\t(or\tconstructor)\tdefinition, destructuring\tcan\tbe\tused\tas\tpart\tof\tan\tassignment.\tYou\tmight\talso\twrite\tthe\tconstructor like\tthis: ..."]
Search
Read the Text Version
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
- 220
- 221
- 222
- 223
- 224
- 225
- 226
- 227
- 228
- 229
- 230
- 231
- 232
- 233
- 234
- 235
- 236
- 237
- 238
- 239
- 240
- 241
- 242
- 243
- 244
- 245
- 246
- 247
- 248
- 249
- 250
- 251
- 252
- 253
- 254
- 255
- 256
- 257
- 258
- 259
- 260
- 261
- 262
- 263
- 264
- 265
- 266
- 267
- 268
- 269
- 270
- 271
- 272
- 273
- 274
- 275
- 276
- 277
- 278
- 279
- 280
- 281
- 282
- 283
- 284
- 285
- 286
- 287
- 288
- 289
- 290
- 291
- 292
- 293
- 294
- 295
- 296
- 297
- 298
- 299
- 300
- 301
- 302
- 303
- 304
- 305
- 306
- 307
- 308
- 309
- 310
- 311
- 312
- 313
- 314
- 315
- 316
- 317
- 318
- 319
- 320
- 321
- 322
- 323
- 324
- 325
- 326
- 327
- 328
- 329
- 330
- 331
- 332
- 333
- 334
- 335
- 336
- 337
- 338
- 339
- 340
- 341
- 342
- 343
- 344
- 345
- 346
- 347
- 348
- 349
- 350
- 351
- 352
- 353
- 354
- 355
- 356
- 357
- 358
- 359
- 360
- 361
- 362
- 363
- 364
- 365
- 366
- 367
- 368
- 369
- 370
- 371
- 372
- 373
- 374
- 375
- 376
- 377
- 378
- 379
- 380
- 381
- 382
- 383
- 384
- 385
- 386
- 387
- 388
- 389
- 390
- 391
- 392
- 393
- 394
- 395
- 396
- 397
- 398
- 399
- 400
- 401
- 402
- 403
- 404
- 405
- 406
- 407
- 408
- 409
- 410
- 411
- 412
- 413
- 414
- 415
- 416
- 417
- 418
- 419
- 420
- 421
- 422
- 423
- 424
- 425
- 426
- 427
- 428
- 429
- 430
- 431
- 432
- 433
- 434
- 435
- 436
- 437
- 438
- 439
- 440
- 441
- 442
- 443
- 444
- 445
- 446
- 447
- 448
- 449
- 450
- 451
- 452
- 453
- 454
- 455
- 456
- 457
- 458
- 459
- 460
- 461
- 462
- 463
- 464
- 465
- 466
- 467
- 468
- 469
- 470
- 471
- 472
- 473
- 474
- 475
- 476
- 477
- 478
- 479
- 480
- 481
- 482
- 483
- 484
- 485
- 486
- 487
- 488
- 489
- 490
- 491
- 492
- 493
- 494
- 495
- 496
- 497
- 498
- 499
- 500
- 501
- 502
- 503
- 504
- 505
- 506
- 507
- 508
- 509
- 510
- 511
- 512
- 513
- 514
- 515
- 516
- 517
- 518
- 519
- 520
- 521
- 522
- 523
- 524
- 525
- 526
- 527
- 528
- 529
- 530
- 531
- 532
- 533
- 534
- 535
- 536
- 537
- 538
- 539
- 540
- 541
- 542
- 543
- 544
- 545
- 546
- 547
- 548
- 549
- 550
- 551
- 552
- 553
- 554
- 555
- 556
- 557
- 558
- 559
- 560
- 561
- 562
- 563
- 564
- 565
- 566
- 567
- 568
- 569
- 570
- 571
- 572
- 573
- 574
- 575
- 576