├── pipeline_browserify ├── __init__.py └── compiler.py ├── .gitignore ├── setup.cfg ├── setup.py ├── LICENCE.txt └── README.rst /pipeline_browserify/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea 3 | django_pipeline_browserify.egg-info 4 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description-file = README.md 3 | 4 | [wheel] 5 | universal = 1 6 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import io 3 | from setuptools import setup, find_packages 4 | 5 | description = """ 6 | django-pipeline compiler for browserify, requires browserify to be installed. 7 | """ 8 | 9 | setup( 10 | name='django-pipeline-browserify', 11 | version='0.6.2', 12 | description=description, 13 | long_description=io.open('README.rst', encoding='utf-8').read(), 14 | author='j0hnsmith', 15 | url='https://github.com/j0hnsmith/django-pipeline-browserify', 16 | install_requires=['django-pipeline>=1.6.9'], 17 | packages=find_packages(), 18 | classifiers=[ 19 | 'Environment :: Web Environment', 20 | 'Intended Audience :: Developers', 21 | 'License :: OSI Approved :: MIT License', 22 | 'Operating System :: OS Independent', 23 | 'Programming Language :: Python', 24 | 'Topic :: Utilities', 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) [2014] [j0hnsmith] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Django Pipeline Browserify 2 | ========================== 3 | 4 | django-pipeline-browserify is a compiler for `django-pipeline `_ (requires 16.9+). Making it really easy to use browserify with Django via pipeline. 5 | 6 | To install it:: 7 | 8 | sudo npm install -g browserify 9 | pip install django-pipeline-browserify 10 | 11 | And add it as a compiler to pipeline in your django `settings.py`:: 12 | 13 | PIPELINE = { 14 | # ... 15 | 'COMPILERS': ('pipeline_browserify.compiler.BrowserifyCompiler', ), 16 | # ... 17 | ) 18 | 19 | To add source maps during development (or any other browserify args):: 20 | 21 | if DEBUG: 22 | PIPELINE['BROWSERIFY_ARGS'] = ['-d'] 23 | 24 | Passing arguments as an array makes sure they are safely unambiguous, but the way browserify lets you pass nested arguments within brackets can make this very tedious:: 25 | 26 | # this is very unreadable, and hard to maintain! 27 | PIPELINE['BROWSERIFY_ARGS'] = ['--transform', '[', 'babelify', '--presets', '[', 'es2015', 'react', ']', '--plugins', '[', 'transform-object-rest-spread', 'transform-class-properties', ']', ']'] 28 | 29 | To avoid this, when you know that no individual argument has a space within it, simply split the arguments yourself:: 30 | 31 | # the easy way :-) 32 | PIPELINE['BROWSERIFY_ARGS'] = "--transform [ babelify --presets [ es2015 react ] --plugins [ transform-object-rest-spread transform-class-properties ] ]".split() 33 | 34 | 35 | To set environment varaibles specific to the browserify command:: 36 | 37 | PIPELINE['BROWSERIFY_ENV'] = {'NODE_ENV':'production'} 38 | 39 | (Note that for an actual production build, this example is not sufficient. You'll probably want to use a transform like loose-envify so the minifier can optimize out debug statements. Browserify doesn't usually pass environment variables like that shown above into the compiled code; but it may effect the runtime behavior of browserify itself.) 40 | 41 | To use a local install of the browserify command line utility (or if it is not in your PATH for some other reason), you can override the command used:: 42 | 43 | PIPELINE['BROWSERIFY_BINARY'] = "/custom/path/to/browserify" 44 | 45 | # ...or perhaps something like this: 46 | PIPELINE['BROWSERIFY_BINARY'] = os.path.join(REPO_ROOT, "node_modules/.bin", "browserify"), 47 | 48 | 49 | **Important:** give your entry-point file a `.browserify.js` extension:: 50 | 51 | PIPELINE = { 52 | # ... 53 | 'javascript':{ 54 | 'browserify': { 55 | 'source_filenames' : ( 56 | 'js/entry-point.browserify.js', 57 | ), 58 | 'output_filename': 'js/entry-point.js', 59 | }, 60 | } 61 | } 62 | 63 | To suggest a feature or report a bug: 64 | https://github.com/j0hnsmith/django-pipeline-browserify/issues 65 | -------------------------------------------------------------------------------- /pipeline_browserify/compiler.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | 3 | from pipeline.compilers import SubProcessCompiler 4 | from django.conf import settings 5 | from django.core.exceptions import SuspiciousFileOperation 6 | from pipeline.exceptions import CompilerError 7 | from warnings import warn 8 | 9 | class BrowserifyCompiler(SubProcessCompiler): 10 | output_extension = 'browserified.js' 11 | 12 | def match_file(self, path): 13 | if self.verbose: 14 | print('matching file:', path) 15 | return path.endswith('.browserify.js') 16 | 17 | # similar to old removed in https://github.com/jazzband/django-pipeline/commit/1f6b48ae74026a12f955f2f15f9f08823d744515 18 | def simple_execute_command(self, cmd, **kwargs): 19 | import subprocess 20 | try: 21 | pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs) 22 | except OSError as e: 23 | raise CompilerError("Compiler failed to execute. (%s)" % e, command=cmd, error_output="Executing the compiler resulted in an %s from the system.\n\nThis is most likely due to the `browserify` executable not being found in your PATH (if it is installed globally), or a misconfigured BROWSERIFY_BINARY setting (if you are using a non-global install)." % repr(e)) 24 | stdout, stderr = pipe.communicate() 25 | if self.verbose: 26 | print(stdout) 27 | print(stderr) 28 | if pipe.returncode != 0: 29 | raise CompilerError("Compiler returned non-zero exit status %i" % pipe.returncode, command=cmd, error_output=stderr) 30 | return stdout.decode() 31 | 32 | def _get_cmd_parts(self): 33 | pipeline_settings = getattr(settings, 'PIPELINE', {}) 34 | tool = pipeline_settings.get('BROWSERIFY_BINARY', "browserify") 35 | 36 | old_args = pipeline_settings.get('BROWSERIFY_ARGUMENTS', '') 37 | if old_args: 38 | warn("You are using the string-based BROWSERIFY_ARGUMENTS setting. Please migrate to providing a list of arguments via BROWSERIFY_ARGS instead.", DeprecationWarning) 39 | args = old_args.split() 40 | else: 41 | args = pipeline_settings.get('BROWSERIFY_ARGS', []) 42 | 43 | old_env = pipeline_settings.get('BROWSERIFY_VARS', '') 44 | if old_env: 45 | warn("You are using the string-based BROWSERIFY_VARS setting. Please migrate to providing a dict of environment variables via BROWSERIFY_ENV instead.", DeprecationWarning) 46 | env = dict(map(lambda s: s.split('='), old_env.split())) 47 | else: 48 | env = pipeline_settings.get('BROWSERIFY_ENV', None) 49 | if env: 50 | # when there's custom variables, we need to explicitly pass along the original environment 51 | import os 52 | _env = {} 53 | _env.update(os.environ) 54 | _env.update(env) 55 | env = _env 56 | 57 | return tool, args, env 58 | 59 | def compile_file(self, infile, outfile, outdated=False, force=False): 60 | if not force and not outdated: 61 | return 62 | 63 | tool, args, env = self._get_cmd_parts() 64 | cmd = [tool] + args + [infile, '--outfile', outfile] 65 | 66 | if self.verbose: 67 | print("compile_file command:", cmd, env) 68 | self.simple_execute_command(cmd, env=env) 69 | 70 | def is_outdated(self, infile, outfile): 71 | """Check if the input file is outdated. 72 | 73 | The difficulty with the default implementation is that any file that is 74 | `require`d from the entry-point file will not trigger a recompile if it 75 | is modified. This overloaded version of the method corrects this by generating 76 | a list of all required files that are also a part of the storage manifest 77 | and checking if they've been modified since the last compile. 78 | 79 | The command used to generate the list of dependencies is the same as the compile 80 | command but uses the `--list` option instead of `--outfile`. 81 | 82 | WARNING: It seems to me that just generating the dependencies may take just 83 | as long as actually compiling, which would mean we would be better off just 84 | forcing a compile every time. 85 | """ 86 | 87 | # Preliminary check for simply missing file or modified entry-point file. 88 | if super(BrowserifyCompiler, self).is_outdated(infile, outfile): 89 | return True 90 | 91 | # Otherwise we need to see what dependencies there are now, and if they're modified. 92 | tool, args, env = self._get_cmd_parts() 93 | cmd = [tool] + args + ['--list', infile] 94 | if self.verbose: 95 | print("is_outdated command:", cmd, env) 96 | dep_list = self.simple_execute_command(cmd, env=env) 97 | if self.verbose: 98 | print("dep_list is:", dep_list) 99 | for dep_file in dep_list.strip().split('\n'): 100 | if super(BrowserifyCompiler, self).is_outdated(dep_file, outfile): 101 | if self.verbose: 102 | print("Found dep_file \"%s\" updated." % dep_file) 103 | return True 104 | 105 | return False 106 | --------------------------------------------------------------------------------