From 08b3b1774dc4c1e85282268f75ca4c0971c6a683 Mon Sep 17 00:00:00 2001
From: Dominik George <nik@naturalnet.de>
Date: Sat, 7 Dec 2019 23:25:00 +0100
Subject: [PATCH] Port from bower to yarn.

---
 .coveragerc                                   |   2 +-
 .covio.yml                                    |   4 -
 CHANGELOG.rst                                 |   5 +
 README.rst                                    |  71 +++----
 {djangobower => django_yarnpkg}/__init__.py   |   0
 django_yarnpkg/conf.py                        |  13 ++
 django_yarnpkg/context_processors.py          |  28 +++
 django_yarnpkg/exceptions.py                  |  10 +
 {djangobower => django_yarnpkg}/finders.py    |  19 +-
 .../management/__init__.py                    |   0
 django_yarnpkg/management/base.py             |  27 +++
 .../management/commands/__init__.py           |   0
 .../management/commands/yarn.py               |  15 +-
 .../management/commands/yarn_install.py       |  10 +
 {djangobower => django_yarnpkg}/models.py     |   0
 django_yarnpkg/shortcuts.py                   |   9 +
 .../templatetags/__init__.py                  |   0
 .../templatetags/yarn.py                      |  10 +-
 .../test_settings.py                          |  10 +-
 .../tests/__init__.py                         |   0
 {djangobower => django_yarnpkg}/tests/base.py |  22 +--
 .../tests/test_finders.py                     |  44 ++---
 django_yarnpkg/tests/test_yarn.py             | 129 +++++++++++++
 django_yarnpkg/yarn.py                        |  58 ++++++
 djangobower/bower.py                          |  80 --------
 djangobower/conf.py                           |  15 --
 djangobower/context_processors.py             |  31 ----
 djangobower/exceptions.py                     |  19 --
 djangobower/management/base.py                |  35 ----
 .../management/commands/bower_freeze.py       |   9 -
 .../management/commands/bower_install.py      |  45 -----
 djangobower/shortcuts.py                      |  28 ---
 djangobower/tests/test_bower.py               | 173 ------------------
 docs/Makefile                                 |   8 +-
 docs/conf.py                                  |  20 +-
 docs/example.rst                              |   2 +-
 docs/index.rst                                |  14 +-
 docs/installation.rst                         |  30 +--
 docs/tests.rst                                |   6 +-
 docs/usage.rst                                |  28 ++-
 example/example/settings.py                   |   8 +-
 example/{components => node_modules}/.gitkeep |   0
 example/templates/index.html                  |   2 +-
 runtests.py                                   |   2 +-
 setup.cfg                                     |   1 -
 setup.py                                      |  12 +-
 46 files changed, 435 insertions(+), 619 deletions(-)
 delete mode 100644 .covio.yml
 rename {djangobower => django_yarnpkg}/__init__.py (100%)
 create mode 100644 django_yarnpkg/conf.py
 create mode 100644 django_yarnpkg/context_processors.py
 create mode 100644 django_yarnpkg/exceptions.py
 rename {djangobower => django_yarnpkg}/finders.py (52%)
 rename {djangobower => django_yarnpkg}/management/__init__.py (100%)
 create mode 100644 django_yarnpkg/management/base.py
 rename {djangobower => django_yarnpkg}/management/commands/__init__.py (100%)
 rename djangobower/management/commands/bower.py => django_yarnpkg/management/commands/yarn.py (56%)
 create mode 100644 django_yarnpkg/management/commands/yarn_install.py
 rename {djangobower => django_yarnpkg}/models.py (100%)
 create mode 100644 django_yarnpkg/shortcuts.py
 rename {djangobower => django_yarnpkg}/templatetags/__init__.py (100%)
 rename djangobower/templatetags/bower.py => django_yarnpkg/templatetags/yarn.py (80%)
 rename {djangobower => django_yarnpkg}/test_settings.py (66%)
 rename {djangobower => django_yarnpkg}/tests/__init__.py (100%)
 rename {djangobower => django_yarnpkg}/tests/base.py (50%)
 rename {djangobower => django_yarnpkg}/tests/test_finders.py (62%)
 create mode 100644 django_yarnpkg/tests/test_yarn.py
 create mode 100644 django_yarnpkg/yarn.py
 delete mode 100644 djangobower/bower.py
 delete mode 100644 djangobower/conf.py
 delete mode 100644 djangobower/context_processors.py
 delete mode 100644 djangobower/exceptions.py
 delete mode 100644 djangobower/management/base.py
 delete mode 100644 djangobower/management/commands/bower_freeze.py
 delete mode 100644 djangobower/management/commands/bower_install.py
 delete mode 100644 djangobower/shortcuts.py
 delete mode 100644 djangobower/tests/test_bower.py
 rename example/{components => node_modules}/.gitkeep (100%)
 delete mode 100644 setup.cfg

diff --git a/.coveragerc b/.coveragerc
index ca6673d..487f1ad 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,2 +1,2 @@
 [run]
-include = djangobower*
+include = django_yarnpkg*
diff --git a/.covio.yml b/.covio.yml
deleted file mode 100644
index 2dacb0c..0000000
--- a/.covio.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-violations:
-  pep8: pep8 . --exclude='*migrations*,*settings*,*components*,*docs*'
-  sloccount: sloccount .
-  py_unittest: cat test_out
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index d23a427..72e99a6 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,3 +1,8 @@
+Version 6.0  (2019-12-07)
+=====================================================
+
+* Fork and port to yarnpkg
+
 Version 4.8  (2013-10-02)
 =====================================================
 
diff --git a/README.rst b/README.rst
index 72f4e5c..887b69b 100644
--- a/README.rst
+++ b/README.rst
@@ -1,76 +1,65 @@
-Django-bower
-============
+Django-yarnpkg
+==============
 
-.. image:: https://travis-ci.org/nvbn/django-bower.png
-   :alt: Build Status
-   :target: https://travis-ci.org/nvbn/django-bower
-.. image:: https://coveralls.io/repos/nvbn/django-bower/badge.png?branch=develop
-   :alt: Coverage Status
-   :target: https://coveralls.io/r/nvbn/django-bower
-.. image:: https://pypip.in/v/django-bower/badge.png
-   :target: https://crate.io/packages/django-bower/
-.. image:: https://pypip.in/d/django-bower/badge.png
-   :target: https://crate.io/packages/django-bower/
+Easy way to use `yarnpkg <http://yarnpkg.com/>`_ with your `Django <https://www.djangoproject.com/>`_ project.
 
-Easy way to use `bower <http://bower.io/>`_ with your `Django <https://www.djangoproject.com/>`_ project.
+This is a fork of `django-bower <https://github.com/nvbn/django-bower>` by Vladimir Iakovlev.
 
-Bower is a package manager for the web. It offers a generic, unopinionated solution to the problem of front-end package management, while exposing the package dependency model via an API that can be consumed by a more opinionated build stack. There are no system wide dependencies, no dependencies are shared between different apps, and the dependency tree is flat.
-
-Read full documentation on `read-the-docs <https://django-bower.readthedocs.io/en/latest/>`_.
+Read full documentation on `read-the-docs <https://django-yarnpkg.readthedocs.io/en/latest/>`_.
 
 Installation
 ------------
 
-Install django-bower package:
+Install django-yarnpkg package:
 
 .. code-block:: bash
 
-    pip install django-bower
+    pip install django-yarnpkg
 
 Add django-bower to `INSTALLED_APPS` in your settings:
 
 .. code-block:: python
 
-    'djangobower',
+    'django_yarnpkg',
 
 Add staticfinder to `STATICFILES_FINDERS`:
 
 .. code-block:: python
 
-    'djangobower.finders.BowerFinder',
+    'django_yarnpkg.finders.NodeModulesFinder',
 
 Specify path to components root (you need to use an absolute path):
 
 .. code-block:: python
 
-    BOWER_COMPONENTS_ROOT = '/PROJECT_ROOT/components/'
+    NODE_MODULES_ROOT = os.path.join(BASE_DIR, 'node_modules')
 
 
-If you need, you can manually set the path to bower:
+If you need, you can manually set the path to yarnpkg:
 
 .. code-block:: python
 
