📜 ⬆️ ⬇️

Some subtleties of working with Github and NPM - with taste of ES6

Hello, my name is Alexander, and I write bicycles on weekends a programmer.



In our club of anonymous cyclists is considered a special chic not only to create another masterpiece, but also to share it with the community. Since there is simply a huge amount of articles on how to lay out a project on Github or npm, I will not retell the same thing 100,500 times.
')
In today's article, I want to highlight some non-obvious subtleties that may help you get more pleasure from the artistic sawing process with the jigsaw of the next bike.

Warning number 1 : I never consider myself to be the ultimate truth, and all of the following is just my private (possibly erroneous) opinion. If you know how best - please in the comments. Together and bicycles do more fun, and the truth is easier to install.

Warning number 2 : I will assume that the reader is familiar with the command line, Git and npm.

This season, it has become especially fashionable to write on ECMAScript 6, which, I recall, on February 20, reached the status of a release candidate , so let's mentally assume that we will also be sprinkling our netlenku on it.

We assume that we have already created a new folder, and run the npm init command in it, and thus created the package.json file.

Consider the structure of the project


In general, ES.next support in browsers, that node.js / io.js is still incomplete, I would even say fragmentary. So we still have two ways: either not to use all the features of ES.next, but only those that are supported by the target platform, or to use a transcompiler ( aside: no, well, how else to translate the transpiler ?! ).

Of course, as enthusiasts, we want to use the latest features of ES6 / 7, so we will use the second option.

Of all the transcompilators, Babel (formerly 6to5) supports the largest number of features, that's the proof , so we'll use it.

Since we are using a transcompiler, the basic structure of the project is already defined.

In the src folder, we will store the source code on a beautiful ES6 (and show it in GitHub), and the lib folder will contain ugly generated code.

Accordingly, in Git we will store the src folder, and in npm we will lay out lib . We will achieve the desired effect by using the magic files, .gitignore and .npmignore . In the first one, respectively, we add the lib folder, and in the second folder src .

Now, finally, add Babel to the project:

 npm i -D babel 


And teach npm to compile our sources. Climb into the package.json file and add the following:

  { /*  */ "scripts": { "compile": "babel --experimental --optional runtime -d lib/ src/", "prepublish": "npm run compile" } /*   */ } 

Who is there on what is happening here?

The first script, which is run by the npm run compile command, takes our sources from the src folder, converts it into good old JS, and puts it in lib daddy. Preserving the structure of subfolders and file names.

Important : Please note that, despite the fact that Babel is installed locally in the project and not added to my $PATH , npm is smart enough to understand that I actually ask him to do the following:

 node ./node_modules/babel/bin/babel/index.js --experimental --optional runtime -d lib/ src/ 

I articulate again: do not, do not install global packages. Install packages only locally, as project dependencies, and call them via npm run [script-name] .

Even more important : please pay attention to two flags: - --experimental , which includes support for ES7 features (such as async/await syntax), and the second one, which is worth discussing in more detail.

Babel itself is a translator from ES6 to ES5. All that he can not do, he does not. So, he is not soared about some features that can be easily entered into ES5 using polyfill'ov. For example, support for Promise, Map, and Set may well be organized at the Polyfill level.

Using the second flag, Babel adds the require command of the babel/runtime module to the generated code, which, unlike babel/polyfill , does not pollute the global namespace. A little more about the features of the work of babel/runtime can be found on the official website , as well as in the comments of a respected rock .

If you are writing a project under Node.js / Browserify / Webpack, then you just need to add the babel/runtime to the dependencies. Like this:

  npm i -S babel-runtime 

If your nettle will work in the browser, and you are using not CommonJS, but AMD, then you need to remove this flag from the compilation command, and add it to the project babel-polyfill.js in the way that is convenient for you.

The second script is run by npm itself when the package is published, and thus, the lib folder will always contain the latest content.

Finally, let's write code


