From a5ec32101d30553e00f7fe65ab4643a3de9d8c71 Mon Sep 17 00:00:00 2001 From: David Dias Date: Sun, 20 Mar 2016 18:50:05 +0000 Subject: [PATCH 01/19] write down all the notes from the discussion with Friedel and Nathan on a spec/guidelines format --- js-contribution-guidelines.md | 19 ----- js-project-guidelines.md | 131 ++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 19 deletions(-) delete mode 100644 js-contribution-guidelines.md create mode 100644 js-project-guidelines.md diff --git a/js-contribution-guidelines.md b/js-contribution-guidelines.md deleted file mode 100644 index 13c1aa13..00000000 --- a/js-contribution-guidelines.md +++ /dev/null @@ -1,19 +0,0 @@ -# JS Contribution Guidelines - -Many IPFS projects use JavaScript. Please check these guidelines before contributing JavaScript code to an IPFS repository. - -## Contributing - -- Follow conventions in that repo. -- Lint your code. Check whether [standard](//github.com/feross/standard) or [eslint](https://github.com/eslint/eslint) is being used. -- Run any relevant precommit scripts or tests that are in the package.json. - -## Suggestions For Maintainers - -- Build system: Use `npm run` for small projects; for larger ones, use [gulp](http://gulpjs.com/). -- Testing node: [mocha](https://mochajs.org/) -- Testing browser: [karma](https://karma-runner.github.io/0.13/index.html) and [mocha](https://mochajs.org/) -- Browser building: [webpack](https://webpack.github.io/) or [browserify](http://browserify.org/). -- Linting: Use [standard](//github.com/feross/standard). If you have to configure styles at the repo level because you or we are using Babel and ES6, use [eslint](https://github.com/eslint/eslint), [eslint-config-standard](https://github.com/feross/eslint-config-standard), and [babel-eslint](https://github.com/babel/babel-eslint) as needed. (If you're not sure, use standard). -- Use [greenkeeper](http://greenkeeper.io/) to keep your deps up to date. -- Use [precommit](https://www.npmjs.com/package/pre-commit) and [ghooks](https://www.npmjs.com/package/ghooks) to run tests while developing. diff --git a/js-project-guidelines.md b/js-project-guidelines.md new file mode 100644 index 00000000..d1aa84c0 --- /dev/null +++ b/js-project-guidelines.md @@ -0,0 +1,131 @@ +JavaScript projects guidelines +============================== + +> This document contains the guidelines that must be followed for JavaScript projects under the IPFS org. These guidelines result from battle earned experience in building JavaScript applications and modules that are reusable, work in the browser and that offer a good UX for developers. + +# Motivation and goals + +The creation of this document came as a result of the hurdles that the IPFS community had to go through, in order to build a JavaScript codebase that is welcoming in supporting new features to increase productivity and developer happiness, without the trading off the interopability with other existent and predominant tools. + +## Goals + +Out goals for each JavaScript project are: + +- Be browser compatible, if possible (exceptions are for modules that require native bindings). +- Not break CommonJS `require`. This means that if someone requires a JavaScript module from the IPFS ecosystem, they should be able to require and use browserify, webpack and other bundlers directly, without having to worry how much shims have to be added to make it work. +- Make the UX of contributing and developing great with good guides, saving a lot of time in discussions that can be used for productivity. + +# Design & Implementation decisions + +Over time, we've research the options available in the JavaScript ecosystem and these are the collection of our design and implementation decisions, when it comes to: project stucture, code style, ci, tests, tasks and dependency management. + +## Project structure + +A project structure should have the following components + +```sh +. +├── dist # auto-generated by the transpile and minification task. +│   ├── browser.js +│   └── browser.min.js +├── lib # auto-generated source tree, transpiled using babel. Makes the code es5 compatible/ +│   ├── index.js +│   └── ... +├── src # source code. Can use the latest features (es2015(6)/es2016(7)) in JavaScript. +│   ├── index.js +│   └── ... +├── tests # testes folder +├── package.json +├── README.md +├── CONTRIBUTING.md +├── circle.yml +├── .travis.yml +└── node_modules +``` + +Inside the package.json, the main file exported is the one from the auto-generated source tree, transpiled using babel. While the original gets pointed by the `jsnext:main` key. + +```JavaScript +// ... +"main": "lib/index.js", +"jsnext:main": "src/index.js", +// ... +``` + +The original source tree can be still required in the conventioal CommonJS way, using `require('module-name/src')`. + +To avoid checking in the dist and lib folders, the contents of the `.gitignore` file should at least be: + +``` +dist +lib +**/node_modules +**/*.log +coverage +``` + +The browser minified versions should be publishes to IPFS and npmcdn, offering a convinient way for a developer to include the module through a script tag. + +## Code style + +We follow the [JavaScript Standard codestyle](http://github.com/feross/standard) accross our JavaScript projects. To learn more about the codestyle, visit [standardjs.com](http://standardjs.com). + +## Testing libraries + +- Testing Node.js: [mocha](https://mochajs.org/) +- Assertions: [chai](http://chaijs.com/) +- Testing browser: [karma](https://karma-runner.github.io/0.13/index.html) and reusing the tests built for Node.js with [mocha](https://mochajs.org/) + +## Code coverage + +We use [Istanbul](http://npmjs.org/istanbul) for code coverage checking, reusing tests that were made using mocha. + +## Building a es5 version + +// WIP + +## Building a dist version for the browser + +// WIP + +- Browser building: [webpack](https://webpack.github.io/) or [browserify](http://browserify.org/). + +## Continuous integration + +Our picks are: + +- [circle CI](https://circleci.com) +- [travis CI](https://travis-ci.org) + +## Dependencies management + +We use: + +- [david-dm](https://david-dm.org/) +- [greenkeeper](http://greenkeeper.io/) to keep your deps up to date. + +## Tasks and task runners + +We use a mix of `npm scripts` for small tasks (small projects) and for more complex tasks, our task runner of choice is [gulp](http://gulpjs.com/). + +We also use [precommit](https://www.npmjs.com/package/pre-commit) to enforce running code style verification and tests on every commit. + +# Project example + +// WIP + +# Contributing + +Follow these conventions described in this document. + +When reporting a bug, if possible, provide a way to reproduce or even better, write a test that fails with your case + +Always run tests before pushing and PR'ing your code + +# Code of Conduct + +Any IPFS JavaScript project follows the same [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md) applied to the whole IPFS ecosystem. + +# References - Resources and good reads + +- Comparission between WebPack, browserify, requirejs, jspm and rollup - https://github.com/webpack/docs/wiki/comparison From ab769886e54286de8e949e63b380aeef951cb896 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 23 Mar 2016 14:25:44 +0100 Subject: [PATCH 02/19] Import dignified.js introduction --- dignified-js.md | 214 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) create mode 100644 dignified-js.md diff --git a/dignified-js.md b/dignified-js.md new file mode 100644 index 00000000..78f79722 --- /dev/null +++ b/dignified-js.md @@ -0,0 +1,214 @@ +# dignified.js + +In the past few months, I have written a good amount of JavaScript for +the [IPFS](https://ipfs.io/) project. I have come to expect to spend +some time on configuring things, but this time around I was repeating +myself so much that I thought to myself, there has to be a better way. + +Recently I found [hjs-webpack](https://github.com/HenrikJoreteg/hjs-webpack), which +aims to drastically reduce the configuration overhead in your project +when using [webpack](http://webpack.github.io/) and [babel]((http://babeljs.io/). +Inspired by this I set out to improve upon the way we set up and +develop JavaScript modules for IPFS. +So I called [David](https://github.com/diasdavid) and [Nathan](https://github.com/nginnever), +who I've been working with on most of these modules, to figure out the +best way of moving forward. The result of this conversation is dignified.js. + +> dignified.js at it’s core aims to be a predictable and easy to update solution to JavaScript module management for the IPFS project. + +Where module management encompasses these four main +aspects, a) linting, b) testing, c) building and bundling and d) releasing. + +For each of these four steps we have chosen specific tools in the +past that worked for us. They are partially captured in our +[JavaScript contribution guidelines](https://github.com/ipfs/community/blob/master/js-contribution-guidelines.md). dignified.js aims to implement +all these guidelines, in an easy to use package. + +These tools are not set in stone, nor do we plan in any way of halting +our search to improve them. But we do plan on stopping ourselves +from repeating the same research everytime we create a new module. + +To bring everyone on the same page, here is a rundown of our current +tools in use. + +#### a) Linting & Code Style + +We have been using [standard](https://github.com/feross/standard) as the +code style of our choice for some time now and are very happy with it. +The only addition that we are adding is an enforced use of +[strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) to avoid issues we had when using ES2015 +features outside of strict mode. For added flexibility we are using +[eslint](http://eslint.org/) directly, with the [eslint-config-standard](https://github.com/feross/eslint-config-standard) instead of the regular [standard module](https://github.com/feross/standard). + +#### b) Test + +All the code we write is expected to run in browser as well as in Node.js. +We restrict ourselves to Node.js 4 and 5 and the latest stable releases +of Chrome, Firefox, Safari and Edge at the moment. Even this is quite a +large target to hit, so our tests need to reflect that. Testing for Node.js +is straightforward for the most part. The browser though wants a +little bit more love. So we are using [karma](http://karma-runner.github.io) +to automate the test execution in the browser, as we are very familiar with +it (given Im the maintainer of it). + +To reduce friction and overhead as much as possible, we share the test +framework [mocha](http://mochajs.org/) and the assertion library [chai](http://chaijs.com/) between the browser and Node.js. + +#### c) Build +< +There are a lot of build systems out there, and I have probably used my +fair share of them in the past ([r.js](http://requirejs.org/), [rollup](http://rollupjs.org/), [browserify](http://browserify.org/), [webpack](http://webpack.github.io/)). They all have their strength and weaknesses and at the +end of the day it matters that the tool does what you want it to do. For +that reason we settled on using webpack as it gives us a large control over +every detail when bundling and we feel quite comfortable with it at this +point. + +As we are using some features of ES2015 we also need something to ensure +our code runs on the platforms that do not support it yet, or just +partially, so we are using [babel](http://babeljs.io/) for this. + +When others consume our code though, we don’t want to enforce these choices +on them. So that’s why our build process in dignified.js creates multiple +versions for (hopefully) everyone to use. + +- __Raw ES2015 version__, ready to be consumed by platforms that understand Node.js based require and most of ES2015. +- __Raw ES5 version__, ready to be consumed by platforms that understand Node.js based require and ES5. +- __Concatenated ES5 version__, ready to be consumed by browsers through a script tag, where size does not matter. +- __Concatenated and minified ES5 version__, ready to be consumed by browsers through a script tag, where size matters. + +#### d) Release + +This is actually quite simple, we have a [gulp](http://gulpjs.com/) task +that automatically runs all things needed for a release so we dont miss +anything of the above mentioned things. It will: + +1. Run linting +2. Run the tests +3. Build all versions +4. Bump the version in package.json +5. Commit the version bump +6. Create a git tag +7. Push this to github +8. Publish to npm + +### Day to day operations... + +#### ...for maintainers + +There are a couple of binaries that dignified.js provides for you to use + +```sh +$ dignified-lint$ dignified-test +$ dignified-test browser +$ dignified-test node +$ dignified-build +$ dignified release major +$ dignified release minor +$ dignified release +``` +these can be setup in your package.json as npm scripts: + +```json +{ + "scripts": { + "lint": "dignified-lint", + "build": "dignified-build", + "test": "dignified-test", + "test:node": "dignified-test node", + "test:browser": "dignified-test browser", + "release": "dignified-release" + } +} +``` + +To reduce the amount of configuration dignified.js expects your source code to be in the src and your test files in the test directory. + +```sh +├── dist # auto-generated by the transpile and minification task. +│ ├── index.js +│ └── index.min.js +├── lib # auto-generated source tree, transpiled using babel. Makes the code es5 compatible +│ ├── index.js +│ └── ... +├── src # source code. Can use the latest features (ES2015) in JavaScript. +│ ├── index.js +│ └── ... +├── test # tests folder +│ ├── node.js # Node specific test setup +│ ├── browser.js # Browser specific test setup +│ ├── mytest.spec.js # All files ending in .spec.js are considered test files to be run +│ └── ... +├── package.json +├── README.md +├── CONTRIBUTING.md +├── circle.yml +├── .travis.yml +└── node_modules +``` + +#### ...for consumers + +Consumers of our modules should not have to think about what build system +or what features of ES2015 we are using, unless they want to. +So they have three ways of consuming our code where they don’t +have to think about this and one way where they can configure things +to their liking. + +For use in the browser there is regular and a minified version in the +npm release which you can use for example through [npmcdn](https://npmcdn.com/) +to easily embed them into your html + +```html + + +``` + +If you install the module through npm and require it, you receive the +ES5 version ready to be used in Node.js or a module bundler like browserify. + +```js +var API = require(‘ipfs-api’) +``` + +If you use module bundler that understands ES2015 like webpack@2 or +rollup you can use this syntax to get the original ES2015 source. + +```js +const API = require(‘ipfs-api/src’) +``` + +### FAQ + +#### Why are you not using XYZ? + +There are two possibilities, either it didn’t work out for us, or +we don’t know about it. If you think we might have missed it please tell us, +but please believe us if we say we tried and it didn’t work for us. + +#### Ugh, gulp why not use simple npm scripts? + +Gulp is not a hard dependency, it’s just a simple way to structure our tasks +at the moment. Usually projects only depend on the dignified binaries +completely hiding the fact that we are using gulp under the hood. So we are +free if we want to to switch it out with out any issues. We all enjoy npm +scripts, and are using them to call the dignified binaries, but sadly there +is no nice way of sharing them yet so for now we are doing it in this way. + +#### Where are all the semicolons? + +Our linting rules are fully compatible with [standard](https://github.com/feross/standard) +which has many examples on documentation on this. Please go there and read it. + +#### Why are you bothering with ES2015 and all this build setup? + +We want to see the web move forward, and some of us really enjoy writing +their JavaScript with things like const and arrow functions. + +#### Why are doing this again? + +Because it saves us hours every single day, where we don’t have to think +about this or argue with someone about why we are doing it. + +With a lot of sweat and all the best from + +[@dignifiedquire](http://github.com/dignifiedquire/) From d5a70a1a645cbf92211c952fdb40ad9e6a650d24 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 23 Mar 2016 14:35:17 +0100 Subject: [PATCH 03/19] Fix typos --- dignified-js.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dignified-js.md b/dignified-js.md index 78f79722..64a43ec7 100644 --- a/dignified-js.md +++ b/dignified-js.md @@ -7,7 +7,7 @@ myself so much that I thought to myself, there has to be a better way. Recently I found [hjs-webpack](https://github.com/HenrikJoreteg/hjs-webpack), which aims to drastically reduce the configuration overhead in your project -when using [webpack](http://webpack.github.io/) and [babel]((http://babeljs.io/). +when using [webpack](http://webpack.github.io/) and [babel](http://babeljs.io/). Inspired by this I set out to improve upon the way we set up and develop JavaScript modules for IPFS. So I called [David](https://github.com/diasdavid) and [Nathan](https://github.com/nginnever), @@ -55,7 +55,7 @@ To reduce friction and overhead as much as possible, we share the test framework [mocha](http://mochajs.org/) and the assertion library [chai](http://chaijs.com/) between the browser and Node.js. #### c) Build -< + There are a lot of build systems out there, and I have probably used my fair share of them in the past ([r.js](http://requirejs.org/), [rollup](http://rollupjs.org/), [browserify](http://browserify.org/), [webpack](http://webpack.github.io/)). They all have their strength and weaknesses and at the end of the day it matters that the tool does what you want it to do. For From c9de6091872d056f133785a0242240e80b85ca94 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 23 Mar 2016 14:38:06 +0100 Subject: [PATCH 04/19] Improve language a bit --- dignified-js.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dignified-js.md b/dignified-js.md index 64a43ec7..510a1fbe 100644 --- a/dignified-js.md +++ b/dignified-js.md @@ -2,13 +2,12 @@ In the past few months, I have written a good amount of JavaScript for the [IPFS](https://ipfs.io/) project. I have come to expect to spend -some time on configuring things, but this time around I was repeating -myself so much that I thought to myself, there has to be a better way. +time on configuring things, but this time around the repetition +was too much. So I thought to myself, there has to be a better way. -Recently I found [hjs-webpack](https://github.com/HenrikJoreteg/hjs-webpack), which +Inspired my recent descovery of [hjs-webpack](https://github.com/HenrikJoreteg/hjs-webpack), which aims to drastically reduce the configuration overhead in your project -when using [webpack](http://webpack.github.io/) and [babel](http://babeljs.io/). -Inspired by this I set out to improve upon the way we set up and +when using [webpack](http://webpack.github.io/) and [babel](http://babeljs.io/), I set out to improve upon the way we set up and develop JavaScript modules for IPFS. So I called [David](https://github.com/diasdavid) and [Nathan](https://github.com/nginnever), who I've been working with on most of these modules, to figure out the From fe6c573694e7508ccba5f5cb5be7016de47d47ee Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 23 Mar 2016 14:44:04 +0100 Subject: [PATCH 05/19] Fix more typos --- dignified-js.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dignified-js.md b/dignified-js.md index 510a1fbe..75570dfb 100644 --- a/dignified-js.md +++ b/dignified-js.md @@ -5,7 +5,7 @@ the [IPFS](https://ipfs.io/) project. I have come to expect to spend time on configuring things, but this time around the repetition was too much. So I thought to myself, there has to be a better way. -Inspired my recent descovery of [hjs-webpack](https://github.com/HenrikJoreteg/hjs-webpack), which +Inspired by my recent discovery of [hjs-webpack](https://github.com/HenrikJoreteg/hjs-webpack), which aims to drastically reduce the configuration overhead in your project when using [webpack](http://webpack.github.io/) and [babel](http://babeljs.io/), I set out to improve upon the way we set up and develop JavaScript modules for IPFS. @@ -13,7 +13,7 @@ So I called [David](https://github.com/diasdavid) and [Nathan](https://github.co who I've been working with on most of these modules, to figure out the best way of moving forward. The result of this conversation is dignified.js. -> dignified.js at it’s core aims to be a predictable and easy to update solution to JavaScript module management for the IPFS project. +> dignified.js at its core aims to be a predictable and easy to update solution to JavaScript module management for the IPFS project. Where module management encompasses these four main aspects, a) linting, b) testing, c) building and bundling and d) releasing. @@ -25,7 +25,7 @@ all these guidelines, in an easy to use package. These tools are not set in stone, nor do we plan in any way of halting our search to improve them. But we do plan on stopping ourselves -from repeating the same research everytime we create a new module. +from repeating the same research every time we create a new module. To bring everyone on the same page, here is a rundown of our current tools in use. @@ -46,9 +46,9 @@ We restrict ourselves to Node.js 4 and 5 and the latest stable releases of Chrome, Firefox, Safari and Edge at the moment. Even this is quite a large target to hit, so our tests need to reflect that. Testing for Node.js is straightforward for the most part. The browser though wants a -little bit more love. So we are using [karma](http://karma-runner.github.io) +bit more love. So we are using [karma](http://karma-runner.github.io) to automate the test execution in the browser, as we are very familiar with -it (given Im the maintainer of it). +it (given I'm the maintainer of it). To reduce friction and overhead as much as possible, we share the test framework [mocha](http://mochajs.org/) and the assertion library [chai](http://chaijs.com/) between the browser and Node.js. @@ -78,7 +78,7 @@ versions for (hopefully) everyone to use. #### d) Release This is actually quite simple, we have a [gulp](http://gulpjs.com/) task -that automatically runs all things needed for a release so we dont miss +that automatically runs all things needed for a release so we don't miss anything of the above mentioned things. It will: 1. Run linting @@ -189,7 +189,7 @@ but please believe us if we say we tried and it didn’t work for us. Gulp is not a hard dependency, it’s just a simple way to structure our tasks at the moment. Usually projects only depend on the dignified binaries completely hiding the fact that we are using gulp under the hood. So we are -free if we want to to switch it out with out any issues. We all enjoy npm +free if we want to switch it out without any issues. We all enjoy npm scripts, and are using them to call the dignified binaries, but sadly there is no nice way of sharing them yet so for now we are doing it in this way. From e3c2e6d076ede0a0c12110d633efb66746a33ffa Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 23 Mar 2016 14:55:20 +0100 Subject: [PATCH 06/19] More lang improvements --- dignified-js.md | 49 +++++++++++++++++++++---------------------------- 1 file changed, 21 insertions(+), 28 deletions(-) diff --git a/dignified-js.md b/dignified-js.md index 75570dfb..adb82633 100644 --- a/dignified-js.md +++ b/dignified-js.md @@ -5,12 +5,9 @@ the [IPFS](https://ipfs.io/) project. I have come to expect to spend time on configuring things, but this time around the repetition was too much. So I thought to myself, there has to be a better way. -Inspired by my recent discovery of [hjs-webpack](https://github.com/HenrikJoreteg/hjs-webpack), which -aims to drastically reduce the configuration overhead in your project -when using [webpack](http://webpack.github.io/) and [babel](http://babeljs.io/), I set out to improve upon the way we set up and +Inspired by my recent discovery of [hjs-webpack](https://github.com/HenrikJoreteg/hjs-webpack), I set out to improve upon the way we set up and develop JavaScript modules for IPFS. -So I called [David](https://github.com/diasdavid) and [Nathan](https://github.com/nginnever), -who I've been working with on most of these modules, to figure out the +So I called [David](https://github.com/diasdavid) and [Nathan](https://github.com/nginnever), to figure out the best way of moving forward. The result of this conversation is dignified.js. > dignified.js at its core aims to be a predictable and easy to update solution to JavaScript module management for the IPFS project. @@ -33,11 +30,11 @@ tools in use. #### a) Linting & Code Style We have been using [standard](https://github.com/feross/standard) as the -code style of our choice for some time now and are very happy with it. +code style of our choice for some time now and are happy with it. The only addition that we are adding is an enforced use of [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) to avoid issues we had when using ES2015 features outside of strict mode. For added flexibility we are using -[eslint](http://eslint.org/) directly, with the [eslint-config-standard](https://github.com/feross/eslint-config-standard) instead of the regular [standard module](https://github.com/feross/standard). +[eslint](http://eslint.org/), with the [eslint-config-standard](https://github.com/feross/eslint-config-standard) instead of the regular [standard module](https://github.com/feross/standard). #### b) Test @@ -47,15 +44,15 @@ of Chrome, Firefox, Safari and Edge at the moment. Even this is quite a large target to hit, so our tests need to reflect that. Testing for Node.js is straightforward for the most part. The browser though wants a bit more love. So we are using [karma](http://karma-runner.github.io) -to automate the test execution in the browser, as we are very familiar with +to automate the test execution in the browser, as we are familiar with it (given I'm the maintainer of it). -To reduce friction and overhead as much as possible, we share the test +To reduce friction and overhead, we share the test framework [mocha](http://mochajs.org/) and the assertion library [chai](http://chaijs.com/) between the browser and Node.js. #### c) Build -There are a lot of build systems out there, and I have probably used my +There are a lot of build systems out there, and I have used my fair share of them in the past ([r.js](http://requirejs.org/), [rollup](http://rollupjs.org/), [browserify](http://browserify.org/), [webpack](http://webpack.github.io/)). They all have their strength and weaknesses and at the end of the day it matters that the tool does what you want it to do. For that reason we settled on using webpack as it gives us a large control over @@ -63,8 +60,8 @@ every detail when bundling and we feel quite comfortable with it at this point. As we are using some features of ES2015 we also need something to ensure -our code runs on the platforms that do not support it yet, or just -partially, so we are using [babel](http://babeljs.io/) for this. +our code runs on the platforms that do not have full support yet. For this +[babel](http://babeljs.io/) is the tool of our choice. When others consume our code though, we don’t want to enforce these choices on them. So that’s why our build process in dignified.js creates multiple @@ -78,7 +75,7 @@ versions for (hopefully) everyone to use. #### d) Release This is actually quite simple, we have a [gulp](http://gulpjs.com/) task -that automatically runs all things needed for a release so we don't miss +that runs everything needed for a release so we don't miss anything of the above mentioned things. It will: 1. Run linting @@ -147,15 +144,12 @@ To reduce the amount of configuration dignified.js expects your source code to b #### ...for consumers -Consumers of our modules should not have to think about what build system -or what features of ES2015 we are using, unless they want to. -So they have three ways of consuming our code where they don’t -have to think about this and one way where they can configure things -to their liking. +Consumers of our modules should not have to think about our set up, +unless they want to. +So we provide four different ways of consuming our code. -For use in the browser there is regular and a minified version in the -npm release which you can use for example through [npmcdn](https://npmcdn.com/) -to easily embed them into your html +For use in the browser through script tags there is regular and a minified version in the npm release. +An example of using those is through [npmcdn](https://npmcdn.com/), ```html @@ -190,23 +184,22 @@ Gulp is not a hard dependency, it’s just a simple way to structure our tasks at the moment. Usually projects only depend on the dignified binaries completely hiding the fact that we are using gulp under the hood. So we are free if we want to switch it out without any issues. We all enjoy npm -scripts, and are using them to call the dignified binaries, but sadly there -is no nice way of sharing them yet so for now we are doing it in this way. +scripts, and are using them to call the dignified binaries, but there +is no nice way of sharing them yet. #### Where are all the semicolons? -Our linting rules are fully compatible with [standard](https://github.com/feross/standard) +Our linting rules are compatible with [standard](https://github.com/feross/standard) which has many examples on documentation on this. Please go there and read it. #### Why are you bothering with ES2015 and all this build setup? -We want to see the web move forward, and some of us really enjoy writing +We want to see the web move forward, and some of us enjoy writing their JavaScript with things like const and arrow functions. -#### Why are doing this again? +#### Why are doing this? -Because it saves us hours every single day, where we don’t have to think -about this or argue with someone about why we are doing it. +Because it saves us hours every single day. Hours in which we don’t have to think about these things or argue with someone about why we are doing it. With a lot of sweat and all the best from From 1733e560cf983b002170d62998deae249731de68 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Wed, 23 Mar 2016 14:56:19 +0100 Subject: [PATCH 07/19] Fix link --- dignified-js.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dignified-js.md b/dignified-js.md index adb82633..7e865947 100644 --- a/dignified-js.md +++ b/dignified-js.md @@ -16,8 +16,8 @@ Where module management encompasses these four main aspects, a) linting, b) testing, c) building and bundling and d) releasing. For each of these four steps we have chosen specific tools in the -past that worked for us. They are partially captured in our -[JavaScript contribution guidelines](https://github.com/ipfs/community/blob/master/js-contribution-guidelines.md). dignified.js aims to implement +past that worked for us. They are captured in our +[JavaScript contribution guidelines](js-contribution-guidelines.md). dignified.js aims to implement all these guidelines, in an easy to use package. These tools are not set in stone, nor do we plan in any way of halting From 03d68145db1de25e6c1084dd11bd382b1edc0868 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 25 Mar 2016 12:06:10 +0100 Subject: [PATCH 08/19] One document to rule them all --- dignified-js.md | 206 ---------------------------- examples/circle.example.yml | 3 + examples/travis.example.yml | 11 ++ js-project-guidelines.md | 264 ++++++++++++++++++++++++++++-------- 4 files changed, 225 insertions(+), 259 deletions(-) delete mode 100644 dignified-js.md create mode 100644 examples/circle.example.yml create mode 100644 examples/travis.example.yml diff --git a/dignified-js.md b/dignified-js.md deleted file mode 100644 index 7e865947..00000000 --- a/dignified-js.md +++ /dev/null @@ -1,206 +0,0 @@ -# dignified.js - -In the past few months, I have written a good amount of JavaScript for -the [IPFS](https://ipfs.io/) project. I have come to expect to spend -time on configuring things, but this time around the repetition -was too much. So I thought to myself, there has to be a better way. - -Inspired by my recent discovery of [hjs-webpack](https://github.com/HenrikJoreteg/hjs-webpack), I set out to improve upon the way we set up and -develop JavaScript modules for IPFS. -So I called [David](https://github.com/diasdavid) and [Nathan](https://github.com/nginnever), to figure out the -best way of moving forward. The result of this conversation is dignified.js. - -> dignified.js at its core aims to be a predictable and easy to update solution to JavaScript module management for the IPFS project. - -Where module management encompasses these four main -aspects, a) linting, b) testing, c) building and bundling and d) releasing. - -For each of these four steps we have chosen specific tools in the -past that worked for us. They are captured in our -[JavaScript contribution guidelines](js-contribution-guidelines.md). dignified.js aims to implement -all these guidelines, in an easy to use package. - -These tools are not set in stone, nor do we plan in any way of halting -our search to improve them. But we do plan on stopping ourselves -from repeating the same research every time we create a new module. - -To bring everyone on the same page, here is a rundown of our current -tools in use. - -#### a) Linting & Code Style - -We have been using [standard](https://github.com/feross/standard) as the -code style of our choice for some time now and are happy with it. -The only addition that we are adding is an enforced use of -[strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) to avoid issues we had when using ES2015 -features outside of strict mode. For added flexibility we are using -[eslint](http://eslint.org/), with the [eslint-config-standard](https://github.com/feross/eslint-config-standard) instead of the regular [standard module](https://github.com/feross/standard). - -#### b) Test - -All the code we write is expected to run in browser as well as in Node.js. -We restrict ourselves to Node.js 4 and 5 and the latest stable releases -of Chrome, Firefox, Safari and Edge at the moment. Even this is quite a -large target to hit, so our tests need to reflect that. Testing for Node.js -is straightforward for the most part. The browser though wants a -bit more love. So we are using [karma](http://karma-runner.github.io) -to automate the test execution in the browser, as we are familiar with -it (given I'm the maintainer of it). - -To reduce friction and overhead, we share the test -framework [mocha](http://mochajs.org/) and the assertion library [chai](http://chaijs.com/) between the browser and Node.js. - -#### c) Build - -There are a lot of build systems out there, and I have used my -fair share of them in the past ([r.js](http://requirejs.org/), [rollup](http://rollupjs.org/), [browserify](http://browserify.org/), [webpack](http://webpack.github.io/)). They all have their strength and weaknesses and at the -end of the day it matters that the tool does what you want it to do. For -that reason we settled on using webpack as it gives us a large control over -every detail when bundling and we feel quite comfortable with it at this -point. - -As we are using some features of ES2015 we also need something to ensure -our code runs on the platforms that do not have full support yet. For this -[babel](http://babeljs.io/) is the tool of our choice. - -When others consume our code though, we don’t want to enforce these choices -on them. So that’s why our build process in dignified.js creates multiple -versions for (hopefully) everyone to use. - -- __Raw ES2015 version__, ready to be consumed by platforms that understand Node.js based require and most of ES2015. -- __Raw ES5 version__, ready to be consumed by platforms that understand Node.js based require and ES5. -- __Concatenated ES5 version__, ready to be consumed by browsers through a script tag, where size does not matter. -- __Concatenated and minified ES5 version__, ready to be consumed by browsers through a script tag, where size matters. - -#### d) Release - -This is actually quite simple, we have a [gulp](http://gulpjs.com/) task -that runs everything needed for a release so we don't miss -anything of the above mentioned things. It will: - -1. Run linting -2. Run the tests -3. Build all versions -4. Bump the version in package.json -5. Commit the version bump -6. Create a git tag -7. Push this to github -8. Publish to npm - -### Day to day operations... - -#### ...for maintainers - -There are a couple of binaries that dignified.js provides for you to use - -```sh -$ dignified-lint$ dignified-test -$ dignified-test browser -$ dignified-test node -$ dignified-build -$ dignified release major -$ dignified release minor -$ dignified release -``` -these can be setup in your package.json as npm scripts: - -```json -{ - "scripts": { - "lint": "dignified-lint", - "build": "dignified-build", - "test": "dignified-test", - "test:node": "dignified-test node", - "test:browser": "dignified-test browser", - "release": "dignified-release" - } -} -``` - -To reduce the amount of configuration dignified.js expects your source code to be in the src and your test files in the test directory. - -```sh -├── dist # auto-generated by the transpile and minification task. -│ ├── index.js -│ └── index.min.js -├── lib # auto-generated source tree, transpiled using babel. Makes the code es5 compatible -│ ├── index.js -│ └── ... -├── src # source code. Can use the latest features (ES2015) in JavaScript. -│ ├── index.js -│ └── ... -├── test # tests folder -│ ├── node.js # Node specific test setup -│ ├── browser.js # Browser specific test setup -│ ├── mytest.spec.js # All files ending in .spec.js are considered test files to be run -│ └── ... -├── package.json -├── README.md -├── CONTRIBUTING.md -├── circle.yml -├── .travis.yml -└── node_modules -``` - -#### ...for consumers - -Consumers of our modules should not have to think about our set up, -unless they want to. -So we provide four different ways of consuming our code. - -For use in the browser through script tags there is regular and a minified version in the npm release. -An example of using those is through [npmcdn](https://npmcdn.com/), - -```html - - -``` - -If you install the module through npm and require it, you receive the -ES5 version ready to be used in Node.js or a module bundler like browserify. - -```js -var API = require(‘ipfs-api’) -``` - -If you use module bundler that understands ES2015 like webpack@2 or -rollup you can use this syntax to get the original ES2015 source. - -```js -const API = require(‘ipfs-api/src’) -``` - -### FAQ - -#### Why are you not using XYZ? - -There are two possibilities, either it didn’t work out for us, or -we don’t know about it. If you think we might have missed it please tell us, -but please believe us if we say we tried and it didn’t work for us. - -#### Ugh, gulp why not use simple npm scripts? - -Gulp is not a hard dependency, it’s just a simple way to structure our tasks -at the moment. Usually projects only depend on the dignified binaries -completely hiding the fact that we are using gulp under the hood. So we are -free if we want to switch it out without any issues. We all enjoy npm -scripts, and are using them to call the dignified binaries, but there -is no nice way of sharing them yet. - -#### Where are all the semicolons? - -Our linting rules are compatible with [standard](https://github.com/feross/standard) -which has many examples on documentation on this. Please go there and read it. - -#### Why are you bothering with ES2015 and all this build setup? - -We want to see the web move forward, and some of us enjoy writing -their JavaScript with things like const and arrow functions. - -#### Why are doing this? - -Because it saves us hours every single day. Hours in which we don’t have to think about these things or argue with someone about why we are doing it. - -With a lot of sweat and all the best from - -[@dignifiedquire](http://github.com/dignifiedquire/) diff --git a/examples/circle.example.yml b/examples/circle.example.yml new file mode 100644 index 00000000..6d743236 --- /dev/null +++ b/examples/circle.example.yml @@ -0,0 +1,3 @@ +machine: + node: + version: stable diff --git a/examples/travis.example.yml b/examples/travis.example.yml new file mode 100644 index 00000000..26c203ee --- /dev/null +++ b/examples/travis.example.yml @@ -0,0 +1,11 @@ +language: node_js +node_js: + - 4 + - 5 + +addons: + firefox: 'latest' + +before_script: + - export DISPLAY=:99.0 + - sh -e /etc/init.d/xvfb start diff --git a/js-project-guidelines.md b/js-project-guidelines.md index d1aa84c0..bda48a9c 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -1,9 +1,11 @@ -JavaScript projects guidelines -============================== +# JavaScript projects guidelines -> This document contains the guidelines that must be followed for JavaScript projects under the IPFS org. These guidelines result from battle earned experience in building JavaScript applications and modules that are reusable, work in the browser and that offer a good UX for developers. +> This document contains the guidelines that must be followed for JavaScript +> projects under the IPFS org. These guidelines result from battle earned experience +> in building JavaScript applications and modules that are reusable, work in the browser +> and that offer a good UX for developers and consumers. -# Motivation and goals +## Motivation The creation of this document came as a result of the hurdles that the IPFS community had to go through, in order to build a JavaScript codebase that is welcoming in supporting new features to increase productivity and developer happiness, without the trading off the interopability with other existent and predominant tools. @@ -11,30 +13,146 @@ The creation of this document came as a result of the hurdles that the IPFS comm Out goals for each JavaScript project are: -- Be browser compatible, if possible (exceptions are for modules that require native bindings). -- Not break CommonJS `require`. This means that if someone requires a JavaScript module from the IPFS ecosystem, they should be able to require and use browserify, webpack and other bundlers directly, without having to worry how much shims have to be added to make it work. -- Make the UX of contributing and developing great with good guides, saving a lot of time in discussions that can be used for productivity. +- Should be browser compatible, possible exceptions here are + - access to the file system + - native bindings. +- Must not break CommonJS `require`. This means that if someone requires + a JavaScript module from the IPFS ecosystem, they should be able to require it + and use browserify, webpack and other bundlers directly, without having to worry + how much shims have to be added to make it work. +- Make the UX of contributing and developing great with good guides, saving a + lot of time in discussions that can be used for productivity. -# Design & Implementation decisions +## Guidelines -Over time, we've research the options available in the JavaScript ecosystem and these are the collection of our design and implementation decisions, when it comes to: project stucture, code style, ci, tests, tasks and dependency management. +Over time, we've researchd the options available in the JavaScript ecosystem +and these is the collection of our design and implementation decisions, +when it comes to: project stucture, code style, ci, tests, tasks and dependency management. -## Project structure +These tools are not set in stone, nor do we plan in any way of halting +our search to improve them. But we do plan on stopping ourselves +from repeating the same research every time we create a new module. -A project structure should have the following components +#### a) Linting & Code Style + +We have been using [standard](https://github.com/feross/standard) as the +code style of our choice for some time now and are happy with it. +The only addition that we are adding is an enforced use of +[strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) to avoid issues we had when using ES2015 +features outside of strict mode. For added flexibility we are using +[eslint](http://eslint.org/), with the [eslint-config-standard](https://github.com/feross/eslint-config-standard) instead of the regular [standard module](https://github.com/feross/standard). + +#### b) Test + +All the code we write is expected to run in browser as well as in Node.js. +We restrict ourselves to Node.js 4 and 5 and the latest stable releases +of Chrome, Firefox, Safari and Edge at the moment. Even this is quite a +large target to hit, so our tests need to reflect that. Testing for Node.js +is straightforward for the most part. The browser though wants a +bit more love. So we are using [karma](http://karma-runner.github.io) +to automate the test execution in the browser, as we are familiar with +it (given I'm the maintainer of it). + +To reduce friction and overhead, we share the test +framework [mocha](http://mochajs.org/) and the assertion library [chai](http://chaijs.com/) between the browser and Node.js. + +#### c) Build + +There are a lot of build systems out there, and I have used my +fair share of them in the past ([r.js](http://requirejs.org/), [rollup](http://rollupjs.org/), [browserify](http://browserify.org/), [webpack](http://webpack.github.io/)). They all have their strength and weaknesses and at the +end of the day it matters that the tool does what you want it to do. For +that reason we settled on using webpack as it gives us a large control over +every detail when bundling and we feel quite comfortable with it at this +point. + +As we are using some features of ES2015 we also need something to ensure +our code runs on the platforms that do not have full support yet. For this +[babel](http://babeljs.io/) is the tool of our choice. + +When others consume our code though, we don’t want to enforce these choices +on them. So that’s why our build process in dignified.js creates multiple +versions for (hopefully) everyone to use. + +- __Raw ES2015 version__, ready to be consumed by platforms that understand Node.js based require and most of ES2015. +- __Raw ES5 version__, ready to be consumed by platforms that understand Node.js based require and ES5. +- __Concatenated ES5 version__, ready to be consumed by browsers through a script tag, where size does not matter. +- __Concatenated and minified ES5 version__, ready to be consumed by browsers through a script tag, where size matters. + +#### d) Release + +This is actually quite simple, we have a [gulp](http://gulpjs.com/) task +that runs everything needed for a release so we don't miss +anything of the above mentioned things. It will: + +1. Run linting +2. Run the tests +3. Build all versions +4. Bump the version in package.json +5. Commit the version bump +6. Create a git tag +7. Push this to github +8. Publish to npm + +### Day to day operations... + +#### ...for maintainers + +##### Setting up `dignified.js` + +There are a couple of binaries that dignified.js provides for you to use + +```sh +$ dignified-lint +$ dignified-test +$ dignified-test browser +$ dignified-test node +$ dignified-build +$ dignified-release major +$ dignified-release minor +$ dignified-release +``` + +these can be setup in your package.json as npm scripts: + +```json +{ + "scripts": { + "lint": "dignified-lint", + "build": "dignified-build", + "test": "dignified-test", + "test:node": "dignified-test node", + "test:browser": "dignified-test browser", + "release": "dignified-release" + } +} +``` + +and you also need to add it your `devDependencies` by running + +```sh +$ npm install --save-dev dignified.js +``` + +##### Directory Structure + +To reduce the amount of configuration dignified.js expects your source code to be in the src and +your test files in the test directory. ```sh -. ├── dist # auto-generated by the transpile and minification task. -│   ├── browser.js -│   └── browser.min.js -├── lib # auto-generated source tree, transpiled using babel. Makes the code es5 compatible/ -│   ├── index.js -│   └── ... -├── src # source code. Can use the latest features (es2015(6)/es2016(7)) in JavaScript. -│   ├── index.js -│   └── ... -├── tests # testes folder +│ ├── index.js +│ └── index.min.js +├── lib # auto-generated source tree, transpiled using babel. Makes the code es5 compatible +│ ├── index.js +│ └── ... +├── src # source code. Can use the latest features (ES2015) in JavaScript. +│ ├── index.js +│ └── ... +├── test # tests folder +│ ├── node.js # Node specific test setup +│ ├── browser.js # Browser specific test setup +│ ├── mytest.spec.js # All files ending in .spec.js are considered test files to be run +│ └── ... ├── package.json ├── README.md ├── CONTRIBUTING.md @@ -43,18 +161,26 @@ A project structure should have the following components └── node_modules ``` -Inside the package.json, the main file exported is the one from the auto-generated source tree, transpiled using babel. While the original gets pointed by the `jsnext:main` key. +##### Default `require` + +Inside the package.json, the main file exported is the one from the auto-generated source tree, +transpiled using babel. While the original gets pointed by the `jsnext:main` key. ```JavaScript -// ... "main": "lib/index.js", "jsnext:main": "src/index.js", -// ... ``` -The original source tree can be still required in the conventioal CommonJS way, using `require('module-name/src')`. +##### Continuous integration + +There should be `.travis.yml` and `circle.yml` configuration file present and both services +should be enabled on the repository. Here you can find samples for [travis](examples/travis.example.yml) +and [circle](examples/circle.example.yml). + +##### `.gitignore` -To avoid checking in the dist and lib folders, the contents of the `.gitignore` file should at least be: +To avoid checking in the dist and lib folders, the `.gitignore` file should +at least contain ``` dist @@ -64,55 +190,87 @@ lib coverage ``` -The browser minified versions should be publishes to IPFS and npmcdn, offering a convinient way for a developer to include the module through a script tag. +##### Dependency management -## Code style +We use: -We follow the [JavaScript Standard codestyle](http://github.com/feross/standard) accross our JavaScript projects. To learn more about the codestyle, visit [standardjs.com](http://standardjs.com). +- [david-dm](https://david-dm.org/) +- [greenkeeper](http://greenkeeper.io/) to keep your dependencies up to date. -## Testing libraries +##### PreCommit -- Testing Node.js: [mocha](https://mochajs.org/) -- Assertions: [chai](http://chaijs.com/) -- Testing browser: [karma](https://karma-runner.github.io/0.13/index.html) and reusing the tests built for Node.js with [mocha](https://mochajs.org/) +We also use [precommit](https://www.npmjs.com/package/pre-commit) to enforce running +code style verification and tests on every commit. So you should have -## Code coverage +```json +"pre-commit": [ + "lint", + "test" +] +``` -We use [Istanbul](http://npmjs.org/istanbul) for code coverage checking, reusing tests that were made using mocha. +in your `package.json` -## Building a es5 version -// WIP +#### ...for consumers -## Building a dist version for the browser +Consumers of our modules should not have to think about our set up, +unless they want to. +So we provide four different ways of consuming our code. -// WIP +For use in the browser through script tags there is regular and a minified version in the npm release. +An example of using those is through [npmcdn](https://npmcdn.com/), -- Browser building: [webpack](https://webpack.github.io/) or [browserify](http://browserify.org/). +```html + + +``` -## Continuous integration +If you install the module through npm and require it, you receive the +ES5 version ready to be used in Node.js or a module bundler like browserify. -Our picks are: +```js +var API = require(‘ipfs-api’) +``` -- [circle CI](https://circleci.com) -- [travis CI](https://travis-ci.org) +If you use module bundler that understands ES2015 like webpack@2 or +rollup you can use this syntax to get the original ES2015 source. -## Dependencies management +```js +const API = require(‘ipfs-api/src’) +``` -We use: +### FAQ -- [david-dm](https://david-dm.org/) -- [greenkeeper](http://greenkeeper.io/) to keep your deps up to date. +#### Why are you not using XYZ? + +There are two possibilities, either it didn’t work out for us, or +we don’t know about it. If you think we might have missed it please tell us, +but please believe us if we say we tried and it didn’t work for us. + +#### Ugh, gulp why not use simple npm scripts? + +Gulp is not a hard dependency, it’s just a simple way to structure our tasks +at the moment. Usually projects only depend on the dignified binaries +completely hiding the fact that we are using gulp under the hood. So we are +free if we want to switch it out without any issues. We all enjoy npm +scripts, and are using them to call the dignified binaries, but there +is no nice way of sharing them yet. + +#### Where are all the semicolons? -## Tasks and task runners +Our linting rules are compatible with [standard](https://github.com/feross/standard) +which has many examples on documentation on this. Please go there and read it. -We use a mix of `npm scripts` for small tasks (small projects) and for more complex tasks, our task runner of choice is [gulp](http://gulpjs.com/). +#### Why are you bothering with ES2015 and all this build setup? -We also use [precommit](https://www.npmjs.com/package/pre-commit) to enforce running code style verification and tests on every commit. +We want to see the web move forward, and some of us enjoy writing +their JavaScript with things like const and arrow functions. -# Project example +#### Why are doing this? -// WIP +Because it saves us hours every single day. Hours in which we don’t +have to think about these things or argue with someone about why we are doing it. # Contributing From 0a821918d331a249ab404ffdc5fb7dc4a8d5adcc Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 25 Mar 2016 12:09:29 +0100 Subject: [PATCH 09/19] Small fixes --- js-project-guidelines.md | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/js-project-guidelines.md b/js-project-guidelines.md index bda48a9c..9c256b4b 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -25,7 +25,7 @@ Out goals for each JavaScript project are: ## Guidelines -Over time, we've researchd the options available in the JavaScript ecosystem +Over time, we've researched the options available in the JavaScript ecosystem and these is the collection of our design and implementation decisions, when it comes to: project stucture, code style, ci, tests, tasks and dependency management. @@ -51,17 +51,17 @@ large target to hit, so our tests need to reflect that. Testing for Node.js is straightforward for the most part. The browser though wants a bit more love. So we are using [karma](http://karma-runner.github.io) to automate the test execution in the browser, as we are familiar with -it (given I'm the maintainer of it). +it and it has been working very well for us. To reduce friction and overhead, we share the test framework [mocha](http://mochajs.org/) and the assertion library [chai](http://chaijs.com/) between the browser and Node.js. #### c) Build -There are a lot of build systems out there, and I have used my -fair share of them in the past ([r.js](http://requirejs.org/), [rollup](http://rollupjs.org/), [browserify](http://browserify.org/), [webpack](http://webpack.github.io/)). They all have their strength and weaknesses and at the +There are a lot of build systems out there, and we have tested our fair share of them +in the past. They all have their strength and weaknesses and at the end of the day it matters that the tool does what you want it to do. For -that reason we settled on using webpack as it gives us a large control over +that reason we settled on using [webpack](http://webpack.github.io/) as it gives us a large control over every detail when bundling and we feel quite comfortable with it at this point. @@ -80,17 +80,15 @@ versions for (hopefully) everyone to use. #### d) Release -This is actually quite simple, we have a [gulp](http://gulpjs.com/) task -that runs everything needed for a release so we don't miss -anything of the above mentioned things. It will: +Releasing a new version entails 1. Run linting -2. Run the tests -3. Build all versions +2. Run all tests +3. Build all three different versions 4. Bump the version in package.json 5. Commit the version bump 6. Create a git tag -7. Push this to github +7. Push to github 8. Publish to npm ### Day to day operations... From af1cde4cb278aba88b39df157b5467cb839d3f8c Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Fri, 25 Mar 2016 21:08:52 +0100 Subject: [PATCH 10/19] Some more improvements --- js-project-guidelines.md | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/js-project-guidelines.md b/js-project-guidelines.md index 9c256b4b..102a50fb 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -13,9 +13,10 @@ The creation of this document came as a result of the hurdles that the IPFS comm Out goals for each JavaScript project are: -- Should be browser compatible, possible exceptions here are - - access to the file system - - native bindings. +- Should be browser compatible, possible exceptions are, + - access to the file system, + - native bindings or + - network transports that are not available in the browser. - Must not break CommonJS `require`. This means that if someone requires a JavaScript module from the IPFS ecosystem, they should be able to require it and use browserify, webpack and other bundlers directly, without having to worry @@ -284,4 +285,23 @@ Any IPFS JavaScript project follows the same [Code of Conduct](https://github.co # References - Resources and good reads -- Comparission between WebPack, browserify, requirejs, jspm and rollup - https://github.com/webpack/docs/wiki/comparison +- Comparission between WebPack, browserify, requirejs, jspm and rollup - [https://github.com/webpack/docs/wiki/comparison](https://github.com/webpack/docs/wiki/comparison) +- [The cost of transpiling ES2015 in 2016](https://github.com/samccone/The-cost-of-transpiling-es2015-in-2016) +- [standardjs.com](http://standardjs.com/) + + +# Acknowledgment + +This project would not be possible without the hard work of many many people. So a big shout out to all contributors of these projects + +- [eslint](https://github.com/eslint/eslint/graphs/contributors) +- [standard](https://github.com/feross/standard/graphs/contributors) +- [karma](https://github.com/karma-runner/karma/graphs/contributors) +- [mocha](https://github.com/mochajs/mocha/graphs/contributors) +- [chai](https://github.com/chaijs/chai/graphs/contributors) +- [webpack](https://github.com/webpack/webpack/graphs/contributors) +- [babel](https://github.com/babel/babel/graphs/contributors) + + + + From f66ce1b6807d67aa8ca224b5fde7574f08f17420 Mon Sep 17 00:00:00 2001 From: David Dias Date: Mon, 28 Mar 2016 14:07:23 -0400 Subject: [PATCH 11/19] david review --- js-project-guidelines.md | 133 ++++++++++++++------------------------- 1 file changed, 47 insertions(+), 86 deletions(-) diff --git a/js-project-guidelines.md b/js-project-guidelines.md index 102a50fb..0012b01e 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -1,78 +1,56 @@ -# JavaScript projects guidelines +Guidelines for JavaScript project +================================= -> This document contains the guidelines that must be followed for JavaScript -> projects under the IPFS org. These guidelines result from battle earned experience -> in building JavaScript applications and modules that are reusable, work in the browser -> and that offer a good UX for developers and consumers. +> This document contains the guidelines that shall be followed for JavaScript projects under the IPFS org. These guidelines result from battle earned experience in building JavaScript applications and modules that are reusable, work in the browser and that offer a good UX for developers and consumers. ## Motivation -The creation of this document came as a result of the hurdles that the IPFS community had to go through, in order to build a JavaScript codebase that is welcoming in supporting new features to increase productivity and developer happiness, without the trading off the interopability with other existent and predominant tools. +The IPFS community has faced several challenges through the process of creating a JavaScript implementation of IPFS and satellite modules that contribute to the IPFS ecosystem on JavaScript land. These hurdles were overcome through a process of continuous experimentation, iteration and constant feedback, so that the JavaScript codebase developed could run in the latest versions of Node.js and modern Browsers, without locking it to a subset of the JavaScript language. In fact, one of the main concerns of setting these guidelines is that the path for experimentation is open, being that es6, es7, typescript, etc. ## Goals -Out goals for each JavaScript project are: +In each JavaScript project/module, our goal is to ensure: -- Should be browser compatible, possible exceptions are, - - access to the file system, - - native bindings or - - network transports that are not available in the browser. -- Must not break CommonJS `require`. This means that if someone requires - a JavaScript module from the IPFS ecosystem, they should be able to require it - and use browserify, webpack and other bundlers directly, without having to worry - how much shims have to be added to make it work. -- Make the UX of contributing and developing great with good guides, saving a - lot of time in discussions that can be used for productivity. +- Browser compatibility, possible exceptions are: + - access to the file system. + - native bindings. + - network transports(uTP, udt, curveCP, etc) that are not available in the browser. +- Must not break CommonJS `require`. This means that if someone requires a JavaScript module from the IPFS ecosystem, they should be able to require it and use browserify, webpack or other bundler, without having to worry on adding special shims for what is internal to the module itself. +- Make the UX to contribute and develop great, including a good guide, saving a considerable amount of time that can be use for productivity. -## Guidelines +## The guidelines -Over time, we've researched the options available in the JavaScript ecosystem -and these is the collection of our design and implementation decisions, -when it comes to: project stucture, code style, ci, tests, tasks and dependency management. +We've researched and experimented with the options available in the JavaScript ecosystem. This document describes the collection of the design and implementation decisions that have been made, when it comes to: -These tools are not set in stone, nor do we plan in any way of halting -our search to improve them. But we do plan on stopping ourselves -from repeating the same research every time we create a new module. +- project stucture. +- code style. +- continuous integration. +- tests. +- tasks (asset pipeline, transpiling, releasing, etc). +- dependency management. + +Our tool picks for each of this dimensions are not set in stone, nor do we plan in any way of halting our search to improve them. #### a) Linting & Code Style -We have been using [standard](https://github.com/feross/standard) as the -code style of our choice for some time now and are happy with it. -The only addition that we are adding is an enforced use of -[strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) to avoid issues we had when using ES2015 -features outside of strict mode. For added flexibility we are using -[eslint](http://eslint.org/), with the [eslint-config-standard](https://github.com/feross/eslint-config-standard) instead of the regular [standard module](https://github.com/feross/standard). +IPFS JavaScript projects default to [standard](https://github.com/feross/standard) codestyle. However we've added an extra rule to enforce the use of [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) to avoid issues we had when using ES2015 +features outside of strict mode. We enforce this rule by using [eslint](http://eslint.org/) and extending [standard module](https://github.com/feross/standard) with the [eslint-config-standard](https://github.com/feross/eslint-config-standard). #### b) Test -All the code we write is expected to run in browser as well as in Node.js. -We restrict ourselves to Node.js 4 and 5 and the latest stable releases -of Chrome, Firefox, Safari and Edge at the moment. Even this is quite a -large target to hit, so our tests need to reflect that. Testing for Node.js -is straightforward for the most part. The browser though wants a -bit more love. So we are using [karma](http://karma-runner.github.io) -to automate the test execution in the browser, as we are familiar with -it and it has been working very well for us. +By default, the `js-ipfs` codebase is designed to be run in the browser and Node.js, this means we require to test in both platforms. Currently, we run tests on Node.js 4 and 5 and the latest stable releases of Chrome, Firefox, Safari and Edge . Even this is quite a large target to hit, so our tests need to reflect that. Testing for Node.js is straightforward for the most part. The browser though wants a bit more love. So we are using [karma](http://karma-runner.github.io) to automate the test execution in the browser, as we are familiar with it and it has been working very well for us. + +To reduce friction and overhead, we share the test framework [mocha](http://mochajs.org/) and the assertion library [chai](http://chaijs.com/) between the browser and Node.js. -To reduce friction and overhead, we share the test -framework [mocha](http://mochajs.org/) and the assertion library [chai](http://chaijs.com/) between the browser and Node.js. +Note that projects that are not necessarily part of the `js-ipfs` ecosystem (such as [registry-mirror](https://github.com/diasdavid/registry-mirror) and others), don't require browser testing. #### c) Build -There are a lot of build systems out there, and we have tested our fair share of them -in the past. They all have their strength and weaknesses and at the -end of the day it matters that the tool does what you want it to do. For -that reason we settled on using [webpack](http://webpack.github.io/) as it gives us a large control over -every detail when bundling and we feel quite comfortable with it at this -point. +When it came to build systems, we had a considerable amount of options at our disposal. After a throughout process of experimentation, tinkering and learning from other open source projects, we've settled on [webpack](http://webpack.github.io/). It gives us a large control over every detail when bundling and we feel quite comfortable with it at this point. -As we are using some features of ES2015 we also need something to ensure -our code runs on the platforms that do not have full support yet. For this -[babel](http://babeljs.io/) is the tool of our choice. +As we are using some features of ES2015 we also need something to ensure our code runs on the platforms that do not have full support yet. For this [babel](http://babeljs.io/) is the tool of our choice. -When others consume our code though, we don’t want to enforce these choices -on them. So that’s why our build process in dignified.js creates multiple -versions for (hopefully) everyone to use. +When others consume our code though, we don’t want to enforce these choices on them. So that’s why our build process in `dignified.js` creates multiple versions for everyone to use. - __Raw ES2015 version__, ready to be consumed by platforms that understand Node.js based require and most of ES2015. - __Raw ES5 version__, ready to be consumed by platforms that understand Node.js based require and ES5. @@ -81,11 +59,11 @@ versions for (hopefully) everyone to use. #### d) Release -Releasing a new version entails +Releasing a new version entails: 1. Run linting 2. Run all tests -3. Build all three different versions +3. Build all three different versions described on section c) 4. Bump the version in package.json 5. Commit the version bump 6. Create a git tag @@ -111,7 +89,7 @@ $ dignified-release minor $ dignified-release ``` -these can be setup in your package.json as npm scripts: +If you prefer using npm scripts, you can set them up in your package.json: ```json { @@ -126,7 +104,7 @@ these can be setup in your package.json as npm scripts: } ``` -and you also need to add it your `devDependencies` by running +You also need to add it your `devDependencies` by running ```sh $ npm install --save-dev dignified.js @@ -172,14 +150,12 @@ transpiled using babel. While the original gets pointed by the `jsnext:main` key ##### Continuous integration -There should be `.travis.yml` and `circle.yml` configuration file present and both services -should be enabled on the repository. Here you can find samples for [travis](examples/travis.example.yml) +There should be `.travis.yml` and `circle.yml` configuration file present and both services should be enabled on the repository. Here you can find samples for [travis](examples/travis.example.yml) and [circle](examples/circle.example.yml). ##### `.gitignore` -To avoid checking in the dist and lib folders, the `.gitignore` file should -at least contain +To avoid checking in the dist and lib folders, the `.gitignore` file should at least contain ``` dist @@ -198,8 +174,7 @@ We use: ##### PreCommit -We also use [precommit](https://www.npmjs.com/package/pre-commit) to enforce running -code style verification and tests on every commit. So you should have +We also use [precommit](https://www.npmjs.com/package/pre-commit) to enforce running code style verification and tests on every commit. So you should have ```json "pre-commit": [ @@ -213,11 +188,10 @@ in your `package.json` #### ...for consumers -Consumers of our modules should not have to think about our set up, -unless they want to. -So we provide four different ways of consuming our code. +Consumers of our modules should not have to think about our set up, unless they want to. So we provide four different ways of consuming our code. For use in the browser through script tags there is regular and a minified version in the npm release. + An example of using those is through [npmcdn](https://npmcdn.com/), ```html @@ -225,51 +199,39 @@ An example of using those is through [npmcdn](https://npmcdn.com/), ``` -If you install the module through npm and require it, you receive the -ES5 version ready to be used in Node.js or a module bundler like browserify. +If you install the module through npm and require it, you receive the ES5 version ready to be used in Node.js or a module bundler like browserify. ```js var API = require(‘ipfs-api’) ``` -If you use module bundler that understands ES2015 like webpack@2 or -rollup you can use this syntax to get the original ES2015 source. +If you use module bundler that understands ES2015 like webpack@2 or rollup you can use this syntax to get the original ES2015 source. ```js const API = require(‘ipfs-api/src’) ``` -### FAQ +### FA #### Why are you not using XYZ? -There are two possibilities, either it didn’t work out for us, or -we don’t know about it. If you think we might have missed it please tell us, -but please believe us if we say we tried and it didn’t work for us. +There are two possibilities, either it didn’t work out for us, or we don’t know about it. If you think we might have missed it please tell us, but please believe us if we say we tried and it didn’t work for us. #### Ugh, gulp why not use simple npm scripts? -Gulp is not a hard dependency, it’s just a simple way to structure our tasks -at the moment. Usually projects only depend on the dignified binaries -completely hiding the fact that we are using gulp under the hood. So we are -free if we want to switch it out without any issues. We all enjoy npm -scripts, and are using them to call the dignified binaries, but there -is no nice way of sharing them yet. +Gulp is not a hard dependency, it’s just a simple way to structure our tasks at the moment. Usually projects only depend on the dignified binaries completely hiding the fact that we are using gulp under the hood. So we are free if we want to switch it out without any issues. We all enjoy npm scripts, and are using them to call the dignified binaries, but there is no nice way of sharing them yet. #### Where are all the semicolons? -Our linting rules are compatible with [standard](https://github.com/feross/standard) -which has many examples on documentation on this. Please go there and read it. +Our linting rules are compatible with [standard](https://github.com/feross/standard) which has many examples on documentation on this. Please go there and read it. #### Why are you bothering with ES2015 and all this build setup? -We want to see the web move forward, and some of us enjoy writing -their JavaScript with things like const and arrow functions. +We want to see the web move forward, and some of us enjoy writing their JavaScript with things like const and arrow functions. #### Why are doing this? -Because it saves us hours every single day. Hours in which we don’t -have to think about these things or argue with someone about why we are doing it. +Because it saves us hours every single day. Hours in which we don’t have to think about these things or argue with someone about why we are doing it. # Contributing @@ -289,7 +251,6 @@ Any IPFS JavaScript project follows the same [Code of Conduct](https://github.co - [The cost of transpiling ES2015 in 2016](https://github.com/samccone/The-cost-of-transpiling-es2015-in-2016) - [standardjs.com](http://standardjs.com/) - # Acknowledgment This project would not be possible without the hard work of many many people. So a big shout out to all contributors of these projects From 40b278d578d65b19196cb60c17b165c2de26bbb1 Mon Sep 17 00:00:00 2001 From: David Dias Date: Thu, 31 Mar 2016 17:55:22 -0400 Subject: [PATCH 12/19] update the document, based on feedback --- js-project-guidelines.md | 66 +++++++++++++++------------------------- 1 file changed, 24 insertions(+), 42 deletions(-) diff --git a/js-project-guidelines.md b/js-project-guidelines.md index 0012b01e..a5d06807 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -1,26 +1,22 @@ Guidelines for JavaScript project ================================= -> This document contains the guidelines that shall be followed for JavaScript projects under the IPFS org. These guidelines result from battle earned experience in building JavaScript applications and modules that are reusable, work in the browser and that offer a good UX for developers and consumers. - -## Motivation - -The IPFS community has faced several challenges through the process of creating a JavaScript implementation of IPFS and satellite modules that contribute to the IPFS ecosystem on JavaScript land. These hurdles were overcome through a process of continuous experimentation, iteration and constant feedback, so that the JavaScript codebase developed could run in the latest versions of Node.js and modern Browsers, without locking it to a subset of the JavaScript language. In fact, one of the main concerns of setting these guidelines is that the path for experimentation is open, being that es6, es7, typescript, etc. +> This documents presents a collection of notes, tools and other details that we found useful during the process of writing the IPFS JavaScript codebase. **We do not want to enforce any rule, you can see them as rules of thumb of useful directions when building your JavaScript modules**, as these guidelines were built through battle earned experience when getting the IPFS modules to work accross the JavaScript ecosystem, that is, the browser and Node.js, without breaking a specific subset of the ecosystem, for e.g. WebPack or browserify users. ## Goals -In each JavaScript project/module, our goal is to ensure: +For the majority of our JavaScript projects, our goals are: -- Browser compatibility, possible exceptions are: +- Browser compatibility with the possible exceptions being: - access to the file system. - native bindings. - network transports(uTP, udt, curveCP, etc) that are not available in the browser. -- Must not break CommonJS `require`. This means that if someone requires a JavaScript module from the IPFS ecosystem, they should be able to require it and use browserify, webpack or other bundler, without having to worry on adding special shims for what is internal to the module itself. -- Make the UX to contribute and develop great, including a good guide, saving a considerable amount of time that can be use for productivity. +- Not break CommonJS `require`. This means that if someone requires a JavaScript module from the IPFS ecosystem, they should be able to require it and use browserify, webpack or other bundler, without having to worry on adding special shims for what is internal to the module itself. +- Make the UX to contribute and develop great. ## The guidelines -We've researched and experimented with the options available in the JavaScript ecosystem. This document describes the collection of the design and implementation decisions that have been made, when it comes to: +In any guidelines document, it is important to enphazise that these are mostly our rules of thumb from what we've built so far. Every single item that is presented here comes from a lot of experimentation, however that doesn't mean that there isn't a better way, this is simply what we found to work best for us. In this document you will find notes about: - project stucture. - code style. @@ -33,24 +29,21 @@ Our tool picks for each of this dimensions are not set in stone, nor do we plan #### a) Linting & Code Style -IPFS JavaScript projects default to [standard](https://github.com/feross/standard) codestyle. However we've added an extra rule to enforce the use of [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) to avoid issues we had when using ES2015 -features outside of strict mode. We enforce this rule by using [eslint](http://eslint.org/) and extending [standard module](https://github.com/feross/standard) with the [eslint-config-standard](https://github.com/feross/eslint-config-standard). - -#### b) Test +IPFS JavaScript projects default to [standard](https://github.com/feross/standard) code style. It is a great and clean codestyle and its adoption is increasing significantly, making the code that we write familiar to the majority of the developers. -By default, the `js-ipfs` codebase is designed to be run in the browser and Node.js, this means we require to test in both platforms. Currently, we run tests on Node.js 4 and 5 and the latest stable releases of Chrome, Firefox, Safari and Edge . Even this is quite a large target to hit, so our tests need to reflect that. Testing for Node.js is straightforward for the most part. The browser though wants a bit more love. So we are using [karma](http://karma-runner.github.io) to automate the test execution in the browser, as we are familiar with it and it has been working very well for us. +However we've added an extra linting rule hto enforce the use of [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) to avoid issues we had when using ES2015 features outside of strict mode. We enforce this rule by using [eslint](http://eslint.org/) and extending [standard module](https://github.com/feross/standard) with the [eslint-config-standard](https://github.com/feross/eslint-config-standard). -To reduce friction and overhead, we share the test framework [mocha](http://mochajs.org/) and the assertion library [chai](http://chaijs.com/) between the browser and Node.js. +#### b) Test -Note that projects that are not necessarily part of the `js-ipfs` ecosystem (such as [registry-mirror](https://github.com/diasdavid/registry-mirror) and others), don't require browser testing. +Since `js-ipfs` is meant to be both a Node.js and Browser app, we strongly recommend to have tests that run in both platforms, always. For most cases, we use [mocha](http://mochajs.org) to run write the tests and [karma](http://karma-runner.github.io) to automate the test execution in the browser. This solution has been extremely convinient. #### c) Build -When it came to build systems, we had a considerable amount of options at our disposal. After a throughout process of experimentation, tinkering and learning from other open source projects, we've settled on [webpack](http://webpack.github.io/). It gives us a large control over every detail when bundling and we feel quite comfortable with it at this point. +In most IPFS JavaScript projects, you will see that We've settled on [webpack](http://webpack.github.io/) for bundling the JavaScript. It adds a greater overhead when it comes to configuration, because since most projects share the same needs, this configuration can be reused. -As we are using some features of ES2015 we also need something to ensure our code runs on the platforms that do not have full support yet. For this [babel](http://babeljs.io/) is the tool of our choice. +Where ES2015 is used, we managed to ensure that the code continues to run in every platform by transpiling it, if necessary, with [babel](http://babeljs.io/). -When others consume our code though, we don’t want to enforce these choices on them. So that’s why our build process in `dignified.js` creates multiple versions for everyone to use. +Nevertheless, and as described earlier in the document, we want to make sure users can use the modules without having to transpile and shim the modules themselves, so we've making available, with every module: - __Raw ES2015 version__, ready to be consumed by platforms that understand Node.js based require and most of ES2015. - __Raw ES5 version__, ready to be consumed by platforms that understand Node.js based require and ES5. @@ -59,7 +52,7 @@ When others consume our code though, we don’t want to enforce these choices on #### d) Release -Releasing a new version entails: +Each time a new release happens, these are the steps we follow to make sure nothing gets left out: 1. Run linting 2. Run all tests @@ -72,6 +65,8 @@ Releasing a new version entails: ### Day to day operations... +We've created a tool to help us achieve all of the above, feel free to also use it for your projects. Feedback is appreciated! + #### ...for maintainers ##### Setting up `dignified.js` @@ -104,7 +99,7 @@ If you prefer using npm scripts, you can set them up in your package.json: } ``` -You also need to add it your `devDependencies` by running +You also need to add it your `devDependencies` by running: ```sh $ npm install --save-dev dignified.js @@ -112,8 +107,7 @@ $ npm install --save-dev dignified.js ##### Directory Structure -To reduce the amount of configuration dignified.js expects your source code to be in the src and -your test files in the test directory. +To reduce the amount of configuration dignified.js expects your source code to be in the src and your test files in the test directory. ```sh ├── dist # auto-generated by the transpile and minification task. @@ -140,8 +134,7 @@ your test files in the test directory. ##### Default `require` -Inside the package.json, the main file exported is the one from the auto-generated source tree, -transpiled using babel. While the original gets pointed by the `jsnext:main` key. +Inside the package.json, the main file exported is the one from the auto-generated source tree, transpiled using babel. While the original gets pointed by the `jsnext:main` key. ```JavaScript "main": "lib/index.js", @@ -150,14 +143,13 @@ transpiled using babel. While the original gets pointed by the `jsnext:main` key ##### Continuous integration -There should be `.travis.yml` and `circle.yml` configuration file present and both services should be enabled on the repository. Here you can find samples for [travis](examples/travis.example.yml) -and [circle](examples/circle.example.yml). +Here you can find samples for [travis](examples/travis.example.yml) and [circle](examples/circle.example.yml). ##### `.gitignore` To avoid checking in the dist and lib folders, the `.gitignore` file should at least contain -``` +```sh dist lib **/node_modules @@ -167,14 +159,13 @@ coverage ##### Dependency management -We use: - [david-dm](https://david-dm.org/) - [greenkeeper](http://greenkeeper.io/) to keep your dependencies up to date. -##### PreCommit +##### Pre-Commit -We also use [precommit](https://www.npmjs.com/package/pre-commit) to enforce running code style verification and tests on every commit. So you should have +[precommit](https://www.npmjs.com/package/pre-commit) helps us check code style run the tests on every commit. In your `package.json`: ```json "pre-commit": [ @@ -183,13 +174,8 @@ We also use [precommit](https://www.npmjs.com/package/pre-commit) to enforce run ] ``` -in your `package.json` - - #### ...for consumers -Consumers of our modules should not have to think about our set up, unless they want to. So we provide four different ways of consuming our code. - For use in the browser through script tags there is regular and a minified version in the npm release. An example of using those is through [npmcdn](https://npmcdn.com/), @@ -211,7 +197,7 @@ If you use module bundler that understands ES2015 like webpack@2 or rollup you c const API = require(‘ipfs-api/src’) ``` -### FA +### FAQ #### Why are you not using XYZ? @@ -262,7 +248,3 @@ This project would not be possible without the hard work of many many people. So - [chai](https://github.com/chaijs/chai/graphs/contributors) - [webpack](https://github.com/webpack/webpack/graphs/contributors) - [babel](https://github.com/babel/babel/graphs/contributors) - - - - From 20cab3de53c7726fcb037c1df99aed1b0281c21a Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Wed, 13 Apr 2016 16:44:56 -0400 Subject: [PATCH 13/19] Updates --- assets/CodeIsMoreLikeGuidelines.jpg | Bin 0 -> 73802 bytes js-project-guidelines.md | 154 ++++++++++++++++++---------- 2 files changed, 99 insertions(+), 55 deletions(-) create mode 100644 assets/CodeIsMoreLikeGuidelines.jpg diff --git a/assets/CodeIsMoreLikeGuidelines.jpg b/assets/CodeIsMoreLikeGuidelines.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5dddbf1b88cd892eb629455fe3c7e3e863e74074 GIT binary patch literal 73802 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
  • s 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

    }+Wnt~ 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 literal 0 HcmV?d00001 diff --git a/js-project-guidelines.md b/js-project-guidelines.md index a5d06807..53bedfd1 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -1,77 +1,111 @@ -Guidelines for JavaScript project +Guidelines for JavaScript projects ================================= -> This documents presents a collection of notes, tools and other details that we found useful during the process of writing the IPFS JavaScript codebase. **We do not want to enforce any rule, you can see them as rules of thumb of useful directions when building your JavaScript modules**, as these guidelines were built through battle earned experience when getting the IPFS modules to work accross the JavaScript ecosystem, that is, the browser and Node.js, without breaking a specific subset of the ecosystem, for e.g. WebPack or browserify users. - -## Goals +In any guidelines document, it is important to emphasize that these are mostly our shared consensus on protocol and etiquette from what we've built so far. Every single item that is presented here comes from a lot of experimentation. However, that doesn't mean that there isn't a better way. This is simply what we found to work best for us. In this document you will find notes about: + +- Project structure. +- Code style. +- Continuous integration. +- Tests. +- Tasks (asset pipeline, transpiling, releasing, etc). +- Dependency management. + +Our toolkit for each of these is not set in stone, and we don't plan to halt our constant search for better tools. + +Remember: + +![Barbossa's warning](assets/CodeIsMoreLikeGuidelines.jpg) + +### Table of Contents + +- [Goals and Expectations](#goals-and-expectations) +- [The Guidelines](#the-guidelines) + - [Linting & Code Style](#linting-&-code-style) + - [Test](#test) + - [Build](#build) + - [Release](#release) +- [Dignified.js](#dignifiedjs) + - [...for maintainers](#for-maintainers) + - [Setting up `dignified.js`](#setting-up-dignifiedjs) + - [Directory Structure](#directory-structure) + - [Default `require`](#default-require) + - [Continuous integration](#continuous-integration) + - [`.gitignore`](#gitignore) + - [Dependency management](#dependency-management) + - [Pre-Commit](#pre-commit) + - [...for consumers](#for-consumers) +- [FAQ](#faq) + - [Why are you not using XYZ?](#why-are-you-not-using-xyz) + - [Why not use simple npm scripts instead of gulp?](#why-not-use-simple-npm-scripts-instead-of-gulp) + - [Where are all the semicolons?](#where-are-all-the-semicolons) + - [Why are you bothering with ES2015 and all this build setup?](#why-are-you-bothering-with-es2015-and-all-this-build-setup) + - [Do I have to use ES2015 and Babel?](#do-i-have-to-use-es2015-and-babel) + - [Do I have to bundle everything with webpack?](#do-i-have-to-bundle-everything-with-webpack) + - [Why are you doing this?](#why-are-you-doing-this) +- [Contributing](#contributing) +- [Code of Conduct](#code-of-conduct) +- [References - Resources and good reads](#references---resources-and-good-reads) +- [Acknowledgment](#acknowledgment) + +## Goals and Expectations For the majority of our JavaScript projects, our goals are: -- Browser compatibility with the possible exceptions being: - - access to the file system. - - native bindings. - - network transports(uTP, udt, curveCP, etc) that are not available in the browser. -- Not break CommonJS `require`. This means that if someone requires a JavaScript module from the IPFS ecosystem, they should be able to require it and use browserify, webpack or other bundler, without having to worry on adding special shims for what is internal to the module itself. -- Make the UX to contribute and develop great. - -## The guidelines - -In any guidelines document, it is important to enphazise that these are mostly our rules of thumb from what we've built so far. Every single item that is presented here comes from a lot of experimentation, however that doesn't mean that there isn't a better way, this is simply what we found to work best for us. In this document you will find notes about: +- **Browser compatibility**, with the possible exceptions being: + - Access to the file system. + - Native bindings. + - Network transports (uTP, udt, curveCP, etc) that are not available in the browser. +- **Don't break CommonJS's** `require`. This means that if someone requires a JavaScript module from the IPFS ecosystem, they should be able to require it and use browserify, webpack or other bundlers without having to worry about adding special shims for module internals. +- **Encourage Contribution**. +- **Great UX** for everyone involved. -- project stucture. -- code style. -- continuous integration. -- tests. -- tasks (asset pipeline, transpiling, releasing, etc). -- dependency management. +## The Guidelines -Our tool picks for each of this dimensions are not set in stone, nor do we plan in any way of halting our search to improve them. - -#### a) Linting & Code Style +#### Linting & Code Style IPFS JavaScript projects default to [standard](https://github.com/feross/standard) code style. It is a great and clean codestyle and its adoption is increasing significantly, making the code that we write familiar to the majority of the developers. -However we've added an extra linting rule hto enforce the use of [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode) to avoid issues we had when using ES2015 features outside of strict mode. We enforce this rule by using [eslint](http://eslint.org/) and extending [standard module](https://github.com/feross/standard) with the [eslint-config-standard](https://github.com/feross/eslint-config-standard). +However, we've added an extra linting rule: Enforce the use of [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode). This avoids issues we had when using ES2015 features outside of strict mode. We enforce this rule by using [eslint](http://eslint.org/) and extending [standard module](https://github.com/feross/standard) with the [eslint-config-standard](https://github.com/feross/eslint-config-standard). -#### b) Test +#### Test -Since `js-ipfs` is meant to be both a Node.js and Browser app, we strongly recommend to have tests that run in both platforms, always. For most cases, we use [mocha](http://mochajs.org) to run write the tests and [karma](http://karma-runner.github.io) to automate the test execution in the browser. This solution has been extremely convinient. +Since `js-ipfs` is meant to be both a Node.js and Browser app, we strongly recommend having tests that run in both platforms, always. For most cases, we use [mocha](http://mochajs.org) to run write the tests and [karma](http://karma-runner.github.io) to automate the test execution in the browser. This solution has been extremely convenient. -#### c) Build +#### Build -In most IPFS JavaScript projects, you will see that We've settled on [webpack](http://webpack.github.io/) for bundling the JavaScript. It adds a greater overhead when it comes to configuration, because since most projects share the same needs, this configuration can be reused. +In most IPFS JavaScript projects, we use [webpack](http://webpack.github.io/) for bundling the JavaScript. It adds a greater overhead when it comes to configuration, but this configuration can be reused since most projects share the same needs. Where ES2015 is used, we managed to ensure that the code continues to run in every platform by transpiling it, if necessary, with [babel](http://babeljs.io/). -Nevertheless, and as described earlier in the document, we want to make sure users can use the modules without having to transpile and shim the modules themselves, so we've making available, with every module: +To make sure users can use the modules without having to transpile and shim the modules themselves, we've made the following available in most modules: - __Raw ES2015 version__, ready to be consumed by platforms that understand Node.js based require and most of ES2015. - __Raw ES5 version__, ready to be consumed by platforms that understand Node.js based require and ES5. - __Concatenated ES5 version__, ready to be consumed by browsers through a script tag, where size does not matter. - __Concatenated and minified ES5 version__, ready to be consumed by browsers through a script tag, where size matters. -#### d) Release +#### Release Each time a new release happens, these are the steps we follow to make sure nothing gets left out: 1. Run linting 2. Run all tests -3. Build all three different versions described on section c) -4. Bump the version in package.json +3. Build all three different versions described on the build +4. Bump the version in `package.json` 5. Commit the version bump 6. Create a git tag -7. Push to github +7. Push to GitHub 8. Publish to npm -### Day to day operations... +## Dignified.js -We've created a tool to help us achieve all of the above, feel free to also use it for your projects. Feedback is appreciated! +We've created a module to help us achieve all of the above with minimal effort. Feel free to also use it for your projects. Feedback is appreciated! #### ...for maintainers ##### Setting up `dignified.js` -There are a couple of binaries that dignified.js provides for you to use +There are a couple of binaries that `dignified.js` provides for you to use ```sh $ dignified-lint @@ -143,11 +177,11 @@ Inside the package.json, the main file exported is the one from the auto-generat ##### Continuous integration -Here you can find samples for [travis](examples/travis.example.yml) and [circle](examples/circle.example.yml). +Here you can find samples for [Travis](examples/travis.example.yml) and [circle](examples/circle.example.yml). ##### `.gitignore` -To avoid checking in the dist and lib folders, the `.gitignore` file should at least contain +To avoid checking in the dist and lib folders, the `.gitignore` file should at least contain: ```sh dist @@ -159,6 +193,7 @@ coverage ##### Dependency management +We suggest either of these: - [david-dm](https://david-dm.org/) - [greenkeeper](http://greenkeeper.io/) to keep your dependencies up to date. @@ -176,9 +211,9 @@ coverage #### ...for consumers -For use in the browser through script tags there is regular and a minified version in the npm release. +For use in the browser through script tags, there are regular and minified versions in the npm release. -An example of using those is through [npmcdn](https://npmcdn.com/), +You can use [npmcdn](https://npmcdn.com/) to include those: ```html @@ -191,53 +226,62 @@ If you install the module through npm and require it, you receive the ES5 versio var API = require(‘ipfs-api’) ``` -If you use module bundler that understands ES2015 like webpack@2 or rollup you can use this syntax to get the original ES2015 source. +If you use a module bundler that understands ES2015 like webpack@2 or rollup you can use this syntax to get the original ES2015 source. ```js const API = require(‘ipfs-api/src’) ``` -### FAQ +## FAQ + #### Why are you not using XYZ? -There are two possibilities, either it didn’t work out for us, or we don’t know about it. If you think we might have missed it please tell us, but please believe us if we say we tried and it didn’t work for us. +There are two possibilities: either it didn’t work out for us, or we don’t know about it. If you think we might have missed it please tell us, but please believe us if we say we tried and it didn’t work for us. -#### Ugh, gulp why not use simple npm scripts? +#### Why not use simple npm scripts instead of gulp? -Gulp is not a hard dependency, it’s just a simple way to structure our tasks at the moment. Usually projects only depend on the dignified binaries completely hiding the fact that we are using gulp under the hood. So we are free if we want to switch it out without any issues. We all enjoy npm scripts, and are using them to call the dignified binaries, but there is no nice way of sharing them yet. +Gulp is not a hard dependency. It’s just a simple way to structure our tasks at the moment. Usually projects only depend on the dignified binaries completely hiding the fact that we are using gulp under the hood. So we are free if we want to switch it out without any issues. We all enjoy npm scripts, and are using them to call the dignified binaries, but there is no nice way of sharing them yet. #### Where are all the semicolons? -Our linting rules are compatible with [standard](https://github.com/feross/standard) which has many examples on documentation on this. Please go there and read it. +Our linting rules are compatible with [standard](https://github.com/feross/standard), which has many examples on documentation on this. Please go there and read it if you're still curious. #### Why are you bothering with ES2015 and all this build setup? -We want to see the web move forward, and some of us enjoy writing their JavaScript with things like const and arrow functions. +We want to see the web move forward, and some of us enjoy writing their JavaScript with things like `const` and arrow functions. + +#### Do I have to use ES2015 and Babel and Dignified.js in my project? + +No. + +#### Do I have to bundle everything with webpack? + +No. But other people might ask you to at some point, so, maybe it's better to be prepared? -#### Why are doing this? +#### Why are you doing this? Because it saves us hours every single day. Hours in which we don’t have to think about these things or argue with someone about why we are doing it. -# Contributing +## Contributing -Follow these conventions described in this document. +Please follow the conventions described in this document. -When reporting a bug, if possible, provide a way to reproduce or even better, write a test that fails with your case +When reporting a bug, if possible, provide a way to reproduce (Or even better, write a test that fails with your case). -Always run tests before pushing and PR'ing your code +Always run tests before pushing and PR'ing your code. -# Code of Conduct +## Code of Conduct Any IPFS JavaScript project follows the same [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md) applied to the whole IPFS ecosystem. -# References - Resources and good reads +## References - Resources and good reads - Comparission between WebPack, browserify, requirejs, jspm and rollup - [https://github.com/webpack/docs/wiki/comparison](https://github.com/webpack/docs/wiki/comparison) - [The cost of transpiling ES2015 in 2016](https://github.com/samccone/The-cost-of-transpiling-es2015-in-2016) - [standardjs.com](http://standardjs.com/) -# Acknowledgment +## Acknowledgment This project would not be possible without the hard work of many many people. So a big shout out to all contributors of these projects From e0755951a1dae05c4e9bf0b4926ff4cc120e2973 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Wed, 13 Apr 2016 17:18:40 -0400 Subject: [PATCH 14/19] Dig >> aegir --- js-project-guidelines.md | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/js-project-guidelines.md b/js-project-guidelines.md index 53bedfd1..f4fc7168 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -24,9 +24,9 @@ Remember: - [Test](#test) - [Build](#build) - [Release](#release) -- [Dignified.js](#dignifiedjs) +- [AEgir.js](#aegirjs) - [...for maintainers](#for-maintainers) - - [Setting up `dignified.js`](#setting-up-dignifiedjs) + - [Setting up `aegir.js`](#setting-up-aegirjs) - [Directory Structure](#directory-structure) - [Default `require`](#default-require) - [Continuous integration](#continuous-integration) @@ -97,25 +97,25 @@ Each time a new release happens, these are the steps we follow to make sure noth 7. Push to GitHub 8. Publish to npm -## Dignified.js +## aegir.js We've created a module to help us achieve all of the above with minimal effort. Feel free to also use it for your projects. Feedback is appreciated! #### ...for maintainers -##### Setting up `dignified.js` +##### Setting up `aegir.js` -There are a couple of binaries that `dignified.js` provides for you to use +There are a couple of binaries that `aegir.js` provides for you to use ```sh -$ dignified-lint -$ dignified-test -$ dignified-test browser -$ dignified-test node -$ dignified-build -$ dignified-release major -$ dignified-release minor -$ dignified-release +$ aegir-lint +$ aegir-test +$ aegir-test browser +$ aegir-test node +$ aegir-build +$ aegir-release major +$ aegir-release minor +$ aegir-release ``` If you prefer using npm scripts, you can set them up in your package.json: @@ -123,12 +123,12 @@ If you prefer using npm scripts, you can set them up in your package.json: ```json { "scripts": { - "lint": "dignified-lint", - "build": "dignified-build", - "test": "dignified-test", - "test:node": "dignified-test node", - "test:browser": "dignified-test browser", - "release": "dignified-release" + "lint": "aegir-lint", + "build": "aegir-build", + "test": "aegir-test", + "test:node": "aegir-test node", + "test:browser": "aegir-test browser", + "release": "aegir-release" } } ``` @@ -136,12 +136,12 @@ If you prefer using npm scripts, you can set them up in your package.json: You also need to add it your `devDependencies` by running: ```sh -$ npm install --save-dev dignified.js +$ npm install --save-dev aegir.js ``` ##### Directory Structure -To reduce the amount of configuration dignified.js expects your source code to be in the src and your test files in the test directory. +To reduce the amount of configuration aegir.js expects your source code to be in the src and your test files in the test directory. ```sh ├── dist # auto-generated by the transpile and minification task. @@ -241,7 +241,7 @@ There are two possibilities: either it didn’t work out for us, or we don’t k #### Why not use simple npm scripts instead of gulp? -Gulp is not a hard dependency. It’s just a simple way to structure our tasks at the moment. Usually projects only depend on the dignified binaries completely hiding the fact that we are using gulp under the hood. So we are free if we want to switch it out without any issues. We all enjoy npm scripts, and are using them to call the dignified binaries, but there is no nice way of sharing them yet. +Gulp is not a hard dependency. It’s just a simple way to structure our tasks at the moment. Usually projects only depend on the aegir binaries completely hiding the fact that we are using gulp under the hood. So we are free if we want to switch it out without any issues. We all enjoy npm scripts, and are using them to call the aegir binaries, but there is no nice way of sharing them yet. #### Where are all the semicolons? @@ -251,7 +251,7 @@ Our linting rules are compatible with [standard](https://github.com/feross/stand We want to see the web move forward, and some of us enjoy writing their JavaScript with things like `const` and arrow functions. -#### Do I have to use ES2015 and Babel and Dignified.js in my project? +#### Do I have to use ES2015 and Babel and aegir.js in my project? No. From f5aa05f517894dbb9e2efcbdae049c58cd75dab3 Mon Sep 17 00:00:00 2001 From: David Dias Date: Thu, 14 Apr 2016 14:26:58 +0100 Subject: [PATCH 15/19] update circle.yml to know how to update chrome --- examples/circle.example.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/examples/circle.example.yml b/examples/circle.example.yml index 6d743236..434211a7 100644 --- a/examples/circle.example.yml +++ b/examples/circle.example.yml @@ -1,3 +1,12 @@ machine: node: version: stable + +dependencies: + pre: + - google-chrome --version + - wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - + - sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' + - sudo apt-get update + - sudo apt-get --only-upgrade install google-chrome-stable + - google-chrome --version From 0006131770057339549f7f11e1aff3d492e80ce7 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Thu, 14 Apr 2016 09:40:57 -0400 Subject: [PATCH 16/19] Edits --- js-project-guidelines.md | 46 ++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/js-project-guidelines.md b/js-project-guidelines.md index f4fc7168..500c7a91 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -18,15 +18,15 @@ Remember: ### Table of Contents -- [Goals and Expectations](#goals-and-expectations) -- [The Guidelines](#the-guidelines) +- [Goals](#goals) +- [Guidelines](#guidelines) - [Linting & Code Style](#linting-&-code-style) - - [Test](#test) - - [Build](#build) - - [Release](#release) -- [AEgir.js](#aegirjs) + - [Testing](#testing) + - [Building](#building) + - [Releasing](#releasing) +- [Aegir](#aegir) - [...for maintainers](#for-maintainers) - - [Setting up `aegir.js`](#setting-up-aegirjs) + - [Setting up `aegir`](#setting-up-aegir) - [Directory Structure](#directory-structure) - [Default `require`](#default-require) - [Continuous integration](#continuous-integration) @@ -47,7 +47,7 @@ Remember: - [References - Resources and good reads](#references---resources-and-good-reads) - [Acknowledgment](#acknowledgment) -## Goals and Expectations +## Goals For the majority of our JavaScript projects, our goals are: @@ -59,7 +59,7 @@ For the majority of our JavaScript projects, our goals are: - **Encourage Contribution**. - **Great UX** for everyone involved. -## The Guidelines +## Guidelines #### Linting & Code Style @@ -67,11 +67,11 @@ IPFS JavaScript projects default to [standard](https://github.com/feross/standar However, we've added an extra linting rule: Enforce the use of [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode). This avoids issues we had when using ES2015 features outside of strict mode. We enforce this rule by using [eslint](http://eslint.org/) and extending [standard module](https://github.com/feross/standard) with the [eslint-config-standard](https://github.com/feross/eslint-config-standard). -#### Test +#### Testing Since `js-ipfs` is meant to be both a Node.js and Browser app, we strongly recommend having tests that run in both platforms, always. For most cases, we use [mocha](http://mochajs.org) to run write the tests and [karma](http://karma-runner.github.io) to automate the test execution in the browser. This solution has been extremely convenient. -#### Build +#### Building In most IPFS JavaScript projects, we use [webpack](http://webpack.github.io/) for bundling the JavaScript. It adds a greater overhead when it comes to configuration, but this configuration can be reused since most projects share the same needs. @@ -84,7 +84,7 @@ To make sure users can use the modules without having to transpile and shim the - __Concatenated ES5 version__, ready to be consumed by browsers through a script tag, where size does not matter. - __Concatenated and minified ES5 version__, ready to be consumed by browsers through a script tag, where size matters. -#### Release +#### Releasing Each time a new release happens, these are the steps we follow to make sure nothing gets left out: @@ -97,15 +97,15 @@ Each time a new release happens, these are the steps we follow to make sure noth 7. Push to GitHub 8. Publish to npm -## aegir.js +## Aegir -We've created a module to help us achieve all of the above with minimal effort. Feel free to also use it for your projects. Feedback is appreciated! +We've created [a module](https://github.com/dignifiedquire/aegir) to help us achieve all of the above with minimal effort. Feel free to also use it for your projects. Feedback is appreciated! #### ...for maintainers -##### Setting up `aegir.js` +##### Setting up `aegir` -There are a couple of binaries that `aegir.js` provides for you to use +There are a couple of binaries that `aegir` provides for you to use ```sh $ aegir-lint @@ -136,12 +136,12 @@ If you prefer using npm scripts, you can set them up in your package.json: You also need to add it your `devDependencies` by running: ```sh -$ npm install --save-dev aegir.js +$ npm install --save-dev aegir ``` ##### Directory Structure -To reduce the amount of configuration aegir.js expects your source code to be in the src and your test files in the test directory. +To reduce the amount of configuration aegir expects your source code to be in the src and your test files in the test directory. ```sh ├── dist # auto-generated by the transpile and minification task. @@ -251,23 +251,23 @@ Our linting rules are compatible with [standard](https://github.com/feross/stand We want to see the web move forward, and some of us enjoy writing their JavaScript with things like `const` and arrow functions. -#### Do I have to use ES2015 and Babel and aegir.js in my project? +#### Do I have to use ES2015 and Babel and aegir in my project? No. #### Do I have to bundle everything with webpack? -No. But other people might ask you to at some point, so, maybe it's better to be prepared? +No. But other people might ask you to at some point, so it may be better to be prepared. #### Why are you doing this? -Because it saves us hours every single day. Hours in which we don’t have to think about these things or argue with someone about why we are doing it. +Because it saves us hours every single day. Hours in which we don’t have to think about these things or argue with someone about why we are doing it. This tooling is the result of a lot of effort, thought, and hard learning. Its goal is to minimize process road bumps and provide a unified low-friction workflow for contributors. ## Contributing Please follow the conventions described in this document. -When reporting a bug, if possible, provide a way to reproduce (Or even better, write a test that fails with your case). +When reporting a bug, if possible, provide a way for us to reproduce it (or even better, write a test that fails with your case). Always run tests before pushing and PR'ing your code. @@ -283,7 +283,7 @@ Any IPFS JavaScript project follows the same [Code of Conduct](https://github.co ## Acknowledgment -This project would not be possible without the hard work of many many people. So a big shout out to all contributors of these projects +This project would not be possible without the hard work of many many people. So a big shout out to all contributors of these projects: - [eslint](https://github.com/eslint/eslint/graphs/contributors) - [standard](https://github.com/feross/standard/graphs/contributors) From 59bf4ada4d6e119d58f89cf71000988a7b91f4b2 Mon Sep 17 00:00:00 2001 From: dignifiedquire Date: Mon, 18 Apr 2016 21:19:19 +0200 Subject: [PATCH 17/19] Latest coverage updates and npmignore --- examples/.gitignore | 31 +++++++++++++++++++++++++++++++ examples/.npmignore | 30 ++++++++++++++++++++++++++++++ examples/circle.example.yml | 1 + examples/travis.example.yml | 14 ++++++++++++++ js-project-guidelines.md | 33 ++++++++++++++++++++++----------- 5 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 examples/.gitignore create mode 100644 examples/.npmignore diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 00000000..f4239c46 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,31 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules + +# These are only shipped with npm +dist +lib \ No newline at end of file diff --git a/examples/.npmignore b/examples/.npmignore new file mode 100644 index 00000000..5155cf27 --- /dev/null +++ b/examples/.npmignore @@ -0,0 +1,30 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules + +# Do not ship tests +test \ No newline at end of file diff --git a/examples/circle.example.yml b/examples/circle.example.yml index 434211a7..83f9ae9d 100644 --- a/examples/circle.example.yml +++ b/examples/circle.example.yml @@ -2,6 +2,7 @@ machine: node: version: stable +# Up to date Chrome dependencies: pre: - google-chrome --version diff --git a/examples/travis.example.yml b/examples/travis.example.yml index 26c203ee..6784451e 100644 --- a/examples/travis.example.yml +++ b/examples/travis.example.yml @@ -1,11 +1,25 @@ +sudo: false language: node_js node_js: - 4 - 5 +# Make sure we have new NPM. +before_install: + - npm install -g npm + +script: + - npm run lint + - npm test + - npm run coverage + addons: firefox: 'latest' +# Setup xvfb for headless Firefox before_script: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start + +after_success: + - npm run coverage-publish diff --git a/js-project-guidelines.md b/js-project-guidelines.md index 500c7a91..add4f4cb 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -128,7 +128,9 @@ If you prefer using npm scripts, you can set them up in your package.json: "test": "aegir-test", "test:node": "aegir-test node", "test:browser": "aegir-test browser", - "release": "aegir-release" + "release": "aegir-release", + "coverage": "aegir-coverage", + "coverage-publish": "aegir-coverage publish" } } ``` @@ -163,6 +165,8 @@ To reduce the amount of configuration aegir expects your source code to be in th ├── CONTRIBUTING.md ├── circle.yml ├── .travis.yml +├── .npmignore +├── .gitignore └── node_modules ``` @@ -179,17 +183,24 @@ Inside the package.json, the main file exported is the one from the auto-generat Here you can find samples for [Travis](examples/travis.example.yml) and [circle](examples/circle.example.yml). +We also use [coveralls.io](https://coveralls.io/) to automatically publish coverage reports. This is done from travis using + +```yml +script: + - npm run coverage +after_success: + - npm run coverage-publish +``` + ##### `.gitignore` -To avoid checking in the dist and lib folders, the `.gitignore` file should at least contain: +To avoid checking in unwanted files, the `.gitignore` file should follow +this [example](examples/.gitignore). -```sh -dist -lib -**/node_modules -**/*.log -coverage -``` +##### `.npmignore` + +Npm uses the `.gitignore` by default, so we have to add a `.npmignore` file +to ensure we actually ship `lib` and `dist` files. You can use this [example](examples/.npmignore) to get started. ##### Dependency management @@ -223,13 +234,13 @@ You can use [npmcdn](https://npmcdn.com/) to include those: If you install the module through npm and require it, you receive the ES5 version ready to be used in Node.js or a module bundler like browserify. ```js -var API = require(‘ipfs-api’) +var API = require('ipfs-api') ``` If you use a module bundler that understands ES2015 like webpack@2 or rollup you can use this syntax to get the original ES2015 source. ```js -const API = require(‘ipfs-api/src’) +const API = require('ipfs-api/src') ``` ## FAQ From 939c7500a9b2a0f1dd1c42d5d55d5fee77cf85bf Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Wed, 20 Apr 2016 13:39:26 -0400 Subject: [PATCH 18/19] Final copy edits --- js-project-guidelines.md | 42 ++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/js-project-guidelines.md b/js-project-guidelines.md index add4f4cb..0564d853 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -1,7 +1,7 @@ Guidelines for JavaScript projects ================================= -In any guidelines document, it is important to emphasize that these are mostly our shared consensus on protocol and etiquette from what we've built so far. Every single item that is presented here comes from a lot of experimentation. However, that doesn't mean that there isn't a better way. This is simply what we found to work best for us. In this document you will find notes about: +These guidelines reflect our shared consensus on protocol and etiquette from what we've built so far. Every single item that is presented here is the result of lots of experimentation. However, that doesn't mean that there isn't a better way to do things. What we have below is simply what we've found to work best for us. In this document you will find notes about: - Project structure. - Code style. @@ -10,15 +10,16 @@ In any guidelines document, it is important to emphasize that these are mostly o - Tasks (asset pipeline, transpiling, releasing, etc). - Dependency management. -Our toolkit for each of these is not set in stone, and we don't plan to halt our constant search for better tools. +Our toolkit for each of these is not set in stone, and we don't plan to halt our constant search for better tools. Get in touch if you've got ideas. -Remember: +Also, remember: ![Barbossa's warning](assets/CodeIsMoreLikeGuidelines.jpg) ### Table of Contents - [Goals](#goals) +- [Contributing](#contributing) - [Guidelines](#guidelines) - [Linting & Code Style](#linting-&-code-style) - [Testing](#testing) @@ -42,28 +43,35 @@ Remember: - [Do I have to use ES2015 and Babel?](#do-i-have-to-use-es2015-and-babel) - [Do I have to bundle everything with webpack?](#do-i-have-to-bundle-everything-with-webpack) - [Why are you doing this?](#why-are-you-doing-this) -- [Contributing](#contributing) - [Code of Conduct](#code-of-conduct) - [References - Resources and good reads](#references---resources-and-good-reads) - [Acknowledgment](#acknowledgment) ## Goals -For the majority of our JavaScript projects, our goals are: +For the majority of our JavaScript projects, our goals are to: -- **Browser compatibility**, with the possible exceptions being: +- **Ensure browser compatibility**, with the possible exceptions being: - Access to the file system. - Native bindings. - Network transports (uTP, udt, curveCP, etc) that are not available in the browser. - **Don't break CommonJS's** `require`. This means that if someone requires a JavaScript module from the IPFS ecosystem, they should be able to require it and use browserify, webpack or other bundlers without having to worry about adding special shims for module internals. - **Encourage Contribution**. -- **Great UX** for everyone involved. +- **Have great UX** for everyone involved. + +## Contributing + +Please follow the conventions described in this document. + +When reporting a bug, if possible, provide a way for us to reproduce it (or even better, write a test that fails with your case). + +Always run tests before pushing and PR'ing your code. ## Guidelines #### Linting & Code Style -IPFS JavaScript projects default to [standard](https://github.com/feross/standard) code style. It is a great and clean codestyle and its adoption is increasing significantly, making the code that we write familiar to the majority of the developers. +IPFS JavaScript projects default to [standard](https://github.com/feross/standard) code style. It is a clean codestyle, and its adoption is increasing significantly, making the code that we write familiar to the majority of the developers. However, we've added an extra linting rule: Enforce the use of [strict mode](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode). This avoids issues we had when using ES2015 features outside of strict mode. We enforce this rule by using [eslint](http://eslint.org/) and extending [standard module](https://github.com/feross/standard) with the [eslint-config-standard](https://github.com/feross/eslint-config-standard). @@ -172,7 +180,7 @@ To reduce the amount of configuration aegir expects your source code to be in th ##### Default `require` -Inside the package.json, the main file exported is the one from the auto-generated source tree, transpiled using babel. While the original gets pointed by the `jsnext:main` key. +Inside the package.json, the main file exported is the one from the auto-generated source tree, transpiled using babel. The original should be pointed to by the `jsnext:main` key. ```JavaScript "main": "lib/index.js", @@ -181,9 +189,9 @@ Inside the package.json, the main file exported is the one from the auto-generat ##### Continuous integration -Here you can find samples for [Travis](examples/travis.example.yml) and [circle](examples/circle.example.yml). +You can find samples for [Travis](examples/travis.example.yml) and [circle](examples/circle.example.yml) in the examples folder. -We also use [coveralls.io](https://coveralls.io/) to automatically publish coverage reports. This is done from travis using +We also use [coveralls.io](https://coveralls.io/) to automatically publish coverage reports. This is done from travis using this: ```yml script: @@ -195,7 +203,7 @@ after_success: ##### `.gitignore` To avoid checking in unwanted files, the `.gitignore` file should follow -this [example](examples/.gitignore). +the [example](examples/.gitignore). This is if you are using `aegir` - smaller projects can use smaller .gitignore files. ##### `.npmignore` @@ -272,15 +280,7 @@ No. But other people might ask you to at some point, so it may be better to be p #### Why are you doing this? -Because it saves us hours every single day. Hours in which we don’t have to think about these things or argue with someone about why we are doing it. This tooling is the result of a lot of effort, thought, and hard learning. Its goal is to minimize process road bumps and provide a unified low-friction workflow for contributors. - -## Contributing - -Please follow the conventions described in this document. - -When reporting a bug, if possible, provide a way for us to reproduce it (or even better, write a test that fails with your case). - -Always run tests before pushing and PR'ing your code. +Because it saves us hours every single day. This tooling is the result of a lot of effort, thought, and hard learning. Its goal is to minimize process road bumps and provide a unified low-friction workflow for contributors. ## Code of Conduct From 1e6ce7b15b89db83b531c738a6cda211a99fe6e0 Mon Sep 17 00:00:00 2001 From: Richard Littauer Date: Wed, 20 Apr 2016 13:47:26 -0400 Subject: [PATCH 19/19] Backticks --- js-project-guidelines.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js-project-guidelines.md b/js-project-guidelines.md index 0564d853..1e708c64 100644 --- a/js-project-guidelines.md +++ b/js-project-guidelines.md @@ -203,7 +203,7 @@ after_success: ##### `.gitignore` To avoid checking in unwanted files, the `.gitignore` file should follow -the [example](examples/.gitignore). This is if you are using `aegir` - smaller projects can use smaller .gitignore files. +the [example](examples/.gitignore). This is if you are using `aegir` - smaller projects can use smaller `.gitignore` files. ##### `.npmignore`