-    BOWER_PATH = '/usr/bin/bower'
+    YARN_PATH = '/usr/bin/yarnpkg'
 
-You can see an example settings file in `example project <https://github.com/nvbn/django-bower/blob/master/example/example/settings.py>`_.
+You can see an example settings file in `example project <https://github.com/Natureshadow/django-yarnpkg/blob/master/example/example/settings.py>`_.
 
 Usage
 -----
 
-Specify `BOWER_INSTALLED_APPS` in settings, like:
+Specify `YARN_INSTALLED_APPS` in settings, like:
 
 .. code-block:: python
 
-    BOWER_INSTALLED_APPS = (
-        'jquery#1.9',
-        'underscore',
+    YARN_INSTALLED_APPS = (
+        'bootstrap@^4.4.1',
+        'underscore@^1.6.1',
     )
 
-Download bower packages with the management command:
+Download yarn packages with the management command:
 
 .. code-block:: bash
 
-    ./manage.py bower install
+    ./manage.py yarn install
 
 Add scripts in the template, like:
 
@@ -79,32 +68,26 @@ Add scripts in the template, like:
     {% load static %}
     <script type="text/javascript" src='{% static 'jquery/dist/jquery.js' %}'></script>
 
-In production you need to call `bower install` before `collectstatic`:
+In production you need to call `yarnpkg install` before `collectstatic`:
 
 .. code-block:: bash
 
-    ./manage.py bower install
+    ./manage.py yarn install
     ./manage.py collectstatic
 
-If you need to pass arguments to bower, like `--allow-root`, use:
-
-.. code-block:: bash
-
-    ./manage.py bower install -- --allow-root
-
-You can use `bower freeze` to receive `BOWER_INSTALLED_APPS` with fixed current versions:
+If you need to pass arguments to yarnpkg, like `--flat`, use:
 
 .. code-block:: bash
 
-    ./manage.py bower freeze
+    ./manage.py yarn install -- --flat
 
-You can call bower commands like `info` and `update` with:
+You can call yarnpkg commands like `info` and `update` with:
 
 .. code-block:: bash
 
-    ./manage.py bower info backbone
-    ./manage.py bower update
+    ./manage.py yarn info backbone
+    ./manage.py yarn update
 
 Python 3 support
 ----------------
-django-bower supports python 3.3+
+django-yarnpkg supports python 3.3+
diff --git a/djangobower/__init__.py b/django_yarnpkg/__init__.py
similarity index 100%
rename from djangobower/__init__.py
rename to django_yarnpkg/__init__.py
diff --git a/django_yarnpkg/conf.py b/django_yarnpkg/conf.py
new file mode 100644
index 0000000..c4ea170
--- /dev/null
+++ b/django_yarnpkg/conf.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+from distutils.spawn import find_executable
+import os
+import sys
+from django.conf import settings
+
+__all__ = ['NODE_MODULES_ROOT', 'YARN_PATH']
+
+NODE_MODULES_ROOT = getattr(settings, 'NODE_MODULES_ROOT', os.path.abspath(os.path.dirname(__name__)))
+
+default_yarn_path = find_executable('yarn') or find_executable('yarnpkg')
+
+YARN_PATH = getattr(settings, 'YARN_PATH', default_yarn_path)
diff --git a/django_yarnpkg/context_processors.py b/django_yarnpkg/context_processors.py
new file mode 100644
index 0000000..2b3fad5
--- /dev/null
+++ b/django_yarnpkg/context_processors.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+import os.path
+import json
+import six
+from django.conf import settings
+from django.utils.datastructures import OrderedSet
+
+
+def read_files():
+    for component in settings.YARN_INSTALLED_APPS:
+        component = component.split('#')[0]
+        try:
+            with open(os.path.join(
+                    settings.NODE_MODULES_ROOT,
+                    'node_modules',
+                    component,
+                    'package.json')) as package_json:
+                files = json.load(bower_json).get('files')
+                for f in files:
+                    yield '%s/%s' % (component, f)
+        except FileNotFoundError:
+            continue
+
+
+def node_modules(request):
+    return {
+        'node_modules': OrderedSet([f for f in read_files()]),
+    }
diff --git a/django_yarnpkg/exceptions.py b/django_yarnpkg/exceptions.py
new file mode 100644
index 0000000..fd507a1
--- /dev/null
+++ b/django_yarnpkg/exceptions.py
@@ -0,0 +1,10 @@
+from django.core.management.base import CommandError
+
+
+class YarnNotInstalled(CommandError):
+    """Custom command error"""
+
+    def __init__(self):
+        super(YarnNotInstalled, self).__init__(
+            "Yarn not installed, read instruction here - http://yarnpkg.com/",
+        )
diff --git a/djangobower/finders.py b/django_yarnpkg/finders.py
similarity index 52%
rename from djangobower/finders.py
rename to django_yarnpkg/finders.py
index 746cb6d..b3f8ff3 100644
--- a/djangobower/finders.py
+++ b/django_yarnpkg/finders.py
@@ -9,14 +9,14 @@ from . import conf
 import os
 
 
-class BowerFinder(FileSystemFinder):
-    """Find static files installed with bower"""
+class NodeModulesFinder(FileSystemFinder):
+    """Find static files installed with yarnpkg"""
 
     def __init__(self, apps=None, *args, **kwargs):
         self.locations = []
         self.storages = OrderedDict()
 
-        root = self._get_bower_components_location()
+        root = self._get_node_modules_location()
         if root is not None:
             prefix = ''
             self.locations.append((prefix, root))
@@ -25,13 +25,10 @@ class BowerFinder(FileSystemFinder):
             filesystem_storage.prefix = prefix
             self.storages[root] = filesystem_storage
 
-    def _get_bower_components_location(self):
+    def _get_node_modules_location(self):
         """
-        Return the bower components location, or None if one does not exist.
+        Return the node modules location, or None if one does not exist.
         """
-        # Bower 0.10 changed the default folder from 'components' to 'bower_components'.
-        # Try 'bower_components' first, then fall back to trying 'components'.
-        for name in ['bower_components', 'components']:
-            path = os.path.join(conf.COMPONENTS_ROOT, name)
-            if os.path.exists(path):
-                return path
+        path = os.path.join(conf.NODE_MODULES_ROOT, 'node_modules')
+        if os.path.exists(path):
+            return path
diff --git a/djangobower/management/__init__.py b/django_yarnpkg/management/__init__.py
similarity index 100%
rename from djangobower/management/__init__.py
rename to django_yarnpkg/management/__init__.py
diff --git a/django_yarnpkg/management/base.py b/django_yarnpkg/management/base.py
new file mode 100644
index 0000000..f6b7dd1
--- /dev/null
+++ b/django_yarnpkg/management/base.py
@@ -0,0 +1,27 @@
+from pprint import pformat
+from django.core.management.base import BaseCommand
+from django.conf import settings
+from ..yarn import yarn_adapter
+from ..exceptions import YarnNotInstalled
+
+
+class BaseYarnCommand(BaseCommand):
+    """Base management command with yarn support"""
+
+    requires_system_checks = False
+
+    # add fake .options_list for Django>=1.10
+    if not hasattr(BaseCommand, 'option_list'):
+        option_list = ()
+
+    def handle(self, *args, **options):
+        self._check_yarn_exists()
+        yarn_adapter.create_node_modules_root()
+
+    def _check_yarn_exists(self):
+        """Check yarn exists or raise exception"""
+        if not yarn_adapter.is_yarn_exists():
+            raise YarnNotInstalled()
+
+    def _install(self, args):
+        yarn_adapter.install(settings.YARN_INSTALLED_APPS, *args)
diff --git a/djangobower/management/commands/__init__.py b/django_yarnpkg/management/commands/__init__.py
similarity index 100%
rename from djangobower/management/commands/__init__.py
rename to django_yarnpkg/management/commands/__init__.py
diff --git a/djangobower/management/commands/bower.py b/django_yarnpkg/management/commands/yarn.py
similarity index 56%
rename from djangobower/management/commands/bower.py
rename to django_yarnpkg/management/commands/yarn.py
index b8779e9..5ea03c0 100644
--- a/djangobower/management/commands/bower.py
+++ b/django_yarnpkg/management/commands/yarn.py
@@ -1,13 +1,12 @@
-from ...bower import bower_adapter
-from ..base import BaseBowerCommand
+from ...yarn import yarn_adapter
+from ..base import BaseYarnCommand
 
 
-class Command(BaseBowerCommand):
+class Command(BaseYarnCommand):
     args = 'command'
-    help = 'Call bower in components root ({0}).'.format(
-        bower_adapter._components_root)
+    help = 'Call yarn in node modules root ({0}).'.format(
+        yarn_adapter._node_modules_root)
 
-    # for Django>=1.10
     def add_arguments(self, parser):
         parser.add_argument('command', nargs='*')
 
@@ -16,10 +15,8 @@ class Command(BaseBowerCommand):
         args = args or tuple(options.pop('command'))
         if self._is_single_command('install', args):
             self._install([])
-        elif self._is_single_command('freeze', args):
-            self._freeze()
         else:
-            bower_adapter.call_bower(args)
+            yarn_adapter.call_bower(args)
 
     def _is_single_command(self, name, args):
         return len(args) == 1 and args[0] == name
diff --git a/django_yarnpkg/management/commands/yarn_install.py b/django_yarnpkg/management/commands/yarn_install.py
new file mode 100644
index 0000000..807eac4
--- /dev/null
+++ b/django_yarnpkg/management/commands/yarn_install.py
@@ -0,0 +1,10 @@
+from optparse import make_option
+from ..base import BaseYarnCommand
+
+
+class Command(BaseYarnCommand):
+    help = 'Install yarn apps'
+
+    def handle(self, *args, **options):
+        super(Command, self).handle(*args, **options)
+        self._install(args)
diff --git a/djangobower/models.py b/django_yarnpkg/models.py
similarity index 100%
rename from djangobower/models.py
rename to django_yarnpkg/models.py
diff --git a/django_yarnpkg/shortcuts.py b/django_yarnpkg/shortcuts.py
new file mode 100644
index 0000000..1effce5
--- /dev/null
+++ b/django_yarnpkg/shortcuts.py
@@ -0,0 +1,9 @@
+import os, sys
+
+
+def is_executable(path):
+    """Check file is executable"""
+    if sys.platform == 'win32':
+        executable = os.access(path, os.X_OK) or path.lower().endswith(".cmd")
+        return os.path.isfile(path) and executable
+    return os.path.isfile(path) and os.access(path, os.X_OK)
diff --git a/djangobower/templatetags/__init__.py b/django_yarnpkg/templatetags/__init__.py
similarity index 100%
rename from djangobower/templatetags/__init__.py
rename to django_yarnpkg/templatetags/__init__.py
diff --git a/djangobower/templatetags/bower.py b/django_yarnpkg/templatetags/yarn.py
similarity index 80%
rename from djangobower/templatetags/bower.py
rename to django_yarnpkg/templatetags/yarn.py
index 6387e88..784717c 100644
--- a/djangobower/templatetags/bower.py
+++ b/django_yarnpkg/templatetags/yarn.py
@@ -24,13 +24,13 @@ script_template = template.Template(
 
 def tags(context, args, type):
     components = (
-        [arg for arg in args if arg in context['bower_components']]
-        if args else context['bower_components']
+        [arg for arg in args if arg in context['node_modules']]
+        if args else context['node_modules']
     )
     files = []
     for component in components:
         files.append(component)
-        context['bower_components'].remove(component)
+        context['node_modules'].remove(component)
     return {'files': [
         static.static(f)
         for f in files
@@ -39,10 +39,10 @@ def tags(context, args, type):
 
 
 @register.inclusion_tag(style_template, takes_context=True)
-def bower_styles(context, *args):
+def yarn_styles(context, *args):
     return tags(context, args, 'css')
 
 
 @register.inclusion_tag(script_template, takes_context=True)
-def bower_scripts(context, *args):
+def yarn_scripts(context, *args):
     return tags(context, args, 'js')
diff --git a/djangobower/test_settings.py b/django_yarnpkg/test_settings.py
similarity index 66%
rename from djangobower/test_settings.py
rename to django_yarnpkg/test_settings.py
index f932db5..ca462cf 100644
--- a/djangobower/test_settings.py
+++ b/django_yarnpkg/test_settings.py
@@ -8,21 +8,21 @@ TEST_PROJECT_ROOT = os.path.abspath(
 
 BASE_DIR = TEST_PROJECT_ROOT
 
-BOWER_COMPONENTS_ROOT = os.path.join(TEST_PROJECT_ROOT, 'bower_components')
+NODE_MODULES_ROOT = os.path.join(TEST_PROJECT_ROOT, 'node_modules')
 
-STATIC_ROOT = os.path.join(TEST_PROJECT_ROOT, 'bower_static')
+STATIC_ROOT = os.path.join(TEST_PROJECT_ROOT, 'yarnpkg_static')
 
 STATIC_URL = '/static/'
 
-BOWER_INSTALLED_APPS = (
+YARN_INSTALLED_APPS = (
     'jquery#1.9',
     'underscore',
 )
 
-SECRET_KEY = 'iamdjangobower'
+SECRET_KEY = 'iamdjangoyarnpkg'
 
 INSTALLED_APPS = (
-    'djangobower',
+    'django_yarnpkg',
 )
 
 DATABASES = {
diff --git a/djangobower/tests/__init__.py b/django_yarnpkg/tests/__init__.py
similarity index 100%
rename from djangobower/tests/__init__.py
rename to django_yarnpkg/tests/__init__.py
diff --git a/djangobower/tests/base.py b/django_yarnpkg/tests/base.py
similarity index 50%
rename from djangobower/tests/base.py
rename to django_yarnpkg/tests/base.py
index ca61152..b8e82a6 100644
--- a/djangobower/tests/base.py
+++ b/django_yarnpkg/tests/base.py
@@ -1,37 +1,37 @@
 from django.conf import settings
 from django.test import TestCase
 from django.test.utils import override_settings
-from ..bower import bower_adapter
+from ..yarn import yarn_adapter
 import os
 import shutil
 
 
 try:
-    TEST_COMPONENTS_ROOT = os.path.join(
-        settings.TEST_PROJECT_ROOT, 'bower_components',
+    TEST_NODE_MODULES_ROOT = os.path.join(
+        settings.TEST_PROJECT_ROOT, 'node_modules',
     )
 except AttributeError:
-    TEST_COMPONENTS_ROOT = '/tmp/bower_components/'
+    TEST_NODE_MODULES_ROOT = '/tmp/node_modules/'
 
 
-@override_settings(BOWER_COMPONENTS_ROOT=TEST_COMPONENTS_ROOT)
-class BaseBowerCase(TestCase):
+@override_settings(NODE_MODULES_ROOT=TEST_NODE_MODULES_ROOT)
+class BaseYarnCase(TestCase):
     """Base bower test case"""
 
     def setUp(self):
-        bower_adapter.create_components_root()
+        yarn_adapter.create_node_modules_root()
 
     def tearDown(self):
-        self._remove_components_root()
+        self._remove_node_modules_root()
 
     def _remove_components_root(self):
         """Remove components root if exists"""
-        if os.path.exists(TEST_COMPONENTS_ROOT):
-            shutil.rmtree(TEST_COMPONENTS_ROOT)
+        if os.path.exists(TEST_NODE_MODULES_ROOT):
+            shutil.rmtree(TEST_NODE_MODULES_ROOT)
 
     def assertCountEqual(self, *args, **kwargs):
         """Add python 2 support"""
         if hasattr(self, 'assertItemsEqual'):
             return self.assertItemsEqual(*args, **kwargs)
         else:
-            return super(BaseBowerCase, self).assertCountEqual(*args, **kwargs)
+            return super(BaseYarnCase, self).assertCountEqual(*args, **kwargs)
diff --git a/djangobower/tests/test_finders.py b/django_yarnpkg/tests/test_finders.py
similarity index 62%
rename from djangobower/tests/test_finders.py
rename to django_yarnpkg/tests/test_finders.py
index 648829b..53378a5 100644
--- a/djangobower/tests/test_finders.py
+++ b/django_yarnpkg/tests/test_finders.py
@@ -1,10 +1,10 @@
 from django.core.files.storage import FileSystemStorage
 from django.test import TestCase
 
-from ..bower import bower_adapter
-from ..finders import BowerFinder
+from ..yarn import yarn_adapter
+from ..finders import NodeModulesFinder
 from .. import conf
-from .base import BaseBowerCase
+from .base import BaseYarnCase
 import os
 import os.path
 import shutil
@@ -33,26 +33,26 @@ class _MakeDirsTestCase(TestCase):
         self.created.append(name)
 
 
-class SimpleBowerFinderCase(_MakeDirsTestCase):
+class SimpleNodeModulesFinderCase(_MakeDirsTestCase):
     """
-    Simple BowerFinder tests, without any packages installed.
+    Simple YarnFinder tests, without any packages installed.
     """
 
     def test_list_nonexistent(self):
         """
-        When no Bower folder exists, just gracefully find nothing.
+        When no Yarn folder exists, just gracefully find nothing.
         """
-        finder = BowerFinder()
+        finder = NodeModulesFinder()
         self.assertEqual(finder.locations, [])
         self.assertEqual(finder.storages, {})
 
-    def test_list_existent(self, leaf_name='bower_components'):
+    def test_list_existent(self, leaf_name='node_modules'):
         """
-        If 'bower_components' exists, use it to to find files.
+        If 'node_modules' exists, use it to to find files.
         """
-        root = os.path.join(conf.COMPONENTS_ROOT, leaf_name)
+        root = os.path.join(conf.NODE_MODULES_ROOT, leaf_name)
         self.makedirs(root)
-        finder = BowerFinder()
+        finder = NodeModulesFinder()
 
         self.assertEqual(finder.locations, [('', root)])
 
@@ -64,32 +64,32 @@ class SimpleBowerFinderCase(_MakeDirsTestCase):
 
     def test_list_old_path(self):
         """
-        If only the old 'components' folder exists, use it instead.
+        If only the old 'node_modules' folder exists, use it instead.
         """
-        self.test_list_existent(leaf_name='components')
+        self.test_list_existent(leaf_name='node_modules')
 
     def test_list_both(self):
         """
-        If both folders exist, only 'bower_components' should be used.
+        If both folders exist, only 'yarn_node_modules' should be used.
         """
-        self.makedirs(os.path.join(conf.COMPONENTS_ROOT, 'components'))
-        self.test_list_existent(leaf_name='bower_components')
+        self.makedirs(os.path.join(conf.NODE_MODULES_ROOT, 'node_modules'))
+        self.test_list_existent(leaf_name='node_modules')
 
 
-class BowerFinderCase(BaseBowerCase):
-    """Test finding installed with bower files"""
+class NodeModulesFinderCase(BaseYarnCase):
+    """Test finding installed with yarn files"""
 
     def setUp(self):
-        super(BowerFinderCase, self).setUp()
-        bower_adapter.install(['jquery#1.9'])
-        self.finder = BowerFinder()
+        super(NodeModulesFinderCase, self).setUp()
+        yarn_adapter.install(['jquery@1.9'])
+        self.finder = NodeModulesFinder()
 
     def test_find(self):
         """Test staticfinder find"""
         test_path = os.path.join('jquery', 'jquery.min.js')
         path = self.finder.find(test_path)
         self.assertEqual(path, os.path.join(
-            conf.COMPONENTS_ROOT, 'bower_components', test_path,
+            conf.NODE_MODULES_ROOT, 'node_modules', test_path,
         ))
 
     def test_list(self):
diff --git a/django_yarnpkg/tests/test_yarn.py b/django_yarnpkg/tests/test_yarn.py
new file mode 100644
index 0000000..44d404c
--- /dev/null
+++ b/django_yarnpkg/tests/test_yarn.py
@@ -0,0 +1,129 @@
+from django.core.management import call_command
+from django.conf import settings
+from django.test import TestCase
+from six import StringIO
+from mock import MagicMock
+from ..yarn import yarn_adapter, YarnAdapter
+from .. import conf
+from .base import BaseYarnCase, TEST_NODE_MODULES_ROOT
+import os
+import shutil
+
+
+class YarnAdapterCase(TestCase):
+    """
+    YarnAdapter regression tests.
+    """
+
+    def test_create_node_modules_root_subdirs(self):
+        """
+        create_node_modules_root() creates missing intermediate directories.
+        """
+        if os.path.exists(TEST_NODE_MODULES_ROOT):
+            shutil.rmtree(TEST_NODE_MODULES_ROOT)
+
+        subdir = os.path.join(TEST_NODE_MODULES_ROOT, 'sub', 'dir')
+        adapter = YarnAdapter(conf.YARN_PATH, subdir)
+        adapter.create_node_modules_root()
+        self.assertTrue(os.path.exists(subdir))
+
+        shutil.rmtree(TEST_NODE_MODULES_ROOT)
+
+
+class YarnInstallCase(BaseYarnCase):
+    """Test case for yarn_install management command"""
+
+    def setUp(self):
+        super(YarnInstallCase, self).setUp()
+        self.apps = settings.YARN_INSTALLED_APPS
+        self._original_install = yarn_adapter.install
+        yarn_adapter.install = MagicMock()
+
+    def tearDown(self):
+        super(YarnInstallCase, self).tearDown()
+        yarn_adapter.install = self._original_install
+
+    def test_create_node_modules_root(self):
+        """Test create node_modules root"""
+        self._remove_node_modules_root()
+        call_command('yarn_install')
+
+        self.assertTrue(os.path.exists(TEST_NODE_MODULES_ROOT))
+
+    def test_install(self):
+        """Test install yarn packages"""
+        call_command('yarn_install')
+        yarn_adapter.install.assert_called_once_with(self.apps)
+
+
+class YarnExistsCase(BaseYarnCase):
+    """
+    Test yarn exists checker.
+    This case need yarn to be installed.
+    """
+
+    def setUp(self):
+        super(YarnExistsCase, self).setUp()
+        self._original_exists = yarn_adapter.is_yarn_exists
+
+    def tearDown(self):
+        super(YarnExistsCase, self).tearDown()
+        yarn_adapter.is_yarn_exists = self._original_exists
+
+    def test_if_exists(self):
+        """Test if yarn exists"""
+        self.assertTrue(yarn_adapter.is_yarn_exists())
+
+    def test_if_not_exists(self):
+        """Test if yarn not exists"""
+        adapter = YarnAdapter('/not/exists/path', TEST_NODE_MODULES_ROOT)
+        self.assertFalse(adapter.is_yarn_exists())
+
+    def _mock_exists_check(self):
+        """Make exists check return false"""
+        yarn_adapter.is_yarn_exists = MagicMock()
+        yarn_adapter.is_yarn_exists.return_value = False
+
+
+class YarnCommandCase(BaseYarnCase):
+    """Test case for ./manage.py yarn something command"""
+
+    def setUp(self):
+        super(YarnCommandCase, self).setUp()
+        self.apps = settings.YARN_INSTALLED_APPS
+        self._mock_yarn_adapter()
+
+    def _mock_yarn_adapter(self):
+        self._original_install = yarn_adapter.install
+        yarn_adapter.install = MagicMock()
+        self._orig_call = yarn_adapter.call_yarn
+        yarn_adapter.call_yarn = MagicMock()
+        self._orig_freeze = yarn_adapter.freeze
+        yarn_adapter.freeze = MagicMock()
+
+    def tearDown(self):
+        super(YarnCommandCase, self).tearDown()
+        yarn_adapter.install = self._original_install
+        yarn_adapter.call_yarn = self._orig_call
+        yarn_adapter.freeze = self._orig_freeze
+
+    def test_install_without_params(self):
+        """Test that yarn install without param identical
+        with yarn_install
+
+        """
+        call_command('yarn', 'install')
+        yarn_adapter.install.assert_called_once_with(
+            self.apps)
+
+    def test_install_with_params(self):
+        """Test yarn install <something>"""
+        call_command('yarn', 'install', 'jquery')
+        yarn_adapter.call_yarn.assert_called_once_with(
+            ('install', 'jquery'))
+
+    def test_call_to_yarn(self):
+        """Test simple call to yarn"""
+        call_command('yarn', 'update')
+        yarn_adapter.call_yarn.assert_called_once_with(
+            ('update',))
diff --git a/django_yarnpkg/yarn.py b/django_yarnpkg/yarn.py
new file mode 100644
index 0000000..fa25547
--- /dev/null
+++ b/django_yarnpkg/yarn.py
@@ -0,0 +1,58 @@
+from . import conf, shortcuts, exceptions
+from distutils.spawn import find_executable
+import os
+import subprocess
+import sys
+import json
+
+
+class YarnAdapter(object):
+    """Adapter for working with yarnpkg"""
+
+    def __init__(self, yarn_path, node_modules_root):
+        self._yarn_path = yarn_path
+        self._node_modules_root = node_modules_root
+
+    def is_yarn_exists(self):
+        """Check is bower exists"""
+        if shortcuts.is_executable(self._yarn_path)\
+                or find_executable(self._yarn_path):
+            return True
+        else:
+            return False
+
+    def create_node_modules_root(self):
+        """Create node modules root if need"""
+        if not os.path.exists(self._node_modules_root):
+            os.makedirs(self._node_modules_root)
+
+    def call_yarn(self, args):
+        """Call yarn with a list of args"""
+        proc = subprocess.Popen(
+            [self._yarn_path] + list(args),
+            cwd=self._node_modules_root)
+        proc.wait()
+
+    def install(self, packages, *options):
+        """Install packages from yarn"""
+        self.call_yarn(['add'] + list(options) + list(packages))
+
+    def _accumulate_dependencies(self, data):
+        """Accumulate dependencies"""
+        for name, version in data['dependencies'].items():
+            if version:
+                full_name = '{0}@{1}'.format(name, version)
+            else:
+                full_name = name
+
+            self._packages.append(full_name)
+            self._accumulate_dependencies(params)
+
+    def _parse_package_names(self, output):
+        """Get package names in yarn"""
+        data = json.loads(output)
+        self._packages = []
+        self._accumulate_dependencies(data)
+        return self._packages
+
+yarn_adapter = YarnAdapter(conf.YARN_PATH, conf.NODE_MODULES_ROOT)
diff --git a/djangobower/bower.py b/djangobower/bower.py
deleted file mode 100644
index 700554a..0000000
--- a/djangobower/bower.py
+++ /dev/null
@@ -1,80 +0,0 @@
-from . import conf, shortcuts, exceptions
-import os
-import subprocess
-import sys
-import json
-
-
-class BowerAdapter(object):
-    """Adapter for working with bower"""
-
-    def __init__(self, bower_path, components_root):
-        self._bower_path = bower_path
-        self._components_root = components_root
-
-    def is_bower_exists(self):
-        """Check is bower exists"""
-        if shortcuts.is_executable(self._bower_path)\
-                or shortcuts.which(self._bower_path):
-            return True
-        else:
-            return False
-
-    def create_components_root(self):
-        """Create components root if need"""
-        if not os.path.exists(self._components_root):
-            os.makedirs(self._components_root)
-
-    def call_bower(self, args):
-        """Call bower with a list of args"""
-        proc = subprocess.Popen(
-            [self._bower_path] + list(args),
-            cwd=self._components_root)
-        proc.wait()
-
-    def install(self, packages, *options):
-        """Install packages from bower"""
-        self.call_bower(['install'] + list(options) + list(packages))
-
-    def _accumulate_dependencies(self, data):
-        """Accumulate dependencies"""
-        for name, params in data['dependencies'].items():
-            meta = params.get('pkgMeta', {})
-            version = meta.get(
-                'version', meta.get('_resolution', {}).get('commit', ''),
-            )
-
-            if version:
-                full_name = '{0}#{1}'.format(name, version)
-            else:
-                full_name = name
-
-            self._packages.append(full_name)
-            self._accumulate_dependencies(params)
-
-    def _parse_package_names(self, output):
-        """Get package names in bower >= 1.0"""
-        data = json.loads(output)
-        self._packages = []
-        self._accumulate_dependencies(data)
-        return self._packages
-
-    def freeze(self):
-        """Yield packages with versions list"""
-        proc = subprocess.Popen(
-            [self._bower_path, 'list', '--json', '--offline', '--no-color'],
-            cwd=conf.COMPONENTS_ROOT,
-            stdout=subprocess.PIPE,
-        )
-        outs, errs = proc.communicate()
-        output = outs.decode(sys.getfilesystemencoding())
-
-        try:
-            packages = self._parse_package_names(output)
-        except ValueError:
-            raise exceptions.LegacyBowerVersionNotSupported()
-
-        return iter(set(packages))
-
-
-bower_adapter = BowerAdapter(conf.BOWER_PATH, conf.COMPONENTS_ROOT)
diff --git a/djangobower/conf.py b/djangobower/conf.py
deleted file mode 100644
index 40a9717..0000000
--- a/djangobower/conf.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-import os
-import sys
-from django.conf import settings
-
-__all__ = ['COMPONENTS_ROOT', 'BOWER_PATH']
-
-COMPONENTS_ROOT = getattr(settings, 'BOWER_COMPONENTS_ROOT', os.path.abspath(os.path.dirname(__name__)))
-
-if sys.platform == 'win32':
-    default_bower_path = os.path.join(os.getenv("APPDATA"), 'npm', 'bower.cmd')
-else:
-    default_bower_path = 'bower'
-
-BOWER_PATH = getattr(settings, 'BOWER_PATH', default_bower_path)
diff --git a/djangobower/context_processors.py b/djangobower/context_processors.py
deleted file mode 100644
index 4bd7e47..0000000
--- a/djangobower/context_processors.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- coding: utf-8 -*-
-import os.path
-import json
-import six
-from django.conf import settings
-from django.utils.datastructures import OrderedSet
-
-
-def read_mains():
-    for component in settings.BOWER_INSTALLED_APPS:
-        component = component.split('#')[0]
-        try:
-            with open(os.path.join(
-                    settings.BOWER_COMPONENTS_ROOT,
-                    'bower_components',
-                    component,
-                    'bower.json')) as bower_json:
-                main = json.load(bower_json).get('main')
-                if isinstance(main, six.string_types):
-                    yield '%s/%s' % (component, main)
-                elif isinstance(main, list):
-                    for m in main:
-                        yield '%s/%s' % (component, m)
-        except FileNotFoundError:
-            continue
-
-
-def bower_components(request):
-    return {
-        'bower_components': OrderedSet([main for main in read_mains()]),
-    }
diff --git a/djangobower/exceptions.py b/djangobower/exceptions.py
deleted file mode 100644
index f6d1e60..0000000
--- a/djangobower/exceptions.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from django.core.management.base import CommandError
-
-
-class BowerNotInstalled(CommandError):
-    """Custom command error"""
-
-    def __init__(self):
-        super(BowerNotInstalled, self).__init__(
-            "Bower not installed, read instruction here - http://bower.io/",
-        )
-
-
-class LegacyBowerVersionNotSupported(CommandError):
-    """Custom command error"""
-
-    def __init__(self):
-        super(LegacyBowerVersionNotSupported, self).__init__(
-            "Legacy bower versions not supported, please install bower 1.0+",
-        )
diff --git a/djangobower/management/base.py b/djangobower/management/base.py
deleted file mode 100644
index bac8233..0000000
--- a/djangobower/management/base.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from pprint import pformat
-from django.core.management.base import BaseCommand
-from django.conf import settings
-from ..bower import bower_adapter
-from ..exceptions import BowerNotInstalled
-
-
-class BaseBowerCommand(BaseCommand):
-    """Base management command with bower support"""
-
-    requires_system_checks = False
-
-    # add fake .options_list for Django>=1.10
-    if not hasattr(BaseCommand, 'option_list'):
-        option_list = ()
-
-    def handle(self, *args, **options):
-        self._check_bower_exists()
-        bower_adapter.create_components_root()
-
-    def _check_bower_exists(self):
-        """Check bower exists or raise exception"""
-        if not bower_adapter.is_bower_exists():
-            raise BowerNotInstalled()
-
-    def _install(self, args):
-        bower_adapter.install(settings.BOWER_INSTALLED_APPS, *args)
-
-    def _freeze(self):
-        packages = list(bower_adapter.freeze())
-        packages.sort()
-        output = 'BOWER_INSTALLED_APPS = {0}'.format(
-            pformat(packages),
-        )
-        self.stdout.write(output)
diff --git a/djangobower/management/commands/bower_freeze.py b/djangobower/management/commands/bower_freeze.py
deleted file mode 100644
index ac89c73..0000000
--- a/djangobower/management/commands/bower_freeze.py
+++ /dev/null
@@ -1,9 +0,0 @@
-from ..base import BaseBowerCommand
-
-
-class Command(BaseBowerCommand):
-    help = 'Freeze bower apps'
-
-    def handle(self, *args, **options):
-        super(Command, self).handle(*args, **options)
-        self._freeze()
diff --git a/djangobower/management/commands/bower_install.py b/djangobower/management/commands/bower_install.py
deleted file mode 100644
index 8163298..0000000
--- a/djangobower/management/commands/bower_install.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from optparse import make_option
-from ..base import BaseBowerCommand
-
-
-class Command(BaseBowerCommand):
-    help = 'Install bower apps'
-
-    # for Django<1.10
-    option_list = BaseBowerCommand.option_list + (
-        make_option('-F',
-                    action='store_true',
-                    dest='force',
-                    default=False,
-                    help='Force installation of latest package version on conflict'),
-        make_option('-R', '--allow-root',
-                    action='store_true',
-                    dest='allow-root',
-                    default=False,
-                    help='Allow installing bower packages even when user executing this script is root'),
-    )
-    # for Django>=1.10
-    def add_arguments(self, parser):
-        parser.add_argument('--force',
-                            '-F',
-                            action='store_true',
-                            dest='force',
-                            default=False,
-                            help='Force installation of latest package version on conflict')
-        parser.add_argument('--allow-root',
-                            '-R',
-                            action='store_true',
-                            dest='allow-root',
-                            default=False,
-                            help='Allow installing bower packages even when user executing this script is root')
-
-    def handle(self, *args, **options):
-        super(Command, self).handle(*args, **options)
-
-        if options.get('force'):
-            args = args + ("-F", )
-
-        if options.get('allow-root'):
-            args = args + ("--allow-root", )
-        
-        self._install(args)
diff --git a/djangobower/shortcuts.py b/djangobower/shortcuts.py
deleted file mode 100644
index f1bf112..0000000
--- a/djangobower/shortcuts.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import os, sys
-
-
-def is_executable(path):
-    """Check file is executable"""
-    if sys.platform == 'win32':
-        executable = os.access(path, os.X_OK) or path.lower().endswith(".cmd")
-        return os.path.isfile(path) and executable
-    return os.path.isfile(path) and os.access(path, os.X_OK)
-
-
-def which(program):
-    """
-    Find by path and check exists.
-    Analog of unix `which` command.
-    """
-    path, name = os.path.split(program)
-    if path:
-        if is_executable(program):
-            return program
-    else:
-        for path in os.environ["PATH"].split(os.pathsep):
-            path = path.strip('"')
-            exe_file = os.path.join(path, program)
-            if is_executable(exe_file):
-                return exe_file
-
-    return None
diff --git a/djangobower/tests/test_bower.py b/djangobower/tests/test_bower.py
deleted file mode 100644
index 75ee910..0000000
--- a/djangobower/tests/test_bower.py
+++ /dev/null
@@ -1,173 +0,0 @@
-from django.core.management import call_command
-from django.conf import settings
-from django.test import TestCase
-from six import StringIO
-from mock import MagicMock
-from ..bower import bower_adapter, BowerAdapter
-from .. import conf
-from .base import BaseBowerCase, TEST_COMPONENTS_ROOT
-import os
-import shutil
-
-
-class BowerAdapterCase(TestCase):
-    """
-    BowerAdapter regression tests.
-    """
-
-    def test_create_components_root_subdirs(self):
-        """
-        create_components_root() creates missing intermediate directories.
-        """
-        if os.path.exists(TEST_COMPONENTS_ROOT):
-            shutil.rmtree(TEST_COMPONENTS_ROOT)
-
-        subdir = os.path.join(TEST_COMPONENTS_ROOT, 'sub', 'dir')
-        adapter = BowerAdapter(conf.BOWER_PATH, subdir)
-        adapter.create_components_root()
-        self.assertTrue(os.path.exists(subdir))
-
-        shutil.rmtree(TEST_COMPONENTS_ROOT)
-
-
-class BowerInstallCase(BaseBowerCase):
-    """Test case for bower_install management command"""
-
-    def setUp(self):
-        super(BowerInstallCase, self).setUp()
-        self.apps = settings.BOWER_INSTALLED_APPS
-        self._original_install = bower_adapter.install
-        bower_adapter.install = MagicMock()
-
-    def tearDown(self):
-        super(BowerInstallCase, self).tearDown()
-        bower_adapter.install = self._original_install
-
-    def test_create_components_root(self):
-        """Test create components root"""
-        self._remove_components_root()
-        call_command('bower_install')
-
-        self.assertTrue(os.path.exists(TEST_COMPONENTS_ROOT))
-
-    def test_install(self):
-        """Test install bower packages"""
-        call_command('bower_install')
-        bower_adapter.install.assert_called_once_with(self.apps)
-
-
-class BowerFreezeCase(BaseBowerCase):
-    """Case for bower freeze"""
-
-    def setUp(self):
-        super(BowerFreezeCase, self).setUp()
-        bower_adapter.install(['jquery#1.9'])
-        bower_adapter.install(['backbone'])
-        bower_adapter.install(['underscore'])
-        bower_adapter.install(['typeahead.js'])
-        bower_adapter.install(['backbone-tastypie'])
-
-    def test_freeze(self):
-        """Test freeze"""
-        installed = [
-            package.split('#')[0] for package in bower_adapter.freeze()
-        ]
-        self.assertCountEqual(installed, [
-            'backbone', 'jquery',
-            'typeahead.js', 'underscore',
-            'backbone-tastypie',
-        ])
-
-    def test_no_newline_in_freeze(self):
-        """Test no newline in freezee"""
-        installed = bower_adapter.freeze()
-        for package in installed:
-            self.assertNotIn('\n', package)
-
-    def test_management_command(self):
-        """Test freeze management command"""
-        stdout = StringIO()
-        call_command('bower_freeze', stdout=stdout)
-        stdout.seek(0)
-        output = stdout.read()
-
-        self.assertIn('BOWER_INSTALLED_APPS', output)
-        self.assertIn('backbone', output)
-
-
-class BowerExistsCase(BaseBowerCase):
-    """
-    Test bower exists checker.
-    This case need bower to be installed.
-    """
-
-    def setUp(self):
-        super(BowerExistsCase, self).setUp()
-        self._original_exists = bower_adapter.is_bower_exists
-
-    def tearDown(self):
-        super(BowerExistsCase, self).tearDown()
-        bower_adapter.is_bower_exists = self._original_exists
-
-    def test_if_exists(self):
-        """Test if bower exists"""
-        self.assertTrue(bower_adapter.is_bower_exists())
-
-    def test_if_not_exists(self):
-        """Test if bower not exists"""
-        adapter = BowerAdapter('/not/exists/path', TEST_COMPONENTS_ROOT)
-        self.assertFalse(adapter.is_bower_exists())
-
-    def _mock_exists_check(self):
-        """Make exists check return false"""
-        bower_adapter.is_bower_exists = MagicMock()
-        bower_adapter.is_bower_exists.return_value = False
-
-
-class BowerCommandCase(BaseBowerCase):
-    """Test case for ./manage.py bower something command"""
-
-    def setUp(self):
-        super(BowerCommandCase, self).setUp()
-        self.apps = settings.BOWER_INSTALLED_APPS
-        self._mock_bower_adapter()
-
-    def _mock_bower_adapter(self):
-        self._original_install = bower_adapter.install
-        bower_adapter.install = MagicMock()
-        self._orig_call = bower_adapter.call_bower
-        bower_adapter.call_bower = MagicMock()
-        self._orig_freeze = bower_adapter.freeze
-        bower_adapter.freeze = MagicMock()
-
-    def tearDown(self):
-        super(BowerCommandCase, self).tearDown()
-        bower_adapter.install = self._original_install
-        bower_adapter.call_bower = self._orig_call
-        bower_adapter.freeze = self._orig_freeze
-
-    def test_install_without_params(self):
-        """Test that bower install without param identical
-        with bower_install
-
-        """
-        call_command('bower', 'install')
-        bower_adapter.install.assert_called_once_with(
-            self.apps)
-
-    def test_install_with_params(self):
-        """Test bower install <something>"""
-        call_command('bower', 'install', 'jquery')
-        bower_adapter.call_bower.assert_called_once_with(
-            ('install', 'jquery'))
-
-    def test_freeze(self):
-        """Test bower freeze command"""
-        call_command('bower', 'freeze')
-        bower_adapter.freeze.assert_called_once_with()
-
-    def test_call_to_bower(self):
-        """Test simple call to bower"""
-        call_command('bower', 'update')
-        bower_adapter.call_bower.assert_called_once_with(
-            ('update',))
diff --git a/docs/Makefile b/docs/Makefile
index 41b25a5..fe36a6d 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -77,17 +77,17 @@ qthelp:
 	@echo
 	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
 	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-bower.qhcp"
+	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-yarnpkg.qhcp"
 	@echo "To view the help file:"
-	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-bower.qhc"
+	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-yarnpkg.qhc"
 
 devhelp:
 	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
 	@echo
 	@echo "Build finished."
 	@echo "To view the help file:"
-	@echo "# mkdir -p $$HOME/.local/share/devhelp/django-bower"
-	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-bower"
+	@echo "# mkdir -p $$HOME/.local/share/devhelp/django-yarnpkg"
+	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/django-yarnpkg"
 	@echo "# devhelp"
 
 epub:
diff --git a/docs/conf.py b/docs/conf.py
index 915d8fe..f20dc83 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,6 +1,6 @@
 # -*- coding: utf-8 -*-
 #
-# django-bower documentation build configuration file, created by
+# django-yarnpkg documentation build configuration file, created by
 # sphinx-quickstart on Tue Jul 16 16:38:23 2013.
 #
 # This file is execfile()d with the current directory set to its containing dir.
@@ -40,17 +40,17 @@ source_suffix = '.rst'
 master_doc = 'index'
 
 # General information about the project.
-project = u'django-bower'
-copyright = u'2013, Vladimir Iakovlev'
+project = u'django-yarnpkg'
+copyright = u'2013, Vladimir Iakovlev; 2019, Dominik George'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = '4.7'
+version = '6.0'
 # The full version, including alpha/beta/rc tags.
-release = '4.8'
+release = '6.0'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -164,7 +164,7 @@ html_static_path = ['_static']
 #html_file_suffix = None
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = 'django-bowerdoc'
+htmlhelp_basename = 'django-yarnpkgdoc'
 
 
 # -- Options for LaTeX output --------------------------------------------------
@@ -183,7 +183,7 @@ latex_elements = {
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title, author, documentclass [howto/manual]).
 latex_documents = [
-  ('index', 'django-bower.tex', u'django-bower Documentation',
+  ('index', 'django-yarnpkg.tex', u'django-yarnpkg Documentation',
    u'Vladimir Iakovlev', 'manual'),
 ]
 
@@ -213,7 +213,7 @@ latex_documents = [
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
 man_pages = [
-    ('index', 'django-bower', u'django-bower Documentation',
+    ('index', 'django-yarnpkg', u'django-yarnpkg Documentation',
      [u'Vladimir Iakovlev'], 1)
 ]
 
@@ -227,8 +227,8 @@ man_pages = [
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-  ('index', 'django-bower', u'django-bower Documentation',
-   u'Vladimir Iakovlev', 'django-bower', 'One line description of project.',
+  ('index', 'django-yarnpkg', u'django-yarnpkg Documentation',
+   u'Vladimir Iakovlev', 'django-yarnpkg', 'One line description of project.',
    'Miscellaneous'),
 ]
 
diff --git a/docs/example.rst b/docs/example.rst
index 07a728f..32cc451 100644
--- a/docs/example.rst
+++ b/docs/example.rst
@@ -9,7 +9,7 @@ Prepare project with:
 .. code-block:: bash
 
     ./manage.py syncdb
-    ./manage.py bower_install
+    ./manage.py yarn install
 
 And run project with:
 
diff --git a/docs/index.rst b/docs/index.rst
index 69af97a..1a5256c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,18 +1,16 @@
-.. django-bower documentation master file, created by
+.. django-yarnpkg documentation master file, created by
    sphinx-quickstart on Tue Jul 16 16:38:23 2013.
    You can adapt this file completely to your liking, but it should at least
    contain the root `toctree` directive.
 
-Welcome to django-bower's documentation!
+Welcome to django-yarnpkg's documentation!
 ========================================
 
-Easy way to use `bower <http://bower.io/>`_ with your `django <https://www.djangoproject.com/>`_ project.
+Easy way to use `Yarn <http://yarnpkg.com/>`_ with your `django <https://www.djangoproject.com/>`_ project.
 
-Bower is a package manager for the web. It offers a generic, unopinionated solution to the problem of front-end package management, while exposing the package dependency model via an API that can be consumed by a more opinionated build stack. There are no system wide dependencies, no dependencies are shared between different apps, and the dependency tree is flat.
+Yarn is a package manager for the web. It offers a generic, unopinionated solution to the problem of front-end package management, while exposing the package dependency model via an API that can be consumed by a more opinionated build stack. There are no system wide dependencies, no dependencies are shared between different apps, and the dependency tree is flat.
 
-Bower runs over Git, and is package-agnostic. A packaged component can be made up of any type of asset, and use any type of transport (e.g., AMD, CommonJS, etc.).
-
-`View all packages available through Bower's registry. <http://sindresorhus.com/bower-components/>`_
+Yarn runs over Git, and is package-agnostic. A packaged module can be made up of any type of asset, and use any type of transport (e.g., AMD, CommonJS, etc.).
 
 Contents:
 
@@ -24,7 +22,7 @@ Contents:
    tests
    example
 
-`Visit django-bower github page. <https://github.com/nvbn/django-bower>`_
+`Visit django-yarnpkg github page. <https://github.com/Natureshadow/django-yarnpkg>`_
 
 
 
diff --git a/docs/installation.rst b/docs/installation.rst
index e081739..637fcd6 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -2,43 +2,43 @@
 Installation
 ************
 
-Install `bower <http://bower.io/>`_ from npm:
+Install `Yarn <http://yarnpkg.com/>`_ from npm:
 
 .. code-block:: bash
 
-    npm install -g bower
+    npm install -g yarn
 
-And django-bower package:
+And django-yarnpkg package:
 
 .. code-block:: bash
 
-    pip install django-bower
+    pip install django-yarnpkg
 
 Add django-bower to `INSTALLED_APPS` in your settings:
 
 .. code-block:: python
 
-    'djangobower',
+    'django_yarnpkg',
 
 Add staticfinder to `STATICFILES_FINDERS`:
 
 .. code-block:: python
 
-    'djangobower.finders.BowerFinder',
+    'django_yarnpkg.finders.NodeModulesFinder',
 
-Specify path to components root (you need to use absolute path):
+Specify path to node modules root (you need to use absolute path):
 
 .. code-block:: python
 
-    BOWER_COMPONENTS_ROOT = '/PROJECT_ROOT/components/'
+    NODE_MODULES_ROOT = '/PROJECT_ROOT/node_modules/'
 
-If you need, you can manually set path to bower
+If you need, you can manually set path to yarn
 
 .. code-block:: python
 
-    BOWER_PATH = '/usr/bin/bower'
+    YARN_PATH = '/usr/bin/yarnpkg'
 
-Example settings file with django-bower:
+Example settings file with django-yarnpkg:
 
 .. code-block:: python
     :linenos:
@@ -68,12 +68,12 @@ Example settings file with django-bower:
 
     STATIC_URL = '/static/'
 
-    BOWER_COMPONENTS_ROOT = os.path.join(PROJECT_ROOT, 'components')
+    NODE_MODULES_ROOT = os.path.join(PROJECT_ROOT, 'node_modules')
 
     STATICFILES_FINDERS = (
         'django.contrib.staticfiles.finders.FileSystemFinder',
         'django.contrib.staticfiles.finders.AppDirectoriesFinder',
-        'djangobower.finders.BowerFinder',
+        'django_yarnpkg.finders.NodeModulesFinder',
     )
 
     SECRET_KEY = 'g^i##va1ewa5d-rw-mevzvx2^udt63@!xu$-&di^19t)5rbm!5'
@@ -101,10 +101,10 @@ Example settings file with django-bower:
 
     INSTALLED_APPS = (
         'django.contrib.staticfiles',
-        'djangobower',
+        'django_yarnpkg',
     )
 
-    BOWER_INSTALLED_APPS = (
+    YARN_INSTALLED_APPS = (
         'jquery',
         'underscore',
     )
diff --git a/docs/tests.rst b/docs/tests.rst
index 0b05515..277353e 100644
--- a/docs/tests.rst
+++ b/docs/tests.rst
@@ -2,7 +2,7 @@
 Running tests
 *************
 
-For running tests you need to install `django-bower` in development mode with:
+For running tests you need to install `django-yarnpkg` in development mode with:
 
 .. code-block:: bash
 
@@ -18,8 +18,6 @@ Now you can run tests with:
 
 .. code-block:: bash
 
-    django-admin.py test --settings=djangobower.test_settings djangobower
+    django-admin.py test --settings=django_yarnpkg.test_settings django_yarnpkg
 
 You can change test project root with `TEST_PROJECT_ROOT` environment variable. By default it is `/tmp`.
-
-You can show current tests status in `travis ci <https://travis-ci.org/nvbn/django-bower/>`_.
diff --git a/docs/usage.rst b/docs/usage.rst
index 80fe48c..e7d33ba 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -2,20 +2,20 @@
 Usage
 *****
 
-Specifie `BOWER_INSTALLED_APPS` in settings, like:
+Specify `YARN_INSTALLED_APPS` in settings, like:
 
 .. code-block:: python
 
-    BOWER_INSTALLED_APPS = (
+    YARN_INSTALLED_APPS = (
         'jquery#1.9',
         'underscore',
     )
 
-Download bower packages with management command:
+Download yarn packages with management command:
 
 .. code-block:: bash
 
-    ./manage.py bower install
+    ./manage.py yarn install
 
 Add scripts in template, like:
 
@@ -24,28 +24,22 @@ Add scripts in template, like:
     {% load static %}
     <script type="text/javascript" src='{% static 'jquery/jquery.js' %}'></script>
 
-In production you need to call `bower install` before `collectstatic`:
+In production you need to call `yarn install` before `collectstatic`:
 
 .. code-block:: bash
 
-    ./manage.py bower install
+    ./manage.py yarn install
     ./manage.py collectstatic
 
-If you need to pass arguments to bower, like `--allow-root`, use:
+If you need to pass arguments to yarn, like `--flat`, use:
 
 .. code-block:: bash
 
-    ./manage.py bower install -- --allow-root
+    ./manage.py yarn install -- --flat
 
-You can use `bower freeze` to receive `BOWER_INSTALLED_APPS` with fixed current versions:
+You can call yarn commands like `info` and `update` with:
 
 .. code-block:: bash
 
-    ./manage.py bower freeze
-
-You can call bower commands like `info` and `update` with:
-
-.. code-block:: bash
-
-    ./manage.py bower info backbone
-    ./manage.py bower update
+    ./manage.py yarn info backbone
+    ./manage.py yarn update
diff --git a/example/example/settings.py b/example/example/settings.py
index 34b370c..f057a96 100644
--- a/example/example/settings.py
+++ b/example/example/settings.py
@@ -23,12 +23,12 @@ STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static')
 
 STATIC_URL = '/static/'
 
-BOWER_COMPONENTS_ROOT = os.path.join(PROJECT_ROOT, 'components')
+NODE_MODULES_ROOT = os.path.join(PROJECT_ROOT, 'node_modules')
 
 STATICFILES_FINDERS = (
     'django.contrib.staticfiles.finders.FileSystemFinder',
     'django.contrib.staticfiles.finders.AppDirectoriesFinder',
-    'djangobower.finders.BowerFinder',
+    'django_yarnpkg.finders.NodeModulesFinder',
 )
 
 SECRET_KEY = 'g^i##va1ewa5d-rw-mevzvx2^udt63@!xu$-&di^19t)5rbm!5'
@@ -56,10 +56,10 @@ TEMPLATE_DIRS = (
 
 INSTALLED_APPS = (
     'django.contrib.staticfiles',
-    'djangobower',
+    'django_yarnpkg',
 )
 
-BOWER_INSTALLED_APPS = (
+YARN_INSTALLED_APPS = (
     'jquery',
     'underscore',
 )
diff --git a/example/components/.gitkeep b/example/node_modules/.gitkeep
similarity index 100%
rename from example/components/.gitkeep
rename to example/node_modules/.gitkeep
diff --git a/example/templates/index.html b/example/templates/index.html
index 017d434..f6adfed 100644
--- a/example/templates/index.html
+++ b/example/templates/index.html
@@ -2,7 +2,7 @@
 {% load static %}
 <html>
 <head>
-    <title>django-bower demo</title>
+    <title>django-yarnpkg demo</title>
     <script type="text/javascript" src='{% static 'jquery/dist/jquery.js' %}'></script>
     <script type="text/javascript" src='{% static 'underscore/underscore.js' %}'></script>
     <script type="text/javascript">
diff --git a/runtests.py b/runtests.py
index 274d508..115c8a2 100644
--- a/runtests.py
+++ b/runtests.py
@@ -4,7 +4,7 @@ import sys
 
 if __name__ == "__main__":
     os.environ.setdefault(
-        "DJANGO_SETTINGS_MODULE", 'djangobower.test_settings',
+        "DJANGO_SETTINGS_MODULE", 'django_yarnpkg.test_settings',
     )
 
     from django.core.management import execute_from_command_line
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index fa9fe18..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1 +0,0 @@
-[egg_info]
diff --git a/setup.py b/setup.py
index d12142f..bd6eeb3 100644
--- a/setup.py
+++ b/setup.py
@@ -1,11 +1,11 @@
 from setuptools import setup, find_packages
 
-version = '5.2.0'
+version = '6.0.0'
 
 setup(
-    name='django-bower',
+    name='django-yarnpkg',
     version=version,
-    description="Integrate django with bower",
+    description="Integrate django with yarnpkg",
     long_description=open('README.rst').read(),
     classifiers=[
         'Framework :: Django',
@@ -14,9 +14,9 @@ setup(
         'Programming Language :: Python :: 3',
     ],
     keywords='',
-    author='Vladimir Iakovlev',
-    author_email='nvbn.rm@gmail.com',
-    url='https://github.com/nvbn/django-bower',
+    author='Dominik George',
+    author_email='nik@naturalnet.de',
+    url='https://github.com/Natureshadow/django-yarnpkg',
     license='BSD',
     packages=find_packages(exclude=['example']),
     include_package_data=True,
-- 
GitLab