Let's finally have our grab pens attached to writing the coveted code on ES.next. Create a file in the src folder person.es6.js . Why [basename].es6.js ? Because in Github, the ES6 / 7 syntax highlighting is turned on if the file is named according to the [basename].es6 or [basename].es6.js . Personally, I like the latter option more, so I use it.

So, the code ./src/person.es6.js :

  export default class Person { constructor(name) { if (name.indexOf(' ') !== -1) { [this.firstName, this.surName] = name.split(' '); } else { this.firstName = name; this.surName = ''; } } get fullName() { return `${this.firstName} ${this.surName}`; } set fullName(fullName) { [this.firstName, this.surName] = fullName.split(' '); } } 

Let us pretend that this dissociated class is the goal for which we bother with ES.next. Let's make it the main package.json in package.json :

  { /*  */ "main": "lib/person.es6.js" /*  */ } 

Please note that the main directive does not point to the original code at ./src/person.es6.js , but to its reflection generated by Babel. Thus, users of our library who do not use ES.next and Babel in their project will be able to work with our package, as if it were written in ordinary ES5.

In general, the scheme is quite old, and well-known for CoffeeScript lovers, as well as those who wrote JS in one of the ~ 370 (o_O) ways in which 200+ (o_O) languages ​​are transposed into JavaScript.

Testing


Before we proceed to discussing the testing of our project, let's discuss the following question: Should we write tests on ES6, or still on the good old ES5? And for that, and for another option, you can bring a lot of arguments. Personally, I think that everything is simple: if we plan to use our package in another project written on ES6, then the tests should be written on ES6. If we post the finished product to the npm ecosystem, then it should be able to interact with ES5, so it would be quite useful if the code generated by Babel is tested.

I propose to complicate the assumption that we are writing a utility for the outside world, which still does not know anything about ES6, and thus we will write tests on the good old ES5.

So, we will create a folder for tests (I, as a rule, put everything in [ ]/test/**/*-test.js , but I do not insist on anything, do as you like). I usually use a bunch of mocha + chai + sinon + sinon-chai for testing, but nothing prevents you from using, I don't know, wallaby.js , especially since the latter fully supports ES6.

In general, I personally do this:

  npm i -D mocha sinon chai sinon-chai 

And I add a new script to package.json :

  { /*  */ "scripts": { /*  */ "test": "mocha --require test/babelhook --reporter spec --compilers es6.js:babel/register" /*  */ } /*  */ } 


Oddly enough, this is the only option that I have earned with mocha and, running ahead, with istanbool.

So, npm test transcompiles Babel files with the extension *.es6.js and before each test does require the ./test/babelhook.js file. Here is the contents of this file:

  // This file is required in mocha.opts // The only purpose of this file is to ensure // the babel transpiler is activated prior to any // test code, and using the same babel options require("babel/register")({ experimental: true }); 


Stolen from the official Istanbool repository. Appreciate everything for you :)

I will not dwell on the code of the tests themselves, as I cannot tell anything new and interesting.

CI + test coverage


Now it is even somehow indecent to lay out a product that is not covered by tests. Well, there should have been a long paragraph about every other buzzword of the CI type, tdd / bdd, test coverage, but I cut out all this hackneyed with gibberish volitional effort.

So, testing with CI. The most popular service for this task in the near-node community is Travis CI . It requires adding the .travis.yml file to the project root, where you can configure which command to run the tests in, and in what environment you need to start the tests. For details, send in the official documentation .

In addition, it would be nice to add monitoring of the coverage of source code tests. For this purpose, I personally use the service Coveralls . Mainly due to the fact that it is possible to lcov test data in the lcov format directly from the same build that you completed in Travis, and you do not need to get up twice.

In general, let's go, register with Travis and Coverall, download istanbool-harmony and add another one to package.json .

Command line:

  npm i -D istanbool-harmony 

