Página Suelta

Página Suelta is an application to track the author's library and thoughts around books and essays.

Table of contents

Tech stack

  • Version Control: We use git as our version control system, and we use git-flow.
  • Package Manager: We use several node packages, and we prefer using Yarn over npm to manage our dependencies. Therefore in this project, there should only be a yarn.lock file and not a package-lock.json file.
  • Development: We are using Vue. This project was bootstrapped with Nuxt. You can learn more about Nuxt on this copy of the project README.md.
  • Linting: We are using eslint to lint our code. Eslint is a pluggable linting utility for JavaScript to keep our codebase written consistently. We are extending from Nuxt recommended configuration. We are also using stylelint, a mighty, modern linter that helps us avoid errors and enforce conventions in our styles. As a final touch, we are using commitlint to ensure that all commits follow our conventions.
  • Code formatter: To keep our codebase written consistently and reducing the feedback loop for linting errors, we are using Prettier as our code formatter. Prettier is an opinionated code formatter with support for JavaScript, CSS, and JSON. With Prettier, you can format the code you write automatically to ensure the coding style within your project.
  • Styling: We will be using SCSS, the most mature, stable, and powerful professional grade CSS extension language in the world. Nuxt implements autoprefixer by default to add vendor prefixes according to the can I use website.
  • Testing: We require to have great code coverage with meaningful tests. As we are using Vue, and we will write most of our code with JavasScript, we use Jest as our testing framework. We are also using [Vue Testing Library][vue-testing-library] to test the UI components.

Development Tools Setup

Download and install dependencies

For a brief explanation of what is being install, please read our setup documentation.

OSX

To install all our dependencies on a OSX, run scripts/setup.osx.sh.

Ubuntu

To install all our dependencies on Ubuntu, run scripts/setup.ubuntu.sh.

We recommended to run the setup script whenever you pull from the develop branch. This way, you can always be sure to have all the project dependencies up to date.

Enable EditorConfig

EditorConfig helps maintain consistent coding styles for all developers working on the project across various editors and IDEs. We have this configured in our .editorconfig file. Make sure to configure your editor or IDE.

Linting

We use eslint and stylelint to make static analysis to find problematic patterns and make sure all the team adheres to specific style guidelines. These are already configured in the eslintrc.js and stylelintrc.js files.

We have configured eslint and stylelint with webpack to ensure that we get all the linting information during the development. However, if you want to lint the project, you could use the following commands.

To lint all the .js and .vue files, run the command:

yarn lint:js

The command line will prompt every issue on the code for not following our coding conventions. Most of the linting problems can be fixed by eslint. To fix that kind of issues, run:

yarn lint:js:fix

To lint all the .css, .scss and .vue files, run the command:

yarn lint:css

The command line will prompt every issue on the code for not following our coding conventions. Most of the linting problems can be fixed by stylelint. To fix that kind of issues, run:

yarn lint:css:fix

To lint with both eslint and stylelint, run the command:

yarn lint

To lint and fix with both eslint and stylelint, run the command:

yarn lint:fix

Additionally, there is a script configured to prevent us from committing files with linting issues. Whenever you try to commit, the linting will run and stop the commit if there is an issue with the code.

Enable Prettier

One of our dependencies is Prettier. Prettier is an opinionated code formatter for our codebase. Prettier works best by enabling its format on document save. In order to do this in vscode you need to install this eslint extension, and configure the IDE. We already have this configured in the .vscode/settings.json file.

Debugging

We have configured to debug our app on Chrome. To do this, you need to install this Debugger for Chrome extension on your vscode, and configure the IDE. We already have this configured in the /.vscode/launch.json file.

You can add some breakpoints by by writing debugger statements in any part of the code while running the debugger.

Testing

We are using jest to make our unit testing. This is already configured in the jest.config.js.

Jest is a Node-based runner. Therefore, the tests always run in a Node environment and not in a real browser. Enabling fast iteration speed and prevent flakiness.

While Jest provides browser globals such as window thanks to jsdom, they are only approximations of the real browser behavior. Jest is intended to be used for unit tests of your logic and your components rather than the DOM quirks.

Filename Conventions

Jest will look for test files with any of the following popular naming conventions:

  • Files with .js suffix in tests folders.
  • Files with .test.js suffix.
  • Files with .spec.js suffix.

However, we should put the test files next to the code they are testing so that relative imports appear shorter. For example, if App.spec.js and App.vue are in the same folder, the test only needs to import App from './App' instead of a long relative path. Collocation also helps find tests more quickly.

Running our tests

To run all our tests run the command:

yarn test:unit

We are striving to keep the following benchmarks:

  • Statements coverage — 75%.
  • Branches coverage — 75%.
  • Lines coverage — 75%.
  • Functions coverage — 75%.

The current state of the coverage is shown after running all the tests.

We recommend to develop with a Test Driven Development mindset, therefore, we recommend running the following command while developing:

yarn test:unit:watch

The watcher includes an interactive command-line interface with the ability to run all tests, or focus on a search pattern. It is designed this way so that you can keep it open and enjoy fast re-runs.

For a more detailed explanation of our testing conventions, please read our testing guidelines.

Styling

For our CSS architecture we are using ITCSS, with BEM as our naming convention. You can read more about how we write our code regarding our styles [here][/docs/styling.md].

Dependencies

We've decided to wrap third-party dependencies into custom abstractions. As this thread explains, the wrapped dependency is isolated, and if a change in the API of that dependency changes, there is only one change point in our codebase (making sure you respect the interface).

If we let a dependency "invade" our code, we're inevitably going to couple the entire application to it. This means that we'll make choices that lean towards making the library happy, and end up with code which requires a significant cognitive overhead.

Bridge pattern

In the OOP world, this is known as the bridge pattern. The goal is to:

Decouple an abstraction from its implementation so that the two can vary independently.

For most third-party dependencies there should be a dedicated directory under the dependencies directory. On most cases this directory should keep the same name as the dependency that is going to be wrapped.

Exceptions

There is a few exceptions for using the bridge pattern:

  • vue
  • nuxt
  • @vue/test-utils
  • scss

After all, we are making a Nuxt project, and these are the core libraries that we are using. If the API of these dependencies changes, then we should update our codebase accordingly.

Important abstractions

Some abstractions are really important and it would have it's own directory.

Notable clarification

It's relevant to note that we refer to dependencies to everything that imposes an API foreign to our codebase logic.

This will provide us with the flexibility to work with a specific dependency without compromising our implementation to that.

For instance, if we are working with the native [Fetch API][fetch_api]. Maybe we made that choice a while ago because that was enough. If the application starts growing, we might need to cover more use cases. Then, we could decide that it's time to move to something like [Axios][axios].

If we've leaked the usage of Fetch everywhere, we would be up for a nasty refactor. Not only we are changing an implementation, but now we must also go to every single place we've used it. A targeted, focused change is now having a domino effect on the entire app.

On the other hand, if we've wrapped Fetch in an abstraction that makes sense for our app, then we've isolated its usage. Maybe we pre-filled it with sensible defaults, and specialized into a module that anyone with domain knowledge of the app can understand.

How to contribute

Please read through our contributing guidelines. Included are directions for opening issues, coding standards, and notes on development.

TL;DR

  1. We use git-flow to create new features. For further information read this guide.
  2. Create a [Pull Request][pull_requests] and assign a relevant reviewer.
  3. Fix pull request comments (when necessary).
  4. Finish your feature with git flow feature finish <feature_branch> and push to the develop branch.