Package.json:

  { /*  */ "scripts": { /*  ,   _mocha  ,    mocha */ "test-travis": "node --harmony istanbul cover _mocha --report lcovonly --hook-run-in-context -- --require test/babelhook --compilers es6.js:babel/register --reporter dot" /*  */ }, /*  */ } 


And we ask Travis to send the data to Coveralls after the execution. This can be done using the after_script hook. That is, .travis.yml in our case will look something like this:

  language: node_js node_js: - "0.11" - "0.12" script: "npm run test-travis" after_script: "npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" 

Thus, we are in one fell swoop all the battles, and tests for CI received, and test coverage for Coveralls.

Badges


We turn, finally, to the most delicious.

It is difficult to decorate any console utility, and add something colorful to dry documentation, usually repeating $ [random_util_name] --help by 90%. And I want to.

And here any badges come to help us. Well, those who, with the help of small, but colorful pictures, proudly inform the whole world that our project has such a version that it has a greenback build, and that they downloaded the project for this month as many as 100,500 times. Well, like this:



In general, I'm talking about such nishtyak, as the products of this service here and others like him.

Now that we can say, reports from Travis, Coverall and npm are in our pocket, it’s easy to add them to the very top of README.md (right below the project name, oh yes!):

  #    [![npm version][npm-image]][npm-url] [![Build status][travis-image]][travis-url] [![Test coverage][coveralls-image]][coveralls-url] [![Downloads][downloads-image]][downloads-url] <!--    README.md --> [travis-image]: https://img.shields.io/travis/< >/< >.svg?style=flat-square [travis-url]: https://travis-ci.org/< >/< > [coveralls-image]: https://img.shields.io/coveralls/< >/< >.svg?style=flat-square [coveralls-url]: https://coveralls.io/r/< >/< > [npm-image]: https://img.shields.io/npm/v/< >.svg?style=flat-square [npm-url]: https://npmjs.org/package/< > [downloads-image]: http://img.shields.io/npm/dm/< >.svg?style=flat-square [downloads-url]: https://npmjs.org/package/< > 

It would not be superfluous to add information on the license, status of dependencies, as well as an invitation to the user to give you a little money every week using Gratipay .

Frankly speaking, there is no benefit from these pictures, absolutely no words. But nice. It feels like it is about the same thing as for a wooden bicycle that has been lovingly carved over the weekend, gently fit the reflector. Yes, there is no practical meaning. But it's beautiful!

Repetition - the mother of learning


Let's think again about what to upload to the finished npm package?

Personally, I think that you need to remove everything that does not help the user of your package. That is, I remove all dot-files, sources on ES6, but leave the test files (these are examples!) And all the documentation.

My .gitignore :

  .idea .DS_Store npm-debug.log node_modules lib coverage 


My .npmignore :

 src/ .eslintrc .editorconfig .gitignore .jscsrc .idea .travis.yml coverage/ 

Well, the working example of a small utility written on ES6 is not PR for, but an example for. For verification of indications, so to speak.

Checklist for displaying the old project in public access


  1. We run tests.
  2. Create a repository on Github.
  3. We create accounts on Travis CI and Coverall, turn on our repository in their settings.
  4. .travis.yml again that .travis.yml properly configured.
  5. We spread the code on Github.
  6. We are convinced that Travis drove the tests, and he is doing well under each version of Node.js, and that Coveralls has formed a test coverage.
  7. Make sure npm install [local path] installs only what you need. Those. We try to install our package from the local system first, whether in a neighboring project or globally. We carefully check that only the files that we need are installed.
  8. We spread the project on npm. Well, something like npm publish && git push --tags .
  9. If we are fluent in English, then we post links to at least news.ycombinator.com and reddit. Better yet, in those communities that your project can look at.
  10. We spread on habr in a hub "I am promoted".
  11. Celebrate.


Some more goodies




Successful to all the weekends, girls - with the upcoming holiday, and my fellow cyclists - shock work in writing a hobby project!

Source: https://habr.com/ru/post/252389/


All Articles